The road so far….

May 14, 2011

JEE6 CDI : Using Apache OpenWebBeans

Filed under: java — Tags: — Rahul Sharma @ 7:53 pm

Past couple of weeks  I was playing with JEE6 Context and dependency injection(CDI) using the Apache OpenWebBeans implementation. My intention was  have a look at the SPI, play around with it and make it work for a few samples. I started with a simple application and tested it in standalone manner using Junit, then created a web app that deployed in Tomcat and then added a web services layer over it. Before I can go forward I need to download a an implementation of JEE6 CDI specification. I am using the Apache OWB implementation for this purpose. The OWB project released their 1.1 version few weeks back. The project uses the specs from the Apache Geromino (open src JEE server) and I believe OWB will be used in the project for its JEE6 implementation.

        <dependency>
            <groupId>org.apache.openwebbeans</groupId>
            <artifactId>openwebbeans-impl</artifactId>
            <version>${owb.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-atinject_1.0_spec</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-jcdi_1.0_spec</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-interceptor_1.1_spec</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.openwebbeans</groupId>
            <artifactId>openwebbeans-spi</artifactId>
            <version>${owb.version}</version>
        </dependency>

Creating a standalone application was simple and straight forward.It gave me a playground to work with JEE6 annotations in the most simple manner. My Application was simple, I have the following RandomService that can perform some RandomOperation.

public class RandomService {
    @Inject
    OperationFactory factory;

    String perform(Integer... args) {
        RandomOperation operation = factory.getOperation();
        System.out.println(operation.getClass() + ": hashcode :" + operation.hashCode());
        return operation.exec(args);
    }
}
public interface RandomOperation {
    T exec(E... args);
}

There can be a couple of RandomOperations like Addition, Multiplication etc. I would also describe a type for them as MathOperation. Also there is a OperationFactory that is responsible for creating the RandomOperation.

The @Inject annotation in the service should just inject  an instance of this OperationFactory.

public abstract class MathOperation implements RandomOperation {
    @PostConstruct
    void init() {
        System.out.println("int up called on :" + this.getClass());
    }

    @PreDestroy
    void cleanup() {
        System.out.println("clean up called on :" + this.getClass());
    }}
class Addition extends MathOperation {
    @Override
    public String exec(Integer... args) {
        Integer sum = 0;
        for (Integer integer : args) {
            sum += integer;
        }
        return this.getClass() + ":" + sum.toString();
    }}
class Multiplication extends MathOperation {
    @Override
    public String exec(Integer... args) {
        Integer product = 1;
        for (Integer integer : args) {
            product *= integer;
        }
        return this.getClass() + ":" + product.toString();
    }}
@Singleton
class OperationFactory {
    @Inject
    Provider operation;

    public RandomOperation getOperation() {
        return operation.get();
    }

    @PreDestroy
    void cleanup() {
        System.out.println(this.getClass() + " : clean up called on");
    }
}

There a couple of things used here :

  • The @PostConstruct, invoked by the container after the object is created,  and @PreDestroy, invoked by the container when an object is destroyed,  are used here.
  • By default every time you ask the container you would get a new instance but we do not want this with the OperationFactory. So @Singleton annotation is used here to return a single instance every time you ask the container.
  • The specifications also provide a Provider interface that should be used to get a container backed factory that can give back an instance from the context. The OperationFactory uses this provider interface to get an instance of operation.

Now this much of code is good enough for testing. I tried loading this app in standalone manner in my Junit test case.

Loading a container is straightforward as shown in the following test case.

public class ContextLoaderTest {
    private ContainerLifecycle container;
    private RandomService service;

    @Before
    public void loadContext() {
        WebBeansContext currentInstance = WebBeansContext.currentInstance();
        container = currentInstance.getService(ContainerLifecycle.class);
        container.startApplication(null);
        BeanManager beanManager = container.getBeanManager();
        service = getBeanInstance(beanManager, RandomService.class);
    }

    private  T getBeanInstance(BeanManager beanManager, Class type) {
        Set> beans = beanManager.getBeans(type);
        Bean bean = beans.iterator().next();
        return type.cast(bean.create(null));
    }

    @Test
    public void test1() throws Exception {
        service.perform(new Integer[] { 20, 10, 1 });
    }

    @After
    public void stopApp() {
        container.stopApplication(null);
    }
}

But the container will not load, it requires a META-INF/beans.xml  file in its classpath. I could not make any use of the xml file, I created a following empty file at the required location:

<beans xmlns="urn:java:ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:java:ee http://java.sun.com/jee/beans-1.0.xsd">
</beans>

Now the container starts loading up, but gives back the an exception stating  “There is more than one api type with : info.jee6.MathOperation with qualifiers : Qualifiers“.  The problem here is that the container is unable to decide which class to instantiate from Addition or Multiplication.  So we can solve it in one of the following manner :

  • use a Provider with a specific type, and not use the generic MathOperation type.
  • use a @Typed annotation over any of the Addition or Multiplication. The annotation will make either of the class type specific and the other one would be constructed for the generic MathOperation type.
@Typed(Addition.class)
class Addition extends MathOperation{
....
}

The test should execute successfully now. If we look at the sysouts that get printed at in the test there is one interesting thing. The init method defined in the MathOperation  gets called but not the cleanup method. Only the cleanup in OperationFactory gets called. This was quite odd and to know why this was happening I had to look into the StandaloneLifeCycle class of  OpenWebbeans. According to the implementation the destroy method gets called for a singleton context, application, session, request contexts etc, but not for a default one (prototype context in Spring terms)  this was quite strange for me. Thus if I put a @Singleton annotation on the Multiplication class the cleanup method starts getting called.

I also created a ServiceFactory that can be used to create an instance of RandomService using the @Produces and @Disposes annotations. These annotation are used to make factory method for the container.

class ServiceFactory {
    @Produces
    RandomService create(OperationFactory factory) {
        System.out.println("creating  service instance");
        RandomService service = new RandomService();
        service.factory = factory;
        return service;
    }

    void destroy(@Disposes RandomService service) {
        System.out.println("Destroying service instance");
    }
}

Here the create method has a @Produces annotation, the container would call this method for creating instance of RandomService. Similarly we can create a @Disposes method that can be called  to destroy this bean.

But if I run the test now, it would simply fail again complaining “multiple APIs having type RandomService“.

  • Using the RandomService class constructor
  • Using the @Produces method in the factory.

In order to fix it I created a qualifier annotation @NonInstantiable and applied it on the RandomeService class as I wanted to use the factory for creating an RandomService.

@java.lang.annotation.Retention(RetentionPolicy.RUNTIME)
@Qualifier
@interface NonInstantiable {}
@NonInstantiable
public class RandomService{...}

The NonInstantiable annotation should  have the @Qualifier annotation and a run-time retention policy only then it would work as a qualifier. After I fix this my @Produces method starts getting called for bean creation but I could not make the @Disposes method work. May be this has something to do with the implementation of the StandaloneLifeCycle OWB container or there can be something in the specs that I do not know for now.

The standalone application gives me a good idea of what can I do with the CDI, but that is not enough. I extended this standalone app to a web app, created a Servlet for the service and deployed it on Tomcat 6. I would share that experience in another blog.

Advertisements

3 Comments »

  1. […] am I JEE6 CDI : Using Apache OpenWebBeans […]

    Pingback by Apache OpenWebBeans CDI, from standalone to webapp « The road so far…. — May 15, 2011 @ 9:33 pm

  2. Hello Rahul,
    I downloaded from Apache site and trying to run some examples. There is no documentation on how to use it. I tried your example and I get java.util.NoSuchElementException exception when I run junit test. Can you please help me run this example?

    Thanks in Advance,
    SK

    Comment by SK — January 15, 2012 @ 3:46 am

    • Hi SK
      I do not know the issue you are facing. But if you can send some stacktrace, then I can look around.

      regards
      Rahul

      Comment by Rahul Sharma — January 16, 2012 @ 8:41 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: