6. プロファイル
2009.04.03 シナジーマーケティング株式会社 鈴木 圭
- 6.1. プロファイルとは
 - 6.2. プロファイルの種類
 - 6.3. プロファイルの書き方
 - 6.4. プロファイルの有効化
 - 6.5. Tips
 - 6.6. まとめ
 
今回は、環境依存の設定を記述するためのプロファイルという機能について解説します。プロファイル機能を活用することで、プロジェクトのビルドをより安全かつポータブルにすることができます。
6.1. プロファイルとは
プロファイルとは、ビルド環境やリリース環境などの環境に依存する設定を記述するためのものです。環境に依存する設定には、次の二種類が考えられます。
- リリース環境によって異なる設定(接続先データベースやログファイルの出力先)
 - ビルド環境によって異なる設定(OS の種類やアプリケーションサーバのインストール先)
 
環境依存の設定を上手く扱えない場合、以下のような問題が発生します。
- 複数人が参加するプロジェクトにおいて、開発者はそれぞれの環境に合うように設定ファイルを修正しなければならない。
 - 普段は開発用にビルドしているから問題無いが、本番用にビルドする場合は設定ファイルを本番環境用に修正しなければならない。
 
このような問題に対応するために Maven に用意されている機能がプロファイルです。
6.2. プロファイルの種類
プロファイルは以下のファイルに記述することができます。
| ファイル | 説明 | 
|---|---|
| pom.xml | プロジェクトの pom.xml に記述されたプロファイルは、そのプロジェクトだけで利用可能となります。 | 
| profiles.xml | profiles.xml は pom.xml からプロファイルの設定だけを抜き出したファイルです。profiles.xml に記述されたプロファイルは、そのプロジェクトだけで利用可能となります。注意として、本章執筆時点では profiles.xml に記述したプロファイルがデフォルトで有効になってしまうという不具合があります(http://jira.codehaus.org/browse/MNG-2605)。回避策としては、後述する activation/activeByDefault 要素に明示的に false を指定するなどが考えられます。 | 
| %USER_HOME%/.m2/settings.xml | %USER_HOME%/.m2/settings.xml に記述されたプロファイルは、そのユーザだけで利用可能となります。 | 
| %M2_HOME%/conf/settings.xml | %M2_HOME%/conf/settings.xml に記述されたプロファイルは、Maven を利用する全ユーザで利用可能となります。 | 
プロファイルをどのファイルに記述するかについてですが、「なるべく pom.xml に記述する」「無理な場合は settings.xml に記述する」というスタンスが良いと考えます。settings.xml に記述するということは、プロジェクトに新しく参加する人も同様に settings.xml にプロファイルを記述しなければならないためです。プロジェクトのビルドをよりポータブルにするという観点では、pom.xml に記述する方が良いでしょう。
6.3. プロファイルの書き方
プロファイルは、settings.xml に記述する場合は settings/profiles/profile 要素内に、pom.xml の場合は project/profiles/profile 要素内に記述します。profile 要素は必要な数だけ記述します。以下に pom.xml への記述例を示します。
<project ...>
    ...
    <profiles>
        <profile>
            <id><プロファイルの ID></id>
            <activation>
                <プロファイルが有効化する条件>
            </activation>
            <このプロファイルでの設定>
        </profile>
        <profile>
            ...
        </profile>
    </profiles>
    ...
</project>
<プロファイルの ID>
「<プロファイルの ID>」には、そのプロファイルを識別するためのユニークな値を指定します。他のプロファイルと重複しない、任意の値を指定することができます。どのような場合に使用するプロファイルなのか分かりやすい値を指定すると良いでしょう。
<プロファイルが有効化する条件>
「<プロファイルが有効化する条件>」には、どういう場合にプロファイルが有効となるのか、という条件を記述します。プロファイルが有効化される条件には以下のものがあります(詳しくは後述します)。
- コマンドライン引数
 - settings.xml の activeProfiles 要素
 - デフォルト
 - JDK のバージョン
 - OS の種類
 - システムプロパティ
 - ファイルの有無
 
プロファイルを有効化するときは常にコマンドライン引数で指定する、という場合は、この項目を省略することが出来ます。
<このプロファイルでの設定>
「<このプロファイルでの設定>」には、このプロファイルが有効化されたときに使用される設定を記述します。記述できる内容は pom.xml に記述できる内容のサブセットです。以下に、profile 要素の子要素として記述できる要素を示します。
| 要素 | 説明 | 
|---|---|
| repositories | ライブラリのリポジトリ | 
| pluginRepositories | プラグインのリポジトリ | 
| dependencies | ライブラリの設定 | 
| plugins | プラグインの設定 | 
| properties | プロパティの設定 | 
| modules | モジュールの設定 | 
| reporting | レポートの設定 | 
| dependencyManagement | 依存性管理の設定 | 
| distributionManagement | 配備管理の設定 | 
| build/defaultGoal | デフォルトのゴール | 
| build/resources | リソースの設定 | 
| build/testResources | テスト用リソースの設定 | 
| build/finalName | 成果物の名前 | 
6.4. プロファイルの有効化
前述したとおり、プロファイルを有効化する方法には以下のものがあります。
- コマンドライン引数
 - settings.xml の activeProfiles 要素
 - デフォルト
 - JDK のバージョン
 - OS の種類
 - システムプロパティ
 - ファイルの有無
 
これらを大きく分けると、有効化するプロファイルを明示的に指定する方法と、特定の条件が成立した場合に有効化されるように指定する方法があります。前者は「コマンドライン引数」と「settings.xml の activeProfiles 要素」、それ以外は後者です。リリース環境に関係するプロファイルは明示的に指定するように、ビルド環境に依存する設定は特定の条件が成立した場合に有効化されるように設定すると良いでしょう。
コマンドライン引数
mvn コマンドの -P オプションで有効化するプロファイルを指定することができます。複数のプロファイルを指定する場合は、カンマ(「,」)で区切って指定します(カンマの前後にスペースを含めるとエラーとなりますのでご注意ください)。
以下の例は、profile-1 と profile-2 を有効化します。
mvn -P profile-1,profile-2
また、Maven 2.1.0(もとは 2.0.10)からはプロファイルを無効化する方法が提供される予定です。プロファイル名の先頭にエクスクラメーション・マーク(!)またはマイナス記号(-)を付加することで、特定のプロファイルを無効化することができます。Maven 2.1.0 は本章執筆時点ではプレビュー版しか公開されていませんが、この機能は既に実装されているようです。
settings.xml の activeProfiles 要素
常に有効化しておきたいプロファイルがある場合は、settings.xml の activeProfiles/activeProfile 要素に記述します。以下に例を示します。
<settings>
  ...
  <activeProfiles>
    <activeProfile>alwaysActiveProfile</activeProfile>
    <activeProfile>anotherAlwaysActiveProfile</activeProfile>
  </activeProfiles>
  ...
</settings>
settings.xml に記述した設定は複数のプロジェクトで有効となるので、扱いには注意が必要です。
デフォルト
デフォルトのプロファイルは、他のプロファイルが一つも有効化されなかった場合に有効となります。デフォルトのプロファイルにするには、以下のように activeByDefault 要素に true を指定します。
<profile>
  <id>profile-1</id>
  <activation>
    <activeByDefault>true</activeByDefault>
  </activation>
  ...
</profile>
例えば、開発用プロファイルと本番用プロファイルの二つを定義するとします。そして、普段は開発用プロファイルを使用する(本番用プロファイルは本番リリースのときにしか使用しない)のであれば、開発用プロファイルに「<activeByDefault>true</activeByDefault>」と指定しておくと良いでしょう。
JDK のバージョン
JDK のバージョンによってプロファイルを有効化する場合は、以下のように記述します。
<profile>
  <id>profile-1</id>
  <activation>
    <jdk>1.6</jdk>
  </activation>
  ...
</profile>
JDK のバージョンは「javac -version」や「mvn -v」で確認できます。jdk 要素に指定した値と JDK のバージョンの先頭が一致すればプロファイルは有効化されます。例えば、jdk 要素に 1.6 を指定した場合は、JDK のバージョンが 1.6.0_05 や 1.6.0_06 の場合もプロファイルは有効化されます。
OS の種類
OS の種類によってプロファイルを有効化する場合は、以下のように記述します。
<profile>
  <id>profile-1</id>
  <activation>
    <os>
      <name>windows vista</name>
      <family>windows</family>
      <arch>x86</arch>
      <version>6.0</version>
    </os>
  </activation>
    ...
</profile>
os 要素の子要素に指定できる要素は以下の通りです。
| 要素 | 説明 | 
|---|---|
| name | OS の名前 | 
| family | OS の種類 | 
| arch | CPU のアーキテクチャ | 
| version | OS のバージョン | 
これらの値は任意の組み合わせて指定することができ、複数指定した場合は各条件が全て成立した場合に、プロファイルが有効化されます。OS の情報は「mvn enforcer:display-info」や「mvn -v」で確認することができます。
システムプロパティ
システムプロパティが定義されているかどうか、またはシステムプロパティの値によってプロファイルを有効化する場合は、以下のように記述します。
<profile>
  <id>profile-1</id>
  <activation>
    <property>
      <name>env</name>
      <value>debug</value>
    </property>
  </activation>
  ...
</profile>
「特定の名前のシステムプロパティが定義されていれば、その値は何でも良い」という場合は value 要素を省略します。
ファイルの有無
ファイルの有無によってプロファイルを有効化する場合は、以下のように記述します。
<profile>
  <id>profile-1</id>
  <activation>
    <file>
      <exists>/path/to/file</exists>
      <missing>/path/to/file</missing>
    </file>
  </activation>
  ...
</profile>
特定のファイルが存在する場合に有効化する場合は exists 要素を、特定のファイルが存在しない場合に有効化する場合は missing 要素を使用します。exists 要素と missing 要素の両方を指定した場合は、両方の条件が成立した場合に有効化されます。
有効なプロファイルの確認
「mvn help:active-profiles」を実行すると、有効化されているプロファイルを確認することができます。これまで見てきたとおり、プロファイルを有効化する方法はいくつもあります。そのため、実際にどのプロファイルが有効になっているのか分かりづらくなることもあります。その場合は、設定ファイルを確認するよりも「mvn help:active-profiles」を利用すると便利です。
複数のプロファイルでの設定項目がコンフリクトした場合の動作
複数のプロファイルでの設定項目がコンフリクトしている場合の動作についてです。例えば、profile-A と profile-B というプロファイルがあり、どちらも value というプロパティを定義しているとします。設定としては以下のような状態です。
<profile>
  <id>profile-A</id>
  <properties>
    <value>AAA</value>
  </properties>
</profile>
<profile>
  <id>profile-B</id>
  <properties>
    <value>BBB</value>
  </properties>
</profile>
このとき、profile-A と profile-B が同時に有効化されたらどうなるのでしょうか。profile-A で定義されている value と profile-B で定義されている value のどちらが有効になるのか、という問題です。
いくつかのパターンで試したところ、profiles.xml の記述より pom.xml の記述が優先される、pom.xml 内では最後に定義されているものが優先される、という動作になりました(Maven 2.0.9 で確認)。ただし、Maven の公式サイトにプロファイルの設定内容がコンフリクトした場合の動作についての記述は見つけられませんでしたので、あくまで現在の実装ではそうなると考えたほうが良さそうです。複数のプロファイルで設定内容がコンフリクトしないように気をつけている方が無難でしょう。
6.5. Tips
プロファイルに関する説明は一通り行いましたので、ここからはプロファイルに関する Tips をご紹介します。
プロファイルによって設定を変更するアプローチ
プロファイルによって設定を変更するアプローチは二つあります。
- profile/properties 要素でのプロパティ定義とフィルタ機能を組み合わせる方法
 - 環境の数だけ設定ファイルを用意し、それを有効化されたプロファイルによって変更する方法
 
前者の「profile/properties 要素でのプロパティ定義とフィルタ機能を組み合わせる方法」とは、環境による設定ファイルの差異が限定的な場合に有効なアプローチです。例として、log4j でのログの出力レベルを、本番環境では警告レベル以上、開発時は全てをログに出力する場合を考えます。プロファイルでは次のようにログの出力レベルを表すプロパティ(ここでは「log.level」)を定義します。
<!-- 開発環境用プロファイル -->
<profile>
  <id>development</id>
  <properties>
    <log.level>ALL</log.level>
  </properties>
</profile>
<!-- 本番環境用プロファイル -->
<profile>
  <id>production</id>
  <properties>
    <log.level>WARN</log.level>
  </properties>
</profile>
そして、log4j の設定ファイル log4j.properties には次のように記述します。
log4j.rootLogger=${log.level}, ...
...
このように設定ファイルの一部だけを変更すれば良い場合は、フィルタ機能を利用するアプローチが簡単です。
後者の「環境の数だけ設定ファイルを用意し、それを有効化されたプロファイルによって変更する方法」は、設定ファイルの一部を変更するだけでは対応しきれない場合や、環境依存の設定をプロパティで置き換えるには数が多すぎる場合に有効です。ここでは例として、本番環境用と開発環境用の設定ファイルをそれぞれ以下のディレクトリに置いたとします。
src/main/resources/development/ ... 開発環境用の設定ファイル src/main/resources/production/ ... 本番環境用の設定ファイル
そして、pom.xml では次のように記述します。
<project ...>
  ...
  <profiles>
    <!-- 開発環境用プロファイル -->
    <profile>
      <id>development</id>
      <properties>
        <resources.directory>${basedir}/src/main/resources/development</resources.directory>
      </properties>
    </profile>
    <!-- 本番環境用プロファイル -->
    <profile>
      <id>production</id>
      <properties>
        <resources.directory>${basedir}/src/main/resources/production</resources.directory>
      </properties>
    </profile>
  </profiles>
  <build>
    <resources>
      <resource>
        <directory>${resources.directory}</directory>
      </resource>
    </resources>
  </build>
  ...
</project>
こうすることで、開発環境用のプロファイル(development)が有効な場合は src/main/resources/development に置かれた設定ファイルが、本番環境用のプロファイル(production)が有効な場合は src/main/resources/production に置かれた設定ファイルが使用されるようになります。
開発者ごとに接続先 DB を変更する
複数の開発者が関わるプロジェクトで、開発用 DB が一つしかない場合、以下のような問題が発生します。
- 知らぬ間にデータが変更されてしまう
 - テーブル構造を変更するときに、他の開発者とタイミングを調整しなければならない
 
このような問題への解決策として、以下の対応が考えられます。
- 開発者ごとに専用の DB を用意する
 - プロファイルで自動的に各人専用の DB に接続するように設定する
 
このとき、DB の名前には OS の環境変数で設定されているユーザ名(Windows であれば %USERNAME%、Linux であれば $USER の値)を含めておきます。そして、プロファイルで接続先 DB を変更するように設定します。
<profile>
  ...
  <activation>
    <activeByDefault>true</activeByDefault>
  </activation>
  <properties>
    <jdbc.url>jdbc:postgresql://hostname:5432/myapp.${env.USERNAME}</jdbc.url>
    <jdbc.driverClassName>org.postgresql.Driver</jdbc.driverClassName>
    <jdbc.username>username</jdbc.username>
    <jdbc.password></jdbc.password>
  </properties>
  ...
</profile>
こうすることで、各開発者は専用の DB で自由に動作確認することができるようになります。また、上記設定例では activeByDefault 要素に true を指定しています。こうすることで、プロファイルの存在を意識しなくても各自の DB にアクセスできるようになります。
6.6. まとめ
今回は、Maven で環境依存の設定を扱うための「プロファイル」という機能について解説しました。「環境依存の設定」はほとんど必ず存在するものですので、プロファイルによって上手く扱い、よりポータブルなビルドを実現しましょう。

