Introduction
By definition, the OpenSocial spec supports extensions to its data model. In Apache Shindig, implementing data model extensions requires familiarity with Java and Shindig's back-end and requires continued maintenance as extensions evolve. This article documents how to enable arbitrary extensions in Apache Shindig's data model, circumventing the need for explicitly hardcoding and maintaining extensions.
...
Code Block |
---|
{ id: "activity123", ... object: { ... replies: [{...}, {...}, {...}] // This field isn't hardcoded in Shindig, but it will handle it }, extensions: { dynamicExtension: "Another extension that was not predefined" } } |
How to Enable Arbitrary Extensions
Currently, Activity Streams support arbitrary extensions in Apache Shindig. Additional data models may support arbitrary extensions with a very quick and simple change: the data model's interface must extend ExtendableBean, and the interface's implementing class must extend ExtendableBeanImpl.
...
Code Block |
---|
public class ActivityEntryImpl extends ExtendableBeanImpl implements ActivityEntry { . . . } |
XML Limitations
I recommend using the JSON API when handling extensions. The JSON API fully supports all CRUD operations with extensions "out-of-the-box" by simply extending ExtendableBean. XML serialization requires a couple of extra steps and has a number of limitations due to Shindig's design and use of xstream. Specifically:
...
Code Block |
---|
--- JSON SERIALIZATION --- extension: { key1: "value1", key2: "value2", stringList: [ "element1", "element2", "element3" ], mapList: [ { key1: "value1" }, { key2: "value2" } ] } --- XML SERIALIZATION --- <extensions> <key1>value1</key1> <key2>value2</key2> <stringList> <java.lang.String>element1</java.lang.String> <java.lang.String>element2</java.lang.String> <java.lang.String>element3</java.lang.String> </stringList> <mapList> <map> <entry> <key>key1</key> <value>value1</value> </entry> </map> <map> <entry> <key>key2</key> <value>value2</value> </entry> </map> </mapList> </extensions> |
How It's Implemented
Here are some technical high-level implementation details of how I implemented support for arbitrary extensions are supported in Shindig. It should come in helpful if anyone needs to expand or modify this work.be a helpful starting point to anyone who wants to modify or expand this work.
First, ExtendableBean and ExtendableBeanImpl are nothing more than wrappers for Map and HashMap, respectively. Thus, when a data object extends from ExtendableBean, it itself is a Map.
Next, when a JSON POST is received, the BeanJsonConverter is called to convert the raw JSON string to the appropriate target object type. A simple check was added within BeanJsonConverter to determine if the target class type extends ExtendableBean. If it does, the BeanJsonConverter will treat the object as a Map to initialize the Map's key/value pairs to represent the JSON object in addition to initializing the object normally (using its getters and setters).
When the data object is persisted on disk, the object is stored as a Map to retain all properties of the initial POST request, and vice versa.
To support XML serialization for GET requests, I wrote a custom converter and registered it with xstream. The converter is named ExtendableBeanConverter, and it is registered with xstream within XStream081Configuration.java.
For more detail, check out the relevant source files for more comments: ExtendableBean.java, ExtendableBeanImpl.java, BeanJsonConverter.java, ExtendableBeanConverter.java