Bug 47502

Summary: clustering fails on serializing javax.security.auth.subject
Product: Tomcat 6 Reporter: Ronald Klop <ronald-lists>
Component: ClusterAssignee: Tomcat Developers Mailing List <dev>
Status: RESOLVED FIXED    
Severity: critical    
Priority: P2    
Version: 6.0.20   
Target Milestone: default   
Hardware: Other   
OS: Linux   

Description Ronald Klop 2009-07-09 05:57:29 UTC
Clustering fails on serializing javax.security.auth.subject. See stack below.

I looked a little into the Tomcat code. In ./java/org/apache/catalina/connector/Request.java on line 1752 Tomcat puts the 'javax.security.auth.subject' on the session if you use a securitymanager. This is the MemoryUser in my case I think.

I must use the securitymanager because I use RMI.

Is there a solution possible by making the MemoryUser serializable or by not putting it in the session as an attribute. Mark Thomas <markt_at_apache_dot_org> suggested a note on the session at the Tomcat user-mailinglist. I've never seen notes on sessions.

The MemoryUser comes from the security-constraint in my web.xml.


Jul 8, 2009 5:53:52 PM org.apache.catalina.ha.session.DeltaSession writeObject
SEVERE: Cannot serialize session attribute javax.security.auth.subject for session 9C533E0EB4A79ED5B206B8F5A5DB09AD
java.io.NotSerializableException: org.apache.catalina.users.MemoryUser
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1156)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
    at java.util.LinkedList.writeObject(LinkedList.java:943)
    at sun.reflect.GeneratedMethodAccessor216.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
    at java.io.ObjectOutputStream.access$300(ObjectOutputStream.java:143)
    at java.io.ObjectOutputStream$PutFieldImpl.writeFields(ObjectOutputStream.java:1668)
    at java.io.ObjectOutputStream.writeFields(ObjectOutputStream.java:454)
    at javax.security.auth.Subject$SecureSet.writeObject(Subject.java:1281)
    at sun.reflect.GeneratedMethodAccessor215.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1509)
    at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:416)
    at java.util.Collections$SynchronizedCollection.writeObject(Collections.java:1602)
    at sun.reflect.GeneratedMethodAccessor214.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1509)
    at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:416)
    at javax.security.auth.Subject.writeObject(Subject.java:919)
    at sun.reflect.GeneratedMethodAccessor213.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
    at org.apache.catalina.ha.session.DeltaSession.writeObject(DeltaSession.java:714)
    at org.apache.catalina.ha.session.DeltaSession.writeObjectData(DeltaSession.java:475)
    at org.apache.catalina.ha.session.DeltaSession.writeObjectData(DeltaSession.java:472)
    at org.apache.catalina.ha.session.DeltaManager.serializeSessions(DeltaManager.java:733)
    at org.apache.catalina.ha.session.DeltaManager.sendSessions(DeltaManager.java:1513)
    at org.apache.catalina.ha.session.DeltaManager.handleGET_ALL_SESSIONS(DeltaManager.java:1479)
    at org.apache.catalina.ha.session.DeltaManager.messageReceived(DeltaManager.java:1310)
    at org.apache.catalina.ha.session.DeltaManager.messageDataReceived(DeltaManager.java:1093)
    at org.apache.catalina.ha.session.ClusterSessionListener.messageReceived(ClusterSessionListener.java:87)
    at org.apache.catalina.ha.tcp.SimpleTcpCluster.messageReceived(SimpleTcpCluster.java:901)
    at org.apache.catalina.ha.tcp.SimpleTcpCluster.messageReceived(SimpleTcpCluster.java:882)
    at org.apache.catalina.tribes.group.GroupChannel.messageReceived(GroupChannel.java:269)
    at org.apache.catalina.tribes.group.ChannelInterceptorBase.messageReceived(ChannelInterceptorBase.java:79)
    at org.apache.catalina.tribes.group.ChannelInterceptorBase.messageReceived(ChannelInterceptorBase.java:79)
    at org.apache.catalina.tribes.group.interceptors.TcpFailureDetector.messageReceived(TcpFailureDetector.java:110)
    at org.apache.catalina.tribes.group.ChannelInterceptorBase.messageReceived(ChannelInterceptorBase.java:79)
    at org.apache.catalina.tribes.group.ChannelCoordinator.messageReceived(ChannelCoordinator.java:241)
    at org.apache.catalina.tribes.transport.ReceiverBase.messageDataReceived(ReceiverBase.java:225)
    at org.apache.catalina.tribes.transport.nio.NioReplicationTask.drainChannel(NioReplicationTask.java:188)
    at org.apache.catalina.tribes.transport.nio.NioReplicationTask.run(NioReplicationTask.java:91)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:619)
Comment 1 Tim Funk 2009-11-05 10:10:32 UTC
StandardSession excludes the session attribute named javax.security.auth.subject from being serialized. DeltaSession does not. 

Here's what StandardSession excludes from serializing:
    protected static final String[] excludedAttributes = {
        Globals.SUBJECT_ATTR
    };


Of course ... I suspect (without a deeper code dive) that excluding this from serialization might mean that you are not logged in across the whole cluster.

How to test ...

Here is the existing code in DeltaSession.writeObject
        for (int i = 0; i < keys.length; i++) {
            Object value = null;
            value = attributes.get(keys[i]);
            if (value == null)
                continue;
            else if (value instanceof Serializable) {
                saveNames.add(keys[i]);
                saveValues.add(value);
            }
        }

try changing it to (so we are excluding it from serialization) 
        for (int i = 0; i < keys.length; i++) {
            Object value = null;
            value = attributes.get(keys[i]);
            if (value == null || exclude(keys[i]))
                continue;
            else if (value instanceof Serializable) {
                saveNames.add(keys[i]);
                saveValues.add(value);
            }
        }


HERE is the patch ...
Index: java/org/apache/catalina/ha/session/DeltaSession.java
===================================================================
--- java/org/apache/catalina/ha/session/DeltaSession.java       (revision 833086)
+++ java/org/apache/catalina/ha/session/DeltaSession.java       (working copy)
@@ -731,7 +731,7 @@
         for (int i = 0; i < keys.length; i++) {
             Object value = null;
             value = attributes.get(keys[i]);
-            if (value == null)
+            if (value == null || exclude(keys[i]))
                 continue;
             else if (value instanceof Serializable) {
                 saveNames.add(keys[i]);
Comment 2 Tim Funk 2009-11-05 10:29:10 UTC
changing status to NEEDINFO based on last comment
Comment 3 Mark Thomas 2009-12-01 16:33:25 UTC
This session attribute used when running under a security manager. Authentication is handled separately. The patch looks good to me.

I've applied it to trunk and proposed it for 6.0.x
Comment 4 Mark Thomas 2009-12-15 11:09:16 UTC
This has been fixed in 6.0.x and will be included in 6.0.21 onwards.