メモ代わり。てきとーに。 いや、ですからてきとーですって。 2年前ぐらいにPythonあたりでメールくれた方、ごめんなさい。メール紛失してしまい無視した形になってしまいました。。。

2009年7月25日土曜日

[OpenSocial][その他] gooホームでディベロッパー申請承認された

数日かかるものかと思いきや、ほぼ1日で承認いただけた

とりあえず、ほっとしたー。

.

[Apache Shindig][お勉強][OpenSocial] メモ92 shindig開発のメーリングリストのアーカイブ

http://www.mail-archive.com/shindig-dev@incubator.apache.org/

.

[Apache Shindig][お勉強][OpenSocial] メモ91 gadgets.rpc(3)

JavaScript難しい・・。

features/rpc/rpc.jsをざっと眺めてみた。

あっているかどうかしらないけど、多分流れ的にはこんな感じだと思う。

gadgets.rpcの流れ
1) gadgets.rpc.callが呼ばれる
2) gadgets.rpc.callの中で、postMessageなるものを使って別ドメインのiframeな感じのJavaScriptへメッセージ送信
3) rpc_relay.htm内でgadgets.rpc.receiveをコールし、postMessageで送信されたメッセージを受信し、該当コールバックを実行。
メッセージの内容はJSON形式の文字列で、


s: Service Name
f: From
c: The callback ID or 0 if none.
a: The arguments for this RPC call.
t: The authentication token.

といった内容。


gadgets.rpc.receive
postMessageで送られてくるJSONを元に、実際に処理をコールするところっぽい。
やっていることは以下の感じ。
1) securityTokenの値をチェック。だめならException
2) AsyncなRPCのためにcallbackをrpcコンテキストにくっつけておく
3) すでに登録されているサービスを実行
4) コールバックをコール。(gadgets.rpc.call経由で)

多分こんな感じ。
すでに登録されているサービスというのは、
gadgets.rpc.registerメソッドで登録される。
gadgets.rpc.registerの定義は、
  1. register: function(serviceName, handler) {  
  2.    if (serviceName === CALLBACK_NAME) {  
  3.      throw new Error("Cannot overwrite callback service");  
  4.    }  
  5.   
  6.    if (serviceName === DEFAULT_NAME) {  
  7.      throw new Error("Cannot overwrite default service: use registerDefault");  
  8.    }  
  9.   
  10.    services[serviceName] = handler;  
  11.  },  

となっていて、registerがコールされるとservicesメンバ変数(呼び方がわからん!)に
function(上記だとhandlerがそれにあたる)が保存される。




ところで、

って'='が3つ続いているけど、これは何????


--
大体分かったつもりだけど、どうしてrequestSendMessageはgadgets.rpc経由にするんだろう。。
そんな必要ない気がするんだけど。。

requestSendMessage should use the RPC mechanism as similar features do

って言ってる。。

ってことは、親なり別frameなりでダイアログでも表示させるってことかしらー。

--
ははぁーん。

What we want to do is just send a message as soon as a gadget asks us with
the requestSendMessage method.

という投稿発見。
それに対して、

I think, though, the default implementation should really be to send
an RPC to the containing page

だって。

これでRPC経由になったのかしら。
ところで、何て仰っているのかな?


.

[Apache Shindig][お勉強][OpenSocial] メモ90 gadgets.rpc(2)

gadgets.rpcの本体は、


features/rpc/rpc.js

にて定義されている模様。

そんだけ。
.

[その他] 死語ランキング

http://ranking.goo.ne.jp/ranking/999/dead_language/

うそピョーン

.

[その他] 週間新記録

週間POST数新記録。

.

[その他] フレームワーク思考がはやっているんですか?

徘徊しているとフレームワーク思考なるものを見かける。

はやっているのかなー。

みっしーとか7つの習慣とかですかね。



ここは、みっしーとは程遠いブログということぐらいは知っているよ、多分。

.

[Apache Shindig][お勉強][OpenSocial] メモ89 gadgets.rpc(1)

gadgets.rpcを理解していない。。
ので、ちゃんとコード読んでみようっと。

requestSendMessageに限らず、requestNavicateToもgadgets.rpc.callって呼ばれているのよねー。

なんだろかー。

.

[Apache Shindig][お勉強][OpenSocial] メモ88 opensocial.requestSendMessageするガジェット

とりあえず、簡単なガジェットを作成し、Shindig上で実行してみる。

作成したガジェットは以下のとおり。

  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <Module>  
  3.   <ModulePrefs title="opensocial.Message">  
  4.     <Require feature="opensocial-0.8" />  
  5.   </ModulePrefs>  
  6.   <Content type="html" view="home,profile,canvas">  
  7.     <![CDATA[ 
  8.     <script type="text/javascript"> 
  9.       function sendMessage() { 
  10.         var opt_params = []; 
  11.         opt_params[opensocial.Message.Field.TITLE] = 'タイトルだよーん'; 
  12.         opt_params[opensocial.Message.Field.TYPE] = opensocial.Message.Type.PRIVATE_MESSAGE; 
  13.         var body = 'テストメッセージだよーん'; 
  14.         var msg = opensocial.newMessage(body, opt_params); 
  15.         opensocial.requestSendMessage(opensocial.IdSpec.PersonId.OWNER, msg, call_back); 
  16.       } 
  17.  
  18.       function call_back(status) { 
  19.         if (status.hadError()) { 
  20.           document.getElementById('result').innerHTML = '失敗したよ:' + status.getErrorCode(); 
  21.         } else { 
  22.           document.getElementById('result').innerHTML = '送ったよ'; 
  23.         } 
  24.       } 
  25.  
  26.     </script> 
  27.     <div id="result"></div> 
  28.     <input type="button" value="メッセージ送信" onclick="sendMessage();" /> 
  29.  
  30.     ]]>  
  31.   </Content>  
  32. </Module>  


でMy Shindig環境で表示させて、メッセージ送信ボタンを押下してみる。

すると、ありゃ?

opensocial is not defined
(?)()()gadgets.js (line 244)
[Break on this error] opt_callback(new opensocial.ResponseItem(

とFirebugのコンソールに表示されて動かない。

Shindigのサンプル実装では実装されていないのか、
それともMy Shindig環境がおかしいのかは、
これから調べる。

.

[Apache Shindig][お勉強][OpenSocial] メモ87 opensocial.Message

opensocial.Message周りについて実装しながら理解する。

多分友達やある特定ユーザに対してメッセージを送るような仕組みを提供するもの
ではないか、と思う。

さて、
opensocial.Messageそのもののサーバ側の仕組みを作る。
多分普通は既存のSNSなんかを使用するのだろうが、面白くないので、
最初から実装する。

opensocial.Messageの項目

Google Codeのopensocial 0.8.1のドキュメントを見ると、


<static> object BODY
メッセージのメイン テキストです。
<static> object BODY_ID
メッセージ テンプレートとしてのメッセージのメイン テキストです。
<static> object TITLE
メッセージのタイトルです。
<static> object TITLE_ID
メッセージ テンプレートとしてのメッセージのタイトルです。
<static> object TYPE
メッセージのタイトルです。opensocial.Message.Type として指定します。

なんてなふうに書かれている。

opensocial.requestSendMessageのAPIリファレンスなんかを見ながら、
必要そうなテーブルを作ってみた。
  1. CREATE TABLE gms_message (  
  2.   id VARCHAR(64) not null,  
  3.   from_person_id VARCHAR(64) not null,  
  4.   to_person_id VARCHAR(64) not null,  
  5.   body text not null,  
  6.   body_id VARCHAR(256),  
  7.   title text not null,  
  8.   title_id VARCHAR(256),  
  9.   type VARCHAR(32) not null, /* EMAIL, NOTIFICATION, PRIVATE_MESSAGE, PUBLIC_MESSAGE */  
  10.   created_at DATETIME not null,  
  11.   PRIMARY KEY(id)  
  12. )  
  13. TYPE=innoDB;  

良い悪いは別として、大体こんな感じですかねー。

DAOも用意して、準備完了。

さて、次からrequestSendMessageあたりから実装してみる。
Shindigデフォルトで動作するならそのまま。

.

[myminicity] int使い切った?

myminicityに行ってみたら、MySQLのエラー。
思いっきりint使い切ったって感じのエラー。

うーむ。
.

[YUI][JavaScript] やべ

YUI使ってdialog出して見た。

かっこいいー

今さらだけど。


My Shindig コンテナで、requestPermissionのときとか、sendMessageのときとかに
使おうっと。
.

[YUI][JavaScript] YUIダウンロード中

YUIをダウンロード中。

GoogleにしろYahooにしろ、すごいねー。

そんだけ。

--
ダウンロード終わった。
何をすればよいのやら。。
.

[OpenSocial][その他]とりあえず、gooホームでディベロッパー申請してきた

これから審査するんだそうで。

審査きらい。


mixiは、ユーザ名、パスワードともに忘れた・・。
.

[Apache Shindig][お勉強][OpenSocial] メモ86 DataRequest.newFetchActivitiesRequestのサーバ側実装をしてみる(4)

DataRequest.newFetchActivitiesRequestのactivityId指定しない版できた。

まず、GadgetXMLのJavaScriptは、

  1. function getData() {  
  2.   /*===================================================================*/  
  3.   /* OWNER IdSpec                                                      */  
  4.   /*===================================================================*/  
  5.   var idspec = new opensocial.IdSpec();  
  6.   idspec.setField(opensocial.IdSpec.Field.USER_ID,  opensocial.IdSpec.PersonId.OWNER);  
  7.   idspec.setField(opensocial.IdSpec.Field.GROUP_ID, opensocial.IdSpec.GroupId.SELF);  
  8.   /*===================================================================*/  
  9.   /* DataRequestオブジェクトを作成し、                                 */  
  10.   /* そこにActivity取得リクエストを追加.                               */  
  11.   /* レスポンスを取得するときのキーは'get_activity'                    */  
  12.   /*===================================================================*/  
  13.   
  14.   var req = opensocial.newDataRequest();  
  15.   req.add(req.newFetchActivitiesRequest(idspec), 'get_activity');  
  16.   /*===================================================================*/  
  17.   /* 送信!                                                             */  
  18.   /*===================================================================*/  
  19.   req.send(function(response){  
  20.     var activity = response.get('get_activity')  
  21.     if (activity.hadError()) {  
  22.       document.getElementById('result').innerHTML  
  23.         = 'エラーだったよ:' + activity.getErrorMessage();  
  24.       return;  
  25.     }  
  26.     var out = document.createElement('ul');  
  27.     if (activity.getData().size() == 0) {  
  28.       var li = document.createElement('li');  
  29.       li.innerHTML = 'Activity0件';  
  30.       out.appendChild(li);  
  31.     }  
  32.     activity.getData().each(function(act) {  
  33.       var li = document.createElement('li');  
  34.       var title = act.getField(opensocial.Activity.Field.TITLE);  
  35.       var media = act.getField(opensocial.Activity.Field.MEDIA_ITEMS);  
  36.       if (media != undefined) {  
  37.         for (var ii=0, len = media.length; ii < len; ii++) {  
  38.           if (media[ii].getField(opensocial.MediaItem.Field.TYPE) == opensocial.MediaItem.Type.IMAGE) {  
  39.             title +=  
  40.                 '  
  41. g src="'  
  42.               + media[ii].getField(opensocial.MediaItem.Field.URL);  
  43.               + '/>';  
  44.           }  
  45.         }  
  46.       }  
  47.       li.innerHTML = title;  
  48.       out.appendChild(li);  
  49.     });  
  50.   
  51.     document.getElementById('result').appendChild(out);  
  52.   
  53.   });  
  54. }  

と、こんな感じ。
newFetchActivitiesRequestの第二引数はactivityIdを指定しないので、必要なし。

で、このリクエストに対応するサーバ側の処理は、
まずAcitivityServiceのjava、
  1. public Future<RestfulCollection<Activity>> getActivities(Set<UserId> userIds,  
  2.     GroupId groupId, String appId, Set<String> fields, CollectionOptions options, SecurityToken token)  
  3.     throws ProtocolException {  
  4.   return ImmediateFuture.newInstance(  
  5.     new RestfulCollection<Activity>(logic.getActivities(userIds,groupId,appId,fields,options,token)));  
  6.   
  7. }  

な感じ。
とりあえずパラメータはそのままlogicの方に渡す。
で、そのlogic。
  1. public List<Activity> getActivities(Set<UserId> userIds, GroupId groupId, String appId, Set<String> fields, CollectionOptions options, SecurityToken token)  
  2.     throws ProtocolException {  
  3.   GmsPerson viewer = getUser();  
  4.   List<Activity> activities = new ArrayList<Activity>();  
  5.   logger.info("アクティビティ取得(userId複数指定)開始:");  
  6.   if (viewer == null) {  
  7.     if (logger.isDebugEnabled()) {  
  8.       logger.debug("Guestは取得できない:");  
  9.     }  
  10.     throw new ProtocolException(HttpServletResponse.SC_FORBIDDEN,  
  11.       "Guestのため、取得できない:loginid:[" + viewer.getLoginId() + "]");  
  12.   }  
  13.   if (! isInstalledGadget(viewer, token.getModuleId())) {  
  14.     if (logger.isDebugEnabled()) {  
  15.       logger.debug("ガジェット未インストール:"  
  16.         + "viewerId:[" + viewer.getLoginId() + "]:"  
  17.         + "ガジェットID:[" + token.getModuleId() + "]"  
  18.       );  
  19.     }  
  20.     throw new ProtocolException(HttpServletResponse.SC_FORBIDDEN,  
  21.       "未インストールのため、取得できない:loginid:[" + viewer.getLoginId() + "]");  
  22.   }  
  23.   List<String> userLoginIds = getUserList(userIds, token);  
  24.   ExtendedGmsPerson[] gmsPersons = null;  
  25.   List<String> personIds = new ArrayList<String>();  
  26.   int first = 0;  
  27.   int max   = 20;  
  28.   for (;;) {  
  29.     gmsPersons = gmsPersonDao.peopleGet(  
  30.       userLoginIds,  
  31.       groupId.getType().toString(),  
  32.       groupId.getGroupId(),  
  33.       null,  
  34.       null,  
  35.       null,  
  36.       null,  
  37.       null,  
  38.       first,  
  39.       max,  
  40.       null);  
  41.     if (logger.isDebugEnabled()) {  
  42.       logger.debug("count:[" + gmsPersons.length + "]");  
  43.     }  
  44.     int length = gmsPersons.length;  
  45.     for (int ii=0; ii<length; ii++) {  
  46.       personIds.add(gmsPersons[ii].getId());  
  47.     }  
  48.     if (length < max) {  
  49.       break;  
  50.     }  
  51.     first += max;  
  52.   }  
  53.   ExtendedGmsActivity[] gmsActivities = gmsActivityDao.selectByGmsPersonIds(personIds, options.getFirst(), options.getMax());  
  54.   for (int ii=0,len = gmsActivities.length; ii<len; ii++) {  
  55.     activities.add(mapToActivity(gmsActivities[ii], gmsActivities[ii].getGmsPerson().getLoginId()));  
  56.   }  
  57.   logger.info("アクティビティ取得(userId複数指定)終了:");  
  58.   return activities;  
  59. }  

大したことはしていない。
まずパーミッションをチェックして、
userIdとgroupIdからUserIdのリストを取得と。
そしたら、そのUserIdとひもづくactivityを取得する。

そんだけ。

.

2009年7月24日金曜日

[Apache Shindig][お勉強][OpenSocial] メモ85 DataRequest.newFetchActivitiesRequestのサーバ側実装をしてみる(3)

複数件activityIdを指定したnewFetchActivitiesRequestできた。

GadgetXMLで、表示のところのJavaScriptで間違えがあったので、ちょっとだけ修正。
修正したJavaScriptは、以下な感じ。

  1.       function getTwoData() {  
  2.         /*===================================================================*/  
  3.         /* OWNER IdSpec                                                      */  
  4.         /*===================================================================*/  
  5.         var idspec = new opensocial.IdSpec();  
  6.         idspec.setField(opensocial.IdSpec.Field.USER_ID,  opensocial.IdSpec.PersonId.OWNER);  
  7.         idspec.setField(opensocial.IdSpec.Field.GROUP_ID, opensocial.IdSpec.GroupId.SELF);  
  8.         var params = {};  
  9.         params['activityId'] = [  
  10.           'tjt0tt30k9f0hje01eu0emq0zr505bd0',  
  11.           'w920slc0ze20nmf0diq0cnk0mj71kbz0'  
  12.         ];  
  13.   
  14.         /*===================================================================*/  
  15.         /* DataRequestオブジェクトを作成し、                                 */  
  16.         /* そこにActivity取得リクエストを追加.                               */  
  17.         /* レスポンスを取得するときのキーは'get_activity'                    */  
  18.         /*===================================================================*/  
  19.   
  20.         var req = opensocial.newDataRequest();  
  21.         req.add(req.newFetchActivitiesRequest(idspec, params), 'get_activity');  
  22.         /*===================================================================*/  
  23.         /* 送信!                                                             */  
  24.         /*===================================================================*/  
  25.         req.send(function(response){  
  26.           var activity = response.get('get_activity')  
  27.           if (activity.hadError()) {  
  28.             document.getElementById('result').innerHTML  
  29.               = 'エラーだったよ:' + activity.getErrorMessage();  
  30.             return;  
  31.           }  
  32.           var out = document.createElement('ul');  
  33.           if (activity.getData().size() == 0) {  
  34.             var li = document.createElement('li');  
  35.             li.innerHTML = 'Activity0件';  
  36.             out.appendChild(li);  
  37.           }  
  38.           activity.getData().each(function(act) {  
  39.             var li = document.createElement('li');  
  40.             var title = act.getField(opensocial.Activity.Field.TITLE);  
  41.             var media = act.getField(opensocial.Activity.Field.MEDIA_ITEMS);  
  42.             if (media != undefined) {  
  43.               for (var ii=0, len = media.length; ii < len; ii++) {  
  44.                 if (media[ii].getField(opensocial.MediaItem.Field.TYPE) == opensocial.MediaItem.Type.IMAGE) {  
  45.                   title +=  
  46.                       '  
  47. <img src="'  
  48.                     + media[ii].getField(opensocial.MediaItem.Field.URL);  
  49.                     + '/>';  
  50.                 }  
  51.               }  
  52.             }  
  53.             li.innerHTML = title;  
  54.             out.appendChild(li);  
  55.           });  
  56.   
  57.           document.getElementById('result').appendChild(out);  
  58.   
  59.         });  
  60.       }  


で、jsonrpccontainer.jsは修正済みのものを使用っと。

で、メインのサーバ側ActivityServiceのgetActivities(activityId複数版)。
  1. public Future<RestfulCollection<Activity>> getActivities(UserId userId, GroupId groupId,  
  2.     String appId, Set<String> fields, CollectionOptions options, Set<String> activityIds, SecurityToken token)  
  3.     throws ProtocolException {  
  4.   if (logger.isDebugEnabled()) {  
  5.     logger.debug("userId:[" + userId + "]:");  
  6.     logger.debug("groupId:[" + groupId + "]:");  
  7.     logger.debug("appId:[" + appId + "]:");  
  8.     for (String field: fields) {  
  9.       logger.debug("field:[" + field + "]");  
  10.     }  
  11.     logger.debug("options:sortBy:[" + options.getSortBy() + "]");  
  12.     logger.debug("options:sortOrder:[" + options.getSortOrder() + "]");  
  13.     logger.debug("options:filter:[" + options.getFilter() + "]");  
  14.     logger.debug("options:filterOperation:[" + options.getFilterOperation() + "]");  
  15.     logger.debug("options:filterValue:[" + options.getFilterValue() + "]");  
  16.     logger.debug("options:first:[" + options.getFirst() + "]");  
  17.     logger.debug("options:max:[" + options.getMax() + "]");  
  18.     logger.debug("options:updatedSince:[" + options.getUpdatedSince() + "]");  
  19.     for (String activityId: activityIds) {  
  20.       logger.debug("activityId:[" + activityId + "]");  
  21.     }  
  22.   }  
  23.   return ImmediateFuture.newInstance(  
  24.     new RestfulCollection<Activity>(  
  25.       logic.getActivities(userId, activityIds, token, options.getFirst(), options.getMax())));  
  26. }  

logic.getActivitiesは、userIdとactivityIdsをキーにテーブルからデータを持ってくるだけ。


そんだけ。

次は最後のactivityIds指定なし版のnewFetchActivitiesRequestのサーバ側実装をやってみる。

.

[Apache Shindig][お勉強][OpenSocial] メモ84 DataRequest.newFetchActivitiesRequestのサーバ側実装をしてみる(3)

次は、複数activityId指定で、newFetchActivitiesRequestしてみる。

対応するActivityServiceのメソッドは、

  1. public Future<RestfulCollection<Activity>> getActivities(UserId userId, GroupId groupId,  
  2.     String appId, Set<String> fields, CollectionOptions options, Set<String> activityIds, SecurityToken token)  
  3.     throws ProtocolException;  

これ。

GadgetのXMLに書くJavaScriptは、
  1. function getTwoData() {  
  2.   /*===================================================================*/  
  3.   /* OWNER IdSpec                                                      */  
  4.   /*===================================================================*/  
  5.   var idspec = new opensocial.IdSpec();  
  6.   idspec.setField(opensocial.IdSpec.Field.USER_ID,  opensocial.IdSpec.PersonId.OWNER);  
  7.   idspec.setField(opensocial.IdSpec.Field.GROUP_ID, opensocial.IdSpec.GroupId.SELF);  
  8.   var params = {};  
  9.   params['activityId'] = [  
  10.     'tjt0tt30k9f0hje01eu0emq0zr505bd0',  
  11.     'w920slc0ze20nmf0diq0cnk0mj71kbz0'  
  12.   ];  
  13.   
  14.   /*===================================================================*/  
  15.   /* DataRequestオブジェクトを作成し、                                 */  
  16.   /* そこにActivity取得リクエストを追加.                               */  
  17.   /* レスポンスを取得するときのキーは'get_activity'                    */  
  18.   /*===================================================================*/  
  19.   
  20.   var req = opensocial.newDataRequest();  
  21.   req.add(req.newFetchActivitiesRequest(idspec, params), 'get_activity');  
  22.   /*===================================================================*/  
  23.   /* 送信!                                                             */  
  24.   /*===================================================================*/  
  25.   req.send(function(response){  
  26.     var activity = response.get('get_activity')  
  27.     if (activity.hadError()) {  
  28.       document.getElementById('result').innerHTML  
  29.         = 'エラーだったよ:' + activity.getErrorMessage();  
  30.       return;  
  31.     }  
  32.     var out = document.createElement('ul');  
  33.     var li = document.createElement('li');  
  34.     if (activity.getData().size() == 0) {  
  35.       li.innerHTML = 'Activity0件';  
  36.       out.appendChild(li);  
  37.     }  
  38.     activity.getData().each(function(act) {  
  39.       var title = act.getField(opensocial.Activity.Field.TITLE);  
  40.       var media = act.getField(opensocial.Activity.Field.MEDIA_ITEMS);  
  41.       if (media != undefined) {  
  42.         for (var ii=0, len = media.length; ii < len; ii++) {  
  43.           if (media[ii].getField(opensocial.MediaItem.Field.TYPE) == opensocial.MediaItem.Type.IMAGE) {  
  44.             title +=  
  45.                 '  
  46. g src="'  
  47.               + media[ii].getField(opensocial.MediaItem.Field.URL);  
  48.               + '/>';  
  49.           }  
  50.         }  
  51.       }  
  52.       li.innerHTML = title;  
  53.       out.appendChild(li);  
  54.     });  
  55.   
  56.     document.getElementById('result').appendChild(out);  
  57.   
  58.   });  
  59. }  

こんな感じ。

params['activityId'] に今度は配列(というんですかね?)をセット。
jsonrpccontainer.jsのnewFetchActivitiesRequestもいい感じに修正し、
リクエストしてみると、、、

ちゃんとサーバ側に全てのパラメータがわたる。

わたっている様はこんな感じ。

[リクエスト:[20481986]] 2009-07-24 22:35:34,328 DEBUG jp.qsdn.gms.social.service.ActivityServiceImpl - userId:[OWNER]:
[リクエスト:[20481986]] 2009-07-24 22:35:34,328 DEBUG jp.qsdn.gms.social.service.ActivityServiceImpl - groupId:[SELF]:
[リクエスト:[20481986]] 2009-07-24 22:35:34,328 DEBUG jp.qsdn.gms.social.service.ActivityServiceImpl - appId:[http://localhost/opensocial/hello.xml]:
[リクエスト:[20481986]] 2009-07-24 22:35:34,328 DEBUG jp.qsdn.gms.social.service.ActivityServiceImpl - options:sortBy:[topFriends]
[リクエスト:[20481986]] 2009-07-24 22:35:34,328 DEBUG jp.qsdn.gms.social.service.ActivityServiceImpl - options:sortOrder:[ascending]
[リクエスト:[20481986]] 2009-07-24 22:35:34,328 DEBUG jp.qsdn.gms.social.service.ActivityServiceImpl - options:filter:[null]
[リクエスト:[20481986]] 2009-07-24 22:35:34,328 DEBUG jp.qsdn.gms.social.service.ActivityServiceImpl - options:filterOperation:[contains]
[リクエスト:[20481986]] 2009-07-24 22:35:34,328 DEBUG jp.qsdn.gms.social.service.ActivityServiceImpl - options:filterValue:[]
[リクエスト:[20481986]] 2009-07-24 22:35:34,328 DEBUG jp.qsdn.gms.social.service.ActivityServiceImpl - options:first:[0]
[リクエスト:[20481986]] 2009-07-24 22:35:34,328 DEBUG jp.qsdn.gms.social.service.ActivityServiceImpl - options:max:[20]
[リクエスト:[20481986]] 2009-07-24 22:35:34,328 DEBUG jp.qsdn.gms.social.service.ActivityServiceImpl - options:updatedSince:[null]
[リクエスト:[20481986]] 2009-07-24 22:35:34,329 DEBUG jp.qsdn.gms.social.service.ActivityServiceImpl - activityId:[tjt0tt30k9f0hje01eu0emq0zr505bd0]
[リクエスト:[20481986]] 2009-07-24 22:35:34,329 DEBUG jp.qsdn.gms.social.service.ActivityServiceImpl - activityId:[w920slc0ze20nmf0diq0cnk0mj71kbz0]



ということで、次はgetActivitiesの中身を実装する。

.

[Apache Shindig][お勉強][OpenSocial] メモ83 DataRequest.newFetchActivitiesRequestのサーバ側実装をしてみる(2)

jsonrpccontainer.jsを元にコンテナを実装した場合、activityIdが渡せないので、

  1. JsonRpcContainer.prototype.newFetchActivitiesRequest = function(idSpec,    
  2.     opt_params) {    
  3.   var rpc = { method : "activities.get" };    
  4.   rpc.params = this.translateIdSpec(idSpec);    
  5.   rpc.params.appId = "@app";    
  6.   /* ここから */    
  7.   if (opt_params['activityId']) {    
  8.     rpc.params.activityId = opt_params['activityId'];    
  9.   }    
  10.   /* ここまで */    
  11.   FieldTranslations.translateStandardArguments(opt_params, rpc.params);    
  12.   FieldTranslations.translateNetworkDistance(idSpec, rpc.params);    
  13.     
  14.   return new JsonRpcRequestItem(rpc,    
  15.       function(rawJson) {    
  16.         /* さらに修正 ここから */    
  17.         if (rawJson['list']) {    
  18.           rawJson = rawJson['list'];    
  19.         }    
  20.         else {    
  21.           rawJson = [rawJson];    
  22.         }    
  23.         /* さらに修正 ここまで */    
  24.         var activities = [];    
  25.         for (var i = 0; i < rawJson.length; i++) {    
  26.           activities.push(new JsonActivity(rawJson[i]));    
  27.         }    
  28.         return new opensocial.Collection(activities);    
  29.       });    
  30. };    


とした。

んで、ActivityServiceの方は、
  1. public Future<Activity> getActivity(UserId userId, GroupId groupId, String appId,  
  2.     Set<String> fields, String activityId, SecurityToken token)  
  3.     throws ProtocolException {  
  4.   if (logger.isDebugEnabled()) {  
  5.     logger.debug("userId:[" + userId + "]:");  
  6.     logger.debug("groupId:[" + groupId + "]:");  
  7.     logger.debug("appId:[" + appId + "]:");  
  8.     for (String field: fields) {  
  9.       logger.debug("field:[" + field + "]");  
  10.     }  
  11.     logger.debug("activityId:[" + activityId + "]");  
  12.   }  
  13.   Activity activity = logic.getActivity(userId, activityId, token);  
  14.   if ( activity != null  ) {  
  15.     return ImmediateFuture.newInstance(activity);  
  16.   }  
  17.   throw new ProtocolException(HttpServletResponse.SC_BAD_REQUEST,"Cant find activity");  
  18. }  

とした。
logic.getActivityは、ただ単にテーブルからactivityをひっぱってくるだけ。

一応これでactivityId指定の1件取得newFetchActivitiesRequest完成。

次はgetActivities()の2つ。
.

[Apache Shindig][お勉強][OpenSocial] メモ82 DataRequest.newFetchActivitiesRequest時のopt_paramに指定できるもの

newFetchActivitiesRequest発行時のサーバ側の処理は3のメソッドにそれぞれ分岐するところ
までは分かった。

さて、分岐させるべく、Gadget側でいろいろなパラメータでnewFetchActivitiesRequestをコールしてみたいよ。

OpenSocial0.8.1のAPIリファレンスを見ると、


Object newFetchActivitiesRequest(idSpec, opt_params)
サーバーからアクティビティ ストリームを要求するアイテムを作成します。

と書いてある。
他のnewFetchシリーズと同様に第二引数でいろいろと指定できそう。

このopt_paramsに何を指定することができるかってーと、

features/opensocial-reference/datarequest.js

を見てみると書いてあった。
  1. opensocial.DataRequest.ActivityRequestFields = {  
  2.   /** 
  3.    * {String} If provided will filter all activities by this app Id. 
  4.    * @private - at the moment you can only request activities for your own app 
  5.    */  
  6.   APP_ID : 'appId',  
  7.   /** 
  8.    * When paginating, the index of the first item to fetch. 
  9.    * Specified as a <code>Number</code>. 
  10.    * 
  11.    * @member opensocial.DataRequest.ActivityRequestFields 
  12.    */  
  13.   FIRST : 'first',  
  14.   
  15.   /** 
  16.    * The maximum number of items to fetch; defaults to 20. If set to a larger 
  17.    * number, a container may honor the request, or may limit the number to a 
  18.    * container-specified limit of at least 20. 
  19.    * Specified as a <code>Number</code>. 
  20.    * 
  21.    * @member opensocial.DataRequest.ActivityRequestFields 
  22.    */  
  23.   MAX : 'max'  
  24. };  

だって。
maxとfirstとappIdと。


maxとfirstは指定しないと、firstには0が、maxには20がセットされる。
  1. opensocial.DataRequest.prototype.newFetchActivitiesRequest = function(idSpec,  
  2.     opt_params) {  
  3.   opt_params = opt_params || {};  
  4.   
  5.   var fields = opensocial.DataRequest.ActivityRequestFields;  
  6.   
  7.   this.addDefaultParam(opt_params, fields.FIRST, 0);  
  8.   this.addDefaultParam(opt_params, fields.MAX, 20);  
  9.   
  10.   return opensocial.Container.get().newFetchActivitiesRequest(idSpec,  
  11.       opt_params);  
  12. };  

ほら。


あれ?activityIdは???
activityIdはjsonrpccontainer.jsの方で指定しているのか、
と思いきや、書いてない。。
どうも、Shindigのサンプル実装だとactivityIdは渡せないっぽい。

ということで、features/opensocial-jsonrpc/jsonrpccontainer..jsを修正。
  1. JsonRpcContainer.prototype.newFetchActivitiesRequest = function(idSpec,  
  2.     opt_params) {  
  3.   var rpc = { method : "activities.get" };  
  4.   rpc.params = this.translateIdSpec(idSpec);  
  5.   rpc.params.appId = "@app";  
  6.   FieldTranslations.translateStandardArguments(opt_params, rpc.params);  
  7.   FieldTranslations.translateNetworkDistance(idSpec, rpc.params);  
  8.   
  9.   return new JsonRpcRequestItem(rpc,  
  10.       function(rawJson) {  
  11.         rawJson = rawJson['list'];  
  12.         var activities = [];  
  13.         for (var i = 0; i < rawJson.length; i++) {  
  14.           activities.push(new JsonActivity(rawJson[i]));  
  15.         }  
  16.         return new opensocial.Collection(activities);  
  17.       });  
  18. };  


  1. JsonRpcContainer.prototype.newFetchActivitiesRequest = function(idSpec,  
  2.     opt_params) {  
  3.   var rpc = { method : "activities.get" };  
  4.   rpc.params = this.translateIdSpec(idSpec);  
  5.   rpc.params.appId = "@app";  
  6.   /* ここから */  
  7.   if (opt_params['activityId']) {  
  8.     rpc.params.activityId = opt_params['activityId'];  
  9.   }  
  10.   /* ここまで */  
  11.   FieldTranslations.translateStandardArguments(opt_params, rpc.params);  
  12.   FieldTranslations.translateNetworkDistance(idSpec, rpc.params);  
  13.   
  14.   return new JsonRpcRequestItem(rpc,  
  15.       function(rawJson) {  
  16.         /* さらに修正 ここから */  
  17.         if (rawJson['list']) {  
  18.           rawJson = rawJson['list'];  
  19.         }  
  20.         else {  
  21.           rawJson = [rawJson];  
  22.         }  
  23.         /* さらに修正 ここまで */  
  24.         var activities = [];  
  25.         for (var i = 0; i < rawJson.length; i++) {  
  26.           activities.push(new JsonActivity(rawJson[i]));  
  27.         }  
  28.         return new opensocial.Collection(activities);  
  29.       });  
  30. };  

に修正。


さらに、GadgetXMLも
  1. var idspec = new opensocial.IdSpec();  
  2. idspec.setField(opensocial.IdSpec.Field.USER_ID,  opensocial.IdSpec.PersonId.OWNER);  
  3. idspec.setField(opensocial.IdSpec.Field.GROUP_ID, opensocial.IdSpec.GroupId.SELF);  
  4. var params = {};  
  5. params['activityId'] = '6ng0wgx0lgb0nxc0t9t02ur03bw0hcx0';  
  6.   
  7. /*===================================================================*/  
  8. /* DataRequestオブジェクトを作成し、                                 */  
  9. /* そこにActivity取得リクエストを追加.                               */  
  10. /* レスポンスを取得するときのキーは'get_activity'                    */  
  11. /*===================================================================*/  
  12.   
  13. var req = opensocial.newDataRequest();  
  14. req.add(req.newFetchActivitiesRequest(idspec, params), 'get_activity');  

として実行。

ばっちりサーバ側でactivityIdを受け取れる。
これでサーバ側3処理にそれぞれ処理を投げることができるようになった。

あと、サーバ側インタフェースを見ると、fieldsも指定できるようなシグネチャに
なっているんだけど、今のところ意図的に無視している。
なので、そこはそのまま。

さて、つづき。

--
レスポンスが返ってくるようになったけど、JsonRpcRequestItemの第二引数が
だめだったので追加で修正。

--
結局のところ、newFetchActivitiesRequestの第二引数で指定できるものは、
Shindigそのままのjsonrpccontainer.jsのサンプル実装だと、
* appId
* max
* first
サーバ側のサンプル実装だと、上記に加えて
* activityId
ぐらいか。な?




.

[Apache Shindig][お勉強][OpenSocial] メモ81 DataRequest.newFetchActivitiesRequestのサーバ側実装をしてみる(1)

DataRequest.newFetchActivitiesRequestに対応するサーバ側処理を実装してみる。

ActivityServiceインタフェースに定義されているメソッドを実装すればよいんだけど、
newFetchActivitiesRequestに対応しそうなメソッドが3つある。

  1. Future<Activity> getActivity(UserId userId, GroupId groupId, String appId,  
  2.       Set<String> fields, String activityId, SecurityToken token)  
  3.       throws ProtocolException;  
  4. Future<RestfulCollection<Activity>> getActivities(UserId userId, GroupId groupId,  
  5.       String appId, Set<String> fields, CollectionOptions options, Set<String> activityIds, SecurityToken token)  
  6.       throws ProtocolException;  
  7. Future<RestfulCollection<Activity>> getActivities(Set<UserId> userIds,  
  8.       GroupId groupId, String appId, Set<String> fields, CollectionOptions options, SecurityToken token)  
  9.       throws ProtocolException;  

の3つ。

最初のgetActivityはactivityId指定の一件のActivityを取得するメソッドっぽい。
で、次の2つは、、UserIdが複数指定できるどうかとactivityIdsが指定できるかどうか。

newFetchActivitiesRequestでのJSON-RPC自体にいろいろなバリエーションがありそう。
ということで、

./java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ActivityHandler.java

のActivityHandler.javaを見てみる。

  1. @Operation(httpMethods="GET")  
  2.  public Future<?> get(SocialRequestItem request)  
  3.      throws ProtocolException {  
  4.    Set<UserId> userIds = request.getUsers();  
  5.    Set<String> optionalActivityIds = ImmutableSet.copyOf(request.getListParameter("activityId"));  
  6.   
  7.    CollectionOptions options = new CollectionOptions(request);  
  8.   
  9.    // Preconditions  
  10.    HandlerPreconditions.requireNotEmpty(userIds, "No userId specified");  
  11.    if (userIds.size() > 1 && !optionalActivityIds.isEmpty()) {  
  12.      throw new IllegalArgumentException("Cannot fetch same activityIds for multiple userIds");  
  13.    }  
  14.   
  15.    if (!optionalActivityIds.isEmpty()) {  
  16.      if (optionalActivityIds.size() == 1) {  
  17.        return service.getActivity(userIds.iterator().next(), request.getGroup(),  
  18.            request.getAppId(), request.getFields(), optionalActivityIds.iterator().next(),  
  19.            request.getToken());  
  20.      } else {  
  21.        return service.getActivities(userIds.iterator().next(), request.getGroup(),  
  22.            request.getAppId(), request.getFields(), options, optionalActivityIds, request.getToken());  
  23.      }  
  24.    }  
  25.   
  26.    return service.getActivities(userIds, request.getGroup(),  
  27.        request.getAppId(),  
  28.        // TODO: add pagination and sorting support  
  29.        // getSortBy(params), getFilterBy(params), getStartIndex(params), getCount(params),  
  30.        request.getFields(), options, request.getToken());  
  31.  }  

activityIdパラメータがあるかどうか、あるなら1件かどうかで処理が分岐するらしい。

最後のreturn文にTODO:と書いてあるのが気になるが、、
これで実装できる、かな。

.

[Apache Shindig][お勉強][OpenSocial] メモ80 opensocial.requestCreateActivityのサーバ側実装をしてみる(3)

とりあえず、requestCreateActivityに対応するサーバ側処理完成。

くそソースだけど以下な感じ。

  1. public void createActivity(UserId userId, GroupId groupId, String appId,  
  2.     Set<String> fields, Activity activity, SecurityToken token) throws ProtocolException {  
  3.   GmsPerson viewer = getUser();  
  4.   logger.info("createActivity開始"  
  5.     + "viewer:[" + ((viewer == null) ? "Guest" : viewer.getLoginId()) + "]:"  
  6.     + "userId:[" + userId + "]:"  
  7.     + "appId:[" + appId + "]:"  
  8.   );  
  9.   if (viewer == null) {  
  10.     logger.info("createActivity終了:Guestは保存できない:"  
  11.       + "viewer:[" + ((viewer == null) ? "Guest" : viewer.getLoginId()) + "]:"  
  12.       + "userId:[" + userId + "]:"  
  13.       + "appId:[" + appId + "]:"  
  14.     );  
  15.     throw new ProtocolException(HttpServletResponse.SC_FORBIDDEN,  
  16.       "Guestは保存できない: 未ログイン");  
  17.   }  
  18.   
  19.   String loginId = userId.getUserId(token);  
  20.   if ("Guest".equalsIgnoreCase(loginId)) {  
  21.     logger.info("createActivity終了:Guestは保存できない:"  
  22.       + "viewer:[" + ((viewer == null) ? "Guest" : viewer.getLoginId()) + "]:"  
  23.       + "userId:[" + userId + "]:"  
  24.       + "appId:[" + appId + "]:"  
  25.     );  
  26.     throw new ProtocolException(HttpServletResponse.SC_FORBIDDEN,  
  27.       "Guestは保存できない: loginid:[" + loginId + "]");  
  28.   }  
  29.   List<String> userLoginIds = new ArrayList<String>();  
  30.   userLoginIds.add(loginId);  
  31.   GmsPerson[] gmsPersons = gmsPersonDao.peopleGet(  
  32.     userLoginIds,  
  33.     "self",  
  34.     null,  
  35.     null,  
  36.     null,  
  37.     null,  
  38.     null,  
  39.     null,  
  40.     0,  
  41.     1,  
  42.     null);  
  43.   if (gmsPersons.length == 0) {  
  44.     logger.info("createActivity終了:該当Personなし:"  
  45.       + "viewer:[" + ((viewer == null) ? "Guest" : viewer.getLoginId()) + "]:"  
  46.       + "userId:[" + userId + "]:"  
  47.       + "appId:[" + appId + "]:"  
  48.     );  
  49.     throw new ProtocolException(HttpServletResponse.SC_FORBIDDEN,  
  50.       "Guestは保存できない: loginid:[" + loginId + "]");  
  51.   }  
  52.   gmsPersonDao.select(gmsPersons[0].getId(), true);  
  53.   GmsActivity target = mapToGmsActivity(gmsPersons[0], appId, activity, token);  
  54.   if (supportsField(token.getContainer(), "activity", Activity.Field.MEDIA_ITEMS.toString()) && activity.getMediaItems() != null) {  
  55.     for(MediaItem m : activity.getMediaItems()) {  
  56.       GmsMediaItem gmsMediaItem = new GmsMediaItem();  
  57.       gmsMediaItem.setId(UuidGenerator.compress(UuidGenerator.generate()));  
  58.       gmsMediaItem.setActivityId(target.getId());  
  59.       if (supportsField(token.getContainer(), "mediaItem", MediaItem.Field.MIME_TYPE.toString()) && m.getMimeType() != null) {  
  60.         gmsMediaItem.setMimeType(m.getMimeType());  
  61.       }  
  62.       else {  
  63.         gmsMediaItem.setMimeType("");  
  64.       }  
  65.       if (supportsField(token.getContainer(), "mediaItem", MediaItem.Field.TYPE.toString()) && m.getType() != null  
  66.           && (MediaItem.Type.AUDIO == m.getType() || MediaItem.Type.IMAGE == m.getType() || MediaItem.Type.VIDEO == m.getType())) {  
  67.         gmsMediaItem.setMediaType(m.getType().toString());  
  68.       }  
  69.       else {  
  70.         gmsMediaItem.setMediaType("");  
  71.       }  
  72.       if (supportsField(token.getContainer(), "mediaItem", MediaItem.Field.URL.toString()) && m.getUrl() != null) {  
  73.         gmsMediaItem.setUrl(m.getUrl());  
  74.       }  
  75.       else {  
  76.         gmsMediaItem.setUrl("");  
  77.       }  
  78.       gmsMediaItem.setAlbumId("");  
  79.       Timestamp now = new Timestamp(System.currentTimeMillis());  
  80.       gmsMediaItem.setCreated(now);  
  81.       gmsMediaItem.setDescription("");  
  82.       gmsMediaItem.setDuration(-1);  
  83.       gmsMediaItem.setFileSize(-1);  
  84.       gmsMediaItem.setLanguage("");  
  85.       gmsMediaItem.setLastUpdated(now);  
  86.       gmsMediaItem.setLocationId("");  
  87.       gmsMediaItem.setNumComments(0);  
  88.       gmsMediaItem.setNumViews(0);  
  89.       gmsMediaItem.setNumVotes(0);  
  90.       gmsMediaItem.setRating(0);  
  91.       gmsMediaItem.setStartTime(now);  
  92.       gmsMediaItem.setTaggedPeople("");  
  93.       gmsMediaItem.setTags("");  
  94.       gmsMediaItem.setThumbnailUrl("");  
  95.       gmsMediaItem.setTitle("");  
  96.       gmsMediaItemDao.insert(gmsMediaItem);  
  97.     }  
  98.   }  
  99.   gmsActivityDao.insert(target);  
  100.   logger.info("createActivity終了"  
  101.     + "viewer:[" + ((viewer == null) ? "Guest" : viewer.getLoginId()) + "]:"  
  102.     + "userId:[" + userId + "]:"  
  103.     + "appId:[" + appId + "]:"  
  104.   );  
  105. }  
  106. public GmsActivity mapToGmsActivity(GmsPerson person, String appId, Activity activity, SecurityToken token) {  
  107.   GmsActivity result = new GmsActivity();  
  108.   result.setId(UuidGenerator.compress(UuidGenerator.generate()));  
  109.   result.setGmsPersonId(person.getId());  
  110.   result.setAppId(appId);  
  111.   String appHashId = toStringDigest(getStringDigest(appId));  
  112.   if (logger.isDebugEnabled()) {  
  113.     logger.debug("appHashId:[" + appHashId + "]");  
  114.   }  
  115.   result.setAppHashId(appHashId);  
  116.   if (supportsField(token.getContainer(), "activity", Activity.Field.BODY.toString()) && activity.getBody() != null) {  
  117.     result.setBody(activity.getBody());  
  118.   }  
  119.   else {  
  120.     result.setBody("");  
  121.   }  
  122.   if (supportsField(token.getContainer(), "activity", Activity.Field.BODY_ID.toString()) && activity.getBodyId() != null) {  
  123.     result.setBodyId(activity.getBodyId());  
  124.   }  
  125.   else {  
  126.     result.setBodyId("");  
  127.   }  
  128.   if (supportsField(token.getContainer(), "activity", Activity.Field.EXTERNAL_ID.toString()) && activity.getExternalId() != null) {  
  129.     result.setExternalId(activity.getExternalId());  
  130.   }  
  131.   else {  
  132.     result.setExternalId("");  
  133.   }  
  134.   result.setPostedTime(new Timestamp(System.currentTimeMillis())); /* 取り出すときにはlongに変換すること */  
  135.   if (supportsField(token.getContainer(), "activity", Activity.Field.PRIORITY.toString()) && activity.getPriority() != null) {  
  136.     result.setPriority((double)(float)activity.getPriority());  
  137.   }  
  138.   else {  
  139.     result.setPriority(0.0d);  
  140.   }  
  141.   if (supportsField(token.getContainer(), "activity", Activity.Field.STREAM_FAVICON_URL.toString()) && activity.getStreamFaviconUrl() != null) {  
  142.     result.setStreamFaviconUrl(activity.getStreamFaviconUrl());  
  143.   }  
  144.   else {  
  145.     result.setStreamFaviconUrl("");  
  146.   }  
  147.   if (supportsField(token.getContainer(), "activity", Activity.Field.STREAM_SOURCE_URL.toString()) && activity.getStreamSourceUrl() != null) {  
  148.     result.setStreamSourceUrl(activity.getStreamSourceUrl());  
  149.   }  
  150.   else {  
  151.     result.setStreamSourceUrl("");  
  152.   }  
  153.   if (supportsField(token.getContainer(), "activity", Activity.Field.STREAM_TITLE.toString()) && activity.getStreamTitle() != null) {  
  154.     result.setStreamTitle(activity.getTitle());  
  155.   }  
  156.   else {  
  157.     result.setStreamTitle("");  
  158.   }  
  159.   if (supportsField(token.getContainer(), "activity", Activity.Field.STREAM_URL.toString()) && activity.getStreamUrl() != null) {  
  160.     result.setStreamUrl(activity.getUrl());  
  161.   }  
  162.   else {  
  163.     result.setStreamUrl("");  
  164.   }  
  165.   Map<String, String> params = activity.getTemplateParams();  
  166.   if (supportsField(token.getContainer(), "activity", Activity.Field.TEMPLATE_PARAMS.toString()) && params != null) {  
  167.     result.setTemplateParams(beanJsonConverter.convertToString(params));  
  168.   }  
  169.   else {  
  170.     result.setTemplateParams("");  
  171.   }  
  172.   if (supportsField(token.getContainer(), "activity", Activity.Field.TITLE.toString()) && activity.getTitle() != null) {  
  173.     result.setTitle(activity.getTitle());  
  174.   }  
  175.   else {  
  176.     result.setTitle("");  
  177.   }  
  178.   if (supportsField(token.getContainer(), "activity", Activity.Field.TITLE_ID.toString()) && activity.getTitleId() != null) {  
  179.     result.setTitleId(activity.getTitleId());  
  180.   }  
  181.   else {  
  182.     result.setTitleId("");  
  183.   }  
  184.   if (supportsField(token.getContainer(), "activity", Activity.Field.URL.toString()) && activity.getUrl() != null) {  
  185.     result.setUrl(activity.getUrl());  
  186.   }  
  187.   else {  
  188.     result.setUrl("");  
  189.   }  
  190.   return result;  
  191. }  


TEMPLATE_PARAMSはMapをJSON形式の文字列に変換後DBにセット。
container.jsのsupportsFieldと連動させたいのでsupportsFieldコールしまくり。

MediaItemは使用していない項目が大量にあるけど、これは0.9用らしい。


でActivityの保存ができた。

次はDataRequest.newFetchActivitiesRequest(idSpec, opt_params)のサーバ側処理を
実装してみるつもり。



--
というか、入力チェック一切行っていないのを今さら気づいた。

--
というか、hasPermissionをせっかく作ったのに使ってなかった。。

--
というか、サニタイズは?

--
簡易サニタイズつけた。
hasPermission呼ぶようにした。
.

[Apache Shindig][お勉強][OpenSocial] メモ79 opensocial.Activityの全フィールドをsupportsFieldにする

とりあえずopensocial.Activityの全フィールドを
container.js上有効にしてみた。

全フィールドの抽出方法は、

  1. for (xx in opensocial.Activity.Field) {  
  2.   console.log('"' + opensocial.Activity.Field[xx] + '",');  
  3. }  

でFirebugに出力して、コピペ。

で、その結果作成されたcontainer.jsのsupportsField。
  1.        "activity" : [  
  2. "titleId",  
  3. "title",  
  4. "templateParams",  
  5. "url",  
  6. "mediaItems",  
  7. "bodyId",  
  8. "body",  
  9. "externalId",  
  10. "streamTitle",  
  11. "streamUrl",  
  12. "streamSourceUrl",  
  13. "streamFaviconUrl",  
  14. "priority",  
  15. "id",  
  16. "userId",  
  17. "appId",  
  18. "postedTime"  
  19. ]  


さらにMediaItemの全項目も。
  1.       "mediaItem" : [  
  2. "type",  
  3. "mimeType",  
  4. "url"  
  5. ]  


そんだけ。

.

[Apache Shindig][お勉強][OpenSocial] メモ78 opensocial.requestCreateActivityのサーバ側実装をしてみる(2)

サンプルのjsonrpccontainer.jsを見ると、requestCreateActivityメソッドは、

  1. JsonRpcContainer.prototype.requestCreateActivity = function(activity, priority,  
  2.     opt_callback) {  
  3.   opt_callback = opt_callback || function(){};  
  4.   
  5.   var req = opensocial.newDataRequest();  
  6.   var viewer = new opensocial.IdSpec({'userId' : 'VIEWER'});  
  7.   req.add(this.newCreateActivityRequest(viewer, activity), 'key');  
  8.   req.send(function(response) {  
  9.     opt_callback(response.get('key'));  
  10.   });  
  11. };  

となっている。
一応サーバ側の実装ではGroupIdも@selfに限らず受け取れるようになっている
ものの、@self以外は送られてこないと思って良さそう。
UserIdも必ずVIEWERっぽい。

responseからResponseItemを取得するためのキーも必ず'key'となっている模様。
ただ、直接newCreateActivityRequestのような処理を行われると困るので、
サーバ側ではサンプル実装のとおり、GroupIdは無視するのが良いかも。

.

2009年7月23日木曜日

[Apache Shindig][お勉強][OpenSocial] メモ77 opensocial.requestCreateActivityのサーバ側実装をしてみる(1)

requestCreateActivityを使ってサーバにActivityを保存する。
requestCreateActivityに対応するサーバ側の処理を実装してみる。

テストで使用するGadget XMLは、goo ディベロッパーキッチンのサンプルを参考に作成。

  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <Module>  
  3.   <ModulePrefs title="Activity">  
  4.     <Require feature="opensocial-0.8" />  
  5.   </ModulePrefs>  
  6.   <Content type="html" view="home,profile,canvas">  
  7.     <![CDATA[ 
  8.     <script type="text/javascript"> 
  9.       function get() { 
  10.         /*===================================================================*/ 
  11.         /* OWNER IdSpec                                                      */ 
  12.         /*===================================================================*/ 
  13.         var idspec = new opensocial.IdSpec(); 
  14.         idspec.setField(opensocial.IdSpec.Field.USER_ID,  opensocial.IdSpec.PersonId.OWNER); 
  15.         idspec.setField(opensocial.IdSpec.Field.GROUP_ID, opensocial.IdSpec.GroupId.SELF); 
  16.  
  17.         /*===================================================================*/ 
  18.         /* DataRequestオブジェクトを作成し、                                 */ 
  19.         /* そこにActivity取得リクエストを追加.                               */ 
  20.         /* レスポンスを取得するときのキーは'get_activity'                    */ 
  21.         /*===================================================================*/ 
  22.         var req = opensocial.newDataRequest(); 
  23.         req.add(req.newFetchActivitiesRequest(idspec), 'get_activity'); 
  24.         /*===================================================================*/ 
  25.         /* 送信!                                                             */ 
  26.         /*===================================================================*/ 
  27.         req.send(function(response){ 
  28.           var activity = response.get('get_activity') 
  29.           if (activity.hadError()) { 
  30.             document.getElementById('result').innerHTML 
  31.               = 'エラーだったよ:' + activity.getErrorMessage(); 
  32.             return; 
  33.           } 
  34.           var out = document.createElement('ul'); 
  35.           var li = document.createElement('li'); 
  36.           if (activity.getData().size() == 0) { 
  37.             li.innerHTML = 'Activity0件'; 
  38.             out.appendChild(li); 
  39.           } 
  40.           activity.getData().each(function(act) { 
  41.             var title = act.getField(opensocial.Activity.Field.TITLE); 
  42.             var media = act.getField(opensocial.Activity.Field.MEDIA_ITEMS); 
  43.             if (media != undefined) { 
  44.               for (var ii=0, len = media.length; ii < len; ii++) { 
  45.                 if (media[ii].getField(opensocial.MediaItem.Field.TYPE) == opensocial.MediaItem.Type.IMAGE) { 
  46.                   title += 
  47.                       ' 
  48. <img src="' 
  49.                     + media[ii].getField(opensocial.MediaItem.Field.URL); 
  50.                     + '/>'; 
  51.                 } 
  52.               } 
  53.             } 
  54.             li.innerHTML = title; 
  55.             out.appendChild(li); 
  56.           }); 
  57.  
  58.           document.getElementById('result').appendChild(out); 
  59.         }); 
  60.       } 
  61.       function update() { 
  62.         var params = {}; 
  63.         params[opensocial.Activity.Field.TITLE] = 'アクティビティテストです'; 
  64.         /*===================================================================*/ 
  65.         /* Activity作成                                                      */ 
  66.         /*===================================================================*/ 
  67.         var act = opensocial.newActivity(params); 
  68.         /*===================================================================*/ 
  69.         /* 送信                                                              */ 
  70.         /*===================================================================*/ 
  71.         opensocial.requestCreateActivity(act, opensocial.CreateActivityPriority.HIGH, callback); 
  72.  
  73.         function callback(status){ 
  74.           if (status.hadError()) { 
  75.             document.getElementById('result').innerHTML = 'Activity更新失敗'; 
  76.             return; 
  77.           } 
  78.           document.getElementById('result').innerHTML = 'Activity更新成功'; 
  79.         }; 
  80.       }; 
  81.  
  82.       function media() { 
  83.         var opt_params = {}; 
  84.         opt_params[opensocial.MediaItem.Field.TYPE] = opensocial.MediaItem.Type.IMAGE; 
  85.         var mediaItem = opensocial.newMediaItem( 
  86.           'image/gif', 
  87.           'http://www.google.co.jp/intl/ja_jp/images/logo.gif', 
  88.           opt_params 
  89.         ); 
  90.         var params = {}; 
  91.         params[opensocial.Activity.Field.TITLE] = 'MediaItemを使ったアクティビティテストです'; 
  92.         params[opensocial.Activity.Field.MEDIA_ITEMS] = [mediaItem]; 
  93.         var act = opensocial.newActivity(params); 
  94.         opensocial.requestCreateActivity(act, opensocial.CreateActivityPriority.HIGH, callback); 
  95.  
  96.         function callback(status){ 
  97.           if (status.hadError()) { 
  98.             document.getElementById('result').innerHTML = 'Activity更新失敗 With MediaItemType'; 
  99.             return; 
  100.           } 
  101.           document.getElementById('result').innerHTML = 'Activity更新成功 With MediaItemType'; 
  102.         }; 
  103.       } 
  104.     </script> 
  105.     <div id="result"></div> 
  106.     <input type="button" value="アクティビティを取得する" onclick="get();" /> 
  107.  
  108.     <input type="button" value="アクティビティを更新する" onclick="update();" /> 
  109.  
  110.     <input type="button" value="MediaItemを使ってアクティビティを更新する" onclick="media();" /> 
  111.  
  112.     ]]>  
  113.   </Content>  
  114. </Module>  

な感じ。

サンプルによると、「MediaItemを使ったアクティビティ更新」というものも存在するらしい。

で、「アクティビティを更新する」ボタンを押下。
そのときのFirebugの出力は、
  1. [  
  2.   {  
  3.     "method":"activities.create",  
  4.     "params":{  
  5.        "userId":["@viewer"],  
  6.        "groupId":"@self",  
  7.        "appId":"@app",  
  8.        "activity":{  
  9.          "title":"アクティビティテストです",  
  10.          "mediaItems":[]  
  11.        }  
  12.      },  
  13.      "id":"key"  
  14.   }  
  15. ]  

となっている。
見難いので整形済み。

UserIdは複数指定できる。
GroupIdはサーバ側サンプル実装によると、見ていないので、どうしたものか。
@appはサーバ側でガジェットのURLに変換される。

どうも、requestCreateActivityを使うと、レスポンス取得キーは'key'になるらしい。

気になるところはGroupIdとUserId。
@friendsとか指定されたらどうなるんだろう。。

.

[Apache Shindig][お勉強][OpenSocial] メモ76 ActivityServiceインタフェース

とりあえず、Shindigを使用する際、実装すべきサービスを見る。

インタフェースは、


org.apache.shindig.social.opensocial.spi.ActivityService

に定義されている。

このインタフェースには、
  1. Future<RestfulCollection<Activity>> getActivities(Set<UserId> userIds,  
  2.     GroupId groupId, String appId, Set<String> fields, CollectionOptions options, SecurityToken token)  
  3.     throws ProtocolException;  
  4.   
  5. Future<RestfulCollection<Activity>> getActivities(UserId userId, GroupId groupId,  
  6.     String appId, Set<String> fields, CollectionOptions options, Set<String> activityIds, SecurityToken token)  
  7.     throws ProtocolException;  
  8.   
  9.   
  10. Future<Activity> getActivity(UserId userId, GroupId groupId, String appId,  
  11.     Set<String> fields, String activityId, SecurityToken token)  
  12.     throws ProtocolException;  
  13.   
  14. Future<Void> deleteActivities(UserId userId, GroupId groupId, String appId,  
  15.     Set<String> activityIds, SecurityToken token) throws ProtocolException;  
  16.   
  17. Future<Void> createActivity(UserId userId, GroupId groupId, String appId,  
  18.     Set<String> fields, Activity activity, SecurityToken token) throws ProtocolException;  

と5つのメソッドが書いてある。

アクティビティ取得系が3つ、あとは削除と作成。
更新はしないみたい。

実装の際、参考になるソースは、

./java/samples/src/main/java/org/apache/shindig/social/opensocial/jpa/spi/ActivityServiceDb.java

においてある。と。

getActivitiesでは、GroupIdによって処理を変えているみたい。
多分PersonServiceとかAppDataServiceとかと似たような処理な気がする。

とりあえずcreateからやってみよう。
.

[Apache Shindig][お勉強][OpenSocial] メモ75 Activityって何よ

Activityって何よ?

goo ディベロッパーキッチンによると、


アクティビティとは、ユーザーの様々な行動の記録を指しています。

とのこと。

なるへそー。
.

[Apache Shindig][お勉強][OpenSocial] メモ74 DataRequest.newRemovePersonAppDataRequestのサーバ側実装をしてみる(2)

できた。

コードは、

  1. public void deletePersonData(UserId userId, GroupId groupId,  
  2.     String appId, Set<String> fields, SecurityToken token) throws ProtocolException {  
  3.   GmsPerson viewer = getUser();  
  4.   logger.info("deletePersonData開始"  
  5.     + "viewer:[" + ((viewer == null) ? "Guest" : viewer.getLoginId()) + "]:"  
  6.     + "userId:[" + userId + "]:"  
  7.     + "appId:[" + appId + "]:"  
  8.   );  
  9.   if (viewer == null) {  
  10.     logger.info("updatePersonData処理終了:Guestは保存できない:"  
  11.       + "userId:[" + userId + "]:"  
  12.       + "groupId:[" + groupId + "]:"  
  13.       + "appId:[" + appId + "]:"  
  14.     );  
  15.     throw new ProtocolException(HttpServletResponse.SC_FORBIDDEN,  
  16.       "Guestは保存できない: 未ログイン");  
  17.   }  
  18.   
  19.   String appHashId = toStringDigest(getStringDigest(appId));  
  20.   if (logger.isDebugEnabled()) {  
  21.     logger.debug("appHashId:[" + appHashId + "]");  
  22.   }  
  23.   String loginId = userId.getUserId(token);  
  24.   if ("Guest".equalsIgnoreCase(loginId)) {  
  25.     logger.info("updatePersonData処理終了:Guestは保存できない:"  
  26.       + "userId:[" + userId + "]:"  
  27.       + "groupId:[" + groupId + "]:"  
  28.       + "appId:[" + appId + "]:"  
  29.     );  
  30.     throw new ProtocolException(HttpServletResponse.SC_FORBIDDEN,  
  31.       "Guestは保存できない: loginid:[" + loginId + "]");  
  32.   }  
  33.   List<String> userLoginIds = new ArrayList<String>();  
  34.   userLoginIds.add(loginId);  
  35.   ExtendedGmsPerson[] gmsPersons = null;  
  36.   List<String> personIds = new ArrayList<String>();  
  37.   int first = 0;  
  38.   int max = 20;  
  39.   for (;;) {  
  40.     gmsPersons = gmsPersonDao.peopleGet(  
  41.       userLoginIds,  
  42.       groupId.getType().toString(),  
  43.       groupId.getGroupId(),  
  44.       null,  
  45.       null,  
  46.       null,  
  47.       null,  
  48.       null,  
  49.       first,  
  50.       max,  
  51.       null);  
  52.     if (logger.isDebugEnabled()) {  
  53.       logger.debug("count:[" + gmsPersons.length + "]");  
  54.     }  
  55.     int length = gmsPersons.length;  
  56.     for (int ii=0; ii<length; ii++) {  
  57.       personIds.add(gmsPersons[ii].getId());  
  58.     }  
  59.     if (length < max) {  
  60.       break;  
  61.     }  
  62.     first += max;  
  63.   }  
  64.   int count = 0;  
  65.   for (String personId: personIds) {  
  66.     gmsPersonDao.select(personId, true);  
  67.     for (String field: fields) {  
  68.       GmsAppdata[] appdatas = null;  
  69.       appdatas = gmsAppdataDao.selectByAppHashIdAndGmsPersonIdAndName(appHashId, personId, field);  
  70.       for (int ii=0,length=appdatas.length; ii<length; ii++) {  
  71.         if (appId.equalsIgnoreCase(appdatas[ii].getApplicationId())) {  
  72.           gmsAppdataDao.delete(appdatas[ii]);  
  73.           count++;  
  74.           break;  
  75.         }  
  76.       }  
  77.     }  
  78.   }  
  79.   if (logger.isDebugEnabled()) {  
  80.     logger.debug("[" + count + "]件削除しちゃった");  
  81.   }  
  82.   logger.info("deletePersonData終了"  
  83.     + "viewer:[" + ((viewer == null) ? "Guest" : viewer.getLoginId()) + "]:"  
  84.     + "userId:[" + userId + "]:"  
  85.     + "appId:[" + appId + "]:"  
  86.   );  
  87. }  

な感じ。

これで、AppDataシリーズは完了。


次はアクティビティまわりかな。

調べてみようっと。

.

[Apache Shindig][お勉強][OpenSocial] メモ73 DataRequest.newRemovePersonAppDataRequestのサーバ側実装をしてみる(1)

DataRequest.newRemovePersonAppDataRequestに対応するサーバ側処理を実装してみる。

GadgetのXMLは

と一緒。

で、newRemovePersonAppDataRequestをしている部分だけ抜き出し。

  1. function removeAppData() {  
  2.   var req = opensocial.newDataRequest();  
  3.   req.add(  
  4.     req.newRemovePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, 'key'),  
  5.     'get_response');  
  6.   /*=====================================================================*/  
  7.   /* 送信                                                                */  
  8.   /*=====================================================================*/  
  9.   req.send(function(response){  
  10.     var result = response.get('get_response');  
  11.     if (result.hadError()) {  
  12.       document.getElementById('result').innerHTML = 'appDataの削除に失敗したよー:'  
  13.         + result.getErrorMessage();  
  14.       } else {  
  15.       document.getElementById('result').innerHTML = '削除したよー';  
  16.     }  
  17.   });  
  18. }  

やろうとしていることは、VIEWERのkeyという名のキーのデータを削除すること。

FireBugによるPOSTリクエストのダンプは、
  1. [  
  2.   {  
  3.     "method":"appdata.delete",  
  4.     "params":{  
  5.                "userId":["@viewer"],  
  6.                "groupId":"@self",  
  7.                "appId":"@app",  
  8.                "fields":"key"  
  9.               },  
  10.     "id":"get_response"  
  11.   }  
  12. ]  

ちょっと見難いので整形。
メソッド名はappdata.delete。

対応するAppDataServiceのメソッドは、
  1. public Future<Void> deletePersonData(UserId userId, GroupId groupId,  
  2.     String appId, Set<String> fields, SecurityToken token) throws ProtocolException {  
  3.   if (logger.isDebugEnabled()) {  
  4.     logger.debug("userId:[" + userId + "]");  
  5.     logger.debug("userId2:[" + userId.getUserId(token) + "]");  
  6.     logger.debug("groupId:[" + groupId + "]");  
  7.     logger.debug("appId:[" + appId + "]");  
  8.     for (String field: fields) {  
  9.       logger.debug("field:[" + field + "]");  
  10.     }  
  11.   }  
  12.   throw new UnsupportedOperationException();  
  13. }  

と。

こいつを実装してやればよい。

.

2009年7月22日水曜日

[Apache Shindig][お勉強][OpenSocial] メモ72 DataRequest.newFetchPersonAppDataRequestのサーバ側実装をしてみる(3)

できた。

コードは、

  1. public Map<String, Map<String, String>> getPersonData(Set<UserId> userIds, GroupId groupId,  
  2.     String appId, Set<String> fields, SecurityToken token) throws ProtocolException {  
  3.   logger.info("getPersonData開始:"  
  4.     + "groupId:[" + groupId + "]:"  
  5.     + "appId:[" + appId + "]:"  
  6.   );  
  7.   
  8.   Map<String, Map<String, String>> results = new HashMap<String, Map<String, String>>();  
  9.   String appHashId = toStringDigest(getStringDigest(appId));  
  10.   if (logger.isDebugEnabled()) {  
  11.     logger.debug("appHashId:[" + appHashId + "]");  
  12.   }  
  13.   
  14.   List<String> userLoginIds = getUserList(userIds, token);  
  15.   ExtendedGmsPerson[] gmsPersons = null;  
  16.   List<String> personIds = new ArrayList<String>();  
  17.   int first = 0;  
  18.   int max = 20;  
  19.   for (;;) {  
  20.     gmsPersons = gmsPersonDao.peopleGet(  
  21.       userLoginIds,  
  22.       groupId.getType().toString(),  
  23.       groupId.getGroupId(),  
  24.       null,  
  25.       null,  
  26.       null,  
  27.       null,  
  28.       null,  
  29.       first,  
  30.       max,  
  31.       null);  
  32.     if (logger.isDebugEnabled()) {  
  33.       logger.debug("count:[" + gmsPersons.length + "]");  
  34.     }  
  35.     int length = gmsPersons.length;  
  36.     for (int ii=0; ii<length; ii++) {  
  37.       personIds.add(gmsPersons[ii].getId());  
  38.     }  
  39.     if (length < max) {  
  40.       break;  
  41.     }  
  42.     first += max;  
  43.   }  
  44.   Timestamp now = new Timestamp(System.currentTimeMillis());  
  45.   for (String personId: personIds) {  
  46.     gmsPersonDao.select(personId, true);  
  47.     Map<String, String> m = Maps.newHashMap();  
  48.     for (String field: fields) {  
  49.       GmsAppdata[] appdatas = null;  
  50.       appdatas = gmsAppdataDao.selectByAppHashIdAndGmsPersonIdAndName(appHashId, personId, field);  
  51.       GmsAppdata appdata = null;  
  52.       for (int ii=0,length=appdatas.length; ii<length; ii++) {  
  53.         if (appId.equalsIgnoreCase(appdatas[ii].getApplicationId())) {  
  54.           m.put(field, appdatas[ii].getData());  
  55.           break;  
  56.         }  
  57.       }  
  58.     }  
  59.     results.put(personId, m);  
  60.   }  
  61.   
  62.   logger.info("getPersonData終了:"  
  63.     + "groupId:[" + groupId + "]:"  
  64.     + "appId:[" + appId + "]:"  
  65.   );  
  66.   return results;  
  67. }  

な感じ。

Service側は、
  1. public Future<DataCollection> getPersonData(Set<UserId> userIds, GroupId groupId,  
  2.     String appId, Set<String> fields, SecurityToken token) throws ProtocolException {  
  3.   if (logger.isDebugEnabled()) {  
  4.     for (UserId userId: userIds) {  
  5.       logger.debug("userId:[" + userId + "]");  
  6.       logger.debug("userId2:[" + userId.getUserId(token) + "]");  
  7.     }  
  8.     logger.debug("groupId:[" + groupId + "]");  
  9.     logger.debug("appId:[" + appId + "]");  
  10.     for (String field: fields) {  
  11.       logger.debug("field:[" + field + "]");  
  12.     }  
  13.   }  
  14.   Map<String, Map<String, String>> results = logic.getPersonData(userIds, groupId, appId, fields, token);  
  15.   DataCollection dc = new DataCollection(results);  
  16.   return ImmediateFuture.newInstance(dc);  
  17. }  

な感じ。

これで、newFetchPersonAppDataRequestとnewUpdatePersonAppDataRequestに対応できた。
ちなみに、キーごとに保存できるデータの最大長は、システムの許す限り。


で、ちょっとだけ、動くように修正したガジェットXMLは、
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <Module>  
  3.   <ModulePrefs title="AppData">  
  4.     <Require feature="opensocial-0.8" />  
  5.   </ModulePrefs>  
  6.   <Content type="html" view="home,profile,canvas">  
  7.     <![CDATA[ 
  8.     <script type="text/javascript"> 
  9.     function updateAppData() { 
  10.       /*=====================================================================*/ 
  11.       /* これから保存したいオブジェクト                                      */ 
  12.       /*=====================================================================*/ 
  13.       var obj = {'param1':'データだよーん', 'param2':'だよーんデータ'}; 
  14.       /*=====================================================================*/ 
  15.       /* オブジェクトを文字列に変換(OpenSocial推奨らしい)                    */ 
  16.       /*=====================================================================*/ 
  17.       var strObj = gadgets.json.stringify(obj); 
  18.  
  19.       /*=====================================================================*/ 
  20.       /* DataRequest生成、セットアップ                                       */ 
  21.       /*=====================================================================*/ 
  22.       var req = opensocial.newDataRequest(); 
  23.       req.add(req.newUpdatePersonAppDataRequest( 
  24.         opensocial.IdSpec.PersonId.VIEWER, 
  25.         'key', 
  26.         strObj), 
  27.         'get_response'); 
  28.       /*=====================================================================*/ 
  29.       /* 送信!                                                              */ 
  30.       /*=====================================================================*/ 
  31.       req.send(function(response){ 
  32.         var result = response.get('get_response'); 
  33.         if (result.hadError()) { 
  34.           document.getElementById('result').innerHTML = '失敗したよー:' 
  35.             + result.getErrorMessage(); 
  36.         } else { 
  37.           document.getElementById('result').innerHTML 
  38.             = 'newUpdatePersonAppDataRequest成功'; 
  39.         } 
  40.       }); 
  41.     } 
  42.     function getAppData() { 
  43.       var idspec = new opensocial.IdSpec(); 
  44.       idspec.setField(opensocial.IdSpec.Field.USER_ID,  opensocial.IdSpec.PersonId.VIEWER); 
  45.       idspec.setField(opensocial.IdSpec.Field.GROUP_ID, opensocial.IdSpec.GroupId.SELF); 
  46.  
  47.       var req = opensocial.newDataRequest(); 
  48.       /*=====================================================================*/ 
  49.       /* まずVIEWERを取得                                                    */ 
  50.       /*=====================================================================*/ 
  51.       req.add( 
  52.         req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), 
  53.         'get_viewer'); 
  54.       /*=====================================================================*/ 
  55.       /* APPDATA取得のリクエストを生成                                       */ 
  56.       /*=====================================================================*/ 
  57.       req.add( 
  58.         req.newFetchPersonAppDataRequest(idspec, 'key'), 
  59.         'get_appdata'); 
  60.       /*=====================================================================*/ 
  61.       /* 送信                                                                */ 
  62.       /*=====================================================================*/ 
  63.       req.send(function(response){ 
  64.         var viewer = response.get('get_viewer'); 
  65.         if (viewer.hadError()) { 
  66.           document.getElementById('result').innerHTML = 'Viewerの取得に失敗したよー:' 
  67.             + viewer.getErrorMessage(); 
  68.         } else { 
  69.           var appdata = response.get('get_appdata'); 
  70.           if (appdata.hadError()) { 
  71.             document.getElementById('result').innerHTML = 'AppDataの取得に失敗したよー:' 
  72.               + appdata.getErrorMessage(); 
  73.           } else { 
  74.             if (appdata.getData()[viewer.getData().getId()]) { 
  75.               /*=============================================================*/ 
  76.               /* VIEWER固有のAppData取得                                     */ 
  77.               /*=============================================================*/ 
  78.               var srcObj = appdata.getData()[viewer.getData().getId()]['key']; 
  79.               /*=============================================================*/ 
  80.               /* unescape                                                    */ 
  81.               /*=============================================================*/ 
  82.               console.log(srcObj); 
  83.               var jsonStr = gadgets.util.unescapeString(srcObj); 
  84.               var jsonObj = gadgets.json.parse(jsonStr); 
  85.               document.getElementById('result').innerHTML = jsonObj['param1'] + ' 
  86. ' + jsonObj['param2']; 
  87.             } else { 
  88.               document.getElementById('result').innerHTML = 'がちょーん'; 
  89.             } 
  90.           } 
  91.         } 
  92.       }); 
  93.     } 
  94.     function removeAppData() { 
  95.       var req = opensocial.newDataRequest(); 
  96.       req.add( 
  97.         req.newRemovePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, 'key'), 
  98.         'get_response'); 
  99.       /*=====================================================================*/ 
  100.       /* 送信                                                                */ 
  101.       /*=====================================================================*/ 
  102.       req.send(function(response){ 
  103.         var result = response.get('get_response'); 
  104.         if (result.hadError()) { 
  105.           document.getElementById('result').innerHTML = 'appDataの削除に失敗したよー:' 
  106.             + result.getErrorMessage(); 
  107.           } else { 
  108.           document.getElementById('result').innerHTML = '削除したよー'; 
  109.         } 
  110.       }); 
  111.     } 
  112.     </script> 
  113.     <div id="result"></div> 
  114.     <input type="button" value="更新" onclick="updateAppData()" /> 
  115.  
  116.     <input type="button" value="取得" onclick="getAppData()" /> 
  117.  
  118.     <input type="button" value="削除" onclick="removeAppData()" /> 
  119.  
  120.     ]]>  
  121.   </Content>  
  122. </Module>  

な感じー。


まだremoveAppDataは動かないけど。
.

[Apache Shindig][お勉強][OpenSocial] メモ71

うーむ。。

良くわからないけど、
これ
だと動かない気がする。。

うーむ。

My Shindig環境で実行するとI.replace is not functionってJavaScriptエラーになる。

  1. var appdata_param = appdata.getData()[viewer.getData().getId()];    

では無くて、
  1. var appdata_param = appdata.getData()[viewer.getData().getId()]['appdata_key'];  

とすれば動くんだけど、、

うーむ。。
JavaScriptなんで良くわかんないけど、なんとなく変な気がする。。
わからん。。


まぁいいや。
.

[Apache Shindig][お勉強][OpenSocial] メモ70 DataRequest.newFetchPersonAppDataRequestのサーバ側実装をしてみる(2)

ShindigのAppDataServiceDb.javaを見ると、

  1. // load the map up  
  2. List<ApplicationDataMapDb> dataMaps = JPQLUtils.getListQuery(entityManager, sb.toString(),  
  3.     paramList, null);  
  4. Map<String, Map<String, String>> results = new HashMap<String, Map<String, String>>();  
  5.   
  6. // only add in the fields  
  7. if (fields == null || fields.size() == 0) {  
  8.   for (ApplicationDataMapDb adm : dataMaps) {  
  9.     results.put(adm.getPersonId(), adm.getValues());  
  10.   }  
  11. else {  
  12.   for (ApplicationDataMapDb adm : dataMaps) {  
  13.     Map<String, String> m = Maps.newHashMap();  
  14.     for (String f : fields) {  
  15.       String value = adm.getValues().get(f);  
  16.       if (null != value) {  
  17.         m.put(f, value);  
  18.       }  
  19.     }  
  20.     results.put(adm.getPersonId(), m);  
  21.   }  
  22. }  
  23. DataCollection dc = new DataCollection(results);  
  24. return ImmediateFuture.newInstance(dc);  

となっていて、見たこともないDataCollectionというクラスがImmediateFutre.newInstanceされて返されている。。

結局のところ、AppDataService.getPersonDataは、同じように
  1. DataCollection dc = new DataCollection(results);  
  2. return ImmediateFuture.newInstance(dc);  

としてやればよいと。

resultsは、
  1. Map<String, Map<String, String>> results = new HashMap<String, Map<String, String>>();  

と。

このresultsをせっせと作って、
org.apache.shindig.protocol.DataCollection
にして返す。

大体わかった。
.

[Apache Shindig][お勉強][OpenSocial] メモ69 DataRequest.newFetchPersonAppDataRequestのサーバ側実装をしてみる(1)

DataRequest.newFetchPersonAppDataRequestに対応するサーバ側の実装をしてみる。

ガジェットXMLは、
ここ
で使ったものをそのまま使う。

とりあえず、どんなリクエストが来るのか見てみる。

FirBugでのPOSTデータは、

  1. [{"method":"people.get","params":{"userId":["@viewer"],"groupId":"@self","fields":["id","name","thumbnailUrl"  
  2. ,"id","displayName"]},"id":"get_viewer"},{"method":"appdata.get","params":{"userId":["@viewer"],"groupId"  
  3. :"@self","appId":"@app","fields":["key"]},"id":"get_appdata"}]  

な感じ。

「取得」ボタンを押下すると、people.getでVIEWERは取れるんだけど、
appdata.getでは取れない。もちろん未実装だから。
見難いけど、上記は、people.getとappdata.getの2つのRPCが行われている。

で、上記リクエストを投げたときのサーバ側のログ。

[リクエスト:[7642812]] 2009-07-22 22:31:19,062 INFO jp.qsdn.gms.web.spring.controller.gadget.ApiController - dispatcher:[jp.qsdn.gms.social.protocol.SpringHandlerRegistry@5e7663]
[リクエスト:[7642812]] 2009-07-22 22:31:19,063 DEBUG jp.qsdn.gms.social.service.AppDataServiceImpl - userId:[VIEWER]
[リクエスト:[7642812]] 2009-07-22 22:31:19,063 DEBUG jp.qsdn.gms.social.service.AppDataServiceImpl - userId2:[root]
[リクエスト:[7642812]] 2009-07-22 22:31:19,063 DEBUG jp.qsdn.gms.social.service.AppDataServiceImpl - groupId:[SELF]
[リクエスト:[7642812]] 2009-07-22 22:31:19,063 DEBUG jp.qsdn.gms.social.service.AppDataServiceImpl - appId:[http://localhost/opensocial/hello.xml]
[リクエスト:[7642812]] 2009-07-22 22:31:19,063 DEBUG jp.qsdn.gms.social.service.AppDataServiceImpl - field:[key]
[リクエスト:[7642812]] 2009-07-22 22:31:19,063 WARN jp.qsdn.gms.web.spring.controller.gadget.ApiController - Returning a response error as result of an exception
java.lang.UnsupportedOperationException
at jp.qsdn.gms.social.service.AppDataServiceImpl.getPersonData(AppDataServiceImpl.java:60)
at org.apache.shindig.social.opensocial.service.AppDataHandler.get(AppDataHandler.java:122)


な感じ。

appdata.updateとは違って、UserIdが配列で飛んでくる。
そこさえ注意していれば、割と簡単そう。

.

[Apache Shindig][お勉強][OpenSocial] メモ68 DataRequest.newUpdatePersonAppDataRequestのサーバ側実装をしてみる(3)

DataRequest.newUpdatePersonAppDataRequestのサーバ側実装ができた。

へぼいけどざっとこんな感じ。

  1. public void updatePersonData(UserId userId, GroupId groupId, String appId, Set<String> fields, Map<String, String> values, SecurityToken token)  
  2.      throws ProtocolException {  
  3.   logger.info("updatePersonData処理開始:"  
  4.     + "userId:[" + userId + "]:"  
  5.     + "groupId:[" + groupId + "]:"  
  6.     + "appId:[" + appId + "]:"  
  7.   );  
  8.   
  9.   assert appId != null;  
  10.   
  11.   GmsPerson viewer = getUser();  
  12.   if (viewer == null) {  
  13.     logger.info("updatePersonData処理終了:Guestは保存できない:"  
  14.       + "userId:[" + userId + "]:"  
  15.       + "groupId:[" + groupId + "]:"  
  16.       + "appId:[" + appId + "]:"  
  17.     );  
  18.     throw new ProtocolException(HttpServletResponse.SC_FORBIDDEN,  
  19.       "Guestは保存できない: 未ログイン");  
  20.   }  
  21.   
  22.   String appHashId = toStringDigest(getStringDigest(appId));  
  23.   if (logger.isDebugEnabled()) {  
  24.     logger.debug("appHashId:[" + appHashId + "]");  
  25.   }  
  26.   String loginId = userId.getUserId(token);  
  27.   if ("Guest".equalsIgnoreCase(loginId)) {  
  28.     logger.info("updatePersonData処理終了:Guestは保存できない:"  
  29.       + "userId:[" + userId + "]:"  
  30.       + "groupId:[" + groupId + "]:"  
  31.       + "appId:[" + appId + "]:"  
  32.     );  
  33.     throw new ProtocolException(HttpServletResponse.SC_FORBIDDEN,  
  34.       "Guestは保存できない: loginid:[" + loginId + "]");  
  35.   }  
  36.   List<String> userLoginIds = new ArrayList<String>();  
  37.   userLoginIds.add(loginId);  
  38.   ExtendedGmsPerson[] gmsPersons = null;  
  39.   List<String> personIds = new ArrayList<String>();  
  40.   int first = 0;  
  41.   int max = 20;  
  42.   for (;;) {  
  43.     gmsPersons = gmsPersonDao.peopleGet(  
  44.       userLoginIds,  
  45.       groupId.getType().toString(),  
  46.       groupId.getGroupId(),  
  47.       null,  
  48.       null,  
  49.       null,  
  50.       null,  
  51.       null,  
  52.       first,  
  53.       max,  
  54.       null);  
  55.     if (logger.isDebugEnabled()) {  
  56.       logger.debug("count:[" + gmsPersons.length + "]");  
  57.     }  
  58.     int length = gmsPersons.length;  
  59.     for (int ii=0; ii<length; ii++) {  
  60.       personIds.add(gmsPersons[ii].getId());  
  61.     }  
  62.     if (length < max) {  
  63.       break;  
  64.     }  
  65.     first += max;  
  66.   }  
  67.   Timestamp now = new Timestamp(System.currentTimeMillis());  
  68.   for (String personId: personIds) {  
  69.     gmsPersonDao.select(personId, true);  
  70.     for (String field: fields) {  
  71.       GmsAppdata[] appdatas = null;  
  72.       appdatas = gmsAppdataDao.selectByAppHashIdAndGmsPersonIdAndName(appHashId, personId, field);  
  73.       GmsAppdata appdata = null;  
  74.       for (int ii=0,length=appdatas.length; ii<length; ii++) {  
  75.         if (appId.equalsIgnoreCase(appdatas[ii].getApplicationId())) {  
  76.           appdata = appdatas[ii];  
  77.           break;  
  78.         }  
  79.       }  
  80.       if (appdata != null) {  
  81.         appdata.setData(values.get(field));  
  82.         appdata.setUpdatedAt(now);  
  83.         appdata.setUpdator(viewer.getId());  
  84.         gmsAppdataDao.update(appdata);  
  85.       }  
  86.       else {  
  87.         appdata = new GmsAppdata();  
  88.         appdata.setId(UuidGenerator.compress(UuidGenerator.generate()));  
  89.         appdata.setAppHashId(appHashId);  
  90.         appdata.setApplicationId(appId);  
  91.         appdata.setGmsPersonId(personId);  
  92.         appdata.setName(field);  
  93.         appdata.setData(values.get(field));  
  94.         appdata.setCreatedAt(now);  
  95.         appdata.setCreator(viewer.getId());  
  96.         appdata.setUpdatedAt(now);  
  97.         appdata.setUpdator(viewer.getId());  
  98.         gmsAppdataDao.insert(appdata);  
  99.       }  
  100.     }  
  101.   }  
  102.   
  103.   logger.info("updatePersonData処理終了:"  
  104.     + "userId:[" + userId + "]:"  
  105.     + "groupId:[" + groupId + "]:"  
  106.     + "appId:[" + appId + "]:"  
  107.   );  
  108. }  


Shindigのサンプル実装のAppDataServiceImplをまねして作ったけど、
友達がいっぱいいる人はとっても重い。友達いない人向けということで。
更新すべき人は誰かを決定するのにUserIdとGroupIdを使用するんで、
以前作ったpeople.getの処理をコールしている。
filterとかは一切なし。

GmsAppdataというValueObjectはgms_appdataというテーブルを表しているんだけど、
application_idとgms_person_idを一意のキーにしなかった。
というのは、MySQLって長い文字列をプライマリーキーにできないし、INDEXもはれないから。
(今はDerby使っているけど)
だから、面倒だけど、application_idからHASH値を求めて、そのHASH値でデータを
持ってくるようにした。。

途中で使っているgetUser()は、現在ログイン中のPersonオブジェクトを持ってくる。
ログインしていないとnullが返る。

GuestがVIEWERかOWNERの時は(ありえないけど)、データは保存しない。
そのときはorg.apache.shindig.protocol.ProtocolExceptionを投げる。

org.apache.shindig.protocol.ProtocolExceptionを投げておくと、Handler内で、いい感じのエラーJSONにしてくれる。

で、一応保存できた。


ij> select data from ROOTTEST.gms_appdata;
DATA
--------------------------------------------------------------------------------------------------------------------------------
{"param1":"データだよーん","param2":"だよーんデータ"}

1 row selected


な感じ。

そんだけ。
.

[OpenSocial][その他] iGoogleでcanvas

いつの間にかiGoogleで新スタイルのガジェットが使えるようになったっぽい。

これで"home"とか"canvas"とかが使えるのかな?

.

[Apache Shindig][お勉強][OpenSocial] メモ67 DataRequest.newUpdatePersonAppDataRequestのサーバ側実装をしてみる(2)

Shindigに付属するサンプル実装の


./java/samples/src/main/java/org/apache/shindig/social/opensocial/jpa/spi/AppDataServiceDb.java

を見てみる。

UserIdとGroupIdが指定できるので、
やはり@friendsやら@allやらが指定できる。

update時にGroupIdに@allが指定されると
UserIdに指定された人に関係するすべての人用のapp_dataが保存される。
同様にGroupIdに@friendsが指定されると
UserIdに指定された人の友達全ての人用のapp_dataが保存される。

だもんで、appData使うアプリを作る際は、いろいろと気をつけなきゃならなそう。
mixiやらgooやらではどうしているんだろうか。。

たぶんパーミッションモデルなるもので制限していることとは思うんだけど。



.

[Apache Shindig][お勉強][OpenSocial] メモ66 DataRequest.newUpdatePersonAppDataRequestのサーバ側実装をしてみる(1)

AppDataを扱うメソッドのうち、

  1. Object newUpdatePersonAppDataRequest(id, key, value)   
  2. # 指定した個人のアプリケーション フィールドを更新するように要求するアイテムを作成します。   

をやってみる。

まずはガジェットXML。
gooディベロッパーキッチンを見ながら作った。
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <Module>  
  3.   <ModulePrefs title="AppData">  
  4.     <Require feature="opensocial-0.8" />  
  5.   </ModulePrefs>  
  6.   <Content type="html" view="home,profile,canvas">  
  7.     <![CDATA[ 
  8.     <script type="text/javascript"> 
  9.     function updateAppData() { 
  10.       /*====================================================================*/ 
  11.       /* これから保存したいオブジェクト                                     */ 
  12.       /*====================================================================*/ 
  13.       var obj = {'param1':'データだよーん', 'param2':'だよーんデータ'}; 
  14.       /*====================================================================*/ 
  15.       /* オブジェクトを文字列に変換(OpenSocial推奨らしい)                   */ 
  16.       /*====================================================================*/ 
  17.       var strObj = gadgets.json.stringify(obj); 
  18.  
  19.       /*====================================================================*/ 
  20.       /* DataRequest生成、セットアップ                                      */ 
  21.       /*====================================================================*/ 
  22.       var req = opensocial.newDataRequest(); 
  23.       req.add(req.newUpdatePersonAppDataRequest( 
  24.         opensocial.IdSpec.PersonId.VIEWER, 
  25.         'key', 
  26.         strObj), 
  27.         'get_response'); 
  28.       /*====================================================================*/ 
  29.       /* 送信!                                                             */ 
  30.       /*====================================================================*/ 
  31.       req.send(function(response){ 
  32.         var result = response.get('get_response'); 
  33.         if (result.hadError()) { 
  34.           document.getElementById('result').innerHTML = '失敗したよー:' 
  35.             + result.getErrorMessage(); 
  36.         } else { 
  37.           document.getElementById('result').innerHTML 
  38.             = 'newUpdatePersonAppDataRequest成功'; 
  39.         } 
  40.       }); 
  41.     } 
  42.     function getAppData() { 
  43.       var idspec = new opensocial.IdSpec(); 
  44.       idspec.setField(opensocial.IdSpec.Field.USER_ID,  opensocial.IdSpec.PersonId.VIEWER); 
  45.       idspec.setField(opensocial.IdSpec.Field.GROUP_ID, opensocial.IdSpec.GroupId.SELF); 
  46.  
  47.       var req = opensocial.newDataRequest(); 
  48.       /*====================================================================*/ 
  49.       /* まずVIEWERを取得                                                   */ 
  50.       /*====================================================================*/ 
  51.       req.add( 
  52.         req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), 
  53.         'get_viewer'); 
  54.       /*====================================================================*/ 
  55.       /* APPDATA取得のリクエストを生成                                      */ 
  56.       /*====================================================================*/ 
  57.       req.add( 
  58.         req.newFetchPersonAppDataRequest(idspec, 'key'), 
  59.         'get_appdata'); 
  60.       /*====================================================================*/ 
  61.       /* 送信                                                               */ 
  62.       /*====================================================================*/ 
  63.       req.send(function(response){ 
  64.         var viewer = response.get('get_viewer'); 
  65.         if (viewer.hadError()) { 
  66.           document.getElementById('result').innerHTML = 'Viewerの取得に失敗したよー:' 
  67.             + viewer.getErrorMessage(); 
  68.         } else { 
  69.           var appdata = response.get('get_appdata'); 
  70.           if (appdata.hadError()) { 
  71.             document.getElementById('result').innerHTML = 'AppDataの取得に失敗したよー:' 
  72.               + appdata.getErrorMessage(); 
  73.           } else { 
  74.             if (appdata.getData()[viewer.getData().getId()]) { 
  75.               /*============================================================*/ 
  76.               /* VIEWER固有のAppData取得                                    */ 
  77.               /*============================================================*/ 
  78.               var srcObj = appdata.getData()[viewer.getData().getId()]; 
  79.               /*============================================================*/ 
  80.               /* unescape                                                   */ 
  81.               /*============================================================*/ 
  82.               var jsonStr = gadgets.util.unescapeString(srcObj); 
  83.               var jsonObj = gadgets.json.parse(jsonStr); 
  84.               document.getElementById('result').innerHTML = jsonObj['param1'] + ' 
  85. ' + jsonObj['param2']; 
  86.             } else { 
  87.               document.getElementById('result').innerHTML = 'がちょーん'; 
  88.             } 
  89.           } 
  90.         } 
  91.       }); 
  92.     } 
  93.     function removeAppData() { 
  94.       var req = opensocial.newDataRequest(); 
  95.       req.add( 
  96.         req.newRemovePersonAppDataRequest(opensocial.IdSpec.PersonId.VIEWER, 'key'), 
  97.         'get_response'); 
  98.       /*====================================================================*/ 
  99.       /* 送信                                                                */ 
  100.       /*====================================================================*/ 
  101.       req.send(function(response){ 
  102.         var result = response.get('get_response'); 
  103.         if (result.hadError()) { 
  104.           document.getElementById('result').innerHTML = 'appDataの削除に失敗したよー:' 
  105.             + result.getErrorMessage(); 
  106.           } else { 
  107.           document.getElementById('result').innerHTML = '削除したよー'; 
  108.         } 
  109.       }); 
  110.     } 
  111.     </script> 
  112.     <div id="result"></div> 
  113.     <input type="button" value="更新" onclick="updateAppData()" /> 
  114.  
  115.     <input type="button" value="取得" onclick="getAppData()" /> 
  116.  
  117.     <input type="button" value="削除" onclick="removeAppData()" /> 
  118.  
  119.     ]]>  
  120.   </Content>  
  121. </Module>  

というか、サンプルのまんま。

で、Gadgetを表示させてみて、表示されたボタンのうち「更新」ボタンを押下してみる。
今のところ、サーバ側では、java.lang.UnsupportedOperationException
を投げるようにしてあるんで、やっぱりjava.lang.UnsupportedOperationExceptionがスローされる。

スタックトレースは、

java.lang.UnsupportedOperationException
at jp.qsdn.gms.social.service.AppDataServiceImpl.updatePersonData(AppDataServiceImpl.java:62)
at org.apache.shindig.social.opensocial.service.AppDataHandler.create(AppDataHandler.java:106)
at org.apache.shindig.social.opensocial.service.AppDataHandler.update(AppDataHandler.java:78)

な感じ。
AppDataHandlerからAppDataServiceをimplementsしたAppDataServiceImplの
updatePersonDataがコールされる模様。

今のところのAppDataServiceImplは以下な感じ。
  1. package jp.qsdn.gms.social.service;  
  2.   
  3. import org.apache.shindig.auth.SecurityToken;  
  4. import org.apache.shindig.protocol.DataCollection;  
  5. import org.apache.shindig.protocol.ProtocolException;  
  6.   
  7. import java.util.Map;  
  8. import java.util.Set;  
  9. import java.util.concurrent.Future;  
  10.   
  11. import org.apache.shindig.social.opensocial.spi.AppDataService;  
  12. import org.apache.shindig.social.opensocial.spi.UserId;  
  13. import org.apache.shindig.social.opensocial.spi.GroupId;  
  14. import org.apache.shindig.auth.SecurityToken;  
  15.   
  16. import org.apache.commons.logging.Log;  
  17. import org.apache.commons.logging.LogFactory;  
  18.   
  19. /** 
  20.  * AppDataServiceの実装 
  21.  */  
  22. public class AppDataServiceImpl implements AppDataService {  
  23.   protected final Log logger = LogFactory.getLog(AppDataServiceImpl.class);  
  24.   
  25.   public Future<DataCollection> getPersonData(Set<UserId> userIds, GroupId groupId,  
  26.       String appId, Set<String> fields, SecurityToken token) throws ProtocolException {  
  27.     throw new UnsupportedOperationException();  
  28.   }  
  29.   
  30.   public Future<Void> deletePersonData(UserId userId, GroupId groupId,  
  31.       String appId, Set<String> fields, SecurityToken token) throws ProtocolException {  
  32.     throw new UnsupportedOperationException();  
  33.   }  
  34.   /** 
  35.    * Updates app data for the specified user and group with the new values. 
  36.    * 
  37.    * @param userId  The user 
  38.    * @param groupId The group 
  39.    * @param appId   The app 
  40.    * @param fields  The fields to filter the data by. Empty set implies all 
  41.    * @param values  The values to set 
  42.    * @param token   The security token 
  43.    * @return an error if one occurs 
  44.    */  
  45.   public Future<Void> updatePersonData(UserId userId, GroupId groupId,  
  46.       String appId, Set<String> fields, Map<String, String> values, SecurityToken token)  
  47.       throws ProtocolException {  
  48.     if (logger.isDebugEnabled()) {  
  49.       logger.debug("userId:[" + userId + "]");  
  50.       logger.debug("userId2:[" + userId.getUserId(token) + "]");  
  51.       logger.debug("groupId:[" + groupId + "]");  
  52.       logger.debug("appId:[" + appId + "]");  
  53.       for (String field: fields) {  
  54.         logger.debug("field:[" + field + "]");  
  55.       }  
  56.       for (String key: values.keySet()) {  
  57.         logger.debug("key:[" + key + "] value:[" + values.get(key) + "]");  
  58.       }  
  59.     }  
  60.     throw new UnsupportedOperationException();  
  61.   }  
  62. }  


で、ログはこんな感じ。

[リクエスト:[22256969]] 2009-07-22 01:40:16,912 DEBUG jp.qsdn.gms.social.service.AppDataServiceImpl - userId:[VIEWER]
[リクエスト:[22256969]] 2009-07-22 01:40:16,912 DEBUG jp.qsdn.gms.social.service.AppDataServiceImpl - userId2:[Guest]
[リクエスト:[22256969]] 2009-07-22 01:40:16,914 DEBUG jp.qsdn.gms.social.service.AppDataServiceImpl - groupId:[SELF]
[リクエスト:[22256969]] 2009-07-22 01:40:16,914 DEBUG jp.qsdn.gms.social.service.AppDataServiceImpl - appId:[http://localhost/opensocial/hello.xml]
[リクエスト:[22256969]] 2009-07-22 01:40:16,915 DEBUG jp.qsdn.gms.social.service.AppDataServiceImpl - field:[key]
[リクエスト:[22256969]] 2009-07-22 01:40:16,915 DEBUG jp.qsdn.gms.social.service.AppDataServiceImpl - key:[key] value:[{"param1":"データだよーん","param2":"だよーんデータ"}]



ログインしないで「更新」ボタンを押下したんで、userId2がGuest(Anonymous)になっている。
普通、GuestからのAppData保存はどうするんだろう。。

たぶん、認証してないよエラーを返すのが正解な気がする。

それは置いといて、
fieldとvaluesの関係がよくわからない。

values中のfieldsキーが示すデータを保存すればよいのかなぁ。。

ちなみに、POSTリクエスト時のデータは、

[{"method":"appdata.update","params":{"userId":["@viewer"],"groupId":"@self","appId":"@app","data":{"key"

:"{\"param1\":\"データだよーん\",\"param2\":\"だよーんデータ\"}"},"fields":"key"},"id":"get_response"}]

な感じ。

--
どうも、AppDataServiceDB.javaによると、
values中のfieldsで指定されたキーの値を、fieldsで指定されたキーで保存するみたい。


.

2009年7月21日火曜日

[Apache Shindig][お勉強][OpenSocial] メモ65

次は、
DataRequest.newUpdatePersonAppDataRequest
DataRequest.newFetchPersonAppDataRequest
あたりをやろうっと。

ちょっと検索してみると、MySpaceでは動かないことが良くわかった。。

PersistenceAPI?

うーむ。
.

[Apache Shindig][お勉強][OpenSocial] メモ64 DataRequest.newFetchPeopleRequestのサーバ側実装(3)

とりあえず版だけど、newFetchPeopleRequestのサーバ側実装ができた。

友達一覧取得できた。

一応そんときのGadgetのxml。

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <Module>  
  3.   <ModulePrefs title="友達一覧取得">  
  4.     <Require feature="opensocial-0.8" />  
  5.     <Require feature="dynamic-height" />  
  6.   </ModulePrefs>  
  7.   <Content type="html" view="home,profile,canvas"><![CDATA[ 
  8.     <div id='friends_list'></div> 
  9. <script type="text/javascript"> 
  10.  
  11.   function requestGetOwnerProfile() { 
  12.     var params = {}; 
  13.     params[opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS] = [ 
  14.       opensocial.Person.Field.ID, 
  15.       opensocial.Person.Field.NICKNAME, 
  16.       opensocial.Person.Field.THUMBNAIL_URL, 
  17.       opensocial.Person.Field.PROFILE_URL 
  18.     ]; 
  19.     // params[opensocial.DataRequest.PeopleRequestFields.FILTER] = opensocial.DataRequest.FilterType.HAS_APP; 
  20.     var req = opensocial.newDataRequest(); 
  21.     var idSpecParam = {}; 
  22.     idSpecParam[opensocial.IdSpec.Field.USER_ID]  = opensocial.IdSpec.PersonId.OWNER; 
  23.     idSpecParam[opensocial.IdSpec.Field.GROUP_ID] = opensocial.IdSpec.GroupId.FRIENDS; 
  24.     var idSpec = opensocial.newIdSpec(idSpecParam); 
  25.  
  26.     req.add(req.newFetchPeopleRequest(idSpec, params), "get_friends"); 
  27.     req.send(handleRequestGetFriendsProfile); 
  28.   }; 
  29.  
  30.   function handleRequestGetFriendsProfile(data) { 
  31.     var friends = data.get("get_friends"); 
  32.     if (friends.hadError()) { 
  33.       //Handle error using viewer.getError()... 
  34.       document.getElementById('friends_list').innerHTML = 'エラーだよーん'; 
  35.       return; 
  36.     } 
  37.     var data = friends.getData(); 
  38.     var out = document.createElement('ul'); 
  39.     data.each(function(friend) { 
  40.       var li = document.createElement('li'); 
  41.       var thumbnailUrl = friend.getField(opensocial.Person.Field.THUMBNAIL_URL); 
  42.       li.innerHTML = '<img src="'+thumbnailUrl+'" />'+friend.getField(opensocial.Person.Field.NICKNAME); 
  43.       out.appendChild(li); 
  44.     }); 
  45.     document.getElementById('friends_list').appendChild(out); 
  46.  
  47.     // 自動調節 
  48.     gadgets.window.adjustHeight(); 
  49.   }; 
  50.  
  51.   gadgets.util.registerOnLoadHandler(requestGetOwnerProfile); 
  52.  
  53. </script> 
  54.   ]]>  
  55.   </Content>  
  56. </Module>  

うーん。すばらしい。

サーバ側のSQLは悲惨な感じ。
以下、PersonServiceとDAOの間のロジック。
  1. public List<Person> getPeople(List<String> userLoginIds, String paramGroupType, String paramGroupId, CollectionOptions collectionOptions, SecurityToken token, Set<String> fields) {  
  2.   List<Person> result = new ArrayList<Person>();  
  3.   logger.info("getPeople開始:");  
  4.   if (logger.isDebugEnabled()) {  
  5.     logger.debug("collectionOptions:sortBy:[" + collectionOptions.getSortBy() + "]");  
  6.     logger.debug("collectionOptions:sortOrder:[" + collectionOptions.getSortOrder() + "]");  
  7.     logger.debug("collectionOptions:filter:[" + collectionOptions.getFilter() + "]");  
  8.     logger.debug("collectionOptions:filterOperation:[" + collectionOptions.getFilterOperation() + "]");  
  9.     logger.debug("collectionOptions:filterValue:[" + collectionOptions.getFilterValue() + "]");  
  10.     logger.debug("collectionOptions:first:[" + collectionOptions.getFirst() + "]");  
  11.     logger.debug("collectionOptions:max:[" + collectionOptions.getMax() + "]");  
  12.     logger.debug("collectionOptions:updatedSince:[" + collectionOptions.getUpdatedSince() + "]");  
  13.   }  
  14.   
  15.   String filter = collectionOptions.getFilter();  
  16.   String filterValue = "";  
  17.   if ("hasApp".equalsIgnoreCase(filter)) {  
  18.     filterValue = "" + token.getModuleId();  
  19.   }  
  20.   else {  
  21.     filterValue = collectionOptions.getFilterValue();  
  22.   }  
  23.   
  24.   ExtendedGmsPerson[] gmsPersons = gmsPersonDao.peopleGet(  
  25.     userLoginIds,  
  26.     paramGroupType,  
  27.     paramGroupId,  
  28.     collectionOptions.getSortBy(),  
  29.     collectionOptions.getSortOrder().toString(),  
  30.     filter,  
  31.     collectionOptions.getFilterOperation().toString(),  
  32.     filterValue,  
  33.     collectionOptions.getFirst(),  
  34.     collectionOptions.getMax(),  
  35.     collectionOptions.getUpdatedSince());  
  36.   if (logger.isDebugEnabled()) {  
  37.     logger.debug("count:[" + gmsPersons.length + "]");  
  38.   }  
  39.   int length = gmsPersons.length;  
  40.   for (int ii=0; ii<length; ii++) {  
  41.     result.add(mapToPerson(gmsPersons[ii], fields, PERMIT_LEVEL_ANONYMOUS, token));  
  42.   }  
  43.   
  44.   logger.info("getPeople終了:");  
  45.   return result;  
  46. }  


mapToPersonはDAOのValueObjectからShindigのPersonオブジェクトにマップするメソッド。
項目はとりあえずANONYMOUSの場合の項目しか返さない。多分あとで公開レベルに応じて
処理をわけなきゃいけなさそう。

  1. filter=hasApp  


でリクエストが来たら、検索結果のPersonのうち、同じガジェットをインストールしている
人のみ返す。
また、上記のコードからじゃわからないけど、
  1. filter=isFriendsWith  

が来た場合には、filterValueに友達IDが入っているものとして、
検索結果のうち、友達にfilterValueの友達IDの人がいるPersonのみ返す。
(isFriendsWithは良くわからない。。)
  1. filter=all  

の場合にはフィルタ処理は何もしない。
  1. filter=topFriends  

の場合には、二度手間だけど、検索結果のうち、友達のみ返す。
groupIdがfriends以外のときのみ有効。

で、filterと関連するへぼSQLの一部。
  1. <!-- for Filter -->  
  2. <dynamic>  
  3.   <isEqual property="filter" compareValue="all">  
  4.   </isEqual>  
  5.   <isEqual property="filter" compareValue="hasApp" prepend="AND">  
  6.     exists (  
  7.       select  
  8.         'X'  
  9.       from  
  10.         gms_user_gadget  
  11.       where  
  12.             gms_user_gadget.module_id = #filterValue#  
  13.         and gms_user_gadget.gms_person_id = friends.id  
  14.     )  
  15.   </isEqual>  
  16.   <isEqual property="filter" compareValue="topFriends" prepend="AND">  
  17.     <!-- UserIdに指定された人々の友達 -->  
  18.     exists (  
  19.       select  
  20.         'X'  
  21.       from  
  22.         gms_friend ff1,  
  23.         gms_person pp1  
  24.       where  
  25.             pp1.id = ff1.gms_person_id  
  26.         and pp1.login_id IN  
  27.           <iterate property="loginId"  
  28.                    open="(" close=")" conjunction="," >  
  29.               #loginId[]#  
  30.           </iterate>  
  31.         and ff1.friend_id = friends.id  
  32.     )  
  33.   </isEqual>  
  34.   <isEqual property="filter" compareValue="isFriendsWith">  
  35.     exists (  
  36.       select  
  37.         'X'  
  38.       from  
  39.         gms_friend ff2  
  40.       where  
  41.             ff2.gms_person_id = friends.id  
  42.         and ff2.friend_id = #filterValue#  
  43.     )  
  44.   </isEqual>  
  45.   <!-- ここから項目検索 -->  
  46.   <isEqual property="filter" compareValue="id" prepend="AND">  
  47.     <isEqual property="filterOperation" compareValue="contains">  
  48.       friends.id like '%$filterValue$%'  
  49.     </isEqual>  
  50.     <isEqual property="filterOperation" compareValue="equals">  
  51.       friends.id = #filterValue#  
  52.     </isEqual>  
  53.     <isEqual property="filterOperation" compareValue="startsWith">  
  54.       friends.id like '$filterValue$%'  
  55.     </isEqual>  
  56.     <isEqual property="filterOperation" compareValue="present">  
  57.       friends.id = #filterValue#  
  58.     </isEqual>  
  59.   </isEqual>  
  60.   <!-- ここまで。続きは随時追加 -->  
  61. </dynamic>  

なんだかもう、混乱。。。

ところで、firstとmaxというパラメータが指定できて、
ページングできそうなんだけど、そうすると総件数がほしいような。

どうやって取得するんだろう。
.