Question

I'm running a JSF project in TomEE. I upgraded it to MyFace 2.1.8. I'm running everything on OSX with Oracle JVM 1.7.04

So I have this ManagedBean which is ViewScoped:

@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>

Results in this stack trace. What the heck is going on? I know it needs to deserialize the handles, but why would it have trouble finding my service class on the classpath?

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)

EDIT UPDATE:

Logged Ticket https://issues.apache.org/jira/browse/MYFACES-3581

Was it helpful?

Solution

Looks like we need to patch the deserializeView method in MyFaces. Long story short ObjectInputStream does not use the Thread context ClassLoader (Thread.currentThread().getClassLoader()). So this kind of "use the right classloader, damn it!" fixing is very pervasive in app servers. If MyFaces were in the WEB-INF/lib/ then this would magically go away because then MyFaces and the class being deserialized would be in the same classloader. Moving MyFaces is not the right fix, though. Patching it is better.

When the class doing the deserializing is in a parent classloader and therefore cannot see classes from the numerous child classloaders (one for each webapp), you typically have to do this:

import java.io.*;
import java.lang.reflect.Proxy;

public class SmarterObjectInputStream extends ObjectInputStream {

    public SmarterObjectInputStream(InputStream in) throws IOException {
        super(in);
    }

    protected Class resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException {
        try {
            return Class.forName(classDesc.getName(), false, getClassloader());
        } catch (ClassNotFoundException e) {
            String n = classDesc.getName();
            if (n.equals("boolean")) return boolean.class;
            if (n.equals("byte")) return byte.class;
            if (n.equals("char")) return char.class;
            if (n.equals("short")) return short.class;
            if (n.equals("int")) return int.class;
            if (n.equals("long")) return long.class;
            if (n.equals("float")) return float.class;
            if (n.equals("double")) return double.class;

            throw e;
        }
    }

    protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
        Class[] cinterfaces = new Class[interfaces.length];
        for (int i = 0; i < interfaces.length; i++)
            cinterfaces[i] = getClassloader().loadClass(interfaces[i]);

        try {
            return Proxy.getProxyClass(getClassloader(), cinterfaces);
        } catch (IllegalArgumentException e) {
            throw new ClassNotFoundException(null, e);
        }
    }

    private ClassLoader getClassloader() {
        return Thread.currentThread().getContextClassLoader();
    }
}

We'll need to give MyFaces this SmarterObjectInputStream and make sure they use it in the deserializeView method.

If you'd like the honors of doing the patching, that'd be fantastic and I'm sure MyFaces would be quite enthused to see TomEE users contributing fixes. You've had some great contributions so far!

OTHER TIPS

A possible workaround may be set the following in your web.xml:

<context-param>
    <param-name>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</param-name>
    <param-value>false</param-value>
</context-param>

This is fairly non-standard though and I haven't tested this.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top