Merge pull request #8 from jasnell/typeextmore
Javadocs and other minor tweaks
This commit is contained in:
commit
cb0a252911
|
@ -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.
|
|
@ -115,7 +115,7 @@ public abstract class CachingResolutionStrategy
|
|||
final TypeValue tv = Makers.type(t.id());
|
||||
cache.invalidate(tv);
|
||||
try {
|
||||
TypeValue tt = cache.get(tv, new Callable<TypeValue>() {
|
||||
cache.get(tv, new Callable<TypeValue>() {
|
||||
public TypeValue call() {
|
||||
return t;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@ package com.ibm.common.activitystreams.registry;
|
|||
import com.ibm.common.activitystreams.IO;
|
||||
import com.ibm.common.activitystreams.TypeValue;
|
||||
|
||||
/**
|
||||
* Preloads TypeValue instances.
|
||||
*/
|
||||
public interface PreloadStrategy {
|
||||
|
||||
void load(IO io, Receiver<TypeValue> receiver);
|
||||
|
|
|
@ -9,6 +9,11 @@ import com.ibm.common.activitystreams.TypeValue;
|
|||
*/
|
||||
public interface ResolutionStrategy {
|
||||
|
||||
/**
|
||||
* Returns the Receiver instance that is used to
|
||||
* process preloaded TypeValue instances
|
||||
* @return Receiver<TypeValue>
|
||||
*/
|
||||
Receiver<TypeValue> preloader();
|
||||
|
||||
/**
|
||||
|
@ -17,6 +22,9 @@ public interface ResolutionStrategy {
|
|||
*/
|
||||
Callable<TypeValue> resolverFor(TypeValue tv);
|
||||
|
||||
/**
|
||||
* Shutdown and cleanup any resources
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
public static final ResolutionStrategy nonop =
|
||||
|
|
|
@ -35,20 +35,41 @@ import com.ibm.common.activitystreams.ext.ExtModule;
|
|||
public final class TypeValueRegistry
|
||||
implements Function<TypeValue,Future<TypeValue>> {
|
||||
|
||||
/**
|
||||
* Return a new TypeValueRegistry.Builder
|
||||
* @return Builder
|
||||
*/
|
||||
public static Builder make () {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a default TypeValueRegistry instance
|
||||
* @return TypeValueRegistry
|
||||
*/
|
||||
public static TypeValueRegistry makeDefault() {
|
||||
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() {
|
||||
return make()
|
||||
.resolver(DefaultResolutionStrategy.make().silentfail().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) {
|
||||
return make()
|
||||
.io(io)
|
||||
|
@ -65,6 +86,12 @@ public final class TypeValueRegistry
|
|||
.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) {
|
||||
return make()
|
||||
.io(io)
|
||||
|
@ -96,25 +123,48 @@ public final class TypeValueRegistry
|
|||
DefaultResolutionStrategy.makeDefault();
|
||||
private IO io;
|
||||
|
||||
/**
|
||||
* Set the IO object used
|
||||
* @param io
|
||||
* @return Builder
|
||||
*/
|
||||
public Builder io(IO io) {
|
||||
this.io = io;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ExecutorService used
|
||||
* @param executor
|
||||
* @return Builder
|
||||
*/
|
||||
public Builder executor(ExecutorService executor) {
|
||||
this.executor = executor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PreloadStrategy to be used. By default the
|
||||
* ClasspathPreloader is used.
|
||||
* @param strategy
|
||||
* @return Builder
|
||||
*/
|
||||
public Builder preloader(PreloadStrategy strategy) {
|
||||
this.preloader = strategy != null ?
|
||||
strategy : ClasspathPreloader.instance;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ResolutionStrategy to use. By default, the
|
||||
* DefaultResolutionStrategy is used.
|
||||
* @param strategy
|
||||
* @return Builder
|
||||
*/
|
||||
public Builder resolver(ResolutionStrategy strategy) {
|
||||
this.strategy = strategy != null ?
|
||||
strategy : ResolutionStrategy.nonop;
|
||||
strategy :
|
||||
DefaultResolutionStrategy.makeDefault();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -194,12 +244,26 @@ public final class TypeValueRegistry
|
|||
return loadError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Block indefinitely until the preload process has completed
|
||||
* @throws InterruptedException
|
||||
* @throws ExecutionException
|
||||
*/
|
||||
public void waitForPreloader()
|
||||
throws InterruptedException,
|
||||
ExecutionException {
|
||||
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)
|
||||
throws InterruptedException,
|
||||
ExecutionException,
|
||||
|
@ -216,18 +280,42 @@ public final class TypeValueRegistry
|
|||
(ThreadPoolExecutor)newFixedThreadPool(1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given ID without waiting for the preloader to finish
|
||||
* @param id
|
||||
* @return Future<TypeValue>
|
||||
*/
|
||||
public Future<TypeValue>resolveNoWait(String id) {
|
||||
return resolveNoWait(Makers.type(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given ID. Will wait for the preload process to complete
|
||||
* before returning
|
||||
* @param id
|
||||
* @return Future<TypeValue>
|
||||
*/
|
||||
public Future<TypeValue>resolve(String 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<TypeValue>
|
||||
*/
|
||||
public Future<TypeValue>resolve(String id, long duration, TimeUnit unit) {
|
||||
return resolve(Makers.type(id),duration,unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given ID without waiting for the preload process to complete
|
||||
* @param tv
|
||||
* @return Future<TypeValue>
|
||||
*/
|
||||
public Future<TypeValue>resolveNoWait(TypeValue tv) {
|
||||
try {
|
||||
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<TypeValue>
|
||||
*/
|
||||
public Future<TypeValue> resolve(TypeValue tv) {
|
||||
try {
|
||||
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<TypeValue>
|
||||
*/
|
||||
public Future<TypeValue> resolve(
|
||||
TypeValue tv,
|
||||
long timeout,
|
||||
|
|
|
@ -1,17 +1,10 @@
|
|||
package com.ibm.common.activitystreams.ext.test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
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.Makers;
|
||||
import com.ibm.common.activitystreams.TypeValue;
|
||||
import com.ibm.common.activitystreams.ext.ExtModule;
|
||||
import com.ibm.common.activitystreams.registry.TypeValueRegistry;
|
||||
|
|
Loading…
Reference in New Issue