J'ai mis un peu de temps à comprendre de façon sommaire de la façon dont fonctionne la deploy task sous ant, fournis par tomcat, alors autant en faire profiter tout le monde..
Edition 23.04 : il est à noter que j'ai ensuite découvert le Tomcat client deployer, téléchargeable pour les version 5.x et 6.x au moins. La documentation se trouve ici. La technique expliqué ci-dessous doit être similaire, sauf qu'elle est intégré à votre build.xml.
But
Le but de l'opération est assez simple : être en mesure, via une commande ant, de déployer un projet J2EE sur le Servlet Containre Tomcat. Pour aller un peu plus loin, je voulais aussi pouvoir avoir plusieurs profils de déploiement, ou autrement dit, pouvoir déployer le code sur différent Tomcat.
Préalable(s)
Certaines librairies en plus sont nécessaire au bon fonctionnement de ces tâches Ant. Nous utilisons les tasks de déploiement et les task ssh. Il est nécessaire d'ajouter les 2 librairies suivantes dans le classpath de Ant.
- catalina-ant.jar : provenant de Apache Tomcat. La librairie se trouve dans le répertoire Tomcat, puis server/lib/catalina-ant.jar.
- jsch-xxx.jar : permet l'utilisation des tâches sshexec et scp.
En temps normal, les librairies nécessaires sont dans ${basedir}/lib/ant, et le fichier build.xml les prend en considération. On verra dans la Mise en place qu'il peut en être autrement (on embarque tout avec nous).
Dans mon cas particulier, les ports d'apache tomcat ne sont pas ouvert sur l'exterieur, et la manager de tomcat non plus, et ceci pour des raisons presque évidentes de sécurité. Il est alors nécessaire de faire un tunnel ssh, avec les moyens du bord. Voici un exemple, en ligne de commande, sous Linux :
ssh -N -p 22 root@host -L 9080/127.0.0.1/8080
Ici, le port distant 8080 de la machine host est redirigé, via un tunnel ssh, au port local 9080. Ainsi, l'instance distante tomcat, ainsi que son manager, vont être accessible en local, sur le port 9080.
Mise en place
La façon dont le déploiement s'effectue va suivre les différentes étapes :
- Compilation : étape normale de ant
- Génération du war : un fichier war (web archive) est généré (dans distbin/war/)
- Upload du war sur la machine distante, en utilisant ssh, dans le répértoire /tmp/
- Un-deploy de l'application, afin de pouvoir la re-déployer ensuite.
- Deploy de l'application.
- Reload du context de l'application.
- Diverses tâches : suppression du war dans le /tmp/ et copie des fichiers images nécessaires.
Nous allons maintenant étudiés comment mettre en place ces étapes dans notre build.xml.
Définir les tâches
La première étape va être de définir les tâches. Prenons par exemple la tâche deploy, fournit par le fichier catalina-ant.jar. Nous avons les deux librairies java dans le répertoire lib/ant/. La définition de la tâche deploy se fait en début de fichier, en utilisant le tag taskdef.
<taskdef name="deploy" classname="org.apache.catalina.ant.DeployTask">
<classpath>
<pathelement path="${lib.ant}/catalina-ant.jar"/>
</classpath>
</taskdef>
Nous spécifions à ant le classpath, pour qu'il trouve la classe Deploytask. Il est à noter que ${lib.ant} est définit en début de fichier, pointant vers le dossier où les librairies sont. Nous faisons de même pour les autres tâches, soit :
<taskdef name="deploy" classname="org.apache.catalina.ant.DeployTask">
<classpath>
<pathelement path="${lib.ant}/catalina-ant.jar"/>
</classpath>
</taskdef>
<taskdef name="reload" classname="org.apache.catalina.ant.ReloadTask">
<classpath>
<pathelement path="${lib.ant}/catalina-ant.jar"/>
</classpath>
</taskdef>
<taskdef name="undeploy" classname="org.apache.catalina.ant.UndeployTask">
<classpath>
<pathelement path="${lib.ant}/catalina-ant.jar"/>
</classpath>
</taskdef>
<taskdef name="sshexec" classname="org.apache.tools.ant.taskdefs.optional.ssh.SSHExec">
<classpath>
<pathelement path="${lib.ant}/jsch.jar"/>
<pathelement path="${lib.ant}/ant-jsch.jar" />
</classpath>
</taskdef>
<taskdef name="scp" classname="org.apache.tools.ant.taskdefs.optional.ssh.Scp">
<classpath>
<pathelement path="${lib.ant}/jsch.jar"/>
<pathelement path="${lib.ant}/ant-jsch.jar" />
</classpath>
</taskdef>
Gestion des profils
On va utiliser une astuce pour pouvoir spécifier un profil a ant et surtout en définir un par défault. Pour définir une variable au moment de l'exécution de ant, il faut faire quelque chose du genre : ant -Dprofil=xxx. Tout se passe en 3 lignes :
<property name="profil" value="local" />
<property name="undeploy" value="true" />
<!-- On charge un fichier en fonction du profil.. définit par défault ou écraser par la ligne de commande -->
<property file="${basedir}/${profil}.properties"/>
Le fichier xxx.properties
Le fichier ${profil}.properties contient toutes les variables nécessaire au déploiement. Dans mon cas, voici mon local.properties :
deploy.host=localhost
deploy.username=vincent
deploy.password=412hez
deploy.app.path=/qualif
deploy.app.webip=127.0.0.1
deploy.app.webimages=/usr/share/energy/images
deploy.manager=http://${deploy.host}:8180/manager
deploy.manager.username=admin
deploy.manager.password=xxxxxxxx
Tâche et astuces finale
Enfin, on définit les tâches, mais faudra vous débrouiller pour les tâches de compilation, de génération du war, et autres (très bien documenté).
On va aussi faire preuve de malice ici. Mon profil par défaut est un profil local, je n'ai donc pas besoin de scp ou de sshexec. J'ai donc ici utilisé les conditions, en définissant une variable d'un façon différente en fonction du profil, et ainsi appeler ensuite un tâche différente.
Pour le reste, c'est du classique, et du bien documenté sur le site de ant.
<target name="deploy" depends="compile_java">
<filter token="WEBIP" value="${deploy.app.webip}" />
<copy todir="${dist.tomcat.WEB-INF}" filtering="yes">
<fileset dir="${src.conf}"/>
</copy>
<antcall target="war" />
<echo message="Deploying ${basedir}/${war}/${name}-${version}.war on Tomcat : ${deploy.manager}.."/>
<condition property="deployer.function" value="local" else="default">
<equals arg1="${profil}" arg2="local" />
</condition>
<condition property="deploy.undeploy" value="yes" else="no">
<istrue value="${undeploy}" />
</condition>
<antcall target="scp-${deployer.function}" />
<antcall target="undeploy-${deploy.undeploy}" />
<!-- (re)Deploy app -->
<deploy url="${deploy.manager}"
username="${deploy.manager.username}"
password="${deploy.manager.password}"
path="${deploy.app.path}"
localWar="/tmp/${name}-${version}.war"
/>
<!-- Reload the Context -->
<reload url="${deploy.manager}"
username="${deploy.manager.username}"
password="${deploy.manager.password}"
path="${deploy.app.path}" />
<!-- Remove the temporary war file -->
<antcall target="sshexec-${deployer.function}" />
</target>
<target name="scp-local">
<echo>Local deployement, no need to to scp, just copy..</echo>
<copy file="${war}/${name}-${version}.war" todir="/tmp/" />
</target>
<target name="scp-default">
<scp file="${war}/${name}-${version}.war"
todir="${deploy.username}:${deploy.password}@${deploy.host}:/tmp/"
/>
</target>
<target name="sshexec-local">
<echo>Local deployement, no need to do sshexec</echo>
</target>
<target name="undeploy-yes">
<undeploy url="${deploy.manager}"
username="${deploy.manager.username}"
password="${deploy.manager.password}"
path="${deploy.app.path}"
/>
</target>
<target name="undeploy-no">
<echo>No un-deploy</echo>
</target>
<target name="sshexec-default">
<sshexec host="${deploy.host}"
username="${deploy.username}"
password="${deploy.password}"
command="rm /tmp/${name}-${version}.war"/>
</target>
Usage
La procédure est censée être la plus simple possible, en admettant que le port de tomcat est ouvert, et que le manager de tomcat est actif. Il ne s'agit que d'une ligne de commande :
ant deploy
Le profil de déploiement peut également se spéficier. Par défaut, il s'agit du profil local, dont les propriétés sont disponible dans le fichiers local.properties.
ant -Dprofil=toto deploy
En spécifiant le profil toto, par exemple, ant va chercher les propriétés dans toto.properties et fait le déploiement selon ces propriétés.
Il est à noter qu'il faut forcément undeploy le context pour pouvoir le redéployer. Ant n'est pas très intelligent sur ce point (c'est là entre autre qu'on dit vive Maven), et se plantera en bon et due forme au moment du undeploy.. s'il n'a pas été déjà déployé. Pour cela, un petit booléen peu être passé en argument :
ant -Dprofil=ns -Dundeploy=false deploy