Posts tagged ‘GlassFish’

ご注意(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

GlassFish の今後のロードマップ

GlassFish の今後のロードマップが発表されています。発表資料

この資料中では、直近でリリース予定の GlassFish v2.1.1, v3.0.1の他、GlassFish v3.1 、v3.2 、v4 についても触れらてています。

この資料の中で特に興味深いのは、GlassFish v4 という名前が初登場した事で、次期 Java EE である Java EE 7 に対応する製品になるようです。資料中に記載されているスケジュールはあくまでも予定ですので(今までの経験から)今後変更される事が予想できますが、今後も GlassFish が継続してオープンソースのアプリケーションサーバとして提供されていく事をご確認いただけるかと思います。
ぜひ、この資料をご確認ください。

2010年3月26日 at 3:03 PM 1件のコメント

ここから始める Java EE 6

2009年12月に Java EE 6 がリリースされて、約3ヶ月経ちました。Java EE 6 にはかなり多くの改善がみられますが、どこから始めればいいのかわからない方に、JSF2,JPA2,EJB 3.1を使ったかんたんな Java EE 6 アプリケーションの書き方を紹介します。

Java EE 6 には非常にたくさんの技術が含まれているので一度に全てを理解することは大変だと思いますが、まずはこのサンプルを通じて、JSF,JPA,EJB にどのような変更が加わったのか、またどのような点が楽になったのか等を理解して頂ければと思います。

開発/実行環境:
開発環境:NetBeans 6.8
実行環境:Oracle GlassFish Server v3

(上記は別途インストールしておいてください。)

サンプルアプリケーションの概要:

コードを書く前にこれから作成するサンプルアプリケーションの概要について説明します。このアプリケーションはとても簡単なアプリケーションでユーザ(データ)を登録して参照するだけという、いたってかんたんなアプリケーションです。また処理フローもとてもシンプルです。

サンプルアプリケーションの処理フロー

アプリケーションの完成予想イメージは下記です。ユーザ登録画面で「作成」ボタンを押下しデータを登録した後、一覧表示画面が表示されます。また一覧表示画面中の「新規作成」のリンクを押下すると登録画面に遷移します。

サンプルアプリケーションの完成イメージ

1.新規プロジェクトの作成

それでは早速プロジェクトを作成していきます。今回作成するアプリケーションは、EJBを含んでいますが新規プロジェクトの作成時「Web アプリケーション」を選択して作成することにします。

補足:
Java EE 6 におけるパッケージ構成の変更点を紹介します。過去の Java EE のバージョンでは下記のように EJB を ear にまとめたり、JSF/Servlet 等を war にまとめたりといったようにパッケージ構成が面倒でした。Java EE 6 からはこのようなパッケージ化の必要はなくなり、EJB も war に含めてデプロイできるようになりました。 

パッケージの簡略化

それでは、メニューから「ファイル」→「新規プロジェクト(W)…」→「Java Web」→「Web アプリケーション」を選択し、「次へ>」ボタンを押下してください。

新規プロジェクトの作成

プロジェクト名に「PersonManagement」を入力し「次へ>」ボタンを押下します。

新規プロジェクト作成

次に、「サーバと設定」で配備するアプリケーションサーバとJava EE のバージョンを選択します。ここでは、「GlassFish v3 ドメイン」と「Java EE 6 Web」を選択し「次へ>」ボタンを押下します。

稼働サーバとフレームワークの選択

次に、使用するフレームワークを選択します。今回は、「JavaServer Fcaces」を使用しますのでこれにチェックを付け「完了(F)」ボタンを押下します。

フレームワークの選択

プロジェクトを作成すると下記のような画面が表示されます。

プロジェクト作成完了

2. JPA 2.0:Entity Bean の作成

新規プロジェクトを作成後、Entity Bean を作成します。Java EE 5 から Entity CMP 2.x に代わり JPA 1.0 が登場し、DBに対する持続性のコード記述がかなり楽になりました。JPA 2.0 ではさらに扱いやすくなり、機能面でも改善が施されています。(JPA 2.0 の新機能の詳細は別途記載する予定です。また今回はとても簡単なサンプルのためJAP 2.0の新機能はあまり使用していません。JPA 1.0と同等レベルの記述内容)

それでは、実際に Entity Bean を作成してみましょう。メニューから「ファイル」→「新規ファイル(N)…」→「持続性」を選択してください。

NetBeans を利用すると既存のデータベースのテーブルから Entity クラスを自動的に生成する事ができます。既に DB テーブルを作成している場合は「データベースからのエンティティークラス」を選択して作成してください。NetBeans を使う上で、この機能は大変便利です。私も普段は先に DB のテーブルを作成して、Entity クラスは NetBeans で自動生成しますので一から Entity クラスを作成する事はあまりしません。是非お試しください。

今回、私は DB のテーブルが作成されていない状態で新規にエンティティークラスを生成しますので「エンティティークラス」を選択し「次へ>」ボタンを押下します。この方法では、エンティティークラスを先に作成した後、エンティティクラスに該当するテーブルを DB 上に後から構築します(Entity クラスを元にテーブルを自動生成することもできます)。

エンティティークラスの作成

ここで新しいエンティティークラス名「Person」、パッケージ名「ent」を入力します。「主キー型」はこのテーブルの主キーの型を選択します。Person を ID で管理しますので、そのまま、「Long」型を設定します。

エンティティクラスの作成

「完了(F)」ボタンを押下する前に1点注意してください。ワーニングで「プロジェクトに持続性ユニットがありません。エンティティークラスを持続さ…」というメッセージが表示されています。これはこのエンティティクラスに対する持続性ユニットが存在していない事を意味しています。そこで「持続性ユニットを作成…」ボタンを押下し、このエンティティクラスに紐づく持続性ユニットを作成します。ボタンを押下すると下記の画面が表示されます。

持続性ユニットの作成

持続性プロバイダとして JPA 2.0 の参照実装である「EclipseLink(JPA 2.0)(デフォルト)」と、データソースを選択します。また表生成の方針ですが、「作成」を選択すると自動的に DB にテーブルを作成します。全ての入力が完了した後、最後に「作成」ボタンを押下します。
この時、後のプログラミング時に使用しますので、入力した持続性ユニット名(PersonManagementPU)を覚えておいてください。

「作成」ボタンを押下すると、「新規エンティティ作成画面」に戻ってきますが、持続性ユニットに関するワーニングが消えたことを確認した後、最後に「完了(F)」ボタンを押下してください。ボタンを押下すると入力された情報を元に ID のみを持つ Person エンティティのひな形が自動生成されます。また同時に、構成ファイル配下に、持続性ユニットの設定ファイル persistence.xmlが自動生成されます。

作成された Entity クラス(Person)のひな形を編集していきます。

ソースコードを確認すると @Entity のアノテーションが自動的に付加されている事に気づきます。このアノテーションを付加することによりコンパイラや、EJB コンテナが本クラスをエンティティクラスと解釈してくれるようになります。

名前、住所、電話番号、年齢をあつかえるようにフィールドを追加し、それぞれに対するセッタ、ゲッタメソッドを記載してください。また、@NamedQuery で JPQL を記載し、DBに登録されている全ての Person を取得できるようにします。

package ent;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;&nbsp;

@Entity
@NamedQuery(name="findAllPerson", query="SELECT p FROM Person p")
public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable=false,length=256)
    private String name;

    @Column(nullable=false,length=256)
    private String address1;

    @Column(nullable=false,length=12)
    private String telephone;

    @Column(nullable=false)
    private Long age;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        if (!(object instanceof Person)) {
            return false;
        }
        Person other = (Person) object;
        if ((this.id == null &amp;&amp; other.id != null) || (this.id != null &amp;&amp; !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "entity.Person[id=" + id + "]";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress1() {
        return address1;
    }

    public void setAddress1(String address1) {
        this.address1 = address1;
    }

    public String getTelephone() {
        return telephone;
    }

    public void setTelephone(String telephone) {
        this.telephone = telephone;
    }

    public Long getAge() {
        return age;
    }

    public void setAge(Long age) {
        this.age = age;
    }
}

以上で、このサンプルアプリケーションに必要なエンティティクラスはできました。

3.EJB 3.1:ステートレスセッション Bean の作成

エンティティクラスを生成したので、次に EJB (ステートレス・セッション Bean) を作成します。「ファイル」→「新規ファイル(N)…」→「Java EE」→「セッション Bean」を選択し、「次へ>」ボタンを押下してください。

EJB セッション Bean の作成

ボタンを押下すると下記の画面が表示されます。ここで「EJB 名(N)」、「パッケージ(K)」を入力し「完了(F)」ボタンを押下します。

ステートレスセッション Bean の作成

補足:
上記画面を確認するとローカルインタフェース、リモートインタフェースが選択できるようになっています。
EJB 3.1 からローカルインタフェースがオプション化されインタフェースを作成する必要がなくなりました。EJB 3.0 では下記の例のようにインタフェースを定義し、それを実装しなければなりませんでしたが、EJB 3.1 からはアノテーションを付加するだけでコンテナが理解してくれるようになります。 

EJB 3.0 でのローカルインタフェースの実装例

EJB 3.0

実際にコードで書くと下記のようになります。

public interface Hello {
    public String sayHello();
}

@Stateless
public class HelloBean implements Hello {
    public String sayHello() { return "hello"; }
}

EJB の利用時

@EJB Hello helloRef;
    ...
    helloRef.sayHello();

これが、EJB 3.1 では下記のようにインタフェースを定義する必要はなくなります。

 

EJB 3.1

EJB 3.1 を利用する時のコードは下記となります。

EJB 3.1

 

それではコードを編集していきましょう。今回は、データの検索と追加を行いますので、それぞれの処理を行う2つのメソッドを実装しています。まず、@PersistenceContextのアノテーションで、(JPA Entity クラスを作成した時に作成した持続性ユニット名。persistence.xml に記載されている。)持続性ユニット名(“PersonManagementPU”)を記載します。これにより持続機能を持った EntityManager のオブジェクト(em)がセットされます。

package pejb;

import ent.Person;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

@Stateless
public class PersonEJB {

    @PersistenceContext(unitName="PersonManagementPU")
    private EntityManager em;

    public List<Person> findAllPersons(){
    //Entity(Person)クラスの @NamedQuery の記載に基づき検索
        Query query = em.createNamedQuery("findAllPerson");
        return (List<Person>) query.getResultList();
    }

    public Person createPerson(Person person){
        em.persist(person);
        return person;
    }
}

4.JSF:Managed Bean の作成

EJBと、Entity を作成したので、JSF から利用する Managed Bean を作成しましょう。「ファイル」→「新規ファイル(N)…」→「JavaServer Faces」→「JSF 管理対象 Bean」を選択し「次へ>」ボタンを押下してください。

「クラス名(N)」、「パッケージ(K)」を入力し「完了(F)」ボタンを押下します。

補足:
JSF 2.0 からは下記のような faces-config.xml の設定ファイルがオプション化されました。そこで Managed Bean の設定や画面遷移を XML ファイルに記載する必要はなく、アノテーションを利用しプログラム上で実現することができるようになりました。 

JSF1.2 までの faces-confg.xml の設定例:

<managed-bean>
<managed-bean-name>PersonController</managed-bean-name>
<managed-bean-class>PersonController</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<navigation-rule>
<from-view-id>/foo.jsp</from-view-id>
<navigation-case>
<from-outcome>ok</from-outcome>
<to-view-id>/bar.jsp</to-view-id>
</navigation-case>
</navigation-rule>

 

 

コードを編集していきましょう。作成するクラスが Managed Bean であることをコンテナに理解させるため、@ManagedBean のアノテーションを利用します。アノテーションを指定する際、”name” 属性は必須で Managed Bean を識別する名前を入力します。

package mbean;

import ent.Person;
import pejb.PersonEJB;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.ejb.EJB;

@ManagedBean(name="PersonController")
@RequestScoped
public class PersonController {

    public PersonController() {
    }

    @EJB
    private PersonEJB personEJB;

    private Person person = new Person();
    private List<Person> personList = new ArrayList<Person>();

    //Facelets の action="#{PersonController.doNew} により画面遷移
    public String doNew(){
        return "createPerson.xhtml";
    }

    //Facelets の action="#{PersonController.doCreatePerson} により画面遷移
    public String doCreatePerson(){
        //画面から入力された Person を DB に登録
        setPerson(personEJB.createPerson(getPerson()));
        //DB に登録されている全 Person を personList に設定
        setPersonList(personEJB.findAllPersons());
        return "listPersons.xhtml";
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    public List<Person> getPersonList() {
        return personList;
    }

    public void setPersonList(List<Person> personList) {
        this.personList = personList;
    }
}

5.JSF 2.0: ベージの作成

最後に、表示画面を作成します。JSF 2.0 から facelet を xhtml で記載できるようになりました。

「ファイル」→「新規ファイル(N)…」→「JavaServer Faces」を選択し「次へ>」ボタンを
押下してください。

JSF 表示画面の作成

ファイル名 (createPerson.xhtml) を入力すると自動的に .xhtml の拡張子が付加されます、最後に「完了(F)」ボタンを押下します。

JSF 表示画面の作成

自動生成されたひな形を編集して下記のコードを記載してください。Facelets で扱えるタグ等は別途紹介する予定ですが、みていただければわかるとおり、HTML も記載する事ができますし、スタイルシート等を記載することも可能です。また、JSF 2.0 から Ajax をサポートしていますので、Ajax のコードを利用してサーバと通信する事もできるようになります。

<?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:h="http://java.sun.com/jsf/html">
      <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
            <table>
                <tr>
                    <td>
                <h:outputLabel value="名前:"/>
                </td>
                <td>
                <h:inputText value="#{PersonController.person.name}"/>
                </td>
                </tr>
                <tr>
                    <td>
                <h:outputLabel value="住所"/>
                </td>
                <td>
                <h:inputText value="#{PersonController.person.address1}"/>
                </td>
                </tr>
                <tr>
                    <td>
                <h:outputLabel value="電話番号"/>
                </td>
                <td>
                <h:inputText value="#{PersonController.person.telephone}"/>
                </td>
                </tr>
                <tr>
                    <td>
                <h:outputLabel value="年齢"/>
                </td>
                <td>
                <h:inputText value="#{PersonController.person.age}"/>
                </td>
                </tr>
            </table>
            <h:commandButton value="作成" action="#{PersonController.doCreatePerson}"/>
        </h:form>
    </h:body>
</html>

最後に、登録された一覧を表示する画面を作成します。
「ファイル」→「新規ファイル(N)…」→「JavaServer Faces」を選択し「次へ>」ボタンを
押下してください。

JSF 表示画面の作成

ファイル名(listPersons)を入力し「完了(F)」ボタンを押下します。

<?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:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
      <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body> 

        <h:dataTable value="#{PersonController.personList}" var="psn" border="1">
            <h:column>
                <f:facet name="header">
                    <h:outputText value="名前"/>
                </f:facet>
                <h:outputText value="#{psn.name}"/>
            </h:column>

            <h:column>
                <f:facet name="header">
                    <h:outputText value="住所"/>
                </f:facet>
                <h:outputText value="#{psn.address1}"/>
            </h:column>

            <h:column>
                <f:facet name="header">
                    <h:outputText value="電話番号"/>
                </f:facet>
                <h:outputText value="#{psn.telephone}"/>
            </h:column>

            <h:column>
                <f:facet name="header">
                    <h:outputText value="年齢"/>
                </f:facet>
                <h:outputText value="#{psn.age}"/>
            </h:column>
        </h:dataTable>
        <h:form>
            <h:commandLink action="#{PersonController.doNew}">
                新規作成</h:commandLink>
        </h:form>
    </h:body>
</html>

以上で、このアプリケーションの実装は完了です。

NetBeans のメニューより「実行(R)」→「主プロジェクトを実行(R)」を選択してください。実行すると自動的にブラウザがオープンしアプリケーションのデフォルトのコンテンツが表示されます。

コンテキストルートでアクセスされた時のデフォルトページを変更したい場合は、web.xml ファイルを編集する必要があります。下記のように welcome-file-list を編集してください。

web.xml の編集

<welcome-file-list>
<welcome-file>faces/createPerson.xhtml</welcome-file>
</welcome-file-list>

設定を変更した後、もう一度プロジェクトを実行してください。すると下記のように自動的に登録画面が表示されるようになります。

データを入力して「作成」ボタンを押下してみましょう。下記のように一覧表示画面が表示されれば問題はありません。

最後に、エラーハンドリングについて紹介します。例外が発生した際に特定のエラーページにリダイレクトさせるようにweb.xml の<error-page> タグを記載する必要があります。<exception-type>で、ハンドリングした例外毎にエラーページを表示させる事が可能です。本番環境においては、少なくとも JSF で発生する例外の親である javax.servlet.ServletException を記載してください。

web.xml の編集

<error-page>
<exception-type>javax.servlet.ServletException</exception-type>
<location>/error.xhtml</location>
</error-page>

 

以上で、今回の Java EE 6 のアプリケーション作成は終了です。NetBeans を利用するとウィザードを使って、コードのひな形を作成してくれたり、また設定も極力 XML ファイルを手動で編集しなくても済むようになります。是非お試しください。今回紹介したサンプルは Java EE 6 を利用する上で最も基本的な構成と思います、上記の例を元に色々と修正を加え、DI/CDI や Bean Validation 等を加えて Java EE 6 の機能を是非色々と試してみてください。

最後に、私の環境で作成したNetBeans 6.8 のプロジェクトを下記から入手できるようにしました。
(※ Sun の mediacast.sun.com サイトにファイルをアップロードしていますので、
仮にこのサイトが閉鎖してしまった場合は申し訳ありませんがご了承ください。)

● NetBeans 6.8 のプロジェクト(ソース、設定ファイル等を含む)
● GlassFish v3 配備用 WARファイル

2010年2月26日 at 3:33 PM 2件のコメント

GlassFish データベースの設定

GlassFishはデータベースとの接続にデータベースベンダーが提供するJDBCドライバを使用します.

GlassFishにはインストール時デフォルトで Java DBが付属しており,あらかじめJava DBに対するJDBCリソースと接続プールの設定がされています.そして GlassFish の監視系の機能の一つである,「呼び出しフロー」のデータ保存先として使用されています.
ここでは,新たにMySQL 5.1との接続設定方法について紹介します.MySQLと接続するためには,MySQLのJDBCドライバを事前に入手しておいてください.
また以降の手順ではMySQLが既にインストールされていることを前提に説明します.

JDBCドライバに対するクラスパスを設定

dashost > asadmin get “cluster1-config.java-config.server-classpath”
cluster1-config.java-config.server-classpath =

dashost > asadmin set “cluster1-config.java-config.server-classpath=/sun/glassfish-v2.1.1/lib/mysql-connector-java-5.1.5-bin.jar”
cluster1-config.java-config.server-classpath = /sun/glassfish-v2.1.1/lib/mysql-connector-java-5.1.5-bin.jar

クラスパスの設定は,管理コンソールから左ペインの「設定」→「対象のコンフィグ(cluster1-config)」→「JVM 設定」を選択し右ペインの「サーバクラスパス」内に記載し設定できます.


次に JDBC の接続プールを作成します.

dashost > asadmin create-jdbc-connection-pool –datasourceclassname com.mysql.jdbc.jdbc2.optional.MysqlDataSource –restype=javax.sql.DataSource –property user=root:password=password:DatabaseName=MySQL:ServerName=localhost:port=3306:URL=”jdbc\:mysql\://localhost\:3306/test” MySQL-Pool
コマンド create-jdbc-connection-pool は正常に実行されました。


分散トランザクションが必要な場合,リソースタイプ,データソースにそれぞれ下記を使用してください.
javax.sql.XADataSource
com.mysql.jdbc.jdbc2.optional.MysqlXADataSource

管理コンソールからは下記の手順に従い設定してください.
まず、左ペインの「リソース」→「JDBC」→ 「接続プール」を選択し右ペインの「新規 …」ボタンを押下します.

次に,「接続プール名」,「リソースタイプ」,「データベースベンダー」を選択し「次へ」ボタンを押下します.

最後に,JDBC の各種詳細な設定項目を選択/入力し「完了」ボタンを押下します.






JDBCドライバ等の設定項目一覧が数多くリストされますが,ここでは最低限下記の項目のみ編集して設定を完了します.

名前
user root
password password
DatabaseName MySQL
ServerName localhost
port 3306
URL jdbc:mysql://localhost:3306/test


「完了」ボタンを押下すると下記の画面が表示されます.

ここで、作成した JDBC 接続プールが有効か否か疎通確認を行います.

成功時:

dashost > asadmin ping-connection-pool MySQL-Pool
コマンド ping-connection-pool は正常に実行されました。


失敗時:

dashost > asadmin ping-connection-pool MySQL-Pool
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
CLI137 コマンド ping-connection-pool は失敗しました。


また,管理コンソールから左ペインの「リソース」→「 JDBC」→「 接続プール」より対象の接続プールを選択し「Ping」ボタンを押下し確認できます.
成功時:

失敗時:

次に、作成した JDBC 接続プールに対して,リソース設定を行います.
アプリケーションはここで設定する JNDI 名を利用して DB にアクセスできるようになります.

dashost > asadmin create-jdbc-resource –connectionpoolid MySQL-Pool jdbc/mysql
コマンド create-jdbc-resource は正常に実行されました。


管理コンソールからは下記の手順に従い設定してください.
まず、左ペインの「リソース」→「JDBC」→ 「 JDBCリソース」を選択し右ペインより「新規 …」ボタンを押下します.


続いて,「JNDI 名」,「プール名」,有効状態のチェックをつけ「利用可能なターゲット」に対象のクラスタ(cluster1)を追加して「了解」ボタンを押下します.


「了解」ボタンを押下すると下記の画面が表示され JDBC リソースの設定は完了します.以降アプリケーションはここで設定した “jdbc/mysql” を利用して DB
に対して接続ができるようになります.

DBの設定情報の取得
ここで,DB の設定可能な項目の一覧をリストしてみます.asadmin の get コマンドを実行して確認ができます.各設定項目は asadmin set コマンドで設定変更を更新できますので必要に応じて設定変更を行ってください.

dashost > asadmin get “domain.resources.jdbc-connection-pool.MySQL-Pool.*”
domain.resources.jdbc-connection-pool.MySQL-Pool.allow-non-component-callers = false
domain.resources.jdbc-connection-pool.MySQL-Pool.associate-with-thread = false
domain.resources.jdbc-connection-pool.MySQL-Pool.connection-creation-retry-attempts = 0
domain.resources.jdbc-connection-pool.MySQL-Pool.connection-creation-retry-interval-in-seconds = 10
domain.resources.jdbc-connection-pool.MySQL-Pool.connection-leak-reclaim = false
domain.resources.jdbc-connection-pool.MySQL-Pool.connection-leak-timeout-in-seconds = 0
domain.resources.jdbc-connection-pool.MySQL-Pool.connection-validation-method = auto-commit
domain.resources.jdbc-connection-pool.MySQL-Pool.datasource-classname = com.mysql.jdbc.jdbc2.optional.MysqlDataSource
domain.resources.jdbc-connection-pool.MySQL-Pool.description =
domain.resources.jdbc-connection-pool.MySQL-Pool.fail-all-connections = false
domain.resources.jdbc-connection-pool.MySQL-Pool.idle-timeout-in-seconds = 300
domain.resources.jdbc-connection-pool.MySQL-Pool.is-connection-validation-required = false
domain.resources.jdbc-connection-pool.MySQL-Pool.is-isolation-level-guaranteed = false
domain.resources.jdbc-connection-pool.MySQL-Pool.lazy-connection-association = false
domain.resources.jdbc-connection-pool.MySQL-Pool.lazy-connection-enlistment = false
domain.resources.jdbc-connection-pool.MySQL-Pool.match-connections = false
domain.resources.jdbc-connection-pool.MySQL-Pool.max-connection-usage-count = 0
domain.resources.jdbc-connection-pool.MySQL-Pool.max-pool-size = 32
domain.resources.jdbc-connection-pool.MySQL-Pool.max-wait-time-in-millis = 60000
domain.resources.jdbc-connection-pool.MySQL-Pool.name = MySQL-Pool
domain.resources.jdbc-connection-pool.MySQL-Pool.non-transactional-connections = false
domain.resources.jdbc-connection-pool.MySQL-Pool.pool-resize-quantity = 2
domain.resources.jdbc-connection-pool.MySQL-Pool.property.AllowLoadLocalInfile = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.AllowMultiQueries = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.AllowNanAndInf = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.AllowUrlInLocalInfile = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.AlwaysSendSetIsolation = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.AutoClosePStmtStreams = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.AutoDeserialize = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.AutoGenerateTestcaseScript = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.AutoReconnectForPools = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.AutoSlowLog = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.BlobSendChunkSize = 1048576
domain.resources.jdbc-connection-pool.MySQL-Pool.property.BlobsAreStrings = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.CacheCallableStatements = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.CacheCallableStmts = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.CachePrepStmts = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.CachePreparedStatements = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.CacheResultSetMetadata = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.CacheServerConfiguration = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.CallableStatementCacheSize = 100
domain.resources.jdbc-connection-pool.MySQL-Pool.property.CallableStmtCacheSize = 100
domain.resources.jdbc-connection-pool.MySQL-Pool.property.CapitalizeTypeNames = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.ClientInfoProvider = com.mysql.jdbc.JDBC4CommentClientInfoProvider
domain.resources.jdbc-connection-pool.MySQL-Pool.property.ClobberStreamingResults = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.ConnectTimeout = 0
domain.resources.jdbc-connection-pool.MySQL-Pool.property.ContinueBatchOnError = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.CreateDatabaseIfNotExist = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.DatabaseName = MySQL
domain.resources.jdbc-connection-pool.MySQL-Pool.property.DefaultFetchSize = 0
domain.resources.jdbc-connection-pool.MySQL-Pool.property.DontTrackOpenResources = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.DumpMetadataOnColumnNotFound = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.DumpQueriesOnException = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.DynamicCalendars = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.ElideSetAutoCommits = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.EmptyStringsConvertToZero = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.EmulateLocators = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.EmulateUnsupportedPstmts = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.EnablePacketDebug = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.EnableQueryTimeouts = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.ExplainSlowQueries = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.FailOverReadOnly = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.FunctionsNeverReturnBlobs = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.GatherPerfMetrics = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.GatherPerformanceMetrics = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.GenerateSimpleParameterMetadata = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.HoldResultsOpenOverStatementClose = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.IgnoreNonTxTables = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.IncludeInnodbStatusInDeadlockExceptions = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.InitialTimeout = 2
domain.resources.jdbc-connection-pool.MySQL-Pool.property.InteractiveClient = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.IsInteractiveClient = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.JdbcCompliantTruncation = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.JdbcCompliantTruncationForReads = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.LoadBalanceStrategy = random
domain.resources.jdbc-connection-pool.MySQL-Pool.property.LocatorFetchBufferSize = 1048576
domain.resources.jdbc-connection-pool.MySQL-Pool.property.LogSlowQueries = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.LogXaCommands = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.Logger = com.mysql.jdbc.log.StandardLogger
domain.resources.jdbc-connection-pool.MySQL-Pool.property.LoggerClassName = com.mysql.jdbc.log.StandardLogger
domain.resources.jdbc-connection-pool.MySQL-Pool.property.LoginTimeout = 0
domain.resources.jdbc-connection-pool.MySQL-Pool.property.MaintainTimeStats = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.MaxQuerySizeToLog = 2048
domain.resources.jdbc-connection-pool.MySQL-Pool.property.MaxReconnects = 3
domain.resources.jdbc-connection-pool.MySQL-Pool.property.MaxRows = -1
domain.resources.jdbc-connection-pool.MySQL-Pool.property.MetadataCacheSize = 50
domain.resources.jdbc-connection-pool.MySQL-Pool.property.NetTimeoutForStreamingResults = 600
domain.resources.jdbc-connection-pool.MySQL-Pool.property.NoAccessToProcedureBodies = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.NoDatetimeStringSync = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.NoTimezoneConversionForTimeType = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.NullCatalogMeansCurrent = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.NullNamePatternMatchesAll = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.OverrideSupportsIntegrityEnhancementFacility = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.PacketDebugBufferSize = 20
domain.resources.jdbc-connection-pool.MySQL-Pool.property.PadCharsWithSpace = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.Paranoid = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.Password = password
domain.resources.jdbc-connection-pool.MySQL-Pool.property.Pedantic = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.PinGlobalTxToPhysicalConnection = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.PopulateInsertRowWithDefaultValues = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.Port = 3306
domain.resources.jdbc-connection-pool.MySQL-Pool.property.PortNumber = 3306
domain.resources.jdbc-connection-pool.MySQL-Pool.property.PrepStmtCacheSize = 25
domain.resources.jdbc-connection-pool.MySQL-Pool.property.PrepStmtCacheSqlLimit = 256
domain.resources.jdbc-connection-pool.MySQL-Pool.property.PreparedStatementCacheSize = 25
domain.resources.jdbc-connection-pool.MySQL-Pool.property.PreparedStatementCacheSqlLimit = 256
domain.resources.jdbc-connection-pool.MySQL-Pool.property.ProcessEscapeCodesForPrepStmts = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.ProfileSQL = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.ProfileSql = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.QueriesBeforeRetryMaster = 50
domain.resources.jdbc-connection-pool.MySQL-Pool.property.ReconnectAtTxEnd = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.RelaxAutoCommit = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.ReportMetricsIntervalMillis = 30000
domain.resources.jdbc-connection-pool.MySQL-Pool.property.RequireSSL = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.ResultSetSizeThreshold = 100
domain.resources.jdbc-connection-pool.MySQL-Pool.property.RewriteBatchedStatements = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.RollbackOnPooledClose = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.RoundRobinLoadBalance = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.RunningCTS13 = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.SecondsBeforeRetryMaster = 30
domain.resources.jdbc-connection-pool.MySQL-Pool.property.ServerName = localhost
domain.resources.jdbc-connection-pool.MySQL-Pool.property.SlowQueryThresholdMillis = 2000
domain.resources.jdbc-connection-pool.MySQL-Pool.property.SlowQueryThresholdNanos = 0
domain.resources.jdbc-connection-pool.MySQL-Pool.property.SocketFactory = com.mysql.jdbc.StandardSocketFactory
domain.resources.jdbc-connection-pool.MySQL-Pool.property.SocketFactoryClassName = com.mysql.jdbc.StandardSocketFactory
domain.resources.jdbc-connection-pool.MySQL-Pool.property.SocketTimeout = 0
domain.resources.jdbc-connection-pool.MySQL-Pool.property.StrictFloatingPoint = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.StrictUpdates = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.TcpKeepAlive = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.TcpNoDelay = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.TcpRcvBuf = 0
domain.resources.jdbc-connection-pool.MySQL-Pool.property.TcpSndBuf = 0
domain.resources.jdbc-connection-pool.MySQL-Pool.property.TcpTrafficClass = 0
domain.resources.jdbc-connection-pool.MySQL-Pool.property.TinyInt1isBit = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.TraceProtocol = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.TransformedBitIsBoolean = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.TreatUtilDateAsTimestamp = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.URL = jdbc:mysql://:3306/
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UltraDevHack = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.Url = jdbc:mysql://localhost:3306/test
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseBlobToStoreUTF8OutsideBMP = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseCompression = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseCursorFetch = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseDirectRowUnpack = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseDynamicCharsetInfo = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseFastDateParsing = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseFastIntParsing = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseGmtMillisForDatetimes = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseHostsInPrivileges = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseInformationSchema = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseJDBCCompliantTimezoneShift = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseJvmCharsetConverters = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseLocalSessionState = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseNanosForElapsedTime = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseOldAliasMetadataBehavior = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseOldUTF8Behavior = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseOnlyServerErrorMessages = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseReadAheadInput = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseSSL = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseSSPSCompatibleTimezoneShift = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseServerPrepStmts = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseServerPreparedStmts = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseSqlStateCodes = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseStreamLengthsInPrepStmts = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseTimezone = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseUltraDevWorkAround = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseUnbufferedInput = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseUnicode = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.UseUsageAdvisor = false
domain.resources.jdbc-connection-pool.MySQL-Pool.property.User = root
domain.resources.jdbc-connection-pool.MySQL-Pool.property.YearIsDateType = true
domain.resources.jdbc-connection-pool.MySQL-Pool.property.ZeroDateTimeBehavior = exception
domain.resources.jdbc-connection-pool.MySQL-Pool.res-type = javax.sql.DataSource
domain.resources.jdbc-connection-pool.MySQL-Pool.statement-timeout-in-seconds = -1
domain.resources.jdbc-connection-pool.MySQL-Pool.steady-pool-size = 8
domain.resources.jdbc-connection-pool.MySQL-Pool.transaction-isolation-level =
domain.resources.jdbc-connection-pool.MySQL-Pool.validate-atmost-once-period-in-seconds = 0
domain.resources.jdbc-connection-pool.MySQL-Pool.validation-table-name =
domain.resources.jdbc-connection-pool.MySQL-Pool.wrap-jdbc-objects = false


コネックションプールのモニタリング
最後に、JDBC の接続プールに対してモニタリングの設定を行う方法を紹介します.モニタリングを有効にするために下記の手順に従い設定を行い,確認してください.

dashost > asadmin list “*monitor*”
server.monitoring-service
server.monitoring-service.module-monitoring-levels
dashost > asadmin set “server.monitoring-service.module-monitoring-levels.jdbc-connection-pool=HIGH”
server.monitoring-service.module-monitoring-levels.jdbc-connection-pool = HIGH
dashost > asadmin get “server.monitoring-service.module-monitoring-levels.*”
server.monitoring-service.module-monitoring-levels.connector-connection-pool = OFF
server.monitoring-service.module-monitoring-levels.connector-service = OFF
server.monitoring-service.module-monitoring-levels.ejb-container = OFF
server.monitoring-service.module-monitoring-levels.http-service = OFF
server.monitoring-service.module-monitoring-levels.jdbc-connection-pool = HIGH
server.monitoring-service.module-monitoring-levels.jms-service = OFF
server.monitoring-service.module-monitoring-levels.jvm = OFF
server.monitoring-service.module-monitoring-levels.orb = OFF
server.monitoring-service.module-monitoring-levels.thread-pool = OFF
server.monitoring-service.module-monitoring-levels.transaction-service = OFF
server.monitoring-service.module-monitoring-levels.web-container = OFF
dashost > asadmin list –monitor=true “*MySQL*”
server.resources.MySQL-Pool
dashost > asadmin get –monitor=true “server.resources.MySQL-Pool.*”

2010年2月8日 at 12:21 PM 1件のコメント

GlassFish SSLの設定(クライアント認証)

GlassFish のドメイン管理サーバ上でHTTP リスナーの設定をクライアント認証対応に変更

dashost > asadmin set “cluster1-config.http-service.http-listener.http-listener-2.ssl.client-auth-enabled = true”

クライアント証明書の秘密鍵を作成

ca-admin > openssl genrsa -des3 -out /tmp/client-private-key 2048
Generating RSA private key, 2048 bit long modulus
……….+++
………………………………….+++
e is 65537 (0x10001)
Enter pass phrase for /tmp/client-private-key:
Verifying – Enter pass phrase for /tmp/client-private-key:


クライアント証明書用のCSRを作成

下記の例では便宜上,認証局(CA)上で CSR を生成していますが,CSRの生成はどの環境で構築しても問題ありません.

ca-admin > openssl req -new -days 365 -key /tmp/client-private-key -out /tmp/clientcsr.pem
Enter pass phrase for /tmp/client-private-key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter ‘.’, the field will be left blank.
—–
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Setagaya
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Sun Microsystems
Organizational Unit Name (eg, section) []:Software Practice
Common Name (eg, YOUR name) []:Tarou Yamada
Email Address []:Tarou.Yamada@Sun.COM

Please enter the following ‘extra’ attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:


認証局(CA)でクライアント証明書を署名

クライアント証明書のCSRを認証局(CA)で署名します。

ca-admin > openssl ca -config ./openssl-client.cnf -in /tmp/clientcsr.pem -out /tmp/signed-clientcert.pem
Using configuration from ./openssl-client.cnf
Enter pass phrase for ./demoCA/private/cakey.pem:
Check that the request matches the signatureSignature ok
Certificate Details:
Serial Number:
cd:ea:db:f4:c8:e3:93:92
Validity
Not Before: Dec  4 08:27:14 2009 GMT
Not After : Dec  4 08:27:14 2010 GMT
(省略)


署名されたクライアント証明書をGlassFishのキーストアにインポートするため,X.509形式に変更

ca-admin > openssl x509 -in /tmp/signed-clientcert.pem -out /tmp/signed-clientcert.x509


GlassFishのキーストアに対してクライアント証明書をインポート

dashost > keytool -import -alias yamada -file /tmp/signed-clientcert.x509
キーストアのパスワードを入力してください:[changeit]
新規パスワードを再入力してください:[changeit]
所有者: EMAILADDRESS=Tarou.Yamada@Sun.COM, CN=Tarou Yamada, OU=Software Practice, O=Sun Microsystems, ST=Tokyo, C=JP
発行者: EMAILADDRESS=caadmin@Sun.COM, CN=ca-server.sun.com, OU=Software Practice, O=Sun Microsystems, ST=Tokyo, C=JP
シリアル番号: cdeadbf4c8e39392
有効期間の開始日: Fri Dec 04 17:27:14 JST 2009 終了日: Sat Dec 04 17:27:14 JST 2010
証明書のフィンガープリント:
MD5:  59:06:3C:95:79:5D:31:D7:A3:92:0E:AA:A2:23:73:6D
SHA1: 26:00:30:F1:15:DB:20:D4:8E:AF:99:AB:96:13:CB:17:DA:E5:71:15
署名アルゴリズム名: SHA1withRSA
バージョン: 3
(省略)
この証明書を信頼しますか? [no]:  yes
証明書がキーストアに追加されました。


クライアント証明書をブラウザにインポートするためPKCS12形式に変換

ca-admin > openssl pkcs12 -export -in /tmp/signed-clientcert.pem -inkey /tmp/client-private-key -certfile ./demoCA/cacert.pem -out /tmp/clientcert.p12
Enter pass phrase for /tmp/client-private-key:
Enter Export Password:
Verifying – Enter Export Password:
ca-admin > ls -l /tmp/clientcert.p12
-rw-r–r–   1 root     root        3525 12月  4日  17:29 /tmp/clientcert.p12


最後にブラウザにPKCS12形式のクライアント証明書をインポートします.

上記で設定は完了です,ブラウザからアクセスしてみてください.

2010年2月8日 at 11:44 AM

GlassFish ドメイン管理サーバのバックアップとリストア

※ GlassFish のドメイン管理サーバのバックアップとリストアは
管理コンソールからは行えません.コマンドを使用してください.

ドメイン管理サーバが稼働するサーバが何らかの障害により起動できなくなった場合,ドメインの管理ができなくなります.ドメイン管理サーバが稼働していない状態でもサーバインスタンスを稼働させることはできますが,ドメイン管理サーバが壊れている状態ではアプリケーションの更新やリソースの設定変更等ができなくなります.このような場合、ドメイン管理サーバの設定をバックアップしておくことで,他のサーバをドメイン管理サーバの代替えとして利用することができます.バックアップはドメイン管理サーバ上で何らかの設定変更を行った際,その都度取得しておくことを推奨します.

バックアップを取得する前に,ドメイン管理サーバを停止してください.ドメイン管理サーバを停止しても提供しているサービスに影響は及びませんのでこの作業は本番稼働中に行っても問題ありません.

dashost > asadmin stop-domain clusterDomain
ドメイン clusterDomain が停止しました。


次にドメイン管理サーバのバックアップを行います.–domaindirには対象のドメインが含まれるディレクトリを指定します.

dashost > asadmin backup-domain –domaindir /export/home/appserv/domains clusterDomain
ドメインを正常にバックアップしました
説明: 1259776406949
バックアップファイル名: /export/home/appserv/domains/clusterDomain/backups/sjsas_backup_v00001.zip
バックアップを実行した日付と時刻: Thu Dec 03 02:53:26 JST 2009
ドメインディレクトリ: /export/home/appserv/domains
ドメインディレクトリ: /export/home/appserv/domains/clusterDomain
ドメイン名: clusterDomain
バックアップを実行したユーザーの名前: appserv


バックアップコマンドを実行すると,sjsas_backup_vXXXXX.zipと名付けられたバックアップファイルが作成されます.このファイルをドメイン管理サーバのバックアップ用のサーバにFTP等を利用してコピーしておきます.ここではドメイン管理サーバのバックアップ用のサーバとしてnodeagent1を使用します.本番環境ではノードエージェントとは別のマシン上に構築することを推奨します.

nodeagent1 > asadmin restore-domain –filename ./sjsas_backup_v00001.zip clusterDomain
ドメイン clusterDomain の /export/home/appserv/domains/clusterDomain への復元に成功しました
説明: 1259776406949
バックアップファイル名: /export/home/appserv/domains/clusterDomain/backups/sjsas_backup_v00001.zip
バックアップを実行した日付と時刻: Thu Dec 03 02:53:26 JST 2009
ドメインディレクトリ: /export/home/appserv/domains
ドメインディレクトリ: /export/home/appserv/domains/clusterDomain
ドメイン名: clusterDomain
バックアップを実行したユーザーの名前: appser


展開したファイル中よりドメイン管理サーバ名が記載されている箇所を手動で編集します.domains/clusterDomain/config /domain.xml中より修正する必要のある該当箇所を表示し,dashostからnodeagent1に変更します.

nodeagent1 > grep dashost domain.xml
<property name="client-hostname" value="dashost“/>
<jms-host admin-password="admin" admin-user-name="admin" host="dashost” name=”default_JMS_host” port=”5076″/>
<property name="client-hostname" value="dashost“/>
<jms-host admin-password="admin" admin-user-name="admin" host="dashost” name=”default_JMS_host” port=”${JMS_PROVIDER_PORT}”/>
<property name="client-hostname" value="dashost“/>
<jms-host admin-password="admin" admin-user-name="admin" host="dashost” name=”default_JMS_host” port=”${JMS_PROVIDER_PORT}”/>


新しい環境上で再度環境変数を設定した後,ドメインを起動します.

nodeagent1 > setenv AS_ADMIN_HOST nodeagent1.japan.sun.com
nodeagent1 > env |grep AS
AS_ADMIN_USER=clusterAdmin
AS_ADMIN_PASSWORDFILE=/export/home/appserv/.passwordfile
AS_ADMIN_HOST=nodeagent1.japan.sun.com
AS_ADMIN_PORT=5048
nodeagent1 > asadmin start-domain clusterDomain
ドメイン clusterDomain を起動しています。お待ちください。
デフォルトのログの場所は /export/home/appserv/domains/clusterDomain/logs/server.log です。
出力を /export/home/appserv/domains/clusterDomain/logs/server.log にリダイレクトしています
ドメイン clusterDomain が起動しました。
ドメイン [clusterDomain] はその設定で [Sun GlassFish Enterprise Server v2.1.1 ((v2.1 Patch06)(9.1_02 Patch12)) (build b31g-fcs)] を実行しています。ログは [/export/home/appserv/domains] にあります。
管理コンソールは [http://localhost:5048] で使用できます。
“asadmin” コマンドにも同じポート [5048] を使用します。
ユーザーの Web アプリケーションは次の URL で使用できます:
[http://localhost:5080 https://localhost:5081 ]。
次の web-contexts を使用できます:
[/web1 /__wstx-services ]。
標準の JMX クライアント (JConsole など) はドメイン管理のために JMXServiceURL:
[service:jmx:rmi:///jndi/rmi://nodeagent1.japan.sun.com:5086/jmxrmi]
に接続できます。
ドメインは少なくとも次のポートで接続を待機しています:
[5080 5081 5048 5037 5038 5039 5086 ]。
ドメインはアプリケーションサーバークラスタおよびその他のスタンドアロンインスタンスをサポートします。


上記で,ドメインのリストアは完了です.仮にドメイン管理サーバにアクセスするためにSSLを有効にしている場合にドメイン管理サーバのホスト名が変わる場合,サーバ証明書の更新も必要です.デフォルトでインストールされている自己署名サーバ証明書は変更前のドメイン管理サーバ名が記載されていますので更新の必要があります.
最後に,念のためノードエージェントが保持するドメイン管理サーバへの参照情報が更新されているか確認してください.全てのノードエージェントはagentディレクトリ中にconfigディレクトリが存在しています.このディレクトリ配下にdas.propertiesファイルが存在しており,ドメイン管理サーバへ接続するための接続情報などが記載されています.そこでファイルの内容を確認しdashostからnodeagent1に変更されていることを確認してください.仮に変更されていない場合は手動で変更してください.

nodeagent1 > pwd
/export/home/appserv/nodeagents/nodeagent1/agent/config
nodeagent1 > grep das.host das.properties
agent.das.host=nodeagent1
nodeagent2 > pwd
/export/home/appserv/nodeagents/nodeagent2/agent/config
nodeagent2 > grep das.host das.properties
agent.das.host=nodeagent1

2010年2月5日 at 4:35 PM

GlassFish クラスタとクラスタインスタンス管理(作成、削除、起動、停止)

ここでは管理ツールを使用しクラスタの管理を行う方法について紹介します.

クラスタの作成

クラスタ構成を作成するためにはcreate-clusterコマンドを使用します.コマンド引数にクラスタ名を指定してクラスタを作成します.

asadmin create-cluster cluster1
コマンド create-cluster は正常に実行されました。

また,管理コンソールから左ペインの「クラスタ」を選択し右ペインの「新規…」ボタンを押すことによって作成することができます.

クラスタの作成

クラスタの作成

クラスタサーバインスタンスの作成

クラスタに所属するサーバインスタンスを作成するためには,create-instanceコマンドを実行します.コマンド引数にインスタンスを配置するノードエージェント名とクラスタ名を指定してインスタンスを作成します.

dashost > asadmin create-instance –nodeagent nodeagent1 –cluster cluster1 instance1
コマンド create-instance は正常に実行されました。
dashost > asadmin create-instance –nodeagent nodeagent2 –cluster cluster1 instance2
コマンド create-instance は正常に実行されました

また,クラスタに所属するサーバインスタンスは「クラスタ」作成のウィザードから「作成するサーバインスタンス(2)」のテーブルから,「新規…」ボタンを押すことによって作成することができます.この時ノードエージェントの選択コンボボックスからサーバインスタンスが稼働するノードエージェントを選択します.

クラスタインスタンスの作成

クラスタインスタンスの作成

クラスタの一覧表示

ドメイン内に存在するクラスタの一覧を表示するためにはlist-clustersコマンドを使用します.またコマンドを実行すると一覧表示の他クラスタの稼働状態も表示します.

dashost > asadmin list-clusters
cluster2 実行していません
cluster1 実行しています
コマンド list-clusters は正常に実行されました。

また,管理コンソールから左ペインの「クラスタ」を選択して確認できます.

クラスタの一覧表示

クラスタの一覧表示

クラスタの起動

クラスタの起動はstart-clusterコマンドを使用します.コマンド引数にクラスタ名を指定して起動します.

dashost > asadmin start-cluster cluster2
クラスタ化されたインスタンス instance3 の起動に成功しました。
クラスタ化されたインスタンス instance4 の起動に成功しました。
コマンド start-cluster は正常に実行されました。

また,管理コンソールから左ペインの「クラスタ」を選択し右ペイン中より対象のクラスタにチェックを付け「クラスタの起動」ボタンを押すことによって作成することができます.

クラスタの起動

クラスタの起動

クラスタの停止

クラスタの停止はstop-clusterコマンドを実行します.コマンド引数にクラスタ名を指定して停止します.

dashost > asadmin stop-cluster cluster1
クラスタ化されたインスタンス instance1 の停止に成功しました。
クラスタ化されたインスタンス instance2 の停止に成功しました。
コマンド は正常に実行されました。

また,管理コンソールから左ペインの「クラスタ」を選択し右ペイン中より対象のクラスタにチェックを付け「クラスタの停止」ボタンを押すことによってクラスタを停止することができます.

クラスタの停止

クラスタの停止

クラスタの削除

クラスタを削除するためには,クラスタに所属している全てのインスタンスを停止/削除した後に削除を行います.
まず,クラスタの一覧とクラスタに所属するインスタンスを表示します.

dashost > asadmin list-clusters
cluster2 実行していません
cluster1 実行していません
コマンド list-clusters は正常に実行されました。
dashost > asadmin list-instances
instance1 実行していません
instance2 実行していません
instance3 実行していません
instance4 実行していません
コマンド list-instances は正常に実行されました。

次に,cluster1に属するinstance1,instance2を削除します.

dashost > asadmin delete-instance instance1
コマンド delete-instance は正常に実行されました。
dashost > asadmin delete-instance instance2
コマンド delete-instance は正常に実行されました。

最後にcluster1を削除します.

dashost > asadmin delete-cluster cluster1
コマンド delete-cluster は正常に実行されました。

また,管理コンソールから左ペインの「クラスタ」を選択し右ペイン中より対象のクラスタにチェックを付け「削除」ボタンを押すことによってクラスタを削除することができます.管理コンソールから削除を実行すると,クラスタ内に属する全てのインスタンスも同時に削除する事ができます.

クラスタの削除

クラスタの削除

2010年2月5日 at 3:31 PM

過去の投稿 新しい投稿


Java Champion & Evangelist

Translate

ご注意

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

カレンダー

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

カテゴリー

Twitter

clustermap

ブログ統計情報

  • 1,172,846 hits

RSSフィード

アーカイブ