使用Apache Shindig将社交内容引入自定义应用程序
社交网络是当前Web上最热门的事物之一,因此,许多Web应用程序都在尝试合并社交网络内容以吸引更多客户也就不足为奇了。 许多开发人员都面临的挑战是,尽管试图标准化API [1] ,但是大多数现有的社交网络网站仍在提供专有API或支持不同版本的OpenSocial。 结果,当前有大量的API和支持它们的大量开源代码[2] 。 在本文中,我将讨论如何使用OpenSocial实现Apache Shindig [3]来缓解其中的一些问题。
我将首先讨论OpenSocial标准和Shindig架构,然后说明如何将它们用于将社交网络内容带入应用程序。 作为示例,我将描述Shindig在Gypsii社交网络[4]实现的OpenSocial启用中的用法。
OpenSocial API
根据OpenSocial规范[5]的定义:
“ OpenSocial是用于构建在网络上运行的社交应用程序的一组API。 OpenSocial的目标是通过提供可在许多不同上下文中使用的通用API,使更多应用可供更多用户使用。 开发人员可以使用标准JavaScript和HTML创建在实现了OpenSocial API的社交网站上运行的应用程序。 这些网站称为OpenSocial容器,允许开发人员访问其社交信息。 作为回报,他们会为用户提供大量的应用程序。
OpenSocial API公开了在[社交网络]容器的上下文中访问有关人,他们的朋友和他们的数据的信息的方法。”
社交应用围绕人及其活动和关系展开。 人是社交网络软件和OpenSocial API的基本组成部分。 Person对象提供对用户信息的访问,该信息取决于站点,可以包括从“喜欢的电视节目”到“我的工作愿望”再到“喜欢的地方”的任何内容。 OpenSocial API为查看和更改个人数据提供支持。 另一个重要的用户信息是他们在社交图中具有的一组连接。 能够共享信息并与朋友互动可以改变用户体验的动态。 OpenSocial支持用户朋友的概念,并提供用于检索人的朋友及其信息的API。 当应用程序想要与“朋友的朋友”进行交互或显示数据时,OpenSocial规范支持通过添加可选的过滤器来扩展对朋友的查询-例如,该过滤器使您可以按照朋友与用户的距离对朋友进行排序。 容器可以可选地支持“朋友的朋友”查询,“朋友的朋友”查询等等。 OpenSocial还允许朋友共享有关他们最近在做的事情的信息-这些被称为活动。 OpenSocial公开了活动流,这些活动流是用户已执行的操作的集合。 活动模板允许应用程序开发人员使用占位符为应用程序或用户数据段定义消息。 数据和表示的这种分离允许将多个活动合并为活动摘要:合并的活动捆绑包,使用户可以知道他们的朋友在做什么,而不必费力地查看大量消息。 社交互动的另一个重要方面是能够读取,发布和删除网络中用户之间的消息。 OpenSocial定义了多种消息类型,包括公共消息(例如,个人资料评论)和私人消息(仅限于某些个人和团体的消息)。 消息被分组到MessageCollections中,其中包括唯一的ID,名称和消息数量的大小。
当前规范定义了API的两个版本-JavaScript [6]和REST [7] 。
OpenSocial JavaScript规范旨在支持小工具[8]中的 OpenSocial功能。 图1给出了这种情况的典型架构。

图1使用JavaScript OpenSocial API的典型架构
在这种情况下,将用户应用程序构建为小工具的集合,其中小工具本身使用OpenSocial API来访问社交信息。 结果,JavaScript API与Gadget API紧密结合,可能并不总是适用于非基于Gadget的实施。
另一方面,OpenSocial REST API未直接与小工具连接,并且允许任何客户端应用程序查看和发布用户的OpenSocial数据。 我们将使用这些API进行实施。
阿帕奇·辛迪格(Apache Shindig)
Apache Shindig是Gadget API和OpenSocial API的开源实现。 以下是Shindig是什么的简要回顾; Apache Shindig是一个OpenSocial容器,通过提供用于渲染小工具,代理请求以及处理REST和RPC请求的代码,可以帮助您快速开始托管OpenSocial应用。

图2 Apache Shindig [9]
对于OpenSocial,Shindig同时实现了JavaScript和REST API。 图3展示了Shindig OpenSocial实施的总体架构[10] 。

图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还允许其参与者存储和共享位置和位置信息,包括地址,地理位置,可选图片和描述。 Gypsii中的一个特殊位置是当前用户位置。 它还支持POI(兴趣点)和广告。
Gypsii提供了一组API-GyPSii OpenExperience API(OEx) [13] -XML-RPC API,提供对所有Gypsii功能的访问。 XML-RPC的GyPSii变体受XML-RPC规范的启发[14] ,但已针对GyPSii的需求进行了量身定制。 该协议定义了一种客户端-服务器通信模式,双方都使用XML与对方进行通信。 针对开发人员的Gypsii网站提供了有关XML-RPC协议以及使用该协议的Java客户端实现的文档
实作
总体实施架构
我们的实现的总体体系结构如图4所示。我们利用现有的Shindig对OpenSocial REST支持的支持,但是用自定义提供程序替换Shindig提供的默认示例OpenSocial容器,该容器是使用Gypsii适配器实现的。 请注意,该自定义提供商可以连接到任何其他社交网络提供商,例如Facebook,Myspace或Ning。

图4整体解决方案架构
这种方法的优势在于,我们可以有效地将应用程序与社交网络实现/集成方法的细节隔离开来。 客户端API由Shindig提供,并在内部进行控制。 因此,我们的应用程序不依赖于社交网络提供商公开的特定技术和API。 这些详细信息由用于特定网络的适配器封装。
安全
与多个社交网络提供商集成的复杂问题之一是安全性/凭据支持,范围从使用名称/密码的登录支持到OpenID [15]到OAuth [16] 。 我们的实现基于图5所示的简单身份转换模式。

图5身份转换
用户请求Shindig,带有NAVTEQ的用户ID-令牌。 给定社交网络的适配器将此令牌转换为网络所需的凭据。 此转换基于用户存储,其中包含此转换所需的信息,无论是用户名/密码,OpenID,特定密钥还是特定转换所需的其他任何信息。
这使我们可以将NAVTEQ的用户ID用作OpenSocial REST API的标准化路径参数。 根据以下命名约定,它还允许在多个社交网络提供程序之间简单路由请求的实现:
OpenSocialUID = NAVTEQUID @网络
例如,用户ID 123468 @ gypsii意味着访问ID为123468的NAVTEQ用户的Gypsii。这允许构建一个非常简单的网络选择器(图4),该选择器根据提供的用户名将请求路由到适当的社交网络适配器。
覆盖默认的社会服务
由于依赖注入的支持,Shindig的默认社交网络服务的覆盖非常简单,涉及以下主要步骤:
- 实施定制OpenSocial服务 。 定制服务既可以作为单个类(清单1)实现,也可以作为一组类实现,共同实现由Shindig定义的API。
public class CustomOpenSocialService implements ActivityService,
AppDataService, PersonService, MessageService {
…………………………………..
}
清单1.自定义OpenSocial服务
- 创建绑定类。 一旦定制的OpenSocial服务到位(我们假设是一个,清单1),就必须实现一个绑定类(清单2),告诉Shindig调用它来处理请求。
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服务
- 配置Shindig以使用绑定类。 最后,必须让Shindig实现知道此绑定。 这是通过更改web.xml配置(清单3)来完成的。 此配置由
org . apache . shindig .common. servlet .GuiceServletContextListener
处理org . apache . shindig .common. servlet .GuiceServletContextListener
org . apache . shindig .common. servlet .GuiceServletContextListener
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 API
我们需要从浏览器访问我们的OpenSocial REST API。 这里的问题是现代浏览器施加的同源策略限制[17] :
“同源策略禁止从一个域加载的脚本从另一域获取或操纵文档的属性。 即,请求的URL的域必须与当前网页的域相同。 从根本上讲,这意味着浏览器会将来自不同来源的内容隔离开来,以防止内容遭到篡改。”
有几种常见的方法可以克服此限制:
- 使Web页面从/到Web服务器页面的GET / POST数据起源于Web服务器,并使Web服务器充当将请求中继到实际第三方服务器的代理。 尽管已广泛使用,但该技术不可扩展,并且会增加延迟(在出现大量薪水的情况下会增加)以请求处理。
- 使用框架元素在当前网页中创建用于访问任何第三方内容的新区域。 这通常适用于没有返回数据的POST,但由于帧之间对数据的访问有限,因此在GET中很少使用。 同样,在获取帧之后,框架中的内容将受到相同来源策略的限制。
- 实现GET返回JSONP(带有填充的JSON)-封装在函数调用中的JSON数据。 在这种情况下,数据将在脚本中返回。 脚本加载后将执行。 之所以起作用,是因为同源策略不会阻止动态脚本插入,而是将脚本视为从提供网页的域中加载脚本一样。
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代码,以支持从框架中发布。 Shindig分发禁止使用内容类型"application/x-www-form-urlencoded"
因为它会干扰OAuth主体签名。 因为我们没有使用OAuth,并且浏览器POST使用的内容类型是"application/x-www-form-urlencoded"
,所以我们不得不修改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正文处理数据。 一个实现将正文内容添加为附加的名称/值参数。 问题在于,在提交浏览器的情况下,主体是空主体,并且参数的内容将被覆盖。 更改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禁用空响应
样品申请
本文中描述的OpenSocial实现用于构建GeoSpatial OpenSocial混搭,从而允许用户和他的朋友和/或他们最喜欢的位置放置在地图上(图6)。

图6用户和他的朋友在地图上
它还允许计算到用户或/和他最喜欢的地点的路线,并使用社交网络消息传递(图7)将其提交给朋友

图7社交网络消息传递
这种简单的实现只是社交网络和地理空间数据之间许多潜在协同作用中的一种。 基于所描述的将两者结合的方法,可以实现许多其他有趣的混搭。
结论
开放式社交API的激增使得难以构建支持多个现有社交网络的应用程序。 本文提出的解决方案可以通过引入中介提供基于标准的API来“隐藏”与前端实现的差异,从而克服其中的一些挑战。 提供此类API和强大的“可插拔”架构的Apache Shindig的选择极大地简化了总体实施。
致谢
我感谢NAVTEQ的同事们,特别是Stefan Balkowiec和Robert Camp关于实现体系结构的许多讨论,并感谢Andreas Schuetten将我指向JSONP和实现GUI支持。
[1] http://www.opensocial.org/
[2] http://www.thegoodnamesweregone.com/post/83-open-source-web-api-frameworks.aspx
[3] http://incubator.apache.org/shindig/
[4] http://www.gypsii.com/
[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]资料来源: http : //incubator.apache.org/shindig/
[10] Rajdeep Dua。 Shindig的体系结构概述,这是一个OpenSocial参考实现。 http://chrisschalk.com/shindig_docs/rajdeep/shindig-overview/onjava-shindig-overview-tidy.html和Rajdeep Dua。 Shindig-Java版本中的REST实现概述。 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
[14] http://www.xmlrpc.com/
[15] http://openid.net/
[16] http://oauth.net/
[17] SedaÖzses,SalihErgül使用JSONP进行跨域通信,第1部分:结合JSONP和jQuery来快速构建强大的混搭。 http://www.ibm.com/developerworks/library/wa-aj-jsonp1/
[18] http://code.google.com/p/jsonp-java/
翻译自: https://www.infoq.com/articles/social-content-apache-shindig/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1