Re: Exception with Tomcat's session restore capability

From: Mike Kienenberger (mkienen..mail.com)
Date: Mon Sep 11 2006 - 17:02:48 EDT

  • Next message: Dave Dombrosky: "Re: Exception with Tomcat's session restore capability"

    Dave,

    I've sent you some unit tests offlist that I was using for testing
    serialization when I had problems with it.

    Note that testSerializationOfDataContextWithNewObject does not pass.
    The other two may or may not pass for you -- I have defined my own
    equality method for DataObjects.

    However, this should give you a good starting point for trying to test
    serialization separately from everything else.

    Here's what I use for equality in my base DataObject superclass.

        protected static boolean safeEquals(Object A, Object B) {
            return (A == null) ? (B == null) : A.equals(B);
        }

        public boolean equals(Object obj)
        {
            if (obj != null && (obj.getClass().equals(this.getClass())))
            {
                    BaseDataObjectImpl other = (BaseDataObjectImpl)obj;
                    
                    if (false == safeEquals(objectId, other.getObjectId()))
                            return false;
                    
                    return true;
            }

            return false;
        }

            public int hashCode ()
        {
            // This meets requirements (1) and (2). You always return the
            // same value for each object because you always return the
            // same value for all objects. You also return identical
            // hashCode values when two objects test as equals because
            // you always return identical hashCode values. There is no
            // requirement to return different hashCode values when
            // two objects test as not equal.
            //
            // The only real problem with this implementation is that it
            // is an almost totally useless implementation of hashCode.
            // It can turn a Hashtable lookup into a linear search.
            //
            // With JDK 1.1 you can't return 'x', because it can change
            // and the requirements for hashCode are that the same value
            // must be returned each time hashCode is called on the same object.
            //
            // Java 2 (formerly JDK 1.2) allows 'return x'; because Java 2
    allows the hashCode
            // value to change if the underlying data changes. This is more
            // friendly, but still allows data to be lost in hashtables
            // if the underlying hashCode value changes.
            //
            return 0;
        }

    Here's some other serialization stuff. Dont' know if it'll be helpful
    for you. I mark my temporary data context with the "temporary" user
    property. A lot of this code isn't really used anymore, though.

        ////// Serialization

        private void writeObject(java.io.ObjectOutputStream out)
                throws IOException
        {
                Logger logger = Logger.getLogger("serialization");
                if (null == this.getDataContext())
                {
                    logger.info("writeObject: " + this.getClass().getName() + "
    [" + String.valueOf(this.hashCode()) + "] has no DC.");
                    logger.info("writeObject: " + this);
                    Thread.dumpStack();
                }
            out.writeBoolean(Boolean.TRUE ==
    this.getDataContext().getUserProperty("temporary"));
            out.defaultWriteObject();

                if (Boolean.TRUE == this.getDataContext().getUserProperty("temporary"))
                {
                    logger.info("writeObject: " + this.getClass().getName() + "
    [" + String.valueOf(this.hashCode()) + "] has DC [" +
    String.valueOf(this.getDataContext().hashCode()) + "] and is marked
    temporary.");
                }
                else
                {
                    logger.info("writeObject: " + this.getClass().getName() + "
    [" + String.valueOf(this.hashCode()) + "] has DC [" +
    String.valueOf(this.getDataContext().hashCode()) + "] and is not
    marked temporary.");
                }
        }

        private void readObject(java.io.ObjectInputStream in)
                throws IOException, ClassNotFoundException
        {
            boolean isInTemporaryDataContext = in.readBoolean();
            in.defaultReadObject();
            this.isInTemporaryDataContext = isInTemporaryDataContext;

            Logger logger = Logger.getLogger("serialization");
                if (null != this.getDataContext())
                {
                    if (isInTemporaryDataContext)
                    {
                        logger.info("readObject: " + this.getClass().getName() +
    " [" + String.valueOf(this.hashCode()) + "] has DC [" +
    String.valueOf(this.getDataContext().hashCode()) + "] and is marked
    temporary.");
                    }
                    else
                    {
                        logger.info("readObject: " + this.getClass().getName() +
    " [" + String.valueOf(this.hashCode()) + "] has DC [" +
    String.valueOf(this.getDataContext().hashCode()) + "] and is not
    marked temporary.");
                    }
                }
                else
                {
                    if (isInTemporaryDataContext)
                    {
                        logger.info("readObject: " + this.getClass().getName() +
    " [" + String.valueOf(this.hashCode()) + "] has no DC and is marked
    temporary.");
                    }
                    else
                    {
                        logger.info("readObject: " + this.getClass().getName() +
    " [" + String.valueOf(this.hashCode()) + "] has no DC and is not
    marked temporary.");
                    }
                }
       }
        /**
         * Substitute self with an object registered in thread
         * DataContext with the same ID.
         */
        protected Object readResolve() throws ObjectStreamException
        {
            if (null == getDataContext())
            {
                if (isInTemporaryDataContext)
                {
                        Logger logger = Logger.getLogger("serialization");
                        logger.info("readResolve: " + this.getClass().getName() +
    " [" + String.valueOf(this.hashCode()) + "] has no DC and is marked
    temporary.");
                        
                        return this;
                }
                else
                {
                        Logger logger = Logger.getLogger("serialization");
                        logger.info("readResolve: " + this.getClass().getName() +
    " [" + String.valueOf(this.hashCode()) + "] has no DC and is not
    marked temporary.");

                        DataContext context;

                    try {
                        context = DataContext.getThreadDataContext();
                    }
                    catch (IllegalStateException e) {
                        throw new NotActiveException("Can't deserialize
    object for id '"
                                + getObjectId()
                                + "' - no DataContext bound to thread.");
                    }

                    // return context.registeredObject(getObjectId());
                    return context.localObject(getObjectId(), null);
                }
            }
            else
            {
                    Logger logger = Logger.getLogger("serialization");
                    logger.info("readResolve: " + this.getClass().getName() + "
    [" + String.valueOf(this.hashCode()) + "] has DC [" +
    String.valueOf(this.getDataContext().hashCode()) + "].");
                return this;
            }
        }

    On 9/11/06, Mike Kienenberger <mkienen..mail.com> wrote:
    > No, I wouldn't try changing the transient nature.
    >
    > It does seem kinda odd, though. I know that I'm saving serialized
    > objects two different ways -- by individual DataObject for one
    > read-only context, and by DataContext for a modified set of objects.
    > I don't think I have seen this particular problem.
    >
    > Are you serializing the DataContext directly?
    >
    >
    > On 9/11/06, Dave Dombrosky <dombr..mail.com> wrote:
    > > I downloaded the 1.2.1 source and added some code to figure out what
    > > is going on. It appears that context.getChannel() from
    > > ObjectStore.java line 945 is returning null. I am guessing that this
    > > is because of DataContext line 182 which declares the channel to be
    > > transient. Transient properties are not serialized, which is how
    > > tomcat saves the session, so it is reverting back to it's default
    > > value (null).
    > >
    > > I tried removing the "transient" part of the variable declaration, but
    > > then Tomcat complains on a different piece of code. I'm not even sure
    > > if that's the best way to fix it so I don't think I'll explore that
    > > any further yet. Is this issue something that I should add to JIRA?
    > >
    > > -Dave
    > >
    > > On 9/11/06, Mike Kienenberger <mkienen..mail.com> wrote:
    > > > My suggestion would be to look at the 1.2.1 source and see what's
    > > > going on at ObjectStore.java, line 945.
    > > >
    > > > On 9/11/06, Dave Dombrosky <dombr..mail.com> wrote:
    > > > > Problem still occurs on 1.2.1
    > > > >
    > > > > On 9/11/06, Mike Kienenberger <mkienen..mail.com> wrote:
    > > > > > Maybe http://issues.apache.org/cayenne/browse/CAY-614 ?
    > > > > >
    > > > > > This was fixed for 1.2.1. It might be related.
    > > > > >
    > > > > > On 9/11/06, Dave Dombrosky <dombr..mail.com> wrote:
    > > > > > > I am currently having problems when trying to use Cayenne 1.2 along
    > > > > > > with Tomcat's (v5.0.28) session restore capability. That is to say,
    > > > > > > if I deploy new code and restart tomcat, it saves the session data
    > > > > > > instead of starting with all new sessions when it starts up.
    > > > > > >
    > > > > > > My problem is that I used to be able to restart and stay logged in
    > > > > > > using version 1.1. Version 1.2 seems to have problems with trying to
    > > > > > > look up data after I have restarted Tomcat, and I get this stack trace
    > > > > > > caused by a NullPointerException:
    > > > > > >
    > > > > > > org.objectstyle.cayenne.access.ObjectStore.resolveHollow(ObjectStore.java:945)
    > > > > > > org.objectstyle.cayenne.access.DataContext.prepareForAccess(DataContext.java:1842)
    > > > > > > org.objectstyle.cayenne.CayenneDataObject.readProperty(CayenneDataObject.java:238)
    > > > > > >
    > > > > > > I couldn't find anyone else with this problem, so I assume I am doing
    > > > > > > something wrong and it's not a bug. Any ideas?
    > > > > > >
    > > > > > > -Dave
    > > > > > >
    > > > > >
    > > > >
    > > >
    > >
    >



    This archive was generated by hypermail 2.0.0 : Mon Sep 11 2006 - 17:03:12 EDT