JBoss Embedded and Maven
My current project uses the Embedded Jboss container. If your not familiar with this piece of work, basically, it is an attempt by the Jboss guys to allow your applications targeted to the Jboss container to run within the its own classloader. That means that your EJBs, SEAM components, MDBs etc can be used inside of a standalone application, JUnit tested outside of the Jboss container, or even embedded into another application server (!*&#$*@). Cool, right? Yes, but with some strong words of caution. The Jboss embedded container is still in BETA which means … that its still a bit wabi sabi (to put it gently). Anyhow, its a great idea whose time is finally here. One problem though… being the maven kind of guy, how can I get all my POMs configured so that the embedded container is available without having to copy the 20 Mbs of embedded-ness into each project that I want to use it with? Well, here is the recipe that I put together for your reading pleasure.
Step 1
Grab the latest and the greatest embedded jboss. You can pick up the latest download location from here: http://wiki.jboss.org/wiki/EmbeddedJBoss. Create a maven project to hold all of this code and all of the Jboss Embedded dependencies. Here is a sample:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>your.group.id</groupId> <artifactId>jboss-embedded</artifactId> <name>${project.artifactId}</name> <version>1.0-SNAPSHOT</version> <description> Project encapsulates the Jboss embedded container and any dependencies with the container. </description> <repositories> <repository> <releases> <enabled>true</enabled> <updatePolicy>always</updatePolicy> <checksumPolicy>warn</checksumPolicy> </releases> <snapshots> <enabled>false</enabled> <updatePolicy>never</updatePolicy> <checksumPolicy>fail</checksumPolicy> </snapshots> <id>jboss</id> <name>JBoss Repository</name> <url>http://repository.jboss.com/maven2</url> <layout>default</layout> </repository> </repositories> <dependencies> <dependency> <groupId>org.jboss.embedded</groupId> <artifactId>jboss-embedded-all</artifactId> <version>beta3</version> </dependency> <dependency> <groupId>org.jboss.embedded</groupId> <artifactId>jboss-embedded</artifactId> <version>beta3</version> </dependency> <dependency> <groupId>org.jboss.embedded</groupId> <artifactId>thirdparty-all</artifactId> <version>beta3</version> </dependency> <dependency> <groupId>org.jboss.embedded</groupId> <artifactId>hibernate-all</artifactId> <version>beta3</version> </dependency> </dependencies> </project>
Crack open the Jboss-embedded zip file that you downloaded from sourceforge. Pull out the “bootstrap” folder and copy its contents (not including bootstrap itself) into the src/main/resources directory of this project. At this point, this project is totally configured. Do a mvn install to make it available to other projects that we will need momentarily.
Step 2
Create yourself a parent pom to encapsulate all of the settings that you would like across each project that will use the embedded container. In our world, we have service projects that hold ejb3 components. So it made sense to go ahead and create a service-pom project that can serve as the parent for all “service” projects. I would use the cool import feature, but unfortunately, we need more than just dependencies as you’ll see in a moment. Take a look at the sample POM below.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>your.group.id</groupId> <artifactId>service-pom</artifactId> <name>${project.artifactId}</name> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <description> Encapsulate all the service dependencies and configuration. </description> <repositories> <repository> <releases> <enabled>true</enabled> <updatePolicy>always</updatePolicy> <checksumPolicy>warn</checksumPolicy> </releases> <snapshots> <enabled>false</enabled> <updatePolicy>never</updatePolicy> <checksumPolicy>fail</checksumPolicy> </snapshots> <id>jboss</id> <name>JBoss Repository</name> <url>http://repository.jboss.com/maven2</url> <layout>default</layout> </repository> </repositories> <build> <testResources> <testResource> <directory>target/jboss-embedded</directory> </testResource> </testResources> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> <plugin> <artifactId>maven-ejb-plugin</artifactId> <configuration> <ejbVersion>3.0</ejbVersion> <archive> <manifest> <addClasspath>true</addClasspath> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>unpack</id> <phase>generate-sources</phase> <goals> <goal>unpack</goal> </goals> <configuration> <artifactItems> <artifactItem> <groupId>your.group.id</groupId> <artifactId> jboss-embedded </artifactId> <type>jar</type> <overWrite>true</overWrite> <outputDirectory> target/jboss-embedded </outputDirectory> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> </plugins> </build> <reporting> <plugins> <plugin> <artifactId>maven-javadoc-plugin</artifactId> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> </plugin> <plugin> <artifactId>maven-clover-plugin</artifactId> </plugin> </plugins> </reporting> <dependencies> <dependency> <groupId>your-group-id</groupId> <artifactId>jboss-embedded</artifactId> <version>1.0-SNAPSHOT</version> <scope>provided</scope> </dependency> </dependencies> </project>
Ok, now this one will need some explaining. There are really 3 important things here. Starting with the most important, declare the dependency on your jboss-embedded project. Number 2, you have to add a reference to the maven-dependency-plugin. Basically what this snippet does, is take the jboss-embedded project from your maven repository, and explode its contents into your target/jboss-embedded directory. Notice how I bound the unpack into the generate-sources phase. This seemed like the most reasonable place to bind it in. Turns out that the embedded container can’t simply bootstrap itself if the configs are encapsulated in a jar file. There might be a way to do this, but I haven’t been able to figure it out. Now to the last important step. You have to add a testResources section to tell maven about the new directory. I chose to put the embedded container in the “target” tree so that when I do a mvn clean, it gets blown away properly.
Final Step
Any project that needs the embedded container functionality now just has to extend from this parent pom and your good to go. Here is a sample pom.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>your.group.id</groupId> <artifactId>service-pom</artifactId> <version>1.0-SNAPSHOT</version> </parent> <groupId>your.group.id</groupId> <artifactId>really-cool-service</artifactId> <name>${project.artifactId}</name> <version>1.0-SNAPSHOT</version> <packaging>ejb</packaging> <description> Sample Service Implementation </description> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> </dependency> </dependencies> </project>
Step for Eclipse people
If your an eclipse user like I am, don’t forget to add target/jboss-embedded to your .classpath file so that your unit tests work from within eclipse.
The Test Case
Just in case your not familiar with what an EJB3 test case looks like using the embedded container, I’ll include a snippet below:
package your.package; import javax.naming.InitialContext; import javax.naming.NamingException; import org.jboss.deployers.spi.DeploymentException; import org.jboss.embedded.Bootstrap; import org.jboss.embedded.junit.BaseTestCase; import org.jboss.virtual.plugins.context.vfs.AssembledContextFactory; import org.jboss.virtual.plugins.context.vfs.AssembledDirectory; import your.package.CoolServiceLocal; import junit.framework.Test; public class CoolServiceTest extends BaseTestCase { private static AssembledDirectory ejbJar; private static AssembledDirectory configSar; public AdjudicationServiceTest() { super("EmbeddedEjb3TestCase"); } public static Test suite() throws Exception { return preProcessedTest(CoolServiceTest.class); } public void testService() throws NamingException { InitialContext ctx = new InitialContext(); CoolServiceLocal local = (CoolServiceLocal) ctx.lookup("CoolService/local"); local.execute(DO_SOMETHING); } public static void deploy() { ejbJar = AssembledContextFactory.getInstance().create("ejbTestCase.jar"); ejbJar.addClass(your.package.CoolService.class); try { Bootstrap.getInstance().deploy(ejbJar); } catch (DeploymentException e) { throw new RuntimeException("Unable to deploy", e); } } public static void undeploy() { try { Bootstrap.getInstance().undeploy(ejbJar); } catch (DeploymentException e) { throw new RuntimeException("Unable to deploy", e); } } }
Aggregation versus inheritance in maven?
In Josh Bloch’s Effective Java we are urged to “Favor aggregation over inheritance”. It didn’t take too long to convince me that this was a good idea. I was sold so quickly because of the countless projects where good meaning java programmers have over-engineered deeply nested class hierarchies all in the name of proper OO design. No where can this be more damaging then when structuring your organization’s corpus of maven artifacts.
To give an example, imagine a centrally managed POM file with all the “approved” versions of artifacts — I don’t have to imagine this, since that is exactly what I did. In this “parent” pom was a “dependencyManagement” section with all the preferred versions of libraries within the organization. Sounds like a good idea right? Wrong! Now imagine that you have 100+ projects all inheriting from this parent project… Because all the version numbers for 3rd party jars are centrally managed, any revving of any specific jar (say log4j goes from 1.2.13 to 1.2.15) will cause a re-release of anything inheriting from it. Yup… that’s 100+ redeployments — hows that for a nasty impact analysis.
The alternative has been to not inherit from any pom for fear that it may change. For the most part, this is what I’ve been doing. Unfortunately this approach has problems too — namely dependency convergence. That is the convergence of differing versions of the artifacts declared throughout your enterprise. Maven has a whole algorithm for calculating which dependency gets included and which ones do not. Typically, the dependency declared “closest” to where you are wins. This can lead to some very frustrating side effects, where most of them sound like this, “Why in the world is THIS version of this jar included.” This has led many a frustrated engineer (including myself) to “pin” the version in the current POM, even if that project doesn’t have a dependency on that asset directly (yuck).
Well, could much of the crying and nashing of teeth be ending? Did the maven guys come up with something in between? A way to aggregate dependencies without using the parent pom? It appears that as of version 2.0.9, their is a new scope on a dependency called import. Here is a sample from a pom which uses this scope:
<dependencyManagement> <dependencies> <dependency> <groupId>maven</groupId> <artifactId>A</artifactId> <version>1.0</version> <type>pom</type> <scope>import</scope> </dependency> <dependencies> <dependencyManagement>
What this says is to import the dependencies declared in the artifact A into the current project! Time will tell whether or not this added feature will be all that it seems to be, but in the meantime, I raise my glass to the maven engineers and other contributors who continue to push these tools forward. Bravo!
-
Recent
-
Links
-
Archives
- May 2008 (2)
-
Categories
-
RSS
Entries RSS
Comments RSS