前回まではクライアント側のAndroidについて簡単なサンプルを作成しましたが、今回はStruts2で作成したサーバ側を紹介したいと思います。サーバ側の環境そのものは以前のStruts2+Springのものを流用しています。CentOS 5.7(64bit)のマシンにTomcat6、Eclipse、jre 6 24という開発環境です。異なる点は、通常はStruts2からの結果はjspなりファイルのダウンロードという形(stream)などで返すところを、Android側でレスポンス解析が簡単に済むという理由で配列形式のJSONで返すようにしたところです。Struts2付属のjarを追加して、それに伴う設定を追加または変更しています。AndroidWebAppという名でWebアプリケーションのプロジェクトを作成しました:
前述のようにStruts2+Springの環境を流用しているのでSpringに関する余計なjarもありますが、下記のファイルをWEB-INF/libに入れてあります:
aopalliance-1.0.jar
asm-3.2.jar
cglib-2.2.jar
commons-dbcp-1.2.2.jar
commons-digester-1.8.jar
commons-fileupload-1.2.1.jar
commons-io-1.3.2.jar
commons-logging-1.0.4.jar
commons-pool-1.5.3.jar
freemarker-2.3.15.jar
iText-2.1.7.jar
json-lib-2.1-jdk15.jar
mysql-connector-java-5.1.10-bin.jar
ognl-2.7.3.jar
spring-aop.jar
spring-beans-2.5.6.jar
spring-context-2.5.6.jar
spring-core-2.5.6.jar
spring-jdbc.jar
spring-tx.jar
spring-web-2.5.6.jar
struts2-convention-plugin-2.1.8.jar
struts2-core-2.1.8.jar
struts2-json-plugin-2.2.1.1.jar
struts2-spring-plugin-2.1.8.jar
velocity-1.6.2-dep.jar
velocity-tools-view-1.4.jar
xwork-core-2.1.6.jar
JSONのモジュールを有効する為に"struts-default"ではなく"json-default"からextendsさせています。AuthCheckerとは以前の記事でも使ったインターセプターによるログイン状態のチェッカーです。クラス名が変わっているだけで中身は同じものです。
<struts> <package name="mytest" namespace="/" extends="json-default"> <!-- インターセプター --> <interceptors> <interceptor name="login" class="webapp.interceptors.AuthChecker" /> <interceptor-stack name="myDefaultStack"> <interceptor-ref name="login" /> <interceptor-ref name="defaultStack" /> </interceptor-stack> </interceptors> <!-- デフォルトinterceptorを再定義 --> <default-interceptor-ref name="myDefaultStack"/> <global-results> <result name="login" type="redirectAction">login</result> <result name="login-success">/WEB-INF/jsp/menu.jsp</result> </global-results> </package> </struts>
Androidから何かしらのActionを受け取ると、上述のインターセプタが先に補足してログイン状態の有無をチェックするようになっています。チェックといっても単純にセッション変数にログインユーザの情報が格納されているか否かを判定してなければ"login"アクションに強制的に飛ばすだけです。"login"アクションはAndroidに対してMSG_NEED_LOGINで定義された文字列定数を返却しAndroidにログインを促します。
@ParentPackage(value="mytest") @InterceptorRefs({ @InterceptorRef(value="defaultStack"), @InterceptorRef(value="json"), }) public class DefaultAction extends ActionSupport { private static final long serialVersionUID = -5902882504249677314L; private final String MSG_NEED_LOGIN = "ログインしてください"; private final String MSG_LOGIN_SUCCESS = "ログインに成功しました"; private final String MSG_LOGIN_FAILURE = "ログインに失敗しました"; private List<String> resultList = null; private String user, password;
"login"アクションでは文字列型の配列"resultList"をJSON形式で返すようになっています(他のアクションでも同様)。
@Action(value="login", results={ @Result(name="success",type="json",params={"root","resultList","ignoreHierarchy","false"}), }) public String execute() throws Exception { this.resultList = new ArrayList<String>(); resultList.add("json"); resultList.add(MSG_NEED_LOGIN); return "success"; }
MSG_NEED_LOGINを受け取ったAndroidは、ユーザ名とパスワードをパラメータに"loginauth"アクションを呼び出します。
@Action(value="loginauth", results={ @Result(name="success",type="json",params={"root","resultList","ignoreHierarchy","false"}), }) public String loginauth() throws Exception { this.resultList = new ArrayList<String>(); resultList.add("json"); // 実験なので単純にAndroidから送られてきたユーザ名とパスワードを固定の文字列と比較しているだけです // 本来ならDBからユーザマスタを照会するなりまともな実装をすべきところですが省略しています if (this.user.equals("user") && this.password.equals("password")) { resultList.add(MSG_LOGIN_SUCCESS); // ログインに成功したらログインユーザの情報をセッションに保存します // ここでも実験の為、単にObjectを代入するのみです HttpSession session = ServletActionContext.getRequest().getSession(); session.setAttribute("LOGIINUSER", new Object()); } else resultList.add(MSG_LOGIN_FAILURE); return "success"; } public void setUser(String value) { this.user = value; } public void setPassword(String value) { this.password = value; } public void setResultList(List<String> value) { this.resultList = value; } public List<String> getResultList() { return this.resultList; } }
Androidは品番をパラメータに"search"アクションを呼び出してきます。POSTで送られてきた"hinban"パラメータから商品を照会し結果を返します。
@ParentPackage(value="mytest") public class SearchAction extends ActionSupport { private static final long serialVersionUID = 4838223809549728833L; private final String MSG_SEARCH_SUCCESS = "検索結果該当あり"; private final String MSG_SEARCH_FAILURE = "検索結果該当なし"; private List<String> resultList = null; private String hinban; @Action(value="search", results={ @Result(name="success",type="json",params={"root","resultList","ignoreHierarchy","false"}), }) @Override public String execute() throws Exception { this.resultList = new ArrayList<String>(); resultList.add("json"); // ここでも実験の為、単純なハードコーディングされた文字列との比較のみで // 商品照会を済ませています if (this.hinban.equals("123")) { resultList.add(MSG_SEARCH_SUCCESS); resultList.add("これは商品その1です。"); } else if (this.hinban.equals("567")) { resultList.add(MSG_SEARCH_SUCCESS); resultList.add("これは商品その2です。"); } else resultList.add(MSG_SEARCH_FAILURE); return "success"; } public void setHinban(String value) { this.hinban = value; } public String getHinban() { return this.hinban; } public void setResultList(List<String> value) { this.resultList = value; } public List<String> getResultList() { return this.resultList; } }
思い切った手抜きですが、クライアントとサーバ間の動きをみるだけならこれで間に合いますねw
次回はAndroid側のログイン処理の紹介をしていこうかと思います。