You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@abdera.apache.org by jm...@apache.org on 2011/12/22 22:37:39 UTC

svn commit: r1222470 - in /abdera/abdera2: docs/Getting.Started/activities.xml test/src/main/java/org/apache/abdera2/test/activities/ActivitiesTest.java

Author: jmsnell
Date: Thu Dec 22 21:37:38 2011
New Revision: 1222470

URL: http://svn.apache.org/viewvc?rev=1222470&view=rev
Log:
Fixes and documentation

Modified:
    abdera/abdera2/docs/Getting.Started/activities.xml
    abdera/abdera2/test/src/main/java/org/apache/abdera2/test/activities/ActivitiesTest.java

Modified: abdera/abdera2/docs/Getting.Started/activities.xml
URL: http://svn.apache.org/viewvc/abdera/abdera2/docs/Getting.Started/activities.xml?rev=1222470&r1=1222469&r2=1222470&view=diff
==============================================================================
--- abdera/abdera2/docs/Getting.Started/activities.xml (original)
+++ abdera/abdera2/docs/Getting.Started/activities.xml Thu Dec 22 21:37:38 2011
@@ -513,23 +513,518 @@ cw.writeObject(
   </section>
     
   <section title="Custom Type Adapters and Property Mappings">
-    <t>TBD</t>
+    <t>Out of the box, the IO object is capable of working with a broad
+    range of simple and complex object types, including most of the 
+    types typically associated with Atom and Activity Streams implementations
+    (Joda-Time DateTime objects, Entity Tags, URI Templates, Collection
+    objects, Maps, etc). However, there are occasions when an application
+    has to use a custom class object.</t>
+    
+    <t>For instance, suppose we have the following class:</t>
+    
+    <figure><artwork>
+  public static class MyObject {
+    private final String val;
+    public MyObject(String v) {
+      this.val = v;
+    }
+    public String toString() {
+      return val;
+    }
+    public String getVal() {
+      return val;
+    }
+  }
+    </artwork></figure>
+    
+    <t>We want to be able to set an instance of MyObject as the value of 
+    an Activity Objects "foo" property, like so:</t>
+    
+    <figure><artwork>
+  PersonObject person = 
+    makePerson("Joe")
+      .set("foo", new MyObject("bar"));
+    </artwork></figure>
+    
+    <t>When serialized into JSON, we want this is appear as a regular 
+    String field:</t>
+    
+    <figure><artwork>
+  {
+    "objectType":"person",
+    "displayName":"Joe",
+    "foo":"bar"
+  }
+    </artwork></figure>
+    
+    <t>When parsing that JSON, however, we want the "foo" property to 
+    be interpreted as a MyObject instance so that calling object.getProperty("foo")
+    returns MyObject. To achieve that goal, we first need to create a custom 
+    type adapter:</t>
+    
+    <figure><artwork><![CDATA[
+  @AdaptedType(MyObject.class)
+  public static class MyObjectAdapter
+    extends SimpleAdapter<MyObject> {
+    protected MyObject deserialize(String v) {
+      return new MyObject(v);
+    }    
+  }  
+    ]]></artwork></figure>
+    
+    <t>The org.apache.abdera2.activities.io.gson.SimpleAdapter class is an
+    abstract base class that handles most of the difficult work for us. By
+    default, it uses the custom objects toString() method to serialize the 
+    object into a JSON string. If you need a more complex serialization, 
+    you will need to overload the serialize method.</t>
+    
+    <t>Note the use of the @AdaptedType annotation, this is required for
+    custom type adapters. It tells the IO class which type of object this
+    custom adapter is for.</t>
+    
+    <t>Once created, we need to register the type adapter with the IO object
+    and tell it to associate the "foo" property with MyObject instances:</t>
+    
+    <figure><artwork>
+  IO io = IO.make()
+    .adapter(new MyObjectAdapter())
+    .property("foo", MyObject.class)
+    .get();
+    </artwork></figure>
+    
+    <t>Note that we're creating a new instance of the IO object. Since IO
+    objects are immutable, custom type adapters and property assignments 
+    MUST be set up during the construction of the IO object. Once the IO
+    instance is created, we can proceed as usual:</t>
+    
+    <figure><artwork>
+  PersonObject person = 
+    makePerson("Joe")
+      .set("foo", new MyObject("bar"));
+  String str = io.write(base);
+  // now parse it to check..
+  person = io.readObject(new StringReader(str));
+  MyObject obj = person.getProperty("foo");
+  System.out.println(obj.getVal());
+    </artwork></figure>
+    
+    <t>Note that you need to take care when mapping property names to specific
+    kinds of objects because IO will attempt to treat all instances of that 
+    property name as the given object type. This will cause problems if you
+    use the same property name with different types of values within a single
+    document.</t>
+    
+    <t>By default, IO uses the following property mappings -- meaning that 
+    whenever fields with these names are encountered in a document, they 
+    will automatically be interpreted as the given type. You can override
+    the default interpretation by registering your own property mapping during
+    IO construction.</t>
+    
+    <texttable>
+    <ttcol align="left" width="15%">Name</ttcol>
+    <ttcol align="left">Type</ttcol>
+    <c>verb</c><c>org.apache.abdera2.activities.model.Verb</c>
+    <c>url",org.apache.abdera2.common.iri.IRI</c>
+    <c>fileUrl</c><c>org.apache.abdera2.common.iri.IRI</c>
+    <c>gadget</c><c>org.apache.abdera2.common.iri.IRI</c>
+    <c>updated</c><c>org.joda.time.DateTime</c>
+    <c>published</c><c>org.joda.time.DateTime</c>
+    <c>lang</c><c>org.apache.abdera2.common.lang.Lang</c>
+    <c>@language</c><c>org.apache.abdera2.common.lang.Lang</c>
+    <c>@base</c><c>org.apache.abdera2.common.iri.IRI</c>
+    <c>$ref</c><c>org.apache.abdera2.common.iri.IRI</c>
+    <c>icon</c><c>org.apache.abdera2.activities.model.MediaLink</c>
+    <c>image</c><c>org.apache.abdera2.activities.model.MediaLink</c>
+    <c>totalItems</c><c>Integer</c>
+    <c>duration</c><c>Integer</c>
+    <c>height</c><c>Integer</c>
+    <c>location</c><c>org.apache.abdera2.activities.model.objects.PlaceObject</c>
+    <c>reactions</c><c>org.apache.abdera2.activities.model.objects.TaskObject</c>
+    <c>mood</c><c>org.apache.abdera2.activities.model.objects.Mood</c>
+    <c>address</c><c>org.apache.abdera2.activities.model.objects.Address</c>
+    <c>stream</c><c>org.apache.abdera2.activities.model.MediaLink</c>
+    <c>fullImage</c><c>org.apache.abdera2.activities.model.MediaLink</c>
+    <c>endTime</c><c>org.joda.time.DateTime</c>
+    <c>startTime</c><c>org.joda.time.DateTime</c>
+    <c>mimeType</c><c>javax.activation.MimeType</c>
+    <c>rating</c><c>Double</c>
+    <c>position</c><c>org.apache.abdera2.common.geo.IsoPosition</c>
+    <c>etag</c><c>org.apache.abdera2.common.http.EntityTag</c>
+    <c>attending</c><c>org.apache.abdera2.activities.model.Collection</c>
+    <c>followers</c><c>org.apache.abdera2.activities.model.Collection</c>
+    <c>following</c><c>org.apache.abdera2.activities.model.Collection</c>
+    <c>friends</c><c>org.apache.abdera2.activities.model.Collection</c>
+    <c>friend-requests</c><c>org.apache.abdera2.activities.model.Collection</c>
+    <c>likes</c><c>org.apache.abdera2.activities.model.Collection</c>
+    <c>notAttending</c><c>org.apache.abdera2.activities.model.Collection</c>
+    <c>maybeAttending</c><c>org.apache.abdera2.activities.model.Collection</c>
+    <c>members</c><c>org.apache.abdera2.activities.model.Collection</c>
+    <c>replies</c><c>org.apache.abdera2.activities.model.Collection</c>
+    <c>reviews</c><c>org.apache.abdera2.activities.model.Collection</c>
+    <c>saves</c><c>org.apache.abdera2.activities.model.Collection</c>
+    <c>shares</c><c>org.apache.abdera2.activities.model.Collection</c>
+    </texttable>
+
+    <t>Note that property mapping applies even if the properties value is
+    an array, for instance, given our custom type mapping using the MyObject
+    class, "foo":["bar","baz"] would be interpreted as collection of MyObject
+    values:</t>
+    
+    <figure><artwork><![CDATA[
+  PersonObject person = io.readObject(new StringReader(str));
+  Iterable<MyObject> list = person.getProperty("foo");
+  for (MyObject obj : list) {...}
+    ]]></artwork></figure>
+    
   </section>
   
   <section title="Object Types">
-    <t>TBD</t>
+    <t>The Activity Streams model is extensible, allowing developers to 
+    describe any type of activity with any type of object. As part of the
+    standard, Activity Streams defines a handful of common basic object
+    types and provides the mechanism for creating more. Abdera2 supports
+    all of the core standard object types and introduces a number of its
+    own. Refer to the API Documentation for details on each of Abdera's 
+    provided object types.</t>
+    
+    <t>To support creation of new object types, Abdera2 gives you the 
+    choice of either using the dynamic org.apache.abdera2.activities.model.ASObject
+    API or the ability to extend the core objects to create a static extension.</t>
+    
+    <t>Creating a new object type using the dynamic API is simple:</t>
+    
+    <figure><artwork>
+  ASObject myObject = 
+    ASObject.makeObject()
+      .objectType("foo")
+      .displayName("My Foo Object")
+      .set("bar","baz")
+      .get();
+    </artwork></figure>
+    
+    <t>When serialized, this will look like:</t>
+    
+    <figure><artwork>
+  {
+    "objectType":"foo",
+    "displayName":"My Foo Object",
+    "bar":"baz"
+  }
+    </artwork></figure>
+    
+    <t>Creating a new static object type requires a few more steps but 
+    is pretty straightforward:</t>
+    
+    <figure><artwork><![CDATA[
+  public class FooObject
+    extends ASObject {
+  
+    public static <T extends FooObject>Builder makeFoo() {
+      return new Builder("foo");
+    }
+  
+    @Name("foo")
+    public static final class Builder extends ASObject.Builder<FooObject,Builder> {
+      public Builder() {
+        super(FooObject,Builder.class);
+      }
+      public Builder(String objectType) {
+        super(objectType,FooObject,Builder.class);
+      }
+      public Builder(Map<String,Object> map) {
+        super(map,FooObject,Builder.class);
+      }
+      public Builder bar(String val) {
+        set("bar","val");
+        return this;
+      }
+    }
+  
+    public FooObject(Map<String,Object> map) {
+      super(map,Builder.class,FooObject.class);
+    }
+  
+    public <X extends FooObject, M extends ASObject.Builder<X,M>>FooObject(Map<String,Object> map, Class<M> _class, Class<X>_obj) {
+      super(map,_class,_obj);
+    }
+    
+    public String getBar() {
+      return getProperty("bar");
+    }
+  }
+    ]]></artwork></figure>
+    
+    <t>Here, we're creating two objects: FooObject and FooObject.Builder. 
+    FooObject.Builder extends from the core ASObject.Builder and provides
+    all the necessary methods for constructing immutable instances of 
+    the FooObject class. FooObject extends from ASObject, inheriting all 
+    of the core Activity object fields and introducing a single extension
+    field called "bar".</t>
+    
+    <t>Once defined, we can use our custom object type:</t>
+    
+    <figure><artwork>
+  FooObject foo = 
+    FooObject.makeFoo()
+      .displayName("My Foo Object")
+      .bar("baz")
+      .get();
+    </artwork></figure>
+    
+    <t>The output is identical to that produced by the dynamic API:</t>
+    
+    <figure><artwork>
+  {
+    "objectType":"foo",
+    "displayName":"My Foo Object",
+    "bar":"baz"
+  }
+    </artwork></figure>
+    
+    <t>The final step is to register your custom object type with IO, in 
+    order to have IO automatically generate instances of your custom
+    object type when encountered within a document:</t>
+    
+    <figure><artwork>
+  IO io = IO.make()
+    .object(FooObject.Builder.class);
+    .get();
+    
+  FooObject foo = io.readObject(...);
+    </artwork></figure>
+    
   </section>
   
-  <section title="Audience Targeting">
-    <t>TBD</t>
+  <section title="Custom Verbs">
+    <t>Another key extension point within Activity Streams are the 
+    use of custom verbs. Within the standard, a collection of common
+    verbs are defined and supported by Abdera, along with a handful of
+    additional extension verbs. These include:</t>
+    
+    <t><list>
+ <t>add</t>
+ <t>cancel</t>
+ <t>checkin</t>
+ <t>delete</t>
+ <t>favorite</t>
+ <t>follow</t>
+ <t>give</t>
+ <t>ignore</t>
+ <t>invite</t>
+ <t>join</t>
+ <t>leave</t>
+ <t>like</t>
+ <t>make-friend</t>
+ <t>post</t>
+ <t>play</t>
+ <t>receive</t>
+ <t>remove</t>
+ <t>remove-friend</t>
+ <t>request-friend</t>
+ <t>rsvp-maybe</t>
+ <t>rsvp-no</t>
+ <t>rsvp-yes</t>
+ <t>save</t>
+ <t>share</t>
+ <t>stop-following</t>
+ <t>tag</t>
+ <t>unfavorite</t>
+ <t>unlike</t>
+ <t>unsave</t>
+ <t>update</t>
+ <t>comment</t>
+ <t>purchase</t>  
+ <t>consume</t>
+ <t>host</t>
+ <t>read</t>
+ <t>approve</t>
+ <t>reject</t>
+ <t>archive</t>
+ <t>install</t>
+ <t>close</t>
+ <t>open</t>
+ <t>resolve</t>
+    </list></t>
+    
+      <t>Each of these common verbs correlate to a constant on the 
+  org.apache.abdera2.activities.model.Verb object. Whenever possible,
+  applications should always use an existing verb. However, there are 
+  cases when a new verb must be created.</t>
+  
+  <t>There are a couple points to keep in mind when creating a new verb:
+  1) They are always a single token value, whitespace is not allowed and
+  2) They are always case-insensitive, "Post" is equivalent to "post".</t>
+  
+  <t>To create a new verb within Abdera2, simple call the Verb.get() method,
+  passing in the name of the verb:</t>
+  
+  <figure><artwork>
+  Activity activity = 
+    Activity.makeActivity()
+      .actor(PersonObject.makePerson("Me")))
+      .verb(<b>Verb.get("foo")</b>)
+      .object(NoteObject.makeNote("A Note"))
+      .get();
+  </artwork></figure>
   </section>
   
-  <section title="Converting Activities to and from Atom">
-    <t>TBD</t>
+  <section title="Replies and Responses">
+  
+  <t>The "Responses for Activity Streams" 
+  <eref target="http://activitystrea.ms/specs/json/replies/1.0/">specification</eref> 
+  defines an extensions to Activity Streams that support threaded conversations.
+  Support for the extension has been built into Abdera2.</t>
+  
+  <t>Any Activity Streams object may have an "inReplyTo" property, whose 
+  value is an array of one or more objects for which the containing object
+  is considered a response. The conceptual model is generally identical to
+  that defined by the <eref target="http://www.ietf.org/rfc/rfc4685.txt">Atom Threading Extensions</eref>.</t>
+  
+  <figure><artwork>
+    NoteObject note = NoteObject
+      .makeNote()
+      .id("urn:foo")
+      .content("This is a note")
+      .get();
+    NoteObject comment = NoteObject
+      .makeNote()
+      .id("urn:bar")
+      .content("This is a comment")
+      <b>.inReplyTo(note)</b>
+      .get();
+  </artwork></figure>
+  
+  <t>A common application use case is the ability to show the number of 
+  responses that are known for a given type of object. For that, the 
+  responses specification defines a number of common property names 
+  that are mapped to Activity Collection object values. These include:</t>
+  
+  <t><list>
+  <t>attending</t>
+  <t>followers</t>
+  <t>following</t>
+  <t>friends</t>
+  <t>friend-requests</t>
+  <t>likes</t>
+  <t>notAttending</t>
+  <t>maybeAttending</t>
+  <t>members</t>
+  <t>replies</t>
+  <t>reviews</t>
+  <t>saves</t>
+  <t>shares</t>
+  </list></t>
+  
+  <t>For example, if I have a note object that has received two comments,
+  has been shared by one person, and liked by five people, within the JSON
+  serialization it would look something like:</t>
+  
+  <figure><artwork>
+  {
+    "objectType":"note",
+    "content":"This is a note",
+    "replies":{
+      "totalItems":2,
+      "items":[
+        {"objectType":"note",
+         "content":"This is a comment"},
+        {"objectType":"note",
+         "content":"This is another comment"}
+      ]
+    },
+    "shares":{
+      "totalItems":1,
+      "items":[
+        {"objectType":"person",
+         "displayName":"Joe"}
+      ]
+    },
+    "likes":{
+      "totalItems":5
+    }
+  }
+  </artwork></figure>
+  
+  <t>Within the Abdera2 API, this would be:</t>
+  
+  <figure><artwork><![CDATA[
+  NoteObject note = io.readObject(...);
+  Collection<ASObject> replies = note.getProperty("replies");
+  Collection<ASObject> shares = note.getProperty("shares");
+  Collection<ASObject> likes = note.getProperty("likes");
+  
+  System.out.println(
+    String.format(
+      "%d Comments, %d Shares, %d Likes",
+      replies.getTotalItems(),
+      shares.getTotalItems(),
+      likes.getTotalItems());
+  ]]></artwork></figure>
+  
   </section>
   
-  <section title="Miscellaneous">
-    <t>TBD</t>
+  <section title="Audience Targeting">
+    <t>The "Audience Targeting for JSON Activities"
+    <eref target="http://activitystrea.ms/specs/json/targeting/1.0/">specification</eref>
+    defines a set of extension properties used to identify the target audience 
+    of the activity. Each of these properties ("to","cc","bto" and "bcc") are
+    defined as arrays of objects. For instance:</t>
+    
+    <figure><artwork>
+  {
+    "to":[{"objectType":"person","displayName":"Joe"},
+          {"objectType":"person","displayName":"Sally"}],
+    "bto":[{"alias":"@network"}]
+  }
+    </artwork></figure>
+    
+    <t>Abdera2 includes built in support for the Audience Targeting extension:</t>
+    
+    <figure><artwork>
+  Activity activity = 
+    Activity.makeActivity()
+      .actor(PersonObject.makePerson("James"))
+      .verb(Verb.POST)
+      .object(NoteObject.makeNote().content("Test").get())
+      .target(ServiceObject.makeService().id("urn:my:wall").get())
+      .to(PersonObject.makePerson("Joe"))
+      .to(PersonObject.makePerson("Sally"))
+      .bto(Objects.NETWORK)   
+      .get();
+    </artwork></figure>
+    
+    <t>Once created, you can use an extensive array of static methods from the
+    org.apache.abdera2.activities.extra.Extra class to determine the audience
+    of an activity, for instance:</t>
+    
+    <figure><artwork>
+    import static org.apache.abdera2.activities.model.objects.PersonObject.*;
+    import static org.apache.abdera2.activities.extra.Extra.*;
+    
+    isToMeOr(makePerson("Joe").get()).select(activity); // true
+    isTo(makePerson("Joe").get()).select(activity); // true
+    isCc(makePerson("Jane").get()).select(activity); // false
+    isBccMe().select(activity); // false
+    isBtoNetwork().select(activity); // true
+    isTo(makePerson("Joe").id("urn:foo").get()).select(activity); // false
+    </artwork></figure>
+    
+    <t>The audience testing methods are integrated with the Abdera2 Selector
+    framework making it possible to filter Activity Streams based on the 
+    audience. For instance, suppose you want to grab only the activities 
+    from a stream that are targeted directly to Joe (using the "to" property):</t>
+    
+    <figure><artwork><![CDATA[
+  import static org.apache.abdera2.activities.model.objects.PersonObject.*;
+  import static org.apache.abdera2.activities.extra.Extra.*;
+    
+  Collection<Activity> stream = io.readCollection(...);
+  Iterable<Activity> items = 
+    stream.getItems(
+      isTo(makePerson("Joe").get());
+  for (Activity activity : items) {...}
+    ]]></artwork></figure>
+    
   </section>
     
   </middle>

Modified: abdera/abdera2/test/src/main/java/org/apache/abdera2/test/activities/ActivitiesTest.java
URL: http://svn.apache.org/viewvc/abdera/abdera2/test/src/main/java/org/apache/abdera2/test/activities/ActivitiesTest.java?rev=1222470&r1=1222469&r2=1222470&view=diff
==============================================================================
--- abdera/abdera2/test/src/main/java/org/apache/abdera2/test/activities/ActivitiesTest.java (original)
+++ abdera/abdera2/test/src/main/java/org/apache/abdera2/test/activities/ActivitiesTest.java Thu Dec 22 21:37:38 2011
@@ -184,7 +184,6 @@ public class ActivitiesTest {
       assertNotNull(base);
       assertThat(base, hasItems("a","b","c","d","e","@base","@language"));
     } catch (Throwable t) {}
-    
   }
   
   @Test