Monday, October 14, 2013

How to install CDI in Tomcat?

Introduction

JSF is slowly moving towards CDI for bean management. Since JSF 2.2, as part of Java EE 7, there's the new CDI compatible @ViewScoped and there's the CDI-only @FlowScoped which doesn't have an equivalent for @ManagedBean. Ultimately, with Java EE 8, or perhaps 9, the @ManagedBean and associated scopes from javax.faces.bean package will be deprecated in favor of CDI.

Now, there are some JSF users using Tomcat which does as being a barebones JSP/Servlet container not support CDI out the box (also not JSF, you know, you had to supply JSF JARs yourself). If you intend to use CDI on Tomcat, the most straightforward step would be to upgrade it to TomEE. It's exactly like Tomcat, but then with among others OpenWebBeans on top of it, which is Apache's CDI implementation. TomEE installs as easy as Tomcat: just download the ZIP and unzip it. TomEE integrates in Eclipse as easy as Tomcat: just use existing Tomcat 7 server plugin. As a bonus, TomEE also comes with EJB and JPA, making services and DB interaction a breeze.

However, perhaps you just have no control over upgrading the server. In that case, you'd like to supply CDI along with the webapp itself then in flavor of some JARs and additional configuration entries/files. So far, there are 2 major CDI implementations: Weld (the reference implementation) and OpenWebBeans. We'll treat them both in this article.

Install Weld in Tomcat (last updated: 4 Dec 2016)

Perform the following steps:

  1. Drop weld-servlet.jar in webapp's /WEB-INF/lib. In case you're using Maven, this is the coordinate:
    <dependency>
        <groupId>org.jboss.weld.servlet</groupId>
        <artifactId>weld-servlet</artifactId>
        <version>2.4.1.Final</version>
    </dependency>
    
  2. Create /META-INF/context.xml file in webapp's web content with following content (or, if you already have one, add just the <Resource> entry to it):
    <Context>
        <Resource name="BeanManager" 
            auth="Container"
            type="javax.enterprise.inject.spi.BeanManager"
            factory="org.jboss.weld.resources.ManagerObjectFactory" />
    </Context>
    
    This will register Weld's BeanManager factory in Tomcat's JNDI. This cannot be performed programmatically by Weld because Tomcat's JNDI is strictly read-only. This step is not necessary if you're targeting at least Mojarra 2.2.11 and/or OmniFaces 2.4 or newer. Both are able to find it in ServletContext instead. However, there may be other libraries which still expect to find BeanManager in JNDI, you'd then best keep this configuration file anyway for those libraries.
  3. Create a (empty) /WEB-INF/beans.xml file (no, not in /META-INF! that's only for JARs such as OmniFaces).
  4. Optionally: if you also want to use JSR-303 Bean Validation (@NotNull and friends), then drop validation-api.jar and hibernate-validator.jar in webapp's /WEB-INF/lib, or use below Maven coordinate:
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.2.4.Final</version>
    </dependency>
    

Now your webapp is ready for CDI in Tomcat via Weld! Note that in previous Weld versions you needed to register a <listener> in web.xml. This is not necessary anymore with at least Weld 2.2.0 on a "recent" Tomcat version!

Install OpenWebBeans in Tomcat (last updated: 9 Jun 2016)

Perform the following steps:

  1. This is easiest with Maven as OpenWebBeans has quite some sub-dependencies. Here are the coordinates (do note that it also includes JSR-303 Bean Validation API as without it OpenWebBeans would fail deployment with java.lang.ClassNotFoundException: javax.validation.ConstraintViolation):
    <dependency>
        <groupId>javax.enterprise</groupId>
        <artifactId>cdi-api</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.openwebbeans</groupId>
        <artifactId>openwebbeans-impl</artifactId>
        <version>1.6.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.openwebbeans</groupId>
        <artifactId>openwebbeans-spi</artifactId>
        <version>1.6.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.openwebbeans</groupId>
        <artifactId>openwebbeans-web</artifactId>
        <version>1.6.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.openwebbeans</groupId>
        <artifactId>openwebbeans-jsf</artifactId>
        <version>1.6.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.openwebbeans</groupId>
        <artifactId>openwebbeans-el22</artifactId>
        <version>1.6.3</version>
    </dependency>
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>
    
  2. Create /META-INF/context.xml file in webapp's web content with following content (or, if you already have one, add just the <Resource> entry to it):
    <Context>
        <Resource name="BeanManager" 
            auth="Container"
            type="javax.enterprise.inject.spi.BeanManager"
            factory="org.apache.webbeans.container.ManagerObjectFactory" />
    </Context>
    
    This will register OpenWebBeans' BeanManager factory in Tomcat's JNDI. This cannot be performed programmatically by OpenWebBeans because Tomcat's JNDI is strictly read-only.
  3. Add the below <listener> entry to webapp's web.xml:
    <listener>
        <listener-class>org.apache.webbeans.servlet.WebBeansConfigurationListener</listener-class>
    </listener>
    
    This will make sure that OpenWebBeans initializes before OmniFaces, otherwise you may face a java.lang.IllegalStateException: It's not allowed to call getBeans(Type, Annotation...) before AfterBeanDiscovery.
  4. Create a (empty) /WEB-INF/beans.xml file (no, not in /META-INF! that's only for JARs such as OmniFaces).
  5. Optionally: if you also want to use JSR-303 Bean Validation (@NotNull and friends), add the below Maven coordinate:
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.2.4.Final</version>
    </dependency>
    

Now your webapp is ready for CDI in Tomcat via OpenWebBeans!

27 comments:

Howard Smith said...

+1 great post BalusC! I like your reference to TomEE and OpenWebBeans (Apache's CDI implementation). :)

James RM said...

Great post BalusC. Lots of your post helped me. By the way can you also help me with this one? http://stackoverflow.com/questions/19681933/embed-pdf-in-webpage

Lucut Ioan said...

Thanks for your posts, BalusC! They are all great!

Robinson Castilho said...

Have you tried it in a production enviroment? :) It's ok?

ireti Oluwagbemi said...

The Weld guide I just downloaded from the Weld site specifies two listeners to be added for tomcat:
'org.jboss.weld.servlet.WeldInitialListener' and 'org.jboss.weld.servlet.WeldTerminalListener'. I tried that and it didn't work. However, yours works. Is it an outdated method in the guide?

nena said...

Hi BalusC - first, thanks so much for being an expert and for sharing your knowledge to help others.

I tried everything exactly as you posted and I have a custom constraint in jee 6 - but for some reason it's not called? when i attempt to submit the form with a button, my fieldValidator is not called - any idea why that could be?

thanks!

Unknown said...

Are you 100% positive that this is all what is required? I'm getting a

SEVERE: Exception sending request initialized lifecycle event to listener instance of class com.sun.faces.config.ConfigureListener
java.lang.UnsupportedOperationException
at javax.faces.context.FacesContext.getExceptionHandler(FacesContext.java:287)
at javax.faces.event.ExceptionQueuedEventContext.getListenersForEventClass(ExceptionQueuedEventContext.java:262)
[..]

when giving it a try using Tomcat 7.0.x, JSF Mojarra 2.2.5 and weld-servlet 2.1.0.

whaefelinger said...

[..] using the "simplegreeting" example of JavaEE 6 tutorial as (natural) starting point.

Btw, which is your starting point? Would be good to be able to download the example project somehow/somewhere.

whaefelinger said...

Ok, got it working by adding the "Faces Servlet" servlet to web.xml. As it turns out, JavaEE is probably not the best starting point.

Arjan Tijms said...

Note that newer versions of Weld throw the following exception when the org.jboss.weld.environment.servlet.Listener is not configured:

javax.naming.NamingException: WELD-001300: Unable to locate BeanManager
at org.jboss.weld.resources.ManagerObjectFactory.getObjectInstance(ManagerObjectFactory.java:52)
at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:304)

Also, besides Weld and OpenWebBeans there's a third CDI implementation called CanDI (it's natively used by Caucho's Resin).

أحمد شاهين said...

Hello Balusc,
Thanks for this post, is it possible to install WELD instead of OpenWebBeans for TomEE ?

Admin said...

Muito bom, obrigado pela dica.

Moses Gone said...

I have tried to configure tomcat 7.0.50 to work with Weld( weld-servlet 2.2.6), but I am getting a strange java.lang.NoSuchMethodError: com.sun.faces.util.Util.isCdiOneOneOrGreater()Z

I am using it with a java webapp with jsf 2.2 + omnifaces

Bauke Scholtz said...

@Moses: The webapp's runtime classpath is polluted with a duplicate JSF (Mojarra) impl JAR file of an older version lacking that method, e.g. 2.0 or 2.1. Just cleanup it.

Gimby said...

Really nice article which has saved me quite a lot of time. I would just like to state that when you use a decently up to date version of Tomcat 7 (or Tomcat 8, or any other servlet 3.0 compatible container) you don't actually need to configure any listener, it will just magically work.

Parminder Singh said...

We package our code in multiple jars under WEB-INF/lib. Does every jar need a beans.xml file?

Cristian David Arteaga Sanchez said...

Awesome BalusC!!! Great post, was very useful for me. Thank you very much

Stephan Rauh said...

I also had to add the dependency org.jboss:jandex:1.2.3.Final to get Weld up and running.

Karl Kildén said...

OWB docs are better now ;)

http://openwebbeans.apache.org

Joerison Silva said...

Thanks!!! Love you.

simone santos said...

Simplesmente perfeito!
Você salvou minha vida.
Muiito obrigado!

sitio2011 said...

Great post! Tks!

Eudson Bambo said...

Well done... Great post... I use CDI env in my work but i couldn't understand why we were using Widfly. But now, reading your post, i got it.

Thanks...

Mathias S said...

It doesn't work with weld-servlet-2.3.3.Final.jar and cdi-api-1.2.jar. I am still getting an checkCDIAvailable error.

cthiebaud said...

Thanks for the post. I wrote a 'hello-CDI-on-java-SE-world' project for me to learn the techniques exposed here and elsewhere.

For those that maybe interested: https://github.com/aequologica/hello-CDI-on-java-SE-world

The code is live here: https://tebaldi051108trial.hanatrial.ondemand.com/modules/cdi/ (at least at this very moment, may not be live for ever)

Thanks!

cthiebaud said...

In fact, the live example for 'hello-CDI-on-java-SE-world' has just changed : https://tebaldi051108trial.hanatrial.ondemand.com/lifebuoy/cdi.jsp

Kolya Yanchiy said...

Many thanks)))