0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one
0003: * or more contributor license agreements. See the NOTICE file
0004: * distributed with this work for additional information
0005: * regarding copyright ownership. The ASF licenses this file
0006: * to you under the Apache License, Version 2.0 (the
0007: * "License"); you may not use this file except in compliance
0008: * with the License. You may obtain a copy of the License at
0009: *
0010: * http://www.apache.org/licenses/LICENSE-2.0
0011: *
0012: * Unless required by applicable law or agreed to in writing,
0013: * software distributed under the License is distributed on an
0014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0015: * KIND, either express or implied. See the License for the
0016: * specific language governing permissions and limitations
0017: * under the License.
0018: */
0019: package org.apache.openjpa.kernel;
0020:
0021: import java.io.IOException;
0022: import java.io.ObjectInputStream;
0023: import java.io.ObjectOutputStream;
0024: import java.io.Serializable;
0025: import java.lang.reflect.Modifier;
0026: import java.security.AccessController;
0027: import java.util.AbstractCollection;
0028: import java.util.ArrayList;
0029: import java.util.BitSet;
0030: import java.util.Collection;
0031: import java.util.Collections;
0032: import java.util.HashMap;
0033: import java.util.HashSet;
0034: import java.util.Iterator;
0035: import java.util.LinkedList;
0036: import java.util.List;
0037: import java.util.Map;
0038: import java.util.Set;
0039: import javax.transaction.Status;
0040: import javax.transaction.Synchronization;
0041:
0042: import org.apache.commons.collections.iterators.IteratorChain;
0043: import org.apache.commons.collections.map.IdentityMap;
0044: import org.apache.commons.collections.map.LinkedMap;
0045: import org.apache.commons.collections.set.MapBackedSet;
0046: import org.apache.openjpa.conf.Compatibility;
0047: import org.apache.openjpa.conf.OpenJPAConfiguration;
0048: import org.apache.openjpa.datacache.DataCache;
0049: import org.apache.openjpa.ee.ManagedRuntime;
0050: import org.apache.openjpa.enhance.PCRegistry;
0051: import org.apache.openjpa.enhance.PersistenceCapable;
0052: import org.apache.openjpa.event.LifecycleEvent;
0053: import org.apache.openjpa.event.LifecycleEventManager;
0054: import org.apache.openjpa.event.RemoteCommitEventManager;
0055: import org.apache.openjpa.event.TransactionEvent;
0056: import org.apache.openjpa.event.TransactionEventManager;
0057: import org.apache.openjpa.kernel.exps.ExpressionParser;
0058: import org.apache.openjpa.lib.log.Log;
0059: import org.apache.openjpa.lib.util.J2DoPrivHelper;
0060: import org.apache.openjpa.lib.util.Localizer;
0061: import org.apache.openjpa.lib.util.ReferenceHashMap;
0062: import org.apache.openjpa.lib.util.ReferenceHashSet;
0063: import org.apache.openjpa.lib.util.ReferenceMap;
0064: import org.apache.openjpa.lib.util.concurrent.ReentrantLock;
0065: import org.apache.openjpa.meta.ClassMetaData;
0066: import org.apache.openjpa.meta.FieldMetaData;
0067: import org.apache.openjpa.meta.MetaDataRepository;
0068: import org.apache.openjpa.meta.SequenceMetaData;
0069: import org.apache.openjpa.meta.ValueMetaData;
0070: import org.apache.openjpa.meta.ValueStrategies;
0071: import org.apache.openjpa.util.ApplicationIds;
0072: import org.apache.openjpa.util.CallbackException;
0073: import org.apache.openjpa.util.Exceptions;
0074: import org.apache.openjpa.util.GeneralException;
0075: import org.apache.openjpa.util.ImplHelper;
0076: import org.apache.openjpa.util.InternalException;
0077: import org.apache.openjpa.util.InvalidStateException;
0078: import org.apache.openjpa.util.NoTransactionException;
0079: import org.apache.openjpa.util.ObjectExistsException;
0080: import org.apache.openjpa.util.ObjectId;
0081: import org.apache.openjpa.util.ObjectNotFoundException;
0082: import org.apache.openjpa.util.OpenJPAException;
0083: import org.apache.openjpa.util.OptimisticException;
0084: import org.apache.openjpa.util.RuntimeExceptionTranslator;
0085: import org.apache.openjpa.util.StoreException;
0086: import org.apache.openjpa.util.UnsupportedException;
0087: import org.apache.openjpa.util.UserException;
0088:
0089: /**
0090: * Concrete {@link Broker}. The broker handles object-level behavior,
0091: * but leaves all interaction with the data store to a {@link StoreManager}
0092: * that must be supplied at initialization.
0093: *
0094: * @author Abe White
0095: */
0096: public class BrokerImpl implements Broker, FindCallbacks, Cloneable,
0097: Serializable {
0098:
0099: /**
0100: * Incremental flush.
0101: */
0102: protected static final int FLUSH_INC = 0;
0103:
0104: /**
0105: * Flush in preparation of commit.
0106: */
0107: protected static final int FLUSH_COMMIT = 1;
0108:
0109: /**
0110: * Flush to check consistency of cache, then immediately rollback changes.
0111: */
0112: protected static final int FLUSH_ROLLBACK = 2;
0113:
0114: /**
0115: * Run persistence-by-reachability and other flush-time operations without
0116: * accessing the database.
0117: */
0118: protected static final int FLUSH_LOGICAL = 3;
0119:
0120: static final int STATUS_INIT = 0;
0121: static final int STATUS_TRANSIENT = 1;
0122: static final int STATUS_OID_ASSIGN = 2;
0123: static final int STATUS_COMMIT_NEW = 3;
0124:
0125: private static final int FLAG_ACTIVE = 2 << 0;
0126: private static final int FLAG_STORE_ACTIVE = 2 << 1;
0127: private static final int FLAG_CLOSE_INVOKED = 2 << 2;
0128: private static final int FLAG_PRESTORING = 2 << 3;
0129: private static final int FLAG_DEREFDELETING = 2 << 4;
0130: private static final int FLAG_FLUSHING = 2 << 5;
0131: private static final int FLAG_STORE_FLUSHING = 2 << 6;
0132: private static final int FLAG_FLUSHED = 2 << 7;
0133: private static final int FLAG_FLUSH_REQUIRED = 2 << 8;
0134: private static final int FLAG_REMOTE_LISTENER = 2 << 9;
0135: private static final int FLAG_RETAINED_CONN = 2 << 10;
0136: private static final int FLAG_TRANS_ENDING = 2 << 11;
0137:
0138: private static final Object[] EMPTY_OBJECTS = new Object[0];
0139:
0140: private static final Localizer _loc = Localizer
0141: .forPackage(BrokerImpl.class);
0142:
0143: // the store manager in use; this may be a decorator such as a
0144: // data cache store manager around the native store manager
0145: private transient DelegatingStoreManager _store = null;
0146:
0147: private FetchConfiguration _fc = null;
0148: private String _user = null;
0149: private String _pass = null;
0150:
0151: // these must be rebuilt by the facade layer during its deserialization
0152: private transient Log _log = null;
0153: private transient Compatibility _compat = null;
0154: private transient ManagedRuntime _runtime = null;
0155: private transient LockManager _lm = null;
0156: private transient InverseManager _im = null;
0157: private transient ReentrantLock _lock = null;
0158: private transient OpCallbacks _call = null;
0159: private transient RuntimeExceptionTranslator _extrans = null;
0160:
0161: // ref to producing factory and configuration
0162: private transient AbstractBrokerFactory _factory = null;
0163: private transient OpenJPAConfiguration _conf = null;
0164:
0165: // cache class loader associated with the broker
0166: private transient ClassLoader _loader = null;
0167:
0168: // user state
0169: private Synchronization _sync = null;
0170: private Map _userObjects = null;
0171:
0172: // managed object caches
0173: private ManagedCache _cache = null;
0174: private TransactionalCache _transCache = null;
0175: private Set _transAdditions = null;
0176: private Set _derefCache = null;
0177: private Set _derefAdditions = null;
0178:
0179: // these are used for method-internal state only
0180: private transient Map _loading = null;
0181: private transient Set _operating = null;
0182:
0183: private Set _persistedClss = null;
0184: private Set _updatedClss = null;
0185: private Set _deletedClss = null;
0186: private Set _pending = null;
0187: private int findAllDepth = 0;
0188:
0189: // track instances that become transactional after the first savepoint
0190: // (the first uses the transactional cache)
0191: private Set _savepointCache = null;
0192: private LinkedMap _savepoints = null;
0193: private transient SavepointManager _spm = null;
0194:
0195: // track open queries and extents so we can free their resources on close
0196: private transient ReferenceHashSet _queries = null;
0197: private transient ReferenceHashSet _extents = null;
0198:
0199: // track operation stack depth. Transient because operations cannot
0200: // span serialization.
0201: private transient int _operationCount = 0;
0202:
0203: // options
0204: private boolean _nontransRead = false;
0205: private boolean _nontransWrite = false;
0206: private boolean _retainState = false;
0207: private int _autoClear = CLEAR_DATASTORE;
0208: private int _restoreState = RESTORE_IMMUTABLE;
0209: private boolean _optimistic = false;
0210: private boolean _ignoreChanges = false;
0211: private boolean _multithreaded = false;
0212: private boolean _managed = false;
0213: private boolean _syncManaged = false;
0214: private int _connRetainMode = CONN_RETAIN_DEMAND;
0215: private boolean _evictDataCache = false;
0216: private boolean _populateDataCache = true;
0217: private boolean _largeTransaction = false;
0218: private int _autoDetach = 0;
0219: private int _detachState = DETACH_LOADED;
0220: private boolean _detachedNew = true;
0221: private boolean _orderDirty = false;
0222:
0223: // status
0224: private int _flags = 0;
0225:
0226: // this is not in status because it should not be serialized
0227: private transient boolean _isSerializing = false;
0228:
0229: // transient because closed brokers can't be serialized
0230: private transient boolean _closed = false;
0231: private transient RuntimeException _closedException = null;
0232:
0233: // event managers
0234: private TransactionEventManager _transEventManager = null;
0235: private int _transCallbackMode = 0;
0236: private LifecycleEventManager _lifeEventManager = null;
0237: private int _lifeCallbackMode = 0;
0238:
0239: private transient boolean _initializeWasInvoked = false;
0240:
0241: /**
0242: * Set the persistence manager's authentication. This is the first
0243: * method called after construction.
0244: *
0245: * @param user the username this broker represents; used when pooling
0246: * brokers to make sure that a request to the factory for
0247: * a connection with an explicit user is delegated to a suitable broker
0248: * @param pass the password for the above user
0249: */
0250: public void setAuthentication(String user, String pass) {
0251: _user = user;
0252: _pass = pass;
0253: }
0254:
0255: /**
0256: * Initialize the persistence manager. This method is called
0257: * automatically by the factory before use.
0258: *
0259: * @param factory the factory used to create this broker
0260: * @param sm a concrete StoreManager implementation to
0261: * handle interaction with the data store
0262: * @param managed the transaction mode
0263: * @param connMode the connection retain mode
0264: * @param fromDeserialization whether this call happened because of a
0265: * deserialization or creation of a new BrokerImpl.
0266: */
0267: public void initialize(AbstractBrokerFactory factory,
0268: DelegatingStoreManager sm, boolean managed, int connMode,
0269: boolean fromDeserialization) {
0270: _initializeWasInvoked = true;
0271: _loader = (ClassLoader) AccessController
0272: .doPrivileged(J2DoPrivHelper
0273: .getContextClassLoaderAction());
0274: if (!fromDeserialization)
0275: _conf = factory.getConfiguration();
0276: _compat = _conf.getCompatibilityInstance();
0277: _factory = factory;
0278: _log = _conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);
0279: if (!fromDeserialization)
0280: _cache = new ManagedCache(this );
0281: initializeOperatingSet();
0282: _connRetainMode = connMode;
0283: _managed = managed;
0284: if (managed)
0285: _runtime = _conf.getManagedRuntimeInstance();
0286: else
0287: _runtime = new LocalManagedRuntime(this );
0288:
0289: if (!fromDeserialization) {
0290: _lifeEventManager = new LifecycleEventManager();
0291: _transEventManager = new TransactionEventManager();
0292: int cmode = _conf.getMetaDataRepositoryInstance()
0293: .getMetaDataFactory().getDefaults()
0294: .getCallbackMode();
0295: setLifecycleListenerCallbackMode(cmode);
0296: setTransactionListenerCallbackMode(cmode);
0297:
0298: // setup default options
0299: _factory.configureBroker(this );
0300: }
0301:
0302: // make sure to do this after configuring broker so that store manager
0303: // can look to broker configuration; we set both store and lock managers
0304: // before initializing them because they may each try to access the
0305: // other in thier initialization
0306: _store = sm;
0307: _lm = _conf.newLockManagerInstance();
0308: _im = _conf.newInverseManagerInstance();
0309: _spm = _conf.getSavepointManagerInstance();
0310: _store.setContext(this );
0311: _lm.setContext(this );
0312:
0313: if (_connRetainMode == CONN_RETAIN_ALWAYS)
0314: retainConnection();
0315: if (!fromDeserialization) {
0316: _fc = _store.newFetchConfiguration();
0317: _fc.setContext(this );
0318: }
0319:
0320: // synch with the global transaction in progress, if any
0321: if (_factory.syncWithManagedTransaction(this , false))
0322: beginInternal();
0323: }
0324:
0325: private void initializeOperatingSet() {
0326: _operating = MapBackedSet.decorate(new IdentityMap());
0327: }
0328:
0329: /**
0330: * Gets the unmodifiable set of instances being operated.
0331: */
0332: protected Set getOperatingSet() {
0333: return Collections.unmodifiableSet(_operating);
0334: }
0335:
0336: public Object clone() throws CloneNotSupportedException {
0337: if (_initializeWasInvoked)
0338: throw new CloneNotSupportedException();
0339: else {
0340: return super .clone();
0341: }
0342: }
0343:
0344: /**
0345: * Create a {@link Map} to be used for the primary managed object cache.
0346: * Maps oids to state managers. By default, this creates a
0347: * {@link ReferenceMap} with soft values.
0348: */
0349: protected Map newManagedObjectCache() {
0350: return new ReferenceHashMap(ReferenceMap.HARD,
0351: ReferenceMap.SOFT);
0352: }
0353:
0354: //////////////////////////////////
0355: // Implementation of StoreContext
0356: //////////////////////////////////
0357:
0358: public Broker getBroker() {
0359: return this ;
0360: }
0361:
0362: //////////////
0363: // Properties
0364: //////////////
0365:
0366: public void setImplicitBehavior(OpCallbacks call,
0367: RuntimeExceptionTranslator ex) {
0368: if (_call == null)
0369: _call = call;
0370: if (_extrans == null)
0371: _extrans = ex;
0372: }
0373:
0374: RuntimeExceptionTranslator getInstanceExceptionTranslator() {
0375: return (_operationCount == 0) ? _extrans : null;
0376: }
0377:
0378: public BrokerFactory getBrokerFactory() {
0379: return _factory;
0380: }
0381:
0382: public OpenJPAConfiguration getConfiguration() {
0383: return _conf;
0384: }
0385:
0386: public FetchConfiguration getFetchConfiguration() {
0387: return _fc;
0388: }
0389:
0390: public int getConnectionRetainMode() {
0391: return _connRetainMode;
0392: }
0393:
0394: public boolean isManaged() {
0395: return _managed;
0396: }
0397:
0398: public ManagedRuntime getManagedRuntime() {
0399: return _runtime;
0400: }
0401:
0402: public ClassLoader getClassLoader() {
0403: return _loader;
0404: }
0405:
0406: public DelegatingStoreManager getStoreManager() {
0407: return _store;
0408: }
0409:
0410: public LockManager getLockManager() {
0411: return _lm;
0412: }
0413:
0414: public InverseManager getInverseManager() {
0415: return _im;
0416: }
0417:
0418: public String getConnectionUserName() {
0419: return _user;
0420: }
0421:
0422: public String getConnectionPassword() {
0423: return _pass;
0424: }
0425:
0426: public boolean getMultithreaded() {
0427: return _multithreaded;
0428: }
0429:
0430: public void setMultithreaded(boolean multithreaded) {
0431: assertOpen();
0432: _multithreaded = multithreaded;
0433: if (multithreaded && _lock == null)
0434: _lock = new ReentrantLock();
0435: else if (!multithreaded)
0436: _lock = null;
0437: }
0438:
0439: public boolean getIgnoreChanges() {
0440: return _ignoreChanges;
0441: }
0442:
0443: public void setIgnoreChanges(boolean val) {
0444: assertOpen();
0445: _ignoreChanges = val;
0446: }
0447:
0448: public boolean getNontransactionalRead() {
0449: return _nontransRead;
0450: }
0451:
0452: public void setNontransactionalRead(boolean val) {
0453: assertOpen();
0454: if ((_flags & FLAG_PRESTORING) != 0)
0455: throw new UserException(_loc.get("illegal-op-in-prestore"));
0456:
0457: // make sure the runtime supports it
0458: if (val
0459: && !_conf.supportedOptions().contains(
0460: _conf.OPTION_NONTRANS_READ))
0461: throw new UnsupportedException(_loc
0462: .get("nontrans-read-not-supported"));
0463:
0464: _nontransRead = val;
0465: }
0466:
0467: public boolean getNontransactionalWrite() {
0468: return _nontransWrite;
0469: }
0470:
0471: public void setNontransactionalWrite(boolean val) {
0472: assertOpen();
0473: if ((_flags & FLAG_PRESTORING) != 0)
0474: throw new UserException(_loc.get("illegal-op-in-prestore"));
0475:
0476: _nontransWrite = val;
0477: }
0478:
0479: public boolean getOptimistic() {
0480: return _optimistic;
0481: }
0482:
0483: public void setOptimistic(boolean val) {
0484: assertOpen();
0485: if ((_flags & FLAG_ACTIVE) != 0)
0486: throw new InvalidStateException(_loc.get("trans-active",
0487: "Optimistic"));
0488:
0489: // make sure the runtime supports it
0490: if (val
0491: && !_conf.supportedOptions().contains(
0492: _conf.OPTION_OPTIMISTIC))
0493: throw new UnsupportedException(_loc
0494: .get("optimistic-not-supported"));
0495:
0496: _optimistic = val;
0497: }
0498:
0499: public int getRestoreState() {
0500: return _restoreState;
0501: }
0502:
0503: public void setRestoreState(int val) {
0504: assertOpen();
0505: if ((_flags & FLAG_ACTIVE) != 0)
0506: throw new InvalidStateException(_loc.get("trans-active",
0507: "Restore"));
0508:
0509: _restoreState = val;
0510: }
0511:
0512: public boolean getRetainState() {
0513: return _retainState;
0514: }
0515:
0516: public void setRetainState(boolean val) {
0517: assertOpen();
0518: if ((_flags & FLAG_PRESTORING) != 0)
0519: throw new UserException(_loc.get("illegal-op-in-prestore"));
0520: _retainState = val;
0521: }
0522:
0523: public int getAutoClear() {
0524: return _autoClear;
0525: }
0526:
0527: public void setAutoClear(int val) {
0528: assertOpen();
0529: _autoClear = val;
0530: }
0531:
0532: public int getAutoDetach() {
0533: return _autoDetach;
0534: }
0535:
0536: public void setAutoDetach(int detachFlags) {
0537: assertOpen();
0538: _autoDetach = detachFlags;
0539: }
0540:
0541: public void setAutoDetach(int detachFlag, boolean on) {
0542: assertOpen();
0543: if (on)
0544: _autoDetach |= detachFlag;
0545: else
0546: _autoDetach &= ~detachFlag;
0547: }
0548:
0549: public int getDetachState() {
0550: return _detachState;
0551: }
0552:
0553: public void setDetachState(int mode) {
0554: assertOpen();
0555: _detachState = mode;
0556: }
0557:
0558: public boolean isDetachedNew() {
0559: return _detachedNew;
0560: }
0561:
0562: public void setDetachedNew(boolean isNew) {
0563: assertOpen();
0564: _detachedNew = isNew;
0565: }
0566:
0567: public boolean getSyncWithManagedTransactions() {
0568: return _syncManaged;
0569: }
0570:
0571: public void setSyncWithManagedTransactions(boolean sync) {
0572: assertOpen();
0573: _syncManaged = sync;
0574: }
0575:
0576: public boolean getEvictFromDataCache() {
0577: return _evictDataCache;
0578: }
0579:
0580: public void setEvictFromDataCache(boolean evict) {
0581: assertOpen();
0582: _evictDataCache = evict;
0583: }
0584:
0585: public boolean getPopulateDataCache() {
0586: return _populateDataCache;
0587: }
0588:
0589: public void setPopulateDataCache(boolean cache) {
0590: assertOpen();
0591: _populateDataCache = cache;
0592: }
0593:
0594: public boolean isTrackChangesByType() {
0595: return _largeTransaction;
0596: }
0597:
0598: public void setTrackChangesByType(boolean largeTransaction) {
0599: assertOpen();
0600: _largeTransaction = largeTransaction;
0601: }
0602:
0603: public Object getUserObject(Object key) {
0604: beginOperation(false);
0605: try {
0606: return (_userObjects == null) ? null : _userObjects
0607: .get(key);
0608: } finally {
0609: endOperation();
0610: }
0611: }
0612:
0613: public Object putUserObject(Object key, Object val) {
0614: beginOperation(false);
0615: try {
0616: if (val == null)
0617: return (_userObjects == null) ? null : _userObjects
0618: .remove(key);
0619:
0620: if (_userObjects == null)
0621: _userObjects = new HashMap();
0622: return _userObjects.put(key, val);
0623: } finally {
0624: endOperation();
0625: }
0626: }
0627:
0628: //////////
0629: // Events
0630: //////////
0631:
0632: public void addLifecycleListener(Object listener, Class[] classes) {
0633: beginOperation(false);
0634: try {
0635: _lifeEventManager.addListener(listener, classes);
0636: } finally {
0637: endOperation();
0638: }
0639: }
0640:
0641: public void removeLifecycleListener(Object listener) {
0642: beginOperation(false);
0643: try {
0644: _lifeEventManager.removeListener(listener);
0645: } finally {
0646: endOperation();
0647: }
0648: }
0649:
0650: public int getLifecycleListenerCallbackMode() {
0651: return _lifeCallbackMode;
0652: }
0653:
0654: public void setLifecycleListenerCallbackMode(int mode) {
0655: beginOperation(false);
0656: try {
0657: _lifeCallbackMode = mode;
0658: _lifeEventManager
0659: .setFailFast((mode & CALLBACK_FAIL_FAST) != 0);
0660: } finally {
0661: endOperation();
0662: }
0663: }
0664:
0665: /**
0666: * Give state managers access to the lifecycle event manager.
0667: */
0668: public LifecycleEventManager getLifecycleEventManager() {
0669: return _lifeEventManager;
0670: }
0671:
0672: /**
0673: * Fire given lifecycle event, handling any exceptions appropriately.
0674: *
0675: * @return whether events are being processed at this time
0676: */
0677: boolean fireLifecycleEvent(Object src, Object related,
0678: ClassMetaData meta, int eventType) {
0679: if (_lifeEventManager == null)
0680: return false;
0681: handleCallbackExceptions(_lifeEventManager.fireEvent(src,
0682: related, meta, eventType), _lifeCallbackMode);
0683: return true;
0684: }
0685:
0686: /**
0687: * Take actions on callback exceptions depending on callback mode.
0688: */
0689: private void handleCallbackExceptions(Exception[] exceps, int mode) {
0690: if (exceps.length == 0 || (mode & CALLBACK_IGNORE) != 0)
0691: return;
0692:
0693: OpenJPAException ce;
0694: if (exceps.length == 1)
0695: ce = new CallbackException(exceps[0]);
0696: else
0697: ce = new CallbackException(_loc.get("callback-err"))
0698: .setNestedThrowables(exceps);
0699: if ((mode & CALLBACK_ROLLBACK) != 0
0700: && (_flags & FLAG_ACTIVE) != 0) {
0701: ce.setFatal(true);
0702: setRollbackOnlyInternal(ce);
0703: }
0704: if ((mode & CALLBACK_LOG) != 0 && _log.isWarnEnabled())
0705: _log.warn(ce);
0706: if ((mode & CALLBACK_RETHROW) != 0)
0707: throw ce;
0708: }
0709:
0710: public void addTransactionListener(Object tl) {
0711: beginOperation(false);
0712: try {
0713: _transEventManager.addListener(tl);
0714: if (tl instanceof RemoteCommitEventManager)
0715: _flags |= FLAG_REMOTE_LISTENER;
0716: } finally {
0717: endOperation();
0718: }
0719: }
0720:
0721: public void removeTransactionListener(Object tl) {
0722: beginOperation(false);
0723: try {
0724: if (_transEventManager.removeListener(tl)
0725: && (tl instanceof RemoteCommitEventManager))
0726: _flags &= ~FLAG_REMOTE_LISTENER;
0727: } finally {
0728: endOperation();
0729: }
0730: }
0731:
0732: public int getTransactionListenerCallbackMode() {
0733: return _transCallbackMode;
0734: }
0735:
0736: public void setTransactionListenerCallbackMode(int mode) {
0737: beginOperation(false);
0738: try {
0739: _transCallbackMode = mode;
0740: _transEventManager
0741: .setFailFast((mode & CALLBACK_FAIL_FAST) != 0);
0742: } finally {
0743: endOperation();
0744: }
0745: }
0746:
0747: /**
0748: * Fire given transaction event, handling any exceptions appropriately.
0749: */
0750: private void fireTransactionEvent(TransactionEvent trans) {
0751: if (_transEventManager != null)
0752: handleCallbackExceptions(_transEventManager
0753: .fireEvent(trans), _transCallbackMode);
0754: }
0755:
0756: ///////////
0757: // Lookups
0758: ///////////
0759:
0760: public Object find(Object oid, boolean validate, FindCallbacks call) {
0761: int flags = OID_COPY | OID_ALLOW_NEW | OID_NODELETED;
0762: if (!validate)
0763: flags |= OID_NOVALIDATE;
0764: return find(oid, _fc, null, null, flags, call);
0765: }
0766:
0767: public Object find(Object oid, FetchConfiguration fetch,
0768: BitSet exclude, Object edata, int flags) {
0769: return find(oid, fetch, exclude, edata, flags, null);
0770: }
0771:
0772: /**
0773: * Internal finder.
0774: */
0775: protected Object find(Object oid, FetchConfiguration fetch,
0776: BitSet exclude, Object edata, int flags, FindCallbacks call) {
0777: if (call == null)
0778: call = this ;
0779: oid = call.processArgument(oid);
0780: if (oid == null) {
0781: if ((flags & OID_NOVALIDATE) == 0)
0782: throw new ObjectNotFoundException(_loc.get("null-oid"));
0783: return call.processReturn(oid, null);
0784: }
0785: if (fetch == null)
0786: fetch = _fc;
0787:
0788: beginOperation(true);
0789: try {
0790: assertNontransactionalRead();
0791:
0792: // cached instance?
0793: StateManagerImpl sm = getStateManagerImplById(oid,
0794: (flags & OID_ALLOW_NEW) != 0 || hasFlushed());
0795: if (sm != null) {
0796: if (!requiresLoad(sm, true, fetch, edata, flags))
0797: return call.processReturn(oid, sm);
0798:
0799: if (!sm.isLoading()) {
0800: // make sure all the configured fields are loaded; do this
0801: // after making instance transactional for locking
0802: if (!sm.isTransactional()
0803: && useTransactionalState(fetch))
0804: sm.transactional();
0805: boolean loaded;
0806: try {
0807: loaded = sm.load(fetch,
0808: StateManagerImpl.LOAD_FGS, exclude,
0809: edata, false);
0810: } catch (ObjectNotFoundException onfe) {
0811: if ((flags & OID_NODELETED) != 0
0812: || (flags & OID_NOVALIDATE) != 0)
0813: throw onfe;
0814: return call.processReturn(oid, null);
0815: }
0816:
0817: // if no data needed to be loaded and the user wants to
0818: // validate, just make sure the object exists
0819: if (!loaded && (flags & OID_NOVALIDATE) == 0
0820: && _compat.getValidateTrueChecksStore()
0821: && !sm.isTransactional()
0822: && !_store.exists(sm, edata)) {
0823: if ((flags & OID_NODELETED) == 0)
0824: return call.processReturn(oid, null);
0825: throw new ObjectNotFoundException(_loc.get(
0826: "del-instance",
0827: sm.getManagedInstance(), oid))
0828: .setFailedObject(sm
0829: .getManagedInstance());
0830: }
0831: }
0832:
0833: // since the object was cached, we may need to upgrade lock
0834: // if current level is higher than level of initial load
0835: if ((_flags & FLAG_ACTIVE) != 0) {
0836: int level = fetch.getReadLockLevel();
0837: _lm.lock(sm, level, fetch.getLockTimeout(), edata);
0838: sm.readLocked(level, fetch.getWriteLockLevel());
0839: }
0840: return call.processReturn(oid, sm);
0841: }
0842:
0843: // if there's no cached sm for a new/transient id type, we
0844: // it definitely doesn't exist
0845: if (oid instanceof StateManagerId)
0846: return call.processReturn(oid, null);
0847:
0848: // initialize a new state manager for the datastore instance
0849: sm = newStateManagerImpl(oid, (flags & OID_COPY) != 0);
0850: boolean load = requiresLoad(sm, false, fetch, edata, flags);
0851: sm = initialize(sm, load, fetch, edata);
0852: if (sm == null) {
0853: if ((flags & OID_NOVALIDATE) != 0)
0854: throw new ObjectNotFoundException(oid);
0855: return call.processReturn(oid, null);
0856: }
0857:
0858: // make sure all configured fields were loaded
0859: if (load) {
0860: try {
0861: sm.load(fetch, StateManagerImpl.LOAD_FGS, exclude,
0862: edata, false);
0863: } catch (ObjectNotFoundException onfe) {
0864: if ((flags & OID_NODELETED) != 0
0865: || (flags & OID_NOVALIDATE) != 0)
0866: throw onfe;
0867: return call.processReturn(oid, null);
0868: }
0869: }
0870: return call.processReturn(oid, sm);
0871: } catch (OpenJPAException ke) {
0872: throw ke;
0873: } catch (RuntimeException re) {
0874: throw new GeneralException(re);
0875: } finally {
0876: endOperation();
0877: }
0878: }
0879:
0880: /**
0881: * Initialize a newly-constructed state manager.
0882: */
0883: protected StateManagerImpl initialize(StateManagerImpl sm,
0884: boolean load, FetchConfiguration fetch, Object edata) {
0885: if (!load) {
0886: sm.initialize(sm.getMetaData().getDescribedType(),
0887: PCState.HOLLOW);
0888: } else {
0889: PCState state = (useTransactionalState(fetch)) ? PCState.PCLEAN
0890: : PCState.PNONTRANS;
0891: sm.setLoading(true);
0892: try {
0893: if (!_store.initialize(sm, state, fetch, edata))
0894: return null;
0895: } finally {
0896: sm.setLoading(false);
0897: }
0898: }
0899: return sm;
0900: }
0901:
0902: public Object[] findAll(Collection oids, boolean validate,
0903: FindCallbacks call) {
0904: int flags = OID_COPY | OID_ALLOW_NEW | OID_NODELETED;
0905: if (!validate)
0906: flags |= OID_NOVALIDATE;
0907: return findAll(oids, _fc, null, null, flags, call);
0908: }
0909:
0910: public Object[] findAll(Collection oids, FetchConfiguration fetch,
0911: BitSet exclude, Object edata, int flags) {
0912: return findAll(oids, fetch, exclude, edata, flags, null);
0913: }
0914:
0915: /**
0916: * Internal finder.
0917: */
0918: protected Object[] findAll(Collection oids,
0919: FetchConfiguration fetch, BitSet exclude, Object edata,
0920: int flags, FindCallbacks call) {
0921: findAllDepth++;
0922:
0923: // throw any exceptions for null oids up immediately
0924: if (oids == null)
0925: throw new NullPointerException("oids == null");
0926: if ((flags & OID_NOVALIDATE) != 0 && oids.contains(null))
0927: throw new UserException(_loc.get("null-oids"));
0928:
0929: // we have to use a map of oid->sm rather than a simple
0930: // array, so that we make sure not to create multiple sms for equivalent
0931: // oids if the user has duplicates in the given array
0932: if (_loading == null)
0933: _loading = new HashMap((int) (oids.size() * 1.33 + 1));
0934:
0935: if (call == null)
0936: call = this ;
0937: if (fetch == null)
0938: fetch = _fc;
0939:
0940: beginOperation(true);
0941: try {
0942: assertNontransactionalRead();
0943:
0944: // collection of state managers to pass to store manager
0945: List load = null;
0946: StateManagerImpl sm;
0947: boolean initialized;
0948: boolean transState = useTransactionalState(fetch);
0949: Object obj, oid;
0950: int idx = 0;
0951: for (Iterator itr = oids.iterator(); itr.hasNext(); idx++) {
0952: // if we've already seen this oid, skip repeats
0953: obj = itr.next();
0954: oid = call.processArgument(obj);
0955: if (oid == null || _loading.containsKey(obj))
0956: continue;
0957:
0958: // if we don't have a cached instance or it is not transactional
0959: // and is hollow or we need to validate, load it
0960: sm = getStateManagerImplById(oid,
0961: (flags & OID_ALLOW_NEW) != 0 || hasFlushed());
0962: initialized = sm != null;
0963: if (!initialized)
0964: sm = newStateManagerImpl(oid,
0965: (flags & OID_COPY) != 0);
0966:
0967: _loading.put(obj, sm);
0968: if (requiresLoad(sm, initialized, fetch, edata, flags)) {
0969: transState = transState
0970: || useTransactionalState(fetch);
0971: if (initialized && !sm.isTransactional()
0972: && transState)
0973: sm.transactional();
0974: if (load == null)
0975: load = new ArrayList(oids.size() - idx);
0976: load.add(sm);
0977: } else if (!initialized)
0978: sm.initialize(sm.getMetaData().getDescribedType(),
0979: PCState.HOLLOW);
0980: }
0981:
0982: // pass all state managers in need of loading or validation to the
0983: // store manager
0984: if (load != null) {
0985: PCState state = (transState) ? PCState.PCLEAN
0986: : PCState.PNONTRANS;
0987: Collection failed = _store.loadAll(load, state,
0988: StoreManager.FORCE_LOAD_NONE, fetch, edata);
0989:
0990: // set failed instances to null
0991: if (failed != null && !failed.isEmpty()) {
0992: if ((flags & OID_NOVALIDATE) != 0)
0993: throw newObjectNotFoundException(failed);
0994: for (Iterator itr = failed.iterator(); itr
0995: .hasNext();)
0996: _loading.put(itr.next(), null);
0997: }
0998: }
0999:
1000: // create results array; make sure all configured fields are
1001: // loaded in each instance
1002: Object[] results = new Object[oids.size()];
1003: boolean active = (_flags & FLAG_ACTIVE) != 0;
1004: int level = fetch.getReadLockLevel();
1005: idx = 0;
1006: for (Iterator itr = oids.iterator(); itr.hasNext(); idx++) {
1007: oid = itr.next();
1008: sm = (StateManagerImpl) _loading.get(oid);
1009: if (sm != null
1010: && requiresLoad(sm, true, fetch, edata, flags)) {
1011: try {
1012: sm.load(fetch, StateManagerImpl.LOAD_FGS,
1013: exclude, edata, false);
1014: if (active) {
1015: _lm.lock(sm, level, fetch.getLockTimeout(),
1016: edata);
1017: sm.readLocked(level, fetch
1018: .getWriteLockLevel());
1019: }
1020: } catch (ObjectNotFoundException onfe) {
1021: if ((flags & OID_NODELETED) != 0
1022: || (flags & OID_NOVALIDATE) != 0)
1023: throw onfe;
1024: sm = null;
1025: }
1026: }
1027: results[idx] = call.processReturn(oid, sm);
1028: }
1029: return results;
1030: } catch (OpenJPAException ke) {
1031: throw ke;
1032: } catch (RuntimeException re) {
1033: throw new GeneralException(re);
1034: } finally {
1035: findAllDepth--;
1036: if (findAllDepth == 0)
1037: _loading = null;
1038: endOperation();
1039: }
1040: }
1041:
1042: private boolean hasFlushed() {
1043: return (_flags & FLAG_FLUSHED) != 0;
1044: }
1045:
1046: /**
1047: * Return whether the given instance needs loading before being returned
1048: * to the user.
1049: */
1050: private boolean requiresLoad(OpenJPAStateManager sm,
1051: boolean initialized, FetchConfiguration fetch,
1052: Object edata, int flags) {
1053: if (!fetch.requiresLoad())
1054: return false;
1055: if ((flags & OID_NOVALIDATE) == 0)
1056: return true;
1057: if (edata != null) // take advantage of existing result
1058: return true;
1059: if (initialized && sm.getPCState() != PCState.HOLLOW)
1060: return false;
1061: if (!initialized
1062: && sm.getMetaData().getPCSubclasses().length > 0)
1063: return true;
1064: return !_compat.getValidateFalseReturnsHollow();
1065: }
1066:
1067: /**
1068: * Return whether to use a transactional state.
1069: */
1070: private boolean useTransactionalState(FetchConfiguration fetch) {
1071: return (_flags & FLAG_ACTIVE) != 0
1072: && (!_optimistic || _autoClear == CLEAR_ALL || fetch
1073: .getReadLockLevel() != LOCK_NONE);
1074: }
1075:
1076: public Object findCached(Object oid, FindCallbacks call) {
1077: if (call == null)
1078: call = this ;
1079: oid = call.processArgument(oid);
1080: if (oid == null)
1081: return call.processReturn(oid, null);
1082:
1083: beginOperation(true);
1084: try {
1085: StateManagerImpl sm = getStateManagerImplById(oid, true);
1086: return call.processReturn(oid, sm);
1087: } finally {
1088: endOperation();
1089: }
1090: }
1091:
1092: public Class getObjectIdType(Class cls) {
1093: if (cls == null)
1094: return null;
1095:
1096: beginOperation(false);
1097: try {
1098: ClassMetaData meta = _conf.getMetaDataRepositoryInstance()
1099: .getMetaData(cls, _loader, false);
1100: if (meta == null
1101: || meta.getIdentityType() == ClassMetaData.ID_UNKNOWN)
1102: return null;
1103: if (meta.getIdentityType() == ClassMetaData.ID_APPLICATION)
1104: return meta.getObjectIdType();
1105:
1106: return _store.getDataStoreIdType(meta);
1107: } catch (OpenJPAException ke) {
1108: throw ke;
1109: } catch (RuntimeException re) {
1110: throw new GeneralException(re);
1111: } finally {
1112: endOperation();
1113: }
1114: }
1115:
1116: public Object newObjectId(Class cls, Object val) {
1117: if (val == null)
1118: return null;
1119:
1120: beginOperation(false);
1121: try {
1122: ClassMetaData meta = _conf.getMetaDataRepositoryInstance()
1123: .getMetaData(cls, _loader, true);
1124: switch (meta.getIdentityType()) {
1125: case ClassMetaData.ID_DATASTORE:
1126: // delegate to store manager for datastore ids
1127: if (val instanceof String
1128: && ((String) val)
1129: .startsWith(StateManagerId.STRING_PREFIX))
1130: return new StateManagerId((String) val);
1131: return _store.newDataStoreId(val, meta);
1132: case ClassMetaData.ID_APPLICATION:
1133: if (ImplHelper.isAssignable(meta.getObjectIdType(), val
1134: .getClass())) {
1135: if (!meta.isOpenJPAIdentity()
1136: && meta.isObjectIdTypeShared())
1137: return new ObjectId(cls, val);
1138: return val;
1139: }
1140:
1141: // stringified app id?
1142: if (val instanceof String
1143: && !_conf.getCompatibilityInstance()
1144: .getStrictIdentityValues()
1145: && !Modifier.isAbstract(cls.getModifiers()))
1146: return PCRegistry.newObjectId(cls, (String) val);
1147:
1148: Object[] arr = (val instanceof Object[]) ? (Object[]) val
1149: : new Object[] { val };
1150: return ApplicationIds.fromPKValues(arr, meta);
1151: default:
1152: throw new UserException(_loc.get("meta-unknownid", cls));
1153: }
1154: } catch (OpenJPAException ke) {
1155: throw ke;
1156: } catch (ClassCastException cce) {
1157: throw new UserException(_loc.get("bad-id-value", val, val
1158: .getClass().getName(), cls)).setCause(cce);
1159: } catch (RuntimeException re) {
1160: throw new GeneralException(re);
1161: } finally {
1162: endOperation();
1163: }
1164: }
1165:
1166: /**
1167: * Create a new state manager for the given oid.
1168: */
1169: private StateManagerImpl newStateManagerImpl(Object oid,
1170: boolean copy) {
1171: // see if we're in the process of loading this oid in a loadAll call
1172: StateManagerImpl sm;
1173: if (_loading != null) {
1174: sm = (StateManagerImpl) _loading.get(oid);
1175: if (sm != null && sm.getPersistenceCapable() == null)
1176: return sm;
1177: }
1178:
1179: // find metadata for the oid
1180: Class pcType = _store.getManagedType(oid);
1181: MetaDataRepository repos = _conf
1182: .getMetaDataRepositoryInstance();
1183: ClassMetaData meta;
1184: if (pcType != null)
1185: meta = repos.getMetaData(pcType, _loader, true);
1186: else
1187: meta = repos.getMetaData(oid, _loader, true);
1188:
1189: // copy the oid if needed
1190: if (copy && _compat.getCopyObjectIds()) {
1191: if (meta.getIdentityType() == ClassMetaData.ID_APPLICATION)
1192: oid = ApplicationIds.copy(oid, meta);
1193: else if (meta.getIdentityType() == ClassMetaData.ID_UNKNOWN)
1194: throw new UserException(_loc
1195: .get("meta-unknownid", meta));
1196: else
1197: oid = _store.copyDataStoreId(oid, meta);
1198: }
1199:
1200: sm = newStateManagerImpl(oid, meta);
1201: sm.setObjectId(oid);
1202: return sm;
1203: }
1204:
1205: /**
1206: * Create a state manager for the given oid and metadata.
1207: */
1208: protected StateManagerImpl newStateManagerImpl(Object oid,
1209: ClassMetaData meta) {
1210: return new StateManagerImpl(oid, meta, this );
1211: }
1212:
1213: ///////////////
1214: // Transaction
1215: ///////////////
1216:
1217: public void begin() {
1218: beginOperation(true);
1219: try {
1220: if ((_flags & FLAG_ACTIVE) != 0)
1221: throw new InvalidStateException(_loc.get("active"));
1222: _factory.syncWithManagedTransaction(this , true);
1223: beginInternal();
1224: } finally {
1225: endOperation();
1226: }
1227: }
1228:
1229: /**
1230: * Notify the store manager of a transaction.
1231: */
1232: private void beginInternal() {
1233: try {
1234: beginStoreManagerTransaction(_optimistic);
1235: _flags |= FLAG_ACTIVE;
1236:
1237: // start locking
1238: if (!_optimistic) {
1239: _fc.setReadLockLevel(_conf.getReadLockLevelConstant());
1240: _fc
1241: .setWriteLockLevel(_conf
1242: .getWriteLockLevelConstant());
1243: _fc.setLockTimeout(_conf.getLockTimeout());
1244: }
1245: _lm.beginTransaction();
1246:
1247: if (_transEventManager.hasBeginListeners())
1248: fireTransactionEvent(new TransactionEvent(this ,
1249: TransactionEvent.AFTER_BEGIN, null, null, null,
1250: null));
1251: } catch (OpenJPAException ke) {
1252: // if we already started the transaction, don't let it commit
1253: if ((_flags & FLAG_ACTIVE) != 0)
1254: setRollbackOnlyInternal(ke);
1255: throw ke.setFatal(true);
1256: } catch (RuntimeException re) {
1257: // if we already started the transaction, don't let it commit
1258: if ((_flags & FLAG_ACTIVE) != 0)
1259: setRollbackOnlyInternal(re);
1260: throw new StoreException(re).setFatal(true);
1261: }
1262:
1263: if (_pending != null) {
1264: StateManagerImpl sm;
1265: for (Iterator it = _pending.iterator(); it.hasNext();) {
1266: sm = (StateManagerImpl) it.next();
1267: sm.transactional();
1268: if (sm.isDirty())
1269: setDirty(sm, true);
1270: }
1271: _pending = null;
1272: }
1273: }
1274:
1275: public void beginStore() {
1276: beginOperation(true);
1277: try {
1278: assertTransactionOperation();
1279: if ((_flags & FLAG_STORE_ACTIVE) == 0)
1280: beginStoreManagerTransaction(false);
1281: } catch (OpenJPAException ke) {
1282: throw ke;
1283: } catch (RuntimeException re) {
1284: throw new StoreException(re);
1285: } finally {
1286: endOperation();
1287: }
1288: }
1289:
1290: /**
1291: * Begin a store manager transaction.
1292: */
1293: private void beginStoreManagerTransaction(boolean optimistic) {
1294: if (!optimistic) {
1295: retainConnection();
1296: _store.begin();
1297: _flags |= FLAG_STORE_ACTIVE;
1298: } else {
1299: if (_connRetainMode == CONN_RETAIN_TRANS)
1300: retainConnection();
1301: _store.beginOptimistic();
1302: }
1303: }
1304:
1305: /**
1306: * End the current store manager transaction. Throws an
1307: * exception to signal a forced rollback after failed commit, otherwise
1308: * returns any exception encountered during the end process.
1309: */
1310: private RuntimeException endStoreManagerTransaction(boolean rollback) {
1311: boolean forcedRollback = false;
1312: boolean releaseConn = false;
1313: RuntimeException err = null;
1314: try {
1315: if ((_flags & FLAG_STORE_ACTIVE) != 0) {
1316: releaseConn = _connRetainMode != CONN_RETAIN_ALWAYS;
1317: if (rollback)
1318: _store.rollback();
1319: else
1320: _store.commit();
1321: } else {
1322: releaseConn = _connRetainMode == CONN_RETAIN_TRANS;
1323: _store.rollbackOptimistic();
1324: }
1325: } catch (RuntimeException re) {
1326: if (!rollback) {
1327: forcedRollback = true;
1328: try {
1329: _store.rollback();
1330: } catch (RuntimeException re2) {
1331: }
1332: }
1333: err = re;
1334: } finally {
1335: _flags &= ~FLAG_STORE_ACTIVE;
1336: }
1337:
1338: if (releaseConn) {
1339: try {
1340: releaseConnection();
1341: } catch (RuntimeException re) {
1342: if (err == null)
1343: err = re;
1344: }
1345: }
1346:
1347: if (forcedRollback)
1348: throw err;
1349: return err;
1350: }
1351:
1352: public void commit() {
1353: beginOperation(false);
1354: try {
1355: assertTransactionOperation();
1356:
1357: javax.transaction.Transaction trans = _runtime
1358: .getTransactionManager().getTransaction();
1359: if (trans == null)
1360: throw new InvalidStateException(_loc.get("null-trans"));
1361:
1362: // this commit on the transaction will cause our
1363: // beforeCompletion method to be invoked
1364: trans.commit();
1365: } catch (OpenJPAException ke) {
1366: if (_log.isTraceEnabled())
1367: _log.trace(_loc.get("end-trans-error"), ke);
1368: throw ke;
1369: } catch (Exception e) {
1370: if (_log.isTraceEnabled())
1371: _log.trace(_loc.get("end-trans-error"), e);
1372: throw new StoreException(e);
1373: } finally {
1374: endOperation();
1375: }
1376: }
1377:
1378: public void rollback() {
1379: beginOperation(false);
1380: try {
1381: assertTransactionOperation();
1382:
1383: javax.transaction.Transaction trans = _runtime
1384: .getTransactionManager().getTransaction();
1385: if (trans != null)
1386: trans.rollback();
1387: } catch (OpenJPAException ke) {
1388: if (_log.isTraceEnabled())
1389: _log.trace(_loc.get("end-trans-error"), ke);
1390: throw ke;
1391: } catch (Exception e) {
1392: if (_log.isTraceEnabled())
1393: _log.trace(_loc.get("end-trans-error"), e);
1394: throw new StoreException(e);
1395: } finally {
1396: endOperation();
1397: }
1398: }
1399:
1400: public boolean syncWithManagedTransaction() {
1401: assertOpen();
1402: lock();
1403: try {
1404: if ((_flags & FLAG_ACTIVE) != 0)
1405: return true;
1406: if (!_managed)
1407: throw new InvalidStateException(_loc
1408: .get("trans-not-managed"));
1409: if (_factory.syncWithManagedTransaction(this , false)) {
1410: beginInternal();
1411: return true;
1412: }
1413: return false;
1414: } finally {
1415: unlock();
1416: }
1417: }
1418:
1419: public void commitAndResume() {
1420: endAndResume(true);
1421: }
1422:
1423: public void rollbackAndResume() {
1424: endAndResume(false);
1425: }
1426:
1427: private void endAndResume(boolean commit) {
1428: beginOperation(false);
1429: try {
1430: if (commit)
1431: commit();
1432: else
1433: rollback();
1434: begin();
1435: } finally {
1436: endOperation();
1437: }
1438: }
1439:
1440: public boolean getRollbackOnly() {
1441: beginOperation(true);
1442: try {
1443: if ((_flags & FLAG_ACTIVE) == 0)
1444: return false;
1445:
1446: javax.transaction.Transaction trans = _runtime
1447: .getTransactionManager().getTransaction();
1448: if (trans == null)
1449: return false;
1450: return trans.getStatus() == Status.STATUS_MARKED_ROLLBACK;
1451: } catch (OpenJPAException ke) {
1452: throw ke;
1453: } catch (Exception e) {
1454: throw new GeneralException(e);
1455: } finally {
1456: endOperation();
1457: }
1458: }
1459:
1460: public Throwable getRollbackCause() {
1461: beginOperation(true);
1462: try {
1463: if ((_flags & FLAG_ACTIVE) == 0)
1464: return null;
1465:
1466: javax.transaction.Transaction trans = _runtime
1467: .getTransactionManager().getTransaction();
1468: if (trans == null)
1469: return null;
1470: if (trans.getStatus() == Status.STATUS_MARKED_ROLLBACK)
1471: return _runtime.getRollbackCause();
1472:
1473: return null;
1474: } catch (OpenJPAException ke) {
1475: throw ke;
1476: } catch (Exception e) {
1477: throw new GeneralException(e);
1478: } finally {
1479: endOperation();
1480: }
1481: }
1482:
1483: public void setRollbackOnly() {
1484: setRollbackOnly(new UserException());
1485: }
1486:
1487: public void setRollbackOnly(Throwable cause) {
1488: beginOperation(true);
1489: try {
1490: assertTransactionOperation();
1491: setRollbackOnlyInternal(cause);
1492: } finally {
1493: endOperation();
1494: }
1495: }
1496:
1497: /**
1498: * Mark the current transaction as rollback-only.
1499: */
1500: private void setRollbackOnlyInternal(Throwable cause) {
1501: try {
1502: javax.transaction.Transaction trans = _runtime
1503: .getTransactionManager().getTransaction();
1504: if (trans == null)
1505: throw new InvalidStateException(_loc.get("null-trans"));
1506: // ensure tran is in a valid state to accept the setRollbackOnly
1507: int tranStatus = trans.getStatus();
1508: if ((tranStatus != Status.STATUS_NO_TRANSACTION)
1509: && (tranStatus != Status.STATUS_ROLLEDBACK)
1510: && (tranStatus != Status.STATUS_COMMITTED))
1511: _runtime.setRollbackOnly(cause);
1512: else if (_log.isTraceEnabled())
1513: _log.trace(_loc.get("invalid-tran-status", new Integer(
1514: tranStatus), "setRollbackOnly"));
1515: } catch (OpenJPAException ke) {
1516: throw ke;
1517: } catch (Exception e) {
1518: throw new GeneralException(e);
1519: }
1520: }
1521:
1522: public void setSavepoint(String name) {
1523: beginOperation(true);
1524: try {
1525: assertActiveTransaction();
1526: if (_savepoints != null && _savepoints.containsKey(name))
1527: throw new UserException(_loc.get("savepoint-exists",
1528: name));
1529:
1530: if (hasFlushed() && !_spm.supportsIncrementalFlush())
1531: throw new UnsupportedException(_loc
1532: .get("savepoint-flush-not-supported"));
1533:
1534: OpenJPASavepoint save = _spm.newSavepoint(name, this );
1535: if (_savepoints == null || _savepoints.isEmpty()) {
1536: save.save(getTransactionalStates());
1537: _savepoints = new LinkedMap();
1538: } else {
1539: if (_savepointCache == null)
1540: save.save(Collections.EMPTY_LIST);
1541: else {
1542: save.save(_savepointCache);
1543: _savepointCache.clear();
1544: }
1545: }
1546: _savepoints.put(name, save);
1547: } catch (OpenJPAException ke) {
1548: throw ke;
1549: } catch (Exception e) {
1550: throw new GeneralException(e);
1551: } finally {
1552: endOperation();
1553: }
1554: }
1555:
1556: public void releaseSavepoint() {
1557: beginOperation(false);
1558: try {
1559: if (_savepoints == null || _savepoints.isEmpty())
1560: throw new UserException(_loc.get("no-lastsavepoint"));
1561: releaseSavepoint((String) _savepoints.get(_savepoints
1562: .size() - 1));
1563: } finally {
1564: endOperation();
1565: }
1566: }
1567:
1568: public void releaseSavepoint(String savepoint) {
1569: beginOperation(false);
1570: try {
1571: assertActiveTransaction();
1572:
1573: int index = (_savepoints == null) ? -1 : _savepoints
1574: .indexOf(savepoint);
1575: if (index < 0)
1576: throw new UserException(_loc.get("no-savepoint",
1577: savepoint));
1578:
1579: // clear old in reverse
1580: OpenJPASavepoint save;
1581: while (_savepoints.size() > index + 1) {
1582: save = (OpenJPASavepoint) _savepoints
1583: .remove(_savepoints.size() - 1);
1584: save.release(false);
1585: }
1586:
1587: save = (OpenJPASavepoint) _savepoints.remove(index);
1588: save.release(true);
1589: if (_savepointCache != null)
1590: _savepointCache.clear();
1591: } catch (OpenJPAException ke) {
1592: throw ke;
1593: } catch (Exception e) {
1594: throw new GeneralException(e);
1595: } finally {
1596: endOperation();
1597: }
1598: }
1599:
1600: public void rollbackToSavepoint() {
1601: beginOperation(false);
1602: try {
1603: if (_savepoints == null || _savepoints.isEmpty())
1604: throw new UserException(_loc.get("no-lastsavepoint"));
1605: rollbackToSavepoint((String) _savepoints.get(_savepoints
1606: .size() - 1));
1607: } finally {
1608: endOperation();
1609: }
1610: }
1611:
1612: public void rollbackToSavepoint(String savepoint) {
1613: beginOperation(false);
1614: try {
1615: assertActiveTransaction();
1616:
1617: int index = (_savepoints == null) ? -1 : _savepoints
1618: .indexOf(savepoint);
1619: if (index < 0)
1620: throw new UserException(_loc.get("no-savepoint",
1621: savepoint));
1622:
1623: // clear old in reverse
1624: OpenJPASavepoint save;
1625: while (_savepoints.size() > index + 1) {
1626: save = (OpenJPASavepoint) _savepoints
1627: .remove(_savepoints.size() - 1);
1628: save.release(false);
1629: }
1630:
1631: save = (OpenJPASavepoint) _savepoints.remove(index);
1632: Collection saved = save.rollback(_savepoints.values());
1633: if (_savepointCache != null)
1634: _savepointCache.clear();
1635: if (hasTransactionalObjects()) {
1636: // build up a new collection of states
1637: TransactionalCache oldTransCache = _transCache;
1638: TransactionalCache newTransCache = new TransactionalCache(
1639: _orderDirty);
1640: _transCache = null;
1641:
1642: // currently there is the assumption that incremental
1643: // flush is either a) not allowed, or b) required
1644: // pre-savepoint. this solves a number of issues including
1645: // storing flushed states as well as OID handling.
1646: // if future plugins do not follow this, we need to cache
1647: // more info per state
1648: SavepointFieldManager fm;
1649: StateManagerImpl sm;
1650: for (Iterator itr = saved.iterator(); itr.hasNext();) {
1651: fm = (SavepointFieldManager) itr.next();
1652: sm = fm.getStateManager();
1653: sm.rollbackToSavepoint(fm);
1654: oldTransCache.remove(sm);
1655: if (sm.isDirty())
1656: newTransCache.addDirty(sm);
1657: else
1658: newTransCache.addClean(sm);
1659: }
1660: for (Iterator itr = oldTransCache.iterator(); itr
1661: .hasNext();) {
1662: sm = (StateManagerImpl) itr.next();
1663: sm.rollback();
1664: removeFromTransaction(sm);
1665: }
1666: _transCache = newTransCache;
1667: }
1668: } catch (OpenJPAException ke) {
1669: throw ke;
1670: } catch (Exception e) {
1671: throw new GeneralException(e);
1672: } finally {
1673: endOperation();
1674: }
1675: }
1676:
1677: public void flush() {
1678: beginOperation(true);
1679: try {
1680: // return silently if no trans is active, or if this is a reentrant
1681: // call, which can happen if the store manager tries to get an
1682: // auto-inc oid during flush
1683: if ((_flags & FLAG_ACTIVE) == 0
1684: || (_flags & FLAG_STORE_FLUSHING) != 0)
1685: return;
1686:
1687: // make sure the runtime supports it
1688: if (!_conf.supportedOptions().contains(
1689: _conf.OPTION_INC_FLUSH))
1690: throw new UnsupportedException(_loc
1691: .get("incremental-flush-not-supported"));
1692: if (_savepoints != null && !_savepoints.isEmpty()
1693: && !_spm.supportsIncrementalFlush())
1694: throw new UnsupportedException(_loc
1695: .get("savepoint-flush-not-supported"));
1696:
1697: try {
1698: flushSafe(FLUSH_INC);
1699: _flags |= FLAG_FLUSHED;
1700: } catch (OpenJPAException ke) {
1701: // rollback on flush error; objects may be in inconsistent state
1702: setRollbackOnly(ke);
1703: throw ke.setFatal(true);
1704: } catch (RuntimeException re) {
1705: // rollback on flush error; objects may be in inconsistent state
1706: setRollbackOnly(re);
1707: throw new StoreException(re).setFatal(true);
1708: }
1709: } finally {
1710: endOperation();
1711: }
1712: }
1713:
1714: public void preFlush() {
1715: beginOperation(true);
1716: try {
1717: if ((_flags & FLAG_ACTIVE) != 0)
1718: flushSafe(FLUSH_LOGICAL);
1719: } finally {
1720: endOperation();
1721: }
1722: }
1723:
1724: public void validateChanges() {
1725: beginOperation(true);
1726: try {
1727: // if no trans, just return; if active datastore trans, flush
1728: if ((_flags & FLAG_ACTIVE) == 0)
1729: return;
1730: if ((_flags & FLAG_STORE_ACTIVE) != 0) {
1731: flush();
1732: return;
1733: }
1734:
1735: // make sure the runtime supports inc flush
1736: if (!_conf.supportedOptions().contains(
1737: _conf.OPTION_INC_FLUSH))
1738: throw new UnsupportedException(_loc
1739: .get("incremental-flush-not-supported"));
1740:
1741: try {
1742: flushSafe(FLUSH_ROLLBACK);
1743: } catch (OpenJPAException ke) {
1744: throw ke;
1745: } catch (RuntimeException re) {
1746: throw new StoreException(re);
1747: }
1748: } finally {
1749: endOperation();
1750: }
1751: }
1752:
1753: public boolean isActive() {
1754: beginOperation(true);
1755: try {
1756: return (_flags & FLAG_ACTIVE) != 0;
1757: } finally {
1758: endOperation();
1759: }
1760: }
1761:
1762: public boolean isStoreActive() {
1763: // we need to lock here, because we might be in the middle of an
1764: // atomic transaction process (e.g., commitAndResume)
1765: beginOperation(true);
1766: try {
1767: return (_flags & FLAG_STORE_ACTIVE) != 0;
1768: } finally {
1769: endOperation();
1770: }
1771: }
1772:
1773: /**
1774: * Return whether the current transaction is ending, i.e. in the 2nd phase
1775: * of a commit or rollback
1776: */
1777: boolean isTransactionEnding() {
1778: return (_flags & FLAG_TRANS_ENDING) != 0;
1779: }
1780:
1781: public boolean beginOperation(boolean syncTrans) {
1782: lock();
1783: try {
1784: assertOpen();
1785:
1786: if (syncTrans && _operationCount == 0 && _syncManaged
1787: && (_flags & FLAG_ACTIVE) == 0)
1788: syncWithManagedTransaction();
1789: return _operationCount++ == 1;
1790: } catch (OpenJPAException ke) {
1791: unlock();
1792: throw ke;
1793: } catch (RuntimeException re) {
1794: unlock();
1795: throw new GeneralException(re);
1796: }
1797: }
1798:
1799: /**
1800: * Mark the operation over. If outermost caller of stack, returns true
1801: * and will detach managed instances if necessary.
1802: */
1803: public boolean endOperation() {
1804: try {
1805: if (_operationCount == 1
1806: && (_autoDetach & DETACH_NONTXREAD) != 0
1807: && (_flags & FLAG_ACTIVE) == 0) {
1808: detachAllInternal(null);
1809: }
1810: if (_operationCount < 1)
1811: throw new InternalException(_loc
1812: .get("multi-threaded-access"));
1813: return _operationCount == 1;
1814: } catch (OpenJPAException ke) {
1815: throw ke;
1816: } catch (RuntimeException re) {
1817: throw new GeneralException(re);
1818: } finally {
1819: _operationCount--;
1820: if (_operationCount == 0)
1821: initializeOperatingSet();
1822: unlock();
1823: }
1824: }
1825:
1826: public Synchronization getSynchronization() {
1827: return _sync;
1828: }
1829:
1830: public void setSynchronization(Synchronization sync) {
1831: assertOpen();
1832: _sync = sync;
1833: }
1834:
1835: ///////////////////////////////////////////////
1836: // Implementation of Synchronization interface
1837: ///////////////////////////////////////////////
1838:
1839: public void beforeCompletion() {
1840: beginOperation(false);
1841: try {
1842: // user-supplied synchronization
1843: if (_sync != null)
1844: _sync.beforeCompletion();
1845:
1846: flushSafe(FLUSH_COMMIT);
1847: } catch (OpenJPAException ke) {
1848: if (_log.isTraceEnabled())
1849: _log.trace(_loc.get("end-trans-error"), ke);
1850: throw translateManagedCompletionException(ke);
1851: } catch (RuntimeException re) {
1852: if (_log.isTraceEnabled())
1853: _log.trace(_loc.get("end-trans-error"), re);
1854: throw translateManagedCompletionException(new StoreException(
1855: re));
1856: } finally {
1857: endOperation();
1858: }
1859: }
1860:
1861: public void afterCompletion(int status) {
1862: beginOperation(false);
1863: try {
1864: assertActiveTransaction();
1865:
1866: _flags |= FLAG_TRANS_ENDING;
1867: endTransaction(status);
1868: if (_sync != null)
1869: _sync.afterCompletion(status);
1870:
1871: if ((_autoDetach & DETACH_COMMIT) != 0)
1872: detachAllInternal(null);
1873: else if (status == Status.STATUS_ROLLEDBACK
1874: && (_autoDetach & DETACH_ROLLBACK) != 0) {
1875: detachAllInternal(null);
1876: }
1877:
1878: // in an ee context, it's possible that the user tried to close
1879: // us but we didn't actually close because we were waiting on this
1880: // transaction; if that's true, then close now
1881: if ((_flags & FLAG_CLOSE_INVOKED) != 0
1882: && _compat.getCloseOnManagedCommit())
1883: free();
1884: } catch (OpenJPAException ke) {
1885: if (_log.isTraceEnabled())
1886: _log.trace(_loc.get("end-trans-error"), ke);
1887: throw translateManagedCompletionException(ke);
1888: } catch (RuntimeException re) {
1889: if (_log.isTraceEnabled())
1890: _log.trace(_loc.get("end-trans-error"), re);
1891: throw translateManagedCompletionException(new StoreException(
1892: re));
1893: } finally {
1894: _flags &= ~FLAG_ACTIVE;
1895: _flags &= ~FLAG_FLUSHED;
1896: _flags &= ~FLAG_TRANS_ENDING;
1897:
1898: // event manager nulled if freed broker
1899: if (_transEventManager != null
1900: && _transEventManager.hasEndListeners()) {
1901: fireTransactionEvent(new TransactionEvent(
1902: this ,
1903: status == Status.STATUS_COMMITTED ? TransactionEvent.AFTER_COMMIT_COMPLETE
1904: : TransactionEvent.AFTER_ROLLBACK_COMPLETE,
1905: null, null, null, null));
1906: }
1907:
1908: endOperation();
1909: }
1910: }
1911:
1912: /**
1913: * If we're in a managed transaction, use our implicit behavior exception
1914: * translator to translate before/afterCompletion callback errors.
1915: */
1916: private RuntimeException translateManagedCompletionException(
1917: RuntimeException re) {
1918: return (!_managed || _extrans == null) ? re : _extrans
1919: .translate(re);
1920: }
1921:
1922: /**
1923: * Flush safely, catching reentrant calls.
1924: */
1925: private void flushSafe(int reason) {
1926: if ((_flags & FLAG_FLUSHING) != 0)
1927: throw new InvalidStateException(_loc.get("reentrant-flush"));
1928:
1929: _flags |= FLAG_FLUSHING;
1930: try {
1931: flush(reason);
1932: } finally {
1933: _flags &= ~FLAG_FLUSHING;
1934: }
1935: }
1936:
1937: /**
1938: * Flush the transactional state to the data store. Subclasses that
1939: * customize commit behavior should override this method. The method
1940: * assumes that the persistence manager is locked, is not closed,
1941: * and has an active transaction.
1942: *
1943: * @param reason one of {@link #FLUSH_INC}, {@link #FLUSH_COMMIT},
1944: * {@link #FLUSH_ROLLBACK}, or {@link #FLUSH_LOGICAL}
1945: * @since 0.2.5
1946: */
1947: protected void flush(int reason) {
1948: // this will enlist proxied states as necessary so we know whether we
1949: // have anything to flush
1950: Collection transactional = getTransactionalStates();
1951:
1952: // do we actually have to flush? only if our flags say so, or if
1953: // we have transaction listeners that need to be invoked for commit
1954: // (no need to invoke them on inc flush if nothing is dirty). we
1955: // special case the remote commit listener used by the datacache cause
1956: // we know it doesn't require the commit event when nothing changes
1957: boolean flush = (_flags & FLAG_FLUSH_REQUIRED) != 0;
1958: boolean listeners = (_transEventManager.hasFlushListeners() || _transEventManager
1959: .hasEndListeners())
1960: && ((_flags & FLAG_REMOTE_LISTENER) == 0 || _transEventManager
1961: .getListeners().size() > 1);
1962: if (!flush && (reason != FLUSH_COMMIT || !listeners))
1963: return;
1964:
1965: Collection mobjs = null;
1966: _flags |= FLAG_PRESTORING;
1967: try {
1968: if (flush) {
1969: // call pre store on all currently transactional objs
1970: for (Iterator itr = transactional.iterator(); itr
1971: .hasNext();)
1972: ((StateManagerImpl) itr.next()).beforeFlush(reason,
1973: _call);
1974: flushAdditions(transactional, reason);
1975: }
1976:
1977: // hopefully now all dependent instances that are going to end
1978: // up referenced have been marked as such; delete unrefed
1979: // dependents
1980: _flags |= FLAG_DEREFDELETING;
1981: if (flush && _derefCache != null && !_derefCache.isEmpty()) {
1982: for (Iterator itr = _derefCache.iterator(); itr
1983: .hasNext();)
1984: deleteDeref((StateManagerImpl) itr.next());
1985: flushAdditions(transactional, reason);
1986: }
1987:
1988: if (reason != FLUSH_LOGICAL) {
1989: // if no datastore transaction, start one; even if we don't
1990: // think we'll need to flush at this point, our transaction
1991: // listeners might introduce some dirty objects or interact
1992: // directly with the database
1993: if ((_flags & FLAG_STORE_ACTIVE) == 0)
1994: beginStoreManagerTransaction(false);
1995:
1996: if ((_transEventManager.hasFlushListeners() || _transEventManager
1997: .hasEndListeners())
1998: && (flush || reason == FLUSH_COMMIT)) {
1999: // fire events
2000: mobjs = new ManagedObjectCollection(transactional);
2001: if (reason == FLUSH_COMMIT
2002: && _transEventManager.hasEndListeners()) {
2003: fireTransactionEvent(new TransactionEvent(this ,
2004: TransactionEvent.BEFORE_COMMIT, mobjs,
2005: _persistedClss, _updatedClss,
2006: _deletedClss));
2007:
2008: flushAdditions(transactional, reason);
2009: flush = (_flags & FLAG_FLUSH_REQUIRED) != 0;
2010: }
2011:
2012: if (flush && _transEventManager.hasFlushListeners()) {
2013: fireTransactionEvent(new TransactionEvent(this ,
2014: TransactionEvent.BEFORE_FLUSH, mobjs,
2015: _persistedClss, _updatedClss,
2016: _deletedClss));
2017: flushAdditions(transactional, reason);
2018: }
2019: }
2020: }
2021: } finally {
2022: _flags &= ~FLAG_PRESTORING;
2023: _flags &= ~FLAG_DEREFDELETING;
2024: _transAdditions = null;
2025: _derefAdditions = null;
2026:
2027: // also clear derefed set; the deletes have been recorded
2028: if (_derefCache != null)
2029: _derefCache = null;
2030: }
2031:
2032: // flush to store manager
2033: List exceps = null;
2034: try {
2035: if (flush && reason != FLUSH_LOGICAL) {
2036: _flags |= FLAG_STORE_FLUSHING;
2037: exceps = add(exceps, newFlushException(_store
2038: .flush(transactional)));
2039: }
2040: } finally {
2041: _flags &= ~FLAG_STORE_FLUSHING;
2042:
2043: if (reason == FLUSH_ROLLBACK)
2044: exceps = add(exceps, endStoreManagerTransaction(true));
2045: else if (reason != FLUSH_LOGICAL)
2046: _flags &= ~FLAG_FLUSH_REQUIRED;
2047:
2048: // mark states as flushed
2049: if (flush) {
2050: StateManagerImpl sm;
2051: for (Iterator itr = transactional.iterator(); itr
2052: .hasNext();) {
2053: sm = (StateManagerImpl) itr.next();
2054: try {
2055: // the state may have become transient, such as if
2056: // it is embedded and the owner has been deleted during
2057: // this flush process; bug #1100
2058: if (sm.getPCState() == PCState.TRANSIENT)
2059: continue;
2060:
2061: sm.afterFlush(reason);
2062: if (reason == FLUSH_INC) {
2063: // if not about to clear trans cache for commit
2064: // anyway, re-cache dirty objects with default soft
2065: // refs; we don't need hard refs now that the
2066: // changes have been flushed
2067: sm.proxyFields(true, false);
2068: _transCache.flushed(sm);
2069: }
2070: } catch (Exception e) {
2071: exceps = add(exceps, e);
2072: }
2073: }
2074: }
2075: }
2076:
2077: // throw any exceptions to shortcut listeners on fail
2078: throwNestedExceptions(exceps, true);
2079:
2080: if (flush && reason != FLUSH_ROLLBACK
2081: && reason != FLUSH_LOGICAL
2082: && _transEventManager.hasFlushListeners()) {
2083: fireTransactionEvent(new TransactionEvent(this ,
2084: TransactionEvent.AFTER_FLUSH, mobjs,
2085: _persistedClss, _updatedClss, _deletedClss));
2086: }
2087: }
2088:
2089: /**
2090: * Flush newly-transactional objects.
2091: */
2092: private void flushAdditions(Collection transactional, int reason) {
2093: boolean loop;
2094: do {
2095: // flush new transactional instances; note logical or
2096: loop = flushTransAdditions(transactional, reason)
2097: | deleteDerefAdditions(_derefCache);
2098: } while (loop);
2099: }
2100:
2101: /**
2102: * Flush transactional additions.
2103: */
2104: private boolean flushTransAdditions(Collection transactional,
2105: int reason) {
2106: if (_transAdditions == null || _transAdditions.isEmpty())
2107: return false;
2108:
2109: // keep local transactional list copy up to date
2110: transactional.addAll(_transAdditions);
2111:
2112: // copy the change set, then clear it for the next iteration
2113: StateManagerImpl[] states = (StateManagerImpl[]) _transAdditions
2114: .toArray(new StateManagerImpl[_transAdditions.size()]);
2115: _transAdditions = null;
2116:
2117: for (int i = 0; i < states.length; i++)
2118: states[i].beforeFlush(reason, _call);
2119: return true;
2120: }
2121:
2122: /**
2123: * Delete new dereferenced objects.
2124: */
2125: private boolean deleteDerefAdditions(Collection derefs) {
2126: if (_derefAdditions == null || _derefAdditions.isEmpty())
2127: return false;
2128:
2129: // remember these additions in case one becomes derefed again later
2130: derefs.addAll(_derefAdditions);
2131:
2132: StateManagerImpl[] states = (StateManagerImpl[]) _derefAdditions
2133: .toArray(new StateManagerImpl[_derefAdditions.size()]);
2134: _derefAdditions = null;
2135:
2136: for (int i = 0; i < states.length; i++)
2137: deleteDeref(states[i]);
2138: return true;
2139: }
2140:
2141: /**
2142: * Delete a dereferenced dependent.
2143: */
2144: private void deleteDeref(StateManagerImpl sm) {
2145: int action = processArgument(OpCallbacks.OP_DELETE, sm
2146: .getManagedInstance(), sm, null);
2147: if ((action & OpCallbacks.ACT_RUN) != 0)
2148: sm.delete();
2149: if ((action & OpCallbacks.ACT_CASCADE) != 0)
2150: sm.cascadeDelete(_call);
2151: }
2152:
2153: /**
2154: * Determine the action to take based on the user's given callbacks and
2155: * our implicit behavior.
2156: */
2157: private int processArgument(int op, Object obj,
2158: OpenJPAStateManager sm, OpCallbacks call) {
2159: if (call != null)
2160: return call.processArgument(op, obj, sm);
2161: if (_call != null)
2162: return _call.processArgument(op, obj, sm);
2163: return OpCallbacks.ACT_RUN | OpCallbacks.ACT_CASCADE;
2164: }
2165:
2166: /**
2167: * Throw the proper exception based on the given set of flush errors, or
2168: * do nothing if no errors occurred.
2169: */
2170: private OpenJPAException newFlushException(Collection exceps) {
2171: if (exceps == null || exceps.isEmpty())
2172: return null;
2173:
2174: Throwable[] t = (Throwable[]) exceps
2175: .toArray(new Throwable[exceps.size()]);
2176: List failed = new ArrayList(t.length);
2177:
2178: // create fatal exception with nested exceptions for all the failed
2179: // objects; if all OL exceptions, throw a top-level OL exception
2180: boolean opt = true;
2181: for (int i = 0; opt && i < t.length; i++) {
2182: opt = t[i] instanceof OptimisticException;
2183: if (opt) {
2184: Object f = ((OptimisticException) t[i])
2185: .getFailedObject();
2186: if (f != null)
2187: failed.add(f);
2188: }
2189: }
2190: if (opt && !failed.isEmpty())
2191: return new OptimisticException(failed, t);
2192: if (opt)
2193: return new OptimisticException(t);
2194: return new StoreException(_loc.get("rolled-back"))
2195: .setNestedThrowables(t).setFatal(true);
2196: }
2197:
2198: /**
2199: * End the current transaction, making appropriate state transitions.
2200: */
2201: protected void endTransaction(int status) {
2202: // if a data store transaction was in progress, do the
2203: // appropriate transaction change
2204: boolean rollback = status != Status.STATUS_COMMITTED;
2205: List exceps = null;
2206:
2207: try {
2208: exceps = add(exceps, endStoreManagerTransaction(rollback));
2209: } catch (RuntimeException re) {
2210: rollback = true;
2211: exceps = add(exceps, re);
2212: }
2213:
2214: // go back to default none lock level
2215: _fc.setReadLockLevel(LOCK_NONE);
2216: _fc.setWriteLockLevel(LOCK_NONE);
2217: _fc.setLockTimeout(-1);
2218:
2219: Collection transStates;
2220: if (hasTransactionalObjects())
2221: transStates = _transCache;
2222: else
2223: transStates = Collections.EMPTY_LIST;
2224:
2225: // fire after rollback/commit event
2226: Collection mobjs = null;
2227: if (_transEventManager.hasEndListeners()) {
2228: mobjs = new ManagedObjectCollection(transStates);
2229: int eventType = (rollback) ? TransactionEvent.AFTER_ROLLBACK
2230: : TransactionEvent.AFTER_COMMIT;
2231: fireTransactionEvent(new TransactionEvent(this , eventType,
2232: mobjs, _persistedClss, _updatedClss, _deletedClss));
2233: }
2234:
2235: // null transactional caches now so that all the removeFromTransaction
2236: // calls as we transition each object don't have to do any work; don't
2237: // clear trans cache object because we still need the transStates
2238: // reference to it below
2239: _transCache = null;
2240: if (_persistedClss != null)
2241: _persistedClss = null;
2242: if (_updatedClss != null)
2243: _updatedClss = null;
2244: if (_deletedClss != null)
2245: _deletedClss = null;
2246:
2247: // new cache would get cleared anyway during transitions, but doing so
2248: // immediately saves us some lookups
2249: _cache.clearNew();
2250:
2251: // tell all derefed instances they're no longer derefed; we can't
2252: // rely on rollback and commit calls below cause some instances might
2253: // not be transactional
2254: if (_derefCache != null && !_derefCache.isEmpty()) {
2255: for (Iterator itr = _derefCache.iterator(); itr.hasNext();)
2256: ((StateManagerImpl) itr.next())
2257: .setDereferencedDependent(false, false);
2258: _derefCache = null;
2259: }
2260:
2261: // peform commit or rollback state transitions on each instance
2262: StateManagerImpl sm;
2263: for (Iterator itr = transStates.iterator(); itr.hasNext();) {
2264: sm = (StateManagerImpl) itr.next();
2265: try {
2266: if (rollback) {
2267: // tell objects that may have been derefed then flushed
2268: // (and therefore deleted) to un-deref
2269: sm.setDereferencedDependent(false, false);
2270: sm.rollback();
2271: } else
2272: sm.commit();
2273: } catch (RuntimeException re) {
2274: exceps = add(exceps, re);
2275: }
2276: }
2277:
2278: // notify the lock manager to clean up and release remaining locks
2279: _lm.endTransaction();
2280:
2281: // clear old savepoints in reverse
2282: OpenJPASavepoint save;
2283: while (_savepoints != null && _savepoints.size() > 0) {
2284: save = (OpenJPASavepoint) _savepoints.remove(_savepoints
2285: .size() - 1);
2286: save.release(false);
2287: }
2288: _savepoints = null;
2289: _savepointCache = null;
2290:
2291: // fire after state change event
2292: if (_transEventManager.hasEndListeners())
2293: fireTransactionEvent(new TransactionEvent(this ,
2294: TransactionEvent.AFTER_STATE_TRANSITIONS, mobjs,
2295: null, null, null));
2296:
2297: // now clear trans cache; keep cleared version rather than
2298: // null to avoid having to re-create the set later; more efficient
2299: if (transStates != Collections.EMPTY_LIST) {
2300: _transCache = (TransactionalCache) transStates;
2301: _transCache.clear();
2302: }
2303:
2304: throwNestedExceptions(exceps, true);
2305: }
2306:
2307: ////////////////////
2308: // Object lifecycle
2309: ////////////////////
2310:
2311: public void persist(Object obj, OpCallbacks call) {
2312: persist(obj, null, true, call);
2313: }
2314:
2315: public OpenJPAStateManager persist(Object obj, Object id,
2316: OpCallbacks call) {
2317: return persist(obj, id, true, call);
2318: }
2319:
2320: public void persistAll(Collection objs, OpCallbacks call) {
2321: persistAll(objs, true, call);
2322: }
2323:
2324: /**
2325: * Persist the given objects. Indicate whether this was an explicit persist
2326: * (PNEW) or a provisonal persist (PNEWPROVISIONAL).
2327: */
2328: public void persistAll(Collection objs, boolean explicit,
2329: OpCallbacks call) {
2330: if (objs.isEmpty())
2331: return;
2332:
2333: beginOperation(true);
2334: List exceps = null;
2335: try {
2336: assertWriteOperation();
2337:
2338: for (Iterator itr = objs.iterator(); itr.hasNext();) {
2339: try {
2340: persist(itr.next(), explicit, call);
2341: } catch (UserException ue) {
2342: exceps = add(exceps, ue);
2343: }
2344: }
2345: } finally {
2346: endOperation();
2347: }
2348: throwNestedExceptions(exceps, false);
2349: }
2350:
2351: /**
2352: * If the given element is not null, add it to the given list,
2353: * creating the list if necessary.
2354: */
2355: private List add(List l, Object o) {
2356: if (o == null)
2357: return l;
2358: if (l == null)
2359: l = new LinkedList();
2360: l.add(o);
2361: return l;
2362: }
2363:
2364: /**
2365: * Throw an exception wrapping the given nested exceptions.
2366: */
2367: private void throwNestedExceptions(List exceps, boolean datastore) {
2368: if (exceps == null || exceps.isEmpty())
2369: return;
2370: if (datastore && exceps.size() == 1)
2371: throw (RuntimeException) exceps.get(0);
2372:
2373: boolean fatal = false;
2374: Throwable[] t = (Throwable[]) exceps
2375: .toArray(new Throwable[exceps.size()]);
2376: for (int i = 0; i < t.length; i++) {
2377: if (t[i] instanceof OpenJPAException
2378: && ((OpenJPAException) t[i]).isFatal())
2379: fatal = true;
2380: }
2381: OpenJPAException err;
2382: if (datastore)
2383: err = new StoreException(_loc.get("nested-exceps"));
2384: else
2385: err = new UserException(_loc.get("nested-exceps"));
2386: throw err.setNestedThrowables(t).setFatal(fatal);
2387: }
2388:
2389: /**
2390: * Persist the given object. Indicate whether this was an explicit persist
2391: * (PNEW) or a provisonal persist (PNEWPROVISIONAL)
2392: */
2393: public void persist(Object obj, boolean explicit, OpCallbacks call) {
2394: persist(obj, null, explicit, call);
2395: }
2396:
2397: /**
2398: * Persist the given object. Indicate whether this was an explicit persist
2399: * (PNEW) or a provisonal persist (PNEWPROVISIONAL).
2400: * See {@link Broker} for details on this method.
2401: */
2402: public OpenJPAStateManager persist(Object obj, Object id,
2403: boolean explicit, OpCallbacks call) {
2404: if (obj == null)
2405: return null;
2406:
2407: beginOperation(true);
2408: try {
2409: assertWriteOperation();
2410:
2411: StateManagerImpl sm = getStateManagerImpl(obj, true);
2412: if (!_operating.add(obj))
2413: return sm;
2414:
2415: int action = processArgument(OpCallbacks.OP_PERSIST, obj,
2416: sm, call);
2417: if (action == OpCallbacks.ACT_NONE)
2418: return sm;
2419:
2420: // ACT_CASCADE
2421: if ((action & OpCallbacks.ACT_RUN) == 0) {
2422: if (sm != null)
2423: sm.cascadePersist(call);
2424: else
2425: cascadeTransient(OpCallbacks.OP_PERSIST, obj, call,
2426: "persist");
2427: return sm;
2428: }
2429:
2430: // ACT_RUN
2431: PersistenceCapable pc;
2432: if (sm != null) {
2433: if (sm.isDetached())
2434: throw new ObjectExistsException(_loc.get(
2435: "persist-detached", Exceptions
2436: .toString(obj)))
2437: .setFailedObject(obj);
2438:
2439: if (!sm.isEmbedded()) {
2440: sm.persist();
2441: _cache.persist(sm);
2442: if ((action & OpCallbacks.ACT_CASCADE) != 0)
2443: sm.cascadePersist(call);
2444: return sm;
2445: }
2446:
2447: // an embedded field; notify the owner that the value has
2448: // changed by becoming independently persistent
2449: sm.getOwner().dirty(sm.getOwnerIndex());
2450: _cache.persist(sm);
2451: pc = sm.getPersistenceCapable();
2452: } else {
2453: pc = assertPersistenceCapable(obj);
2454: if (pc.pcIsDetached() == Boolean.TRUE)
2455: throw new ObjectExistsException(_loc.get(
2456: "persist-detached", Exceptions
2457: .toString(obj)))
2458: .setFailedObject(obj);
2459: }
2460:
2461: ClassMetaData meta = _conf.getMetaDataRepositoryInstance()
2462: .getMetaData(obj.getClass(), _loader, true);
2463: fireLifecycleEvent(obj, null, meta,
2464: LifecycleEvent.BEFORE_PERSIST);
2465:
2466: // create id for instance
2467: if (id == null) {
2468: if (meta.getIdentityType() == ClassMetaData.ID_APPLICATION)
2469: id = ApplicationIds.create(pc, meta);
2470: else if (meta.getIdentityType() == ClassMetaData.ID_UNKNOWN)
2471: throw new UserException(_loc.get("meta-unknownid",
2472: meta));
2473: else
2474: id = StateManagerId.newInstance(this );
2475: }
2476:
2477: // make sure we don't already have the instance cached
2478: checkForDuplicateId(id, obj);
2479:
2480: // if had embedded sm, null it
2481: if (sm != null)
2482: pc.pcReplaceStateManager(null);
2483:
2484: // create new sm
2485: sm = new StateManagerImpl(id, meta, this );
2486: if ((_flags & FLAG_ACTIVE) != 0) {
2487: if (explicit)
2488: sm.initialize(pc, PCState.PNEW);
2489: else
2490: sm.initialize(pc, PCState.PNEWPROVISIONAL);
2491: } else
2492: sm.initialize(pc, PCState.PNONTRANSNEW);
2493: if ((action & OpCallbacks.ACT_CASCADE) != 0)
2494: sm.cascadePersist(call);
2495: return sm;
2496: } catch (OpenJPAException ke) {
2497: throw ke;
2498: } catch (RuntimeException re) {
2499: throw new GeneralException(re);
2500: } finally {
2501: endOperation();
2502: }
2503: }
2504:
2505: /**
2506: * Temporarily manage the given instance in order to cascade the given
2507: * operation through it.
2508: */
2509: private void cascadeTransient(int op, Object obj, OpCallbacks call,
2510: String errOp) {
2511: PersistenceCapable pc = assertPersistenceCapable(obj);
2512:
2513: // if using detached state manager, don't replace
2514: if (pc.pcGetStateManager() != null)
2515: throw newDetachedException(obj, errOp);
2516:
2517: ClassMetaData meta = _conf.getMetaDataRepositoryInstance()
2518: .getMetaData(obj.getClass(), _loader, true);
2519: StateManagerImpl sm = new StateManagerImpl(StateManagerId
2520: .newInstance(this ), meta, this );
2521: sm.initialize(pc, PCState.TLOADED);
2522: try {
2523: switch (op) {
2524: case OpCallbacks.OP_PERSIST:
2525: sm.cascadePersist(call);
2526: break;
2527: case OpCallbacks.OP_DELETE:
2528: sm.cascadeDelete(call);
2529: break;
2530: case OpCallbacks.OP_REFRESH:
2531: sm.gatherCascadeRefresh(call);
2532: break;
2533: default:
2534: throw new InternalException(String.valueOf(op));
2535: }
2536: } finally {
2537: sm.release(true);
2538: }
2539: }
2540:
2541: public void deleteAll(Collection objs, OpCallbacks call) {
2542: beginOperation(true);
2543: try {
2544: assertWriteOperation();
2545:
2546: List exceps = null;
2547: Object obj;
2548: for (Iterator itr = objs.iterator(); itr.hasNext();) {
2549: try {
2550: obj = itr.next();
2551: if (obj != null)
2552: delete(obj, getStateManagerImpl(obj, true),
2553: call);
2554: } catch (UserException ue) {
2555: exceps = add(exceps, ue);
2556: }
2557: }
2558: throwNestedExceptions(exceps, false);
2559: } finally {
2560: endOperation();
2561: }
2562: }
2563:
2564: public void delete(Object obj, OpCallbacks call) {
2565: if (obj == null)
2566: return;
2567:
2568: beginOperation(true);
2569: try {
2570: assertWriteOperation();
2571: delete(obj, getStateManagerImpl(obj, true), call);
2572: } catch (OpenJPAException ke) {
2573: throw ke;
2574: } catch (RuntimeException re) {
2575: throw new GeneralException(re);
2576: } finally {
2577: endOperation();
2578: }
2579: }
2580:
2581: /**
2582: * Internal delete.
2583: */
2584: void delete(Object obj, StateManagerImpl sm, OpCallbacks call) {
2585: if (!_operating.add(obj))
2586: return;
2587:
2588: int action = processArgument(OpCallbacks.OP_DELETE, obj, sm,
2589: call);
2590: if (action == OpCallbacks.ACT_NONE)
2591: return;
2592:
2593: // ACT_CASCADE
2594: if ((action & OpCallbacks.ACT_RUN) == 0) {
2595: if (sm != null)
2596: sm.cascadeDelete(call);
2597: else
2598: cascadeTransient(OpCallbacks.OP_DELETE, obj, call,
2599: "delete");
2600: return;
2601: }
2602:
2603: // ACT_RUN
2604: if (sm != null) {
2605: if (sm.isDetached())
2606: throw newDetachedException(obj, "delete");
2607: if ((action & OpCallbacks.ACT_CASCADE) != 0)
2608: sm.cascadeDelete(call);
2609: sm.delete();
2610: } else if (assertPersistenceCapable(obj).pcIsDetached() == Boolean.TRUE)
2611: throw newDetachedException(obj, "delete");
2612: }
2613:
2614: /**
2615: * Throw an exception indicating that the current action can't be
2616: * performed on a detached object.
2617: */
2618: private OpenJPAException newDetachedException(Object obj,
2619: String operation) {
2620: throw new UserException(_loc.get("bad-detached-op", operation,
2621: Exceptions.toString(obj))).setFailedObject(obj);
2622: }
2623:
2624: public void releaseAll(Collection objs, OpCallbacks call) {
2625: beginOperation(false);
2626: try {
2627: List exceps = null;
2628: for (Iterator itr = objs.iterator(); itr.hasNext();) {
2629: try {
2630: release(itr.next(), call);
2631: } catch (UserException ue) {
2632: exceps = add(exceps, ue);
2633: }
2634: }
2635: throwNestedExceptions(exceps, false);
2636: } finally {
2637: endOperation();
2638: }
2639: }
2640:
2641: public void release(Object obj, OpCallbacks call) {
2642: if (obj == null)
2643: return;
2644:
2645: beginOperation(false);
2646: try {
2647: StateManagerImpl sm = getStateManagerImpl(obj, true);
2648: int action = processArgument(OpCallbacks.OP_RELEASE, obj,
2649: sm, call);
2650:
2651: if (sm == null)
2652: return;
2653: if ((action & OpCallbacks.ACT_RUN) != 0
2654: && sm.isPersistent()) {
2655: boolean pending = sm.isPendingTransactional();
2656: sm.release(true);
2657: if (pending)
2658: removeFromPendingTransaction(sm);
2659: }
2660: } catch (OpenJPAException ke) {
2661: throw ke;
2662: } catch (RuntimeException re) {
2663: throw new GeneralException(re);
2664: } finally {
2665: endOperation();
2666: }
2667: }
2668:
2669: public OpenJPAStateManager embed(Object obj, Object id,
2670: OpenJPAStateManager owner, ValueMetaData ownerMeta) {
2671: beginOperation(true);
2672: try {
2673: StateManagerImpl orig = getStateManagerImpl(obj, true);
2674: if (orig != null) {
2675: // if already embedded, nothing to do
2676: if (orig.getOwner() == owner
2677: && orig.getMetaData().getEmbeddingMetaData() == ownerMeta)
2678: return orig;
2679:
2680: // otherwise make sure pc is fully loaded for when we copy its
2681: // data below
2682: orig.load(_fc, StateManagerImpl.LOAD_ALL, null, null,
2683: false);
2684: }
2685:
2686: // create new state manager with embedded metadata
2687: ClassMetaData meta = ownerMeta.getEmbeddedMetaData();
2688: if (meta == null)
2689: throw new InternalException(_loc.get("bad-embed",
2690: ownerMeta));
2691:
2692: if (id == null)
2693: id = StateManagerId.newInstance(this );
2694:
2695: StateManagerImpl sm = new StateManagerImpl(id, meta, this );
2696: sm.setOwner((StateManagerImpl) owner, ownerMeta);
2697:
2698: PersistenceCapable copy;
2699: PCState state;
2700: Class type = meta.getDescribedType();
2701: if (obj != null) {
2702: // give copy and the original instance the same state manager
2703: // so that we can copy fields from one to the other
2704: StateManagerImpl copySM;
2705: PersistenceCapable pc;
2706: if (orig == null) {
2707: copySM = sm;
2708: pc = assertPersistenceCapable(obj);
2709: pc.pcReplaceStateManager(sm);
2710: } else {
2711: copySM = orig;
2712: pc = orig.getPersistenceCapable();
2713: }
2714:
2715: try {
2716: // copy the instance. we do this even if it doesn't already
2717: // have a state manager in case it is later assigned to a
2718: // PC field; at that point it's too late to copy
2719: copy = PCRegistry.newInstance(type, copySM, false);
2720: int[] fields = new int[meta.getFields().length];
2721: for (int i = 0; i < fields.length; i++)
2722: fields[i] = i;
2723: copy.pcCopyFields(pc, fields);
2724: state = PCState.ECOPY;
2725: copy.pcReplaceStateManager(null);
2726: } finally {
2727: // if the instance didn't have a state manager to start,
2728: // revert it to being transient
2729: if (orig == null)
2730: pc.pcReplaceStateManager(null);
2731: }
2732: } else {
2733: copy = PCRegistry.newInstance(type, sm, false);
2734: if ((_flags & FLAG_ACTIVE) != 0 && !_optimistic)
2735: state = PCState.ECLEAN;
2736: else
2737: state = PCState.ENONTRANS;
2738: }
2739:
2740: sm.initialize(copy, state);
2741: return sm;
2742: } catch (OpenJPAException ke) {
2743: throw ke;
2744: } catch (RuntimeException re) {
2745: throw new GeneralException(re);
2746: } finally {
2747: endOperation();
2748: }
2749: }
2750:
2751: /**
2752: * If not already cached, create an empty copy of the given state
2753: * manager in the given state.
2754: */
2755: OpenJPAStateManager copy(OpenJPAStateManager copy, PCState state) {
2756: beginOperation(true);
2757: try {
2758: assertOpen();
2759: Object oid = copy.fetchObjectId();
2760: Class type = copy.getManagedInstance().getClass();
2761: if (oid == null)
2762: throw new InternalException();
2763: // cached instance?
2764: StateManagerImpl sm = null;
2765: if (!copy.isEmbedded())
2766: sm = getStateManagerImplById(oid, true);
2767: if (sm == null) {
2768: MetaDataRepository repos = _conf
2769: .getMetaDataRepositoryInstance();
2770: ClassMetaData meta = repos.getMetaData(type, _loader,
2771: true);
2772: // construct a new state manager with all info known
2773: sm = new StateManagerImpl(oid, meta, this );
2774: sm.setObjectId(oid);
2775: sm.initialize(sm.getMetaData().getDescribedType(),
2776: state);
2777: }
2778: return sm;
2779: } finally {
2780: endOperation();
2781: }
2782: }
2783:
2784: public void refreshAll(Collection objs, OpCallbacks call) {
2785: if (objs.isEmpty())
2786: return;
2787:
2788: beginOperation(true);
2789: try {
2790: assertNontransactionalRead();
2791:
2792: for (Iterator itr = objs.iterator(); itr.hasNext();)
2793: gatherCascadeRefresh(itr.next(), call);
2794: if (_operating.isEmpty())
2795: return;
2796: if (_operating.size() == 1)
2797: refreshInternal(_operating.iterator().next(), call);
2798: else
2799: refreshInternal(_operating, call);
2800: } finally {
2801: endOperation();
2802: }
2803: }
2804:
2805: public void refresh(Object obj, OpCallbacks call) {
2806: if (obj == null)
2807: return;
2808:
2809: beginOperation(true);
2810: try {
2811: assertNontransactionalRead();
2812:
2813: gatherCascadeRefresh(obj, call);
2814: if (_operating.isEmpty())
2815: return;
2816: if (_operating.size() == 1)
2817: refreshInternal(_operating.iterator().next(), call);
2818: else
2819: refreshInternal(_operating, call);
2820: } finally {
2821: endOperation();
2822: }
2823: }
2824:
2825: /**
2826: * Gathers all objects reachable through cascade-refresh relations
2827: * into the operating set.
2828: */
2829: void gatherCascadeRefresh(Object obj, OpCallbacks call) {
2830: if (obj == null)
2831: return;
2832: if (!_operating.add(obj))
2833: return;
2834:
2835: StateManagerImpl sm = getStateManagerImpl(obj, false);
2836: int action = processArgument(OpCallbacks.OP_REFRESH, obj, sm,
2837: call);
2838: if ((action & OpCallbacks.ACT_CASCADE) == 0)
2839: return;
2840:
2841: if (sm != null)
2842: sm.gatherCascadeRefresh(call);
2843: else
2844: cascadeTransient(OpCallbacks.OP_REFRESH, obj, call,
2845: "refresh");
2846: }
2847:
2848: /**
2849: * This method is called with the full set of objects reachable via
2850: * cascade-refresh relations from the user-given instances.
2851: */
2852: protected void refreshInternal(Collection objs, OpCallbacks call) {
2853: List exceps = null;
2854: try {
2855: // collect instances that need a refresh
2856: Collection load = null;
2857: StateManagerImpl sm;
2858: Object obj;
2859: for (Iterator itr = objs.iterator(); itr.hasNext();) {
2860: obj = itr.next();
2861: if (obj == null)
2862: continue;
2863:
2864: try {
2865: sm = getStateManagerImpl(obj, true);
2866: if ((processArgument(OpCallbacks.OP_REFRESH, obj,
2867: sm, call) & OpCallbacks.ACT_RUN) == 0)
2868: continue;
2869:
2870: if (sm != null) {
2871: if (sm.isDetached())
2872: throw newDetachedException(obj, "refresh");
2873: else if (sm.beforeRefresh(true)) {
2874: if (load == null)
2875: load = new ArrayList(objs.size());
2876: load.add(sm);
2877: }
2878: } else if (assertPersistenceCapable(obj)
2879: .pcIsDetached() == Boolean.TRUE)
2880: throw newDetachedException(obj, "refresh");
2881: } catch (OpenJPAException ke) {
2882: exceps = add(exceps, ke);
2883: }
2884: }
2885:
2886: // refresh all
2887: if (load != null) {
2888: Collection failed = _store.loadAll(load, null,
2889: StoreManager.FORCE_LOAD_REFRESH, _fc, null);
2890: if (failed != null && !failed.isEmpty())
2891: exceps = add(exceps,
2892: newObjectNotFoundException(failed));
2893:
2894: // perform post-refresh transitions and make sure all fetch
2895: // group fields are loaded
2896: for (Iterator itr = load.iterator(); itr.hasNext();) {
2897: sm = (StateManagerImpl) itr.next();
2898: if (failed != null && failed.contains(sm.getId()))
2899: continue;
2900:
2901: try {
2902: sm.afterRefresh();
2903: sm.load(_fc, StateManagerImpl.LOAD_FGS, null,
2904: null, false);
2905: } catch (OpenJPAException ke) {
2906: exceps = add(exceps, ke);
2907: }
2908: }
2909: }
2910:
2911: // now invoke postRefresh on all the instances
2912: for (Iterator itr = objs.iterator(); itr.hasNext();) {
2913: try {
2914: sm = getStateManagerImpl(itr.next(), true);
2915: if (sm != null && !sm.isDetached())
2916: fireLifecycleEvent(sm.getManagedInstance(),
2917: null, sm.getMetaData(),
2918: LifecycleEvent.AFTER_REFRESH);
2919: } catch (OpenJPAException ke) {
2920: exceps = add(exceps, ke);
2921: }
2922: }
2923: } catch (OpenJPAException ke) {
2924: throw ke;
2925: } catch (RuntimeException re) {
2926: throw new GeneralException(re);
2927: }
2928: throwNestedExceptions(exceps, false);
2929: }
2930:
2931: /**
2932: * Optimization for single-object refresh.
2933: */
2934: protected void refreshInternal(Object obj, OpCallbacks call) {
2935: try {
2936: StateManagerImpl sm = getStateManagerImpl(obj, true);
2937: if ((processArgument(OpCallbacks.OP_REFRESH, obj, sm, call) & OpCallbacks.ACT_RUN) == 0)
2938: return;
2939:
2940: if (sm != null) {
2941: if (sm.isDetached())
2942: throw newDetachedException(obj, "refresh");
2943: else if (sm.beforeRefresh(false)) {
2944: sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null,
2945: false);
2946: sm.afterRefresh();
2947: }
2948: fireLifecycleEvent(sm.getManagedInstance(), null, sm
2949: .getMetaData(), LifecycleEvent.AFTER_REFRESH);
2950: } else if (assertPersistenceCapable(obj).pcIsDetached() == Boolean.TRUE)
2951: throw newDetachedException(obj, "refresh");
2952: } catch (OpenJPAException ke) {
2953: throw ke;
2954: } catch (RuntimeException re) {
2955: throw new GeneralException(re);
2956: }
2957: }
2958:
2959: public void retrieveAll(Collection objs, boolean dfgOnly,
2960: OpCallbacks call) {
2961: if (objs.isEmpty())
2962: return;
2963: if (objs.size() == 1) {
2964: retrieve(objs.iterator().next(), dfgOnly, call);
2965: return;
2966: }
2967:
2968: List exceps = null;
2969: beginOperation(true);
2970: try {
2971: assertOpen();
2972: assertNontransactionalRead();
2973:
2974: // collect all hollow instances for load
2975: Object obj;
2976: Collection load = null;
2977: StateManagerImpl sm;
2978: Collection sms = new ArrayList(objs.size());
2979: for (Iterator itr = objs.iterator(); itr.hasNext();) {
2980: obj = itr.next();
2981: if (obj == null)
2982: continue;
2983:
2984: try {
2985: sm = getStateManagerImpl(obj, true);
2986: if ((processArgument(OpCallbacks.OP_RETRIEVE, obj,
2987: sm, call) & OpCallbacks.ACT_RUN) == 0)
2988: continue;
2989:
2990: if (sm != null) {
2991: if (sm.isDetached())
2992: throw newDetachedException(obj, "retrieve");
2993: if (sm.isPersistent()) {
2994: sms.add(sm);
2995: if (sm.getPCState() == PCState.HOLLOW) {
2996: if (load == null)
2997: load = new ArrayList();
2998: load.add(sm);
2999: }
3000: }
3001: } else if (assertPersistenceCapable(obj)
3002: .pcIsDetached() == Boolean.TRUE)
3003: throw newDetachedException(obj, "retrieve");
3004: } catch (UserException ue) {
3005: exceps = add(exceps, ue);
3006: }
3007: }
3008:
3009: // load all hollow instances
3010: Collection failed = null;
3011: if (load != null) {
3012: int mode = (dfgOnly) ? _store.FORCE_LOAD_DFG
3013: : _store.FORCE_LOAD_ALL;
3014: failed = _store.loadAll(load, null, mode, _fc, null);
3015: if (failed != null && !failed.isEmpty())
3016: exceps = add(exceps,
3017: newObjectNotFoundException(failed));
3018: }
3019:
3020: // retrieve all non-failed instances
3021: for (Iterator itr = sms.iterator(); itr.hasNext();) {
3022: sm = (StateManagerImpl) itr.next();
3023: if (failed != null && failed.contains(sm.getId()))
3024: continue;
3025:
3026: int mode = (dfgOnly) ? StateManagerImpl.LOAD_FGS
3027: : StateManagerImpl.LOAD_ALL;
3028: try {
3029: sm.beforeRead(-1);
3030: sm.load(_fc, mode, null, null, false);
3031: } catch (OpenJPAException ke) {
3032: exceps = add(exceps, ke);
3033: }
3034: }
3035: } catch (OpenJPAException ke) {
3036: throw ke;
3037: } catch (RuntimeException re) {
3038: throw new GeneralException(re);
3039: } finally {
3040: endOperation();
3041: }
3042: throwNestedExceptions(exceps, false);
3043: }
3044:
3045: public void retrieve(Object obj, boolean dfgOnly, OpCallbacks call) {
3046: if (obj == null)
3047: return;
3048:
3049: beginOperation(true);
3050: try {
3051: assertOpen();
3052: assertNontransactionalRead();
3053:
3054: StateManagerImpl sm = getStateManagerImpl(obj, true);
3055: if ((processArgument(OpCallbacks.OP_RETRIEVE, obj, sm, call) & OpCallbacks.ACT_RUN) == 0)
3056: return;
3057:
3058: if (sm != null) {
3059: if (sm.isDetached())
3060: throw newDetachedException(obj, "retrieve");
3061: if (sm.isPersistent()) {
3062: int mode = (dfgOnly) ? StateManagerImpl.LOAD_FGS
3063: : StateManagerImpl.LOAD_ALL;
3064: sm.beforeRead(-1);
3065: sm.load(_fc, mode, null, null, false);
3066: }
3067: } else if (assertPersistenceCapable(obj).pcIsDetached() == Boolean.TRUE)
3068: throw newDetachedException(obj, "retrieve");
3069: } catch (OpenJPAException ke) {
3070: throw ke;
3071: } catch (RuntimeException re) {
3072: throw new GeneralException(re);
3073: } finally {
3074: endOperation();
3075: }
3076: }
3077:
3078: public void evictAll(OpCallbacks call) {
3079: beginOperation(false);
3080: try {
3081: // evict all PClean and PNonTrans objects
3082: Collection c = getManagedStates();
3083: StateManagerImpl sm;
3084: for (Iterator itr = c.iterator(); itr.hasNext();) {
3085: sm = (StateManagerImpl) itr.next();
3086: if (sm.isPersistent() && !sm.isDirty())
3087: evict(sm.getManagedInstance(), call);
3088: }
3089: } finally {
3090: endOperation();
3091: }
3092: }
3093:
3094: public void evictAll(Collection objs, OpCallbacks call) {
3095: List exceps = null;
3096: beginOperation(false);
3097: try {
3098: for (Iterator itr = objs.iterator(); itr.hasNext();) {
3099: try {
3100: evict(itr.next(), call);
3101: } catch (UserException ue) {
3102: exceps = add(exceps, ue);
3103: }
3104: }
3105: } finally {
3106: endOperation();
3107: }
3108: throwNestedExceptions(exceps, false);
3109: }
3110:
3111: public void evictAll(Extent extent, OpCallbacks call) {
3112: if (extent == null)
3113: return;
3114:
3115: beginOperation(false);
3116: try {
3117: // evict all PClean and PNonTrans objects in extent
3118: Collection c = getManagedStates();
3119: StateManagerImpl sm;
3120: Class cls;
3121: for (Iterator itr = c.iterator(); itr.hasNext();) {
3122: sm = (StateManagerImpl) itr.next();
3123: if (sm.isPersistent() && !sm.isDirty()) {
3124: cls = sm.getMetaData().getDescribedType();
3125: if (cls == extent.getElementType()
3126: || (extent.hasSubclasses() && extent
3127: .getElementType().isAssignableFrom(
3128: cls)))
3129: evict(sm.getManagedInstance(), call);
3130: }
3131: }
3132: } finally {
3133: endOperation();
3134: }
3135: }
3136:
3137: public void evict(Object obj, OpCallbacks call) {
3138: if (obj == null)
3139: return;
3140:
3141: beginOperation(false);
3142: try {
3143: StateManagerImpl sm = getStateManagerImpl(obj, true);
3144: if ((processArgument(OpCallbacks.OP_EVICT, obj, sm, call) & OpCallbacks.ACT_RUN) == 0)
3145: return;
3146: if (sm == null)
3147: return;
3148:
3149: sm.evict();
3150: if (_evictDataCache && sm.getObjectId() != null) {
3151: DataCache cache = sm.getMetaData().getDataCache();
3152: if (cache != null)
3153: cache.remove(sm.getObjectId());
3154: }
3155: } catch (OpenJPAException ke) {
3156: throw ke;
3157: } catch (RuntimeException re) {
3158: throw new GeneralException(re);
3159: } finally {
3160: endOperation();
3161: }
3162: }
3163:
3164: public Object detach(Object obj, OpCallbacks call) {
3165: if (obj == null)
3166: return null;
3167: if (call == null)
3168: call = _call;
3169:
3170: beginOperation(true);
3171: try {
3172: return new DetachManager(this , false, call).detach(obj);
3173: } catch (OpenJPAException ke) {
3174: throw ke;
3175: } catch (RuntimeException re) {
3176: throw new GeneralException(re);
3177: } finally {
3178: endOperation();
3179: }
3180: }
3181:
3182: public Object[] detachAll(Collection objs, OpCallbacks call) {
3183: if (objs == null)
3184: return null;
3185: if (objs.isEmpty())
3186: return EMPTY_OBJECTS;
3187: if (call == null)
3188: call = _call;
3189:
3190: beginOperation(true);
3191: try {
3192: return new DetachManager(this , false, call).detachAll(objs);
3193: } catch (OpenJPAException ke) {
3194: throw ke;
3195: } catch (RuntimeException re) {
3196: throw new GeneralException(re);
3197: } finally {
3198: endOperation();
3199: }
3200: }
3201:
3202: public void detachAll(OpCallbacks call) {
3203: detachAll(call, true);
3204: }
3205:
3206: public void detachAll(OpCallbacks call, boolean flush) {
3207: beginOperation(true);
3208: try {
3209: // If a flush is desired (based on input parm), then check if the
3210: // "dirty" flag is set before calling flush().
3211: if ((flush) && ((_flags & FLAG_FLUSH_REQUIRED) != 0))
3212: flush();
3213: detachAllInternal(call);
3214: } catch (OpenJPAException ke) {
3215: throw ke;
3216: } catch (RuntimeException re) {
3217: throw new GeneralException(re);
3218: } finally {
3219: endOperation();
3220: }
3221: }
3222:
3223: private void detachAllInternal(OpCallbacks call) {
3224: Collection states = getManagedStates();
3225: StateManagerImpl sm;
3226: for (Iterator itr = states.iterator(); itr.hasNext();) {
3227: sm = (StateManagerImpl) itr.next();
3228: if (!sm.isPersistent())
3229: itr.remove();
3230: else if (!sm.getMetaData().isDetachable()) {
3231: sm.release(true);
3232: itr.remove();
3233: }
3234: }
3235: if (states.isEmpty())
3236: return;
3237:
3238: if (call == null)
3239: call = _call;
3240: new DetachManager(this , true, call)
3241: .detachAll(new ManagedObjectCollection(states));
3242: }
3243:
3244: public Object attach(Object obj, boolean copyNew, OpCallbacks call) {
3245: if (obj == null)
3246: return null;
3247:
3248: beginOperation(true);
3249: try {
3250: // make sure not to try to set rollback only if this fails
3251: assertWriteOperation();
3252: try {
3253: return new AttachManager(this , copyNew, call)
3254: .attach(obj);
3255: } catch (OptimisticException oe) {
3256: setRollbackOnly(oe);
3257: throw oe.setFatal(true);
3258: } catch (OpenJPAException ke) {
3259: throw ke;
3260: } catch (RuntimeException re) {
3261: throw new GeneralException(re);
3262: }
3263: } finally {
3264: endOperation();
3265: }
3266: }
3267:
3268: public Object[] attachAll(Collection objs, boolean copyNew,
3269: OpCallbacks call) {
3270: if (objs == null)
3271: return null;
3272: if (objs.isEmpty())
3273: return EMPTY_OBJECTS;
3274:
3275: beginOperation(true);
3276: try {
3277: // make sure not to try to set rollback only if this fails
3278: assertWriteOperation();
3279: try {
3280: return new AttachManager(this , copyNew, call)
3281: .attachAll(objs);
3282: } catch (OptimisticException oe) {
3283: setRollbackOnly(oe);
3284: throw oe.setFatal(true);
3285: } catch (OpenJPAException ke) {
3286: throw ke;
3287: } catch (RuntimeException re) {
3288: throw new GeneralException(re);
3289: }
3290: } finally {
3291: endOperation();
3292: }
3293: }
3294:
3295: public void nontransactionalAll(Collection objs, OpCallbacks call) {
3296: beginOperation(true);
3297: try {
3298: List exceps = null;
3299: for (Iterator itr = objs.iterator(); itr.hasNext();) {
3300: try {
3301: nontransactional(itr.next(), call);
3302: } catch (UserException ue) {
3303: exceps = add(exceps, ue);
3304: }
3305: }
3306: throwNestedExceptions(exceps, false);
3307: } finally {
3308: endOperation();
3309: }
3310: }
3311:
3312: public void nontransactional(Object obj, OpCallbacks call) {
3313: if (obj == null)
3314: return;
3315:
3316: beginOperation(true);
3317: try {
3318: StateManagerImpl sm = getStateManagerImpl(obj, true);
3319: if ((processArgument(OpCallbacks.OP_NONTRANSACTIONAL, obj,
3320: sm, call) & OpCallbacks.ACT_RUN) == 0)
3321: return;
3322: if (sm != null)
3323: sm.nontransactional();
3324: } catch (OpenJPAException ke) {
3325: throw ke;
3326: } catch (RuntimeException re) {
3327: throw new GeneralException(re);
3328: } finally {
3329: endOperation();
3330: }
3331: }
3332:
3333: /**
3334: * Make the given instances transactional.
3335: */
3336: public void transactionalAll(Collection objs,
3337: boolean updateVersion, OpCallbacks call) {
3338: if (objs.isEmpty())
3339: return;
3340: if (objs.size() == 1) {
3341: transactional(objs.iterator().next(), updateVersion, call);
3342: return;
3343: }
3344:
3345: beginOperation(true);
3346: try {
3347: // collect all hollow instances for load, and make unmananged
3348: // instances transient-transactional
3349: Collection load = null;
3350: Object obj;
3351: StateManagerImpl sm;
3352: ClassMetaData meta;
3353: Collection sms = new ArrayList(objs.size());
3354: List exceps = null;
3355: for (Iterator itr = objs.iterator(); itr.hasNext();) {
3356: obj = itr.next();
3357: if (obj == null)
3358: continue;
3359:
3360: try {
3361: sm = getStateManagerImpl(obj, true);
3362: if ((processArgument(OpCallbacks.OP_TRANSACTIONAL,
3363: obj, sm, call) & OpCallbacks.ACT_RUN) == 0)
3364: continue;
3365:
3366: if (sm == null) {
3367: // manage transient instance
3368: meta = _conf.getMetaDataRepositoryInstance()
3369: .getMetaData(obj.getClass(), _loader,
3370: true);
3371:
3372: sm = new StateManagerImpl(StateManagerId
3373: .newInstance(this ), meta, this );
3374: sm.initialize(assertPersistenceCapable(obj),
3375: PCState.TCLEAN);
3376: } else if (sm.isPersistent()) {
3377: assertActiveTransaction();
3378: sms.add(sm);
3379: if (sm.getPCState() == PCState.HOLLOW) {
3380: if (load == null)
3381: load = new ArrayList();
3382: load.add(sm);
3383: }
3384:
3385: sm.setCheckVersion(true);
3386: if (updateVersion)
3387: sm.setUpdateVersion(true);
3388: _flags |= FLAG_FLUSH_REQUIRED; // version check/up
3389: }
3390: } catch (UserException ue) {
3391: exceps = add(exceps, ue);
3392: }
3393: }
3394:
3395: // load all hollow instances
3396: Collection failed = null;
3397: if (load != null) {
3398: failed = _store.loadAll(load, null,
3399: _store.FORCE_LOAD_NONE, _fc, null);
3400: if (failed != null && !failed.isEmpty())
3401: exceps = add(exceps,
3402: newObjectNotFoundException(failed));
3403: }
3404:
3405: transactionalStatesAll(sms, failed, exceps);
3406: } catch (OpenJPAException ke) {
3407: throw ke;
3408: } catch (RuntimeException re) {
3409: throw new GeneralException(re);
3410: } finally {
3411: endOperation();
3412: }
3413: }
3414:
3415: /**
3416: * Make the given instances transactional.
3417: */
3418: public void transactional(Object obj, boolean updateVersion,
3419: OpCallbacks call) {
3420: if (obj == null)
3421: return;
3422:
3423: beginOperation(true);
3424: try {
3425: StateManagerImpl sm = getStateManagerImpl(obj, true);
3426: if ((processArgument(OpCallbacks.OP_TRANSACTIONAL, obj, sm,
3427: call) & OpCallbacks.ACT_RUN) == 0)
3428: return;
3429:
3430: if (sm != null && sm.isPersistent()) {
3431: assertActiveTransaction();
3432: sm.transactional();
3433: sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null,
3434: false);
3435: sm.setCheckVersion(true);
3436: if (updateVersion)
3437: sm.setUpdateVersion(true);
3438: _flags |= FLAG_FLUSH_REQUIRED; // version check/up
3439: } else if (sm == null) {
3440: // manage transient instance
3441: ClassMetaData meta = _conf
3442: .getMetaDataRepositoryInstance().getMetaData(
3443: obj.getClass(), _loader, true);
3444: Object id = StateManagerId.newInstance(this );
3445: sm = new StateManagerImpl(id, meta, this );
3446: sm.initialize(assertPersistenceCapable(obj),
3447: PCState.TCLEAN);
3448: }
3449: } catch (OpenJPAException ke) {
3450: throw ke;
3451: } catch (RuntimeException re) {
3452: throw new GeneralException(re);
3453: } finally {
3454: endOperation();
3455: }
3456: }
3457:
3458: /**
3459: * Transition the given state managers to transactional.
3460: */
3461: private void transactionalStatesAll(Collection sms,
3462: Collection failed, List exceps) {
3463: // make instances transactional and make sure they are loaded
3464: StateManagerImpl sm;
3465: for (Iterator itr = sms.iterator(); itr.hasNext();) {
3466: sm = (StateManagerImpl) itr.next();
3467: if (failed != null && failed.contains(sm.getId()))
3468: continue;
3469:
3470: try {
3471: sm.transactional();
3472: sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null,
3473: false);
3474: } catch (OpenJPAException ke) {
3475: exceps = add(exceps, ke);
3476: }
3477: }
3478: throwNestedExceptions(exceps, false);
3479: }
3480:
3481: /////////////////
3482: // Extent, Query
3483: /////////////////
3484:
3485: public Extent newExtent(Class type, boolean subclasses) {
3486: return newExtent(type, subclasses, null);
3487: }
3488:
3489: private Extent newExtent(Class type, boolean subclasses,
3490: FetchConfiguration fetch) {
3491: beginOperation(true);
3492: try {
3493: ExtentImpl extent = new ExtentImpl(this , type, subclasses,
3494: fetch);
3495: if (_extents == null)
3496: _extents = new ReferenceHashSet(ReferenceHashSet.WEAK);
3497: _extents.add(extent);
3498:
3499: return extent;
3500: } catch (OpenJPAException ke) {
3501: throw ke;
3502: } catch (RuntimeException re) {
3503: throw new GeneralException(re);
3504: } finally {
3505: endOperation();
3506: }
3507: }
3508:
3509: public Iterator extentIterator(Class type, boolean subclasses,
3510: FetchConfiguration fetch, boolean ignoreChanges) {
3511: Extent extent = newExtent(type, subclasses, fetch);
3512: extent.setIgnoreChanges(ignoreChanges);
3513: return extent.iterator();
3514: }
3515:
3516: public Query newQuery(String lang, Class cls, Object query) {
3517: Query q = newQuery(lang, query);
3518: q.setCandidateType(cls, true);
3519: return q;
3520: }
3521:
3522: public Query newQuery(String lang, Object query) {
3523: // common mistakes
3524: if (query instanceof Extent || query instanceof Class)
3525: throw new UserException(_loc.get("bad-new-query"));
3526:
3527: beginOperation(false);
3528: try {
3529: StoreQuery sq = _store.newQuery(lang);
3530: if (sq == null) {
3531: ExpressionParser ep = QueryLanguages
3532: .parserForLanguage(lang);
3533: if (ep != null)
3534: sq = new ExpressionStoreQuery(ep);
3535: else if (QueryLanguages.LANG_METHODQL.equals(lang))
3536: sq = new MethodStoreQuery();
3537: else
3538: throw new UnsupportedException(lang);
3539: }
3540:
3541: Query q = newQueryImpl(lang, sq);
3542: q.setIgnoreChanges(_ignoreChanges);
3543: if (query != null)
3544: q.setQuery(query);
3545:
3546: // track queries
3547: if (_queries == null)
3548: _queries = new ReferenceHashSet(ReferenceHashSet.WEAK);
3549: _queries.add(q);
3550: return q;
3551: } catch (OpenJPAException ke) {
3552: throw ke;
3553: } catch (RuntimeException re) {
3554: throw new GeneralException(re);
3555: } finally {
3556: endOperation();
3557: }
3558: }
3559:
3560: /**
3561: * Create a new query.
3562: */
3563: protected QueryImpl newQueryImpl(String lang, StoreQuery sq) {
3564: return new QueryImpl(this , lang, sq);
3565: }
3566:
3567: public Seq getIdentitySequence(ClassMetaData meta) {
3568: if (meta == null)
3569: return null;
3570: return getSequence(meta, null);
3571: }
3572:
3573: public Seq getValueSequence(FieldMetaData fmd) {
3574: if (fmd == null)
3575: return null;
3576: return getSequence(fmd.getDefiningMetaData(), fmd);
3577: }
3578:
3579: /**
3580: * Return a sequence for the given class and optional field.
3581: */
3582: private Seq getSequence(ClassMetaData meta, FieldMetaData fmd) {
3583: // get sequence strategy from metadata
3584: int strategy;
3585: if (fmd == null)
3586: strategy = meta.getIdentityStrategy();
3587: else
3588: strategy = fmd.getValueStrategy();
3589:
3590: // we can handle non-native strategies without the store manager
3591: switch (strategy) {
3592: case ValueStrategies.UUID_HEX:
3593: return UUIDHexSeq.getInstance();
3594: case ValueStrategies.UUID_STRING:
3595: return UUIDStringSeq.getInstance();
3596: case ValueStrategies.SEQUENCE:
3597: SequenceMetaData smd = (fmd == null) ? meta
3598: .getIdentitySequenceMetaData() : fmd
3599: .getValueSequenceMetaData();
3600: return smd.getInstance(_loader);
3601: default:
3602: // use store manager for native sequence
3603: if (fmd == null) {
3604: // this will return a sequence even for app id classes,
3605: // which is what we want for backwards-compatibility
3606: return _store.getDataStoreIdSequence(meta);
3607: }
3608: return _store.getValueSequence(fmd);
3609: }
3610: }
3611:
3612: ///////////
3613: // Locking
3614: ///////////
3615:
3616: public void lock(Object obj, OpCallbacks call) {
3617: if (obj == null)
3618: return;
3619:
3620: beginOperation(true); // have to sync or lock level always NONE
3621: try {
3622: lock(obj, _fc.getWriteLockLevel(), _fc.getLockTimeout(),
3623: call);
3624: } finally {
3625: endOperation();
3626: }
3627: }
3628:
3629: public void lock(Object obj, int level, int timeout,
3630: OpCallbacks call) {
3631: if (obj == null)
3632: return;
3633:
3634: beginOperation(true);
3635: try {
3636: assertActiveTransaction();
3637:
3638: StateManagerImpl sm = getStateManagerImpl(obj, true);
3639: if ((processArgument(OpCallbacks.OP_LOCK, obj, sm, call) & OpCallbacks.ACT_RUN) == 0)
3640: return;
3641: if (sm == null || !sm.isPersistent())
3642: return;
3643:
3644: _lm.lock(sm, level, timeout, null);
3645: sm.readLocked(level, level); // use same level for future write
3646: } catch (OpenJPAException ke) {
3647: throw ke;
3648: } catch (RuntimeException re) {
3649: throw new GeneralException(re);
3650: } finally {
3651: endOperation();
3652: }
3653: }
3654:
3655: public void lockAll(Collection objs, OpCallbacks call) {
3656: if (objs.isEmpty())
3657: return;
3658:
3659: beginOperation(true); // have to sync or lock level always NONE
3660: try {
3661: lockAll(objs, _fc.getWriteLockLevel(),
3662: _fc.getLockTimeout(), call);
3663: } finally {
3664: endOperation();
3665: }
3666: }
3667:
3668: public void lockAll(Collection objs, int level, int timeout,
3669: OpCallbacks call) {
3670: if (objs.isEmpty())
3671: return;
3672: if (objs.size() == 1) {
3673: lock(objs.iterator().next(), level, timeout, call);
3674: return;
3675: }
3676:
3677: beginOperation(true);
3678: try {
3679: assertActiveTransaction();
3680:
3681: Collection sms = new ArrayList(objs.size());
3682: Object obj;
3683: StateManagerImpl sm;
3684: for (Iterator itr = objs.iterator(); itr.hasNext();) {
3685: obj = itr.next();
3686: if (obj == null)
3687: continue;
3688:
3689: sm = getStateManagerImpl(obj, true);
3690: if ((processArgument(OpCallbacks.OP_LOCK, obj, sm, call) & OpCallbacks.ACT_RUN) == 0)
3691: continue;
3692: if (sm != null && sm.isPersistent())
3693: sms.add(sm);
3694: }
3695:
3696: _lm.lockAll(sms, level, timeout, null);
3697: for (Iterator itr = sms.iterator(); itr.hasNext();)
3698: ((StateManagerImpl) itr.next())
3699: .readLocked(level, level);
3700: } catch (OpenJPAException ke) {
3701: throw ke;
3702: } catch (RuntimeException re) {
3703: throw new GeneralException(re);
3704: } finally {
3705: endOperation();
3706: }
3707: }
3708:
3709: //////////////
3710: // Connection
3711: //////////////
3712:
3713: public boolean cancelAll() {
3714: // this method does not lock, since we want to allow a different
3715: // thread to be able to cancel on a locked-up persistence manager
3716:
3717: assertOpen();
3718: try {
3719: // if we're flushing, have to set rollback only -- do this before we
3720: // attempt to cancel, because otherwise the cancel might case the
3721: // transaction to complete before we have a chance to set the
3722: // rollback only flag
3723: if ((_flags & FLAG_STORE_FLUSHING) != 0)
3724: setRollbackOnlyInternal(new UserException());
3725: return _store.cancelAll();
3726: } catch (OpenJPAException ke) {
3727: throw ke;
3728: } catch (RuntimeException re) {
3729: throw new StoreException(re);
3730: }
3731: }
3732:
3733: public Object getConnection() {
3734: assertOpen();
3735: if (!_conf.supportedOptions().contains(
3736: _conf.OPTION_DATASTORE_CONNECTION))
3737: throw new UnsupportedException(_loc
3738: .get("conn-not-supported"));
3739:
3740: return _store.getClientConnection();
3741: }
3742:
3743: public boolean hasConnection() {
3744: assertOpen();
3745: return (_flags & FLAG_RETAINED_CONN) != 0;
3746: }
3747:
3748: /**
3749: * Tell store to retain connection if we haven't already.
3750: */
3751: private void retainConnection() {
3752: if ((_flags & FLAG_RETAINED_CONN) == 0) {
3753: _store.retainConnection();
3754: _flags |= FLAG_RETAINED_CONN;
3755: }
3756: }
3757:
3758: /**
3759: * Tell store to release connection if we have retained one.
3760: */
3761: private void releaseConnection() {
3762: if ((_flags & FLAG_RETAINED_CONN) != 0) {
3763: _store.releaseConnection();
3764: _flags &= ~FLAG_RETAINED_CONN;
3765: }
3766: }
3767:
3768: /////////
3769: // Cache
3770: /////////
3771:
3772: public Collection getManagedObjects() {
3773: beginOperation(false);
3774: try {
3775: return new ManagedObjectCollection(getManagedStates());
3776: } finally {
3777: endOperation();
3778: }
3779: }
3780:
3781: public Collection getTransactionalObjects() {
3782: beginOperation(false);
3783: try {
3784: return new ManagedObjectCollection(getTransactionalStates());
3785: } finally {
3786: endOperation();
3787: }
3788: }
3789:
3790: public Collection getPendingTransactionalObjects() {
3791: beginOperation(false);
3792: try {
3793: return new ManagedObjectCollection(
3794: getPendingTransactionalStates());
3795: } finally {
3796: endOperation();
3797: }
3798: }
3799:
3800: public Collection getDirtyObjects() {
3801: beginOperation(false);
3802: try {
3803: return new ManagedObjectCollection(getDirtyStates());
3804: } finally {
3805: endOperation();
3806: }
3807: }
3808:
3809: public boolean getOrderDirtyObjects() {
3810: return _orderDirty;
3811: }
3812:
3813: public void setOrderDirtyObjects(boolean order) {
3814: _orderDirty = order;
3815: }
3816:
3817: /**
3818: * Return a copy of all managed state managers.
3819: */
3820: protected Collection getManagedStates() {
3821: return _cache.copy();
3822: }
3823:
3824: /**
3825: * Return a copy of all transactional state managers.
3826: */
3827: protected Collection getTransactionalStates() {
3828: if (!hasTransactionalObjects())
3829: return Collections.EMPTY_LIST;
3830: return _transCache.copy();
3831: }
3832:
3833: /**
3834: * Whether or not there are any transactional objects in the current
3835: * persistence context. If there are any instances with untracked state,
3836: * this method will cause those instances to be scanned.
3837: */
3838: private boolean hasTransactionalObjects() {
3839: _cache.dirtyCheck();
3840: return _transCache != null;
3841: }
3842:
3843: /**
3844: * Return a copy of all dirty state managers.
3845: */
3846: protected Collection getDirtyStates() {
3847: if (!hasTransactionalObjects())
3848: return Collections.EMPTY_LIST;
3849:
3850: return _transCache.copyDirty();
3851: }
3852:
3853: /**
3854: * Return a copy of all state managers which will become
3855: * transactional upon the next transaction.
3856: */
3857: protected Collection getPendingTransactionalStates() {
3858: if (_pending == null)
3859: return Collections.EMPTY_LIST;
3860: return new ArrayList(_pending);
3861: }
3862:
3863: /**
3864: * Set the cached StateManager for the instance that had the given oid.
3865: * This method must not be called multiple times for new instances.
3866: *
3867: * @param id the id previously used by the instance
3868: * @param sm the state manager for the instance; if the state
3869: * manager is transient, we'll stop managing the instance;
3870: * if it has updated its oid, we'll re-cache under the new oid
3871: * @param status one of our STATUS constants describing why we're
3872: * setting the state manager
3873: */
3874: void setStateManager(Object id, StateManagerImpl sm, int status) {
3875: lock();
3876: try {
3877: switch (status) {
3878: case STATUS_INIT:
3879: _cache.add(sm);
3880: break;
3881: case STATUS_TRANSIENT:
3882: _cache.remove(id, sm);
3883: break;
3884: case STATUS_OID_ASSIGN:
3885: assignObjectId(_cache, id, sm);
3886: break;
3887: case STATUS_COMMIT_NEW:
3888: _cache.commitNew(id, sm);
3889: break;
3890: default:
3891: throw new InternalException();
3892: }
3893: } finally {
3894: unlock();
3895: }
3896: }
3897:
3898: /**
3899: * Notify the broker that the given state manager should
3900: * be added to the set of instances involved in the current transaction.
3901: */
3902: void addToTransaction(StateManagerImpl sm) {
3903: // we only add clean instances now; dirty instances are added in
3904: // the setDirty callback
3905: if (sm.isDirty())
3906: return;
3907:
3908: lock();
3909: try {
3910: if (!hasTransactionalObjects())
3911: _transCache = new TransactionalCache(_orderDirty);
3912: _transCache.addClean(sm);
3913: } finally {
3914: unlock();
3915: }
3916: }
3917:
3918: /**
3919: * Notify the persistence manager that the given state manager should
3920: * be removed from the set of instances involved in the current transaction.
3921: */
3922: void removeFromTransaction(StateManagerImpl sm) {
3923: lock();
3924: try {
3925: if (_transCache != null)
3926: // intentional direct access; we don't want to recompute
3927: // dirtiness while removing instances from the transaction
3928: _transCache.remove(sm);
3929: if (_derefCache != null && !sm.isPersistent())
3930: _derefCache.remove(sm);
3931: } finally {
3932: unlock();
3933: }
3934: }
3935:
3936: /**
3937: * Notification that the given instance has been dirtied. This
3938: * notification is given when an object first transitions to a dirty state,
3939: * and every time the object is modified by the user thereafter.
3940: */
3941: void setDirty(StateManagerImpl sm, boolean firstDirty) {
3942: if (sm.isPersistent())
3943: _flags |= FLAG_FLUSH_REQUIRED;
3944:
3945: if (_savepoints != null && !_savepoints.isEmpty()) {
3946: if (_savepointCache == null)
3947: _savepointCache = new HashSet();
3948: _savepointCache.add(sm);
3949: }
3950:
3951: if (firstDirty && sm.isTransactional()) {
3952: lock();
3953: try {
3954: // cache dirty instance
3955: if (!hasTransactionalObjects())
3956: _transCache = new TransactionalCache(_orderDirty);
3957: _transCache.addDirty(sm);
3958:
3959: // also record that the class is dirty
3960: if (sm.isNew()) {
3961: if (_persistedClss == null)
3962: _persistedClss = new HashSet();
3963: _persistedClss.add(sm.getMetaData()
3964: .getDescribedType());
3965: } else if (sm.isDeleted()) {
3966: if (_deletedClss == null)
3967: _deletedClss = new HashSet();
3968: _deletedClss.add(sm.getMetaData()
3969: .getDescribedType());
3970: } else {
3971: if (_updatedClss == null)
3972: _updatedClss = new HashSet();
3973: _updatedClss.add(sm.getMetaData()
3974: .getDescribedType());
3975: }
3976:
3977: // if tracking changes and this instance wasn't already dirty,
3978: // add to changed set; we use this for detecting instances that
3979: // enter the transaction during pre store
3980: if ((_flags & FLAG_PRESTORING) != 0) {
3981: if (_transAdditions == null)
3982: _transAdditions = new HashSet();
3983: _transAdditions.add(sm);
3984: }
3985: } finally {
3986: unlock();
3987: }
3988: }
3989: }
3990:
3991: /**
3992: * Notify the broker that the given state manager should
3993: * be added to the set of instances that will become transactional
3994: * on the next transaction
3995: */
3996: void addToPendingTransaction(StateManagerImpl sm) {
3997: lock();
3998: try {
3999: if (_pending == null)
4000: _pending = new HashSet();
4001: _pending.add(sm);
4002: } finally {
4003: unlock();
4004: }
4005: }
4006:
4007: /**
4008: * Notify the persistence manager that the given state manager should
4009: * be removed from the set of instances involved in the next transaction.
4010: */
4011: void removeFromPendingTransaction(StateManagerImpl sm) {
4012: lock();
4013: try {
4014: if (_pending != null)
4015: _pending.remove(sm);
4016: if (_derefCache != null && !sm.isPersistent())
4017: _derefCache.remove(sm);
4018: } finally {
4019: unlock();
4020: }
4021: }
4022:
4023: /**
4024: * Add a dereferenced dependent object to the persistence manager's cache.
4025: * On flush, these objects will be deleted.
4026: */
4027: void addDereferencedDependent(StateManagerImpl sm) {
4028: lock();
4029: try {
4030: // if we're in the middle of flush and introducing more derefs
4031: // via instance callbacks, add them to the special additions set
4032: if ((_flags & FLAG_DEREFDELETING) != 0) {
4033: if (_derefAdditions == null)
4034: _derefAdditions = new HashSet();
4035: _derefAdditions.add(sm);
4036: } else {
4037: if (_derefCache == null)
4038: _derefCache = new HashSet();
4039: _derefCache.add(sm);
4040: }
4041: } finally {
4042: unlock();
4043: }
4044: }
4045:
4046: /**
4047: * Remove the given previously dereferenced dependent object from the
4048: * cache. It is now referenced.
4049: */
4050: void removeDereferencedDependent(StateManagerImpl sm) {
4051: lock();
4052: try {
4053: boolean removed = false;
4054: if (_derefAdditions != null)
4055: removed = _derefAdditions.remove(sm);
4056: if (!removed
4057: && (_derefCache == null || !_derefCache.remove(sm)))
4058: throw new InvalidStateException(_loc.get("not-derefed",
4059: Exceptions.toString(sm.getManagedInstance())))
4060: .setFailedObject(sm.getManagedInstance())
4061: .setFatal(true);
4062: } finally {
4063: unlock();
4064: }
4065: }
4066:
4067: public void dirtyType(Class cls) {
4068: if (cls == null)
4069: return;
4070:
4071: beginOperation(false);
4072: try {
4073: if (_updatedClss == null)
4074: _updatedClss = new HashSet();
4075: _updatedClss.add(cls);
4076: } finally {
4077: endOperation();
4078: }
4079: }
4080:
4081: public Collection getPersistedTypes() {
4082: if (_persistedClss == null || _persistedClss.isEmpty())
4083: return Collections.EMPTY_LIST;
4084: return Collections.unmodifiableCollection(_persistedClss);
4085: }
4086:
4087: public Collection getUpdatedTypes() {
4088: if (_updatedClss == null || _updatedClss.isEmpty())
4089: return Collections.EMPTY_LIST;
4090: return Collections.unmodifiableCollection(_updatedClss);
4091: }
4092:
4093: public Collection getDeletedTypes() {
4094: if (_deletedClss == null || _deletedClss.isEmpty())
4095: return Collections.EMPTY_LIST;
4096: return Collections.unmodifiableCollection(_deletedClss);
4097: }
4098:
4099: ///////////
4100: // Closing
4101: ///////////
4102:
4103: public boolean isClosed() {
4104: return _closed;
4105: }
4106:
4107: public boolean isCloseInvoked() {
4108: return _closed || (_flags & FLAG_CLOSE_INVOKED) != 0;
4109: }
4110:
4111: public void close() {
4112: beginOperation(false);
4113: try {
4114: // throw an exception if closing in an active local trans
4115: if (!_managed && (_flags & FLAG_ACTIVE) != 0)
4116: throw new InvalidStateException(_loc.get("active"));
4117:
4118: // only close if not active; if active managed trans wait
4119: // for completion
4120: _flags |= FLAG_CLOSE_INVOKED;
4121:
4122: if ((_flags & FLAG_ACTIVE) == 0)
4123: free();
4124: } finally {
4125: endOperation();
4126: }
4127: }
4128:
4129: /**
4130: * Free the resources used by this persistence manager.
4131: */
4132: protected void free() {
4133: RuntimeException err = null;
4134: if ((_autoDetach & DETACH_CLOSE) != 0) {
4135: try {
4136: detachAllInternal(_call);
4137: } catch (RuntimeException re) {
4138: err = re;
4139: }
4140: }
4141:
4142: _sync = null;
4143: _userObjects = null;
4144: _cache.clear();
4145: _transCache = null;
4146: _persistedClss = null;
4147: _updatedClss = null;
4148: _deletedClss = null;
4149: _derefCache = null;
4150: _pending = null;
4151: _loader = null;
4152: _transEventManager = null;
4153: _lifeEventManager = null;
4154:
4155: OpenJPASavepoint save;
4156: while (_savepoints != null && !_savepoints.isEmpty()) {
4157: save = (OpenJPASavepoint) _savepoints.remove(_savepoints
4158: .size() - 1);
4159: save.release(false);
4160: }
4161: _savepoints = null;
4162: _savepointCache = null;
4163:
4164: if (_queries != null) {
4165: for (Iterator itr = _queries.iterator(); itr.hasNext();) {
4166: try {
4167: ((Query) itr.next()).closeResources();
4168: } catch (RuntimeException re) {
4169: }
4170: }
4171: _queries = null;
4172: }
4173:
4174: if (_extents != null) {
4175: Extent e;
4176: for (Iterator itr = _extents.iterator(); itr.hasNext();) {
4177: e = (Extent) itr.next();
4178: try {
4179: e.closeAll();
4180: } catch (RuntimeException re) {
4181: }
4182: }
4183: _extents = null;
4184: }
4185:
4186: try {
4187: releaseConnection();
4188: } catch (RuntimeException re) {
4189: }
4190:
4191: _lm.close();
4192: _store.close();
4193: _flags = 0;
4194: _closed = true;
4195: if (_log.isTraceEnabled())
4196: _closedException = new IllegalStateException();
4197:
4198: if (err != null)
4199: throw err;
4200: }
4201:
4202: ///////////////////
4203: // Synchronization
4204: ///////////////////
4205:
4206: public void lock() {
4207: if (_lock != null)
4208: _lock.lock();
4209: }
4210:
4211: public void unlock() {
4212: if (_lock != null)
4213: _lock.unlock();
4214: }
4215:
4216: ////////////////////
4217: // State management
4218: ////////////////////
4219:
4220: public Object newInstance(Class cls) {
4221: assertOpen();
4222:
4223: if (!cls.isInterface()
4224: && Modifier.isAbstract(cls.getModifiers()))
4225: throw new UnsupportedOperationException(_loc.get(
4226: "new-abstract", cls).getMessage());
4227:
4228: // 1.5 doesn't initialize classes without a true Class.forName
4229: if (!PCRegistry.isRegistered(cls)) {
4230: try {
4231: Class.forName(cls.getName(), true,
4232: (ClassLoader) AccessController
4233: .doPrivileged(J2DoPrivHelper
4234: .getClassLoaderAction(cls)));
4235: } catch (Throwable t) {
4236: }
4237: }
4238:
4239: if (_conf.getMetaDataRepositoryInstance().getMetaData(cls,
4240: getClassLoader(), false) == null)
4241: throw new IllegalArgumentException(_loc.get(
4242: "no-interface-metadata", cls.getName())
4243: .getMessage());
4244:
4245: try {
4246: return PCRegistry.newInstance(cls, null, false);
4247: } catch (IllegalStateException ise) {
4248: IllegalArgumentException iae = new IllegalArgumentException(
4249: ise.getMessage());
4250: iae.setStackTrace(ise.getStackTrace());
4251: throw iae;
4252: }
4253: }
4254:
4255: public Object getObjectId(Object obj) {
4256: assertOpen();
4257: if (ImplHelper.isManageable(obj))
4258: return (ImplHelper.toPersistenceCapable(obj, _conf))
4259: .pcFetchObjectId();
4260: return null;
4261: }
4262:
4263: public int getLockLevel(Object o) {
4264: assertOpen();
4265: if (o == null)
4266: return LockLevels.LOCK_NONE;
4267:
4268: OpenJPAStateManager sm = getStateManager(o);
4269: if (sm == null)
4270: return LockLevels.LOCK_NONE;
4271: return getLockManager().getLockLevel(sm);
4272: }
4273:
4274: public Object getVersion(Object obj) {
4275: assertOpen();
4276: if (ImplHelper.isManageable(obj))
4277: return (ImplHelper.toPersistenceCapable(obj, _conf))
4278: .pcGetVersion();
4279: return null;
4280: }
4281:
4282: public boolean isDirty(Object obj) {
4283: assertOpen();
4284: if (ImplHelper.isManageable(obj)) {
4285: PersistenceCapable pc = ImplHelper.toPersistenceCapable(
4286: obj, _conf);
4287: return pc.pcIsDirty();
4288: }
4289: return false;
4290: }
4291:
4292: public boolean isTransactional(Object obj) {
4293: assertOpen();
4294: if (ImplHelper.isManageable(obj))
4295: return (ImplHelper.toPersistenceCapable(obj, _conf))
4296: .pcIsTransactional();
4297: return false;
4298: }
4299:
4300: public boolean isPersistent(Object obj) {
4301: assertOpen();
4302: if (ImplHelper.isManageable(obj))
4303: return (ImplHelper.toPersistenceCapable(obj, _conf))
4304: .pcIsPersistent();
4305: return false;
4306: }
4307:
4308: public boolean isNew(Object obj) {
4309: assertOpen();
4310: if (ImplHelper.isManageable(obj))
4311: return (ImplHelper.toPersistenceCapable(obj, _conf))
4312: .pcIsNew();
4313: return false;
4314: }
4315:
4316: public boolean isDeleted(Object obj) {
4317: assertOpen();
4318: if (ImplHelper.isManageable(obj))
4319: return (ImplHelper.toPersistenceCapable(obj, _conf))
4320: .pcIsDeleted();
4321: return false;
4322: }
4323:
4324: public boolean isDetached(Object obj) {
4325: if (!(ImplHelper.isManageable(obj)))
4326: return false;
4327:
4328: PersistenceCapable pc = ImplHelper.toPersistenceCapable(obj,
4329: _conf);
4330: Boolean detached = pc.pcIsDetached();
4331: if (detached != null)
4332: return detached.booleanValue();
4333:
4334: // last resort: instance is detached if it has a store record
4335: ClassMetaData meta = _conf.getMetaDataRepositoryInstance()
4336: .getMetaData(
4337: ImplHelper.getManagedInstance(pc).getClass(),
4338: _loader, true);
4339: Object oid = ApplicationIds.create(pc, meta);
4340: if (oid == null)
4341: return false;
4342: return find(oid, null, EXCLUDE_ALL, null, 0) != null;
4343: }
4344:
4345: public OpenJPAStateManager getStateManager(Object obj) {
4346: assertOpen();
4347: return getStateManagerImpl(obj, false);
4348: }
4349:
4350: /**
4351: * Return the state manager for the given instance, or null.
4352: *
4353: * @param assertThisContext if true, thow an exception if the given
4354: * object is managed by another broker
4355: */
4356: protected StateManagerImpl getStateManagerImpl(Object obj,
4357: boolean assertThisContext) {
4358: if (ImplHelper.isManageable(obj)) {
4359: PersistenceCapable pc = ImplHelper.toPersistenceCapable(
4360: obj, _conf);
4361: if (pc.pcGetGenericContext() == this )
4362: return (StateManagerImpl) pc.pcGetStateManager();
4363: if (assertThisContext && pc.pcGetGenericContext() != null)
4364: throw new UserException(_loc.get("not-managed",
4365: Exceptions.toString(obj))).setFailedObject(obj);
4366: }
4367: return null;
4368: }
4369:
4370: /**
4371: * Return the state manager for the given oid.
4372: *
4373: * @param allowNew if true, objects made persistent in the current
4374: * transaction will be included in the search; if
4375: * multiple new objects match the given oid, it is
4376: * undefined which will be returned
4377: */
4378: protected StateManagerImpl getStateManagerImplById(Object oid,
4379: boolean allowNew) {
4380: return _cache.getById(oid, allowNew);
4381: }
4382:
4383: /**
4384: * Return the given instance as a {@link PersistenceCapable}.
4385: * If the instance is not manageable throw the proper exception.
4386: */
4387: protected PersistenceCapable assertPersistenceCapable(Object obj) {
4388: if (obj == null)
4389: return null;
4390: if (ImplHelper.isManageable(obj))
4391: return ImplHelper.toPersistenceCapable(obj, _conf);
4392:
4393: // check for different instances of the PersistenceCapable interface
4394: // and throw a better error that mentions the class loaders
4395: Class[] intfs = obj.getClass().getInterfaces();
4396: for (int i = 0; intfs != null && i < intfs.length; i++) {
4397: if (intfs[i].getName().equals(
4398: PersistenceCapable.class.getName())) {
4399: throw new UserException(
4400: _loc
4401: .get(
4402: "pc-loader-different",
4403: Exceptions.toString(obj),
4404: (ClassLoader) AccessController
4405: .doPrivileged(J2DoPrivHelper
4406: .getClassLoaderAction(PersistenceCapable.class)),
4407: (ClassLoader) AccessController
4408: .doPrivileged(J2DoPrivHelper
4409: .getClassLoaderAction(intfs[i]))))
4410: .setFailedObject(obj);
4411: }
4412: }
4413:
4414: // not enhanced
4415: throw new UserException(_loc.get("pc-cast", Exceptions
4416: .toString(obj))).setFailedObject(obj);
4417: }
4418:
4419: /////////
4420: // Utils
4421: /////////
4422: /**
4423: * Throw an exception if the context is closed. The exact message and
4424: * content of the exception varies whether TRACE is enabled or not.
4425: */
4426: public void assertOpen() {
4427: if (_closed) {
4428: if (_closedException == null) // TRACE not enabled
4429: throw new InvalidStateException(_loc
4430: .get("closed-notrace")).setFatal(true);
4431: else {
4432: OpenJPAException e = new InvalidStateException(_loc
4433: .get("closed"), _closedException)
4434: .setFatal(true);
4435: e.setCause(_closedException);
4436: throw e;
4437: }
4438: }
4439: }
4440:
4441: public void assertActiveTransaction() {
4442: if ((_flags & FLAG_ACTIVE) == 0)
4443: throw new NoTransactionException(_loc.get("not-active"));
4444: }
4445:
4446: /**
4447: * Throw exception if a transaction-related operation is attempted and
4448: * no transaction is active.
4449: */
4450: private void assertTransactionOperation() {
4451: if ((_flags & FLAG_ACTIVE) == 0)
4452: throw new InvalidStateException(_loc.get("not-active"));
4453: }
4454:
4455: public void assertNontransactionalRead() {
4456: if ((_flags & FLAG_ACTIVE) == 0 && !_nontransRead)
4457: throw new InvalidStateException(_loc.get("non-trans-read"));
4458: }
4459:
4460: public void assertWriteOperation() {
4461: if ((_flags & FLAG_ACTIVE) == 0
4462: && (!_nontransWrite || (_autoDetach & DETACH_NONTXREAD) != 0))
4463: throw new NoTransactionException(_loc
4464: .get("write-operation"));
4465: }
4466:
4467: /**
4468: * Return an object not found exception containing nested exceptions
4469: * for all of the given failed objects.
4470: */
4471: private static ObjectNotFoundException newObjectNotFoundException(
4472: Collection failed) {
4473: Throwable[] t = new Throwable[failed.size()];
4474: int idx = 0;
4475: for (Iterator itr = failed.iterator(); itr.hasNext(); idx++)
4476: t[idx] = new ObjectNotFoundException(itr.next());
4477: return new ObjectNotFoundException(failed, t);
4478: }
4479:
4480: ////////////////////////////////
4481: // FindCallbacks implementation
4482: ////////////////////////////////
4483:
4484: public Object processArgument(Object oid) {
4485: return oid;
4486: }
4487:
4488: public Object processReturn(Object oid, OpenJPAStateManager sm) {
4489: return (sm == null) ? null : sm.getManagedInstance();
4490: }
4491:
4492: private void writeObject(ObjectOutputStream out) throws IOException {
4493: assertOpen();
4494: lock();
4495: try {
4496: if (isActive()) {
4497: if (!getOptimistic())
4498: throw new InvalidStateException(_loc
4499: .get("cant-serialize-pessimistic-broker"));
4500: if (hasFlushed())
4501: throw new InvalidStateException(_loc
4502: .get("cant-serialize-flushed-broker"));
4503: if (hasConnection())
4504: throw new InvalidStateException(_loc
4505: .get("cant-serialize-connected-broker"));
4506: }
4507:
4508: try {
4509: _isSerializing = true;
4510: out.writeObject(_factory.getPoolKey());
4511: out.defaultWriteObject();
4512: } finally {
4513: _isSerializing = false;
4514: }
4515: } finally {
4516: unlock();
4517: }
4518: }
4519:
4520: private void readObject(ObjectInputStream in)
4521: throws ClassNotFoundException, IOException {
4522: Object factoryKey = in.readObject();
4523: AbstractBrokerFactory factory = AbstractBrokerFactory
4524: .getPooledFactoryForKey(factoryKey);
4525:
4526: // this needs to happen before defaultReadObject so that it's
4527: // available for calls to broker.getConfiguration() during
4528: // StateManager deserialization
4529: _conf = factory.getConfiguration();
4530:
4531: in.defaultReadObject();
4532: factory.initializeBroker(_managed, _connRetainMode, this , true);
4533:
4534: // re-initialize the lock if needed.
4535: setMultithreaded(_multithreaded);
4536:
4537: if (isActive() && _runtime instanceof LocalManagedRuntime)
4538: ((LocalManagedRuntime) _runtime).begin();
4539: }
4540:
4541: /**
4542: * Whether or not this broker is in the midst of being serialized.
4543: *
4544: * @since 1.1.0
4545: */
4546: boolean isSerializing() {
4547: return _isSerializing;
4548: }
4549:
4550: /**
4551: * Transactional cache that holds soft refs to clean instances.
4552: */
4553: static class TransactionalCache implements Set, Serializable {
4554:
4555: private final boolean _orderDirty;
4556: private Set _dirty = null;
4557: private Set _clean = null;
4558:
4559: public TransactionalCache(boolean orderDirty) {
4560: _orderDirty = orderDirty;
4561: }
4562:
4563: /**
4564: * Return a copy of all transactional state managers.
4565: */
4566: public Collection copy() {
4567: if (isEmpty())
4568: return Collections.EMPTY_LIST;
4569:
4570: // size may not be entirely accurate due to refs expiring, so
4571: // manually copy each object; doesn't matter this way if size too
4572: // big by some
4573: List copy = new ArrayList(size());
4574: if (_dirty != null)
4575: for (Iterator itr = _dirty.iterator(); itr.hasNext();)
4576: copy.add(itr.next());
4577: if (_clean != null)
4578: for (Iterator itr = _clean.iterator(); itr.hasNext();)
4579: copy.add(itr.next());
4580: return copy;
4581: }
4582:
4583: /**
4584: * Return a copy of all dirty state managers.
4585: */
4586: public Collection copyDirty() {
4587: if (_dirty == null || _dirty.isEmpty())
4588: return Collections.EMPTY_LIST;
4589: return new ArrayList(_dirty);
4590: }
4591:
4592: /**
4593: * Transfer the given instance from the dirty cache to the clean cache.
4594: */
4595: public void flushed(StateManagerImpl sm) {
4596: if (sm.isDirty() && _dirty != null && _dirty.remove(sm))
4597: addCleanInternal(sm);
4598: }
4599:
4600: /**
4601: * Add the given instance to the clean cache.
4602: */
4603: public void addClean(StateManagerImpl sm) {
4604: if (addCleanInternal(sm) && _dirty != null)
4605: _dirty.remove(sm);
4606: }
4607:
4608: private boolean addCleanInternal(StateManagerImpl sm) {
4609: if (_clean == null)
4610: _clean = new ReferenceHashSet(ReferenceHashSet.SOFT);
4611: return _clean.add(sm);
4612: }
4613:
4614: /**
4615: * Add the given instance to the dirty cache.
4616: */
4617: public void addDirty(StateManagerImpl sm) {
4618: if (_dirty == null) {
4619: if (_orderDirty)
4620: _dirty = MapBackedSet.decorate(new LinkedMap());
4621: else
4622: _dirty = new HashSet();
4623: }
4624: if (_dirty.add(sm))
4625: removeCleanInternal(sm);
4626: }
4627:
4628: /**
4629: * Remove the given instance from the cache.
4630: */
4631: public boolean remove(StateManagerImpl sm) {
4632: return removeCleanInternal(sm)
4633: || (_dirty != null && _dirty.remove(sm));
4634: }
4635:
4636: private boolean removeCleanInternal(StateManagerImpl sm) {
4637: return _clean != null && _clean.remove(sm);
4638: }
4639:
4640: public Iterator iterator() {
4641: IteratorChain chain = new IteratorChain();
4642: if (_dirty != null && !_dirty.isEmpty())
4643: chain.addIterator(_dirty.iterator());
4644: if (_clean != null && !_clean.isEmpty())
4645: chain.addIterator(_clean.iterator());
4646: return chain;
4647: }
4648:
4649: public boolean contains(Object obj) {
4650: return (_dirty != null && _dirty.contains(obj))
4651: || (_clean != null && _clean.contains(obj));
4652: }
4653:
4654: public boolean containsAll(Collection coll) {
4655: for (Iterator itr = coll.iterator(); itr.hasNext();)
4656: if (!contains(itr.next()))
4657: return false;
4658: return true;
4659: }
4660:
4661: public void clear() {
4662: if (_dirty != null)
4663: _dirty = null;
4664: if (_clean != null)
4665: _clean = null;
4666: }
4667:
4668: public boolean isEmpty() {
4669: return (_dirty == null || _dirty.isEmpty())
4670: && (_clean == null || _clean.isEmpty());
4671: }
4672:
4673: public int size() {
4674: int size = 0;
4675: if (_dirty != null)
4676: size += _dirty.size();
4677: if (_clean != null)
4678: size += _clean.size();
4679: return size;
4680: }
4681:
4682: public boolean add(Object obj) {
4683: throw new UnsupportedOperationException();
4684: }
4685:
4686: public boolean addAll(Collection coll) {
4687: throw new UnsupportedOperationException();
4688: }
4689:
4690: public boolean remove(Object obj) {
4691: throw new UnsupportedOperationException();
4692: }
4693:
4694: public boolean removeAll(Collection coll) {
4695: throw new UnsupportedOperationException();
4696: }
4697:
4698: public boolean retainAll(Collection c) {
4699: throw new UnsupportedOperationException();
4700: }
4701:
4702: public Object[] toArray() {
4703: throw new UnsupportedOperationException();
4704: }
4705:
4706: public Object[] toArray(Object[] arr) {
4707: throw new UnsupportedOperationException();
4708: }
4709: }
4710:
4711: /**
4712: * Unique id for state managers of new datastore instances without assigned
4713: * object ids.
4714: */
4715: private static class StateManagerId implements Serializable {
4716:
4717: public static final String STRING_PREFIX = "openjpasm:";
4718:
4719: private static long _generator = 0;
4720:
4721: private final int _bhash;
4722: private final long _id;
4723:
4724: public static StateManagerId newInstance(Broker b) {
4725: return new StateManagerId(System.identityHashCode(b),
4726: _generator++);
4727: }
4728:
4729: private StateManagerId(int bhash, long id) {
4730: _bhash = bhash;
4731: _id = id;
4732: }
4733:
4734: public StateManagerId(String str) {
4735: str = str.substring(STRING_PREFIX.length());
4736: int idx = str.indexOf(':');
4737: _bhash = Integer.parseInt(str.substring(0, idx));
4738: _id = Long.parseLong(str.substring(idx + 1));
4739: }
4740:
4741: public boolean equals(Object other) {
4742: if (other == this )
4743: return true;
4744: if (!(other instanceof StateManagerId))
4745: return false;
4746: StateManagerId sid = (StateManagerId) other;
4747: return _bhash == sid._bhash && _id == sid._id;
4748: }
4749:
4750: public int hashCode() {
4751: return (int) (_id ^ (_id >>> 32));
4752: }
4753:
4754: public String toString() {
4755: return STRING_PREFIX + _bhash + ":" + _id;
4756: }
4757: }
4758:
4759: /**
4760: * Collection type that holds state managers but whose interface deals
4761: * with the corresponding managed objects.
4762: */
4763: private static class ManagedObjectCollection extends
4764: AbstractCollection {
4765:
4766: private final Collection _states;
4767:
4768: public ManagedObjectCollection(Collection states) {
4769: _states = states;
4770: }
4771:
4772: public Collection getStateManagers() {
4773: return _states;
4774: }
4775:
4776: public int size() {
4777: return _states.size();
4778: }
4779:
4780: public Iterator iterator() {
4781: return new Iterator() {
4782: private final Iterator _itr = _states.iterator();
4783:
4784: public boolean hasNext() {
4785: return _itr.hasNext();
4786: }
4787:
4788: public Object next() {
4789: return ((OpenJPAStateManager) _itr.next())
4790: .getManagedInstance();
4791: }
4792:
4793: public void remove() {
4794: throw new UnsupportedException();
4795: }
4796: };
4797: }
4798: }
4799:
4800: /**
4801: * Assign the object id to the cache. Exception will be
4802: * thrown if the id already exists in the cache.
4803: */
4804: protected void assignObjectId(Object cache, Object id,
4805: StateManagerImpl sm) {
4806: ((ManagedCache) cache).assignObjectId(id, sm);
4807: }
4808:
4809: /**
4810: * This method makes sure we don't already have the instance cached
4811: */
4812: protected void checkForDuplicateId(Object id, Object obj) {
4813: StateManagerImpl other = getStateManagerImplById(id, false);
4814: if (other != null && !other.isDeleted() && !other.isNew())
4815: throw new ObjectExistsException(_loc.get("cache-exists",
4816: obj.getClass().getName(), id)).setFailedObject(obj);
4817: }
4818: }
|