ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Flexible User and Environment Ant Configuration Flexible User and Environment Ant Configuration

by Grant Bremer
05/19/2004

The de facto standard for building, packaging, and deploying Java applications is Apache Ant. Small differences in developers' environments or preferences may cause problems with some Ant tasks that involve finding or copying files. Standardization takes care of many environmental problems, but there may be problems when some developers use Linux while others are using Windows.

There are many good reasons to customize a development environment but, like a tiny pebble caught in your shoe, seemingly minor customizations may cause others a great deal of pain. Changing the build settings to reflect your environment is simple: make the changes and check them into CVS or VSS. Switching to Jikes from javac or to an app server customized to use appdomain instead of mydomain seem insignificant in the grand scheme of things, but can cause a great deal of confusion and mistrust in the build process. There are, however, legitimate reasons to deviate from the standardized development environment; therefore, the developer should have the option to do so without imposing upon the other developers. In this article, you will use standard features of Ant to provide developers and build processes with simple and painless configuration to support their special requirements.

Local Properties

The Ant core tasks include the ability to load properties files, and to use the properties as build variables. Ant provides the ability to define and use build variables to hold directory and application values. The variables can be defined within the build script, the build.xml file, or can be defined externally, in a properties file. An interesting feature of Ant is that build variables will keep the original value if the variable is defined and redefined by the build file or external properties files.

All of the build variables in my build scripts are stored in a properties file called build.properties. The build.properties file holds all of the settings of a standardized default development environment. Any special settings are in a properties file named local.properties. Any property that is defined in local.properties redefines a property from build.properties. In my build script, I check for the existence of the local.properties file. If the file is available, I load it. Then I load the build.properties file. The properties from local.properties will have precedence over the properties from build.properties. My Ant scripts always start with the following code to load those properties.

<?xml version="1.0"?>
<project name="Foo" default="usage" basedir=".">
  <target name="start">
    <tstamp/>
    <available file="${basedir}/local.properties" 
           property="local.properties.available"/>
  </target>
                                                                                           
  <target name="properties-local" 
          if="local.properties.available" 
          depends="start">
    <echo message="Loading local properties file"/>
    <property file="${basedir}/local.properties"/>
  </target>
                                                                                           
  <target name="properties" 
          depends="properties-local">
    <echo message="Loading build properties file"/>
    <property file="build.properties"/>
  </target>
</project>

A drawback of this strategy is that the local.properties files are not stored in source control. However, a well-written build.xml and build.properties file will have minimum settings that can be reasonably overridden. If local.properties holds a large number of values, the build.properties file may be poorly defined or the standardized development environment may not be so standardized.

Organizing the Properties

When a large number of settings are defined for a build script, the build developer and users need an easy way to view them, preferably before the build task is executed. For complex settings that depend on other settings, viewing their values is extremely important. For example, the build script may have a property for the project directory name, project.dir, and a set of properties to describe the subdirectories for the source, the Java source, the Python source, the libraries, and the configuration files that are based on the project.dir setting.

############################################################
#
# Project directory settings
#
############################################################

# Project directory
project.dir=${basedir}

# Source directory, relative to the project directory
project.src.dir=${project.dir}/src

# Java source, relative to the source directory
project.java.dir=${project.src.dir}/java

# Python source, relative to the source directory
project.python.dir=${project.src.dir}/python

# Libraries, relative to the project directory
project.lib.dir=${project.dir}/lib

# Configuration directory, relative to the project directory
project.conf.dir=${project.dir}/conf

Using previously defined settings to define new settings is an intelligent and effective way of avoiding the duplication of information. Knowing how Ant processes the settings is important when embedding properties to define others. When a property is interpreted, Ant performs the property expansion and the value is set. Once set, the value becomes immutable. A common problem is property expansion of an undefined property. If Ant attempts to process the project.java.dir property above, and project.src.dir was not yet set, the value would be ${project.src.dir}/java. Attempting to change into the ${project.src.dir}/java will result in unexpected and unwanted behavior.

I use an additional term to describe properties as they are interpreted by Ant during a build: effective properties. A effective property is a fully expanded value, and holds the value that Ant will use to execute its tasks. Ensuring that the effective properties are what the developer expects will ensure that the build script executes as expected. Therefore, a level of transparency is needed to view the properties as Ant will use them.

A build task can and should provide a task to display the effective build settings. The task simply displays the properties that the build task will use as it executes. An important step is that Ant performs any variable replacement that is required, so the properties displayed by the task properly reflect how Ant will see them.

<?xml version="1.0"?>
<project name="Foo" default="usage" basedir=".">

  <!-- properties tasks here -->

  <target name="settings" depends="properties">

    <echo message="******************************************"/>
    <echo message="App Settings"/>
    <echo message="******************************************"/>
    <echo message=""/>
                 
    <echo message="app.Name       ${app.Name}"/>
    <echo message="app.name       ${app.name}"/>

    <echo message="app.version    ${app.version}"/>
    <echo message="app.packages   ${app.packages}"/>
    <echo message=""/>
                 
    <echo message="******************************************"/>
    <echo message="Build Settings"/>
    <echo message="******************************************"/>

    <echo message=""/>
                
    <echo message="build.dir          ${build.dir}"/>
    <echo message="build.lib.dir      ${build.lib.dir}"/>
    <echo message="build.conf.dir     ${build.conf.dir}"/>
    <echo message="build.src.dir      ${build.src.dir}"/>
    <echo message="build.classes.dir  ${build.classes.dir}"/>

    <echo message="build.apidocs.dir  ${build.apidocs.dir}"/>
    <echo message="build.dist.dir     ${build.dist.dir}"/>
    <echo message=""/>
                 
    <echo message="******************************************"/>
    <echo message="Project Settings"/>
    <echo message="******************************************"/>

    <echo message=""/>
                                                                                           
    <echo message="project.lib.dir      ${project.lib.dir}"/>
    <echo message="project.docs.dir     ${project.docs.dir}"/>
    <echo message="project.conf.dir     ${project.conf.dir}"/>
    <echo message="project.classes.dir  ${project.classes.dir}"/>
    <echo message="project.src.dir      ${project.src.dir}"/>
    <echo message="project.java.dir     ${project.java.dir}"/>
    <echo message="project.python.dir   ${project.python.dir}"/>

    <echo message=""/>

  </target>

</project>

When using the local.properties file, this properties task is important to make sure that the local properties are being picked up properly, and that the proper properties are being overridden.

In Practice: Deploying to a J2EE App Server

A common use for using local properties at my job is to deploy our J2EE application to WebLogic. We recently standardized on WebLogic 7.0 from 6.1, and during the migration developers found themselves needing to use 6.1 for bug fixing. The web application for WebLogic 6.1 needed to be deployed into the C:\bea\wlserver6.1\config\mydomain\applications\FooApp directory, and for WebLogic 7.0, C:\bea\user_projects\mydomain\applications\FooApp. The updated build.properties file, after the migration, contained the following lines for deploying.

#####################################################################
#
# Web application deployment settings
#
#####################################################################
 
deploy.appserver.dir=C:/bea/user_projects
deploy.domain.dir=${deploy.appserver.dir}/mydomain
deploy.webapp.name=FooWeb
deploy.webapp.dir=${deploy.domain.dir}/applications/${deploy.webapp.name}

When I want to deploy my web application, I copy files to the ${deploy.webapp.dir}, which translates to C:/bea/user_projects/mydomain/applications/FooWeb. When I want to deploy the application to WebLogic 6.1, I need to adjust the ${deploy.appserver.dir} and ${deploy.domain.dir} properties. I create a local.properties file, and override those two properties. The file looks like the following.

#####################################################################
#
# Web application deployment settings
#
#####################################################################
 
deploy.appserver.dir=C:/bea/wlserver6.1
deploy.domain.dir=${deploy.appserver.dir}/config/mydomain

The local.properties overridden properties could have, in fact, been limited to one. The ${deploy.appserver.dir} could have been changed to C:/bea/wlserver.6.1/config, and the ${deploy.domain.dir} been left unchanged. I chose to change two because the app server directory is C:\bea\wlserver6.1 instead of C:\bea\wlserver6.1\config, in case I need to get to the C:\bea\wlserver6.1\lib or C:\bea\wlserver6.1\bin directories.

The changes are simple and tied to the environment of the system, and therefore can safely live outside of source control and on the edge of standardization. The developer has not deviated from the standardized environment in a way that will introduce problems to other developers.

Conclusion

Introducing the ability to work within a flexible area for build and deployment configuration can reap benefits when small and isolated changes are required. Used appropriately, the flexibility can free developers from stiff and unforgiving development environment standards.

Resources

Grant Bremer is currently a professional software developer in New York City, though he will insist that he lives in Brooklyn.


Return to ONJava.com.