Редеплойинг приложения на Tomcat средствами Ant

Сб, 15 Сен 2007 at 1238.27 (j2ee, java)

This acrticle at wikidot.com

J2EE Apache Tomcat Apache Ant

Процесс разработки нашего проекта обладает одним минусом, свойственным многим J2EE-проектам: при изменении кода проекта и пересборке его сервер не сразу подхватывает обновку, а требует полного останова себя, очистки кэша, запуска себя по-новой и передеплойинга пакета.

Для того чтобы не делать это каждый раз ручками есть несколько простых способов: например, те же скрипты (batch’и для windows и shell-скрипты для linux). Но мне показалось более простым сделать так, чтобы делал это сам собирающий ant-скрипт (сценарий?): одно нажатие клавиши и все просходит автоматически…

(версия для windows)

Поэтому я взял скрипт существующий и стал его править. Пришлось столкнуться с несколькими проблемами/хитростями ant’а на windows (опытному в ant’о-строении человеку они конечно нипочем :) ), в результате чего получился рабочий скрипт, выдержки из которого я и разберу ниже.

Вот – файл build.properties. Он содержит некоторые значения, которые возможно будут часто меняться и поэтому лучше хранить их отдельно от ant-срипта.

# имя пакета
war.name = SomeProjectPackage

# путь к JDK
java.home = "C:WorktableJavajdk1.5.0_12"
# путь к корневому каталогу сервера
server.dir = C:/Worktable/Java/apache-tomcat-5.5.25
# скрипт, использующийся для запуска сервера
server.command = catalina.bat

# путь к расположению проекта
root.dir = C:/Workspace/SomeProject
# путь к месту, куда на сервере будет выкладываться пакет
deploy.dir = ${server.dir}/webapps/

# настройки JPDA (удаленнный дебаггинг может осуществляться
# (например, средствами Eclipse) подключением к указанному порту)
jpda.transport = dt_socket
jpda.port = 56666

# путь к библиотекам проекта
lib.dir = ${root.dir}/lib/
# путь к временному месту сборки проекта
dist.dir = ${root.dir}/dist/
# путь к каталогу с веб-содержимым - страницами, скриптами и т.п.
web.dir = ${root.dir}/WebContent/

Теперь по частям рассмотрим сам скрипт. В заголовке – включаем наш файл .properties.

<?xml version="1.0" encoding="UTF-8"?>

    <project name="SomeProject" default="redeploy" basedir=".">
    <property file="build.properties"/>

    . . .

Далее идут цели сборки (build), очистки временных каталогов, использованных при сборке (clean) и цель пересборки – очищающая, а затем собирающая (rebuild). Их подробное рассмотрение не относится к цели статьи :).

    . . .

    <!-- Compiles project with all dependencies. -->

    <target name="build"
            description="--> compiles project with all dependencies">
        <mkdir dir="${dist.dir}"/>
        <mkdir dir="${dist.dir}/classes"/>
        <javac source="1.5"
            srcdir="${root.dir}/src"
            destdir="${dist.dir}/classes"
            debug="on"
            verbose="false"
            optimize="on">
            <classpath>
                <fileset dir="${lib.dir}" includes="**/*.jar"/>
            </classpath>
        </javac>
    </target>

    <!-- Cleans the build. -->

    <target name="clean"
            description="--> cleans the build">
        <delete quiet="true" dir="${dist.dir}"/>
    </target>

    <!-- Rebuild. -->

    <target name="rebuild" depends="clean,build"
            description="--> [clean, build]"/>

    . . .

А вот, собственно, наши подчиненные – цели деплойинга (выкладывания нового пакета на сервер), де-деплойинга (забирания старого пакета с сервера) и пере-деплойинга (забирания старого, а потом выкладывания нового).

При выкладывании (deploy) мы компилируем код (depends="build"), затем создаем на сервере каталог для логов, собираем пакет из скомпилированных исходников (командой jar), выкладывая его во временный каталог, а затем запускаем сервер. Для Windows сервер из ant-скрипта может быть запущен только в своем окружении, для этого приходится вызывать его командой cmd /c catalina.bat jpda start, через команду ant’а exec (аргументы должны быть разделены командами arg именно так, как представлено ниже – для того чтобы exec обернул команду catalina jpda start в кавычки, для ее целостности). Также серверу нужно передать несколько переменных окружения, что мы и делаем, используя команды env. Сервер мы запускаем в отдельном потоке (spawn="true" – иначе скрипт будет ожидать от сервера команды завершения и не будет производить дальнейших действий) и в чистом виде (не через ява-машину – vmlauncher="false"). Сервер запущен, можно выложить туда пакет и удалить временные каталоги и файлы (последовательность команд copy и двух delete).

Для выгрузки пакета с сервера (undeploy) мы останавливаем серевер по правилам, описанным выше (при остановке мы можем не указывать переменные окружения для JPDA и подолждать пока сервер остановится; но если что-то не вышло – это нормально – возможно сервер и не был запущен (failifexecutionfails="false")). Затем мы очищаем каталог на сервере, в которые он распаковывал наш пакет, удаляем сам пакет и очищаем кэш сервера.

При перевыладке (redeploy) – цели по умолчанию – старая версия пакета удаляется с сервера (undeploy), очищаются временные каталоги (clean), и пакет собирается и выкладывается на сервер (deploy).

    . . .

    <!-- Prepares deployment -->

    <target name="pre-deploy">
        <mkdir dir="${dist.dir}/war"/>
        <mkdir dir="${dist.dir}/war/WEB-INF"/>
        <mkdir dir="${dist.dir}/war/WEB-INF/classes"/>
        <mkdir dir="${dist.dir}/war/WEB-INF/lib"/>
        <copy todir="${dist.dir}/war">
            <fileset dir="${web.dir}">
                <include name="**/*.*"/>
            </fileset>
        </copy>
        <copy todir="${dist.dir}/war/WEB-INF/classes">
            <fileset dir="${dist.dir}/classes">
                <include name="**/*.*"/>
            </fileset>
        </copy>
        <copy todir="${dist.dir}/war/WEB-INF/lib" flatten="true">
            <fileset dir="${lib.dir}">
                <include name="**/*.jar"/>
            </fileset>
        </copy>
    </target>

    <!-- Deploys application on server. -->

    <target name="deploy" depends="rebuild, pre-deploy"
            description="--> deploys application on server">
        <mkdir dir="${server.dir}/logs"/>
        <jar jarfile="${war.name}.war" basedir="${dist.dir}/war"/>
        <exec dir="${server.dir}/bin" executable="cmd"
                vmlauncher="false" spawn="true">
            <env key="JAVA_HOME" value="${java.home}"/>
            <env key="JPDA_TRANSPORT" value="${jpda.transport}" />
            <env key="JPDA_ADDRESS" value="${jpda.port}" />
            <env key="CATALINA_HOME" value="${server.dir}"/>
            <arg value="/c" />
            <arg value="${server.command} jpda start"/>
        </exec>

        <copy file="${war.name}.war" todir="${deploy.dir}"/>
        <delete dir="${dist.dir}" failonerror="false" />
        <delete file="${war.name}.war" failonerror="false" />
    </target>

    <!-- Un-deploys application from server. -->

    <target name="undeploy"
            description="--> un-deploys application from server">
        <exec dir="${server.dir}/bin" executable="cmd"
                failifexecutionfails="false" vmlauncher="false">
            <env key="JAVA_HOME" value="${java.home}"/>
            <env key="CATALINA_HOME" value="${server.dir}"/>
            <arg value="/c" />
            <arg value="${server.command} stop"/>
        </exec>

        <delete quiet="true">
            <fileset dir="${deploy.dir}">
                <include name="${war.name}*"/>
            </fileset>
        </delete>

        <delete dir="${deploy.dir}/${war.name}" failonerror="false"/>
        <delete file="${deploy.dir}/${war.name}.war" failonerror="false" />
        <delete dir="${server.dir}/work/Catalina" failonerror="false" />
    </target>

    <!-- Re-deploys application on server. -->

    <target name="redeploy"
            depends="undeploy,clean,deploy"
            description="--> [undeploy,clean,deploy]">
    </target>

    </project>

</xml>

Собственно, все :)

P.S. Теперь основные мои статьи будут уходить, видимо, на realcoding.net. И, скорее всего, это будут циклы статей. Здесь, конечно же, я буду извещать вас о появлении их там.

Комментарии (3)

  1. Медвед сказал,

    Тут есть ньюанс(тм) – томкэт не умеет релоадить приложения по нормальному. наскоа я я помню – релоад если исключить распакоку сводится к подмене класс лоадера – т.е. регулярные релоады приложения могут приводить к самым неожиданным эффетам. ну и к утечкам памти естественно тоже…

  2. zapalyt сказал,

    izvenite no nam nado sdelat test
    vi ved ne bydete protiv admini

  3. shaman.sir сказал,

    Действительно, как мне подсказали, есть Tomcat Client Deployer – пакет, имеющий свои средства (в том числе таски) для деплоинга через Ant, но требующий сервер быть всегда запущенным.

    И конечно же, чтобы избежать новых претензий, есть дополнительные средства, облегчающие этот процесс вроде CruiseControl и так далее – при большом количестве проектов и действительно большой команде они бы, возможно, были бы хорошим решением (пока у меня такого опыта [к счастью?] нет).

Прокомментировать