November 27, 2009

Integration test with Maven, Cargo and JBoss

It's Friday...

... and I thought it would be a good idea to setup an integration test of some EJBs we are creating in a new project. Actually, it was not, since it nearly ruined my evening. But eventually I got it to work, and here is how.

To test the EJBs, I need to create an EAR file, deploy that to the application server (JBoss 5.1.0 in our case) and run JUnit test cases against this server. For Maven, I have setup a separate integration-test module for executing the integration test for the following reasons:

  • to better separate unit tests (with JUnit) from integration tests (also with JUnit), which simplifies Maven configuration a bit.
  • to be able to run this module outside of the normal CI (continuous integration) build due to its lack of performance.

Cargo Maven Plugin

For automatically starting the container, deploying the EAR file and stopping the container when the tests are finished, I use the Cargo Maven plugin. I did this several times before (with Tomcat, though) – so I thought that'd be easy...

Well, when using JBoss, there are some tricks you have to know. Before going into the details, some more information on Cargo.

A Container is the base concept in Cargo. It represents an existing application server runtime, and Cargo hides all the details of the actual server implementation for you. There are two types of containers:

  • Local Container: this is executing on the machine where Cargo runs. This can either be an Installed Container which is, well, installed on the local machine and is run in a separate VM, or an Embedded Container that is executing in the same JVM where Cargo is running (currently only supported for Jetty).
  • Remote Container: a container that is already running anywhere (local or remote). It's not under Cargo's control and can't be started or stopped by Cargo.

You use a Configuration to specify how the container is configured (logging, data sources, location where to put the deployables, etc). The available configuration depends on the container type:

  • Local Configuration: for local containers. There are two local configuration types: Standalone Local Configuration which configures the container from scratch in a directory of your choice, and Existing Local Configuration that re-uses an existing container installation already residing on your hard drive.
  • Runtime Configuration: You use a runtime configuration when you want to access your container as a black box through a remote protocol (JMX, etc). This is perfect for remote containers.

In my case, I wanted to use a Local Installed Container with a Standalone Local Configuration, to eliminate dependencies from other deployments.

JBoss with Cargo

Well, and here are the pitfalls when using JBoss in this setting:

  1. Experimental: JBoss 5.x is still an experimental container for Cargo. This is a bit strange given the fact that this version is now out for a while, but fortunately not really an issue.
  2. Extensive Logging: When Cargo builds the JBoss configuration – remember, I use Standalone Local Configuration so Cargo creates one from scratch – it uses a logging setup (independantly from what is used with your JBoss installation!) that is way too chatty. The console scrolls forever, and things are slowing down in a way that you think everything is stuck in an infinite loop.
    Thus, you have to tell Cargo to use another logging configuration file, which is a bit tricky and not documented very well (see this issue).
  3. Shutdown Port: Now the container starts up, the tests are run, but after that JBoss AS is not shutting down. It's telling me javax.naming.CommunicationException: Could not obtain connection to any of these urls: localhost:1299, which means the wrong port is used for shutdown. Standard shutdown port is 1099, so we have to tell Cargo to use that port number.

All in all, the configuration now looks like this. Mentioned settings are highlighted. Perhaps this is useful for someone else...


<!-- *** Cargo plugin: start/stop JBoss application server and deploy the ear
file before/after integration tests *** -->
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.0</version>
<configuration>
<wait>false</wait>
<!-- Container configuration -->
<container>
<containerId>jboss5x</containerId>
<type>installed</type>
<home>${it.jboss5x.home}</home>
<timeout>300000</timeout>
</container>
<!-- Configuration to use with the Container -->
<configuration>
<type>standalone</type>
<home>${project.build.directory}/jboss5x</home>
<properties>
<cargo.jboss.configuration>default</cargo.jboss.configuration>
<cargo.servlet.port>${it.jboss5x.port}</cargo.servlet.port>
<cargo.rmi.port>1099</cargo.rmi.port>
<cargo.jvmargs>-Xmx512m</cargo.jvmargs>
</properties>
<deployables>
<deployable>
<groupId>com.fja.ipl</groupId>
<artifactId>ipl-lc-ear</artifactId>
<type>ear</type>
</deployable>
</deployables>
<!-- Override logging created by Cargo (which is way to chatty) with default
file from JBoss (see http://jira.codehaus.org/browse/CARGO-585) -->
<configfiles>
<configfile>
<file>src/test/resources/jboss-log4j.xml</file>
<todir>conf</todir>
</configfile>
</configfiles>
</configuration>
</configuration>

<executions>
<!-- before integration tests are run: start server -->
<execution>
<id>start-container</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<!-- after integration tests are run: stop server -->
<execution>
<id>stop-container</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
<properties>
<it.jboss5x.home>${basedir}/../../../tools/bin/jboss-5.1.0.GA</it.jboss5x.home>
<it.jboss5x.port>8080</it.jboss5x.port>
</properties>

5 comments:

  1. Hi

    Your tutorial is helping a lot. I am using seam, jboss 4.2.3 , maven 2 and cargo. I get the org.codehaus.cargo.container.ContainerException. I tried changing the RMI port to 1099 which was previously configured to 1098. But it did not help. Is this cargo related problem?

    Thanks
    Venkat

    ReplyDelete
  2. Venkat,

    I'm glad that this stuff could help a bit...

    What is the exact problem you are facing? The port is just the shutdown port, that is the JBoss container should come up and the application should be accessible without changing that port. Is that the case?

    The exception class is from Cargo, so it probably is a Cargo related problem, yes. But hard to provide any further help without any stacktrace, log file etc. You could also search or post on the Cargo mailing list (http://cargo.codehaus.org/Mailing+Lists).

    Best regards,
    ::Christoph

    ReplyDelete
  3. Christoph

    Thanks for your reply. I moved past the previos problem. Your blog helped me setup cargo maven and jboss. I am stuck at a different point. Although this place might not be ideal to ask this question, I am hoping you might be able to help me with your experience.

    Since, I have configured tests in a seperate module, maven is looking for richfaces libraries that are being used by the WAR module of the application. I have tried adding the dependencies in the test module pom, but that did not do the trick. Have you faced this kind of problem?

    I have attached a blog post that I have started in this regard.please find the link attached below.

    http://www.seamframework.org/Community/SeamTestHelp#comment129711

    Thanks
    Venkat

    ReplyDelete
  4. Venkat,

    I do not fully understand what are you trying to do, neither do I use Rich Faces or Seam. So here are just some general thoughts...

    The integration tests depend on a working EAR (or WAR) module. If you build that with Maven and deploy it manually, does it work? Are you able to connect and to call some EJB or some web pages?

    The integration test is meant to execute unit tests against the running JEE application. For instance, calling some functionality provided by one of the EJBs contained in the deployed EAR file. Is this the kind of test you are trying to execute? Does the test run successfully when started manually (not by Maven) against the deployed application? If so, what happens if you run the unit tests with Maven against the deployed application, i.e. without configuring cargo?

    What I'm puzzled about is the fact that the integration test seems to be requiring Rich Faces classes. Does that make sense?

    I still think the Maven (or maybe Rich Faces) mailing lists are more appropriate for questions like this...

    Regards,
    ::Christoph

    ReplyDelete
  5. FYI: CARGO-585 has now been fixed (Cargo 1.0.3). So you can remove that extra configuration.

    ReplyDelete