JavaOne Tokyo 2005
技術調査と称してJavaOneへ
午前中の基調セッションを聴く。
意外と聴衆の年齢層が高かったのは驚き。
当然通訳なしで聞くでしょう!
大きなケーキでHappy Birthday Java 10thを祝ったのは
さすがアメリカの会社。歌手まで連れてきてましたからね。
もらったものリスト
技術調査と称してJavaOneへ
午前中の基調セッションを聴く。
意外と聴衆の年齢層が高かったのは驚き。
当然通訳なしで聞くでしょう!
大きなケーキでHappy Birthday Java 10thを祝ったのは
さすがアメリカの会社。歌手まで連れてきてましたからね。
もらったものリスト
JDKにも付属しているが使いやすいのは
DJ De Compilerである。
なんでこういうのが必要かといえばソースがなかったり、今あるバージョンとCVSのソースの整合性に自信が持てないケースだったりするから。
最終的には動いているのが最新バージョンということになるとこいつの出番である。
検索キーワードを見ていると相変わらずStrutsのActionがスレッドセーフであるか否かを探していると見られるものが多い。
最近出たStruts2は知らんが、Struts1.x系においては一度作ったActionインスタンスはアプリケーションサーバー(Tomcat等)を再起動するまではずーとプールされる。
ためしにこんなコードをActionクラスのexecuteに入れてみれば一発だ。
System.out.println(this.hashCode());
なんどリロードしてもハッシュコードは一緒。つまり同一インスタンスなので読取専用以外のフィールドは持たせないほうがよいということになる。ユーザー固有の情報(たとえばHttpSessionなど)をフィールドにもたせてActionクラスを作ってしまうと自分一人のテストでは問題が発覚しなくても、結合テスト時などの複数人同時に利用したときに問題が発覚して大慌てになるので注意。
Actionがスレッドセーフでない理由はパフォーマンス的にリクエスト毎にActionをnewするのはよろしくないという設計者の思想があるからしょうがない。
ActionをスレッドセーフにするためにSpringなどのDIコンテナを利用してActionインスタンスをリクエスト固有で生成するようにしていたやり方もある。
本番環境にUPする時など環境が変わったときに遭遇しやすいこのエラー
開発時は問題なかったのにいざ本番で出るとびびる上にだいたいそういう時はテンパリモード1000% なので冷静な判断ができないだろう。
しかし結局のところクラスパスの設定が間違っているというのがほとんどなのでクラスパスを列挙させてみるといい。 たとえばjspなら以下のコードを入れてみる。
<%そしてきちんと動く環境のクラスパスと比較してみる。 クラスパスがまったく一緒なのに動かない場合はJARファイルなどのライブラリがきちんと存在しているか? コンパイル環境ではコンパイルエラーとなるので気づくが、実行環境ではクラスをロードするときまでエラーが出ないので厄介だ。
たいていの場合において開発環境にはたくさんJARファイルが入っていてどれがどれに依存しているかわからなくなって、 必要なJARを本番環境にコピーし忘れてこのエラーに遭遇するというケースも多い。
JDKはすでに6.0が出ているというのに、いまだアプリケーションサーバー各種での採用があまりなく(特に商用)利用例が少ないのが実情ではないだろうか?
各いう自分も今やっているプロジェクトはJDK1.4系である。しかしTomcatなどのオープンソースプロダクトの最新版はもうJDK5.x以上しかサポートしていない。というわけで複数の別バージョンのJDKを入れるというニーズもあるだろうからそのやり方を書いておく。
Linuxと違いJDKのWindows版はインストーラーしかない。しかしデフォルトではバージョン名がフルに入ったディレクトリにインストールされるので別バージョンを入れたとしても上書きされるような事態にはならない。なので後は使うアプリケーションごとにJAVA_HOMEの値を切り替えて置けばよい。
Linuxではアーカイブ版があるので違ったディレクトリに解凍し、こちらも環境変数の値を使うバージョンにあわせて設定すれば共存ができる。
ちなみになんでJDK6を入れたかというとTomcat6を試してみたかったからなのである。
ApachetとTomcatといえばmod_jkで連携するのがこれまでの定番であった。 Apache2.2からはApacheの組み込みモジュールで連携することができる。
実験はVineLinux4.1で行った。 Apache2.2はaptでいれるとmod_proxyやmod_proxy_jspは入っているはずである。
Tomcatのバージョンは6.0.13だが、Tomcat側はajpプロトコルが使えればいいので5だろうと4だろうと多分大丈夫。
下の例は同じマシンで動くTomcatのexampleコンテキストに/exampleでアクセスできるように設定したものである。
#Tomcat Connect Setting
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
<Location /examples/>
ProxyPass ajp://127.0.0.1:8009/examples/
</Location>
Apache2.2が出て間もないのと、mod_jk並に細かい設定ができるかわからないので、 JKによる連携が急激に廃れることはないと思うが、 コンパイルする必要がなく設定だけで連携できるのは個人のテスト環境としては楽でいいのではないだろうか?
PHPに比べるとめんどくさいのね。
入力はString型とする。
入力日付フォーマットをチェックしつつ日付の妥当性も検証する。 なお内部的にはCalendarクラスのsetLenientメソッドを呼んでいるので引数がintの場合はCalendarクラスを利用したサンプル (そっちは世の中にありふれてる)を使うといいだろう。
String dateString = "2007-07-30";
SimpleDateFormat sdf = new
SimpleDateFormat("yyyy-MM-dd");
// 日付解析を厳密に行う(2/31などはNG)
sdf.setLenient(false);
try {
Date date = sdf.parse(dateString);
return sdf.format(date);
} catch (ParseException e) {
// 不正な日付のときはこのブロックに到達
}
JavaScriptとかASPでも同種の処理を作った覚えがある。 たしかフォーマットをかける前とかけた後を比較して同じだったら正常日付、違ったら不正日付とみなしていた。
JDBCドライバをダウンロードする時に困るのが複数タイプのドライバがあってどれをダウンロードすべきかということである。とりあえずどんなタイプがあるのかだけでも抑えておこう。
タイプ1 ODBC経由。windows環境でお手軽に試す場合。JDBCドライバが用意されていなくてもODBCドライバがあればよい。最近ではSQLServerもJDBCドライバを提供しているので本番環境での出番はなさそうである。
タイプ2 クライアント側にネイティブライブラリ必要。OracleのJDBC接続はまだこれが主流ではないだろうか?Oracleクライアントの設定とさらにJDBCの設定が必要なので面倒だ。
タイプ3 ミドルウェアサーバー経由。というだけでほとんど実利用されているのを聞いたことがない。絶滅寸前か?
タイプ4 100%JavaでJDBCドライバのみで接続可能。一般に重い商用DBクライアントをインストールしなくていいので便利。OracleやDB2など主要なDBMSではドライバが提供されている。
はじめてみたこのエラー
コンパイルされたのが5.0で実行したのが1.4なのが原因のようだ。
5.0でコンパイル→古いバージョン1.4では認識できずにエラーとなった。
ちゅー感じ
動かそうとしたアプリは1.4対応と書いてあったのでJDK1.4環境でコンパイルしなおせばよいのだが、もうJDK5.0が当たり前の時代なのかと思わせる出来事なのであった。
特に難しいことはない
SunのサイトよりRPMファイルのJDK(原稿執筆時点ではjdk-6u7-linux-i586-rpm.bin)をダウンロードして、 実行権限を付与して、ライセンスに同意するのみ
CentOSではパスの通っているところにシンボリックリンクが作られるので特段設定は必要ない
なおwgetを使ってダウンロードするときにファイル名が長すぎる時は以前のエントリーを参考に対処してほしい
wgetでファイル名が長すぎると出た時
前のエントリーに続きCentOSでJava環境構築シリーズ
Apache2.2になってmod_proxy_ajpを使うようになり、mod_jk(もはや非推奨)の時代より格段に楽になった。
通常CentOSのApache2.2にはモジュールがすでにインストールされた状態なので後はTomcatのインストールと連携の設定をhttpd.confに加えるだけとなる。
公式サイトからダウンロードして解凍する。/usr/javaに解凍してstartup.shを実行したらそのまま実行できた。 環境変数の設定いらないんだ・・・
一応ブラウザで単体で動くことを確認しておこう
http://サーバIP:8080/
以下の設定をhttpd.confに加えるだけ、LoadModuleの設定は不要だが、 VineLinuxでやるときは必要だったのでコメント行として残しておいた。
#Tomcat Connect Setting
#LoadModule proxy_module modules/mod_proxy.so
#LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
<Location /examples/>
ProxyPass ajp://127.0.0.1:8009/examples/
</Location>
http://サーバIP/examples/
Tomcatのサンプルが出てくればOK
相変わらず
Struts Action スレッドセーフ
のキーワードが多いことから、Struts1が使われていることが多いんだろうけどもStruts2になるとリクエストごとにオブジェクトを生成するようにアーキテクチャが変わった。
ほかにもActionクラスはPOJOオブジェクトでよいなど、1のバージョンアップというより別アーキテクチャのフレームワークと考えた方がいいだろう。
というか1もまだ精力的にバージョンアップされているのである。
このエラーに遭遇するのは二回目。
原因としては6.0でコンパイルしたものを1.4で動かそうとしたからなんだけどもね。
いろんな変更を同時にやってしまった場合は、いきなりエラーが出てあせるかもしれない。
ソースをもってきて、1.4環境でコンパイルしなおせば解決する。
いくら6.0が出てるとはいえバージョンアップしたことによるアプリによっては、不具合報告を聞いているのでまだ5. 0の需要はあるのではないか?
もしくは既存プロジェクトのためJDK1.4.2を使わざるを得ないとかね。
というわけでいざとなると見つけづらい過去バージョンのURLは以下になる。
SeaSar2プロジェクトのSuperAgileStruts
設定ファイルを書かなくていい&HotDeploy対応なのでほんとさくさくと開発が進められる。
HelloWorldを出すのに何ステップも手順が必要だった従来のStrutsよりはるかに使いやすい
そして公式ドキュメントにも記載があるが、リクエストごとにActionインスタンスを生成するのでスレッドセーフである。
昨日も出た。
前回のエントリーでは以前のバージョンでクラスのコンパイルし直しだったが、今回の根本原因はJDKが複数はいっており、古いJDKで実行しようとしていたことであった。
なのでJDK6にJAVA_HOMEを向けてやれば再コンパイル不要。
たまに行儀悪いアプリケーションが勝手にJDKのパスを書き換えて、知らない間に動作しなくなることもあるので、どのアプリを実行するにしても個別にJAVA_HOMEを設定しておくことをお勧めする。
こちらもより詳細にエントリーを書き直しました。リンク先を御覧ください。
これもよく遭遇するといえば遭遇するエラー
エラーメッセージの日本語訳が微妙なのと、該当コンテキスト以外はTomcatが正常に動いており、404を返すのでなんで??と思ってしまいがち。
根本原因はweb.xmlやfilterなどの設定がおかしいためこのコンテキストの初期化に失敗しており、その場合Tomcatはそのコンテキストが存在しないものとして扱う。というわけで404エラーがでていたらちゃんとTomcatのログを見よう。特にweb.xmlを大幅に編集する場合は、事前に動いているweb.xmlのバックアップを取得しておくべきだろう。
二重送信を防いでくれる同期トークン。いろいろ議論はあるがCSRF対策にもなるだろう。
お手軽にできるのだが、タブブラウザ全盛時代には使用上の注意がある。
同期トークンの仕組みはHIDDENの値とセッションの値が同一であることを比較する。
つまりセッションに入った値が空もしくは別のトークンであればエラーとなる。
ここに問題があり、同一セッションを持つブラウザで別のトークンを発行した時点で、以前発行されたトークンはセッションから追い出されて処理が継続できない。
これが作られた時点ではあまり問題にならなかったのだろうけど、今はタブブラウザ全盛だからな。
改良の余地あり。
エントリーを新たにわかりやすく書き直しました。
リンク先をご覧ください。
NoClassDefFoundErrorなどに比べるとわかりやすい。
ClassForNameしているところが原因なのでたいていはスペルミス。
スペルが正しいのならNoClassDefFoundErrorと同じ対処法でjarのライブラリをチェック!
テストクラスが外部リソース(DBや外部サイト)に接続している場合、単なるコンパイルだけでテストしてしまうのは非常に時間がかかり生産性が落ちる。
2時間かかっても終わらなかったのでテストなんてやってられるか!最後に一回やればいいんじゃというわけでテストをスキップするオプション
-Dmaven.test.skip=true
ゲームソフトメーカーぢゃないよ。
HudsonというCIツールを仕事で使うことになり、必死で勉強してますわ。
404は要求したリソース(≒ページ)が見つからない時に出るエラー
一番最初に確認すべきはURLが正しいか?
ピリオドがカンマになっていたりとか、パスが間違っていたりとか。テンパっている時こそ落ち着いて確認すべき。
次に仮にURLが正しい場合で404エラーが出る場合は、HTMLなどのスタティックコンテンツを同じディレクトリにおいてみる。 そして404エラーが出ないようであれば設定漏れ(web.xmlを更新して再起動してないとか)
最後にHTMLコンテンツですら見えない場合は、コンテキストの起動に失敗している可能性が高い。 たとえばweb.xmlが不正であったり、filterの設定でクラス名を間違えて、 ClassNotFoundExceptionが発生している場合そのContextは無効になる。 他のコンテキストは大丈夫なので一見エラーではないと勘違いしがちであるが、 Tomcatの起動ログではエラーのスタックトレースが発生しているはずである。
web.xmlの大掛かりな変更をする場合は、バックアップをとるのは必須といえよう。
Javaコンパイル時に発生したエラー
UTF-8で先頭に特殊文字が入っていると出る可能性がある。
とくにメモ帳で開くとかならずこのエラーが出てしまうので注意。
BOM除去で検索すると対処法が見つかるかも
自分が遭遇することも多いので必然的に検索ランキング上位に位置するようになった前回の 「NoClassDefFoundErrorの原因を探る 」 エントリーでは特にWebアプリの対応を挙げていなかったので今回はTomcatに焦点を絞ってみたい。
Tomcatの場合共通系のライブラリにjarファイルを置くことはあまりしない(JDBCドライバぐらい)はずなのでjarファイルを置く場所としては下記の場所になる。
コンテキストベース/WEB-INF/lib
ここに目的のjarファイルがないとNoClassDefFoundErrorが実行時にでてしまう。 難しいのは実行時にしか起こらないことと、コンパイル時には判明しないjar同士の依存関係があること。
あわてることは無いので一つ一つ出るたびにjarファイルを探してきて、 上記ディレクトリに配置してTomcatあげなおすの繰り返しで解決するしかない。
実行時エラーとはいえコンパイルエラーと同様本来起こってはいけないエラーなので、 設定系を担当している人はこれが出てしまったら自分が怠けているってことだぜ!
TomcatがどのJDKで動くのかというバージョン対応表
超基礎レベルだけど意外と検索キーワードが多いので
コマンドラインで
java -version
これだけ。
そもそもそのコマンドがどこにあるのかはちゃんと起動スクリプトなり、JAVA_HOMEをみるなりして確認しないとだめですぞ。
環境によっては複数のJDKを入れていて、共存させるためにJAVA_HOMEはき同スクリプトで指定させる場合もあるので、 自分がどのパスのJDKを使っているかをはっきりさせるのが第一。
結構よさげ。家帰ったら試そう
なにも指定しないとOSの標準の一時ディレクトリ場所になるが、指定したほうがよい。
というわけで起動オプションに下記の文字列を指定する
-Djava.io.tmpdir=/hoge/tmp
Javaの資格試験の一つ
正直なところ最近はフレームワークばかりなのであまりServlet,JSPなどを直接触ることもないし、 多少APIを覚えたところで、google様に頼れば仕事はできるからこの手の資格はあまり重要視しない。
世の中的にはどのぐらいニーズがあるのか知りたいけど。
UTF-8を表すためのバイナリが先頭3バイトに入るのだが、見た目ではぜんぜん気づかないので厄介。
バイナリエディタで該当ファイルを開き、EF BB BFの順番で始まっていればBOM付
XMLの読み込みなどでエラーが出る可能性が高い。混じっても実行時までわからないのが厄介どころ。
Byte Order Markの略でBOMだが実際には爆弾の意味と等しいかと。
今日始めて遭遇。
コンストラクタで例外が発生してしまうと初期化に失敗する。以後そのクラスは無いものとして扱われる。
NoClassDefFoundErrorの前に初期化に失敗している旨のメッセージが出ているはずである。
jarファイルにすると当然一つのファイルにまとまる利点がある反面そう簡単に中身を更新はできない。 故にリソースファイル系にちょっと修正を加えて実行したい時にまたjarを作り直すのも面倒な話。
本来こういう場合はリソースファイルだけを切り出して、 クラスパスの優先順位を上位にすればそちらのリソースがjarの中身より優先して読まれるのだが、 残念ながらそうなっていないプロジェクトもある。
というわけでその場でjarファイル解凍して、再び作成するテクニック
1.作業用ディレクトリ確保
mkdir -p /tmp/jartmp
2.作業用ディレクトリに移動
cd /tmp/jartmp
3.解凍
jar xvf 目的のjarファイル
4.リソースファイルを変更する
5.jar再作成
jar xvf 新しいjarファイル名 *
これでOK!
へー使えるんだといまさら発見
下記のように使う。
<Context>
path="/Sample"
docBase="${catalina.home}/webapps/Sample">
</Context>
hudsonでsvnを利用する場合、過度に負担をかけないように「アップデートの使用」というオプションがある。
これだと前のファイルが残るのだが、圧倒的に早いためこれを使い出したところ、 コンフリクトを起こしたファイルはいくら直してもコンフリクト状態のままという現象になる。 おそらく前の衝突ファイルが残っているためだと思われる。
ソースがほぼ衝突しないような安定したプロジェクトでない限り、このオプションの利用はお勧めできない。
antを使ってみた。もっと他に楽なやり方があるかもしれない。 用途としてはSeasar2のdiconファイルの一括リネームだった。
<move todir="対象ディレクトリ"
includeEmptyDirs="no">
<fileset dir="対象ディレクトリ"/>
<mapper type="regexp" from="(.*)_dev.dicon"
to="\1_ut.dicon"></mapper>
</move>
起動時の引数を利用してserver.xmlの内容を置換えるようにできる。
これは目からうろこだった。
たとえばクラスタリングにつかうjvmRouteの値などはほとんど同じ設定の二台でjvmRouteだけが違うのに二つのファイルを用意するのはミスの元。
というわけでjava起動時の-Dオプションで指定しておけばあとはシェルでおなじみの変数表記で置換えてくれる。
Tomcat起動引数に以下のようにして起動
-DjvmRoute=a
server.xmlでは以下のように記述
<Engine name="Catalina" defaultHost="localhost" jvmRoute="${jvmRoute}">
そうするとjvmRoute="a"と記述したのと同じことになる。すばらしい!
NoClassDefFoundErrorよりは出現頻度が低いけど、出たら焦ってしまうエラー。
根本原因はコンパイル時に使ったjarファイルと実行時のjarファイルが違うこと。
jarファイルが同じものか確認することと、同じクラスを含むjarファイルが混在していたりしないかチェック。
一度あったのが、アプリをバージョンアップした後に古いjarがworkディレクトリに残っていたためにこのエラーが出たというケース。
CLDCやらwireless Toolkitやら
どれをインストールすればいいんじゃー!と思っていたら、オールインワンの開発パッケージがSUNからでてた。
今後はこれを使っていくのがいいのかな。eclipseはJ2ME分野ではまだ情報が少ないし。
早速インストールしてみる。
開発環境とエミュレータセットですぐ開発開始できる!
すばらしい。