Posts tagged ‘Java EE 7’
Virtual Developer Day-Java 開催 (6/19 or 6/25)
Oracle Technology Network (通称:OTN) 主催で Virtual Developer Day : Java が開催されます。下記のスケジュールに詳細を記述していますが、Java SE/FX/Embedded/EE の各テクノロジーに関して、無償で、オンラインでご覧頂く事ができます。Java SE 8 に含まれる 52 の新機能、Lambda 式、JavaFX、Java EE 7、Raspberry Pi など各テクノロジーの最新情報をご確認いただけます。またライブ・チャットもご用意しておりますので、エキスパートに対して直接質問を投げかける事もできます。本イベントにご興味のある方はご登録の上受講してください。
(※ 日本の開発者の皆様は恐らく 6 月 25 日開催のヨーロッパ向けの方が受講しやすい時間帯かと思います。)
- アメリカ :6 月 19 日(日本時間夜中の1時〜5時)
- ヨーロッパ:6 月 25 日(日本時間夕方5時〜9時)
詳しくは、コチラをご参照ください。
祝 Java EE 7 ローンチの日本における反応
日本時間の深夜から午後に掛けて、世界に渡る Java EE 7 ローンチ・イベントがおかげさまで無事終了致しました。日本から深夜、もしくは午後にご参加頂いたエンジニアもいらっしゃいますが、本イベントにご参加頂いた皆様には、Java EE 7 の3つテーマ(HTML 5 対応、開発生産性の向上、エンタープライズ・ニーズへの対応)をご理解頂けたのではないかと思います。
また主要な統合開発環境(Eclipse, NetBeans, Intellij)も Java EE 7 へ対応する事で、今後日本におけるエンタープライズ開発の標準も Java EE 7 へ移行していく物と思われます。今回リリースされた Java EE 7 には様々なテクノロジーが含まれますが、Java EE 7 に含まれる各種機能を試された方は、是非その試された内容をブログや Wiki 等で記載して頂ければ嬉しく思います。
Java EE 7 は EE 6 に対して開発生産性を向上したバージョンであるため、本来であれば Java EE 7 を直接試していただきたいのですが、仮に Java EE にはじめて触れられる方で Java EE の全体像が理解できない方、もしくは英語が大の苦手な方は、既存の Java EE 6 の日本語書籍(Amazon : Beginning Java EE 6 GlassFish 3で始めるエンタープライズJava (Programmer’s SELECTION) [大型本]) をご参照頂き全体像をご理解頂いた上で、Java EE 6 と EE 7 の差分を抑えて頂く事も可能ですので、いずれかのやりやすい方法で Java EE 7 に触れていただければ幸いです。
最後に、Java EE 7 のローンチに関連して各種メディア、ブログ等に記載された内容を下記にご紹介します。今まで Java EE に実際に触れて来られた方も、この新しい Java EE 7 に期待している内容などが記載されていますので、是非下記の記事もご参照頂ければ幸いです。
(※ 私もブログ書いたよという方がいらっしゃいましたら是非ご連絡ください、下記に追加させて頂きます。)
オンラインメディア
- Publickey:Java EE 7対応のアプリケーションサーバ「GlassFish 4」オープンソース版が公開
- マイナビ ニュース:Oracle、Java EE 7を提供開始
- クラウド Watch : 米OracleがJava EE 7をリリース、HTML5対応アプリの構築を支援する新機能などを追加
- SourceForge.JP:「Java EE 7」が正式発表、米Oracleから初の登場
- @IT : HTML5のサポート強化や開発者の生産性を向上:Oracle、「Java EE 7」を提供開始
個人ブログ
- 武者返し.com:祝! Java EE 7リリース
- きしだのはてな:WebSocketをネタにJava EE 7正式版を試してみる
- しんさんの出張所 はてな編:JavaEE 7 登場
- Challenge Java EE !:NetBeans7.3.1でJava EE7とGlassFish4を少しのぞいてみました チラ| |д・)
- Programming Studio : GlassFish 4.0 正式リリース (GlassFish ユーザ・グループ・ジャパン副会長)
- yoshio3.com:Java EE 7 ローンチ (US 時間: 06/12 午前9時)
Java EE 7 対応関連書籍
ご注意(NOTE): WebSocket with Concurrency for EE
Java EE 7 のリリースが控えておりますが、先日 Java Day Tokyo で私のセッションの中でデモした WebSocket (JSR-356) と Concurrency Utilities for EE (JSR-236) を組み合わせたデモのコードについてご紹介すると共に、実装時の注意点をご報告いたします。
下記は、実際に私が WebSocket (JSR-356) と Concurrency Utilities for EE (JSR-236) を組み合わせたコードを実装する際にハマった内容をお届けします。
コードは下記のようなコードを記載しています。
package jp.co.oracle.websocket; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Resource; import javax.enterprise.concurrent.ManagedExecutorService; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import jp.co.oracle.tasks.WebSocketAIRSearchTask; import jp.co.oracle.tasks.WebSocketHotelSearchTask; @ServerEndpoint(value = "/asyncResult") public class AsyncResultWebSocketEndpoint { private static final Logger logger = Logger.getLogger(AsyncResultWebSocketEndpoint.class.getPackage().getName()); // Concurrency Utilities for EE の ManagedExecutorService をインジェクト @Resource(name = "concurrent/DefaultManagedExecutorService") ManagedExecutorService managedExecsvc; // WebSocket のコネクションがオープンした際の処理 @OnOpen public void initOpen(Session session) { executeTasks(session); } // WebSocket クライアントからメッセージを受信した際の処理 @OnMessage public void receivedMessage(String message, Session session) { if (!message.equals("re-execute")) { return; } executeTasks(session); } // 実際の処理内容 private void executeTasks(Session session) { // 複数タスクの実行の際に終わった順に処理結果を取り出す ExecutorCompletionService<String> execCompService = new ExecutorCompletionService<>(managedExecsvc); // 複数タスクの登録 List<Future<String>> futures = new ArrayList<>(); for (int i = 0; i < 100; i++) { WebSocketHotelSearchTask task = new WebSocketHotelSearchTask(i); futures.add(execCompService.submit(task)); } try { // 終了したタスクの順番に処理結果を取得し // 処理結果を WebSocket のクライアント・エンドポイント // に対して処理結果を送信 for (Future<String> results : futures) { String resultString = execCompService.take().get(); session.getBasicRemote().sendText(resultString); } } catch (IOException | InterruptedException | ExecutionException ex) { logger.log(Level.SEVERE, null, ex); } } }
ダミーのタスク
package jp.co.oracle.tasks; import java.util.concurrent.Callable; import java.util.logging.Level; import java.util.logging.Logger; public class WebSocketHotelSearchTask implements Callable<String> { private static final Logger logger = Logger.getLogger(WebSocketHotelSearchTask.class.getPackage().getName()); private int counter; public WebSocketHotelSearchTask(int counter) { this.counter = counter; } // タスクの処理内容によっては時間のかかるタスクもあるため // 半分のタスクをわざと 4 秒待たせ、タスクの登録順にタスクが // 完了しないように作成 @Override public String call() { String result = ""; if (counter % 2 == 1) { Thread.sleep(4000); } result = "ホテル検索タスク完了 : 終了タスクの ID" + counter;; } catch (InterruptedException ex) { logger.log(Level.SEVERE, null, ex); } return result; } }
今回、なぜ下記のようにタスクの一括登録を行った後に、タスクの処理が終わった順に WebSocket のクライアント・エンドポイントに対してメッセージ配信を行うコードを実装したかというと(つまり、タスクの処理コード中から WebSocket のクライアントに対してメッセージ配信をしていない)、WebSocket 側のスレッドの制限があったためです。
// 複数タスクの登録 List<Future<String>> futures = new ArrayList<>(); for (int i = 0; i < 100; i++) { WebSocketHotelSearchTask task = new WebSocketHotelSearchTask(i); futures.add(execCompService.submit(task)); } try { // 終了したタスクの順番に処理結果を取得し // 処理結果を WebSocket のクライアント・エンドポイント // に対して処理結果を送信 for (Future<String> results : futures) { String resultString = execCompService.take().get(); session.getBasicRemote().sendText(resultString); }
当初、下記のように Runnable (Callable の call() 中で実装も可)のインタフェースを実装したタスクを作成し、タスクの処理の中で WebSocket のエンドポイントに対してメッセージを配信するコードを記載しました。例えば、下記のような感じです。
public class SomeMyTask implements Runnable{ Session session; public SomeMyTask(Session session){ this.session = session; } @Override void run(){ // 何らかの処理を実施 // タスクの処理の最後に、WebSocket のクライアント・エンドポイント // に対してメッセージを配信 session.getBasicRemote().sendText(resultString); } }
そして、下記のコードを書いてタスクを実行しました。
for( int i = 0 ; i < 10 ; i++ ){ SomeMyTask task = new SomeMyTask(session); managedExecsvc.submit(task); }
すると下記の例外が発生しました。
例外の出力内容:
java.lang.IllegalStateException: HeapBuffer has already been disposed at org.glassfish.grizzly.memory.HeapBuffer.checkDispose(HeapBuffer.java:802) at org.glassfish.grizzly.memory.HeapBuffer.position(HeapBuffer.java:188) at org.glassfish.grizzly.nio.transport.TCPNIOAsyncQueueWriter.fillByteBuffer(TCPNIOAsyncQueueWriter.java:194) at org.glassfish.grizzly.nio.transport.TCPNIOAsyncQueueWriter.writeComposite0(TCPNIOAsyncQueueWriter.java:151) at org.glassfish.grizzly.nio.transport.TCPNIOAsyncQueueWriter.write0(TCPNIOAsyncQueueWriter.java:80) at org.glassfish.grizzly.nio.AbstractNIOAsyncQueueWriter.processAsync(AbstractNIOAsyncQueueWriter.java:458) at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:110) at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77) at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838) at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113) at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115) at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55) at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135) at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564) at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544) at java.lang.Thread.run(Thread.java:722) |
なぜだろうと、Grizzly のソースコードを追ってみると、既にヒープが開放されてしまっているようです。
800 protected final void [More ...] checkDispose() { 801 if (heap == null) { 802 throw new IllegalStateException( 803 "HeapBuffer has already been disposed", 804 disposeStackTrace); 805 } 806 }
当初バグかとも思ったのですが念のため、WebSocket (JSR-356) 仕様のスレッド関連部分をチェックしてみました。すると下記の 5.1 に WebSocket に関するスレッドの注意書きが記載されておりました。
5.1 Threading Considerations Implementations of the WebSocket API may employ a variety of threading strategies in order to provide a scalable implementation. The specification aims to allow a range of strategies. However, the implementation must fulfill certain threading requirements in order to provide the developer a consistent threading environment for their applications. Unless backed by a Java EE component with a different lifecycle (See Chapter 7), the container must use a unique instance of the endpoint per peer. [WSC-5.1-1] In all cases, the implementation must not invoke an endpoint instance with more than one thread per peer at a time. [WSC-5.1-2] The implementation may not invoke the close method on an endpoint until after the open method has completed. [WSC-5.1-3] This guarantees that a websocket endpoint instance is never called by more than one container thread at a time per peer. [WSC-5.1-4] If the implementation decides to process an incoming message in parts, it must ensure that the corresponding onMessage() calls are called sequentially, and do not interleave either with parts of the same message or with other messages [WSC-5.1.5]. |
つまり、タスクの実行自身は複数のスレッドで実行できるのですが、WebSocket のクライアント・エンドポイントへのメッセージ送信は1箇所にまとめて実装しなければならない事に気付き上記のようなコードを書いています。
個人的には、マルチスレッドから WebSocket のクライアント・エンドポイントにメッセージ送信ができるようになるとより便利になるのではないかと思いますが、皆様如何でしょう? もちろん、既に仕様は FIX して Java EE 7 のリリース時点では無理ですし、Grizzly 等サーバ側の実装も今のままだと難しい部分があるかもしれません。しかし、皆様で声を上げていけば、時期 Java EE 8 の WebSocket 1.1 当たりで、マルチスレッドからのメッセージ送信もできるかも?!しれないので、賛同して頂ける方がいらっしゃったら、まとめて報告したいなと思っております。
(仕様上ダメって断られる可能性ももちろんありますが。(^_^;))
でも、昔と違って今の Java はこういった事がスペック・リードやエキスパート・グループメンバーに伝えやすい環境なんですよ!!
JJUG として Adopt-A-JSR プログラムに参加し、日本から改善要望なども出していけるといいですね。
JJUG CCC 2013 Spring の発表資料について
本日、JJUG CCC 2013 Spring が開催されました。Oracle からはUS Oracle Corporation からJim Weaver (Twitter : @JavaFXpert) が基調講演で「What’s New for JavaFX in JDK 8」を発表し、午後一のセッションで、「Java EE 6 から Java EE 7 に向かって」というセッションを担当しそれぞれ発表しました。2人の発表資料を公開しましたのでご報告します。
(※ 私のプレゼン資料ですが、SlideShare にアップロードした際、SlideShare 側の問題で、フォントが無いせいか、私が使用しているオリジナル・フォントからは変わって表示されています。その点ご了承頂ければ幸いです。)
基調講演-2 What’s New for JavaFX in JDK 8
H-1 Java EE 6 から Java EE 7 に向かって
今日の私のプレゼン中で行った EL(Expression Language) 3.0のデモのソース・コードも下記に公開します。デモ用に簡単に作ったものであるため、本来 JPA で DB 接続すべき所を簡単にダミーデータ(Person#createDummyData())を作成しています。
本コードは JSF 2.0 と CDI をご存知の方であれば容易にご理解いただけるかと思いますが、「全データ抽出」のボタンを押下すると、CDI の getAllData() が呼び出され、indexManagedBean.data(ArrayList) に全データがコピーされ、その一覧が dataTable に表示されます。
次に、「年齢フィルタ」のテキストフィールド(デフォルト:0)に対して年齢を入力すると、入力された年齢以上のデータを表示しています。内部的には Ajax を使って、入力された年齢情報(indexManagedBean.ageFileter)をサーバに送信し、Ajax のリスナーとして定義しているindexManagedBean.updateData()を実行していいます。ただ、indexManagedBean.updateData()は処理は何もしていません、ここでは execute=”ageFilter” をサーバに送信し、その結果を render=”tabledata” 、つまり dataTable の内容を更新するためだけにupdateData()を呼び出しています。
<h:dataTable id="tabledata" value="#{afilter = indexManagedBean.ageFileter;indexManagedBean.data.stream().filter(p-> p.age >= afilter).toList()}" var="person" border="1">
この例では、一度 DBに対してクエリを実行し、その結果をコレクションにコピーした後、さらにそのコピーしたデータに対して絞り込みを行っています。DB に対して再度クエリをなげるのではなく、EL 3.0 の Lambda 式を使って、一旦取得したデータを元に再フィルタリングを 行っています。
セッション中でも話をしましたが EL 3.0 で Lambda 式(及び LINQ 式)が使えるようになった事でビューにロジックを埋め込む事が可能になりますが、あまりやりすぎると可読性の低下にもつながりますので使う範囲はよくご検討頂いた方がよいのではないかと思います。(※ .Net の LINQ 式は DB に対しても操作可能ですが、EL 3.0 における LINQ 式はコレクションに対してのみ有効です。)
今回の例では、一旦取得したデータのフィルタンリグ等でご使用頂く事で、DB に対する負荷、インメモリ・グリッドにあるキャッシュから取得するよりもパフォーマンスがよくなる事を想定し記載しています。なぜならばヒープメモリ内にあるデータを直接操作する方がパフォーマンス的に優れるためです。(※ コレクションに対して有効な操作である事をご認識頂き用途は十分にご検討ください。)
<?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:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" > <h:head> <title>Facelet Title</title> </h:head> <h:body> <h:form> <h:commandButton value="全データ抽出" action="#{indexManagedBean.getAllData()}"/><br/> <h:outputLabel value="年齢フィルタ"/>: <h:inputText id="ageFilter" value="#{indexManagedBean.ageFileter}" autocomplete="off"> <f:ajax event="keyup" execute="ageFilter" render="tabledata" listener="#{indexManagedBean.updateData()}"/> </h:inputText> <h:dataTable id="tabledata" value="#{afilter = indexManagedBean.ageFileter;indexManagedBean.data.stream().filter(p-> p.age >= afilter).toList()}" var="person" border="1"> <h:column> <f:facet name="header"> <h:outputText value="名前"/> </f:facet> <h:outputText value="#{person.name}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="年齢"/> </f:facet> <h:outputText value="#{person.age}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="性別"/> </f:facet> <h:outputText value="#{person.sex}"/> </h:column> </h:dataTable> </h:form> </h:body> </html>
package jp.co.oracle.ee7samples.cdi; import java.util.ArrayList; import javax.faces.view.ViewScoped; import javax.inject.Named; import jp.co.oracle.ee7samples.model.Person; @Named @ViewScoped public class IndexManagedBean { private ArrayList data; private Integer ageFileter; public ArrayList getData() { return data; } public void setData(ArrayList data) { this.data = data; } public Integer getAgeFileter() { if (ageFileter == null) { return new Integer(0); } return ageFileter; } public void setAgeFileter(Integer ageFileter) { this.ageFileter = ageFileter; } public String getAllData() { setData(Person.createDummyData()); return ""; } public String updateData() { return ""; } }
デモの中でもお伝えしましたが、本来 JPA で DB に接続して Person Entity に対して全レコードを抽出するコードを書く方が現実的なのですが、EL 3.0 は本来 Collection を対象とする事をわかりやすくするため、JPA を使わずに自分でダミーのデータをcreateDummyData()で作成しています。
package jp.co.oracle.ee7samples.model; import java.util.ArrayList; public class Person { private String name; private Integer age; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public static ArrayList createDummyData() { ArrayList<Person> data = new ArrayList<>(); Person person; for (int i = 0; i < 100; i++) { person = new Person(); person.setAge(Integer.valueOf(i)); if (i % 2 == 1) { person.setName("山田 太郎" + i); person.setSex("男性"); } else { person.setName("山田 花子" + i); person.setSex("女性"); } data.add(person); } return data; } }