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.

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@597155 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Patrick Linskey 2007-11-21 17:40:54 +00:00
parent fad6f35201
commit 5018dfaa88
41 changed files with 1879 additions and 428 deletions

View File

@ -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;
@ -134,5 +137,10 @@ public abstract class AbstractJDBCSavepointManager
AbstractJDBCSavepointManager.this.setDataStore(this);
super.save(states);
}
private void writeObject(ObjectOutputStream out)
throws IOException {
throw new NotSerializableException();
}
}
}

View File

@ -582,8 +582,8 @@ public class EmbedFieldStrategy
return _owner;
}
public ValueMetaData getOwnerMetaData() {
return _vmd;
public int getOwnerIndex() {
return _vmd.getFieldMetaData().getIndex();
}
public boolean isEmbedded() {

View File

@ -245,6 +245,36 @@ public class PCEnhancer {
+ cls.getName().replace('.', '$') + "$pcsubclass";
}
/**
* 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.
*/
@ -2718,6 +2748,9 @@ public class PCEnhancer {
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();

View File

@ -121,6 +121,17 @@ public class PCRegistry {
return (meta.pc == null) ? null : meta.pc.pcNewInstance(sm, oid, clear);
}
/**
* 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.

View File

@ -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.meta.ClassMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.FieldMetaData;
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.FieldMetaData;
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 @@ import org.apache.openjpa.kernel.StateManagerImpl;
* @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 class ReflectingPersistenceCapable
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 class ReflectingPersistenceCapable
}
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 @@ public class ReflectingPersistenceCapable
// ##### 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);
@ -350,4 +371,17 @@ public class ReflectingPersistenceCapable
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);
}
}

View File

@ -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 @@ import org.apache.openjpa.meta.MetaDataDefaults;
* @nojavadoc
*/
public class LifecycleEventManager
implements CallbackModes {
implements CallbackModes, Serializable {
private static final Exception[] EMPTY_EXCEPTIONS = new Exception[0];

View File

@ -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.datacache.DataCacheStoreManager;
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 @@ public abstract class AbstractBrokerFactory
/**
* 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 @@ public abstract class AbstractBrokerFactory
* 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 @@ public abstract class AbstractBrokerFactory
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 @@ public abstract class AbstractBrokerFactory
}
}
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 @@ public abstract class AbstractBrokerFactory
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
@ -756,6 +779,15 @@ public abstract class AbstractBrokerFactory
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());
}
/**
* Simple synchronization listener to remove completed transactions
* from our cache.

View File

@ -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 @@ import org.apache.openjpa.util.UserException;
* @author Abe White
*/
public class BrokerImpl
implements Broker, FindCallbacks, Cloneable {
implements Broker, FindCallbacks, Cloneable, Serializable {
/**
* Incremental flush.
@ -132,30 +135,35 @@ public class BrokerImpl
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 @@ public class BrokerImpl
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 @@ public class BrokerImpl
// (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 @@ public class BrokerImpl
// 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 @@ public class BrokerImpl
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 @@ public class BrokerImpl
* 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 @@ public class BrokerImpl
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 @@ public class BrokerImpl
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 @@ public class BrokerImpl
// 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 @@ public class BrokerImpl
// 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 @@ public class BrokerImpl
}
}
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 @@ public class BrokerImpl
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 @@ public class BrokerImpl
// 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 @@ public class BrokerImpl
return (sm == null) ? null : 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"));
}
try {
_isSerializing = true;
out.writeObject(_factory.getPoolKey());
out.defaultWriteObject();
} finally {
_isSerializing = false;
}
} finally {
unlock();
}
}
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);
// re-initialize the lock if needed.
setMultithreaded(_multithreaded);
if (isActive() && _runtime instanceof LocalManagedRuntime)
((LocalManagedRuntime) _runtime).begin();
}
/**
* Cache of managed objects.
* Whether or not this broker is in the midst of being serialized.
*
* @since 1.1.0
*/
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());
}
}
/**
* 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);
}
}
/**
* 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 = 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;
}
private void dirtyCheck() {
if (_untracked == null)
return;
for (Iterator iter = _untracked.iterator(); iter.hasNext(); )
((StateManagerImpl) iter.next()).dirtyCheck();
}
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;

View File

@ -171,8 +171,7 @@ class DetachedStateAttachStrategy
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];

View File

@ -145,7 +145,7 @@ public class DetachedStateManager
}
}
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 @@ public class DetachedStateManager
throw new UnsupportedOperationException();
}
public ValueMetaData getOwnerMetaData() {
public int getOwnerIndex() {
throw new UnsupportedOperationException();
}

View File

@ -88,8 +88,8 @@ public class DetachedValueStateManager
return null;
}
public ValueMetaData getOwnerMetaData() {
return null;
public int getOwnerIndex() {
throw new UnsupportedOperationException();
}
public boolean isEmbedded() {

View File

@ -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();
}
}

View File

@ -309,8 +309,8 @@ public class ObjectIdStateManager
return _owner;
}
public ValueMetaData getOwnerMetaData() {
return _vmd;
public int getOwnerIndex() {
return _vmd.getFieldMetaData().getIndex();
}
public boolean isEmbedded() {

View File

@ -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 @@ import java.util.Map;
* @author Steve Kim
* @since 0.3.4
*/
public class OpenJPASavepoint {
public class OpenJPASavepoint implements Serializable {
private final Broker _broker;
private final String _name;

View File

@ -102,9 +102,11 @@ public interface OpenJPAStateManager
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

View File

@ -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 @@ import org.apache.openjpa.util.ProxyManager;
* @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 @@ public class SaveFieldManager
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 @@ public class SaveFieldManager
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 @@ public class SaveFieldManager
// 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 @@ public class SaveFieldManager
_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);
}
}

View File

@ -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 @@ import org.apache.openjpa.util.ProxyManager;
* @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 @@ class SavepointFieldManager
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 @@ class SavepointFieldManager
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);
}
}

View File

@ -20,6 +20,7 @@ package org.apache.openjpa.kernel;
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 @@ import org.apache.openjpa.util.UserException;
* @author Abe White
*/
class SingleFieldManager
extends TransferFieldManager {
extends TransferFieldManager
implements Serializable {
private static final Localizer _loc = Localizer.forPackage
(SingleFieldManager.class);
@ -235,7 +237,7 @@ class SingleFieldManager
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 @@ class SingleFieldManager
// 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);
}

View File

@ -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 @@ import serp.util.Numbers;
* @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 @@ public class StateManagerImpl
(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 @@ public class StateManagerImpl
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 @@ public class StateManagerImpl
// 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 @@ public class StateManagerImpl
*/
void setOwner(StateManagerImpl owner, ValueMetaData ownerMeta) {
_owner = owner;
_ownerMeta = ownerMeta;
_ownerIndex = ownerMeta.getFieldMetaData().getIndex();
}
/**
@ -371,7 +375,7 @@ public class StateManagerImpl
// 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 @@ public class StateManagerImpl
return _owner;
}
public ValueMetaData getOwnerMetaData() {
return _ownerMeta;
public int getOwnerIndex() {
return _ownerIndex;
}
public boolean isEmbedded() {
@ -594,9 +598,9 @@ public class StateManagerImpl
// 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 @@ public class StateManagerImpl
// 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 @@ public class StateManagerImpl
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 @@ public class StateManagerImpl
* 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 @@ public class StateManagerImpl
// 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 @@ public class StateManagerImpl
// 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;
}
}

View File

@ -47,6 +47,10 @@ public class GeneralException
super(msg, cause);
}
public GeneralException(String msg, Throwable cause) {
super(msg, cause);
}
public int getType() {
return GENERAL;
}

View File

@ -304,6 +304,11 @@ public class ImplHelper {
}
}
public static void registerPersistenceCapable(
ReflectingPersistenceCapable pc) {
_unenhancedInstanceMap.put(pc.getManagedInstance(), pc);
}
/**
* @return the user-visible representation of <code>o</code>.
* @since 1.0.0

View File

@ -390,3 +390,9 @@ multi-threaded-access: Multiple concurrent threads attempted to access a \
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.

View File

@ -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));
}
}

View File

@ -648,13 +648,15 @@ public class ConfigurationImpl
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

View File

@ -49,7 +49,11 @@ public class ReferenceHashSet implements Set, Serializable {
*/
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;

View File

@ -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 @@ import org.apache.openjpa.lib.util.EventManager;
*
* @author Abe White
*/
public abstract class AbstractConcurrentEventManager implements EventManager {
public abstract class AbstractConcurrentEventManager
implements EventManager, Serializable {
private static Exception[] EMPTY_EXCEPTIONS = new Exception[0];

View File

@ -102,6 +102,7 @@ public abstract class AbstractUnenhancedClassTest
PersistenceCapable pc = PCRegistry.newInstance(
getUnenhancedClass(), null, false);
assertNotNull(pc);
assertEquals(pc.getClass(), PCRegistry.getPCType(getUnenhancedClass()));
}
public void testClearingOnSubtypeInstance() {

View File

@ -0,0 +1,446 @@
/*
* 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.EntityManager;
import org.apache.openjpa.event.AbstractLifecycleListener;
import org.apache.openjpa.event.AbstractTransactionListener;
import org.apache.openjpa.event.LifecycleEvent;
import org.apache.openjpa.event.TransactionEvent;
import org.apache.openjpa.persistence.InvalidStateException;
import org.apache.openjpa.persistence.JPAFacadeHelper;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory;
import org.apache.openjpa.persistence.jdbc.JDBCFetchPlan;
import org.apache.openjpa.persistence.jdbc.JoinSyntax;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
/*
* To test:
* - managed transactions
* - converting non-enhanced classes to enhanced subclasses
* (maybe an ugly ThreadLocal, maybe through PCData?)
*/
public abstract class AbstractBrokerSerializationTest<T>
extends SingleEMFTestCase {
private static LifeListener deserializedLifeListener;
private static int testGlobalRefreshCount = 0;
private static TxListener deserializedTxListener;
private static int testGlobalBeginCount = 0;
private Object id;
public void setUp() {
testGlobalRefreshCount = 0;
deserializedLifeListener = null;
testGlobalBeginCount = 0;
deserializedTxListener = null;
setUp(getManagedType(), getSecondaryType(), CLEAR_TABLES,
"openjpa.EntityManagerFactoryPool", "true");
T e = newManagedInstance();
OpenJPAEntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(e);
em.getTransaction().commit();
id = em.getObjectId(e);
em.close();
}
@Override
public void tearDown() throws Exception {
super.tearDown();
testGlobalRefreshCount = 0;
deserializedLifeListener = null;
testGlobalBeginCount = 0;
deserializedTxListener = null;
}
public void testEmptyBrokerSerialization() {
OpenJPAEntityManager em = emf.createEntityManager();
OpenJPAEntityManager em2 = deserializeEM(serialize(em));
assertTrue(em != em2);
assertTrue(
JPAFacadeHelper.toBroker(em) != JPAFacadeHelper.toBroker(em2));
assertSame(em.getEntityManagerFactory(), em2.getEntityManagerFactory());
assertSame(em2, JPAFacadeHelper.toBroker(em2)
.getUserObject(JPAFacadeHelper.EM_KEY));
em.close();
assertTrue(em2.isOpen());
em2.close();
}
public void testNontransactionalBrokerSerialization() {
OpenJPAEntityManager em = emf.createEntityManager();
T e = em.find(getManagedType(), id);
OpenJPAEntityManager em2 = deserializeEM(serialize(em));
assertFalse(em2.getTransaction().isActive());
assertFalse(em2.contains(e));
assertEquals(1*graphSize(), em2.getManagedObjects().size());
T e2 = em2.find(getManagedType(), id);
assertEquals(em.getObjectId(e), em2.getObjectId(e2));
em.close();
em2.close();
}
public void testUnflushedOptimisticTxBrokerSerialization() {
OpenJPAEntityManager em = emf.createEntityManager();
T e = em.find(getManagedType(), id);
OpenJPAEntityManager em2 = null;
OpenJPAEntityManager em3 = null;
try {
em.getTransaction().begin();
modifyInstance(e);
T newe = newManagedInstance();
em.persist(newe);
em2 = deserializeEM(serialize(em));
assertTrue(em2.getTransaction().isActive());
assertFalse(em2.contains(e));
T e2 = em2.find(getManagedType(), id);
assertEquals(em.getObjectId(e), em2.getObjectId(e2));
assertEquals("modified", getModifiedValue(e2));
em.getTransaction().rollback();
assertTrue(em2.getTransaction().isActive());
em2.getTransaction().commit();
em3 = emf.createEntityManager();
T e3 = em3.find(getManagedType(), id);
assertEquals(getModifiedValue(e2), getModifiedValue(e3));
assertTrue(1 < ((Number) em3.createQuery("select count(o) from "
+ getManagedType().getName() + " o").getSingleResult())
.intValue());
} finally {
close(em);
close(em2);
close(em3);
}
}
public void testFlushedOptimisticTxBrokerSerialization() {
OpenJPAEntityManager em = emf.createEntityManager();
T e = em.find(getManagedType(), id);
em.getTransaction().begin();
modifyInstance(e);
em.flush();
try {
serialize(em);
} catch (InvalidStateException ise) {
// expected
assertTrue(ise.getMessage().contains("flushed"));
} finally {
em.getTransaction().rollback();
em.close();
}
}
public void testConnectedOptimisticTxBrokerSerialization() {
Map m = new HashMap();
m.put("openjpa.ConnectionRetainMode", "always");
OpenJPAEntityManager em = emf.createEntityManager(m);
try {
serialize(em);
} catch (InvalidStateException ise) {
// expected
assertTrue(ise.getMessage().contains("connected"));
} finally {
em.close();
}
}
public void testEmptyPessimisticTxBrokerSerialization() {
Map m = new HashMap();
m.put("openjpa.Optimistic", "false");
OpenJPAEntityManager em = emf.createEntityManager(m);
em.getTransaction().begin();
try {
serialize(em);
fail("should not be able to serialize");
} catch (InvalidStateException ise) {
// expected
assertTrue(ise.getMessage().contains("datastore (pessimistic)"));
} finally {
em.getTransaction().rollback();
em.close();
}
}
public void testNonEmptyPessimisticTxBrokerSerialization() {
Map m = new HashMap();
m.put("openjpa.Optimistic", "false");
OpenJPAEntityManager em = emf.createEntityManager(m);
T e = em.find(getManagedType(), id);
em.getTransaction().begin();
try {
serialize(em);
fail("should not be able to serialize");
} catch (InvalidStateException ise) {
// expected
assertTrue(ise.getMessage().contains("datastore (pessimistic)"));
} finally {
em.getTransaction().rollback();
em.close();
}
}
public void testFetchConfigurationMutations() {
OpenJPAEntityManager em = emf.createEntityManager();
JDBCFetchPlan plan = (JDBCFetchPlan) em.getFetchPlan();
assertNotEquals(17, plan.getLockTimeout());
assertNotEquals(JoinSyntax.TRADITIONAL, plan.getJoinSyntax());
plan.setLockTimeout(17);
plan.setJoinSyntax(JoinSyntax.TRADITIONAL);
OpenJPAEntityManager em2 = deserializeEM(serialize(em));
JDBCFetchPlan plan2 = (JDBCFetchPlan) em2.getFetchPlan();
assertEquals(17, plan2.getLockTimeout());
assertEquals(JoinSyntax.TRADITIONAL, plan2.getJoinSyntax());
}
public void testInMemorySavepointsWithNewInstances() {
emf.close();
OpenJPAEntityManagerFactory emf = createEMF(
getManagedType(), getSecondaryType(),
"openjpa.EntityManagerFactoryPool", "true",
"openjpa.SavepointManager", "in-mem");
OpenJPAEntityManager em = emf.createEntityManager();
OpenJPAEntityManager em2 = null;
try {
em.getTransaction().begin();
T t = newManagedInstance();
Object orig = getModifiedValue(t);
em.persist(t);
Object id = em.getObjectId(t);
em.setSavepoint("foo");
modifyInstance(t);
assertNotEquals(orig, getModifiedValue(t));
em2 = deserializeEM(serialize(em));
T t2 = em2.find(getManagedType(), id);
assertNotEquals(orig, getModifiedValue(t2));
em.rollbackToSavepoint("foo");
assertEquals(orig, getModifiedValue(t));
em2.rollbackToSavepoint("foo");
assertEquals(orig, getModifiedValue(t2));
} finally {
close(em);
close(em2);
}
}
public void testInMemorySavepointsWithModifiedInstances() {
emf.close();
OpenJPAEntityManagerFactory emf = createEMF(
getManagedType(), getSecondaryType(),
"openjpa.EntityManagerFactoryPool", "true",
"openjpa.SavepointManager", "in-mem");
OpenJPAEntityManager em = emf.createEntityManager();
OpenJPAEntityManager em2 = null;
try {
em.getTransaction().begin();
T t = em.find(getManagedType(), id);
Object orig = getModifiedValue(t);
em.setSavepoint("foo");
modifyInstance(t);
assertNotEquals(orig, getModifiedValue(t));
em2 = deserializeEM(serialize(em));
T t2 = em2.find(getManagedType(), id);
assertNotEquals(orig, getModifiedValue(t2));
em.rollbackToSavepoint("foo");
assertEquals(orig, getModifiedValue(t));
em2.rollbackToSavepoint("foo");
assertEquals(orig, getModifiedValue(t2));
} finally {
close(em);
close(em2);
}
}
public void testEventManagers() {
TxListener txListener = new TxListener();
emf.addTransactionListener(txListener);
LifeListener lifeListener = new LifeListener();
emf.addLifecycleListener(lifeListener, null);
OpenJPAEntityManager em = emf.createEntityManager();
T t = em.find(getManagedType(), id);
assertEquals(0, lifeListener.refreshCount);
em.refresh(t);
assertEquals(1*graphSize(), lifeListener.refreshCount);
em.getTransaction().begin();
em.getTransaction().commit();
em.getTransaction().begin();
em.getTransaction().commit();
assertEquals(2, txListener.beginCount);
OpenJPAEntityManager em2 = deserializeEM(serialize(em));
assertNotNull(deserializedLifeListener);
assertEquals(1* graphSize(),
deserializedLifeListener.refreshCount);
assertNotSame(lifeListener, deserializedLifeListener);
T t2 = em2.find(getManagedType(), id);
em2.refresh(t2);
assertEquals(2* graphSize(),
deserializedLifeListener.refreshCount);
// if this is 3*refreshMultiplier(), that means that there are
// extra registered listeners
assertEquals(2* graphSize(), testGlobalRefreshCount);
assertNotNull(deserializedTxListener);
assertEquals(2, deserializedTxListener.beginCount);
assertNotSame(txListener, deserializedTxListener);
em2.getTransaction().begin();
em2.getTransaction().rollback();
assertEquals(3, deserializedTxListener.beginCount);
// if this is 4, that means that there are extra registered listeners
assertEquals(3, testGlobalBeginCount);
}
byte[] serialize(Object o) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.flush();
return baos.toByteArray();
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
OpenJPAEntityManager deserializeEM(byte[] bytes) {
return (OpenJPAEntityManager) deserialize(bytes);
}
private Object deserialize(byte[] bytes) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (IOException ioe) {
throw new RuntimeException(ioe);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
void close(EntityManager em) {
if (em != null && em.isOpen() && em.getTransaction().isActive())
em.getTransaction().rollback();
if (em != null && em.isOpen())
em.close();
}
protected abstract Class<T> getManagedType();
protected abstract T newManagedInstance();
protected abstract void modifyInstance(T t);
protected abstract Object getModifiedValue(T t);
/**
* The number of instances in the graph created
* by {@link #newManagedInstance()} of type T.
*/
protected int graphSize() {
return 1;
}
/**
* An additional type that must be available in this PC. May be null.
*/
protected Class getSecondaryType() {
return null;
}
private static class TxListener
extends AbstractTransactionListener
implements Serializable {
private int beginCount = 0;
public TxListener() {
}
@Override
public void afterBegin(TransactionEvent event) {
beginCount++;
testGlobalBeginCount++;
}
private void readObject(ObjectInputStream in)
throws ClassNotFoundException, IOException {
in.defaultReadObject();
deserializedTxListener = this;
}
}
private static class LifeListener
extends AbstractLifecycleListener
implements Serializable {
private int refreshCount = 0;
@Override
public void afterRefresh(LifecycleEvent event) {
refreshCount++;
testGlobalRefreshCount++;
}
private void readObject(ObjectInputStream in)
throws ClassNotFoundException, IOException {
in.defaultReadObject();
deserializedLifeListener = this;
}
}
}

View File

@ -0,0 +1,52 @@
/*
* 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 org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.enhance.UnenhancedSubtype;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.util.ImplHelper;
public abstract class AbstractUnenhancedRelationBrokerSerializationTest<T>
extends AbstractBrokerSerializationTest<T> {
public void testNewUnenhancedSMsRegisteredGlobally() {
OpenJPAEntityManager em = emf.createEntityManager();
OpenJPAEntityManager em2 = null;
try {
em.getTransaction().begin();
UnenhancedSubtype newe = (UnenhancedSubtype) newManagedInstance();
em.persist(newe);
em2 = deserializeEM(serialize(em));
for (Object o : em2.getManagedObjects()) {
assertFalse(o instanceof PersistenceCapable);
assertNotNull(ImplHelper.toPersistenceCapable(o,
emf.getConfiguration()));
if (o instanceof UnenhancedSubtype)
assertNotNull(ImplHelper.toPersistenceCapable(
((UnenhancedSubtype) o).getRelated(),
emf.getConfiguration()));
}
} finally {
close(em);
close(em2);
}
}
}

View File

@ -0,0 +1,52 @@
/*
* 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 org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.persistence.query.SimpleEntity;
public class TestEnhancedInstanceBrokerSerialization
extends AbstractBrokerSerializationTest<SimpleEntity> {
@Override
public void setUp() {
assertTrue(
PersistenceCapable.class.isAssignableFrom(SimpleEntity.class));
super.setUp();
}
protected Class<SimpleEntity> getManagedType() {
return SimpleEntity.class;
}
protected SimpleEntity newManagedInstance() {
SimpleEntity e = new SimpleEntity();
e.setName("foo");
e.setValue("bar");
return e;
}
protected void modifyInstance(SimpleEntity e) {
e.setValue("modified");
}
protected Object getModifiedValue(SimpleEntity e) {
return e.getValue();
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.util.HashMap;
import java.util.Map;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
public class TestEntityManagerFactoryPool
extends SingleEMFTestCase {
public void setUp() {
setUp("openjpa.EntityManagerFactoryPool", Boolean.TRUE);
emf.createEntityManager().close();
}
public void testBrokerFactoryPoolHit() {
Map m = new HashMap();
// also tests string values for the property
m.put("openjpa.EntityManagerFactoryPool", "True");
EntityManagerFactory emf = Persistence.createEntityManagerFactory(
"test", m);
assertSame(this.emf, emf);
}
public void testBrokerFactoryPoolMiss() {
Map m = new HashMap();
m.put("openjpa.EntityManagerFactoryPool", Boolean.TRUE);
EntityManagerFactory emf = Persistence.createEntityManagerFactory(
"second-persistence-unit", m);
assertNotSame(this.emf, emf);
}
}

View File

@ -0,0 +1,51 @@
/*
* 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 org.apache.openjpa.persistence.query.ManyOneEntity;
public class TestInstanceGraphBrokerSerialization
extends AbstractBrokerSerializationTest<ManyOneEntity> {
protected Class<ManyOneEntity> getManagedType() {
return ManyOneEntity.class;
}
protected ManyOneEntity newManagedInstance() {
ManyOneEntity e = new ManyOneEntity();
e.setName("foo");
ManyOneEntity rel = new ManyOneEntity();
rel.setName("bar");
e.setRel(rel);
return e;
}
protected void modifyInstance(ManyOneEntity e) {
e.getRel().setName("modified");
}
protected Object getModifiedValue(ManyOneEntity e) {
return e.getRel().getName();
}
@Override
protected int graphSize() {
return 2;
}
}

View File

@ -0,0 +1,43 @@
/*
* 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 org.apache.openjpa.enhance.UnenhancedFieldAccess;
public class TestUnenhancedFieldAccessInstanceBrokerSerialization
extends AbstractBrokerSerializationTest<UnenhancedFieldAccess> {
protected Class<UnenhancedFieldAccess> getManagedType() {
return UnenhancedFieldAccess.class;
}
protected UnenhancedFieldAccess newManagedInstance() {
UnenhancedFieldAccess e = new UnenhancedFieldAccess();
e.setStringField("foo");
return e;
}
protected void modifyInstance(UnenhancedFieldAccess e) {
e.setStringField("modified");
}
protected Object getModifiedValue(UnenhancedFieldAccess e) {
return e.getStringField();
}
}

View File

@ -0,0 +1,57 @@
/*
* 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 org.apache.openjpa.enhance.UnenhancedFieldAccess;
import org.apache.openjpa.enhance.UnenhancedFieldAccessSubclass;
public class TestUnenhancedFieldAccessWithRelationInstanceBrokerSerialization
extends AbstractUnenhancedRelationBrokerSerializationTest
<UnenhancedFieldAccessSubclass> {
protected Class getSecondaryType() {
return UnenhancedFieldAccess.class;
}
protected Class<UnenhancedFieldAccessSubclass> getManagedType() {
return UnenhancedFieldAccessSubclass.class;
}
protected UnenhancedFieldAccessSubclass newManagedInstance() {
UnenhancedFieldAccessSubclass e = new UnenhancedFieldAccessSubclass();
e.setStringField("foo");
UnenhancedFieldAccess related = new UnenhancedFieldAccess();
related.setStringField("bar");
e.setRelated(related);
return e;
}
protected void modifyInstance(UnenhancedFieldAccessSubclass e) {
e.getRelated().setStringField("modified");
}
protected Object getModifiedValue(UnenhancedFieldAccessSubclass e) {
return e.getRelated().getStringField();
}
@Override
protected int graphSize() {
return 2;
}
}

View File

@ -0,0 +1,43 @@
/*
* 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 org.apache.openjpa.enhance.UnenhancedPropertyAccess;
public class TestUnenhancedPropertyAccessInstanceBrokerSerialization
extends AbstractBrokerSerializationTest<UnenhancedPropertyAccess> {
protected Class<UnenhancedPropertyAccess> getManagedType() {
return UnenhancedPropertyAccess.class;
}
protected UnenhancedPropertyAccess newManagedInstance() {
UnenhancedPropertyAccess e = new UnenhancedPropertyAccess();
e.setStringField("foo");
return e;
}
protected void modifyInstance(UnenhancedPropertyAccess e) {
e.setStringField("modified");
}
protected Object getModifiedValue(UnenhancedPropertyAccess e) {
return e.getStringField();
}
}

View File

@ -0,0 +1,58 @@
/*
* 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 org.apache.openjpa.enhance.UnenhancedPropertyAccess;
import org.apache.openjpa.enhance.UnenhancedPropertyAccessSubclass;
public class TestUnenhancedPropertyAccessWithRelationInstanceBrokerSerialization
extends AbstractUnenhancedRelationBrokerSerializationTest
<UnenhancedPropertyAccessSubclass> {
protected Class getSecondaryType() {
return UnenhancedPropertyAccess.class;
}
protected Class<UnenhancedPropertyAccessSubclass> getManagedType() {
return UnenhancedPropertyAccessSubclass.class;
}
protected UnenhancedPropertyAccessSubclass newManagedInstance() {
UnenhancedPropertyAccessSubclass e =
new UnenhancedPropertyAccessSubclass();
e.setStringField("foo");
UnenhancedPropertyAccess related = new UnenhancedPropertyAccess();
related.setStringField("bar");
e.setRelated(related);
return e;
}
protected void modifyInstance(UnenhancedPropertyAccessSubclass e) {
e.getRelated().setStringField("modified");
}
protected Object getModifiedValue(UnenhancedPropertyAccessSubclass e) {
return e.getRelated().getStringField();
}
@Override
protected int graphSize() {
return 2;
}
}

View File

@ -18,6 +18,7 @@
*/
package org.apache.openjpa.persistence.query;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
@ -29,7 +30,7 @@ import javax.persistence.Version;
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class ManyOneEntity {
public class ManyOneEntity implements Serializable {
@Id
@GeneratedValue

View File

@ -18,6 +18,7 @@
*/
package org.apache.openjpa.persistence.query;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
@ -43,7 +44,7 @@ import javax.persistence.Table;
@FieldResult(name = "value", column = "VALUE") }))
@Entity(name = "simple")
@Table(name = "SIMPLE_ENTITY")
public class SimpleEntity {
public class SimpleEntity implements Serializable {
@Id
@GeneratedValue

View File

@ -18,6 +18,15 @@
*/
package org.apache.openjpa.persistence;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
@ -30,6 +39,9 @@ import javax.persistence.Query;
import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.ee.ManagedRuntime;
import org.apache.openjpa.enhance.PCEnhancer;
import org.apache.openjpa.enhance.PCRegistry;
import org.apache.openjpa.kernel.AbstractBrokerFactory;
import org.apache.openjpa.kernel.Broker;
import org.apache.openjpa.kernel.DelegatingBroker;
import org.apache.openjpa.kernel.FindCallbacks;
@ -40,8 +52,8 @@ import org.apache.openjpa.kernel.QueryFlushModes;
import org.apache.openjpa.kernel.QueryLanguages;
import org.apache.openjpa.kernel.Seq;
import org.apache.openjpa.kernel.jpql.JPQLParser;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.util.Closeable;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.QueryMetaData;
@ -59,30 +71,36 @@ import org.apache.openjpa.util.UserException;
* @nojavadoc
*/
public class EntityManagerImpl
implements OpenJPAEntityManagerSPI,
implements OpenJPAEntityManagerSPI, Externalizable,
FindCallbacks, OpCallbacks, Closeable, OpenJPAEntityTransaction {
private static final Localizer _loc = Localizer.forPackage
(EntityManagerImpl.class);
private final DelegatingBroker _broker;
private final EntityManagerFactoryImpl _emf;
private FetchPlan _fetch = null;
private static final Object[] EMPTY_OBJECTS = new Object[0];
private DelegatingBroker _broker;
private EntityManagerFactoryImpl _emf;
private FetchPlan _fetch = null;
private RuntimeExceptionTranslator ret =
PersistenceExceptions.getRollbackTranslator(this);
public EntityManagerImpl() {
// for Externalizable
}
/**
* Constructor; supply factory and delegate.
*/
public EntityManagerImpl(EntityManagerFactoryImpl factory,
Broker broker) {
initialize(factory, broker);
}
private void initialize(EntityManagerFactoryImpl factory, Broker broker) {
_emf = factory;
RuntimeExceptionTranslator translator =
PersistenceExceptions.getRollbackTranslator(this);
_broker = new DelegatingBroker(broker, translator);
_broker.setImplicitBehavior(this, translator);
_broker = new DelegatingBroker(broker, ret);
_broker.setImplicitBehavior(this, ret);
}
/**
@ -1180,4 +1198,144 @@ public class EntityManagerImpl
return false;
return _broker.equals(((EntityManagerImpl) other)._broker);
}
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
try {
ret = PersistenceExceptions.getRollbackTranslator(this);
// this assumes that serialized Brokers are from something
// that extends AbstractBrokerFactory.
Object factoryKey = in.readObject();
AbstractBrokerFactory factory =
AbstractBrokerFactory.getPooledFactoryForKey(factoryKey);
byte[] brokerBytes = (byte[]) in.readObject();
ObjectInputStream innerIn = new BrokerBytesInputStream(brokerBytes,
factory.getConfiguration());
Broker broker = (Broker) innerIn.readObject();
EntityManagerFactoryImpl emf = (EntityManagerFactoryImpl)
JPAFacadeHelper.toEntityManagerFactory(
broker.getBrokerFactory());
broker.putUserObject(JPAFacadeHelper.EM_KEY, this);
initialize(emf, broker);
} catch (RuntimeException re) {
try {
re = ret.translate(re);
} catch (Exception e) {
// ignore
}
throw re;
}
}
public void writeExternal(ObjectOutput out) throws IOException {
try {
// this requires that only AbstractBrokerFactory-sourced
// brokers can be serialized
Object factoryKey = ((AbstractBrokerFactory) _broker
.getBrokerFactory()).getPoolKey();
out.writeObject(factoryKey);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream innerOut = new ObjectOutputStream(baos);
innerOut.writeObject(_broker.getDelegate());
innerOut.flush();
out.writeObject(baos.toByteArray());
} catch (RuntimeException re) {
try {
re = ret.translate(re);
} catch (Exception e) {
// ignore
}
throw re;
}
}
private static class BrokerBytesInputStream extends ObjectInputStream {
private OpenJPAConfiguration conf;
BrokerBytesInputStream(byte[] bytes, OpenJPAConfiguration conf)
throws IOException {
super(new ByteArrayInputStream(bytes));
if (conf == null)
throw new IllegalArgumentException(
"Illegal null argument to ObjectInputStreamWithLoader");
this.conf = conf;
}
/**
* Make a primitive array class
*/
private Class primitiveType(char type) {
switch (type) {
case 'B': return byte.class;
case 'C': return char.class;
case 'D': return double.class;
case 'F': return float.class;
case 'I': return int.class;
case 'J': return long.class;
case 'S': return short.class;
case 'Z': return boolean.class;
default: return null;
}
}
protected Class resolveClass(ObjectStreamClass classDesc)
throws IOException, ClassNotFoundException {
String cname = classDesc.getName();
if (cname.startsWith("[")) {
// An array
Class component; // component class
int dcount; // dimension
for (dcount=1; cname.charAt(dcount)=='['; dcount++) ;
if (cname.charAt(dcount) == 'L') {
component = lookupClass(cname.substring(dcount+1,
cname.length()-1));
} else {
if (cname.length() != dcount+1) {
throw new ClassNotFoundException(cname);// malformed
}
component = primitiveType(cname.charAt(dcount));
}
int dim[] = new int[dcount];
for (int i=0; i<dcount; i++) {
dim[i]=0;
}
return Array.newInstance(component, dim).getClass();
} else {
return lookupClass(cname);
}
}
/**
* If this is a generated subclass, look up the corresponding Class
* object via metadata.
*/
private Class lookupClass(String className)
throws ClassNotFoundException {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
if (PCEnhancer.isPCSubclassName(className)) {
String superName = PCEnhancer.toManagedTypeName(className);
ClassMetaData[] metas = conf.getMetaDataRepositoryInstance()
.getMetaDatas();
for (int i = 0; i < metas.length; i++) {
if (superName.equals(
metas[i].getDescribedType().getName())) {
return PCRegistry.getPCType(
metas[i].getDescribedType());
}
}
// if it's not found, try to look for it anyways
return Class.forName(className);
} else {
throw e;
}
}
}
}
}

View File

@ -51,8 +51,8 @@ public class PersistenceExceptions
* and {@link NonUniqueResultException} in accordance with
* section 3.7 of the EJB 3.0 specification.
*/
public static RuntimeExceptionTranslator getRollbackTranslator
(final OpenJPAEntityManager em) {
public static RuntimeExceptionTranslator getRollbackTranslator(
final OpenJPAEntityManager em) {
return new RuntimeExceptionTranslator() {
private boolean throwing = false;

View File

@ -22,6 +22,8 @@ import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.spi.ClassTransformer;
import javax.persistence.spi.PersistenceProvider;
@ -54,6 +56,7 @@ public class PersistenceProviderImpl
implements PersistenceProvider {
static final String CLASS_TRANSFORMER_OPTIONS = "ClassTransformerOptions";
private static final String EMF_POOL = "EntityManagerFactoryPool";
private static final Localizer _loc = Localizer.forPackage(
PersistenceProviderImpl.class);
@ -72,17 +75,37 @@ public class PersistenceProviderImpl
String resource, Map m) {
PersistenceProductDerivation pd = new PersistenceProductDerivation();
try {
Object poolValue = Configurations.removeProperty(EMF_POOL, m);
ConfigurationProvider cp = pd.load(resource, name, m);
if (cp == null)
return null;
BrokerFactory factory = Bootstrap.newBrokerFactory(cp, null);
BrokerFactory factory = getBrokerFactory(cp, poolValue, null);
return JPAFacadeHelper.toEntityManagerFactory(factory);
} catch (Exception e) {
throw PersistenceExceptions.toPersistenceException(e);
}
}
private BrokerFactory getBrokerFactory(ConfigurationProvider cp,
Object poolValue, ClassLoader loader) {
// handle "true" and "false"
if (poolValue instanceof String
&& ("true".equalsIgnoreCase((String) poolValue)
|| "false".equalsIgnoreCase((String) poolValue)))
poolValue = Boolean.valueOf((String) poolValue);
if (poolValue != null && !(poolValue instanceof Boolean)) {
// we only support boolean settings for this option currently.
throw new IllegalArgumentException(poolValue.toString());
}
if (poolValue == null || !((Boolean) poolValue).booleanValue())
return Bootstrap.newBrokerFactory(cp, loader);
else
return Bootstrap.getBrokerFactory(cp, loader);
}
public OpenJPAEntityManagerFactory createEntityManagerFactory(String name,
Map m) {
return createEntityManagerFactory(name, null, m);
@ -92,6 +115,7 @@ public class PersistenceProviderImpl
PersistenceUnitInfo pui, Map m) {
PersistenceProductDerivation pd = new PersistenceProductDerivation();
try {
Object poolValue = Configurations.removeProperty(EMF_POOL, m);
ConfigurationProvider cp = pd.load(pui, m);
if (cp == null)
return null;
@ -117,7 +141,7 @@ public class PersistenceProviderImpl
BrokerValue.NON_FINALIZING_ALIAS);
}
BrokerFactory factory = Bootstrap.newBrokerFactory(cp,
BrokerFactory factory = getBrokerFactory(cp, poolValue,
pui.getClassLoader());
if (transformerException != null) {
Log log = factory.getConfiguration().getLog(