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. As to installing CDI in Tomcat, Weld documentation is pretty clear, while OpenWebBeans documentation is in its current form unclear. The "Adding OpenWebBeans to your Servlet Container project" page is non-technical (too functional and not detailed enough and therefore confusing to starters) and contains broken links. The "Tomcat plugins 6 & 7" page contains a "Coming soon..." text for months. Too bad, OpenWebBeans works quite nice in TomEE and their support (handling of issue reports) is great. We'll head to Weld in this article.
Install Weld in Tomcat
Perform the following steps:
- Drop
weld-servlet.jar
in webapp's/WEB-INF/lib
. - Create
/META-INF/context.xml
file in webapp's web content with following content (or, if you already have one, add just the
to it):
name="BeanManager"
auth="Container"
type="javax.enterprise.inject.spi.BeanManager"
factory="org.jboss.weld.resources.ManagerObjectFactory" />
This will register Weld'sBeanManager
factory in Tomcat's JNDI. - Add the following entries to webapp's
/WEB-INF/web.xml
:
org.jboss.weld.environment.servlet.Listener
BeanManager
javax.enterprise.inject.spi.BeanManager
The listener entry basically enables Weld in webapp. The resource entry basically tells webapp to use theBeanManager
as available in JNDI. - Create a (empty)
/WEB-INF/beans.xml
file (no, not in/META-INF
! that's only for JARs such as OmniFaces) - Optionally: if you also want to use JSR-303 Bean Validation (
@NotNull
and friends), then dropvalidation-api.jar
andhibernate-validator.jar
in webapp's/WEB-INF/lib
.
Now your webapp is ready for CDI in Tomcat! You can now use @Named
and the associated CDI scope annotations in your JSF backing beans. If you're not on JSF 2.2 yet and would like to use @ViewScoped
in CDI, then OmniFaces has one for you.
Troubleshooting
You'll face the following exceptions when one of those things are missing or misconfigured (line numbers match Tomcat 7.0.53, Mojarra 2.1.28 and Weld 2.2.0.Final):
If the weld-servlet.jar
is absent, then during startup:
SEVERE: Error configuring application listener of class org.jboss.weld.environment.servlet.Listener
java.lang.ClassNotFoundException: org.jboss.weld.environment.servlet.Listener
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1720)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1571)
at org.apache.catalina.core.DefaultInstanceManager.loadClass(DefaultInstanceManager.java:529)
at org.apache.catalina.core.DefaultInstanceManager.loadClassMaybePrivileged(DefaultInstanceManager.java:511)
at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:139)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4888)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5467)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
...
If the listener isn't properly registered in web.xml
, then during runtime:
java.lang.IllegalStateException: Singleton is not set
org.jboss.weld.bootstrap.api.helpers.IsolatedStaticSingletonProvider$IsolatedStaticSingleton.get(IsolatedStaticSingletonProvider.java:52)
org.jboss.weld.Container.instance(Container.java:54)
org.jboss.weld.jsf.WeldPhaseListener.instance(WeldPhaseListener.java:161)
org.jboss.weld.jsf.WeldPhaseListener.activateConversations(WeldPhaseListener.java:98)
org.jboss.weld.jsf.WeldPhaseListener.beforePhase(WeldPhaseListener.java:85)
com.sun.faces.lifecycle.Phase.handleBeforePhase(Phase.java:228)
com.sun.faces.lifecycle.Phase.doPhase(Phase.java:99)
com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:116)
com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
...
If the context.xml
is absent and/or
isn't properly declared in web.xml
: well, actually nothing bad happens. On Tomcat 7.0.53 at Ubuntu, everything works for me without them as with them. However, the Weld documentation says to use it, so just keep it in to be on the safe side. Technically, those things are indeed really required when Tomcat has a read-only JNDI. It's however not entirely clear to me when that's the case. Perhaps on Windows only and/or with an ancient Weld version.
If the faces-config.xml
is absent, then an infinite loop/stackoverflow during runtime with the following repeating part (weirdly enough, the combination of Weld+JSF without a faces-config.xml
somehow causes JSF to treat Facelets files like JSP files and then JSF endlessly attempts to locate the JSP file in all vain):
...
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:594)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338)
at com.sun.faces.context.ExternalContextImpl.dispatch(ExternalContextImpl.java:568)
at com.sun.faces.application.view.JspViewHandlingStrategy.executePageToBuildView(JspViewHandlingStrategy.java:363)
at com.sun.faces.application.view.JspViewHandlingStrategy.buildView(JspViewHandlingStrategy.java:153)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:99)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:594)
...
If the beans.xml
file is absent, then most likely a kind of javax.el
exception during runtime, depending on your JSF page, at least boiling down to "Target Unreachable, identifier 'bean' resolved to null"
:
SEVERE: javax.el.PropertyNotFoundException: /index.xhtml @20,40 value="#{bean.input}": Target Unreachable, identifier 'bean' resolved to null
at com.sun.faces.facelets.el.TagValueExpression.getType(TagValueExpression.java:100)
at com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getConvertedValue(HtmlBasicInputRenderer.java:95)
at javax.faces.component.UIInput.getConvertedValue(UIInput.java:1034)
at javax.faces.component.UIInput.validate(UIInput.java:964)
at javax.faces.component.UIInput.executeValidate(UIInput.java:1237)
at javax.faces.component.UIInput.processValidators(UIInput.java:702)
at javax.faces.component.UIForm.processValidators(UIForm.java:253)
at com.sun.faces.context.PartialViewContextImpl$PhaseAwareVisitCallback.visit(PartialViewContextImpl.java:536)
at com.sun.faces.component.visit.PartialVisitContext.invokeVisitCallback(PartialVisitContext.java:183)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1652)
at javax.faces.component.UIForm.visitTree(UIForm.java:371)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1663)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1663)
at com.sun.faces.context.PartialViewContextImpl.processComponents(PartialViewContextImpl.java:383)
at com.sun.faces.context.PartialViewContextImpl.processPartial(PartialViewContextImpl.java:257)
at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:1162)
at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:76)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
...
Noted should be that older Weld versions (at least until with 1.1.14) have an accidental dependency on JSR303 bean validation API. You might receive a kind of the following exception then (the solution is obviously to upgrade Weld to a newer version, or if that's not an option, at least drop validation-api.jar
in the /WEB-INF/lib
):
SEVERE: Exception sending context initialized event to listener instance of class org.jboss.weld.environment.servlet.Listener
org.jboss.weld.exceptions.WeldException: Exception message for key UNABLE_TO_LOAD_CACHE_VALUE not found due to String index out of range: -1
at org.jboss.weld.util.cache.LoadingCacheUtils.getCacheValue(LoadingCacheUtils.java:73)
...
at java.lang.Thread.run(Thread.java:724)
Caused by: com.google.common.util.concurrent.UncheckedExecutionException: java.lang.TypeNotPresentException: Type javax.validation.ConstraintViolation not present
at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2258)
...
... 19 more
Caused by: java.lang.TypeNotPresentException: Type javax.validation.ConstraintViolation not present
at sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:117)
...
... 23 more
Caused by: java.lang.ClassNotFoundException: javax.validation.ConstraintViolation
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:270)
at sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:114)
... 39 more
Source:http://balusc.blogspot.com/2013/10/how-to-install-cdi-in-tomcat.html
Tidak ada komentar:
Posting Komentar