JBossをAntで起動/停止する

In-Containerなテストを行う場合など,テストの実施のためにJBossを起動しておくことが必要な場合がある.そういう場合に,AntからJBossの起動/停止を含めてテストスイートの呼び出しを行えるようにしておくと,テストの手間を大幅に減らすことが出来,テストの自動化もできるようになる.

Antの達人の方々からすれば対したTipsではないが,うちのようなAntの初心者の方にとっては少しは参考になるかもと思い,build.xmlのサンプルを紹介したい.

JBossをAntから起動する方法の例

<property name="jboss.home" value="${env.JBOSS_HOME}"/>

<target name="startJBoss">
    <exec dir="${jboss.home}/bin" executable="run.sh" spawn="true">
        <arg value="-c default"/>   
    </exec>      
    <waitfor>
        <http url="http://localhost:8080/jmx-console"/>
    </waitfor>
    <echo message="JBoss started"/>
</target>

この例ではexecタスクを使ってrun.shを直接呼び出している.

spawn=trueでAntとは別プロセスで呼び出し.そうじゃないとrun.shはJBossが停止するまで戻らないのでAntが固まる.

別プロセスによる非同期呼び出しをしているのでwaitforタスクでJBossの起動が完了するまで待つようにしている.JBossの起動をどのように判定すべきかはもしかするともっと良い手があるかもしれないのだが,ここではJBossの起動シーケンスの最後のほうに起動するTomcatへのHTTPリクエストの受け付けを確認することで行っている.

例えばEJBのテストなどをする場合,8080番ポートへのHTTPリクエストが成功する段階では必ずEJBのデプロイが完了しているのでこれでいける.

次は,JBossをAntから停止する方法の例

<property name="jboss.home" value="${env.JBOSS_HOME}"/>

<target name="stopJBoss">
    <exec dir="${jboss.home}/bin" executable="shutdown.sh">
        <arg value="--shutdown"/>
    </exec>
    <waitfor>
        <not>
            <http url="http://localhost:8080/jmx-console"/>
        </not>
    </waitfor>
    <echo message="JBoss stopped"/>
</target>

やってることの原理は先ほどと同じ.

さて,ここまでが第一段階.このコードを書いたbuild.xmlを使ってtarget要素のdepends属性とかでテストスイート呼び出し用タスクを挟んであげたりすれば「起動→テスト実施→停止」とかができる.

データソース(mysql-ds.xmlなど)やEJBそのもの(jbossxatest.jar)のデプロイ/アンデプロイとかも挟みたい場合は以下のようにCopyタスクとかDeleteタスクを使えばよい.

<target name="deploy" description="Deploy jbossxatest.jar and mysql-ds.xml">
    <copy file="build/${deploy.file}" todir="${deploy.dir}"/>
    <copy file="mysql-ds.xml" todir="${deploy.dir}"/>
</target>

<target name="undeploy" description="Undeploy the EJB jar file and mysql-ds.xml">
    <delete file="${deploy.dir}/${deploy.file}"/>
    <delete file="${deploy.dir}/mysql-ds.xml"/>
</target>

しかし,このままでは1つ問題がある.Unix系OSではこのbuild.xmlは動作するけれども,Windows上では動かない.

Windows上でのJBossの起動スクリプトは"run.bat",拡張子が違うだけ.

幸いにしてAntには動作中のOSを判定する機能がある.

<condition property="os" value="win">
    <os family="windows"/>
</condition>

<condition property="os" value="unix">
    <not>
        <os family="windows"/>
    </not>
</condition>

Conditionタスクを使ってbuild.xml内で使う変数"os"にOSの種類を突っ込んでいる.ここの部分を直接拡張子に突っ込むようにする手もあるように見えるのだが,結論を先に言っておくと,最初に書いたbuild.xmlのコードの"run.sh"を"run.bat"に変えてもWindows上では動かない.

AntのexecタスクはRuntimeクラスのexecメソッドの呼び出しに委譲されるわけだが,Javaからバッチファイルを呼び出す場合,ちゃんと"CMD.exe"を指定して,バッチファイル名はその引数として与えてあげないとならないためだ.この注意点はAntのWEBサイトにも載っている.
http://ant.apache.org/faq.html#batch-shell-execute

そこでWindows用としては以下のようなものとなる.

<target name="startJBoss-win">
    <exec dir="${jboss.home}/bin" executable="cmd" spawn="true">
        <arg value="/c run.bat"/>
    </exec>
    <waitfor>
        <http url="http://localhost:8080/jmx-console"/>
    </waitfor>
    <echo message="JBoss started"/>
</target>  

<target name="stopJBoss-win">
    <exec dir="${jboss.home}/bin" executable="cmd">
        <arg line="/c shutdown.bat --shutdown"/>
    </exec>
    <waitfor>
        <not>
            <http url="http://localhost:8080/jmx-console"/>
        </not>
    </waitfor>
    <echo message="JBoss stopped"/>
</target>  

後は,Antが呼ばれたときにOS(Unix系かWin系か)を判断して適切に処理するようにしてあげるだけ.Antcallタスクで呼び出すtarget名を動的に決めるようにするなど.全部あわせるとこんな感じでいける.

<project name="xasimpletest" default="test">

<property environment="env" />
<property name="jboss.home" value="${env.JBOSS_HOME}"/>
<property name="deploy.dir" value="${jboss.home}/server/default/deploy"/>
<property name="deploy.file" value="jbossxatest.jar"/>
<property name="mysql.home" value="${env.MYSQL_HOME}"/>

<path id="class.path">
    <fileset dir="build">
        <include name="jbossxatest.jar"/>
    </fileset>
    <fileset dir="..">
        <include name="jbossall-client.jar"/>
        <include name="junit.jar"/>
        <include name="jboss-j2ee.jar"/>
        <include name="mysql-connector-java-5.0.0-beta-jver-20050822-01-bin.jar"/>
    </fileset>   
</path>

<condition property="os" value="win">
    <os family="windows"/>
</condition>

<condition property="os" value="unix">
    <not>
        <os family="windows"/>
    </not>
</condition>

<target name="test" depends="deploy,startMySQL,startJBoss,runTest,stopJBoss,stopMySQL,undeploy"
    description="Run the deploy,startMySQL,startJBoss,runTest,stopJBoss,stopMySQL,undeploy"/>

<target name="runTest" description="Invoke the testsuite">
    <echo message="Starting the test JBossXATestClient"/>
    <java classname="testsuite.integration.xa.simple.jboss.JBossXATestClient" classpathref="class.path"/>
    <echo message="The test finished"/>
</target>

<target name="deploy" description="Deploy jbossxatest.jar and mysql-ds.xml">
    <copy file="build/${deploy.file}" todir="${deploy.dir}"/>
    <copy file="mysql-ds.xml" todir="${deploy.dir}"/>
</target>

<target name="undeploy" description="Undeploy the EJB jar file and mysql-ds.xml">
    <delete file="${deploy.dir}/${deploy.file}"/>
    <delete file="${deploy.dir}/mysql-ds.xml"/>
</target>

<target name="startMySQL" description="Start the MySQL Server">
    <exec dir="${mysql.home}/bin" executable="mysqld" spawn="true">
        <arg value="--basedir=${mysql.home}"/>
        <arg value="--datadir=${mysql.home}/data"/>    
    </exec>      
    <waitfor>
        <socket server="localhost" port="3306"/>
    </waitfor>
    <echo message="MySQL started"/>
</target>

<target name="stopMySQL" description="Stop the MySQL Server">
    <exec dir="${mysql.home}/bin" executable="mysqladmin">
        <arg line="-uroot shutdown"/>
    </exec>
    <waitfor>
        <not>
            <socket server="localhost" port="3306"/>
        </not>
    </waitfor>
    <echo message="MySQL stopped"/>
</target>

<target name="startJBoss" description="Start the JBoss Server">
    <antcall target="startJBoss-${os}"/>
</target>   

<target name="startJBoss-unix">
    <exec dir="${jboss.home}/bin" executable="run.${exten}" spawn="true">
        <arg value="-c default"/>   
    </exec>      
    <waitfor>
        <http url="http://localhost:8080/jmx-console"/>
    </waitfor>
    <echo message="JBoss started"/>
</target>

<target name="startJBoss-win">
    <exec dir="${jboss.home}/bin" executable="cmd" spawn="true">
        <arg value="/c run.bat"/>
    </exec>
    <waitfor>
        <http url="http://localhost:8080/jmx-console"/>
    </waitfor>
    <echo message="JBoss started"/>
</target>   

<target name="stopJBoss" description="Stop the JBoss Server">
    <antcall target="stopJBoss-${os}"/>
</target>   

<target name="stopJBoss-unix">
    <exec dir="${jboss.home}/bin" executable="shutdown.${exten}">
        <arg value="--shutdown"/>
    </exec>
    <waitfor>
        <not>
            <http url="http://localhost:8080/jmx-console"/>
        </not>
    </waitfor>
    <echo message="JBoss stopped"/>
</target>

<target name="stopJBoss-win">
    <exec dir="${jboss.home}/bin" executable="cmd">
        <arg line="/c shutdown.bat --shutdown"/>
    </exec>
    <waitfor>
        <not>
            <http url="http://localhost:8080/jmx-console"/>
        </not>
    </waitfor>
    <echo message="JBoss stopped"/>
</target>   
</project>

元ネタはXDocletを使ったテストアプリのビルド用のスクリプトも入ってて量が多かったので適当に端折りました.ついでにMySQLの起動・停止のも入ってます.

こうしておくと、あとは以下のようにやるだけで"デプロイ,MySQL起動,JBoss起動,テスト実施,JBoss停止,MySQL停止,アンデプロイ"をやってくれます.

Shell> ant
Buildfile: build.xml

deploy:
    [copy] Copying 1 file to /usr/local/jboss/server/default/deploy
    [copy] Copying 1 file to /usr/local/jboss/server/default/deploy

startMySQL:
    [echo] MySQL started

startJBoss:

startJBoss-unix:
    [echo] JBoss started

runTest:
    [echo] Starting the test JBossXATestClient
    [java] .....
    [java] Time: 2.907

    [java] OK (5 tests)

    [echo] The test finished

stopJBoss:

stopJBoss-unix:
    [exec] Shutdown message has been posted to the server.
    [exec] Server shutdown may take a while - check logfiles for completion
    [echo] JBoss stopped

stopMySQL:
    [echo] MySQL stopped

undeploy:
  [delete] Deleting: /usr/local/jboss/server/default/deploy/jbossxatest.jar
  [delete] Deleting: /usr/local/jboss/server/default/deploy/mysql-ds.xml

test:

BUILD SUCCESSFUL
Total time: 50 seconds

とまあ自分が書いたbuild.xmlをこうやって晒してみたものの,脱Ant初心者中なのでここはこうしたほうが良いとか結構あるかもしれません.添削してくれる心やさしい方がいましたら,コメント欄まで足をお運びいただけますと幸いです・・・.