Monday, October 14, 2013

How to install CDI in Tomcat?

Introduction

JSF was since version 2.2 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. Since JSF 2.3, as part of Java EE 8, the @ManagedBean and associated scopes from javax.faces.bean package are deprecated in favor of CDI.

Now, there are some JSF / Jakarta Faces 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 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 10+ (last updated: 1 Dec 2022)

Tomcat 10.0.x is the first version to be "Jakartified", i.e. it's using jakarta.* package instead of javax.* package for the API classes. It is using Servlet 5.0 API of Jakarta EE 9. Tomcat 10.1.x is the second Jakartified version, using Servlet 6.0 API of Jakarta EE 10.

In order to install CDI on Tomcat 10.x, perform the following steps:

  1. For Tomcat 10.1.x, drop weld-servlet-shaded.jar of version 5.x 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-shaded</artifactId>
        <version>5.1.0.Final</version>
    </dependency>
    

    For Tomcat 10.0.x, use weld-servlet-shaded.jar of version 4.x instead:
    <dependency>
        <groupId>org.jboss.weld.servlet</groupId>
        <artifactId>weld-servlet-shaded</artifactId>
        <version>4.0.3.Final</version>
    </dependency>
    
  2. Create a /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="jakarta.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 for Mojarra and OmniFaces because both libraries are able to find it in ServletContext instead. However, there may be other libraries which still expect to find BeanManager in JNDI, so 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 inside JAR files such as OmniFaces).
  4. Optionally: if you also want to use JSR-303 Bean Validation (@NotNull and friends), then drop jakarta.validation-api.jar of version 3.0.x and hibernate-validator.jar of version 8.0.x in webapp's /WEB-INF/lib, or use below Maven coordinate:
    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>8.0.0.Final</version>
    </dependency>
    

    For Tomcat 10.0.x, use hibernate-validator.jar of version 7.0.x instead.

Now your webapp is ready for CDI in Tomcat 10+ via Weld!

Install Weld in Tomcat 9- (last updated: 1 Dec 2022)

The difference with Tomcat 10+ is that Tomcat 9- still uses the old javax.* package instead of the new jakarta.* package. This is not compatible with Weld 4+, you need Weld 3 instead. Perform the following steps:

  1. Drop weld-servlet-shaded.jar of version 3.x 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-shaded</artifactId>
        <version>3.1.6.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 inside JAR files such as OmniFaces).
  4. Only if your web.xml is declared conform Servlet version 4.0 instead of 3.1, then you also need to put the @javax.faces.annotation.FacesConfig annotation on an arbitrary CDI managed bean somewhere in the project (usually the one representing the "application-wide config" would be OK).
    package com.example;
    
    import javax.enterprise.context.ApplicationScoped;
    import javax.faces.annotation.FacesConfig;
    
    @FacesConfig
    @ApplicationScoped
    public class Config {
    
    }
    It is indeed utterly unnecessary, but it is what it is.
  5. Optionally: if you also want to use JSR-303 Bean Validation (@NotNull and friends), then drop jakarta.validation-api.jar and hibernate-validator.jar in webapp's /WEB-INF/lib, or use below Maven coordinate:
    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.2.5.Final</version>
    </dependency>
    

Now your webapp is ready for CDI in Tomcat 9- 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 9- version!

Install OpenWebBeans in Tomcat 9- (last updated: 3 January 2021)

The difference with Tomcat 10+ is that Tomcat 9- still uses the old javax.* package instead of the new jakarta.* package. 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 unexpectedly fail deployment with java.lang.TypeNotPresentException: Type javax.validation.ConstraintViolation not present caused by java.lang.ClassNotFoundException: javax.validation.ConstraintViolation):
    <dependency>
        <groupId>javax.enterprise</groupId>
        <artifactId>cdi-api</artifactId>
        <version>2.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.openwebbeans</groupId>
        <artifactId>openwebbeans-jsf</artifactId>
        <version>2.0.20</version>
    </dependency>
    <dependency>
        <groupId>jakarta.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>2.0.2</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.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.2.0.Final</version>
    </dependency>
    

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

40 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

Unknown said...

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

Unknown 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!

whaefelinger 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

BalusC 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?

arteagac said...

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

Unknown said...

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

Unknown said...

OWB docs are better now ;)

http://openwebbeans.apache.org

Unknown said...

Thanks!!! Love you.

Unknown said...

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

sitio2011 said...

Great post! Tks!

Unknown 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...

Unknown 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

gokareless said...

Many thanks)))

Unknown said...

Thanks BalusC for this post.

I am using WELD 2.4.1.Final for my web application running on Tomcat 7. I am having JSF and Jax-RS Rest services in my web application. I have implemented asynchronous JAX-RS Rest Services. Async Rest call works perfect, but in logs I got following Warnings :

WELD-000715: HttpContextLifecycle guard not set. The Servlet container is not fully compliant.
WELD-000714: HttpContextLifecycle guard leak detected. The Servlet container is not fully compliant. The value was 1
WELD-000335: Conversation context is already active, most likely it was not cleaned up properly during previous request processing

I got from this website : http://weld.cdi-spec.org/documentation/, that these are issues from Tomcat.

Should I consider them or just ignore? If I ignore, does it affect performance of my web app? Is there any Workaround to get rid of this warnings?

Unknown said...

Hey BalusC! First of all I want to thank you for sharing your expertise on these matters. Since I started using JSF for my projects some years ago your apportations in these and other forums have been invaluable for me and I'm sure for many others.
Having say that, I'm curious about something. Can Weld be used with TomEE?

Ximo Dante said...

Hey BalusC!

Very nice post.

Cannot still use session scoped beans in Tomcat with Weld?

Thanks

megiddo said...

This was such a big help to me... thanks BalusC!!!! +1

Berkson said...

Thank you. My spring application was running in windows without problem, but not in linux. It was a problem with weld. The log only displayed MalformedParameterizedTypeException and nothing more. After try your tip using weld-servlet-shaded worked like a breeze.

Anonymous said...

Thanks balusc for all your contribution, this article helped me a lot. But I have an error that I have not been able to solve.

I am using Tomcat 9 and the weld-servlet-shaded dependency and it throws me this error when importing @Postconstruct

The package javax.annotation is accessible from more than one module: , annotations.api

In my Java Build Path I have the tomcat 9 library and in the pom.xml file the dependency weld-servlet-shaded

Eric Rosas said...


serve what I add to



BeanManager
javax.enterprise.inject.spi.BeanManager



web.xml

gracias.

Eric Rosas

Unknown said...

Thank you so much! I was closely to theow my laptop through the window because my jsf application could run in glassfish but not in tomcat.

Unknown said...

Great post but I am rather stuck. I keep getting:

8-Jul-2021 09:31:20.352 INFO [http-nio-8095-exec-9] org.jboss.weld.environment.servlet.EnhancedListener.onStartup WELD-ENV-001008: Initialize Weld using ServletContainerInitializer
08-Jul-2021 09:31:20.409 INFO [http-nio-8095-exec-9] org.jboss.weld.bootstrap.WeldStartup. WELD-000900: 4.0.1 (Final)
08-Jul-2021 09:31:20.560 WARN [http-nio-8095-exec-9] org.jboss.weld.environment.servlet.WeldServletLifecycle.initialize WELD-ENV-000028: Weld initialization skipped - no bean archive found
08-Jul-2021 09:31:20.561 INFO [http-nio-8095-exec-9] org.jboss.weld.environment.servlet.Listener.contextInitialized WELD-ENV-001006: org.jboss.weld.environment.servlet.EnhancedListener used to initialize Weld
08-Jul-2021 09:31:20.621 INFO [http-nio-8095-exec-9] com.sun.faces.config.ConfigureListener.contextInitialized Initializing Mojarra 3.0.1 for context '/test'
08-Jul-2021 09:31:20.729 INFO [http-nio-8095-exec-9] com.sun.faces.spi.InjectionProviderFactory.createInstance JSF1048: PostConstruct/PreDestroy annotations present. ManagedBeans methods marked with these annotations will have said annotations processed.
08-Jul-2021 09:31:21.547 SEVERE [http-nio-8095-exec-9] com.sun.faces.config.ConfigureListener.contextInitialized Critical error during deployment:
jakarta.faces.FacesException: Unable to find CDI BeanManager


using tomcat 10.0.0.8 and Jakarta EE 9
I have carefully put beans.xml in WEB-INF folder


beans xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/beans_3_0.xsd" version="3.0"

I have been looking at this for hours! any idea where I might be going wrong?

Unknown said...

Thanks a lot, this post allowed me to solve the CDI initialization problem in the jsf application delpoyment on an Apache Tomcat server. Good continuation :).

Unknown said...

I really appreciate reading this post. It helped me a lot.

Is there an opportunity to use Tomcat10+ with OpenWebBeans?

Unknown said...

Hello @BALUSC,
Great Post We too are migrating our application to myfaces 2.3-next.
We have changed our Managed Beans to CDI (using jboss weld implementation weld-servlet-shaded-3.1.6.Final.jar).
In our application, once we instantiate a request-scoped bean, we encrypt and store them in a String, and for the next consecutive calls we restore beans again from this encrypted String instead of creating it again, by extending ViewHandlerWrapper and writing an implementation in restoreView method (this was done till 2.3.X when beans where managed by MyFaces).

How can we restore a bean in CDI from an encrypted String from 2.3-next? as MyFaces does not Manage the beans anymore, or more precisely which classes can be implemented/extended.

The solution implemented for now:- We have rewritten the class org.jboss.weld.contexts.AbstractContext and integrated implementation in method public T get(Contextual contextual, CreationalContext creationalContext) to restore bean from encrypted String.

Andrey said...

org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type CafeRepository with qualifiers @Default

Tomcat Tomcat 10.1.18