GAE/JのURL FetchでYAHOO! Web Search APIを使う

YUI(先月v3がリリースされましたねぇ。v2も2.8.0rになってます)にはクロスドメインJavascriptライブラリー「Getツール」があるのだが、GAE/JのURL FetchをつかってProxyとなるプログラムを作成してみた。
Fetchしたのは、YAHOO Web Search API。たとえば、「YAHOO」というキーワードで検索した場合、以下のようなXMLが戻ってくる。

appidには、自分のapplication idが入ってくる。

<?xml version="1.0" encoding="UTF-8"?>
<ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:yahoo:jp:srch" xsi:schemaLocation="urn:yahoo:jp:srch http://search.yahooapis.jp/WebSearchService/V1/WebSearchResponse.xsd" totalResultsAvailable="5920000000" totalResultsReturned="10" firstResultPosition="1" pgr="278352159">

<Result>
<Title>Yahoo! JAPANホームページ</Title>
<Summary>ポータルサイト。検索、オークション、ショッピング、トピックス、ファイナンス、ブログ、メール、モバイル、きっず等のサービス。 ... Yahoo!カテゴリ登録審査 ... どこでもYahoo! JAPAN. 無料で遊べるイケメン育成ゲーム「きらぷろ」が登場 ...</Summary>
<Url>http://www.yahoo.co.jp/</Url>
<ClickUrl>http://wrs.search.yahoo.co.jp/l=WS1/R=1/wdm=0/IPC=jp/ln=ja/H=0/;_ylt=A8vY5mtzacJKZ54AgV0DUAx.;_ylu=X3oDMTB2cXVjNTM5BGNvbG8DdwRsA1dTMQRwb3MDMQRzZWMDc3IEdnRpZAM-/SIG=11bdqoiti/EXP=1254341363/*-http%3A//www.yahoo.co.jp/</ClickUrl>
<ModificationDate>1254150000</ModificationDate>
<MimeType>text/html</MimeType>
<Cache>
<Url>http://wrs.search.yahoo.co.jp/l=WS5/R=1/;_ylt=A8vY5mtzacJKZ54Agl0DUAx.;_ylu=X3oDMTBwOHA5a2tvBGNvbG8DdwRwb3MDMQRzZWMDc3IEdnRpZAM-/SIG=175hp3ksr/EXP=1254341363/*-http%3A//cache.yahoofs.jp/search/cache?ei=UTF-8&amp;appid=[your appid]&amp;query=YAHOO&amp;results=10&amp;u=www.yahoo.co.jp/&amp;w=yahoo+yahoo%21&amp;d=IVojrd29TjVK&amp;icp=1&amp;.intl=jp</Url>
<Size>80115</Size>
</Cache>
</Result>

<Result>
<Title>Yahoo!</Title>
<Summary>アメリカのYahoo! ... Yahoo! Search ... oneSearch. Get up-to-the-minute flight info and more with Yahoo! oneSearch on your phone. ...</Summary>
<Url>http://www.yahoo.com/</Url>
<ClickUrl>http://wrs.search.yahoo.co.jp/l=WS1/R=2/wdm=0/IPC=jp/ln=en/H=0/;_ylt=A8vY5mtzacJKZ54Ag10DUAx.;_ylu=X3oDMTB2ZjQ4dDExBGNvbG8DdwRsA1dTMQRwb3MDMgRzZWMDc3IEdnRpZAM-/SIG=119omo837/EXP=1254341363/*-http%3A//www.yahoo.com/</ClickUrl><ModificationDate>1254063600</ModificationDate>
<MimeType>text/html</MimeType>
</Result>

<Result>
<Title>Yahoo!メール</Title>
<Summary>ブラウザ上から利用できる無料メールサービス。メールソフトによる送受信や外部メールの確認、フィルター、アドレスブック機能、ウイルスチェック機能も。</Summary>
<Url>http://mail.yahoo.co.jp/</Url>
<ClickUrl>http://wrs.search.yahoo.co.jp/l=WS1/R=3/wdm=0/IPC=jp/ln=ja/H=0/;_ylt=A8vY5mtzacJKZ54AhF0DUAx.;_ylu=X3oDMTB2M3MzdmZoBGNvbG8DdwRsA1dTMQRwb3MDMwRzZWMDc3IEdnRpZAM-/SIG=11civb7nv/EXP=1254341363/*-http%3A//mail.yahoo.co.jp/</ClickUrl>
<ModificationDate>1254063600</ModificationDate>
<MimeType>text/html</MimeType>
<Cache>
<Url>http://wrs.search.yahoo.co.jp/l=WS5/R=3/;_ylt=A8vY5mtzacJKZ54AhV0DUAx.;_ylu=X3oDMTBwZ2dpZ2ZkBGNvbG8DdwRwb3MDMwRzZWMDc3IEdnRpZAM-/SIG=1761m6buk/EXP=1254341363/*-http%3A//cache.yahoofs.jp/search/cache?ei=UTF-8&amp;appid=[your appid]&amp;query=YAHOO&amp;results=10&amp;u=mail.yahoo.co.jp/&amp;w=yahoo+yahoo%21&amp;d=F-VRW929TjKa&amp;icp=1&amp;.intl=jp</Url>
<Size>13011</Size>
</Cache>
</Result>

<Result>
<Title>Yahoo!きっず</Title>
<Summary>小中学生のためのインターネットガイド。 ... Yahoo!きっずの特長やイベントの紹介、インターネットに潜む危険から子どもを守るためのヒント、 ... 子どもと一緒にインターネットをするなら、Yahoo! あんしんねっと(無料) ...</Summary>
<Url>http://kids.yahoo.co.jp/</Url>
<ClickUrl>http://wrs.search.yahoo.co.jp/l=WS1/R=4/wdm=0/IPC=jp/ln=ja/H=0/;_ylt=A8vY5mtzacJKZ54Ahl0DUAx.;_ylu=X3oDMTB2OWM1MXQ5BGNvbG8DdwRsA1dTMQRwb3MDNARzZWMDc3IEdnRpZAM-/SIG=11c0dna4c/EXP=1254341363/*-http%3A//kids.yahoo.co.jp/</ClickUrl>
<ModificationDate>1254150000</ModificationDate><MimeType>text/html</MimeType>
<Cache>
<Url>http://wrs.search.yahoo.co.jp/l=WS5/R=4/;_ylt=A8vY5mtzacJKZ54Ah10DUAx.;_ylu=X3oDMTBwYzFnNmkxBGNvbG8DdwRwb3MDNARzZWMDc3IEdnRpZAM-/SIG=1766325hg/EXP=1254341363/*-http%3A//cache.yahoofs.jp/search/cache?ei=UTF-8&amp;appid=[your appid]&amp;query=YAHOO&amp;results=10&amp;u=kids.yahoo.co.jp/&amp;w=yahoo+yahoo%21&amp;d=DheCcN29TjT3&amp;icp=1&amp;.intl=jp</Url>
<Size>64484</Size>
</Cache>
</Result>

<Result>
<Title>Yahoo!知恵袋</Title>
<Summary>ジャンル別の質問、回答サービス。回答数ランキング、質問、回答の履歴を確認できる「My知恵袋」、知恵コレクション等。</Summary>
<Url>http://chiebukuro.yahoo.co.jp/</Url>
<ClickUrl>http://wrs.search.yahoo.co.jp/l=WS1/R=5/wdm=0/IPC=jp/ln=ja/H=0/;_ylt=A8vY5mtzacJKZ54AiF0DUAx.;_ylu=X3oDMTB2NW50aG1hBGNvbG8DdwRsA1dTMQRwb3MDNQRzZWMDc3IEdnRpZAM-/SIG=11ivfjb6d/EXP=1254341363/*-http%3A//chiebukuro.yahoo.co.jp/</ClickUrl>
<ModificationDate>1254150000</ModificationDate>
<MimeType>text/html</MimeType>
<Cache>
<Url>http://wrs.search.yahoo.co.jp/l=WS5/R=5/;_ylt=A8vY5mtzacJKZ54AiV0DUAx.;_ylu=X3oDMTBwdDJ0dDBoBGNvbG8DdwRwb3MDNQRzZWMDc3IEdnRpZAM-/SIG=17cjbh1pk/EXP=1254341363/*-http%3A//cache.yahoofs.jp/search/cache?ei=UTF-8&amp;appid=[your appid]&amp;query=YAHOO&amp;results=10&amp;u=chiebukuro.yahoo.co.jp/&amp;w=yahoo+yahoo%21&amp;d=M-x60t29TjPe&amp;icp=1&amp;.intl=jp</Url>
<Size>82791</Size>
</Cache>
</Result>

<Result>
<Title>Yahoo!オークションホームページ</Title>
<Summary>不要品をユーザー間で自由売買。チャリティーオークション、エスクローサービス等も。 ... Yahoo! JAPAN - オークション利用登録 - ヘルプ ... 「品川女子学院×Yahoo!オークション」夏休みの課題選考. 最近よく届きます ...</Summary>
<Url>http://auctions.yahoo.co.jp/</Url>
<ClickUrl>http://wrs.search.yahoo.co.jp/l=WS1/R=6/wdm=0/IPC=jp/ln=ja/H=0/;_ylt=A8vY5mtzacJKZ54Ail0DUAx.;_ylu=X3oDMTB2azJrdmpwBGNvbG8DdwRsA1dTMQRwb3MDNgRzZWMDc3IEdnRpZAM-/SIG=11g1gugog/EXP=1254341363/*-http%3A//auctions.yahoo.co.jp/</ClickUrl>
<ModificationDate>1254150000</ModificationDate>
<MimeType>text/html</MimeType>

<Cache>
<Url>http://wrs.search.yahoo.co.jp/l=WS5/R=6/;_ylt=A8vY5mtzacJKZ54Ai10DUAx.;_ylu=X3oDMTBwYmwxdWo4BGNvbG8DdwRwb3MDNgRzZWMDc3IEdnRpZAM-/SIG=17a22mbm0/EXP=1254341363/*-http%3A//cache.yahoofs.jp/search/cache?ei=UTF-8&amp;appid=[your appid]&amp;query=YAHOO&amp;results=10&amp;u=auctions.yahoo.co.jp/&amp;w=yahoo+yahoo%21&amp;d=RMH9It29TjTh&amp;icp=1&amp;.intl=jp</Url>
<Size>82398</Size>
</Cache>
</Result>

<Result>
<Title>Yahoo!路線情報</Title>
<Summary>出発地、目的地から、鉄道、航空便等を対象に路線経路、電車運賃、定期代、最終電車時刻表を検索。全国のJR、私鉄、地下鉄の運行情報も。 ... Yahoo!JAPAN IDでログインすると、「運行情報の設定」から、よく使う路線を登録し、 ...</Summary>
<Url>http://transit.map.yahoo.co.jp/</Url>
<ClickUrl>http://wrs.search.yahoo.co.jp/l=WS1/R=7/wdm=0/IPC=jp/ln=ja/H=0/;_ylt=A8vY5mtzacJKZ54AjF0DUAx.;_ylu=X3oDMTB2bDEyNXM5BGNvbG8DdwRsA1dTMQRwb3MDNwRzZWMDc3IEdnRpZAM-/SIG=11j83nkgs/EXP=1254341363/*-http%3A//transit.map.yahoo.co.jp/</ClickUrl>
<ModificationDate>1254063600</ModificationDate>
<MimeType>text/html</MimeType>
<Cache>
<Url>http://wrs.search.yahoo.co.jp/l=WS5/R=7/;_ylt=A8vY5mtzacJKZ54AjV0DUAx.;_ylu=X3oDMTBwMTB0ZWFoBGNvbG8DdwRwb3MDNwRzZWMDc3IEdnRpZAM-/SIG=17du41g9i/EXP=1254341363/*-http%3A//cache.yahoofs.jp/search/cache?ei=UTF-8&amp;appid=[your appid]&amp;query=YAHOO&amp;results=10&amp;u=transit.map.yahoo.co.jp/&amp;w=yahoo+yahoo%21&amp;d=aJop8N29TjMQ&amp;icp=1&amp;.intl=jp</Url>
<Size>71645</Size>
</Cache>
</Result>

<Result>
<Title>Yahoo!グループ - 無料でメーリングリストを作成</Title>
<Summary>無料で開設できるメーリングリストサービス。予定表や共有フォルダ機能も。 ... Yahoo!グループニュースレターに連載されているシリーズ「管理人に聞け! ... Yahoo!グループひろば. 多くのグループが紹介を行っているグループです。 ...</Summary>
<Url>http://groups.yahoo.co.jp/</Url>
<ClickUrl>http://wrs.search.yahoo.co.jp/l=WS1/R=8/wdm=0/IPC=jp/ln=ja/H=0/;_ylt=A8vY5mtzacJKZ54Ajl0DUAx.;_ylu=X3oDMTB2bTU4Zm85BGNvbG8DdwRsA1dTMQRwb3MDOARzZWMDc3IEdnRpZAM-/SIG=11ep5jjua/EXP=1254341363/*-http%3A//groups.yahoo.co.jp/</ClickUrl>
<ModificationDate>1254150000</ModificationDate>
<MimeType>text/html</MimeType>
<Cache>
<Url>http://wrs.search.yahoo.co.jp/l=WS5/R=8/;_ylt=A8vY5mtzacJKZ54Aj10DUAx.;_ylu=X3oDMTBwYTZ1aGtmBGNvbG8DdwRwb3MDOARzZWMDc3IEdnRpZAM-/SIG=178nttjuq/EXP=1254341363/*-http%3A//cache.yahoofs.jp/search/cache?ei=UTF-8&amp;appid=[your appid]&amp;query=YAHOO&amp;results=10&amp;u=groups.yahoo.co.jp/&amp;w=yahoo+yahoo%21&amp;d=XMKwGN29TjTL&amp;icp=1&amp;.intl=jp</Url>
<Size>27815</Size>
</Cache>
</Result>

<Result>
<Title>Yahoo!ブログ</Title>
<Summary>無料ブログサービス。容量無制限。 ... Yahoo!ブログでは、インターネット全体に公開されている投稿記事、投稿日時などの投稿に関する情報を、研究目的利用に限定して大学、独立行政法人等の研究機関に提供します。 ...</Summary>
<Url>http://blogs.yahoo.co.jp/</Url>
<ClickUrl>http://wrs.search.yahoo.co.jp/l=WS1/R=9/wdm=0/IPC=jp/ln=ja/H=0/;_ylt=A8vY5mtzacJKZ54AkF0DUAx.;_ylu=X3oDMTB2dW8yNmN0BGNvbG8DdwRsA1dTMQRwb3MDOQRzZWMDc3IEdnRpZAM-/SIG=11dihhg5k/EXP=1254341363/*-http%3A//blogs.yahoo.co.jp/</ClickUrl>
<ModificationDate>1254150000</ModificationDate>
<MimeType>text/html</MimeType>
<Cache>
<Url>http://wrs.search.yahoo.co.jp/l=WS5/R=9/;_ylt=A8vY5mtzacJKZ54AkV0DUAx.;_ylu=X3oDMTBwOTRzYmZlBGNvbG8DdwRwb3MDOQRzZWMDc3IEdnRpZAM-/SIG=177hm12tf/EXP=1254341363/*-http%3A//cache.yahoofs.jp/search/cache?ei=UTF-8&amp;appid=[your appid]&amp;query=YAHOO&amp;results=10&amp;u=blogs.yahoo.co.jp/&amp;w=yahoo+yahoo%21&amp;d=DgNm1N29TjTN&amp;icp=1&amp;.intl=jp</Url>
<Size>59707</Size>
</Cache>
</Result>

<Result>
<Title>Yahoo!メール - 無料なのに充実! ずっと使い続けられるメール</Title><Summary>1つのYahoo! JAPAN IDにつき1人の利用者とした場合。( 2009年5月現在) ... サービス連携 - 一緒に使うとさらに便利なYahoo! JAPANのサービス. 新着メールをすぐに確認できるYahoo!ツールバーなど 詳細はこちら ...</Summary>
<Url>http://promo.mail.yahoo.co.jp/</Url>
<ClickUrl>http://wrs.search.yahoo.co.jp/l=WS1/R=10/wdm=0/IPC=jp/ln=ja/H=0/;_ylt=A8vY5mtzacJKZ54Akl0DUAx.;_ylu=X3oDMTEwc3RuNTk4BGNvbG8DdwRsA1dTMQRwb3MDMTAEc2VjA3NyBHZ0aWQD/SIG=11if69e2n/EXP=1254341363/*-http%3A//promo.mail.yahoo.co.jp/</ClickUrl>
<ModificationDate>1254063600</ModificationDate>
<MimeType>text/html</MimeType>
<Cache>
<Url>http://wrs.search.yahoo.co.jp/l=WS5/R=10/;_ylt=A8vY5mtzacJKZ54Ak10DUAx.;_ylu=X3oDMTBxNTUzbTg4BGNvbG8DdwRwb3MDMTAEc2VjA3NyBHZ0aWQD/SIG=17cl635r9/EXP=1254341363/*-http%3A//cache.yahoofs.jp/search/cache?ei=UTF-8&amp;appid=[your appid]&amp;query=YAHOO&amp;results=10&amp;u=promo.mail.yahoo.co.jp/&amp;w=yahoo+yahoo%21&amp;d=ATIFJd29Ti6F&amp;icp=1&amp;.intl=jp</Url>
<Size>7647</Size>
</Cache>
</Result>
</ResultSet>
<!-- pls105.search.tnz.yahoo.co.jp uncompressed/chunked Wed Sep 30 05:09:23 JST 2009 -->

GAE/Jではファイルシステムへの書き出しができないので、取得した上記のデータはXPathで解析して、CSV(Text)形式でクライアントに渡すことにした。
URL FetchのトリガーはjspからのXHRで発生させ、< Title >と< Url >下のテキストノードを、YUIのData Tableに表示した。

いつものことであるが、JavaXMLをParseするのはやりにくいし、PHPと比較して大仰な感が否めない(mezawa氏が、同じようなことをいっていたが、その通りだと思う)。とりあえずは、メモとして残しておこう。

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

プロジェクトの作成

YUI2.7.0とStruts1.3を使うので、先のログ「GAE/Jの認証をタイムアウトさせる」からプロジェクトをコピーする。HttpClientが必要になるので、apache.orgからダウンロードする(HttpClient v4.0を用いた)。

log4j.propertiesの修正

log4j.logger.httpclient.wire.header=WARN,A2

の一文を追加する。以下に全文を示す。

# Configure the console as our one appender
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] - %m%n

# tighten logging on the DataNucleus Categories
log4j.category.DataNucleus.JDO=WARN, A1
log4j.category.DataNucleus.Persistence=WARN, A1
log4j.category.DataNucleus.Cache=WARN, A1
log4j.category.DataNucleus.MetaData=WARN, A1
log4j.category.DataNucleus.General=WARN, A1
log4j.category.DataNucleus.Utility=WARN, A1
log4j.category.DataNucleus.Transaction=WARN, A1
log4j.category.DataNucleus.Datastore=WARN, A1
log4j.category.DataNucleus.ClassLoading=WARN, A1
log4j.category.DataNucleus.Plugin=WARN, A1
log4j.category.DataNucleus.ValueGeneration=WARN, A1
log4j.category.DataNucleus.Enhancer=WARN, A1
log4j.category.DataNucleus.SchemaTool=WARN, A1

log4j.rootLogger=DEBUG,A2
log4j.appender.A2=org.apache.log4j.ConsoleAppender
log4j.appender.A2.layout=org.apache.log4j.PatternLayout
log4j.appender.A2.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] - %m%n
log4j.logger.org.apache.struts=WARN,A2
log4j.logger.org.apache.commons=WARN,A2
log4j.logger.httpclient.wire.header=WARN,A2

Actionの作成

コピーしてきた.javaファイル(javaプログラム)は不要なので破棄し、次のActionクラスを作成する。このサンプルでは、URL Fetchをする際のキーワードを「Yahoo(英半角)」とした。日本語を利用する際には、urlエンコーディングをしなければならない。また、XMLをParseするに際してはIBMのサイトを参考にした。Yahoo! Web Searchから得られる結果には、name spaceが定義されているので、それを意識しなくてはならない(name spaceが定義されている場合のXPathの利用については、こちらのサイトを参考にした。)。

package test;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import java.net.MalformedURLException;
import java.net.URL;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

public class YahooSearchAction extends Action {
      private static final Log log = LogFactory.getLog(YahooSearchAction.class);

	private static final String YAHOO_SEARCH_URL=
		"http://search.yahooapis.jp/WebSearchService/V1/webSearch?appid=WNislC2xg657Ewox0C7K8jehGPbPyM3vrmx99lgvZPkjr8I.GQ6gnLfTwtdmmcg7Jrg-&query=yahoo&results=10";
	
	private static final String PATH_TITLE 
    	= "/urn:yahoo:jp:srch:ResultSet/urn:yahoo:jp:srch:Result/urn:yahoo:jp:srch:Title/text()";
      private static final String PATH_URL 
    	= "/urn:yahoo:jp:srch:ResultSet/urn:yahoo:jp:srch:Result/urn:yahoo:jp:srch:Url/text()";

    public ActionForward execute(
    	ActionMapping mapping,
        ActionForm form,
        HttpServletRequest request,
        HttpServletResponse response) throws IOException
    {
    	
    	String searchedResult = null;
    	InputStream is = null;
    	
    	try {
             URL url = new URL(YAHOO_SEARCH_URL);
             BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(),"utf-8"));
             String line;
             StringBuffer sb = new StringBuffer();

             while ((line = reader.readLine()) != null) {
            	 sb.append(line);
             }
             reader.close();
             
             searchedResult = sb.toString();
             is = new ByteArrayInputStream(searchedResult.getBytes("utf-8"));              

         } catch (MalformedURLException e) {
 			e.printStackTrace();
         } catch (IOException e) {
 			e.printStackTrace();
         }
    	
    	/*
    	 * Java XPath APIを参考
    	 * http://www.ibm.com/developerworks/jp/xml/library/x-javaxpathapi.html
    	 */
    	DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware(true); // never forget this!
        DocumentBuilder builder;
	try {
		// Documentオブジェクトの作成
		builder = domFactory.newDocumentBuilder();
		Document doc = builder.parse(is);
	        
	        // XPath式の作成と評価
	        XPathFactory factory = XPathFactory.newInstance();
	        XPath xpath = factory.newXPath();
	        XPathExpression expr 
	         = xpath.compile(PATH_TITLE);
	        XPathExpression expr2 
	         = xpath.compile(PATH_URL);

	        // XPath式で、Documentオブジェクトを検索する
	        Object result 	= expr.evaluate(doc, XPathConstants.NODESET);
	        Object result2 	= expr2.evaluate(doc, XPathConstants.NODESET);
	        // NodeListの取得
	        NodeList nodes 	= (NodeList) result;
	        NodeList nodes2 = (NodeList) result2;
	        log.debug("number of nodes; "+nodes.getLength());
	        
	        StringBuffer sb = new StringBuffer();
	        byte[] bStr;
	        
	        for (int i = 0; i < nodes.getLength(); i++) {
	        	sb.append(nodes.item(i).getNodeValue());
	        	sb.append(",");
	        	sb.append(nodes2.item(i).getNodeValue());
	        	sb.append("\n");
	        }
	        
        	bStr = sb.toString().getBytes("UTF-8");
              response.setContentType("text/html; charset=UTF-8"); 
              ServletOutputStream outputStream;
              outputStream = response.getOutputStream();
              outputStream.write(bStr);
              outputStream.flush();
	        
	} catch (ParserConfigurationException e) {
		e.printStackTrace();
	} catch (SAXException e) {
		e.printStackTrace();
	} catch (XPathExpressionException e) {
		e.printStackTrace();
	}

	return null;
    }
}

struts-config.xml

以下のように定義する。このサンプルでは、index.html表示の際に、JavascriptでActionにフォワードして、表を形成するjsp(searchResultList.jsp)を表示する。この際、ForwardActionを利用した。

<?xml version="1.0" encoding="utf-8" ?>
<!--
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
    this work for additional information regarding copyright ownership.
    The ASF licenses this file to You under the Apache License, Version 2.0
    (the "License"); you may not use this file except in compliance with
    the License.  You may obtain a copy of the License at
   
         http://www.apache.org/licenses/LICENSE-2.0
   
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
-->

<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
          "http://struts.apache.org/dtds/struts-config_1_3.dtd">

<struts-config>

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

<!-- ========================================= Global Exception Definitions -->
    <global-exceptions>
    </global-exceptions>

<!-- =========================================== Global Forward Definitions -->
    <global-forwards>
        <!-- Default forward to "Welcome" action -->
        <forward
            name="welcome"
            path="/index.hml"/>
    </global-forwards>

<!-- =========================================== Action Mapping Definitions -->
    <action-mappings>
		<action path="/getYahooSearch"
			type="test.YahooSearchsAction"
			name="dummyForm"
			scope="request">
		</action>
		
	   <action path="/index"
      		type="org.apache.struts.actions.ForwardAction"
   		  	parameter="/searchResultList.jsp"/>

    </action-mappings>
<!-- ======================================== Message Resources Definitions -->
    <message-resources parameter="MessageResources" />

<!-- =============================================== Plug Ins Configuration -->

  <!-- ======================================================= Tiles plugin -->

  <!-- =================================================== Validator plugin -->
   
</struts-config>

表を形成するjsp

YUIのData Tableを形成するjspを以下のように作成する。このjspのDOM Readyのタイミングで、Actionを呼び出し、XHRでデータを取得後、Data Tableを生成する。

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

<style type="text/css"> 
body {
	margin: 2px;
	padding: 3px;
}
</style> 
 
<link rel="stylesheet" type="text/css" href="./scripts/lib/yui/build/fonts/fonts-min.css" /> 
<link rel="stylesheet" type="text/css" href="./scripts/lib/yui/build/paginator/assets/skins/sam/paginator.css" /> 
<link rel="stylesheet" type="text/css" href="./scripts/lib/yui/build/datatable/assets/skins/sam/datatable.css" /> 
<script type="text/javascript" src="./scripts/lib/yui/build/utilities/utilities.js"></script> 
<script type="text/javascript" src="./scripts/lib/yui/build/paginator/paginator-min.js"></script> 
<script type="text/javascript" src="./scripts/lib/yui/build/datasource/datasource-min.js"></script> 
<script type="text/javascript" src="./scripts/lib/yui/build/datatable/datatable-min.js"></script> 

<script>
XHRDataTableSample = function() {
	var myDataSource;
	var myDataTable;

	return{
		init:function() {

			// DataTable用:列定義
    		var myColumnDefs = [
             	{	key:"title",
                 	label:"Title",
             		parser:"text",
                 	width:250,
                 	resizeable:true,
                 	sortable:true},
                {	key:"url",
                    label:"URL",
             		parser:"text",
                    width:250,
                    resizeable:true,
                    sortable:true}
    		];

 		   	// DataTable用:コンフィグ属性
    		var myConfigs = {
            paginator: 
                new YAHOO.widget.Paginator({
                	rowsPerPage: 5,
                	template: YAHOO.widget.Paginator.TEMPLATE_ROWS_PER_PAGE,
                	rowsPerPageOptions: [10,25,50,100],
                	pageLinks: 5
            	}),
           	caption:"Yahoo Search API",
			// 列のDrag and Drop
           	draggableColumns:true,
           	// 行の選択は1つだけ
           	selectionMode:"single"
    		};

    		// DataSourceのインスタンス化;struts actionを呼ぶ
        	myDataSource = new YAHOO.util.DataSource('/getYahooSearch.do');
	        	myDataSource.responseType = YAHOO.util.DataSource.TYPE_TEXT;
    		myDataSource.responseSchema = {
   			// 行区切り
            	recordDelim: '\n',
            	// フィールド区切り
            	fieldDelim: ',',
        		fields: ['title','url']
    		};

        	// DataTableのインスタンス化
	        myDataTable = new YAHOO.widget.DataTable("output", 
	    	    myColumnDefs, myDataSource, myConfigs);

			/*
			* 行の選択補助。
			*/
			// クリックでハイライトするようにハンドラを設定
        	myDataTable.subscribe("rowClickEvent",
            	myDataTable.onEventSelectRow);
        	myDataTable.subscribe("rowMouseoverEvent", 
       			myDataTable.onEventHighlightRow);
        	myDataTable.subscribe("rowMouseoutEvent", 
       			myDataTable.onEventUnhighlightRow);

		}, // initの終わり
	   	oDS: myDataSource,
   		oDT: myDataTable
	};
}();


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

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

<h2>Example of URL Fetch</h2>

YAHOO! JapanのWebサービスで「Yahoo」について検索した結果です。
<br>
<br>

<div id="output">
</div>

</body>
</html>

index.html

index.htmlは、以下のように簡易なものである。

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

web.xml

<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

  <display-name>GAE/J Sample Application</display-name>

  	<!-- Standard Action Servlet Configuration -->
  	<servlet>
    	<servlet-name>action</servlet-name>
    	<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    	<init-param>
      	<param-name>config</param-name>
      	<param-value>/WEB-INF/struts-config.xml</param-value>
    	</init-param>
    	<load-on-startup>1</load-on-startup>
  	</servlet>

  	<!-- Standard Action Servlet Mapping -->
  	<servlet-mapping>
    	<servlet-name>action</servlet-name>
    	<url-pattern>*.do</url-pattern>
  	</servlet-mapping>

  	<!-- session configuration -->
 	<session-config>
	  	<session-timeout>5</session-timeout>
	</session-config>

  	<!-- The Usual Welcome File List -->
  	<welcome-file-list>
    	<welcome-file>index.html</welcome-file>
  	</welcome-file-list>

</web-app>

やっぱり

ここまで書いてきて、やっぱり、「こんなに大仰なことする必要あんの?」と思ってしまった。
XMLをFetchする場合、どうしても「動的な操作」がしたいと思ってしまう。そうすると、この気持ちと、「コンパイルという行為」そのものがしっくりこない。PHPでも、Javascriptでも、スクリプト言語で処理した方がさっぱりするのだろうなあ。mezawa氏が正しいと思った。