This is one of those articles that is not finished right from the start. I keep adding to this in my own time. For example I'm slowly adding in JBoss 6.x stuff. As it is this article will be mostly oriented on JBoss 5.1, but with additions on how JBoss 6 may differ.
Maven dependencies to compile and unit test EJB code
When Mavenizing your project, you may run into the problem of needing libraries to be able to compile and possibly unit test all the EJB code. There are a few possibilities. Note that you need specific maven repositories setup, not only Maven central. For more information on these Maven repositories, check my Maven article.1)Add JBoss to your maven artifacts. This will add MANY transitive dependencies to the classpath, most of which you don't need. But it is certainly the least work to setup. All JBoss instances are in the JBoss maven repository.
<dependency> <groupid>org.jboss.jbossas</groupid> <artifactid>jboss-as-client</artifactid> <version>5.1.0.GA</version> </dependency>
(you can also use version 6.0.0.Final and version 6.1.0.Final).
2) Use the javaee API. This is a jar provided by Oracle which allows you to compile JavaEE code. However: they are only stub classes so you cannot use it in for example unit tests, you'll only get exceptions. These dependencies are available through the java.net Maven repository.
JEE5 API:
<dependency> <groupid>javaee</groupid> <artifactid>javaee-api</artifactid> <version>5</version> </dependency>
JEE6 API:
<dependency> <groupid>javax</groupid> <artifactid>javaee-api</artifactid> <version>6</version> </dependency>
3) use very specific APIs. All the required components are actually available as separate dependencies, you just need to know which ones. For JEE5 applications, use the following set from the java.net Maven repository:
<dependency> <groupId>javax.jms</groupId> <artifactId>jms</artifactId> <version>1.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.ejb</groupId> <artifactId>ejb-api</artifactId> <version>3.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.persistence</groupId> <artifactId>persistence-api</artifactId> <version>1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4.4</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.4</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>jsr250-api</artifactId> <version>1.0</version> <scope>provided</scope> </dependency>
The jsr250-api dependency is not strictly required as Java6 provides those packages out of the box, but I like to keep my poms as "java 5 friendly" as possible.
This provides the EJB 3.0 API. What if you want to compile for JEE6 / EJB 3.1? You're out of luck, Oracle does not provide an EJB 3.1 API that I know of. Any documentation you'll find on the internet in relation to Maven will point to the JEE6 API, but as you know that one cannot work in unit tests.
Our friends at JBoss come to the rescue however. If you want to compile for JEE6, use the following dependency from the JBoss Maven repository in place of the EJB 3.0 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>
Again, I provide some exclusions to keep your classpath as clean as possible.
Which one of these three methods you should use is really up to your needs. If you only need to compile and you do not unit test (that is going to bite you some day), by all means use method 2) and only include the JEE API. If you are using a unit testing framework such as Arquillian which runs unit tests inside the container, you have no choice but to use method 1). If you are like me and you unit test your EJB code as if they are plain old Java classes, method 3) is really the most efficient way to go.
Exploded deployment
I don't want to repeat what I documented before. If you want to learn about exploded deployments, I suggest you check out my hot deployment article.Subdirectories
JBoss allows you to deploy your files inside a subdirectory in the deploy directory. The server scans fully recursive. You could create the next Facebook application called Acebook, and deploy files like this:deploy\acebook\acebook.ear
deploy\acebook\acebook-ds.xml
deploy\acebook\acebook-scheduler-service.xml
etc.
You see where it benefits: you can keep all application related files close together. This also makes it easier to create an installer for your application should you need one.
Service MBeans
Did you know that you can add services to a JBoss instance? Even roll your own?JBoss defines a naming convention for this - any file in your deploy directory (or a subdirectory of the deploy directory) that is called XXX-service.xml, JBoss will treat like a file defining additional mbean services. Replace XXX with any file that you want.
The content of such a file must be like this:
<?xml version="1.0" encoding="UTF-8"?> <server> <mbean code="CLASSNAME" name="SERVICEDECLARATION"> ... </mbean> <mbean code="CLASSNAME" name="SERVICEDECLARATION"> ... </mbean> </server>
You can define any type of mbean in there and any number that you like. Although the deploy directory already contains a mail-service.xml and a scheduler-service.xml, you are not obliged to declare mail and scheduler services in those files; you can put them all in your own acebook-service.xml file if you want.
I suggest you snoop around your deploy directory and inspect any file that ends with -service.xml.
The property service
An often overlooked little gem, the property service can be a useful little thing. Its function is quite simple: to define system properties. Being able to define those in a service helps to work around the fact that properties are usually very much bound to an environment.An example:
<?xml version="1.0" encoding="UTF-8"?> <server> <mbean code="org.jboss.varia.property.SystemPropertiesService" name="jboss:type=Service,name=SystemProperties"> <attribute name="Properties"> acebook.backup.dir=/usr/gimby/acebook/backups acebook.testmode=true acebook.email.from=gimby@somewhere.cm </attribute> </mbean> </server>
These properties are now available through the System.getProperty() call.
Note:
A standard JBoss 5.1 installation will already have a file properties-service.xml that defines the properties service mbean; you should add all your properties to the existing one. Adding another one will create a deployment error, as MBeans must have unique names.
The scheduler service
JBoss has a built in scheduler that can be of great use to you. It may not be as powerful as for example Quartz, it gets the job done.The things you can schedule are MBeans, otherwise known as service beans. To be able to create MBeans, you need a JBoss specific library; the maven details are:
<dependency> <groupId>org.jboss.ejb3</groupId> <artifactId>jboss-ejb3-ext-api</artifactId> <version>1.1.1</version> <scope>provided</scope> <exclusions> <exclusion> <groupId>org.jboss.javaee</groupId> <artifactId>jboss-ejb-api</artifactId> </exclusion> <exclusion> <groupId>org.jboss.metadata</groupId> <artifactId>jboss-metadata</artifactId> </exclusion> </exclusions> </dependency>
I add the exclusions to not pollute the classpath; you don't need those transient dependencies (and THEIR transient dependencies, etc).
Now on to the code. Lets create a useful service that prints hello ever 5 seconds. Here's the code:
@Local public interface HelloWorld{ public void sayHello(); }
@Service(objectName = "myhello:service=HelloWorld", name = "HelloWorld") @Management(HelloWorld.class) public class HelloWorldBean implements HelloWorld { public void sayHello(){ System.out.println("Hello!"); } }
Note that the management interface is mandatory.
Now to schedule this MBean, we need to add a service in an XXX-service.xml. For example, you could use the existing scheduler-service.xml file.
<mbean code="org.jboss.varia.scheduler.Scheduler" name=":service=HelloWorldScheduler"> <attribute name="StartAtStartup">true</attribute> <attribute name="SchedulableMBean">myhello:service=HelloWorld</attribute> <attribute name="SchedulableMBeanMethod">sayHello</attribute> <attribute name="InitialStartDate">NOW</attribute> <attribute name="SchedulePeriod">5000</attribute> <attribute name="InitialRepetitions">-1</attribute> <attribute name="FixedRate">true</attribute> <depends>myhello:service=HelloWorld</depends> </mbean>
Startup JBoss with this class deployed in it (for example in an EJB module) and if everything is correct, you should see "hello!" appearing in your log every 5 seconds. Not very useful I know, but I'm sure you get the idea :)
Note:
By adding the management interface, you can now also invoke the sayHello() method through the jmx-console! Go to http://localhost:8080/jmx-console and see for yourself.
And also note that through the jmx-console, you can stop and (re)start scheduled instances of Mbeans.
A word of caution: the @Service and @Management annotations are JBoss specific and thus not portable... not even across versions of JBoss unfortunately. I deal with that myself by keeping MBeans away from my actual application, often deploying them as a separate EJB jar.
Classpath isolation
Why oh why is this turned off by default? (well actually: since JBoss 6 it is turned on by default).
Classpath isolation. Turn it on because really, it saves so many headaches. What is it? Basically it is to create a private little island within JBoss for each of your EAR packaged applications. They each have their own classloader and thus their own private set of classes; it doesn't share anything from the outside world, it doesn't share anything with the outside world (not even local interfaces!)
So what does it help? Library clashes. Each EAR can have its own private version of a library (Jboss Seam 2 being the prime example; without classpath isolation you can run into many conflicts with it); one application can be running on Hibernate 3.2.6 while another is running on Hibernate 3.5 - it doesn't matter.
Now don't get me wrong, to be able to use classpath isolation you need a clean classpath. This can be especially troublesome when you are using Maven and you don't regulate with an iron fist which libraries go into your application. You can get into trouble when you have JBoss libraries in your application (those provided by the JBoss instance) - you will probably get into the conflict of the same class being loaded from two different classloaders - to Java it are then two different classes and objects created from them are not assignable to each other.
So the resolution:
- turn on classpath isolation
- clean out redundant libraries from your classpath
- live long and prosper
That leaves the question HOW to turn it on. Quite simple; in the deployers directory of your instance, locate the file ear-deployer-jboss-beans.xml. In this file, probably all the way at the bottom of it, locate this piece of xml:
<bean name="EARClassLoaderDeployer" class="org.jboss.deployment.EarClassLoaderDeployer"> <property name="isolated">false</property> </bean>
Simply change the false to true and you're done.
EJB Transaction timeout
EJB methods tend to have a running transaction. This transaction has a timeout value, which is by default set to 5 minutes in JBoss 5.1 (and JBoss 6).So what if you invoke some code that may take longer than 2 minutes to run? You have a few basic options.
- run the code in an EJB method with no transaction at all (NOT_SUPPORTED)
- use an EJB timer (to be discussed later)
- use the EJB 3.1 feature to create an asynchronous EJB call
- increase the EJB timeout
The latter is the easiest, but of course not entirely safe. Say you increase the transaction timeout to 10 minutes, whats to stop the code from taking 30 minutes to complete? But in those situations where you know how much time you need, it is valid to increase the timeout.
To do that, you need to open the file deploy/transaction-jboss-beans.xml of your instance, and locate the section marked:
<property name="transactionTimeout">300</property>
This is the transaction timeout. It is in seconds, so 300 seconds = 5 minutes. Adjust according to your needs.
them pesky log entries
One thing I wish the JBoss devs would be a little more strict in, is the produced logging. Take a vanilla JBoss 5.1 installation and it will definitely give you scary looking trace logs that seem to indicate something is wrong.In fact, these logs are not for your eyes; they are there as a sort of reminders for the JBoss devs. But unfortunately you still see them. I got annoyed by it, so I tweaked the conf/jboss-log4j.xml file until I saw only logs that I wanted to see.
To get the same clean logging environment as I have, I suggest that you add the following configuration options:
<category name="com.arjuna"> <priority value="WARN"/> </category> <category name="org.jboss.cache.interceptors.CacheLoaderInterceptor"> <priority value="ERROR"/> </category> <category name="org.hibernate"> <priority value="ERROR"/> </category> <category name="org.jboss.ejb3"> <priority value="ERROR" /> </category> <category name="org.jboss.resource.deployers.RARDeployment"> <priority value="ERROR" /> </category>
The file should already have entries for Facelets, Seam and Ajax4jsf.
I'll give a short overview:
com.arjuna
this is the transaction manager used. Most of what it logs is basically useless, but if you are having problems that you might suspect are transaction related, comment out this category.
org.hibernate (all entries)
Hibernate likes to give trace messages that are basically of the type "todo, you are using deprecated stuff" - unfortunately it is not your problem, but that of the Hibernate devs.
org.jboss.ejb3.interceptors
The same story as Hibernate - the EJB module likes to give regular trace deprecated notices that are really not interesting to you and me.
That's it for basic JBoss 5.1 - luckily the 5.1 configuration already contains most of the entries I had to add myself to JBoss 4.2.X.
EJB 3.0 - asynchronous EJB invocations
Note that this is not JBoss specific, but it is a nice fun fact anyway. The EJB 3.1 spec (part of JEE6) adds support for asynchronous EJB invocation. But what if we are not using JEE6 yet?Slightly less documented, since EJB 2.x there is built in timer support. JBoss can even make these timers persistent (in the TIMER table that JBoss will create in the database pointed to by DefaultDS).
Timers can be used to, after a specific interval, invoke a certain EJB method. In other words, this is an asynchronous EJB invocation. We can abuse this fact to easily do a long-running task, for example the processing of a file, in the background while our application goes its merry own way. When the EJB method finishes you can implement some kind of notification if you want, that is up to you.
On the code level, you create an EJB and into it you inject a TimerService.
@Resource private TimerService timer;
Now we need to define an EJB call that is going to be invoked asynchronously.
@Timeout @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public void myAsynchronousTask(Timer timer) { ... }
The method has to be of return type void and accept a Timer as a parameter; the name is up to you. The @Timeout annotation defines which method will be invoked by the timer. I annotate this method as not having a transaction; this is up to you, but realize that if it is a long running task, you may get a transaction timeout. In general I'd expect these kind of tasks to not need an active transaction (but it may invoke other EJB calls that do run in their own transaction).
All we need now is a way to actually schedule the task.
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public void invokeTask(MyObject myObject) { timer.createTimer(0L, myObject); }
createTimer() schedules the task, in this case with a delay of 0 milliseconds, meaning the asynchronous call will happen instantly. The second parameter is a generic object you can pass along to the invoked EJB call; you can fetch it back like this:
@Timeout @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public void myAsynchronousTask(Timer timer) { MyObject myObject = (MyObject) timer.getInfo(); ... }
Now all you have to do is call invokeTask() - your code will continue as if nothing has happened, but in the background the server will be busy running your long-running myAsynchronousTask() call. Neat, isn't it!?
A word of caution: persistent timers will go through a datasource. If the datasource you use for JBoss and for your application are different, make sure to use an XA datasource for your application because you are effectively creating a distributed transaction. The JBoss datasource can stay a local-tx-datasource.
Defining a context for a web module
There are multiple ways to couple a context name to a web module.Option 1: define in the application.xml
If you deploy your application as an EAR you don't have to think long about it. Just declare the web root in your application.xml file, like this:
<module> <web> <web-uri>your-web-module.war</web-uri> <context-root>/appname</context-root> </web> </module>
When the JBoss instance is running on the localhost, the application will be available under http://localhost:8080/appname.
Option 2: jboss-web.xml
If you deploy only a war, then you can add a file META-INF/jboss-web.xml, with the following basic configuration:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 5.0//EN" "http://www.jboss.org/j2ee/dtd/jboss-web_5_0.dtd";> <jboss-web> <context-root>appname</context-root> </jboss-web>
There is even an option 3: defining a context.xml file in your war. I would stick with one of the two defined options though. The JBoss documentation also advises you to use jboss-web.xml over context.xml. The context.xml really only exists for compatibility with Apache Tomcat (which the web components of JBoss are built on).
Option 4? Don't define any context anywhere. In that case the context will be equal to the name of your war; so if your war is named myapp.war, the context will be /myapp.
The JNDI view
You know about the nifty jmx-console built into JBoss right? It provides many functions that are useful for maintenance and monitoring. One of the nice features it provides is the jndiView. Invoking the list method on this MBean will give you a nice overview of the entire JNDI tree as it exists at that point in JBoss; this can help you diagnose deployment problems as it not only tells you which resources (such as EJBs) are deployed or aren't deployed, but it also tells you under which name they can be looked up.Defining your own JNDI name for an EJB
You know, or should know, that by default under JBoss an EJB will have a JNDI name with the following pattern:EARNAME/BeanName/INTERFACE
Where EARNAME is the name of the ear you deployed, BeanName is the actual name of the EJB bean class and INTERFACE is either local or remote. When you deploy an EJB jar without an EAR, the pattern is BeanName/INTERFACE.
Using a JBoss specific annotation you can define at the code level what the JNDI lookup name for a specific EJB is going to be, overriding the name that JBoss assigns to it. The annotation in question is @LocalBinding; to use it in your code you need to have the jboss-ejb3-ext-api dependency on your classpath, as is described in the scheduler paragraph.
You use this annotation like this:
@Stateless @LocalBinding(jndiBinding = "MyEjb/local") public class MyEjbBean implements MyEjbLocal
Of course there is also a @RemoteBinding annotation. Try it out; if you check the jndiView function of the jmx-console, you'll notice that the EJB (or the local interface of that EJB to be more exact) is now deployed under the name you specified here!
Note that you can provide any name you want (although there may be a restriction on the characters you can use); you could even use a name as simple as this:
@Stateless @LocalBinding(jndiBinding = "MyEjb") public class MyEjbBean implements MyEjbLocal
Just beware that simplifying the name too much will invite name clashes.
Watch it!
These are JBoss specific annotations; using them makes it so your code is not portable among different application servers. In fact, it is not even portable between JBoss versions. A more portable way (across JBoss versions anyway) is to configure your EJBs in a META-INF/jboss.xml file in your EJB:
<jboss> <enterprise-beans> <session> <ejb-name>MyEjbBean</ejb-name> <local-jndi-name>MyEjb</local-jndi-name> </session> </enterprise-beans> </jboss>
local-jndi-name is the name for the local binding. If you want to override the remote binding also, use jndi-name.
Looking up a remote interface from a client
Seriously, you want to do a remote EJB lookup? I have one piece of advice for you: turn the EJB into a jax-ws webservice. Not only does it make your EJB far more portable, but you also don't need a whole host of JBoss dependencies on your client's classpath to make it work; a basic Java 6 runtime is enough. I even wrote an article to show how it is done.If you must persist, here's how you do it.
STEP 1: give the EJB of choice a remote interface (duh)
STEP 2: in the client, define the correct JNDI lookup properties
Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory"); env.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces"); env.put(Context.PROVIDER_URL, "jnp://localhost:1099"); InitialContext context = new InitialContext(env);
Of course, substitute localhost for the correct host name when you are not connecting to a server running on the localhost.
Hardcoding connection properties is hardly a good idea; you can also create a file jndi.properties and put it on the root of the classpath of your client. Give it the following content:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=localhost:1099
And then change your code to simply this:
InitialContext context = new InitialContext();
STEP 3: lookup the bean of your choice.
MyBeanRemote ejb = (MyBeanRemote) context.lookup("MyEar/MyBean/remote");
Trying this code will net you ClassNotFound errors. To be able to make it work you need the JBoss client libraries on your classpath, which you can find in the JBOSS_HOME/client directory of your server installation. You can start with adding jbossall-client.jar to your client's classpath; you may need to add a few more additional transient dependencies such as commons-logging. Only one way to find out; keep trying and adding missing dependencies until you stop getting errors :/
This is the official way it should work when running JBoss 5.1. For completeness, if you want to do the same for a JBoss 4.2.X server, you'd need to change the code to the following:
InitialContext context = new InitialContext(); Object key = context.lookup("MyEar/MyBean/remote"); MyBeanRemote ejb = (MyBeanRemote) PortableRemoteObject.narrow(key, MyBeanRemote.class);
The old 'EJB 2.X' way of doing it, even if you are looking up an EJB 3.0 bean. This may seem weird, but you have to remember that JBoss 4.2.X is basically a J2EE 1.4 server with EJB 3.0 support patched into it.
Changing port bindings
By default, JBoss listens on port 8080 (HTTP) and 8443 (HTTPS). If you want to put JBoss in a production environment, you will want to change this to the default port 80 to not have to add the port to the web url.To do this, we need to modify the conf/bindingservice.beans/META-INF/bindings-jboss-beans.xml file.
To change the web port, search for the following: jboss.web:service=WebServer. Here, change the default port 8080 to 80, or whatever port you want. Search for the same search string again; this time you'll get the HTTPS binding; change the 8443 port to 443 or whatever port you want.
Note that it is not recommended to change the default port when you are deploying to this server from Eclipse.
Reaching a JBoss instance through a network
Note: this works on any JBoss up to and including JBoss 6.Need to give a quick demo in some office and you don't have time to setup a pc there? No problem: you can simply run your own development JBoss by making it listen on your local (internal) IP address.
To do this, go to the bin directory of your JBoss server and execute the following command:
run -c INSTANCENAME -b IP.ADDRESS.GOES.HERE
Where INSTANCENAME is the name of the JBoss instance you want to start (default for example) and of course input your computer's internal IP address after the -b switch.
Try it out by going to any other computer in the same network and trying to reach a webapp in your instance in any browser by using your computer's IP address as the hostname.
Performance tuning:disable JSP checks
In this day and age, are you still using JSPs? I hope not, there are far better alternatives out there like Velocity, Wicket, Play framework or Facelets.So one thing we can safely do is turn of periodic checks that are done to JSPs, such as checking for changes. This can improve runtime performance slightly.
Open the file deployers/jboss-web.deployer/web.xml. In this file, search the declaration of the jsp servlet. Here you will most likely have to add the following two parameters:
<init-param> <param-name>reloading</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>checkInterval</param-name> <param-value>200000</param-value> </init-param> <init-param> <param-name>development</param-name> <param-value>false</param-value> </init-param>
This will not completely disable JSP modification checks (for some reason a value of 0 for checkInterval was not accepted by my server instance, while the Tomcat documentation seems to suggest this will turn the feature off), but it will at least make them very infrequent.
Disable AJP
By default JBoss enables AJP. If you want to use the HTTP connector directly, you are best of disabling AJP altogether. Simply do this by opening deploy/jbossweb.sar/server.xml and commenting out the ajp connector you find there.Note: be wary when you do this; turning off AJP might decrease performance in certain cases. If you have no reason to turn it off, then don't.
Performance tuning by removing unneeded services
(This applies to JBoss 5.1 only).JBoss 5.1, although you don't see it directly, is quite modular. It has a service stack that are tiny mini applications within JBoss that can be included and excluded by choice.
By default, JBoss takes some time to start up. On a fast machine with a fast harddrive it can still take up a good 25 seconds before you're up and running. By removing a few of the services you aren't using you might be able to shave a few seconds off. When you start to remove services, be sure that you do not do this in the default server instance; you should keep that one unmodified just in case you need to restore some mistakes you made.
I could sum it up myself, but it is easier to just look at this forum post that sums it up very well:
http://community.jboss.org/wiki/jboss5xtuningslimming
I wouldn't recommend blindly stripping everything mentioned in this article! Please apply some careful consideration to it ;) Removing all unnecessary services and preventing excessive logging in my instance, I managed to shave 3 seconds off the startup time. Is it really worth it? You be the judge. The article does mention a way to reduce memory usage though, which is worth checking out.