Bootstrapping CDI in several environments

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.10.Final (the CDI reference implementation)
Java SE 1.6.0_33
GlassFish 3.1.2.2
Maven 3.0.3
Tomcat 6.0.33
Jetty 6.1.26

This first post is about how to bootstrap CDI in several environments, well, how to bootstrap Weld in fact. CDI 1.0 doesn’t specify a standard way to be bootstrapped, that’s a specificity of Weld (the reference implementation of CDI). And what are the environments where you can bootstrap Weld ? 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 and 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.

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) to build its internal metamodel. With the beans.xml file CDI knows it has beans to discover. This file can be used to enable interceptors, decorators, alternatives and so on (more on that in following posts). But on 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 6 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 in GlassFish.

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 MainEJB31Test {

    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[]{new File("target/classes"), new File("target/test-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/MainEJB31!org.agoncal.sample.cdi.bootstrapping.ejb.MainEJB31");

        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 = "/mainServlet30")
public class MainServlet30 extends HttpServlet {

    @Inject
    Hello hello;

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter out = resp.getWriter();
        out.println(saySomething());
        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 /mainServlet30 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/sampleCdiBootstrappingServlet30Glassfish/mainServlet30 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 {
        PrintWriter out = resp.getWriter();
        out.println(saySomething());
        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:

<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.agoncal.sample.cdi.bootstrapping.servlet.MainServlet25</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>MainServlet25</servlet-name>
    <url-pattern>/mainServlet25</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.Resource">
        <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>
    </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 tohttp://localhost:8080/bootstrapping-servlet25-jetty6/mainServlet25 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.

Comments
17 Responses to “Bootstrapping CDI in several environments”
  1. Hello Antonio,

    I put my ‘scope’ bridge into a little tiny JAR (or better project), at github:

    https://github.com/matzew/spring-cdi-bridge

    If my (personal) time would allow it, I’d play a bit more about the integration of the two :-)

  2. Hi, great post, thanks!

    You don’t need any annotation on Hello and World classes definition to inject them into other classes (like MainServlet25) ?

    In other examples i saw injected classes were always annotated with @Named, @SessionScoped, @Singelton etc.

    Is it required?

    Thanks

    Loic

    • agoncal says:

      No you don’t need any extra annotation. I’ll talk about it in future posts but @Named is when you need to access your bean using expression language (typically in a JSF page) and a bean as a default scope, so you don’t need @SessionScoped and so on if the default suits you.

  3. Ok that’s great! Very simple to use….

  4. Thanks a lot, Antonio, to bring CDI to the developpers :-)

    I find 2 things that don’t feet me well in CDI :
    – As you say, it is too bad the “CDI container” behaviour has not been evocated in the spec ! Having to rely on Weld container to inject POJO classes in our tests is harsh :(
    – I don’t catch how, with the Weld container, we can retrieve this or that Hello instance.
    You will surely write about it in a further article, but I cannot see how, with annotations, we can define Hello instances saying either “hello” or “bonjour” depending on a Locale attribute (for example).
    … or maybe we can only do this by defining beans in beans.xml ?

  5. dxxvi says:

    Hope the 3rd edition of your book has CDI, Servlet3.

  6. jherr says:

    Did you test with GAE container ?
    It should work (http://java.dzone.com/articles/weld-101-runs-gae) but I had ClassNotFoundException with Weld 1.1.0.CR1

  7. Tomcat 7 has just reached its first stable release with version 7.0.6, just in case you want to add it to that list.

  8. grega says:

    Is it possible to do something like this on Tomcat 6:

    package test;

    import javax.inject.Inject;
    import javax.jws.WebService;

    /**
    *
    * @author grega
    */
    @WebService()
    public class Ws {

    @Inject
    Hello hello;

    public String saySomething() {
    return hello.sayHelloWorld();
    }

    }

  9. dxxvi says:

    Hm… it doesn’t work in Tomcat 7.0.6 (of course it works with tomcat-6.0.32 because Antonio said so :) ). I got this error:
    WELD-001408 Unsatisfied dependencies for type [Service1] with qualifiers [@Default] at injection point [[field] @Inject private home.Servlet1.service1]

    I even gave it a qualifier but still got the message:
    WELD-001408 Unsatisfied dependencies for type [Service1] with qualifiers [@Service1Qualifier] at injection point [[field] @Inject @Service1Qualifier private home.Servlet1.service1]

    Does anybody has any luck with tomcat-7? And although we use tomcat-7 we still need the web.xml to host org.jboss.weld.environment.servlet.Listener and

    BeanManager
    javax.enterprise.inject.spi.BeanManager

    Right?

  10. Good post. With regards to Spring integration I agree with Jürgen. CDI and Spring are alternatives to me. It’s pointless to integrate the two technologies. It makes sence to use JSR 330 annotations though in my eyes. But then you loose some features that Spring provides and also you decide not to have pure POJOs. Thx again for the post.

  11. Dhananjay says:

    Great Post. Works very well.
    Is it possible to use this using openEJB. I have OpenEJB embedded in Tomcat. I am able to use @Inject in my Servlet; but how can i do the same in my EJB?

  12. mauro says:

    hi . i have tried to get CDI into tomcat6.0.35 and it work right.
    But i have tried to implement also the @Intercptor into my web application int otomcat6.0.35, for gete methods transactionals without ejb .

    I have write the interface and the intercpetor. i have edited the beans.xml for enable the @Interceptor work with cdi .

    But whrn deploy my application , not starts.
    Help me please.
    Into glassfish the code it work .
    But into tomcat not work.
    Tank you mauro

  13. Uwe Langjahr says:

    Hello Antonio,
    thank for the great post, i found it while troubleshooting to run
    CDI in java se.

    Can you help me with maven (I’m a beginner)
    I want have a standalone runnable jar file which include all
    necessary classes/configurations, to run your example.
    So I can start the program with java -jar cdi-example.jar

    I found plugins and examples with
    maven-shade-plugin
    maven-assembly-plugin
    but I can’t get it working.

    If I try this with hints from the internet, I always get an error like:

    520 [main] INFO org.jboss.weld.ClassLoading – WELD-000119 Not generating any bean definitions from org.jboss.weld.injection.spi.helpers.ForwardingJpaInjectionSe
    rvices because of underlying class loading error
    523 [main] INFO org.jboss.weld.ClassLoading – WELD-000119 Not generating any bean definitions from org.jboss.weld.injection.spi.JpaInjectionServices because of
    underlying class loading error
    ….

    Can you show in our example (Java SE), how to create a standalone runnable jar?

    Uwe

  14. wh says:

    Your Tomcat6 integration example is bit to simple for my taste, and to be truly honest, who creates Servlets nowadays?

    So I changed your example and annotated class World.java with “javax.enterprise.context.ApplicationScoped” and added a “@PostConstruct” method.

    My very naive expectation was that weld would take care that there “World” singleton right from the beginning. As usual when working with JavaEE, it does not work :-(

Trackbacks
Check out what others are saying...


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

Follow

Get every new post delivered to your Inbox.

Join 7,484 other followers

%d bloggers like this: