Mavenのprofilesとmaven-war-pluginを利用して環境に依存しないwarファイルを作る

# はじめに
マルチモジュールプロジェクトでwebモジュールと環境設定モジュールを別にし環境に依存しないwarファイルを作ってTomcatで実行できるか試してみました。

* 環境設定とは今回はDBの接続先に限定します。つまり、開発時や検証環境、本番環境と用途ごとに接続先は解れるが、warファイルは同じものを利用できるようにすることが目標です。
* 考え方は[Terasoluna 3.5. 開発プロジェクトのビルド](http://terasolunaorg.github.io/guideline/5.3.0.RELEASE/ja/ImplementationAtEachLayer/CreateProject.html)の『envモジュールのjarファイルをwarファイルに含めないビルド方法』をベースにしています。
* ただし検証に利用するプロジェクトがレガシー過ぎるので、このプロジェクトで対応できる方法で考えました。
* "xxx"はwebアプリのartifactidです。またversionは0.0.1-SNAPSHOTなので生成されるjarも`xxx-env-0.0.1-SNAPSHOT.jar`となりますがVersion表記は省略しています。

# 目標
1. webモジュール内の環境設定ファイルを別モジュールに分ける
2. DBの接続先ごとに設定ファイルを作りprofileを利用してjarを作成する
3. Webモジュールに環境設定ファイルのpathを通す
4. webモジュールをTomcatにデプロイする

# 動作環境
* Windows7
* Maven 3.3.9
* JDK 1.7
* Tomcat 7
* Oracle 12.1.0.1
* IDE:Eclipse 4.7.0

※Tomcatのインストール先は、開発環境(IDEと同じPC)です。

# 1. webモジュール内の環境設定ファイルを別モジュールに分ける

環境設定ファイル`webapp/WEB-INF/applicationContext.xml`を`src/main/resources/META-INF`に移動しても動作することを確認するため、struts-config.xmlのプラグイン｢org.springframework.web.struts.ContextLoaderPlugIn｣の設定ファイルのパラメータで指定していた`/WEB-INF/applicationContext.xml`を`classpath*:/META-INF/applicationContext.xml`に変更したところ問題無く動作しました。

そこでマルチモジュールプロジェクト配下に、環境設定用のモジュールxxx-envを作成しapplicationContext.xmlを`src/main/resources/META-INF`に移動し、webモジュール側のdependencyにxxx-env.jarを追加し動作を確認しました。

これによりアプリケーションの環境依存部分を別モジュールに切り分けることができました。

# 2. DBの接続先ごとに設定ファイルを作りprofileを利用してjarを作成する

xxx-envモジュールに、local(開発)/staging(検証)/prod(本番)の3つを作成し異なるDB接続先を定義したapplicationContext.xmlを用意しました。
* local・・・・`src/main/resources/META-INF/applicationContext.xml`
* staging・・・`configs/staging/resources/META-INF/applicationContext.xml`
* prod・・・・`configs/prod/resources/META-INF/applicationContext.xml`

xxx-envモジュールのpom.xmlで以下のようなprofilesを定義しました。

``` pom.xml(env)
・・・
<build>
　　<finalName>${buildFinalName}</finalName>
　　<resources>
　　　　<resource>
　　　　　　<directory>${resource.directory}</directory>
　　　　</resource>
　　</resources>
</build>
<profiles>
　　<profile>
　　　　<id>local</id>
　　　　<activation>
　　　　　　<activeByDefault>true</activeByDefault>
　　　　</activation>
　　　　<properties>
　　　　　　<resource.directory>src/main/resources</resource.directory>
　　　　　　<buildFinalName>${project.artifactId}-${project.version}-dev</buildFinalName>
　　　　</properties>
　　</profile>
　　<profile>
　　　　<id>staging</id>
　　　　<properties>
　　　　　　<resource.directory>${basedir}/configs/staging/resources</resource.directory>
　　　　　　<buildFinalName>${project.artifactId}-${project.version}-staging</buildFinalName>
　　　　</properties>
　　</profile>
　　<profile>
　　　　<id>prod</id>
　　　　<properties>
　　　　　　<resource.directory>${basedir}/configs/prod/resources</resource.directory>
　　　　　　<buildFinalName>${project.artifactId}-${project.version}</buildFinalName>
　　　　</properties>
    </profile>
</profiles>
・・・
```

mvnコマンドで、PackageやInstallフェーズを実行時に、-PオプションでprofileのIDを指定すると対象のresourceでjarファイルが作成されます。
上記の設定の場合、`-P prod`なら本番環境用のxxx-env.jar、`-P staging`なら検証環境用のxxx-env-staging.jar、何も指定しない（デフォルト）は開発用のxxx-env-dev.jarが実行されます。

# 3. Webモジュールに環境設定ファイルのpathを通す 

Tomcat7のディレクトリ`opt/environment/app`にxxx-env.jarを置き、webappsのモジュールから参照できるようにします。
今回はWebモジュールのsrc/main/webapp/META-INF/context.xmlに、VirtualWebappLoaderを設定し、WebモジュールのClasspathにxxx-env.jarを含めるようにします。

``` src/main/webapp/META-INF/context.xml
<?xml version='1.0' encoding='utf-8'?>
<Context>
  <Loader className="org.apache.catalina.loader.VirtualWebappLoader" virtualClasspath="/opt/environment/logtime/prod/*.jar"/>
  <JarScanner scanAllDirectories="true"/>
</Context>
```

maven-war-pluginを実行してcontext.xmlをWARファイルに含めるように設定します。

``` pom.xml(web)
・・・
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-war-plugin</artifactId>
  <version>2.5</version>
  <configuration>
    <containerConfigXML>src/main/webapp/META-INF/context.xml</containerConfigXML>
  </configuration>
</plugin>
・・・
```

# 4. webモジュールをTomcatにデプロイする

環境設定を排除したWARファイルを作成し、Tomcatにデプロイします。
dependencyからxxxx-envを削除し、profilesでWAR作成時はxxxx-envを含めないように設定します。

``` pom.xml(Web)
・・・
<profiles>
  <profile>
    <id>local</id>
    <activation>
      <activeByDefault>true</activeByDefault>
    </activation>
    <dependencies>
      <dependency>
        <groupId>${project.groupId}</groupId>
        <artifactId>xxxx-env</artifactId>
	<version>0.0.1-SNAPSHOT</version>
      </dependency>
    </dependencies>
  </profile>
  <profile>
    <id>warpack</id>
  </profile>
</profiles>
・・・
```

上記により、'-P warpack'を指定した時だけ、xxx-envから依存性を排除します。
つまり、`mvn -P warpack clean package`とすると、xxx-env.jarが含まれないWARが作成されます。
※デフォルトでは、xxx-envへの依存性が定義されているので開発時などIDEで実行するときは参照します。

作成したWARをTomcatのwebappsに配備します。

# 動作確認とまとめ

Tomcatを起動しWebアプリケーションにアクセスすると、本番環境用のDBへアクセスすることが確認できました。
`opt/environment/app`のjarを`-P staging`で作成した、検証環境用のxxx-env-staging.jarに置き換え、Tomcatを再起動すると今度は検証環境用のDBにアクセスすることが確認できました。

こうすることで、例えば検証用と本番用でWARファイルが使いまわせるため、モジュールの同一性が保証でき、リリース間違いが減らせるようになります。

# 参考
[Terasoluna 3.5. 開発プロジェクトのビルド](http://terasolunaorg.github.io/guideline/5.3.0.RELEASE/ja/ImplementationAtEachLayer/CreateProject.html)
[War ファイルにコンテキスト固有の情報を設定する](http://qiita.com/hidekatsu-izuno/items/f2ffef67817627dfd48c)
[Apache Maven WAR Plugin](https://maven.apache.org/plugins/maven-war-plugin/index.html)
[[Maven] Maven2 で Apache Tomcat の context.xml を WAR に追加する方法](http://www.in-vitro.jp/blog/index.cgi/Maven/20080825_01.html)
[コンテキスト情報の記述場所](http://d.hatena.ne.jp/incarose86/20110905/1315228200)