IBM Rational ClearCase, Ant, and CruiseControl: The Java Developers Guide to Accelerating and Automating the Build Process

Once you have created your Release Packages and have a process in place for automating your deployment work flow, you use this work flow to help deploy your Release Packages to their runtime environments. The actual method of deployment, as I stated earlier, depends on the format of your Release Package and the environment you are deploying to. However, this section discusses the following:

  • Locating the set of files to be deployed

  • Deploying via file systems (such as copying or FTP)

  • Deploying to Web or application servers

  • Deploying interactively using Ant

A number of tools can help you with the physical deployment process, such as Tivoli Provisioning Manager and Microsoft Systems Management Server. These are not discussed in detail, but I will describe how to integrate with them where possible.

If your physical deployment process is quite straightforward, you could create Ant targets for deployment inside your main project build.xml file. However, if you have a complex set of deployment scripts, possibly encompassing a number of files, you could version-control them in the separate ClearCase project or deployment component directory, as discussed in Chapter 10. Similarly, if your deployment team maintains its own set of deployment scripts (possibly in a different ClearCase environment), you need some way for them to locate the set of files to be deployed. This is where the Deployment Unit file that was created in the preceding section can be used, as discussed next.

Locating the Files to Deploy

If you used the Build and Deployment Tracking capability and the du_tool utility as described in the preceding section, you have a single Deployment Unit file that lists the exact set of files to be deployed. However, the Deployment Unit file might have been generated in a different ClearCase environment, such as using a different ClearCase view or at a remote site. Therefore, the list of files contained in the Deployment Unit needs to be "resolved" via the du_tool --resolve command to your current local environment. This turns all the OREFs in the Deployment Unit file into your own view-specific versions so that you can retrieve and deploy them. For example, a Deployment Engineer might be handed the Deployment Unit file. He would first create his own ClearCase view and then resolve the contents of the Deployment Unit:

[View full width]

>cleartool mkview -snapshot -tag deploy_view -stgloc ccstg_d_views C:\Views\deploy_view >cd C:\Views\deploy_view >ratlperl "C:\Program Files\Rational\ClearCase\etc\du_tool.pl" -resolve RatlBankWeb_R1 .0_20_decorated.xml -out RatlBankWeb_R1.0_20_resolved.xml

Here's an example of a resolved file:

<?xml version="1.0" encoding="UTF-8"?> <tracking:deploymentUnit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" instance version="1.0" name="RatlBankModel" xmlns:tracking="http://www.ibm.com/deployment/schema/ deploymentTracking.xsd"> <file path="\RatlBankReleases\model\RatlBankModel.jar" versionURI="ccase:/RatlBankReleases/model/ RatlBankModel.jar? OREF=oid%3A647ea4ab.90fc46b6.8ddf.d6%3A82%3A8c%3Aab %3A8c%3Ad5%40vobuuid%3A961a67f4.a4554a26.ab21.a0 %3A77%3A7e%3A60%3A9b%3Aa9&amp;registry_server=GORILLA &amp;region=Windows" xname="C:\Views\ratlbank_deploy\RatlBankReleases\model\ RatlBankModel.jar@@\main\RatlBankModel_Int\24" csrule="RATLBANKMODEL_REL_28_INT -nocheckout" origPath="RatlBankModel.jar" /> <CCOrigin> <baseline baselineName="RATLBANKMODEL_20_BL_INT" originAuthority="UserProvided" /> <configSpec> ... </configSpec> </CCOrigin> <history changeOperation="Resolve" changeNote="" user="pat" hostname="gorilla" datetime="2006-01-10T13:48:34-05:00"/> <history changeOperation="Decorate" changeNote="" user="pat" hostname="gorilla" datetime="2006-01-10T13:32:28-05:00"/> <history changeOperation="Generate" changeNote="" user="pat" hostname="gorilla" datetime="2006-01-10T13:30:45-05:00"/> </tracking:deploymentUnit>

Note that the RatlBankModel.jar file has been resolved back to a view-specified version. Also notice that a snapshot view called deploy_view has been created. Why a snapshot view? So that the next step can be executedretrieving the exact set of ClearCase version-extended files using the snapshot cleartool get command. This command can retrieve specific version-extended files:

[View full width]

>cleartool get -to RatlBankModel.jar C:\Views\deploy_view\RatlBankReleases\model \RatlBankModel.jar@@\main\RatlBankModel_Int\24

This retrieves the exact version of the RatlBankModel.jar file that was previously staged and writes its contents to the local view file RatlBankModel.jar. To automate this process, you can write a script that parses the entire contents of the resolved Deployment Unit file and executes the cleartool get commands for you. Listing 11.3 is an example of a script to achieve this.

Listing 11.3. Perl Script to Retrieve Staged Filesdu_get.pl

use XML::Parser; my ($filepath) = @ARGV; if ((@ARGV != 1) or ($filepath eq "")) { die("Error: invalid deployment unit file specified.\n"); } my $debug = 1; my $CLEARTOOL_CMD = "cleartool"; '$CLEARTOOL_CMD pwv' or die "Error: can't find cleartool command: $!\n"; my $xname = ""; my $origPath = ""; # Parse the xml file and get the version into the view if("${filepath}" ne "") { eval { my $parse = new XML::Parser(); $parse->setHandlers(Start => \&handler_start); $parse->parsefile($filepath); sub handler_start { my ($parser, $element) = @_; if ($element eq 'file') { while (@_) { my ($att) = shift; if ($att eq 'xname') { $xname = shift; } if ($att eq 'origPath') { $origPath = shift; cleartool_exec("get -to $origPath $xname"); } } } } }; if($@) { die("Invalid XML Format: $!\n"); } } sub cleartool_exec { print "[DEBUG] Executing cleartool @_\n" if $debug; return '$CLEARTOOL_CMD @_' or die("Error failed to execute cleartool @_: $!\n"); }

This script takes a resolved Deployment Unit file as input:

>ratlperl du_get.pl RatlBankWeb_R1.0_20_resolved.xml

It then parses the input file, searching for any file elements. For each element it finds, it retrieves the extended name (xname) and original path (origPath) attributes and uses them as inputs to the cleartool get command. Through the du_tool script and its supporting Perl module RD_utils.pm, you can integrate the Deployment Unit generation mechanism with your own deployment tool. You can automatically construct views, retrieve the contents of the Deployment Unit, and deploy them while at all times maintaining traceability. This is a powerful and open capability. In fact, a similar mechanism is used to integrate the Build and Deployment Tracking capability with IBM Tivoli Provisioning Manager (see the following sidebar).

Once you have set up an environment for deploying and are looking at the correct versions or have retrieved them using an approach similar to that described in this section, the next process is to physically deploy them. I discuss this next.

Release Deployment with IBM Tivoli Provisioning Manager

IBM Tivoli Provisioning Manager is a comprehensive enterprise tool for provisioning and configuring servers, operating systems, middleware, applications, storage, and network devices. It allows you to either reuse a prebuilt automation package for major vendors' products (such as Microsoft Windows) or create your own customized automation packages (for deploying internal software applications and processes). Using these automation packages, Tivoli Provisioning Manager can provision and deploy a server (from bare-metal to full production) with a click of a button.

An additional ClearQuest package for Tivoli Provisioning Manager can be installed alongside the Build and Deployment Tracking capability, as discussed in the preceding section. This enables a basic integration between the products to automate the physical deployment process. This integration will be significantly enhanced in future releases. For more information on using ClearCase with Tivoli Provisioning Manager, see the redpaper titled "Deploying Applications Using IBM Rational ClearCase and IBM Tivoli Provisioning Manager," located at www.redbooks.ibm.com/abstracts/redp4105.html.

Deploying to File Systems

One of the simplest methods of deployment is via the file system, either directly over a local (or shared) disk file system, or over the network to a remote file system via FTP. To copy files over a disk file system, you can use the Ant <copy> task:

<copy todir="X:\dest\dir"> <fileset dir="${dir.stage}/model"> <include name="**/*.jar"/> <include name="**/*.html"/> </fileset> </copy>

This example copies all .jar and .html files from the ${dir.stage}/model directory to the directory X:\dest\dir (which could be on the same machine or a mapped drive on another machine). To copy files over the network to a remote system, you can use the Ant <ftp> task:

<ftp server="ftp.ratlbank.com" remotedir="${ant.project.name}" user password="${name.password.ftp}" verbose="yes"> <fileset dir="${dir.stage}/web"> <include name="${ant.project.name}.war"/> <include name="ccblreport.html"/> </fileset> </ftp>

This example FTPs the project .war and .html files from the ${dir.stage}/web directory to the project directory on the FTP server ftp.ratlbank.com.

Prerequisites for the FTP Task

To use the FTP task with Ant 1.6, the Apache Jakarta Commons Net and ORO .jar files must be downloaded and built against (http://jakarta.apache.org/site/downloads/index.html). See the note titled "Building Ant with Support for Third-Party Libraries" in Chapter 4, "Defining Your Build and Release Scripts," for more information.

The example also uses properties for the username and password; you can also specify these properties in the default.properties, as discussed in Chapter 5, "Apache Ant Best Practices." For more security you can specify them from the command line instead:

>ant deploy -Dname.user.ftp=user -Dname.password.ftp=password

You can also use the Ant <input> task to interactively prompt for these values (as described in more detail later in this section). An alternative to using FTP is to use the secure copy protocol (SCP) via Ant's <scp> task. This is defined in a way similar to the <ftp> task; see the Apache Ant user manual for more information.

Deploying to Web Servers

If the application you are developing is a Java web or J2EE application, the method of deployment will include installing the build's Web (.war) or enterprise (.ear) archive into the Web container or application server. Examples of these servers include Apache Tomcat, JBoss Application Server, and IBM WebSphere Application Server. These servers usually have some form of Web-based administration console for installation. However, in most cases they also support automated installation via a set of Java classes or, at worst, the command line. The remainder of this section walks through an example of how to achieve deployment using Ant to both Apache Tomcat and WebSphere Application Server.

Apache Tomcat is an open-source servlet container that can be used to run Java web applications. It includes a set of Ant tasks for installing, listing, and removing Web archives. These tasks are included in the file catalina-ant.jar, which is held in the server/lib directory of the Tomcat installation. I recommend copying this file into your JavaTools libs directory so that these tasks are version-controlled. Before you can automate the installation, Tomcat should be installed at a known URL, and a Tomcat deployment user should be created with the "manager" role. Tomcat users can be defined either by creating an entry in the tomcat-users.xml file or via the Tomcat Web administration tool, which is available from http://servername/admin/index.jsp. See the Tomcat documentation for more information. Once this has been completed, you can include this information in your default.properties or build.properties file as follows:

name.url.manager = http://server01:8080/manager name.user.manager = depmgr name.password.manager = password name.web.context = /RatlBank

In this example a deployment user called depmgr has been created, and the Tomcat manager (which is used as a parameter for the installation tasks) is available at the URL http://server01:8080/manager.

Next, an Ant target called deploy can be created to deploy a Web application:

<target name="deploy" depends="release, undeploy"> <!-- define the install task --> <taskdef name="install" classname="org.apache.catalina.ant.InstallTask"> <classpath> <path location="${env.ANT_HOME}/lib/catalina-ant.jar"/> </classpath> </taskdef> <!-- convert relative path to fully qualified path --> <pathconvert dirsep="/" property="fullWarDir"> <path> <pathelement location="${dir.stage}/web/${ant.project.name}.war"/> </path> </pathconvert> <!-- deploy the application --> <install url="${name.url.manager}" username="${name.user.manager}" password="${name.password.manager}" path="${name.web.context}" war="jar:file:/${fullWarDir}!/"/> </target>

This task essentially has three parts. The first part defines a new task called <install> using the InstallTask class from the catalina-ant.jar file. The second part converts the path to the .war file in the dir.stage directory (our Staging VOB) into a fully qualified path suitable for installation. Finally, the <install> task is executed as the user defined earlier using the fully qualified path to the .war file.

For deployment to be successful, you also need to undeploy any old versions of the application that have already been installed. To achieve this, you can create an Ant target called undeploy:

<target name="undeploy"> <!-- define the remove task --> <taskdef name="remove" classname="org.apache.catalina.ant.RemoveTask"> <classpath> <path location="${env.ANT_HOME}/lib/catalina-ant.jar"/> </classpath> </taskdef> <!-- remove the app --> <remove url="${name.url.manager}" username="${name.user.manager}" password="${name.password.manager}" path="${name.web.context}"/> </target>

This example is similar to the deploy target, the difference being that this target defines a new task called <remove> using the RemoveTask class from the catalina-ant.jar file. With these targets in place, you can execute a Release Deployment simply by executing this command:

>ant deploy

Since the deploy target is also dependent on the release and undeploy targets, its execution also triggers a build, packaging, and removal of any existing application from the Tomcat Server.

Deploying to Application Servers

IBM WebSphere Application Server (WAS) is the leading commercial runtime container for Java web and J2EE-based applications. You can use a number of approaches to automate the deployment of applications to a WAS infrastructure. A copy of Ant and some Ant tasks for deployment come preinstalled with WAS. However, if you are creating build and release scripts for the complete software development life cycle (as discussed in this book), you probably want to take advantage of the capabilities in the latest version of Antthis was downloaded and built in Chapter 4, "Defining your Build and Release Scripts." Fortunately, WAS also has a command-line interface that can be invoked via the wsadmin.bat or wsadmin.sh commands and that you can call from Ant. Deploying an application to WAS can actually consist of multiple calls to the wsadmin command. I therefore recommend creating an Ant macro that wraps a call to wsadmin, as in the following example for Windows:

<!-- macro for executing a WAS operation --> <macrodef name="was-op"> <attribute name="host"/> <attribute name="port"/> <attribute name="command"/> <sequential> <exec executable="cmd.exe" os="Windows 2000, Windows XP, Windows 2003"> <arg line="/c ${name.was.dir}\bin\wsadmin.bat-conntype SOAP"/> <arg line="-host @{host} -port @{port} -lang jacl"/> <arg line="-c '@{command}'"/> </exec> </sequential> </macrodef>

Macros should be familiar from Chapter 5. In this example three parameters are required for the macro: the hostname and port that the WAS command is to execute on, and the command itself. To deploy or undeploy an application to WAS, you first define a set of properties in your default.properties or build.properties file:

name.was.dir = D:\IBM\WebSphere\AppServer name.was.host = server01 value.was.port = 8880

Then you include targets similar to the following in your build.xml file:

[View full width]

<!-- deploy the EAR file to the WAS instance --> <target name="deploy" depends="package"> <was-op host="${name.was.host}"" port="${value.was.port}" command="$AdminApp install ${dir.stage}/web/RatlBank.ear"/> <was-op host="${name.was.host}" port="${value.was.port}" command="$AdminConfig save"/> <was-op host="${name.was.host}" port="${value.was.port}" command="$AdminControl invoke [$AdminControl queryNames type=ApplicationManager,*] startApplication RationalBank"/> </target> <!-- undeploy the EAR file from the WAS instance --> <target name="undeploy"> <was-op host="${name.was.host}" port="${value.was.port}" command="$AdminControl invoke [$AdminControl queryNames type=ApplicationManager,*] stopApplication RationalBank"/> <was-op host="${name.was.host}" port="${value.was.port}" command="$AdminApp uninstall RationalBank"/> <was-op host="${name.was.host}" port="${value.was.port}" command="$AdminConfig save"/> </target>

This example deploys or undeploys the RationalBank application via the file ${dir.stage}/web/RatlBank.ear. The targets are a direct replacement for those defined for deploying to Apache Tomcat, as defined earlier. Note that this is a simple example for deployment to a single WAS instance. More complex environments could have multiple servers and connections. For more information on configuring on and deploying to WAS environments, see Barcia, Hines, Alcott, and Botzum [Barcia04].

Rapid Deployment Using WebSphere

For rapid deployment to development servers, you can also use the WebSphere Rapid Deploy capability. Using this capability you can configure WAS to monitor a specific file system directory. Then, whenever a Java web or J2EE archive is copied to that directory, WAS automatically deploys it to the development server for you. The files you copy to this location do not even need to be packaged together into an archive; you can simply copy unstructured Web files. For example, .jsp files and WAS automatically generate the packaged archive for you. Similarly, deleting or updating files in this monitored directory also undeploys or redeploys the archive to WAS for you.

Interactive Deployment

Normally, your Ant scripts execute noninteractively. In other words, you specify a target for Ant to execute, and then Ant executes the sequence of dependent targets and tasks that are required to achieve that target without any further input. However, you can also create targets that prompt the user for input. This mechanism can be used if you want Ant to be executed by a novice user, or maybe a tester or deployment manager, and you want to prompt for and request configuration information. To request information, you can use Ant's <input> task, which displays a message (such as Enter installation directory) and sets a property with a value based on the user's response. For example, a target to prompt the user for a username and password for the <ftp> task would be as follows:

<target name="ftp"> <input message="FTP username:" addproperty="ftp.user" defaultvalue="user"/> <input message="FTP password:" addproperty="ftp.password" defaultvalue="password"/> <ftp server="ftp.ratlbank.com" remotedir="${ant.project.name}" user password="${ftp.password}" verbose="yes"> <fileset dir="${dir.stage}/web"> <include name="${ant.project.name}.war"/> <include name="ccblreport.html"/> </fileset> </ftp> </target>

This example should be familiar from the section "Deploying to File Systems." However, this time, rather than the properties being specified in a properties file or via the command line, they are dynamically set by the <input> task. If this target were executed, you would see output similar to the following:

>ant ftp Buildfile: build.xml ftp: [input] FTP username: user [input] FTP password: password [ftp] sending files [ftp] transferring ...\web\RatlBank.war [ftp] transferring ...\web\ccblreport.html [ftp] 2 files sent BUILD SUCCESSFUL Total time: 11 seconds

If the user does not enter a string, the properties are set to the value of the defaultvalue attribute.

Remember That Properties Cannot Be Overridden!

In Chapter 4, I stated that properties are immutable: once they have a value, they cannot be overridden. Therefore, if you are using the <input> task, you should ensure that the property you are setting does not already have a defined value.

As well as setting property values for dynamically configuring Ant tasks, you can use the <input> task to request a response from a specific set of values. For example, to request a "yes" or "no" response, you could use the following:

<target name="delete-check"> <input message="Delete existing data from database (y/n)?" validargs="y,n" addproperty="do.db-delete"/> </target> <target name="delete-db" depends="delete-check" if="do.db- delete"> <!-- delete database tables --> ... </target>

This example asks the user whether he or she wants to delete existing data from a database and sets the property do.db-delete with his or her response. The difference is that this time the <input> task allows only values of y or n for the response. The property is then subsequently used by the delete-db target to see if its tasks should be executed.

Other examples where you could use the <input> task are to dynamically request an environment name, such as UAT or LIVE, or to configure the Web or J2EE archive deployment descriptors dynamically (such as the JDBC database connection name in the web.xml file).

Категории