Recently I needed a quick solution to deploy a Java EE 6 web application on a GlassFish instance automatically and subsequent to a successful build of the project on the integration server.

It took only a few steps using Jenkins, Maven and the Cargo plugin and I’d like to share this quick solution with you here.

Prerequisites

We need the following software installed and configured:

GlassFish Setup

We’re setting up a new domain named myapp with an administrator account admin and password admin:

asadmin> create-domain mydomain
Enter admin user name [Enter to accept default "admin" / no password]> admin
Enter the admin password [Enter to accept default of no password]>
Enter the admin password again>
Using default port 4848 for Admin.
Default port 8080 for HTTP Instance is in use. Using 50447
Using default port 7676 for JMS.
Using default port 3700 for IIOP.
Using default port 8181 for HTTP_SSL.
Using default port 3820 for IIOP_SSL.
Using default port 3920 for IIOP_MUTUALAUTH.
Using default port 8686 for JMX_ADMIN.
Using default port 6666 for OSGI_SHELL.
Using default port 9009 for JAVA_DEBUGGER.
Distinguished Name of the self-signed X.509 Server Certificate is:
[CN=localhost,OU=GlassFish,O=Oracle Corporation,L=Santa Clara,ST=California,C=US]
Distinguished Name of the self-signed X.509 Server Certificate is:
[CN=localhost-instance,OU=GlassFish,O=Oracle Corporation,L=Santa Clara,ST=California,C=US]
No domain initializers found, bypassing customization step
Domain mydomain created.
Domain mydomain admin port is 4848.
Domain mydomain admin user is "admin".
Command create-domain executed successfully.
asadmin> start-database
Database started on host 0.0.0.0, port 1527.
Command start-database executed successfully.
asadmin> start-domain mydomain
Waiting for mydomain to start ...
Successfully started the domain : mydomain
[..]
Command start-domain executed successfully.

Our web application runs on port 50447 because port 8080 is already in use on my machine because I’m running Jenkins and GlassFish on the same host for this tutorial.

Project Setup / Git

To continue this tutorial we need a Java EE 6 application and of course some tests to run for the project.

For this purpose I am using the source from my tutorial Arquillian Tutorial: Writing Java EE 6 Integration Tests and more.. here.

The project contains a JEE web application and Arquillian based integration tests for the repository layer implemented using stateless session beans.

I don’t want to configure a Selenium grid here that’s why I have removed one integration tests using WebDriver and Arquillian Drone here – perhaps it is the topic of another tutorial later ;)

To make our life easier here, I’ve setup this modified project as a clonable repository here:

git clone https://bitbucket.org/hascode/glassfish-continuous-deployment-tutorial.git

Maven and Cargo Configuration

We’re using the Cargo Plugin for Maven to deploy the application, configured for a dedicated target container: glassfish3x.

Configuration for the deployment container and its credentials may be added to the plugin configuration but should be kept separate using placeholders or dedicated Maven profiles.

Please note the additional dependency for deployment-client – this is necessary to get a JSR-88 compatible deployment client.

Adding the following lines to the project’s pom.xml should do the trick:

<build>
	<plugins>
		<plugin>
			<groupId>org.codehaus.cargo</groupId>
			<artifactId>cargo-maven2-plugin</artifactId>
			<version>1.3.3</version>
			<configuration>
				<container>
					<containerId>glassfish3x</containerId>
					<type>remote</type>
				</container>
				<configuration>
					<type>runtime</type>
					<properties>
						<cargo.hostname>localhost</cargo.hostname>
						<cargo.remote.username>admin</cargo.remote.username>
						<cargo.remote.password>admin</cargo.remote.password>
						<cargo.remote.port>50447</cargo.remote.port>
						<cargo.glassfish.domain.name>/myapp</cargo.glassfish.domain.name>
					</properties>
				</configuration>
				<deployables>
					<deployable>
						<groupId>${project.groupId}</groupId>
						<artifactId>${project.artifactId}</artifactId>
						<type>war</type>
						<properties>
							<context>/myapp</context>
						</properties>
					</deployable>
				</deployables>
			</configuration>
			<dependencies>
				<dependency>
					<groupId>org.glassfish.deployment</groupId>
					<artifactId>deployment-client</artifactId>
					<version>3.2-b06</version>
				</dependency>
			</dependencies>
		</plugin>
	</plugins>
</build>

Now we’re ready on the project and we just need to add some configuration to the integration server..

Testing Maven Deployment

We should test if we’ve set-up everything correctly here. Running the following command should have deployed the application on the GlassFish, accessible at the following location: http://localhost:50447/myapp/

mvn package cargo:deploy
application deployed on glassfish
Figure 1. The application deployed on the GlassFish server.
running the application
Figure 2. Running the JEE Web Application

Jenkins/Hudson Build Configuration

Now we’re nearly done – all we need now is to setup two builds on the integration server:

  • Build #1: This is just a regular build that runs the integration tests – the only special thing here is that a successful build triggers a dependant build, Build #2

  • Build #2: This build builds and deploys the application on the target GlassFish server instance

Build #1: Regular Build

First of all, we’re creating a new Maven based job..

Create a Maven based build

We’re creating a new job using the preset “Build a maven 2/3 project” named “my-jee-app”

create normal build
Figure 3. Creating a new maven 2/3 job on the integration server.

Specify Git Repository

In this step we’re referencing the git repository we want to poll. To keep it simple, I’m using a local repository on my file system here…

configure build 1 git repo
Figure 4. Setting the git repository

Polling the Repository

In this section we may specify an interval to poll the repository for changes..

build1 periodic polling
Figure 5. Set up the repository polling

Configure Maven Goals

We’re configuring Maven to run unit- and integration tests..

build1 maven configuration
Figure 6. Configuring Maven

Testing the Build

Now when we’re initiating the build by selecting "Build now" we should be able to watch Jenkins fetching the sources from the git repository and executing the test cases successfully.

build1 running tests
Figure 7. Test execution in job 1
build1 build success
Figure 8. Job 1 finished successfully

Build #2: Deploying on a GlassFish

Now we’re ready to create another job on our integration server to deploy the application when the first job running the tests finished successfully.

Clone existing Build

We’re simply cloning our first job here and name our new build “my-jee-app-DEPLOYMENT” to reflect the purpose of this job.

build2 clone from existing
Figure 9. Cloning an existing job

Configure Maven / Cargo

In the next step, we’re adding Maven goals to redeploy the application on the GlassFish server using cargo:redeploy.

We’re skipping all tests here because they’ve already been run in job #1.

build2 configure maven
Figure 10. Maven / Cargo Configuration

Build #1: Add Post-Build Action

We need to add one last post-build-action to trigger the execution of job#2 when job#1 run without an error:

final build1 add post build action
Figure 11. Trigger job #2 using a post-build-action

Continuous Deployment in Action

We’re finished! When the first build is triggered and the tests do not fail, the second build is triggered and redeploys the application on the configured GlassFish instance.

The process could look similar to the following screenshots:

triggered dependant build
Figure 12. Dependant build triggered by post-build-action
dependant build success
Figure 13. Build- and deployment success

Troubleshooting

  • [ERROR] Failed to execute goal org.codehaus.cargo:cargo-maven2-plugin:1.3.3:deploy (default-cli) on project arquillian-tutorial: Execution default-cli of goal org.codehaus.cargo:cargo-maven2-plugin:1.3.3:deploy failed: Cannot locate the JSR-88 deployer class org.glassfish.deployapi.SunDeploymentFactory: You need a JSR-88 compatible deployment client – you may fix this by adding the following Maven dependency to the Cargo plugin reference:

    <dependency>
    	<groupId>org.glassfish.deployment</groupId>
    	<artifactId>deployment-client</artifactId>
    	<version>3.2-b06</version>
    </dependency>

Alternatives

There are many other alternatives – you could configure SSH credentials for a connection to the target server and simply upload the created web archive or enterprise archive to the autodeploy directory using SCP.

The solution above might not fit special requirements e.g. complex replication scenarios so please feel free to leave a comment here if you’ve some experience to share! :)

Tutorial Sources

Please feel free to download the tutorial sources from my GitHub repository, fork it there or clone it using Git:

git clone https://github.com/hascode/glassfish-continuous-deployment-tutorial.git