LoginSignup
47
46

More than 5 years have passed since last update.

jenkins.war のような実行可能 war ファイル作りたい

Last updated at Posted at 2013-05-13

Jenkins (Hudson)が配布している war ファイルは2つの使い方がある。

  • サーブレットコンテナに読み込ませて war ファイルとして利用
  • $ java -jar jenkins.war ように単体で実行

この2つを実現するための簡単なサンプルを作ったので、実現するための要所を簡単に書く。

サンプルのビルド方法

まず実際に動いているのを確認してもらうために、ビルド・実行してほしい。

このサンプルは、比較のために Jetty, Winstone, Tomcat, Glassfish を使って、実行可能 war を構築する。

ビルドには Maven 3 が必要。

git clone git@github.com:kui/executable-war-sample.git
cd executable-war-sample
mvn package
ls **/sample.war
java -jar winstone/target/sample.war

実行可能 war を作るためのポイント

やるべきことは大体3つ:

  • 依存関係にサーブレットコンテナを追加
  • Main-Class の作成・指定・コピー
  • サーブレットコンテナの関連クラスのコピー

Jetty を使った実行可能 war サンプルを例に説明していきます。基本的にはどのコンテナも同じです。

依存関係にサーブレットコンテナを追加

実行時に必要ですが、後述する通り自力でコピーしてしまうので、scope は provided で大丈夫:

pom.xml
<project >
  ...
  <dependencies>
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-webapp</artifactId>
      <version>9.0.2.v20130417</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

Main-Class の作成

サーブレットコンテナを立ち上げて、自身を war ファイルとして読み込ませるようにする。例えば Jetty だとこんな感じ:

Main.java
package jp.k_ui.sample;

import java.net.URL;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;

public class Main {
    public static void main(String[] args) throws Exception {
        int port = Integer.parseInt(System.getProperty("port", "8888"));
        URL warLocation = Main.class.getProtectionDomain().getCodeSource()
                .getLocation();

        WebAppContext webapp = new WebAppContext();
        webapp.setWar(warLocation.toExternalForm());
        webapp.setContextPath("/");

        Server server = new Server(port);
        server.setHandler(webapp);
        server.start();
        server.join();
    }
}

このまま $ mvn package 実行して sample.warを作ったとしても、$ java -jar sample.war の時に、どのクラスを実行すればよいかわからない。なので、次の設定をする必要がある。

Main-Class の指定

どのクラスの #main(String[]) を呼べば良いのか、という指定をする設定:

<project >
  <build>
    ...
    <plugins>
      ...
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.3</version>
        <configuration>
          <archive>
            <manifest>
              <mainClass>jp.k_ui.sample.Main</mainClass>
            </manifest>
          </archive>
        </configuration>
      </plugin>

この状態で $ mvn package もできるし、$ java -jar sample.jar で実行も可能。

ただし、$ unzip -l sample.jar で見るとわかるとおり、jp.k_ui.sample.Main が無いため実行は失敗する。これをコピーする設定を書く。

Main-Class のコピー

上で梱包されていないことが分かった Main クラスをコピーする設定:

<project >
  <build>
    ...
    <plugins>
      ...
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.7</version>
        <executions>
          <execution>
            <id>main-class-placement</id>
            <phase>prepare-package</phase>
            <configuration>
              <tasks>
                <move todir="${project.build.directory}/${project.build.finalName}/">
                  <fileset dir="${project.build.directory}/classes/">
                    <include name="jp/k_ui/sample/Main.class" />
                  </fileset>
                </move>
              </tasks>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

Apache Ant を使って、war 化する前のディレクトリに Main.class をコピーしている。

この状態で java -jar sample.war を実行すると、Main 見つからないという文句は消える代わりに、Jetty 関連のクラスが見つからないと言ってくる。

これは先程同様、unzip -l sample.jar で中身を見てわかるとおり、サーブレットに関わるクラスが war に梱包されていないのが問題になっている。

サーブレットコンテナの関連クラスのコピー

サーブレットコンテナに関わるクラス全てを、これから war 化する前のディレクトリにコピーする設定:

<project >
  <build>
    ...
    <plugins>
      ...
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.7</version>
        <executions>
          <execution>
            <id>jetty-classpath</id>
            <phase>prepare-package</phase>
            <goals>
              <goal>unpack-dependencies</goal>
            </goals>
            <configuration>
              <includeGroupIds>org.eclipse.jetty,javax.servlet</includeGroupIds>
              <excludes>META-INF/ECLIPSEF.*</excludes>
              <outputDirectory>
                ${project.build.directory}/${project.build.finalName}
              </outputDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>
      ...

これでおわり

$ mvn clean package で、jenkins.war のような、実行可能 war が作成できるようになりました。

なんだか意外と結構な文章量になってしまった。。。

各コンテナの比較は、github の README でやってます。

この記事は、jenkins.war みたいな実行可能な war ファイルの作成 - 電卓片手に で書いた内容とほとんど同じだが、差分ができたのでこっちにも書いてみた

47
46
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
47
46