Java

Google Talk(XMPP)のクライアントを作る

XMPPを理解したくてマスタリングXMPPを購入、ちょっと読んでみた。基本的なプロトコルはシンプルそうだ。

Smack APIとやらを使うと、簡単にJavaでXMPPのクライアントが作れると言うことで、作ってみた。
次のコードでログインする。

connectionConfiguration = new ConnectionConfiguration("talk.google.com",5222,"gmail.com");
xmppConnection = new XMPPConnection(connectionConfiguration);
xmppConnection.connect();
xmppConnection.login(username, password);

次のコードでチャットを開き、受信したメッセージを表示。

Chat chat = xmppConnection.getChatManager().createChat(toAddress, new MessageListener() {
@Override
  public void processMessage(Chat chat, Message message ) {
    System.out.println(message.getFrom()+":"+message.getBody());
  }
});

メッセージの送信は次のコードでOK。

BufferedReader br = new BufferedReader( new InputStreamReader( System.in ));
String line = null;
while( ( line = br.readLine() ) != null ){
  chat.sendMessage(line);
}

これでGoogle Talkのクライアントが完成。めちゃ簡単。
ソースコード全部はこちら

JavaでSIGINTをハンドル

LinuxでJavaアプリを開発しているときに、Ctrl-Cでの終了をハンドルする必要が出た。

次の二つの方法でハンドル出来るけど、Ant経由で起動したからかハンドル出来たり出来なかったりする。
ちなみにAnt1.7.1 , Java1.6.0_13 fork=trueの環境。
謎・・・。

java標準ではないが、Sunのパッケージを使う場合
import sun.misc.Signal;
import sun.misc.SignalHandler;
Signal.handle(new Signal(“INT”), new SignalHandler() {
public void handle(Signal sig) {
log.info(“SIGINTを受信したためプログラムを終了します。”);
System.exit(0);
}
});

Runtime#addShutdownHookでも出来る。こっちの方が標準ライブラリ内で出来るので良いかも。
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
log.info(“プログラムを終了します。”);
}
});

Antから実行すると、シャットダウンフックの終了を待たずにVMが終了するようで、シャットダウンフックの処理が実行されたりされなかったりするようだ。
Javaコマンドで実行した際にはシャットダウンフックの内容が正しく処理された。

Antを使いつつ正しくシャットダウンフックを行う方法は未調査。

Javaでグローバルフック

現在、SWTを用いたアプリケーション開発をしていて、グローバルフックを使った入力が必要になった。
通常Javaからはグローバルフックは無理だけど、SWTの拡張でグローバルフックを使う事が出来るライブラリがあったので使ってみる。

SWT Win32 Extension

次のような事が出来る。サンプルもあるので気軽に動作を確認する事が出来る。
# Window Decorations: making windows always-on-top, transparent, flashing on the taskbar, etc.
# Custom Shape Window: creating non-rectangular windows using custom Regions.
# Access to Windows Registry.
# Shell Folders: getting paths and icons of the user folders (Favorites, My Pictures, etc)
# Shell Links: managing the system link files.
# System Info: gathering CPU, memory, system variables information.
# Hooks: using system hooks and allowing to intercept some system events.
# System Menu Manager: managing the shell system menu, user can define custom menu item.
# Windows Session: managing system session. User can logoff, shutdown, reboot computer.
# Ole Control: providing some ole control wrappers, such as flash.

グローバルフックでマウスのイベントを取るやり方。

使い終わったら Mouse_LLHook.unInstallHook(); して解放しないとダメ。

XP にマホトーンをとなえる for Java

XPを使っていると死ぬほどうざい「コンピュータの更新を完了させ、更新を有効にするには、コンピュータを再起動してください。」と言うダイアログ。

それを黙らせる「XP にマホトーンをとなえる」と言うアプリがおもしろかったので、それのJava版を作ってみたw
実行するときはSWTのライブラリをクラスパスに追加してね!!


仕組みは単純で、自動更新のウインドウを非表示にしてるだけ。
邪魔なウインドウを画面外にドラッグするのが一番手っ取り早いがw

INDRIのJavaインターフェイスコンパイルエラー

仕事でINDRIと言う新しめの検索エンジンを使うことになった。
言語モデルと推論ネットワークを組み合わせて、良くなったぜ的なものらしい。

C++で書かれているが、JavaやPHPで使用できるラッパーがある。
で、そのJavaAPI部分のcompileに失敗していたので解決方法を。検索しても英語の情報すら出てこなかったので誰かの役に立つことを願ってメモしておく。

普通にlemur-4.9を落としてくる。
$ ./configure –prefix=$HOME –enable-java –with-javahome=$JAVA_HOME –with-swig=/usr/bin/swig
としてmakeするが、次のようなエラーが出る。次のエラーメッセージは4.6のものだけど、まぁこんな感じでJavaのコードがめちゃくちゃになってる。

/JAVA_HOME/javac -classpath java -d ../obj/java java/lemurproject/lemur/ArrayAccumulator.java
java/lemurproject/lemur/RetrievalMethod.java:12: がありません。
public abstract class class RetrievalMethod {

これはJavaのコードを自動生成したswigのバージョンが古いからのようだ。ちなみにCentOS4.4に最初から入っているswig1.3.21でおかしいコードが自動生成されているようだ。

最新のswig1.3.39を入れてmakeすると正しくinstallすることができた。

アスペクト指向プログラミングで自動ログイン処理

一時期オブジェクト指向プログラミングの次はアスペクト指向プログラミングだ!とか言われていたけど、Web開発してなければあまり使う機会がない。(置き換わるものではないのでこの説明は正しくない)
しかし、今回例のサイトに自動ログイン機能を付けることになり、アスペクト指向プログラミング(以後AOP)が必須となったので使ってみた。

通常のログイン機能は、あるフォームにID、パスワードを入力しSubmitするのでログイン機能が一カ所に集中しているので問題ない。
しかし自動ログイン機能は、ログアウト状態の時にどこかのページを見たら、そのページの処理の前に自動ログイン処理を行わなくてはならない。そして何事もなかったかのようにリクエストされたページを処理し表示する。

AOPが無ければすべてのActionのメソッドの開始時に”自動ログイン処理”を自分で追加しなくてはならない。
しかしAOPがあれば「この処理をすべてのActionのメソッド開始前に実行してね」と書くと、後は自動でやってくれる。

AOPを使うことで便利になるような横断的関心事はそれほど多くない。
例としては今回のような自動ログインとかトランザクション管理とか、AOPの説明で良く出てくるロギングぐらいか?

SeasarだとAOPもサクッとできたのでメモを残しておく。

まずorg.aopalliance.intercept.MethodInterceptorをimplementsしたクラスを作成する。
これが織り込まれる処理となるので、処理をinvoke()に書く。
今回はこんな感じだ。

methodInvocation.proceed();で実際の要求された処理を実行している。
SAStrutsの場合、戻り値は表示するJSPなので、そのままreturnする。
HttpServletRequestなどはS2Containerから取得する。
S2Containerはpublicなフィールドにしておくと勝手にDIされる。
よって
HttpServletRequest request = (HttpServletRequest)container.getComponent(HttpServletRequest.class);
などして取得。

そしてapp.diconに
<component name=”autoLoginInterceptor” class=”foo.bar.AutoLoginInterceptor”/>
とかしてコンポーネント登録。

次に、実際にActionに処理を織り込む設定をする。
Actionをカスタマイズするためのcustomizer.diconを修正する。
コンポーネント名がactionCustomizerのところに
<initMethod name=”addAspectCustomizer”>
  <arg>”autoLoginInterceptor”</arg>
</initMethod>
と追加する。

これで完了、楽ちんぽん!

自動ログイン処理は以下のように実装した。
通常ログイン時に、乱数を生成し、その乱数をさらにSHA-256でハッシュしキーを作成する。
そのキーを”チケット”としてCookieに保存。同じチケットをユーザ情報としてDBに保存。
ログアウト状態でページを表示しようとすると、CookieとDBのチケットを比較。同じであれば自動ログイン成功。
成功すると新しいチケットを発行しなおして次回の自動ログインに備える。
発行し直すことで同じチケットは1回限りしか使用できないようにする。

また、自動ログインした場合はセッションに自動ログインフラグが立ち、そのフラグが立っている場合にアカウント情報修正など重要な処理を実行する場合は再度認証を行うようにする。

ログアウトボタンを押下した場合はチケットを破棄し、自動ログインを無効にする。

とりあえずこんなもんでいいっしょ。
自動ログインは本来セキュリティ上好ましくないけど、特にクリティカルな情報を扱うわけでもないしこのサイトでは良いよね!

loader constraint violation

SAStrutsはすばらしい。スーパーアジャイルなStrutsと言うことで、RoRに対抗してサクサク感を最大化したとのふれこみは伊達じゃないな。
Strutsもまぁ悪くはないんだけど、テンポは確かに悪い。めんどくさいし。
めんどくさいと言う要素は結構くせ者で、趣味のプログラミングでは「あれ作ろうかな。でもめんどくさいしやめとこ。」と言うことになり、先に進めなくなる。これはまずい。SAStrutsはstruts-config.xmlと言う呪縛から解放されるので、気軽にプログラミングができる。

そんな快適なSAStrutsだけど、今さっき躓いた。
クラスローダー関連のエラーLinkageError : loader constraint violationが出てしまう。
SAStrutsはHotデプロイ機能のため、動的にクラス定義を変えることができるようになっているんだけど、Seasar管理外の部分と連携するとクラス定義に不整合が起こるようだ。

具体的にはSAStrutsのWebアプリから通常のServletへ、とあるインスタンスをsetすると発生する。
SAStrutsのSeasar用クラスローダと、TomcatのWebappClassLoaderとがぶつかっているようだ。

ふーむ。どうすれば良いんだろう。
通常ServletもSeasar用クラスローダを使うHotデプロイ対象にすれば良いのか?しかしどうやって?
Hotデプロイをやめれば問題は出ないだろうけど、それではSAStrutsの意味がない。
SAStrutsとServletの連携が変態過ぎると言うこともあるけど、Cometの実装の都合上Strutsだけでは実現不可能だ。
SAStrutsとCometServletとの間をSocketにしたりして結合度を下げる解決策もあるだろうけどめんどくさい上になんか邪道だ。

うーん。どうしよう。

スマートなプロパティ変更通知

あるオブジェクトのプロパティが変更された場合に、他のオブジェクトへ通知する必要がある場合は多い。
自分で実装しても良いが、JavaBeans用の既存の仕組みを使わせてもらう。

具体的にはイベント通知のサポートをしてくれるPropertyChangeSupportをフィールドに持ち、PropertyChangeListenerを外部から受け入れ、任意の箇所でPropertyChangeSupport.firePropertyChangeを呼び出して各リスナに通知する。

以下のページに簡単に解説してある。

プロパティの変更を通知する仕組みを作成する


プロパティのバインドとサポート・クラスの利用

簡単便利でお手軽により協調的な仕組みを作ることができる。ナイス。

Readability over Convention

最近、流行のS2Daoを試してみた。
S2DaoはCoC(Convention over Configuration:あるルールでコードを書けば設定ファイルなんて要らないZE☆)と言う思想で、設定ファイルを極力書かずにORマッピングができるので非常に便利だった。アクセステーブル名はクラス名、カラム名はプロパティ名、DAOの検索メソッドはselectから始まるメソッドなど。制限も少なく、さくさく書ける。あとはフレームワークが全部してくれる。iBATISとかHibernateと比べると圧倒的に楽だ。

が、Ruby on Railsで流行りだしたCoCブームも一段落すると欠点も見えてきたようだ。
CoCだと規約(Convention)を理解していないと、何をやっているか全くわからないという点だ。(全くというのは言い過ぎか)
CoCはおそらく開発者のリズムを崩さないと言う要素を最大化する思想なので、精通した技術者が少人数でガシガシコードを書くには最適だと思われる。
しかし現実のプロジェクトでは人の入れ替わりは激しいし、人によって技術の差も大きい。
よってそんなときCoCは足かせとなってしまう。

と言うことで、それは問題だ!と思う人向けにS2JDBCと言うのが出てる。
こちらは既存のフレームワークのリズムの悪さと、CoCの規約まみれの解決策として可読性を最大化するように設計されているようだ。
こちらはまだ全然使っていないので、何とも言えないが、コードを見る限りよさげだ。
たとえば検索クエリは以下のようになる。

S2DaoよりもSQLを細かいところまで自動生成してくれるようなので、S2JDBCの方がさくさく書けるかもしれない。

Web系Javaはこういう「こんなん便利じゃね?」「いやいやこういうやり方こそスマートだ!」みたいなのがどんどん出てくるので面白いな。
おそらく、全部のフレームワークの良いところばかり集めたバランスの良いフレームワークが一番なんだろうけど、今はまだ試行錯誤の段階でRuby on RailsやS2Daoのような提案手法に特化したものが出尽くすのを待つ時期なんだろう。

GEFがスタンドアローンではうごかないっぽ

Eclipse上で簡単にグラフィカルなエディタを作成することのできるGEFを使って、仕事で使うツールを作成しようとしたら以前よりEclipseランタイムとの結合が強くなっているのかどんどんEclipseのライブラリを要求され、そのうち「Eclipseが初期化されてないぜ!」とか怒られた。つまり自分で作ったアプリの上でGEFだけ使いたいと言うことができない。Eclipse上のプラグインとしてならもちろん動作するだろう。
ふーむ、Eclipse2.0時代のGEFならいけたんだけど・・・。

GEFのFAQを見てみると以下のように書いてあった。

ふぐぐ・・・。無念。
古いバージョンのGEFでやってみるか。古いGEFでも多少Eclipseのライブラリを要求されたので同じバージョンのをそろえるのが大変そうだが。