EJB3.1 in AS 6.0.0.M2

Overview

 

JBoss AS 6.0.0.M2 which has been released on Feb 16th 2010, contains the initial support for EJB3.1. More specifically, it includes support for:

 

 

  • EJB3.1 no-interface view

  • EJB deployment through .war files

 

What to download and how to use

 

JBoss AS 6.0.0.M2 can be downloaded from here. After downloading, start and stop the server once to ensure that it boots fine.

 

The next step would be to deploy a EJB3.1 app into this server. Let's first look at a simple EJB3.1 nointerface view bean:

 

EJB3.1 no-interface view:

 

package org.jboss.ejb3.nointerface.example;

import javax.ejb.Stateless;

@Stateless
public class Calculator
{
   public int subtract(int a, int b)
   {
      return a - b;
   }

   public int add(int a, int b)
   {
      return a + b;
   }
}

 

That's it for a no-interface view EJB. Now let's write a client which uses this no-interface view bean. Remember that the no-interface view is a local view, which means that the client has to run in the same JVM as the bean. So for the sake of simplicity, in this example, let's create another bean which acts as a client of this no-interface view bean. Here's the AccountManagerBean stateless bean which exposes a @Remote view:

 

package org.jboss.ejb3.nointerface.example;

public interface AccountManager
{
   /**
    * Credits the amount from the account corresponding to the
    * accountNumber
    *
    * @param accountNumber Account number
    * @param amount Amount to be credited
    * @return
    */
   int credit(long accountNumber, int amount);
  
   /**
    * Debits the amount from the account corresponding to the
    * accountNumber
    *
    * @param accountNumber Account number
    * @param amount Amount to be debited
    * @return
    */
   int debit(long accountNumber, int amount);
}

 

package org.jboss.ejb3.nointerface.example;

import javax.ejb.EJB;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

@Stateless
@Remote(AccountManager.class)
public class AccountManagerBean implements AccountManager
{

   /**
    * Inject the no-interface view of the Calculator
    */
   @EJB
   private Calculator simpleCalculator;

   /**
    * @see org.jboss.ejb3.nointerface.integration.test.common.AccountManager#credit(int)
    */
   @Override
   public int credit(long accountNumber, int amount)
   {
      // get current account balance of this account number, from DB.
      // But for this example let's just hardcode it
      int currentBalance = 100;

      Calculator calculator = null;
      // lookup the no-interface view of the Calculator
      // We could have used the injected Calculator too, but
      // in this method we wanted to demonstrate how to lookup an no-interface view
      try
      {
         Context context = new InitialContext();
         calculator = (Calculator) context.lookup(Calculator.class.getSimpleName() + "/no-interface");
      }
      catch (NamingException ne)
      {
         throw new RuntimeException("Could not lookup no-interface view of calculator: ", ne);
      }
      return calculator.add(currentBalance, amount);

   }

   /**
    * @see org.jboss.ejb3.nointerface.integration.test.common.AccountManager#debit(int)
    */
   @Override
   public int debit(long accountNumber, int amount)
   {
      // get current account balance of this account number, from DB.
      // But for this example let's just hardcode it
      int currentBalance = 100;
     
      // let's use the injected calculator
      return this.simpleCalculator.subtract(currentBalance, amount);
   }

}

 

The AccountManagerBean has 2 methods, each of which uses the no-interface view Calculator bean. The credit() method looks up the no-interface view by the JNDI name, whereas the debit() method uses an injected reference of the no-interface view Calculator. These 2 methods of the AccountManagerBean demonstrate the 2 ways in which you can get hold of the no-interface view of the bean.

 

Now package all these classes into a .jar and deploy it to the server, you built earlier. That's it! You now have the beans deployed on the server. The next step is to write a simple client which access the AccountManager to perform the operations. In this example, let's use a standalone java class which through its main() method, looks up the remote view of the AccountManagerBean and invokes the operations. Remember that the AccountManagerBean is NOT a no-interface view bean and hence can be accessed remotely (i.e. from the standalone java client). Here's our client:

 

package org.jboss.ejb3.nointerface.example.client;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class Client
{
   /**
    * Simple test client to be used in the no-interface view example
    *
    * @param args
    */
   public static void main(String[] args)
   {
      AccountManager accountManager = null;
      // lookup the account manager bean
      try
      {
         Context context = new InitialContext();
         accountManager = (AccountManager) context.lookup(AccountManagerBean.class.getSimpleName() + "/remote");
      }
      catch (NamingException ne)
      {
         throw new RuntimeException("Could not lookup AccountManagerBean: ", ne);
      }
      long dummyAccountNumber = 123;
      // credit 50 dollars (Note that the current balance is hard coded in the bean to 100)
      // so after crediting, the current balance is going to be 150
      int currentBalance = accountManager.credit(dummyAccountNumber, 50);
     
      System.out.println("Current balance after crediting 50$ is " + currentBalance);
     
      // now let's debit 10 dollars (Note that the current balance is again hard coded in the bean to 100).
      // So after debiting, the current balance is going to be 90
      currentBalance = accountManager.debit(dummyAccountNumber, 10);
     
      System.out.println("Current balance after debiting 10$ is " + currentBalance);
   }

}

 

 

What should I try next?

 

The above example was just to get you started with EJB3.1 no-interface view. Try out the no-interface view within your own applications and let us know if you run into any issues. Feel free to start a discussion about any issues around this, in our EJB3 user forum or ping us on IRC. The more issues you find now, the better - because we can get some of them fixed before AS 6.0.0.M2 is released.

 

I have some tutorial for no-interface, Can I contribute?

 

Similar to our other EJB3 tutorials, we are going to include a tutorial for the no-interface view. Infact, the example that is posted here in the wiki, can perhaps be just added as a tutorial in SVN. So if anyone of you wants to contribute a tutorial (either the one that's here or any better one) and a chapter in our guide, then feel free to let us know - either through the forums or IRC.

 

Deployment of EJBs through a .war file:

 

Now that we have seen the no-interface view example, let's now move on to the next EJB3.1 feature that's being included in 6.0.0.M2 (and is currently available in AS trunk). EJB3.1 spec lets .war files to contain EJBs. So now you can deploy your EJBs through the .war files. EJBs in .war files have to be packaged in either of the following ways:

 

  • In .war/WEB-INF/classes

  • In .war/WEB-INF/lib/somejar.jar

  • A .war/WEB-INF/ejb-jar.xml

 

(For the complete details about the deployment packaging, please refer to section 20.4 of EJB3.1 spec)

 

Let's consider an example for this. Let's first see our no-interface view calculator:

 

package org.jboss.ejb3.war.deployment.example;

import javax.ejb.Stateless;


/**
* CalculatorInWEBINFClasses
*
* A no-interface view bean which will be placed in the .war/WEB-INF/classes folder.
*
*/
@Stateless
public class CalculatorInWEBINFClasses
{

   public int add (int a, int b)
   {
      return a + b;
   }
  
   public int subtract (int a, int b)
   {
      return a - b;
   }
}

 

This is a simple no-interface view Stateless bean which we will be placing in the .war/WEB-INF/classes folder.

 

Now let's see another bean which uses this calculator no-interface view. It's a Stateful counter bean which exposes a remote view:

 

package org.jboss.ejb3.war.deployment.example;

/**
* Counter
*
*/
public interface Counter
{

   int increment();
  
   int decrement();
}

 

package org.jboss.ejb3.war.deployment.example;

import javax.ejb.EJB;
import javax.ejb.Remote;
import javax.ejb.Stateful;



/**
* CounterBeanInWEBINFLibJar
*
* A Stateful bean configured deployed through a jar file in .war/WEB-INF/lib folder.
*
*/
@Stateful
@Remote (Counter.class)
public class CounterBeanInWEBINFLibJar implements Counter
{

   private int count = 0;
  
   /**
    * Inject the no-interface view bean
    */
   @EJB
   private CalculatorInWEBINFClasses calculator;
  
   @Override
   public int decrement()
   {
      this.count = this.calculator.subtract(this.count, 1);
      return this.count;
   }

   @Override
   public int increment()
   {
      this.count = this.calculator.add(this.count, 1);
      return this.count;
   }

}

 

So we have the CounterBeanInWEBINFLibJar bean which is @Stateful and uses the no-interface view calculator bean (see the @EJB injection):

 

  /**
    * Inject the no-interface view bean
    */
   @EJB
   private CalculatorInWEBINFClasses calculator;

 

We'll be packaging this CounterBeanInWEBINFLibJar bean class in a jar file (let's call it my-ejb3-library.jar) and placing that jar file in .war/WEB-INF/lib folder.

 

These 2 beans should be enough for testing our deployment, but for the sake of showing that we can even package a ejb-jar.xml in the .war/WEB-INF folder, let's add one more bean to this deployment and let's use a ejb-jar.xml to configure it. So here's the DelegateBean which just delegates the calls to the CounterBeanInWEBINFLibJar bean:

 

package org.jboss.ejb3.war.deployment.example;

import javax.ejb.EJB;


/**
* DelegateBean
*
* A Stateful bean configured through a ejb-jar.xml in .war/WEB-INF folder. This bean
* just delegates the calls to a bean ({@link CounterBeanInWEBINFLibJar}) which is deployed
* in the .war/WEB-INF/lib/.jar
*
*/
public class DelegateBean implements Counter
{


   /**
    * Inject the remote view of CounterBeanInWEBINFLibJar
    */
   @EJB(beanName = "CounterBeanInWEBINFLibJar")
   private Counter counterBean;

  
   @Override
   public int decrement()
   {
      return this.counterBean.decrement();
   }

   @Override
   public int increment()
   {
      return this.counterBean.increment();
   }

}

 

Notice that we haven't used any @Stateful/@Stateless annotations on this bean. We could have used it, but since we want to show the ejb-jar.xml usage, we decided not to do so. So here's the ejb-jar.xml for this bean:

 

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
             http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
      version="3.1">
   <enterprise-beans>
      <session>
         <ejb-name>DelegateBean</ejb-name>
         <business-remote>org.jboss.test.ejb3.war.deployment.Counter</business-remote>
         <ejb-class>org.jboss.test.ejb3.war.deployment.DelegateBean</ejb-class>
         <session-type>Stateful</session-type>
      </session>
   </enterprise-beans>
</ejb-jar>

 

This ejb-jar.xml will be placed in .war/WEB-INF folder.

 

With this, we now have 3 beans. Let's finally write a simple client which looks up the remote view of the DelegateBean and invokes a method on it:

 

package org.jboss.ejb3.war.deployment.example.client;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

/**
* Client
*
* @author Jaikiran Pai
* @version $Revision: $
*/
public class Client
{
   /**
    * Simple test client to be used in the no-interface view example
    *
    * @param args
    */
   public static void main(String[] args)
   {
      Context ctx = new InitialContext();
      Counter counter = (Counter) ctx.lookup("DelegateBean/remote");

      int count = counter.increment();
      System.out.println("Count after increment is: " + count);

      // increment one more time
      count = counter.increment();
      System.out.println("Count after second increment is: " + count);
     
      // now decrement
      count = counter.decrement();
      System.out.println("Count after decrement is: " + count);

      // decrement one more time
      count = counter.decrement();
      System.out.println("Count after second decrement is: " + count);
   }
}

 

To summarize the flow, here's how it will all look like:

 

Client -> (Stateful) DelegateBean -> (Stateful) CounterBeanInWEBINFLibJar -> (no-interface view) CalculatorInWEBINFClasses

 

and the packaging will look like this:

 


my-ejb3-app.war
|
|--- WEB-INF
|       |
|       |--- web.xml
|       |--- ejb-jar.xml (contains DelegateBean *configuration*)
|       |
|       |--- lib
|       |     |--- my-ejb3-library.jar (contains annotated CounterBeanInWEBINFLibJar bean)
|       |     |         |
|       |     |         |--- org.jboss.ejb3.war.deployment.example.CounterBeanInWEBINFLibJar
|       |
|       |
|       |--- classes (contains annotated CalculatorInWEBINFClasses bean, Counter interface and the DelegateBean class)
|       |       |
|       |       |--- org.jboss.ejb3.war.deployment.example.CalculatorInWEBINFClasses
|       |       |--- org.jboss.ejb3.war.deployment.example.Counter
|       |       |--- org.jboss.ejb3.war.deployment.example.DelegateBean

 

So that's how the deployment looks like. If you look closely, you will notice that we have a web.xml in that .war file. The important bit about the web.xml is that it should use the web-app_3_0.xsd to indicate that it's a 3.0 web-app. This is very important because, the EJB deployments will be skipped if this is not a 3.0 web-app. At the very least, the web.xml should contain:

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee web-app_3_0.xsd"
      version="3.0">
     
     
</web-app>     

 

We think that adding web-app_3.0.xsd declaration is going to be one of the common thing that the user is going to forget, while writing the application. So we have added this to our EJB3.1 FAQ

 

So that's it for our .war deployment. Place that .war in the AS deploy folder and run the (standalone) client program to test the application.

 

What next?

 

This was just a simple (non-practical) example to get you started with deploying EJBs through .war files. Try out your own application .war deployments and let us know if you run into any issue. Feel free to start a discussion about any issues around this, in our EJB3 user forum or ping us on IRC. To repeat myself - the more issues you find now, the better - because we can get some of them fixed before AS 6.0.0.M2 is released

 

I have some tutorial for EJB deployments through .war file, Can I contribute?

 

We are going to include a .war deployment example, similar to the one explained here, in our EJB3 tutorials. We might use this same example that is posted here in the wiki. But if you have a better example and want to contribute, then feel free to let us know - either through the forums or IRC.

 

Next steps for EJB3.1 support in AS-6

 

We'll be adding more and more EJB3.1 support to AS-6. We are planning to deliver the next set of features incrementally before 6.0.0.M3. So stay tuned!