2008-07-19

Springと一緒に Wicketを使う

我が社は主に Flexと BlazeDSを用いた RIAに注力している。このプラットフォームを用いる場合、サーバ側ではほぼ純粋にビジネスロジックを提供するだけで良いため典型的なMVCタイプの Webアプリケーションよりもさらに分業が容易であるという利点がある。

しかしながら、常にユーザの環境が RIAに適しているとは限らないし、「文書を読ませる」機能に関して言えばそれを Flash Player上に実現した場合あまりユーザにとって快適とは言い難いものになる可能性が高い。

などの理由から、従来通りHTMLで表現される Webアプリケーションを実装する場合も少なからずあるため、近頃先進的な向きに人気の高い Wicketを調べてみた(むろん、同様の目的には Spring MVCや Struts2を使っても良いのだが)。

ここでは、既に Spring Frameworkが Webアプリケーションに導入されていることを前提に Wicketを動作させるために必要な最低限のセットアップを行う手順を示す。
なお、アプリケーションに BlazeDSが既に導入されている場合でも共存が可能であることを確認している。

WEB-INF/lib に設置した jarファイルは下記の通り。

wicket-1.3.4.jar
wicket-ioc-1.3.4.jar
wicket-spring-1.3.4.jar
wicket-spring-annot-1.3.4.jar
slf4j-api-1.5.0.jar
slf4j-jcl-1.5.0.jar

slf4jについては好きな実装を用いて良いと思うが、Springが前提であれば Commons Loggingが存在するはずなのでここでは slf4j-jclを用いている。

必要なライブラリを追加したら、web.xmlに Wicketの設定を追加する。Wicketはサーブレット又はサーブレットフィルタとして適用することが出来るが、後者が推奨されているようだ。


<filter>
<filter-name>wicket</filter-name>
<filter-class>
org.apache.wicket.protocol.http.WicketFilter
</filter-class>
<init-param>
<param-name>applicationFactoryClassName</param-name>
<param-value>
org.apache.wicket.spring.SpringWebApplicationFactory
</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>wicket</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


典型的な Wicketアプリケーションを構成する要素は、ひとつの WebApplicationクラスと ページ数分の WebPageクラス(とそれぞれに対応するHTMLファイル)である。
applicationFactoryClassNameに SpringWebApplicationFactoryをセットすることで、Wicketは Springの applicationContext.xml内に定義されている Beanの中から WebApplicationのインスタンスを選び出して自動的に用いるようになる。
(そのため、ことSpringを用いる場合は web.xmlにWebApplicationの実装クラス名を直接書き込む必要がない)

この設定では、Webアプリケーションに対する全てのリクエストが Wicketのフィルタに一旦渡されるが、Wicketは自身の守備範囲でない URIへのリクエストをスルーするため、画像などのリソースや他のサーブレット(BlazeDSの通信エンドポイントもそうだ)に対するリクエストを妨害されることはないようだ。

さて、最小限の WebApplicationはこのような実装になる。

package net.stbbs.wicket;

import org.apache.wicket.spring.injection.annot.SpringComponentInjector;

public class WebApplication extends
org.apache.wicket.protocol.http.WebApplication {

@Override
public void init()
{
super.init();
this.addComponentInstantiationListener(new SpringComponentInjector(this));
}

@Override
public Class getHomePage() {
return HomePage.class;
}

}

Springと統合するために、initメソッドに対し addComponentInstantiationListenerの呼び出しを追加している。これによって、WebPageクラスのインスタンスが生成される際にはアノテーションに従って自動的にフィールドインジェクションが行われるようになる。例えばこのように。

public class HomePage extends WebPage {
@SpringBean(name="helloService")
private HelloService helloService;
:
:

上記の例ではアノテーションのパラメータとして name を渡しているが、型の合致するBeanがアプリケーションコンテキスト内にひとつしか無い場合は省略しても良い。

WebPageオブジェクトのライフサイクルは Wicketの管理下にあり Springのそれとは一致しないが、WebPageオブジェクトにフィールドインジェクションされた Beanは実際には適切に自動生成されたプロキシであり、これによりライフサイクルの差異は吸収されるらしい。

インジェクションの対象となるフィールドには初期化の記述をしないように注意すること(例えば private HelloService helloService = null; のように)。インジェクタはフィールドの初期化処理よりも前に走るため、フィールドの初期化を記述するとそれで上書きされてしまうそうだ。

あとは Wicketの作法に従って WebPageクラスと HTMLファイルを必要なだけ作ることで UIを構築していけば良いだろう。

2008-07-13

Windows XP Professionalをマルチユーザー仕様に改造する

ここで紹介するハックはマイクロソフトのEULAに抵触する可能性が高いため、あくまで海外サイトから得た情報を参考までに紹介するに止めるうえ、上級者向けに必要最低限の内容のみ掲載する。情報を利用した結果ついて当方では一切の責任を負うことができないので、各自自己責任のもとで行って頂きたい。
(なおこのネタ自体は 2006年頃から存在するようだが、要点だけまとめた日本語の情報が他に見あたらなかった)

Windows XP Professionalはリモートデスクトップクライアントを用いることで離れた端末からネットワークを通じてログインし、利用できる。しかしながら、同時に利用することが可能なセッションはローカル・リモート合わせてひとつだけである。つまり、誰かがリモートからログインしようとするとその WinXP Proマシンを操作中のユーザはログアウトを迫られる。

複数のユーザが同時にログインしてオペレーティングシステムの機能を利用することは、UNIXやその派生OSでは当たり前に出来ることであるが、Windows(サーバ向け除く)はそのマーケティングにおける性質上、「技術的には出来るけど、出来ないようにしてある」といったところだと思われる。実は以前 Windows XP SP2でこの制限が緩和され同時に2セッションまで利用出来るようになるという噂が流れたのだが、これはガセネタだったようだ。

ここからが本題である。この制限は、termsrv.dll という DLLを3バイト書き換え、いくつかのレジストリ項目を登録し、グループポリシーを修正することで外すことが出来る

termsrv.dllは Windowsフォルダ以下の system32, ServicePackFiles/i386, system32/dllcache にそれぞれ存在している可能性がある(環境によってそれぞれ存在してしていたりしなかったりするようだ)。そこにある全ての termsrv.dllに対し、下記のような書き換えを行う(全て同じ内容のはずなので、1個書き換えてコピーすれば良い)。

0x00022A17
0x74を、0x75に変更
0x00022A69-0x00022A6A
0x7F 0x16を、0x90 0x90に変更

もちろん Windows XP Professionalが通常起動した状態では、このDLLを書き換えることは出来ない。セーフモードをうまく使うか、一旦 Linuxでブートして書き換えるなどの方法があるだろう。ここでは詳しい説明を省くが、セーフモードを使う方法については下記サイトに含まれている。
Enable Multiple Concurrent Remote Desktop Connections or Sessions in Windows XP

DLLを書き換えたら Windows XP Professionalを通常起動し、レジストリに下記のような変更を加える。

[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Terminal Server\Licensing Core]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon]

に、それぞれ DWORD値 "EnableConcurrentSessions"を作成し 1にセットする。

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon]

に、DWORD値 "AllowMultipleTSSessions" を作成し 1にセットする。
(これは環境によっては既にそうなっているかもしれない)

レジストリの書き換えが終わったら、「ファイル名を指定して実行」から gpedit.msc (グループポリシーエディタ)を起動し、「コンピュータの構成」→「管理用テンプレート」→「Windowsコンポーネント」→「ターミナル サービス」まで降りていき、「接続数を制限する」設定を「有効」にし、最大接続数を好きな値にセットする。
次にコントロールパネルから「ユーザー アカウント」→「ユーザのログオンやログオフの方法を変更する」へ降りていき、「ユーザーの簡易切り替えを使用する」をチェックする。

上記で作業はおおかた終了である。リモートデスクトップをまだオンにしていないならば、「システムのプロパティ」→「リモート」からリモートデスクトップを有効にする。これで PCを再起動すれば、Windows XP Professionalが複数ユーザー同時ログイン可能な状態で動作する。
なおコンピュータがドメインに参加している場合、書き換えたレジストリの内容が戻ってしまう問題があるらしい。

関連記事


パスワードなしのアカウントで Windows XP Professionalにリモートデスクトップ接続する ( 推奨はしない )

関連サイト

Remote Desktop Connection Client for Mac

Microsoftが開発・配布している Mac版リモートデスクトップクライアント。Messengerもそうだが、意外と Mac版の存在を知らない人が多い。

rdesktop: A Remote Desktop Protocol Client

オープンソースのリモートデスクトップクライアント

おまけ


Linuxでバイナリファイルを書き換える時は bvi というプログラムが便利だ。操作は viに似ており、: で行番号のかわりにアドレスを16進入力して目的のオフセットへ飛べる。ファイルを開いた時はリードオンリーモードになっているが、:set mm コマンドで書き換えが可能なモードに切り替わるので後は x や i といったように viのような使い方で書き換えを行い、終わったら :wq で上書き終了すると良い。

Xenのハイパバイザ(完全仮想化)モードで運用しているバーチャルマシンの仮想ディスクに含まれているパーティションを Linuxでマウントしたい場合は、multipath-toolsに含まれる kpartxコマンドを使う。
参考記事:ディスクイメージとパーティション

2008-06-29

ファイル型データベースをリモートで使う

普段 Macや Linuxで仕事をしている人間にとっては、Windows機で作業をするのが苦痛だ。
しかし世の中には dBASEや Microsoft Accessで作られたデータベースというものがあって、時にはそういった「ファイル型データベース」を読み書きする仕事もあったりする。

Microsoft SQL Serverのようなクライアントサーバ型データベースであれば、それがたとえ Windowsでしか動作しなくとも JDBCを使ってリモートから利用できるため手元の Macで開発作業が出来るわけだが、ファイル型データベースの場合はそうもいかない。

最近 dBASEファイルをどうしても読み書きする必要があって、仕方ないので Virtual JDBCを使って Windows機上にあるデータベースファイルをリモートからアクセス出来るようにしてみたのが下記。
(なおWindowsには dBASEの ODBCドライバが標準で搭載されている)



Windowsで作業したくないばかりにこんなことまでやるかという気もしないでもないが、それはさておき。

(a) JDBC URLとして サーブレットのURLと vjdbcに設定したDB名を与える。
"jdbc:vjdbc:servlet:http://my-windows-host:8080/vjdbc/vjdbc,mydb"

(b) web.xmlにて vjdbcサーブレットをマッピングする。

<servlet>
<servlet-name>VJdbcServlet</servlet-name>
<servlet-class>
de.simplicit.vjdbc.server.servlet.ServletCommandSink
</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>VJdbcServlet</servlet-name>
<url-pattern>/vjdbc</url-pattern>
</servlet-mapping>

(c) vjdbcがリクエストを転送する先となるターゲットデータベースの設定を /WEB-INF/vjdbc-config.xmlに記述する

(d) (vjdbc-config.xml内) JDBC-ODBCブリッジのURLで ODBCデータソース名を指定
"jdbc:odbc:mydbase"

(e) Windowsの「管理ツール」→「ODBC」で dBASE用のデータソースを設定する(*.DBFファイルの置いてあるフォルダを指定)。データソース名は (d)で指定した物と合わせる。

この構成で JDBCを使って dBASEファイルへリモートアクセスが出来るようになった。
・・・なったのだが、トランザクション操作をしようとするとこの通り・・・
java.sql.SQLException: 
[Microsoft][ODBC dBase Driver]オプションの機能は実装されていません。

Windowsに標準でくっついてるようなドライバにそこまで期待すんなということですな。

ちなみに dBASEの JDBCドライバ(商用)もあって、5同時アクセスライセンスが 150ドルで買える。お金を取るくらいだからもちろんトランザクションもサポートしているに違いない。真面目に今後もdBASEと付き合っていく必要がある、かつ、ライセンス料を顧客に負担してもらえる場合はこれを使ったほうが無難だと思う(試してないけど)。

JDBC-ODBCブリッジを使ってファイル型データベースにアクセスする用途以外でも、何らかの理由で データベースアクセスをHTTPで中継したい場合に vjdbcは役に立つだろう。

Macと Xenと VNCクライアント

Xenで動作する仮想マシンの画面を表示するインターフェイスとしては、SDLとVNCの2種類がある。
SDLの場合、(特別な設定をしなければ)画面表示に X Window が用いられる。XなのでSSHを経由してリモートに画面を表示させることも出来るが、画面を閉じると仮想マシンも強制終了してしまうという凶悪な仕様なため主にリモートでヘッドレス運用をする場合は使えない。
VNCと比べた場合の利点は、(ローカルマシンで画面を表示するなら)高速だろうという点である。

いっぽう、VNCを使用する場合、仮想マシンごとに 5900番以降の空いているポートを用いて VNC接続の待ち受けが行われる(VMの設定ファイルでポート番号を固定することもできる)。
リモート運用にはこれが最も適しているのだが、ひとつ重大な問題がある。

Mac OS Xでまともに動く唯一のフリーな VNCクライアントである Chicken of the VNC (a.k.a. cotvnc) と Xenの VNCサーバの相性が悪く、使えないのだ(画面が全く表示できない又は乱れる、エラーで接続が切れる)。

cotvncが悪いのか Xenに内蔵されているVNCサーバが悪いのか(もしくは両方なのか)知らないが、Xenのメーリングリストで「cotvncでちゃんとVMの画面が表示できないよー」という投稿が過去に一度あったものの解決されずに今に至っていることや cotvncの開発が止まっていることから、もはや状況は絶望的かと思われた。
X over SSHを使って dom0上の vncviewerを動かすという手は使えるのだが、ものすごく遅いし受け付けてくれないキーがあったりするしたまにバグって切れたりするので絶望的な状況であることに変わりはなかったと言えよう。

そんな時に現れた救世主、
Redstone Software社の Vine Viewer
Vineといっても昔あった Linuxのそれとは関係ないし、Windows互換レイヤのソフトウェアでもない。

・XenのVNCサーバと接続できる
・VNC over SSHサポート(出先からのアクセスも安心)
・画面の録画ができる(QuickTime形式の動画ファイルになる)

これでバーチャルマシンの管理がしやすくなるなあ。
値段は 35ドル。

2008-06-25

SpringでJNDIを提供する 続き

以前のエントリで、XBeanの成果物を拝借して Springで JNDIを提供させる方法を示したのだが、この org.apache.xbean.spring.jndi.SpringInitialContextFactoryは Beanファクトリとして ApplicationContextのバリアントを使っていないためそのままだと PropertyPlaceholderConfigurerなどを使って Bean定義ファイル内の値をパラメタ化することが出来ない。
(ApplicationContextの子孫は、BeanFactoryPostProcessorを自動処理するのだが、それ以外の BeanFactoryではプログラムコードで明示的に postProcessBeanFactoryを呼んでやらなければ処理されない)
ApplicationContextはあくまで「アプリケーションの」コンテキストであるため JNDIの提供に適用しない事自体は正しいと思うが、せめてpostProcessBeanFactoryを呼んで欲しいところだ。
この問題(?)は2005年に報告されているが完全に無視という形のようだ。こんなのただの副産物だから本来の用途にさえ使えればよくてそれ以外興味ないねということだろうか。

仕方がないのでSpringInitialContextFactoryの派生クラスを作って BeanFactoryの生成を行っているメソッドをオーバーライドし、postProcessBeanFactoryの呼び出しを追加することにした。

package net.stbbs.xbean.spring.jndi;

import java.util.Map;

import org.apache.xbean.spring.context.impl.XBeanXmlBeanFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.core.io.Resource;

public class SpringInitialContextFactory extends
org.apache.xbean.spring.jndi.SpringInitialContextFactory {

@Override
protected BeanFactory createContext(Resource resource) {
BeanFactory factory = super.createContext(resource);
Map<String,Object> postProcessors =
((XBeanXmlBeanFactory)factory)
.getBeansOfType(BeanFactoryPostProcessor.class);
for (Map.Entry pp:postProcessors.entrySet()) {
((BeanFactoryPostProcessor)pp.getValue())
.postProcessBeanFactory((XBeanXmlBeanFactory)factory);
}
return factory;
}
}

jndi.propertiesは
java.naming.factory.initial=org.apache.xbean.spring.jndi.SpringInitialContextFactory

java.naming.factory.initial=net.stbbs.xbean.spring.jndi.SpringInitialContextFactory
に書き換えた。

ラベル:

DbUnitでテーブルをエクセル出力する

以前のエントリで、DbUnitを使ってフィクスチャによるデータベース事前条件の定義を含むユニットテストを行う方法を示した。

事後条件については、ターゲットとなるメソッドの実行が完了した後にSimpleJdbcTemplateなどでデータベースに対しクエリを行うことでチェックすることが出来る。理想としてはそのようにしてプログラムコードだけで事後条件を自動的に検査出来るほうが良い。よく練られた多角的なテストケースが存在することにより、プログラムは修正作業によるエンバグの発生から逃れやすくなるだろう。

しかし、テストケースに山のような数のselect文やループ構文を記述している時間が惜しい時があるのも事実だ。人間が表の状態を視覚的に確認することで、(ミスの検出確率は下がるものの)事後条件のチェックにかかる時間が節約出来ることも多々ある。

ところが、基本的にフィクスチャを用いるユニットテストは各テストメソッドの実行が1つのトランザクションとなっており、成功にせよ失敗にせよテストの完了時にトランザクションはロールバックされてしまいデータベース上のテーブルは空に(正確にはテスト実行前の状態に)戻ってしまうため、テストの実行後に GUIのフロントエンドなどを用いてデータベースの内容を確認しようとしても無駄である。

DbUnitには、テーブルの内容をファイルにダンプする機能があるので、それをテスト中の任意の時点で呼び出すことでこの問題を解決出来る。下記の例では、addCommentToArticle()メソッドの実行後に users, articles, commentsという3つのテーブルをエクセルファイルに出力している。エクセルファイル上では、1つのテーブルが1つのシートに割り当てられる。

public void testAddCommentToArticle()
{
target.addCommentToArticle(1, 2, "コメント");

// addCommentToArticle()実行後のデータベース内容をエクセルファイルにダンプ
IDatabaseConnection con = this.getConnection();
IDataSet dataset = con.createDataSet(
new String[]{"users","articles","comments"});
OutputStream os = new FileOutputStream("addCommentToArticle.xls");
XlsDataSet.write(dataset, os);
os.close();
con.close();
}

繰り返しになるが、ダンプされたエクセルデータを目視で確認し事後条件の検査をするという方法はユニットテストのあり方として理想的ではない。いうまでもなく、事後条件の検査はテストケースの中に assert文を使って記述されるべきだ。それをわかった上でどこを妥協するかは時間的制約との兼ね合いだろう。

事後条件の検査以外にもテーブルのダンプは有益な場合がある。たとえばロジックの実装者はデバッグのためにそれがあると便利なはずだ。事後条件の検査を自動で行う行わないに関わらず、テーブルのダンプを常に生成するようにしておいた方が実装者に優しいといえる。ただ、テストの最後でダンプを行うようにテストメソッドをコーディングした場合、途中のassertに引っかかるとダンプが行われないためデバッグ時には不便かもしれない。ダンプのタイミングは状況に応じて選ぶと良いだろう。

2008-06-22

Railsとsqlite3

RedmineをGentooで動かそうとしたら、"Unable to create the anonymous user"と言われて動かない。
Macだと(Railsを2.0.2に更新すれば)同じようにして動いたのにおかしいなと思ったら、どうやら RailsではSQLite 3.3.8以降をうまく扱えないらしい。
参考記事:Using SQLite3 with Rails

ところが Portageに入っているSQLite3は 一番古くても 3.4.1からだったりするもので。バカー!

仕方ないので MySQLで運用する事にした。めんどくせえなあ。