2011年2月10日木曜日

slim3 + JSONICメモ3



前回slim3+JSONICメモ2のエントリーでRESTfulでAjaxなCRUDアプリの実装内容をメモ。
タイトルと説明を入力して登録、一覧リストに表示された行を選択して
編集→更新/削除と動作します。
もちろんデータはBigTableへ反映されています。
こちらからアプリを試せます。

前回と同様にJSONICのRESTサーブレットを使用してコントローラを実装します。
JSONICのRESTサーブレットを使用すると、リクエストからの値をJSON形式で受取る事ができます。
RESTサーブレットが変換してくれます。
そして下記のようなHTTPメソッドに対応したメソッドをコントローラに用意すれば、
javascript側でHTTPメソッドを変えて投げてやれば対応したメソッドを実行してくれます。

GETfind()
POSTcreate()
PUTupdate()
DELETEdelete()
※DELETEのみPOSTでクエリ変数に"?_method=DELETEとします。

使用できるようにするにはweb.xmlに設定が必要です。
前前回のエントリー参照
今回はユニットテストは無視です。

それでは下記の順に作っていきます。

1.IndexController ← (slim3のbuld.xmlで生成。初回アクセス時に呼ばれる)
2.MyReferenceModel ← (永続化するデータモデル)
3.MyReferenceService ← (CRUD処理するサービス)
4.index.jsp ← (アプリケーション画面)
5.MyRefRESTfulController ← (新規に作成するコントローラクラス)
6.common.js ← (クライアント側を制御するjavascript)

1.IndexController


slim3のbuild.xml・gen-controllerを実行し、IndexControllerを生成します。

内容は初回にアクセスされた時、サービスからリストを取得しJSON形式に変換し、
"jsonList"という名前でJSON文字列をリクエストにセットし、"index.jsp"へforwardするだけ。

public class IndexController extends Controller {

    private MyReferenceService service = new MyReferenceService();

    @Override
    public Navigation run() throws Exception {

        List list = service.getMyReferenceList();

        String jsonData = JSON.encode(list);
        requestScope("jsonList", jsonData);

        return forward("index.jsp");
    }
}

この時点でサービス・モデルはまだないので順次作っていきます。

2.MyReferenceModel


slim3のbuild.xml・gen-modelを実行し、MyReferenceModelを生成します。
生成するとserialVersionUIDとkeyとversionのフィールドのみ出来ています。
今回は下記のようにフィールドを追加しgetter/setterを追加します。

@Model(schemaVersion = 1)
public class MyReferenceModel implements Serializable {

    private static final long serialVersionUID = 1L;

    @Attribute(primaryKey = true)
    private Key key;

    @Attribute(version = true)
    private Long version;

    @Attribute(persistent=false)
    private String strKey;

    private String title;

    private String description;

    private Date createDate = new Date();

・・・

    public void setStrKey(String strKey) {
        this.strKey = strKey;
        // key文字列を設定
        if (this.key != null) {
            this.strKey = KeyFactory.keyToString(this.key);
        }
    }

・・・

    public void setKey(Key key) {
        this.key = key;
        // key文字列を設定
        if (key != null) {
            this.strKey = KeyFactory.keyToString(key);
        }
    }

・・・


strKeyフィールドに"@Attribute(persistent=false)"というアノテーションを追加しています。
これはこのフィールドのみ永続化対象としない設定です。
keyとstrKeyのsetterメソッドを少し追加します。

com.google.appengine.api.datastore.Key型を文字列にエンコードして扱う必要があり、
通常のWebアプリケーションであれば、jspではslim3・JSTLファンクション"${f:h(xxx)}"で
KeyをBase64エンコードしたり、コントローラでは"asKey()"メソッドなど使えば簡単に扱えます。
今回はJSONとして送受信したいので、ちょっと現状は簡単に使えなさそうです。
(近いうちにslim3もJSON対応してくるでしょうね。)

なので、JSON内にBase64エンコードされたKey文字列を保持してやる必要があり、
strKeyフィールドを用意しています。(無理矢理な方法ですが)
で、Keyをsetする時にstrKeyも同時に設定する処理を追加しています。


今回はここまでで、次回サービスの実装を行いたいと思います。



参考サイト:
blazedsを使用の内容ですが、modelでKey文字列保持して永続化対象させないところを参考にさせて頂きました。↓↓↓
[blazeds][slim3] アップデート(更新処理)が、できま10(テン)!

2011年2月8日火曜日

slim3 + JSONICメモ2

slim3 + JSONIC でCRUD実装してましたのでメモ。
以前にslim3 + JSONICで試しに作ったものをベースに作りました。

とりあえず動くものはこちら
javascriptでCRUD処理を非同期に行っていて画面遷移が発生しません。

はまったのは、GAE環境ではJSONIC・RESTサーブレットのHTTP/DELETEメソッド送信時、削除処理してくれませんでした。
(ローカルでは普通にDELETEできるのに。)
そこで、JSONIC・RESTサーブレットで用意されている

"PUT/DELETEが使えない場合があります。そのような場合の代替手段として、クエリ変数に「_method=HTTP Method名」を指定することもできます。"


というのを参考に、DELETEの場合のみPOST+クエリ変数追加にして試したところうまくいきました。
これが正解かどうかはわかりませんが。
あとはControllerにfind(),create(Xxxx model),update(Xxxx mode),delete(Xxxx model)を用意。
RESTサーブレットが自動でJSON → JavaBean (model) へ変換して、HTTPメソッドを判断し各メソッドへ振分けてくれます。

下記が非同期REST送受信javascriptです。
(jqueryとjquery.jsonというライブラリを使用する前提です)
...

/** 非同期処理 */
function sendJSON(method, jsonval, actionURI, displayId, delURI) {
 // 一覧取得URI設定
 var getURI = actionURI;
 // delURIが存在する場合「削除処理」と判断
 if (delURI != undefined) actionURI = delURI;
 $.ajax({
  type:method,
  url:actionURI,
  contentType:"application/json",
  data:jsonval,
  cache:false,
  success:function(data){displayMessage(getURI,displayId);}
  });
}
/** 登録を行います */
function createSend(form, actionURI, displayId) {
 sendJSON("POST", createJSON(form), actionURI, displayId);
}
/** 更新を行います */
function updateSend(form, actionURI, displayId) {
 sendJSON("PUT", createJSON(form), actionURI, displayId);
}
/** 削除処理を行います */
function deleteSend(form, actionURI, displayId) {
 var delURI = actionURI+"?_method=DELETE";
 // HTTP/DELETEメソッドが使用できなさそうなのでクエリ変数へDELETE設定
 sendJSON("POST", createJSON(form), actionURI, displayId, delURI);
}
/** formの内容をJSON形式に変換 */
function createJSON(form) {
 var form = $("#" + form);
 var param = {};
 $(form.serializeArray()).each(function(i,v){param[v.name] = v.value;});
 var jsonval = $.toJSON(param);
 return jsonval;
}
...

実装の中身は次のエントリーで書いていこうと思います。

あと、RESTサーブレットを使用するとslim3コントローラを使用しない為、
ユニットテストが出来ないということになります。
せっかく標準で用意されていて、簡単にテストが行えるのでなんとかしてテストできないか考えないと...。
ていうかRESTfulに実装しなければいいのか。

2011年2月7日月曜日

Eclipse プラグイン開発セミナー受講してきました

ので後々の為にメモ。

講師の方が資料をアップして下さっていました。
この資料がとても分かりやすく、セミナーを受講していない人でも
ある程度簡単に単純なEclipseプラグインができてしまうと思います。

普段仕事でEclipseとEclipseプラグインを使用してjavaを書いていましたが、
自分の慣れた使い方しかしていない事に気づきました。

プラグイン・スパイとか勉強になりました。

ありがとうございました。m(_ _)m

何かプラグイン作ったらブログにのせていこうと思います。

2011年1月15日土曜日

Android:JSON表示クライアントアプリ

こないだGoogle App Engine + slim3 + JSONICで作ったサンプルをAndroidで表示するアプリを作ってみたのでメモ。

とりあえず実現したい事は、JSONをHTTPのgetで受け取ってListViewで表示までを目標に。
で、下記のように実装。
(URIベタ書きですが...、とりあえず動かしたいので。)

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class JsonListActivity extends Activity {

 private String strUri = "http://1.latest.slim3demo001.appspot.com/AjaxTweet.json";

 private String json = null;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpParams params = httpClient.getParams();
        HttpConnectionParams.setConnectionTimeout(params, 1000);
        HttpConnectionParams.setSoTimeout(params, 1000);
        HttpGet httpRequest = new HttpGet(strUri);
        HttpResponse httpResponse = null;

        try {
   httpResponse = httpClient.execute(httpRequest);
  } catch (ClientProtocolException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
  if (httpResponse != null && httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
   HttpEntity httpEntity = httpResponse.getEntity();

   try {
    json = EntityUtils.toString(httpEntity);
   } catch (ParseException e) {
    e.printStackTrace();
   } catch (IOException e) {
    e.printStackTrace();
   } finally {
    try {
     httpEntity.consumeContent();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }

  httpClient.getConnectionManager().shutdown();
  ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1);

  try {
   JSONArray jsonArray = new JSONArray(json);
   int cnt = jsonArray.length();
   List jlist = new ArrayList();
   for (int i = 0; i < cnt; i++) {
    jlist.add(jsonArray.getJSONObject(i));
   }
   for (JSONObject obj : jlist) {
    String item = obj.getString("content");
    adapter.add(item);
   }
  } catch (JSONException e) {
   e.printStackTrace();
  }
  ListView listView = (ListView)findViewById(R.id.jsonlistview);
  listView.setAdapter(adapter);
    }
}
main.xmlは下記のように
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <ListView
     android:id="@+id/jsonlistview"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"/>
</LinearLayout>
ネットにつなげるので、Manifestファイルにも"android.permission.INTERNET"を追加
<uses-permission android:name="android.permission.INTERNET"/>
一応無事に動きました。
参考サイト: Android で JSON を使おう ~ 前編~CommentsAdd Star Android で JSON を使おう ~ 後編 ~CommentsAdd Star

2011年1月12日水曜日

Androidメモ:Android で証明書が期限切れ

久しぶりに以前作ったサンプルプログラムを参考にしようとEclipseでプロジェクトを開いたら、
「Error generating final archive: Debug Certificate expired ...」
とか言われてビルドできませんでした。

検索してみると、Android SDK の debug 用キーストアの有効期限がすぎているみたい。

C:\Documents and Settings\USER_NAME\.android以下のdebug.keystoreを削除したら正常にビルドしてくれました。

参考サイト:Android で証明書が期限切れというエラーがでて apk が作成できない

2011年1月7日金曜日

JavaのList内容をソートする

セッションに貯めたレコードのリストをソートする必要があったのでメモ

 /**
  * Listソート処理(Key昇順)
  * @param list
  */
 public void sortKeyAsc(List<DataModel> list) {
  Collections.sort(list, new Comparator<DataModel>() {
   public int compare(DataModel t1, DataModel t2) {
    // Key昇順
    return t1.getKey().compareTo(t2.getKey());
   }
  });
 }
降順はreturnしてる箇所を下記のように逆にする。
  return t2.getKey().compareTo(t1.getKey());

2011年1月6日木曜日

slim3 + JSONICメモ




slim3のtutorialをJSONICを使ってajaxでTweetするようにと、RESTfulに変えてみたのでメモ
できたのはこんなかんじ


JSONICの使い方メモ

準備

 最新のjsonic.jarをダウンロードしてプロジェクトに配置・ライブラリに追加

※追記 2011/02/01 jsonic.jarの配置場所が、war/WEB-INF/lib以下に置かないとローカルで動きませんでした。

エンコード・デコード

 JSON.decode(String json, XXX.class)でjson→javaオブジェクト
 JSON.encode(XXX)でjavaオブジェクト→json

WebサービスAPIを使う

RESTサーブレットを使用する場合

web.xmlへ追加


     restServlet
     net.arnx.jsonic.web.RESTServlet
     
      config{
       "mappings":{
        "/{class}.json":"tutorial.controller.twitter.${class}Controller"
       }
      }
      
    
    ・
    ・
    ・
    
     restServlet
     *.json
    

コントローラは下記のように実装。

RESTfullな操作はJSONICのRESTサーブレットがやってくれるので、

HTTP MethodのGETでfind()メソッド、POSTでcreate()メソッドを呼出すように実装します。


import java.util.List;

import tutorial.model.Tweet;
import tutorial.service.TwitterService;

public class AjaxTweetController {

    private TwitterService service;

    public List find() {

        this.service = new TwitterService();

        List tweetList = service.getTweetList();

        return tweetList;
    }

    public void create(Tweet tweet) {

        this.service = new TwitterService();

        this.service.tweet(tweet);
    }
}

TwitterServiceにメソッド追加

/**
     * JSON形式でもらってDataStoreへ追加
     * @param jsonTweet
     * @return String
     */
    public String jsonTweet(String jsonTweet) {

        Tweet tweet = JSON.decode(jsonTweet, Tweet.class);
        tweet = this.tweet(tweet);
        String retJsonTweet = JSON.encode(tweet);
        return retJsonTweet;
    }

viewは下記

<p>What are you doing?</p>
<form id="tweetForm" action="tweet" method="post" name="tweetForm">
<textarea id="content" name="content"></textarea><br/>
<input type="button" value="tweet" onclick="sendTweetJSON()"/>
</form>

<div id="ajaxTweetList"></div>

</body>
<script type="text/javascript">

function sendTweetJSON() {
 var form = $("#tweetForm");
 var param = {};
 $(form.serializeArray()).each(function(i,v){
  param[v.name] = v.value;
 });
 var jsonval = $.toJSON(param);

 $.ajax({
  type:"POST",
  url:"/AjaxTweet.json",
  contentType:"application/json",
  data:jsonval,
  cache:false,
  success:function(data){getAjaxList();}
  });
}

function displayList(jsonData) {
 var list = jsonData;
 var resultData = "<table><tr><th>TweetList</th></tr>";

 for (var i in list) {
  var content = list[i].content;
  resultData += "<tr><td>" + content + "</td></tr>";
 }
 resultData += "</table>";
 return resultData;
}

function getAjaxList() {
 var list;
 $.get("/AjaxTweet.json", function(data){
  $('#ajaxTweetList').html(displayList(data));
 });
}

$(function() {
 getAjaxList();
});
</script>

jqueryを使用しているのでheadでライブラリを読込み

<script type="text/javascript" src="/js/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="/js/jquery.json-2.2.min.js"></script>
<script type="text/javascript" src="/js/deserialize.js"></script>



参考サイト:
jQueryでフォームの内容をJSONで投げ、受け取ったJSONをフォームに戻す方法

JSONIC