GAE/JでYUI2.7.0+Struts1.3をつかってAjaxでテキストを取得する。

2009/9/20: 追記 TextForwardActionが動かないと思って、見直したら、String.getByteでエンコーディングの指定を忘れていました。ので、それを修正しました。

========================================================

前回のログでは、YUI2.7のImageLoaderをつかって、Google App Engine/Java(GAE/J)のデータソースにある画像を表示してみた。ImageLoaderを使うと、レンダリングを非同期に行えるので、画像をたくさん貼るようなサイトでは顧客体験が劣化しなくてよい。

この延長線上として、デプロイしたテキストファイルをAjaxで取得できるだろう、ということが推測できる。デプロイ時にコンテキストパス下においたファイルを読み込むことができるのは、実証済みなので、それをレスポンスしてやればいい。Ajaxでのテキストデータの取得は、YUI2.7のConnectionManagerを使って定石通りに行う。

サンプル

サンプルは、以前に作成したものをベースにする(ログ「Connection; YAHOO.util.Connect.asyncRequestとresponseTextのサンプル」)。


初期画面は以下。


コンテキストパス配下(dataディレクトリ)に以下のテキスト(people.txt)を用意する。

hogehoge,Hawaii,1000.50
hoge,hokkaido,1200.50


上の画面で「実行」ボタンを押すと、以下のようにレスポンスヘッダーと、people.txtの内容を表示する。


また、サーバーサイドでエラーが発生した場合、以下のような表示とする(以下の画面はInternal Server Errorが発生した場合)。


以下の作業は、GAE/JのプラグインがインストールされたEclipse3.4(Ganymede)で行った。

プロジェクトの作成

前回のログのサンプルをコピーして、新規のWeb Applicationプロジェクトを作成する。
war/WEB-INF/dataディレクトリを作成し、そこに(上記の)people.txtをおく。ファイルエンコーディングutf-8とした。

Actionクラスの作成

前回のログで作成したサンプルプログラムは必要ないので削除する。
そして、以下のActionクラスを作成した。

TextForwardAction

package test;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.BaseAction;

public class TextForwardAction extends BaseAction {

    @Override
    public ActionForward execute(
                ActionMapping mapping, 
                ActionForm form,
                HttpServletRequest req, 
                HttpServletResponse res) 
            throws IOException
    {
        // サーブレットコンテキストの取得
        ServletContext sc = this.getServlet().getServletContext();

        String path = sc.getRealPath("/data/people.txt");
        
        String tmpStr;
        StringBuffer sb = new StringBuffer();
        byte[] bStr;

        try {
            FileInputStream fi;
            fi = new FileInputStream(path);
            InputStreamReader is = new InputStreamReader(fi, "UTF8");
            BufferedReader br = new BufferedReader(is);
            
            while ((tmpStr = br.readLine()) != null) {
                sb.append("<br>");
                sb.append(tmpStr);
            }
            bStr = sb.toString().getBytes("UTF-8");

            br.close();
            is.close();
            fi.close();

            res.setContentType("text/html; charset=UTF-8"); 
            ServletOutputStream outputStream;
            outputStream = res.getOutputStream();
            outputStream.write(bStr);
            outputStream.flush();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }

        return null;
    }
}

struts-config.xmlの変更

struts-config.xmlは以下のように変更。

<form-beans>
    <form-bean
	name="dummyForm"
	type="org.apache.struts.action.DynaActionForm" >
    </form-bean>
</form-beans>


<action-mappings>
    <action path="/forwardText"
		type="test.TextForwardAction"
		name="dummyForm"
		scope="request">
    </action>
</action-mappings>

index.htmlの変更

index.htmlが一番重たい。以下に全文を示す。scriptはmodule patternで作成した。

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Ajax+Strutsのサンプル</title>
<link rel="shortcut icon" href="../images/egp-favicon.ico" >

<style type="text/css"> 
body {
	margin:3;
	padding:0;
}

#header_result {
    width: 400px;
    background-color: #566F4E;
    color: white;
    border: 1px solid black;
}

#output {
    width: 400px;
    background-color: #6D739A;
    color: white;
    border: 1px solid black;
}

</style> 
 
<link rel="stylesheet" type="text/css" href="./scripts/lib/yui/build/fonts/fonts-min.css" /> 
<script type="text/javascript" src="./scripts/lib/yui/build/yahoo/yahoo-min.js"></script> 
<script type="text/javascript" src="./scripts/lib/yui/build/yahoo-dom-event/yahoo-dom-event.js"></script> 
<script type="text/javascript" src="./scripts/lib/yui/build/connection/connection-min.js"></script> 

<script>
XHRSample = function() {
	//aliasの定義
	var ImageLoader = YAHOO.util.ImageLoader;
	var Dom 	= YAHOO.util.Dom;
	var Connect = YAHOO.util.Connect;
	var Event 	= YAHOO.util.Event;
	
	//xhrハンドラー
	var xhrHandlers = {
	
		// 受信成功時の処理
		success : function(oj){
			// 取得したデータの表示
			Dom.get('output').innerHTML
								='data/people.txt: ' +oj.responseText;
			var sSuccess = 'レスポンスヘッダー: <br>' + oj.getAllResponseHeaders;
			// Http Headerの表示
			Dom.get('header_result').innerHTML = sSuccess;
		},

		// 受信失敗時の処理
		failure : function(oj){
			// backgroudを赤にする
			Dom.setStyle('output','background-color','red');
			Dom.get('output').innerHTML = 'data/people.txtの取得に失敗しました';

			// backgroudを赤にする
			Dom.setStyle('header_result','background-color','red');
			var sFailure='エラーステータス: ' + oj.status +'<br>' 
			 					+ 'ステータステキスト: ' + oj.statusText + '<br>' 
								+'読み込みに失敗しました。';
			// レスポンスオブジェクトの表示
			Dom.get('header_result').innerHTML = sFailure;
		},

		// テキストを読み込む
		startRequest : function(url){
			Connect.asyncRequest('GET',url,
					xhrCallback, null);
		} 
	};

	// XHR成功/失敗時の振り分け
	var xhrCallback = {
		success:xhrHandlers.success,
		failure:xhrHandlers.failure,
		// cache: falseのしないと、HttpServerがajax_test.txt
		// の内容をcacheしてしまう。
		cache: false,
		scope: xhrHandlers
	};

	// Buttonの処理 ; ボタンハンドラを定義
	var btnHandlers = {
		click:function(){
			xhrHandlers.startRequest("/forwardText.do")
		}
	};
	
	return{
		init:function() {
			Event.on('btn','click', btnHandlers.click);
		}
	};
}();


//DOMが完全にloadされたら、サンプルを初期化する。
YAHOO.util.Event.onDOMReady(
		//DomReadyイベントで発火するハンドラ
		XHRSample.init,
		//ハンドラに渡すオブジェクト(関数)
		XHRSample,
		//ハンドラは、上記のオブジェクトのスコープをもつ。   
		true
);

</script>
</head> 
 
<body class="yui-skin-sam"> 

<h2>YUI ConnectionManager + Struts</h2>

実行ボタンを押してください。
<br>
<ul>
<li>Serverのdata/people.txtを取得して、内容を表示します。
<li>取得の成功、失敗に関わらず、レスポンスオブジェクトのプロパティーを表示します。
</ul>
<input id="btn" type="button" value="実行" />
<br>
<br>

<div id="header_result">
レスポンスヘッダーを表示します<br/>
</div>
<br>
<br>

<div id="output">
data/peopleのコンテンツを表示します<br/>
</div>

</body>
</html>

テストとデプロイ

ローカルサーバーでテストをし、GAE/Jにデプロイする。