Процесс разработки нашего проекта обладает одним минусом, свойственным многим J2EE-проектам: при изменении кода проекта и пересборке его сервер не сразу подхватывает обновку, а требует полного останова себя, очистки кэша, запуска себя по-новой и передеплойинга пакета.
Для того чтобы не делать это каждый раз ручками есть несколько простых способов: например, те же скрипты (batch’и для windows и shell-скрипты для linux). Но мне показалось более простым сделать так, чтобы делал это сам собирающий ant-скрипт (сценарий?): одно нажатие клавиши и все просходит автоматически…
(версия для windows)
Поэтому я взял скрипт существующий и стал его править. Пришлось столкнуться с несколькими проблемами/хитростями ant’а на windows (опытному в ant’о-строении человеку они конечно нипочем :) ), в результате чего получился рабочий скрипт, выдержки из которого я и разберу ниже.
Вот – файл build.properties
. Он содержит некоторые значения, которые возможно будут часто меняться и поэтому лучше хранить их отдельно от ant-срипта.
# имя пакета
war.name = SomeProjectPackage
# путь к JDK
java.home = "C:\Worktable\Java\jdk1.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. Действительно, как мне подсказывают, есть Tomcat Client Deployer – пакет, имеющий свои средства (в том числе таски) для деплоинга через Ant, но требующий сервер быть всегда запущенным.
И конечно же, чтобы избежать новых претензий, есть дополнительные средства, облегчающие этот процесс вроде CruiseControl и так далее - при большом количестве проектов и действительно большой команде они бы, возможно, были бы хорошим решением (пока у меня такого опыта [к счастью?] нет).
А ещё у Maven есть, например, war-плагин. Я об этом тоже знаю, правда-правда :).