DevOps Zone is brought to you in partnership with:

Software Architect at Lynden, Inc. Working with Enterprise Java technology as well as the Netbeans Rich Client Platform for desktop applications. Rob has posted 14 posts at DZone. You can read more from them at their website. View Full User Profile

Creating a Deployment Pipeline with Jenkins, Nexus, Ant and Glassfish

06.17.2012
| 10132 views |
  • submit to reddit

In a previous post I discussed how we created a build pipeline using Jenkins to create application binaries and move them into our Nexus repository. (Blog post here

In this post I will show how we are using Jenkins to pull a versioned binary out of Nexus and deploy to one of our remote test, staging or production Glassfish servers.  By remote I mean that the Glassfish instance does not live on the same box as the Jenkins CI instance, but both machines are on the same network.

In a previous post I also discussed how to set up Glassfish v3 to allow deployments pushed from remote servers (Blog post here), so if you haven't explicitly configured your Glassfish to allow this feature, you will need to do so before you get started.

On our Jenkins CI box we have an Ant script, which will be executed by a Jenkins job manually kicked off by a user. The script defines tasks to perform the following operations:

  • Ensure all needed parameters were entered by the user. (app name, version
    number, admin username/password, etc).
  • Copy the specified version of the application from Nexus to a local temp
    directory for deployment to a remote Glassfish instance.
  • Undeploy a previous version of the app from the target Glassfish instance. 
    (Optional).
  • Deploy the app from the temp directory to the target Glassfish instance.
  • Record the deployment info in a deployment tracking database table. 
    Historical deployment info can then be viewed from a web app.


Ant Script


Below are some of the more interesting code snippets from our Ant script that
will be doing the heavy lifting for our deployment pipeline.  The first code
snippet below defines the Ant tasks needed for deploying and undeploying
applications from Glassfish.  These Ant tasks are bundled with Glassfish, but
not installed by default.  If you haven't installed them, you will need to do so
from your Glassfish update center admin page.
<taskdef name="sun-appserv-deploy" classname="org.glassfish.ant.tasks.DeployTask">
   <classpath>
      <pathelement location="/nas/apps/cisunwk/glassfish311/glassfish/lib/ant/ant-tasks.jar"/>
   </classpath>
</taskdef>
    
<taskdef name="sun-appserv-undeploy" classname="org.glassfish.ant.tasks.UndeployTask">
   <classpath>
      <pathelement location="/nas/apps/cisunwk/glassfish311/glassfish/lib/ant/ant-tasks.jar"/>
   </classpath>
</taskdef>    

Once we have the tasks defined, we create a new target for pulling the binary
from Nexus and copying it to a temporary location from where it will be deployed
to Glassfish.

<target name="copy.from.nexus">
   <echo message="copying from nexus"/>    
   <get src="http://cisunwk:8081/nexus/content/repositories/Lynden-Java-Release/com/lynden/${app.name}/${version.number}/${app.name}-${version.number}.${package.type}" dest="/tmp/${app.name}-${version.number}.war"/>
</target>

Next is a target to undeploy a previous version of the application from
Glassfish.  This step is optional and only executed if the user specifies a
version to undeploy from Jenkins.

<target name="undeploy.from.glassfish" if="env.Undeploy_Version">
   <echo message="Undeploying app: ${app.name}-${undeploy.version}"/>
   <echo file="/tmp/gf.txt" message="AS_ADMIN_PASSWORD=${env.Admin_Password}"/>
   <sun-appserv-undeploy name="${app.name}-${undeploy.version}" host="${server.name}" port="${admin.port}" user="${env.Admin_Username}" passwordfile="/tmp/gf.txt" installDir="/nas/apps/cisunwk/glassfish311"/>
   <delete file="/tmp/gf.txt"/>
</target>

Next, we then define a target to do the deployment of the application to
Glassfish.

<target name="deploy.to.glassfish.with.context" if="context.is.set">
    <sun-appserv-deploy file="/tmp/${app.name}-${version.number}.war" name="${app.name}-${version.number}" force="true" host="${server.name}" port="${admin.port}" user="${env.Admin_Username}" passwordfile="/tmp/gf.txt" installDir="/nas/apps/cisunwk/glassfish311" contextroot="${App_Context}"/>
</target>

And then finally, we define a target, which will invoke a server and pass
information to it, such as app name, version, who deployed the app, etc.so that
it can be recorded in our deployment database.

<target name="tag.uv.deploy.file">
   <tstamp>
      <format property="time" pattern="yyyyMMdd-HHmmss"/>
   </tstamp> 

   <!--Ampersand character for the URL -->
   <property name="A" value="&amp;"/>
   <get src="http://shuyak.lynden.com:8080/DeploymentRecorder/DeploymentRecorderServlet?app=${app.name}${A}date=${time}${A}environment=${deploy.env}${A}serverName=${server.name}${A}serverPort=${server.port}${A}adminPort=${admin.port}${A}serverType=${server.type}${A}version=${version.number}${A}who=${deploy.user}${A}contextName=${App_Context}" dest="/dev/null"/>

   <echo message="tagging deployment info to UV"/>
</target>


Jenkins Configuration

Now that we have an Ant script to perform the actions that we need to do a
deployment, we set up a Jenkins job to deploy to servers in each one of our
environments (test/staging/prod). 


In order to kick off a deployment to one of our servers, the appropriate
environment is selected from the screenshot above, and the "Build Now" link is
clicked which presents the user with the screen below. In this case we are
deploying to a test Glassfish domain named "bjorn" on unga.lynden.com


The user can select from the drop down list the server they wish to deploy to
and the application they wish to deploy.  The version number is a required entry
in a text field.  If the script can't find the specified version in Nexus, the
build will fail.  There are also optional parameters for specifying an existing
version to undeploy as well as an application context in the event the app name
shouldn't be used as the default context.


In the screenshot below we are deploying version 5.0.0 of the Crossdock App
to the "bjorn" domain running on unga.lynden.com


Once the job completes, if we log into the bjorn Glassfish admin page on
unga.lynden.com, we see that Crossdock-5.0.0 has been deployed to the
server.

The screenshot below is an example of undeploying version 5.0.0 of Crossdock,
and deploying version 5.0.1 of Crossdock.  Also, in this example, we are telling
the script that we want the web context in Glassfish to be /Crossdock, rather
than the default /Crossdock-5.0.1

The screenshot of the Glassfish admin page below shows that
Crossdock-5.0.0 has been unistalled, and that Crossdock-5.0.1 is now installed
with a Context Root of /Crossdock.

Deployment History

Finally, as I mentioned previously, the Ant script is also saving the
deployment information to a historical deployment table in our database.  We
have written a simple web application which will display this historical data. 
The screenshot below shows all of the applications that have been deployed to
our test environment. (We have similar pages for Staging and Production as
well).  Included in this information is the application name, version number,
date, and who deployed it, among some other miscellaneous info.

 

We can then drill into the history of a specific application by clicking the
"Crossdock" link on the screen above and get a detailed history about the
deployments for that application including version numbers or dates.  We
maintain more than 60 different web applications serving various purposes here
at Lynden, so this has been a great tool us to see exactly what versions of our
applications are currently deployed where, as well as see the history of the
deployment of a specific application in the event we need to roll back to a
previous version.

As we have learned firsthand, Jenkins is a very useful and versatile tool
that is easy to extend for purposes beyond automated builds and continuous
integration.

Published at DZone with permission of its author, Rob Terpilowski.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)