JSF2.0+JPA2.0+eclipse 構築編:やり方を見直す

前のエントリ

  • glassfish-embeddedの起動が遅い
  • web.xmlを書くのがめんどい

とぼやいていたらid:shinさんにコメントで耳寄り情報をいただいたので早速試してみました。

glassfish-embeddedは再起動せずに再デプロイするようにする

今まではクラスに変更入れたらいちいちコンテナを再起動していたのですが、再デプロイだけすれば速い、とのこと。
試しにコンソールでEnter押したら再デプロイするようにソースを書き変えてみました。
こんな感じです。

package sandbox.server;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

import org.glassfish.api.deployment.DeployCommandParameters;
import org.glassfish.api.embedded.ContainerBuilder;
import org.glassfish.api.embedded.EmbeddedContainer;
import org.glassfish.api.embedded.EmbeddedDeployer;
import org.glassfish.api.embedded.EmbeddedFileSystem;
import org.glassfish.api.embedded.LifecycleException;
import org.glassfish.api.embedded.Server;

/**
 * 
 */
public class Boot {
    private static final String APPLICATION_NAME = "sandbox-application";
    private static final String CONTEXT          = "/";
    private static final int    PORT             = 18080;

    private final String        fileName;

    private EmbeddedDeployer    deployer;
    private Server              server;

    /**
     * @param pFileName
     */
    public Boot(final String pFileName) {
        this.fileName = pFileName;
    }

    /**
     * 
     */
    public void boot() {
        try {
            start();
            deploy();
        } catch (final IOException e) {
            throw new IllegalStateException(e);
        } catch (final LifecycleException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * 
     */
    public void redeploy() {
        System.out.println("再デプロイ開始…");
        this.deployer.undeployAll();
        deploy();
        System.out.println("再デプロイ完了");
    }

    /**
     * @throws LifecycleException
     */
    public void stop() throws LifecycleException {
        System.out.println("アンデプロイ開始…");
        this.deployer.undeployAll();
        System.out.println("サーバー停止…");
        this.server.stop();
        System.out.println("サーバー停止完了");
    }

    private void deploy() {
        System.out.println("デプロイ開始…");
        this.deployer = this.server.getDeployer();
        final DeployCommandParameters deployParams = new DeployCommandParameters();
        deployParams.name = APPLICATION_NAME;
        deployParams.contextroot = CONTEXT;

        final File archive = new File(this.fileName);
        this.deployer.deploy(archive, deployParams);
        System.out.println("デプロイ完了");
    }

    private void start() throws IOException, LifecycleException {
        System.out.println("サーバー起動開始…");
        final Server.Builder serverBuilder = new Server.Builder("testBuilder");

        final EmbeddedFileSystem.Builder fsBuilder = new EmbeddedFileSystem.Builder();
        final EmbeddedFileSystem fs = fsBuilder.build();
        serverBuilder.embeddedFileSystem(fs);
        this.server = serverBuilder.build();

        final ContainerBuilder<EmbeddedContainer> containerBuilder = this.server
                .createConfig(ContainerBuilder.Type.web);
        this.server.addContainer(containerBuilder);
        containerBuilder.create(this.server);
        this.server.createPort(PORT);

        this.server.start();
        System.out.println("サーバー起動完了");
    }

    /**
     * @param args
     * @throws Exception
     */
    public static void main(final String[] args) throws Exception {
        final Boot server = new Boot("WebContent");
        server.boot();

        final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        for (String line = reader.readLine(); !"end".equals(line); line = reader.readLine()) {
            server.redeploy();
        }
        server.stop();
    }
}

変えたのは

  • 「undeploy」メソッドを「stop」に改名
  • 新たに「redeploy」を作成
  • コンソールでEnterを押したら基本的にはredeployを呼ぶ

です。

再デプロイは1秒前後で完了。めちゃくちゃ速くなりました。もうJettyいらない。

コンソールでEnter押すのもめんどくさい場合は特定ページにアクセスしたら再デプロイとか、または一定時間毎に自動で再デプロイとかも出来ますね。
良い方法を模索していこうと思います。

web.xmlを使わずにFacesServletを登録する

書いてもいいけど書きたくないweb.xml

Servlet3.0からはweb.xmlがなくともサーブレットやフィルタを検出して自動でJavaEEコンテナに登録してくれるそうです。
さらにプログラムからサーブレットやフィルタ、イベントリスナの登録が可能。
というわけで以下のようにすればFacesServletをweb.xmlなしに登録出来ます。

  1. ServletContextListener実装クラスを作成
  2. contextInitializedメソッドの中でFacesServletを登録

実際のクラスはこんな感じ。

/**
 * 
 */
package sandbo.server;

import javax.faces.webapp.FacesServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

/**
 * @author jabaraster
 */
@WebListener
public class ServletRegisterer implements ServletContextListener {

    /**
     * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
     */
    @Override
    public void contextDestroyed(final ServletContextEvent pSce) {
        // 処理なし
    }

    /**
     * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
     */
    @Override
    public void contextInitialized(final ServletContextEvent pSce) {
        final ServletContext context = pSce.getServletContext();
        context.addServlet("jsfServlet", FacesServlet.class).addMapping("*.xhtml");
    }

}

かんたん!


結果はどれもすごく良好。
id:shinさんに大感謝です!



追記

当初は良好だったのですが再デプロイしてもクラスの変更が反映されないときがあります。ブラウザのキャッシュかと思いましたがそういうわけでもなさそう。
もう少し調査が必要なようです。

さらに追記

再デプロイしてもクラスの変更が反映されない、という件ですが、メソッドの中身くらいならJVMを再起動せずとも変更を反映してくれるのですが、インスタンス変数名の変更やメソッドの追加など、クラス構造が変化するような変更はムリのようです。
なおメソッドの中身の変更を即時反映してくれるのは、Hot Code Replace(ホットコード置換?)というeclipseの機能のようですね。デバッグ起動のときしかやってくれないのかな?
ただこの機能、JDKの機能って記述もちらほら見るのでeclipseの機能とは限らないのかもしれません。

いずれにしろ、大きな変更を入れる場合はglassfish-embeddedは再起動が必要のようです。