Posts tagged ‘Java EE 7’

Java EE 7 HoL on JJUG CCC

2013 年 11 月 9 日に JJUG CCC 2013 Fallベルサール西新宿で開催されますが、13:15 – 15:05 まで「R5-1 Java EEハンズオン」を実施します。今日はそのハンズオンで実施する内容についてご紹介します。

2013年11月 11日追記:JJUG CCC で実施したハンズオンの資料を下記に公開しました。また、本プロジェクトの全ソースコードは下記より参照できます。
https://github.com/yoshioterada/JavaEE7-HoL/
HoL の資料は Step by Step で記載したためページ数が多いですが、実装コード量はとても少ないです。

本 HoL では Java EE 7 に含まれる技術だけを使ってリアルタイムの情報配信をするアプリケーションを作成します。WebSocket のサンプルというとチャットのようなアプリケーションは数多くありますが、WebSocket はアイディア次第でとてもおもしろい物を作れるだけでなく、実際にビジネスで即使えるような物も作れます。

ただ、WebSocket を本番環境で大規模に扱うためには負荷も考慮しなければなりません。基本的には Java EE 7 に準拠したアプリケーション・サーバ1台で WebSocket アプリケーションを動作させた場合、そのサーバに接続するクライアントにのみしか状況発信、共有などができないため、おのずと接続数に限界が生じます。実際には接続数ではなく、情報配信の部分がボトルネックとなり接続数を制限せざるおえない状況になるでしょう。

それでは、単純に台数を増やしてクラスタ構成を組めば良いと考えるかもしれませんがそんなに簡単にはいきません。なぜならば JSR-356 の WebSocket ではサーバ・エンドポイントに接続するクライアントの管理は自身でする事になっており、インスタンスをまたいで接続しているクライアントの情報を管理するのはとても大変だからです。
例えば、Collection に WebSocket のクライアント・エンドポイントの情報(全クライアント情報)を入れたとして、その情報をインスタンス間で交換するのはデータ量も多くナンセンスです。また、ある一つのインスタンスで受信したメッセージを他の全インスタンスに共有させるのも、インスタンスの増加の度にコードを修正しなければならなくなるので現実的ではありません。

そこでこうした WebSocket のクラスタリングを実現するために、外部のメッセージ・プロバイダ(MQ 関連製品)を使用します。メッセージ・プロバイダにメッセージをキューイングし、アプリケーション・サーバの各インスタンスがキューを監視していれば、キューにデータが入ってきた時点でそのメッセージを取り出し処理をする事ができます。

実際には、内部的には JMS の Publish-Subscriber を利用しています。Web アプリケーションで1人が JMS のトピックに対して配信したメッセージを、各インスタンス上で稼働する MDB が同一トピックをサブスクライブしており、メッセージが入ってきた事を検知した後、全 WebSocket クライアント・エンドポイントに対してメッセージを配信を行います。



また、Web アプリケーションと WebSocket 側の実装パッケージをを分ける事によって、上記の概念図に示すように、よりセキュアに情報発信側と受信側を分けて運用する事もできるようになります。だれでもが WebSocket サーバに接続する全クライアントに情報配信ができようなシステムは危険ですよね。

そこで、今回は上記のような概念図を念頭に、簡単なアプリケーションを構築し、GlassFish のクラスタリング構成を作成します。実際にクラスタ環境で WebSocket を動かしている動画を下記に示します。
※ 今回は Load Balancer や Firewall の部分は対象範囲外とします。

最後に、
今回のハンズオンは少し応用するだけで、スポーツ・ニュースの配信サイトで行われているようなリアルタイムの試合経過速報等をとても簡単にさらに効率良く提供する事ができるようになります。またそれ以外にもお客様にリアルタイムの情報をいち早くお届けしたいニーズがあればすぐにご利用いただけるでしょう。
今回このようなアプリケーションを作成する HoL を実施しますので楽しみにしておいてください。
また HoL を元に大規模 WebSocket リアルタイム情報配信を是非ご検討ください。

HoL で必要なのは事前準備としては JDK 7 u45 以降のインストールと NetBeans 7.4 (GlassFish バンドル版)以降のインストールをしていただくだけです。それ以外は一切必要ございません。
それぞれ、下記より御入手ください。
※ Java SE 8 の HoL も両方参加される方は JDK 7,8 共にインストールが必要ですが、本 HoL では JDK 7 を使用して行います。

● http://www.oracle.com/technetwork/java/javase/downloads/index.html
● https://netbeans.org/downloads/index.html 
 (Java EE : 203 MB版 もしくは、すべて : 220 MB 版)

※ さて、これから説明、配布用の資料を作成しよう。(^_^;)

2013年11月 11日追記:JJUG CCC で実施したハンズオンの資料を下記に公開しました。

2013年10月23日 at 3:53 PM 1件のコメント

You are the “Make the future Java”

本日、GlassFish ユーザ・グループ・ジャパンによる Java EE 7 リリース記念のナイトセミナーが開催されました。本日ご登壇いただいた、上妻さん、久保田さん、槇さん、蓮沼さんには大変感謝すると共に、ご参加頂いた皆様にも大変感謝いたしております。(下記の写真の通り会場は満員で、ご登録者数は 140 名、実際にご参加頂いた方も 88 名でした。)
Java EE 7 そして GlassFish の最新情報をコミュニティ・ドリブンでお届け頂ける、このような機会はとても貴重だと感じております。GlassFish ユーザ・グループ・ジャパン副会長のの蓮沼さん、そして参加者の皆様ありがとうございます。

GlassFish は元々、日本では元 Sun の3人のメンバーが日本での啓蒙活動を開始しましたが、今や GlassFish はこの3人の手を離れ、Java EE の参照実装として、コミュニティ・ドリブンで情報提供がなされ、さらには多くのユーザが GlassFish の良さに気付きご賛同頂きはじめた事を、日本で活動を始めた3人の内の1人として大変嬉しく思っております。

Java EE 7 は、WebSocket, JOSN, jBatch, Concurrency Utilities for EE といった新機能が含まれ、特に WebSocket, JSON あたりの技術は特に開発者が注目する技術だと思います。また本日、上妻さんに発表して頂いた、jBatch に関しても非常に詳しい内容をご紹介頂いたため、今後多くの開発者にとってとても有用な情報だったのではないかと思います。

その一方でツイート上を除くと「Java EE 7 が流行ればいいな」といった、(言葉が若干悪いかもしれませんがお許しください)他人まかせなご意見も見受けられました。これに対して私が皆様にご期待する内容として、本ブログのタイトルにもございますように、「将来の Java を創っていくのは皆様です!!」

もし、Java EE 7 もしくは GlassFish に関して、ご興味を頂いた方、もしくは試して見ようと思われ方がいらっしゃったら、どんな些細な事でも結構です。実際に試して頂いた内容を、体裁問わず、ブログや Wiki、その他何でも結構です、試された内容を是非公に公開してください。それらの情報が他の開発者にとっても有用な情報になり、それが流行(トレンド)になっていく物と心より信じております。

例えば、今このエントリで Java EE 7 のどの技術にご興味があるかアンケートを実施しています。結果をみると、圧倒的に Java EE 7 のご興味のある技術は WebSocket になっていますが、全ての開発者が WebSocket に興味を持っているわけではなく、JAX-RS 2.0, JSF 2.2, JSON, Concurrency , CDI 等の技術にご興味を持って頂いている方も多く見受けられます。実際、jBatch に関しては現在7番目の人気となっておりますが、本日、上妻さんにご登壇頂いた内容は多くの開発者にとってとても有用だったと思います。

ここで申し上げたい事は、皆様、それぞれ異なる興味分野を持っていらっしゃると思います。1人が全てを一度に試す(把握する)事は困難ですが、自分の興味分野、試した内容を公開する事でかならず、他の Java 開発者の為にもなります。

もちろん、私も今後も継続して情報発信してまいりますが、1人でできる事はとても限られています。スケールも致しません。それを支えていただけるのは皆様です。仮に、試してダメだと思った所は、正直この機能のここがダメだとフィードバックをください。それが将来の Java を創っていく事だと思います。
「将来の Java を創っていくのは皆様」なのです。

是非、お試しいただいた内容を、どのような形でも結構です。是非情報をご共有頂ければ大変嬉しく思います。
最後にくどいようですが、繰り返します「将来の Java を創っていくのは皆様です!!」

2013年6月15日 at 2:14 AM

Java EE 7 トレーニング・コースについて

Oracle University では、現在 Java EE 7 のトレーニング・コース(Java EE 7: New Features Coming Soon (¥145,530) )を全世界への提供に向けて準備しています。

これは、Java EE 7 に含まれる新機能を2日間で紹介し、加えてラボで実際に手を動かしながら演習を行うこともできるトレーニング・コースになっています。これによりいち早く Java EE 7 の全体像とプログラミング方法を習得できます(このトレーニング・コースに関してはレビューの要望が私の元にも入ってきたため、一部私もレビューをし改善を加えた部分もあります)。

本トレーニング・コースでは Batch, JSON, WebSocket, JAX-RS, EL 3.0, JMS 2.0 , EJB, JPA, CDI, Bean Validation 等 Java EE 7 に含まれるの技術をとりあげ、既存のアプリケーションを Java EE 7 に対応させるために必要な情報も提供してくれます。

本トレーニング・コースについて日本の担当者と話をした所、本コースは世界で正式公開された後も、現時点では英語でのみ提供予定のようです。ただし、日本の Oracle University に対して、日本語での開催リクエストを行い、かつご希望者が多い場合、日本人講師による日本語でのサービス提供も可能との事でした。

Java EE 7 のトレーニング・コースを日本語で受講されたい方は、今から本トレーニング・コースに対する日本語開催リクエストを出されてみては如何でしょうか。

トレーニングの紹介ページにアクセスした後、「コース開催日のリクエスト」を押下するとリクエストを行う事ができます。途中の質問で「集合研修(Clasroom Training)」にチェックしてください。

どうぞ宜しくお願いします。

2013年6月14日 at 5:29 PM

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時)

詳しくは、コチラをご参照ください。

2013年6月14日 at 3:28 PM

祝 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 に期待している内容などが記載されていますので、是非下記の記事もご参照頂ければ幸いです。
(※ 私もブログ書いたよという方がいらっしゃいましたら是非ご連絡ください、下記に追加させて頂きます。)

オンラインメディア

個人ブログ

Java EE 7 対応関連書籍

2013年6月13日 at 6:42 PM

ご注意(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]
(※ まさにここに記載されており、同時にピア毎に1つ以上のスレッドからエンドポイントのインスタンスを呼び出してはならない。)
と記載されておりました。

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 プログラムに参加し、日本から改善要望なども出していけるといいですね。

2013年5月23日 at 4:34 PM

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;
    }
}

2013年5月11日 at 3:01 PM

新しい投稿


Java Champion & Evangelist

Translate

ご注意

このエントリは個人の見解であり、所属する会社の公式見解ではありません

カレンダー

2020年2月
« 1月    
 12
3456789
10111213141516
17181920212223
242526272829  

カテゴリー

Twitter

clustermap

ブログ統計情報

  • 1,172,846 hits

RSSフィード

アーカイブ