The road so far….

May 15, 2011

Apache OpenWebBeans CDI, from standalone to webapp

Filed under: java — Tags: — Rahul Sharma @ 9:33 pm

In my post regarding “JEE6 : Apache OpenWebbeans” I created a standalone app that was being tested using Junit but that was not sufficient. In my this post I will extend the same application and use servlet to create a webapp that can be deployed on Tomcat 6.  The OpenWebBeans project provides a Tomcat 6 plugin that will be used here. Also in order to enable the servlet to be initialized by the ContainerLifecycle we  will be required to use the WebBeansConfigurationListener available in the openwebbeans-web project.

We already had a container in our project now if we include openwebbeans-web dependency in the project there will be one more container implementation in the project. The  ContainerLifecyle API  that we used in out Junit  would now complain as it will try to load the WebContainerLifeCycle  and that would require a ServletContext. There can be couple of ways to fix this issue.

  • explicitly ask for StandaloneLifeCycle using the get API of ContainerLifecycle
    WebBeansContext currentInstance = WebBeansContext.currentInstance();
    container = currentInstance.get(StandaloneLifeCycle.class);
    container.startApplication(null);
    
  •  create a  openwebbeans.propeties file at META-INF/openwebbeans location in the tests classpath and explicitly ask for the StandaloneLifeCycle.
    org.apache.webbeans.spi.ContainerLifecycle=org.apache.webbeans.lifecycle.StandaloneLifeCycle
    
  • do not put the openwebbeans-web project as there will be nothing specific that will be used and add it to Tomcat lib directory.

Additionally I was required to add the servlet-api dependency so that I can write a servlet.

<dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>

I created a RandomServlet extending through the HTTPServlet class. The class uses the RandomService earlier created earlier.

public class RandomServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Inject
    @Named("myservice")
    RandomService service;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        int a1 = Integer.parseInt(req.getParameter("a1"));
        int a2 = Integer.parseInt(req.getParameter("a2"));
        int a3 = Integer.parseInt(req.getParameter("a3"));
        String data = service.perform(a1, a2, a3);
        PrintWriter writer = resp.getWriter();
        writer.append(data);
    }
}

The RandomService here gets created by a @Produces method. Additionally I am using @Named annotation here.

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

This should be enough, so now if I run my test it would still fail complaining back  an exception “If a producer method or field of scope @Dependent returns an serializable object for injection into an injection“. This is a pretty cryptic  message and it took me some time to realize that it meant that the Servlet is a serializable object and the Random service I created is not. So there are the following ways of fixing this :

  • either create the field as transient, that should fix the problem immediately
  • or use the Provider API to get an instance of the service rather directly using an instance of the RandomService.
    public class RandomServlet extends HttpServlet {
        @Inject
        Provider<RandomService> instanceService;
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    	    RandomService service=instanceService.get();
    		.......
    		}}
    

Also according to the specs the BeanManager SPI is available in the ServletContext. We can retrieve the manager from the context and ask for the required beans.

public class RandomServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        BeanManager manager = (BeanManager) getServletContext().getAttribute(BeanManager.class.getName());
        System.out.println(manager.getClass() + ":" + manager.hashCode());
    }
    .......
}

One thing that I missed in my last app was testing the support of properties files. The CDI specs do not have any take on that. Loading of properties is provided by OWB  by the PropertiesLoader class. If I have to use property files in my application then I have to create a bean of  Properties using the PropertiesLoader. Now I should inject this bean in my required class and retrieve the values individually.

class PropsFactory {
    @Produces
    Properties loadProperties() {
        return PropertyLoader.getProperties("my_sample_app.properties");
    }}

There is one interesting property used by the PropertyLoader that can be used for controlling the loading of property files. In case I have multiple properties files with the same name I can specify a configuration.ordinal property in each of them.  The file in which the property has the highest value will be loaded.

I also need to add a web.xml to this project that can be packaged with the war. The web.xml would contain the WebBeansConfigurationListener responsible for creating the context.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 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/web-app_2_5.xsd">
    <display-name>jee6 Sample integration</display-name>
    <description>jee6 Sample integration</description>
    <listener>
        <listener-class>org.apache.webbeans.servlet.WebBeansConfigurationListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>randomServlet</servlet-name>
        <servlet-class>info.jee6.RandomServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>randomServlet</servlet-name>
        <url-pattern>/RandomServlet</url-pattern>
    </servlet-mapping>
</web-app>

Now I have a complete war that can be deployed in Tomcat, but before I do that  I need to do some update in Tomcat configuration so that it can load this war successfully. In Tomcat we need to add the ContextLifecycleListener so that it can load OWB context. In order to do so, edit the server.xml file in Tomcat conf folder. Add the following :

<Listener className="org.apache.webbeans.web.tomcat.ContextLifecycleListener" />

This class can be found in the openwebbeans-tomcat6 project and the jar of the same should be copied to the Tomcat lib folder. Only copying the openwebeans-tomcat6 jar will not be sufficient as it has its own set of dependencies on the opeanwebbeans-spi, javasassit  and a couple of more dependencies. I ended copying all of my project dependencies to the lib folder. Now if I start Tomcat with my war the context gets loaded and the servlet starts responding.

After this was done I tried to create a Webservice over the same service. The Webservice was created using Metro and was deployed in Tomcat. For all of this  I was required to do some extra workarounds which I would share in another blog.

Advertisements

4 Comments »

  1. […] am I Apache OpenWebBeans CDI, from standalone to webapp […]

    Pingback by Integrating Apache OpenWebbeans with other frameworks « The road so far…. — May 17, 2011 @ 9:02 pm

  2. hi form mauro
    how i can get a bean @ApplicationScoped tith cdi into a web application like the managedbeans JSF with eager=true ?’
    I would that the @applicationScoped it is created at startup of webapplication .

    I not have view on documentation of cdi the attribute eager-=true for the @applicationscoped (eager=ture) .

    JSF2 it has the ManagedBean @ApplicationScopde (eager=true)

    you can help me? please

    mauro

    Comment by mauro — March 12, 2013 @ 12:07 am

    • Sorry mate I do not have much clue on this one. It has been some quite some time since I worked on this one. But if you are able to get a fix, please let me know about it.

      regards
      Rahul

      Comment by Rahul Sharma — April 7, 2013 @ 7:05 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: