2014-04-22

Now on Github: Activity Streams 2.0 Reference Implementation

I am quite happy to report that IBM gave me the green light to open source Activity Streams 2.0 Reference implementations for Java and JavaScript. The projects are out on Github and have been contributed as part of the OpenSocial Incubator:

Along with this, I have also pushed a minor update to the base Activity Streams 2.0 draft spec that includes some new advice on the creation of new extension verbs and objectTypes along with a few other minor bits and pieces. The updated draft can be found here: http://tools.ietf.org/html/draft-snell-activitystreams-07.

First, let's look at the Java reference implementation.

Java:

Because the code currently is considered a pre-release snapshot, we're not currently pushing artifacts to a maven repository, so to grab the code and make use of it, clone from git and do a local maven install:


git clone https://github.com/OpenSocial/activitystreams.git
cd activitystreams
mvn install

(To generate the Javadocs for the project, do an additional mvn javadoc:javadoc call. The javadocs for each module will be located in each modules target directory)

The maven project consists of a single core package and three extension modules. The mvn install command will build each of the modules and install the artifacts into your local maven repository. Once complete, you can use the core and extension modules by adding the following to your maven pom.xml files:


  <dependency>
    <groupId>com.ibm.common</groupId>
    <artifactId>activitystreams</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </dependency>

  <dependency>
    <groupId>com.ibm.common</groupId>
    <artifactId>activitystreams-actions</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </dependency>

  <dependency>
    <groupId>com.ibm.common</groupId>
    <artifactId>activitystreams-geo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </dependency>

  <dependency>
    <groupId>com.ibm.common</groupId>
    <artifactId>activitystreams-legacy</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </dependency>

(note: the package names on the java implementation will be changing to org.opensocial.activitystreams.* sometime before the 1.0 release occurs)

The core module depends on three other common open source projects:

If you're not a maven user, you'll need to make sure each of these are in your classpath to make use of the reference implementation.

Once you've got the dependencies worked out, you can use the reference implementation to easily produce and consume Activity Streams 2.0 data structures:


import static com.ibm.common.activitystreams.Makers.activity;
import static com.ibm.common.activitystreams.Makers.object;

public class Example {

  public static void main(String... args) {

    Activity activity = 
     activity()
       .actor(object("person").id("acct:joe@example.org"))
       .object(object("note").content("my note"))
       .verb("post")
       .get();

    activity.writeTo(System.out);

  }

}

The reference implementation uses a fluent generator API pattern throughout. Builder's are used to construct instances of specific object types. Those instances, once created, are immutable. The Builder APIs are designed to allow methods to be chained.

In the example above, the com.ibm.common.activitystreams.Makers.activity creates and returns a new instance of the com.ibm.common.activitystreams.Activity.Builder class. Calling the actor, object and verb methods on that builder set the corresponding properties on the underlying JSON element. The get method returns the constructed, immutable Activity object.

Looking at the call to actor above, the actor property is being set as an object with objectType "person" and an id of "acct:joe@example.org". The call to object("person") creates and returns a com.ibm.common.activitystreams.ASObject.Builder instance with the objectType property pre-set.

The call to activity.writeTo(System.out) serializes the Activity object out to stdout using the default options.

While this API is a significant departure from the traditional Java POJO getter/setter pattern, it allows developers to write clear, concise code that naturally follows the Activity Streams 2.0 data model.

The com.ibm.common.activitystreams.IO object is used to read Activity Stream objects from raw JSON. For instance:



  private final static IO io = IO.makeDefault();
  //...

  InputStream in = ...;

  Activity activity = io.readAsActivity(in);

  TypeValue tv = activity.verb();
  System.out.println(tv.id()); // "post"

  ASObject actor = activity.firstActor();
  System.out.println(actor.id()); // "acct:joe@example.org"

  for (ASObject object : activity.object())
    System.out.println(object.objectType().id()) // "note"

  // iterate all properties
  for (String key : activity)
    System.out.println(activity.get(key));

The IO object is the interface primarily responsible for serializing and deserializing the JSON Activity Stream objects. Instances of the IO object are immutable and threadsafe. Typically, you'll want to just create one instance and set it as a static constant. Once created, you can use of several read* methods to parse an InputStream, Reader or raw input String.

Again, the API exposed by the reference implementation is not a traditional getter/setter pattern. The objects are immutable and instead of calling a method like "getVerb()" to get the verb (for instance), you'd just call "verb()". Refer to the Javadocs for complete details on all of the various methods on the objects.

You can find a few more examples on how to use the core API here.

(One quick sidenote... even though the objects do not follow the traditional Java POJO model and are immutable, they do implement java.io.Serializable.)

The core project includes complete support for the primary Activity Streams 2.0 vocabulary. It is possible to extend the capabilities of the core project through the use of modules. The reference implementation comes with three such modules out of the box:

  • Activity Streams 2.0 Actions (see http://tools.ietf.org/html/draft-snell-activitystreams-actions-03),
  • GeoJSON (see http://geojson.org/), and
  • Legacy objectTypes (see https://github.com/activitystreams/activity-schema/blob/master/activity-schema.md)

The Actions module provides currently experimental support for the data structures defined by the draft Activity Streams 2.0 Action Handlers specification. Action Handlers are something that I've discussed before on this blog so I won't go into too much detail here just yet. Take a look here for a detailed example of how the Actions module is used to produce and consume Action Handler markup.

The GeoJSON module provides an implementation of the GeoJSON specification and adds the ability to include GeoJSON objects in an Activity Streams 2.0 document.

The Legacy module provides an implementation of many of the older Activity Streams 1.0 core objectTypes defined in the original "Activity Streams Base Schema".

Refer to the documentation for details on how to enable and use each of the modules.

JavaScript:

The JavaScript reference implementation provides a thin wrapper around ordinary JavaScript objects that allows easy and consistent handling as "Activity Stream Objects". (Note that, currently, the implementation makes use of several newer features of the JavaScript language that may not be implemented in older Web browsers). You can use the JavaScript library both on the browser and within Node (the node module is not yet registered with npm but that will be coming very soon).

You'll need the Yeoman toolchain to build:


git co https://github.com/OpenSocial/activitystreams.js.git
cd activitystreams.js
grunt

Once built, you simply drop the javascript library into your app and use:

HTML:


<html>
<head>
  <script src="js/activitystreams.min.js"></script>
  <script>
    var activity = asms.Activity({
      verb: 'post',
      actor: {
        displayName: 'Joe',
        id: 'acct:joe@example.org'
      },
      object: {
        objectType: 'note',
        content: 'This is a short note'
      },
      updated: new Date()
    });

    console.log(activity.verb.id);
    console.log(activity.actor.displayName);
    console.log(activity.object.objectType.id);
    console.log(activity.object.content['*']);

    console.log(activity.toString()); // output json
  </script>
</head>
</html>

Node.js:


var asms = require('activitystreams');
var activity = asms.Activity({
  verb: 'post',
  actor: {
    displayName: 'Joe',
    id: 'acct:joe@example.org'
  },
  object: {
    objectType: 'note',
    content: 'This is a short note'
  },
  updated: new Date()
});

console.log(activity.verb.id);
console.log(activity.actor.displayName);
console.log(activity.object.objectType.id);
console.log(activity.object.content['*']);

console.log(activity.toString()); // output json

In each of the two examples above, we create an ordinary JavaScript object and pass it as an argument to the asms.Activity() method. This method wraps the JavaScript object within an wrapper that enforces the Activity Streams data format.

Refer to the documentation here for more details. Also, the grunt build will generate jsdocs for the library.

Contributions Welcome...

The Java and JavaScript reference implementations have been contributed by IBM to the OpenSocial Incubator. To contribute, refer to the "CONTRIBUTING" document in each of the projects. It's fairly painless... there's a Contributor License Agreement to sign, then fork the repo, make your changes and submit pull requests. Issues can be tracked here and here.

I will be looking to get the first official 1.0 release ready by the end of May 2014.

2014-03-10

Verbs and Nouns in Activity Streams

While the Activity Streams data format is generally intended to provide machine readable descriptions of past, present or future events, those of use who use the format often have the need to translate those machine readable statements into human readable ones. For example, the following:


{
  "actor": {
    "displayName": "Joe"
  },
  "verb": "give",
  "object": {
    "objectType": "book",
    "id": "http://example.org/books/123",
    "displayName": "Moby Dick"
  },
  "target": {
    "displayName": "Sally"
  }
}

Can be translated into the English sentence, "Joe gave Sally the book 'Moby Dick'"; or, more generally, "Joe gave Sally a book". As we all know, there are several alternative forms that all express the same idea (some clearly less desirable than others): "Joe gave a book to Sally", "The book, 'Moby Dick' was given to Sally by Joe", "Sally received a book from Joe", and so on.

Exactly how an Activity Statement is translated into English depends entirely on the characteristics of the statement, the object types used and the verb. The Activity Streams vocabulary and structure has been intentionally left wide open to allow for a great deal of flexibility in how it is used. Unfortunately, this flexibility causes a great deal of ambiguity and difficulty when summarizing statements using natural language. This difficulty is increased significantly when we take extension verbs and objectTypes into consideration. To help deal with these ambiguities, I have been kicking around the notion of introducing formal linguistic characteristics to the "verb" and "objectType" definitions in Activity Streams 2.0.

If you look at the current AS 2.0 specification draft, you'll see that the "verb" and "objectType" properties have been reimagined as "Type Values". A "Type Value" is represented as either an implicit or explicit object. Implicit object type values are represented as Strings that contain either a simple token (e.g. "post") or an absolute IRI (e.g. "http://example.org/verbs/post"). Explicit object types values are represented directly as JSON objects.

For example:


  {
    ...,
    "verb": "post",
    ...
  }

Here, the verb "post" is semantically equivalent* to:


  {
    "verb": {
      "id": "post"
    }
  }

(* there is a caveat here in that simple token verbs such as "post" MUST always be represented in String form... so while "verb":"post" is equivalent to "verb":{"id":"post"}, only the former can be used within an Activity Streams 2.0 document. This is because the "id" property value is always required to be an absolute IRI.)

What this essentially means is that all verbs (and objectType's) are first class objects within Activity Streams and can have their own properties and characteristics. For example, a verb can have a "displayName":


  {
    "verb": {
      "id": "http://example.org/verbs/foo",
      "displayName": "Foo"
    }
  }

A Type Value's characteristics can help an implementation figure out what exactly the type value means and how to use it. This, for instance, comes in handy when we want to associate an extension objectType with an existing semantic model:


  {
    "objectType": "objectType",
    "id": "http://example.org/foo",
    "@type": "http://schema.org/Person"
  }

In this case, we are using the Activity Streams 2.0 JSON-LD alignment (the "@type" property) to indicate that extension objectType "http://example.org/foo" is a realization of the Schema.org Person type.

The "Linguistic Characteristics" of a verb and objectType are the collection of properties that describe how those objects are used within a natural language. For verbs, these characteristics include things like valency, conjugation regularity, tense, aspect and modality. For objectTypes, they include concepts such as countability, gender, collectiveness and animation. By understanding these characteristics, we can better interpret (and express) exactly what information is conveyed within an Activity Statement.

Let's start with verbs.

While the specific form and conjugation of verbs vary greatly from one language to the next, the type and function of verbs is generally universal. There are, for instance, Intransitive Verbs (verbs that only require a subject, such as "I walk", "He is walking", "You walked", etc), Transitive Verbs (those that have both a subject and a direct object, such as "Joe read the newspaper", and Ditransitive Verbs (those that have a subject, direct object and indirect object, such as "Joe gave the money to Sally"). There are also Linking Verbs ("Joe became worried") and Existential verbs ("Joe is alive", "Mike was here", etc).

Within the Activity Streams format, we are primarily concerned with Intransitive, Transitive, Ditransitive and Existential verb forms that may or may not be expressed in terms of tense or aspect. In other words, using Activity Streams 2.0, we can express concepts such as "Joe lives", "Joe is running", "Joe wrote a letter", and "Joe gave a book to Sally" but generally not statements such as "Joe became worried". (Note that, in very rare cases, it is possible for an Activity Statement to convey avalent verbs, that is verbs with no actor, object or target. I'll discuss these cases later.)

In order make it easier to comprehend the use of verbs within an Activity Stream document, I am exploring the ability to explicitly mark the linguistic characteristics of verbs within the Type Value definition. For instance:


  {
    "objectType": "verb",
    "id": "http://example.org/verbs/sneeze",
    "displayName": "sneeze", 
    "linguistic": {
      "en": {
        "infinitive": "sneeze",
        "category": "semelfactive",
        "flags": ["intransitive"]
      }
    }
  }

Here, the verb identified as "http://example.org/verbs/sneeze" is assigned the infinitive English-language root "sneeze". It is defined as a semelfactive verb with the intransitive characteristic. This essentially means that Activity Statements using this verb will not contain an "object" property, and that the "startTime"/"endTime" properties (if present) will be the same (the event was instantaneous).


  {
    "actor": {
      "displayName": "Joe"
    },
    "verb": "http://example.org/verbs/sneeze",
    "startTime": "2014-12-12T12:12:12Z",
    "endTime": "2014-12-12T12:12:12Z"
  }

It is possible for intransitive verbs to have an indirect object (the "target" of the action). For instance:


  {
    "objectType": "verb",
    "id": "http://example.org/verbs/sit",
    "displayName": "sit",
    "linguistic": {
      "en": {
        "infinitive": "sit",
        "category": "activity",
        "flags": ["intransitive"]
      }
    }
  }

  {
    "actor": {
      "displayName": "Joe"
    },
    "verb": "http://example.org/verbs/sit", 
    "target": {
      "objectType": "chair"
    },
    "startTime": "2014-01-01T12:00:00Z"
  }

Note that in this example, there is a startTime but no endTime. Because the "sit" verb is defined as belonging to the "activity" linguistic category, it is assumed to be durative (occurring over time) without having a natural end point. In other words, the above statement indicates that Joe is currently sitting on a chair (the target).

In most cases, verbs used in Activity Statements will be "transitive" (actor+object) and/or "ditransitive" (actor+object+target).


  {
    "objectType": "verb",
    "id": "http://example.org/verbs/approve",
    "displayName": "approve",
    "linguistic": {
      "en": {
        "infinitive": "approve",
        "category": "semelfactive",
        "flags": ["transitive"]
      }
    }
  }

  {
    "objectType": "verb",
    "id": "http://example.org/verbs/upload",
    "displayName": "upload",
    "linguistic": {
      "en": {
        "infinitive": "upload",
        "category": "accomplishment",
        "flags": ["transitive","ditransitive"]
      }
    }
  }

"Joe approved the proposal":

  {
    "actor": {
      "displayName": "Joe"
    },
    "verb": "http://example.org/verbs/approve",
    "object": {
      "objectType": "proposal"
    } 
  }

"Joe uploaded the proposal to the server":

  {
    "actor": {
      "displayName": "Joe"
    },
    "verb": "http://example.org/verbs/upload",
    "object": {
      "objectType": "proposal"
    },
    "target": {
      "objectType": "server"
    }
  }

While it is perfectly legal to use these verbs in an intransitive sense ("The file uploaded", "The proposal was approved"), doing so using the Activity Streams format is generally not going to be all that useful.

Note that the "linguistic" property examples below are all scoped to the "en" language tag (English). This allows us to specify distinct handling of verbs depending on specific language context -- that is, while a specific verb infinitive might use regular conjugation in one language, it may be irregular in another. If you end up needing to define some characteristics globally independent of language, but still define language specific characteristics, the special "*" language can be used:


  {
    "objectType": "verb",
    "id": "http://example.org/verbs/bake",
    "displayName": "bake",
    "linguistic": {
      "*": {
        "category": "accomplishment",
        "flags": ["transitive"]
      },
      "en": {
        "infinitive": "bake"
      },
      "de": {
        "infinitive": "backen",
        "present": "backt",
        "perfect": "gebacken",
        "passive": "gebacken",
        "imperfect": "backte",
        "flags": ["irregular"]
      }
    }
  }

In this case, when dealing with an English language context, we can determine that the verb "http://example.org/verbs/bake" is a regularly conjugated, transitive accomplishment (i.e. "baked", "baking", "have baked", "will bake", etc). However, when dealing with a German language context, the verb is handled as irregular with specific conjugations given.

Within the linguistic property, the possible values for "category" are:

  • activity
  • accomplishment
  • state
  • achievement
  • semelfactive

These correlate to the generally accepted Lexical Aspects of all verbs (that is, they describe how the verb relates to time).

The "flags" property value is an array containing one or more of:

  • intransitive
  • transitive
  • ditransitive
  • irregular
  • idiomatic

The flags are cumulative. For instance, a verb marked as being "intransitive" and "transitive" can be used with or without an "object" property in the Activity statement.

The "irregular" flag indicates that the verb forms are "irregular" in respect to default language-specific conjugation rules. Depending on the language context, additional properties may be specified that detail the specific conjugations.

The "idiomatic" flag indicates that the verb form is idiomatic. A common example of this is the verb "flag-as-inappropriate". Such verb forms do not have specific infinitives in the traditional sense, but are rather expressed in terms of a "verb phrase", i.e. "Joe flagged the post as inappropriate".

The "infinitive" property is used express the base form of the verb in any given language. This is also referred to as the "dictionary form". Verbs marked as being "idiomatic" will not typically include the "infinitive" property.

Verbs are only part of the equation, however. The nouns (objectTypes) are equally as important. For those, I propose an equivalent linguistic definition that capture characteristic such as countability, rationality, collectiveness and gender:


  {
    "objectType": "objectType",
    "id": "http://example.org/nouns/woman",
    "displayName": "woman",
    "@type": "http://schema.org/Person",
    "linguistic": {
      "*": {
        "flags": ["feminine","countable","rational"]
      },
      "en": {
        "root": "woman",
        "collective": "women",
        "flags": ["irregular"]
      }
    }
  }

  {
    "objectType": "objectType",
    "id": "http://example.org/nouns/man",
    "displayName": "man",
    "@type": "http://schema.org/Person",
    "linguistic": {
      "*": {
        "flags": ["masculine","countable","rational"]
      }, 
      "en": {
        "root": "man",
        "collective": "men",
        "flags": ["irregular"]
      }
    }
  }

To a native English speaker, the notion of gender specific treatment of nouns and verbs is a bit odd, as there are very few gender specific nouns in modern use (pronouns are a different story, of course). But in many languages, Russian for instance, nouns can be assigned specific gender qualities. The gender of a noun changes the structure of the natural language sentence that uses the noun. So if I have the existential Activity Statement:


  {
    "actor": {"displayName": "Mary"},
    "verb": "is",
    "object": {
      "objectType": "leader"
    }
  }

Absent any gender specific clues, I'd likely end up with a grammatically correct but semantically incorrect Russian translation: "Мария был лидером". If, however, we introduce the ability to specify gender clues, we can derive a proper feminine form translation: "Мария была лидером"


  {
    "actor": {
      "displayName": "Mary",
      "objectType": "http://example.org/noun/woman"
    },
    "verb": "is",
    "object": {
      "objectType": "leader"
    }
  }

The "flags" for objectTypes currently include:

  • irregular - nouns that are irregular in respect to language default plurality
  • feminimne - feminine gender
  • masculine - masculine gender
  • neutral - neutral gender
  • countable - nouns that have a plural form
  • uncountable - nouns that do not have a natural plural form
  • collective - nouns that have a collective aspect
  • rational - nouns that represent rational (sentient, animated, etc) constructs
  • abstract - nouns that represent abstract concepts
  • concrete - nouns that represent concrete concepts

As with verbs, the flags are cumulative. A noun that specifies generally mutually exclusive flags (e.g. "countable" and "uncountable") indicates that the noun can be used in both ways.

The "root" property is used to specify the root dictionary form of the noun. For irregular nouns, the "collective" property defines the specific plural form.

Now, there's a natural question here: if I'm using extension verbs, am I supposed to include this linguistic detail *EVERY* time I use the verb or objectType? The answer, of course, is: You can, but you don't have to.

By convention, one approach is to ensure that all of your extension verb and objectType identifiers use HTTP dereferencable URIs. For instance:


  {
    "actor": {
      "objectType": "http://example.org/nouns#woman",
      "displayName": "Sally"
    }
  }

If I've never encountered the "http://example.org/nouns/woman" objectType before, I have several options: 1) I can fallback to some default behavior specific to my implementation, 2) I can ignore it or 3) I could send an HTTP GET request to "http://example.org/nouns/woman" in an attempt to retrieve some information about that objectType. Something like:


GET /nouns HTTP/1.1
Host: example.org

HTTP/1.1 200 OK
Content-Type: application/activity+json

{
  "items": [
  {
    "objectType": "objectType",
    "id": "http://example.org/nouns#woman",
    "displayName": "woman",
    "@type": "http://schema.org/Person",
    "linguistic": {
      "*": {
        "flags": ["feminine","countable","rational"]
      },
      "en": {
        "root": "woman",
        "collective": "women",
        "flags": ["irregular"]
      }
    }
  },
  {
    "objectType": "objectType",
    "id": "http://example.org/nouns#man",
    "displayName": "man",
    "@type": "http://schema.org/Person",
    "linguistic": {
      "*": {
        "flags": ["masculine","countable","rational"]
      }, 
      "en": {
        "root": "man",
        "collective": "men",
        "flags": ["irregular"]
      }
    }
  } 
  ]
}

This approach allows implementers who are publishing new extension verb and objectType values to define those as reusable libraries that are incrementally discoverable by consuming applications without inflating the verboseness of the actual Activity Statements being passed around. The discovery step can happen asynchronously to the actual processing of the statement. And the result returned is durable and cacheable.

For now, that is all. Keep in mind that this is all just something that I've been kicking around. It's not yet a part of the core Activity Streams spec (I'll probably write it up as an extension draft). Any comments/feedback is welcomed and appreciated. There is a lot of work that would need to go into solidifying this idea.

2014-01-24

JSON id references

The JSON Pointer spec provides a great bit of functionality for referencing into the structure of a JSON document. For instance, if I have a bit of JSON like {"a":{"b":{"c":1}}}, I can create a JSON Pointer like "/a/b/c" to reference the value of "c". That's really a critical piece of functionality when referencing into a JSON document structure.

However, there is one important bit of functionality that JSON Pointer is missing, however: referencing JSON objects by ID the way we can in HTML and XML. The challenge, of course, is that JSON documents do not have a standardized "ID" attribute the way HTML and XML have. So let's define one.

Suppose we have the following JSON document at http://example.org/foo.json


{
  "items": [
    {
      "id": "a",
      "title": "foo",
      "child": {
        "id": "b",
        "title": "bar"
      }
    },
    {
      "id": "c",
      "title": "baz"
    }
  ]
}

The URL http://example.org/foo.json#$b denotes a reference to the object that contains "id": "b", regardless of where it appears within the overall document. To keep things simple, there are no parent/child/ancestor types of relationships. If there happens to be more than one object with "id": "b" in the document, the reference will be to all matching objects. Obviously, this reference syntax only allows referencing JSON objects.

The same approach works if the value of "id" is a number rather than a string:


{
  "items": [
    {
      "id": 1,
      "title": "foo",
      "child": {
        "id": 2,
        "title": "bar"
      }
    },
    {
      "id": 3,
      "title": "baz"
    }
  ]
}

The URL http://example.org/foo.json#$2 still just works.

But what if the value of "id" is not a JSON string or number? Well, then we just ignore it and assume we can't reference it.

But what if we're using a JSON variant like JSON-LD that uses it's own variation on "id" (JSON-LD uses "@id"). Well, in such cases, we can go ahead and assert that "id" and "@id" are equivalent as far as resolution is concerned. So if I happen to have:


{
  "items": [
    {
      "@id": "a",
      "title": "foo",
      "child": {
        "@id": "b",
        "title": "bar"
      }
    },
    {
      "@id": "c",
      "title": "baz"
    }
  ]
}

Then http://example.org/foo.json#$b still Just Works as expected.

Ok so that looks fine, but hold on a sec: In many JSON vocabularies that use "id" and "@id", the value of the identifier is a fully qualified URI in it's own right. It would suck if we always had to drop a full URL in as the value of a document fragment identifier. So for these cases, an alternative "anchor" attribute could be adopted. For instance:


{
  "items": [
    {
      "id": "http://example.org/Item/1A2B3C",
      "anchor": "a",
      "title": "foo",
      "child": {
        "id": "http://example.org/Item/123ABC",
        "anchor": "b",
        "title": "bar"
      }
    },
    {
      "id": "http://example.org/Item/ABC123",
      "anchor": "c",
      "title": "baz"
    }
  ]
}

To reference an object by it's anchor value, we'd use a slightly different syntax: "http://example.org/foo.json#@b". In the example above this reference is equivalent to "http://example.org/foo.json#$http://example.org/Item/123ABC". For consistency with JSON-LD, we could say that "anchor" and "@anchor" are equivalent.

Ok, so let's look at a practical example of this in action. Let's suppose I've got an Activity Stream that uses a new extension verb and object type. I want to make it possible to discover new and interesting things about the new verb and objectType. First, I would publish a JSON document that contains my verb and objectType definitions. Let's put this at http://example.org/types:


{
  "title": "My Activity Extensions",
  "items": [
    {
      "objectType": "verb",
      "id": "http://example.org/types#@plusone",
      "anchor": "plusone",
      "displayName": "+1",
      "alias": "like" 
    },
    {
      "objectType": "objectType",
      "id": "http://example.org/types#@article",
      "anchor": "article",
      "displayName": "An Article!"
    }
  ]
}

Then in my Activity statement, I could do:


{
  "verb": "http://example.org/types#@plusone",
  "actor": "acct:joe@example.com",
  "object": {
    "objectType": "http://example.org/types#@article",
    "displayName": "Thoughts",
    "id": "http://example.org/articles/1"
  }
}

When processing this Activity Statement, if the processor has never encountered the "http://example.org/types#@plusone" verb and "http://example.org/types#@article" objectType before, it could deference the URI, resolve the anchor and find out anything it needs to know from the reference.

Any thoughts?

2013-12-16

Activity Streams 2.0 Action Handlers

For review: Activity Streams 2.0 Action Handlers.

Co-authored with my friend and former IBM colleague Matthew Marum (now with SugarCRM), this draft introduces a number of new Activity Streams 2.0 "Action Handler" object types for use with the new Actions mechanism included in Activity Streams 2.0. These are essentially an evolution of several concepts including OpenSocial Embedded Experiences, Google's "Actions in the Inbox", the recent Schema.org "Actions" proposal, WebIntents, etc.

The best way to describe how this works is just to jump right into an example:


     {
       "objectType": "video",
       "displayName": "A Movie!",
       ...,
       "actions": {
         "watch": [
           {
             "objectType": "EmbedActionHandler",
             "displayName": "HD",
             "mediaType": "video/mpeg",
             "url": "http://cdn.example.org?id=123amp;fmt=HD",
           },
           {
             "objectType": "EmbedActionHandler",
             "displayName": "SD",
             "mediaType": "video/mpeg",
             "url": "http://cdn.example.org?id=123&fmt=SD",
           },
           {
             "objectType": "application",
             "displayName": "Watch on Netflix",
             "url": "http://netflix.com..."
           }
         ],
         "like": {
           "objectType": "EmbedActionHandler",
           "mediaType": "text/html",
           "url": "http://www.facebook.com/plugins/like.php...",
           "style": {
             "width": "150px",
             "height": "50px"
           }
         },
         "share": [
           {
             "objectType": "HttpActionHandler",
             "displayName": "Twitter",
             "url": "https://twitter.com/share?url=...",
             "target": "DIALOG"
           },
           {
             "objectType": "HttpActionHandler",
             "displayName": "Facebook",
             "url": "https://www.facebook.com/sharer/sharer.php?u=...",
             "target": "DIALOG"
           }
         ],
         "save": [
           {
             "objectType": "service",
             "id": "http://getpocket.com",
             "displayName": "Pocket",
             "context": {
               "url": "http://example.org/movie?id=123",
               "title": "A Movie!",
               "tags": "foo, bar, baz"
             }
           },
           {
             "objectType": "service",
             "id": "http://instapaper.com",
             "displayName": "Instapaper",
             "context": {
               "url": "http://example.org/movie?id=123",
               "title": "A Movie!",
               "selection": "An action movie!"
             }
           }
         ],
         "review": {
           "objectType": "HttpActionHandler",
           "displayName": "Rate this movie!",
           "url": "http://review.example.org/movie?id=123",
           "method": "POST",
           "expects": {
             "objectType": "HtmlForm",
             "mediaType": "application/x-www-form-urlencoded",
             "parameters": {
               "rating": {
                 "id": "http://schema.org/ratingValue",
                 "bestRating": 5,
                 "worstRating": 0,
                 "displayName": "Rating",
                 "required": true
               },
               "comments": {
                 "id": "http://schema.org/commentText",
                 "displayName": "Comments",
                 "required": "false"
               }
             }
           }
         }
       }
     }

Here, we have a simple "video" object with five specified "actions": "watch", "like", "share", "save" and "review". Each action is associated with one or more "action handlers". The handlers that have "objectType": "HttpActionHandler" describe HTTP request/response flows; the handlers that have "objectType": "EmbedActionHandler" describe embeddable content (e.g. embedding a video, or an OpenSocial gadget, etc). The handlers that have "objectType": "service" reference third party specific services (in this case Pocket and Instapaper, both of which have specific REST APIs that require customer keys, etc).

This is definitely still a work in progress that is far from being complete. As such, feedback and comments are definitely very welcome. Please direct any input you may have to the Activity Streams mailing list.

2013-11-06

Update to LINK/UNLINK Internet-Draft

I posted a fairly significant update to the LINK/UNLINK draft based on feedback received from Julian Reschke. There are several important changes.

One, in previous versions, link uniqueness was based on the tuple (context IRI, link relation, target IRI). In the current version, this is expanded to include all target attributes.

Two, previous versions of the Link draft did not adequately address the use of the Link header field's anchor attribute and the determination of the Context IRI. In the current draft, the Context IRI is resolved in accordance to Section 5.2 of RFC 5988. This allows the anchor attribute to be used to override the default context IRI.

This draft also deals with ambiguities in IRI comparison, 2xx response codes and conditional requests (well, I added the parts about condition requests in the previous iteration but had not specifically called those out as a change yet).

As always, feedback is requested and very welcomed.

2013-11-04

Evolving the urn:social Namespace

I've posted another update to the very draft "urn:social" Namespace definition. This fixes a few spec bugs but also expands the definition of the "urn:social:common" URN to allow for listing of specific common attribute dimensions. For instance, to reference the subset of a population sharing the same gender and approximate age, you could do something like "urn:social:common:gender;age". Or, to reference a subset sharing a history of similar activities, you could do something like "urn:social:common:history". The actual dimension tokens themselves are undefined. It would be up to implementations to define what those tokens are, what they mean and how they are to be used.

For a practical idea on how this would be used, consider sharing an article with members of a social network who are approximately the same age as you:


  POST /articles/joys-of-being-a-middle-aged-dad HTTP/1.1
  Host: example.org
  Link: <urn:social:common:age>; rel="to"

  ...

For the next iteration of the draft, I am considering the possibility of accommodating additional social network dimensions and relationship ties. Specifically:

  • urn:social:tie:evaluation:{dimension} --> refers to the subset evaluated by the context entity. For example:
    • urn:social:tie:evaluation:liked --> the subset of the population "liked" by the context entity
    • urn:social:tie:evaluation:disliked --> the subset of the population "disliked" by the context entity
    • urn:social:tie:evaluation:respect --> the subset of the population "respected" by the context entity
  • urn:social:tie:transaction:{role} --> refers to the subset sharing a specific transactional role with the context entity. For example:
    • urn:social:tie:transaction:customer --> the subset of the population considered to be a customer of the context entity
    • urn:social:tie:transaction:lender --> the subset of the population considered to be a lender to the context entity
  • urn:social:tie:affiliation:{dimension} --> refers to the subset sharing a common affiliation with the context entity. For example:
    • urn:social:tie:affiliation:employment --> refers to the subset affiliated by employment with the context entity (possible alternative: urn:social:common:employment)
    • urn:social:tie:affiliation:belief --> refers to the subset affiliated by belief system with the context entity (possible alternative: urn:social:common:belief)
  • urn:social:tie:interaction:{mode} --> refers to the subset that has interacted with the context entity. For example:
    • urn:social:tie:interaction --> refers to the subset that has interacted with the context entity in any mode.
    • urn:social:tie:interaction:chat --> refers to the subset that has "chatted" with the context entity.
    • urn:social:tie:interaction:email --> refers to the subset that has emailed the context entity.
  • urn:social:tie:familial:{role} --> refers to the subset sharing a familial relationship with the context entity. For example:
    • urn:social:tie:familial:spouse --> refers to the spouse/partner of the context entity.
    • urn:social:tie:familial:parent --> refers to the parent of the context entity.

These URNs would, quite admittedly, be very specialized. For instance, imagine sharing a post within a social network only with acknowledged members of your immediate and extended family, but only showing a notification to your parents and siblings.


  POST /images/pictures-of-my-son HTTP/1.1
  Host: example.org
  Link: <urn:social:tie:familial>; rel="scope" 
  Link: <urn:social:tie:familial:parent;sibling>; rel="to"

Or, sending an offer only to members of the community who have previously purchased items from you before, with specific notification given only to those who have "opted-in".


  POST /offers/1 HTTP/1.1
  Host: example.org
  Link: <urn:social:tie:transaction:customer>; rel="scope"
  Link: <urn:social:tie:interaction:opt-in>; rel="to"

Again, I'm just kicking these around and experimenting thus far, but it would be great to receive feedback on the overall direction. I know there's already a very rich history of work in the semantic web area around family history and structure, but the goal here is quite distinct from documenting the structure of a graph. Comments are more than welcome.

W3C Social Web Working Group

Introducing the W3C Social Web Working Group.

The Social Web Working Group will develop a Recommendation-track data-format to allow the transfer of social information, such as status updates, across differing social systems. This data format will be based on ActivityStreams and JSON in order to promote interoperability with Web Applications.

The Social Web Working Group may develop a Recommendation-track document that defines an API, based on OpenSocial, that lets developers embed of third party information with bidirectional communication inside Web applications. The focus will be on enabling the secure capabilities needed by social applications, which is necessary to bring areas such as social business to their full potential and avoid fragmentation.

The Social Web Working Group may develop a Recommendation-track Web protocol to allow the federation of status updates and other data, such as profile information, between heterogeneous Web-based social networking systems. This work will be based work on a federated social web explored both in HTTP (using Pubsubhububub) and XMPP.

The primary starting point of this WG will be the Activity Streams 2.0 draft... which will soon switch from being an Independently-Submitted IETF Internet-Draft to a W3C Note and eventual Recommendation. I have volunteered to continue serving as the editor for that specification. I will be posting weekly status updates (as much as possible at least) to both the Activity Streams mailing list and to this blog.