Author: markt Date: Tue Aug 16 13:03:26 2011 New Revision: 1158254 URL: http://svn.apache.org/viewvc?rev=1158254&view=rev Log: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=51647 Session replication fails with ClassNotFoundException when session attribute is Java dynamic proxy
Modified: tomcat/tc5.5.x/trunk/STATUS.txt tomcat/tc5.5.x/trunk/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ReplicationStream.java tomcat/tc5.5.x/trunk/container/modules/groupcom/src/share/org/apache/catalina/tribes/io/ReplicationStream.java tomcat/tc5.5.x/trunk/container/webapps/docs/changelog.xml Modified: tomcat/tc5.5.x/trunk/STATUS.txt URL: http://svn.apache.org/viewvc/tomcat/tc5.5.x/trunk/STATUS.txt?rev=1158254&r1=1158253&r2=1158254&view=diff ============================================================================== --- tomcat/tc5.5.x/trunk/STATUS.txt (original) +++ tomcat/tc5.5.x/trunk/STATUS.txt Tue Aug 16 13:03:26 2011 @@ -93,10 +93,3 @@ PATCHES PROPOSED TO BACKPORT: as well as menu names etc.) I think there is not much demand for this feature in 5.5 to justify this. The rest of changes and fixes are OK to backport (e.g. allow to specify port numbers). - -* Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=51647 - Session replication fails with ClassNotFoundException when session attribute - is Java dynamic proxy - https://issues.apache.org/bugzilla/attachment.cgi?id=27375 - +1: markt, kfujino, kkolinko - -1: Modified: tomcat/tc5.5.x/trunk/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ReplicationStream.java URL: http://svn.apache.org/viewvc/tomcat/tc5.5.x/trunk/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ReplicationStream.java?rev=1158254&r1=1158253&r2=1158254&view=diff ============================================================================== --- tomcat/tc5.5.x/trunk/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ReplicationStream.java (original) +++ tomcat/tc5.5.x/trunk/container/modules/cluster/src/share/org/apache/catalina/cluster/session/ReplicationStream.java Tue Aug 16 13:03:26 2011 @@ -22,6 +22,8 @@ import java.io.InputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; /** * Custom subclass of <code>ObjectInputStream</code> that loads from the @@ -86,6 +88,43 @@ public final class ReplicationStream ext } } + /** + * ObjectInputStream.resolveProxyClass has some funky way of using + * the incorrect class loader to resolve proxy classes, let's do it our way instead + */ + protected Class resolveProxyClass(String[] interfaces) + throws IOException, ClassNotFoundException { + + ClassLoader latestLoader = classLoader; + ClassLoader nonPublicLoader = null; + boolean hasNonPublicInterface = false; + + // define proxy in class loader of non-public interface(s), if any + Class[] classObjs = new Class[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) { + Class cl = this.findWebappClass(interfaces[i]); + if (latestLoader == null) latestLoader = cl.getClassLoader(); + if ((cl.getModifiers() & Modifier.PUBLIC) == 0) { + if (hasNonPublicInterface) { + if (nonPublicLoader != cl.getClassLoader()) { + throw new IllegalAccessError( + "conflicting non-public interface class loaders"); + } + } else { + nonPublicLoader = cl.getClassLoader(); + hasNonPublicInterface = true; + } + } + classObjs[i] = cl; + } + try { + return Proxy.getProxyClass(hasNonPublicInterface ? nonPublicLoader + : latestLoader, classObjs); + } catch (IllegalArgumentException e) { + throw new ClassNotFoundException(null, e); + } + } + public Class findReplicationClass(String name) throws ClassNotFoundException, IOException { return Class.forName(name, false, getClass().getClassLoader()); Modified: tomcat/tc5.5.x/trunk/container/modules/groupcom/src/share/org/apache/catalina/tribes/io/ReplicationStream.java URL: http://svn.apache.org/viewvc/tomcat/tc5.5.x/trunk/container/modules/groupcom/src/share/org/apache/catalina/tribes/io/ReplicationStream.java?rev=1158254&r1=1158253&r2=1158254&view=diff ============================================================================== --- tomcat/tc5.5.x/trunk/container/modules/groupcom/src/share/org/apache/catalina/tribes/io/ReplicationStream.java (original) +++ tomcat/tc5.5.x/trunk/container/modules/groupcom/src/share/org/apache/catalina/tribes/io/ReplicationStream.java Tue Aug 16 13:03:26 2011 @@ -22,6 +22,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; /** * Custom subclass of <code>ObjectInputStream</code> that loads from the @@ -71,23 +73,68 @@ public final class ReplicationStream ext public Class resolveClass(ObjectStreamClass classDesc) throws ClassNotFoundException, IOException { String name = classDesc.getName(); - boolean tryRepFirst = name.startsWith("org.apache.catalina.tribes"); try { - try - { - if ( tryRepFirst ) return findReplicationClass(name); - else return findExternalClass(name); - } - catch ( Exception x ) - { - if ( tryRepFirst ) return findExternalClass(name); - else return findReplicationClass(name); - } + return resolveClass(name); } catch (ClassNotFoundException e) { return super.resolveClass(classDesc); } } + public Class resolveClass(String name) + throws ClassNotFoundException, IOException { + + boolean tryRepFirst = name.startsWith("org.apache.catalina.tribes"); + try { + if (tryRepFirst) + return findReplicationClass(name); + else + return findExternalClass(name); + } catch (Exception x) { + if (tryRepFirst) + return findExternalClass(name); + else + return findReplicationClass(name); + } + } + + /** + * ObjectInputStream.resolveProxyClass has some funky way of using + * the incorrect class loader to resolve proxy classes, let's do it our way instead + */ + protected Class resolveProxyClass(String[] interfaces) + throws IOException, ClassNotFoundException { + + ClassLoader latestLoader = (classLoaders!=null && classLoaders.length==0)?null:classLoaders[0]; + ClassLoader nonPublicLoader = null; + boolean hasNonPublicInterface = false; + + // define proxy in class loader of non-public interface(s), if any + Class[] classObjs = new Class[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) { + Class cl = this.resolveClass(interfaces[i]); + if (latestLoader==null) latestLoader = cl.getClassLoader(); + if ((cl.getModifiers() & Modifier.PUBLIC) == 0) { + if (hasNonPublicInterface) { + if (nonPublicLoader != cl.getClassLoader()) { + throw new IllegalAccessError( + "conflicting non-public interface class loaders"); + } + } else { + nonPublicLoader = cl.getClassLoader(); + hasNonPublicInterface = true; + } + } + classObjs[i] = cl; + } + try { + return Proxy.getProxyClass(hasNonPublicInterface ? nonPublicLoader + : latestLoader, classObjs); + } catch (IllegalArgumentException e) { + throw new ClassNotFoundException(null, e); + } + } + + public Class findReplicationClass(String name) throws ClassNotFoundException, IOException { Class clazz = Class.forName(name, false, getClass().getClassLoader()); Modified: tomcat/tc5.5.x/trunk/container/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/tc5.5.x/trunk/container/webapps/docs/changelog.xml?rev=1158254&r1=1158253&r2=1158254&view=diff ============================================================================== --- tomcat/tc5.5.x/trunk/container/webapps/docs/changelog.xml (original) +++ tomcat/tc5.5.x/trunk/container/webapps/docs/changelog.xml Tue Aug 16 13:03:26 2011 @@ -111,6 +111,10 @@ name of the authentication scheme if request has already been authenticated. (kfujino) </fix> + <fix> + <bug>51647</bug>: Fix session replication when a session attribute is a + Java dynamic proxy. Based on a patch by Tomasz Skutnik. (markt) + </fix> </changelog> </subsection> <subsection name="Webapps"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org