J2EE Application Deployment to EAS Using ANT
This document summarizes the process used at Hennepin County to deploy a J2EE application developed in IBM Web Sphere Application Developer (WSAD) to Sybase Enterprise Application Server (EAS) using Apache ANT tool.
Background
The application described here is the Community Services Group Data Sharing (CSG Data Sharing) system. It consists of Web application used by the CSG staff to manage client records in the central database, and the messaging part that constantly populates the database with the information coming from different independent systems through the IBM WebSphere MQ Integrator. The messages are converted into common format by the Integrator and placed in the output queue. The receive side CSG system has to process incoming messages and update the central database. The system has one service component reading messages from the Integrator queue and publishing them to the EAS message topic. The Message Driven Bean EJBs, each for one of five different message types, are registered as listeners on the topic, and invoke other session façade EJB to process these messages.
Development team is using WSAD for programming and testing, including some testing in the Web Sphere Test Environment which is part of the studio. The application is designed with portability in mind, so it may be deployed to either server, WebSphere or EAS. The project source code is saved in the Rational ClearCase source control repository.
At the time when this project started, WSAD version 4.0 was available, which had limited Ant support, meaning it wasn?t easy to configure the tool within the studio to use it for EAS deployment. That was the reason that Ant is used as an external tool. The build script compiles source code files, packages them into JAR, WAR, and EAR files, deploys the EAR file to the EAS. It also builds JavaDoc API, and uses Java2HTML ( www.java2html.org tool to create an HTML formatted view for the project Java source code. The process may be extended to include checking in and out files from a source control repository, running JUnit tests after deployment to integration server. The same process may be useful for a scheduled build where the development studio is not required at all.
Setup
The setup pretty much follows
EAS System
Administration Guide, Chapter 12
Using jagtool and jagant. The EAS development server is installed in
the same workstation as the WSAD. Although deployment to a remote server is
used as well. The Ant is installed in D:\jakarta-ant-1.5,
and ANT_HOME
environment variable set to point to this directory. The tool is run using
%JAGUAR%\bin\jagant.bat script from a command prompt. The
script sets CLASSPATH and invokes the default JVM at
C:\Program Files\Sybase\Shared\Sun\jdk\jdk1.3
The application workspace is located in the ClearCase view subdirectory, and has these separate projects in it:
- J2EE project
- EJB project
- Web project
- Common classes Java project. The project is for developing EJB client view classes that are common for EJB layer and web application.
- Test Java project that contains various JUnit tests.
The project workspace structure looks like this:
ClearCase
view folder
|_ . . .
|_ . . . other view content, documentation, model etc.
|_ . . .
|_ source folder
|_ WSAD_workspace
|_ J2EE Project
| . . .
| CommonClasses.jar
|_META-INF
application.xml
|_ Common Project
|_ bin
|_ src
|_ EJB Project
|_ ejbModule
. . .
|_ META-INF
ejb-jar.xml
|_ Web Project
|_ Java Source
|_ Web Content
. . .
|_ WEB-INF
|_ classes
|_ lib
web.xml
|_ Test and build Project
|_ lib
|_ bin
|_ src (JUnit tests source)
build.xml
build.properties
builddefault.properties
buildversion.properties
jagtasks.xml
build.bat
The Test and build Project is used for the JUnit tests as well as for running the ANT build script.
The following files are placed in this project directory:
- the Ant build script, build.xml
- build default properties file builddefault.properties
- optional build properties file build.properties that overrides some of the default properties in the builddefault.properties
-
buildversion.properties file
keeps previous build version number, and initially should have this property
defined:
build.version = 0001 - EAS Ant task definitions file jagtasks.xml copied from the %JAGUAR%\samples\jagant\jagtasks.xml
- optionally, the DTD for use by the WSAD XML editor to simplify editing the
build script,
project.dtd. This file is generated by the Ant using this
script:
<?xml version = "1.0" ?>
<!DOCTYPE project [ <!ENTITY jagtasks SYSTEM "jagtasks.xml"> ] >
<project default = "gendtd" basedir = "." >
<!-- include Jaguar task definitions -->
&jagtasks;
<target name = "gendtd" >
<antstructure output = "project.dtd" />
</target>
</project>
Assuming the above script was saved in the file antstructure.xml, it can be run by this command that will produce the project.dtd file in the current directory:
D:\>jagant -buildfile antstructure.xml
D:\<ClearCase view>\srs\WSAD_workspace\CSGTest\>
jagant -buildfile deploy.xml
-logfile deploy.log deploy
The ? deploy? is the default build target, it compiles source files, and builds JAR, WAR and EAR archives, then deploys the EAR file to a specified EAS. To run other targets in the same script, change ?deploy? to the target name. For instance, to generate JavaDoc API, change it to ? apidoc?, to add generate Java2HTML files use ? j2html? target. To run all targets use ? all? or ? publish?. For details see Deployment Script section below.
Building and deploying application
There are these points that have to be considered when deploying the application.
-
The script handles project that have these modules:
- Common classes JAR file
- Web module as WAR file
- EJB project as JAR file
Any of these modules can be omitted by commenting out corresponding project name in the default build properties file, so the application may have only Web part, or only EJB part, or both. The common classes module is optional in any case.
The common classes Java project is a collection of classes that are part of the component remote interface method signatures, so they have to be deployed to both, EJB and Web containers. Keeping these classes in a separate JAR simplifies deployment of the web application when it?s deployed to a separate server, Tomcat for instance. These classes must be present on the CLASSPATH during deployment in order to compile component stubs. This is done if the common JAR file is included in the manifest of every module that requires it. When working with the WSAD it is done by including it in the list of dependant JAR files in the Web and EJB project properties. The build script does not modify manifest files, instead it just uses the files as they are maintained by the studio.
EAR deployment overrides all EAS specific EJB component settings modified in
Jaguar Manager. It doesn?t change any property modifications done to the CORBA
service component that is part of the same package. Therefore after the initial
deployment, all EAS specific settings are done in the Jaguar Manager, and the
application is exported using Jaguar Manager commands Export J2EE EAR, Export
EJB JAR, and Export J2EE WAR, with ?Export with EAServer XML configuration file?
options selected. Then the sybase-easerver-config.xml file
is extracted from each exported archive and placed in the appropriate WSAD project
META-INF directory. This way when the EAR file is deployed, EAS component property
changes are applied based on these configuration files.
The deployment script below includes the target that exports these three configuration files, but the CSGDataMAtchingEJB package configuration file doesn?t have all the component settings in it, so it is exported manually at this time.
The CSG project also has a CORBA Java component that is developed in the EJB project and is part of the same package as the EJB. When deploying the EAR file the new component implementation is deployed as well, but the component properties if they were cofigured using Jaguar Manager, are not changed. The script below has deploy.with.service target that will check if the component exists or not on the server, and if it doesn?t, then the component will be deployed using properties file that is defined in the build properties.
When the web application is in use, or has been in use after the server last restart, then JAR files in the WEB-INF\lib directory are locked (we are using EAS 4.1), and deployment fails. To avoid that we put server in the ?admin? mode before the deployment, and set to ?ready? mode after the deployment is finished.
The application initial deployment is done in these steps:
After that repeat deployments are done just by running the deployment script.
The script default properties
The default properties file is used by the project team, and each developer can create a local build.properties file that overrides any of the default. For instance the workspace location may be different for each developer.
Listing 1.
#####################################################################
#
Default global properties for this build
#
#
To override create build.property file in the same directory as
#
this file, copy this file content into the new file, and modify
#
accordingly
#
#####################################################################
#------------------------------------------------------------
#
Project specific properties
#------------------------------------------------------------
project.name
=
< project name as it would appear in the EAR file name prefix>
project.version
= 1.00
project.j2ee.name
= < WSAD J2EE project name >
project.ejb.name
= < WSAD EJB project name >
project.web.name
= < WSAD Web project name >
project.common.name
= < WSAD Common classes Java project name >
project.test.name
= < WSAD Test and build Java project name
>
project.webapp.name
= < Web application context path >
project.path
= < WSAD workspace path>
archive.dir
=
< Project distribution directory for EAR file, JavaDoc, and Java2HTML files>
javadoc.packages
= hennepin.co.*
#------------------------------------------------------------
#
optionaly install a CORBA service component if it is missing
#
comment out the next two lines if project doesn't use a
#
service component, and use "deploy" target instead of
#
"deploy.with.service"
#------------------------------------------------------------
service.comp.name
=
${project.ejb.name}/QCopy
service.comp.props.file
= ${ejb.src.dir}/META-INF/${service.comp.name}.props
#------------------------------------------------------------
#
EAS connection
#------------------------------------------------------------
jaguar.host
= localhost
jaguar.port
= 9000
jaguar.user
= jagadmin
jaguar.password
=
#------------------------------------------------------------
#
workspace directories reflect usual WSAD workspace structure
#------------------------------------------------------------
project.j2ee.path
= ${project.path}/${project.j2ee.name}
project.ejb.path
= ${project.path}/${project.ejb.name}
project.web.path
= ${project.path}/${project.web.name}
project.common.path
= ${project.path}/${project.common.name}
project.test.path
= ${project.path}/${project.test.name}
lib.dir = ${project.test.path}/lib
ear.dir = ${project.j2ee.path}
ejb.dir
=
${project.ejb.path}
ejb.src.dir
=
${ejb.dir}/ejbModule
ejb.build.dir
=
${ejb.dir}/ejbModule
war.dir
=
${project.web.path}
war.src.dir
=
${war.dir}/Java Source
war.jsp.dir
=
${war.dir}/Web Content
war.lib.dir
=
${war.dir}/Web Content/WEB-INF/lib
war.build.dir
=
${war.dir}/Web Content/WEB-INF/classes
common.dir
=
${project.common.path}
common.src.dir
= ${project.common.path}/src
common.build.dir
= ${project.common.path}/bin
distrib.dir
=
${project.path}/dist
api.dir
=
${distrib.dir}/api
api.src
=
${distrib.dir}/src
j2html.dir
=
${distrib.dir}/j2html
api.publish.dir
= ${archive.dir}/api
j2html.publish.dir
= ${archive.dir}/j2html
#------------------------------------------------------------
#
Java compiler options
#------------------------------------------------------------
compiler.type
=
modern
compiler.deprecation=
on
compiler.debug
= off
compiler.optimize
= on
compiler.verbose
= off
#------------------------------------------------------------
#
Javadoc options.
#------------------------------------------------------------
javadoc.author
=
true
javadoc.private
= true
javadoc.use
=
true
javadoc.version
= true
The build script
The deployment script build.xml content listed below (see Listing 2.)includes comments to make it self-explanatory.
Listing 2.
<?xml version = "1.0" ?>
<!DOCTYPE project PUBLIC "-//ANT//DTD project//EN" "project.dtd" [
<!ENTITY jagtasks SYSTEM "jagtasks.xml">
] >
<!-- =======================================================================
-->
<!?- Build and deploy EAR to EAS -->
<!--
-->
<!-- Properties for this build are
in the builddefault.properties file, to
-->
<!-- override create build.property
file in the same directory, copy
-->
<!-- properties you want to override
from bulddefault.properties into the
-->
<!-- the new file and modify accordingly. -->
<!--
-->
<!-- Author: Yuriy Natarius -->
<!--
-->
<!-- =======================================================================
-->
<project default = "deploy" basedir = "." >
<description> Build and deploy J2EE application to EAS, generate JavaDoc and java2HTML files. </description>
<target name = "antstructure" description = "Run this target to generate project.dtd including EAS tasks. Use the DTD with your XML editor" >
<antstructure output = "project.dtd" />
</target>
<!-- include Jaguar task definitions -->
&jagtasks;
<!--============= Properties for this build =======-->
<property file = "build.properties" />
<property file = "builddefault.properties"
/>
<property file = "buildversion.properties"
/>
<!--============= All ============================-->
<target name = "all"
description
= "build and deploy EAR
file" >
<echo> **************************************************************
</echo>
<echo> Full build, deploy, doc,
publish </echo>
<echo> --------------------------------
</echo>
<echo> for partial buld use these
tragets: </echo>
<echo> deploy to deploy EAR file
only </echo>
<echo> deploy.with.service to
deploy service component as well </echo>
<echo> apidoc to generate JavaDOC
API </echo>
<echo> j2html to generate java2HTML
source view </echo>
<echo> publish same as this "all"
target </echo>
<echo> **************************************************************
</echo>
<antcall target
= "publish" />
</target>
<!--============= Init ============================-->
<target name = "init"
description
= "Initialize project
class path" >
<tstamp>
<format property
= "DSTAMP" pattern
= "MM-dd-yyyy" />
<format property
= "TSTAMP" pattern
= "h:mm:ss a" />
</tstamp>
<echo message
= "${DSTAMP} ${TSTAMP}" ></echo>
<echo> ********************************************************
</echo>
<echo> setting project class path </echo>
<echo> ********************************************************
</echo>
<path id
= "project.class.path"
>
<fileset dir
= "${lib.dir}" includes
= "*.jar" />
<fileset dir
= "${ear.dir}" includes
= "*.jar" />
<fileset dir
= "${war.lib.dir}"
includes
= "*.jar" />
</path>
<echo />
<echo> ********************************************************
</echo>
<echo> deleting existing classes </echo>
<echo> ********************************************************
</echo>
<delete quiet
= "false" failonerror
= "false"
>
<fileset dir
= "${common.build.dir}"
includes
= "**/*.class" />
<fileset dir
= "${ejb.build.dir}"
includes
= "**/*.class" />
<fileset dir
= "${war.build.dir}"
includes
= "**/*.class" />
</delete>
<delete dir
= "${distrib.dir}"
/>
<mkdir dir
= "${distrib.dir}"
/>
</target>
<!--============= Build ============================-->
<!-- build new common jar file
-->
<target name = "build.common.jar" depends
= "init" if = "project.common.name"
description
= "Compile and build
common classes JAR file" >
<echo> ********************************************************
</echo>
<echo> compile and build common
files JAR file </echo>
<echo> ********************************************************
</echo>
<javac srcdir
= "${common.src.dir}"
destdir
= "${common.build.dir}"
debug = "${compiler.debug}" compiler
= "${compiler.type}"
optimize
= "${compiler.optimize}"
deprecation
= "${compiler.deprecation}"
verbose
= "${compiler.verbose}"
>
<classpath refid
= "project.class.path"
/>
</javac>
<copy todir
= "${common.build.dir}"
>
<fileset dir
= "${common.src.dir}"
includes
= "**/*.properties"
/>
</copy>
<jar basedir
= "${common.build.dir}"
destfile
= "${distrib.dir}/${project.common.name}.jar"
includes
= "**/*.class" />
<copy file
= "${distrib.dir}/${project.common.name}.jar"
todir
= "${project.j2ee.path}"
/>
</target>
<!-- build EJB module JAR file
-->
<target name = "build.ejb.jar" depends
= "init, build.common.jar"
if
= "project.ejb.name"
description
= "Compile and build
EAJB module JAR file" >
<echo> ********************************************************
</echo>
<echo> compile and build EJB module
JAR file </echo>
<echo> ********************************************************
</echo>
<javac srcdir
= "${ejb.src.dir}"
destdir
= "${ejb.build.dir}"
debug
= "${compiler.debug}"
compiler
= "${compiler.type}"
optimize
= "${compiler.optimize}"
deprecation
= "${compiler.deprecation}"
verbose
= "${compiler.verbose}"
excludes
= "**/_*.java,
**/EJS*.java,**/Concrete*.java,
**/websphere_deploy/**/*.java"
>
<classpath refid
= "project.class.path"
/>
</javac>
<copy todir
= "${ejb.build.dir}"
>
<fileset dir
= "${ejb.src.dir}"
includes
= "**/*.properties"
/>
</copy>
<jar destfile
= "${distrib.dir}/${project.ejb.name}.jar"
manifest
= "${ejb.build.dir}/META-INF/MANIFEST.MF"
>
<fileset dir
= "${ejb.build.dir}"
includes
= "**/*.class" />
<fileset dir
= "${ejb.src.dir}"
includes
= "META-INF/*.*"
/>
</jar>
</target>
<!-- build web module WAR file
-->
<target name = "build.web.war" depends
= "init, build.common.jar"
description
= "Compile and build
web application WAR file" >
<echo> ********************************************************
</echo>
<echo> compile and build Web module
WAR file </echo>
<echo> ********************************************************
</echo>
<javac srcdir
= "${war.src.dir}"
destdir
= "${war.build.dir}"
debug = "${compiler.debug}"
compiler
= "${compiler.type}"
optimize
= "${compiler.optimize}"
deprecation
= "${compiler.deprecation}"
verbose
= "${compiler.verbose}"
>
<classpath refid
= "project.class.path"
/>
<classpath>
<fileset dir = "${war.lib.dir}" includes
= "*.jar" />
</classpath>
</javac>
<copy todir
= "${war.build.dir}"
>
<fileset dir
= "${war.src.dir}"
includes
= "**/*.properties"
/>
</copy>
<jar basedir
= "${war.jsp.dir}"
destfile
= "${distrib.dir}/${project.web.name}.war"
manifest
= "${war.jsp.dir}/META-INF/MANIFEST.MF"
/>
</target>
<!-- build J2EE module EAR file
-->
<target name = "build.ear" depends
= "init, build.ejb.jar,
build.web.war, build.counter"
description
= "Build J2EE application
EAR file" >
<echo> ********************************************************
</echo>
<echo> build J2EE EAR file </echo>
<echo> ********************************************************
</echo>
<property name
= "ear.name" value = "${distrib.dir}/${project.name}-${DSTAMP}-${project.version}.${build.version}.ear"
/>
<jar destfile = "${ear.name}" >
<fileset dir = "${distrib.dir}" includes = "${project.ejb.name}.jar" />
<fileset dir = "${distrib.dir}" includes = "${project.web.name}.war" />
<fileset dir = "${project.j2ee.path}" includes = "**/*.*" />
</jar>
</target>
<!-- create a counter -->
<target name = "build.counter" >
<propertyfile file = "./buildversion.properties" >
<entry key = "build.version" type = "int" operation = "+" value = "1" pattern = "0000" />
</propertyfile>
<echo message = "Build counter: ${project.version}.${build.version}" />
</target>
<!--============= Connect to EAS ==================-->
<target name = "connect" depends = "init" >
<echo> ******************************************************** </echo>
<echo> connecting to EAS </echo>
<echo> ******************************************************** </echo>
<jag_connect host = "${jaguar.host}" port = "${jaguar.port}" user = "${jaguar.user}" password = "${jaguar.password}" />
</target>
<!--============= Deploy J2EE EAR file to EAS =====-->
<target name = "deploy" depends = "connect, build.ear" >
<echo> ******************************************************** </echo>
<echo> deploy J2EE EAR file to EAS </echo>
<echo> ******************************************************** </echo>
<jag_set_admin reason = "clean up and unlock files before deploy" />
<jag_restart />
<sleep seconds = "15" />
<waitfor checkevery = "5" checkeveryunit = "second" maxwait = "5" maxwaitunit = "minute" >
<socket server = "${jaguar.host}" port = "${jaguar.port}" />
</waitfor>
<jag_deploy type = "ear" stubsandskels = "true" install = "true" strategy = "full" verbose = "true" file = "${ear.name}" />
<jag_refresh entity = "Application:${project.j2ee.name}" />
<jag_set_ready />
</target>
<!--============= Deploy with service component ====-->
<target name = "check.service.comp" depends = "connect" description = "Check if service component exists" >
<jag_exists entity = "Component:${service.comp.name}" property = "comp.exists" />
</target>
<target name = "deploy.with.service" depends = "check.service.comp, deploy" unless = "comp.exists" description = "Deploy service component" >
<jag_create entity = "Component:${service.comp.name}" file = "${service.comp.props.file}" />
</target>
<!--============= Copy source files to one location ====-->
<target name = "makedir" depends = "init, build.ear" description = "make directories for api and j2html" >
<mkdir dir = "${api.dir}" />
<mkdir dir = "${api.src}" />
</target>
<target name = "copy.common.src" depends = "makedir" if = "project.common.name" description = "copy Common Java project source files for JavaDoc and java2HTML processing" >
<copy todir = "${api.src}" >
<fileset dir = "${common.src.dir}" includes = "**/*.*" />
</copy>
</target>
<target name = "copy.ejb.src" depends = "copy.common.src" if = "project.ejb.name" description = "copy EJB module source files for JavaDoc and java2HTML processing" >
<copy todir = "${api.src}" failonerror = "false" >
<fileset dir = "${ejb.src.dir}" includes = "**/*.java" excludes = "**/_*.*, **/EJS*.*" />
</copy>
</target>
<target name = "copy.src" depends = "copy.ejb.src" if = "project.web.name" description = "copy Web module source files for JavaDoc and java2HTML processing" >
<copy todir = "${api.src}" >
<fileset dir = "${war.src.dir}" includes = "**/*.*" />
</copy>
</target>
<!--============= Generate JavaDoc API =============-->
<target name = "apidoc" depends = "copy.src" >
<echo> ******************************************************** </echo>
<echo> generating JavaDOC files </echo>
<echo> ******************************************************** </echo>
<javadoc packagenames = "${javadoc.packages}" destdir = "${api.dir}" author = "${javadoc.author}" version = "${javadoc.version}" use = "${javadoc.use}" private = "${javadoc.private}" windowtitle = "${project.name} API" doctitle = "${project.name} ${project.version}" link = "http://jakarta.apache.org/struts/doc-1.0.2/api" >
<sourcepath>
<pathelement path = "${api.src}" />
</sourcepath>
<classpath refid = "project.class.path" />
</javadoc>
</target>
<!--============= Generate Java2HTML =============-->
<target name = "j2html" description = "Creates html from java files" depends = "copy.src" >
<echo> ******************************************************** </echo>
<echo> generating java2HTML files </echo>
<echo> ******************************************************** </echo>
<mkdir dir = "${j2html.dir}" />
<java classname = "j2h" fork = "yes" >
<classpath refid = "project.class.path" />
<arg line = "-d ${j2html.dir}" />
<arg line = "-js ${api.src}" />
</java>
</target>
<!--============= Publishing =============-->
<target name = "publish" depends = "deploy, apidoc, j2html" >
<echo> ******************************************************** </echo>
<echo> Publishing build files </echo>
<echo> ******************************************************** </echo>
<echo> -------- Publishing EAR file --------- </echo>
<copy file = "${ear.name}" todir = "${archive.dir}" />
<echo> -------- Publishing JavaDoc API -------- </echo>
<delete file = "${archive.dir}/${project.name}_API.zip" />
<zip basedir = "${api.dir}" destfile = "${archive.dir}/${project.name}_API.zip" />
<delete quiet = "true" >
<fileset dir = "${api.publish.dir}" includes = "**/*.*" />
</delete>
<copy todir = "${api.publish.dir}" >
<fileset dir = "${api.dir}" includes = "**/*.*" />
</copy>
<echo> -------- Publishing java2HTML files -------- </echo>
<delete quiet = "true" >
<fileset dir = "${j2html.publish.dir}" includes = "**/*.*" />
</delete>
<copy todir = "${j2html.publish.dir}" >
<fileset dir = "${j2html.dir}" includes = "**/*.*" />
</copy>
</target>
</project>
Credits
Yuriy Natarius, Hennepin County, IT
Mirek Chowaniok, Hennepin County, IT