Abdera2 - Common Library TBD
Date and Time Handling within Abdera2 extensively leverages the capabilities of both the Joda-Time and Guava Libraries. Specifically, Joda-Time handles all operations with regards to the handling of the ISO-8601 formatted timestamps required by both the Atom Syndication Format and JSON Activity Streams standards. The Guava Library is used as the foundation for a range of utilities that make working with those dates easier, and more fluid within the Abdera2 API. Use of the Date-Time utilities provided by the Common Library is driven deep and extensively throughout the Abdera2 API for both the Atom Syndication Format and Activity Streams implementations. For example, suppose we have a stream of Activity objects and we want to iterate only those published before the current date and time:
import static org.apache.abdera2.activities.extra.Extra.activityPublished; import static org.apache.abdera2.common.date.DateTimes.beforeNow; //... col.getItems(activityPublished(beforeNow()));
The response to getItems() called this way will be an Iterable whose items specify published property values are date-times occurring before now. Obviously, this is a bit of a contrived example given that, typically, all items in the stream will be published before the current date and time, but you should get the basic idea. In the following example, we ask only for the activities that have been published within the last 60 seconds:
DateTime now = now(); DateTime before = now().minusSeconds(60); col.getItems(activityPublished(atOrBetween(before,now)));
The Abdera2 Common Library includes support for a handful of complex HTTP Headers including WWW-Authenticate, Cache-Control, ETag, Link and Preference.
The Authentication header support makes it easier to support custom authentication mechanisms within Abdera2 applications. For instance, to use OAuth 2.0 Bearer Tokens, you can use:
Authentication bearer = Authentication.make() .scheme("bearer") .param("realm", "example") .get();
Which generates: WWW-Authenticate: bearer realm="example"
Adding additional parameters is straightforward:
Authentication bearer = Authentication.make() .scheme("bearer") .param("realm", "example") .param("error", "invalid_token") .param("error_description", "The access token expired") .get();
Parsing the header is equally straightforward:
auths = Authentication.parse( "bearer realm=\"example\", error=invalid_token"+ ", error_description=\"The access token expired\""); Authentication auth = auths.iterator().next(); System.out.println(auth.getScheme()); for (String name : auth) { System.out.println( String.format( "%s = %s", name, auth.getParam(name))); } ]]>
The CacheControl class makes it simple to generate and parse any combination of Cache-Control directives:
CacheControl cc = CacheControl.make() .isPublic() .maxAge(1000) .mustRevalidate() .get();
Generates: Cache-Control: public, must-revalidate, max-age=1000
CacheControl cc = CacheControl.make() .noCache() .noStore() .noTransform() .isPrivate() .get();
Generates: Cache-Control: private, no-cache, no-store, no-transform
Extension directives are also supported:
CacheControl cc = CacheControl.make() .isPublic() .extension("foo", "bar") .get();
Generates: Cache-Control: public, foo=bar
Create a simple strong EntityTag: EntityTag tag = EntityTag.create("FooBarBaz")
Generates: ETag: "FooBarBaz"
Create a weak EntityTag: EntityTag tag = EntityTag.weak("FooBarBaz");
Generates: ETag: W/"FooBarBaz"
Generating an Entity Tag from source material: EntityTag tag = EntityTag.generate("foo","bar","baz");
Generates: ETag: "6DF23DC03F9B54CC38A0FC1483DF6E21"
Parsing Entity Tags: list = EntityTag.parseTags( "\"FooBarBaz\", W/\"6DF23DC03F9B54CC38A0FC1483DF6E21\""); for (EntityTag tag : list) { System.out.println( String.format( "%s, is weak? %s", tag.getTag(), tag.isWeak() )); ]]>
Outputs: FooBarBaz, is weak? false 6DF23DC03F9B54CC38A0FC1483DF6E21, is weak? true
WebLink link = WebLink.make() .iri("styles.css") .rel("stylesheet") .title("Just an example") .media("print") .get();
Generates:;rel="stylesheet";media="print";title="Just an example" ]]>
Parsing: list = WebLink.parse( ";rel=\"stylesheet\";"+ "media=\"print\";title=\"Just an example\""); for (WebLink link : list) { System.out.println(link.getIri()); System.out.println(link.getRel()); System.out.println(link.getMedia()); System.out.println(link.getTitle()); } ]]>
Outputs: styles.css [stylesheet] [print] Just an example
The Prefer header is a proposed extension to HTTP.
import static org.apache.abdera2.common.http.Preference.*; System.out.println( toString( PREF_LENIENT, PREF_RETURN_ASYNCH, WAIT(1000)));
Generates: Prefer: lenient,return-asynch,wait=1000
Parsing: list = parse( "lenient,return-asynch,wait=1000"); if (contains(list, LENIENT)) { // use lenient processing } if (contains(list, RETURN_ASYNCH)) { // use asynchronous processing Preference wait = get(list, WAIT); long time = wait.getLongValue(); // ... } ]]>
The Abdera2 Common Library contains a handful of specialized InputStream and Reader implementations for performing a variety of tasks including limited character set encoding detection based on byte order marks, character code filtering, Multipart MIME parsing, peeking ahead in a stream without consuming the bytes, and rewinding an already read stream. These utilities are available in the org.apache.abdera2.common.io package.
Applying content compression to an output stream: ByteArrayOutputStream bytes = new ByteArrayOutputStream(); OutputStream out = CompressionCodec.DEFLATE.wrap(bytes); byte[] bytes = ... // bytes to output out.write(bytes); out.close();
IRI iri = new IRI("http://example.org:8080/foo/bar?baz=xyz#123456"); System.out.println(iri.getScheme()); System.out.println(iri.getHost()); System.out.println(iri.getPort()); System.out.println(iri.getPath()); System.out.println(iri.getQuery()); System.out.println(iri.getFragment());
WebLink link = WebLink.make() .iri("style.css") .get(); System.out.println(link.getResolvedIri(iri));
A nonsensical example: Lang lang = Lang.parse("en-FR-Cyril"); for (Subtag tag : lang) System.out.println( String.format( "%s\t%s", tag, tag.type()));
Outputs: en LANGUAGE FR REGION cyril VARIANT
Testing a range: Range range = Range.parse("en-*-Cyril",true); if (range.matches(lang)) { // language matches! }
TBD
Creating a javax.activation.MimeType without having to catch the MimeTypeParseException error: MimeType mime = MimeTypeHelper.create("text/plain");
Creating an immutable javax.activation.MimeType instance: MimeType mime = MimeTypeHelper.unmodifiableMimeType("text/plain");
TBD
r = cm.getReceiver("foo"); r.startListening( new SimpleListener() { public void onItem(Activity t) { System.out.println(t.getObject().getDisplayName()); latch.countDown(); } } ); } } ); Pusher pusher = cm.getPusher("foo"); for (int n = 0; n < 3; n++) pusher.push( gen.template() .set("object", makeNote() .displayName(format("My note #%d",n+1))) ); latch.await(); cm.shutdown(); ]]>
The org.apache.abdera2.common.security.HashHelper class provides a handful of helpful utility methods and Guava Function implementations for generating MD5 hashed, HMACs and simple digital signatures.
An MD5 generating Function object: md5 = HashHelper.md5(); String hash = md5.apply(new byte[] {1,2,3,4,5,6,7,8,9,0}); System.out.println(hash); ]]>
A SHA-256 generating Function object: hmac = HashHelper.sha256(key); String mac = hmac.apply(new byte[] {1,2,3,4,5,6,7,8,9,0}); System.out.println(mac); ]]>
A Digital Signature generating Function object: sig = HashHelper.sig(pair.getPrivate(), "DSA"); String s = sig.apply(new byte[] {1,2,3,4,5,6,7,8,9,0}); System.out.println(s); ]]>
A Digital Signature verifying Predicate object: verify = HashHelper.stringSignatureValid( pair.getPublic(), "DSA", new byte[] {1,2,3,4,5,6,7,8,9,0}); // the source material the verify System.out.println(verify.apply(s)); ]]>
Generating an API Key: byte[] key = new byte[] {1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0}; ApiKey apikey = ApiKey.STRONG(key); System.out.println(apikey.generateNext());
A Time-based One-time Password: byte[] key = new byte[] {1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0}; Otp.Totp totp = new Otp.Totp(30, key); System.out.println(totp.generateNext());
Implementing a custom One-Time Password Implementation:>> 24), (byte)(val >>> 16), (byte)(val >>> 8), (byte)val}; } } ]]>
Using the custom OTP provider: Otp otp = new MyOtp(); System.out.println(otp.generateNext());
The Abdera2 Selector Framework is an extension to the Guava Libraries Predicate interface and is used extensively throughout the Abdera2 API. A single Selector instance can be used to perform a broad range of tests and conditions operations.
Creating a simple selector: selector = new AbstractSelector() { public boolean select(Object item) { return item.equals("Foo"); } }; ]]>
First, let's create a simple set: set = of("Foo","Bar");]]>
We can test every item in the set...
Or perform filtering operations on the set...
Or pick matching items out of the set...<[!CDATA[ System.out.println(selector.choose(set)); // selects "Foo" System.out.println(selector.chooseNot(set)); // selects "Bar" ]]>
Or use the selector as a Predicate object:
Or as a constraint:
Or as a test...
Selectors can be composed together with others to perform more complex matching operations. For instance, the follow will select no more than 5 Strings matching either "Foo" or "Bar": selector = Selectors.of("Foo") .or(Selectors.of("Bar")) .limit(5); ]]>
Most Selector implementations are threadsafe and immutable, maintaining no internal state. However, Selectors returned by the limit() method, illustrated above, maintain an internal counter and are not considered to be threadsafe. Such Selectors should implement the org.apache.abdera2.common.selector.StatefulSelector interface to indicate that they are not threadsafe.
Abdera2 includes an implementation of the current URI Template specification. A URI Template is a specially formatted string containing tokens that can be replaced to expand into a URI or IRI. For example:
private static final Template template = new Template( "http://example.org/~{user}{/categories*}{?foo,bar}");
Expanding the template using a java.util.Map:
The URI generated by this template is: http://example.org/~johndoe/a/b/c?foo=xyz&bar=123
Expanding the template using a com.google.common.collect.Multimap: map = LinkedHashMultimap.create(); map.put("user", "james"); map.put("categories", "a"); map.put("categories", "b"); map.put("categories", "c"); map.put("foo", "abc"); map.put("bar", "xyz"); System.out.println(template.expand(map)); ]]>
Expanding the template using a Java Object: getCategories() { return ImmutableList.of("a","b","c"); } public Foo getFoo() { return new Foo(); } public String getBar() { return "xyz"; } } static class Foo { public String toString() { return "xyz"; } } System.out.println(template.expand(new MyObject())); ]]>
TBD
>, Iterable>>> f1 = compose( new MyMapper(), MapRed.countingReducer() ); private final static ReducerFunction f2 = asFunction(MapRed.invertReducer(), Collections.reverseOrder()); private final static Function< Iterable>, Iterable>>> f3 = Functions.compose(f2,f1); private final static ExecutorService exec = MoreExecutors2.getExitingExecutor(); private static final Function,Iterable>> transform = new Function,Iterable>>() { public Iterable> apply(Collection input) { return Pair.make() .index(MoreFunctions.alwaysVoid(), input.getItems()); } }; private final static Function< Collection, Future>>>> ff = Functions.compose( MoreFunctions.< Iterable>, Iterable>>>futureFunction(f3,exec), transform); private Activity getActivity(String name,int n) { return Activity.makeActivity() .actor(PersonObject.makePerson(name)) .id(String.format("urn:%s:%s",name,n)) .get(); } @Test public void testMapRed() throws Exception { Collection col = Collection.makeCollection() .item(getActivity("Joe",1)) .item(getActivity("Joe",2)) .item(getActivity("Mark",3)) .item(getActivity("Mark",4)) .item(getActivity("Sally",5)) .get(); // This is basically MapReduce contained within a Function, // Runs asynch using exiting executorservice... call to // ff.apply(gen).get() hides all the magic... // this particular function looks at the activity stream // and counts the number of activities per actor Iterable>> ret = ff.apply(col).get(); Pair> first = Iterables.get(ret,0); Pair> second = Iterables.get(ret,1); assertEquals(Integer.valueOf(2),first.first()); assertThat(first.second(),hasItems("Joe","Mark")); assertEquals(Integer.valueOf(1),second.first()); assertThat(second.second(),hasItems("Sally")); } static class MyMapper implements Mapper { public void map( Void key, Activity val, Collector context) { String ot = val.getActor().getDisplayName(); context.collect(ot!=null?ot:"", val.getActor()); } } ]]>
Running this code will result in output like (the value of ret is an iterable containing the following data): 2, [Joe, Mark] 1, [Sally]
There's quite a bit there... so let's break down exactly what's going on... First of all, the Abdera2 MapReduce implementation is integrated tightly with the Guava Libraries Function interface. Essentially, all of the core components of the MapReduce operations (e.g. the mapper, the reducer, combiners, etc) are all implemented as Function objects.
For instance, we define an initial Mapper function with a basic counting reducer using this bit of code:>, Iterable>>> f1 = compose( new MyMapper(), MapRed.countingReducer() ); ]]>
The MyMapper class is straightforward and should be recognizable to anyone familiar with MapReduce in general...
{ public void map( Void key, Activity val, Collector context) { String ot = val.getActor().getDisplayName(); context.collect(ot!=null?ot:"", val.getActor()); } } ]]>
If it's not clear what's going on in the mapper, we basically take an Activity as input, grab the displayName of the Actor object, and collect using the displayName as key and the actor object as the value. The counting reducer, then, goes through and counts the number of unique values we've collected. This particular implementation doesn't keep track of different actors who happen to share the same name, but that's not important for now. Note that the mapper and the counting reducer are wrapped inside a Guava Function object that takes an Iterable of org.apache.abdera2.common.misc.Pair<Void,Activity> objects and outputs an Iterable of org.apache.abdera2.common.misc.Pair<String,Iterable<Integer>> objects. The output is a mapping of each actor displayName to the number of activities in which that name appeared in the input collection. That gives us our counts, but doesn't quite give us the output we want.. so we need to define a bit more...
f2 = asFunction(MapRed.invertReducer(), Collections.reverseOrder()); ]]>
This code creates another function that will take as input the output of our initial mapper (f1) and perform an inversion mapping (swap the key and the value), then reverse the order using the natural order of the keys. Since the keys are integers, the highest numbers will appear first. So now we have two functions, f1 and f2. We could call these separately, but since the output of one becomes the output of the second, it's easier if we just go ahead and compose those into a single function...
>, Iterable>>> f3 = Functions.compose(f2,f1); ]]>
So now we have a function (f3) that takes as input a bunch of Activity objects and outputs a sorted listing of actor names ordered by number of occurrences. But we're still not quite done yet... note that the input of the function is an Iterable of Pair<Void,Activity> objects. That's kind of annoying really because what I really want to start off with is an Activity Stream. So let's create a function that converts an Activity Stream to the appropriate Iterable...
,Iterable>> transform = new Function,Iterable>>() { public Iterable> apply(Collection input) { return Pair.make() .index(MoreFunctions.alwaysVoid(), input.getItems()); } }; ]]>
I've marked in bold the important bits.. basically, the Pair object has a static method called index that takes a collection of items and an Key-generating Function object (which in this case always returns void) and generates an Iterable of Pair objects for each of the Activities in the Stream. Once we have our transform function, it's time to do a bit more composition...
, Future>>>> ff = Functions.compose( MoreFunctions.< Iterable>, Iterable>>>futureFunction(f3,exec), transform); ]]>
Note that here, we're creating another Function that takes as input an Activity Stream Collection object and outputs a java.util.concurrent.Future whose value, when set, will be our sorted listing of actors. The new function is composed of two individual functions: our transform created above, and the combined MapReduce function (f3) that we created previously. However, first, we wrap f3 within another special Abdera2 construct called a "futureFunction", which is essentially a Guava Function object that operates asynchronously using a java.util.concurrent.ExecutorService. Once all of this is defined and composed together (note that everything is stored in static, final, immutable thread-safe constants) and once we've built our Activity Stream, we can call our analysis operation using a single simple line of code (shown in bold below):
col = Collection.makeCollection() .item(getActivity("Joe",1)) .item(getActivity("Joe",2)) .item(getActivity("Mark",3)) .item(getActivity("Mark",4)) .item(getActivity("Sally",5)) .get(); Iterable>> ret = ff.apply(col).get(); ]]>
The call to ff.apply(col) returns our Future object. Calling get() on that blocks until the asynchronous operation is complete. Obviously this is just an example; there are a variety of ways we can use that Future object so that blocking the current thread isn't required (the Future returned is actually a Guava ListenableFuture).