• JavaEE 7 リリース!

    JavaEE 7 がというかGlassFish 4 がリリースされた。 会社でテストサーバとして使っているのは、GlassFish 3.1.2.2 なので早速入れ替えてみた。 余談だが運用サーバはTomcatなので、どちらかというとテストサーバの方が性能が良い。 で、意気揚々と起動してみた訳なのだが、起動できない。 理由は単純で、GlassFish 4 はJDK7が必須にもかかわらず、プロジェクトのJDKのバージョンがJDK6なのである。 まあコンパイル時にサーバのクラスのバージョンよりも古いバージョンでコンパイルしているよ、という警告が出ていたので 何となくは思っていたのだが、改めて起動できないとちょっとショックではある。 ひとまず、DataSourceの設定が今までと同じように出来ることは確認したので、 それについて書こうと思う。 ドライバの配置 GlassFishで利用できるJDBCドライバには下記のものがある。 Administration Guide: ■ IBM DB2 Database Type 2 Driver ■ IBM DB2 Database Type 4 Driver ■ Java DB/Derby Type 4 Driver ■ MySQL Server Database Type 4 Driver ■ Oracle 10 Database Driver ■ Oracle 11 Database Driver ■ PostgreSQL Type 4 Driver ■ DataDirect Type 4 Driver for IBM DB2 Database
  • JSF2.0でCSSリソース内の画像を読み込む

    JSF 2.0 でCSSリソース内の画像を読み込むにはコツが必要です。 Bootstrapのglyphicons-halflings.pngを読み込むのに暫く悩んだのでそのメモなど。 フォルダ構成は次のようになっています。(Mavenです) root +-- src +-- webapp +-- resources +-- css | +-- bootstrap.min.css +-- img +-- glyphicons-halflings.png JSFなのでXHTMLを使い、CSSの読み込みは以下のようにします。 <h:outputStylesheet library="css" name="bootstrap.min.css"/> このとき、bootstrap.min.css内のイメージのパスは../img/glyphicons-halflings.pngとなっていますが、 このままでは画像が読み込まれません。まあ、パスからも明らかですね。 問題を解決するには次のように、イメージのパスを変更する必要があります。 background-image:url("#{resource['img/glyphicons-halflings.png']}") Jun 20, 2013 追記 twitterで@den2snさんに教えてもらったのですが、outputStylesheetを下記のようにlibraryを削除して宣言することにより、 bootstrap.min.cssを書き換えなくても良くなります。 <h:outputStylesheet name="css/bootstrap.min.css"/>
  • cookie on java web start (JWS)

    今更感がありますが、JWSでのcookieの扱い、というかjsessionidの扱いについて。 JWSからサーバへの最初のアクセスでjsessionidの指定が無いと、サーバでjsessionidが発行されます。 すると、JWSからのそれ以降のアクセスには、その生成されたjsessionidがcookieとしてサーバに送信されます。 これは良く出来た仕組みで、プログラマが意識しなくてもセッションを継続することが可能になります。 ただ、自動でjsessionidを送信してしまうため、意図したセッションを継続させることが出来ないことがあります。 任意のjsessionidを送信したい 前提として以下のことが成り立っているとします。 認証はプラウザで行う 認証状態をセッションで維持している JWSでもその認証状態を維持したい、つまり認証されていなければJWSを操作させたくない場合、 簡単な方法としてJWS起動時にsessionidを渡せば、JWSからのリクエストにjsessionidを付与することが可能になり、 認証状態のあるセッションを継続することが出来るようになります。 例えば、以下のようにJWSからHttpURLConnectionを利用してリクエストを送信するケースですが、 HttpURLConection#setRequestProperty(String, String)を使うことによってcookieにjsessionidを含めることが可能です。 もちろん、URLの最後に;jsessionid=XXXXXという形でjsessionidを付けても問題ありません。 ちなみに、URLの最後に付けるのと、setRequestPropertyでの設定と両方の指定がある場合は、 setRequestPropertyの方が優先されます。 final HttpURLConnection connection = (HttpURLConnection) new URL("http://localhost/mycontext").openConnection(); connection.setRequestProperty("Cookie", "JSESSIONID=" + sessionid); このリクエストがJWSからの最初のリクエストでは無く、かつ最初のリクエストでjsessionidを明示的に送っていない場合、 Cookieには2つのjsessionidが設定された状態でリクエストされます。 サーバではこの2つのjsessionidのうち、最初に設定されている方を有効と見なして処理が行われます。 よって、上記コードで明示的に設定したjsessionidが無視されることになります。 では、このケースで明示的に設定したjsessionidを有効にするにはどうしたら良いのか。 自動で設定されるcookieを無効にする 自動で設定されるcookieはCookieHandlerで設定されるようです。 なので、このCookieHandlerを機能させなくすれば、cookieが自動で設定されなくなるはず。 というわけで、下記のコードを追加します。 CookieHandler.setDefault(null); これでcookieの自動設定が行われないはずなのですが、実はこのメソッド、セキュリティが all-permissionsでないと、AccessControlExceptionがスローされてしまいます。 JWSでall-permissionsにするには、jarファイルに署名しなければなりません。 最後に jarファイルに署名できないような政治的な事情がある場合はこの方法が取れないので、 JWSからの最初のリクエストにちゃんとjsessionidが付与されるようにしましょう。 まあ、署名するための証明書もオレオレ証明書じゃない場合は、 それなりに費用もかかるので、署名できないことも多い訳ですが… :P それにしても、JNLPのこととか、JWSなどの記事が軒並み古いので、 もうほとんど使われてないのかなぁと思ったりした快晴の木曜日でした。 enjoy!
  • jersey on glassfish-4

    GlassFish-4を使ってみたらJerseyのパッケージが変わってた。 GlassFish-3.1.2.2までは以下のようにcom.sunパッケージにあるJerseyのサーブレットがロードできていたけど、 <servlet-name>jersey-serlvet</servlet-name> <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> </servlet> GlassFish-4ではcom.sunパッケージにJerseyはなく、org.glassfishのパッケージとなっている。 <servlet-name>jersey-serlvet</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> </servlet>
  • Performance of JPA

    JPAはJavaSE上でも動作するので、DBアクセスをする場合、最近はもっぱらJDBCではなくJPAを利用している。 サーバサイドを利用しない、クライアントアプリでDBアクセスをするのは、主にデータ移行ツールとかが多く、 ひとつのトランザクション内で処理されるレコード数が数万レコードになる事が多い。 数万レコードを移行する為に、JPAを利用した移行ツールを作り、実データでテストをしたところ、 想定外のパフォーマンス劣化が発生し、その解決に幾分か時間が掛かってしまった。 そのため、パフォーマンスが劣化する状況とその解決方法を簡易に示す為の簡易なコードを作成、備忘録として残しておく。 アプリ構成 JavaはJDK7、JPAの実装はEclipseLink-2.3.2を利用。DBはDerby。 コード 検証に利用するコードは下記の通り。 Bookエンティティを1万レコード永続化する。この際、永続化だけではパフォーマンスの劣化を確認できないため、 永続化されたBookエンティティのうち、bookname='name1'のレコードを抽出する。 また、検証に利用する為に、ループ1回の処理時間(ms)をファイルに出力する。 検証 まず改善前の結果。縦軸は処理時間(ms)、横軸は回数。 約5,000回で一度速度が改善しているが、全体的に右肩上がりで処理時間が劣化している。 簡易なコードであり、処理時間が数msのため、このままでも致命的な状況にならないかもしれないが、 tachされたエンティティの数が増加すると、それに比例して劣化していく。 次に改善後の結果。 約5,000回で速度が改善するのは同じだが、一定速度以上の劣化は起こらず安定している。 改善方法 では、何をすれば改善するのか。 結論から言えば、エンティティをdetachすれば良い。 detachすることによってエンティティはエンティティマネージャの管理外になり、通常のJavaのオブジェクトになる。 そのため、同期のためのオーバヘッドがなくなり、パフォーマンスが改善するのだと考えられるが確証はないので、 あとでEclipseLinkのソースコードを確認してみようと思う。 ただし、これによりDBとの同期は行えなくなるため、detachをする位置には注意が必要。 検証コードでは、ループ内の最後に下記2行を追加した。 em.flush(); em.clear(); em.flush()を呼び出しているのは、Bookエンティティが永続化される前にdetachされるのを回避するため。 ただし、これも注意が必要で、リレーション関係のあるエンティティを両方とも永続化しようとするケース等で、 片方をpersist、他方をpersistする前にflushしてしまうと、他方の永続化時にエラーが発生してしまう。 ちなみにflushやclearに関しては次の本が詳しい。
  • Convert the Alerts component of 'Bootstrap' to a component of JSF 2.0

    JSFのMessagesコンポーネントは、Managed Beanで設定されたFacesMessageを出力する為のコンポーネントです。 出力方法は、listとTableの二つのレイアウトを利用した方法があり、デフォルトはlistレイアウトです。 listレイアウトは以下のような出力になります。 (Bootstrapのalert alert-errorを適用) これに対してTableレイアウトは以下のような出力になります。 どちらもエラーを表示するには十分ですが、ユーザとしては確認したらエラー情報を消したいところです。 MessagesコンポーネントはHTMLをカスタマイズする事ができません。 正確にはレンダラをカスタマイズすれば、出力するHTMLを書き換える事が出来ますし、 そういった情報を扱ったブログもありますが、Messagesレンダラのカスタマイズは、 com.sunパッケージのクラスを継承して拡張する必要があり、JSFの実装依存のコードになってしまいます。 もちろん独自で一からレンダラを書いても良いのですが、あまり現実的ではありません。 そこで、標準のMessagesコンポーネントではなくBootstrapのAlertsコンポーネントを使うことにします。 BootstrapのAlertsコンポーネントはクローズボタンを表示する事ができます。 このクローズボタンをクリックする事により、エラー情報を消すことができます。 クローズボタンを出すには、HTMLを下記のように記載する必要があります。 <div class="alert alert-error"> <button type="button" class="close" data-dismiss="alert">&times;</button> <h4>Summary Message</h4> Detail Message </div> これをJSF合成コンポーネントにします。 webapp/resources/bootstrapフォルダを作成し、alert.xhtmlファイルを配置します。(フォルダ構成はMavenです) alert.xhtml: <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:cc="http://java.sun.com/jsf/composite" xmlns:c="http://java.sun.com/jsp/jstl/core"> <!-- INTERFACE --> <cc:interface> </cc:interface> <!-- IMPLEMENTATION --> <cc:implementation> <c:forEach var="message" items="#{facesContext.messageList}"> <c:if test="#{message.severity == 'ERROR 2'}"> <div class="alert alert-error"> <button type="button" class="close" data-dismiss="alert">&times;</button> <h4>#{message.
  • Point-to-Point on JMS

    JavaEE Advent Calendar 2012の15日目のエントリーです。 昨日は@yoshioteradaさんのJava EE 7 WebSocket Client Sample Application with JavaFXです。 明日は@akirakoyasuさんです。 普段は使わないJMSを使う おそらくJMSの本来の利用方法は非同期通信を利用した分散処理なのだと思うけど、今回はそんな高尚な目的ではなく、単純なメッセンジャーとして利用します。 世の中のどの位のプロジェクトでJMSが利用されているのか分からないけど、JavaEEの仕様にJMSが含まれているにも関わらず、今まで本格的に利用した事がありません。 分散処理をするケースがあるプロジェクトに参加した事が無いのか、またはサーバがいつもTomcatだからなのか。 おそらく後者なのだと思うけど、ということはつまり分散処理の必要がないプロジェクトということなんだと思います。 そんな事もあって、JMSの知識が皆無だったわけですが、 JavaEEのアドベンドカレンダーをやるに当たって何か普段は触らないようなことをやりたいなと思い立ち、JMSを使ってみる事にしました。 必要なもの GlassFish-3.1.2.2 jms-api-1.1-rev-1.jar <dependency> <groupId>javax.jms</groupId> <artifactId>jms-api</artifactId> <version>1.1-rev-1</version> </dependency> imq-4.5.2.jar <dependency> <groupId>org.glassfish.mq</groupId> <artifactId>imq</artifactId> <version>4.5.2</version> </dependency> JMSを利用する為には、メッセージプロバイダが必要になります。プロバイダにはOpenMQ, MQSeries, SonicMQなどがありますが、 今回は導入が簡単なOpenMQを利用します。OpenMQはGlassFishに付属してインストールされます。インストール時に特に何かを意識する必要はありません。 また、OpenMQにアクセスする為に2つのライブラリが必要になります。jms-apiとimqです。Mavenリポジトリに登録されているので、 こちらも容易に入手可能です。 事前準備 GlassFishを起動しておく必要があります。ポートとして7676を利用するので、GlassFishがローカルではなく、リモート環境にある場合は、 ポートへのアクセスを許可する必要があるかもしれません。 メッセージプロデューサーを作る メッセージを送信するプロデューサーを作ります。 package jp.co.baykraft.jmsexample; import com.sun.messaging.ConnectionConfiguration; import com.sun.messaging.QueueConnectionFactory; import javax.jms.JMSException; import javax.jms.Queue; import javax.jms.QueueConnection; import javax.jms.QueueSender; import javax.jms.QueueSession; import javax.
  • WiiRemoteJで遊ぼう on OSX 10.8.7

    Java Advent Calendar 2012の11日目のエントリーです。 昨日は@cero_tさんです。 明日は@snuffkinさんです。 クリスマスだから楽しいことをしよう ということで、WiiUも発売したことだし、今更ながら、WiiRemoteJを取り上げてみたいと思います。 WiiRemoteJはBluetoothを利用して、WiiリモコンでJavaのアプリを操作するためのライブラリです。 最新版はv1.6というのがあるようなのですが、見つけることができなかったため、v1.4を使ってみたいと思います。 OSX 10.8.7 Mountain Lionで動かす 今回やりたいことは、Macbook Pro上にWiiリモコンのレシーバーとなるアプリを起動し、 Wiiリモコンを使ってそのアプリを操作する、ということです。 レシーバーアプリは下記2つのライブラリが必要になります。 BlueCove.jar WiiRemoteJ.jar BlueCoveはJavaのBluetoothを利用する為のAPIの規約であるJSR-82の実装ライブラリです。 WiiRemoteJを動かす為に必要になります。 BlueCoveの最新版は2.1.0です。ところがこのBlueCove-2.1.0、Mountain Lion上では動かす事ができません。 Mountain Lionで動かす為には、まだ正式にリリースされていないBlueCove-2.1.1-SNAPSHOTを利用する必要があります。 ただ、この2.1.1-SNAPSHOTも問題があります。 BlueCoveはBluetoothにアクセスする為に/System/Library/Frameworks/IOBluetooth.frameworkというフレームワークを利用しているのですが、 BlueCove-2.1.1-SNAPSHOTはこのIOBluetooth.frameworkに対応できていないため、実行時にエラーが発生してしまいます。 という訳で、このままでは実行できそうにありません。 そこで、ここで提供されている、 BlueCove-2.1.1-SNAPSHOTで操作できるIOBluetooth.frameworkに置き換えることにします。 置き換える事により、他のアプリで問題が発生するかもしれません。 置き換える前に、元のIOBluetooth.frameworkのバックアップを作る事をお勧めします。 さあ、ここまでできれば、あとはレシーバーを作るだけです。 レシーバーを実装する 下記が実装コードになります。動作は単純で、1ボタン、2ボタン、マイナスボタン、プラスボタン、Aボタン、Bボタン、十字キーを押した場合は、 それを標準出力に表示、ホームボタンを押したらレシーバーを終了します。 import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import wiiremotej.WiiRemote; import wiiremotej.WiiRemoteJ; import wiiremotej.event.WRAccelerationEvent; import wiiremotej.event.WRButtonEvent; import wiiremotej.event.WRStatusEvent; import wiiremotej.event.WiiRemoteAdapter; import wiiremotej.event.WiiRemoteDiscoveredEvent; import wiiremotej.event.WiiRemoteDiscoveryListener; /** * WiiRemoteJサンプルアプリ * @author Katsumi */ public class Wii extends WiiRemoteAdapter implements WiiRemoteDiscoveryListener { private WiiRemote _remote; public static void main(String.
  • OpenJFXをコンパイルしようとして挫折した記録

    JavaFX Advent Calendar 2012の7日目のエントリーです。 昨日は@fukai_yasさんの「AppletでFXMLを使って罠にハマる」です。 明日は@btnrougeさんです。 JOptionPaneが使いたい JavaFX 2.2.3で業務に利用する簡単なツールを作っていたのですが、入力エラーを通知する為に、 SwingでいうところのJOptionPane相当のものを探したのですがみつかりませんでした。 誰か知っている人はいないだろうかとTwitterでつぶやいてみたところ、@skrbさんよりこんなお返事を頂きました。 @kokuzawa Project Sandboxというのがあって、そこでJOptionPaneのように使えるDialogクラスを提供してますよ。正式にはJavaFX 8で入る予定です。 http://t.co/w6AswYKA — Yuichi Sakuraba (@skrb) 2012年11月30日 Project SandboxでJOptionPane相当のDialogsクラスが提供されているということ。JavaFX 8 に入るらしい。 すばらしい!すばらしいけど、今使いたい。そこでひとまずProject Sandboxを動かしてみる事にしました。 OpenJFXをビルドする Project SandboxはOpenJFXのSandboxプロジェクトで、これを利用する為には、Mercurialに登録されているソースをコンパイルする必要があるようです。 そこでOpenJFXのページに書かれているビルド手順を実行します。 でもこれ、記述が古い。JDKのフォルダ構成とか、今のものとはかなり違う。さて困った。 そこで新たな情報を求めていると、Building OpenJFXという、 まさに望んだ通りのページがありました。手順は次のようになります。 また、コンパイルにはJDK 8 が必要です。 mkdir -p ~/open-jfx cd ~/open-jfx hg clone http://hg.openjdk.java.net/openjfx/8/master cd master mkdir -p artifacts/sdk/rt/lib cp -r <PATH TO JDK>/jre/lib/jfxrt.jar artifacts/sdk/rt/lib hg clone http://hg.openjdk.java.net/openjfx/8/master/rt cd rt ant 今度こそ、コンパイル出来そうな感じです。最後のantを実行すると、何個かのモジュールのjarが出来上がっていきます。 が、javafx-ui-commonというモジュールのコンパイルが通りません。JDKのバージョンの問題かもしれないと思ったのですが、 JDK 8 はJDK 8 b65というもので、この時点では最新のものです。 ここで1日試行錯誤を繰り返したものの、解決の糸口はなく、ビルドはあきらめる事にしました。
  • GlassFish3.1.2へのデプロイ時のエラー

    java.util.concurrent.ExecutionException: com.sun.faces.config.ConfigurationException: Unable to parse document ‘bundle://213.0:1/com/sun/faces/jsf-ri-runtime.xml’: DTD factory class org.apache.xerces.impl.dv.dtd.DTDDVFactoryImpl does not extend from DTDDVFactory. JSFを利用していると発生しているようなので、warファイル内のjavax.faces-2.1.4.jarを取り除いて再度デプロイ。でも変化なし。 (JSFのライブラリはGlassFishに含まれている。バージョンはわからないけど) 何度か再デプロイをしていると起動できるので、それほど真剣に調べていなかったけど、本腰をいれて調べてみることにした。 結果 ログを見るとorg.apache.xerces.impl.dv.dtd.DTDDVFactoryImpl関係でエラーが発生してbundle://213.0:1/com/sun/faces/jsf-ri-runtime.xmlがパースできないことがわかる。 っていうかbundle://213.0:1/com/sun/faces/jsf-ri-runtime.xmlって何だろう?ひとまずこれについてはあとで調査。 おそらくGlassFishで利用しているXMLパーサとぶつかっているせいだと思う。warファイル内からxercesImpl-2.8.1.jarを取り除いてデプロイしたところ正常に起動ができた。 取り除いても正常に起動できたのでXMLパーサがGlassFishに含まれてるのは間違いないと推測できるが、GlassFishのどこにXMLパーサが含まれているのかがわからない。 追記: Nov 11, 2012 運用環境はTomcatだからxercesImpl-2.8.1.jarを取り除いちゃダメなんじゃないかと思う。