MyFaces Core
  1. MyFaces Core
  2. MYFACES-3581

@EJB injection into a @ViewScoped bean causes CNFE

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 2.1.8
    • Fix Version/s: 2.0.15, 2.1.9
    • Component/s: None
    • Labels:
      None
    • Environment:
      Oracle JVM 1.7.04, Apache TomEE

      Description

      If you have a ViewScoped ManagedBean, @EJB injection appears to fail when an ajax request is submitted. This may be because the MyFacesObjectInputStream is not using the context class loader.

      See original bug report here: http://stackoverflow.com/questions/11371573/jsf2-0-ejb-injection-into-viewscoped-causing-myfaces-to-cfne/11371846#11371846

      @ManagedBean
      @ViewScoped
      public class BlogMB implements Serializable

      { private static final long serialVersionUID = 1L; @EJB private CategoryService categoryService; }

      Here's the view code:

      <h:commandLink value="#

      {category.name}

      ">
      <f:setPropertyActionListener
      target="#

      {blogMB.selectedCategory}

      "
      value="#

      {category}

      " />
      <f:ajax
      listener="#

      {blogMB.filterPostsByCategory()}

      "
      execute="@this"
      render=":blogPosts" />
      </h:commandLink>

      This will cause the following exception:
      Jul 06, 2012 8:25:51 PM org.apache.myfaces.renderkit.ServerSideStateCacheImpl deserializeView
      SEVERE: Exiting deserializeView - Could not deserialize state: com.xxx.blog.service.CategoryService
      java.lang.ClassNotFoundException: com.xxx.blog.service.CategoryService
      at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
      at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
      at java.security.AccessController.doPrivileged(Native Method)
      at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
      at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
      at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
      at java.lang.Class.forName0(Native Method)
      at java.lang.Class.forName(Class.java:264)
      at java.io.ObjectInputStream.resolveProxyClass(ObjectInputStream.java:694)
      at java.io.ObjectInputStream.readProxyDesc(ObjectInputStream.java:1549)
      at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1511)
      at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750)
      at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
      at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1964)
      at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1888)
      at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
      at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
      at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
      at java.util.HashMap.readObject(HashMap.java:1043)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:601)
      at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1004)
      at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1866)
      at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
      at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
      at java.io.ObjectInputStream.readArray(ObjectInputStream.java:1685)
      at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1341)
      at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
      at java.util.HashMap.readObject(HashMap.java:1043)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:601)
      at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1004)
      at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1866)
      at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
      at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
      at java.io.ObjectInputStream.readArray(ObjectInputStream.java:1685)
      at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1341)
      at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
      at org.apache.myfaces.renderkit.ServerSideStateCacheImpl.deserializeView(ServerSideStateCacheImpl.java:497)
      at org.apache.myfaces.renderkit.ServerSideStateCacheImpl.getSerializedViewFromServletSession(ServerSideStateCacheImpl.java:289)
      at org.apache.myfaces.renderkit.ServerSideStateCacheImpl.restoreSerializedView(ServerSideStateCacheImpl.java:891)
      at org.apache.myfaces.renderkit.html.HtmlResponseStateManager.getState(HtmlResponseStateManager.java:205)
      at org.apache.myfaces.view.facelets.DefaultFaceletsStateManagementStrategy.restoreView(DefaultFaceletsStateManagementStrategy.java:207)
      at org.apache.myfaces.application.StateManagerImpl.restoreView(StateManagerImpl.java:130)
      at org.apache.myfaces.shared.view.ViewDeclarationLanguageBase.restoreView(ViewDeclarationLanguageBase.java:106)
      at org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguage.restoreView(FaceletViewDeclarationLanguage.java:2109)
      at org.apache.myfaces.application.ViewHandlerImpl.restoreView(ViewHandlerImpl.java:300)
      at com.ocpsoft.pretty.faces.application.PrettyViewHandler.restoreView(PrettyViewHandler.java:109)
      at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:83)
      at org.apache.myfaces.lifecycle.RestoreViewExecutor.execute(RestoreViewExecutor.java:127)
      at org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java:170)
      at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:117)
      at javax.faces.webapp.FacesServlet.service(FacesServlet.java:197)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
      at com.ocpsoft.pretty.PrettyFilter.doFilter(PrettyFilter.java:145)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
      at org.omnifaces.filter.GzipResponseFilter.doFilter(GzipResponseFilter.java:147)
      at org.omnifaces.filter.HttpFilter.doFilter(HttpFilter.java:75)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
      at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:225)
      at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169)
      at org.apache.tomee.catalina.OpenEJBValve.invoke(OpenEJBValve.java:44)
      at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
      at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
      at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
      at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
      at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
      at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
      at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:999)
      at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:565)
      at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:309)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
      at java.lang.Thread.run(Thread.java:722)

      1. MYFACES-3581-2.patch
        2 kB
        Leonardo Uribe
      2. MYFACES-3581.patch
        2 kB
        David Blevins

        Activity

        Hide
        David Blevins added a comment -

        I have a hunch it may be a slightly different but similar thing. Proxy classes generated for the @EJB ref. Attached a potential patch. Is it possible you can try it out?

        Show
        David Blevins added a comment - I have a hunch it may be a slightly different but similar thing. Proxy classes generated for the @EJB ref. Attached a potential patch. Is it possible you can try it out?
        Hide
        David Blevins added a comment -

        Patched against:

        Path: .
        URL: https://svn.apache.org/repos/asf/myfaces/core/trunk
        Repository Root: https://svn.apache.org/repos/asf
        Repository UUID: 13f79535-47bb-0310-9956-ffa450edef68
        Revision: 1358600
        Node Kind: directory
        Schedule: normal
        Last Changed Author: lu4242
        Last Changed Rev: 1358086
        Last Changed Date: 2012-07-06 02:19:56 -0700 (Fri, 06 Jul 2012)

        Show
        David Blevins added a comment - Patched against: Path: . URL: https://svn.apache.org/repos/asf/myfaces/core/trunk Repository Root: https://svn.apache.org/repos/asf Repository UUID: 13f79535-47bb-0310-9956-ffa450edef68 Revision: 1358600 Node Kind: directory Schedule: normal Last Changed Author: lu4242 Last Changed Rev: 1358086 Last Changed Date: 2012-07-06 02:19:56 -0700 (Fri, 06 Jul 2012)
        Hide
        Jonathan S Fisher added a comment -

        Patch fixes the issue for me

        Show
        Jonathan S Fisher added a comment - Patch fixes the issue for me
        Hide
        Leonardo Uribe added a comment -

        Sounds like something that shouldn't be fixed in that way. Take a look at the fragment:

        @ManagedBean
        @ViewScoped
        public class BlogMB implements Serializable

        { private static final long serialVersionUID = 1L; @EJB private CategoryService categoryService; }

        When view scope implements Serializable, it means all info is serialized/deserialized automatically. So, the serialization algorithm doesn't understand the bean annotation, just serialize CategoryService and the exception is received when the bean in deserialized, because it tries to create CategoryService, which clearly shouldn't be included into view state.

        The solution is create a request scope bean and execute the operations between the view scope bean and the service there. Even if with the patch works, the reference stored by categoryService will be invalid.

        Maybe it is a good idea to create a custom scope that understand the logic behind the annotations and can deal with serialization/deserialization process in a gracefully way, but it maybe sounds overkill. If no objections I'll close this one as invalid.

        Show
        Leonardo Uribe added a comment - Sounds like something that shouldn't be fixed in that way. Take a look at the fragment: @ManagedBean @ViewScoped public class BlogMB implements Serializable { private static final long serialVersionUID = 1L; @EJB private CategoryService categoryService; } When view scope implements Serializable, it means all info is serialized/deserialized automatically. So, the serialization algorithm doesn't understand the bean annotation, just serialize CategoryService and the exception is received when the bean in deserialized, because it tries to create CategoryService, which clearly shouldn't be included into view state. The solution is create a request scope bean and execute the operations between the view scope bean and the service there. Even if with the patch works, the reference stored by categoryService will be invalid. Maybe it is a good idea to create a custom scope that understand the logic behind the annotations and can deal with serialization/deserialization process in a gracefully way, but it maybe sounds overkill. If no objections I'll close this one as invalid.
        Hide
        Jonathan S Fisher added a comment -

        With respect, I do object, I think your analysis is incorrect. The EJB injection specification says something along the lines that handles and proxies to EJBs are indeed serializable and must survive being put into Session, even if the Session is passivated.

        Show
        Jonathan S Fisher added a comment - With respect, I do object, I think your analysis is incorrect. The EJB injection specification says something along the lines that handles and proxies to EJBs are indeed serializable and must survive being put into Session, even if the Session is passivated.
        Hide
        Leonardo Uribe added a comment -

        It could be good to know the exact lines, to keep them as reference. Checking again the stack trace, I can see that resolveClass() is not called in this case, and instead resolveProxyClass() is called. Sounds like a not common use case but valid. Anyway, the patch is incomplete, because it does follow the protocol used in ClassUtils.classForName(), which is try to resolve the class using the context classloader and then use the class classloader. Still need some work and a justification about when it happens, but now sounds more reasonable.

        Show
        Leonardo Uribe added a comment - It could be good to know the exact lines, to keep them as reference. Checking again the stack trace, I can see that resolveClass() is not called in this case, and instead resolveProxyClass() is called. Sounds like a not common use case but valid. Anyway, the patch is incomplete, because it does follow the protocol used in ClassUtils.classForName(), which is try to resolve the class using the context classloader and then use the class classloader. Still need some work and a justification about when it happens, but now sounds more reasonable.
        Hide
        Jonathan S Fisher added a comment -

        Thanks I'll dig around the @EJB specification... in the mean time, I've found a lot of articles showing how this 'Just Works'(tm) such as this one: http://www.oracle.com/technetwork/articles/java/ejb-3-1-175064.html http://www.adam-bien.com/roller/abien/entry/ejb_3_1_killed_the

        As far as the patch goes, it uses the ClassUtils.getContextClassLoader() first. Are you saying you'd like to see a failover to use ClassUtils.class.getClassLoader()?

        Show
        Jonathan S Fisher added a comment - Thanks I'll dig around the @EJB specification... in the mean time, I've found a lot of articles showing how this 'Just Works'(tm) such as this one: http://www.oracle.com/technetwork/articles/java/ejb-3-1-175064.html http://www.adam-bien.com/roller/abien/entry/ejb_3_1_killed_the As far as the patch goes, it uses the ClassUtils.getContextClassLoader() first. Are you saying you'd like to see a failover to use ClassUtils.class.getClassLoader()?
        Hide
        David Blevins added a comment -

        Thanks for the comments. Leonardo, you're absolutely right, this has nothing to do with EJB and boils down to the serialization API being different for any java.lang.reflect.Proxy generated class.

        I mentioned the patch being not "correct" in terms of the clearly intended architecture in an email maybe last week on dev@myfaces.a.o ("Patch for MYFACES-3581") – seemed like the better place for design discussion. More than happy to rework the patch to better fit the architecture, just want to make sure I do that in the desired way.

        Show
        David Blevins added a comment - Thanks for the comments. Leonardo, you're absolutely right, this has nothing to do with EJB and boils down to the serialization API being different for any java.lang.reflect.Proxy generated class. I mentioned the patch being not "correct" in terms of the clearly intended architecture in an email maybe last week on dev@myfaces.a.o ("Patch for MYFACES-3581 ") – seemed like the better place for design discussion. More than happy to rework the patch to better fit the architecture, just want to make sure I do that in the desired way.
        Hide
        Leonardo Uribe added a comment -

        I have attached a patch with the proposed solution. It uses ClassUtils.classForName instead loadClass and has a fallback to the class classloader, to be friendly with OSGi. Unfortunately, the articles proposed are not useful to solve the point, which is if a JSF Managed Bean with @EJB injected fields can be serialized/deserialized (which is doing view scope).

        Show
        Leonardo Uribe added a comment - I have attached a patch with the proposed solution. It uses ClassUtils.classForName instead loadClass and has a fallback to the class classloader, to be friendly with OSGi. Unfortunately, the articles proposed are not useful to solve the point, which is if a JSF Managed Bean with @EJB injected fields can be serialized/deserialized (which is doing view scope).
        Hide
        David Blevins added a comment -

        The proposed patch looks great – not sure why that didn't occur to me.

        On the spec requirements note, it's somewhat orthogonal as the root issue is derialization of any java.lang.reflect.Proxy and should be fixed regardless, but I'd say, yes. It's certainly a requirement for EJB and CDI that container supplied refs like @EJB and @Resource survive passivation/activation. I haven't checked the JSF spec, but if there is no such text we could maybe get that added so there is a more direct A to B wording. It's currently sort of A to B to C in that CDI has this requirement spelled out for all passivation capable scopes. A primary one is @ConversationScoped which applies only to JSF.

        Show
        David Blevins added a comment - The proposed patch looks great – not sure why that didn't occur to me. On the spec requirements note, it's somewhat orthogonal as the root issue is derialization of any java.lang.reflect.Proxy and should be fixed regardless, but I'd say, yes. It's certainly a requirement for EJB and CDI that container supplied refs like @EJB and @Resource survive passivation/activation. I haven't checked the JSF spec, but if there is no such text we could maybe get that added so there is a more direct A to B wording. It's currently sort of A to B to C in that CDI has this requirement spelled out for all passivation capable scopes. A primary one is @ConversationScoped which applies only to JSF.
        Hide
        Leonardo Uribe added a comment -

        I'll apply this patch and close it as fixed, in the understanding that MyFaces should check for context classloader first and then to class classloader to be friendly with OSGi. There is still controversy about how serialization of annotated fields should be, but this will be dealt in further spec versions.

        Show
        Leonardo Uribe added a comment - I'll apply this patch and close it as fixed, in the understanding that MyFaces should check for context classloader first and then to class classloader to be friendly with OSGi. There is still controversy about how serialization of annotated fields should be, but this will be dealt in further spec versions.

          People

          • Assignee:
            Leonardo Uribe
            Reporter:
            Jonathan S Fisher
          • Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Time Tracking

              Estimated:
              Original Estimate - 48h
              48h
              Remaining:
              Remaining Estimate - 48h
              48h
              Logged:
              Time Spent - Not Specified
              Not Specified

                Development