システム開発現場の道具箱

株式会社モノクレアが運営するIT技術系のブログです。システム開発の現場で役に立つ情報を掲載しています。

JavaのWebアプリ開発でソースコードの変更を即時反映する

JavaのWebアプリ開発で問題になることの一つに、再デプロイの待ち時間があると思います。ソースコードを1行変更しただけでもブラウザで確認するのに5分待つ、これで生産性を上げろというのは何とも無茶な話です。(残念ながらこういう現場もたまに見かけますが。。)

そこで今回は、ソースコードの変更を即座に反映(ホットデプロイ)する方法を紹介します。IDE + APサーバーの組み合わせによってはホットデプロイできるものもありますが、APサーバーのインストールとIDE連携が面倒くさい!という人のために組み込みのAPサーバーを使ってその手間も省きにいきます。

今回紹介するのはEclipse + m2e(Mave Plugin) + Embedded GlassFishの構成ですが、原理は他のIDE、APサーバーにも応用できます。

設定概要

pom.xmlでclassファイルの出力先xhtmlなどのWeb資源ファイルのコピー先を変更します。この設定により、IDEでjavaファイルWeb資源ファイルを保存した直後に、実行中のWebアプリ内の対応するファイルが上書きされるようになります。

pom.xml

pom.xml全体は以下のようになります。EclipseでMavenプロジェクトを作成し、このpom.xmlをコピーするとホットデプロイが有効になったEmbedded GrassFishが使えるようになります。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>

  <modelVersion>4.0.0</modelVersion>

  <!-- The Basics -->
  <groupId>a.b.c</groupId>
  <artifactId>myproject</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  <dependencies>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-api</artifactId>
      <version>7.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <webappDirectory>${project.build.directory}/${project.build.finalName}</webappDirectory>
  </properties>

  <!-- Build Settings -->
  <build>
    <defaultGoal>package embedded-glassfish:run</defaultGoal>
    <outputDirectory>${webappDirectory}/WEB-INF/classes</outputDirectory>   <!-- 1. -->
    <resources>
      <resource>
        <directory>src/main/resources</directory>
      </resource>
      <resource>   <!-- 2. -->
        <directory>src/main/webapp</directory>
        <targetPath>${webappDirectory}</targetPath>
      </resource>
    </resources>
    <plugins>
      <plugin>
        <groupId>org.glassfish.embedded</groupId>
        <artifactId>maven-embedded-glassfish-plugin</artifactId>
        <version>4.1.1</version>
        <configuration>
          <app>${webappDirectory}</app>   <!-- 3. -->
          <contextRoot>/</contextRoot>
          <port>8080</port>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>deploy</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

</project>

GlassFishの起動するには、Eclipseのプロジェクト・エクスプローラー、またはパッケージ・エクスプローラーでプロジェクトを右クリック>デバッグ>Maven buildを選択し、表示されたデバッグ構成ダイアログで実行ボタンをクリックします。

設定詳細

pom.xmlの1.〜3.の設定で、classファイルの出力先Webアプリ資源のコピー先を変更し、GlassFishが実行時に読み込むディレクトリを指定しています。順に説明します。

  1. outputDirectory要素は、Javaファイルをコンパイルしたclassファイルの出力先です。出力先には${webappDirectory}/WEB-INF/classesを指定します。このディレクトリは実行中のWebアプリのclassファイルが配置されます。
  2. このresource要素で、Webアプリ資源(src/main/webapp)を${webappDirectory}にコピーするように設定しています。
  3. app要素は、GlassFishが実行時にWebアプリを読み込むディレクトリです。言い換えると、このディレクトリ以下をGlassFishはWebアプリと認識します。

また、この方法でホットデプロイできるファイルは以下のとおりです。

  • 静的コンテンツ(xhtml, html, js, css, jsp, 画像)
  • java(メソッド内部の変更のみ)

javaファイルはJVMの仕様上、メソッド内部の変更のみがホットデプロイ可能です。メソッド名の変更やメソッドの追加などがあった場合は従来通り再デプロイする必要があります。Embeded Glassfishで再デプロイするには、コンソールビューEnterキーをタイプします。

web.xml (JSFを使用する場合)

JSFを仕様してxhtmlのホットデプロイを有効にするには、更にweb.xmlに以下の2つのcontext-paramを追加します。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd http://www.w3.org/XML/1998/namespace http://www.w3.org/2001/xml.xsd" version="3.1">
  <display-name>myproject</display-name>
  <context-param>
    <param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>
    <param-value>0</param-value>
  </context-param>
  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
  </context-param>
</web-app>