Friday, December 30, 2011

JBoss 7 getting started

Introduction

Please be aware that I am in the process of upgrading this document to JBoss 7.1/7.2. Some screenshots may still be outdated

I like JBoss, it is the application server with the least amount of issues IMO. JBoss 5.1 was good but slow at startup, JBoss 6 was technologically a whole lot better and even had preliminary JEE6 web profile support but was still slow starting up and not officially supported.

And then there was JBoss 7.x. I must state up front and honestly: JBoss 7.x is one of the finest pieces of Java engineering I've ever seen. It starts up in mere seconds, needs minimal configuration and it is actually designed around a true production environment with many professional deployment options. This software really is rock solid and cool. On top of that JBoss seems to make the effort to write proper documentation that you can actually use (try to read the Glassfish documentation... ugh!). Hurray!

Where it blows up for me is the incredibly vague support around the product. The public versions you can download on the site are horribly outdated and the versions that are up to date (the EAP versions) are basically not for community usage, you need a costly support contract to be allowed to deploy them for production usage. Which is not nice because since JBoss 7.1.1 (the latest downloadable 'free' version) hundreds of bugs have been fixed, some of them quite critical showstoppers.

In this article I hope to not only get you up and running with an AS installation for development purposes, but also perhaps help you to get a patched version that is slightly more of this time.

Assumptions made in this article: You are familiar with JEE development, at least JEE5 spec. This is not a tutorial on JEE development though, the main focus of the article is to get up and running with JBoss 7.x. At times I'll make references to how things were done in older JBoss environments, in case you are migrating.

On the topic of migration: if you are migrating or are investigating to migrate, this document provided by Jboss might be of vital importance.

Download stuff (or build)

To easily work with JBoss 7.x in a development environment, download:

1: The server software
2: Eclipse EE version
3: Be sure to install the latest JBoss Tools eclipse plugin, at least version 3.3.0 but if there is a newer version take that, preferably a stable one. When installing a stable release, you should use the Eclipse Marketplace in stead of the download site.

Whichever path you choose, be sure to install minimally the JBossAS tools to get the JBoss 7.x runtime connectors. If you use Maven, also be sure to install the JBoss Tools maven integration. Other tools are up to your own preferences.

Unpack JBoss 7.x in a directory of your choosing. If you want to run JBoss from the command prompt also or use one if its tools like the add-user script, be sure to set the JAVA_HOME and JBOSS_HOME variables like you always would.


Building for the lastest version
However what you will get is a very old version; since then hundreds of bugs have been fixed in the supported EAP releases, which you will not get. To get them anyway you will need to build it yourself from source. To do this you will need to download a release from github (like 7.2.0.Final-testsuite-fix) and unpack it somewhere. Then invoke the build script like this to save yourself lots of time:
./build.sh -DskipTests -Drelease=true install
I figured that out using this source, so the credit goes to Christian Kaltepoth.

If you're on Windows I highly suggest you run the above command through Cygwin in stead of using the batch version of the script; I couldn't get it to work in the same way. After a long building time in which every Java dependency known to man is downloaded from the net (seriously: my local m2 repository was 12gb in size after this exercise), you'll get your JBoss-AS installation in zip form in the dist/target folder.

Note that the build script is designed to use an internal Maven installation in stead of a Maven installation you may already have. If like me you don't use the default repository location, you will need to modify either the settings.xml of this Maven installation to match your own (in the tools directory) or alter the build.sh file to just use the default MAVEN_HOME you already have set.

Warning: what I'm still not exactly sure about is if you are allowed to commercially deploy this self-built AS. If you are doing this as part of a high value application stack and especially if you charge a client for your deployment services, I would contact RedHat about it if I were you.

To deploy to JBoss 7.2 from Eclipse, you need minimally Jboss Tools 4.1.0.1 (in other words: you also need Eclipse Kepler). Installation instructions are the same as listed above.

A quick first glance

Remember Jboss 4/5/6? Well get ready to be shocked, because JBoss 7.x is completely different. The first thing you'll notice is that you actually have to search for an XML configuration file. As it turns out JBoss 7 follows a standard in relation to defining "connectors", the Java Connector Architecture 1.6 specification to be more precise. The implementation is dubbed IronJacamar. The main configuration files stem from IronJacamar, not JBoss 7 directly. As such for the best information about said configuration files I direct you to the IronJacamar user guide which you should keep bookmarked:

IronJacamar 1.1 user guide

Gone are the instances with the deploy directory we have so long called home, we have now entered the age of cloud-readiness. As such there are now two basic instances: standalone and domain. Standalone is your basic 'default' instance as you are familiar with. It is built to function as a single instance that runs in an isolated JVM environment and will generally service one application (although you can of course deploy more). The main benefit of standalone is that you can easily hot-deploy applications to it, so it will be the instance of choice for development purposes.

What else has changed? Everything of course. There are no longer deployers. There are no longer services. You won't even have to deploy datasource files anymore. JBoss classloading? Gone.

There are no server libraries anymore either. In stead there are now modules, a whole new system built into the server to manage classpaths which allows you to finely control what is exposed to JBoss and your applications and what is not. Similarly to the old JBoss classloader structure, but without the head aches (yet also not entirely without, as the small paragraph on JSF 2.1 will demonstrate).

On top of the modules, the server configuration is now managed from a single configuration point of entry; in case of the standalone instance that is standalone.xml. Under normal usage that is the only configuration file you will have to deal with. In this file you configure logging settings, but you also define for example the datasources exposed by the server. We'll get to that later, first lets setup the IDE.

Again, for documentation about the standalone.xml file I refer you to the IronJacamar user guide linked earlier. But of course I'll also deal with it in an "I'm new to JBoss 7!" way right here :)


Setup Eclipse

If you are starting with a fresh Eclipse of course setup your workspace according to your own taste. I use Maven as my project type in combination with the regular Java perspective, this has always given me the best development experience in Eclipse.

The part that we're interested in is setting up a server runtime so we can boot the server from Eclipse and hot-deploy our application to it. You do this as always, through the Eclipse preferences under server runtimes.


Click Add. Which runtime to use depends on which version of JBoss you're using.

JBoss 7.1.1: Jboss Community -> JBoss 7.1 runtime
JBoss 7.2 / EAP 6.1: Jboss Enterprise Middleware > JBoss Enterprise Platform 6.1 Runtime

Create your runtime, make sure to choose the correct installation directory, make sure it is a standalone one. Give it a proper name and make sure that a proper JDK is chosen. For JBoss 7.x I would recommend using the latest Java 7 release.


Next, we'll want to create an actual instance of the runtime. Open the servers view, right click in it and choose to create a new server.


If you choose JBoss 7.1, the runtime you just created should already be selected. Of course if you're using JBoss 7.2, choose the EAP 6.1 runtime in stead.


Create the runtime and it will appear in the servers view. In this case I created a JBoss 7.2 instance (and manually named it JBoss 7.2).


Thats it for now, you can try booting the server by clicking the green arrow button in the server view. The icon that looks like an insect will start the server in debug mode so you can set breakpoints.


Create a project

As said, I use Maven so that is what I'll show an example of. I want to do something simple: I want to deploy a simple war. No separate EJB module, but I do want to use JPA. I'm not going to ignore JBoss either and let it manage my transactions, so I'll use the perfect middle ground that JEE6 gives me: I'll just put some EJBs in the war itself. Basic enough but still using the server's features to the fullest, I like that.

To get all this done I'll need a project with a parent module (type pom) and a web module (type war). Using the m2eclipse maven project and maven module wizards this is done in a minute.


Warning: Don't forget to remove the exclusions from the resources directories in the project build path that m2eclipse wants to set by default :(

I could do this without a parent module to be honest, but I like to have my dependency management global just in case I want to add more modules (such as jar libraries or a second war). Do what is comfortable to you. Of course we'll also need some proper dependency management. Since this is JBoss 7 I want to use the latest and greatest. In fact I have to, as JBoss 7 provides the latest and greatest; Hibernate 4, JPA 2, JSF 2.1.3, servlet spec 3.0. To be able to compile such code we need proper Maven dependencies.

There starts our first problem (one that took me a full evening to properly sort out). Oracle doesn't provide proper Maven dependencies to compile and run against for JEE6, like Sun did for JEE5. Sure there is the javaee-api, but that is just a pile of stub (AKA fake) classes. You can use it for compilation but as soon as you use that in for example a unit test things blow up. I don't like to pollute my project with such dependencies.

JBoss to the rescue however. They provide their own dependencies which you can include in your project. We're talking about the following which you'll need for a basic web module:

<dependency>
  <groupId>testapp</groupId>
  <artifactId>testapp-web</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>javax.jms</groupId>
  <artifactId>jms</artifactId>
  <version>1.1</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>javax.transaction</groupId>
  <artifactId>jta</artifactId>
  <version>1.1</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifactId>
  <version>4.0.0.Final</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-entitymanager</artifactId>
  <version>4.0.0.Final</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.hibernate.javax.persistence</groupId>
  <artifactId>hibernate-jpa-2.0-api</artifactId>
  <version>1.0.1.Final</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>org.jboss.spec.javax.servlet</groupId>
  <artifactId>jboss-servlet-api_3.0_spec</artifactId>
  <version>1.0.0.Final</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>javax.faces</groupId>
  <artifactId>javax.faces-api</artifactId>
  <version>2.1</version>
  <scope>provided</scope>
</dependency>

All provided, as JBoss already has them on board. With these you'll be able to develop and unit test classes that are built with servlet 3.0, JSF 2.1, JPA 2, Hibernate 4 and JMS related technologies. Note that since the java.net Maven repository has been merged into Maven Central, the JSF 2.1 API dependency has made its way there too; you don't even need the maven.java.net Nexus repository to be able to use it.

Since I also want to add some EJBs, I add the following dependency:

<dependency>
  <groupId>org.jboss.ejb3</groupId>
  <artifactId>jboss-ejb3-api</artifactId>
  <version>3.1.0</version>
  <scope>provided</scope>
  <exclusions>
    <exclusion>
     <groupId>jboss.jbossws</groupId>
     <artifactId>jboss-jaxrpc</artifactId>
    </exclusion>
    <exclusion>
     <groupId>org.jboss.javaee</groupId>
     <artifactId>jboss-transaction-api</artifactId>
    </exclusion>
  </exclusions>
</dependency>

This allows you to compile EJBs against the EJB 3.1 spec. I added the exclusions to put only the bare minimum that you need on the compile/runtime classpath.

NOTE: also make sure to give any EJB module the "EJB" packaging type and to define the EJB spec version in the EJB plugin declaration, just as my Maven articles describe. This is important for JBoss Tools to properly do its job later on.

After setting up all the basics (if you don't know how, I suggest you read my Maven articles) you will want to add JSF capabilities to your web module. You do this by right clicking the project and choosing configuration and then add JSF capabilities. JBoss Tools supports you here if you installed the JSF tools and will automatically choose the right facets for you. The only thing I needed to do myself was manage the JSF 2.1 API jar; you know you have to do the same when you see a red error message stating that further configuration required.

For the JSF API jar I chose Disable library configuration, which makes sense since I manage the classpath using Maven, not Eclipse. Also I changed the url mapping from */faces/* to *.xhtml. That makes it nice and easy; anything that ends with .xhtml should be processed through the JSF controller servlet, everything else should not.

Picture is out of date; the url mapping is *.xhtml

When you apply, automatically a web.xml (with the faces servlet setup) and empty faces-config.xml should be added to your project. Alright, the project is all setup! ... almost, but we'll get to the last step in a moment.

Warning: when mucking around with the JSF capabilities, make sure that the project stays a Maven project. I have managed to transform it into a regular war project in one way or another. If the same happens to you, you can simply restore it to a maven project by choosing configure -> add maven capabilities. When you do you'll have to remove project built path exclusions again.

Setup a database driver

When you create an enterprise application, you are going to use a database in one way or another. Be it a prototype that uses an in-memory database or a regular application that connects with a physical one, there is a database involved.

So you have to know how to set one up. Deploying a -ds.xml file isn't going to do it anymore however (rectification: since JBoss 7.1 support for datasource files has been brought back), and neither will it help to put your driver jar in a lib directory.

The first thing we want to do is setup the database driver so JBoss has it on its classpath. To do this we can define the driver as a JBoss module. Lets assume you want to use an Oracle database, you need the OJDBC driver which you can download from the Oracle website. For the purpose of demonstration I am going to pretend that the OJDBC5 driver is used in stead of OJDBC6; why this distinction is important you'll see in a moment. Just to note: the number indicates the JDK version for which the driver is built, it is not a driver version number. OJDBC6 happens to be the latest and greatest JDBC 4 enabled driver; this is a clue we'll examine shortly.

Alright, add the module. The following is for JBoss 7.1, if you have built your own JBoss 7.2 or are using EAP 6.1, the modules go in modules/system/layers/base.

Step 1: create a directory structure modules/com/oracle/ojdbc5/main.
Step 2: copy the module.xml from modules/com/h2database/h2/main to your newly created directory.
Step 3: place the OJDBC driver jar in the modules/com/oracle/ojdbc5/main directory
Step 4: open the module.xml in a text editor and change the content to this:

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.0" name="com.oracle.ojdbc5">
  <resources>
    <resource-root path="ojdbc5-11.2.0.1.0.jar"/>
  </resources>
  <dependencies>
    <module name="javax.api"/>
    <module name="javax.transaction.api"/>
  </dependencies>
</module>

Make sure that the XML is correctly formatted (no newlines or spaces at the start of the document for example) and make sure the name matches the directory path inside the modules directory, without main. Also change the jar name to whatever driver version you downloaded, the one I list as an example is the one I have the best experiences with.

Thats it for the driver. Now we can create a datasource that uses it.

Setup an XA datasource


This is where the documentation and all existing articles on the net get sketchy. First a bit of history; if you've set up a datasource under JBoss 4/5/6 you know there are two basic types: local-tx and XA. Keep that in mind.

Open up standalone/configuration/standalone.xml. Scroll down until you find the datasources. The first thing we want to do here is add in our newly created driver module:

<drivers>
  <driver name="ojdbc5" module="com.oracle.ojdbc5">
  <driver-class>oracle.jdbc.OracleDriver</driver-class>
  <xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
  </driver>
  ...
</drivers>

Two important details. First of all, the module attribute refers to the directory structure inside the modules directory, not the name you defined in the module.xml file (although you should keep the two synchronized).

Secondly, what is different in my article from all other articles you may find on the net is that I define the driver-class. This has a very important reason: this is mandatory when the driver is not JDBC 4 compliant, which is the case for the OJDBC5 driver. If you neglect to define the driver, when you start the server JBoss will complain about the driver not being found.

If you use an JDBC 4 driver, like OJDBC6, the driver-class and even the xa-datasource-class is optional. The driver itself specifies through its meta data what classes to use and JBoss 7 knows how to use that information.

Alright, second step is to define the actual datasource. You could use the spiffy new management interface to do this, but what is faster than just copy/pasting some XML? Lets start with what you'll find in most other articles about this very subject, including the JBoss documentation itself (which is probably what most people blindly copy/paste into their own blogs without understanding what it means).

<datasource jndi-name="java:/DefaultDS" jta="true" enabled="true" use-java-context="true" pool-name="ORACLEDS">
  <connection-url>jdbc:oracle:thin:@HOST:1521:SID</connection-url>
  <driver>odjbc5</driver>
  <pool>
    <min-pool-size>1</min-pool-size>
    <max-pool-size>20</max-pool-size>
    <prefill>true</prefill>          
    <is-same-rm-override>false</is-same-rm-override>
    <no-tx-separate-pools />                             
  </pool>
  <security>
    <user-name>USERNAME</user-name>
    <password>PASSWORD</password>
  </security>
</datasource>

Make sure the driver matches the name of the one you just added (so not the module name, the driver name). Fill in HOST, SID, USERNAME and PASSWORD yourself of course. This is a basic datasource setup. But what type? Answer: local-tx, not an XA datasource. "But but... the datasource class is the Oracle XA datasource class!" I hear you cry. Yeah, so? That doesn't matter, you define a 'normal' datasource so that is what JBoss is going to give you.

If you want an XA datasource, you have to configure an... *trumpets* ... XA datasource! This is slightly different:

<xa-datasource jndi-name="java:/DefaultDS" enabled="true" use-java-context="true" pool-name="ORACLEDS">
  <xa-datasource-property name="URL">jdbc:oracle:thin:@HOST:1521:SID</xa-datasource-property>
  <driver>odjbc5</driver>
  <xa-pool>
    <min-pool-size>1</min-pool-size>
    <max-pool-size>20</max-pool-size>
    <prefill>true</prefill>          
    <is-same-rm-override>false</is-same-rm-override>
    <no-tx-separate-pools />                             
  </xa-pool>
  <security>
    <user-name>USERNAME</user-name>
    <password>PASSWORD</password>
  </security>
</xa-datasource>

Tada. This is all not very clear when you read the documentation, but I figured out as much from the IronJacamar user manual.

At this point startup the server and check that there are no problems. If you are using an XA datasource in combination with Oracle, you may run into some ORA errors at this point that keep repeating if JBoss 7 behaves similarly to JBoss 6. I have a section about that in my JBoss 6.x migration article, you need to add (or make sure a DBA does it) some grants to your database user.

migration guide to Jboss 5.1 / JBoss 6

(search for XA to skip to the relevant information)

Adding a persistence unit

We have our datasource, now we can define a persistence unit around it. The configuration is as you expect:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
   <persistence-unit name="your-pu">
      <jta-data-source>java:/DefaultDS</jta-data-source>
      <properties>
         <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
      </properties>
   </persistence-unit>
</persistence>

Jboss has a nice section on JPA 2 configuration in combination with Hibernate:

JPA 2 and Hibernate setup guide

When using JBoss 7: always use a datasource. You'll have the least amount of grief that way. On top of that, try to stick to JTA enabled datasources in enterprise environments.


Hot-deploying the application

At this point you can attempt to deploy the application to your configured server. There are at least 5 different ways to do it, the easiest is simply letting Eclipse / Jboss Tools deal with it.

First of all, make sure that your war project is deployable. If you installed the JBoss Tools maven integration this should be the case already, otherwise you'll have to mark as deployable yourself (right click on the project). When that is done, simply drag the project node from the project view to your runtime server as this picture indicates:



If you do it correctly you should end up with this:


From this moment on Eclipse/JBoss Tools will take care of keeping your project synchronized with its deployed alter ego. You can also easily force an application restart by right clicking the module in the server view.



Neat stuff, this is why I prefer Eclipse over Netbeans for Enterprise development. The tools, when you pick the right ones, are really comfortable and helpful! Note that because we added an empty faces-config.xml file (or JBoss Tools did that for us to be more precise), a full publish will also have the effect of redeploying the application; JBoss happens to monitor changes to that file.

NOTE: at this point do check out the deployments directory of your AS7 instance. If everything is correct, you will see here your application in fully exploded form (so everything is a directory, not a packaged file). If one or more modules are deployed in packaged form (a jar file or war file in stead of a directory), your POM setup is likely not correct. Check all the packaging types of your maven modules and if you make changes, be sure to let m2eclipse update your project configuration. Jboss Tools deployment does not work with Maven directly; it only looks at the Eclipse project facets to know how and what to deploy. It is important that your poms are clean and m2eclipse is properly syncing them to your project settings if JBoss Tools is to be able to do its job properly.

Overriding hot-deployed names of modules

There can be a problem here; it might just be that JBoss Tools is deploying one or more of your modules under the wrong name. This is because you can override the deployment name in your Maven poms (finalName or in the EAR build configuration) but JBoss Tools will not pick up these settings (yet). This won't be a problem until you do manual JNDI lookups of EJBs; the JNDI name consists of the module names so it will be different from when you deploy the EAR built in Maven.

To work around this you can force JBoss Tools to use specific names upon deployment. You can manage this by double clicking your server runtime (in the servers view). In the resulting dialog, click on the deployment tab. Here you can override the deployed name of the project modules. Note: this may only be properly filled if you deployed the application at least once.


Full publish after the change and your modules should deploy as you want them.


Specify the web context

If you check the console log while you start the server, you'll notice that the web context is simply the name of your war. You will probably want to specify your own. When deploying as an EAR you do that as part of the application.xml file, but what if you only deploy a war?

The answer is the same as if you would use JBoss 4/5/6. You add a WEB-INF/jboss-web.xml and you define the web context there:

<jboss-web>
    <context-root>yourapp</context-root>
</jboss-web>

Fortunately not everything changed :) No slash prefixed to it or else it doesn't seem to have any effect. Note also that if you want the WAR to deploy as your server root, use the following:

<jboss-web>
    <context-root>/</context-root>
</jboss-web>


Logging, problem solving and adding modules to the application classpath

You made a typo or you forgot to add something somewhere, it is hard to know or diagnose (so don't try asking me about it in the comments, I can't look in your system to know what is wrong). What you need to know is how to make JBoss spill the beans, as the logging by default is not very helpful when things go wrong.

Fortunately you can enable trace logging to make the server (a lot) more verbose. You do this by opening standalone.xml again. Right at the top you will find the logging configuration. The configuration already contains a hint as to what you should do: look for this:

<periodic-rotating-file-handler name="FILE">
<level name="INFO"/>
...

Change INFO to TRACE and boot the server. When you have diagnosed and fixed the problem, revert back to INFO. It is useful to set this to trace any time you make configuration changes, just to make sure that what you expect to happen is in fact happening.

On the topic of logging, if you want to use logging in your own applications I suggest you use SLF4J which is the exact logging service that the server itself uses. Since the server exposes SLF4J as a module, you can add that module to your application's runtime classpath without having to deploy any jars, keeping the size of your application down. Of course we also need the API during compile time, so we need a provided Maven dependency for it:

Maven dependencies:

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.6.4</version>
  <scope>provided</scope>
</dependency>

You don't need any logging implementation binding, that we'll let JBoss deal with. SLF4J is not one of the modules that is put on the application classpath by default, we need to configure it ourselves. You do this by adding a file WEB-INF/jboss-deployment-structure.xml to your war (or META-INF/jboss-deployment-structure.xml in case of an EAR).

Through this file you can include modules that are not implicit and you can exclude modules that are implicit (if you need to for some reason, for example when you want to use an older version of Hibernate). You can also define application specific modules if you so please. To include the SLF4J module you would add the following content:

<jboss-deployment-structure>
  <deployment>
    <dependencies>
        <module name="org.slf4j" />
    </dependencies>
  </deployment>
</jboss-deployment-structure>

This puts the SLF4J API module on your application's classpath; you need more than the API to actually use SLF4J during runtime, but because the module defines the proper module dependencies itself, you don't need to add anything else. Neat!

We also need some configuration for our application logging. Open up standalone.xml and add a logger category:

<logger category="myapp.package">
  <level name="DEBUG"/>
</logger>

In category, fill in the base package of your application. I tend to put the application name as the root package and then create a category for the whole application.

The default log output may bother you, as it did me. By default the thread name is for example printed yet there is no linenumber in sight anywhere. I would change the default log pattern configuration from this:

<formatter>
  <pattern-formatter pattern="%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n"/>
</formatter>

To this:

<formatter>
  <pattern-formatter pattern="%d{HH:mm:ss,SSS} %-5p [%c:%L] %s%E%n"/>
</formatter>

This will output the linenumber (%L) and no thread name (%T). Note that for a clickable classname and linenumber you could replace the %c%L with a single %l (lowercase) but I found the output, at least in the Eclipse console, to be an eye sore.

If you want to keep your application as portable as possible: there is nothing stopping you from simply deploying SLF4J and your favorite logging API with your application (don't forget the SLF4J bridge jar for that logging API). But if you're like me you treat JBoss as part of your application framework, so I make use of the fact that you can keep the size of your deployment way down by properly using the server's features.

More information on managing your application's classpath can be found in this documentation:

JBoss 7 classloading

Thinking in modules is starting to grow on me!

Deploying jboss-deployment-structure.xml in an EAR

Just to be complete, if you want to deploy as an EAR and want to put SLF4J on the classpath of all your modules you have some more work. First of all the file goes in the META-INF directory of your EAR. If you use Maven this means you put the jboss-deployment-structure.xml file in the src/main/application/META-INF folder of your ear module.

How to define the content depends on if you want to expose SLF4J to all modules or only select ones. Say you want to expose to only the EJB module:

<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>  
  <deployment>  
    <dependencies>  
        <module name="org.slf4j" />  
    </dependencies>
  </deployment>  
  <sub-deployment name="app-ejb.jar">
    <dependencies>  
        <module name="org.slf4j" />  
    </dependencies>
  </sub-deployment>  
</jboss-deployment-structure>

All of this is mandatory; it won't work if you leave out the deployment element, it won't work if you leave out the sub-deployment elements.

If you want to add SLF4J to all modules of the EAR, you can simply do this:

<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure> 
  <deployment> 
    <dependencies> 
      <module name="org.slf4j" export="true" /> 
    </dependencies>
  </deployment> 
</jboss-deployment-structure>

So add the export="true" attribute. I would prefer this method as you don't have to specifically name the modules of your EAR, which gives less chance of having to do double maintenance when making changes.

A quick word on JSF deployment

JBoss 6 was pretty neat. It could actually manage JSF implementations and you had fine grained control over which one was used for which application. You could also add your own versions easily, making upgrading JSF 2 painless.

All good things come to an end, which in this case is quite strange as JBoss 6 was developed only months before JBoss 7. You no longer have much control really, you use JSF 2.1.3 (or in other words: the one that the server provides) or you can take a hike. This is not really a problem as the JSF module is maintained as new versions of JBoss 7 are released, but it would have been nice if it was more configurable. Perhaps this will happen in JBoss 7.1 though.

The one thing that may be blocking you is that you may need to use JSF 1.2 in stead of the default JSF 2.1.3, for legacy apps. JBoss provides for this as JSF 1.2 has a module too; luckily you don't have to fark around with the jboss-deployment-structure to actually use it; like you would do in JBoss 6 you can force it to be used by adding this to your web.xml:

<context-param>
  <param-name>org.jboss.jbossfaces.JSF_CONFIG_NAME</param-name>
  <param-value>Mojarra-1.2</param-value>
</context-param>

If you are a fan of Myfaces - there is no way to reliably use it yet apparently.

Making sure JSF is functioning

To actually have something to deploy, lets see if JSF 2.1 is doing what it is supposed to do. Although I generally despise putting a hello world example in my blogs, this is not a tutorial on JSF so I'll make an exception this time.

Lets create a backing bean, choose whatever package you like:

@ManagedBean
@ViewScoped
public class HelloWorld {
  public String getGreeting(){
    return "hello!";
  }
}

Note 1: annotations not found even with the fix imports Eclipse feature? You probably didn't add the JSF 2 API to your war classpath.

Note 2: to prevent warnings you should make backing beans serialized.

Now also create a test.xhtml file in the webapps directory.

<html lang="en"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">
    
        <title>Test</title>
    </h:head>
    <h:body>
        Message: #{helloWorld.greeting}
    </h:body>
</html>

I picked the backing bean name so you can see the default naming convention in action. The managed bean name will by default be the classname, but with the first letter in lowercase. HelloWorld=helloWorld, MySuperDuperBean=mySuperDuperBean, etc. You can of course override the name through the @ManagedBean annotation.

Boot the server, navigate to http://localhost:8080/test.xhtml and see what happens. If you get the raw xhtml content, you did not setup the faces servlet like me. Make sure the faces servlet is mapped to *.xhtml. If you make the change and it still does not change things, be sure to clean your browser cache and try to refresh a few times.

Great! We did our first managed bean invocation without even touching faces-config.xml.

Upgrading JSF

JBoss 7 comes with JSF 2.1.3 out of the box. This release fixes many bugs, but I was running into a persistent one in combination with Richfaces 4.1; JSF 2.1.4 fixed that issue for me. So I wanted to install it in my JBoss 7 instance.

Since JSF 2.1.3 the implementation and API jar are merged into one jar; the sneaky people at JBoss however have split it up again. There are two modules that deal with JSF:

javax.faces.api (main); this is the API and you won't ever have to touch this
com.sun.jsf-impl (main); this is the JSF implementation (Mojarra).

To upgrade JSF, you need to replace the implementation jar in com/sun/jsf-impl/main and the API jar in javax/faces/api/main. Download the latest version from the java.net website. After downloading, make a copy of it so you have the jar twice - because you need split the one jar into the two jars. Open up one of the jars in your favorite zip tool (7zip is a powerful and free possibility) and remove the javax folder from it. This is the API, so you have just created the implementation jar. In the other copy of the jar, remove the com directory which is the implementation, so this is the API jar.

Put this modified implementation jar in the com/sun/jsf-impl/main folder. Remove or rename the old jar that is there and modify the module.xml to point to your new implementation jar. Do the same for the API jar, only copy it to javax/faces/api/main and modify the module.xml there. Done!

Upgrade RestEasy

JBoss comes with RestEasy as the JAX-RS implementation in stead of the reference implementation Jersey. If you want to use RESTful webservices you are advised to upgrade it, since especially community versions of JBoss 7.1 have an outdated version with certain issues. My JAX-RS server article contains exact steps how to do that.

Upgrade Weld

Weld is the reference implementation of the CDI spec. You are advised to keep it up to date as many bugfixes and performance upgrades have been done.

JBoss 7.x is a JEE6 container and thus implements the CDI 1.0 specification; this means you need a Weld implementation in the 1.1.X version line, not of the 2.x version line. At the time of writing 1.1.16 was the latest version. To upgrade, download the latest Weld 1.1.X from the download page. Extract artifacts/weld/weld-core.jar from it.

Now in your JBoss installation, remove or rename the file modules/org/jboss/weld/core/main/weld-core-1.1.5.AS71.Final.jar and copy the weld-core.jar you extracted to this folder. I would rename the file to put the proper version in it. Modify the module.xml as you should be familiar doing by now to update it to the new jar name. Done! If you have an application that uses CDI, you will notice this in the server log at startup:
[org.jboss.weld.Version:206] WELD-000900 1.1.16 (Final)

Upgrade JbossWS

You may want to install the latest and greatest JBossWS, which will give you access to the latest and greatest version of Apache CXF for SOAP/JAX-WS related webservices. At this time of writing this was JBoss WS 4.2.2, which is backed by Apache CXF 2.7.7. To do so you will need to download the latest version of JBossWS and unpack it somewhere. Now you will need to do some work. The following will assume you are installing into JBoss 7.2 / EAP 6.1, but you can adapt it for lower versions:
  • Backup the JBoss instance in which you'll be installing JbossWS!
  • In the directory you just unpacked, rename the ant.properties.example file to ant.properties
  • Open it up in a proper text editor and make the following changes;
    • Put the proper path to the root of your JBoss installation into jboss720.home; use forward slashes on Windows (ex: c:/jboss720)
    • set jbossws.integration.target to jboss_720
    • set jboss.bind.address to the proper IP/host name (if necessary)


Other than that everything should be fine as it is. Now to do the actual installation (double check the path you set), you will need to have Apache ANT installed and working (read: when you type 'ant' on the command prompt/shell, you get an actual response from ANT and not the OS telling you it doesn't know what you're talking about). Open up a command prompt, navigate to whatever folder you unpacked JbossWS in and execute:
ant deploy-jboss720

That should give you a big pile of output stating files being deleted and added; at this point you're done and you can throw away the JBossWS installation files again. Startup the JBoss instance to validate that its all still working. Similarly if you wanted to deploy on JBoss 7.1.3 / EAP 6.0, you'd run:
ant deploy-jboss713

Unfortunately if you're stuck on JBoss 7.1.1 community you can't install the latest JBossWS, the best you can do is install JBossWS 4.1.1.

Taking EJB 3.1 for a little spin

The EJB 3.1 spec changed a few things.

- JNDI names are now standardized. The old "earname/EjbBeanName/local" pattern isn't going to work anymore to do manual lookups.
- You no longer need to define any business interface; public methods of an EJB are automatically exposed.
- You can put EJBs in a war!

Lets try that out. Create a class in any package you want. I'll name it TestDao. For completeness let me also throw in the logger we should have available to us by adding the JBoss module on the classpath.

@Stateless
public class TestDao {
  @PersistenceContext
  private EntityManager em;

  private Logger logger = LoggerFactory.getLogger(getClass());
  

  public void someDaoMethod(){
    // do things and stuff with the entity manager
  }

  @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
  public String getGreeting(){
    logger.info("Invoking getGreeting()!");
    return "hello!";
  }
}

You should be able to compile this class if you added the proper EJB and SLF4J dependencies to the war classpath. See the paragraphs on project setup and logging for more details.

If you deploy the war at this point you'll see that under INFO logging the server is nice enough to tell you under which JNDI names you can reach your new EJB. Quite nice for people new to JBoss 7, but when the number of EJBs grows the logging can become quite verbose and repetitive. You can filter it out by adding the following category to the standalone.xml:

<logger category="org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor">
  <level name="WARN"/>
</logger>


We can now modify the backing bean to use the EJB, just to test it is all working:

@ManagedBean
@ViewScoped
public class Dashboard implements Serializable {

  private static final long serialVersionUID = -5987527103079343725L;

  @EJB
  private TestDao testDao;
  
  public String getHello(){
    return testDao.getGreeting();
  }
}

Republish, start server and check the login page again. It should still work and a log line should now appear in your Eclipse console. Success! And with so little effort too!

Let's do that again, with CDI

Hey we have CDI/Weld on board, so lets use it. To "activate" CDI in your project you need to create a WEB-INF/beans.xml file. This file can be as empty as this, you'll do most things through annotations.
<beans/>

To inject an EntityManager with CDI we need to help it out a bit so it can know which one to inject, by introducing a qualifier and a producer. You can name these however you want of course; you should match the name of the qualifier to the name of your persistence unit so it stays understandable which one you're using in the code.
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({
    ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE
})
public @interface TestPU {
}
public class TestPUProducer {

    @Produces
    @PersistenceContext(unitName = "TestPU")
    @TestPU
    private EntityManager entityManager;
}

And now we can define our EJB like this:
@Stateless
public class TestDao {
  @Inject @TestPU
  private EntityManager em;

  private Logger logger = LoggerFactory.getLogger(getClass());
  

  public void someDaoMethod(){
    // do things and stuff with the entity manager
  }

  @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
  public String getGreeting(){
    logger.info("Invoking getGreeting()!");
    return "hello!";
  }
}
Injecting the EJB requires no effort at all:
@ManagedBean
@ViewScoped
public class Dashboard implements Serializable {

  private static final long serialVersionUID = -5987527103079343725L;

  @Inject
  private TestDao testDao;
  
  public String getHello(){
    return testDao.getGreeting();
  }
}


Taking JMS for a spin

Jboss 7.x has HornetQ, a very powerful JMS implementation. To use it is surprisingly easy, once you piece together the documentation and procedures from all over the net. In my mission to get all information in one place, I'm going to document how to setup a queue, how to listen on that queue, how to send a message to that queue. Not only from within the same server, but from an external client as well. But first we need to configure the server to actually expose JMS, because by default it does not.

At this point you have two options.

- rename standalone-full.xml to standalone.xml and copy over all the configuration changes you made in the old file
- copy over the relevant parts from standalone-full.xml to standalone.xml


I decided to do the last. The following will activate HornetQ AND MDB support:

STEP 1: under extensions, copy over the org.jboss.as.messaging module
STEP 2: copy over the <mdb> block to the ejb3 subsystem.
STEP 3: under the same subsystem, copy over the mdb-strict-max-pool instance pool.
STEP 4: copy over the entire urn:jboss:domain:messaging subsystem (under profile)
STEP 5: under socket-binding-group, copy over the messaging and messaging-throughput socket-binding elements.

That's it, your default standalone server now has HornetQ enabled.

Queues and topics can be defined inside the messaging subsystem, under jms-destinations. Example:
<subsystem xmlns="urn:jboss:domain:messaging:1.3">
  <hornetq-server>
  ...
     <jms-destinations>
        <jms-queue name="testQueue">
           <entry name="queue/test"/>
           <entry name="java:jboss/exported/jms/queue/test"/>
        </jms-queue>
        <jms-topic name="testTopic">
           <entry name="topic/test"/>
           <entry name="java:jboss/exported/jms/topic/test"/>
        </jms-topic>
        <jms-queue name="YourQueue">
           <entry name="queue/yourqueue"/>
           <entry name="java:jboss/exported/jms/queue/yourqueue"/>
        </jms-queue>
     </jms-destinations>
  </hornetq-server>
</subsystem>

If you need more information about HornetQ, I discovered this very useful blog that is dedicated to it. The linked article is what tipped me off to the fact that messaging was enabled in standalone-full.xml. My Jboss 6 migration guide also contains some entries on HornetQ which were specific to JBoss 6; if you run into issues you may just find an answer there.

To test out that it works, you could use this simplistic code. To compile this code you need the JMS API on your classpath, using Maven:
        <dependency>
            <groupId>javax.jms</groupId>
            <artifactId>jms</artifactId>
            <version>1.1</version>
            <scope>provided</scope>
        </dependency>

First of all a test MDB listening on the test queue:

@MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
    @ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/test") })
public class MyTestMDB implements MessageListener {

  protected Logger                      logger = LoggerFactory.getLogger(getClass());
  
  public void onMessage(Message msg) {
    
    ObjectMessage om = (ObjectMessage) msg;
    
    try{
      String text = (String) om.getObject();
      logger.info("Message received! : " + text);
    } catch(Throwable t){
      logger.error("Exception receiving message", t);
    }
  }
}

This simple MDB will consume messages on the test queue and print out a log each time it receives one.

Now an EJB that can send messages:

@Stateless
public class MyTestEjb {

  protected Logger                      logger = LoggerFactory.getLogger(getClass());
  
  @Resource(name = "QueueConnectionFactory", mappedName = "java:/ConnectionFactory")
  protected QueueConnectionFactory connectionFactory;
  
  public void sendMessage(){
    sendObjectMessage("java:/queue/test", "Test");
  }
  
  
  
  private void sendObjectMessage(String queueName, Serializable msgBody){
    
    QueueConnection connection = null;
    QueueSession session = null;
    MessageProducer producer;
    
    try {
      InitialContext context = new InitialContext();
      javax.jms.Queue queue = (javax.jms.Queue) context.lookup(queueName);

      connection = connectionFactory.createQueueConnection();
      session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
      producer = session.createProducer(queue);
      producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

      ObjectMessage omsg = session.createObjectMessage();
      omsg.setObject(msgBody);
      omsg.setJMSCorrelationID("1");
      omsg.setJMSDeliveryMode(DeliveryMode.NON_PERSISTENT);
      producer.send(omsg);
    }
    catch (Throwable e) {
      logger.error("Exception sending message", e);
      throw new IllegalStateException("Exception while trying to send JMS queue message!", e);
    }
    finally {
      
      try {
        if (session != null) { session.close(); }
        if (connection != null) { connection.close(); }
      }
      catch (JMSException jmse) {
        logger.error("Exception sending message: ", jmse);
      }
    }
  }
}

This code has been slightly simplified from what I normally use, for example the correlation ID is normally uniquely generated. Note the lookup of the connection factory by using the JNDI name "java:/ConnectionFactory". You can find this name configured in the messaging subsystem. You'll find that this is bound to the "in-vm" connector, which is what you use when you send messages to a queue within the same enterprise server / JVM. If you want to send persistent messages that are part of a two-phase transaction you would use the connector configured under "java:/JmsXA".

For a discussion on connectors and transports I refer you to the HornetQ manual.


JMS... for clients

It gets interesting when you actually want to connect to a queue (or topic) from OUTSIDE the application server, such as from another enterprise server entirely. This is fully possible because JMS is a standard that is very well-defined, but it takes some extra work and knowledge.

To connect to a remote queue from a client, you first need access. Before we can get access, we need to GIVE access. The connection factory we need is in fact already setup in our messaging subsystem; it is called RemoteConnectionFactory and it is deployed in the JNDI under java:jboss/exported/jms/RemoteConnectionFactory, which in the context of a client means we will be able to look it up using the JNDI name jms/RemoteConnectionFactory. More on that in a bit. This remote connection factory is linked to the netty acceptor, and netty requires authentication. The problem is: we don't have an actual user yet. But JBoss provides a tool for us to create one.

To create a user, open a command prompt and navigate to the bin subdirectory of your JBoss installation, where you will find a script add-user.sh / add-user.bat. Run this script and answer its questions. I answered as follows:

TYPE: application user (b)
REALM: ApplicationRealm (just press enter)
USERNAME: guest
PASSWORD: hello123$
ROLE: guest
PROCESS TO PROCESS: no

The tool will modify the files standalone/configuration/application-users.properties and standalone/configuration/application-roles.properties for you; check them out to make sure the tool did its job properly.

application-roles.properties
guest=guest

Read that as "user guest has role guest".

application-users.properties
guest=6672aea9a9157a51c0446dc100160d2e

Read that as "user guest has password BLA". As you can see the password of the user is actually encoded to prevent it from being human-readable, hence the need to use this tool to add users. The password hash will likely be different for you. Since the messaging subsystem is by default setup to allow everything for users in the guest role (check out the security-settings element of the messaging subsystem), our guest user is now ready to be used by clients.

As hinted at earlier in order to do that, we need (or rather: should) use JNDI to make the actual connection to our server from the client. To be able to do that you need a set of JBoss 7.x specific client jars on your application's classpath. The easiest way to manage that is as per usual through Maven:
            <dependency>
                <groupId>org.jboss.as</groupId>
                <artifactId>jboss-as-jms-client-bom</artifactId>
                <version>7.2.0.Final</version> 
                <type>pom</type>
            </dependency>

And you of course also need the JMS-API mentioned earlier to be able to compile code. Now we're ready to write some code. To setup the JNDI connection, you need to know on which host and which port to connect. Only you know the real host name, lets assume the server is running on the localhost for now. To know the port, go back to the standalone.xml file and go all the way to the bottom where you will find the socket bindings. See that socket-binding named remoting? That's the port you need. By default it should be 4447, but you can change it to whatever you need it to be. Make sure there is no firewall blocking traffic of course.

Knowing all that, the code is as follows. I am hardcoding all settings, of course you know better and you'll pull this kind of information from an application configuration file.
    private InitialContext buildInitialContext() throws NamingException {

        String url = "remote://localhost:4447"; // be sure to replace localhost with the correct hostname/IP address
        String user = "guest";
        String pass = "hello123$";

        Hashtable<String, String> jndiProperties = new Hashtable<String, String>();
        jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
        jndiProperties.put(Context.PROVIDER_URL, url); 
        jndiProperties.put(Context.SECURITY_PRINCIPAL, user);
        jndiProperties.put(Context.SECURITY_CREDENTIALS, pass);

        return new InitialContext(jndiProperties);
    }

A note about the JNDI user credentials: I didn't seem to need them when doing tests on my own local machine between two servers (even when I bound on my internal IP address in stead of the localhost) but when performing tests connecting from a different machine on the network, connections kept failing (with the most vague error you can imagine) until I added the JNDI credentials too; which makes sense because if you investigate the standalone.xml file, you'll notice that the remoting subsystem is in the same ApplicationRealm security realm. Apparently JBoss 7 is lenient when you are making "remote" connections from the same host.

In any case that's step one done. To actually send a message, we need to adapt our earlier sendObjectMessage() method a little bit:
    public void sendObjectMessage(Serializable msgBody) {

        QueueConnection connection = null;
        QueueSession session = null;
        MessageProducer producer;

        try {
            InitialContext context = buildInitialContext();
            String user = "guest";
            String pass = "hello123$";
            String connectionFactory = "jms/RemoteConnectionFactory";
            String queueName = "jms/queue/test"; 

            // lookup the connection factory from remote JNDI
            QueueConnectionFactory connectionFactory = (QueueConnectionFactory) context.lookup(connectionFactory);

            // and also the deployed queue
            Queue queue = (Queue) context.lookup(queueName);

            // now setup the session to the queue to be able to send a message. If multiple messages need to be sent,
            // this would be quite inefficient and a scheme should be developed to open one session to send multiple
            // messages.
            connection = connectionFactory.createQueueConnection(user, pass);
            session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
            producer = session.createProducer(queue);

            // non-persistent = JMS messages are not persisted to a datastore for retransmission support
            producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

            ObjectMessage omsg = session.createObjectMessage();
            omsg.setObject(msgBody);

            // correlationID should normally be a unique identifier for this message
            omsg.setJMSCorrelationID("1");
            omsg.setJMSDeliveryMode(DeliveryMode.NON_PERSISTENT);
            producer.send(omsg);

        } catch (Throwable e) {
            throw new IllegalStateException("Exception while trying to send JMS queue message!", e);
        } finally {

            try {
                if (session != null) {
                    session.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (JMSException jmse) {
            }
        }
    }


If you compare the code you'll see it is mostly the same, only we lookup some stuff through our remote JNDI context. The name of the queue you defined in the standalone.xml:
                <jms-destinations>
                    <jms-queue name="testQueue">
                        <entry name="queue/test"/>  
                        <entry name="java:jboss/exported/jms/queue/test"/> 
                    </jms-queue>
                    ...

And guess what? This should be all you need to do to get JMS working from any client (supporting JMS) to your JBoss 7.x server! Lets setup a checklist to make sure you didn't forget anything.

- you copied over the messaging related configuration bits to standalone.xml
- you added your queue to the configuration
- you created a user for external access with the correct role (verify the content of the mentioned files!)
- you made sure that connections are actually allowed on the remoting port

Please don't post any comments asking for help with connection errors - I can't see or debug your network environment so you'll have to figure it out with your network administrator. As you can see it doesn't require much configuration or code at all so any problem you have will more likely be in the networking environment such as a firewall, or a typo. Triple check everything.

In closing

I'm going to end the article right here as I don't want to cram too much information into it. At this point you should have your development environment in its most basic form; a database connection, a persistence unit, a web project that is hot-deployed to the server and possibly some JMS related services. You are now ready to start adding code. Sneakily without giving the subjects paragraphs of their own I've also shown you how to define your own server-wide modules and how to put modules on your application's classpath. The basics of course, there is much more to learn in the articles I linked to.

What is next is that you will probably want to start playing around with JSF 2 and see how things work under JBoss 7. I'm not going to cover JSF 2.x development myself, for that I leave you into the very capable hands of BalusC.

BalusC's JSF blog