In my Java EE 6 server comparison of June 2011, JBoss AS 6.0.0 was not exactly the shining star, both in terms of performance and usability.
The next major release JBoss AS 7, claiming to be lightning fast, was published in July 2011, followed by two maintenance updates in August and September.
Time to take another look at JBoss and check if it now compares more favourably to other servers.
This post is based on JBoss AS 7.0.2 (Full Profile) and the same real-world application which I had ported from Spring to Java EE 6 for the GlassFish/Resin/JBoss AS 6 comparison.
With some minor modifications, I was able to deploy the same WAR on JBoss AS 7 and to repeat the measurements I did for JBoss AS 6.
Compared to JBoss AS 6 which did not even offer a Getting Started Guide, the documentation of JBoss AS 7 has improved a lot, but there are still quite a few gaps to be closed. At any rate I found sufficient input for my experiments in the following two documents:
Preparing the serverMy application contains a JPA 2.0 persistence unit developed on Hibernate 3.6.x and MySQL 5.1, so first of all I had to configure the data source and the JDBC driver.
To do so, I added this section to the main configuration file of JBoss AS 7 in
<datasource jndi-name="jdbc/myapp" pool-name="myapp" enabled="true" jta="true" use-java-context="true" use-ccm="true"> <connection-url> jdbc:mysql://localhost/myapp </connection-url> <driver-class> com.mysql.jdbc.Driver </driver-class> <driver> mysql-connector-java-5.1.13.jar </driver> <transaction-isolation> TRANSACTION_READ_COMMITTED </transaction-isolation> <pool> <min-pool-size> 4 </min-pool-size> <max-pool-size> 30 </max-pool-size> <prefill> true </prefill> <use-strict-min> false </use-strict-min> <flush-strategy> FailingConnectionOnly </flush-strategy> </pool> <security> <user-name> MYUSER </user-name> <password> SECRET </password> </security> </datasource> <drivers> <driver name="mysql-connector-java-5.1.13.jar" module="com.mysql"/> </drivers>
Then I copied both the JDBC driver
mysql-connector-java-5.1.13.jarand my application
standalone/deployments/and started the server via
Of course, this did not work out of the box. The first exception I had was
java.lang.ClassCastException: org.dom4j.DocumentFactory cannot be cast to org.dom4j.DocumentFactory
This looked like a classloader conflict for dom4j. My WAR has a dom4j.jar in its WEB-INF/lib, but dom4j is also provided by JBoss as an transitive dependency of Hibernate. To resolve the conflict, I rebuilt my WAR without dom4j.
The next exception was
My original application included jaxb-impl-2.2.2.jar, which I'd had to remove for JBoss AS 6, so I re-added this dependency to my WAR and redeployed it.
JBoss AS 7 marks any failed deployment with a
foo.war.failedfile in the deployment directory and will not attempt to redeploy the application until the marker file is deleted.
After restarting the server, the next problem was a
org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [MyType] with qualifiers [@Default] at injection point [[field] @Inject private MyClient.myType]
This was confusing. I had
beans.xmlfiles in all my application modules, and the same WAR did not cause any CDI exceptions on other servers. So I enabled DEBUG logging for Weld to find out what was going on by adding
<logger category="org.jboss.weld"> <level name="DEBUG"/> </logger>
to the logging section of
standalone.xml. This did not have the desired effect. I also had to change the
child elements of the
ALL. (The Logging Configuration documentation does not mention this, but some additional information is hidden in the Getting Started Guide, section Configure Logging in JBoss Application Server 7.)
DEBUGoutput from Weld revealed the cause of the problem:
17:33:39,799 DEBUG [org.jboss.weld.ClassLoading] (MSC service thread 1-8) WELD-000119 Not generating any bean definitions from MyType because of underlying class loading error Caused by: java.lang.NoClassDefFoundError: Lorg/dom4j/Document;
Strange, dom4j had caused a classloader conflict at first, and now it was no longer visible at all? I had a closer look at Class Loading in AS7 to understand the background of this.
In short, JBoss AS 7 has a module structure, and server modules are not visible to applications by default, unless they are imported or exported explicitly.
Both dom4j and jaxb-impl are JBoss modules, so I re-deleted jaxb-impl and jaxb-api from my application and made the JBoss modules globally visible by adding the following to
<subsystem xmlns="urn:jboss:domain:ee:1.0"> <global-modules> <module name="org.dom4j" slot="main"/> <module name="com.sun.xml.bind" slot="main"/> </global-modules> </subsystem>
(Global visibility is not the best solution, of course, but for this experiment I would rather modify the server configuration than add JBoss-specific manifest headers or deployment descriptors to my application WAR.)
Finally, I had another ClassNotFoundException for an internal Xerces class from the JDK required by my application. (Yes, such a dependency is bad practice, but as I said above, I'm using a real-world application for these tests...)
I made this class visible to my application by adding
modules/sun/jdk/main/module.xml- no idea if you are supposed to do it this way, but it works for me...
Now at last I was able to deploy and run my application and take some measurements.
The following table compares some performance indicators for JBoss AS 7.0.2, JBoss AS 6.0.0 and GlassFish 3.1.1. For JBoss AS 6, I did not repeat the measurements but simply copied the results of June 2011. For GlassFish, I had been working with a pre-release build at that time, so I've now re-run my tests with the official GlassFish 3.1.1 release.
|JBoss AS 7.0.2||JBoss AS 6.0.0||GlassFish 3.1.1|
|Empty server startup time||1.9 s||12 s||3.2 s|
|Empty server heap memory||10.5 MB||100 MB||26.5 MB|
|Empty server PermGen memory||36.3 MB||70 MB||28.4 MB|
|MyApp deployment time||5.8 s||47 s||13 s|
|Server + MyApp restart time||8 s||30 s||14.5 s|
|Server + MyApp heap memory||52.8 MB||236 MB||55.3 MB|
|Server + MyApp PermGen memory||80.9 MB||175 MB||84.5 MB|
|MyApp redeployment time||3.5 s||30 s||7 s|
All in all, there is an amazing improvement of performance and memory footprint in JBoss AS 7, compared to JBoss AS 6. JBoss AS 7 is now at a competitive level with Resin and Glassfish and actually outperforms Glassfish in almost all of these tests.
I don't know if this indeed qualifies as lightning fast, but it is now safe to say that JBoss AS 6 was blatantly fat and slow, as confirmed by its own successor.
I tested redeploying my application by simply touching the WAR file in the
deploymentsdirectory and watching the memory usage via jvisualvm.
I could see the PermGen usage grow at a constant rate, and after a couple of redeployments, there was an OutOfMemoryError, so JBoss AS 7 appears to have a new classloader leak. (The same test did pass successfully on JBoss AS 6.)
In contrast to the drastic improvements of the server itself, I could find no significant progress with the Eclipse integration. I installed the JBoss AS Tools from JBoss Tools 3.3.0.M3 into a fresh copy of Eclipse Indigo 3.7.1 with m2e 1.0 and m2e-wtp 0.14.0.
I was able to deploy my application directly from the workspace, but editing and saving a source file did not redeploy the application. An Incremental Publish did not help either. Only a Full Publish was sufficient to redeploy the application.
This is a major productivity issue for Eclipse users. Redeploy-on-save works smoothly with the built-in Tomcat integration of Eclipse WTP, so something must be broken in JBoss AS Tools.
JBoss AS 7.0.2 comes bundled with Hibernate 4.0.0.CR2. This is a bad smell - an official product release should never contain any release candidates of upstream components, especially not for components originating from the same company.
The good news is that my application which was developed on Hibernate 3.6.0 works without problems on this Hibernate 4.x release candidate.
But the real bad news is that JBoss AS 7 does not currently support other persistence providers like Eclipselink, OpenJPA or DataNucleus (according to the JPA Reference Guide). With GlassFish and Resin, you can simply drop the JARs of your preferred provider and its dependencies in a designated folder of your server installation and edit your
persistence.xmlto override the default provider of the server.
JBoss AS 7 appears to require an adapter per persistence provider, which to me looks like an unfortunate and unnecessary design decision.
At any rate, this lack of flexibility is a severe restriction. In most of my projects, I had to replace Hibernate by OpenJPA due to a number of bugs related to new JPA 2.0 features.
Given the drastic performance improvements in JBoss AS 7, which was released only six months and a half after JBoss AS 6, I can only wonder why RedHat and the JBoss community ever spent any resources on an AS 6 release which clearly was not competitive, instead of focusing on AS 7.
Anybody looking for a production-quality Java EE 6 server in the first half of 2011 was better off with GlassFish 3.1.1 or one of its pre-releases.
With AS 7, JBoss is now back in the game, it is surprisingly lean and fast, and it even has the potential to take over the lead from GlassFish.
But performance is not the only factor that counts. The documentation continues to be sketchy and far below the standard of JBoss AS 5.
Different projects have different needs, but for me, the top-level performance of JBoss AS 7 is more than outweighed by classloader leaks, productivity issues of the Eclipse integration and lack of support for JPA providers other than Hibernate.
Each of these is currently a blocker for using JBoss AS 7 in production. GlassFish 3.x has taken more than a year to reach production quality. Let's see if JBoss AS 7.x can do the same lightning fast.