2007年5月30日水曜日

jhat, net/ftp, net/telnet

64-bit JVM の binary heap dump を見る必要が生じたので昔セットアップした
hat で見てみる。そうすると 64-bit はサポートしていない (4 byte の id
しかサポートしていない) といわれる。

適当に探すと jdk6 の jhat は 64-bit JVM のデータも見れるとの事。
確かに動いた。以前は興味もわかなかったが OQL という SQL みたいな
言語で heap dump にいろいろ検索をかけられる。複雑なクエリでは
クラスローダのリストを出したり System のプロパティをダンプしたりと
いうこともできるらしい。勉強してみよう。

---
ruby の ftp などは mechanize などを使わないといけないと早合点して
いたが、標準の Net パッケージに入っていることに気づいた。
jruby 上では ftp のリストや telnet の login 等がエラーになって
しまった。windows の ruby でやると エラーにならないことから、
jruby っぽい。bug をあげればいいのだろうか?

2007年5月28日月曜日

some experiments

企画中の Web Application の中に簡単にサーバー上のパスをたどれる
機能を持たせたい。まだ Struts を学び始めのころいろいろ試行錯誤
して断念したのを覚えていて、それからこれは億劫だ。

でも、Rails ならば手間が少ないだろうとはじめてみる。



  1. パス用の TextField の右に link を用意して、そのリンクが押されたら
    ディレクトリが選べるようにする。一旦ルートを離れると同様のアクションを
    引数つきで呼び出すファイルのリストも表示する。一旦構成要素が選ばれ
    たら TextField の内容も更新する。

    これは結構簡単にできて、よさそうだったが、テキストフィールドの
    初期値を処理に反映できない。わかっている部分までパスを入れて、
    その後の部分をアクションに補助してもらうということができない。
    なぜなら、link はフォームのフィールドを送れないからだ。たとえ
    text_field でドメインオブジェクトを TextField に結び付けて
    いてもだ。




  2. submit_tag でボタンを作ってそのアクションでディレクトリを選択して
    いく。この方法ではアクションをボタンの id で判別するしかないし、
    引数は渡せない。1と併用しなければならないし、そうするとコードが
    増えるだけだ。ボタン固有の引数が渡せると便利なのだが...


    もうひとつ、このような機能を持たせたいフィールド部分だけ、専用の
    フォームとして使用できないかと考えてみた。これにかんしては
    form tag のネストは標準的でないということで断念。




  3. 最後の方法は JavaScript の利用だ。できればこれは避けたかった。
    汎用的なものを作るのが難しいのと、デバッグがやりにくいというのが
    大きな理由だ。ただし、DOM と非同期通信を使えばいろいろなことが
    できるはずだ。


    Spring 2.0 本でみた DWR のサンプルを思い出した。ぱっと見は現在の
    ウインドウの中にモーダルウィンドウが現れて、そこに操作をして
    submit という流れに見える。



    サンプルを見ていくと script.aculo.us の effect の Effect.Appear
    を使って小さなウィンドウを表示しているように見える。その機能だけを
    試してみると画面上の div 要素を効果をつけて表示している。どう見ても
    モーダルウィンドウを表示するとはことなる動きだ。



    ちょっと考えて、確認して納得したが、サンプルでは CSS を使って
    位置、サイズを固定した div 要素に対して Effect.Appear を使っている。
    サンプルとしてはいいのだが、これでは困ったことがおきるのではないか?
    画面上に他の画面に移行するフォームやリンクがあったらどうする?



    script.aculo.us を検索していると lightbox というライブラリを見つけた。

    http://www.huddletogether.com/projects/lightbox2/

    サンプルで写真を選ぶと (CSS で作ったと思われる) ウィンドウが現れ
    拡大された写真が表示される。背景にはシェーディングがかかって
    暗くなっている。見た目はなかなかいいぞ。




元はといえば WLS の deploy 画面が便利だなという漠然とした感想が
あったのだが、よく考えるとこちらはフォームを使っていない。
何らかの基点を記憶させて置くことはできるのかも知れないが、
これくらいなら作れそう。

http://localhost:7001/console/console.portal?
_nfpb=true&
_pageLabel=AppApplicationInstallPage&
AppApplicationInstallPortletISUNIX=YES&
AppApplicationInstallPortletFILECHOOSERPATH=%2Fhome%2Froot


----
関連情報を探しているうちに ajax4jsf というのを見つけた。
サンプルの war が見当たらない...

https://ajax4jsf.dev.java.net/
http://jboss.org/projects/jbossAjax4jsf

2007年5月20日日曜日

Ruby on Rails notes

Rails に関するページや本をざっとみてこれはデータベース依存の大きな
フレームワークで、色々な制約があって単純に Web Application を移植
する対象として見れないのではないかという印象を受けていたが、よく
見て行くと、そうでもないらしい。

単純なケースとして rails で作成したプロジェクトにひとつコントローラを
設けるだけでページとして機能する。

> rails simple
> cd simple
> ruby script\generate controller info
> notepad app\views\index.rhtml

これだけでよい。以前、色々試したときの経験から自動生成されている
config\database.yml の設定が邪魔をするかとも心配したが、モデルが
何もなければデータベース接続は発生しない。

次のステップとしてリンクやフォームの処理が気になってくる。

コントローラの中には次の様な記述がよく現れる。
render :action => 'new'

ruby では推測可能な部分の括弧は書かなくてもよいという特徴がある。
なんとなくハッシュを渡しているのはわかるが、なぜキーがシンボル
(:name :に続けて識別子のようなもの)なのか今ひとつ不明。
http://www.rubyonrails.org/ から API のページを見てみる。
http://api.rubyonrails.org/ 色々と説明はあるのだが今ひとつ
仕組みがぴんとこない。

http://www.rubyonrails.org/ に Source のリンクがある。
そこに SVN の URL がある。ブラウザで開いてみるとソースをブラウズ
出来た。
http://svn.rubyonrails.org/rails/branches/1-2-stable

API の各ページのはじめに定義があるファイル名が表示されている。
そのパスを参考に実際のソースを見ることが出来る。

render の場合は次のパス。
http://svn.rubyonrails.org/rails/branches/1-2-stable/actionpack/lib/action_view/base.rb

link_to の場合は次のパスになる。
http://svn.rubyonrails.org/rails/branches/1-2-stable/actionpack/lib/action_view/helpers/url_helper.rb

これらのソースに symbolize_keys というメソッドがハッシュに対して
よばれているところがある。ハッシュに関するメソッドかと思い、小さな
スクリプトで動作を確認しようとすると、そんなメソッドはないといわれて
しまう。

Rails の API を見ると vendor/rails/activesupport/lib/active_support/core_ext/hash/keys.rb
の中で定義されている。パスの名前から (core_ext) rails の activesupport
でハッシュの機能を拡張していることのように見える。

activesupport を require して試してみようとも思ったが rails の
コントローラで試しても同じことだろうと思ってコントローラの中で
ハッシュに対して symbolize_keys をハッシュに対して呼んでみる。

その結果 "abc" => "ABC" が :abc => "ABC" のように変わって
入ることがわかった。これからいくと render/link_to の引数で

render :action => 'new'

と書くのは

render 'action' => 'new'

と同じということだろう。ではなぜか?

おそらく、文字列のためのメモリをセーブするためではないか。
Java でいうところの String.intern() にあたるのではないかと
おもう。

----------
では上記の render 文が何をするかということだが、action で
指定された view をクライアントに返すということのようだ。
極端な場合、コントローラーアクションは色々定義しても
ひとつの view (rhtml file) で表示させることも出来る。

コントローラを作ると ApplicationController のサブクラスが
作られるがこの中にアクションに対応したメソッドを定義して
おくことで呼び出すことが出来る。


def list
@item_pages, @items = paginate :items, :per_page => 10
end


この後、アクションと同じ名前 + .rhtml で表示が行われる。

index.rhtml に関しては index というアクションがなくても
良いようだ。また、'index' というアクションで index ページ
を表示させることもできる。

----------
.rhtml (embedded ruby) の側ではリンクに次の様なものが現れる。

<%= link_to 'Show', :action => 'show', :id => item %>
<%= link_to 'Previous page', { :page => @item_pages.current.previous } if @item_pages.current.previous %>

link_to も Rails のメソッドで API doc を見て行くと仕様がわかる。

link_to(name, options = {}, html_options = nil, *parameters_for_method_reference)

最初のケースでは :id => item という引数を渡しているがここには任意の
物が指定できるようだ。ただし、受けたコントローラでは symbolize_keys
の逆操作が行われているようで、key を :myparam としてもコントローラで
params をダンプしてみるとキーは "myparam" となっている。ただし、
依然として params[:myparam] での参照は可能。

また、item は item.id と同意ということのようだが、これがどこからきている
かは今のところはっきりしない。

----------
http://railsapi.masuidrive.jp
AJAX を使用した部分的に日本語化された rails のマニュアル。

----------

def list
@item_pages, @items = paginate :items, :per_page => 10
end


これはレコード数が多くなる可能性のあるページに使うイディオムの
様だが左辺が二つある。よく考えると多重代入だし pagenate の API
にも pagenator と collection を返すと書いてある。

複数の値を返すという概念は C++/Java にはないので少し困惑したが、
多重代入の本質はリストを返すことと、それをどのように代入、グルーピング
するかということで、メソッドの最後でリストを評価すればいいと言う
事。便利だが、仕様をしっかり書いておく必要がある。

----------
フォーム


<% form_tag :action => 'escape' do %>
<%= text_area 'etext', 'rawtext', :rows => 8 %><br/>
<%= submit_tag "Escape" %>
<% end %>


Form は form_tag を使って作ることが出来る。:action にポストを
受けるアクションの名前を指定する。
text_area の引数に迷ってしまったが、適当でもポストはできた。
ただ、同じ view でうけると表示上のデータは消えてしまう。
要は部品にバインドできていない状態だ。

これは最初の引数をコントローラのフィールドのドメインオブジェクトの
名前に、二番目をそのフィールドにすることで解決できたようだ。
上記の例では


def escape
@etext = Etext.new
@etext.rawtext = params[:etext][:rawtext]
@etext.escape
render :action => "index"
end


Etext は app/models/etext.rb で次のように定義している


class Etext
attr_accessor :rawtext
attr_accessor :escaped_text
def escape
@escaped_text = @rawtext.gsub(/&/, '&amp;').
gsub(/</, '&lt;').gsub(/>/, '&gt;');
end
end


生成される HTML は次のようになる。


<form action="/info/escape" method="post">
<textarea cols="40" id="etext_rawtext"
name="etext[rawtext]"
rows="8">...</textarea><br/>
<input name="commit" type="submit"
value="Escape" />
</form>


ポストされたデータは params という名前のハッシュで参照できて
次のようになっている。

{"commit"=>"Escape", "action"=>"escape",
"controller"=>"info", "etext"=>{"rawtext"=>"..."}}
----------
||=

これは変数が nil の場合のみ右辺値を左辺値に代入するというオペレータ

----------
variable.nil?

これは変数が nil かどうかをテストするメソッド。未定義、未設定のものに
テストできるのは面白い。C++/Java でやれば例外、エラーになるところだ。

----------
@rawtext.gsub(/&/, '&')

gsub() は指定された置き換えの結果を返すが gsub!(...) では元の
文字列が書き換えられる。! は破壊的動作のしるし。C++ でいうところの
非 const 関数といったところか。? は問い合わせ
をするようなメソッド名の最後につけておくという慣例らしい。

----------
クラス名は大文字で始める必要がある。

フィールドの定義は attr_accessor でできる。引数はフィールド名の
前に : を付けた物(シンボル)

attr_accessor :rawtext
attr_accessor :escaped_text

----------
Model の定義に関係を belongs_to などで指定できる。


class ListItem <ActiveRecord::Base
belongs_to :user
end


一見、設定ファイルに関係性を明示しているようにも見えるが(案外、それが
目的の設計かも知れないが) これは Ruby のクラス定義である。では
belongs_to は一体何なのか。これも API をたどって行ってわかったが、
これは ActiveRecord::Base のクラスメソッドである。クラスメソッドが
このようなところに書いてあると何が起こるかというとクラスがロード
されるときにこのクラスメソッドが実行される。なるほどね~。

----------
Windows 上のファイル編集には Terapad と cygwin 内の vim を使っていた。
たまたまフリーの EmEditor をいうのを見つけたので使ってみると Terapad
の Tab 版といった感じでなかなかよさそう。デスクトップがごちゃごちゃに
ならないところがいい。

----------
view の中では controller のインスタンス変数を文法上そのままアクセス
できるが、これはリフレクションのような手法を使って巧みに作り込まれた
物らしい。controller オブジェクトだが、シングルトンではないらしい。
あるアクションで設定したインスタンスフィールドを別なアクションで
表示してみると nil になっていた。

----------
最初は暗号のように見えたコントローラや rhtml の記述もその意味がわかって
くるとなかなか味がある。Ruby は以前、挑戦したのだが、その時点では
将来性が良くわからず直ぐに忘れてしまった。数年を経て C++ やら Java を
(自分としては)深く使い込んだ今見直してみると中々興味深い言語だ。
Rails はその機能をうまく拡張、利用しているようだ。

native library 依存がない物であればほぼ jruby で動くというところも
すばらしい。ruby - bytecode 変換が早く実現されないかとたのしみでもある。

2007年5月16日水曜日

Ruby on Rails First Impression

JavaOne の Ruby on Rails のデモ -- サンプルを war にして app server に
配置してみたり、動的更新をしてみたり -- がどうも気にかかっていたので、
以前やりかけてやめていた Ruby on Rails から挑戦。

手近に Linux がないので Windows で試す。日本語の短いデモにしたがって
ActiveScriptRuby で出てくるサイトからダウンロード、インストール。
gem は最初から使えるようで、remote で rails をインストール。

MySQL Manager もあるらしいことは知っていたがずっとコマンドラインだった。
でも使いやすそうなのでインストール。

あとはデモどおりに作っていくとその通りに動く。上記の短い日本語のデモは
単純すぎていまひとつだったが、環境構築に役立った。本家のデモを見ていく
事で view のいじり方の雰囲気がわかった。

同じ事をプロキシの中でやろうとしてみた。gem には --http-proxy オプションが
あるのだがこれがなかなか動かない。gem は gem ファイルからもインストール
できるので rails の gem ファイルをダウンロードしてきてインストールしようと
するが、結構依存するモジュールがあり、エラーを食らってはダウンロード、
インストールを繰り返し、何とか動くところまで持っていけた。

次に Unix 上での動作確認をしたいのだが、対象 OS が OSS に弱くてディスクも
あまりないので、あきらめようとしていたが、JRuby、そして JRuby on Rails も
結構動くらしいことを知る。

適当な JRuby をダウンロード、セットアップすれば、JDK があれば動いてくれる。
そのあとは gem 操作はほぼ同じ。ただし、多くのコマンドは

jruby $JRUBY_HOME/bin/gem ... のように jruby でコマンドラインをはじめる
必要があるようだ。ファイルを一つ一つインストールしたときの gem ファイルが
役に立った。

いろいろなところで書かれてはいたが、Rails はデータドリブンなので
どうしてもデータベースを用意しなければいけないらしい。Controller と
View だけでいろいろ試して見ることができたら便利そうだろうと model
の作成を飛ばしてやってみたが、generate scaffold の中でアクセスに
いってしまう。

そういうものだと割り切ってリモートの MySQL のデータベースを指定。
scaffold もできた。

$ jruby script/generate controller abc
exists app/controllers/
exists app/helpers/
create app/views/abc
exists test/functional/
create app/controllers/abc_controller.rb
create test/functional/abc_controller_test.rb
create app/helpers/abc_helper.rb

$ jruby script/generate scaffold abc
exists app/controllers/
exists app/helpers/
create app/views/abcs
exists app/views/layouts/
exists test/functional/
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/abc.rb
create test/unit/abc_test.rb
create test/fixtures/abcs.yml
#28000Access denied for user 'root'@'localhost' (using password: NO)


$ jruby script/generate scaffold abc
exists app/controllers/
exists app/helpers/
exists app/views/abcs
exists app/views/layouts/
exists test/functional/
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
identical app/models/abc.rb
identical test/unit/abc_test.rb
identical test/fixtures/abcs.yml
/usr/local/jruby-0.9.2/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/vendor/mysql.rb:155 warning: JRuby does not currently support defining finalizers
create app/views/abcs/_form.rhtml
create app/views/abcs/list.rhtml
create app/views/abcs/show.rhtml
create app/views/abcs/new.rhtml
create app/views/abcs/edit.rhtml
create app/controllers/abcs_controller.rb
create test/functional/abcs_controller_test.rb
create app/helpers/abcs_helper.rb
create app/views/layouts/abcs.rhtml
create public/stylesheets/scaffold.css

---------

rhtml はほとんど JSP の感覚で触れる。リンク周りの書き方が少しわかりづらいが
テーブルの背景色を互い違いにするといったことぐらいであればうろ覚えの
Ruby コードをスクリプトレットのように使って簡単に実現できてしまった。

<table>
<tr>
<% for column in Abc.content_columns %>
<th><%= column.human_name %></th>
<% end %>
</tr>

<% idx=0; %>
<% for abc in @abcs %>
<% if (idx%2)==0; bgcolor="#ffffff"; else ; bgcolor="#cccccc"; end %>
<tr bgcolor=<%= bgcolor %> >
<% for column in Abc.content_columns %>
<td><%=h abc.send(column.name) %></td>
<% end %>
<td><%= link_to 'Show', :action => 'show', :id => abc %></td>
<td><%= link_to 'Edit', :action => 'edit', :id => abc %></td>
<td><%= link_to 'Destroy', { :action => 'destroy', :id => abc }, :confirm => 'Are you
sure?', :method => :post %></td>
</tr>

<% idx = idx + 1 %>
<% end %>
</table>

<%= link_to 'Previous page', { :page => @abc_pages.current.previous } if @abc_pages.curren
t.previous %>
<%= link_to 'Next page', { :page => @abc_pages.current.next } if @abc_pages.current.next %
>

一旦 rhtml を生成した後でテーブルに変更を加えると list 操作には
すぐ反映されるが new, edit には反映されない。これらは _form.rhtml
をインクルードしていて、それ自身はデータベースの構造変更に
追従するわけではない。ここで generate scaffold すると存在する
ファイルに関しては上書きするかどうか聞いてきてくれる。_form.rhtml は
今のところ触っていなかったので、この方法で更新。

$ jruby script/generate scaffold abc
exists app/controllers/
exists app/helpers/
exists app/views/abcs
exists app/views/layouts/
exists test/functional/
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
identical app/models/abc.rb
identical test/unit/abc_test.rb
identical test/fixtures/abcs.yml
/usr/local/jruby-0.9.2/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/vendor/mysql.rb:155 warning: JRuby does not currently support defining finalizers
overwrite app/views/abcs/_form.rhtml? [Ynaqd] Y
force app/views/abcs/_form.rhtml
overwrite app/views/abcs/list.rhtml? [Ynaqd] n
skip app/views/abcs/list.rhtml
identical app/views/abcs/show.rhtml
identical app/views/abcs/new.rhtml
overwrite app/views/abcs/edit.rhtml? [Ynaqd] n
skip app/views/abcs/edit.rhtml
identical app/controllers/abcs_controller.rb
identical test/functional/abcs_controller_test.rb
identical app/helpers/abcs_helper.rb
identical app/views/layouts/abcs.rhtml
identical public/stylesheets/scaffold.css

このように JRuby on Rails は簡単に動いてしまったが、それを
war ファイルに変換する方法がすぐにわからないので検索してみると
Rails に似た Trails というのがあるらしい。こちらは最初から
java 用で model は java でコーディングする必要があるようだ。
今回は時間切れなので Trails はまたの機会にする。

古い JBoss や Tomcat をはじめて触ってはまりまくったときに比べると
すんなりと動作まで持っていけた。確かに柔軟性にかけそうな部分は
あるし、細かな書き方がいまひとつわからないがあっさりと動いて
くれるともっともっと触りたくなる。こうやって信者になってくのか???

うろ覚えで書いてみたものが動いた。文法は Perl ほどではないが、
どうも好きになれない...

def mathop(n)
rv = 0
i = 0
while i <= n
rv += i
i=i+1
end
return rv
end

printf "result %d\n", mathop(100)

2007年5月9日水曜日

Axis2, Spring Web Services, JavaOne 2007 Opening

XML-RPC に使える OSS として Axis を調べていたけど、どうやら Axis2 に
置き換えられている。Spring Web Services もオフィシャルリリースまで
もう一歩というところ。

---
JavaOne 2007 General Session から
昨年もそうだったが始まる前のステージがかっこいい。
Joe Green も板についてきた。マクネリも半袖のジョブスがうちにもきて
良かったと冗談を言っていた。

何に含まれているのか聞き取れなかったがリアルタイム java platform には
javax.realtime.RealtimeThread というスレッドがある。Solaris はリアルタイム
版の java 環境をサポートするための機能をもつ。この環境での GC が
どういうものなのか気になる。

ERICSSON GlassFish ベースのモジュールをオープンソースとして寄贈する

NASDAQ の取引は java プラットホーム上で行われており日に 50億トランザクションに
上るときもある。2007/02/27 の大量発注も裁ききった。

SONY: BlueRay が順調で何より

NetBeans 6.0: 新しい editor やサポート言語の拡張など

openJDK のオープン化が完了。最近見ていなかったが、クラスライブラリも
open になったのか? TCK というテストキットもオープンになった。
(Apache から test kit がオープンでなかったので Harmony VM が最初の
open JVM になれなかったと苦情があった...)

GPLv2 を利用。

Faster, faster, faster -- Java SE 6.0 に起動、動作にかかわるパフォーマンス
向上を次々にリリースしていく。

JavaFX:
Java SE 上で動くスクリプト環境で、リッチな GUI, Web 等が作れる。
Flash or PS シリーズの GUI 見たいな感じのものをデモしていた。

JavaFX Mobile:
Mobile にも SE 環境を提供し、その上で動く FX も提供していく。

Yahoo GO : Java FX Mobile を使い、リッチなポータル、サーチ画面を提供していく。

インターネットにつながるデバイスの総数で考えると携帯は PC の 20 倍である。
18 億のモバイル Java user がいる。この部分にてこ入れしていく。

教育や、環境にも貢献していきたい...

2007年5月7日月曜日

iBATIS JPetStore-5.0 sample

修正なし、DB セットアップなしで動いてくれるところがうれしい。Tomcat, Jetty で
問題なく動作した。

以前少しだけ触った winstone だが、JSP のタグがそのまま表示されていて
機能が限られていると思っていたが、使い方がまずかった。Winstone 自体には
JSP のランタイム、コンパイラがないのだ。

Tomcat から ./lib または --commonLibFolder=... で指定したディレクトリに
jasper 関連、commons-logging.jar をコピーして --useJasper=true を
つけて起動するとちゃんと動いた。

java -jar ../winstone-0.9.6.jar --httpPort=8989 \
--ajp13Port=8988 --useJasper=true \
--warfile=../ibatis_sample/JPetStore-5.0/build/wars/jpetstore.war

iBATIS で BMP を実装しているケースを少し確認しないといけないので
サンプルを読み解いていく必要がある。

ゼロから作ってもいいのだが、設定ファイルの配置に関する決まりごとなど
あるとはまりそうなのでサンプルからはいるのがよさそう。

あと Abator という Eclipse plugin で設定ファイルが簡単に作れるようだが、
私は NetBeans 派。NetBeans/Eclipse を同時にあげるメモリもない...

---
Spring のサブプロジェクトに rich client とか web service といった
面白そうなものを発見。今度試してみよう。

Transaction

Spring framework の TransactionManager を使用してみる。
(この部分に関しては初めてなので、内容は実験的なもので
実践的なものではありません。)

Hibernate で大量のデータを insert していくと非常に時間がかかる。
メモリ内に持つ場合に比べてオーダが2つか3つ異なる。出し方は
忘れたが、auto-commit モードでは遅いよ見たいな事を言われたことが
あったので、設定ファイルでプロパティとしてオフにしてみたり
SQLConnection/Statement を得てオフにしてみたりしたが効果がない。

ではいったいどうやって auto-commit をオフにするかを考えていた。
もちろん、全てを素の JDBC でやる気は毛頭ない。ぼんやりと思って
いたのは Spring の TransactionManager を使えばオフになるのでは
ということ。

1.X の文法はなんともわかりにくかった。2.0 の aop/tx の形式なら
何とかなるかもということでやってみる。


<aop:config>
<aop:advisor pointcut="execution(* *..XRecDaoImplTx.insertXRecord(..))"
advice-ref="txAdvice"/>
</aop:config>

<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="insertXRecord"
propagation="REQUIRED"
timeout="20"/>
</tx:attributes>
</tx:advice>


ところがこれだけではどうも効果がわからない。
aop, transaction のログレベルを DEBUG にすると動作がなんとなく
わかるようになる。


log4j.category.org.springframework.transaction=DEBUG
log4j.category.org.springframework.aop.config=DEBUG



[main] DEBUG (NameMatchTransactionAttributeSource.java:addTransactionalMethod) - Adding transactional method [insertXRecord] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT,timeout_20]
[main] DEBUG (NameMatchTransactionAttributeSource.java:addTransactionalMethod) - Adding transactional method [xrec1Tx] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT,timeout_20]
[main] DEBUG (TransactionSynchronizationManager.java:bindResource) - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@146c2f2] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@1d9a2ab] to thread [main]
[main] DEBUG (TransactionSynchronizationManager.java:initSynchronization) - Initializing transaction synchronization
[main] DEBUG (TransactionAspectSupport.java:prepareTransactionInfo) - Getting transaction for [ibatisinteg.dao.XRecDao.insertXRecord]
[main] DEBUG (TransactionSynchronizationManager.java:getResource) - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@146c2f2] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@1d9a2ab] bound to thread [main]
[main] DEBUG (TransactionSynchronizationManager.java:getResource) - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@146c2f2] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@1d9a2ab] bound to thread [main]
[main] DEBUG (TransactionAspectSupport.java:commitTransactionAfterReturning) - Completing transaction for [ibatisinteg.dao.XRecDao.insertXRecord]
[main] DEBUG (TransactionSynchronizationManager.java:clearSynchronization) - Clearing transaction synchronization
[main] DEBUG (TransactionSynchronizationManager.java:unbindResource) - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@146c2f2] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@1d9a2ab] from thread [main]
[main] DEBUG (TransactionSynchronizationManager.java:bindResource) - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@178bb14] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@1d9a2ab] to thread [main]
[main] DEBUG (TransactionSynchronizationManager.java:initSynchronization) - Initializing transaction synchronization
[main] DEBUG (TransactionAspectSupport.java:prepareTransactionInfo) - Getting transaction for [ibatisinteg.dao.XRecDao.insertXRecord]
[main] DEBUG (TransactionSynchronizationManager.java:getResource) - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@178bb14] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@1d9a2ab] bound to thread [main]
[main] DEBUG (TransactionSynchronizationManager.java:getResource) - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@178bb14] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@1d9a2ab] bound to thread [main]
[main] DEBUG (TransactionAspectSupport.java:commitTransactionAfterReturning) - Completing transaction for [ibatisinteg.dao.XRecDao.insertXRecord]
[main] DEBUG (TransactionSynchronizationManager.java:clearSynchronization) - Clearing transaction synchronization
[main] DEBUG (TransactionSynchronizationManager.java:unbindResource) - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@178bb14] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@1d9a2ab] from thread [main]


よく見ると Getting Transaction for [method name] と Completing transaction for [method name]
が繰り返し出てきている。実際に数えると insert と同じ回数出てきている。

これから、ポイントカットに入るときに開始して、出るときに終了しているらしいことが
わかる。これでは元のつくりより遅いではないか。

では大量の insert を行う処理を呼ぶ側につけてはどうか。

これをやっているときにも少しつまづいた。ポイントカットに指定するメソッドは
インターフェイスで定義されたものの実装でなければならないようである。
当然、static メソッドは使えない。継承関係が複雑な場合(あるインターフェイスを
実装した抽象クラスのサブクラスが他のインターフェイスを実装していてといった
感じ) でもうまく動かなかった。

試行錯誤の後、この方法でトランザクション中はコミットが入らずデータの追加が
早くできた。失敗した場合のことはまだ考えていない。エラーが起きないように
データを用意するか、それともエラーが起きたときにデータをチェックして正しい
データのみ登録するか、それぞれ面倒そうなのでじっくり考えることにしよう。

最初に設定したデータベース処理に近いポイントカットも enable してみると
パフォーマンスの低下は見られなかった。どうやらネストしたトランザクション
スコープからぬける際はフラッシュはかからないようだ。

ibatis のバッチモードも大量データ追加に効果があるようなことが書かれていたので
試してみたが、いまひとつ。むしろ TransactionManager が効果的のようだ。
(試したサンプルは batch mode を transaction 開始、終了で囲んであったので
batch だけやっても意味はないのかも)

Hibernate のパフォーマンスだが、最終的にはトランザクションを使わない場合の
2, 3 倍程度となった。(Embedded Derby の場合) これは少し期待はずれ。
MySQL でやると改善率は高いのだが、もともとがとても遅いわけでやはりこれも
使えない。

Hibernate をつかったプログラムでは自前でバッチ更新するコードを入れていた
(Hibernate template に Collection をわたす)。この場合、ひとつのセッションの
中で更新・追加が行われるわけで TransactionManager を使わなくとも早く
できることがわかった。ただ、エラーハンドリングをどうするかは依然として
課題である。ibatis のバッチモードもこのようなことなのかも知れない。

かなり入り口の部分でポイントカットを設定して走らせて見ると OutOfMemoryError が
でた。commit するまではどこかにためておかれるみたい。定期的にフラッシュする
簡単な方法があると良いのだが。トランザクションの本来の使い方から大きく
逸脱しているので難しそうだが...

結局、大量のデータ登録を行うには DB 固有の import がもっとも速そう。しかし、
これもいろいろな制約のついたテーブル構造・関係の下ではいろいろなエラーが
おきうるので頭が痛い...

2007年5月3日木曜日

XML catalog

SqlMap の設定を NetBeans で作っていると、補完機能が効かないのに気づいた。

以前作った spring 用の XML catalog ファイルを参考に catalog ファイルを作成してみる。
あっさりと動いた。このケースでは DTD のエントリしかないが、XML Schema でも同じ。

<?xml version="1.0"?>
PUBLIC "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN"
"http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd">
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<system systemId="http://www.ibatis.com/dtd/sql-map-config-2.dtd"
uri="sql-map-config-2.dtd" />
<system systemId="http://www.ibatis.com/dtd/sql-map-2.dtd"
uri="sql-map-2.dtd" />
</catalog>


こちらが spring 用に作ったもの。

<?xml version="1.0"?>
PUBLIC "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN"
"http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd">
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<system systemId="http://www.springframework.org/dtd/spring-beans.dtd"
uri="spring-beans.dtd" />
<system systemId="http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"
uri="spring-beans-2.0.xsd" />
<system systemId="http://www.springframework.org/schema/util/spring-util-2.0.xsd"
uri="spring-util-2.0.xsd" />
<system systemId="http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
uri="spring-aop-2.0.xsd" />
</catalog>


ファイルは DTD, Schema のあるディレクトリに .xcat の拡張子で保存した。
NetBeans の Runtime tab / DTD and XML Schema Catalogs を右クリック, Add Catalogs
で追加できる。

2007年5月1日火曜日

HTML escape

このサイトでは <, > のエスケープが効いていないみたい。プログラムや、XML の
ソースを引用するには少し不便。以前作った PHP 版の簡易エスケープツールを
SpringMVC で作り直してみた。とりあえず動作確認。



<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

<!-- Acegi config -->

<bean id="authenticationManager"
class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
</list>
</property>
</bean>

<bean id="daoAuthenticationProvider"
class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>

<bean id="userDetailsService"
class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
<property name="userProperties">
<props>
<prop key="admin">pass,ROLE_USER,ROLE_ADMIN</prop>
<prop key="guest">pass,ROLE_USER</prop>
</props>
</property>
</bean>

<bean id="accessDecisionManager"
class="org.acegisecurity.vote.AffirmativeBased">
<property name="decisionVoters">
<bean class="org.acegisecurity.vote.RoleVoter"/>
</property>
</bean>

<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="filterProcessesUrl" value="/process-login"/>
<property name="defaultTargetUrl" value="/index.jsp"/>
<property name="authenticationFailureUrl" value="/error/error-login.html"/>
</bean>

<bean id="logoutFilter"
class="org.acegisecurity.ui.logout.LogoutFilter">
<constructor-arg value="/"/>
<constructor-arg>
<bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
</constructor-arg>
<property name="filterProcessesUrl" value="/process-logout"/>
</bean>

<bean id="sessionIntegrationFilter"
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">
</bean>

<bean id="anonymousProcessingFilter"
class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
<property name="userAttribute" value="anonymous,ROLE_ANONYMOUS"/>
<property name="key" value="anonymousKey"/>
</bean>

<bean id="filterSecurityInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/login.html=ROLE_ANONYMOUS,ROLE_USER
/process-login=ROLE_ANONYMOUS,ROLE_USER
/user-info.jsp=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/error/**=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/css/**=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/index.jsp=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/welcome.html=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/vmstat.html=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/*.js=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/dynimg/**=ROLE_ANONYMOUS,ROLE_USER,ROLE_ADMIN
/admin/**=ROLE_ADMIN
/**=ROLE_USER,ROLE_ADMIN
</value>
</property>
</bean>

<bean id="exceptionTranslationFilter"
class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/login.html"/>
<property name="forceHttps" value="false"/>
</bean>
</property>
<property name="accessDeniedHandler">
<bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
<property name="errorPage" value="/error/error-access.html"/>
</bean>
</property>
</bean>

<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=sessionIntegrationFilter,logoutFilter,authenticationProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor
</value>
</property>
</bean>
<!-- end Acegi config -->

<bean id="customerService" class="uxtool.webflow.DefaultCustomerService"></bean>

</beans>