引用说明:原文来自于http://www.infoq.com/cn/articles/social-content-apache-shindig;jsessionid=2DEB7B2D3177D28C5D104F05423FA20B,为了方便本人阅读,文本格式略有调整。
社区网络是目前网络上最热门的事情之一,所以毫不奇怪,许多Web应用程序试图加上社区网络内容来吸引更多的访问者。尽管API正在标准化[1],但大多数现有的社区网站依然提供专有API,或者对OpenSocial的不同版本提供支持,而这正是许多开发人员所面临的挑战。结果是目前OpenSocial有多版本的的API和大量的开源代码支持他们[2]。在这篇文章中我将讨论Apache Shindig[3]如何实现OpenSocial,如何减少其中的一些问题。
高速下载:Adobe Flash Builder 4 简体中文正式版 for Windows
高速下载:Adobe Flash Builder 4 简体中文正式版 for Mac
汇集最新RIA技术相关资源,提供Flash开发平台相关工具高速下载。
我首先讨论OpenSocial的标准和Shingig架构,然后说明它们如何为一个应用程序添加社区网络内容。最后,将以OpenSocial Gypsii社区网络[4]为例,讲解启用Shindig的方法。
OpenSocial 所定义的规范[5]:
“OpenSocial 是一组用于构建在网络上运行的社交应用程序的API。OpenSocial 的目标是通过提供一个可在很多不同的环境中使用的通用API ,让更多的应用程序被更多的用户访问。开发人员可以使用标准的javascript 和html 创建应用程序,运行在那些已经应用OpenSocial的社交网站中。这些网站作为OpenSocial的容器,允许开发者访问他们的社区信息;反过来,又为他们的用户收到了大量的应用程序。
OpenSocail API暴露了在一个[社区网络]容器中获取用户以及他们的好友信息的方式。”
社区应用程序围绕人、活动和关系展开。人是社区网络软件和OpenSocial API的基本组成部分。Person对象提供对用户信息的访问接口,它包含了用户的基本信息或其他扩展的信息,如“最喜爱的电视节目”、“我所期望的工作”以及“最喜欢的地方”。OpenSocial API提供了用于查看和更改个人资料的支持。另一个重要的用户信息是他们在社交图中的连接。为了能够共享信息和体验朋友动态变化,OpenSocial支持用户的朋友概念,并提供检索用户朋友信息的API。当应用程序想进行交互或显示“朋友的朋友”的数据,OpenSocial允许增加一个你所做操作的可选过滤器来支持扩展查询,如用户可以通过朋友的距离来排序。容器可以有选择地支持“朋友的朋友”查询,“朋友的朋友的朋友”的查询,等等。 OpenSocial还允许朋友分享他们最近一直在做的信息-这些被称为活动。OpenSocial的公开活动流是一个用户所采取的行动的集合。活动模板允许应用程序开发人员在消息中为应用程序或用户数据块定义占位符。这种数据层和显示层的分离允许多个活动合并成活动包:活动综合包,让用户知道他们朋友消息,而不必辛苦地搜集大量消息。社区互动的另一个重要方面是能够读取,张贴和删除用户之间在网络中的消息。 OpenSocial定义的消息类型,包括公共消息(如文件的评论)和私人消息(仅限于某些个人和团体)。消息归纳,其中包括一个唯一的ID,名称和信息数量大小。
目前的规范定义了两种版本的API──JavaScript[6]和REST[7]。
OpenSocial的JavaScript规范是提供一个小工具Gadget[8]设计来支持OpenSocial的功能。架构如图1:
图1 使用JavaScript OpenSocial API典型架构
在这种情况下,一个用户应用程序被作为一个Gadgets的集合,Gadget自己用OpenSoical API可以获取社交信息。因此, JavaScript API与Gadget API的紧密结合,并非总是适合不是基于Gadget的实现。
相反的,OpenSocail REST API不直接与Gadgets关联,允许任何客戶端应用程序来查看和发布用户的OpenSocial数据。我们将用这些API来实现OpenSocial应用。
Apache Shindig是Gadget API和OpenSocial API的开源实现。下面将简单回顾一下什么是Apache Shindig;Apache Shindig是一个OpenSocial的容器,帮助您通过提供代码托管、代理请求和处理REST和RPC请求来快速启用OpenSocial。
图2 Apache Shindig[9]
对于OpenSocial,Shindig同时实现JavaScript和REST的API。 Shindig OpenSocial实现[10]总体结构如图3:
图3 Shindig实现Open Social的整体结构图
从图3中我们可以看到Shindig实现包括两个主要部分:客户端的JavaScript容器和Shindig服务器。 服务器是一个WAR文件,它可以运行在任何Servlet容器中-我们使用Tomcat。正如上面所定义的,因为我们正在直接用REST OpenSocial API,所以在这里我们不会讨论客户端JavaScript容器。
服务器执行包含两个监听器:JSON RPC Servlet和数据服务(REST)Servlet。 每个Servlet被它自己的处理程序支持。 因此有两个处理器:RPC和REST的处理器。这两种处理器共享同一基础类库,包括日期转换器(这里没有显示)和一系列OpenSocial的处理器:
Shindig服务实现基于依赖注入框架Guice[11]。它使用的是绑定类(在web.xml文件中配置),所定义的服务被每一个处理器调用:即每个处理程序使用相应的服务。这种架构极其易于使用自定义实现覆盖Shindig发布服务一部分的实现,如实现与现有社区网络应用程序的集成。
Gypsii[12]是一个位置感知的社区网络。为实现共同的社区网络,除了传统的用户归属,它还允许其参与者存储和共享地点与位置信息,包括地址,地理坐标,可选的图片和说明。 GyPSii中一个特殊的地点是当前用户的位置。它也支持POI(兴趣点)和广告。
GyPSii提供了一套API:GyPSii OenExperience API(OEx)[13]、XML-RPC API,还提供了访问GyPSii所有功能的方法。 GyPSii XML-RPC的变种是由XML - RPC规范[14]创造的,但已适应GyPSii的需要。该协议定义使用XML双方沟通时一个客户端服务器的通信模式。 Gypsii为开发人员提供了XML-RPC协议和应用了该协议的JAVA客户端实现的文档。
实现的整体架构如图4。实现一个Gypsii的适配器,通过自定义OpenSocial容器替换Shindig提供的默认OpenSocial容器的方式,使得现有的Shindig 支持OpenSocial REST。 请注意,这个自定义提供者可以连接到任何其他社区网络供应商,如Facebook,MySpace或Ning。
图4 整体解决方案框架
这种方法的好处是有效地将我们的应用程序与特定的社区网络的实现/集成方法屏蔽开来。客户端API由Shindig提供和内部控制。因此,我们的应用程序不依赖于具体的技术和社区网络供应商公开的API。这些细节被封装在一个特定的网络适配器中。
与多个社区网络供应商集成的复杂问题之一是安全/证书支持,范围涉及从一个登录用户名称/密码到OpenID[15],再到OAuth[16]。我们的实现是基于一个简单的身份转换,如图5所示。
图5 身份转换
例如,用户ID 123468@gypsii意味着NAVTEQ用户以ID123468访问Gypsii。
用户请求具有NAVTEQ’s用户ID的Shindig:令牌。网络要求一种特定的社会网络适配器将令牌转换成证书。基于用户存储的转换包含转换所需的信息:用户名/密码,OpenID,特定的键或其他任何一个特定的转换要求。这允许我们使用Navteq公司的用户ID作为OpenSocial REST API的标准化参数。它还允许多个供应商之间的社区网络的请求,基于以下命名约定简单实现:
OpenSocialUID = NAVTEQUID@Network
例如用户ID 123468 @ GyPSii意思是ID 123468的NAVTEQ用户访问Gypsii。这允许构建一个非常简单的网络适配器(图4),社区网络适配器根据所提供的用户名选择线路。
在依赖注入支持下,替换默认的社区网络服务就相当简单,主要包括以下主要步骤:
public class CustomOpenSocialService implements ActivityService, AppDataService, PersonService, MessageService { ………………………………….. }
清单1 客户自定义的OpenSocial 服务
public class NAVTEQBind extends SocialApiGuiceModule { ……………… bind(ActivityService.class).to(CustomOpenSocialService.class); bind(AppDataService.class).to(CustomOpenSocialService.class); bind(PersonService.class).to(CustomOpenSocialService.class); bind(MessageService.class).to(CustomOpenSocialService.class); ……………… }
清单2 绑定自定义OpenSocial服务
org.apache.shindig.common.servlet.GuiceServletContextListener
类调用Shindig应用程序和设置正确的绑定。<context-param> <param-name>guice-modules</param-name> <param-value> org.apache.shindig.common.PropertiesModule:com.navteq.opensocial.bind.NAVTEQBind </param-value> </context-param>
清单3 Web.config变化
我们需要从浏览器中访问OpenSocial REST APIs。这里的问题是现在浏览器都具有同源策略限制[17]:
“同源策略是指阻止代码获得或者更改从另一个域名下获得的文件或者信息。也就是说我们的请求URL域必须和当前网站的域相同。这基本上意味着,Web浏览器将不同域的内容隔离以阻止它们彼此对终端用户的操作。”
这里有几种克服限制的常用方法:
由Shindig提供的OpenSocial API是利用GET从社区网络中获取信息和POST更新信息。为了支持GET方式,我们充分利用JSONP,而为了支持POST方式,我们使用框架技术。
为了实现服务器上JSONP支持,我们使用了开源JSONP过滤器[18],不要求对Shindig的方案变动支持。在Shindig的Web.xml中定义过滤器用法(清单4):
<filter>
<display-name>jsonp</display-name>
<filter-name>jsonp</filter-name>
<filter-class>org.jsonp.JsonpFilter</filter-class>
<init-param>
<param-name>jsonp</param-name>
<param-value>jsonpCallback</param-value>
</init-param>
<init-param>
<param-name>json-mime-types</param-name>
<param-value>application/json</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>jsonp</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
清单4 配置JSONP过滤器
我们还必须稍微修改Shindig代码使之能够支持Post方式。Shindig发布版不允许内容类型为application/x-www-form-urlencoded
,因为它与OAuth body signing冲突。因为我们没有使用OAuth和application/x-www-form-urlencoded
的内容类型的POST浏览器,我们必须修改类org.apache.shindig.protocol.ContentTypes
(清单5)和org.apache.shindig.protocol.DataServiceServlet
(清单6)允许application/x-www-form-urlencoded
:
…………………. public static void checkContentTypes(Set<String> allowedContentTypes, String contentType, boolean disallowUnknownContentTypes) throws InvalidContentTypeException { if (StringUtils.isEmpty(contentType)) { if (disallowUnknownContentTypes) { throw new InvalidContentTypeException( "No Content-Type specified. One of " + StringUtils.join(allowedContentTypes, ", ") + " is required"); } else { // No content type specified, we can fail in other ways later. return; } } contentType = ContentTypes.extractMimePart(contentType); //BL. comented out to support // if (ContentTypes.FORBIDDEN_CONTENT_TYPES.contains(contentType)) { // throw new InvalidContentTypeException( // "Cannot use disallowed Content-Type " + contentType); // } ..........................................
清单5 修改ContentTypes类
…………… public static final Set<String> ALLOWED_CONTENT_TYPES = new ImmutableSet.Builder<String>().addAll(ContentTypes.ALLOWED_JSON_CONTENT_TYPES) .addAll(ContentTypes.ALLOWED_XML_CONTENT_TYPES) .addAll(ContentTypes.FORBIDDEN_CONTENT_TYPES) .addAll(ContentTypes.ALLOWED_ATOM_CONTENT_TYPES).build(); ……………………
清单6 修改DataServiceServlet类
另一个问题是,浏览器POST方式提交名称/值。虽然,Shindig支持POST名称/值,它也支持由POST body直接处理的数据。作为一个执行增加了一个额外的名称/值参数的主体内容。问题是,在浏览器提交的情况下,Body是空的而其参数的内容被覆盖。 org.apache.shindig.protocol.DefaultHandlerRegistry
类(清单7)更改修复了这个问题。
…………….. public Future<?> execute(Map<String, String[]> parameters, Reader body, SecurityToken token, BeanConverter converter) { try { // bind the body contents if available if (body != null) { String bString = IOUtils.toString(body); if(bString.length() > 0) parameters.put(operation.bodyParam(), new String[]{bString}); } RequestItem item = methodCaller.getRestRequestItem(parameters, token, converter, beanJsonConverter); listener.executing(item); return methodCaller.call(handlerProvider.get(), item); ………………………………………..
清单7 禁止覆盖空消息体
最后,当POST方式不会返回任何数据,Shindig实现将空数据转换成一个空的JSON对象,返回的类型为“application/json”。这将导致浏览器尝试主动与用户会话。org.apache.shindig.protocol.DataServiceServlet
(清单7)解决了这一问题:
org.apache.shindig.protocol.DataServiceServlet (Listing 7) solves this problem: servletResponse.setContentType(converter.getContentType()); if (responseItem.getErrorCode() >= 200 && responseItem.getErrorCode() < 400) { Object response = responseItem.getResponse(); // TODO: ugliness resulting from not using RestfulItem if (!(response instanceof DataCollection) && !(response instanceof RestfulCollection)) { //BL - modified to not return fake responce if(response instanceof Map){ if(((Map)response).isEmpty()){ servletResponse.setContentType("text/plain"); return; } } response = ImmutableMap.of("entry", response); }
清单8 禁用空响应
在这篇文章中,我们描述OpenSococial的实现,用于构造GeoSpatial OpenSocial mashup,允许用户和他的朋友把他们最喜欢的地方放在地图上(图6)
图6 地图上用户和他的朋友
它还允许将用户信息和他最爱的地方以及社区网络信息(图7)发送给另一个朋友。
图7 社区网络信息
这种简单的实现只是社区网络和地理空间数据之间许多潜在协同效应之一。基于以上所描述的结合两者的方法,能实现许多其他有趣的混搭网站。
开源社区API的扩展使它很难开发出应用程序支持多种现有的社区网络。在本文中提出的一个解决方案,通过第三方提供的标准API“隐藏”前端实现差异来克服这些问题。 Apache Shindig,提供API和“插件”架构,大大简化了整个实现。
查看英文原文:Bringing in Social Content to Custom Applications with Apache Shindig。
[1] http://www.opensocial.org/
[2] http://www.thegoodnamesweregone.com/post/83-open-source-web-api-frameworks.aspx
[3] http://incubator.apache.org/shindig/
[5] http://www.opensocial.org/Technical-Resources/opensocial-spec-v09/OpenSocial-Specification.html
[6] http://wiki.opensocial.org/index.php?title=JavaScript_API_Reference
[7] http://wiki.opensocial.org/index.php?title=OpenSocial_REST_Developer%27s_Guide
[8] http://www.opensocial.org/Technical-Resources/opensocial-spec-v09/Gadgets-API-Specification.html
[9] Source : http://incubator.apache.org/shindig/
[10] Rajdeep Dua. Architectural Overview of Shindig , an OpenSocial Reference Implementation. http://chrisschalk.com/shindig_docs/rajdeep/shindig-overview/onjava-shindig-overview-tidy.html and Rajdeep Dua. Overview of REST Implementation in Shindig - Java Version. http://sites.google.com/site/opensocialarticles/Home/shindig-rest-java
[11] http://code.google.com/p/google-guice/
[12] http://www.gypsii.com/home.cgi
[13] http://developer.gypsii.com/docs.cgi?op=view&id=api.html
[17] Seda Özses, Salih Ergül Cross-domain communications with JSONP, Part 1: Combine JSONP and jQuery to quickly build powerful mashups.http://www.ibm.com/developerworks/library/wa-aj-jsonp1/
[18] http://code.google.com/p/jsonp-java/
感谢曹云飞对本文的审校。
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至[email protected]。也欢迎大家加入到InfoQ中文站用户讨论组中与我们的编辑和其他读者朋友交流。