Jumat, 15 Agustus 2014

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




  1. Drop weld-servlet.jar in webapp's /WEB-INF/lib.


  2. 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's BeanManager factory in Tomcat's JNDI.


  3. 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 the BeanManager as available in JNDI.


  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), then drop validation-api.jar and hibernate-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