2008-01-25

JSPからSpringを使う

普通そういうことはしないのだが、ちょっとしたトライandエラーに使うと便利な時があるかもしれない。

<%@page contentType="text/html"%>
<%
org.springframework.web.context.WebApplicationContext wac =
org.springframework.web.context.support.
WebApplicationContextUtils.getRequiredWebApplicationContext(
pageContext.getServletContext());
com.acme.MyBean myBean = (com.acme.MyBean)wac.getBean("myBean");
%>
<html>
<body>
ここで myBeanを使っていろいろやる
</body>
</html>

ラベル:

2008-01-24

BlazeDS経由で呼び出されるサービスメソッドの例外ログをAOPで出力する

以前のエントリでこんなことを言った。
BlazeDSを介したメソッド呼び出しでは、メソッドが例外をスローしても BlazeDSがそれを捕まえて Flexアプリケーションに Faultオブジェクトとして(アプリケーションサーバから見れば)正常に返してしまうため、アプリケーションサーバ側では例外ログが出力されない。

アプリケーションサーバ側のログファイル(開発作業中の場合はコンソール)にスタックトレースが出力されないのではデバッグが困難なので、Springの AOPを使って例外をサーバ側でも出力させておくことにする。

必要なjarファイルを追加


Springの配布に同梱されている。
asm-2.2.3.jar
asm-commons-2.2.3.jar
asm-util-2.2.3.jar
aspectjrt.jar
aspectjweaver.jar

applicationContext.xmlにネームスペースを追加(まだ無ければ)


xmlns:aop="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"

自動weavingの設定を追加


aopネームスペースの aspectj-autoproxy要素を入れておくと、アスペクトのWeaving周りで面倒な設定をしなくても自動で色々やってくれる模様。詳しく知らないけどとにかく面倒だったものが簡単に。

<aop:aspectj-autoproxy/>

ワンポイントアドバイス:Spring 1.2の頃はこういう自動化がまだ無く、手で XMLをいっぱい書かなければならなかった。Spring AOPのことについてググる時は、ヒットした記事が Spring 1.2時代のものでないことを都度確認しないとひどい遠回りをするはめになるよ。

アスペクトの作成


例外発生時に実行される処理を担当するアスペクトを作る。こんな感じ。

package com.acme.aspect;

import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;

@Aspect // このクラスはアスペクトだよ。という印
public class ExceptionLoggingAspect {
// "com.acme.servicesパッケージ内にある全てのクラス"
// "全てのメソッド"
// "例外発生直後" を対象にこの処理が行われる
@AfterThrowing(
pointcut="execution(* com.acme.services.*.*(..))"
,throwing="ex")
public void logException(Throwable ex)
{
// 本当は commons-loggingを使うなりもっとマシな方法で
ex.printStackTrace();
}
}
こうして作ったアスペクトを applicationContext.xmlにBeanとして登録する(自動で適用されるためIDをつけて他のBeanから参照する必要は特にない)。

アスペクトの登録


<bean class="com.acme.aspect.ExceptionLoggingAspect"/>

これで FlexのRemoteObjectには Faultを返しつつサーバ側のコンソールにも例外が出力されるようになった。

ラベル:

2008-01-13

NKFによる改行コードの正規化を ActiveRecordオブジェクトに仕込む

ある日ユーザーから入力された改行含みのテキストをデータベースへそのまま保存していたら、改行コードの統一が取れていないことに気付いた。Macの奴は \rだし ケータイの奴は \r\n だ。Windowsは試してないけど、とにかくおまえらいい加減にしやがれ。

仕方ないので正規化を試みるのだが、このデータが様々な内部処理を通ってバックエンドに伝達されていく(またはその逆の)ことを考えるとこういうものはなるべくUIに近い側で早めに正規化されるのが筋だ。
UIからの入力が x-www-form-urlencoded しかないのならばフレームワークの入力フィルタみたいなやつ(Railsでどうやるのか知らないけど、どうせあるんだろそういうの?)で全部のパラメータ値に改行コード正規化をかけるみたいなことも(乱暴だけど)出来るだろうが、今日びの入力ストリームは XMLかもしれないしJSONかもしれないので(私の場合はしばしばAMFだったりするのでなおさら事情が複雑だ)、そういう手段でいっぺんに解決することは出来ず悩ましいところだ。

で、しょうがないから ActiveRecordで水際正規化を行うようにした。改行コードの正規化は NKFで出来る。

require 'nkf'

# ありがちな例でブログのエントリだとおもいねえ
class BlogEntry < ActiveRecord::Base
# 例えば body(本文)の改行コードを常に正規化する
def body=(body)
self[:body] = body && NKF.nkf('-dm0xWw',body)
end
end
NKFのオプションを変えれば改行コードのみならず日本語文字コードの正規化も行う事が出来るだろうが、本当にここでそれをやって良いのかよく考えてからそうするべきだろう。

2008-01-10

An alternative of BlazeDS - Rails and WebORB

Railsに WebORBをプラグインすると、Flexの mx:RemoteObject から呼び出し可能なサービスを Rubyで記述することが出来る。

基本的な構図



Flex(クライアントアプリケーション)側は、通信クライアントとなるオブジェクトとして mx:RemoteObject を使用する。

Rails側は、WebORBをプラグインすることによって生成されたコントローラ weborb を通信のエンドポイントとし、クライアントからの呼び出しを受け付ける。このweborbコントローラが、呼び出し対象となるサービスへFlexアプリケーションからのリクエストをディスパッチする。ここでいうサービスとは、app/services に配置された Rubyオブジェクト。

Rails側


コマンドは LinuxなりMacなりでの例。Windowsは知らん。
特に MacOS X 10.5の場合は RubyもRailsも最初から入っているので迷うことはまずないだろう。

# Railsアプリケーションを生成
rails myrailsapp

# WebORBをプラグイン
cd myrailsapp
./script/plugin install http://themidnightcoders.net:8089/svn/weborb

# サービス(リモートオブジェクト)となるクラスを作成する
# app/services/ に クラス名と同じ名前の .rbファイルを配置することで
# WebORBのディスパッチ対象となる
vi app/services/MyService.rb

〜内容〜
class MyService
def say_hello
# ここではもちろん ActiveRecordも使える
# HTTP Cookieによるセッションにアクセスする必要がある場合は
# RequestContext.get_session
# を使うことが出来る

"Hello, World!"
end
end
# Railsを起動(ポート3000番でHTTPの待ち受けになる)
./script/server &

Flex側

<mx:RemoteObject id="srv" destination="MyService"
endpoint=
"http://localhost:3000/weborb/"/>
<mx:TextInput text="{srv.say_hello.lastResult}"/>
<mx:Button label="Say Hello" click="srv.say_hello()"/>


たやすい事この上ない。

WebORBと似たようなことが出来るものとして RubyAMFもある。こちらは Railsのコントローラに :amf というレンダラを提供するもので、WebORBと比べるとより Railsのアプリケーション作法に則ったものだが、個人的には WebORBのほうが気に入っている。ただし実行速度は RubyAMFの方が良いらしい(手元では未確認)。

メモ

Flexの通信は全て非同期オペレーションである。上の例では srv.say_hello() でリモートの say_helloメソッドをコールしているが、srv.say_hello()の戻り値は "Hello, World!" にはならない。メソッド呼び出しはバックグラウンドで処理され、サーバから結果が返り次第 srv.say_hello.lastResult にセットされるのである(上の例では mx:TextInputからそれを参照している)。

メソッド呼び出し結果の着信をトリガにして何らかの処理を行いたい場合は、イベントリスナの登録によって行うか mx:RemoteObjectの中に mx:method要素を明示的に定義し、result属性にイベント処理の内容を記述することになる。

昔からのプログラマは非同期通信に慣れているだろうが、Webから入門した新しいプログラマは Ajaxのために XHRをいじり倒した経験があるのでもない限り馴染みが薄く戸惑うかもしれない。だが Flexでは同期通信が出来ない。信じがたいかもしれないがこれは本当だ。Adobeのフォーラムか何かでこんなやりとりを見かける。

「同期通信をしたいんだけど、どうしたらいい?」
「同期のことは忘れようよ」

ラベル: ,

BlazeDSと Springを統合する

以前のエントリで予告したとおり、Springと一緒に BlazeDSを使う方法を書きとどめておく。Springを使って組み立てられた既存の Webアプリケーションを Flexで RIA対応化へ導くのであれば、この組み合わせは避けて通れないはずだ。

ここではSpringが既に Webアプリケーションに組み込まれているものとして解説する。
つまり、web.xmlにContextLoaderListenerが登録されており、Bean定義ファイル (デフォルトでは WEB-INF/applicationContext.xml) がロードされるよう構成されていること。

Springと BlazeDSを統合するとはつまり何かというと、BlazeDSのオブジェクトファクトリとして Springを利用するよう構成するということだ。デフォルトで BlazeDSのオブジェクトファクトリは BlazeDS自体の内蔵している簡易な物だが、これを Springに置き換えれば DIや AOPの強力なサポートが得られるわけだ。

BlazeDSとSpringを接続するアダプタとなるモジュールは今のところ下記からダウンロード出来る。
http://www.igenko.org/archiva/repository/igenko/com/adobe/flex/blazeds-spring/
ここから適当に一番新しそうな blazeds-spring-*.jar を持ってきて WEB-INF/lib に入れるといいだろう。

WEB-INF/flex/services-config.xml に、オブジェクトファクトリとして Springを使えるように factory要素を登録する( services-config要素下 )

<factories>
<factory id="spring"
class=
"flex.messaging.service.SpringFactory"/>
</factories>
続いてサービス定義の中、destination要素のプロパティに注目。

<properties>
<source>com.acme.MyService</source>
</properties>
のように、クラス名を直接指定している部分を

<properties>
<factory>spring</factory>
<source>myService</source>
</properties>
のように、Springの Bean IDを使ってオブジェクトを取得するように書き換える。この際、Springの WEB-INF/applicationContext.xmlには下記のような Bean定義が必要となる(むろん実際にはこの Beanに対して property 要素で依存コンポーネントをあれこれ注入する形になるだろう)。
<bean id="myService" class="com.acme.MyService"/>
こうすることで、Springによってインスタンス生成と依存性注入が行われ完成したオブジェクトを BlazeDSを通じて Flexアプリケーションに向けて公開することが出来る。

日付範囲から勤務時間表を出力する Flexアプリケーションの例。


Java側
/**
* Java側メソッド - 指定した日付範囲の勤務時間を抽出する
* from: 開始日付
* to: 終了日付
* 前提条件: JDBCデータソース dataSource が セットされている
*/

public List getWorks(java.sql.Date from, java.sql.Date to)
{
/*
JdbcTemplate#queryForList でSQLを実行し、結果(MapのList)を
そのまま Flex側に返す(ObjectのArrayCollectionへとうまい具合に変換される)。
*/

return new
JdbcTemplate(dataSource).queryForList(
"select 日付,勤務時間 from 勤務表 where 日付>=? and 日付<=?",
new Object[] {from, to});
}
Flex側

destinationやendpointをどう設定するかは以前のエントリを参照
<mx:RemoteObject id="srv" destination="amftest" 
endpoint="http://localhost:8080/amftest/messagebroker/amf"/>
<mx:HBox>
<mx:DateField id="from"/><mx:Label text="から"/>
<mx:DateField id="_to"/><mx:Label text="まで"/>
<mx:Button label="検索実行"
click="srv.getWorks(from.selectedDate, _to.selectedDate)"/>
</mx:HBox>

<mx:DataGrid dataProvider="{srv.getWorks.lastResult}">
<mx:columns>
<!-- ActionScriptでは日本語名のプロパティも通るようだ
勧めはしないが -->
<mx:DataGridColumn dataField="日付" width="300"/>
<mx:DataGridColumn dataField="勤務時間" width="60"/>
</mx:columns>
</mx:DataGrid>
実行画面


テストは Spring 2.5.1と Flex Builder3 Beta3で行った。
日付のフォーマットをきちんとしたければ DataGridColumn.labelFunctionと mx:dateFormatterを使えば良い。

メモ


BlazeDSを介したメソッド呼び出しでは、メソッドが例外をスローしても BlazeDSがそれを捕まえて Flexアプリケーションに Faultオブジェクトとして(アプリケーションサーバから見れば)正常に返してしまうため、アプリケーションサーバ側では例外ログが出力されない。このため、例外の追跡を行えるような何らかの仕掛けをしておくのが望ましいと思う。
(例えば例外のロギングといった地味なAOPの用法はここで大きく役立つと思われる)

ラベル: ,

2008-01-06

Adobe BlazeDSを自分のWebアプリケーションに組み込む設定

ActionScriptのオブジェクトをシリアライズするためのバイナリフォーマットを AMFという。BlazeDSは、AMFとJavaオブジェクトのネットワーク透過な変換レイヤを提供するオープンソースのミドルウェアで、2007年12月にAdobeによってベータ版が公開された。

それまでにもリバースエンジニアリングにより同様の機能を持ったミドルウェアがオープンソースでいくつか開発されているが、Adobe自身が公開に踏み切ったことには大きな意味があるといえるだろう。

なお商用版として AdobeはLCDS(旧名FDS)というミドルウェアを販売している。

BlazeDSで何が出来るのかを平たく説明すると、Flash(正しくはFlex)アプリケーションから、サーバ上にある Java Beanのメソッドを HTTP経由で呼び出すことが出来るのである。EJBクライアントがステートレスセッションBeanのメソッドをネットワーク越しにコールするのに似ているが、当然EJBほど複雑な手続きは要らないし(最近はそうでもないか?)、HTTPベースなので通信環境を選ばない。もちろんリモートの JavaBeansは POJOだ。

さて、BlazeDSに限らず互換プロダクトでもその傾向があるのだが、何もかも含めた全部入りのアーカイブで配布されており、バンドルされている Tomcatをスタートすれば 8080番ポートでサンプルアプリケーションがすぐに動作する、という定番に従っている(Railsが流行ってからというもの、各所でフルスタックごっこが盛んなようである)。動作を試してみたいだけならこれはこれで良いのだが、「既存のWebアプリケーションを一部 RIA化するために BlazeDSをアドオンとして組み込みたい」といった現実的な用途が目前にある向きには、全部入りアーカイブの中のどれとどれが本当に必要なものなのかわかりにくく悩んでしまう。

というわけで、まずはシンプルな JavaBeans呼び出しをするだけに目的を絞って BlazeDSのミニマムスタートアップに必要なモジュールや設定を調べてみた。
注:私が実験した限りだと、Flex SDK 3 の最新ベータ版(Beta3) でないとリモートメソッド呼び出しが常に失敗した。常に最新版を利用されたし。

基本的な構図



Flex(クライアントアプリケーション)側は、通信クライアントとなるオブジェクトとして mx:RemoteObject を使用する。

Webアプリケーション側は、通信のエンドポイントとなるサーブレット MessageBrokerServlet を用意する。このサーブレットが、呼び出し対象となるJavaBeansへメッセージをディスパッチする。

Webアプリケーションに追加するjarファイル


WEB-INF/lib に、これらの jarファイルを追加する。

backport-util-concurrent.jar
flex-messaging-common.jar
flex-messaging-core.jar
flex-messaging-remoting.jar

WEB-INF/web.xmlにBlazeDS関連の設定を追加する



<listener>
<listener-class>flex.messaging.HttpFlexSession</listener-class>
</listener>

<servlet>
<servlet-name>MessageBrokerServlet</servlet-name>
<servlet-class>flex.messaging.MessageBrokerServlet</servlet-class>
<init-param>
<param-name>services.configuration.file</param-name>
<param-value>/WEB-INF/flex/services-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>MessageBrokerServlet</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>

設定ファイルを作成する


設定ファイルは web.xmlに書いた通りのものを作成する。一般的に、 WEB-INF/flex/services-config.xml とされることが多いようだ。
比較的込み入った設定ファイルだが、ひとまず下記のものを destination 要素内のみ自分用に書き換えて使えば良いだろう。

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<!-- service要素には何でもいいからIDが要る模様 -->
<service id="remoting-service" class="flex.messaging.services.RemotingService">
<adapters>
<adapter-definition id="java-object"
class=
"flex.messaging.services.remoting.adapters.JavaAdapter" default="true"/>
</adapters>
<default-channels>
<channel ref="my-amf"/>
</default-channels>

<!-- destination要素のIDが mx:RemoteObjectの destination プロパティに対応する -->
<destination id="amftest">
<properties>
<!-- リモート呼び出ししたいJava Beansのクラス名 -->
<source>amftest.AmfTestService</source>
<!-- 注:この Beanはステートレスである -->
</properties>
</destination>
<!-- リモート呼び出しを可能にしたい Beanの数だけ destination要素を記述する -->

</service>
</services>

<channels>
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<!--
ここで構成されたエンドポイントURLを mx:RemoteObjectの
endpoint プロパティにセットする
{}内は実行時に自動で置き換えられるのでこの記述のままで良い。
-->

<endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf"
class=
"flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
</channels>
</services-config>

JavaBeans(呼び出される側)の例



package amftest;

public class AmfTestService {
public String sayHello()
{
return "Hello, World!";
}
}

Flex(呼び出し側)の例


(サーバ側アプリケーションは amftest.war となってアプリケーションサーバにデプロイされているものとする)


<mx:RemoteObject id="srv" destination="amftest"
endpoint=
"http://localhost:8080/amftest/messagebroker/amf">
<mx:TextInput text="{srv.sayHello.lastResult}"/>
<mx:Button label="Say Hello" click="srv.sayHello()"/>


ここまでで、ブラウザ上で動作している Flashアプリケーションからサーバ上の Javaオブジェクトに対しメソッド呼び出しを行い、結果を表示することが出来た。

巷の入門記事などには、サーバ側・クライアント側ともに設定ファイルをやたらとゴチャゴチャ書かなければいけないように書いてあるが、最低限の機能を使うだけなら実はこの程度で済むのであって、込み入ったことをしたくなったらそれはその時に考えれば良いのである。

後で、SpringとBlazeDSを 一緒に使う方法も調べてみようと思う。

関連記事


BlazeDSと Springを統合する

BlazeDSに Spring Frameworkのパワーを与える

BlazeDSをメッセージブローカにして FlexでPub/Subメッセージングを行う

RPC以外の通信方法

An alternative of BlazeDS - Rails and WebORB

Java + BlazeDSの代わりに Ruby on Rails + WebORB

More alternative of BlazeDS - PHP and WebORB

Java + BlazeDSの代わりに PHP + WebORB

ラベル:

LVMのスナップショットが消せなくなったとき

LVMを使っている人は、バックアップなどのためにスナップショットを活用されていることとおもう。
何らかの理由でバックアップが異常終了してしまった(バックアップ先ディスクのI/Oエラーなどで強制終了したなど)場合などで、スナップショットを lvremoveコマンドで削除しようとしたところ "Can't remove open logical volume" と言われて削除できないことがある。マシンを再起動してもその状態が続いてしまうので困ったものだ。udevの仕業か?

そういう時は、
dmsetup remove /dev/mapper/your-snapshot-name
のようにしてデバイスマッパーから削除してやれば、lvremoveコマンドでスナップショットが消せるようになる。

理屈は知らん。

2008-01-01

マス目文化に苦言を呈す

新年がやってきた。

昨年の経費でペンタブレット買うの忘れてた。
ヨドバシでレシート昨日の日付にしてくれって言っても無理だろうなあ。


仕事をしつつ周囲を見ていると、なんでもマス目に押し込めて物事を考える人が多い。彼らはエクセルが大好きで仕方ない。資料がエクセルフォーマットになっていないと安心できない。何もかもマス目にきっちり収まっていないと気が済まない。会議の資料や納品物としてフリーフォーマットのドキュメントを採用したがらない。思考の途中経過といった、マス目に収められないことはデータとして残さない。結論を表にしてまとめることにしか興味がない。空間的広がり、物語性がない。Wordを立ち上げたらまずは罫線を引く。枠の外にはみでることが怖くて仕方ない。だから仕事のツールとしてマインドマップやアウトラインプロセッサが普及しない。

私は、本来(子供がそうであるように)人間の発想って鉛筆もって真っ白い紙に余白を自在に使って描くのがまず始まりであって、そこで構築されたイマジネーションが基礎となって次の思考や行動に反映され、論理や秩序はその後についてくるものだと思っているので、思考の経過(なぜそのような結論に至ったか)を空間的に表現することで他者と共有する必要性を軽視する(もしくは必要性に気づいていない)文化には苦言を呈したいと思っている。まだ海外で仕事をしたことが無いので推測だが、これは特に日本でそういう傾向があるのだと思う。

日本でオフショア開発の成功事例が乏しいのは、発注先の想像力を喚起するスキルが低すぎることによるものではないかとも考えている。

で、タブレットの話。

私はホワイトボードをよく使うのだけれど、それだと事務所内にいる相手にしか伝わらないし、字が汚くて判別不能になったりするのでアナログとデジタルのちょうど良い中間としてペンタブレットと軽いドローイングソフトを使った思考が出来ると良いなと常々思っている次第。

でも買い忘れたから、今年もコピー用紙+マジック+スキャナだ!