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
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
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”
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…
Polling the Repository
In this section we may specify an interval to poll the repository for changes..
Configure Maven Goals
We’re configuring Maven to run unit- and integration tests..
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.
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.
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.
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:
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:
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