| /* |
| * 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.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.Serializable; |
| import java.lang.reflect.Modifier; |
| import java.security.AccessController; |
| import java.util.AbstractCollection; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.BitSet; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.IdentityHashMap; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.locks.ReentrantLock; |
| |
| import javax.transaction.Status; |
| import javax.transaction.Synchronization; |
| |
| import org.apache.openjpa.conf.Compatibility; |
| import org.apache.openjpa.conf.OpenJPAConfiguration; |
| import org.apache.openjpa.datacache.DataCache; |
| import org.apache.openjpa.datacache.QueryCache; |
| import org.apache.openjpa.datacache.TypesChangedEvent; |
| import org.apache.openjpa.ee.ManagedRuntime; |
| import org.apache.openjpa.enhance.PCRegistry; |
| import org.apache.openjpa.enhance.PersistenceCapable; |
| import org.apache.openjpa.enhance.Reflection; |
| import org.apache.openjpa.event.LifecycleEvent; |
| import org.apache.openjpa.event.LifecycleEventManager; |
| import org.apache.openjpa.event.RemoteCommitEventManager; |
| import org.apache.openjpa.event.TransactionEvent; |
| import org.apache.openjpa.event.TransactionEventManager; |
| import org.apache.openjpa.instrumentation.InstrumentationManager; |
| import org.apache.openjpa.kernel.exps.ExpressionParser; |
| import org.apache.openjpa.lib.conf.Configurations; |
| import org.apache.openjpa.lib.instrumentation.InstrumentationLevel; |
| import org.apache.openjpa.lib.log.Log; |
| import org.apache.openjpa.lib.util.J2DoPrivHelper; |
| import org.apache.openjpa.lib.util.Localizer; |
| import org.apache.openjpa.lib.util.ReferenceHashMap; |
| import org.apache.openjpa.lib.util.ReferenceHashSet; |
| import org.apache.openjpa.lib.util.ReferenceMap; |
| import org.apache.openjpa.lib.util.StringUtil; |
| import org.apache.openjpa.lib.util.collections.AbstractReferenceMap; |
| import org.apache.openjpa.lib.util.collections.IteratorChain; |
| import org.apache.openjpa.lib.util.collections.LinkedMap; |
| import org.apache.openjpa.lib.util.collections.MapBackedSet; |
| import org.apache.openjpa.meta.ClassMetaData; |
| import org.apache.openjpa.meta.FieldMetaData; |
| import org.apache.openjpa.meta.MetaDataRepository; |
| import org.apache.openjpa.meta.SequenceMetaData; |
| import org.apache.openjpa.meta.ValueMetaData; |
| import org.apache.openjpa.meta.ValueStrategies; |
| import org.apache.openjpa.util.ApplicationIds; |
| import org.apache.openjpa.util.CallbackException; |
| import org.apache.openjpa.util.Exceptions; |
| import org.apache.openjpa.util.GeneralException; |
| import org.apache.openjpa.util.ImplHelper; |
| import org.apache.openjpa.util.InternalException; |
| import org.apache.openjpa.util.InvalidStateException; |
| import org.apache.openjpa.util.NoTransactionException; |
| import org.apache.openjpa.util.ObjectExistsException; |
| import org.apache.openjpa.util.ObjectId; |
| import org.apache.openjpa.util.ObjectNotFoundException; |
| import org.apache.openjpa.util.OpenJPAException; |
| import org.apache.openjpa.util.OptimisticException; |
| import org.apache.openjpa.util.RuntimeExceptionTranslator; |
| import org.apache.openjpa.util.StoreException; |
| import org.apache.openjpa.util.UnsupportedException; |
| import org.apache.openjpa.util.UserException; |
| import org.apache.openjpa.util.WrappedException; |
| import org.apache.openjpa.validation.ValidatingLifecycleEventManager; |
| |
| /** |
| * Concrete {@link Broker}. The broker handles object-level behavior, |
| * but leaves all interaction with the data store to a {@link StoreManager} |
| * that must be supplied at initialization. |
| * |
| * @author Abe White |
| */ |
| public class BrokerImpl implements Broker, FindCallbacks, Cloneable, Serializable { |
| private static final long serialVersionUID = 1L; |
| |
| /** |
| * Incremental flush. |
| */ |
| protected static final int FLUSH_INC = 0; |
| |
| /** |
| * Flush in preparation of commit. |
| */ |
| protected static final int FLUSH_COMMIT = 1; |
| |
| /** |
| * Flush to check consistency of cache, then immediately rollback changes. |
| */ |
| protected static final int FLUSH_ROLLBACK = 2; |
| |
| /** |
| * Run persistence-by-reachability and other flush-time operations without |
| * accessing the database. |
| */ |
| protected static final int FLUSH_LOGICAL = 3; |
| |
| static final int STATUS_INIT = 0; |
| static final int STATUS_TRANSIENT = 1; |
| static final int STATUS_OID_ASSIGN = 2; |
| static final int STATUS_COMMIT_NEW = 3; |
| |
| private static final int FLAG_ACTIVE = 2 << 0; |
| private static final int FLAG_STORE_ACTIVE = 2 << 1; |
| private static final int FLAG_CLOSE_INVOKED = 2 << 2; |
| private static final int FLAG_PRESTORING = 2 << 3; |
| private static final int FLAG_DEREFDELETING = 2 << 4; |
| private static final int FLAG_FLUSHING = 2 << 5; |
| private static final int FLAG_STORE_FLUSHING = 2 << 6; |
| private static final int FLAG_FLUSHED = 2 << 7; |
| private static final int FLAG_FLUSH_REQUIRED = 2 << 8; |
| private static final int FLAG_REMOTE_LISTENER = 2 << 9; |
| 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 String _connectionFactoryName = ""; |
| private String _connectionFactory2Name = ""; |
| |
| 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 transient DelegatingStoreManager _store = null; |
| |
| private FetchConfiguration _fc = null; |
| private String _user = null; |
| private String _pass = 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; |
| private transient InstrumentationManager _instm = null; |
| |
| // ref to producing factory and configuration |
| private transient AbstractBrokerFactory _factory = null; |
| private transient OpenJPAConfiguration _conf = null; |
| private transient MetaDataRepository _repo = null; |
| |
| // cache class loader associated with the broker |
| private transient ClassLoader _loader = null; |
| |
| // user state |
| private Synchronization _sync = null; |
| private Map<Object, Object> _userObjects = null; |
| |
| // managed object caches |
| private ManagedCache _cache = null; |
| private TransactionalCache _transCache = null; |
| private Set<StateManagerImpl> _transAdditions = null; |
| private Set<StateManagerImpl> _derefCache = null; |
| private Set<StateManagerImpl> _derefAdditions = null; |
| |
| // these are used for method-internal state only |
| private transient Map<Object, StateManagerImpl> _loading = null; |
| private transient Set<Object> _operating = null; |
| private transient boolean _operatingDirty = true; |
| |
| private Set<Class<?>> _persistedClss = null; |
| private Set<Class<?>> _updatedClss = null; |
| private Set<Class<?>> _deletedClss = null; |
| private Set<StateManagerImpl> _pending = null; |
| private int findAllDepth = 0; |
| |
| // track instances that become transactional after the first savepoint |
| // (the first uses the transactional cache) |
| private Set<StateManagerImpl> _savepointCache = null; |
| private LinkedMap _savepoints = null; |
| private transient SavepointManager _spm = null; |
| |
| // track open queries and extents so we can free their resources on close |
| private transient ReferenceHashSet _queries = null; |
| private transient ReferenceHashSet _extents = null; |
| |
| // track operation stack depth. Transient because operations cannot |
| // span serialization. |
| private transient int _operationCount = 0; |
| |
| // options |
| private boolean _nontransRead = false; |
| private boolean _nontransWrite = false; |
| private boolean _retainState = false; |
| private int _autoClear = CLEAR_DATASTORE; |
| private int _restoreState = RESTORE_IMMUTABLE; |
| private boolean _optimistic = false; |
| private boolean _ignoreChanges = false; |
| private boolean _multithreaded = false; |
| private boolean _managed = false; |
| private boolean _syncManaged = false; |
| private int _connRetainMode = CONN_RETAIN_DEMAND; |
| private boolean _evictDataCache = false; |
| private boolean _populateDataCache = true; |
| private boolean _largeTransaction = false; |
| private int _autoDetach = 0; |
| private int _detachState = DETACH_LOADED; |
| private boolean _detachedNew = true; |
| private boolean _orderDirty = false; |
| private boolean _cachePreparedQuery = true; |
| private boolean _cacheFinderQuery = true; |
| private boolean _suppressBatchOLELogging = false; |
| private boolean _allowReferenceToSiblingContext = false; |
| private boolean _postLoadOnMerge = false; |
| |
| // status |
| private int _flags = 0; |
| |
| // 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; |
| private int _transCallbackMode = 0; |
| private LifecycleEventManager _lifeEventManager = null; |
| private int _lifeCallbackMode = 0; |
| |
| private transient DetachManagerLite _dmLite; |
| |
| private transient boolean _initializeWasInvoked = false; |
| private transient boolean _fromWriteBehindCallback = false; |
| private LinkedList<FetchConfiguration> _fcs; |
| |
| // Set of supported property keys. The keys in this set correspond to bean-style setter methods |
| // that can be set by reflection. The keys are not qualified by any prefix. |
| private static Set<String> _supportedPropertyNames; |
| static { |
| _supportedPropertyNames = new HashSet<>(); |
| _supportedPropertyNames.addAll(Arrays.asList(new String[] { |
| "AutoClear", |
| "AutoDetach", |
| "CacheFinderQuery", |
| "CachePreparedQuery", |
| "DetachedNew", |
| "DetachState", |
| "EvictFromDataCache", |
| "IgnoreChanges", |
| "LifecycleListenerCallbackMode", |
| "Multithreaded", |
| "NontransactionalRead", |
| "NontransactionalWrite", |
| "Optimistic", |
| "PopulateDataCache", |
| "RestoreState", |
| "RetainState", |
| })); |
| } |
| |
| private boolean _printParameters = false; |
| private static final String PRINT_PARAMETERS_CONFIG_STR = "PrintParameters"; |
| |
| /** |
| * Set the persistence manager's authentication. This is the first |
| * method called after construction. |
| * |
| * @param user the username this broker represents; used when pooling |
| * brokers to make sure that a request to the factory for |
| * a connection with an explicit user is delegated to a suitable broker |
| * @param pass the password for the above user |
| */ |
| public void setAuthentication(String user, String pass) { |
| _user = user; |
| _pass = pass; |
| } |
| |
| /** |
| * Initialize the persistence manager. This method is called |
| * automatically by the factory before use. |
| * |
| * @param factory the factory used to create this broker |
| * @param sm a concrete StoreManager implementation to |
| * 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, |
| boolean fromDeserialization) { |
| initialize(factory, sm, managed, connMode, fromDeserialization, false); |
| } |
| |
| public void initialize(AbstractBrokerFactory factory, |
| DelegatingStoreManager sm, boolean managed, int connMode, |
| boolean fromDeserialization, boolean fromWriteBehindCallback) { |
| _fromWriteBehindCallback = fromWriteBehindCallback; |
| _initializeWasInvoked = true; |
| _loader = AccessController.doPrivileged( |
| J2DoPrivHelper.getContextClassLoaderAction()); |
| if (!fromDeserialization){ |
| _conf = factory.getConfiguration(); |
| _repo = _conf.getMetaDataRepositoryInstance(); |
| } |
| _compat = _conf.getCompatibilityInstance(); |
| _factory = factory; |
| _log = _conf.getLog(OpenJPAConfiguration.LOG_RUNTIME); |
| if (!fromDeserialization) |
| _cache = new ManagedCache(this); |
| // Force creation of a new operating set |
| _operatingDirty = true; |
| initializeOperatingSet(); |
| |
| _connRetainMode = connMode; |
| _managed = managed; |
| if (managed) |
| _runtime = _conf.getManagedRuntimeInstance(); |
| else |
| _runtime = new LocalManagedRuntime(this); |
| |
| if (!fromDeserialization) { |
| _lifeEventManager = _conf.getLifecycleEventManagerInstance(); |
| _transEventManager = new TransactionEventManager(); |
| int cmode = _repo.getMetaDataFactory().getDefaults().getCallbackMode(); |
| setLifecycleListenerCallbackMode(cmode); |
| setTransactionListenerCallbackMode(cmode); |
| |
| // 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 |
| // before initializing them because they may each try to access the |
| // other in their initialization |
| _store = sm; |
| _lm = _conf.newLockManagerInstance(); |
| _im = _conf.newInverseManagerInstance(); |
| _spm = _conf.getSavepointManagerInstance(); |
| _store.setContext(this); |
| _lm.setContext(this); |
| |
| if (_connRetainMode == CONN_RETAIN_ALWAYS) |
| retainConnection(); |
| if (!fromDeserialization) { |
| _fc = _store.newFetchConfiguration(); |
| _fc.setContext(this); |
| } |
| |
| _instm = _conf.getInstrumentationManagerInstance(); |
| if (_instm != null) { |
| _instm.start(InstrumentationLevel.BROKER, this); |
| } |
| |
| _dmLite = new DetachManagerLite(_conf); |
| _printParameters = |
| Boolean.parseBoolean(Configurations.parseProperties(_conf.getConnectionFactoryProperties()).getProperty( |
| PRINT_PARAMETERS_CONFIG_STR, "false")); |
| |
| // do it before begin event otherwise transactional listeners can't use it, see @Auditable |
| if (!fromDeserialization) |
| _factory.addListeners(this); |
| |
| // synch with the global transaction in progress, if any |
| if (_factory.syncWithManagedTransaction(this, false)) |
| beginInternal(); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private void initializeOperatingSet() { |
| if(_operatingDirty) { |
| _operatingDirty = false; |
| _operating = MapBackedSet.mapBackedSet(new IdentityHashMap<>()); |
| } |
| } |
| |
| /** |
| * Gets the unmodifiable set of instances being operated. |
| */ |
| protected Set<Object> getOperatingSet() { |
| return Collections.unmodifiableSet(_operating); |
| } |
| |
| @Override |
| public Object clone() |
| throws CloneNotSupportedException { |
| if (_initializeWasInvoked) |
| throw new CloneNotSupportedException(); |
| else { |
| return super.clone(); |
| } |
| } |
| |
| /** |
| * Create a {@link Map} to be used for the primary managed object cache. |
| * Maps oids to state managers. By default, this creates a |
| * {@link ReferenceMap} with soft values. |
| */ |
| protected Map<?,?> newManagedObjectCache() { |
| return new ReferenceHashMap( |
| AbstractReferenceMap.ReferenceStrength.HARD, AbstractReferenceMap.ReferenceStrength.SOFT); |
| } |
| |
| ////////////////////////////////// |
| // Implementation of StoreContext |
| ////////////////////////////////// |
| |
| @Override |
| public Broker getBroker() { |
| return this; |
| } |
| |
| ////////////// |
| // Properties |
| ////////////// |
| |
| @Override |
| public void setImplicitBehavior(OpCallbacks call, |
| RuntimeExceptionTranslator ex) { |
| if (_call == null) |
| _call = call; |
| if (_extrans == null) |
| _extrans = ex; |
| } |
| |
| RuntimeExceptionTranslator getInstanceExceptionTranslator() { |
| return (_operationCount == 0) ? _extrans : null; |
| } |
| |
| @Override |
| public BrokerFactory getBrokerFactory() { |
| return _factory; |
| } |
| |
| @Override |
| public OpenJPAConfiguration getConfiguration() { |
| return _conf; |
| } |
| |
| @Override |
| public FetchConfiguration getFetchConfiguration() { |
| return _fc; |
| } |
| |
| @Override |
| public FetchConfiguration pushFetchConfiguration() { |
| return pushFetchConfiguration(null); |
| } |
| |
| @Override |
| public FetchConfiguration pushFetchConfiguration(FetchConfiguration fc) { |
| if (_fcs == null) |
| _fcs = new LinkedList<>(); |
| _fcs.add(_fc); |
| _fc = (FetchConfiguration) (fc != null ? fc : _fc).clone(); |
| return _fc; |
| } |
| |
| @Override |
| public void popFetchConfiguration() { |
| if (_fcs == null || _fcs.isEmpty()) |
| throw new UserException( |
| _loc.get("fetch-configuration-stack-empty")); |
| _fc = _fcs.removeLast(); |
| } |
| |
| @Override |
| public int getConnectionRetainMode() { |
| return _connRetainMode; |
| } |
| |
| @Override |
| public boolean isManaged() { |
| return _managed; |
| } |
| |
| @Override |
| public ManagedRuntime getManagedRuntime() { |
| return _runtime; |
| } |
| |
| @Override |
| public ClassLoader getClassLoader() { |
| return _loader; |
| } |
| |
| @Override |
| public DelegatingStoreManager getStoreManager() { |
| return _store; |
| } |
| |
| @Override |
| public LockManager getLockManager() { |
| return _lm; |
| } |
| |
| @Override |
| public InverseManager getInverseManager() { |
| return _im; |
| } |
| |
| @Override |
| public String getConnectionUserName() { |
| return _user; |
| } |
| |
| @Override |
| public String getConnectionPassword() { |
| return _pass; |
| } |
| |
| @Override |
| public boolean getMultithreaded() { |
| return _multithreaded; |
| } |
| |
| @Override |
| public void setMultithreaded(boolean multithreaded) { |
| assertOpen(); |
| _multithreaded = multithreaded; |
| if (multithreaded && _lock == null) |
| _lock = new ReentrantLock(); |
| else if (!multithreaded) |
| _lock = null; |
| } |
| |
| @Override |
| public boolean getIgnoreChanges() { |
| return _ignoreChanges; |
| } |
| |
| @Override |
| public void setIgnoreChanges(boolean val) { |
| assertOpen(); |
| _ignoreChanges = val; |
| } |
| |
| @Override |
| public boolean getNontransactionalRead() { |
| return _nontransRead; |
| } |
| |
| @Override |
| public void setNontransactionalRead(boolean val) { |
| assertOpen(); |
| if ((_flags & FLAG_PRESTORING) != 0) |
| throw new UserException(_loc.get("illegal-op-in-prestore")); |
| |
| // make sure the runtime supports it |
| if (val && !_conf.supportedOptions().contains |
| (OpenJPAConfiguration.OPTION_NONTRANS_READ)) |
| throw new UnsupportedException(_loc.get |
| ("nontrans-read-not-supported")); |
| |
| _nontransRead = val; |
| } |
| |
| @Override |
| public boolean getNontransactionalWrite() { |
| return _nontransWrite; |
| } |
| |
| @Override |
| public void setNontransactionalWrite(boolean val) { |
| assertOpen(); |
| if ((_flags & FLAG_PRESTORING) != 0) |
| throw new UserException(_loc.get("illegal-op-in-prestore")); |
| |
| _nontransWrite = val; |
| } |
| |
| @Override |
| public boolean getOptimistic() { |
| return _optimistic; |
| } |
| |
| @Override |
| public void setOptimistic(boolean val) { |
| assertOpen(); |
| if ((_flags & FLAG_ACTIVE) != 0) |
| throw new InvalidStateException(_loc.get("trans-active", |
| "Optimistic")); |
| |
| // make sure the runtime supports it |
| if (val && !_conf.supportedOptions().contains(OpenJPAConfiguration.OPTION_OPTIMISTIC)) |
| throw new UnsupportedException(_loc.get |
| ("optimistic-not-supported")); |
| |
| _optimistic = val; |
| } |
| |
| @Override |
| public int getRestoreState() { |
| return _restoreState; |
| } |
| |
| @Override |
| public void setRestoreState(int val) { |
| assertOpen(); |
| if ((_flags & FLAG_ACTIVE) != 0) |
| throw new InvalidStateException(_loc.get("trans-active", |
| "Restore")); |
| |
| _restoreState = val; |
| } |
| |
| @Override |
| public boolean getRetainState() { |
| return _retainState; |
| } |
| |
| @Override |
| public void setRetainState(boolean val) { |
| assertOpen(); |
| if ((_flags & FLAG_PRESTORING) != 0) |
| throw new UserException(_loc.get("illegal-op-in-prestore")); |
| _retainState = val; |
| } |
| |
| @Override |
| public int getAutoClear() { |
| return _autoClear; |
| } |
| |
| @Override |
| public void setAutoClear(int val) { |
| assertOpen(); |
| _autoClear = val; |
| } |
| |
| @Override |
| public int getAutoDetach() { |
| return _autoDetach; |
| } |
| /** |
| * Sets automatic detachment option. |
| * <br> |
| * If the given flag contains {@link AutoDetach#DETACH_NONE} option, |
| * then no other option can be specified. |
| */ |
| @Override |
| public void setAutoDetach(int detachFlags) { |
| assertOpen(); |
| assertAutoDetachValue(detachFlags); |
| _autoDetach = detachFlags; |
| } |
| |
| @Override |
| public void setAutoDetach(int detachFlag, boolean on) { |
| assertOpen(); |
| assertAutoDetachValue(on ? _autoDetach | detachFlag : _autoDetach & ~detachFlag); |
| if (on) |
| _autoDetach |= detachFlag; |
| else |
| _autoDetach &= ~detachFlag; |
| } |
| |
| @Override |
| public int getDetachState() { |
| return _detachState; |
| } |
| |
| @Override |
| public void setDetachState(int mode) { |
| assertOpen(); |
| _detachState = mode; |
| } |
| |
| @Override |
| public boolean isDetachedNew() { |
| return _detachedNew; |
| } |
| |
| @Override |
| public void setDetachedNew(boolean isNew) { |
| assertOpen(); |
| _detachedNew = isNew; |
| } |
| |
| @Override |
| public boolean getSyncWithManagedTransactions() { |
| return _syncManaged; |
| } |
| |
| @Override |
| public void setSyncWithManagedTransactions(boolean sync) { |
| assertOpen(); |
| _syncManaged = sync; |
| } |
| |
| @Override |
| public boolean getEvictFromDataCache() { |
| return _evictDataCache; |
| } |
| |
| @Override |
| public void setEvictFromDataCache(boolean evict) { |
| assertOpen(); |
| _evictDataCache = evict; |
| } |
| |
| @Override |
| public boolean getPopulateDataCache() { |
| return _populateDataCache; |
| } |
| |
| @Override |
| public void setPopulateDataCache(boolean cache) { |
| assertOpen(); |
| _populateDataCache = cache; |
| } |
| |
| @Override |
| public boolean isTrackChangesByType() { |
| return _largeTransaction; |
| } |
| |
| @Override |
| public void setTrackChangesByType(boolean largeTransaction) { |
| assertOpen(); |
| _largeTransaction = largeTransaction; |
| } |
| |
| @Override |
| public Object getUserObject(Object key) { |
| beginOperation(false); |
| try { |
| return (_userObjects == null) ? null : _userObjects.get(key); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public Object putUserObject(Object key, Object val) { |
| beginOperation(false); |
| try { |
| if (val == null) |
| return (_userObjects == null) ? null : _userObjects.remove(key); |
| |
| if (_userObjects == null) |
| _userObjects = new HashMap<>(); |
| return _userObjects.put(key, val); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Get current configuration property values used by this instance. |
| * This values are combination of the current configuration values |
| * overwritten by values maintained by this instance such as |
| * Optimistic flag. |
| */ |
| @Override |
| public Map<String, Object> getProperties() { |
| Map<String, Object> props = _conf.toProperties(true); |
| for (String s : _supportedPropertyNames) { |
| final Object value = Reflection.getValue(this, s, !"CacheFinderQuery".equals(s)); |
| if (value != null) { |
| props.put("openjpa." + s, value); |
| } |
| } |
| return props; |
| } |
| |
| /** |
| * Gets the property names that can be used to corresponding setter methods of this receiver |
| * to set its value. |
| */ |
| @Override |
| public Set<String> getSupportedProperties() { |
| Set<String> keys = _conf.getPropertyKeys(); |
| for (String s : _supportedPropertyNames) |
| keys.add("openjpa." + s); |
| return keys; |
| } |
| |
| // //////// |
| // Events |
| // //////// |
| |
| @Override |
| public void addLifecycleListener(Object listener, Class[] classes) { |
| beginOperation(false); |
| try { |
| _lifeEventManager.addListener(listener, classes); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void removeLifecycleListener(Object listener) { |
| beginOperation(false); |
| try { |
| _lifeEventManager.removeListener(listener); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public int getLifecycleListenerCallbackMode() { |
| return _lifeCallbackMode; |
| } |
| |
| @Override |
| public void setLifecycleListenerCallbackMode(int mode) { |
| beginOperation(false); |
| try { |
| _lifeCallbackMode = mode; |
| _lifeEventManager.setFailFast((mode & CALLBACK_FAIL_FAST) != 0); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Give state managers access to the lifecycle event manager. |
| */ |
| @Override |
| public LifecycleEventManager getLifecycleEventManager() { |
| return _lifeEventManager; |
| } |
| |
| /** |
| * Fire given lifecycle event, handling any exceptions appropriately. |
| * |
| * @return whether events are being processed at this time |
| */ |
| boolean fireLifecycleEvent(Object src, Object related, ClassMetaData meta, |
| int eventType) { |
| if (_lifeEventManager == null) |
| return false; |
| if (!_lifeEventManager.isActive(meta)) |
| return false; |
| |
| lock(); |
| Exception[] exs; |
| try { |
| exs = _lifeEventManager.fireEvent(src, related, meta, eventType); |
| } finally { |
| unlock(); |
| } |
| handleCallbackExceptions(exs, _lifeCallbackMode); |
| return true; |
| } |
| |
| /** |
| * Take actions on callback exceptions depending on callback mode. |
| */ |
| private void handleCallbackExceptions(Exception[] exceps, int mode) { |
| if (exceps.length == 0 || (mode & CALLBACK_IGNORE) != 0) |
| return; |
| |
| OpenJPAException ce; |
| if (exceps.length == 1) { |
| // If the exception is already a wrapped exception throw the |
| // exception instead of wrapping it with a callback exception |
| if (exceps[0] instanceof WrappedException) |
| throw (WrappedException)exceps[0]; |
| else |
| ce = new CallbackException(exceps[0]); |
| } else { |
| ce = new CallbackException(_loc.get("callback-err")). |
| setNestedThrowables(exceps); |
| } |
| if ((mode & CALLBACK_ROLLBACK) != 0 && (_flags & FLAG_ACTIVE) != 0) { |
| ce.setFatal(true); |
| setRollbackOnlyInternal(ce); |
| } |
| if ((mode & CALLBACK_LOG) != 0 && _log.isWarnEnabled()) |
| _log.warn(ce); |
| if ((mode & CALLBACK_RETHROW) != 0) |
| throw ce; |
| } |
| |
| @Override |
| public void addTransactionListener(Object tl) { |
| beginOperation(false); |
| try { |
| _transEventManager.addListener(tl); |
| if (tl instanceof RemoteCommitEventManager) |
| _flags |= FLAG_REMOTE_LISTENER; |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void removeTransactionListener(Object tl) { |
| beginOperation(false); |
| try { |
| if (_transEventManager.removeListener(tl) |
| && (tl instanceof RemoteCommitEventManager)) |
| _flags &= ~FLAG_REMOTE_LISTENER; |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public Collection<Object> getTransactionListeners() { |
| return _transEventManager.getListeners(); |
| } |
| |
| @Override |
| public int getTransactionListenerCallbackMode() { |
| return _transCallbackMode; |
| } |
| |
| @Override |
| public void setTransactionListenerCallbackMode(int mode) { |
| beginOperation(false); |
| try { |
| _transCallbackMode = mode; |
| _transEventManager.setFailFast((mode & CALLBACK_FAIL_FAST) != 0); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Fire given transaction event, handling any exceptions appropriately. |
| */ |
| private void fireTransactionEvent(TransactionEvent trans) { |
| if (_transEventManager != null && _transEventManager.hasListeners()) |
| handleCallbackExceptions(_transEventManager.fireEvent(trans), |
| _transCallbackMode); |
| } |
| |
| /** |
| * Set whether this Broker will generate verbose optimistic lock exceptions when batching |
| * operations. Defaults to true. |
| */ |
| public void setSuppressBatchOLELogging(boolean b) { |
| _suppressBatchOLELogging = b; |
| } |
| |
| /** |
| * Return whether this Broker will generate verbose optimistic lock exceptions when batching |
| * operations. |
| */ |
| public boolean getSuppressBatchOLELogging() { |
| return _suppressBatchOLELogging; |
| } |
| /////////// |
| // Lookups |
| /////////// |
| |
| @Override |
| public Object find(Object oid, boolean validate, FindCallbacks call) { |
| int flags = OID_COPY | OID_ALLOW_NEW | OID_NODELETED; |
| if (!validate) |
| flags |= OID_NOVALIDATE; |
| return find(oid, _fc, null, null, flags, call); |
| } |
| |
| @Override |
| public Object find(Object oid, FetchConfiguration fetch, BitSet exclude, |
| Object edata, int flags) { |
| return find(oid, fetch, exclude, edata, flags, null); |
| } |
| |
| /** |
| * Internal finder. |
| */ |
| protected Object find(Object oid, FetchConfiguration fetch, BitSet exclude, |
| Object edata, int flags, FindCallbacks call) { |
| if (call == null) |
| call = this; |
| oid = call.processArgument(oid); |
| if (oid == null) { |
| if ((flags & OID_NOVALIDATE) == 0) |
| throw new ObjectNotFoundException(_loc.get("null-oid")); |
| return call.processReturn(oid, null); |
| } |
| if (fetch == null) |
| fetch = _fc; |
| |
| beginOperation(true); |
| try { |
| assertNontransactionalRead(); |
| |
| // cached instance? |
| StateManagerImpl sm = getStateManagerImplById(oid, |
| (flags & OID_ALLOW_NEW) != 0 || hasFlushed()); |
| if (sm != null) { |
| if (!requiresLoad(sm, true, fetch, edata, flags)) |
| return call.processReturn(oid, sm); |
| |
| if (!sm.isLoading()) { |
| // make sure all the configured fields are loaded; do this |
| // after making instance transactional for locking |
| if (!sm.isTransactional() && useTransactionalState(fetch)) |
| sm.transactional(); |
| boolean loaded; |
| try { |
| loaded = sm.load(fetch, StateManagerImpl.LOAD_FGS, |
| exclude, edata, false); |
| } catch (ObjectNotFoundException onfe) { |
| if ((flags & OID_NODELETED) != 0 |
| || (flags & OID_NOVALIDATE) != 0) |
| throw onfe; |
| return call.processReturn(oid, null); |
| } |
| |
| // if no data needed to be loaded and the user wants to |
| // validate, just make sure the object exists |
| if (!loaded && (flags & OID_NOVALIDATE) == 0 |
| && _compat.getValidateTrueChecksStore() |
| && !sm.isTransactional() |
| && !_store.exists(sm, edata)) { |
| if ((flags & OID_NODELETED) == 0) |
| return call.processReturn(oid, null); |
| throw new ObjectNotFoundException(_loc.get |
| ("del-instance", sm.getManagedInstance(), oid)). |
| setFailedObject(sm.getManagedInstance()); |
| } |
| } |
| |
| // since the object was cached, we may need to upgrade lock |
| // if current level is higher than level of initial load |
| if ((_flags & FLAG_ACTIVE) != 0) { |
| int level = fetch.getReadLockLevel(); |
| _lm.lock(sm, level, fetch.getLockTimeout(), edata); |
| sm.readLocked(level, fetch.getWriteLockLevel()); |
| } |
| return call.processReturn(oid, sm); |
| } |
| |
| // if there's no cached sm for a new/transient id type, we |
| // it definitely doesn't exist |
| if (oid instanceof StateManagerId) |
| return call.processReturn(oid, null); |
| |
| // initialize a new state manager for the datastore instance |
| sm = newStateManagerImpl(oid, (flags & OID_COPY) != 0); |
| boolean load = requiresLoad(sm, false, fetch, edata, flags); |
| sm = initialize(sm, load, fetch, edata); |
| if (sm == null) { |
| if ((flags & OID_NOVALIDATE) != 0) |
| throw new ObjectNotFoundException(oid); |
| return call.processReturn(oid, null); |
| } |
| |
| // make sure all configured fields were loaded |
| if (load) { |
| try { |
| sm.load(fetch, StateManagerImpl.LOAD_FGS, exclude, |
| edata, false); |
| } catch (ObjectNotFoundException onfe) { |
| if ((flags & OID_NODELETED) != 0 |
| || (flags & OID_NOVALIDATE) != 0) |
| throw onfe; |
| return call.processReturn(oid, null); |
| } |
| } |
| return call.processReturn(oid, sm); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Initialize a newly-constructed state manager. |
| */ |
| protected StateManagerImpl initialize(StateManagerImpl sm, boolean load, |
| FetchConfiguration fetch, Object edata) { |
| if (!load) { |
| sm.initialize(sm.getMetaData().getDescribedType(), |
| PCState.HOLLOW); |
| } else { |
| PCState state = (useTransactionalState(fetch)) |
| ? PCState.PCLEAN : PCState.PNONTRANS; |
| sm.setLoading(true); |
| try { |
| if (!_store.initialize(sm, state, fetch, edata)) |
| return null; |
| } finally { |
| sm.setLoading(false); |
| } |
| } |
| return sm; |
| } |
| |
| @Override |
| public Object[] findAll(Collection oids, boolean validate, |
| FindCallbacks call) { |
| int flags = OID_COPY | OID_ALLOW_NEW | OID_NODELETED; |
| if (!validate) |
| flags |= OID_NOVALIDATE; |
| return findAll(oids, _fc, null, null, flags, call); |
| } |
| |
| @Override |
| public Object[] findAll(Collection oids, FetchConfiguration fetch, |
| BitSet exclude, Object edata, int flags) { |
| return findAll(oids, fetch, exclude, edata, flags, null); |
| } |
| |
| /** |
| * Internal finder. |
| */ |
| protected Object[] findAll(Collection oids, FetchConfiguration fetch, |
| BitSet exclude, Object edata, int flags, FindCallbacks call) { |
| findAllDepth ++; |
| |
| // throw any exceptions for null oids up immediately |
| if (oids == null) |
| throw new NullPointerException("oids == null"); |
| if ((flags & OID_NOVALIDATE) != 0 && oids.contains(null)) |
| throw new UserException(_loc.get("null-oids")); |
| |
| // we have to use a map of oid->sm rather than a simple |
| // array, so that we make sure not to create multiple sms for equivalent |
| // oids if the user has duplicates in the given array |
| if (_loading == null) |
| _loading = new HashMap<>((int) (oids.size() * 1.33 + 1)); |
| |
| if (call == null) |
| call = this; |
| if (fetch == null) |
| fetch = _fc; |
| |
| beginOperation(true); |
| try { |
| assertNontransactionalRead(); |
| |
| // collection of state managers to pass to store manager |
| List<OpenJPAStateManager> load = null; |
| StateManagerImpl sm; |
| boolean initialized; |
| boolean transState = useTransactionalState(fetch); |
| Object obj, oid; |
| int idx = 0; |
| for (Iterator<?> itr = oids.iterator(); itr.hasNext(); idx++) { |
| // if we've already seen this oid, skip repeats |
| obj = itr.next(); |
| oid = call.processArgument(obj); |
| if (oid == null || _loading.containsKey(obj)) |
| continue; |
| |
| // 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 |
| || hasFlushed()); |
| initialized = sm != null; |
| if (!initialized) |
| sm = newStateManagerImpl(oid, (flags & OID_COPY) != 0); |
| |
| _loading.put(obj, sm); |
| if (requiresLoad(sm, initialized, fetch, edata, flags)) { |
| transState = transState || useTransactionalState(fetch); |
| if (initialized && !sm.isTransactional() && transState) |
| sm.transactional(); |
| if (load == null) |
| load = new ArrayList<>(oids.size() - idx); |
| load.add(sm); |
| } else if (!initialized) |
| sm.initialize(sm.getMetaData().getDescribedType(), |
| PCState.HOLLOW); |
| } |
| |
| // pass all state managers in need of loading or validation to the |
| // store manager |
| if (load != null) { |
| PCState state = (transState) ? PCState.PCLEAN |
| : PCState.PNONTRANS; |
| Collection<Object> failed = _store.loadAll(load, state, |
| StoreManager.FORCE_LOAD_NONE, fetch, edata); |
| |
| // set failed instances to null |
| if (failed != null && !failed.isEmpty()) { |
| if ((flags & OID_NOVALIDATE) != 0) |
| throw newObjectNotFoundException(failed); |
| for (Object o : failed) { |
| _loading.put(o, null); |
| } |
| } |
| } |
| |
| // create results array; make sure all configured fields are |
| // loaded in each instance |
| Object[] results = new Object[oids.size()]; |
| boolean active = (_flags & FLAG_ACTIVE) != 0; |
| int level = fetch.getReadLockLevel(); |
| idx = 0; |
| for (Iterator<?> itr = oids.iterator(); itr.hasNext(); idx++) { |
| oid = itr.next(); |
| sm = _loading.get(oid); |
| if (sm != null && requiresLoad(sm, true, fetch, edata, flags)) { |
| try { |
| sm.load(fetch, StateManagerImpl.LOAD_FGS, |
| exclude, edata, false); |
| if (active) { |
| _lm.lock(sm, level, fetch.getLockTimeout(), edata); |
| sm.readLocked(level, fetch.getWriteLockLevel()); |
| } |
| } |
| catch (ObjectNotFoundException onfe) { |
| if ((flags & OID_NODELETED) != 0 |
| || (flags & OID_NOVALIDATE) != 0) |
| throw onfe; |
| sm = null; |
| } |
| } |
| results[idx] = call.processReturn(oid, sm); |
| } |
| return results; |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| findAllDepth--; |
| if (findAllDepth == 0) |
| _loading = null; |
| endOperation(); |
| } |
| } |
| |
| public boolean isLoading(Object o) { |
| if(_loading == null ) { |
| return false; |
| } |
| return _loading.containsKey(o); |
| } |
| |
| private boolean hasFlushed() { |
| return (_flags & FLAG_FLUSHED) != 0; |
| } |
| |
| /** |
| * Return whether the given instance needs loading before being returned |
| * to the user. |
| */ |
| private boolean requiresLoad(OpenJPAStateManager sm, boolean initialized, |
| FetchConfiguration fetch, Object edata, int flags) { |
| if (!fetch.requiresLoad()) |
| return false; |
| if ((flags & OID_NOVALIDATE) == 0) |
| return true; |
| if (edata != null) // take advantage of existing result |
| return true; |
| if (initialized && sm.getPCState() != PCState.HOLLOW) |
| return false; |
| if (!initialized && sm.getMetaData().getPCSubclasses().length > 0) |
| return true; |
| return !_compat.getValidateFalseReturnsHollow(); |
| } |
| |
| /** |
| * Return whether to use a transactional state. |
| */ |
| private boolean useTransactionalState(FetchConfiguration fetch) { |
| return (_flags & FLAG_ACTIVE) != 0 && (!_optimistic |
| || _autoClear == CLEAR_ALL |
| || fetch.getReadLockLevel() != LOCK_NONE); |
| } |
| |
| @Override |
| public Object findCached(Object oid, FindCallbacks call) { |
| if (call == null) |
| call = this; |
| oid = call.processArgument(oid); |
| if (oid == null) |
| return call.processReturn(oid, null); |
| |
| beginOperation(true); |
| try { |
| StateManagerImpl sm = getStateManagerImplById(oid, true); |
| return call.processReturn(oid, sm); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public Class<?> getObjectIdType(Class<?> cls) { |
| if (cls == null) |
| return null; |
| |
| beginOperation(false); |
| try { |
| ClassMetaData meta = _repo.getMetaData(cls, _loader, false); |
| if (meta == null |
| || meta.getIdentityType() == ClassMetaData.ID_UNKNOWN) |
| return null; |
| if (meta.getIdentityType() == ClassMetaData.ID_APPLICATION) |
| return meta.getObjectIdType(); |
| |
| return _store.getDataStoreIdType(meta); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public Object newObjectId(Class<?> cls, Object val) { |
| if (val == null) |
| return null; |
| |
| beginOperation(false); |
| try { |
| ClassMetaData meta = _repo.getMetaData(cls, _loader, true); |
| switch (meta.getIdentityType()) { |
| case ClassMetaData.ID_DATASTORE: |
| // delegate to store manager for datastore ids |
| if (val instanceof String |
| && ((String) val).startsWith(StateManagerId.STRING_PREFIX)) |
| return new StateManagerId((String) val); |
| return _store.newDataStoreId(val, meta); |
| case ClassMetaData.ID_APPLICATION: |
| if (ImplHelper.isAssignable(meta.getObjectIdType(), |
| val.getClass())) { |
| if (!meta.isOpenJPAIdentity() |
| && meta.isObjectIdTypeShared()) |
| return new ObjectId(cls, val); |
| return val; |
| } |
| |
| // stringified app id? |
| if (val instanceof String |
| && !_conf.getCompatibilityInstance(). |
| getStrictIdentityValues() |
| && !Modifier.isAbstract(cls.getModifiers())) |
| return PCRegistry.newObjectId(cls, (String) val); |
| |
| Object[] arr = (val instanceof Object[]) ? (Object[]) val |
| : new Object[]{ val }; |
| return ApplicationIds.fromPKValues(arr, meta); |
| default: |
| throw new UserException(_loc.get("meta-unknownid", cls)); |
| } |
| } catch (IllegalArgumentException | ClassCastException iae) { |
| // OPENJPA-365 |
| throw new UserException(_loc.get("bad-id-value", val, |
| val.getClass().getName(), cls)).setCause(iae); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } |
| catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Create a new state manager for the given oid. |
| */ |
| private StateManagerImpl newStateManagerImpl(Object oid, boolean copy) { |
| // see if we're in the process of loading this oid in a loadAll call |
| StateManagerImpl sm; |
| if (_loading != null) { |
| sm = _loading.get(oid); |
| if (sm != null && sm.getPersistenceCapable() == null) |
| return sm; |
| } |
| |
| // find metadata for the oid |
| Class<?> pcType = _store.getManagedType(oid); |
| ClassMetaData meta; |
| if (pcType != null) |
| meta = _repo.getMetaData(pcType, _loader, true); |
| else |
| meta = _repo.getMetaData(oid, _loader, true); |
| |
| // copy the oid if needed |
| if (copy && _compat.getCopyObjectIds()) { |
| if (meta.getIdentityType() == ClassMetaData.ID_APPLICATION) |
| oid = ApplicationIds.copy(oid, meta); |
| else if (meta.getIdentityType() == ClassMetaData.ID_UNKNOWN) |
| throw new UserException(_loc.get("meta-unknownid", meta)); |
| else |
| oid = _store.copyDataStoreId(oid, meta); |
| } |
| |
| sm = newStateManagerImpl(oid, meta); |
| sm.setObjectId(oid); |
| return sm; |
| } |
| |
| /** |
| * Create a state manager for the given oid and metadata. |
| */ |
| protected StateManagerImpl newStateManagerImpl(Object oid, |
| ClassMetaData meta) { |
| return new StateManagerImpl(oid, meta, this); |
| } |
| |
| /////////////// |
| // Transaction |
| /////////////// |
| |
| @Override |
| public void begin() { |
| beginOperation(true); |
| try { |
| if ((_flags & FLAG_ACTIVE) != 0) |
| throw new InvalidStateException(_loc.get("active")); |
| _factory.syncWithManagedTransaction(this, true); |
| beginInternal(); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Notify the store manager of a transaction. |
| */ |
| private void beginInternal() { |
| try { |
| beginStoreManagerTransaction(_optimistic); |
| _flags |= FLAG_ACTIVE; |
| |
| // start locking |
| if (!_optimistic) { |
| _fc.setReadLockLevel(_conf.getReadLockLevelConstant()); |
| _fc.setWriteLockLevel(_conf.getWriteLockLevelConstant()); |
| _fc.setLockTimeout(_conf.getLockTimeout()); |
| } |
| _lm.beginTransaction(); |
| |
| if (_transEventManager.hasBeginListeners()) |
| fireTransactionEvent(new TransactionEvent(this, |
| TransactionEvent.AFTER_BEGIN, null, null, null, null)); |
| } catch (OpenJPAException ke) { |
| // if we already started the transaction, don't let it commit |
| if ((_flags & FLAG_ACTIVE) != 0) |
| setRollbackOnlyInternal(ke); |
| throw ke.setFatal(true); |
| } catch (RuntimeException re) { |
| // if we already started the transaction, don't let it commit |
| if ((_flags & FLAG_ACTIVE) != 0) |
| setRollbackOnlyInternal(re); |
| throw new StoreException(re).setFatal(true); |
| } |
| |
| if (_pending != null) { |
| StateManagerImpl sm; |
| for (StateManagerImpl stateManager : _pending) { |
| sm = stateManager; |
| sm.transactional(); |
| if (sm.isDirty()) |
| setDirty(sm, true); |
| } |
| _pending = null; |
| } |
| } |
| |
| @Override |
| public void beginStore() { |
| beginOperation(true); |
| try { |
| assertTransactionOperation(); |
| if ((_flags & FLAG_STORE_ACTIVE) == 0) |
| beginStoreManagerTransaction(false); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new StoreException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Begin a store manager transaction. |
| */ |
| private void beginStoreManagerTransaction(boolean optimistic) { |
| if (!optimistic) { |
| retainConnection(); |
| _store.begin(); |
| _flags |= FLAG_STORE_ACTIVE; |
| } else { |
| if (_connRetainMode == CONN_RETAIN_TRANS) |
| retainConnection(); |
| _store.beginOptimistic(); |
| } |
| } |
| |
| /** |
| * End the current store manager transaction. Throws an |
| * exception to signal a forced rollback after failed commit, otherwise |
| * returns any exception encountered during the end process. |
| */ |
| private RuntimeException endStoreManagerTransaction(boolean rollback) { |
| boolean forcedRollback = false; |
| boolean releaseConn = false; |
| RuntimeException err = null; |
| try { |
| if ((_flags & FLAG_STORE_ACTIVE) != 0) { |
| releaseConn = _connRetainMode != CONN_RETAIN_ALWAYS; |
| if (rollback) |
| _store.rollback(); |
| else { |
| // and notify the query cache. notify in one batch to reduce synch |
| QueryCache queryCache = getConfiguration(). |
| getDataCacheManagerInstance().getSystemQueryCache(); |
| if (queryCache != null) { |
| Collection<Class<?>> pers = getPersistedTypes(); |
| Collection<Class<?>> del = getDeletedTypes(); |
| Collection<Class<?>> up = getUpdatedTypes(); |
| int size = pers.size() + del.size() + up.size(); |
| if (size > 0) { |
| Collection<Class<?>> types = new ArrayList<>(size); |
| types.addAll(pers); |
| types.addAll(del); |
| types.addAll(up); |
| queryCache.onTypesChanged(new TypesChangedEvent(this, types)); |
| } |
| } |
| _store.commit(); |
| } |
| } else { |
| releaseConn = _connRetainMode == CONN_RETAIN_TRANS; |
| _store.rollbackOptimistic(); |
| } |
| } |
| catch (RuntimeException re) { |
| if (!rollback) { |
| forcedRollback = true; |
| try { _store.rollback(); } catch (RuntimeException re2) {} |
| } |
| err = re; |
| } finally { |
| _flags &= ~FLAG_STORE_ACTIVE; |
| } |
| |
| if (releaseConn) { |
| try { |
| releaseConnection(); |
| } catch (RuntimeException re) { |
| if (err == null) |
| err = re; |
| } |
| } |
| |
| if (forcedRollback) |
| throw err; |
| return err; |
| } |
| |
| @Override |
| public void commit() { |
| beginOperation(false); |
| try { |
| assertTransactionOperation(); |
| |
| javax.transaction.Transaction trans = |
| _runtime.getTransactionManager().getTransaction(); |
| if (trans == null) |
| throw new InvalidStateException(_loc.get("null-trans")); |
| |
| // this commit on the transaction will cause our |
| // beforeCompletion method to be invoked |
| trans.commit(); |
| } catch (OpenJPAException ke) { |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("end-trans-error"), ke); |
| throw ke; |
| } catch (Exception e) { |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("end-trans-error"), e); |
| throw new StoreException(e); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void rollback() { |
| beginOperation(false); |
| try { |
| assertTransactionOperation(); |
| |
| javax.transaction.Transaction trans = |
| _runtime.getTransactionManager().getTransaction(); |
| if (trans != null) |
| trans.rollback(); |
| } catch (OpenJPAException ke) { |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("end-trans-error"), ke); |
| throw ke; |
| } catch (Exception e) { |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("end-trans-error"), e); |
| throw new StoreException(e); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public boolean syncWithManagedTransaction() { |
| assertOpen(); |
| lock(); |
| try { |
| if ((_flags & FLAG_ACTIVE) != 0) |
| return true; |
| if (!_managed) |
| throw new InvalidStateException(_loc.get("trans-not-managed")); |
| if (_factory.syncWithManagedTransaction(this, false)) { |
| beginInternal(); |
| return true; |
| } |
| return false; |
| } finally { |
| unlock(); |
| } |
| } |
| |
| @Override |
| public void commitAndResume() { |
| endAndResume(true); |
| } |
| |
| @Override |
| public void rollbackAndResume() { |
| endAndResume(false); |
| } |
| |
| private void endAndResume(boolean commit) { |
| beginOperation(false); |
| try { |
| if (commit) |
| commit(); |
| else |
| rollback(); |
| begin(); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public boolean getRollbackOnly() { |
| beginOperation(true); |
| try { |
| if ((_flags & FLAG_ACTIVE) == 0) |
| return false; |
| |
| javax.transaction.Transaction trans = |
| _runtime.getTransactionManager().getTransaction(); |
| if (trans == null) |
| return false; |
| return trans.getStatus() == Status.STATUS_MARKED_ROLLBACK; |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (Exception e) { |
| throw new GeneralException(e); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public Throwable getRollbackCause() { |
| beginOperation(true); |
| try { |
| if ((_flags & FLAG_ACTIVE) == 0) |
| return null; |
| |
| javax.transaction.Transaction trans = |
| _runtime.getTransactionManager().getTransaction(); |
| if (trans == null) |
| return null; |
| if (trans.getStatus() == Status.STATUS_MARKED_ROLLBACK) |
| return _runtime.getRollbackCause(); |
| |
| return null; |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (Exception e) { |
| throw new GeneralException(e); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void setRollbackOnly() { |
| setRollbackOnly(new UserException()); |
| } |
| |
| @Override |
| public void setRollbackOnly(Throwable cause) { |
| beginOperation(true); |
| try { |
| assertTransactionOperation(); |
| setRollbackOnlyInternal(cause); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Mark the current transaction as rollback-only. |
| */ |
| private void setRollbackOnlyInternal(Throwable cause) { |
| try { |
| javax.transaction.Transaction trans = |
| _runtime.getTransactionManager().getTransaction(); |
| if (trans == null) |
| throw new InvalidStateException(_loc.get("null-trans")); |
| // ensure tran is in a valid state to accept the setRollbackOnly |
| int tranStatus = trans.getStatus(); |
| if ((tranStatus != Status.STATUS_NO_TRANSACTION) |
| && (tranStatus != Status.STATUS_ROLLEDBACK) |
| && (tranStatus != Status.STATUS_COMMITTED)) |
| _runtime.setRollbackOnly(cause); |
| else if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("invalid-tran-status", tranStatus, "setRollbackOnly")); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (Exception e) { |
| throw new GeneralException(e); |
| } |
| } |
| |
| @Override |
| public void setSavepoint(String name) { |
| beginOperation(true); |
| try { |
| assertActiveTransaction(); |
| if (_savepoints != null && _savepoints.containsKey(name)) |
| throw new UserException(_loc.get("savepoint-exists", name)); |
| |
| if (hasFlushed() && !_spm.supportsIncrementalFlush()) |
| throw new UnsupportedException(_loc.get |
| ("savepoint-flush-not-supported")); |
| |
| OpenJPASavepoint save = _spm.newSavepoint(name, this); |
| if (_savepoints == null || _savepoints.isEmpty()) { |
| save.save(getTransactionalStates()); |
| _savepoints = new LinkedMap(); |
| } else { |
| if (_savepointCache == null) |
| save.save(Collections.EMPTY_SET); |
| else { |
| save.save(_savepointCache); |
| _savepointCache.clear(); |
| } |
| } |
| _savepoints.put(name, save); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (Exception e) { |
| throw new GeneralException(e); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void releaseSavepoint() { |
| beginOperation(false); |
| try { |
| if (_savepoints == null || _savepoints.isEmpty()) |
| throw new UserException(_loc.get("no-lastsavepoint")); |
| releaseSavepoint((String) _savepoints.get |
| (_savepoints.size() - 1)); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void releaseSavepoint(String savepoint) { |
| beginOperation(false); |
| try { |
| assertActiveTransaction(); |
| |
| int index = (_savepoints == null) ? -1 |
| : _savepoints.indexOf(savepoint); |
| if (index < 0) |
| throw new UserException(_loc.get("no-savepoint", savepoint)); |
| |
| // clear old in reverse |
| OpenJPASavepoint save; |
| while (_savepoints.size() > index + 1) { |
| save = (OpenJPASavepoint) _savepoints.remove |
| (_savepoints.size() - 1); |
| save.release(false); |
| } |
| |
| save = (OpenJPASavepoint) _savepoints.remove(index); |
| save.release(true); |
| if (_savepointCache != null) |
| _savepointCache.clear(); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (Exception e) { |
| throw new GeneralException(e); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void rollbackToSavepoint() { |
| beginOperation(false); |
| try { |
| if (_savepoints == null || _savepoints.isEmpty()) |
| throw new UserException(_loc.get("no-lastsavepoint")); |
| rollbackToSavepoint((String) _savepoints.get |
| (_savepoints.size() - 1)); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void rollbackToSavepoint(String savepoint) { |
| beginOperation(false); |
| try { |
| assertActiveTransaction(); |
| |
| int index = (_savepoints == null) ? -1 |
| : _savepoints.indexOf(savepoint); |
| if (index < 0) |
| throw new UserException(_loc.get("no-savepoint", savepoint)); |
| |
| // clear old in reverse |
| OpenJPASavepoint save; |
| while (_savepoints.size() > index + 1) { |
| save = (OpenJPASavepoint) _savepoints.remove |
| (_savepoints.size() - 1); |
| save.release(false); |
| } |
| |
| save = (OpenJPASavepoint) _savepoints.remove(index); |
| Collection saved = save.rollback(_savepoints.values()); |
| if (_savepointCache != null) |
| _savepointCache.clear(); |
| if (hasTransactionalObjects()) { |
| // build up a new collection of states |
| TransactionalCache oldTransCache = _transCache; |
| TransactionalCache newTransCache = new TransactionalCache |
| (_orderDirty); |
| _transCache = null; |
| |
| // currently there is the assumption that incremental |
| // flush is either a) not allowed, or b) required |
| // pre-savepoint. this solves a number of issues including |
| // storing flushed states as well as OID handling. |
| // if future plugins do not follow this, we need to cache |
| // more info per state |
| SavepointFieldManager fm; |
| StateManagerImpl sm; |
| for (Object value : saved) { |
| fm = (SavepointFieldManager) value; |
| sm = fm.getStateManager(); |
| sm.rollbackToSavepoint(fm); |
| oldTransCache.remove(sm); |
| if (sm.isDirty()) |
| newTransCache.addDirty(sm); |
| else |
| newTransCache.addClean(sm); |
| } |
| for (Object o : oldTransCache) { |
| sm = (StateManagerImpl) o; |
| sm.rollback(); |
| removeFromTransaction(sm); |
| } |
| _transCache = newTransCache; |
| } |
| } |
| catch (OpenJPAException ke) { |
| throw ke; |
| } catch (Exception e) { |
| throw new GeneralException(e); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Sets the given flag to the status. |
| * |
| * @since 2.3.0 |
| */ |
| protected void setStatusFlag(int flag) { |
| _flags |= flag; |
| } |
| |
| /** |
| * Clears the given flag from the status. |
| * |
| * @since 2.3.0 |
| */ |
| protected void clearStatusFlag(int flag) { |
| _flags &= ~flag; |
| } |
| |
| |
| @Override |
| public void flush() { |
| beginOperation(true); |
| try { |
| // return silently if no trans is active, or if this is a reentrant |
| // call, which can happen if the store manager tries to get an |
| // auto-inc oid during flush |
| if ((_flags & FLAG_ACTIVE) == 0 |
| || (_flags & FLAG_STORE_FLUSHING) != 0) |
| return; |
| |
| // make sure the runtime supports it |
| if (!_conf.supportedOptions().contains(OpenJPAConfiguration.OPTION_INC_FLUSH)) |
| throw new UnsupportedException(_loc.get |
| ("incremental-flush-not-supported")); |
| if (_savepoints != null && !_savepoints.isEmpty() |
| && !_spm.supportsIncrementalFlush()) |
| throw new UnsupportedException(_loc.get |
| ("savepoint-flush-not-supported")); |
| |
| try { |
| flushSafe(FLUSH_INC); |
| _flags |= FLAG_FLUSHED; |
| } catch (OpenJPAException ke) { |
| // rollback on flush error; objects may be in inconsistent state |
| setRollbackOnly(ke); |
| throw ke.setFatal(true); |
| } catch (RuntimeException re) { |
| // rollback on flush error; objects may be in inconsistent state |
| setRollbackOnly(re); |
| throw new StoreException(re).setFatal(true); |
| } |
| } |
| finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void preFlush() { |
| beginOperation(true); |
| try { |
| if ((_flags & FLAG_ACTIVE) != 0) |
| flushSafe(FLUSH_LOGICAL); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void validateChanges() { |
| beginOperation(true); |
| try { |
| // if no trans, just return; if active datastore trans, flush |
| if ((_flags & FLAG_ACTIVE) == 0) |
| return; |
| if ((_flags & FLAG_STORE_ACTIVE) != 0) { |
| flush(); |
| return; |
| } |
| |
| // make sure the runtime supports inc flush |
| if (!_conf.supportedOptions().contains(OpenJPAConfiguration.OPTION_INC_FLUSH)) |
| throw new UnsupportedException(_loc.get |
| ("incremental-flush-not-supported")); |
| |
| try { |
| flushSafe(FLUSH_ROLLBACK); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new StoreException(re); |
| } |
| } |
| finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public boolean isActive() { |
| beginOperation(true); |
| try { |
| return (_flags & FLAG_ACTIVE) != 0; |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public boolean isStoreActive() { |
| // we need to lock here, because we might be in the middle of an |
| // atomic transaction process (e.g., commitAndResume) |
| beginOperation(true); |
| try { |
| return (_flags & FLAG_STORE_ACTIVE) != 0; |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Return whether the current transaction is ending, i.e. in the 2nd phase |
| * of a commit or rollback |
| */ |
| boolean isTransactionEnding() { |
| return (_flags & FLAG_TRANS_ENDING) != 0; |
| } |
| |
| @Override |
| public boolean beginOperation(boolean syncTrans) { |
| lock(); |
| try { |
| assertOpen(); |
| |
| if (syncTrans && _operationCount == 0 && _syncManaged |
| && (_flags & FLAG_ACTIVE) == 0) |
| syncWithManagedTransaction(); |
| return _operationCount++ == 1; |
| } catch (OpenJPAException ke) { |
| unlock(); |
| throw ke; |
| } catch (RuntimeException re) { |
| unlock(); |
| throw new GeneralException(re); |
| } |
| } |
| |
| /** |
| * Mark the operation over. If outermost caller of stack, returns true |
| * and will detach managed instances if necessary. |
| */ |
| @Override |
| public boolean endOperation() { |
| try { |
| if (_operationCount == 1 && (_autoDetach & DETACH_NONTXREAD) != 0 |
| && (_flags & FLAG_ACTIVE) == 0) { |
| detachAllInternal(null); |
| } |
| if (_operationCount < 1) |
| throw new InternalException(_loc.get("multi-threaded-access")); |
| return _operationCount == 1; |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| _operationCount--; |
| if (_operationCount == 0) |
| initializeOperatingSet(); |
| unlock(); |
| } |
| } |
| |
| public Synchronization getSynchronization() { |
| return _sync; |
| } |
| |
| public void setSynchronization(Synchronization sync) { |
| assertOpen(); |
| _sync = sync; |
| } |
| |
| /////////////////////////////////////////////// |
| // Implementation of Synchronization interface |
| /////////////////////////////////////////////// |
| |
| @Override |
| public void beforeCompletion() { |
| beginOperation(false); |
| try { |
| // user-supplied synchronization |
| if (_sync != null) |
| _sync.beforeCompletion(); |
| |
| flushSafe(FLUSH_COMMIT); |
| } catch (OpenJPAException ke) { |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("end-trans-error"), ke); |
| throw translateManagedCompletionException(ke); |
| } catch (RuntimeException re) { |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("end-trans-error"), re); |
| throw translateManagedCompletionException(new StoreException(re)); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void afterCompletion(int status) { |
| beginOperation(false); |
| try { |
| assertActiveTransaction(); |
| |
| _flags |= FLAG_TRANS_ENDING; |
| endTransaction(status); |
| if (_sync != null) |
| _sync.afterCompletion(status); |
| |
| if ((_autoDetach & DETACH_COMMIT) != 0) |
| detachAllInternal(null); |
| else if (status == Status.STATUS_ROLLEDBACK |
| && (_autoDetach & DETACH_ROLLBACK) != 0) { |
| detachAllInternal(null); |
| } |
| |
| // in an ee context, it's possible that the user tried to close |
| // us but we didn't actually close because we were waiting on this |
| // transaction; if that's true, then close now |
| if ((_flags & FLAG_CLOSE_INVOKED) != 0 |
| && _compat.getCloseOnManagedCommit()) |
| free(); |
| } catch (OpenJPAException ke) { |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("end-trans-error"), ke); |
| throw translateManagedCompletionException(ke); |
| } catch (RuntimeException re) { |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("end-trans-error"), re); |
| throw translateManagedCompletionException(new StoreException(re)); |
| } finally { |
| _flags &= ~FLAG_ACTIVE; |
| _flags &= ~FLAG_FLUSHED; |
| _flags &= ~FLAG_TRANS_ENDING; |
| |
| // event manager nulled if freed broker |
| if (_transEventManager != null |
| && _transEventManager.hasEndListeners()) { |
| fireTransactionEvent(new TransactionEvent(this, |
| status == Status.STATUS_COMMITTED |
| ? TransactionEvent.AFTER_COMMIT_COMPLETE |
| : TransactionEvent.AFTER_ROLLBACK_COMPLETE, |
| null, null, null, null)); |
| } |
| |
| endOperation(); |
| } |
| } |
| |
| /** |
| * If we're in a managed transaction, use our implicit behavior exception |
| * translator to translate before/afterCompletion callback errors. |
| */ |
| private RuntimeException translateManagedCompletionException |
| (RuntimeException re) { |
| return (!_managed || _extrans == null) ? re : _extrans.translate(re); |
| } |
| |
| /** |
| * Flush safely, catching reentrant calls. |
| */ |
| private void flushSafe(int reason) { |
| if ((_flags & FLAG_FLUSHING) != 0) |
| throw new InvalidStateException(_loc.get("reentrant-flush")); |
| |
| _flags |= FLAG_FLUSHING; |
| try { |
| flush(reason); |
| } finally { |
| _flags &= ~FLAG_FLUSHING; |
| } |
| } |
| |
| /** |
| * Flush the transactional state to the data store. Subclasses that |
| * customize commit behavior should override this method. The method |
| * assumes that the persistence manager is locked, is not closed, |
| * and has an active transaction. |
| * |
| * @param reason one of {@link #FLUSH_INC}, {@link #FLUSH_COMMIT}, |
| * {@link #FLUSH_ROLLBACK}, or {@link #FLUSH_LOGICAL} |
| * @since 0.2.5 |
| */ |
| protected void flush(int reason) { |
| // this will enlist proxied states as necessary so we know whether we |
| // have anything to flush |
| Collection transactional = getTransactionalStates(); |
| |
| // do we actually have to flush? only if our flags say so, or if |
| // we have transaction listeners that need to be invoked for commit |
| // (no need to invoke them on inc flush if nothing is dirty). we |
| // special case the remote commit listener used by the datacache cause |
| // we know it doesn't require the commit event when nothing changes |
| boolean flush = (_flags & FLAG_FLUSH_REQUIRED) != 0; |
| boolean listeners = (_transEventManager.hasFlushListeners() |
| || _transEventManager.hasEndListeners()) |
| && ((_flags & FLAG_REMOTE_LISTENER) == 0 |
| || _transEventManager.getListeners().size() > 1); |
| if (!flush && (reason != FLUSH_COMMIT || !listeners)) |
| return; |
| |
| Collection mobjs = null; |
| _flags |= FLAG_PRESTORING; |
| try { |
| if (flush) { |
| // call pre store on all currently transactional objs |
| for (Object o : transactional) { |
| ((StateManagerImpl) o).beforeFlush(reason, _call); |
| } |
| flushAdditions(transactional, reason); |
| } |
| |
| // hopefully now all dependent instances that are going to end |
| // up referenced have been marked as such; delete unrefed |
| // dependents |
| _flags |= FLAG_DEREFDELETING; |
| if (flush && _derefCache != null && !_derefCache.isEmpty()) { |
| // mark for delete all elements in deref, otherwise in some situations it |
| // throws ConcurrentModificationException |
| Set<StateManagerImpl> statesMarkedForDelete = new HashSet<>(_derefCache); |
| for (StateManagerImpl state: statesMarkedForDelete) { |
| deleteDeref(state); |
| } |
| |
| flushAdditions(transactional, reason); |
| } |
| |
| if (reason != FLUSH_LOGICAL) { |
| // if no datastore transaction, start one; even if we don't |
| // think we'll need to flush at this point, our transaction |
| // listeners might introduce some dirty objects or interact |
| // directly with the database |
| if ((_flags & FLAG_STORE_ACTIVE) == 0) |
| beginStoreManagerTransaction(false); |
| |
| if ((_transEventManager.hasFlushListeners() |
| || _transEventManager.hasEndListeners()) |
| && (flush || reason == FLUSH_COMMIT)) { |
| // fire events |
| mobjs = new ManagedObjectCollection(transactional); |
| if (reason == FLUSH_COMMIT |
| && _transEventManager.hasEndListeners()) { |
| fireTransactionEvent(new TransactionEvent(this, |
| TransactionEvent.BEFORE_COMMIT, mobjs, |
| _persistedClss, _updatedClss, _deletedClss)); |
| |
| flushAdditions(transactional, reason); |
| flush = (_flags & FLAG_FLUSH_REQUIRED) != 0; |
| } |
| |
| if (flush && _transEventManager.hasFlushListeners()) { |
| fireTransactionEvent(new TransactionEvent(this, |
| TransactionEvent.BEFORE_FLUSH, mobjs, |
| _persistedClss, _updatedClss, _deletedClss)); |
| flushAdditions(transactional, reason); |
| } |
| } |
| } |
| } |
| finally { |
| _flags &= ~FLAG_PRESTORING; |
| _flags &= ~FLAG_DEREFDELETING; |
| _transAdditions = null; |
| _derefAdditions = null; |
| |
| // also clear derefed set; the deletes have been recorded |
| if (_derefCache != null) |
| _derefCache = null; |
| } |
| |
| // flush to store manager |
| List<Exception> exceps = null; |
| try { |
| if (flush && reason != FLUSH_LOGICAL) { |
| _flags |= FLAG_STORE_FLUSHING; |
| exceps = add(exceps, |
| newFlushException(_store.flush(transactional))); |
| } |
| } finally { |
| _flags &= ~FLAG_STORE_FLUSHING; |
| |
| if (reason == FLUSH_ROLLBACK) |
| exceps = add(exceps, endStoreManagerTransaction(true)); |
| else if (reason != FLUSH_LOGICAL) |
| _flags &= ~FLAG_FLUSH_REQUIRED; |
| |
| // mark states as flushed |
| if (flush) { |
| StateManagerImpl sm; |
| for (Object o : transactional) { |
| sm = (StateManagerImpl) o; |
| try { |
| // the state may have become transient, such as if |
| // it is embedded and the owner has been deleted during |
| // this flush process; bug #1100 |
| if (sm.getPCState() == PCState.TRANSIENT) |
| continue; |
| |
| sm.afterFlush(reason); |
| if (reason == FLUSH_INC) { |
| // if not about to clear trans cache for commit |
| // anyway, re-cache dirty objects with default soft |
| // refs; we don't need hard refs now that the |
| // changes have been flushed |
| sm.proxyFields(true, false); |
| _transCache.flushed(sm); |
| } |
| } |
| catch (Exception e) { |
| exceps = add(exceps, e); |
| } |
| } |
| } |
| } |
| |
| // throw any exceptions to shortcut listeners on fail |
| throwNestedExceptions(exceps, true); |
| |
| if (flush && reason != FLUSH_ROLLBACK && reason != FLUSH_LOGICAL |
| && _transEventManager.hasFlushListeners()) { |
| fireTransactionEvent(new TransactionEvent(this, |
| TransactionEvent.AFTER_FLUSH, mobjs, _persistedClss, |
| _updatedClss, _deletedClss)); |
| } |
| } |
| |
| /** |
| * Flush newly-transactional objects. |
| */ |
| private void flushAdditions(Collection transactional, int reason) { |
| boolean loop; |
| do { |
| // flush new transactional instances; note logical or |
| loop = flushTransAdditions(transactional, reason) |
| | deleteDerefAdditions(_derefCache); |
| } while (loop); |
| } |
| |
| /** |
| * Flush transactional additions. |
| */ |
| private boolean flushTransAdditions(Collection transactional, int reason) { |
| if (_transAdditions == null || _transAdditions.isEmpty()) |
| return false; |
| |
| // keep local transactional list copy up to date |
| transactional.addAll(_transAdditions); |
| |
| // copy the change set, then clear it for the next iteration |
| StateManagerImpl[] states = _transAdditions. |
| toArray(new StateManagerImpl[_transAdditions.size()]); |
| _transAdditions = null; |
| |
| for (StateManagerImpl state : states) { |
| state.beforeFlush(reason, _call); |
| } |
| return true; |
| } |
| |
| /** |
| * Delete new dereferenced objects. |
| */ |
| private boolean deleteDerefAdditions(Collection derefs) { |
| if (_derefAdditions == null || _derefAdditions.isEmpty()) |
| return false; |
| |
| // remember these additions in case one becomes derefed again later |
| derefs.addAll(_derefAdditions); |
| |
| StateManagerImpl[] states = _derefAdditions. |
| toArray(new StateManagerImpl[_derefAdditions.size()]); |
| _derefAdditions = null; |
| |
| for (StateManagerImpl state : states) { |
| deleteDeref(state); |
| } |
| return true; |
| } |
| |
| /** |
| * Delete a dereferenced dependent. |
| */ |
| private void deleteDeref(StateManagerImpl sm) { |
| int action = processArgument(OpCallbacks.OP_DELETE, |
| sm.getManagedInstance(), sm, null); |
| if ((action & OpCallbacks.ACT_RUN) != 0) |
| sm.delete(); |
| if ((action & OpCallbacks.ACT_CASCADE) != 0) |
| sm.cascadeDelete(_call); |
| } |
| |
| /** |
| * Determine the action to take based on the user's given callbacks and |
| * our implicit behavior. |
| */ |
| private int processArgument(int op, Object obj, OpenJPAStateManager sm, |
| OpCallbacks call) { |
| if (call != null) |
| return call.processArgument(op, obj, sm); |
| if (_call != null) |
| return _call.processArgument(op, obj, sm); |
| return OpCallbacks.ACT_RUN | OpCallbacks.ACT_CASCADE; |
| } |
| |
| /** |
| * Throw the proper exception based on the given set of flush errors, or |
| * do nothing if no errors occurred. |
| */ |
| private OpenJPAException newFlushException(Collection<Exception> exceps) { |
| if (exceps == null || exceps.isEmpty()) |
| return null; |
| |
| Throwable[] t = exceps.toArray(new Throwable[exceps.size()]); |
| List<Object> failed = new ArrayList<>(t.length); |
| |
| // create fatal exception with nested exceptions for all the failed |
| // objects; if all OL exceptions, throw a top-level OL exception |
| boolean opt = true; |
| for (int i = 0; opt && i < t.length; i++) { |
| opt = t[i] instanceof OptimisticException; |
| if (opt) { |
| Object f = ((OptimisticException) t[i]).getFailedObject(); |
| if (f != null) |
| failed.add(f); |
| } |
| } |
| if (opt && !failed.isEmpty()) { |
| if(_suppressBatchOLELogging){ |
| return new OptimisticException(_loc.get("broker-suppressing-exceptions",t.length)); |
| }else{ |
| return new OptimisticException(failed, t); |
| } |
| } |
| if (opt) |
| return new OptimisticException(t); |
| |
| Object failedObject = null; |
| if (t[0] instanceof OpenJPAException){ |
| failedObject = ((OpenJPAException)t[0]).getFailedObject(); |
| } |
| |
| return new StoreException(_loc.get("rolled-back")). |
| setNestedThrowables(t).setFatal(true).setFailedObject(failedObject); |
| } |
| |
| /** |
| * End the current transaction, making appropriate state transitions. |
| */ |
| protected void endTransaction(int status) { |
| // if a data store transaction was in progress, do the |
| // appropriate transaction change |
| boolean rollback = status != Status.STATUS_COMMITTED; |
| List<Exception> exceps = null; |
| |
| try { |
| exceps = add(exceps, endStoreManagerTransaction(rollback)); |
| } catch (RuntimeException re) { |
| rollback = true; |
| exceps = add(exceps, re); |
| } |
| |
| // go back to default none lock level |
| _fc.setReadLockLevel(LOCK_NONE); |
| _fc.setWriteLockLevel(LOCK_NONE); |
| _fc.setLockTimeout(-1); |
| |
| Collection transStates; |
| if (hasTransactionalObjects()) |
| transStates = _transCache; |
| else |
| transStates = Collections.EMPTY_SET; |
| |
| // fire after rollback/commit event |
| Collection mobjs = null; |
| if (_transEventManager.hasEndListeners()) { |
| mobjs = new ManagedObjectCollection(transStates); |
| int eventType = (rollback) ? TransactionEvent.AFTER_ROLLBACK |
| : TransactionEvent.AFTER_COMMIT; |
| fireTransactionEvent(new TransactionEvent(this, eventType, mobjs, |
| _persistedClss, _updatedClss, _deletedClss)); |
| } |
| |
| // null transactional caches now so that all the removeFromTransaction |
| // calls as we transition each object don't have to do any work; don't |
| // clear trans cache object because we still need the transStates |
| // reference to it below |
| _transCache = null; |
| if (_persistedClss != null) |
| _persistedClss = null; |
| if (_updatedClss != null) |
| _updatedClss = null; |
| if (_deletedClss != null) |
| _deletedClss = null; |
| |
| // new cache would get cleared anyway during transitions, but doing so |
| // immediately saves us some lookups |
| _cache.clearNew(); |
| |
| // tell all derefed instances they're no longer derefed; we can't |
| // rely on rollback and commit calls below cause some instances might |
| // not be transactional |
| if (_derefCache != null && !_derefCache.isEmpty()) { |
| for (StateManagerImpl stateManager : _derefCache) { |
| stateManager.setDereferencedDependent(false, false); |
| } |
| _derefCache = null; |
| } |
| |
| // perform commit or rollback state transitions on each instance |
| StateManagerImpl sm; |
| for (Object transState : transStates) { |
| sm = (StateManagerImpl) transState; |
| try { |
| if (rollback) { |
| // tell objects that may have been derefed then flushed |
| // (and therefore deleted) to un-deref |
| sm.setDereferencedDependent(false, false); |
| sm.rollback(); |
| } |
| else { |
| if (sm.getPCState() == PCState.PNEWDELETED || sm.getPCState() == PCState.PDELETED) { |
| fireLifecycleEvent(sm.getPersistenceCapable(), null, sm.getMetaData(), |
| LifecycleEvent.AFTER_DELETE_PERFORMED); |
| } |
| sm.commit(); |
| } |
| } |
| catch (RuntimeException re) { |
| exceps = add(exceps, re); |
| } |
| } |
| |
| // notify the lock manager to clean up and release remaining locks |
| _lm.endTransaction(); |
| |
| // clear old savepoints in reverse |
| OpenJPASavepoint save; |
| while (_savepoints != null && _savepoints.size() > 0) { |
| save = |
| (OpenJPASavepoint) _savepoints.remove(_savepoints.size() - 1); |
| save.release(false); |
| } |
| _savepoints = null; |
| _savepointCache = null; |
| |
| // fire after state change event |
| if (_transEventManager.hasEndListeners()) |
| fireTransactionEvent(new TransactionEvent(this, TransactionEvent. |
| AFTER_STATE_TRANSITIONS, mobjs, null, null, null)); |
| |
| // now clear trans cache; keep cleared version rather than |
| // null to avoid having to re-create the set later; more efficient |
| if (transStates != Collections.EMPTY_SET) { |
| _transCache = (TransactionalCache) transStates; |
| _transCache.clear(); |
| } |
| |
| throwNestedExceptions(exceps, true); |
| } |
| |
| //////////////////// |
| // Object lifecycle |
| //////////////////// |
| |
| @Override |
| public void persist(Object obj, OpCallbacks call) { |
| persist(obj, null, true, call); |
| } |
| |
| @Override |
| public OpenJPAStateManager persist(Object obj, Object id, |
| OpCallbacks call) { |
| return persist(obj, id, true, call); |
| } |
| |
| @Override |
| public void persistAll(Collection objs, OpCallbacks call) { |
| persistAll(objs, true, call); |
| } |
| |
| /** |
| * Persist the given objects. Indicate whether this was an explicit persist |
| * (PNEW) or a provisonal persist (PNEWPROVISIONAL). |
| */ |
| public void persistAll(Collection objs, boolean explicit, |
| OpCallbacks call) { |
| if (objs.isEmpty()) |
| return; |
| |
| beginOperation(true); |
| List<Exception> exceps = null; |
| try { |
| assertWriteOperation(); |
| |
| for (Object obj : objs) { |
| try { |
| if(obj == null) |
| continue; |
| persistInternal(obj, null, explicit, call, true); |
| } catch (UserException ue) { |
| exceps = add(exceps, ue); |
| } |
| catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } |
| } |
| } finally { |
| endOperation(); |
| } |
| throwNestedExceptions(exceps, false); |
| } |
| |
| /** |
| * If the given element is not null, add it to the given list, |
| * creating the list if necessary. |
| */ |
| private List<Exception> add(List<Exception> l, Exception o) { |
| if (o == null) |
| return l; |
| if (l == null) |
| l = new LinkedList<>(); |
| l.add(o); |
| return l; |
| } |
| |
| /** |
| * Throw an exception wrapping the given nested exceptions. |
| */ |
| private void throwNestedExceptions(List<Exception> exceps, boolean datastore) { |
| if (exceps == null || exceps.isEmpty()) |
| return; |
| if (datastore && exceps.size() == 1) |
| throw (RuntimeException) exceps.get(0); |
| |
| boolean fatal = false; |
| Throwable[] t = exceps.toArray(new Throwable[exceps.size()]); |
| for (Throwable throwable : t) { |
| if (throwable instanceof OpenJPAException |
| && ((OpenJPAException) throwable).isFatal()) |
| fatal = true; |
| } |
| OpenJPAException err; |
| if (datastore) |
| err = new StoreException(_loc.get("nested-exceps")); |
| else |
| err = new UserException(_loc.get("nested-exceps")); |
| throw err.setNestedThrowables(t).setFatal(fatal); |
| } |
| |
| /** |
| * Persist the given object. Indicate whether this was an explicit persist |
| * (PNEW) or a provisonal persist (PNEWPROVISIONAL) |
| */ |
| public void persist(Object obj, boolean explicit, OpCallbacks call) { |
| persist(obj, null, explicit, call); |
| } |
| |
| /** |
| * Persist the given object. Indicate whether this was an explicit persist |
| * (PNEW) or a provisonal persist (PNEWPROVISIONAL). |
| * See {@link Broker} for details on this method. |
| */ |
| public OpenJPAStateManager persist(Object obj, Object id, boolean explicit, |
| OpCallbacks call) { |
| return persist(obj, id, explicit, call, true); |
| } |
| |
| /** |
| * Persist the given object. Indicate whether this was an explicit persist |
| * (PNEW) or a provisonal persist (PNEWPROVISIONAL). |
| * See {@link Broker} for details on this method. |
| */ |
| public OpenJPAStateManager persist(Object obj, Object id, boolean explicit, |
| OpCallbacks call, boolean fireEvent) { |
| if (obj == null) |
| return null; |
| |
| beginOperation(true); |
| try { |
| assertWriteOperation(); |
| |
| return persistInternal(obj, id, explicit, call, fireEvent); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| private OpenJPAStateManager persistInternal(Object obj, Object id, boolean explicit, OpCallbacks call, |
| boolean fireEvent) { |
| StateManagerImpl sm = getStateManagerImpl(obj, true); |
| if (!operatingAdd(obj)) { |
| return sm; |
| } |
| |
| int action = processArgument(OpCallbacks.OP_PERSIST, obj, sm, call); |
| if (action == OpCallbacks.ACT_NONE) { |
| return sm; |
| } |
| |
| // ACT_CASCADE |
| if ((action & OpCallbacks.ACT_RUN) == 0) { |
| if (sm != null) { |
| sm.cascadePersist(call); |
| } else { |
| cascadeTransient(OpCallbacks.OP_PERSIST, obj, call, "persist"); |
| } |
| return sm; |
| } |
| |
| // ACT_RUN |
| PersistenceCapable pc; |
| if (sm != null) { |
| if (sm.isDetached()) { |
| throw new ObjectExistsException(_loc.get("persist-detached", Exceptions.toString(obj))) |
| .setFailedObject(obj); |
| } |
| |
| if (!sm.isEmbedded()) { |
| sm.persist(); |
| _cache.persist(sm); |
| if ((action & OpCallbacks.ACT_CASCADE) != 0) { |
| sm.cascadePersist(call); |
| } |
| return sm; |
| } |
| |
| // an embedded field; notify the owner that the value has |
| // changed by becoming independently persistent |
| sm.getOwner().dirty(sm.getOwnerIndex()); |
| _cache.persist(sm); |
| pc = sm.getPersistenceCapable(); |
| } else { |
| pc = assertPersistenceCapable(obj); |
| if (pc.pcIsDetached() == Boolean.TRUE) { |
| throw new ObjectExistsException(_loc.get("persist-detached", Exceptions.toString(obj))) |
| .setFailedObject(obj); |
| } |
| } |
| |
| ClassMetaData meta = _repo.getMetaData(obj.getClass(), _loader, true); |
| if (fireEvent) { |
| fireLifecycleEvent(obj, null, meta, LifecycleEvent.BEFORE_PERSIST); |
| } |
| |
| // create id for instance |
| if (id == null) { |
| int idType = meta.getIdentityType(); |
| if (idType == ClassMetaData.ID_APPLICATION) { |
| id = ApplicationIds.create(pc, meta); |
| } else if (idType == ClassMetaData.ID_UNKNOWN) { |
| throw new UserException(_loc.get("meta-unknownid", meta)); |
| } else { |
| id = StateManagerId.newInstance(this); |
| } |
| } |
| |
| // make sure we don't already have the instance cached |
| checkForDuplicateId(id, obj, meta); |
| |
| // if had embedded sm, null it |
| if (sm != null) { |
| pc.pcReplaceStateManager(null); |
| } |
| |
| // create new sm |
| sm = newStateManagerImpl(id, meta); |
| if ((_flags & FLAG_ACTIVE) != 0) { |
| if (explicit) { |
| sm.initialize(pc, PCState.PNEW); |
| } else { |
| sm.initialize(pc, PCState.PNEWPROVISIONAL); |
| } |
| } else { |
| sm.initialize(pc, PCState.PNONTRANSNEW); |
| } |
| if ((action & OpCallbacks.ACT_CASCADE) != 0) { |
| sm.cascadePersist(call); |
| } |
| return sm; |
| } |
| |
| /** |
| * Temporarily manage the given instance in order to cascade the given |
| * operation through it. |
| */ |
| private void cascadeTransient(int op, Object obj, OpCallbacks call, |
| String errOp) { |
| PersistenceCapable pc = assertPersistenceCapable(obj); |
| |
| // if using detached state manager, don't replace |
| if (pc.pcGetStateManager() != null) |
| throw newDetachedException(obj, errOp); |
| |
| ClassMetaData meta = _repo.getMetaData(obj.getClass(), _loader, true); |
| StateManagerImpl sm = newStateManagerImpl(StateManagerId. |
| newInstance(this), meta); |
| sm.initialize(pc, PCState.TLOADED); |
| try { |
| switch (op) { |
| case OpCallbacks.OP_PERSIST: |
| sm.cascadePersist(call); |
| break; |
| case OpCallbacks.OP_DELETE: |
| sm.cascadeDelete(call); |
| break; |
| case OpCallbacks.OP_REFRESH: |
| sm.gatherCascadeRefresh(call); |
| break; |
| default: |
| throw new InternalException(String.valueOf(op)); |
| } |
| } |
| finally { |
| sm.release(true); |
| } |
| } |
| |
| @Override |
| public void deleteAll(Collection objs, OpCallbacks call) { |
| beginOperation(true); |
| try { |
| assertWriteOperation(); |
| |
| List<Exception> exceps = null; |
| Object obj; |
| for (Iterator<?> itr = objs.iterator(); itr.hasNext();) { |
| try { |
| obj = itr.next(); |
| if (obj != null) |
| delete(obj, getStateManagerImpl(obj, true), call); |
| } catch (UserException ue) { |
| exceps = add(exceps, ue); |
| } |
| } |
| throwNestedExceptions(exceps, false); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void delete(Object obj, OpCallbacks call) { |
| if (obj == null) |
| return; |
| |
| beginOperation(true); |
| try { |
| assertWriteOperation(); |
| delete(obj, getStateManagerImpl(obj, true), call); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Internal delete. |
| */ |
| void delete(Object obj, StateManagerImpl sm, OpCallbacks call) { |
| if (!operatingAdd(obj)) |
| return; |
| |
| int action = processArgument(OpCallbacks.OP_DELETE, obj, sm, call); |
| if (action == OpCallbacks.ACT_NONE) |
| return; |
| |
| // ACT_CASCADE |
| if ((action & OpCallbacks.ACT_RUN) == 0) { |
| if (sm != null) { |
| if (!sm.isEmbedded() || !sm.getDereferencedEmbedDependent()) { |
| sm.cascadeDelete(call); |
| } |
| } |
| else |
| cascadeTransient(OpCallbacks.OP_DELETE, obj, call, "delete"); |
| return; |
| } |
| |
| // ACT_RUN |
| if (sm != null) { |
| if (sm.isDetached()) |
| throw newDetachedException(obj, "delete"); |
| if ((action & OpCallbacks.ACT_CASCADE) != 0) { |
| if (!sm.isEmbedded() || !sm.getDereferencedEmbedDependent()) { |
| if (ValidatingLifecycleEventManager.class.isAssignableFrom(_lifeEventManager.getClass())) { |
| ValidatingLifecycleEventManager _validatingLCEventManager = |
| (ValidatingLifecycleEventManager) _lifeEventManager; |
| boolean saved = _validatingLCEventManager.setValidationEnabled(false); |
| try { |
| sm.cascadeDelete(call); |
| } finally { |
| _validatingLCEventManager.setValidationEnabled(saved); |
| } |
| } else { |
| sm.cascadeDelete(call); |
| } |
| } |
| } |
| sm.delete(); |
| } else if (assertPersistenceCapable(obj).pcIsDetached() == Boolean.TRUE) |
| throw newDetachedException(obj, "delete"); |
| } |
| |
| /** |
| * Throw an exception indicating that the current action can't be |
| * performed on a detached object. |
| */ |
| private OpenJPAException newDetachedException(Object obj, |
| String operation) { |
| throw new UserException(_loc.get("bad-detached-op", operation, |
| Exceptions.toString(obj))).setFailedObject(obj); |
| } |
| |
| @Override |
| public void releaseAll(Collection objs, OpCallbacks call) { |
| beginOperation(false); |
| try { |
| List<Exception> exceps = null; |
| for (Iterator<?> itr = objs.iterator(); itr.hasNext();) { |
| try { |
| release(itr.next(), call); |
| } catch (UserException ue) { |
| exceps = add(exceps, ue); |
| } |
| } |
| throwNestedExceptions(exceps, false); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void release(Object obj, OpCallbacks call) { |
| if (obj == null) |
| return; |
| |
| beginOperation(false); |
| try { |
| StateManagerImpl sm = getStateManagerImpl(obj, true); |
| int action = processArgument(OpCallbacks.OP_RELEASE, obj, sm, call); |
| |
| if (sm == null) |
| return; |
| if ((action & OpCallbacks.ACT_RUN) != 0 && sm.isPersistent()) { |
| boolean pending = sm.isPendingTransactional(); |
| sm.release(true); |
| if (pending) |
| removeFromPendingTransaction(sm); |
| } |
| } |
| catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public OpenJPAStateManager embed(Object obj, Object id, |
| OpenJPAStateManager owner, ValueMetaData ownerMeta) { |
| beginOperation(true); |
| try { |
| StateManagerImpl orig = getStateManagerImpl(obj, true); |
| if (orig != null) { |
| // if already embedded, nothing to do |
| if (orig.getOwner() == owner && orig.getMetaData(). |
| getEmbeddingMetaData() == ownerMeta) |
| return orig; |
| |
| // otherwise make sure pc is fully loaded for when we copy its |
| // data below |
| orig.load(_fc, StateManagerImpl.LOAD_ALL, null, null, false); |
| } |
| |
| // create new state manager with embedded metadata |
| ClassMetaData meta = ownerMeta.getEmbeddedMetaData(); |
| if (meta == null) |
| throw new InternalException(_loc.get("bad-embed", ownerMeta)); |
| |
| if (id == null) |
| id = StateManagerId.newInstance(this); |
| |
| StateManagerImpl sm = newStateManagerImpl(id, meta); |
| sm.setOwner((StateManagerImpl) owner, ownerMeta); |
| |
| PersistenceCapable copy; |
| PCState state; |
| Class<?> type = meta.getDescribedType(); |
| if (obj != null) { |
| // give copy and the original instance the same state manager |
| // so that we can copy fields from one to the other |
| StateManagerImpl copySM; |
| PersistenceCapable pc; |
| if (orig == null) { |
| copySM = sm; |
| pc = assertPersistenceCapable(obj); |
| pc.pcReplaceStateManager(sm); |
| } else { |
| copySM = orig; |
| pc = orig.getPersistenceCapable(); |
| } |
| |
| try { |
| // copy the instance. we do this even if it doesn't already |
| // have a state manager in case it is later assigned to a |
| // PC field; at that point it's too late to copy |
| copy = PCRegistry.newInstance(type, copySM, false); |
| int[] fields = new int[meta.getFields().length]; |
| for (int i = 0; i < fields.length; i++) |
| fields[i] = i; |
| copy.pcCopyFields(pc, fields); |
| state = PCState.ECOPY; |
| copy.pcReplaceStateManager(null); |
| } finally { |
| // if the instance didn't have a state manager to start, |
| // revert it to being transient |
| if (orig == null) |
| pc.pcReplaceStateManager(null); |
| } |
| } else { |
| copy = PCRegistry.newInstance(type, sm, false); |
| if ((_flags & FLAG_ACTIVE) != 0 && !_optimistic) |
| state = PCState.ECLEAN; |
| else |
| state = PCState.ENONTRANS; |
| } |
| |
| sm.initialize(copy, state); |
| return sm; |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * If not already cached, create an empty copy of the given state |
| * manager in the given state. |
| */ |
| OpenJPAStateManager copy(OpenJPAStateManager copy, PCState state) { |
| beginOperation(true); |
| try { |
| assertOpen(); |
| Object oid = copy.fetchObjectId(); |
| Class<?> type = copy.getManagedInstance().getClass(); |
| if (oid == null) |
| throw new InternalException(); |
| // cached instance? |
| StateManagerImpl sm = null; |
| if (!copy.isEmbedded()) |
| sm = getStateManagerImplById(oid, true); |
| if (sm == null) { |
| MetaDataRepository repos = _conf. |
| getMetaDataRepositoryInstance(); |
| ClassMetaData meta = repos.getMetaData(type, _loader, true); |
| // construct a new state manager with all info known |
| sm = newStateManagerImpl(oid, meta); |
| sm.setObjectId(oid); |
| sm.initialize(sm.getMetaData().getDescribedType(), state); |
| } |
| return sm; |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void refreshAll(Collection objs, OpCallbacks call) { |
| if (objs == null || objs.isEmpty()) |
| return; |
| |
| beginOperation(true); |
| try { |
| assertNontransactionalRead(); |
| |
| for (Object obj : objs) { |
| gatherCascadeRefresh(obj, call); |
| } |
| if (_operating.isEmpty()) |
| return; |
| if (_operating.size() == 1) |
| refreshInternal(_operating.iterator().next(), call); |
| else |
| refreshInternal(_operating, call); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void refresh(Object obj, OpCallbacks call) { |
| if (obj == null) |
| return; |
| |
| beginOperation(true); |
| try { |
| assertNontransactionalRead(); |
| |
| gatherCascadeRefresh(obj, call); |
| if (_operating.isEmpty()) |
| return; |
| if (_operating.size() == 1) |
| refreshInternal(_operating.iterator().next(), call); |
| else |
| refreshInternal(_operating, call); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Gathers all objects reachable through cascade-refresh relations |
| * into the operating set. |
| */ |
| void gatherCascadeRefresh(Object obj, OpCallbacks call) { |
| if (obj == null) |
| return; |
| if (!operatingAdd(obj)) |
| return; |
| |
| StateManagerImpl sm = getStateManagerImpl(obj, false); |
| int action = processArgument(OpCallbacks.OP_REFRESH, obj, sm, call); |
| if ((action & OpCallbacks.ACT_CASCADE) == 0) |
| return; |
| |
| if (sm != null) |
| sm.gatherCascadeRefresh(call); |
| else |
| cascadeTransient(OpCallbacks.OP_REFRESH, obj, call, "refresh"); |
| } |
| |
| /** |
| * This method is called with the full set of objects reachable via |
| * cascade-refresh relations from the user-given instances. |
| */ |
| protected void refreshInternal(Collection objs, OpCallbacks call) { |
| if (objs == null || objs.isEmpty()) |
| return; |
| List<Exception> exceps = null; |
| try { |
| // collect instances that need a refresh |
| Collection<OpenJPAStateManager> load = null; |
| StateManagerImpl sm; |
| Object obj; |
| for (Object o : objs) { |
| obj = o; |
| if (obj == null) |
| continue; |
| |
| try { |
| sm = getStateManagerImpl(obj, true); |
| if ((processArgument(OpCallbacks.OP_REFRESH, obj, sm, call) |
| & OpCallbacks.ACT_RUN) == 0) |
| continue; |
| |
| if (sm != null) { |
| if (sm.isDetached()) |
| throw newDetachedException(obj, "refresh"); |
| else if (sm.beforeRefresh(true)) { |
| if (load == null) |
| load = new ArrayList<>(objs.size()); |
| load.add(sm); |
| } |
| int level = _fc.getReadLockLevel(); |
| int timeout = _fc.getLockTimeout(); |
| _lm.refreshLock(sm, level, timeout, null); |
| sm.readLocked(level, level); |
| } |
| else if (assertPersistenceCapable(obj).pcIsDetached() |
| == Boolean.TRUE) |
| throw newDetachedException(obj, "refresh"); |
| } |
| catch (OpenJPAException ke) { |
| exceps = add(exceps, ke); |
| } |
| } |
| |
| // refresh all |
| if (load != null) { |
| Collection<Object> failed = _store.loadAll(load, null, |
| StoreManager.FORCE_LOAD_REFRESH, _fc, null); |
| if (failed != null && !failed.isEmpty()) |
| exceps = add(exceps, newObjectNotFoundException(failed)); |
| |
| // perform post-refresh transitions and make sure all fetch |
| // group fields are loaded |
| for (OpenJPAStateManager openJPAStateManager : load) { |
| sm = (StateManagerImpl) openJPAStateManager; |
| if (failed != null && failed.contains(sm.getId())) |
| continue; |
| |
| try { |
| sm.afterRefresh(); |
| sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null, |
| false); |
| } |
| catch (OpenJPAException ke) { |
| exceps = add(exceps, ke); |
| } |
| } |
| } |
| |
| // now invoke postRefresh on all the instances |
| for (Iterator<?> itr = objs.iterator(); itr.hasNext();) { |
| try { |
| sm = getStateManagerImpl(itr.next(), true); |
| if (sm != null && !sm.isDetached()) |
| fireLifecycleEvent(sm.getManagedInstance(), null, |
| sm.getMetaData(), LifecycleEvent.AFTER_REFRESH); |
| } catch (OpenJPAException ke) { |
| exceps = add(exceps, ke); |
| } |
| } |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } |
| throwNestedExceptions(exceps, false); |
| } |
| |
| /** |
| * Optimization for single-object refresh. |
| */ |
| protected void refreshInternal(Object obj, OpCallbacks call) { |
| try { |
| StateManagerImpl sm = getStateManagerImpl(obj, true); |
| if ((processArgument(OpCallbacks.OP_REFRESH, obj, sm, call) |
| & OpCallbacks.ACT_RUN) == 0) |
| return; |
| |
| if (sm != null) { |
| if (sm.isDetached()) |
| throw newDetachedException(obj, "refresh"); |
| else if (sm.beforeRefresh(false)) { |
| sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null, false); |
| sm.afterRefresh(); |
| } |
| int level = _fc.getReadLockLevel(); |
| int timeout = _fc.getLockTimeout(); |
| _lm.refreshLock(sm, level, timeout, null); |
| sm.readLocked(level, level); |
| fireLifecycleEvent(sm.getManagedInstance(), null, |
| sm.getMetaData(), LifecycleEvent.AFTER_REFRESH); |
| } else if (assertPersistenceCapable(obj).pcIsDetached() |
| == Boolean.TRUE) |
| throw newDetachedException(obj, "refresh"); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } |
| } |
| |
| |
| @Override |
| public void retrieveAll(Collection objs, boolean dfgOnly, |
| OpCallbacks call) { |
| if (objs == null || objs.isEmpty()) |
| return; |
| if (objs.size() == 1) { |
| retrieve(objs.iterator().next(), dfgOnly, call); |
| return; |
| } |
| |
| List<Exception> exceps = null; |
| beginOperation(true); |
| try { |
| assertOpen(); |
| assertNontransactionalRead(); |
| |
| // collect all hollow instances for load |
| Object obj; |
| Collection<OpenJPAStateManager> load = null; |
| StateManagerImpl sm; |
| Collection<StateManagerImpl> sms = new ArrayList<>(objs.size()); |
| for (Object o : objs) { |
| obj = o; |
| if (obj == null) |
| continue; |
| |
| try { |
| sm = getStateManagerImpl(obj, true); |
| if ((processArgument(OpCallbacks.OP_RETRIEVE, obj, sm, call) |
| & OpCallbacks.ACT_RUN) == 0) |
| continue; |
| |
| if (sm != null) { |
| if (sm.isDetached()) |
| throw newDetachedException(obj, "retrieve"); |
| if (sm.isPersistent()) { |
| sms.add(sm); |
| if (sm.getPCState() == PCState.HOLLOW) { |
| if (load == null) |
| load = new ArrayList<>(); |
| load.add(sm); |
| } |
| } |
| } |
| else if (assertPersistenceCapable(obj).pcIsDetached() |
| == Boolean.TRUE) |
| throw newDetachedException(obj, "retrieve"); |
| } |
| catch (UserException ue) { |
| exceps = add(exceps, ue); |
| } |
| } |
| |
| // load all hollow instances |
| Collection<Object> failed = null; |
| if (load != null) { |
| int mode = (dfgOnly) ? StoreManager.FORCE_LOAD_DFG |
| : StoreManager.FORCE_LOAD_ALL; |
| failed = _store.loadAll(load, null, mode, _fc, null); |
| if (failed != null && !failed.isEmpty()) |
| exceps = add(exceps, newObjectNotFoundException(failed)); |
| } |
| |
| // retrieve all non-failed instances |
| for (StateManagerImpl stateManager : sms) { |
| sm = stateManager; |
| if (failed != null && failed.contains(sm.getId())) |
| continue; |
| |
| int mode = (dfgOnly) ? StateManagerImpl.LOAD_FGS |
| : StateManagerImpl.LOAD_ALL; |
| try { |
| sm.beforeRead(-1); |
| sm.load(_fc, mode, null, null, false); |
| } |
| catch (OpenJPAException ke) { |
| exceps = add(exceps, ke); |
| } |
| } |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| throwNestedExceptions(exceps, false); |
| } |
| |
| @Override |
| public void retrieve(Object obj, boolean dfgOnly, OpCallbacks call) { |
| if (obj == null) |
| return; |
| |
| beginOperation(true); |
| try { |
| assertOpen(); |
| assertNontransactionalRead(); |
| |
| StateManagerImpl sm = getStateManagerImpl(obj, true); |
| if ((processArgument(OpCallbacks.OP_RETRIEVE, obj, sm, call) |
| & OpCallbacks.ACT_RUN) == 0) |
| return; |
| |
| if (sm != null) { |
| if (sm.isDetached()) |
| throw newDetachedException(obj, "retrieve"); |
| if (sm.isPersistent()) { |
| int mode = (dfgOnly) ? StateManagerImpl.LOAD_FGS |
| : StateManagerImpl.LOAD_ALL; |
| sm.beforeRead(-1); |
| sm.load(_fc, mode, null, null, false); |
| } |
| } else if (assertPersistenceCapable(obj).pcIsDetached() |
| == Boolean.TRUE) |
| throw newDetachedException(obj, "retrieve"); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void evictAll(OpCallbacks call) { |
| beginOperation(false); |
| try { |
| // evict all PClean and PNonTrans objects |
| Collection<StateManagerImpl> c = getManagedStates(); |
| StateManagerImpl sm; |
| for (StateManagerImpl stateManager : c) { |
| sm = stateManager; |
| if (sm.isPersistent() && !sm.isDirty()) |
| evict(sm.getManagedInstance(), call); |
| } |
| } |
| finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void evictAll(Collection objs, OpCallbacks call) { |
| List<Exception> exceps = null; |
| beginOperation(false); |
| try { |
| for (Iterator<?> itr = objs.iterator(); itr.hasNext();) { |
| try { |
| evict(itr.next(), call); |
| } catch (UserException ue) { |
| exceps = add(exceps, ue); |
| } |
| } |
| } finally { |
| endOperation(); |
| } |
| throwNestedExceptions(exceps, false); |
| } |
| |
| @Override |
| public void evictAll(Extent extent, OpCallbacks call) { |
| if (extent == null) |
| return; |
| |
| beginOperation(false); |
| try { |
| // evict all PClean and PNonTrans objects in extent |
| Collection<StateManagerImpl> c = getManagedStates(); |
| StateManagerImpl sm; |
| Class<?> cls; |
| for (StateManagerImpl stateManager : c) { |
| sm = stateManager; |
| if (sm.isPersistent() && !sm.isDirty()) { |
| cls = sm.getMetaData().getDescribedType(); |
| if (cls == extent.getElementType() |
| || (extent.hasSubclasses() |
| && extent.getElementType().isAssignableFrom(cls))) |
| evict(sm.getManagedInstance(), call); |
| } |
| } |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void evict(Object obj, OpCallbacks call) { |
| if (obj == null) |
| return; |
| |
| beginOperation(false); |
| try { |
| StateManagerImpl sm = getStateManagerImpl(obj, true); |
| if ((processArgument(OpCallbacks.OP_EVICT, obj, sm, call) |
| & OpCallbacks.ACT_RUN) == 0) |
| return; |
| if (sm == null) |
| return; |
| |
| sm.evict(); |
| if (_evictDataCache && sm.getObjectId() != null) { |
| DataCache cache = _conf.getDataCacheManagerInstance().selectCache(sm); |
| if (cache != null) |
| cache.remove(sm.getObjectId()); |
| } |
| } |
| catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public Object detach(Object obj, OpCallbacks call) { |
| if (obj == null) |
| return null; |
| if (call == null) |
| call = _call; |
| |
| beginOperation(true); |
| try { |
| return new DetachManager(this, false, call).detach(obj); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public Object[] detachAll(Collection objs, OpCallbacks call) { |
| if (objs == null) |
| return null; |
| if (objs.isEmpty()) |
| return EMPTY_OBJECTS; |
| if (call == null) |
| call = _call; |
| |
| beginOperation(true); |
| try { |
| return new DetachManager(this, false, call).detachAll(objs); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void detachAll(OpCallbacks call) { |
| detachAll(call, true); |
| } |
| |
| @Override |
| public void detachAll(OpCallbacks call, boolean flush) { |
| beginOperation(true); |
| try { |
| // If a flush is desired (based on input parm), then check if the |
| // "dirty" flag is set before calling flush(). |
| if (flush && (_flags & FLAG_FLUSH_REQUIRED) != 0) |
| flush(); |
| detachAllInternal(call); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| private void detachAllInternal(OpCallbacks call) { |
| if(_conf.getDetachStateInstance().getLiteAutoDetach()){ |
| detachAllInternalLite(); |
| return; |
| } |
| Collection<StateManagerImpl> states = getManagedStates(); |
| StateManagerImpl sm; |
| for (Iterator<StateManagerImpl> itr = states.iterator(); itr.hasNext();) { |
| sm = itr.next(); |
| if (!sm.isPersistent()) |
| itr.remove(); |
| else if (!sm.getMetaData().isDetachable()) { |
| sm.release(true); |
| itr.remove(); |
| } |
| } |
| if (states.isEmpty()) |
| return; |
| |
| if (call == null) |
| call = _call; |
| // Make sure ALL entities are detached, even new ones that are loaded |
| // during the detach processing |
| boolean origCascade = _compat.getCascadeWithDetach(); |
| _compat.setCascadeWithDetach(true); |
| try { |
| new DetachManager(this, true, call) |
| .detachAll(new ManagedObjectCollection(states)); |
| } |
| finally { |
| _compat.setCascadeWithDetach(origCascade); |
| } |
| } |
| |
| private void detachAllInternalLite() { |
| ManagedCache old = _cache; |
| _cache = new ManagedCache(this); |
| // TODO : should I call clear on old cache first? perhaps a memory leak? |
| Collection<StateManagerImpl> states = old.copy(); |
| |
| // Clear out all persistence context caches. |
| if (_transCache != null) { |
| _transCache.clear(); |
| } |
| if (_transAdditions != null) { |
| _transAdditions.clear(); |
| } |
| if (_pending != null) { |
| _pending = null; |
| } |
| if (_dmLite == null) { |
| _dmLite = new DetachManagerLite(_conf); |
| } |
| _dmLite.detachAll(states); |
| } |
| @Override |
| public Object attach(Object obj, boolean copyNew, OpCallbacks call) { |
| if (obj == null) |
| return null; |
| |
| beginOperation(true); |
| try { |
| // make sure not to try to set rollback only if this fails |
| assertWriteOperation(); |
| try { |
| return new AttachManager(this, copyNew, call).attach(obj); |
| } catch (OptimisticException oe) { |
| setRollbackOnly(oe); |
| throw oe.setFatal(true); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } |
| } |
| finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public Object[] attachAll(Collection objs, boolean copyNew, |
| OpCallbacks call) { |
| if (objs == null) |
| return null; |
| if (objs.isEmpty()) |
| return EMPTY_OBJECTS; |
| |
| beginOperation(true); |
| try { |
| // make sure not to try to set rollback only if this fails |
| assertWriteOperation(); |
| try { |
| return new AttachManager(this, copyNew, call).attachAll(objs); |
| } catch (OptimisticException oe) { |
| setRollbackOnly(oe); |
| throw oe.setFatal(true); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } |
| } |
| finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void nontransactionalAll(Collection objs, OpCallbacks call) { |
| beginOperation(true); |
| try { |
| List<Exception> exceps = null; |
| for (Iterator<?> itr = objs.iterator(); itr.hasNext();) { |
| try { |
| nontransactional(itr.next(), call); |
| } catch (UserException ue) { |
| exceps = add(exceps, ue); |
| } |
| } |
| throwNestedExceptions(exceps, false); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void nontransactional(Object obj, OpCallbacks call) { |
| if (obj == null) |
| return; |
| |
| beginOperation(true); |
| try { |
| StateManagerImpl sm = getStateManagerImpl(obj, true); |
| if ((processArgument(OpCallbacks.OP_NONTRANSACTIONAL, obj, sm, call) |
| & OpCallbacks.ACT_RUN) == 0) |
| return; |
| if (sm != null) |
| sm.nontransactional(); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Make the given instances transactional. |
| */ |
| @Override |
| public void transactionalAll(Collection objs, boolean updateVersion, |
| OpCallbacks call) { |
| if (objs.isEmpty()) |
| return; |
| if (objs.size() == 1) { |
| transactional(objs.iterator().next(), updateVersion, call); |
| return; |
| } |
| |
| beginOperation(true); |
| try { |
| // collect all hollow instances for load, and make unmananged |
| // instances transient-transactional |
| Collection<OpenJPAStateManager> load = null; |
| Object obj; |
| StateManagerImpl sm; |
| ClassMetaData meta; |
| Collection<StateManagerImpl> sms = new LinkedHashSet<>(objs.size()); |
| List<Exception> exceps = null; |
| for (Object o : objs) { |
| obj = o; |
| if (obj == null) |
| continue; |
| |
| try { |
| sm = getStateManagerImpl(obj, true); |
| if ((processArgument(OpCallbacks.OP_TRANSACTIONAL, obj, sm, |
| call) & OpCallbacks.ACT_RUN) == 0) |
| continue; |
| |
| if (sm == null) { |
| // manage transient instance |
| meta = _repo.getMetaData(obj.getClass(), _loader, true); |
| |
| sm = newStateManagerImpl |
| (StateManagerId.newInstance(this), meta); |
| sm.initialize(assertPersistenceCapable(obj), |
| PCState.TCLEAN); |
| } |
| else if (sm.isPersistent()) { |
| assertActiveTransaction(); |
| sms.add(sm); |
| if (sm.getPCState() == PCState.HOLLOW) { |
| if (load == null) |
| load = new ArrayList<>(); |
| load.add(sm); |
| } |
| |
| sm.setCheckVersion(true); |
| if (updateVersion) |
| sm.setUpdateVersion(true); |
| _flags |= FLAG_FLUSH_REQUIRED; // version check/up |
| } |
| } |
| catch (UserException ue) { |
| exceps = add(exceps, ue); |
| } |
| } |
| |
| // load all hollow instances |
| Collection<Object> failed = null; |
| if (load != null) { |
| failed = _store.loadAll(load, null, StoreManager.FORCE_LOAD_NONE, |
| _fc, null); |
| if (failed != null && !failed.isEmpty()) |
| exceps = add(exceps, |
| newObjectNotFoundException(failed)); |
| } |
| |
| transactionalStatesAll(sms, failed, exceps); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Make the given instances transactional. |
| */ |
| @Override |
| public void transactional(Object obj, boolean updateVersion, |
| OpCallbacks call) { |
| if (obj == null) |
| return; |
| |
| beginOperation(true); |
| try { |
| StateManagerImpl sm = getStateManagerImpl(obj, true); |
| if ((processArgument(OpCallbacks.OP_TRANSACTIONAL, obj, sm, call) |
| & OpCallbacks.ACT_RUN) == 0) |
| return; |
| |
| if (sm != null && sm.isPersistent()) { |
| assertActiveTransaction(); |
| sm.transactional(); |
| sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null, false); |
| sm.setCheckVersion(true); |
| if (updateVersion) |
| sm.setUpdateVersion(true); |
| _flags |= FLAG_FLUSH_REQUIRED; // version check/up |
| } else if (sm == null) { |
| // manage transient instance |
| ClassMetaData meta = _repo.getMetaData(obj.getClass(), _loader, true); |
| Object id = StateManagerId.newInstance(this); |
| sm = newStateManagerImpl(id, meta); |
| sm.initialize(assertPersistenceCapable(obj), |
| PCState.TCLEAN); |
| } |
| } |
| catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Transition the given state managers to transactional. |
| */ |
| private void transactionalStatesAll(Collection sms, Collection failed, |
| List<Exception> exceps) { |
| // make instances transactional and make sure they are loaded |
| StateManagerImpl sm; |
| for (Object o : sms) { |
| sm = (StateManagerImpl) o; |
| if (failed != null && failed.contains(sm.getId())) |
| continue; |
| |
| try { |
| sm.transactional(); |
| sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null, false); |
| } |
| catch (OpenJPAException ke) { |
| exceps = add(exceps, ke); |
| } |
| } |
| throwNestedExceptions(exceps, false); |
| } |
| |
| ///////////////// |
| // Extent, Query |
| ///////////////// |
| |
| @Override |
| public Extent newExtent(Class type, boolean subclasses) { |
| return newExtent(type, subclasses, null); |
| } |
| |
| private Extent newExtent(Class type, boolean subclasses, |
| FetchConfiguration fetch) { |
| beginOperation(true); |
| try { |
| ExtentImpl extent = new ExtentImpl(this, type, subclasses, fetch); |
| if (_extents == null) |
| _extents = new ReferenceHashSet(AbstractReferenceMap.ReferenceStrength.WEAK); |
| _extents.add(extent); |
| |
| return extent; |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public Iterator extentIterator(Class type, boolean subclasses, |
| FetchConfiguration fetch, boolean ignoreChanges) { |
| Extent extent = newExtent(type, subclasses, fetch); |
| extent.setIgnoreChanges(ignoreChanges); |
| return extent.iterator(); |
| } |
| |
| @Override |
| public Query newQuery(String lang, Class cls, Object query) { |
| Query q = newQuery(lang, query); |
| q.setCandidateType(cls, true); |
| return q; |
| } |
| |
| @Override |
| public Query newQuery(String lang, Object query) { |
| // common mistakes |
| if (query instanceof Extent || query instanceof Class) |
| throw new UserException(_loc.get("bad-new-query")); |
| |
| beginOperation(false); |
| try { |
| StoreQuery sq = _store.newQuery(lang); |
| if (sq == null) { |
| ExpressionParser ep = QueryLanguages.parserForLanguage(lang); |
| if (ep != null) |
| sq = new ExpressionStoreQuery(ep); |
| else if (QueryLanguages.LANG_METHODQL.equals(lang)) |
| sq = new MethodStoreQuery(); |
| else |
| throw new UnsupportedException(lang); |
| } |
| |
| Query q = newQueryImpl(lang, sq); |
| q.setIgnoreChanges(_ignoreChanges); |
| if (query != null) |
| q.setQuery(query); |
| |
| // track queries |
| if (_queries == null) |
| _queries = new ReferenceHashSet(AbstractReferenceMap.ReferenceStrength.WEAK); |
| _queries.add(q); |
| return q; |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Create a new query. |
| */ |
| protected QueryImpl newQueryImpl(String lang, StoreQuery sq) { |
| return new QueryImpl(this, lang, sq); |
| } |
| |
| @Override |
| public Seq getIdentitySequence(ClassMetaData meta) { |
| if (meta == null) |
| return null; |
| return getSequence(meta, null); |
| } |
| |
| @Override |
| public Seq getValueSequence(FieldMetaData fmd) { |
| if (fmd == null) |
| return null; |
| return getSequence(fmd.getDefiningMetaData(), fmd); |
| } |
| |
| /** |
| * Return a sequence for the given class and optional field. |
| */ |
| private Seq getSequence(ClassMetaData meta, FieldMetaData fmd) { |
| // get sequence strategy from metadata |
| int strategy; |
| if (fmd == null) |
| strategy = meta.getIdentityStrategy(); |
| else |
| strategy = fmd.getValueStrategy(); |
| |
| // we can handle non-native strategies without the store manager |
| switch (strategy) { |
| case ValueStrategies.UUID_HEX: |
| return UUIDHexSeq.getInstance(); |
| case ValueStrategies.UUID_STRING: |
| return UUIDStringSeq.getInstance(); |
| case ValueStrategies.UUID_TYPE4_HEX: |
| return UUIDType4HexSeq.getInstance(); |
| case ValueStrategies.UUID_TYPE4_STRING: |
| return UUIDType4StringSeq.getInstance(); |
| case ValueStrategies.SEQUENCE: |
| SequenceMetaData smd = (fmd == null) |
| ? meta.getIdentitySequenceMetaData() |
| : fmd.getValueSequenceMetaData(); |
| return smd.getInstance(_loader); |
| default: |
| // use store manager for native sequence |
| if (fmd == null) { |
| // This will return a sequence even for app id classes, |
| // which is what we want for backwards-compatibility. |
| // Even if user uses Application Identity, |
| // user might use custom sequence information. |
| // So, first, the sequence should be checked. |
| // Trying to get primary key field if it has |
| // sequence meta data. |
| FieldMetaData[] pks = meta.getPrimaryKeyFields(); |
| if (pks != null && pks.length == 1) { |
| smd = pks[0].getValueSequenceMetaData(); |
| } else { |
| smd = meta.getIdentitySequenceMetaData(); |
| } |
| |
| if (smd != null) { |
| return smd.getInstance(_loader); |
| } else { |
| return _store.getDataStoreIdSequence(meta); |
| } |
| } |
| return _store.getValueSequence(fmd); |
| } |
| } |
| |
| /////////// |
| // Locking |
| /////////// |
| |
| @Override |
| public void lock(Object obj, OpCallbacks call) { |
| if (obj == null) |
| return; |
| |
| beginOperation(true); // have to sync or lock level always NONE |
| try { |
| lock(obj, _fc.getWriteLockLevel(), _fc.getLockTimeout(), call); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void lock(Object obj, int level, int timeout, OpCallbacks call) { |
| if (obj == null) |
| return; |
| |
| beginOperation(true); |
| try { |
| assertActiveTransaction(); |
| |
| StateManagerImpl sm = getStateManagerImpl(obj, true); |
| if ((processArgument(OpCallbacks.OP_LOCK, obj, sm, call) |
| & OpCallbacks.ACT_RUN) == 0) |
| return; |
| if (sm == null || !sm.isPersistent()) |
| return; |
| |
| _lm.lock(sm, level, timeout, null); |
| sm.readLocked(level, level); // use same level for future write |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void lockAll(Collection objs, OpCallbacks call) { |
| if (objs.isEmpty()) |
| return; |
| |
| beginOperation(true); // have to sync or lock level always NONE |
| try { |
| lockAll(objs, _fc.getWriteLockLevel(), _fc.getLockTimeout(), |
| call); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public void lockAll(Collection objs, int level, int timeout, |
| OpCallbacks call) { |
| if (objs.isEmpty()) |
| return; |
| if (objs.size() == 1) { |
| lock(objs.iterator().next(), level, timeout, call); |
| return; |
| } |
| |
| beginOperation(true); |
| try { |
| assertActiveTransaction(); |
| |
| Collection<StateManagerImpl> sms = new LinkedHashSet<>(objs.size()); |
| Object obj; |
| StateManagerImpl sm; |
| for (Object o : objs) { |
| obj = o; |
| if (obj == null) |
| continue; |
| |
| sm = getStateManagerImpl(obj, true); |
| if ((processArgument(OpCallbacks.OP_LOCK, obj, sm, call) |
| & OpCallbacks.ACT_RUN) == 0) |
| continue; |
| if (sm != null && sm.isPersistent()) |
| sms.add(sm); |
| } |
| |
| _lm.lockAll(sms, level, timeout, null); |
| for (StateManagerImpl stateManager : sms) { |
| stateManager.readLocked(level, level); |
| } |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new GeneralException(re); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| ////////////// |
| // Connection |
| ////////////// |
| |
| @Override |
| public boolean cancelAll() { |
| // this method does not lock, since we want to allow a different |
| // thread to be able to cancel on a locked-up persistence manager |
| |
| assertOpen(); |
| try { |
| // if we're flushing, have to set rollback only -- do this before we |
| // attempt to cancel, because otherwise the cancel might case the |
| // transaction to complete before we have a chance to set the |
| // rollback only flag |
| if ((_flags & FLAG_STORE_FLUSHING) != 0) |
| setRollbackOnlyInternal(new UserException()); |
| return _store.cancelAll(); |
| } catch (OpenJPAException ke) { |
| throw ke; |
| } catch (RuntimeException re) { |
| throw new StoreException(re); |
| } |
| } |
| |
| @Override |
| public Object getConnection() { |
| assertOpen(); |
| if (!_conf.supportedOptions().contains |
| (OpenJPAConfiguration.OPTION_DATASTORE_CONNECTION)) |
| throw new UnsupportedException(_loc.get("conn-not-supported")); |
| |
| return _store.getClientConnection(); |
| } |
| |
| @Override |
| public boolean hasConnection() { |
| assertOpen(); |
| return (_flags & FLAG_RETAINED_CONN) != 0; |
| } |
| |
| /** |
| * Tell store to retain connection if we haven't already. |
| */ |
| private void retainConnection() { |
| if ((_flags & FLAG_RETAINED_CONN) == 0) { |
| _store.retainConnection(); |
| _flags |= FLAG_RETAINED_CONN; |
| } |
| } |
| |
| /** |
| * Tell store to release connection if we have retained one. |
| */ |
| private void releaseConnection() { |
| if ((_flags & FLAG_RETAINED_CONN) != 0) { |
| _store.releaseConnection(); |
| _flags &= ~FLAG_RETAINED_CONN; |
| } |
| } |
| |
| ///////// |
| // Cache |
| ///////// |
| |
| @Override |
| public Collection getManagedObjects() { |
| beginOperation(false); |
| try { |
| return new ManagedObjectCollection(getManagedStates()); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public Collection getTransactionalObjects() { |
| beginOperation(false); |
| try { |
| return new ManagedObjectCollection(getTransactionalStates()); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public Collection getPendingTransactionalObjects() { |
| beginOperation(false); |
| try { |
| return new ManagedObjectCollection |
| (getPendingTransactionalStates()); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public Collection getDirtyObjects() { |
| beginOperation(false); |
| try { |
| return new ManagedObjectCollection(getDirtyStates()); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public boolean getOrderDirtyObjects() { |
| return _orderDirty; |
| } |
| |
| @Override |
| public void setOrderDirtyObjects(boolean order) { |
| _orderDirty = order; |
| } |
| |
| /** |
| * Return a copy of all managed state managers. |
| */ |
| protected Collection getManagedStates() { |
| return _cache.copy(); |
| } |
| |
| /** |
| * Return a copy of all transactional state managers. |
| */ |
| protected Collection<StateManagerImpl> getTransactionalStates() { |
| if (!hasTransactionalObjects()) { |
| // return a new empty set. Entities may be added by TransactionListeners |
| return new LinkedHashSet<>(); |
| } |
| return _transCache.copy(); |
| } |
| |
| /** |
| * Whether or not there are any transactional objects in the current |
| * persistence context. If there are any instances with untracked state, |
| * this method will cause those instances to be scanned. |
| */ |
| private boolean hasTransactionalObjects() { |
| _cache.dirtyCheck(); |
| return _transCache != null; |
| } |
| |
| /** |
| * Return a copy of all dirty state managers. |
| */ |
| protected Collection getDirtyStates() { |
| if (!hasTransactionalObjects()) |
| return Collections.EMPTY_SET; |
| |
| return _transCache.copyDirty(); |
| } |
| |
| /** |
| * Return a copy of all state managers which will become |
| * transactional upon the next transaction. |
| */ |
| protected Collection getPendingTransactionalStates() { |
| if (_pending == null) |
| return Collections.EMPTY_SET; |
| return new LinkedHashSet<>(_pending); |
| } |
| |
| /** |
| * Set the cached StateManager for the instance that had the given oid. |
| * This method must not be called multiple times for new instances. |
| * |
| * @param id the id previously used by the instance |
| * @param sm the state manager for the instance; if the state |
| * manager is transient, we'll stop managing the instance; |
| * if it has updated its oid, we'll re-cache under the new oid |
| * @param status one of our STATUS constants describing why we're |
| * setting the state manager |
| */ |
| protected void setStateManager(Object id, StateManagerImpl sm, int status) { |
| lock(); |
| try { |
| switch (status) { |
| case STATUS_INIT: |
| // Only reset the flushed flag is this is a new instance. |
| if (sm.isNew() && _compat.getResetFlushFlagForCascadePersist()) {// OPENJPA-2051 |
| _flags &= ~FLAG_FLUSHED; |
| } |
| _cache.add(sm); |
| break; |
| case STATUS_TRANSIENT: |
| _cache.remove(id, sm); |
| break; |
| case STATUS_OID_ASSIGN: |
| assignObjectId(_cache, id, sm); |
| break; |
| case STATUS_COMMIT_NEW: |
| _cache.commitNew(id, sm); |
| break; |
| default: |
| throw new InternalException(); |
| } |
| } |
| finally { |
| unlock(); |
| } |
| } |
| |
| /** |
| * Notify the broker that the given state manager should |
| * be added to the set of instances involved in the current transaction. |
| */ |
| void addToTransaction(StateManagerImpl sm) { |
| // we only add clean instances now; dirty instances are added in |
| // the setDirty callback |
| if (sm.isDirty()) |
| return; |
| |
| lock(); |
| try { |
| if (!hasTransactionalObjects()) |
| _transCache = new TransactionalCache(_orderDirty); |
| _transCache.addClean(sm); |
| } finally { |
| unlock(); |
| } |
| } |
| |
| /** |
| * Notify the persistence manager that the given state manager should |
| * be removed from the set of instances involved in the current transaction. |
| */ |
| void removeFromTransaction(StateManagerImpl sm) { |
| lock(); |
| try { |
| if (_transCache != null) |
| // intentional direct access; we don't want to recompute |
| // dirtiness while removing instances from the transaction |
| _transCache.remove(sm); |
| if (_derefCache != null && !sm.isPersistent()) |
| _derefCache.remove(sm); |
| } finally { |
| unlock(); |
| } |
| } |
| |
| /** |
| * Notification that the given instance has been dirtied. This |
| * notification is given when an object first transitions to a dirty state, |
| * and every time the object is modified by the user thereafter. |
| */ |
| void setDirty(StateManagerImpl sm, boolean firstDirty) { |
| if (sm.isPersistent()) |
| _flags |= FLAG_FLUSH_REQUIRED; |
| |
| if (_savepoints != null && !_savepoints.isEmpty()) { |
| if (_savepointCache == null) |
| _savepointCache = new HashSet<>(); |
| _savepointCache.add(sm); |
| } |
| |
| if (firstDirty && sm.isTransactional()) { |
| lock(); |
| try { |
| // cache dirty instance |
| if (!hasTransactionalObjects()) |
| _transCache = new TransactionalCache(_orderDirty); |
| _transCache.addDirty(sm); |
| |
| // also record that the class is dirty |
| if (sm.isNew()) { |
| if (_persistedClss == null) |
| _persistedClss = new HashSet<>(); |
| _persistedClss.add(sm.getMetaData().getDescribedType()); |
| } else if (sm.isDeleted()) { |
| if (_deletedClss == null) |
| _deletedClss = new HashSet<>(); |
| _deletedClss.add(sm.getMetaData().getDescribedType()); |
| } else { |
| if (_updatedClss == null) |
| _updatedClss = new HashSet<>(); |
| _updatedClss.add(sm.getMetaData().getDescribedType()); |
| } |
| |
| // if tracking changes and this instance wasn't already dirty, |
| // add to changed set; we use this for detecting instances that |
| // enter the transaction during pre store |
| if ((_flags & FLAG_PRESTORING) != 0) { |
| if (_transAdditions == null) |
| _transAdditions = new HashSet<>(); |
| _transAdditions.add(sm); |
| } |
| } finally { |
| unlock(); |
| } |
| } |
| } |
| |
| /** |
| * Notify the broker that the given state manager should |
| * be added to the set of instances that will become transactional |
| * on the next transaction |
| */ |
| void addToPendingTransaction(StateManagerImpl sm) { |
| lock(); |
| try { |
| if (_pending == null) |
| _pending = new HashSet<>(); |
| _pending.add(sm); |
| } finally { |
| unlock(); |
| } |
| } |
| |
| /** |
| * Notify the persistence manager that the given state manager should |
| * be removed from the set of instances involved in the next transaction. |
| */ |
| void removeFromPendingTransaction(StateManagerImpl sm) { |
| lock(); |
| try { |
| if (_pending != null) |
| _pending.remove(sm); |
| if (_derefCache != null && !sm.isPersistent()) |
| _derefCache.remove(sm); |
| } finally { |
| unlock(); |
| } |
| } |
| |
| /** |
| * Add a dereferenced dependent object to the persistence manager's cache. |
| * On flush, these objects will be deleted. |
| */ |
| void addDereferencedDependent(StateManagerImpl sm) { |
| lock(); |
| try { |
| // if we're in the middle of flush and introducing more derefs |
| // via instance callbacks, add them to the special additions set |
| if ((_flags & FLAG_DEREFDELETING) != 0) { |
| if (_derefAdditions == null) |
| _derefAdditions = new HashSet<>(); |
| _derefAdditions.add(sm); |
| } else { |
| if (_derefCache == null) |
| _derefCache = new HashSet<>(); |
| _derefCache.add(sm); |
| } |
| } |
| finally { |
| unlock(); |
| } |
| } |
| |
| /** |
| * Remove the given previously dereferenced dependent object from the |
| * cache. It is now referenced. |
| */ |
| void removeDereferencedDependent(StateManagerImpl sm) { |
| lock(); |
| try { |
| boolean removed = false; |
| if (_derefAdditions != null) |
| removed = _derefAdditions.remove(sm); |
| if (!removed && (_derefCache == null || !_derefCache.remove(sm))) |
| throw new InvalidStateException(_loc.get("not-derefed", |
| Exceptions.toString(sm.getManagedInstance()))). |
| setFailedObject(sm.getManagedInstance()). |
| setFatal(true); |
| } finally { |
| unlock(); |
| } |
| } |
| |
| @Override |
| public void dirtyType(Class cls) { |
| if (cls == null) |
| return; |
| |
| beginOperation(false); |
| try { |
| if (_updatedClss == null) |
| _updatedClss = new HashSet<>(); |
| _updatedClss.add(cls); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| @Override |
| public Collection getPersistedTypes() { |
| if (_persistedClss == null || _persistedClss.isEmpty()) |
| return Collections.EMPTY_SET; |
| return Collections.unmodifiableCollection(_persistedClss); |
| } |
| |
| @Override |
| public Collection getUpdatedTypes() { |
| if (_updatedClss == null || _updatedClss.isEmpty()) |
| return Collections.EMPTY_SET; |
| return Collections.unmodifiableCollection(_updatedClss); |
| } |
| |
| @Override |
| public Collection getDeletedTypes() { |
| if (_deletedClss == null || _deletedClss.isEmpty()) |
| return Collections.EMPTY_SET; |
| return Collections.unmodifiableCollection(_deletedClss); |
| } |
| |
| /////////// |
| // Closing |
| /////////// |
| |
| @Override |
| public boolean isClosed() { |
| return _closed; |
| } |
| |
| @Override |
| public boolean isCloseInvoked() { |
| return _closed || (_flags & FLAG_CLOSE_INVOKED) != 0; |
| } |
| |
| @Override |
| public void close() { |
| beginOperation(false); |
| try { |
| // throw an exception if closing in an active local trans |
| if (!_managed && (_flags & FLAG_ACTIVE) != 0) |
| throw new InvalidStateException(_loc.get("active")); |
| |
| // only close if not active; if active managed trans wait |
| // for completion |
| _flags |= FLAG_CLOSE_INVOKED; |
| |
| if ((_flags & FLAG_ACTIVE) == 0) |
| free(); |
| } finally { |
| endOperation(); |
| } |
| } |
| |
| /** |
| * Free the resources used by this persistence manager. |
| */ |
| protected void free() { |
| RuntimeException err = null; |
| if ((_autoDetach & DETACH_CLOSE) != 0) { |
| try { |
| detachAllInternal(_call); |
| } catch (RuntimeException re) { |
| err = re; |
| } |
| } |
| |
| _sync = null; |
| _userObjects = null; |
| _cache.clear(); |
| _transCache = null; |
| _persistedClss = null; |
| _updatedClss = null; |
| _deletedClss = null; |
| _derefCache = null; |
| _pending = null; |
| _loader = null; |
| _transEventManager = null; |
| _lifeEventManager = null; |
| |
| OpenJPASavepoint save; |
| while (_savepoints != null && !_savepoints.isEmpty()) { |
| save = |
| (OpenJPASavepoint) _savepoints.remove(_savepoints.size() - 1); |
| save.release(false); |
| } |
| _savepoints = null; |
| _savepointCache = null; |
| |
| if (_queries != null) { |
| for (Iterator<?> itr = _queries.iterator(); itr.hasNext();) { |
| try { |
| ((Query) itr.next()).closeResources(); |
| } catch (RuntimeException re) { |
| } |
| } |
| _queries = null; |
| } |
| |
| if (_extents != null) { |
| Extent e; |
| for (Object extent : _extents) { |
| e = (Extent) extent; |
| try { |
| e.closeAll(); |
| } |
| catch (RuntimeException re) { |
| } |
| } |
| _extents = null; |
| } |
| |
| try { releaseConnection(); } catch (RuntimeException re) {} |
| |
| _lm.close(); |
| _store.close(); |
| if (_instm != null) { |
| _instm.stop(InstrumentationLevel.BROKER, this); |
| } |
| _flags = 0; |
| _closed = true; |
| if (_log.isTraceEnabled()) |
| _closedException = new IllegalStateException(); |
| |
| _factory.releaseBroker(this); |
| |
| if (err != null) |
| throw err; |
| } |
| |
| /////////////////// |
| // Synchronization |
| /////////////////// |
| |
| @Override |
| public void lock() { |
| if (_lock != null) |
| _lock.lock(); |
| } |
| |
| @Override |
| public void unlock() { |
| if (_lock != null) |
| _lock.unlock(); |
| } |
| |
| //////////////////// |
| // State management |
| //////////////////// |
| |
| @Override |
| public Object newInstance(Class cls) { |
| assertOpen(); |
| |
| if (!cls.isInterface() && Modifier.isAbstract(cls.getModifiers())) |
| throw new UnsupportedOperationException(_loc.get |
| ("new-abstract", cls).getMessage()); |
| |
| // 1.5 doesn't initialize classes without a true Class.forName |
| if (!PCRegistry.isRegistered(cls)) { |
| try { |
| Class.forName(cls.getName(), true, |
| AccessController.doPrivileged( |
| J2DoPrivHelper.getClassLoaderAction(cls))); |
| } catch (Throwable t) { |
| } |
| } |
| |
| if (_repo.getMetaData(cls, getClassLoader(), false) == null) |
| throw new IllegalArgumentException( |
| _loc.get("no-interface-metadata", cls.getName()).getMessage()); |
| |
| try { |
| return PCRegistry.newInstance(cls, null, false); |
| } catch (IllegalStateException ise) { |
| IllegalArgumentException iae = |
| new IllegalArgumentException(ise.getMessage()); |
| iae.setStackTrace(ise.getStackTrace()); |
| throw iae; |
| } |
| } |
| |
| @Override |
| public Object getObjectId(Object obj) { |
| assertOpen(); |
| if (ImplHelper.isManageable(obj)) { |
| PersistenceCapable pc = ImplHelper.toPersistenceCapable(obj, _conf); |
| if (pc != null) { |
| if (pc.pcGetStateManager() == null) { |
| // If the statemanager is null the call to pcFetchObjectId always returns null. Create a new object |
| // id. |
| return ApplicationIds.create(pc, _repo.getMetaData(pc.getClass(), null, true)); |
| } |
| return pc.pcFetchObjectId(); |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public int getLockLevel(Object o) { |
| assertOpen(); |
| if (o == null) |
| return LockLevels.LOCK_NONE; |
| |
| OpenJPAStateManager sm = getStateManager(o); |
| if (sm == null) |
| return LockLevels.LOCK_NONE; |
| return getLockManager().getLockLevel(sm); |
| } |
| |
| @Override |
| public Object getVersion(Object obj) { |
| assertOpen(); |
| if (ImplHelper.isManageable(obj)) |
| return (ImplHelper.toPersistenceCapable(obj, _conf)).pcGetVersion(); |
| return null; |
| } |
| |
| @Override |
| public boolean isDirty(Object obj) { |
| assertOpen(); |
| if (ImplHelper.isManageable(obj)) { |
| PersistenceCapable pc = ImplHelper.toPersistenceCapable(obj, _conf); |
| return pc.pcIsDirty(); |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean isTransactional(Object obj) { |
| assertOpen(); |
| if (ImplHelper.isManageable(obj)) |
| return (ImplHelper.toPersistenceCapable(obj, _conf)) |
| .pcIsTransactional(); |
| return false; |
| } |
| |
| @Override |
| public boolean isPersistent(Object obj) { |
| assertOpen(); |
| if (ImplHelper.isManageable(obj)) |
| return (ImplHelper.toPersistenceCapable(obj, _conf)). |
| pcIsPersistent(); |
| return false; |
| } |
| |
| @Override |
| public boolean isNew(Object obj) { |
| assertOpen(); |
| if (ImplHelper.isManageable(obj)) |
| return (ImplHelper.toPersistenceCapable(obj, _conf)).pcIsNew(); |
| return false; |
| } |
| |
| @Override |
| public boolean isDeleted(Object obj) { |
| assertOpen(); |
| if (ImplHelper.isManageable(obj)) |
| return (ImplHelper.toPersistenceCapable(obj, _conf)).pcIsDeleted(); |
| return false; |
| } |
| @Override |
| public boolean isDetached(Object obj) { |
| return isDetached(obj, true); |
| } |
| |
| /** |
| * This method makes a best effort to determine if the provided object is detached. |
| * |
| * @param find |
| * - If true, as a last resort this method will check whether or not the provided object exists in the |
| * DB. If it is in the DB, the provided object is detached. |
| * @return - True if the provided obj is detached, false otherwise. |
| */ |
| public boolean isDetached(Object obj, boolean find) { |
| if (!(ImplHelper.isManageable(obj))) |
| return false; |
| |
| PersistenceCapable pc = ImplHelper.toPersistenceCapable(obj, _conf); |
| if (pc.pcGetStateManager() instanceof DetachedStateManager) |
| return true; |
| Boolean detached = pc.pcIsDetached(); |
| if (detached != null) |
| return detached; |
| |
| if(!find){ |
| return false; |
| } |
| // last resort: instance is detached if it has a store record |
| ClassMetaData meta = _repo.getMetaData(ImplHelper.getManagedInstance(pc).getClass(), _loader, true); |
| Object oid = ApplicationIds.create(pc, meta); |
| if (oid == null) |
| return false; |
| |
| return find(oid, null, EXCLUDE_ALL, null, 0) != null; |
| } |
| |
| @Override |
| public OpenJPAStateManager getStateManager(Object obj) { |
| assertOpen(); |
| return getStateManagerImpl(obj, false); |
| } |
| |
| /** |
| * Return the state manager for the given instance, or null. |
| * |
| * @param assertThisContext if true, thow an exception if the given |
| * object is managed by another broker |
| */ |
| protected StateManagerImpl getStateManagerImpl(Object obj, |
| boolean assertThisContext) { |
| if (ImplHelper.isManageable(obj)) { |
| PersistenceCapable pc = ImplHelper.toPersistenceCapable(obj, _conf); |
| BrokerImpl pcBroker = (BrokerImpl)pc.pcGetGenericContext(); |
| if (pcBroker == this || isFromWriteBehindCallback()) |
| return (StateManagerImpl) pc.pcGetStateManager(); |
| if (assertThisContext && pcBroker != null) |
| throw new UserException(_loc.get("not-managed", |
| Exceptions.toString(obj))).setFailedObject(obj); |
| } |
| return null; |
| } |
| |
| /** |
| * Return the state manager for the given oid. |
| * |
| * @param allowNew if true, objects made persistent in the current |
| * transaction will be included in the search; if |
| * multiple new objects match the given oid, it is |
| * undefined which will be returned |
| */ |
| protected StateManagerImpl getStateManagerImplById(Object oid, |
| boolean allowNew) { |
| return _cache.getById(oid, allowNew); |
| } |
| |
| /** |
| * Return the given instance as a {@link PersistenceCapable}. |
| * If the instance is not manageable throw the proper exception. |
| */ |
| protected PersistenceCapable assertPersistenceCapable(Object obj) { |
| if (obj == null) |
| return null; |
| if (ImplHelper.isManageable(obj)) |
| return ImplHelper.toPersistenceCapable(obj, _conf); |
| |
| // check for different instances of the PersistenceCapable interface |
| // and throw a better error that mentions the class loaders |
| Class<?>[] intfs = obj.getClass().getInterfaces(); |
| for (int i = 0; intfs != null && i < intfs.length; i++) { |
| if (intfs[i].getName().equals(PersistenceCapable.class.getName())) { |
| throw new UserException(_loc.get("pc-loader-different", |
| Exceptions.toString(obj), |
| AccessController.doPrivileged( |
| J2DoPrivHelper.getClassLoaderAction( |
| PersistenceCapable.class)), |
| AccessController.doPrivileged( |
| J2DoPrivHelper.getClassLoaderAction(intfs[i])))) |
| .setFailedObject(obj); |
| } |
| } |
| |
| // not enhanced |
| throw new UserException(_loc.get("pc-cast", |
| Exceptions.toString(obj))).setFailedObject(obj); |
| } |
| |
| ///////// |
| // Utils |
| ///////// |
| /** |
| * Throw an exception if the context is closed. The exact message and |
| * content of the exception varies whether TRACE is enabled or not. |
| */ |
| @Override |
| public void assertOpen() { |
| if (_closed) { |
| if (_closedException == null) // TRACE not enabled |
| throw new InvalidStateException(_loc.get("closed-notrace")) |
| .setFatal(true); |
| else { |
| OpenJPAException e = new InvalidStateException( |
| _loc.get("closed"), _closedException).setFatal(true); |
| e.setCause(_closedException); |
| throw e; |
| } |
| } |
| } |
| |
| @Override |
| public void assertActiveTransaction() { |
| if ((_flags & FLAG_ACTIVE) == 0) |
| throw new NoTransactionException(_loc.get("not-active")); |
| } |
| |
| /** |
| * Throw exception if a transaction-related operation is attempted and |
| * no transaction is active. |
| */ |
| private void assertTransactionOperation() { |
| if ((_flags & FLAG_ACTIVE) == 0) |
| throw new InvalidStateException(_loc.get("not-active")); |
| } |
| |
| @Override |
| public void assertNontransactionalRead() { |
| if ((_flags & FLAG_ACTIVE) == 0 && !_nontransRead) |
| throw new InvalidStateException(_loc.get("non-trans-read")); |
| } |
| |
| @Override |
| public void assertWriteOperation() { |
| if ((_flags & FLAG_ACTIVE) == 0 && (!_nontransWrite |
| || (_autoDetach & DETACH_NONTXREAD) != 0)) |
| throw new NoTransactionException(_loc.get("write-operation")); |
| } |
| |
| /** |
| * Return an object not found exception containing nested exceptions |
| * for all of the given failed objects. |
| */ |
| private static ObjectNotFoundException newObjectNotFoundException |
| (Collection failed) { |
| Throwable[] t = new Throwable[failed.size()]; |
| int idx = 0; |
| for (Iterator<?> itr = failed.iterator(); itr.hasNext(); idx++) |
| t[idx] = new ObjectNotFoundException(itr.next()); |
| return new ObjectNotFoundException(failed, t); |
| } |
| |
| //////////////////////////////// |
| // FindCallbacks implementation |
| //////////////////////////////// |
| |
| @Override |
| public Object processArgument(Object oid) { |
| return oid; |
| } |
| |
| @Override |
| public Object processReturn(Object oid, OpenJPAStateManager sm) { |
| 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(); |
| _repo = _conf.getMetaDataRepositoryInstance(); |
| |
| in.defaultReadObject(); |
| factory.initializeBroker(_managed, _connRetainMode, this, true); |
| |
| // re-initialize the lock if needed. |
| setMultithreaded(_multithreaded); |
| |
| // force recreation of set |
| _operatingDirty = true; |
| initializeOperatingSet(); |
| |
| if (isActive() && _runtime instanceof LocalManagedRuntime) |
| ((LocalManagedRuntime) _runtime).begin(); |
| } |
| |
| /** |
| * Whether or not this broker is in the midst of being serialized. |
| * |
| * @since 1.1.0 |
| */ |
| boolean isSerializing() { |
| return _isSerializing; |
| } |
| |
| /** |
| * @return The value of openjpa.ConnectionFactoryProperties.PrintParameters. Default is false. |
| */ |
| public boolean getPrintParameters() { |
| return _printParameters; |
| } |
| /** |
| * Transactional cache that holds soft refs to clean instances. |
| */ |
| static class TransactionalCache |
| implements Set, Serializable { |
| |
| |
| private static final long serialVersionUID = 1L; |
| private final boolean _orderDirty; |
| private Set<StateManagerImpl> _dirty = null; |
| private Set<StateManagerImpl> _clean = null; |
| |
| public TransactionalCache(boolean orderDirty) { |
| _orderDirty = orderDirty; |
| } |
| |
| /** |
| * Return a copy of all transactional state managers. |
| */ |
| public Collection copy() { |
| if (isEmpty()) { |
| // Transaction Listeners may add entities to the transaction. |
| return new LinkedHashSet(); |
| } |
| |
| // size may not be entirely accurate due to refs expiring, so |
| // manually copy each object; doesn't matter this way if size too |
| // big by some |
| Set copy = new LinkedHashSet(size()); |
| if (_dirty != null) |
| for (StateManagerImpl stateManager : _dirty) { |
| copy.add(stateManager); |
| } |
| if (_clean != null) |
| for (StateManagerImpl stateManager : _clean) { |
| copy.add(stateManager); |
| } |
| return copy; |
| } |
| |
| /** |
| * Return a copy of all dirty state managers. |
| */ |
| public Collection copyDirty() { |
| if (_dirty == null || _dirty.isEmpty()) |
| return Collections.EMPTY_SET; |
| return new LinkedHashSet<>(_dirty); |
| } |
| |
| /** |
| * Transfer the given instance from the dirty cache to the clean cache. |
| */ |
| public void flushed(StateManagerImpl sm) { |
| if (sm.isDirty() && _dirty != null && _dirty.remove(sm)) |
| addCleanInternal(sm); |
| } |
| |
| /** |
| * Add the given instance to the clean cache. |
| */ |
| public void addClean(StateManagerImpl sm) { |
| if (addCleanInternal(sm) && _dirty != null) |
| _dirty.remove(sm); |
| } |
| |
| private boolean addCleanInternal(StateManagerImpl sm) { |
| if (_clean == null) |
| _clean = new ReferenceHashSet(AbstractReferenceMap.ReferenceStrength.SOFT); |
| return _clean.add(sm); |
| } |
| |
| /** |
| * Add the given instance to the dirty cache. |
| */ |
| public void addDirty(StateManagerImpl sm) { |
| if (_dirty == null) { |
| if (_orderDirty) |
| _dirty = MapBackedSet.mapBackedSet(new LinkedMap()); |
| else |
| _dirty = new HashSet<>(); |
| } |
| if (_dirty.add(sm)) |
| removeCleanInternal(sm); |
| } |
| |
| /** |
| * Remove the given instance from the cache. |
| */ |
| public boolean remove(StateManagerImpl sm) { |
| return removeCleanInternal(sm) |
| || (_dirty != null && _dirty.remove(sm)); |
| } |
| |
| private boolean removeCleanInternal(StateManagerImpl sm) { |
| return _clean != null && _clean.remove(sm); |
| } |
| |
| @Override |
| public Iterator iterator() { |
| IteratorChain chain = new IteratorChain(); |
| if (_dirty != null && !_dirty.isEmpty()) |
| chain.addIterator(_dirty.iterator()); |
| if (_clean != null && !_clean.isEmpty()) |
| chain.addIterator(_clean.iterator()); |
| return chain; |
| } |
| |
| @Override |
| public boolean contains(Object obj) { |
| return (_dirty != null && _dirty.contains(obj)) |
| || (_clean != null && _clean.contains(obj)); |
| } |
| |
| @Override |
| public boolean containsAll(Collection coll) { |
| for (Object o : coll) |
| if (!contains(o)) |
| return false; |
| return true; |
| } |
| |
| @Override |
| public void clear() { |
| if (_dirty != null) |
| _dirty = null; |
| if (_clean != null) |
| _clean = null; |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return (_dirty == null || _dirty.isEmpty()) |
| && (_clean == null || _clean.isEmpty()); |
| } |
| |
| @Override |
| public int size() { |
| int size = 0; |
| if (_dirty != null) |
| size += _dirty.size(); |
| if (_clean != null) |
| size += _clean.size(); |
| return size; |
| } |
| |
| @Override |
| public boolean add(Object obj) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean addAll(Collection coll) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean remove(Object obj) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean removeAll(Collection coll) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean retainAll(Collection c) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public Object[] toArray() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public Object[] toArray(Object[] arr) { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| /** |
| * Unique id for state managers of new datastore instances without assigned |
| * object ids. |
| */ |
| public static class StateManagerId |
| implements Serializable { |
| |
| |
| private static final long serialVersionUID = 1L; |
| |
| public static final String STRING_PREFIX = "openjpasm:"; |
| |
| private static long _generator = 0; |
| |
| private final int _bhash; |
| private final long _id; |
| |
| public static StateManagerId newInstance(Broker b) { |
| return new StateManagerId(System.identityHashCode(b), _generator++); |
| } |
| |
| private StateManagerId(int bhash, long id) { |
| _bhash = bhash; |
| _id = id; |
| } |
| |
| public StateManagerId(String str) { |
| str = str.substring(STRING_PREFIX.length()); |
| int idx = str.indexOf(':'); |
| _bhash = Integer.parseInt(str.substring(0, idx)); |
| _id = Long.parseLong(str.substring(idx + 1)); |
| } |
| |
| @Override |
| public boolean equals(Object other) { |
| if (other == this) |
| return true; |
| if ((other == null) || (other.getClass() != this.getClass())) |
| return false; |
| |
| StateManagerId sid = (StateManagerId) other; |
| return _bhash == sid._bhash && _id == sid._id; |
| } |
| |
| @Override |
| public int hashCode() { |
| return (int) (_id ^ (_id >>> 32)); |
| } |
| |
| @Override |
| public String toString() { |
| return STRING_PREFIX + _bhash + ":" + _id; |
| } |
| } |
| |
| /** |
| * Collection type that holds state managers but whose interface deals |
| * with the corresponding managed objects. |
| */ |
| private static class ManagedObjectCollection |
| extends AbstractCollection { |
| |
| private final Collection _states; |
| |
| public ManagedObjectCollection(Collection states) { |
| _states = states; |
| } |
| |
| public Collection getStateManagers() { |
| return _states; |
| } |
| |
| @Override |
| public int size() { |
| return _states.size(); |
| } |
| |
| @Override |
| public Iterator iterator() { |
| return new Iterator() { |
| private final Iterator _itr = _states.iterator(); |
| |
| @Override |
| public boolean hasNext() { |
| return _itr.hasNext(); |
| } |
| |
| @Override |
| public Object next() { |
| return ((OpenJPAStateManager) _itr.next()). |
| getManagedInstance(); |
| } |
| |
| @Override |
| public void remove() { |
| throw new UnsupportedException(); |
| } |
| }; |
| } |
| } |
| |
| /** |
| * Assign the object id to the cache. Exception will be |
| * thrown if the id already exists in the cache. |
| */ |
| protected void assignObjectId(Object cache, Object id, |
| StateManagerImpl sm) { |
| ((ManagedCache) cache).assignObjectId(id, sm); |
| } |
| |
| /** |
| * This method makes sure we don't already have the instance cached |
| */ |
| protected void checkForDuplicateId(Object id, Object obj, ClassMetaData meta) { |
| FieldMetaData[] pks = meta.getPrimaryKeyFields(); |
| if (pks != null && pks.length == 1 && pks[0].getValueStrategy() == ValueStrategies.AUTOASSIGN) { |
| return; |
| } |
| StateManagerImpl other = getStateManagerImplById(id, false); |
| if (other != null && !other.isDeleted() && !other.isNew()) |
| throw new ObjectExistsException(_loc.get("cache-exists", |
| obj.getClass().getName(), id)).setFailedObject(obj); |
| } |
| |
| @Override |
| public boolean getCachePreparedQuery() { |
| lock(); |
| try { |
| return _cachePreparedQuery |
| && _conf.getQuerySQLCacheInstance() != null; |
| } finally { |
| unlock(); |
| } |
| } |
| |
| @Override |
| public void setCachePreparedQuery(boolean flag) { |
| lock(); |
| try { |
| _cachePreparedQuery = flag; |
| } finally { |
| unlock(); |
| } |
| } |
| |
| public boolean getCacheFinderQuery() { |
| lock(); |
| try { |
| return _cacheFinderQuery |
| && _conf.getFinderCacheInstance() != null; |
| } finally { |
| unlock(); |
| } |
| } |
| |
| public void setCacheFinderQuery(boolean flag) { |
| lock(); |
| try { |
| _cachePreparedQuery = flag; |
| } finally { |
| unlock(); |
| } |
| } |
| |
| public boolean isFromWriteBehindCallback() { |
| return _fromWriteBehindCallback; |
| } |
| |
| /** |
| * Return the 'JTA' connectionFactoryName |
| */ |
| @Override |
| public String getConnectionFactoryName() { |
| return _connectionFactoryName; |
| } |
| |
| /** |
| * Set the 'JTA' ConnectionFactoryName. Input will be trimmed to null before being stored. |
| */ |
| @Override |
| public void setConnectionFactoryName(String connectionFactoryName) { |
| this._connectionFactoryName = StringUtil.trimToNull(connectionFactoryName); |
| } |
| |
| /** |
| * Return the 'NonJTA' ConnectionFactoryName. |
| */ |
| @Override |
| public String getConnectionFactory2Name() { |
| return _connectionFactory2Name; |
| } |
| |
| /** |
| * Set the 'NonJTA' ConnectionFactoryName. Input will be trimmed to null before being stored. |
| */ |
| @Override |
| public void setConnectionFactory2Name(String connectionFactory2Name) { |
| this._connectionFactory2Name = StringUtil.trimToNull(connectionFactory2Name); |
| } |
| |
| /** |
| * Return the 'JTA' ConnectionFactory, looking it up from JNDI if needed. |
| * |
| * @return the JTA connection factory or null if connectionFactoryName is blank. |
| */ |
| @Override |
| public Object getConnectionFactory() { |
| if(StringUtil.isNotBlank(_connectionFactoryName)) { |
| return Configurations.lookup(_connectionFactoryName, "openjpa.ConnectionFactory", _log ); |
| } |
| else { |
| return null; |
| } |
| } |
| |
| /** |
| * Return the 'NonJTA' ConnectionFactory, looking it up from JNDI if needed. |
| * |
| * @return the NonJTA connection factory or null if connectionFactoryName is blank. |
| */ |
| @Override |
| public Object getConnectionFactory2() { |
| if(StringUtil.isNotBlank(_connectionFactory2Name)) { |
| return Configurations.lookup(_connectionFactory2Name, "openjpa.ConnectionFactory2", _log); |
| } |
| else { |
| return null; |
| } |
| } |
| |
| @Override |
| public boolean isCached(List<Object> oids) { |
| BitSet loaded = new BitSet(oids.size()); |
| //check L1 cache first |
| for (int i = 0; i < oids.size(); i++) { |
| Object oid = oids.get(i); |
| if (_cache.getById(oid, false) != null) { |
| loaded.set(i); |
| } |
| } |
| if(loaded.cardinality()==oids.size()){ |
| return true; |
| } |
| return _store.isCached(oids, loaded); |
| } |
| |
| @Override |
| public boolean getAllowReferenceToSiblingContext() { |
| return _allowReferenceToSiblingContext; |
| } |
| |
| @Override |
| public void setAllowReferenceToSiblingContext(boolean allow) { |
| _allowReferenceToSiblingContext = allow; |
| } |
| |
| protected boolean isFlushing() { |
| return ((_flags & FLAG_FLUSHING) != 0); |
| } |
| |
| @Override |
| public boolean getPostLoadOnMerge() { |
| return _postLoadOnMerge; |
| } |
| |
| @Override |
| public void setPostLoadOnMerge(boolean allow) { |
| _postLoadOnMerge = allow; |
| } |
| |
| /** |
| * Asserts consistencey of given automatic detachment option value. |
| */ |
| private void assertAutoDetachValue(int value) { |
| if (((value & AutoDetach.DETACH_NONE) != 0) && (value != AutoDetach.DETACH_NONE)) { |
| throw new UserException(_loc.get("detach-none-exclusive", toAutoDetachString(value))); |
| } |
| } |
| |
| /** |
| * Generates a user-readable String from the given integral value of AutoDetach options. |
| */ |
| private String toAutoDetachString(int value) { |
| List<String> result = new ArrayList<>(); |
| for (int i = 0; i < AutoDetach.values.length; i++) { |
| if ((value & AutoDetach.values[i]) != 0) { |
| result.add(AutoDetach.names[i]); |
| } |
| } |
| return Arrays.toString(result.toArray(new String[result.size()])); |
| } |
| |
| private boolean operatingAdd(Object o){ |
| _operatingDirty = true; |
| return _operating.add(o); |
| } |
| |
| } |