首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Eclipse 環境下基於 Apache Shindig 構建 OpenSocial 容器:SPI 擴展實戰

     編輯:Java 相關知識

背景:OpenSocial 與 Shindig

OpenSocial:跨社交網站的通用 API 規范,開發者使用 JavaScript/HTML 構建 Gadget,標準化訪問 People、Activities、AppData 等社交數據。

Apache Shindig:OpenSocial 參考實現,核心組件:

  • Gadget Container JS:客戶端 gadgets.js,提供 UI Layout、Security、RPC 通信。
  • Gadget Rendering Server:解析 Gadget XML → HTML/JS/CSS。
  • OpenSocial Container JS:客戶端 opensocial.js,封裝 REST/RPC 數據訪問 API。
  • OpenSocial Data Server:提供 RESTful/RPC 服務端點,核心 Servlet:GadgetRenderingServlet、DataServiceServlet、JsonRpcServlet。

服務端架構基於 Java Servlet Stack,JsonDbOpensocialService 實現 ActivityService/PersonService/AppDataService 接口,默認提供基於 JSON 文件的數據存儲。

Eclipse 開發環境搭建與調試

  1. 安裝 m2eclipse:Eclipse Update Site → https://m2eclipse.sonatype.org/update/。
  2. Checkout 源碼:SVN https://svn.apache.org/repos/asf/incubator/shindig/trunk/。
  3. 導入 Maven Projects:File → Import → Maven Projects,選中源碼根目錄。
  4. Maven Build 配置:Run/Debug Configurations → Maven Build → Base Directory: shindig-server,Goals: jetty:run。
  5. 啟動調試:Debug 運行,Jetty 默認 8080 端口。訪問 https://localhost:8080/gadgets/files/samplecontainer/samplecontainer.html 驗證。

核心擴展:SPI 適配自有社交數據

Shindig 通過 Guice 依賴注入 綁定 SPI 實現。需實現三大服務接口:

  • PersonService:用戶/好友數據
  • ActivityService:動態/活動流
  • AppDataService:應用數據存儲

實現 PersonService:基於 JSON 文件數據源

public class SocialTestJsonPersonService implements PersonService {
    private static final String PEOPLE_TABLE = "people";
    private static final String FRIEND_LINK_TABLE = "friendLinks";
    private JSONObject db; // 從 JSON 文件加載
    private BeanConverter converter;

    @Override
    public Future<RestfulCollection<Person>> getPeople(Set<UserId> userIds, GroupId groupId, 
            CollectionOptions options, Set<String> fields, SecurityToken token) {
        List<Person> result = Lists.newArrayList();
        try {
            JSONArray people = db.getJSONArray(PEOPLE_TABLE);
            Set<String> idSet = getIdSet(userIds, groupId, token);
            
            for (int i = 0; i < people.length(); i++) {
                JSONObject person = people.getJSONObject(i);
                if (!idSet.contains(person.get(Person.Field.ID.toString()))) continue;
                result.add(filterFields(person, fields, Person.class));
            }
            
            int totalSize = result.size();
            return ImmediateFuture.newInstance(new RestfulCollection<>(
                result, options.getFirst(), totalSize, options.getMax()));
        } catch (JSONException je) {
            throw new ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, je.getMessage(), je);
        }
    }

    @Override
    public Future<Person> getPerson(UserId id, Set<String> fields, SecurityToken token) {
        // 單用戶查詢邏輯類似...
    }
    // 其他接口方法...
}

Guice 模塊綁定

public class SampleModule extends SocialApiGuiceModule {
    @Override
    protected void configure() {
        super.configure();
        bind(String.class).annotatedWith(Names.named("shindig.socialtest.json.db"))
            .toInstance("sampledata/socialtestdb.json"); // 數據文件路徑
        
        bind(ActivityService.class).to(JsonDbOpensocialService.class);
        bind(AppDataService.class).to(JsonDbOpensocialService.class);
        // 關鍵:綁定自定義 PersonService 實現
        bind(PersonService.class).to(SocialTestJsonPersonService.class);
        
        bind(MessageService.class).to(JsonDbOpensocialService.class);
        bind(OAuthDataStore.class).to(SampleOAuthDataStore.class);
        requestStaticInjection(SampleRealm.class);
    }
}

客戶端消費:Gadget 與 REST Client

Gadget 實現 (SocialAppTest.xml)

<Module>
  <ModulePrefs title="Social Application Test">
    <Require feature="osapi"/> <Require feature="dynamic-height"/>
  </ModulePrefs>
  <Content type="html"><![CDATA[
    <button onclick="fetchPeople()">Fetch people and friends</button>
    <div><span id="viewer"></span><ul id="friends"></ul></div>
    <script>
      function fetchPeople() {
        var fields = ['id','age','name','gender','profileUrl','thumbnailUrl'];
        var batch = osapi.newBatch();
        batch.add('viewer', osapi.people.getViewer({sortBy:'name', fields:fields}));
        batch.add('viewerFriends', osapi.people.getViewerFriends({sortBy:'name', fields:fields}));
        batch.execute(render);
      }
      function render(data) {
        document.getElementById('viewer').innerHTML = data.viewer.id;
        data.viewerFriends.list.forEach(p => 
            document.getElementById('friends').innerHTML += '<li>' + p.name.formatted + '</li>');
        gadgets.window.adjustHeight();
      }
    </script>
  ]]></Content>
</Module>

部署至 samplecontainer/socialtest.html,瀏覽器訪問測試。

RESTful Java Client

public class SocialAppTest {
    private static final String BASE_URI = "https://localhost:8080/social/rest/";
    private static final String VIEWER_ID = "john.doe";

    public static void main(String[] args) {
        OpenSocialClient client = new OpenSocialClient("SocialAppTest");
        client.setProperty(OpenSocialClient.Property.REST_BASE_URI, BASE_URI);
        client.setProperty(OpenSocialClient.Property.VIEWER_ID, VIEWER_ID);
        
        OpenSocialPerson viewer = client.fetchPerson(VIEWER_ID);
        System.out.println("Viewer: " + viewer.getId());
        for (OpenSocialPerson friend : client.fetchFriends(viewer.getId())) {
            System.out.println("Friend: " + friend.getId());
        }
    }
}

架構總結與選型建議

OpenSocial 平台兩端選擇:

  • 前端:OpenSocial Gadget (Web) 或 REST/RPC 基礎的 RCP 桌面應用。
  • 後端:
    • 方案一(推薦):基於 Shindig 等成熟容器,通過 SPI 擴展適配現有數據源(本文方案)。
    • 方案二:從頭實現 OpenSocial 規范兼容容器(成本極高)。

本文示例完整源碼可參考附件資源鏈接。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved