Skip to main content
 

Afrofuturism Exhibit

Anyone want to go to Oakland Museum a week from Sunday? With me and some other interesting folks? Pop into this twitter thread..

https://twitter.com/lindner/status/1426379023802724354

 

CORS vs Gmail = no doggo pics

Any of the Chrome folks want to figure out why SameSIte cookies are making
it impossible to add inline images in gmail compose?

Bug appears to be here: https://b.corp.google.com/issues/144398439

Looks like it's more than just Googlers:

https://support.google.com/mail/thread/19811479?hl=en

 

Android Phone Transfer thread

Not sure where to file feedback about things, but my Pixel 2XL screen started to fail so got a Pixel 3 XL (want to keep fingerprint sensor, plus way discounted and pink!)

 

Feedback...

_People long to be treated as co-creators. Not cattle._

[pulled from the massive Google+ feedback thread...]

 

Decentralized Web Summit

Attending the Decentralized Web Summit day 2. Hope to see some familiar and new faces.

http://www.decentralizedweb.net/

 

 

Diversity and Haircuts

There's a good discussion on industryinfo about diversity and how haircuts factor into that. Don't want to derail that thread so posting here..

At 22 I went to work for the UN in Geneva. My wife and I drove 3h to Zurich to get our cuts at Time Tunnel. Geneva Salons were too snobby for a metalhead and a punk used to the Hair Police in Minneapolis.

Eventually we found found our counter cultural tribe at http://usine.ch/ and http://www.lecheveusurlasoupe.net/

What I will say is that working and living abroad taught me valuable lessons on diversity, inclusion and empathy.

http://timetunnelhair.ch/category/salon/

 

Reddit

Digging into some Sharing patterns wrt to Reddit.

This reddit thread:

https://www.reddit.com/r/Android/comments/3mh63x/artem_russakovskii_3450mah_battery_confirmed_on/

Posted by some random user:

https://www.reddit.com/user/sajdx1

Contains a link to a post by an Android Police author and prolific G+ user:

https://plus.google.com/+ArtemRussakovskii

That link in Reddit was clicked on 28k times (probably much more since these logs are only web requests..)

select top(request,15) as h, count(*) as c from
gfstmp_oz.tmp_oz_fe_weblog.20150927 where AppsFrameworkExtension.action_class = 'PermalinkedUpdate' and referer like '%reddit.com%';
+-----------------------------------------------------------------------------------+-------+
| h | c |
+-----------------------------------------------------------------------------------+-------+
| GET /+ArtemRussakovskii/posts/T9fdFDBp1fd HTTP/1.1 | 28917 |
| GET /+ArtemRussakovskii/posts/hpc36iQuRSR HTTP/1.1 | 3665 |
| GET /u/0/108441771977176576567/posts/QQNFtAeEjTY HTTP/1.1 | 2088 |
| GET /100098741112799154035/posts/Lavu4mVTvpX HTTP/1.1 | 1899 |
| GET /+ArtemRussakovskii/posts/bA4ryjrT9MJ HTTP/1.1 | 1827 |

*Someone send Artem a cookie, or some G+ swag...*

https://www.reddit.com/r/Android/comments/3mh63x/artem_russakovskii_3450mah_battery_confirmed_on/

 

Another java project "goes guava"

Another java project "goes guava"

Originally shared by James Snell

More Abdera2 Updates... this is a copy of an email I just sent to the Abdera-dev mailing list...

----------

Ok, for those of you who may not have seen it, I posted another major update to the Abdera2 code yesterday. Where as the first round of updates focused primarily on updating dependencies and the introduction of the Activity Streams capability, this update focused more on API Refactoring and the introduction of two new major dependencies: the Joda-Time Library (http://joda-time.sourceforge.net/) and the Google Guava Libraries (http://code.google.com/p/guava-libraries/).

First up, Joda-Time... for those who aren't familiar with it, joda-time is a comprehensive code library for working with dates, times, durations, intervals, etc. When I wrote the first version of the Abdera Feed Object Model API, there wasn't a good open-source implementation of the ISO8601 DateTime format required by the Atom specification available so I wrote a fairly limited, down and dirty implementation in the form of the AtomDate class. It had decent performance, fairly good coverage and got the job done. Joda-Time, however, has emerged since as a top quality rich implementation of the 8601 standard... so even though it is a breaking change to the existing Feed Object Model API, I have gone through an have replaced Abdera's own implementation with Joda-Time's DateTime class, which, when used in combination with the mechanisms provided by the Google Guava libraries, provides for some very interesting and compelling new capabilities.

Which, of course, brings me to Guava. This library is a collection of extremely useful utility classes from google. This update brings significant deep integration with Guava in a number of ways, the most important of which is the new Selector API that I introduced as part of the first Abdera2 checkin.

Among many other things, Guava defines a number of interfaces and utility classes aimed at making it easier for developers to write quality, functional, readable code that has a more natural flow to it. It is easiest to show by example.

In Abdera 1.x, if I wanted, for instance, to extract a list of entries from an Atom feed that had been edited after a specific date and time, it would look something like this...

AtomDate ad = new AtomDate("2011-09-10T12:12:12Z");

List list = feed.getEntries();

List selected = new ArrayList();

for (Entry entry : list) {

if (entry.getEdited() != null) {

if (entry.getEdited().after(ad.getDate()))

selected.add(entry);

}

}

Note that feed.getEntries() will return every entry from the feed whether we want it or not. We then have to iterate back over that list, check to make sure there's an edited date, compare those and build up a new list. The code is ugly and cumbersome and inefficient. With the code I just checked in, the same process looks like this...

import static org.abdera.abdera2.model.selector.Selectors.*;

import static org.abdera.abdera2.common.date.DateTimes.*;

List list =

feed.getEntries(

edited(after(dt("2011-09-10T12:12:12Z")))

);

The portion, edited(after(dt("..."))) constructs a Selector that filters the list of items returned by getEntries(), keeping us from having to iterate back over the list.

Suppose we wanted to add another condition to the mix.. say some custom selector that checks for the presence of a particular extension... We can implement our CustomSelector by extending the AbstractSelector class, and merely append that in to the code above like so...

import static org.abdera.abdera2.model.selector.Selectors.*;

import static org.abdera.abdera2.common.date.DateTimes.*;

Selector customSel = new AbstractSelector { ... }

List list =

feed.getEntries(

edited(after(dt("2011-09-10T12:12:12Z")))

.and(customSel)

);

Underlying the Selector API a large chunk of the Guava API... specifically the Predicate, Function, and Constraint interfaces. The Selector interface actually extends from Predicate and Constraint and provides a mechanism for being cast as a Function.

A broad range of utility methods have been provided that create constructors for many of the most common cases, in particular DateTime related operations. Look at the following classes for those utility methods...

org.apache.abdera2.common.date.DateTimes

org.apache.abdera2.common.selector.Selector.Utils

org.apache.abdera2.model.selector.Selectors (for Atom specific utilities)

org.apache.abdera2.activities.extra.Extra (for Activity Streams specific utilities)

The Selector mechanism has been baked into both the Atom and Activity Streams APIs now. For instance, suppose we have an Activity stream but we only want a max of 10 activities for which a given user is the intended target (using the Activity Streams Audience Targeting Extension.. which I can explain later .. lol).. We could get that list of entries using...

PersonObject person = new PersonObject();

person.setId("acct:john.doe@example.org");

Iterable list = stream.getItems(isTo(person).limit(10));

Additional changes in this update include....

1. Refactoring classes into immutable thread-safe objects. This will be an ongoing change. As much as possible, a Factory/Builder model for most basic object types will be used as opposed to the more traditional getter/setter model. The motivation behind this change is simple in that it helps make more a much more scalable architecture and significantly more readable code.

For instance, if you wish to construct a Cache-Control header, you can use a simple fluent builder api...

CacheControl cc =

CacheControl

.make()

.isPublic(true)

.noTransform(true)

.maxAge(1000)

.get();

Likewise if you wish to construct a new WebLink HTTP Header,

WebLink link =

WebLink

.make()

.iri("http://example.org")

.rel("alternate")

.title("Home")

.get();

The pattern is simple and consistent throughout.

2. Added support for Guava objects in the URI Template implementation... specifically, a URI Template Context can now include the Guava Multimap, Supplier and Optional values. In addition, support for java.util.concurrent.Future, java.lang.ref.Reference and java.util.concurrent.Callable were also added. The one caveat when using Future, however, is that the context will not wait for a value to become available. The Context calls Future.get() and takes whatever it gets back as the value so before you use a Future in a URI Template, make sure it's been completed.

3. org.apache.abdera2.common.text.CharUtils has been refactored. This class was always a bit of a hacky mess. It's been cleaned up significantly around a new CodepointMatcher that is modeled after Guava's CharMatcher interface. Codepoint matcher, however, is designed to work specifically with Unicode Codepoints rather than Java Chars. For the most part, CharUtils and CodepointMatcher are internal classes that the majority of users won't ever have to mess with.

4. Filter/FilterChain has been refactored. This is the part of the Server framework that allows you to plug in a chain of filters before invoking a Publishing Protocol Provider. Previously, the Filter and FilterChain interfaces were very specific to the Server API.. they have been refactored into generic Chain and Task interfaces and moved to org.apache.abdera2.common.misc.*. This allows the use of Chain anywhere you may need a simple interceptor framework...

A trivial example,

Task lower =

new Task() {

public Void apply(

String input,

Chain flow) {

if (input == null) return null;

return flow.next(input.toLowerCase());

}

};

Function print =

new Function() {

public Void apply(String input) {

System.out.println(input);

return null;

}

};

Function chain = new Chain(print,lower);

chain.apply("HELLO!");

The Google Guava api does provide the means of composing multiple Function objects together such that the output of one flows into the input of another, but it only works in one direction.. the Chain here allows you to intercept inputs and outputs.

There are a number of other additions here and there throughout the code, and there will be more to come. One MAJOR change that I'm currently exploring is the use of Guice as a complete replacement for the Classpath-based configuration model we currently have. Let's face it, the current stuff provides a significant amount of flexibility and power, but initialization is slow. Guice is significantly faster and much more powerful than what we currently have.

Anyway, that's it for now.------

 

private to you.

private to you.

I am not engaging in RN discussions in public and probably won't be doing much of it inside Google due to the mischaracterization of my views on this subject which are not as black and white as some people might think.

Thread hijackiing is pretty rude too you know.

 

The Mysteries of Java Character Set Performance

"Two Characters Sets?  Seems like plenty!"

 

So I've been pushing Java to it's limits lately and finding some real nasty concurrency issues inside the JRE code itself.  Here's one particulary ugly one -- we had 700 threads stuck here:

 
       java.lang.Thread.State: BLOCKED (on object monitor)                                                                    
         at sun.nio.cs.FastCharsetProvider.charsetForName(FastCharsetProvider.java:118)
         - waiting to lock <0x00002aab4cdf91b8> (a sun.nio.cs.StandardCharsets)
         at java.nio.charset.Charset.lookup2(Charset.java:450) 
         at java.nio.charset.Charset.lookup(Charset.java:438)
         at java.nio.charset.Charset.isSupported(Charset.java:480) 
         at java.lang.StringCoding.lookupCharset(StringCoding.java:85) 
         at java.lang.StringCoding.decode(StringCoding.java:165)                                                                      
         at java.lang.String.(String.java:516) 
 
Digging deeper we find the lookupCharset is called all over the place.  The app in question is functions as a web proxy, so it's constantly reading and writing data from web pages in a variety of character sets.  The method charsetForName() uses a synchronized data structure to lookup defined character sets.  (Yay serialized access....)
 
But wait, lookup and lookup2 provide us with a cache so we can avoid the big bad synchronized method..  Sigh, here's the implementation:
 
     private static Charset lookup(String charsetName) {
         if (charsetName == null)
             throw new IllegalArgumentException("Null charset name");
 
         Object[] a;
         if ((a = cache1) != null && charsetName.equals(a[0]))
             return (Charset)a[1];
         // We expect most programs to use one Charset repeatedly.
         // We convey a hint to this effect to the VM by putting the
         // level 1 cache miss code in a separate method.
         return lookup2(charsetName);
     }
 
     private static Charset lookup2(String charsetName) {
         Object[] a;
         if ((a = cache2) != null && charsetName.equals(a[0])) {
             cache2 = cache1;
             cache1 = a;
             return (Charset)a[1];
         }
 
         Charset cs;
         if ((cs = standardProvider.charsetForName(charsetName)) != null ||
             (cs = lookupExtendedCharset(charsetName))           != null ||
             (cs = lookupViaProviders(charsetName))              != null)
         {
             cache(charsetName, cs);
             return cs;
         }
 
         /* Only need to check the name if we didn't find a charset for it */
         checkName(charsetName);
         return null;
     }
 
Yes, a whopping 2-entry cache!!
 
Also, the keys used are not canonical, so if my app asks for "UTF-8", "utf-8", and "ISO-8859-1" with regularity this 2 entry cache is worthless, every call ends up blocking in the evil thread-synchronized data structure.
 
Someone send them a copy of the ConcurrentHashMap doc.  please.
 
....