Abdera2 - Common LibraryTBDDate 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!
}
TBDCreating 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=123Expanding 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).