什么是 OpenSocial
OpenSocial 是基于开放标准的一组通用的 API用于帮助 WEB 的开发者构建跨多个社交网站的可移植的社交应用程序OpenSocial 提供开发者一套通用的 API基于该通用 API 开发的社交应用程序可以运行在任意支持 OpenSocial 规范的社交网站上
关于更多的有关 OpenSocial 内容请读者参见
Apache Shindig
Shindig 是 OpenSocial 规范的引用实现其主要的组件包括 :
Gadget Container JavaScriptOpenSocial Gadget 容器客户端的 JavaScript 类库 (gadgetjs)提供例如 UI LayoutSecurity Communication 等相关的功能
Gadget Rendering Server负责解析 Gadget XML 转化成浏览器使用的 HTML/JavaScript/CSS
OpenSocial Container JavaScript位于客户端的 OpenSocial 容器也是 JavaScript 类库提供 OpenSocial 相关的功能例如存取 People Activity AppData 等相关的社交数据
OpenSocial Data Server提供基于 Restful/RPC 协议的 Services用于存取 People Activity AppData 等相关的社交数据
图 是 Shindig 的服务器端架构图
图 Shindig Architecture( 引自 Chris Schalk@GoogleTM)
从图 中可以看到Shindig 基于 Java Servlet Stack 实现GadgetRenderingServlet 负责 Gadget Rendering 而 DataServiceServlet 和 JsonRpcServlet 实现 OpenSocial Data Server 中相对应的 Restful 及其 RPC 服务JsonDbOpensocialService 通过实现 ActivityService PersonService AppDataService 三个接口向 Shindig OpenSocial 容器提供基于 Json 格式的 OpenSocial 数据客户端的 Gadgets 可以使用标准的 OpenSocial API 访问到这些数据
关于更多的有关 Apache Shindig 内容请读者参见
Eclipse 环境下编译 / 调试 Shindig
我们通过以下的步骤来完成
安装 Maven plugin
Maven 是一个基于 Java 的代码构建和依赖管理工具Apache Shindig 的源代码是通过 Maven 来管理的所以我们需要安装 Maven 的 Eclipse 插件读者可以使用 Eclipse 的 updatesite 机制连接到站点安装
使用 Subversion 下载 Shindig 代码
在 使用 SVN 客户端下载到 Shindig 的源代码
编译和调试 Shindig
首先在 Eclipse IDE 里通过 File/Import/General/Maven Projects 选项导入我们下载的所有 Shindig 的源代码导入完成后Shindig 就作为几个 Maven 工程存在于你当前的 WorkSpace 中
通过 Run/Debug Configurations/Maven Build 配置编译 Shindig 参数如图 所示
图 Package Shindig
如图 所示E:\svn_repository\opensocialshindig是你的 Shindig 源代码的根目录点击 Debug 这将使用 Maven 来 Build 整个 Shindig 代码在 Build 成功后我们使用 Jetty 来启动 Shindig默认情况下Jetty Server 将运行在 端口如图 所示
图 Run Shindig
如图 所示我们设置了 Maven 目标使用 Jetty 来启动 Shindig 而 Base directory 设置为 shindigserver Maven 工程的根目录点击 Debug Jetty Server 运行而 Shindig 部署在 Jetty Server 上 在 Shindig 成功启动后 你就可以使用//localhost:/gadgets/files/samplecontainer/l来访问 Shindig 提供的 Gadget 的例子
Shindig 服务器端 SPI 扩展
Shindig 作为 OpenSocial 规范的引用实现提供了 SPI 的扩展能力允许你把数据适配到 Shindig 容器中去你的这些数据也许存在于诸如 My SQL/Oracle 的关系数据库或者是以 JSON 格式存储的静态文件无论哪种存储你都可能通过 Shindig SPI 将它们适配到 Shindig 从而使这些数据公布在 OpenSocial 平台上
图 Shindig SPI 扩展
如图 所示你的应用需要实现 ActivityService PersonService AppDataService 三个接口利用诸如 JDBC/Hibernate 等机制把数据提供给 Shindig
接下来本文将通过一个例子实现 PersonService 接口向 Shindig 提供 People/Friends 相关的 OpenSocial 数据
清单 是 SocialTestJsonPersonService类的实现
清单 SocialTestJsonPersonService Class
public class SocialTestJsonPersonService implements PersonService {
private static final String PEOPLE_TABLE = people;
private static final String FRIEND_LINK_TABLE = friendLinks;
private JSONObject db;
private BeanConverter converter;
……
public Future<RestfulCollection<Person>> getPeople(Set<UserId>
userIdsGroupId groupId CollectionOptions options Set<String> fields
SecurityToken token) throws ProtocolException {
List<Person> result = ListsnewArrayList();
try {
//Read people data from JSON table
JSONArray people = dbgetJSONArray(PEOPLE_TABLE);
Set<String> idSet = getIdSet(userIds groupId token);
for (int i = ; i < peoplelength(); i++) {
JSONObject person = peoplegetJSONObject(i);
if (!ntains(personget(PersonFieldIDtoString()))) {
continue;
}
// Add group support later
Person personObj = filterFields(person fields Personclass);
resultadd(personObj);
}
if (GroupIdTypeself == groupIdgetType() && resultisEmpty()) {
throw new ProtocolException(HttpServletResponseSC_BAD_REQUEST
Person not found);
}
int totalSize = resultsize();
return ImmediateFuturenewInstance(new RestfulCollection<Person>(
result optionsgetFirst() totalSize optionsgetMax()));
} catch (JSONException je) {
throw new ProtocolException(
HttpServletResponseSC_INTERNAL_SERVER_ERROR je
getMessage() je);
}
}
public Future<Person> getPerson(UserId id Set<String> fields
SecurityToken token) throws ProtocolException {
try {
//Read people data from JSON table
JSONArray people = dbgetJSONArray(PEOPLE_TABLE);
for (int i = ; i < peoplelength(); i++) {
JSONObject person = peoplegetJSONObject(i);
if (id != null
&& personget(PersonFieldIDtoString())equals(
idgetUserId(token))) {
Person personObj = filterFields(person fields
Personclass);
return ImmediateFuturenewInstance(personObj);
}
}
throw new ProtocolException(HttpServletResponseSC_BAD_REQUEST
Person not found);
} catch (JSONException je) {
throw new ProtocolException(
HttpServletResponseSC_INTERNAL_SERVER_ERROR je
getMessage() je);
}
}
……
}
从清单 可以看到SocialTestJsonPersonService 实现了 PersonService 两个接口方法 getPeople 及其 getPersongetPeople 根据传入参数 userIds返回相应于该 ID 列表的用户列表而 getPerson 根据传入参数 id返回相应于该 ID 的用户
注意Shindig 依赖 Guice 做动态的依赖注入 (dependency Injection)我们需要在 orgapacheshindigsocialsampleSampleModule 里指示 Guice 把 PersonService 绑定到 SocialTestJsonPersonService 实现如清单 所示
清单 SampleModule Class
public class SampleModule extends SocialApiGuiceModule {
@Override
protected void configure() {
nfigure();
bind(Stringclass)annotatedWith(Namesnamed(shindigcanonicaljsondb))
toInstance(sampledata/canonicaldbjson);
bind(Stringclass)annotatedWith(Namesnamed(shindigsocialtestjsondb))
toInstance(sampledata/socialtestdbjson);
bind(ActivityServiceclass)to(JsonDbOpensocialServiceclass);
bind(AppDataServiceclass)to(JsonDbOpensocialServiceclass);
//bind(PersonServiceclass)to(JsonDbOpensocialServiceclass);
bind(PersonServiceclass)to(SocialTestJsonPersonServiceclass);
bind(MessageServiceclass)to(JsonDbOpensocialServiceclass);
bind(OAuthDataStoreclass)to(SampleOAuthDataStoreclass);
// We do this so that jsecurity realms can get access to the jsondbservice singleton
requestStaticInjection(SampleRealmclass);
}
……
}
从清单 中还可以看到标记为shindigsocialtestjsondb的字符串绑定到了sampledata/socialtestdbjson socialtestdbjson 是我们示例中的 JSON 数据文件用来保存 People 数据在 SocialTestJsonPersonService 的实现中dbgetJSONArray(PEOPLE_TABLE) 就是从该 JSON 文件获取所有的 People 数据
在下一节我们给出客户端实现来消费 socialtestdbjson 中的 Social 数据
Gadget/Restful 客户端实现
Gadget 实现
清单 SocialAppTestxml
<?xml version= encoding=UTF?>
<Module>
<ModulePrefs
title=Social Application Test
author_email=>
<Require feature=osapi />
<Require feature=dynamicheight />
</ModulePrefs>
<Content type=html ><![CDATA[<! Fetching People and Friends >
<div>
<button onclick=fetchPeople();>Fetch people and friends</button>
<div>
<span id=viewer></span>
<ul id=friends></ul>
</div>
</div>
<script type=text/javascript>
var allPeople;
function render(data) {
var viewer = dataviewer;
allPeople = dataviewerFriendslist;
documentgetElementById(viewer)innerHTML = viewerid;
documentgetElementById(friends)innerHTML = ;
for (var i = ; i < allPeoplelength; i++) {
documentgetElementById(friends)innerHTML += <li> +
allPeople[i]nameformatted + </li>;
}
gadgetswindowadjustHeight();
}
function fetchPeople() {
var fields = [idagenamegenderprofileUrlthumbnailUrl];
var batch = osapinewBatch();
batchadd(viewer osapipeoplegetViewer({sortBy:namefields:fields}));
batchadd(viewerFriends
osapipeoplegetViewerFriends({sortBy:namefields:fields}));
batchadd(viewerData osapiappdataget({keys:[count]}));
batchadd(viewerFriendData
osapiappdataget({groupId:@friendskeys:[count]}));
batchexecute(render);
}
</script>]]></Content>
</Module>
如清单 所示fetchPeople 使用了 osapi 获得 OpenSocial 数据并把它们展示在 HTML 页面上osapi 是一个轻量级的 JavaScript 类库用于帮助客户端获得 OpenSocial 数据显示该 Gadget 的 HTML 页面代码 (l)请读者详见文章后面的资源类表在这里我们就不一一列出
现在我们可以在 Eclipse IDE 中启动 Shindig 在你的浏览器里输入地址
//localhost:/gadgets/files/samplecontainer/l打开 SocialAppTest Gadget点击Fetch people and friends按钮SocialAppTest Gadget 向本地 Shindig 请求数据Shindig 从 socialtestdbjson JSON 文件中获取数据返回给 Gadget 并在浏览器中显示如图 所示
图 SocialAppTest Gadget
RESTful 客户端实现
另外我们还可以选择使用 Java 应用程序通过 REST 协议获得 OpenSocial 数据如清单 所示
清单 RESTful Client 实现
public class SocialAppTest {
private static final String BASE_URI = //localhost:/social/rest/;
private static final String VIEWER_ID = johndoe;
public static void main(String[] args) {
OpenSocialClient client = new OpenSocialClient(SocialAppTest);
clientsetProperty(OpenSocialClientPropertyREST_BASE_URI BASE_URI);
clientsetProperty(OpenSocialClientPropertyVIEWER_ID VIEWER_ID);
try {
OpenSocialPerson viewer = clientfetchPerson(VIEWER_ID);
Systemoutprintln(Viewer: + viewergetId());
Collection<OpenSocialPerson> friends = clientfetchFriends(viewergetId());
for (OpenSocialPerson friend : friends) {
Systemoutprintln(Friend: + friendgetId());
}
} catch (Exception e) {
eprintStackTrace();
}
}
}
清单 的运行结果和 SocialAppTest Gadget 一样显示当前的 Viewer 及其他的朋友
结束语
通过本文读者已经了解了如何使用 Shindig SPI 来将自己的 Social 数据适配到 Shindig 平台也了解了如何构建客户端的应用来消费这些 Social 数据
现在我们不妨回头总结一下整个 OpenSocial 平台的系统结构一般来说OpenSocial 系统应用使用 OpenSocial Gadget 作为应用前端OpenSocial Gadget 类似于 iGoogle Gadget不过增加了 OpenSocial 数据的访问能力当然你也可以使用基于 REST/RPC 协议构建的桌面 RCP 应用作为前端而对于 OpenSocial 平台服务器端也有两个选择一则如本文所讨论的这样利用成熟开源的与 OpenSocial 规范相兼容的 OpenSocial 容器实现 ( 例如 Shindig)通过 SPI 扩展实现自己的 OpenSocial 容器或者你从头开始实现 OpenSocial 规范相兼容的 OpenSocial 容器