Channels ▼
RSS

Tools

Improving the Development Process


Ant Tasks for Application Deployment

Let's take a closer look at the Ant scripts themselves, along with the shell script that makes it easier to pass in all of those parameters. For instance, the shell script in Example 1 sets the path, classpath, and Ant options (such as heap size) to run Ant. It also collects the parameters (CVS user, CVS label, and the environment name), and calls the Ant script to execute the deployment.


#! /bin/sh
ANT_HOME=/usr/local/ant
JAVA_HOME=/usr/java
ANT_OPTS="-Xmx512m"
PATH=${PATH}:${ANT_HOME}/bin
CLASSPATH=$CLASSPATH:$ANT_HOME/lib:$ANT_HOME/lib/jakarta-ant-1.4.1-optional.jar
export ANT_HOME JAVA_HOME ANT_OPTS PATH CLASSPATH

CVSUSER=$1
RELEASE=$2

## ex: qa, prodserver_01, prodserver_02, etc.
ENVIRONMENT=$3

## true or false
FORCE_CHECKOUT=$4

ant -buildfile deploy.xml -Dcvsuser=$CVSUSER -Dlabel=$RELEASE -
Denvironment_name=$ENVIRONMENT -Dforce_checkout=$FORCE_CHECKOUT deploy_app

Example 1: The shell script to call the Ant script for release deployment.

The parameters passed into this shell script are set as environment variables in the line that executes the ant command at the end. These are read in as part of the Ant script, as shown here (see Listing 1 for the entire script):

...
<property environment="env"/>
<property name="cvsuser" value="${env.CVSUSER}" />
<property name="label" value="${env.LABEL}" />
<property name="environment_name" value="${env.ENVIRONMENT_NAME}" />
<property name="force_checkout" value="${env.FORCE_CHECKOUT}" />
...

The properties listed here are set with the parameters entered in the shell script and specified as -D command-line parameters when calling Ant. There are other properties, such as the CVS project name, and the name of application archive file being deployed, that are hard-coded in this example. You'll need to replace these with the correct names for your project, or you can pass them as additional environment variables if you choose.


<project name="My Application Release" default="init" basedir=".">
    <!-- 	
        Read some values set in the environment (as -D parameters)
     -->
    <property environment="env"/>
    <property name="cvsuser" value="${env.CVSUSER}" />
    <property name="label" value="${env.LABEL}" />
    <property name="environment_name" value="${env.ENVIRONMENT_NAME}" />	
    <property name="force_checkout" value="${env.FORCE_CHECKOUT}" />

    <!-- 	
        The following could also be set from the script used to run deploy.xml
        These are examples. Replace with your project's name in CVS, etc.
     -->
    <property name="cvsproject" value="MYAPP"/>
    <property name="ear" value="myapp_ear"/>
	
    <!-- 	
        These params store backup location (to store current release)
        and the directory to deploy the new release to
     -->
    <property name="application_dir" location="/apps"/>
    <property name="backup_dir" location="/apps/backups"/>
    <property name="release_dir" location="/apps/releases"/>
    <property name="cvs_checkout_dest" value="${release_dir}/${label}" />

    <target name="init">
        <echo message="CVS User is ${cvsuser}"/>

        <!--  create the timestamp   --> 
        <tstamp> 
            <format property="backup.date" pattern="yyyy.MM.dd" locale="en"/>
        </tstamp>

        <!-- has backup been made? -->
        <available file="${backup_dir}/${cvsproject}_bak_${backup.date}" 
                   type="dir" property="backup.present"/>
    </target>

    <target name="check_release">
        <!-- 
            Skip checkout of if already there and not forcing a new one
        -->
        <condition property="skip.checkout">
	    <and>
                <available file="${cvs_checkout_dest}/${cvsproject}" 
                           type="dir" property="release.present"/>
                <isfalse value="${force_checkout}" />
            </and>
        </condition>
		
        <echo message="skip checkout=${skip.checkout}" />
    </target>
	
    <target name="cvs_checkout" 
            depends="init,check_release" 
            unless="skip.checkout"
            description="check out project from cvs">

        <!-- delete existing files before checking out -->
        <delete dir="${cvs_checkout_dest}/${cvsproject}" 
                quiet="true" failonerror="false" />

        <!-- perform the checkout (replace cvs server details with your own) -->
        <echo message="Checking out release..."/>
        <mkdir dir="${cvs_checkout_dest}" />
        <cvs command="checkout"
             cvsRoot=":pserver:${cvsuser}@192.168.1.2/repository/cvs"
             dest="${cvs_checkout_dest}"
             package="${cvsproject}/${ear}"
             tag="${label}"
        />

        <echo message="Removing CVS-specific files..."/>
        <delete includeEmptyDirs="true">
            <fileset dir="${cvs_checkout_dest}" defaultexcludes="no">
                <include name="**/CVS/**" />
            </fileset>
        </delete>

        <echo message="Creating version.txt file..."/>
        <!-- update version number -->
        <propertyfile file="${cvs_checkout_dest}/${cvsproject}/${ear}/version.txt">
            <entry key="version" value="${label}" />
        </propertyfile>
    </target>

    <target name="backup" 
            depends="init" 
            unless="backup.present" 
            description="backup old release">
        <echo message="Backing up old release..."/>
        <mkdir dir="${backup_dir}/${cvsproject}_bak_${backup.date}" />
        <copy todir="${backup_dir}/${cvsproject}_bak_${backup.date}">
            <fileset dir="${application_dir}/${ear}">
	    <!-- you can exclude directories here -->
                <exclude name="data/**" />
            </fileset>
        </copy>
    </target>

    <target name="deploy_app" 
            depends="cvs_checkout,backup" 
            description="deploy the application">
        <echo message="Deploying new release..."/>

        <!-- copy application files -->
        <copy todir="${application_dir}" 
              preservelastmodified="true" overwrite="true">
            <fileset dir="${cvs_checkout_dest}/${cvsproject}" includes="${ear}/**" />
        </copy>

        <!-- move environment properties -->
        <copy todir="${application_dir}/properties" 
              preservelastmodified="true" overwrite="true">
    	    <fileset dir="${application_dir}/${ear}/properties/${environment_name}" />
        </copy>

        <!-- move environment scripts and make executable -->
        <copy todir="${application_dir}/scripts" 
              preservelastmodified="true" overwrite="true">
            <fileset dir="${application_dir}/scripts/${environment_name}/${server_type}" />
        </copy>
        <chmod dir="${application_dir}/scripts" perm="ugo+x" includes="**/*"/>
    </target>
</project>

Listing 1: Ant script that pulls from CVS and deploys to the specified server.

The rest of the Ant script contains five targets:

  • init sets the CVS user, and prepares to backup the existing deployment.
  • check_release determines if we can skip the CVS checkout process. This is done when deploying the same release to multiple servers. The checkout is done once, and the same files are< used to deploy multiple times. This can be overridden by setting the FORCE_CHECKOUT parameter to True.
  • backup backs up the existing application files on the server where you're deploying the new release. This is done in case you need to quickly get back to the original files.
  • cvs_checkout checks out the release by specified label from CVS, and creates a file to records that release's version number. You can use this as a sanity to check to ensure the deployment actually occured.
  • deploy_app is the main target that calls all of the other targets (via the dependancy tree) to backup the existing application files, checkout the new release from CVS, and then copy the files to the right places on the specified server.

Ant has built-in support for source code repositories such as CVS. For instance, the cvs_checkout target uses the following command to do its work:


<cvs command="checkout"
       cvsRoot=":pserver:${cvsuser}@192.168.1.2/repository/cvs"
       dest="${cvs_checkout_dest}"
       package="${cvsproject}/${ear}"
      tag="${label}"
/>

Remember to replace the CVS server entry with the address of your actual CVS server, and the path to your CVS repository. The package name, tag name (label), and destination directory are all derived from the parameters you specified in the shell script earlier.

This sample Ant script assumes you're deploying a Java EE enterprise application archive (EAR) file. However, you can pull any number of files and file types. The deploy Ant target contains the entries to copy the files to the server, as shown here:


<!-- copy application files -->
<copy todir="${application_dir}"
      preservelastmodified="true" overwrite="true">
      <fileset dir="${cvs_checkout_dest}/${cvsproject}"
               includes="${ear}/**" />
</copy>

The parameters in this Ant task are pretty self explanatory. The example script contains additional entries that copy other files, such as properties files and execution scripts to start start and stop the server, simply as an example. You can take this further, and create scripts that deploy the actual binaries and other files for your web server, application server, database, and so on. This will enable you to provision new servers identically when you need to, and then deploy your software to them from that point forward.

Conclusion

The success of a software project goes beyond good architecture and code; it relies on good processes and procedures throughout the development, testing, and deployment phases as well. This article illustrates that maintaining good source-code repository practices when it comes to release management and parallel development teams is possible if a proven plan is followed. It also illustrates that maintaining Ant scripts to automate the deployment of labeled releases from your source code repository is not difficult. You can even use them to provision entire servers into your data center. In the end, having a reliable, and error-free, deployment process will save you a lot of time and headaches. Using the processes and scripts provided here in your own projects can help you achieve that.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video