Chapter 6. JBoss EJB 3.0 extensions
Prev |  | Next

## Chapter 6. JBoss EJB 3.0 extensions

JBoss provides a few extensions to the EJB 3.0 spec. This chapter describes those features here.

## 6.1. @Service EJBs

An extension offered by JBoss EJB 3.0 is the notion of a `@org.jboss.annotation.ejb.Service` annotated bean. They are singleton beans and are not pooled, so only one instance of the bean exists in the server. They can have both `@Remote` and `@Local` interfaces so they can be accessed by java clients. When different clients look up the interfaces for `@Service` beans, all clients will work on the same instance of the bean on the server. When installing the bean it gets given a JMX ObjectName in the MBean server it runs on. The default is

    jboss.j2ee:service=EJB3,name=<Fully qualified name of @Service bean>,type=service

You can override this default ObjectName by specifying the `objectName` attribute of the `@Service` annotation.

### 6.1.1. @org.jboss.annotation.ejb.Management interface

In addition to supporting `@Local` and `@Remote` interfaces, a `@Service` bean can also implement an interface annotated with `@Management` . This interface will wrap the bean as an MBean and install it in the JBoss MBean Server. The operations and attributes defined in the `@Management` interfaces become MBean operations and attributes for the installed MBean.. The underlying bean instance is the same as the one accessed via the `@Local` or `@Remote` interfaces.

### 6.1.2. Lifecycle Management and dependencies

Just as for normal MBeans running in JBoss, `@Service` lifecycle management beans support lifecycle management. This involves

- Lifecycle Methods.
- Dependencies.

#### 6.1.2.1. Lifecycle Methods

Your `@Management` interface may contain methods with the following signatures

       void create() throws Exception;
       void start() throws Exception;
       void stop();
       void destroy();

Each of these methods corresponds to when the MBean enters a particular state, so you can handle that. You do not need to include any of these methods, and you can pick and choose which ones you want to use. A description of the MBean states these methods correspond to:

- `create()` - called by the server when the service is created and all the services it depends upon have been created too. At this point the service (and all the services it depends on) is installed in the JMX server, but is not yet fully functional.
- `start()` - called by the server when the service is started and all the services it depends upon have been started too. At this point the service (and all the services it depends on) is fully functional..
- `stop()` - called by the server when the service is stopped. At this point the service (and all the services that depend on it) is no longer fully operational.
- `destroy()` - called by the server when the service is destroyed and removed from the MBean server. At this point the service (and all the services that depend on it) are destroyed.

#### 6.1.2.2. Dependencies

We mentioned dependencies between MBeans in the previous section. You can specify what MBeans you depend on by using the `org.jboss.annotation.ejb.Depends` annotation. This will also work for "proper" EJBs, i.e. ones annotated with `@Stateful`, `@Stateless` and `@MessageDriven`. The Depends annotation-type takes an array of the String representation of the JMX ObjectNames of the service we depend on.

                   @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
                   @Retention(RetentionPolicy.RUNTIME)
                   public @interface Depends
                   {
                      String[] value();
                   }

It can either be used on class level, in which case it simply means "I depend on these services". It can also be used on fields or setters in the bean class that take a type of a `@Management` annotated interface. In addition to the meaning of annotating at class level it will inject the `@Management` interface of the service we depend on.

### 6.1.3. Example

First we define a `@Management` interface

    package org.jboss.tutorial.service.bean;

    import org.jboss.annotation.ejb.Management;

    @Management
    public interface ServiceOneManagement
    {
       void setAttribute(int attribute);
       int getAttribute();
       String sayHello();

       void create() throws Exception;
       void start() throws Exception;
       void stop();
       void destroy();
    }

We also have a Remote interface

    package org.jboss.tutorial.service.bean;

    public interface ServiceOneRemote
    {
       public void setAttribute(int attribute);
       public int getAttribute();
    }

And fnally we have a @Service annotated bean implementing our interfaces

    package org.jboss.tutorial.service.bean;

    import org.test.OtherServiceManagement;

    import javax.ejb.Remote;
    import org.jboss.annotation.ejb.Service;
    import org.jboss.annotation.ejb.Depends;

    @Service (objectName = "jboss:custom=Name")
    @Remote(ServiceOneRemote.class)
    @Depends ({"jboss:service=someService"})
    public class ServiceOne implements ServiceOneRemote, ServiceOneManagement
    {
       int attribute;

       @Depends({"jboss.j2ee:service=EJB3,name=org.test.OtherService,type=service"})
       OtherServiceManagement other;

       public void setAttribute(int attribute)
       {
          this.attribute = attribute;
       }

       public int getAttribute()
       {
          return this.attribute;
       }

       public String sayHello()
       {
          return "Hello from service One";
       }

       // Lifecycle methods
       public void create() throws Exception
       {
          System.out.println("ServiceOne - Creating");
       }

       public void start() throws Exception
       {
          System.out.println("ServiceOne - Starting");
       }

       public void stop()
       {
          System.out.println("ServiceOne - Stopping");
       }

       public void destroy()
       {
          System.out.println("ServiceOne - Destroying");
       }
    }

This bean is accessible from remote clients via the `ServiceOneRemote` interface. The `ServiceOneManagement` interface defines the operations to be made available via JMX. It specfies all the lifecycle methods so we will get notified when this bean is created, started, stopped and destroyed. Our bean uses a custom object name, so it will be installed in the MBean server under the name `jboss:custom=Name`. At bean level we have specified that it depends on a service with the name `jboss:service=someService`, so that service must be started before our bean can start. Also, we have specified that we depend on another JMX service called `jboss.j2ee:service=EJB3,name=org.test.OtherService,type=service` and that we want to inject an MBean Proxy implementing the `org.test.OtherServiceManagement` interface into the `other` field. For a more complete example, take a look at the tutorial.

## 6.2. Message Driven POJOs

Doco not complete yet. See tutorial for more information.

## 6.3. Asynchronous invocations

In JBoss EJB 3.0, you can convert a reference to a local or remote session bean into a reference that will invoke asynchronously on the EJB. This is called obtaining an asychronous proxy or interface. asynchronous interface. Methods called on the asynchronous proxy will be executed asynchronously, and the results can be obtained later on. You obtain an asynchronous proxy by invoking on the `org.jboss.ejb3.asynchronous.Asynch.getAsynchronousProxy` static method. You pass

             package org.jboss.ejb3.asycnhronous;

             public class Asynch
             {
                 public static <T> T getAsynchronousProxy(T ejbRef) {...}

                 public static Future getFutureResult(Object asynchProxy)
             }

The asynchronous proxy created will implement the same local/remote interfaces as the original proxy. When you invoke on a method of the asynch proxy, the method will be invoked in the background. If the method has a return value, the value returned by the method will be null or zero depending on the return type. This will not be the true return value of the asynch method so you can just throw this away. To obtain the real result you must obtain `org.jboss.aspects.asynch.Future` object. This Future object works much the same way a `java.util.concurrent.Future` object does.

Let us look at an example. We have the following session bean.

             package org.acme.test;

             @javax.ejb.Remote
             public interface Test
             {
                int perfomHeavyOperation(int i);
                String echo(String s);
             }

             package org.acme.test;

             @javax.ejb.Stateless
             public class TestBean implements Test
             {
                public int perfomHeavyOperation(int i)
                {
                   try
                   {
                      //Simulate time-consuming operation
                      Thread.sleep(10000);
                      return i;
                   }
                   catch(Exception e)
                   {
                      throw new RuntimeException(e);
                   }
                }

                public String echo(String s)
                {
                   return s;
                }
             }

As you can see there is nothing special about the session bean, now let's look at the client code. First we look up the remote interface:

             InitialContext ctx = new InitialContext();
             Test test = (Echo)ctx.lookup(org.acme.test.Test.getName());

We now have a reference to the bean's normal interface. Calls done on this interface will execute synchronously. The following call to `perfomHeavyOperation()` will block the client thread for 10 seconds.

             int i = test.performHeavyOperation(1);
             //i will be 1

Now to demonstrate the asynchronous functionality, we the asynchronous proxy.

             Test asynchTest = org.jboss.ejb3.asynchronous.Asynch.getAsynchronousProxy(test);

Calls made on the asynchronous interface will return 0 in the case of a simple return type or null in the case of an Object return type. We will see how to obtain the return value further down.

                int j = asynchTest.performHeavyOperation(123);
             //j will be 0

The call to `perfomHeavyOperation()` returns immediately, and our client thread is now free to do other stuff while the business method executes.

             //You can do other stuff in client's thread

Now that we have finished doing things in our thread while the business method has been executing on the server, we obtain the `Future` which will hold the result of our asynchronous invocation.

             Future future = org.jboss.ejb3.asynchronous.Asynch.getFutureResult(asynchTest);

It is important to note that you must call getFutureResult() to obtain the future for the last method call. If you call another method on the asynch proxy, then you will lose the previous Future. The asynchronous invocation might not have finished yet (in case the extra things we did in the client code took < 10 seconds), so we check for this here:

             while (!future.isDone())
             {
                Thread.sleep(100);
             }

Now that the asynchronous invocation is done, we can obtain its return value from the `Future` object.

             int ret = (String)future.get();
             //ret will be 123

Prev | Up | Next
Chapter 5. Hibernate Integration | Home | Chapter 7. JBoss EJB 3.0 jboss.xml deployment descriptor