You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pc...@apache.org on 2007/11/21 18:41:03 UTC

svn commit: r597155 [1/2] - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ openjpa-kernel/src/main/j...

Author: pcl
Date: Wed Nov 21 09:40:54 2007
New Revision: 597155

URL: http://svn.apache.org/viewvc?rev=597155&view=rev
Log:
OPENJPA-126: EntityManager serializability. Also includes a fix to make LoadListener.afterRefresh() work. Committing directly (not via remote queue) as I'm about to lose my internet connection for a while. Hopefully, my local testing is accurate.

Added:
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ManagedCache.java
    openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/enhance/
    openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/enhance/TestPCSubclassNameConversion.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/kernel/AbstractBrokerSerializationTest.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/kernel/AbstractUnenhancedRelationBrokerSerializationTest.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/kernel/TestEnhancedInstanceBrokerSerialization.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/kernel/TestEntityManagerFactoryPool.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/kernel/TestInstanceGraphBrokerSerialization.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/kernel/TestUnenhancedFieldAccessInstanceBrokerSerialization.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/kernel/TestUnenhancedFieldAccessWithRelationInstanceBrokerSerialization.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/kernel/TestUnenhancedPropertyAccessInstanceBrokerSerialization.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/kernel/TestUnenhancedPropertyAccessWithRelationInstanceBrokerSerialization.java
Modified:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractJDBCSavepointManager.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/EmbedFieldStrategy.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ReflectingPersistenceCapable.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/event/LifecycleEventManager.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractBrokerFactory.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateAttachStrategy.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedValueStateManager.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ObjectIdStateManager.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPASavepoint.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPAStateManager.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SaveFieldManager.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SavepointFieldManager.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/GeneralException.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java
    openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties
    openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java
    openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ReferenceHashSet.java
    openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/AbstractConcurrentEventManager.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/AbstractUnenhancedClassTest.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/ManyOneEntity.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/SimpleEntity.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractJDBCSavepointManager.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractJDBCSavepointManager.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractJDBCSavepointManager.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/AbstractJDBCSavepointManager.java Wed Nov 21 09:40:54 2007
@@ -18,6 +18,9 @@
  */
 package org.apache.openjpa.jdbc.kernel;
 
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectOutputStream;
 import java.sql.Connection;
 import java.util.Collection;
 
@@ -133,6 +136,11 @@
         public void save(Collection states) {
             AbstractJDBCSavepointManager.this.setDataStore(this);
             super.save(states);
+        }
+
+        private void writeObject(ObjectOutputStream out)
+            throws IOException {
+            throw new NotSerializableException();
         }
     }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/EmbedFieldStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/EmbedFieldStrategy.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/EmbedFieldStrategy.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/EmbedFieldStrategy.java Wed Nov 21 09:40:54 2007
@@ -582,8 +582,8 @@
             return _owner;
         }
 
-        public ValueMetaData getOwnerMetaData() {
-            return _vmd;
+        public int getOwnerIndex() {
+            return _vmd.getFieldMetaData().getIndex();
         }
 
         public boolean isEmbedded() {

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java Wed Nov 21 09:40:54 2007
@@ -246,6 +246,36 @@
     }
 
     /**
+     * Whether or not <code>className</code> is the name for a
+     * dynamically-created persistence-capable subclass.
+     *
+     * @since 1.1.0
+     */
+    public static boolean isPCSubclassName(String className) {
+        return className.startsWith(Strings.getPackageName(PCEnhancer.class))
+            && className.endsWith("$pcsubclass");
+    }
+
+    /**
+     * If <code>className</code> is a dynamically-created persistence-capable
+     * subclass name, returns the name of the class that it subclasses.
+     * Otherwise, returns <code>className</code>.
+     *
+     * @since 1.1.0
+     */
+    public static String toManagedTypeName(String className) {
+        if (isPCSubclassName(className)) {
+            className = className.substring(
+                Strings.getPackageName(PCEnhancer.class).length() + 1);
+            className = className.substring(0, className.lastIndexOf("$"));
+            // this is not correct for nested PCs
+            className = className.replace('$', '.');
+        }
+        
+        return className;
+    }
+
+    /**
      * Constructor. Supply configuration, type, and metadata.
      */
     public PCEnhancer(OpenJPAConfiguration conf, BCClass type,
@@ -2718,6 +2748,9 @@
             return;
 
         if (getCreateSubclass()) {
+            // ##### what should happen if a type is Externalizable? It looks
+            // ##### like Externalizable classes will not be serialized as PCs
+            // ##### based on this logic.
             if (!Externalizable.class.isAssignableFrom(
                 _meta.getDescribedType()))
                 addSubclassSerializationCode();

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCRegistry.java Wed Nov 21 09:40:54 2007
@@ -122,6 +122,17 @@
     }
 
     /**
+     * Return the persistence-capable type for <code>type</code>. This might
+     * be a generated subclass of <code>type</code>.
+     *
+     * @since 1.1.0
+     */
+    public static Class getPCType(Class type) {
+        Meta meta = getMeta(type);
+        return (meta.pc == null) ? null : meta.pc.getClass();
+    }
+
+    /**
      * Create a new identity object for the given
      * <code>PersistenceCapable</code> class.
      */

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ReflectingPersistenceCapable.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ReflectingPersistenceCapable.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ReflectingPersistenceCapable.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ReflectingPersistenceCapable.java Wed Nov 21 09:40:54 2007
@@ -18,17 +18,23 @@
  */
 package org.apache.openjpa.enhance;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.kernel.StateManagerImpl;
 import org.apache.openjpa.meta.ClassMetaData;
-import org.apache.openjpa.meta.JavaTypes;
 import org.apache.openjpa.meta.FieldMetaData;
-import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.meta.JavaTypes;
 import org.apache.openjpa.util.ApplicationIds;
+import org.apache.openjpa.util.ImplHelper;
 import org.apache.openjpa.util.InternalException;
 import org.apache.openjpa.util.ObjectId;
-import org.apache.openjpa.kernel.StateManagerImpl;
 
 /**
  * Implementation of the {@link PersistenceCapable} interface that can handle
@@ -38,12 +44,19 @@
  * @since 1.0.0
  */
 public class ReflectingPersistenceCapable
-    implements PersistenceCapable, ManagedInstanceProvider {
+    implements PersistenceCapable, ManagedInstanceProvider, Serializable {
 
     private Object o;
     private StateManager sm;
-    private PersistenceCapable pcSubclassInstance;
-    private ClassMetaData meta;
+
+    // this will be reconstituted in readObject()
+    private transient PersistenceCapable pcSubclassInstance;
+
+    // this will reconstituted by a call to pcReplaceStateManager() by the
+    // instance that has a reference to the deserialized data
+    private transient ClassMetaData meta;
+
+    private boolean serializationUserVisible = true;
 
     public ReflectingPersistenceCapable(Object o, OpenJPAConfiguration conf) {
         this.o = o;
@@ -70,6 +83,8 @@
 
     public void pcReplaceStateManager(StateManager sm) {
         this.sm = sm;
+        if (meta == null && sm instanceof OpenJPAStateManager)
+            meta = ((OpenJPAStateManager) sm).getMetaData();
     }
 
     public void pcProvideField(int i) {
@@ -169,6 +184,10 @@
     }
 
     public void pcCopyFields(Object fromObject, int[] fieldIndices) {
+        if (fromObject instanceof ReflectingPersistenceCapable)
+            fromObject = ((ReflectingPersistenceCapable) fromObject)
+                .getManagedInstance();
+        
         for(int i = 0; i < fieldIndices.length; i++)
             pcCopyField(fromObject, fieldIndices[i]);
     }
@@ -305,21 +324,23 @@
         // ##### we can implement this if a state field has been set
     }
 
+    public void pcSetSerializationUserVisible(boolean userVisible) {
+        serializationUserVisible = userVisible;
+    }
+
+    public boolean pcIsSerializationUserVisible() {
+        return serializationUserVisible;
+    }
+
     public Object getManagedInstance() {
         return o;
     }
 
     private Object getValue(int i, Object o) {
         if (meta.getAccessType() == ClassMetaData.ACCESS_PROPERTY) {
-            if (!meta.isIntercepting()) {
-                Method meth = Reflection.findGetter(meta.getDescribedType(),
-                    meta.getField(i).getName(), true);
-                return Reflection.get(o, meth);
-            } else {
-                Field field = Reflection.findField(meta.getDescribedType(),
-                    toFieldName(i), true);
-                return Reflection.get(o, field);
-            }
+            Field field = Reflection.findField(meta.getDescribedType(),
+                toFieldName(i), true);
+            return Reflection.get(o, field);
         } else {
             Field field = (Field) meta.getField(i).getBackingMember();
             return Reflection.get(o, field);
@@ -349,5 +370,18 @@
             Field field = (Field) meta.getField(i).getBackingMember();
             Reflection.set(o, field, val);
         }
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        out.defaultWriteObject();
+        out.writeObject(meta.getDescribedType());
+    }
+
+    private void readObject(ObjectInputStream in)
+        throws ClassNotFoundException, IOException {
+        in.defaultReadObject();
+        Class type = (Class) in.readObject();
+        pcSubclassInstance = PCRegistry.newInstance(type, null, false);
+        ImplHelper.registerPersistenceCapable(this);
     }
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/event/LifecycleEventManager.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/event/LifecycleEventManager.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/event/LifecycleEventManager.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/event/LifecycleEventManager.java Wed Nov 21 09:40:54 2007
@@ -18,6 +18,7 @@
  */
 package org.apache.openjpa.event;
 
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -44,7 +45,7 @@
  * @nojavadoc
  */
 public class LifecycleEventManager
-    implements CallbackModes {
+    implements CallbackModes, Serializable {
 
     private static final Exception[] EMPTY_EXCEPTIONS = new Exception[0];
 

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractBrokerFactory.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractBrokerFactory.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractBrokerFactory.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractBrokerFactory.java Wed Nov 21 09:40:54 2007
@@ -19,16 +19,16 @@
 package org.apache.openjpa.kernel;
 
 import java.io.ObjectStreamException;
+import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.Map;
-import java.util.Properties;
 import java.util.LinkedList;
 import java.util.List;
-import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+import java.util.Properties;
 import javax.transaction.Status;
 import javax.transaction.Synchronization;
 import javax.transaction.Transaction;
@@ -41,23 +41,24 @@
 import org.apache.openjpa.ee.ManagedRuntime;
 import org.apache.openjpa.enhance.PCRegistry;
 import org.apache.openjpa.enhance.PersistenceCapable;
-import org.apache.openjpa.event.RemoteCommitEventManager;
 import org.apache.openjpa.event.BrokerFactoryEvent;
+import org.apache.openjpa.event.RemoteCommitEventManager;
 import org.apache.openjpa.lib.conf.Configuration;
+import org.apache.openjpa.lib.conf.Configurations;
 import org.apache.openjpa.lib.log.Log;
 import org.apache.openjpa.lib.util.J2DoPrivHelper;
+import org.apache.openjpa.lib.util.JavaVersions;
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.lib.util.ReferenceHashSet;
-import org.apache.openjpa.lib.util.JavaVersions;
 import org.apache.openjpa.lib.util.concurrent.ConcurrentHashMap;
 import org.apache.openjpa.lib.util.concurrent.ConcurrentReferenceHashSet;
 import org.apache.openjpa.lib.util.concurrent.ReentrantLock;
 import org.apache.openjpa.meta.MetaDataRepository;
 import org.apache.openjpa.util.GeneralException;
+import org.apache.openjpa.util.InternalException;
 import org.apache.openjpa.util.InvalidStateException;
 import org.apache.openjpa.util.OpenJPAException;
 import org.apache.openjpa.util.UserException;
-import org.apache.openjpa.util.InternalException;
 
 /**
  * Abstract implementation of the {@link BrokerFactory}
@@ -114,11 +115,12 @@
 
     /**
      * Return an internal factory pool key for the given configuration.
-     * We use the conf properties as given by the user because that is what's
-     * passed to {@link #getPooledFactory} when looking for an existing factory.
      */
-    private static Map toPoolKey(OpenJPAConfiguration conf) {
-        return conf.toProperties(false);
+    private static Object toPoolKey(OpenJPAConfiguration conf) {
+        if (conf.getId() != null)
+            return conf.getId();
+        else
+            return conf.toProperties(false);
     }
 
     /**
@@ -126,7 +128,18 @@
      * if none.
      */
     protected static AbstractBrokerFactory getPooledFactory(Map map) {
-        return (AbstractBrokerFactory) _pool.get(map);
+        Object key = Configurations.getProperty("Id", map);
+        if (key == null)
+            key = map;
+        return getPooledFactoryForKey(key);
+    }
+
+    /**
+     * Return the pooled factory matching the given key, or null
+     * if none. The key must be of the form created by {@link #getPoolKey}.
+     */
+    public static AbstractBrokerFactory getPooledFactoryForKey(Object key) {
+        return (AbstractBrokerFactory) _pool.get(key);
     }
 
     /**
@@ -174,32 +187,9 @@
             if (findExisting)
                 broker = findBroker(user, pass, managed);
             if (broker == null) {
-                // decorate the store manager for data caching and custom
-                // result object providers; always make sure it's a delegating
-                // store manager, because it's easier for users to deal with
-                // that way
-                StoreManager sm = newStoreManager();
-                DelegatingStoreManager dsm = null;
-                if (_conf.getDataCacheManagerInstance().getSystemDataCache()
-                    != null)
-                    dsm = new DataCacheStoreManager(sm);
-                dsm = new ROPStoreManager((dsm == null) ? sm : dsm);
-
                 broker = newBrokerImpl(user, pass);
-                broker.initialize(this, dsm, managed, connRetainMode);
-                addListeners(broker);
-
-                // if we're using remote events, register the event manager so
-                // that it can broadcast commit notifications from the broker
-                RemoteCommitEventManager remote = _conf.
-                    getRemoteCommitEventManager();
-                if (remote.areRemoteEventsEnabled())
-                    broker.addTransactionListener(remote);
-
-                loadPersistentTypes(broker.getClassLoader());
+                initializeBroker(managed, connRetainMode, broker, false);
             }
-            _brokers.add(broker);
-            _conf.setReadOnly(Configuration.INIT_STATE_FROZEN);
             return broker;
         } catch (OpenJPAException ke) {
             throw ke;
@@ -208,6 +198,39 @@
         }
     }
 
+    void initializeBroker(boolean managed, int connRetainMode,
+        BrokerImpl broker, boolean fromDeserialization) {
+        assertOpen();
+        makeReadOnly();
+
+        // decorate the store manager for data caching and custom
+        // result object providers; always make sure it's a delegating
+        // store manager, because it's easier for users to deal with
+        // that way
+        StoreManager sm = newStoreManager();
+        DelegatingStoreManager dsm = null;
+        if (_conf.getDataCacheManagerInstance().getSystemDataCache()
+            != null)
+            dsm = new DataCacheStoreManager(sm);
+        dsm = new ROPStoreManager((dsm == null) ? sm : dsm);
+
+        broker.initialize(this, dsm, managed, connRetainMode,
+            fromDeserialization);
+        if (!fromDeserialization)
+            addListeners(broker);
+
+        // if we're using remote events, register the event manager so
+        // that it can broadcast commit notifications from the broker
+        RemoteCommitEventManager remote = _conf.
+            getRemoteCommitEventManager();
+        if (remote.areRemoteEventsEnabled())
+            broker.addTransactionListener(remote);
+
+        loadPersistentTypes(broker.getClassLoader());
+        _brokers.add(broker);
+        _conf.setReadOnly(Configuration.INIT_STATE_FROZEN);
+    }
+
     /**
      * Add factory-registered lifecycle listeners to the broker.
      */
@@ -374,10 +397,10 @@
             assertNoActiveTransaction();
 
             // remove from factory pool
-            Map map = toPoolKey(_conf);
+            Object key = toPoolKey(_conf);
             synchronized (_pool) {
-                if (_pool.get(map) == this)
-                    _pool.remove(map);
+                if (_pool.get(key) == this)
+                    _pool.remove(key);
             }
 
             // close all brokers
@@ -754,6 +777,15 @@
      */
     public Collection getOpenBrokers() {
         return Collections.unmodifiableCollection(_brokers);
+    }
+
+    /**
+     * @return a key that can be used to obtain this broker factory from the
+     * pool at a later time.
+     * @since 1.1.0
+     */
+    public Object getPoolKey() {
+        return toPoolKey(getConfiguration());
     }
 
     /**

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java Wed Nov 21 09:40:54 2007
@@ -18,6 +18,9 @@
  */
 package org.apache.openjpa.kernel;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.Serializable;
 import java.lang.reflect.Modifier;
 import java.security.AccessController;
@@ -91,7 +94,7 @@
  * @author Abe White
  */
 public class BrokerImpl
-    implements Broker, FindCallbacks, Cloneable {
+    implements Broker, FindCallbacks, Cloneable, Serializable {
 
     /**
      * Incremental flush.
@@ -132,30 +135,35 @@
     private static final int FLAG_RETAINED_CONN = 2 << 10;
     private static final int FLAG_TRANS_ENDING = 2 << 11;
 
+    private static final Object[] EMPTY_OBJECTS = new Object[0];
+
     private static final Localizer _loc =
         Localizer.forPackage(BrokerImpl.class);
 
     //	the store manager in use; this may be a decorator such as a
     //	data cache store manager around the native store manager
-    private DelegatingStoreManager _store = null;
+    private transient DelegatingStoreManager _store = null;
 
-    // ref to producing factory and configuration
-    private AbstractBrokerFactory _factory = null;
-    private OpenJPAConfiguration _conf = null;
-    private Compatibility _compat = null;
     private FetchConfiguration _fc = null;
-    private Log _log = null;
     private String _user = null;
     private String _pass = null;
-    private ManagedRuntime _runtime = null;
-    private LockManager _lm = null;
-    private InverseManager _im = null;
-    private ReentrantLock _lock = null;
-    private OpCallbacks _call = null;
-    private RuntimeExceptionTranslator _extrans = null;
+
+    // these must be rebuilt by the facade layer during its deserialization
+    private transient Log _log = null;
+    private transient Compatibility _compat = null;
+    private transient ManagedRuntime _runtime = null;
+    private transient LockManager _lm = null;
+    private transient InverseManager _im = null;
+    private transient ReentrantLock _lock = null;
+    private transient OpCallbacks _call = null;
+    private transient RuntimeExceptionTranslator _extrans = null;
+
+    // ref to producing factory and configuration
+    private transient AbstractBrokerFactory _factory = null;
+    private transient OpenJPAConfiguration _conf = null;
 
     // cache class loader associated with the broker
-    private ClassLoader _loader = null;
+    private transient ClassLoader _loader = null;
 
     // user state
     private Synchronization _sync = null;
@@ -167,8 +175,11 @@
     private Set _transAdditions = null;
     private Set _derefCache = null;
     private Set _derefAdditions = null;
-    private Map _loading = null;
-    private Set _operating = null;
+
+    // these are used for method-internal state only
+    private transient Map _loading = null;
+    private transient Set _operating = null;
+
     private Set _persistedClss = null;
     private Set _updatedClss = null;
     private Set _deletedClss = null;
@@ -179,14 +190,15 @@
     // (the first uses the transactional cache)
     private Set _savepointCache = null;
     private LinkedMap _savepoints = null;
-    private SavepointManager _spm = null;
+    private transient SavepointManager _spm = null;
 
     // track open queries and extents so we can free their resources on close
-    private ReferenceHashSet _queries = null;
-    private ReferenceHashSet _extents = null;
+    private transient ReferenceHashSet _queries = null;
+    private transient ReferenceHashSet _extents = null;
 
-    // track operation stack depth
-    private int _operationCount = 0;
+    // track operation stack depth. Transient because operations cannot
+    // span serialization.
+    private transient int _operationCount = 0;
 
     // options
     private boolean _nontransRead = false;
@@ -210,8 +222,13 @@
 
     // status
     private int _flags = 0;
-    private boolean _closed = false;
-    private RuntimeException _closedException = null;
+
+    // this is not in status because it should not be serialized
+    private transient boolean _isSerializing = false;
+
+    // transient because closed brokers can't be serialized
+    private transient boolean _closed = false;
+    private transient RuntimeException _closedException = null;
 
     // event managers
     private TransactionEventManager _transEventManager = null;
@@ -219,8 +236,7 @@
     private LifecycleEventManager _lifeEventManager = null;
     private int _lifeCallbackMode = 0;
 
-    private boolean _initializeWasInvoked = false;
-    private static final Object[] EMPTY_OBJECTS = new Object[0];
+    private transient boolean _initializeWasInvoked = false;
 
     /**
      * Set the persistence manager's authentication. This is the first
@@ -245,17 +261,22 @@
      * handle interaction with the data store
      * @param managed the transaction mode
      * @param connMode the connection retain mode
+     * @param fromDeserialization whether this call happened because of a
+     * deserialization or creation of a new BrokerImpl.
      */
-    public void initialize(AbstractBrokerFactory factory,
-        DelegatingStoreManager sm, boolean managed, int connMode) {
+    void initialize(AbstractBrokerFactory factory,
+        DelegatingStoreManager sm, boolean managed, int connMode,
+        boolean fromDeserialization) {
         _initializeWasInvoked = true;
         _loader = (ClassLoader) AccessController.doPrivileged(
             J2DoPrivHelper.getContextClassLoaderAction());
-        _conf = factory.getConfiguration();
+        if (!fromDeserialization)
+            _conf = factory.getConfiguration();
         _compat = _conf.getCompatibilityInstance();
         _factory = factory;
         _log = _conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);
-        _cache = new ManagedCache();
+        if (!fromDeserialization)
+            _cache = new ManagedCache(this);
         initializeOperatingSet();
         _connRetainMode = connMode;
         _managed = managed;
@@ -264,15 +285,17 @@
         else
             _runtime = new LocalManagedRuntime(this);
 
-        _lifeEventManager = new LifecycleEventManager();
-        _transEventManager = new TransactionEventManager();
-        int cmode = _conf.getMetaDataRepositoryInstance().
-            getMetaDataFactory().getDefaults().getCallbackMode();
-        setLifecycleListenerCallbackMode(cmode);
-        setTransactionListenerCallbackMode(cmode);
+        if (!fromDeserialization) {
+            _lifeEventManager = new LifecycleEventManager();
+            _transEventManager = new TransactionEventManager();
+            int cmode = _conf.getMetaDataRepositoryInstance().
+                getMetaDataFactory().getDefaults().getCallbackMode();
+            setLifecycleListenerCallbackMode(cmode);
+            setTransactionListenerCallbackMode(cmode);
 
-        // setup default options
-        _factory.configureBroker(this);
+            // setup default options
+            _factory.configureBroker(this);
+        }
 
         // make sure to do this after configuring broker so that store manager
         // can look to broker configuration; we set both store and lock managers
@@ -287,8 +310,10 @@
 
         if (_connRetainMode == CONN_RETAIN_ALWAYS)
             retainConnection();
-        _fc = _store.newFetchConfiguration();
-        _fc.setContext(this);
+        if (!fromDeserialization) {
+            _fc = _store.newFetchConfiguration();
+            _fc.setContext(this);
+        }
 
         // synch with the global transaction in progress, if any
         if (_factory.syncWithManagedTransaction(this, false))
@@ -749,7 +774,7 @@
 
             // cached instance?
             StateManagerImpl sm = getStateManagerImplById(oid,
-                (flags & OID_ALLOW_NEW) != 0 || (_flags & FLAG_FLUSHED) != 0);
+                (flags & OID_ALLOW_NEW) != 0 || hasFlushed());
             if (sm != null) {
                 if (!requiresLoad(sm, true, fetch, edata, flags))
                     return call.processReturn(oid, sm);
@@ -911,7 +936,7 @@
                 // if we don't have a cached instance or it is not transactional
                 // and is hollow or we need to validate, load it
                 sm = getStateManagerImplById(oid, (flags & OID_ALLOW_NEW) != 0
-                    || (_flags & FLAG_FLUSHED) != 0);
+                    || hasFlushed());
                 initialized = sm != null;
                 if (!initialized)
                     sm = newStateManagerImpl(oid, (flags & OID_COPY) != 0);
@@ -986,6 +1011,10 @@
         }
     }
 
+    private boolean hasFlushed() {
+        return (_flags & FLAG_FLUSHED) != 0;
+    }
+
     /**
      * Return whether the given instance needs loading before being returned
      * to the user.
@@ -1457,8 +1486,7 @@
             if (_savepoints != null && _savepoints.containsKey(name))
                 throw new UserException(_loc.get("savepoint-exists", name));
 
-            if ((_flags & FLAG_FLUSHED) != 0
-                && !_spm.supportsIncrementalFlush())
+            if (hasFlushed() && !_spm.supportsIncrementalFlush())
                 throw new UnsupportedException(_loc.get
                     ("savepoint-flush-not-supported"));
 
@@ -2359,8 +2387,7 @@
 
                 // an embedded field; notify the owner that the value has
                 // changed by becoming independently persistent
-                sm.getOwner().dirty(sm.getOwnerMetaData().
-                    getFieldMetaData().getIndex());
+                sm.getOwner().dirty(sm.getOwnerIndex());
                 _cache.persist(sm);
                 pc = sm.getPersistenceCapable();
             } else {
@@ -4357,278 +4384,69 @@
         return (sm == null) ? null : sm.getManagedInstance();
     }
 
-    /**
-     * Cache of managed objects.
-     */
-    private class ManagedCache {
-
-        private Map _main; // oid -> sm
-        private Map _conflicts = null; // conflict oid -> new sm
-        private Map _news = null; // tmp id -> new sm
-        private Collection _embeds = null; // embedded/non-persistent sms
-        private Collection _untracked = null; // hard refs to untracked sms
-
-        /**
-         * Constructor; supply primary cache map.
-         */
-        private ManagedCache() {
-            _main = newManagedObjectCache();
-        }
-
-        /**
-         * Return the instance for the given oid, optionally allowing
-         * new instances.
-         */
-        public StateManagerImpl getById(Object oid, boolean allowNew) {
-            if (oid == null)
-                return null;
-
-            // check main cache for oid
-            StateManagerImpl sm = (StateManagerImpl) _main.get(oid);
-            StateManagerImpl sm2;
-            if (sm != null) {
-                // if it's a new instance, we know it's the only match, because
-                // other pers instances override new instances in _cache
-                if (sm.isNew())
-                    return (allowNew) ? sm : null;
-                if (!allowNew || !sm.isDeleted())
-                    return sm;
-
-                // sm is deleted; check conflict cache
-                if (_conflicts != null) {
-                    sm2 = (StateManagerImpl) _conflicts.get(oid);
-                    if (sm2 != null)
-                        return sm2;
-                }
-            }
-
-            // at this point sm is null or deleted; check the new cache for
-            // any matches. this allows us to match app id objects to new
-            // instances without permanant oids
-            if (allowNew && _news != null && !_news.isEmpty()) {
-                sm2 = (StateManagerImpl) _news.get(oid);
-                if (sm2 != null)
-                    return sm2;
-            }
-            return sm;
-        }
-
-        /**
-         * Call this method when a new state manager initializes itself.
-         */
-        public void add(StateManagerImpl sm) {
-            if (!sm.isIntercepting()) {
-                if (_untracked == null)
-                    _untracked = new HashSet();
-                _untracked.add(sm);
-            }
-
-            if (!sm.isPersistent() || sm.isEmbedded()) {
-                if (_embeds == null)
-                    _embeds = new ReferenceHashSet(ReferenceHashSet.WEAK);
-                _embeds.add(sm);
-                return;
-            }
-
-            // initializing new instance; put in new cache because won't have
-            // permanent oid yet
-            if (sm.isNew()) {
-                if (_news == null)
-                    _news = new HashMap();
-                _news.put(sm.getId(), sm);
-                return;
-            }
-
-            // initializing persistent instance; put in main cache
-            StateManagerImpl orig = (StateManagerImpl) _main.put
-                (sm.getObjectId(), sm);
-            if (orig != null) {
-                _main.put(sm.getObjectId(), orig);
-                throw new UserException(_loc.get("dup-load",
-                    sm.getObjectId(), Exceptions.toString
-                    (orig.getManagedInstance()))).
-                    setFailedObject(sm.getManagedInstance());
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        assertOpen();
+        lock();
+        try {
+            if (isActive()) {
+                if (!getOptimistic())
+                    throw new InvalidStateException(
+                        _loc.get("cant-serialize-pessimistic-broker"));
+                if (hasFlushed())
+                    throw new InvalidStateException(
+                        _loc.get("cant-serialize-flushed-broker"));
+                if (hasConnection())
+                    throw new InvalidStateException(
+                        _loc.get("cant-serialize-connected-broker"));
             }
-        }
 
-        /**
-         * Remove the given state manager from the cache when it transitions
-         * to transient.
-         */
-        public void remove(Object id, StateManagerImpl sm) {
-            // if it has a permanent oid, remove from main / conflict cache,
-            // else remove from embedded/nontrans cache, and if not there
-            // remove from new cache
-            Object orig;
-            if (sm.getObjectId() != null) {
-                orig = _main.remove(id);
-                if (orig != sm) {
-                    if (orig != null)
-                        _main.put(id, orig); // put back
-                    if (_conflicts != null) {
-                        orig = _conflicts.remove(id);
-                        if (orig != null && orig != sm)
-                            _conflicts.put(id, orig); // put back
-                    }
-                }
-            } else if ((_embeds == null || !_embeds.remove(sm))
-                && _news != null) {
-                orig = _news.remove(id);
-                if (orig != null && orig != sm)
-                    _news.put(id, orig); // put back
-            }
-
-            if (_untracked != null)
-                _untracked.remove(sm);
-        }
-
-        /**
-         * An embedded or nonpersistent managed instance has been persisted.
-         */
-        public void persist(StateManagerImpl sm) {
-            if (_embeds != null)
-                _embeds.remove(sm);
-        }
-
-        /**
-         * A new instance has just been assigned a permanent oid.
-         */
-        public void assignObjectId(Object id, StateManagerImpl sm) {
-            // if assigning oid, remove from new cache and put in primary; may
-            // not be in new cache if another new instance had same id
-            StateManagerImpl orig = (StateManagerImpl) _news.remove(id);
-            if (orig != null && orig != sm)
-                _news.put(id, orig); // put back
-
-            // put in main cache, but make sure we don't replace another
-            // instance with the same oid
-            orig = (StateManagerImpl) _main.put(sm.getObjectId(), sm);
-            if (orig != null) {
-                _main.put(sm.getObjectId(), orig);
-                if (!orig.isDeleted())
-                    throw new UserException(_loc.get("dup-oid-assign",
-                        sm.getObjectId(), Exceptions.toString
-                        (sm.getManagedInstance()))).
-                        setFailedObject(sm.getManagedInstance());
-
-                // same oid as deleted instance; put in conflict cache
-                if (_conflicts == null)
-                    _conflicts = new HashMap();
-                _conflicts.put(sm.getObjectId(), sm);
+            try {
+                _isSerializing = true;
+                out.writeObject(_factory.getPoolKey());
+                out.defaultWriteObject();
+            } finally {
+                _isSerializing = false;
             }
+        } finally {
+            unlock();
         }
+    }
 
-        /**
-         * A new instance has committed; recache under permanent oid.
-         */
-        public void commitNew(Object id, StateManagerImpl sm) {
-            // if the id didn't change, the instance was already assigned an
-            // id, but it could have been in conflict cache
-            StateManagerImpl orig;
-            if (sm.getObjectId() == id) {
-                orig = (_conflicts == null) ? null
-                    : (StateManagerImpl) _conflicts.remove(id);
-                if (orig == sm) {
-                    orig = (StateManagerImpl) _main.put(id, sm);
-                    if (orig != null && !orig.isDeleted()) {
-                        _main.put(sm.getObjectId(), orig);
-                        throw new UserException(_loc.get("dup-oid-assign",
-                            sm.getObjectId(), Exceptions.toString
-                            (sm.getManagedInstance()))).setFailedObject
-                            (sm.getManagedInstance()).setFatal(true);
-                    }
-                }
-                return;
-            }
+    private void readObject(ObjectInputStream in)
+        throws ClassNotFoundException, IOException {
+        Object factoryKey = in.readObject();
+        AbstractBrokerFactory factory =
+            AbstractBrokerFactory.getPooledFactoryForKey(factoryKey);
+
+        // this needs to happen before defaultReadObject so that it's
+        // available for calls to broker.getConfiguration() during
+        // StateManager deserialization
+        _conf = factory.getConfiguration();
+        
+        in.defaultReadObject();
+        factory.initializeBroker(_managed, _connRetainMode, this, true);
 
-            // oid changed, so it must previously have been a new instance
-            // without an assigned oid.  remove it from the new cache; ok if
-            // we end up removing another instance with same id
-            if (_news != null)
-                _news.remove(id);
-
-            // and put into main cache now that id is asssigned
-            orig = (StateManagerImpl) _main.put(sm.getObjectId(), sm);
-            if (orig != null && orig != sm && !orig.isDeleted()) {
-                // put back orig and throw error
-                _main.put(sm.getObjectId(), orig);
-                throw new UserException(_loc.get("dup-oid-assign",
-                    sm.getObjectId(), Exceptions.toString
-                    (sm.getManagedInstance()))).setFailedObject
-                    (sm.getManagedInstance()).setFatal(true);
-            }
-        }
-
-        /**
-         * Return a copy of all cached persistent objects.
-         */
-        public Collection copy() {
-            // proxies not included here because the state manager is always
-            // present in other caches too
-
-            int size = _main.size();
-            if (_conflicts != null)
-                size += _conflicts.size();
-            if (_news != null)
-                size += _news.size();
-            if (_embeds != null)
-                size += _embeds.size();
-            if (size == 0)
-                return Collections.EMPTY_LIST;
-
-            List copy = new ArrayList(size);
-            for (Iterator itr = _main.values().iterator(); itr.hasNext();)
-                copy.add(itr.next());
-            if (_conflicts != null && !_conflicts.isEmpty())
-                for (Iterator itr = _conflicts.values().iterator();
-                    itr.hasNext();)
-                    copy.add(itr.next());
-            if (_news != null && !_news.isEmpty())
-                for (Iterator itr = _news.values().iterator(); itr.hasNext();)
-                    copy.add(itr.next());
-            if (_embeds != null && !_embeds.isEmpty())
-                for (Iterator itr = _embeds.iterator(); itr.hasNext();)
-                    copy.add(itr.next());
-            return copy;
-        }
-
-        /**
-         * Clear the cache.
-         */
-        public void clear() {
-            _main = newManagedObjectCache();
-            if (_conflicts != null)
-                _conflicts = null;
-            if (_news != null)
-                _news = null;
-            if (_embeds != null)
-                _embeds = null;
-            if (_untracked != null)
-                _untracked = null;
-        }
-
-        /**
-         * Clear new instances without permanent oids.
-         */
-        public void clearNew() {
-            if (_news != null)
-                _news = null;
-        }
+        // re-initialize the lock if needed.
+        setMultithreaded(_multithreaded);
 
-        private void dirtyCheck() {
-            if (_untracked == null)
-                return;
+        if (isActive() && _runtime instanceof LocalManagedRuntime)
+            ((LocalManagedRuntime) _runtime).begin();
+    }
 
-            for (Iterator iter = _untracked.iterator(); iter.hasNext(); )
-                ((StateManagerImpl) iter.next()).dirtyCheck();
-        }
+    /**
+     * Whether or not this broker is in the midst of being serialized.
+     *
+     * @since 1.1.0 
+     */
+    boolean isSerializing() {
+        return _isSerializing;
     }
 
     /**
      * Transactional cache that holds soft refs to clean instances.
      */
-    private static class TransactionalCache
-        implements Set {
+    static class TransactionalCache
+        implements Set, Serializable {
 
         private final boolean _orderDirty;
         private Set _dirty = null;

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateAttachStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateAttachStrategy.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateAttachStrategy.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateAttachStrategy.java Wed Nov 21 09:40:54 2007
@@ -171,8 +171,7 @@
                 BitSet toLoad = (BitSet) fields.clone();
                 toLoad.andNot(sm.getLoaded()); // skip already loaded fields
                 if (toLoad.length() > 0)
-                    sm.loadFields(toLoad, null, LockLevels.LOCK_NONE, null,
-                        false);
+                    sm.loadFields(toLoad, null, LockLevels.LOCK_NONE, null);
                 //### we should calculate lock level above
             }
             Object version = state[offset];

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java Wed Nov 21 09:40:54 2007
@@ -145,7 +145,7 @@
                 }
             }
             FetchConfiguration fc = broker.getFetchConfiguration();
-            sm.loadFields(load, fc, fc.getWriteLockLevel(), null, true);
+            sm.loadFields(load, fc, fc.getWriteLockLevel(), null);
         }        
         Object origVersion = sm.getVersion();
         sm.setVersion(_version);
@@ -698,7 +698,7 @@
         throw new UnsupportedOperationException();
     }
 
-    public ValueMetaData getOwnerMetaData() {
+    public int getOwnerIndex() {
         throw new UnsupportedOperationException();
     }
 

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedValueStateManager.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedValueStateManager.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedValueStateManager.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedValueStateManager.java Wed Nov 21 09:40:54 2007
@@ -88,8 +88,8 @@
         return null;
     }
 
-    public ValueMetaData getOwnerMetaData() {
-        return null;
+    public int getOwnerIndex() {
+        throw new UnsupportedOperationException();
     }
 
     public boolean isEmbedded() {

Added: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ManagedCache.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ManagedCache.java?rev=597155&view=auto
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ManagedCache.java (added)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ManagedCache.java Wed Nov 21 09:40:54 2007
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.kernel;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.lib.util.ReferenceHashSet;
+import org.apache.openjpa.util.Exceptions;
+import org.apache.openjpa.util.InternalException;
+import org.apache.openjpa.util.UserException;
+
+/**
+ * Cache of managed objects. Must be static for serialization reasons.
+ */
+class ManagedCache implements Serializable {
+
+    private static final Localizer _loc =
+        Localizer.forPackage(ManagedCache.class);
+
+    private Map _main; // oid -> sm
+    private Map _conflicts = null; // conflict oid -> new sm
+    private Map _news = null; // tmp id -> new sm
+    private Collection _embeds = null; // embedded/non-persistent sms
+    private Collection _untracked = null; // hard refs to untracked sms
+    private BrokerImpl broker;
+
+    /**
+     * Constructor; supply primary cache map.
+     */
+    ManagedCache(BrokerImpl broker) {
+        this.broker = broker;
+        _main = broker.newManagedObjectCache();
+    }
+
+    /**
+     * Return the instance for the given oid, optionally allowing
+     * new instances.
+     */
+    public StateManagerImpl getById(Object oid, boolean allowNew) {
+        if (oid == null)
+            return null;
+
+        // check main cache for oid
+        StateManagerImpl sm = (StateManagerImpl) _main.get(oid);
+        StateManagerImpl sm2;
+        if (sm != null) {
+            // if it's a new instance, we know it's the only match, because
+            // other pers instances override new instances in _cache
+            if (sm.isNew())
+                return (allowNew) ? sm : null;
+            if (!allowNew || !sm.isDeleted())
+                return sm;
+
+            // sm is deleted; check conflict cache
+            if (_conflicts != null) {
+                sm2 = (StateManagerImpl) _conflicts.get(oid);
+                if (sm2 != null)
+                    return sm2;
+            }
+        }
+
+        // at this point sm is null or deleted; check the new cache for
+        // any matches. this allows us to match app id objects to new
+        // instances without permanant oids
+        if (allowNew && _news != null && !_news.isEmpty()) {
+            sm2 = (StateManagerImpl) _news.get(oid);
+            if (sm2 != null)
+                return sm2;
+        }
+        return sm;
+    }
+
+    /**
+     * Call this method when a new state manager initializes itself.
+     */
+    public void add(StateManagerImpl sm) {
+        if (!sm.isIntercepting()) {
+            if (_untracked == null)
+                _untracked = new HashSet();
+            _untracked.add(sm);
+        }
+
+        if (!sm.isPersistent() || sm.isEmbedded()) {
+            if (_embeds == null)
+                _embeds = new ReferenceHashSet(ReferenceHashSet.WEAK);
+            _embeds.add(sm);
+            return;
+        }
+
+        // initializing new instance; put in new cache because won't have
+        // permanent oid yet
+        if (sm.isNew()) {
+            if (_news == null)
+                _news = new HashMap();
+            _news.put(sm.getId(), sm);
+            return;
+        }
+
+        // initializing persistent instance; put in main cache
+        StateManagerImpl orig = (StateManagerImpl) _main.put
+            (sm.getObjectId(), sm);
+        if (orig != null) {
+            _main.put(sm.getObjectId(), orig);
+            throw new UserException(_loc.get("dup-load", sm.getObjectId(),
+                Exceptions.toString(orig.getManagedInstance())))
+                .setFailedObject(sm.getManagedInstance());
+        }
+    }
+
+    /**
+     * Remove the given state manager from the cache when it transitions
+     * to transient.
+     */
+    public void remove(Object id, StateManagerImpl sm) {
+        // if it has a permanent oid, remove from main / conflict cache,
+        // else remove from embedded/nontrans cache, and if not there
+        // remove from new cache
+        Object orig;
+        if (sm.getObjectId() != null) {
+            orig = _main.remove(id);
+            if (orig != sm) {
+                if (orig != null)
+                    _main.put(id, orig); // put back
+                if (_conflicts != null) {
+                    orig = _conflicts.remove(id);
+                    if (orig != null && orig != sm)
+                        _conflicts.put(id, orig); // put back
+                }
+            }
+        } else if ((_embeds == null || !_embeds.remove(sm))
+            && _news != null) {
+            orig = _news.remove(id);
+            if (orig != null && orig != sm)
+                _news.put(id, orig); // put back
+        }
+
+        if (_untracked != null)
+            _untracked.remove(sm);
+    }
+
+    /**
+     * An embedded or nonpersistent managed instance has been persisted.
+     */
+    public void persist(StateManagerImpl sm) {
+        if (_embeds != null)
+            _embeds.remove(sm);
+    }
+
+    /**
+     * A new instance has just been assigned a permanent oid.
+     */
+    public void assignObjectId(Object id, StateManagerImpl sm) {
+        // if assigning oid, remove from new cache and put in primary; may
+        // not be in new cache if another new instance had same id
+        StateManagerImpl orig = null;
+        if (_news != null) {
+            orig = (StateManagerImpl) _news.remove(id);
+            if (orig != null && orig != sm)
+                _news.put(id, orig); // put back
+        }
+
+        // put in main cache, but make sure we don't replace another
+        // instance with the same oid
+        orig = (StateManagerImpl) _main.put(sm.getObjectId(), sm);
+        if (orig != null) {
+            _main.put(sm.getObjectId(), orig);
+            if (!orig.isDeleted())
+                throw new UserException(_loc.get("dup-oid-assign",
+                    sm.getObjectId(),
+                    Exceptions.toString(sm.getManagedInstance())))
+                    .setFailedObject(sm.getManagedInstance());
+
+            // same oid as deleted instance; put in conflict cache
+            if (_conflicts == null)
+                _conflicts = new HashMap();
+            _conflicts.put(sm.getObjectId(), sm);
+        }
+    }
+
+    /**
+     * A new instance has committed; recache under permanent oid.
+     */
+    public void commitNew(Object id, StateManagerImpl sm) {
+        // if the id didn't change, the instance was already assigned an
+        // id, but it could have been in conflict cache
+        StateManagerImpl orig;
+        if (sm.getObjectId() == id) {
+            orig = (_conflicts == null) ? null
+                : (StateManagerImpl) _conflicts.remove(id);
+            if (orig == sm) {
+                orig = (StateManagerImpl) _main.put(id, sm);
+                if (orig != null && !orig.isDeleted()) {
+                    _main.put(sm.getObjectId(), orig);
+                    throw new UserException(_loc.get("dup-oid-assign",
+                        sm.getObjectId(), Exceptions.toString(
+                            sm.getManagedInstance())))
+                        .setFailedObject(sm.getManagedInstance())
+                        .setFatal(true);
+                }
+            }
+            return;
+        }
+
+        // oid changed, so it must previously have been a new instance
+        // without an assigned oid.  remove it from the new cache; ok if
+        // we end up removing another instance with same id
+        if (_news != null)
+            _news.remove(id);
+
+        // and put into main cache now that id is asssigned
+        orig = (StateManagerImpl) _main.put(sm.getObjectId(), sm);
+        if (orig != null && orig != sm && !orig.isDeleted()) {
+            // put back orig and throw error
+            _main.put(sm.getObjectId(), orig);
+            throw new UserException(_loc.get("dup-oid-assign",
+                sm.getObjectId(), Exceptions.toString(sm.getManagedInstance())))
+                    .setFailedObject(sm.getManagedInstance()).setFatal(true);
+        }
+    }
+
+    /**
+     * Return a copy of all cached persistent objects.
+     */
+    public Collection copy() {
+        // proxies not included here because the state manager is always
+        // present in other caches too
+
+        int size = _main.size();
+        if (_conflicts != null)
+            size += _conflicts.size();
+        if (_news != null)
+            size += _news.size();
+        if (_embeds != null)
+            size += _embeds.size();
+        if (size == 0)
+            return Collections.EMPTY_LIST;
+
+        List copy = new ArrayList(size);
+        for (Iterator itr = _main.values().iterator(); itr.hasNext();)
+            copy.add(itr.next());
+        if (_conflicts != null && !_conflicts.isEmpty())
+            for (Iterator itr = _conflicts.values().iterator();
+                itr.hasNext();)
+                copy.add(itr.next());
+        if (_news != null && !_news.isEmpty())
+            for (Iterator itr = _news.values().iterator(); itr.hasNext();)
+                copy.add(itr.next());
+        if (_embeds != null && !_embeds.isEmpty())
+            for (Iterator itr = _embeds.iterator(); itr.hasNext();)
+                copy.add(itr.next());
+        return copy;
+    }
+
+    /**
+     * Clear the cache.
+     */
+    public void clear() {
+        _main = broker.newManagedObjectCache();
+        if (_conflicts != null)
+            _conflicts = null;
+        if (_news != null)
+            _news = null;
+        if (_embeds != null)
+            _embeds = null;
+        if (_untracked != null)
+            _untracked = null;
+    }
+
+    /**
+     * Clear new instances without permanent oids.
+     */
+    public void clearNew() {
+        if (_news != null)
+            _news = null;
+    }
+
+    void dirtyCheck() {
+        if (_untracked == null)
+            return;
+
+        for (Iterator iter = _untracked.iterator(); iter.hasNext(); )
+            ((StateManagerImpl) iter.next()).dirtyCheck();
+    }
+}

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ObjectIdStateManager.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ObjectIdStateManager.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ObjectIdStateManager.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ObjectIdStateManager.java Wed Nov 21 09:40:54 2007
@@ -309,8 +309,8 @@
         return _owner;
     }
 
-    public ValueMetaData getOwnerMetaData() {
-        return _vmd;
+    public int getOwnerIndex() {
+        return _vmd.getFieldMetaData().getIndex();
     }
 
     public boolean isEmbedded() {

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPASavepoint.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPASavepoint.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPASavepoint.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPASavepoint.java Wed Nov 21 09:40:54 2007
@@ -18,6 +18,7 @@
  */
 package org.apache.openjpa.kernel;
 
+import java.io.Serializable;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -30,7 +31,7 @@
  * @author Steve Kim
  * @since 0.3.4
  */
-public class OpenJPASavepoint {
+public class OpenJPASavepoint implements Serializable {
 
     private final Broker _broker;
     private final String _name;

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPAStateManager.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPAStateManager.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPAStateManager.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPAStateManager.java Wed Nov 21 09:40:54 2007
@@ -102,9 +102,11 @@
     public OpenJPAStateManager getOwner();
 
     /**
-     * Return the owning value.
+     * Return the owning value's field index
+     *
+     * @since 1.1.0
      */
-    public ValueMetaData getOwnerMetaData();
+    public int getOwnerIndex();
 
     /**
      * Return true if this instance has an owner, meaning it is an embedded

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SaveFieldManager.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SaveFieldManager.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SaveFieldManager.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SaveFieldManager.java Wed Nov 21 09:40:54 2007
@@ -18,13 +18,16 @@
  */
 package org.apache.openjpa.kernel;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
 import java.util.BitSet;
 import java.util.Collection;
 import java.util.Date;
 import java.util.Map;
 
 import org.apache.openjpa.enhance.PersistenceCapable;
-import org.apache.openjpa.enhance.Reflection;
 import org.apache.openjpa.meta.FieldMetaData;
 import org.apache.openjpa.meta.JavaTypes;
 import org.apache.openjpa.util.ProxyManager;
@@ -35,13 +38,14 @@
  * @author Abe White
  */
 public class SaveFieldManager
-    extends ClearFieldManager {
+    extends ClearFieldManager
+    implements Serializable {
 
     private final StateManagerImpl _sm;
     private final BitSet _unloaded;
     private BitSet _saved = null;
     private int[] _copyField = null;
-    private PersistenceCapable _state = null;
+    private transient PersistenceCapable _state = null;
 
     // used to track field value during store/fetch cycle
     private Object _field = null;
@@ -140,7 +144,7 @@
         if (_copyField == null)
             _copyField = new int[1];
         _copyField[0] = field;
-        _state.pcCopyFields(_sm.getPersistenceCapable(), _copyField);
+        getState().pcCopyFields(_sm.getPersistenceCapable(), _copyField);
         return false;
     }
 
@@ -164,7 +168,7 @@
         if (_copyField == null)
             _copyField = new int[1];
         _copyField[0] = field;
-        _sm.getPersistenceCapable().pcCopyFields(_state, _copyField);
+        _sm.getPersistenceCapable().pcCopyFields(getState(), _copyField);
         return false;
     }
 
@@ -177,12 +181,12 @@
         // if the field is not available, assume that it has changed.
         if (_saved == null || !_saved.get(field))
             return false;
-        if (!(_state.pcGetStateManager() instanceof StateManagerImpl))
+        if (!(getState().pcGetStateManager() instanceof StateManagerImpl))
             return false;
 
-        StateManagerImpl sm = (StateManagerImpl) _state.pcGetStateManager();
+        StateManagerImpl sm = (StateManagerImpl) getState().pcGetStateManager();
         SingleFieldManager single = new SingleFieldManager(sm, sm.getBroker());
-        sm.provideField(_state, single, field);
+        sm.provideField(getState(), single, field);
         Object old = single.fetchObjectField(field);
         return current == old || current != null && current.equals(old);
     }
@@ -227,4 +231,15 @@
             _saved.clear(field);
 		}
 	}
+
+    private void writeObject(ObjectOutputStream oos) throws IOException {
+        oos.defaultWriteObject();
+        _sm.writePC(oos, _state);
+    }
+
+    private void readObject(ObjectInputStream ois)
+        throws IOException, ClassNotFoundException {
+        ois.defaultReadObject();
+        _state = _sm.readPC(ois);
+    }
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SavepointFieldManager.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SavepointFieldManager.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SavepointFieldManager.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SavepointFieldManager.java Wed Nov 21 09:40:54 2007
@@ -18,6 +18,10 @@
  */
 package org.apache.openjpa.kernel;
 
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
 import java.util.BitSet;
 import java.util.Collection;
 import java.util.Date;
@@ -37,7 +41,8 @@
  * @since 0.3.4
  */
 class SavepointFieldManager
-    extends ClearFieldManager {
+    extends ClearFieldManager
+    implements Serializable {
 
     private static final Localizer _loc = Localizer.forPackage
         (SavepointFieldManager.class);
@@ -47,7 +52,7 @@
     private final BitSet _dirty;
     private final BitSet _flush;
     private final PCState _state;
-    private PersistenceCapable _copy;
+    private transient PersistenceCapable _copy;
 
     private final Object _version;
     private final Object _loadVersion;
@@ -227,4 +232,15 @@
         if (curVal != null && _field == null)
             throw new InternalException(_loc.get("no-savepoint-copy", fmd));
 	}
+
+    private void writeObject(ObjectOutputStream oos) throws IOException {
+        oos.defaultWriteObject();
+        _sm.writePC(oos, _copy);
+    }
+
+    private void readObject(ObjectInputStream ois)
+        throws IOException, ClassNotFoundException {
+        ois.defaultReadObject();
+        _copy = _sm.readPC(ois);
+    }
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/SingleFieldManager.java Wed Nov 21 09:40:54 2007
@@ -20,6 +20,7 @@
 
 import java.io.IOException;
 import java.io.ObjectOutput;
+import java.io.Serializable;
 import java.sql.Timestamp;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -50,7 +51,8 @@
  * @author Abe White
  */
 class SingleFieldManager
-    extends TransferFieldManager {
+    extends TransferFieldManager
+    implements Serializable {
 
     private static final Localizer _loc = Localizer.forPackage
         (SingleFieldManager.class);
@@ -235,7 +237,7 @@
 
         StateManagerImpl sm = _broker.getStateManagerImpl(obj, false);
         if (sm != null && sm.getOwner() == _sm
-            && sm.getOwnerMetaData() == vmd)
+            && sm.getOwnerIndex() == vmd.getFieldMetaData().getIndex())
             sm.release(true);
     }
 
@@ -380,7 +382,8 @@
         // delete if unknowned or this isn't an embedded field or if owned by us
         StateManagerImpl sm = _broker.getStateManagerImpl(obj, false);
         if (sm != null && (sm.getOwner() == null || !vmd.isEmbeddedPC()
-            || (sm.getOwner() == _sm && sm.getOwnerMetaData() == vmd)))
+            || (sm.getOwner() == _sm
+            && sm.getOwnerIndex() == vmd.getFieldMetaData().getIndex())))
             _broker.delete(sm.getManagedInstance(), sm, call);
     }
 

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java Wed Nov 21 09:40:54 2007
@@ -19,7 +19,11 @@
 package org.apache.openjpa.kernel;
 
 import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
 import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -77,7 +81,7 @@
  * @author Abe White
  */
 public class StateManagerImpl
-    implements OpenJPAStateManager {
+    implements OpenJPAStateManager, Serializable {
 
     public static final int LOAD_FGS = 0;
     public static final int LOAD_ALL = 1;
@@ -105,8 +109,8 @@
         (StateManagerImpl.class);
 
     // information about the instance
-    private PersistenceCapable _pc = null;
-    private ClassMetaData _meta = null;
+    private transient PersistenceCapable _pc = null;
+    private transient ClassMetaData _meta = null;
     private BitSet _loaded = null;
     private BitSet _dirty = null;
     private BitSet _flush = null;
@@ -121,7 +125,7 @@
     private Object _oid = null;
 
     // the managing persistence manager and lifecycle state
-    private final BrokerImpl _broker;
+    private transient BrokerImpl _broker; // this is serialized specially
     private PCState _state = PCState.TRANSIENT;
 
     // the current and last loaded version indicators, and the lock object
@@ -142,7 +146,7 @@
 
     // information about the owner of this instance, if it is embedded
     private StateManagerImpl _owner = null;
-    private ValueMetaData _ownerMeta = null;
+    private int _ownerIndex = -1;
 
     /**
      * Constructor; supply id, type metadata, and owning persistence manager.
@@ -163,7 +167,7 @@
      */
     void setOwner(StateManagerImpl owner, ValueMetaData ownerMeta) {
         _owner = owner;
-        _ownerMeta = ownerMeta;
+        _ownerIndex = ownerMeta.getFieldMetaData().getIndex();
     }
 
     /**
@@ -371,7 +375,7 @@
         // care of checking if the DFG is loaded, making sure version info
         // is loaded, etc
         int lockLevel = calculateLockLevel(active, forWrite, fetch);
-        boolean ret = loadFields(fields, fetch, lockLevel, sdata, forWrite);
+        boolean ret = loadFields(fields, fetch, lockLevel, sdata);
         obtainLocks(active, forWrite, lockLevel, fetch, sdata);
         return ret;
     }
@@ -395,8 +399,8 @@
         return _owner;
     }
 
-    public ValueMetaData getOwnerMetaData() {
-        return _ownerMeta;
+    public int getOwnerIndex() {
+        return _ownerIndex;
     }
 
     public boolean isEmbedded() {
@@ -594,9 +598,9 @@
         
         // Throw exception if field already has a value assigned.
         // @GeneratedValue overrides POJO initial values and setter methods
-        if (!isDefaultValue(field) && !fmd.isValueGenerated())
+        if (!fmd.isValueGenerated() && !isDefaultValue(field))
             throw new InvalidStateException(_loc.get(
-                    "existing-value-override-excep", fmd.getFullName(false)));
+                "existing-value-override-excep", fmd.getFullName(false)));
 
         // for primary key fields, assign the object id and recache so that
         // to the user, so it looks like the oid always matches the pk fields
@@ -1318,7 +1322,16 @@
     // Implementation of StateManager interface
     ////////////////////////////////////////////
 
+    /**
+     * @return whether or not unloaded fields should be closed.
+     */
     public boolean serializing() {
+        // if the broker is in the midst of a serialization, then no special
+        // handling should be performed on the instance, and no subsequent
+        // load should happen
+        if (_broker.isSerializing())
+            return false;
+
         try {
             if (_meta.isDetachable())
                 return DetachManager.preSerialize(this);
@@ -1510,8 +1523,7 @@
 
             if (isEmbedded()) {
                 // notify owner of change
-                _owner.dirty(_ownerMeta.getFieldMetaData().getIndex(),
-                    Boolean.TRUE, loadFetchGroup);
+                _owner.dirty(_ownerIndex, Boolean.TRUE, loadFetchGroup);
             }
 
             // is this a direct mutation of an sco field?
@@ -2862,7 +2874,7 @@
      * Return true if any data is loaded, false otherwise.
      */
     boolean loadFields(BitSet fields, FetchConfiguration fetch, int lockLevel,
-        Object sdata, boolean forWrite) {
+        Object sdata) {
         // can't load version field from store
         if (fields != null) {
             FieldMetaData vfield = _meta.getVersionField();
@@ -2956,7 +2968,7 @@
         // call this method even if there are no unloaded fields; loadFields
         // takes care of things like loading version info and setting PC flags
         try {
-            loadFields(fields, fetch, lockLevel, null, forWrite);
+            loadFields(fields, fetch, lockLevel, null);
         } finally {
             if (lfgAdded)
                 fetch.removeFetchGroup(lfg);
@@ -3154,4 +3166,66 @@
 		// manager lock and broker lock being obtained in different orders
 		_broker.unlock ();
 	}
+
+    private void writeObject(ObjectOutputStream oos) throws IOException {
+        oos.writeObject(_broker);
+        oos.defaultWriteObject();
+        oos.writeObject(_meta.getDescribedType());
+        writePC(oos, _pc);
+    }
+
+    /**
+     * Write <code>pc</code> to <code>oos</code>, handling internal-form
+     * serialization. <code>pc</code> must be of the same type that this
+     * state manager manages.
+     *
+     * @since 1.1.0
+     */
+    void writePC(ObjectOutputStream oos, PersistenceCapable pc)
+        throws IOException {
+        if (!Serializable.class.isAssignableFrom(_meta.getDescribedType()))
+            throw new NotSerializableException(
+                _meta.getDescribedType().getName());
+
+        oos.writeObject(pc);
+    }
+
+    private void readObject(ObjectInputStream in)
+        throws IOException, ClassNotFoundException {
+        _broker = (BrokerImpl) in.readObject();
+        in.defaultReadObject();
+
+        // we need to store the class before the pc instance so that we can
+        // create _meta before calling readPC(), which relies on _meta being
+        // non-null when reconstituting ReflectingPC instances. Sadly, this
+        // penalizes the serialization footprint of non-ReflectingPC SMs also.
+        Class managedType = (Class) in.readObject();
+        _meta = _broker.getConfiguration().getMetaDataRepositoryInstance()
+            .getMetaData(managedType, null, true);
+
+        _pc = readPC(in);
+    }
+
+    /**
+     * Converts the deserialized <code>o</code> to a {@link PersistenceCapable}
+     * instance appropriate for storing in <code>_pc</code>.
+     *
+     * @since 1.1.0
+     */
+    PersistenceCapable readPC(ObjectInputStream in)
+        throws ClassNotFoundException, IOException {
+        Object o = in.readObject();
+
+        if (o == null)
+            return null;
+
+        PersistenceCapable pc;
+        if (!(o instanceof PersistenceCapable))
+            pc = ImplHelper.toPersistenceCapable(o, this);
+        else
+            pc = (PersistenceCapable) o;
+
+        pc.pcReplaceStateManager(this);
+        return pc;
+    }
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/GeneralException.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/GeneralException.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/GeneralException.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/GeneralException.java Wed Nov 21 09:40:54 2007
@@ -47,6 +47,10 @@
         super(msg, cause);
     }
 
+    public GeneralException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+
     public int getType() {
         return GENERAL;
     }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ImplHelper.java Wed Nov 21 09:40:54 2007
@@ -304,6 +304,11 @@
         }
     }
 
+    public static void registerPersistenceCapable(
+        ReflectingPersistenceCapable pc) {
+        _unenhancedInstanceMap.put(pc.getManagedInstance(), pc);
+    }
+
     /**
      * @return the user-visible representation of <code>o</code>.
      * @since 1.0.0

Modified: openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties (original)
+++ openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties Wed Nov 21 09:40:54 2007
@@ -390,3 +390,9 @@
     openjpa.Multithreaded property to true to override the default behavior.
 no-saved-fields: No state snapshot is available for "{0}", but this instance \
     uses state-comparison for dirty detection.
+cant-serialize-flushed-broker: Serialization not allowed once a broker has \
+    been flushed.
+cant-serialize-pessimistic-broker: Serialization not allowed for brokers with \
+    an active datastore (pessimistic) transaction.
+cant-serialize-connected-broker: Serialization not allowed for brokers with \
+    an active connection to the database.
\ No newline at end of file

Added: openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/enhance/TestPCSubclassNameConversion.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/enhance/TestPCSubclassNameConversion.java?rev=597155&view=auto
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/enhance/TestPCSubclassNameConversion.java (added)
+++ openjpa/trunk/openjpa-kernel/src/test/java/org/apache/openjpa/enhance/TestPCSubclassNameConversion.java Wed Nov 21 09:40:54 2007
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.enhance;
+
+import junit.framework.TestCase;
+
+public class TestPCSubclassNameConversion
+    extends TestCase {
+
+    public void testPCSubclassNameConversion() {
+        String name = PCEnhancer.toPCSubclassName(Object.class);
+        assertTrue(PCEnhancer.isPCSubclassName(name));
+        assertEquals(Object.class.getName(),
+            PCEnhancer.toManagedTypeName(name));
+    }
+}
\ No newline at end of file

Modified: openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java (original)
+++ openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ConfigurationImpl.java Wed Nov 21 09:40:54 2007
@@ -648,13 +648,15 @@
         Configurations.removeProperty("properties", remaining);
         
         // now warn if there are any remaining properties that there
-        // is an unhandled prop
+        // is an unhandled prop, and remove the unknown properties
         Map.Entry entry;
         for (Iterator itr = remaining.entrySet().iterator(); itr.hasNext();) {
             entry = (Map.Entry) itr.next();
-            if (entry.getKey() != null)
-                warnInvalidProperty((String) entry.getKey());
-            ser &= entry.getValue() instanceof Serializable;
+            Object key = entry.getKey();
+            if (key != null) {
+                warnInvalidProperty((String) key);
+                map.remove(key);
+            }
         }
 
         // cache properties

Modified: openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ReferenceHashSet.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ReferenceHashSet.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ReferenceHashSet.java (original)
+++ openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/ReferenceHashSet.java Wed Nov 21 09:40:54 2007
@@ -49,7 +49,11 @@
      */
     public static final int WEAK = 2;
 
-    private static final Object DUMMY_VAL = new Object();
+    private static final Object DUMMY_VAL = new Serializable() {
+        public String toString() {
+            return ReferenceHashSet.class.getName() + ".DUMMY_VAL";
+        }
+    };
 
     private final Set _set;
 

Modified: openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/AbstractConcurrentEventManager.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/AbstractConcurrentEventManager.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/AbstractConcurrentEventManager.java (original)
+++ openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/concurrent/AbstractConcurrentEventManager.java Wed Nov 21 09:40:54 2007
@@ -18,6 +18,7 @@
  */
 package org.apache.openjpa.lib.util.concurrent;
 
+import java.io.Serializable;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
@@ -35,7 +36,8 @@
  *
  * @author Abe White
  */
-public abstract class AbstractConcurrentEventManager implements EventManager {
+public abstract class AbstractConcurrentEventManager
+    implements EventManager, Serializable {
 
     private static Exception[] EMPTY_EXCEPTIONS = new Exception[0];
 

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/AbstractUnenhancedClassTest.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/AbstractUnenhancedClassTest.java?rev=597155&r1=597154&r2=597155&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/AbstractUnenhancedClassTest.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/enhance/AbstractUnenhancedClassTest.java Wed Nov 21 09:40:54 2007
@@ -102,6 +102,7 @@
         PersistenceCapable pc = PCRegistry.newInstance(
             getUnenhancedClass(), null, false);
         assertNotNull(pc);
+        assertEquals(pc.getClass(), PCRegistry.getPCType(getUnenhancedClass()));
     }
 
     public void testClearingOnSubtypeInstance() {