Bootstrapping CDI in several environments

By  · Interview
Save
30.4K Views

download i feel like writing some posts about cdi (contexts and dependency injection). so this is the first one of a series of x posts ( 0<x<10 ). i will not go through the entire history of cdi (formerly called web beans, splitted in two jsrs… and so on), but will try to give you information on how to use it in different environments, explain you injection, context management, scoping, decorators and so on. so you can think of this series of posts as a humble step by step cdi tutorial. you can also read the very good documentation on the jboss website (where i got some help and inspiration).

versions of software used for this arcticle
weld 1.1.0.cr1 (the cdi reference implementation)
java se 1.6.0_23
glassfish 3.0.1
maven 3.0.1
tomcat 6.0.29
jetty 6.1.26

this first post is about how to use cdi, or to be precise, how to bootstrap it in several environments. what are the environments you can use ? well, first of all, all the java ee 6 containers ( servlet 3.0 and ejb 3.1 ) but also java se (yes, a simple pojo can use cdi). and finally, your legacy servlet 2.5 container such as tomcat 6.x and jetty 6.x.

use case

let’s start with the best use case ever : hello world . in this post i will not go into details on cdi, i just want to focus on how to bootstrap it, so i’m not using many cdi artifacts… in fact, i’m just using injection here. to make it very simple i’ve developed a hello class that gets a reference of a world class through injection (using @javax.inject.inject). then i have developed several components (a pojo, an ejb 3.1, a servlet 3.0 and a servlet 2.5) that will use these classes through injection. the following class diagram shows you all the available classes of this example :

this is what the main two classes (hello and world ) look like :

import javax.inject.inject;

public class hello {

    @inject
    world world;

    public string sayhelloworld() {
        return "hello " + world.sayworld();
    }
}

i need to be precise in my description. the @inject annotation is not part of the cdi specification , but instead part of the @inject specification ( jsr 330 : dependency injection for java ). but @inject is nothing without cdi, so you can forget this current note, just focus on cdi.

public class world {

    public string sayworld() {
        return "world !!!";
    }
}

pretty simple, isn’t it ? no need to explain much.

good old maven

i’m using my good old friend maven (well, i’ve been complaining for so long that i’m getting use to it now). each bootstrapping environment (java se, ejb, servlet) will be developed in a separate maven project. the parent pom.xml defines the cdi api as follow :

<dependency>
    <groupid>javax.enterprise</groupid>
    <artifactid>cdi-api</artifactid>
    <version>1.0</version>
    <scope>provided</scope>
</dependency>

an empty beans.xml will do

to enable cdi you must have a beans.xml file in your project (under the meta-inf or web-inf). that’s because cdi needs to identify the beans in your classpath (this is called bean discovery) and build its internal metamodel. with the beans.xml file cdi knows it has beans to discover. so, for all the following examples i’ll make it simple and will leave this file completely empty.

java ee 6 containers

let’s start with the easiest possible environment : java ee 6 containers . why is it the simplest ? well, because you don’t have to do anything : cdi is part of java ee 6 as well as the web profile 1.0 so you don’t need to manually bootstrap it. let’s see how to inject a cdi bean within an ejb 3.1 and a servlet 3.0 .

ejb 3.1

since ejb 3.1 you can use the ejbcontainer api to get an in-memory embedded ejb container and you can easily unit test your ejbs. so let’s write an ejb and a test class.

first let’s have a look at the code of the ejb. as you can see, with version 3.1 an ejb is just a pojo : no inheritance, no interface, just one @stateless annotation. it gets a reference of the hello bean buy using the @inject annotation and uses it in the saysomething() method.

@stateless
public class mainejb31 {

    @inject
    hello hello;

    public string saysomething() {
        return hello.sayhelloworld();
    }
}

you can now package the mainejb31, hello and world classes with the empty beans.xml file into a jar, deploy it to glassfish 3.x , and it will work. but if you don’t want to bother deploying it to glassfish and just unit test it, this is what you need to do :

public class mainejbtest {

    private static ejbcontainer ec;
    private static context ctx;

    @beforeclass
    public static void initcontainer() throws exception {
        map properties = new hashmap();
        properties.put(ejbcontainer.modules, new file("target/classes"));
        ec = ejbcontainer.createejbcontainer(properties);
        ctx = ec.getcontext();
    }

    @afterclass
    public static void closecontainer() throws exception {
        if (ec != null)
            ec.close();
    }

    @test
    public void shoulddisplayhelloworld() throws exception {
        // looks up the ejb
        mainejb31 mainejb = (mainejb31) ctx.lookup("java:global/classes/mainejb!org.antoniogoncalves.cdi.helloworld.mainejb");

        assertequals("should say hello world !!!", "hello world !!!", mainejb.saysomething());
    }
}

in the code above the method initcontainer() initializes the ejbcontainer. the shoulddisplayhelloworld() looks up the ejb (using the new portable jndi name ), invokes it and makes sure the saysomething() method returns hello world !!!. green test. that was pretty easy too.

servlet 3.0

servlet 3.0 is part of java ee 6, so again, there is no needed configuration to bootstrap cdi. let’s use the new @webservlet annotation and write a very simple one that injects a reference of hello and displays an html page with hello world !!!. this is what the servlet looks like :

@webservlet(urlpatterns = "/mainservlet")
public class mainservlet30 extends httpservlet {

    @inject
    hello hello;

    @override
    protected void service(httpservletrequest req, httpservletresponse resp) throws servletexception, ioexception {
        resp.setcontenttype("text/html");
        printwriter out = resp.getwriter();
        out.println("<html>");
        out.println("<head><title>bootstrap cdi</title></head>");
        out.println("<body>");
        out.println(saysomething());
        out.println("</body>");
        out.println("</html>");
        out.close();
    }

    public string saysomething() {
        return hello.sayhelloworld();
    }
}

thanks to the @webservlet i don’t need any web.xml (it’s optional in servlet 3.0) to map the mainservlet30 to the /mainservlet url. you can now package the mainservlet30, hello and world classes with the empty beans.xml and no web.xml into a war, deploy it to glassfish 3.x , go to http://localhost:8080/bootstrapping-servlet30-1.0/mainservlet and it will work.

unfortunately servlet 3.0 doesn’t have an api for the container (such as ejbcontainer). there is no servletcontainer api that would let you use an embedded servlet container in a standard way and, why not, easily unit test it.

application client container

not many people know it, but java ee (or even older j2ee versions) comes with an application client container (acc). it’s like an ejb or servlet container but for plain pojos. for example you can develop a swing application (yes, i’m sure that some of you still use swing), run it into the acc and get some extra services given by the container (security, naming, certain annotations…). glassfish v3 has an acc that you can launch in a command line : appclient -jar <the name of your jar>.

so i thought, great, i can use cdi with acc the same way i use it within ejb or servlet container, no need to bootstrap anything, it’s all out of the box. i was wrong . as per the cdi specification (section 12.1), cdi is not required to support application client bean archives. so the glassfish application client container doesn’t support it. i haven’t tried the jboss acc , maybe it works.

other containers

the beauty of cdi is that it doesn’t require java ee 6 . you can use cdi with simple pojos in a java se environment, as well as some servlet 2.5 containers. of course it’s not as easy to bootstrap because you need a bit of configuration. but it then works fine (not always but).

java se 6

ok, so until now there was nothing to do to bootstrap cdi. it is already bundled with the ejb 3.1 and servlet 3.0 containers of java ee 6 (and web profile). so the idea here is to use cdi in a simple java se environment. coming back to our hello and world classes, we need a pojo with an entry point that will bootstrap cdi so we can use injection to get those classes. in standard java se when we say entry point , we think of a public static void main(string[] args) method. well, we need something similar… but different.

weld is the reference implementation of cdi. that means it implements the specification, the standard apis (mostly found in javax.inject and javax.enterprise.context packages) but also some proprietary code (in org.jboss.weld package). bootstrapping cdi in java se is not specified so you will need to use specific weld features. you can do that in two different flavors: by observing the containerinitialized event or using the programatic bootstrap api consisting of the weld and weldcontainer classes.

the following code uses the containerinitialized event. as you can see, it uses the @observes annotation that i’ll explain in a future post. but the idea is that this class is listening to the event and processes the code once the event is triggered.

import org.jboss.weld.environment.se.events.containerinitialized;
import javax.enterprise.event.observes;
import javax.inject.inject;

public class mainjavase6 {

    @inject
    hello hello;

    public void saysomething(@observes containerinitialized event) {
        system.out.println(hello.sayhelloworld());
    }
}

but who trigers the containerinitialized event ? well, it’s the org.jboss.weld.environment.se.startmain class. i’m using maven so a nice trick is to use the exec-maven-plugin to run the startmain class. download the code , have a look at the pom.xml and give it a try.

the other possibility is to programmatically bootstrap the weld container. this can be handy in unit testing. the code below initializes the weld container (with new weld().initialize()) and then looks for the hello class (using weld.instance().select(hello.class).get()).

import org.jboss.weld.environment.se.weld;
import org.jboss.weld.environment.se.weldcontainer;
import org.junit.beforeclass;
import org.junit.test;
import static junit.framework.assert.assertequals;

public class hellotest {

    @test
    public void shoulddisplayhelloworld() {
        weldcontainer weld = new weld().initialize();
        hello hello = weld.instance().select(hello.class).get();
        assertequals("should say hello world !!!", "hello world !!!", hello.sayhelloworld());
    }
}

execute the test with mvn test and it should be green. as you can see, there is a bit more work using cdi in a java se environment, but it’s not that complicated.

tomcat 6.x

ok, and what about your legacy servlet 2.5 containers ? the first one that comes in mind is tomcat 6.x ( note that tomcat 7.x will implement servlet 3.0 but is still in beta version at the time of writing this post ). weld provides support for tomcat but you need to configure it a bit to make cdi work.

first of all, this is a servlet 2.5, not a 3.0. so the code of the servlet is slightly different from the one seen before (no annotation allowed) and of course, you need your good old web.xml file :

public class mainservlet25 extends httpservlet {

    @inject
    hello hello;

    @override
    protected void service(httpservletrequest req, httpservletresponse resp) throws servletexception, ioexception {
        resp.setcontenttype("text/html");
        printwriter out = resp.getwriter();
        out.println("<html>");
        out.println("<head><title>bootstrap cdi in tomcat</title></head>");
        out.println("<body>");
        out.println(saysomething());
        out.println("</body>");
        out.println("</html>");
        out.close();
    }

    public string saysomething() {
        return hello.sayhelloworld();
    }
}

because we don’t have a @webservlet annotation in servlet 2.5, we need to declare and map it in the web.xml (using the servlet and servlet-mapping tags). then, you need to explicitly specify the servlet listener to boot weld and control its interaction with requests (org.jboss.weld.environment.servlet.listener). tomcat has a read-only jndi, so weld can’t automatically bind the beanmanager extension spi. to bind the beanmanager into jndi, you should populate meta-inf/context.xml and make the beanmanager available to your deployment by adding it to your web.xml:

<?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 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">

    <servlet>
        <servlet-name>mainservlet25</servlet-name>
        <servlet-class>org.antoniogoncalves.cdi.bootstrapping.servlet.mainservlet25</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>mainservlet25</servlet-name>
        <url-pattern>/mainservlet</url-pattern>
    </servlet-mapping>

    <listener>
        <listener-class>org.jboss.weld.environment.servlet.listener</listener-class>
    </listener>

    <resource-env-ref>
        <resource-env-ref-name>beanmanager</resource-env-ref-name>
        <resource-env-ref-type>javax.enterprise.inject.spi.beanmanager</resource-env-ref-type>
    </resource-env-ref>

</web-app>

the meta-inf/context.xml file is an optional file which contains a context for a single tomcat web application. this can be used to define certain behaviours for your application, jndi resources and other settings.

<context>
    <resource name="beanmanager"
              auth="container"
              type="javax.enterprise.inject.spi.beanmanager"
              factory="org.jboss.weld.resources.managerobjectfactory"/>
</context>

package all the files (mainservlet25, hello, world, meta-inf/context.xml, beans.xml and web.xml) into a war and deploy it into tomcat 6.x. go to http://localhost:8080/bootstrapping-servlet25-tomcat-1.0/mainservlet and you will see your hello world page.

jetty 6.x

another famous servlet 2.5 containers is jetty 6.x (at codehaus) and jetty 7.x ( note that jetty 8.x will implement servlet 3.0 but it’s still in experimental stage at the time of writing this post ). if you look at the weld documentation, there is actually support for jetty 6.x and 7.x . the code is the same one as tomcat (because it’s a servlet 2.5 container), but the configuration changes. with jetty you need to add two files under web-inf : jetty-env.xml and jetty-web.xml :

<!doctype configure public "-//mort bay consulting//dtd configure//en"
        "http://jetty.mortbay.org/configure.dtd">
<configure id="webappctx" class="org.mortbay.jetty.webapp.webappcontext">
<new id="beanmanager" class="org.mortbay.jetty.plus.naming.enventry">
      <arg><ref id="webappctx"/></arg>
      <arg>beanmanager</arg>
      <arg>
         <new class="javax.naming.reference">
            <arg>javax.enterprise.inject.spi.beanmanager</arg>
            <arg>org.jboss.weld.resources.managerobjectfactory</arg>
            <arg/>
         </new>
      </arg>
      <arg type="boolean">true</arg>
   </new>
</configure>
<!doctype configure public "-//mort bay consulting//dtd configure//en"
        "http://jetty.mortbay.org/configure.dtd">
<configure id="webappctx" class="org.mortbay.jetty.webapp.webappcontext">
    <call class="org.jboss.weld.environment.jetty.weldservlethandler" name="process">
        <arg>
            <ref id="webappctx"/>
        </arg>
    </call>
</configure>

package all the files (mainservlet25, hello, world, web-inf/jetty-env.xml, web-inf/jetty-web.xml, beans.xml and web.xml) into a war and deploy it into jetty 6.x. go to http://localhost:8080/bootstrapping-servlet25-jetty6/mainservlet and you will see your hello world page.

there was a mistake in the weld documentation so i couldn’t make it work. i started a thread on the weld forum and thanks to dan allen , pete muir and all the weld team, this was fixed and i managed to make it work. simple as posting an email to the forum . thanks for your help guys.

spring 3.x

here is the tricky part. spring 3.x implements the jsr 330 : dependency injection for java , which means that @inject works out of the box. but i didn’t find a way to integrate cdi with spring 3.x . the weld documentation mentions that because of its extension points, “ integration with third-party frameworks such as spring (…) was envisaged by the designers of cdi “. i did find this blog that simulates cdi features by enabling spring ones. what i didn’t find is a clear statement or roadmap on springsource about supporting cdi or not in future releases. the last trace of this topic is a comment on a long tss flaming thread . at that time (16 december 2009), juergen huller said “ with respect to implementing cdi on top of spring (…) trying to hammer it into the semantic frame of another framework such as cdi would be an exercise that is certainly achievable (…) but ultimately pointless “. but if you have any fresh news about it, let me know.

conclusion

as i said, this post is not about explaining cdi, i’ll do that in future posts. i just wanted to focus on how to bootstrap it in several environments so you can try by yourself. as you saw, it’s much simpler to use cdi within an ejb 3.1 or servlet 3.0 container in java ee 6. i’ve used glassfish 3.x but it should also work with other java ee 6 or web profile containers such as jboss 6 or resin .

when you don’t use java ee 6, there is a bit more work to do. depending on your environment or servlet container you need some configuration to bootstrap weld. by the way, i’ve used weld because it’s the reference implementation, the one bunddled with glassfish and jboss. but you could also use openwebbeans , another cdi implementation.

download the code , give it a try, and give me some feedback.

from http://agoncal.wordpress.com/2011/01/12/bootstrapping-cdi-in-several-environments/

Opinions expressed by DZone contributors are their own.


Comments