Continuous Deployment using GlassFish, Jenkins, Maven and Git

May 29th, 2013 by

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 neccesary 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
The application deployed on the GlassFish server.

The application deployed on the GlassFish server.

Running the JEE Web Application

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”

Creating a new maven 2/3 job on the integration server.

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…

Setting the git repository.

Setting the git repository.

Polling the Repository

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

Set up the repository polling.

Set up the repository polling.

Configure Maven Goals

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

Configuring Maven.

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.

Test execution in job 1.

Test execution in job 1.

Job 1 finished successfully.

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.

Cloning an existing job.

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.

Maven / Cargo Configuration.

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:

Trigger job #2 using a post-build-action.

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:

Dependant build triggered by post-build-action.

Dependant build triggered by post-build-action.

Build- and deployment success.

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 Bitbucket repository, fork it there or clone it using Git:

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

Resources

Tags: , , , , , , , , , , ,

3 Responses to “Continuous Deployment using GlassFish, Jenkins, Maven and Git”

  1. Ivo Says:

    I was just wandering how to do that. Very good tutorial. Thank you.

  2. Tomek Says:

    Great article,

    How to deploy to remote glassfish when we have access to this glassfish machine by ssh ?

  3. micha kops Says:

    Basically it’s just these steps (I’ll write them up later):
    - Install the SSH/SCP Plugin for Jenkins (e.g. https://wiki.jenkins-ci.org/display/JENKINS/SCP+plugin)
    - Configure the SSH access to the designated server in the global config so that the SSH server is available in your build. When using the SCP plugin, specify a valid root repository path
    - Add a step to your build to copy your war file to the remote server via scp to your domain’s autodeploy directory

Search
Categories