Merge pull request #8 from jasnell/typeextmore

Javadocs and other minor tweaks
This commit is contained in:
James M Snell 2014-04-25 13:26:23 -07:00
commit cb0a252911
6 changed files with 210 additions and 9 deletions

95
typext/README.md Normal file
View File

@ -0,0 +1,95 @@
# Type Value Extension Support
This module provides two basic pieces of functionality relating to the
use of extension Type Value definitions:
1. Verb and ObjectType objects
2. A TypeValueRegistry that is used to resolve information about extension type value identifiers
## The TypeValueRegistry
Suppose we have the following Activity object:
```json
{
"actor": "acct:john@example.org",
"verb": "http://example.org/verbs/like",
"object": "http://example.com/notes/1"
}
```
The verb "http://example.org/verbs/like" is an "extension verb". The first
time an application encounters an extension verb, it may not have any idea
what to do with it, or what exactly it means. The TypeValueRegistry is
intended to provide a partial solution to that problem by allowing an
application to resolve simple TypeValue identifiers into rich object
identifiers.
For example:
```java
TypeValueRegistry tvr =
TypeValueRegistry
.makeDefaultSilent(io);
TypeValue simple = Makers.type("http://example.org/verbs/like");
Future<TypeValue> object = tvr.resolve(simple);
ASObject obj = (ASObject)object.get();
```
By default, the TypeValueRegistry above does three things:
1. When created, the TypeValueRegistry checks the java classpath for Activity
Stream 2.0 Collection documents that contain TypeValue definitions. If
found, these are preloaded into an in-memory cache.
2. When asked to resolve a simple TypeValue, the TypeValueRegistry will first
check to see if a resolved object TypeValue already exists in memory. If
it finds one, it returns it.
3. If there currently is not a resolved object TypeValue in memory, the
TypeValueRegistry will attempt to do an HTTP fetch on the IRI given by
the TypeValue ID. If the ID is not an HTTP URL, this request will fail.
If the GET returns an Activity Streams 2.0 document, the document is
parsed and is examined for information about the given ID. If found,
this information is cached and a resolved object TypeValue is
returned.
In other words, let's say that the URL "http://example.org/verbs/like"
points to the following Activity Streams 2.0 collection document:
```json
{
"objectType": "collection",
"items": [
{
"objectType": "verb",
"id": "http://example.org/verbs/like",
"displayName": "like"
},
{
"objectType": "objectType",
"id": "http://example.org/objects/note",
"displayName": "note"
}
]
}
```
The TypeValueRegistry will, by default, find and cache both of the TypeValue
definitions included in the document. Then, it will return the object that
contains "id": "http://example.org/verbs/like".
Because I used the "makeDefaultSilent" method, if there are any errors
encountered throughout this process, the process will be aborted and the
original simple TypeValue will be returned.
### Customizing the TypeValueRegistry
The TypeValueRegistry implementation can be customized by specifying your
own ResolutionStrategy and PreloadStrategy implementations using the
TypeValueRegistry.Builder. You can also provide your own ExecutorService
implementation. Refer to the Javadocs for details.

View File

@ -115,7 +115,7 @@ public abstract class CachingResolutionStrategy
final TypeValue tv = Makers.type(t.id()); final TypeValue tv = Makers.type(t.id());
cache.invalidate(tv); cache.invalidate(tv);
try { try {
TypeValue tt = cache.get(tv, new Callable<TypeValue>() { cache.get(tv, new Callable<TypeValue>() {
public TypeValue call() { public TypeValue call() {
return t; return t;
} }

View File

@ -3,6 +3,9 @@ package com.ibm.common.activitystreams.registry;
import com.ibm.common.activitystreams.IO; import com.ibm.common.activitystreams.IO;
import com.ibm.common.activitystreams.TypeValue; import com.ibm.common.activitystreams.TypeValue;
/**
* Preloads TypeValue instances.
*/
public interface PreloadStrategy { public interface PreloadStrategy {
void load(IO io, Receiver<TypeValue> receiver); void load(IO io, Receiver<TypeValue> receiver);

View File

@ -9,6 +9,11 @@ import com.ibm.common.activitystreams.TypeValue;
*/ */
public interface ResolutionStrategy { public interface ResolutionStrategy {
/**
* Returns the Receiver instance that is used to
* process preloaded TypeValue instances
* @return Receiver&lt;TypeValue>
*/
Receiver<TypeValue> preloader(); Receiver<TypeValue> preloader();
/** /**
@ -17,6 +22,9 @@ public interface ResolutionStrategy {
*/ */
Callable<TypeValue> resolverFor(TypeValue tv); Callable<TypeValue> resolverFor(TypeValue tv);
/**
* Shutdown and cleanup any resources
*/
void shutdown(); void shutdown();
public static final ResolutionStrategy nonop = public static final ResolutionStrategy nonop =

View File

@ -35,20 +35,41 @@ import com.ibm.common.activitystreams.ext.ExtModule;
public final class TypeValueRegistry public final class TypeValueRegistry
implements Function<TypeValue,Future<TypeValue>> { implements Function<TypeValue,Future<TypeValue>> {
/**
* Return a new TypeValueRegistry.Builder
* @return Builder
*/
public static Builder make () { public static Builder make () {
return new Builder(); return new Builder();
} }
/**
* Create and return a default TypeValueRegistry instance
* @return TypeValueRegistry
*/
public static TypeValueRegistry makeDefault() { public static TypeValueRegistry makeDefault() {
return make().get(); return make().get();
} }
/**
* Create an return a default silent TypeValueRegistry instance.
* Errors encountered during the resolve process will be silenced,
* causing the process to abort and the original "simple" TypeValue
* to be returned
* @return TypeValueRegistry
*/
public static TypeValueRegistry makeDefaultSilent() { public static TypeValueRegistry makeDefaultSilent() {
return make() return make()
.resolver(DefaultResolutionStrategy.make().silentfail().get()) .resolver(DefaultResolutionStrategy.make().silentfail().get())
.get(); .get();
} }
/**
* Create a default TypeValueRegistry instance using the
* given Activity Streams IO object
* @param io
* @return TypeValueRegistry
*/
public static TypeValueRegistry makeDefault(final IO io) { public static TypeValueRegistry makeDefault(final IO io) {
return make() return make()
.io(io) .io(io)
@ -65,6 +86,12 @@ public final class TypeValueRegistry
.get(); .get();
} }
/**
* Create a default silent TypeValueRegistry instance using
* the given Activity Streams IO object
* @param io
* @return TypeValueRegistry
*/
public static TypeValueRegistry makeDefaultSilent(final IO io) { public static TypeValueRegistry makeDefaultSilent(final IO io) {
return make() return make()
.io(io) .io(io)
@ -96,25 +123,48 @@ public final class TypeValueRegistry
DefaultResolutionStrategy.makeDefault(); DefaultResolutionStrategy.makeDefault();
private IO io; private IO io;
/**
* Set the IO object used
* @param io
* @return Builder
*/
public Builder io(IO io) { public Builder io(IO io) {
this.io = io; this.io = io;
return this; return this;
} }
/**
* Set the ExecutorService used
* @param executor
* @return Builder
*/
public Builder executor(ExecutorService executor) { public Builder executor(ExecutorService executor) {
this.executor = executor; this.executor = executor;
return this; return this;
} }
/**
* Set the PreloadStrategy to be used. By default the
* ClasspathPreloader is used.
* @param strategy
* @return Builder
*/
public Builder preloader(PreloadStrategy strategy) { public Builder preloader(PreloadStrategy strategy) {
this.preloader = strategy != null ? this.preloader = strategy != null ?
strategy : ClasspathPreloader.instance; strategy : ClasspathPreloader.instance;
return this; return this;
} }
/**
* Set the ResolutionStrategy to use. By default, the
* DefaultResolutionStrategy is used.
* @param strategy
* @return Builder
*/
public Builder resolver(ResolutionStrategy strategy) { public Builder resolver(ResolutionStrategy strategy) {
this.strategy = strategy != null ? this.strategy = strategy != null ?
strategy : ResolutionStrategy.nonop; strategy :
DefaultResolutionStrategy.makeDefault();
return this; return this;
} }
@ -194,12 +244,26 @@ public final class TypeValueRegistry
return loadError; return loadError;
} }
/**
* Block indefinitely until the preload process has completed
* @throws InterruptedException
* @throws ExecutionException
*/
public void waitForPreloader() public void waitForPreloader()
throws InterruptedException, throws InterruptedException,
ExecutionException { ExecutionException {
loader.get(); loader.get();
} }
/**
* Block up to the given period of time waiting for the preload
* process to complete
* @param duration
* @param unit
* @throws InterruptedException
* @throws ExecutionException
* @throws TimeoutException
*/
public void waitForPreloader(long duration, TimeUnit unit) public void waitForPreloader(long duration, TimeUnit unit)
throws InterruptedException, throws InterruptedException,
ExecutionException, ExecutionException,
@ -216,18 +280,42 @@ public final class TypeValueRegistry
(ThreadPoolExecutor)newFixedThreadPool(1))); (ThreadPoolExecutor)newFixedThreadPool(1)));
} }
/**
* Resolve the given ID without waiting for the preloader to finish
* @param id
* @return Future&lt;TypeValue>
*/
public Future<TypeValue>resolveNoWait(String id) { public Future<TypeValue>resolveNoWait(String id) {
return resolveNoWait(Makers.type(id)); return resolveNoWait(Makers.type(id));
} }
/**
* Resolve the given ID. Will wait for the preload process to complete
* before returning
* @param id
* @return Future&lt;TypeValue>
*/
public Future<TypeValue>resolve(String id) { public Future<TypeValue>resolve(String id) {
return resolve(Makers.type(id)); return resolve(Makers.type(id));
} }
/**
* Resolve the given ID. Will wait the specified length of time for the
* preload process to complete before returning
* @param id
* @param duration
* @param unit
* @return Future&lt;TypeValue>
*/
public Future<TypeValue>resolve(String id, long duration, TimeUnit unit) { public Future<TypeValue>resolve(String id, long duration, TimeUnit unit) {
return resolve(Makers.type(id),duration,unit); return resolve(Makers.type(id),duration,unit);
} }
/**
* Resolve the given ID without waiting for the preload process to complete
* @param tv
* @return Future&lt;TypeValue>
*/
public Future<TypeValue>resolveNoWait(TypeValue tv) { public Future<TypeValue>resolveNoWait(TypeValue tv) {
try { try {
if (tv == null) return immediateCancelledFuture(); if (tv == null) return immediateCancelledFuture();
@ -239,6 +327,12 @@ public final class TypeValueRegistry
} }
} }
/**
* Resolve the given ID. Will block indefinitely until the preload process
* is complete
* @param tv
* @return Future&lt;TypeValue>
*/
public Future<TypeValue> resolve(TypeValue tv) { public Future<TypeValue> resolve(TypeValue tv) {
try { try {
if (tv == null) return immediateCancelledFuture(); if (tv == null) return immediateCancelledFuture();
@ -253,6 +347,14 @@ public final class TypeValueRegistry
} }
} }
/**
* Resolve the given ID. Will block for the given period of time until
* the preload process is complete
* @param tv
* @param timeout
* @param unit
* @return Future&lt;TypeValue>
*/
public Future<TypeValue> resolve( public Future<TypeValue> resolve(
TypeValue tv, TypeValue tv,
long timeout, long timeout,

View File

@ -1,17 +1,10 @@
package com.ibm.common.activitystreams.ext.test; package com.ibm.common.activitystreams.ext.test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import org.junit.Test; import org.junit.Test;
import static com.ibm.common.activitystreams.Makers.type;
import com.google.common.util.concurrent.Monitor;
import com.ibm.common.activitystreams.Collection;
import com.ibm.common.activitystreams.IO; import com.ibm.common.activitystreams.IO;
import com.ibm.common.activitystreams.Makers;
import com.ibm.common.activitystreams.TypeValue; import com.ibm.common.activitystreams.TypeValue;
import com.ibm.common.activitystreams.ext.ExtModule; import com.ibm.common.activitystreams.ext.ExtModule;
import com.ibm.common.activitystreams.registry.TypeValueRegistry; import com.ibm.common.activitystreams.registry.TypeValueRegistry;