Home » apache-openjpa-1.1.0-source » org.apache.openjpa » kernel » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
   10    * http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    * Unless required by applicable law or agreed to in writing,
   13    * software distributed under the License is distributed on an
   14    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    * KIND, either express or implied.  See the License for the
   16    * specific language governing permissions and limitations
   17    * under the License.    
   18    */
   19   package org.apache.openjpa.kernel;
   20   
   21   import java.io.IOException;
   22   import java.io.ObjectInputStream;
   23   import java.io.ObjectOutputStream;
   24   import java.io.Serializable;
   25   import java.lang.reflect.Modifier;
   26   import java.security.AccessController;
   27   import java.util.AbstractCollection;
   28   import java.util.ArrayList;
   29   import java.util.BitSet;
   30   import java.util.Collection;
   31   import java.util.Collections;
   32   import java.util.HashMap;
   33   import java.util.HashSet;
   34   import java.util.Iterator;
   35   import java.util.LinkedList;
   36   import java.util.List;
   37   import java.util.Map;
   38   import java.util.Set;
   39   import javax.transaction.Status;
   40   import javax.transaction.Synchronization;
   41   
   42   import org.apache.commons.collections.iterators.IteratorChain;
   43   import org.apache.commons.collections.map.IdentityMap;
   44   import org.apache.commons.collections.map.LinkedMap;
   45   import org.apache.commons.collections.set.MapBackedSet;
   46   import org.apache.openjpa.conf.Compatibility;
   47   import org.apache.openjpa.conf.OpenJPAConfiguration;
   48   import org.apache.openjpa.datacache.DataCache;
   49   import org.apache.openjpa.ee.ManagedRuntime;
   50   import org.apache.openjpa.enhance.PCRegistry;
   51   import org.apache.openjpa.enhance.PersistenceCapable;
   52   import org.apache.openjpa.event.LifecycleEvent;
   53   import org.apache.openjpa.event.LifecycleEventManager;
   54   import org.apache.openjpa.event.RemoteCommitEventManager;
   55   import org.apache.openjpa.event.TransactionEvent;
   56   import org.apache.openjpa.event.TransactionEventManager;
   57   import org.apache.openjpa.kernel.exps.ExpressionParser;
   58   import org.apache.openjpa.lib.log.Log;
   59   import org.apache.openjpa.lib.util.J2DoPrivHelper;
   60   import org.apache.openjpa.lib.util.Localizer;
   61   import org.apache.openjpa.lib.util.ReferenceHashMap;
   62   import org.apache.openjpa.lib.util.ReferenceHashSet;
   63   import org.apache.openjpa.lib.util.ReferenceMap;
   64   import java.util.concurrent.locks.ReentrantLock;
   65   import org.apache.openjpa.meta.ClassMetaData;
   66   import org.apache.openjpa.meta.FieldMetaData;
   67   import org.apache.openjpa.meta.MetaDataRepository;
   68   import org.apache.openjpa.meta.SequenceMetaData;
   69   import org.apache.openjpa.meta.ValueMetaData;
   70   import org.apache.openjpa.meta.ValueStrategies;
   71   import org.apache.openjpa.util.ApplicationIds;
   72   import org.apache.openjpa.util.CallbackException;
   73   import org.apache.openjpa.util.Exceptions;
   74   import org.apache.openjpa.util.GeneralException;
   75   import org.apache.openjpa.util.ImplHelper;
   76   import org.apache.openjpa.util.InternalException;
   77   import org.apache.openjpa.util.InvalidStateException;
   78   import org.apache.openjpa.util.NoTransactionException;
   79   import org.apache.openjpa.util.ObjectExistsException;
   80   import org.apache.openjpa.util.ObjectId;
   81   import org.apache.openjpa.util.ObjectNotFoundException;
   82   import org.apache.openjpa.util.OpenJPAException;
   83   import org.apache.openjpa.util.OptimisticException;
   84   import org.apache.openjpa.util.RuntimeExceptionTranslator;
   85   import org.apache.openjpa.util.StoreException;
   86   import org.apache.openjpa.util.UnsupportedException;
   87   import org.apache.openjpa.util.UserException;
   88   
   89   /**
   90    * Concrete {@link Broker}. The broker handles object-level behavior,
   91    * but leaves all interaction with the data store to a {@link StoreManager}
   92    * that must be supplied at initialization.
   93    *
   94    * @author Abe White
   95    */
   96   public class BrokerImpl
   97       implements Broker, FindCallbacks, Cloneable, Serializable {
   98   
   99       /**
  100        * Incremental flush.
  101        */
  102       protected static final int FLUSH_INC = 0;
  103   
  104       /**
  105        * Flush in preparation of commit.
  106        */
  107       protected static final int FLUSH_COMMIT = 1;
  108   
  109       /**
  110        * Flush to check consistency of cache, then immediately rollback changes.
  111        */
  112       protected static final int FLUSH_ROLLBACK = 2;
  113   
  114       /**
  115        * Run persistence-by-reachability and other flush-time operations without
  116        * accessing the database.
  117        */
  118       protected static final int FLUSH_LOGICAL = 3;
  119   
  120       static final int STATUS_INIT = 0;
  121       static final int STATUS_TRANSIENT = 1;
  122       static final int STATUS_OID_ASSIGN = 2;
  123       static final int STATUS_COMMIT_NEW = 3;
  124   
  125       private static final int FLAG_ACTIVE = 2 << 0;
  126       private static final int FLAG_STORE_ACTIVE = 2 << 1;
  127       private static final int FLAG_CLOSE_INVOKED = 2 << 2;
  128       private static final int FLAG_PRESTORING = 2 << 3;
  129       private static final int FLAG_DEREFDELETING = 2 << 4;
  130       private static final int FLAG_FLUSHING = 2 << 5;
  131       private static final int FLAG_STORE_FLUSHING = 2 << 6;
  132       private static final int FLAG_FLUSHED = 2 << 7;
  133       private static final int FLAG_FLUSH_REQUIRED = 2 << 8;
  134       private static final int FLAG_REMOTE_LISTENER = 2 << 9;
  135       private static final int FLAG_RETAINED_CONN = 2 << 10;
  136       private static final int FLAG_TRANS_ENDING = 2 << 11;
  137   
  138       private static final Object[] EMPTY_OBJECTS = new Object[0];
  139   
  140       private static final Localizer _loc =
  141           Localizer.forPackage(BrokerImpl.class);
  142   
  143       //	the store manager in use; this may be a decorator such as a
  144       //	data cache store manager around the native store manager
  145       private transient DelegatingStoreManager _store = null;
  146   
  147       private FetchConfiguration _fc = null;
  148       private String _user = null;
  149       private String _pass = null;
  150   
  151       // these must be rebuilt by the facade layer during its deserialization
  152       private transient Log _log = null;
  153       private transient Compatibility _compat = null;
  154       private transient ManagedRuntime _runtime = null;
  155       private transient LockManager _lm = null;
  156       private transient InverseManager _im = null;
  157       private transient ReentrantLock _lock = null;
  158       private transient OpCallbacks _call = null;
  159       private transient RuntimeExceptionTranslator _extrans = null;
  160   
  161       // ref to producing factory and configuration
  162       private transient AbstractBrokerFactory _factory = null;
  163       private transient OpenJPAConfiguration _conf = null;
  164   
  165       // cache class loader associated with the broker
  166       private transient ClassLoader _loader = null;
  167   
  168       // user state
  169       private Synchronization _sync = null;
  170       private Map _userObjects = null;
  171   
  172       // managed object caches
  173       private ManagedCache _cache = null;
  174       private TransactionalCache _transCache = null;
  175       private Set _transAdditions = null;
  176       private Set _derefCache = null;
  177       private Set _derefAdditions = null;
  178   
  179       // these are used for method-internal state only
  180       private transient Map _loading = null;
  181       private transient Set _operating = null;
  182   
  183       private Set _persistedClss = null;
  184       private Set _updatedClss = null;
  185       private Set _deletedClss = null;
  186       private Set _pending = null;
  187       private int findAllDepth = 0;
  188   
  189       // track instances that become transactional after the first savepoint
  190       // (the first uses the transactional cache)
  191       private Set _savepointCache = null;
  192       private LinkedMap _savepoints = null;
  193       private transient SavepointManager _spm = null;
  194   
  195       // track open queries and extents so we can free their resources on close
  196       private transient ReferenceHashSet _queries = null;
  197       private transient ReferenceHashSet _extents = null;
  198   
  199       // track operation stack depth. Transient because operations cannot
  200       // span serialization.
  201       private transient int _operationCount = 0;
  202   
  203       // options
  204       private boolean _nontransRead = false;
  205       private boolean _nontransWrite = false;
  206       private boolean _retainState = false;
  207       private int _autoClear = CLEAR_DATASTORE;
  208       private int _restoreState = RESTORE_IMMUTABLE;
  209       private boolean _optimistic = false;
  210       private boolean _ignoreChanges = false;
  211       private boolean _multithreaded = false;
  212       private boolean _managed = false;
  213       private boolean _syncManaged = false;
  214       private int _connRetainMode = CONN_RETAIN_DEMAND;
  215       private boolean _evictDataCache = false;
  216       private boolean _populateDataCache = true;
  217       private boolean _largeTransaction = false;
  218       private int _autoDetach = 0;
  219       private int _detachState = DETACH_LOADED;
  220       private boolean _detachedNew = true;
  221       private boolean _orderDirty = false;
  222   
  223       // status
  224       private int _flags = 0;
  225   
  226       // this is not in status because it should not be serialized
  227       private transient boolean _isSerializing = false;
  228   
  229       // transient because closed brokers can't be serialized
  230       private transient boolean _closed = false;
  231       private transient RuntimeException _closedException = null;
  232   
  233       // event managers
  234       private TransactionEventManager _transEventManager = null;
  235       private int _transCallbackMode = 0;
  236       private LifecycleEventManager _lifeEventManager = null;
  237       private int _lifeCallbackMode = 0;
  238   
  239       private transient boolean _initializeWasInvoked = false;
  240       private LinkedList _fcs;
  241   
  242       /**
  243        * Set the persistence manager's authentication. This is the first
  244        * method called after construction.
  245        *
  246        * @param user the username this broker represents; used when pooling
  247        * brokers to make sure that a request to the factory for
  248        * a connection with an explicit user is delegated to a suitable broker
  249        * @param pass the password for the above user
  250        */
  251       public void setAuthentication(String user, String pass) {
  252           _user = user;
  253           _pass = pass;
  254       }
  255   
  256       /**
  257        * Initialize the persistence manager. This method is called
  258        * automatically by the factory before use.
  259        *
  260        * @param factory the factory used to create this broker
  261        * @param sm a concrete StoreManager implementation to
  262        * handle interaction with the data store
  263        * @param managed the transaction mode
  264        * @param connMode the connection retain mode
  265        * @param fromDeserialization whether this call happened because of a
  266        * deserialization or creation of a new BrokerImpl.
  267        */
  268       public void initialize(AbstractBrokerFactory factory,
  269           DelegatingStoreManager sm, boolean managed, int connMode,
  270           boolean fromDeserialization) {
  271           _initializeWasInvoked = true;
  272           _loader = (ClassLoader) AccessController.doPrivileged(
  273               J2DoPrivHelper.getContextClassLoaderAction());
  274           if (!fromDeserialization)
  275               _conf = factory.getConfiguration();
  276           _compat = _conf.getCompatibilityInstance();
  277           _factory = factory;
  278           _log = _conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);
  279           if (!fromDeserialization)
  280               _cache = new ManagedCache(this);
  281           initializeOperatingSet();
  282           _connRetainMode = connMode;
  283           _managed = managed;
  284           if (managed)
  285               _runtime = _conf.getManagedRuntimeInstance();
  286           else
  287               _runtime = new LocalManagedRuntime(this);
  288   
  289           if (!fromDeserialization) {
  290               _lifeEventManager = new LifecycleEventManager();
  291               _transEventManager = new TransactionEventManager();
  292               int cmode = _conf.getMetaDataRepositoryInstance().
  293                   getMetaDataFactory().getDefaults().getCallbackMode();
  294               setLifecycleListenerCallbackMode(cmode);
  295               setTransactionListenerCallbackMode(cmode);
  296   
  297               // setup default options
  298               _factory.configureBroker(this);
  299           }
  300   
  301           // make sure to do this after configuring broker so that store manager
  302           // can look to broker configuration; we set both store and lock managers
  303           // before initializing them because they may each try to access the
  304           // other in thier initialization
  305           _store = sm;
  306           _lm = _conf.newLockManagerInstance();
  307           _im = _conf.newInverseManagerInstance();
  308           _spm = _conf.getSavepointManagerInstance();
  309           _store.setContext(this);
  310           _lm.setContext(this);
  311   
  312           if (_connRetainMode == CONN_RETAIN_ALWAYS)
  313               retainConnection();
  314           if (!fromDeserialization) {
  315               _fc = _store.newFetchConfiguration();
  316               _fc.setContext(this);
  317           }
  318   
  319           // synch with the global transaction in progress, if any
  320           if (_factory.syncWithManagedTransaction(this, false))
  321               beginInternal();
  322       }
  323   
  324       private void initializeOperatingSet() {
  325           _operating = MapBackedSet.decorate(new IdentityMap());
  326       }
  327       
  328       /**
  329        * Gets the unmodifiable set of instances being operated.
  330        */
  331       protected Set getOperatingSet() {
  332       	return Collections.unmodifiableSet(_operating);
  333       }
  334   
  335       public Object clone()
  336           throws CloneNotSupportedException {
  337           if (_initializeWasInvoked)
  338               throw new CloneNotSupportedException();
  339           else {
  340               return super.clone();
  341           }
  342       }
  343   
  344       /**
  345        * Create a {@link Map} to be used for the primary managed object cache.
  346        * Maps oids to state managers. By default, this creates a
  347        * {@link ReferenceMap} with soft values.
  348        */
  349       protected Map newManagedObjectCache() {
  350           return new ReferenceHashMap(ReferenceMap.HARD, ReferenceMap.SOFT);
  351       }
  352   
  353       //////////////////////////////////
  354       // Implementation of StoreContext
  355       //////////////////////////////////
  356   
  357       public Broker getBroker() {
  358           return this;
  359       }
  360   
  361       //////////////
  362       // Properties
  363       //////////////
  364   
  365       public void setImplicitBehavior(OpCallbacks call,
  366           RuntimeExceptionTranslator ex) {
  367           if (_call == null)
  368               _call = call;
  369           if (_extrans == null)
  370               _extrans = ex;
  371       }
  372   
  373       RuntimeExceptionTranslator getInstanceExceptionTranslator() {
  374           return (_operationCount == 0) ? _extrans : null;
  375       }
  376   
  377       public BrokerFactory getBrokerFactory() {
  378           return _factory;
  379       }
  380   
  381       public OpenJPAConfiguration getConfiguration() {
  382           return _conf;
  383       }
  384   
  385       public FetchConfiguration getFetchConfiguration() {
  386           return _fc;
  387       }
  388   
  389       public FetchConfiguration pushFetchConfiguration() {
  390           if (_fcs == null)
  391               _fcs = new LinkedList();
  392           _fcs.add(_fc);
  393           _fc = (FetchConfiguration) _fc.clone();
  394           return _fc;
  395       }
  396   
  397       public void popFetchConfiguration() {
  398           if (_fcs == null || _fcs.isEmpty())
  399               throw new UserException(_loc.get("fetch-configuration-stack-empty"));
  400           _fc = (FetchConfiguration) _fcs.removeLast();
  401       }
  402   
  403       public int getConnectionRetainMode() {
  404           return _connRetainMode;
  405       }
  406   
  407       public boolean isManaged() {
  408           return _managed;
  409       }
  410   
  411       public ManagedRuntime getManagedRuntime() {
  412           return _runtime;
  413       }
  414   
  415       public ClassLoader getClassLoader() {
  416           return _loader;
  417       }
  418   
  419       public DelegatingStoreManager getStoreManager() {
  420           return _store;
  421       }
  422   
  423       public LockManager getLockManager() {
  424           return _lm;
  425       }
  426   
  427       public InverseManager getInverseManager() {
  428           return _im;
  429       }
  430   
  431       public String getConnectionUserName() {
  432           return _user;
  433       }
  434   
  435       public String getConnectionPassword() {
  436           return _pass;
  437       }
  438   
  439       public boolean getMultithreaded() {
  440           return _multithreaded;
  441       }
  442   
  443       public void setMultithreaded(boolean multithreaded) {
  444           assertOpen();
  445           _multithreaded = multithreaded;
  446           if (multithreaded && _lock == null)
  447               _lock = new ReentrantLock();
  448           else if (!multithreaded)
  449               _lock = null;
  450       }
  451   
  452       public boolean getIgnoreChanges() {
  453           return _ignoreChanges;
  454       }
  455   
  456       public void setIgnoreChanges(boolean val) {
  457           assertOpen();
  458           _ignoreChanges = val;
  459       }
  460   
  461       public boolean getNontransactionalRead() {
  462           return _nontransRead;
  463       }
  464   
  465       public void setNontransactionalRead(boolean val) {
  466           assertOpen();
  467           if ((_flags & FLAG_PRESTORING) != 0)
  468               throw new UserException(_loc.get("illegal-op-in-prestore"));
  469   
  470           // make sure the runtime supports it
  471           if (val && !_conf.supportedOptions().contains
  472               (_conf.OPTION_NONTRANS_READ))
  473               throw new UnsupportedException(_loc.get
  474                   ("nontrans-read-not-supported"));
  475   
  476           _nontransRead = val;
  477       }
  478   
  479       public boolean getNontransactionalWrite() {
  480           return _nontransWrite;
  481       }
  482   
  483       public void setNontransactionalWrite(boolean val) {
  484           assertOpen();
  485           if ((_flags & FLAG_PRESTORING) != 0)
  486               throw new UserException(_loc.get("illegal-op-in-prestore"));
  487   
  488           _nontransWrite = val;
  489       }
  490   
  491       public boolean getOptimistic() {
  492           return _optimistic;
  493       }
  494   
  495       public void setOptimistic(boolean val) {
  496           assertOpen();
  497           if ((_flags & FLAG_ACTIVE) != 0)
  498               throw new InvalidStateException(_loc.get("trans-active",
  499                   "Optimistic"));
  500   
  501           // make sure the runtime supports it
  502           if (val && !_conf.supportedOptions().contains(_conf.OPTION_OPTIMISTIC))
  503               throw new UnsupportedException(_loc.get
  504                   ("optimistic-not-supported"));
  505   
  506           _optimistic = val;
  507       }
  508   
  509       public int getRestoreState() {
  510           return _restoreState;
  511       }
  512   
  513       public void setRestoreState(int val) {
  514           assertOpen();
  515           if ((_flags & FLAG_ACTIVE) != 0)
  516               throw new InvalidStateException(_loc.get("trans-active",
  517                   "Restore"));
  518   
  519           _restoreState = val;
  520       }
  521   
  522       public boolean getRetainState() {
  523           return _retainState;
  524       }
  525   
  526       public void setRetainState(boolean val) {
  527           assertOpen();
  528           if ((_flags & FLAG_PRESTORING) != 0)
  529               throw new UserException(_loc.get("illegal-op-in-prestore"));
  530           _retainState = val;
  531       }
  532   
  533       public int getAutoClear() {
  534           return _autoClear;
  535       }
  536   
  537       public void setAutoClear(int val) {
  538           assertOpen();
  539           _autoClear = val;
  540       }
  541   
  542       public int getAutoDetach() {
  543           return _autoDetach;
  544       }
  545   
  546       public void setAutoDetach(int detachFlags) {
  547           assertOpen();
  548           _autoDetach = detachFlags;
  549       }
  550   
  551       public void setAutoDetach(int detachFlag, boolean on) {
  552           assertOpen();
  553           if (on)
  554               _autoDetach |= detachFlag;
  555           else
  556               _autoDetach &= ~detachFlag;
  557       }
  558   
  559       public int getDetachState() {
  560           return _detachState;
  561       }
  562   
  563       public void setDetachState(int mode) {
  564           assertOpen();
  565           _detachState = mode;
  566       }
  567   
  568       public boolean isDetachedNew() {
  569           return _detachedNew;
  570       }
  571   
  572       public void setDetachedNew(boolean isNew) {
  573           assertOpen();
  574           _detachedNew = isNew;
  575       }
  576   
  577       public boolean getSyncWithManagedTransactions() {
  578           return _syncManaged;
  579       }
  580   
  581       public void setSyncWithManagedTransactions(boolean sync) {
  582           assertOpen();
  583           _syncManaged = sync;
  584       }
  585   
  586       public boolean getEvictFromDataCache() {
  587           return _evictDataCache;
  588       }
  589   
  590       public void setEvictFromDataCache(boolean evict) {
  591           assertOpen();
  592           _evictDataCache = evict;
  593       }
  594   
  595       public boolean getPopulateDataCache() {
  596           return _populateDataCache;
  597       }
  598   
  599       public void setPopulateDataCache(boolean cache) {
  600           assertOpen();
  601           _populateDataCache = cache;
  602       }
  603   
  604       public boolean isTrackChangesByType() {
  605           return _largeTransaction;
  606       }
  607   
  608       public void setTrackChangesByType(boolean largeTransaction) {
  609           assertOpen();
  610           _largeTransaction = largeTransaction;
  611       }
  612   
  613       public Object getUserObject(Object key) {
  614           beginOperation(false);
  615           try {
  616               return (_userObjects == null) ? null : _userObjects.get(key);
  617           } finally {
  618               endOperation();
  619           }
  620       }
  621   
  622       public Object putUserObject(Object key, Object val) {
  623           beginOperation(false);
  624           try {
  625               if (val == null)
  626                   return (_userObjects == null) ? null : _userObjects.remove(key);
  627   
  628               if (_userObjects == null)
  629                   _userObjects = new HashMap();
  630               return _userObjects.put(key, val);
  631           } finally {
  632               endOperation();
  633           }
  634       }
  635   
  636       //////////
  637       // Events
  638       //////////
  639   
  640       public void addLifecycleListener(Object listener, Class[] classes) {
  641           beginOperation(false);
  642           try {
  643               _lifeEventManager.addListener(listener, classes);
  644           } finally {
  645               endOperation();
  646           }
  647       }
  648   
  649       public void removeLifecycleListener(Object listener) {
  650           beginOperation(false);
  651           try {
  652               _lifeEventManager.removeListener(listener);
  653           } finally {
  654               endOperation();
  655           }
  656       }
  657   
  658       public int getLifecycleListenerCallbackMode() {
  659           return _lifeCallbackMode;
  660       }
  661   
  662       public void setLifecycleListenerCallbackMode(int mode) {
  663           beginOperation(false);
  664           try {
  665               _lifeCallbackMode = mode;
  666               _lifeEventManager.setFailFast((mode & CALLBACK_FAIL_FAST) != 0);
  667           } finally {
  668               endOperation();
  669           }
  670       }
  671   
  672       /**
  673        * Give state managers access to the lifecycle event manager.
  674        */
  675       public LifecycleEventManager getLifecycleEventManager() {
  676           return _lifeEventManager;
  677       }
  678   
  679       /**
  680        * Fire given lifecycle event, handling any exceptions appropriately.
  681        *
  682        * @return whether events are being processed at this time
  683        */
  684       boolean fireLifecycleEvent(Object src, Object related, ClassMetaData meta,
  685           int eventType) {
  686           if (_lifeEventManager == null)
  687               return false;
  688           handleCallbackExceptions(_lifeEventManager.fireEvent(src, related, 
  689               meta, eventType), _lifeCallbackMode);
  690           return true;
  691       }
  692   
  693       /**
  694        * Take actions on callback exceptions depending on callback mode.
  695        */
  696       private void handleCallbackExceptions(Exception[] exceps, int mode) {
  697           if (exceps.length == 0 || (mode & CALLBACK_IGNORE) != 0)
  698               return;
  699   
  700           OpenJPAException ce;
  701           if (exceps.length == 1)
  702               ce = new CallbackException(exceps[0]);
  703           else 
  704               ce = new CallbackException(_loc.get("callback-err")).
  705                   setNestedThrowables(exceps);
  706           if ((mode & CALLBACK_ROLLBACK) != 0 && (_flags & FLAG_ACTIVE) != 0) {
  707               ce.setFatal(true);
  708               setRollbackOnlyInternal(ce);
  709           }
  710           if ((mode & CALLBACK_LOG) != 0 && _log.isWarnEnabled())
  711               _log.warn(ce);
  712           if ((mode & CALLBACK_RETHROW) != 0)
  713               throw ce;
  714       }
  715   
  716       public void addTransactionListener(Object tl) {
  717           beginOperation(false);
  718           try {
  719               _transEventManager.addListener(tl);
  720               if (tl instanceof RemoteCommitEventManager)
  721                   _flags |= FLAG_REMOTE_LISTENER;
  722           } finally {
  723               endOperation();
  724           }
  725       }
  726   
  727       public void removeTransactionListener(Object tl) {
  728           beginOperation(false);
  729           try {
  730               if (_transEventManager.removeListener(tl)
  731                   && (tl instanceof RemoteCommitEventManager))
  732                   _flags &= ~FLAG_REMOTE_LISTENER;
  733           } finally {
  734               endOperation();
  735           }
  736       }
  737   
  738       public int getTransactionListenerCallbackMode() {
  739           return _transCallbackMode;
  740       }
  741   
  742       public void setTransactionListenerCallbackMode(int mode) {
  743           beginOperation(false);
  744           try {
  745               _transCallbackMode = mode;
  746               _transEventManager.setFailFast((mode & CALLBACK_FAIL_FAST) != 0);
  747           } finally {
  748               endOperation();
  749           }
  750       }
  751   
  752       /**
  753        * Fire given transaction event, handling any exceptions appropriately.
  754        */
  755       private void fireTransactionEvent(TransactionEvent trans) {
  756           if (_transEventManager != null)
  757               handleCallbackExceptions(_transEventManager.fireEvent(trans),
  758                   _transCallbackMode);
  759       }
  760   
  761       ///////////
  762       // Lookups
  763       ///////////
  764   
  765       public Object find(Object oid, boolean validate, FindCallbacks call) {
  766           int flags = OID_COPY | OID_ALLOW_NEW | OID_NODELETED;
  767           if (!validate)
  768               flags |= OID_NOVALIDATE;
  769           return find(oid, _fc, null, null, flags, call);
  770       }
  771   
  772       public Object find(Object oid, FetchConfiguration fetch, BitSet exclude,
  773           Object edata, int flags) {
  774           return find(oid, fetch, exclude, edata, flags, null);
  775       }
  776   
  777       /**
  778        * Internal finder.
  779        */
  780       protected Object find(Object oid, FetchConfiguration fetch, BitSet exclude,
  781           Object edata, int flags, FindCallbacks call) {
  782           if (call == null)
  783               call = this;
  784           oid = call.processArgument(oid);
  785           if (oid == null) {
  786               if ((flags & OID_NOVALIDATE) == 0)
  787                   throw new ObjectNotFoundException(_loc.get("null-oid"));
  788               return call.processReturn(oid, null);
  789           }
  790           if (fetch == null)
  791               fetch = _fc;
  792   
  793           beginOperation(true);
  794           try {
  795               assertNontransactionalRead();
  796   
  797               // cached instance?
  798               StateManagerImpl sm = getStateManagerImplById(oid,
  799                   (flags & OID_ALLOW_NEW) != 0 || hasFlushed());
  800               if (sm != null) {
  801                   if (!requiresLoad(sm, true, fetch, edata, flags))
  802                       return call.processReturn(oid, sm);
  803   
  804                   if (!sm.isLoading()) {
  805                       // make sure all the configured fields are loaded; do this
  806                       // after making instance transactional for locking
  807                       if (!sm.isTransactional() && useTransactionalState(fetch))
  808                           sm.transactional();
  809                       boolean loaded;
  810                       try {
  811                           loaded = sm.load(fetch, StateManagerImpl.LOAD_FGS, 
  812                               exclude, edata, false);
  813                       } catch (ObjectNotFoundException onfe) {
  814                           if ((flags & OID_NODELETED) != 0
  815                               || (flags & OID_NOVALIDATE) != 0)
  816                               throw onfe;
  817                           return call.processReturn(oid, null);
  818                       }
  819   
  820                       // if no data needed to be loaded and the user wants to
  821                       // validate, just make sure the object exists
  822                       if (!loaded && (flags & OID_NOVALIDATE) == 0
  823                           && _compat.getValidateTrueChecksStore()
  824                           && !sm.isTransactional()
  825                           && !_store.exists(sm, edata)) {
  826                           if ((flags & OID_NODELETED) == 0)
  827                               return call.processReturn(oid, null);
  828                           throw new ObjectNotFoundException(_loc.get
  829                               ("del-instance", sm.getManagedInstance(), oid)).
  830                               setFailedObject(sm.getManagedInstance());
  831                       }
  832                   }
  833   
  834                   // since the object was cached, we may need to upgrade lock
  835                   // if current level is higher than level of initial load
  836                   if ((_flags & FLAG_ACTIVE) != 0) {
  837                       int level = fetch.getReadLockLevel();
  838                       _lm.lock(sm, level, fetch.getLockTimeout(), edata);
  839                       sm.readLocked(level, fetch.getWriteLockLevel());
  840                   }
  841                   return call.processReturn(oid, sm);
  842               }
  843   
  844               // if there's no cached sm for a new/transient id type, we
  845               // it definitely doesn't exist
  846               if (oid instanceof StateManagerId)
  847                   return call.processReturn(oid, null);
  848   
  849               // initialize a new state manager for the datastore instance
  850               sm = newStateManagerImpl(oid, (flags & OID_COPY) != 0);
  851               boolean load = requiresLoad(sm, false, fetch, edata, flags);
  852               sm = initialize(sm, load, fetch, edata);
  853               if (sm == null) {
  854                   if ((flags & OID_NOVALIDATE) != 0)
  855                       throw new ObjectNotFoundException(oid);
  856                   return call.processReturn(oid, null);
  857               }
  858   
  859               // make sure all configured fields were loaded
  860               if (load) {
  861                   try {
  862                       sm.load(fetch, StateManagerImpl.LOAD_FGS, exclude,
  863                           edata, false);
  864                   } catch (ObjectNotFoundException onfe) {
  865                       if ((flags & OID_NODELETED) != 0
  866                           || (flags & OID_NOVALIDATE) != 0)
  867                           throw onfe;
  868                       return call.processReturn(oid, null);
  869                   }
  870               }
  871               return call.processReturn(oid, sm);
  872           } catch (OpenJPAException ke) {
  873               throw ke;
  874           } catch (RuntimeException re) {
  875               throw new GeneralException(re);
  876           } finally {
  877               endOperation();
  878           }
  879       }
  880   
  881       /**
  882        * Initialize a newly-constructed state manager.
  883        */
  884       protected StateManagerImpl initialize(StateManagerImpl sm, boolean load,
  885           FetchConfiguration fetch, Object edata) {
  886           if (!load) {
  887               sm.initialize(sm.getMetaData().getDescribedType(),
  888                   PCState.HOLLOW);
  889           } else {
  890               PCState state = (useTransactionalState(fetch))
  891                   ? PCState.PCLEAN : PCState.PNONTRANS;
  892               sm.setLoading(true);
  893               try {
  894                   if (!_store.initialize(sm, state, fetch, edata))
  895                       return null;
  896               } finally {
  897                   sm.setLoading(false);
  898               }
  899           }
  900           return sm;
  901       }
  902   
  903       public Object[] findAll(Collection oids, boolean validate,
  904           FindCallbacks call) {
  905           int flags = OID_COPY | OID_ALLOW_NEW | OID_NODELETED;
  906           if (!validate)
  907               flags |= OID_NOVALIDATE;
  908           return findAll(oids, _fc, null, null, flags, call);
  909       }
  910   
  911       public Object[] findAll(Collection oids, FetchConfiguration fetch,
  912           BitSet exclude, Object edata, int flags) {
  913           return findAll(oids, fetch, exclude, edata, flags, null);
  914       }
  915   
  916       /**
  917        * Internal finder.
  918        */
  919       protected Object[] findAll(Collection oids, FetchConfiguration fetch,
  920           BitSet exclude, Object edata, int flags, FindCallbacks call) {
  921           findAllDepth ++;
  922   
  923           // throw any exceptions for null oids up immediately
  924           if (oids == null)
  925               throw new NullPointerException("oids == null");
  926           if ((flags & OID_NOVALIDATE) != 0 && oids.contains(null))
  927               throw new UserException(_loc.get("null-oids"));
  928   
  929           // we have to use a map of oid->sm rather than a simple
  930           // array, so that we make sure not to create multiple sms for equivalent
  931           // oids if the user has duplicates in the given array
  932           if (_loading == null)
  933               _loading = new HashMap((int) (oids.size() * 1.33 + 1));
  934   
  935           if (call == null)
  936               call = this;
  937           if (fetch == null)
  938               fetch = _fc;
  939   
  940           beginOperation(true);
  941           try {
  942               assertNontransactionalRead();
  943   
  944               // collection of state managers to pass to store manager
  945               List load = null;
  946               StateManagerImpl sm;
  947               boolean initialized;
  948               boolean transState = useTransactionalState(fetch);
  949               Object obj, oid;
  950               int idx = 0;
  951               for (Iterator itr = oids.iterator(); itr.hasNext(); idx++) {
  952                   // if we've already seen this oid, skip repeats
  953                   obj = itr.next();
  954                   oid = call.processArgument(obj);
  955                   if (oid == null || _loading.containsKey(obj))
  956                       continue;
  957   
  958                   // if we don't have a cached instance or it is not transactional
  959                   // and is hollow or we need to validate, load it
  960                   sm = getStateManagerImplById(oid, (flags & OID_ALLOW_NEW) != 0
  961                       || hasFlushed());
  962                   initialized = sm != null;
  963                   if (!initialized)
  964                       sm = newStateManagerImpl(oid, (flags & OID_COPY) != 0);
  965   
  966                   _loading.put(obj, sm);
  967                   if (requiresLoad(sm, initialized, fetch, edata, flags)) {
  968                       transState = transState || useTransactionalState(fetch);
  969                       if (initialized && !sm.isTransactional() && transState)
  970                           sm.transactional();
  971                       if (load == null)
  972                           load = new ArrayList(oids.size() - idx);
  973                       load.add(sm);
  974                   } else if (!initialized)
  975                       sm.initialize(sm.getMetaData().getDescribedType(),
  976                           PCState.HOLLOW);
  977               }
  978   
  979               // pass all state managers in need of loading or validation to the
  980               // store manager
  981               if (load != null) {
  982                   PCState state = (transState) ? PCState.PCLEAN
  983                       : PCState.PNONTRANS;
  984                   Collection failed = _store.loadAll(load, state,
  985                       StoreManager.FORCE_LOAD_NONE, fetch, edata);
  986   
  987                   // set failed instances to null
  988                   if (failed != null && !failed.isEmpty()) {
  989                       if ((flags & OID_NOVALIDATE) != 0)
  990                           throw newObjectNotFoundException(failed);
  991                       for (Iterator itr = failed.iterator(); itr.hasNext();)
  992                           _loading.put(itr.next(), null);
  993                   }
  994               }
  995   
  996               // create results array; make sure all configured fields are
  997               // loaded in each instance
  998               Object[] results = new Object[oids.size()];
  999               boolean active = (_flags & FLAG_ACTIVE) != 0;
 1000               int level = fetch.getReadLockLevel();
 1001               idx = 0;
 1002               for (Iterator itr = oids.iterator(); itr.hasNext(); idx++) {
 1003                   oid = itr.next();
 1004                   sm = (StateManagerImpl) _loading.get(oid);
 1005                   if (sm != null && requiresLoad(sm, true, fetch, edata, flags)) {
 1006                       try {
 1007                           sm.load(fetch, StateManagerImpl.LOAD_FGS,
 1008                           	exclude, edata, false);
 1009                           if (active) {
 1010                               _lm.lock(sm, level, fetch.getLockTimeout(), edata);
 1011                               sm.readLocked(level, fetch.getWriteLockLevel());
 1012                           }
 1013                       }
 1014                       catch (ObjectNotFoundException onfe) {
 1015                           if ((flags & OID_NODELETED) != 0
 1016                               || (flags & OID_NOVALIDATE) != 0)
 1017                               throw onfe;
 1018                           sm = null;
 1019                       }
 1020                   }
 1021                   results[idx] = call.processReturn(oid, sm);
 1022               }
 1023               return results;
 1024           } catch (OpenJPAException ke) {
 1025               throw ke;
 1026           } catch (RuntimeException re) {
 1027               throw new GeneralException(re);
 1028           } finally {
 1029               findAllDepth--;
 1030               if (findAllDepth == 0)
 1031                   _loading = null;
 1032               endOperation();
 1033           }
 1034       }
 1035   
 1036       private boolean hasFlushed() {
 1037           return (_flags & FLAG_FLUSHED) != 0;
 1038       }
 1039   
 1040       /**
 1041        * Return whether the given instance needs loading before being returned
 1042        * to the user.
 1043        */
 1044       private boolean requiresLoad(OpenJPAStateManager sm, boolean initialized,
 1045           FetchConfiguration fetch, Object edata, int flags) {
 1046           if (!fetch.requiresLoad())
 1047               return false;
 1048           if ((flags & OID_NOVALIDATE) == 0)
 1049               return true;
 1050           if (edata != null) // take advantage of existing result
 1051               return true;
 1052           if (initialized && sm.getPCState() != PCState.HOLLOW)
 1053               return false;
 1054           if (!initialized && sm.getMetaData().getPCSubclasses().length > 0)
 1055               return true;
 1056           return !_compat.getValidateFalseReturnsHollow();
 1057       }
 1058   
 1059       /**
 1060        * Return whether to use a transactional state.
 1061        */
 1062       private boolean useTransactionalState(FetchConfiguration fetch) {
 1063           return (_flags & FLAG_ACTIVE) != 0 && (!_optimistic
 1064               || _autoClear == CLEAR_ALL
 1065               || fetch.getReadLockLevel() != LOCK_NONE);
 1066       }
 1067   
 1068       public Object findCached(Object oid, FindCallbacks call) {
 1069           if (call == null)
 1070               call = this;
 1071           oid = call.processArgument(oid);
 1072           if (oid == null)
 1073               return call.processReturn(oid, null);
 1074   
 1075           beginOperation(true);
 1076           try {
 1077               StateManagerImpl sm = getStateManagerImplById(oid, true);
 1078               return call.processReturn(oid, sm);
 1079           } finally {
 1080               endOperation();
 1081           }
 1082       }
 1083   
 1084       public Class getObjectIdType(Class cls) {
 1085           if (cls == null)
 1086               return null;
 1087   
 1088           beginOperation(false);
 1089           try {
 1090               ClassMetaData meta = _conf.getMetaDataRepositoryInstance().
 1091                   getMetaData(cls, _loader, false);
 1092               if (meta == null
 1093                   || meta.getIdentityType() == ClassMetaData.ID_UNKNOWN)
 1094                   return null;
 1095               if (meta.getIdentityType() == ClassMetaData.ID_APPLICATION)
 1096                   return meta.getObjectIdType();
 1097   
 1098               return _store.getDataStoreIdType(meta);
 1099           } catch (OpenJPAException ke) {
 1100               throw ke;
 1101           } catch (RuntimeException re) {
 1102               throw new GeneralException(re);
 1103           } finally {
 1104               endOperation();
 1105           }
 1106       }
 1107   
 1108       public Object newObjectId(Class cls, Object val) {
 1109           if (val == null)
 1110               return null;
 1111   
 1112           beginOperation(false);
 1113           try {
 1114               ClassMetaData meta = _conf.getMetaDataRepositoryInstance().
 1115                   getMetaData(cls, _loader, true);
 1116               switch (meta.getIdentityType()) {
 1117               case ClassMetaData.ID_DATASTORE:
 1118                   // delegate to store manager for datastore ids
 1119                   if (val instanceof String
 1120                       && ((String) val).startsWith(StateManagerId.STRING_PREFIX))
 1121                       return new StateManagerId((String) val);
 1122                   return _store.newDataStoreId(val, meta);
 1123               case ClassMetaData.ID_APPLICATION:
 1124                   if (ImplHelper.isAssignable(meta.getObjectIdType(), 
 1125                       val.getClass())) {
 1126                       if (!meta.isOpenJPAIdentity() 
 1127                           && meta.isObjectIdTypeShared())
 1128                           return new ObjectId(cls, val);
 1129                       return val;
 1130                   }
 1131   
 1132                   // stringified app id?
 1133                   if (val instanceof String 
 1134                       && !_conf.getCompatibilityInstance().
 1135                           getStrictIdentityValues()
 1136                       && !Modifier.isAbstract(cls.getModifiers()))
 1137                       return PCRegistry.newObjectId(cls, (String) val);
 1138   
 1139                   Object[] arr = (val instanceof Object[]) ? (Object[]) val
 1140                       : new Object[]{ val };
 1141                   return ApplicationIds.fromPKValues(arr, meta);
 1142               default:
 1143                   throw new UserException(_loc.get("meta-unknownid", cls));
 1144               }
 1145           } catch (OpenJPAException ke) {
 1146               throw ke;
 1147           } catch (ClassCastException cce) {
 1148               throw new UserException(_loc.get("bad-id-value", val,
 1149                   val.getClass().getName(), cls)).setCause(cce);
 1150           } catch (RuntimeException re) {
 1151               throw new GeneralException(re);
 1152           } finally {
 1153               endOperation();
 1154           }
 1155       }
 1156   
 1157       /**
 1158        * Create a new state manager for the given oid.
 1159        */
 1160       private StateManagerImpl newStateManagerImpl(Object oid, boolean copy) {
 1161           // see if we're in the process of loading this oid in a loadAll call
 1162           StateManagerImpl sm;
 1163           if (_loading != null) {
 1164               sm = (StateManagerImpl) _loading.get(oid);
 1165               if (sm != null && sm.getPersistenceCapable() == null)
 1166                   return sm;
 1167           }
 1168   
 1169           // find metadata for the oid
 1170           Class pcType = _store.getManagedType(oid);
 1171           MetaDataRepository repos = _conf.getMetaDataRepositoryInstance();
 1172           ClassMetaData meta;
 1173           if (pcType != null)
 1174               meta = repos.getMetaData(pcType, _loader, true);
 1175           else
 1176               meta = repos.getMetaData(oid, _loader, true);
 1177   
 1178           // copy the oid if needed
 1179           if (copy && _compat.getCopyObjectIds()) {
 1180               if (meta.getIdentityType() == ClassMetaData.ID_APPLICATION)
 1181                   oid = ApplicationIds.copy(oid, meta);
 1182               else if (meta.getIdentityType() == ClassMetaData.ID_UNKNOWN)
 1183                   throw new UserException(_loc.get("meta-unknownid", meta));
 1184               else
 1185                   oid = _store.copyDataStoreId(oid, meta);
 1186           }
 1187   
 1188           sm = newStateManagerImpl(oid, meta);
 1189           sm.setObjectId(oid);
 1190           return sm;
 1191       }
 1192   
 1193       /**
 1194        * Create a state manager for the given oid and metadata.
 1195        */
 1196       protected StateManagerImpl newStateManagerImpl(Object oid,
 1197           ClassMetaData meta) {
 1198           return new StateManagerImpl(oid, meta, this);
 1199       }
 1200   
 1201       ///////////////
 1202       // Transaction
 1203       ///////////////
 1204   
 1205       public void begin() {
 1206           beginOperation(true);
 1207           try {
 1208               if ((_flags & FLAG_ACTIVE) != 0)
 1209                   throw new InvalidStateException(_loc.get("active"));
 1210               _factory.syncWithManagedTransaction(this, true);
 1211               beginInternal();
 1212           } finally {
 1213               endOperation();
 1214           }
 1215       }
 1216   
 1217       /**
 1218        * Notify the store manager of a transaction.
 1219        */
 1220       private void beginInternal() {
 1221           try {
 1222               beginStoreManagerTransaction(_optimistic);
 1223               _flags |= FLAG_ACTIVE;
 1224   
 1225               // start locking
 1226               if (!_optimistic) {
 1227                   _fc.setReadLockLevel(_conf.getReadLockLevelConstant());
 1228                   _fc.setWriteLockLevel(_conf.getWriteLockLevelConstant());
 1229                   _fc.setLockTimeout(_conf.getLockTimeout());
 1230               }
 1231               _lm.beginTransaction();
 1232   
 1233               if (_transEventManager.hasBeginListeners())
 1234                   fireTransactionEvent(new TransactionEvent(this,
 1235                       TransactionEvent.AFTER_BEGIN, null, null, null, null));
 1236           } catch (OpenJPAException ke) {
 1237               // if we already started the transaction, don't let it commit
 1238               if ((_flags & FLAG_ACTIVE) != 0)
 1239                   setRollbackOnlyInternal(ke);
 1240               throw ke.setFatal(true);
 1241           } catch (RuntimeException re) {
 1242               // if we already started the transaction, don't let it commit
 1243               if ((_flags & FLAG_ACTIVE) != 0)
 1244                   setRollbackOnlyInternal(re);
 1245               throw new StoreException(re).setFatal(true);
 1246           }
 1247   
 1248           if (_pending != null) {
 1249               StateManagerImpl sm;
 1250               for (Iterator it = _pending.iterator(); it.hasNext();) {
 1251                   sm = (StateManagerImpl) it.next();
 1252                   sm.transactional();
 1253                   if (sm.isDirty())
 1254                       setDirty(sm, true);
 1255               }
 1256               _pending = null;
 1257           }
 1258       }
 1259   
 1260       public void beginStore() {
 1261           beginOperation(true);
 1262           try {
 1263               assertTransactionOperation();
 1264               if ((_flags & FLAG_STORE_ACTIVE) == 0)
 1265                   beginStoreManagerTransaction(false);
 1266           } catch (OpenJPAException ke) {
 1267               throw ke;
 1268           } catch (RuntimeException re) {
 1269               throw new StoreException(re);
 1270           } finally {
 1271               endOperation();
 1272           }
 1273       }
 1274   
 1275       /**
 1276        * Begin a store manager transaction.
 1277        */
 1278       private void beginStoreManagerTransaction(boolean optimistic) {
 1279           if (!optimistic) {
 1280               retainConnection();
 1281               _store.begin();
 1282               _flags |= FLAG_STORE_ACTIVE;
 1283           } else {
 1284               if (_connRetainMode == CONN_RETAIN_TRANS)
 1285                   retainConnection();
 1286               _store.beginOptimistic();
 1287           }
 1288       }
 1289   
 1290       /**
 1291        * End the current store manager transaction. Throws an
 1292        * exception to signal a forced rollback after failed commit, otherwise
 1293        * returns any exception encountered during the end process.
 1294        */
 1295       private RuntimeException endStoreManagerTransaction(boolean rollback) {
 1296           boolean forcedRollback = false;
 1297           boolean releaseConn = false;
 1298           RuntimeException err = null;
 1299           try {
 1300               if ((_flags & FLAG_STORE_ACTIVE) != 0) {
 1301                   releaseConn = _connRetainMode != CONN_RETAIN_ALWAYS;
 1302                   if (rollback)
 1303                       _store.rollback();
 1304                   else
 1305                       _store.commit();
 1306               } else {
 1307                   releaseConn = _connRetainMode == CONN_RETAIN_TRANS;
 1308                   _store.rollbackOptimistic();
 1309               }
 1310           }
 1311           catch (RuntimeException re) {
 1312               if (!rollback) {
 1313                   forcedRollback = true;
 1314                   try { _store.rollback(); } catch (RuntimeException re2) {}
 1315               }
 1316               err = re;
 1317           } finally {
 1318               _flags &= ~FLAG_STORE_ACTIVE;
 1319           }
 1320   
 1321           if (releaseConn) {
 1322               try {
 1323                   releaseConnection();
 1324               } catch (RuntimeException re) {
 1325                   if (err == null)
 1326                       err = re;
 1327               }
 1328           }
 1329   
 1330           if (forcedRollback)
 1331               throw err;
 1332           return err;
 1333       }
 1334   
 1335       public void commit() {
 1336           beginOperation(false);
 1337           try {
 1338               assertTransactionOperation();
 1339   
 1340               javax.transaction.Transaction trans =
 1341                   _runtime.getTransactionManager().getTransaction();
 1342               if (trans == null)
 1343                   throw new InvalidStateException(_loc.get("null-trans"));
 1344   
 1345               // this commit on the transaction will cause our
 1346               // beforeCompletion method to be invoked
 1347               trans.commit();
 1348           } catch (OpenJPAException ke) {
 1349               if (_log.isTraceEnabled())
 1350                   _log.trace(_loc.get("end-trans-error"), ke);
 1351               throw ke;
 1352           } catch (Exception e) {
 1353               if (_log.isTraceEnabled())
 1354                   _log.trace(_loc.get("end-trans-error"), e);
 1355               throw new StoreException(e);
 1356           } finally {
 1357               endOperation();
 1358           }
 1359       }
 1360   
 1361       public void rollback() {
 1362           beginOperation(false);
 1363           try {
 1364               assertTransactionOperation();
 1365   
 1366               javax.transaction.Transaction trans =
 1367                   _runtime.getTransactionManager().getTransaction();
 1368               if (trans != null)
 1369                   trans.rollback();
 1370           } catch (OpenJPAException ke) {
 1371               if (_log.isTraceEnabled())
 1372                   _log.trace(_loc.get("end-trans-error"), ke);
 1373               throw ke;
 1374           } catch (Exception e) {
 1375               if (_log.isTraceEnabled())
 1376                   _log.trace(_loc.get("end-trans-error"), e);
 1377               throw new StoreException(e);
 1378           } finally {
 1379               endOperation();
 1380           }
 1381       }
 1382   
 1383       public boolean syncWithManagedTransaction() {
 1384           assertOpen();
 1385           lock();
 1386           try {
 1387               if ((_flags & FLAG_ACTIVE) != 0)
 1388                   return true;
 1389               if (!_managed)
 1390                   throw new InvalidStateException(_loc.get("trans-not-managed"));
 1391               if (_factory.syncWithManagedTransaction(this, false)) {
 1392                   beginInternal();
 1393                   return true;
 1394               }
 1395               return false;
 1396           } finally {
 1397               unlock();
 1398           }
 1399       }
 1400   
 1401       public void commitAndResume() {
 1402           endAndResume(true);
 1403       }
 1404   
 1405       public void rollbackAndResume() {
 1406           endAndResume(false);
 1407       }
 1408   
 1409       private void endAndResume(boolean commit) {
 1410           beginOperation(false);
 1411           try {
 1412               if (commit)
 1413                   commit();
 1414               else
 1415                   rollback();
 1416               begin();
 1417           } finally {
 1418               endOperation();
 1419           }
 1420       }
 1421   
 1422       public boolean getRollbackOnly() {
 1423           beginOperation(true);
 1424           try {
 1425               if ((_flags & FLAG_ACTIVE) == 0)
 1426                   return false;
 1427   
 1428               javax.transaction.Transaction trans =
 1429                   _runtime.getTransactionManager().getTransaction();
 1430               if (trans == null)
 1431                   return false;
 1432               return trans.getStatus() == Status.STATUS_MARKED_ROLLBACK;
 1433           } catch (OpenJPAException ke) {
 1434               throw ke;
 1435           } catch (Exception e) {
 1436               throw new GeneralException(e);
 1437           } finally {
 1438               endOperation();
 1439           }
 1440       }
 1441   
 1442       public Throwable getRollbackCause() {
 1443           beginOperation(true);
 1444           try {
 1445               if ((_flags & FLAG_ACTIVE) == 0)
 1446                   return null;
 1447   
 1448               javax.transaction.Transaction trans =
 1449                   _runtime.getTransactionManager().getTransaction();
 1450               if (trans == null)
 1451                   return null;
 1452               if (trans.getStatus() == Status.STATUS_MARKED_ROLLBACK)
 1453                   return _runtime.getRollbackCause();
 1454   
 1455               return null;
 1456           } catch (OpenJPAException ke) {
 1457               throw ke;
 1458           } catch (Exception e) {
 1459               throw new GeneralException(e);
 1460           } finally {
 1461               endOperation();
 1462           }
 1463       }
 1464   
 1465       public void setRollbackOnly() {
 1466           setRollbackOnly(new UserException());
 1467       }
 1468   
 1469       public void setRollbackOnly(Throwable cause) {
 1470           beginOperation(true);
 1471           try {
 1472               assertTransactionOperation();
 1473               setRollbackOnlyInternal(cause);
 1474           } finally {
 1475               endOperation();
 1476           }
 1477       }
 1478   
 1479       /**
 1480        * Mark the current transaction as rollback-only.
 1481        */
 1482       private void setRollbackOnlyInternal(Throwable cause) {
 1483           try {
 1484               javax.transaction.Transaction trans =
 1485                   _runtime.getTransactionManager().getTransaction();
 1486               if (trans == null)
 1487                   throw new InvalidStateException(_loc.get("null-trans"));
 1488               // ensure tran is in a valid state to accept the setRollbackOnly
 1489               int tranStatus = trans.getStatus();
 1490               if ((tranStatus != Status.STATUS_NO_TRANSACTION)
 1491                       && (tranStatus != Status.STATUS_ROLLEDBACK)
 1492                       && (tranStatus != Status.STATUS_COMMITTED))
 1493                   _runtime.setRollbackOnly(cause);
 1494               else if (_log.isTraceEnabled())
 1495                   _log.trace(_loc.get("invalid-tran-status", new Integer(
 1496                           tranStatus), "setRollbackOnly"));
 1497           } catch (OpenJPAException ke) {
 1498               throw ke;
 1499           } catch (Exception e) {
 1500               throw new GeneralException(e);
 1501           }
 1502       }
 1503   
 1504       public void setSavepoint(String name) {
 1505           beginOperation(true);
 1506           try {
 1507               assertActiveTransaction();
 1508               if (_savepoints != null && _savepoints.containsKey(name))
 1509                   throw new UserException(_loc.get("savepoint-exists", name));
 1510   
 1511               if (hasFlushed() && !_spm.supportsIncrementalFlush())
 1512                   throw new UnsupportedException(_loc.get
 1513                       ("savepoint-flush-not-supported"));
 1514   
 1515               OpenJPASavepoint save = _spm.newSavepoint(name, this);
 1516               if (_savepoints == null || _savepoints.isEmpty()) {
 1517                   save.save(getTransactionalStates());
 1518                   _savepoints = new LinkedMap();
 1519               } else {
 1520                   if (_savepointCache == null)
 1521                       save.save(Collections.EMPTY_LIST);
 1522                   else {
 1523                       save.save(_savepointCache);
 1524                       _savepointCache.clear();
 1525                   }
 1526               }
 1527               _savepoints.put(name, save);
 1528           } catch (OpenJPAException ke) {
 1529               throw ke;
 1530           } catch (Exception e) {
 1531               throw new GeneralException(e);
 1532           } finally {
 1533               endOperation();
 1534           }
 1535       }
 1536   
 1537       public void releaseSavepoint() {
 1538           beginOperation(false);
 1539           try {
 1540               if (_savepoints == null || _savepoints.isEmpty())
 1541                   throw new UserException(_loc.get("no-lastsavepoint"));
 1542               releaseSavepoint((String) _savepoints.get
 1543                   (_savepoints.size() - 1));
 1544           } finally {
 1545               endOperation();
 1546           }
 1547       }
 1548   
 1549       public void releaseSavepoint(String savepoint) {
 1550           beginOperation(false);
 1551           try {
 1552               assertActiveTransaction();
 1553   
 1554               int index = (_savepoints == null) ? -1
 1555                   : _savepoints.indexOf(savepoint);
 1556               if (index < 0)
 1557                   throw new UserException(_loc.get("no-savepoint", savepoint));
 1558   
 1559               // clear old in reverse
 1560               OpenJPASavepoint save;
 1561               while (_savepoints.size() > index + 1) {
 1562                   save = (OpenJPASavepoint) _savepoints.remove
 1563                       (_savepoints.size() - 1);
 1564                   save.release(false);
 1565               }
 1566   
 1567               save = (OpenJPASavepoint) _savepoints.remove(index);
 1568               save.release(true);
 1569               if (_savepointCache != null)
 1570                   _savepointCache.clear();
 1571           } catch (OpenJPAException ke) {
 1572               throw ke;
 1573           } catch (Exception e) {
 1574               throw new GeneralException(e);
 1575           } finally {
 1576               endOperation();
 1577           }
 1578       }
 1579   
 1580       public void rollbackToSavepoint() {
 1581           beginOperation(false);
 1582           try {
 1583               if (_savepoints == null || _savepoints.isEmpty())
 1584                   throw new UserException(_loc.get("no-lastsavepoint"));
 1585               rollbackToSavepoint((String) _savepoints.get
 1586                   (_savepoints.size() - 1));
 1587           } finally {
 1588               endOperation();
 1589           }
 1590       }
 1591   
 1592       public void rollbackToSavepoint(String savepoint) {
 1593           beginOperation(false);
 1594           try {
 1595               assertActiveTransaction();
 1596   
 1597               int index = (_savepoints == null) ? -1
 1598                   : _savepoints.indexOf(savepoint);
 1599               if (index < 0)
 1600                   throw new UserException(_loc.get("no-savepoint", savepoint));
 1601   
 1602               // clear old in reverse
 1603               OpenJPASavepoint save;
 1604               while (_savepoints.size() > index + 1) {
 1605                   save = (OpenJPASavepoint) _savepoints.remove
 1606                       (_savepoints.size() - 1);
 1607                   save.release(false);
 1608               }
 1609   
 1610               save = (OpenJPASavepoint) _savepoints.remove(index);
 1611               Collection saved = save.rollback(_savepoints.values());
 1612               if (_savepointCache != null)
 1613                   _savepointCache.clear();
 1614               if (hasTransactionalObjects()) {
 1615                   // build up a new collection of states
 1616                   TransactionalCache oldTransCache = _transCache;
 1617                   TransactionalCache newTransCache = new TransactionalCache
 1618                       (_orderDirty);
 1619                   _transCache = null;
 1620   
 1621                   // currently there is the assumption that incremental
 1622                   // flush is either a) not allowed, or b) required
 1623                   // pre-savepoint.  this solves a number of issues including
 1624                   // storing flushed states as well as OID handling.
 1625                   // if future plugins do not follow this, we need to cache
 1626                   // more info per state
 1627                   SavepointFieldManager fm;
 1628                   StateManagerImpl sm;
 1629                   for (Iterator itr = saved.iterator(); itr.hasNext();) {
 1630                       fm = (SavepointFieldManager) itr.next();
 1631                       sm = fm.getStateManager();
 1632                       sm.rollbackToSavepoint(fm);
 1633                       oldTransCache.remove(sm);
 1634                       if (sm.isDirty())
 1635                           newTransCache.addDirty(sm);
 1636                       else
 1637                           newTransCache.addClean(sm);
 1638                   }
 1639                   for (Iterator itr = oldTransCache.iterator(); itr.hasNext();) {
 1640                       sm = (StateManagerImpl) itr.next();
 1641                       sm.rollback();
 1642                       removeFromTransaction(sm);
 1643                   }
 1644                   _transCache = newTransCache;
 1645               }
 1646           }
 1647           catch (OpenJPAException ke) {
 1648               throw ke;
 1649           } catch (Exception e) {
 1650               throw new GeneralException(e);
 1651           } finally {
 1652               endOperation();
 1653           }
 1654       }
 1655   
 1656       public void flush() {
 1657           beginOperation(true);
 1658           try {
 1659               // return silently if no trans is active, or if this is a reentrant
 1660               // call, which can happen if the store manager tries to get an
 1661               // auto-inc oid during flush
 1662               if ((_flags & FLAG_ACTIVE) == 0
 1663                   || (_flags & FLAG_STORE_FLUSHING) != 0)
 1664                   return;
 1665   
 1666               // make sure the runtime supports it
 1667               if (!_conf.supportedOptions().contains(_conf.OPTION_INC_FLUSH))
 1668                   throw new UnsupportedException(_loc.get
 1669                       ("incremental-flush-not-supported"));
 1670               if (_savepoints != null && !_savepoints.isEmpty()
 1671                   && !_spm.supportsIncrementalFlush())
 1672                   throw new UnsupportedException(_loc.get
 1673                       ("savepoint-flush-not-supported"));
 1674   
 1675               try {
 1676                   flushSafe(FLUSH_INC);
 1677                   _flags |= FLAG_FLUSHED;
 1678               } catch (OpenJPAException ke) {
 1679                   // rollback on flush error; objects may be in inconsistent state
 1680                   setRollbackOnly(ke);
 1681                   throw ke.setFatal(true);
 1682               } catch (RuntimeException re) {
 1683                   // rollback on flush error; objects may be in inconsistent state
 1684                   setRollbackOnly(re);
 1685                   throw new StoreException(re).setFatal(true);
 1686               }
 1687           }
 1688           finally {
 1689               endOperation();
 1690           }
 1691       }
 1692   
 1693       public void preFlush() {
 1694           beginOperation(true);
 1695           try {
 1696               if ((_flags & FLAG_ACTIVE) != 0)
 1697                   flushSafe(FLUSH_LOGICAL);
 1698           } finally {
 1699               endOperation();
 1700           }
 1701       }
 1702   
 1703       public void validateChanges() {
 1704           beginOperation(true);
 1705           try {
 1706               // if no trans, just return; if active datastore trans, flush
 1707               if ((_flags & FLAG_ACTIVE) == 0)
 1708                   return;
 1709               if ((_flags & FLAG_STORE_ACTIVE) != 0) {
 1710                   flush();
 1711                   return;
 1712               }
 1713   
 1714               // make sure the runtime supports inc flush
 1715               if (!_conf.supportedOptions().contains(_conf.OPTION_INC_FLUSH))
 1716                   throw new UnsupportedException(_loc.get
 1717                       ("incremental-flush-not-supported"));
 1718   
 1719               try {
 1720                   flushSafe(FLUSH_ROLLBACK);
 1721               } catch (OpenJPAException ke) {
 1722                   throw ke;
 1723               } catch (RuntimeException re) {
 1724                   throw new StoreException(re);
 1725               }
 1726           }
 1727           finally {
 1728               endOperation();
 1729           }
 1730       }
 1731   
 1732       public boolean isActive() {
 1733           beginOperation(true);
 1734           try {
 1735               return (_flags & FLAG_ACTIVE) != 0;
 1736           } finally {
 1737               endOperation();
 1738           }
 1739       }
 1740   
 1741       public boolean isStoreActive() {
 1742           // we need to lock here, because we might be in the middle of an
 1743           // atomic transaction process (e.g., commitAndResume)
 1744           beginOperation(true);
 1745           try {
 1746               return (_flags & FLAG_STORE_ACTIVE) != 0;
 1747           } finally {
 1748               endOperation();
 1749           }
 1750       }
 1751   
 1752       /**
 1753        * Return whether the current transaction is ending, i.e. in the 2nd phase
 1754        * of a commit or rollback
 1755        */
 1756       boolean isTransactionEnding() {
 1757           return (_flags & FLAG_TRANS_ENDING) != 0;
 1758       }
 1759   
 1760       public boolean beginOperation(boolean syncTrans) {
 1761           lock();
 1762           try {
 1763               assertOpen();
 1764   
 1765               if (syncTrans && _operationCount == 0 && _syncManaged
 1766                   && (_flags & FLAG_ACTIVE) == 0)
 1767                   syncWithManagedTransaction();
 1768               return _operationCount++ == 1;
 1769           } catch (OpenJPAException ke) {
 1770               unlock();
 1771               throw ke;
 1772           } catch (RuntimeException re) {
 1773               unlock();
 1774               throw new GeneralException(re);
 1775           }
 1776       }
 1777   
 1778       /**
 1779        * Mark the operation over. If outermost caller of stack, returns true
 1780        * and will detach managed instances if necessary.
 1781        */
 1782       public boolean endOperation() {
 1783           try {
 1784               if (_operationCount == 1 && (_autoDetach & DETACH_NONTXREAD) != 0
 1785                   && (_flags & FLAG_ACTIVE) == 0) {
 1786                   detachAllInternal(null);
 1787               }
 1788               if (_operationCount < 1)
 1789                   throw new InternalException(_loc.get("multi-threaded-access"));
 1790               return _operationCount == 1;
 1791           } catch (OpenJPAException ke) {
 1792               throw ke;
 1793           } catch (RuntimeException re) {
 1794               throw new GeneralException(re);
 1795           } finally {
 1796               _operationCount--;
 1797               if (_operationCount == 0)
 1798                   initializeOperatingSet();
 1799               unlock();
 1800           }
 1801       }
 1802   
 1803       public Synchronization getSynchronization() {
 1804           return _sync;
 1805       }
 1806   
 1807       public void setSynchronization(Synchronization sync) {
 1808           assertOpen();
 1809           _sync = sync;
 1810       }
 1811   
 1812       ///////////////////////////////////////////////
 1813       // Implementation of Synchronization interface
 1814       ///////////////////////////////////////////////
 1815   
 1816       public void beforeCompletion() {
 1817           beginOperation(false);
 1818           try {
 1819               // user-supplied synchronization
 1820               if (_sync != null)
 1821                   _sync.beforeCompletion();
 1822   
 1823               flushSafe(FLUSH_COMMIT);
 1824           } catch (OpenJPAException ke) {
 1825               if (_log.isTraceEnabled())
 1826                   _log.trace(_loc.get("end-trans-error"), ke);
 1827               throw translateManagedCompletionException(ke);
 1828           } catch (RuntimeException re) {
 1829               if (_log.isTraceEnabled())
 1830                   _log.trace(_loc.get("end-trans-error"), re);
 1831               throw translateManagedCompletionException(new StoreException(re));
 1832           } finally {
 1833               endOperation();
 1834           }
 1835       }
 1836   
 1837       public void afterCompletion(int status) {
 1838           beginOperation(false);
 1839           try {
 1840               assertActiveTransaction();
 1841   
 1842               _flags |= FLAG_TRANS_ENDING;
 1843               endTransaction(status);
 1844               if (_sync != null)
 1845                   _sync.afterCompletion(status);
 1846   
 1847               if ((_autoDetach & DETACH_COMMIT) != 0)
 1848                   detachAllInternal(null);
 1849               else if (status == Status.STATUS_ROLLEDBACK 
 1850                   && (_autoDetach & DETACH_ROLLBACK) != 0) {
 1851                   detachAllInternal(null);
 1852               }
 1853   
 1854               // in an ee context, it's possible that the user tried to close
 1855               // us but we didn't actually close because we were waiting on this
 1856               // transaction; if that's true, then close now
 1857               if ((_flags & FLAG_CLOSE_INVOKED) != 0
 1858                   && _compat.getCloseOnManagedCommit())
 1859                   free();
 1860           } catch (OpenJPAException ke) {
 1861               if (_log.isTraceEnabled())
 1862                   _log.trace(_loc.get("end-trans-error"), ke);
 1863               throw translateManagedCompletionException(ke);
 1864           } catch (RuntimeException re) {
 1865               if (_log.isTraceEnabled())
 1866                   _log.trace(_loc.get("end-trans-error"), re);
 1867               throw translateManagedCompletionException(new StoreException(re));
 1868           } finally {
 1869               _flags &= ~FLAG_ACTIVE;
 1870               _flags &= ~FLAG_FLUSHED;
 1871               _flags &= ~FLAG_TRANS_ENDING;
 1872   
 1873               // event manager nulled if freed broker
 1874               if (_transEventManager != null 
 1875                   && _transEventManager.hasEndListeners()) {
 1876                   fireTransactionEvent(new TransactionEvent(this,
 1877                       status == Status.STATUS_COMMITTED
 1878                           ? TransactionEvent.AFTER_COMMIT_COMPLETE
 1879                           : TransactionEvent.AFTER_ROLLBACK_COMPLETE,
 1880                       null, null, null, null));
 1881               }
 1882   
 1883               endOperation();
 1884           }
 1885       }
 1886   
 1887       /**
 1888        * If we're in a managed transaction, use our implicit behavior exception
 1889        * translator to translate before/afterCompletion callback errors.
 1890        */
 1891       private RuntimeException translateManagedCompletionException
 1892           (RuntimeException re) {
 1893           return (!_managed || _extrans == null) ? re : _extrans.translate(re);
 1894       }
 1895   
 1896       /**
 1897        * Flush safely, catching reentrant calls.
 1898        */
 1899       private void flushSafe(int reason) {
 1900           if ((_flags & FLAG_FLUSHING) != 0)
 1901               throw new InvalidStateException(_loc.get("reentrant-flush"));
 1902   
 1903           _flags |= FLAG_FLUSHING;
 1904           try {
 1905               flush(reason);
 1906           } finally {
 1907               _flags &= ~FLAG_FLUSHING;
 1908           }
 1909       }
 1910   
 1911       /**
 1912        * Flush the transactional state to the data store. Subclasses that
 1913        * customize commit behavior should override this method. The method
 1914        * assumes that the persistence manager is locked, is not closed,
 1915        * and has an active transaction.
 1916        *
 1917        * @param reason one of {@link #FLUSH_INC}, {@link #FLUSH_COMMIT},
 1918        * {@link #FLUSH_ROLLBACK}, or {@link #FLUSH_LOGICAL}
 1919        * @since 0.2.5
 1920        */
 1921       protected void flush(int reason) {
 1922           // this will enlist proxied states as necessary so we know whether we
 1923           // have anything to flush
 1924           Collection transactional = getTransactionalStates();
 1925   
 1926           // do we actually have to flush?  only if our flags say so, or if
 1927           // we have transaction listeners that need to be invoked for commit
 1928           // (no need to invoke them on inc flush if nothing is dirty).  we
 1929           // special case the remote commit listener used by the datacache cause
 1930           // we know it doesn't require the commit event when nothing changes
 1931           boolean flush = (_flags & FLAG_FLUSH_REQUIRED) != 0;
 1932           boolean listeners = (_transEventManager.hasFlushListeners()
 1933               || _transEventManager.hasEndListeners())
 1934               && ((_flags & FLAG_REMOTE_LISTENER) == 0
 1935               || _transEventManager.getListeners().size() > 1);
 1936           if (!flush && (reason != FLUSH_COMMIT || !listeners))
 1937               return;
 1938   
 1939           Collection mobjs = null;
 1940           _flags |= FLAG_PRESTORING;
 1941           try {
 1942               if (flush) {
 1943                   // call pre store on all currently transactional objs
 1944                   for (Iterator itr = transactional.iterator(); itr.hasNext();)
 1945                       ((StateManagerImpl) itr.next()).beforeFlush(reason, _call);
 1946                   flushAdditions(transactional, reason);
 1947               }
 1948   
 1949               // hopefully now all dependent instances that are going to end
 1950               // up referenced have been marked as such; delete unrefed
 1951               // dependents
 1952               _flags |= FLAG_DEREFDELETING;
 1953               if (flush && _derefCache != null && !_derefCache.isEmpty()) {
 1954                   for (Iterator itr = _derefCache.iterator(); itr.hasNext();)
 1955                       deleteDeref((StateManagerImpl) itr.next());
 1956                   flushAdditions(transactional, reason);
 1957               }
 1958   
 1959               if (reason != FLUSH_LOGICAL) {
 1960                   // if no datastore transaction, start one; even if we don't
 1961                   // think we'll need to flush at this point, our transaction
 1962                   // listeners might introduce some dirty objects or interact
 1963                   // directly with the database
 1964                   if ((_flags & FLAG_STORE_ACTIVE) == 0)
 1965                       beginStoreManagerTransaction(false);
 1966   
 1967                   if ((_transEventManager.hasFlushListeners()
 1968                       || _transEventManager.hasEndListeners())
 1969                       && (flush || reason == FLUSH_COMMIT)) {
 1970                       // fire events
 1971                       mobjs = new ManagedObjectCollection(transactional);
 1972                       if (reason == FLUSH_COMMIT
 1973                           && _transEventManager.hasEndListeners()) {
 1974                           fireTransactionEvent(new TransactionEvent(this, 
 1975                               TransactionEvent.BEFORE_COMMIT, mobjs,
 1976                               _persistedClss, _updatedClss, _deletedClss));
 1977   
 1978                           flushAdditions(transactional, reason);
 1979                           flush = (_flags & FLAG_FLUSH_REQUIRED) != 0;
 1980                       }
 1981   
 1982                       if (flush && _transEventManager.hasFlushListeners()) {
 1983                           fireTransactionEvent(new TransactionEvent(this, 
 1984                               TransactionEvent.BEFORE_FLUSH, mobjs,
 1985                               _persistedClss, _updatedClss, _deletedClss));
 1986                           flushAdditions(transactional, reason);
 1987                       }
 1988                   }
 1989               }
 1990           }
 1991           finally {
 1992               _flags &= ~FLAG_PRESTORING;
 1993               _flags &= ~FLAG_DEREFDELETING;
 1994               _transAdditions = null;
 1995               _derefAdditions = null;
 1996   
 1997               // also clear derefed set; the deletes have been recorded
 1998               if (_derefCache != null)
 1999                   _derefCache = null;
 2000           }
 2001   
 2002           // flush to store manager
 2003           List exceps = null;
 2004           try {
 2005               if (flush && reason != FLUSH_LOGICAL) {
 2006                   _flags |= FLAG_STORE_FLUSHING;
 2007                   exceps = add(exceps,
 2008                       newFlushException(_store.flush(transactional)));
 2009               }
 2010           } finally {
 2011               _flags &= ~FLAG_STORE_FLUSHING;
 2012   
 2013               if (reason == FLUSH_ROLLBACK)
 2014                   exceps = add(exceps, endStoreManagerTransaction(true));
 2015               else if (reason != FLUSH_LOGICAL)
 2016                   _flags &= ~FLAG_FLUSH_REQUIRED;
 2017   
 2018               // mark states as flushed
 2019               if (flush) {
 2020                   StateManagerImpl sm;
 2021                   for (Iterator itr = transactional.iterator(); itr.hasNext();) {
 2022                       sm = (StateManagerImpl) itr.next();
 2023                       try {
 2024                           // the state may have become transient, such as if
 2025                           // it is embedded and the owner has been deleted during
 2026                           // this flush process; bug #1100
 2027                           if (sm.getPCState() == PCState.TRANSIENT)
 2028                               continue;
 2029   
 2030                           sm.afterFlush(reason);
 2031                           if (reason == FLUSH_INC) {
 2032                               // if not about to clear trans cache for commit 
 2033                               // anyway, re-cache dirty objects with default soft
 2034                               // refs; we don't need hard refs now that the 
 2035                               // changes have been flushed
 2036                               sm.proxyFields(true, false);
 2037                               _transCache.flushed(sm);
 2038                           }
 2039                       } catch (Exception e) {
 2040                           exceps = add(exceps, e);
 2041                       }
 2042                   }
 2043               }
 2044           }
 2045   
 2046           // throw any exceptions to shortcut listeners on fail
 2047           throwNestedExceptions(exceps, true);
 2048   
 2049           if (flush && reason != FLUSH_ROLLBACK && reason != FLUSH_LOGICAL
 2050               && _transEventManager.hasFlushListeners()) {
 2051               fireTransactionEvent(new TransactionEvent(this,
 2052                   TransactionEvent.AFTER_FLUSH, mobjs, _persistedClss,
 2053                   _updatedClss, _deletedClss));
 2054           }
 2055       }
 2056   
 2057       /**
 2058        * Flush newly-transactional objects.
 2059        */
 2060       private void flushAdditions(Collection transactional, int reason) {
 2061           boolean loop;
 2062           do {
 2063               // flush new transactional instances; note logical or
 2064               loop = flushTransAdditions(transactional, reason)
 2065                   | deleteDerefAdditions(_derefCache);
 2066           } while (loop);
 2067       }
 2068   
 2069       /**
 2070        * Flush transactional additions.
 2071        */
 2072       private boolean flushTransAdditions(Collection transactional, int reason) {
 2073           if (_transAdditions == null || _transAdditions.isEmpty())
 2074               return false;
 2075   
 2076           // keep local transactional list copy up to date
 2077           transactional.addAll(_transAdditions);
 2078   
 2079           // copy the change set, then clear it for the next iteration
 2080           StateManagerImpl[] states = (StateManagerImpl[]) _transAdditions.
 2081               toArray(new StateManagerImpl[_transAdditions.size()]);
 2082           _transAdditions = null;
 2083   
 2084           for (int i = 0; i < states.length; i++)
 2085               states[i].beforeFlush(reason, _call);
 2086           return true;
 2087       }
 2088   
 2089       /**
 2090        * Delete new dereferenced objects.
 2091        */
 2092       private boolean deleteDerefAdditions(Collection derefs) {
 2093           if (_derefAdditions == null || _derefAdditions.isEmpty())
 2094               return false;
 2095   
 2096           // remember these additions in case one becomes derefed again later
 2097           derefs.addAll(_derefAdditions);
 2098   
 2099           StateManagerImpl[] states = (StateManagerImpl[]) _derefAdditions.
 2100               toArray(new StateManagerImpl[_derefAdditions.size()]);
 2101           _derefAdditions = null;
 2102   
 2103           for (int i = 0; i < states.length; i++)
 2104               deleteDeref(states[i]);
 2105           return true;
 2106       }
 2107   
 2108       /**
 2109        * Delete a dereferenced dependent.
 2110        */
 2111       private void deleteDeref(StateManagerImpl sm) {
 2112           int action = processArgument(OpCallbacks.OP_DELETE,
 2113               sm.getManagedInstance(), sm, null);
 2114           if ((action & OpCallbacks.ACT_RUN) != 0)
 2115               sm.delete();
 2116           if ((action & OpCallbacks.ACT_CASCADE) != 0)
 2117               sm.cascadeDelete(_call);
 2118       }
 2119   
 2120       /**
 2121        * Determine the action to take based on the user's given callbacks and
 2122        * our implicit behavior.
 2123        */
 2124       private int processArgument(int op, Object obj, OpenJPAStateManager sm,
 2125           OpCallbacks call) {
 2126           if (call != null)
 2127               return call.processArgument(op, obj, sm);
 2128           if (_call != null)
 2129               return _call.processArgument(op, obj, sm);
 2130           return OpCallbacks.ACT_RUN | OpCallbacks.ACT_CASCADE;
 2131       }
 2132   
 2133       /**
 2134        * Throw the proper exception based on the given set of flush errors, or
 2135        * do nothing if no errors occurred.
 2136        */
 2137       private OpenJPAException newFlushException(Collection exceps) {
 2138           if (exceps == null || exceps.isEmpty())
 2139               return null;
 2140   
 2141           Throwable[] t = (Throwable[]) exceps.toArray
 2142               (new Throwable[exceps.size()]);
 2143           List failed = new ArrayList(t.length);
 2144   
 2145           // create fatal exception with nested exceptions for all the failed
 2146           // objects; if all OL exceptions, throw a top-level OL exception
 2147           boolean opt = true;
 2148           for (int i = 0; opt && i < t.length; i++) {
 2149               opt = t[i] instanceof OptimisticException;
 2150               if (opt) {
 2151                   Object f = ((OptimisticException) t[i]).getFailedObject();
 2152                   if (f != null)
 2153                       failed.add(f);
 2154               }
 2155           }
 2156           if (opt && !failed.isEmpty())
 2157               return new OptimisticException(failed, t);
 2158           if (opt)
 2159               return new OptimisticException(t);
 2160           return new StoreException(_loc.get("rolled-back")).
 2161               setNestedThrowables(t).setFatal(true);
 2162       }
 2163   
 2164       /**
 2165        * End the current transaction, making appropriate state transitions.
 2166        */
 2167       protected void endTransaction(int status) {
 2168           // if a data store transaction was in progress, do the
 2169           // appropriate transaction change
 2170           boolean rollback = status != Status.STATUS_COMMITTED;
 2171           List exceps = null;
 2172   
 2173           try {
 2174               exceps = add(exceps, endStoreManagerTransaction(rollback));
 2175           } catch (RuntimeException re) {
 2176               rollback = true;
 2177               exceps = add(exceps, re);
 2178           }
 2179   
 2180           // go back to default none lock level
 2181           _fc.setReadLockLevel(LOCK_NONE);
 2182           _fc.setWriteLockLevel(LOCK_NONE);
 2183           _fc.setLockTimeout(-1);
 2184   
 2185           Collection transStates;
 2186           if (hasTransactionalObjects())
 2187               transStates = _transCache;
 2188           else
 2189               transStates = Collections.EMPTY_LIST;
 2190   
 2191           // fire after rollback/commit event
 2192           Collection mobjs = null;
 2193           if (_transEventManager.hasEndListeners()) {
 2194               mobjs = new ManagedObjectCollection(transStates);
 2195               int eventType = (rollback) ? TransactionEvent.AFTER_ROLLBACK
 2196                   : TransactionEvent.AFTER_COMMIT;
 2197               fireTransactionEvent(new TransactionEvent(this, eventType, mobjs, 
 2198                   _persistedClss, _updatedClss, _deletedClss));
 2199           }
 2200   
 2201           // null transactional caches now so that all the removeFromTransaction
 2202           // calls as we transition each object don't have to do any work; don't
 2203           // clear trans cache object because we still need the transStates
 2204           // reference to it below
 2205           _transCache = null;
 2206           if (_persistedClss != null)
 2207               _persistedClss = null;
 2208           if (_updatedClss != null)
 2209               _updatedClss = null;
 2210           if (_deletedClss != null)
 2211               _deletedClss = null;
 2212   
 2213           // new cache would get cleared anyway during transitions, but doing so
 2214           // immediately saves us some lookups
 2215           _cache.clearNew();
 2216   
 2217           // tell all derefed instances they're no longer derefed; we can't
 2218           // rely on rollback and commit calls below cause some instances might
 2219           // not be transactional
 2220           if (_derefCache != null && !_derefCache.isEmpty()) {
 2221               for (Iterator itr = _derefCache.iterator(); itr.hasNext();)
 2222                   ((StateManagerImpl) itr.next()).setDereferencedDependent
 2223                       (false, false);
 2224               _derefCache = null;
 2225           }
 2226   
 2227           // peform commit or rollback state transitions on each instance
 2228           StateManagerImpl sm;
 2229           for (Iterator itr = transStates.iterator(); itr.hasNext();) {
 2230               sm = (StateManagerImpl) itr.next();
 2231               try {
 2232                   if (rollback) {
 2233                       // tell objects that may have been derefed then flushed
 2234                       // (and therefore deleted) to un-deref
 2235                       sm.setDereferencedDependent(false, false);
 2236                       sm.rollback();
 2237                   } else
 2238                       sm.commit();
 2239               } catch (RuntimeException re) {
 2240                   exceps = add(exceps, re);
 2241               }
 2242           }
 2243   
 2244           // notify the lock manager to clean up and release remaining locks
 2245           _lm.endTransaction();
 2246   
 2247           // clear old savepoints in reverse
 2248           OpenJPASavepoint save;
 2249           while (_savepoints != null && _savepoints.size() > 0) {
 2250               save =
 2251                   (OpenJPASavepoint) _savepoints.remove(_savepoints.size() - 1);
 2252               save.release(false);
 2253           }
 2254           _savepoints = null;
 2255           _savepointCache = null;
 2256   
 2257           // fire after state change event
 2258           if (_transEventManager.hasEndListeners())
 2259               fireTransactionEvent(new TransactionEvent(this, TransactionEvent.
 2260                   AFTER_STATE_TRANSITIONS, mobjs, null, null, null));
 2261   
 2262           // now clear trans cache; keep cleared version rather than
 2263           // null to avoid having to re-create the set later; more efficient
 2264           if (transStates != Collections.EMPTY_LIST) {
 2265               _transCache = (TransactionalCache) transStates;
 2266               _transCache.clear();
 2267           }
 2268   
 2269           throwNestedExceptions(exceps, true);
 2270       }
 2271   
 2272       ////////////////////
 2273       // Object lifecycle
 2274       ////////////////////
 2275   
 2276       public void persist(Object obj, OpCallbacks call) {
 2277           persist(obj, null, true, call);
 2278       }
 2279   
 2280       public OpenJPAStateManager persist(Object obj, Object id,
 2281           OpCallbacks call) {
 2282           return persist(obj, id, true, call);
 2283       }
 2284   
 2285       public void persistAll(Collection objs, OpCallbacks call) {
 2286           persistAll(objs, true, call);
 2287       }
 2288   
 2289       /**
 2290        * Persist the given objects.  Indicate whether this was an explicit persist
 2291        * (PNEW) or a provisonal persist (PNEWPROVISIONAL).
 2292        */
 2293       public void persistAll(Collection objs, boolean explicit, 
 2294           OpCallbacks call) {
 2295           if (objs.isEmpty())
 2296               return;
 2297   
 2298           beginOperation(true);
 2299           List exceps = null;
 2300           try {
 2301               assertWriteOperation();
 2302   
 2303               for (Iterator itr = objs.iterator(); itr.hasNext();) {
 2304                   try {
 2305                       persist(itr.next(), explicit, call);
 2306                   } catch (UserException ue) {
 2307                       exceps = add(exceps, ue);
 2308                   }
 2309               }
 2310           } finally {
 2311               endOperation();
 2312           }
 2313           throwNestedExceptions(exceps, false);
 2314       }
 2315   
 2316       /**
 2317        * If the given element is not null, add it to the given list,
 2318        * creating the list if necessary.
 2319        */
 2320       private List add(List l, Object o) {
 2321           if (o == null)
 2322               return l;
 2323           if (l == null)
 2324               l = new LinkedList();
 2325           l.add(o);
 2326           return l;
 2327       }
 2328   
 2329       /**
 2330        * Throw an exception wrapping the given nested exceptions.
 2331        */
 2332       private void throwNestedExceptions(List exceps, boolean datastore) {
 2333           if (exceps == null || exceps.isEmpty())
 2334               return;
 2335           if (datastore && exceps.size() == 1)
 2336               throw (RuntimeException) exceps.get(0);
 2337   
 2338           boolean fatal = false;
 2339           Throwable[] t = (Throwable[]) exceps.toArray
 2340               (new Throwable[exceps.size()]);
 2341           for (int i = 0; i < t.length; i++) {
 2342               if (t[i] instanceof OpenJPAException
 2343                   && ((OpenJPAException) t[i]).isFatal())
 2344                   fatal = true;
 2345           }
 2346           OpenJPAException err;
 2347           if (datastore)
 2348               err = new StoreException(_loc.get("nested-exceps"));
 2349           else
 2350               err = new UserException(_loc.get("nested-exceps"));
 2351           throw err.setNestedThrowables(t).setFatal(fatal);
 2352       }
 2353   
 2354       /**
 2355        * Persist the given object.  Indicate whether this was an explicit persist
 2356        * (PNEW) or a provisonal persist (PNEWPROVISIONAL)
 2357        */
 2358       public void persist(Object obj, boolean explicit, OpCallbacks call) {
 2359           persist(obj, null, explicit, call);
 2360       }
 2361   
 2362       /**
 2363        * Persist the given object.  Indicate whether this was an explicit persist
 2364        * (PNEW) or a provisonal persist (PNEWPROVISIONAL).
 2365        * See {@link Broker} for details on this method.
 2366        */
 2367       public OpenJPAStateManager persist(Object obj, Object id, boolean explicit,
 2368           OpCallbacks call) {
 2369           if (obj == null)
 2370               return null;
 2371   
 2372           beginOperation(true);
 2373           try {
 2374               assertWriteOperation();
 2375   
 2376               StateManagerImpl sm = getStateManagerImpl(obj, true);
 2377               if (!_operating.add(obj))
 2378                   return sm;
 2379   
 2380               int action = processArgument(OpCallbacks.OP_PERSIST, obj, sm, call);
 2381               if (action == OpCallbacks.ACT_NONE)
 2382                   return sm;
 2383   
 2384               // ACT_CASCADE
 2385               if ((action & OpCallbacks.ACT_RUN) == 0) {
 2386                   if (sm != null)
 2387                       sm.cascadePersist(call);
 2388                   else
 2389                       cascadeTransient(OpCallbacks.OP_PERSIST, obj, call,
 2390                           "persist");
 2391                   return sm;
 2392               }
 2393   
 2394               // ACT_RUN
 2395               PersistenceCapable pc;
 2396               if (sm != null) {
 2397                   if (sm.isDetached())
 2398                       throw new ObjectExistsException(_loc.get
 2399                           ("persist-detached", Exceptions.toString(obj))).
 2400                           setFailedObject(obj);
 2401   
 2402                   if (!sm.isEmbedded()) {
 2403                       sm.persist();
 2404                       _cache.persist(sm);
 2405                       if ((action & OpCallbacks.ACT_CASCADE) != 0)
 2406                           sm.cascadePersist(call);
 2407                       return sm;
 2408                   }
 2409   
 2410                   // an embedded field; notify the owner that the value has
 2411                   // changed by becoming independently persistent
 2412                   sm.getOwner().dirty(sm.getOwnerIndex());
 2413                   _cache.persist(sm);
 2414                   pc = sm.getPersistenceCapable();
 2415               } else {
 2416                   pc = assertPersistenceCapable(obj);
 2417                   if (pc.pcIsDetached() == Boolean.TRUE)
 2418                       throw new ObjectExistsException(_loc.get
 2419                           ("persist-detached", Exceptions.toString(obj))).
 2420                           setFailedObject(obj);
 2421               }
 2422   
 2423               ClassMetaData meta = _conf.getMetaDataRepositoryInstance().
 2424                   getMetaData(obj.getClass(), _loader, true);
 2425               fireLifecycleEvent(obj, null, meta, LifecycleEvent.BEFORE_PERSIST);
 2426   
 2427               // create id for instance
 2428               if (id == null) {
 2429                   if (meta.getIdentityType() == ClassMetaData.ID_APPLICATION)
 2430                       id = ApplicationIds.create(pc, meta);
 2431                   else if (meta.getIdentityType() == ClassMetaData.ID_UNKNOWN)
 2432                       throw new UserException(_loc.get("meta-unknownid", meta));
 2433                   else
 2434                       id = StateManagerId.newInstance(this);
 2435               }
 2436   
 2437               // make sure we don't already have the instance cached
 2438               checkForDuplicateId(id, obj);
 2439   
 2440               // if had embedded sm, null it
 2441               if (sm != null)
 2442                   pc.pcReplaceStateManager(null);
 2443   
 2444               // create new sm
 2445               sm = new StateManagerImpl(id, meta, this);
 2446               if ((_flags & FLAG_ACTIVE) != 0) {
 2447                   if (explicit)
 2448                       sm.initialize(pc, PCState.PNEW);
 2449                   else
 2450                       sm.initialize(pc, PCState.PNEWPROVISIONAL);
 2451               } else
 2452                   sm.initialize(pc, PCState.PNONTRANSNEW);
 2453               if ((action & OpCallbacks.ACT_CASCADE) != 0)
 2454                   sm.cascadePersist(call);
 2455               return sm;
 2456           } catch (OpenJPAException ke) {
 2457               throw ke;
 2458           } catch (RuntimeException re) {
 2459               throw new GeneralException(re);
 2460           } finally {
 2461               endOperation();
 2462           }
 2463       }
 2464   
 2465       /**
 2466        * Temporarily manage the given instance in order to cascade the given
 2467        * operation through it.
 2468        */
 2469       private void cascadeTransient(int op, Object obj, OpCallbacks call,
 2470           String errOp) {
 2471           PersistenceCapable pc = assertPersistenceCapable(obj);
 2472   
 2473           // if using detached state manager, don't replace
 2474           if (pc.pcGetStateManager() != null)
 2475               throw newDetachedException(obj, errOp);
 2476   
 2477           ClassMetaData meta = _conf.getMetaDataRepositoryInstance().
 2478               getMetaData(obj.getClass(), _loader, true);
 2479           StateManagerImpl sm = new StateManagerImpl(StateManagerId.
 2480               newInstance(this), meta, this);
 2481           sm.initialize(pc, PCState.TLOADED);
 2482           try {
 2483               switch (op) {
 2484                   case OpCallbacks.OP_PERSIST:
 2485                       sm.cascadePersist(call);
 2486                       break;
 2487                   case OpCallbacks.OP_DELETE:
 2488                       sm.cascadeDelete(call);
 2489                       break;
 2490                   case OpCallbacks.OP_REFRESH:
 2491                       sm.gatherCascadeRefresh(call);
 2492                       break;
 2493                   default:
 2494                       throw new InternalException(String.valueOf(op));
 2495               }
 2496           }
 2497           finally {
 2498               sm.release(true);
 2499           }
 2500       }
 2501   
 2502       public void deleteAll(Collection objs, OpCallbacks call) {
 2503           beginOperation(true);
 2504           try {
 2505               assertWriteOperation();
 2506   
 2507               List exceps = null;
 2508               Object obj;
 2509               for (Iterator itr = objs.iterator(); itr.hasNext();) {
 2510                   try {
 2511                       obj = itr.next();
 2512                       if (obj != null)
 2513                           delete(obj, getStateManagerImpl(obj, true), call);
 2514                   } catch (UserException ue) {
 2515                       exceps = add(exceps, ue);
 2516                   }
 2517               }
 2518               throwNestedExceptions(exceps, false);
 2519           } finally {
 2520               endOperation();
 2521           }
 2522       }
 2523   
 2524       public void delete(Object obj, OpCallbacks call) {
 2525           if (obj == null)
 2526               return;
 2527   
 2528           beginOperation(true);
 2529           try {
 2530               assertWriteOperation();
 2531               delete(obj, getStateManagerImpl(obj, true), call);
 2532           } catch (OpenJPAException ke) {
 2533               throw ke;
 2534           } catch (RuntimeException re) {
 2535               throw new GeneralException(re);
 2536           } finally {
 2537               endOperation();
 2538           }
 2539       }
 2540   
 2541       /**
 2542        * Internal delete.
 2543        */
 2544       void delete(Object obj, StateManagerImpl sm, OpCallbacks call) {
 2545           if (!_operating.add(obj))
 2546               return;
 2547   
 2548           int action = processArgument(OpCallbacks.OP_DELETE, obj, sm, call);
 2549           if (action == OpCallbacks.ACT_NONE)
 2550               return;
 2551   
 2552           // ACT_CASCADE
 2553           if ((action & OpCallbacks.ACT_RUN) == 0) {
 2554               if (sm != null)
 2555                   sm.cascadeDelete(call);
 2556               else
 2557                   cascadeTransient(OpCallbacks.OP_DELETE, obj, call, "delete");
 2558               return;
 2559           }
 2560   
 2561           // ACT_RUN
 2562           if (sm != null) {
 2563               if (sm.isDetached())
 2564                   throw newDetachedException(obj, "delete");
 2565               if ((action & OpCallbacks.ACT_CASCADE) != 0)
 2566                   sm.cascadeDelete(call);
 2567               sm.delete();
 2568           } else if (assertPersistenceCapable(obj).pcIsDetached() == Boolean.TRUE)
 2569               throw newDetachedException(obj, "delete");
 2570       }
 2571   
 2572       /**
 2573        * Throw an exception indicating that the current action can't be
 2574        * performed on a detached object.
 2575        */
 2576       private OpenJPAException newDetachedException(Object obj,
 2577           String operation) {
 2578           throw new UserException(_loc.get("bad-detached-op", operation,
 2579               Exceptions.toString(obj))).setFailedObject(obj);
 2580       }
 2581   
 2582       public void releaseAll(Collection objs, OpCallbacks call) {
 2583           beginOperation(false);
 2584           try {
 2585               List exceps = null;
 2586               for (Iterator itr = objs.iterator(); itr.hasNext();) {
 2587                   try {
 2588                       release(itr.next(), call);
 2589                   } catch (UserException ue) {
 2590                       exceps = add(exceps, ue);
 2591                   }
 2592               }
 2593               throwNestedExceptions(exceps, false);
 2594           } finally {
 2595               endOperation();
 2596           }
 2597       }
 2598   
 2599       public void release(Object obj, OpCallbacks call) {
 2600           if (obj == null)
 2601               return;
 2602   
 2603           beginOperation(false);
 2604           try {
 2605               StateManagerImpl sm = getStateManagerImpl(obj, true);
 2606               int action = processArgument(OpCallbacks.OP_RELEASE, obj, sm, call);
 2607   
 2608               if (sm == null)
 2609                   return;
 2610               if ((action & OpCallbacks.ACT_RUN) != 0 && sm.isPersistent()) {
 2611                   boolean pending = sm.isPendingTransactional();
 2612                   sm.release(true);
 2613                   if (pending)
 2614                       removeFromPendingTransaction(sm);
 2615               }
 2616           }
 2617           catch (OpenJPAException ke) {
 2618               throw ke;
 2619           } catch (RuntimeException re) {
 2620               throw new GeneralException(re);
 2621           } finally {
 2622               endOperation();
 2623           }
 2624       }
 2625   
 2626       public OpenJPAStateManager embed(Object obj, Object id,
 2627           OpenJPAStateManager owner, ValueMetaData ownerMeta) {
 2628           beginOperation(true);
 2629           try {
 2630               StateManagerImpl orig = getStateManagerImpl(obj, true);
 2631               if (orig != null) {
 2632                   // if already embedded, nothing to do
 2633                   if (orig.getOwner() == owner && orig.getMetaData().
 2634                       getEmbeddingMetaData() == ownerMeta)
 2635                       return orig;
 2636   
 2637                   // otherwise make sure pc is fully loaded for when we copy its
 2638                   // data below
 2639                   orig.load(_fc, StateManagerImpl.LOAD_ALL, null, null, false);
 2640               }
 2641   
 2642               // create new state manager with embedded metadata
 2643               ClassMetaData meta = ownerMeta.getEmbeddedMetaData();
 2644               if (meta == null)
 2645                   throw new InternalException(_loc.get("bad-embed", ownerMeta));
 2646   
 2647               if (id == null)
 2648                   id = StateManagerId.newInstance(this);
 2649   
 2650               StateManagerImpl sm = new StateManagerImpl(id, meta, this);
 2651               sm.setOwner((StateManagerImpl) owner, ownerMeta);
 2652   
 2653               PersistenceCapable copy;
 2654               PCState state;
 2655               Class type = meta.getDescribedType();
 2656               if (obj != null) {
 2657                   // give copy and the original instance the same state manager
 2658                   // so that we can copy fields from one to the other
 2659                   StateManagerImpl copySM;
 2660                   PersistenceCapable pc;
 2661                   if (orig == null) {
 2662                       copySM = sm;
 2663                       pc = assertPersistenceCapable(obj);
 2664                       pc.pcReplaceStateManager(sm);
 2665                   } else {
 2666                       copySM = orig;
 2667                       pc = orig.getPersistenceCapable();
 2668                   }
 2669   
 2670                   try {
 2671                       // copy the instance.  we do this even if it doesn't already
 2672                       // have a state manager in case it is later assigned to a
 2673                       // PC field; at that point it's too late to copy
 2674                       copy = PCRegistry.newInstance(type, copySM, false);
 2675                       int[] fields = new int[meta.getFields().length];
 2676                       for (int i = 0; i < fields.length; i++)
 2677                           fields[i] = i;
 2678                       copy.pcCopyFields(pc, fields);
 2679                       state = PCState.ECOPY;
 2680                       copy.pcReplaceStateManager(null);
 2681                   } finally {
 2682                       // if the instance didn't have a state manager to start,
 2683                       // revert it to being transient
 2684                       if (orig == null)
 2685                           pc.pcReplaceStateManager(null);
 2686                   }
 2687               } else {
 2688                   copy = PCRegistry.newInstance(type, sm, false);
 2689                   if ((_flags & FLAG_ACTIVE) != 0 && !_optimistic)
 2690                       state = PCState.ECLEAN;
 2691                   else
 2692                       state = PCState.ENONTRANS;
 2693               }
 2694   
 2695               sm.initialize(copy, state);
 2696               return sm;
 2697           } catch (OpenJPAException ke) {
 2698               throw ke;
 2699           } catch (RuntimeException re) {
 2700               throw new GeneralException(re);
 2701           } finally {
 2702               endOperation();
 2703           }
 2704       }
 2705   
 2706       /**
 2707        * If not already cached, create an empty copy of the given state
 2708        * manager in the given state.
 2709        */
 2710       OpenJPAStateManager copy(OpenJPAStateManager copy, PCState state) {
 2711           beginOperation(true);
 2712           try {
 2713               assertOpen();
 2714               Object oid = copy.fetchObjectId();
 2715               Class type = copy.getManagedInstance().getClass();
 2716               if (oid == null)
 2717                   throw new InternalException();
 2718               // cached instance?
 2719               StateManagerImpl sm = null;
 2720               if (!copy.isEmbedded())
 2721                   sm = getStateManagerImplById(oid, true);
 2722               if (sm == null) {
 2723                   MetaDataRepository repos = _conf.
 2724                       getMetaDataRepositoryInstance();
 2725                   ClassMetaData meta = repos.getMetaData(type, _loader, true);
 2726                   // construct a new state manager with all info known
 2727                   sm = new StateManagerImpl(oid, meta, this);
 2728                   sm.setObjectId(oid);
 2729                   sm.initialize(sm.getMetaData().getDescribedType(), state);
 2730               }
 2731               return sm;
 2732           } finally {
 2733               endOperation();
 2734           }
 2735       }
 2736   
 2737       public void refreshAll(Collection objs, OpCallbacks call) {
 2738           if (objs.isEmpty())
 2739               return;
 2740   
 2741           beginOperation(true);
 2742           try {
 2743               assertNontransactionalRead();
 2744   
 2745               for (Iterator itr = objs.iterator(); itr.hasNext();)
 2746                   gatherCascadeRefresh(itr.next(), call);
 2747               if (_operating.isEmpty())
 2748                   return;
 2749               if (_operating.size() == 1)
 2750                   refreshInternal(_operating.iterator().next(), call);
 2751               else
 2752                   refreshInternal(_operating, call);
 2753           } finally {
 2754               endOperation();
 2755           }
 2756       }
 2757   
 2758       public void refresh(Object obj, OpCallbacks call) {
 2759           if (obj == null)
 2760               return;
 2761   
 2762           beginOperation(true);
 2763           try {
 2764               assertNontransactionalRead();
 2765   
 2766               gatherCascadeRefresh(obj, call);
 2767               if (_operating.isEmpty())
 2768                   return;
 2769               if (_operating.size() == 1)
 2770                   refreshInternal(_operating.iterator().next(), call);
 2771               else
 2772                   refreshInternal(_operating, call);
 2773           } finally {
 2774               endOperation();
 2775           }
 2776       }
 2777   
 2778       /**
 2779        * Gathers all objects reachable through cascade-refresh relations
 2780        * into the operating set.
 2781        */
 2782       void gatherCascadeRefresh(Object obj, OpCallbacks call) {
 2783           if (obj == null)
 2784               return;
 2785           if (!_operating.add(obj))
 2786               return;
 2787   
 2788           StateManagerImpl sm = getStateManagerImpl(obj, false);
 2789           int action = processArgument(OpCallbacks.OP_REFRESH, obj, sm, call);
 2790           if ((action & OpCallbacks.ACT_CASCADE) == 0)
 2791               return;
 2792   
 2793           if (sm != null)
 2794               sm.gatherCascadeRefresh(call);
 2795           else
 2796               cascadeTransient(OpCallbacks.OP_REFRESH, obj, call, "refresh");
 2797       }
 2798   
 2799       /**
 2800        * This method is called with the full set of objects reachable via
 2801        * cascade-refresh relations from the user-given instances.
 2802        */
 2803       protected void refreshInternal(Collection objs, OpCallbacks call) {
 2804           List exceps = null;
 2805           try {
 2806               // collect instances that need a refresh
 2807               Collection load = null;
 2808               StateManagerImpl sm;
 2809               Object obj;
 2810               for (Iterator itr = objs.iterator(); itr.hasNext();) {
 2811                   obj = itr.next();
 2812                   if (obj == null)
 2813                       continue;
 2814   
 2815                   try {
 2816                       sm = getStateManagerImpl(obj, true);
 2817                       if ((processArgument(OpCallbacks.OP_REFRESH, obj, sm, call)
 2818                           & OpCallbacks.ACT_RUN) == 0)
 2819                           continue;
 2820   
 2821                       if (sm != null) {
 2822                           if (sm.isDetached())
 2823                               throw newDetachedException(obj, "refresh");
 2824                           else if (sm.beforeRefresh(true)) {
 2825                               if (load == null)
 2826                                   load = new ArrayList(objs.size());
 2827                               load.add(sm);
 2828                           }
 2829                       } else if (assertPersistenceCapable(obj).pcIsDetached()
 2830                           == Boolean.TRUE)
 2831                           throw newDetachedException(obj, "refresh");
 2832                   } catch (OpenJPAException ke) {
 2833                       exceps = add(exceps, ke);
 2834                   }
 2835               }
 2836   
 2837               // refresh all
 2838               if (load != null) {
 2839                   Collection failed = _store.loadAll(load, null,
 2840                       StoreManager.FORCE_LOAD_REFRESH, _fc, null);
 2841                   if (failed != null && !failed.isEmpty())
 2842                       exceps = add(exceps, newObjectNotFoundException(failed));
 2843   
 2844                   // perform post-refresh transitions and make sure all fetch
 2845                   // group fields are loaded
 2846                   for (Iterator itr = load.iterator(); itr.hasNext();) {
 2847                       sm = (StateManagerImpl) itr.next();
 2848                       if (failed != null && failed.contains(sm.getId()))
 2849                           continue;
 2850   
 2851                       try {
 2852                           sm.afterRefresh();
 2853                           sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null, 
 2854                               false);
 2855                       } catch (OpenJPAException ke) {
 2856                           exceps = add(exceps, ke);
 2857                       }
 2858                   }
 2859               }
 2860   
 2861               // now invoke postRefresh on all the instances
 2862               for (Iterator itr = objs.iterator(); itr.hasNext();) {
 2863                   try {
 2864                       sm = getStateManagerImpl(itr.next(), true);
 2865                       if (sm != null && !sm.isDetached())
 2866                           fireLifecycleEvent(sm.getManagedInstance(), null,
 2867                               sm.getMetaData(), LifecycleEvent.AFTER_REFRESH);
 2868                   } catch (OpenJPAException ke) {
 2869                       exceps = add(exceps, ke);
 2870                   }
 2871               }
 2872           } catch (OpenJPAException ke) {
 2873               throw ke;
 2874           } catch (RuntimeException re) {
 2875               throw new GeneralException(re);
 2876           }
 2877           throwNestedExceptions(exceps, false);
 2878       }
 2879   
 2880       /**
 2881        * Optimization for single-object refresh.
 2882        */
 2883       protected void refreshInternal(Object obj, OpCallbacks call) {
 2884           try {
 2885               StateManagerImpl sm = getStateManagerImpl(obj, true);
 2886               if ((processArgument(OpCallbacks.OP_REFRESH, obj, sm, call)
 2887                   & OpCallbacks.ACT_RUN) == 0)
 2888                   return;
 2889   
 2890               if (sm != null) {
 2891                   if (sm.isDetached())
 2892                       throw newDetachedException(obj, "refresh");
 2893                   else if (sm.beforeRefresh(false)) {
 2894                       sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null, false);
 2895                       sm.afterRefresh();
 2896                   }
 2897                   fireLifecycleEvent(sm.getManagedInstance(), null,
 2898                       sm.getMetaData(), LifecycleEvent.AFTER_REFRESH);
 2899               } else if (assertPersistenceCapable(obj).pcIsDetached()
 2900                   == Boolean.TRUE)
 2901                   throw newDetachedException(obj, "refresh");
 2902           } catch (OpenJPAException ke) {
 2903               throw ke;
 2904           } catch (RuntimeException re) {
 2905               throw new GeneralException(re);
 2906           }
 2907       }
 2908   
 2909       public void retrieveAll(Collection objs, boolean dfgOnly,
 2910           OpCallbacks call) {
 2911           if (objs.isEmpty())
 2912               return;
 2913           if (objs.size() == 1) {
 2914               retrieve(objs.iterator().next(), dfgOnly, call);
 2915               return;
 2916           }
 2917   
 2918           List exceps = null;
 2919           beginOperation(true);
 2920           try {
 2921               assertOpen();
 2922               assertNontransactionalRead();
 2923   
 2924               // collect all hollow instances for load
 2925               Object obj;
 2926               Collection load = null;
 2927               StateManagerImpl sm;
 2928               Collection sms = new ArrayList(objs.size());
 2929               for (Iterator itr = objs.iterator(); itr.hasNext();) {
 2930                   obj = itr.next();
 2931                   if (obj == null)
 2932                       continue;
 2933   
 2934                   try {
 2935                       sm = getStateManagerImpl(obj, true);
 2936                       if ((processArgument(OpCallbacks.OP_RETRIEVE, obj, sm, call)
 2937                           & OpCallbacks.ACT_RUN) == 0)
 2938                           continue;
 2939   
 2940                       if (sm != null) {
 2941                           if (sm.isDetached())
 2942                               throw newDetachedException(obj, "retrieve");
 2943                           if (sm.isPersistent()) {
 2944                               sms.add(sm);
 2945                               if (sm.getPCState() == PCState.HOLLOW) {
 2946                                   if (load == null)
 2947                                       load = new ArrayList();
 2948                                   load.add(sm);
 2949                               }
 2950                           }
 2951                       } else if (assertPersistenceCapable(obj).pcIsDetached()
 2952                           == Boolean.TRUE)
 2953                           throw newDetachedException(obj, "retrieve");
 2954                   } catch (UserException ue) {
 2955                       exceps = add(exceps, ue);
 2956                   }
 2957               }
 2958   
 2959               // load all hollow instances
 2960               Collection failed = null;
 2961               if (load != null) {
 2962                   int mode = (dfgOnly) ? _store.FORCE_LOAD_DFG
 2963                       : _store.FORCE_LOAD_ALL;
 2964                   failed = _store.loadAll(load, null, mode, _fc, null);
 2965                   if (failed != null && !failed.isEmpty())
 2966                       exceps = add(exceps, newObjectNotFoundException(failed));
 2967               }
 2968   
 2969               // retrieve all non-failed instances
 2970               for (Iterator itr = sms.iterator(); itr.hasNext();) {
 2971                   sm = (StateManagerImpl) itr.next();
 2972                   if (failed != null && failed.contains(sm.getId()))
 2973                       continue;
 2974   
 2975                   int mode = (dfgOnly) ? StateManagerImpl.LOAD_FGS
 2976                       : StateManagerImpl.LOAD_ALL;
 2977                   try {
 2978                       sm.beforeRead(-1);
 2979                       sm.load(_fc, mode, null, null, false);
 2980                   } catch (OpenJPAException ke) {
 2981                       exceps = add(exceps, ke);
 2982                   }
 2983               }
 2984           } catch (OpenJPAException ke) {
 2985               throw ke;
 2986           } catch (RuntimeException re) {
 2987               throw new GeneralException(re);
 2988           } finally {
 2989               endOperation();
 2990           }
 2991           throwNestedExceptions(exceps, false);
 2992       }
 2993   
 2994       public void retrieve(Object obj, boolean dfgOnly, OpCallbacks call) {
 2995           if (obj == null)
 2996               return;
 2997   
 2998           beginOperation(true);
 2999           try {
 3000               assertOpen();
 3001               assertNontransactionalRead();
 3002   
 3003               StateManagerImpl sm = getStateManagerImpl(obj, true);
 3004               if ((processArgument(OpCallbacks.OP_RETRIEVE, obj, sm, call)
 3005                   & OpCallbacks.ACT_RUN) == 0)
 3006                   return;
 3007   
 3008               if (sm != null) {
 3009                   if (sm.isDetached())
 3010                       throw newDetachedException(obj, "retrieve");
 3011                   if (sm.isPersistent()) {
 3012                       int mode = (dfgOnly) ? StateManagerImpl.LOAD_FGS
 3013                           : StateManagerImpl.LOAD_ALL;
 3014                       sm.beforeRead(-1);
 3015                       sm.load(_fc, mode, null, null, false);
 3016                   }
 3017               } else if (assertPersistenceCapable(obj).pcIsDetached()
 3018                   == Boolean.TRUE)
 3019                   throw newDetachedException(obj, "retrieve");
 3020           } catch (OpenJPAException ke) {
 3021               throw ke;
 3022           } catch (RuntimeException re) {
 3023               throw new GeneralException(re);
 3024           } finally {
 3025               endOperation();
 3026           }
 3027       }
 3028   
 3029       public void evictAll(OpCallbacks call) {
 3030           beginOperation(false);
 3031           try {
 3032               // evict all PClean and PNonTrans objects
 3033               Collection c = getManagedStates();
 3034               StateManagerImpl sm;
 3035               for (Iterator itr = c.iterator(); itr.hasNext();) {
 3036                   sm = (StateManagerImpl) itr.next();
 3037                   if (sm.isPersistent() && !sm.isDirty())
 3038                       evict(sm.getManagedInstance(), call);
 3039               }
 3040           }
 3041           finally {
 3042               endOperation();
 3043           }
 3044       }
 3045   
 3046       public void evictAll(Collection objs, OpCallbacks call) {
 3047           List exceps = null;
 3048           beginOperation(false);
 3049           try {
 3050               for (Iterator itr = objs.iterator(); itr.hasNext();) {
 3051                   try {
 3052                       evict(itr.next(), call);
 3053                   } catch (UserException ue) {
 3054                       exceps = add(exceps, ue);
 3055                   }
 3056               }
 3057           } finally {
 3058               endOperation();
 3059           }
 3060           throwNestedExceptions(exceps, false);
 3061       }
 3062   
 3063       public void evictAll(Extent extent, OpCallbacks call) {
 3064           if (extent == null)
 3065               return;
 3066   
 3067           beginOperation(false);
 3068           try {
 3069               // evict all PClean and PNonTrans objects in extent
 3070               Collection c = getManagedStates();
 3071               StateManagerImpl sm;
 3072               Class cls;
 3073               for (Iterator itr = c.iterator(); itr.hasNext();) {
 3074                   sm = (StateManagerImpl) itr.next();
 3075                   if (sm.isPersistent() && !sm.isDirty()) {
 3076                       cls = sm.getMetaData().getDescribedType();
 3077                       if (cls == extent.getElementType()
 3078                           || (extent.hasSubclasses()
 3079                           && extent.getElementType().isAssignableFrom(cls)))
 3080                           evict(sm.getManagedInstance(), call);
 3081                   }
 3082               }
 3083           } finally {
 3084               endOperation();
 3085           }
 3086       }
 3087   
 3088       public void evict(Object obj, OpCallbacks call) {
 3089           if (obj == null)
 3090               return;
 3091   
 3092           beginOperation(false);
 3093           try {
 3094               StateManagerImpl sm = getStateManagerImpl(obj, true);
 3095               if ((processArgument(OpCallbacks.OP_EVICT, obj, sm, call)
 3096                   & OpCallbacks.ACT_RUN) == 0)
 3097                   return;
 3098               if (sm == null)
 3099                   return;
 3100   
 3101               sm.evict();
 3102               if (_evictDataCache && sm.getObjectId() != null) {
 3103                   DataCache cache = sm.getMetaData().getDataCache();
 3104                   if (cache != null)
 3105                       cache.remove(sm.getObjectId());
 3106               }
 3107           }
 3108           catch (OpenJPAException ke) {
 3109               throw ke;
 3110           } catch (RuntimeException re) {
 3111               throw new GeneralException(re);
 3112           } finally {
 3113               endOperation();
 3114           }
 3115       }
 3116   
 3117       public Object detach(Object obj, OpCallbacks call) {
 3118           if (obj == null)
 3119               return null;
 3120           if (call == null)
 3121               call = _call;
 3122   
 3123           beginOperation(true);
 3124           try {
 3125               return new DetachManager(this, false, call).detach(obj);
 3126           } catch (OpenJPAException ke) {
 3127               throw ke;
 3128           } catch (RuntimeException re) {
 3129               throw new GeneralException(re);
 3130           } finally {
 3131               endOperation();
 3132           }
 3133       }
 3134   
 3135       public Object[] detachAll(Collection objs, OpCallbacks call) {
 3136           if (objs == null)
 3137               return null;
 3138           if (objs.isEmpty())
 3139               return EMPTY_OBJECTS;
 3140           if (call == null)
 3141               call = _call;
 3142   
 3143           beginOperation(true);
 3144           try {
 3145               return new DetachManager(this, false, call).detachAll(objs);
 3146           } catch (OpenJPAException ke) {
 3147               throw ke;
 3148           } catch (RuntimeException re) {
 3149               throw new GeneralException(re);
 3150           } finally {
 3151               endOperation();
 3152           }
 3153       }
 3154   
 3155       public void detachAll(OpCallbacks call) {
 3156           detachAll(call, true);
 3157       }
 3158   
 3159       public void detachAll(OpCallbacks call, boolean flush) {
 3160           beginOperation(true);
 3161           try {
 3162               // If a flush is desired (based on input parm), then check if the
 3163               //   "dirty" flag is set before calling flush().
 3164               if ((flush) && ((_flags & FLAG_FLUSH_REQUIRED) != 0))
 3165                   flush();
 3166               detachAllInternal(call);
 3167           } catch (OpenJPAException ke) {
 3168               throw ke;
 3169           } catch (RuntimeException re) {
 3170               throw new GeneralException(re);
 3171           } finally {
 3172               endOperation();
 3173           }
 3174       }
 3175   
 3176       private void detachAllInternal(OpCallbacks call) {
 3177           Collection states = getManagedStates();
 3178           StateManagerImpl sm;
 3179           for (Iterator itr = states.iterator(); itr.hasNext();) {
 3180               sm = (StateManagerImpl) itr.next();
 3181               if (!sm.isPersistent())
 3182                   itr.remove();
 3183               else if (!sm.getMetaData().isDetachable()) {
 3184                   sm.release(true);
 3185                   itr.remove();
 3186               }
 3187           }
 3188           if (states.isEmpty())
 3189               return;
 3190   
 3191           if (call == null)
 3192               call = _call;
 3193           new DetachManager(this, true, call).detachAll
 3194               (new ManagedObjectCollection(states));
 3195       }
 3196   
 3197       public Object attach(Object obj, boolean copyNew, OpCallbacks call) {
 3198           if (obj == null)
 3199               return null;
 3200   
 3201           beginOperation(true);
 3202           try {
 3203               // make sure not to try to set rollback only if this fails
 3204               assertWriteOperation();
 3205               try {
 3206                   return new AttachManager(this, copyNew, call).attach(obj);
 3207               } catch (OptimisticException oe) {
 3208                   setRollbackOnly(oe);
 3209                   throw oe.setFatal(true);
 3210               } catch (OpenJPAException ke) {
 3211                   throw ke;
 3212               } catch (RuntimeException re) {
 3213                   throw new GeneralException(re);
 3214               }
 3215           }
 3216           finally {
 3217               endOperation();
 3218           }
 3219       }
 3220   
 3221       public Object[] attachAll(Collection objs, boolean copyNew,
 3222           OpCallbacks call) {
 3223           if (objs == null)
 3224               return null;
 3225           if (objs.isEmpty())
 3226               return EMPTY_OBJECTS;
 3227   
 3228           beginOperation(true);
 3229           try {
 3230               // make sure not to try to set rollback only if this fails
 3231               assertWriteOperation();
 3232               try {
 3233                   return new AttachManager(this, copyNew, call).attachAll(objs);
 3234               } catch (OptimisticException oe) {
 3235                   setRollbackOnly(oe);
 3236                   throw oe.setFatal(true);
 3237               } catch (OpenJPAException ke) {
 3238                   throw ke;
 3239               } catch (RuntimeException re) {
 3240                   throw new GeneralException(re);
 3241               }
 3242           }
 3243           finally {
 3244               endOperation();
 3245           }
 3246       }
 3247   
 3248       public void nontransactionalAll(Collection objs, OpCallbacks call) {
 3249           beginOperation(true);
 3250           try {
 3251               List exceps = null;
 3252               for (Iterator itr = objs.iterator(); itr.hasNext();) {
 3253                   try {
 3254                       nontransactional(itr.next(), call);
 3255                   } catch (UserException ue) {
 3256                       exceps = add(exceps, ue);
 3257                   }
 3258               }
 3259               throwNestedExceptions(exceps, false);
 3260           } finally {
 3261               endOperation();
 3262           }
 3263       }
 3264   
 3265       public void nontransactional(Object obj, OpCallbacks call) {
 3266           if (obj == null)
 3267               return;
 3268   
 3269           beginOperation(true);
 3270           try {
 3271               StateManagerImpl sm = getStateManagerImpl(obj, true);
 3272               if ((processArgument(OpCallbacks.OP_NONTRANSACTIONAL, obj, sm, call)
 3273                   & OpCallbacks.ACT_RUN) == 0)
 3274                   return;
 3275               if (sm != null)
 3276                   sm.nontransactional();
 3277           } catch (OpenJPAException ke) {
 3278               throw ke;
 3279           } catch (RuntimeException re) {
 3280               throw new GeneralException(re);
 3281           } finally {
 3282               endOperation();
 3283           }
 3284       }
 3285   
 3286       /**
 3287        * Make the given instances transactional.
 3288        */
 3289       public void transactionalAll(Collection objs, boolean updateVersion,
 3290           OpCallbacks call) {
 3291           if (objs.isEmpty())
 3292               return;
 3293           if (objs.size() == 1) {
 3294               transactional(objs.iterator().next(), updateVersion, call);
 3295               return;
 3296           }
 3297   
 3298           beginOperation(true);
 3299           try {
 3300               // collect all hollow instances for load, and make unmananged
 3301               // instances transient-transactional
 3302               Collection load = null;
 3303               Object obj;
 3304               StateManagerImpl sm;
 3305               ClassMetaData meta;
 3306               Collection sms = new ArrayList(objs.size());
 3307               List exceps = null;
 3308               for (Iterator itr = objs.iterator(); itr.hasNext();) {
 3309                   obj = itr.next();
 3310                   if (obj == null)
 3311                       continue;
 3312   
 3313                   try {
 3314                       sm = getStateManagerImpl(obj, true);
 3315                       if ((processArgument(OpCallbacks.OP_TRANSACTIONAL, obj, sm,
 3316                           call) & OpCallbacks.ACT_RUN) == 0)
 3317                           continue;
 3318   
 3319                       if (sm == null) {
 3320                           // manage transient instance
 3321                           meta = _conf.getMetaDataRepositoryInstance().
 3322                               getMetaData(obj.getClass(), _loader, true);
 3323   
 3324                           sm = new StateManagerImpl
 3325                               (StateManagerId.newInstance(this), meta, this);
 3326                           sm.initialize(assertPersistenceCapable(obj),
 3327                               PCState.TCLEAN);
 3328                       } else if (sm.isPersistent()) {
 3329                           assertActiveTransaction();
 3330                           sms.add(sm);
 3331                           if (sm.getPCState() == PCState.HOLLOW) {
 3332                               if (load == null)
 3333                                   load = new ArrayList();
 3334                               load.add(sm);
 3335                           }
 3336   
 3337                           sm.setCheckVersion(true);
 3338                           if (updateVersion)
 3339                               sm.setUpdateVersion(true);
 3340                           _flags |= FLAG_FLUSH_REQUIRED; // version check/up
 3341                       }
 3342                   }
 3343                   catch (UserException ue) {
 3344                       exceps = add(exceps, ue);
 3345                   }
 3346               }
 3347   
 3348               // load all hollow instances
 3349               Collection failed = null;
 3350               if (load != null) {
 3351                   failed = _store.loadAll(load, null, _store.FORCE_LOAD_NONE,
 3352                       _fc, null);
 3353                   if (failed != null && !failed.isEmpty())
 3354                       exceps = add(exceps,
 3355                           newObjectNotFoundException(failed));
 3356               }
 3357   
 3358               transactionalStatesAll(sms, failed, exceps);
 3359           } catch (OpenJPAException ke) {
 3360               throw ke;
 3361           } catch (RuntimeException re) {
 3362               throw new GeneralException(re);
 3363           } finally {
 3364               endOperation();
 3365           }
 3366       }
 3367   
 3368       /**
 3369        * Make the given instances transactional.
 3370        */
 3371       public void transactional(Object obj, boolean updateVersion,
 3372           OpCallbacks call) {
 3373           if (obj == null)
 3374               return;
 3375   
 3376           beginOperation(true);
 3377           try {
 3378               StateManagerImpl sm = getStateManagerImpl(obj, true);
 3379               if ((processArgument(OpCallbacks.OP_TRANSACTIONAL, obj, sm, call)
 3380                   & OpCallbacks.ACT_RUN) == 0)
 3381                   return;
 3382   
 3383               if (sm != null && sm.isPersistent()) {
 3384                   assertActiveTransaction();
 3385                   sm.transactional();
 3386                   sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null, false);
 3387                   sm.setCheckVersion(true);
 3388                   if (updateVersion)
 3389                       sm.setUpdateVersion(true);
 3390                   _flags |= FLAG_FLUSH_REQUIRED; // version check/up
 3391               } else if (sm == null) {
 3392                   // manage transient instance
 3393                   ClassMetaData meta = _conf.getMetaDataRepositoryInstance().
 3394                       getMetaData(obj.getClass(), _loader, true);
 3395                   Object id = StateManagerId.newInstance(this);
 3396                   sm = new StateManagerImpl(id, meta, this);
 3397                   sm.initialize(assertPersistenceCapable(obj),
 3398                       PCState.TCLEAN);
 3399               }
 3400           }
 3401           catch (OpenJPAException ke) {
 3402               throw ke;
 3403           } catch (RuntimeException re) {
 3404               throw new GeneralException(re);
 3405           } finally {
 3406               endOperation();
 3407           }
 3408       }
 3409   
 3410       /**
 3411        * Transition the given state managers to transactional.
 3412        */
 3413       private void transactionalStatesAll(Collection sms, Collection failed,
 3414           List exceps) {
 3415           // make instances transactional and make sure they are loaded
 3416           StateManagerImpl sm;
 3417           for (Iterator itr = sms.iterator(); itr.hasNext();) {
 3418               sm = (StateManagerImpl) itr.next();
 3419               if (failed != null && failed.contains(sm.getId()))
 3420                   continue;
 3421   
 3422               try {
 3423                   sm.transactional();
 3424                   sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null, false);
 3425               } catch (OpenJPAException ke) {
 3426                   exceps = add(exceps, ke);
 3427               }
 3428           }
 3429           throwNestedExceptions(exceps, false);
 3430       }
 3431   
 3432       /////////////////
 3433       // Extent, Query
 3434       /////////////////
 3435   
 3436       public Extent newExtent(Class type, boolean subclasses) {
 3437           return newExtent(type, subclasses, null);
 3438       }
 3439   
 3440       private Extent newExtent(Class type, boolean subclasses,
 3441           FetchConfiguration fetch) {
 3442           beginOperation(true);
 3443           try {
 3444               ExtentImpl extent = new ExtentImpl(this, type, subclasses, fetch);
 3445               if (_extents == null)
 3446                   _extents = new ReferenceHashSet(ReferenceHashSet.WEAK);
 3447               _extents.add(extent);
 3448   
 3449               return extent;
 3450           } catch (OpenJPAException ke) {
 3451               throw ke;
 3452           } catch (RuntimeException re) {
 3453               throw new GeneralException(re);
 3454           } finally {
 3455               endOperation();
 3456           }
 3457       }
 3458   
 3459       public Iterator extentIterator(Class type, boolean subclasses,
 3460           FetchConfiguration fetch, boolean ignoreChanges) {
 3461           Extent extent = newExtent(type, subclasses, fetch);
 3462           extent.setIgnoreChanges(ignoreChanges);
 3463           return extent.iterator();
 3464       }
 3465   
 3466       public Query newQuery(String lang, Class cls, Object query) {
 3467           Query q = newQuery(lang, query);
 3468           q.setCandidateType(cls, true);
 3469           return q;
 3470       }
 3471   
 3472       public Query newQuery(String lang, Object query) {
 3473           // common mistakes
 3474           if (query instanceof Extent || query instanceof Class)
 3475               throw new UserException(_loc.get("bad-new-query"));
 3476   
 3477           beginOperation(false);
 3478           try {
 3479               StoreQuery sq = _store.newQuery(lang);
 3480               if (sq == null) {
 3481                   ExpressionParser ep = QueryLanguages.parserForLanguage(lang);
 3482                   if (ep != null)
 3483                       sq = new ExpressionStoreQuery(ep);
 3484                   else if (QueryLanguages.LANG_METHODQL.equals(lang))
 3485                       sq = new MethodStoreQuery();
 3486                   else
 3487                       throw new UnsupportedException(lang);
 3488               }
 3489   
 3490               Query q = newQueryImpl(lang, sq);
 3491               q.setIgnoreChanges(_ignoreChanges);
 3492               if (query != null)
 3493                   q.setQuery(query);
 3494   
 3495               // track queries
 3496               if (_queries == null)
 3497                   _queries = new ReferenceHashSet(ReferenceHashSet.WEAK);
 3498               _queries.add(q);
 3499               return q;
 3500           } catch (OpenJPAException ke) {
 3501               throw ke;
 3502           } catch (RuntimeException re) {
 3503               throw new GeneralException(re);
 3504           } finally {
 3505               endOperation();
 3506           }
 3507       }
 3508   
 3509       /**
 3510        * Create a new query.
 3511        */
 3512       protected QueryImpl newQueryImpl(String lang, StoreQuery sq) {
 3513           return new QueryImpl(this, lang, sq);
 3514       }
 3515   
 3516       public Seq getIdentitySequence(ClassMetaData meta) {
 3517           if (meta == null)
 3518               return null;
 3519           return getSequence(meta, null);
 3520       }
 3521   
 3522       public Seq getValueSequence(FieldMetaData fmd) {
 3523           if (fmd == null)
 3524               return null;
 3525           return getSequence(fmd.getDefiningMetaData(), fmd);
 3526       }
 3527   
 3528       /**
 3529        * Return a sequence for the given class and optional field.
 3530        */
 3531       private Seq getSequence(ClassMetaData meta, FieldMetaData fmd) {
 3532           // get sequence strategy from metadata
 3533           int strategy;
 3534           if (fmd == null)
 3535               strategy = meta.getIdentityStrategy();
 3536           else
 3537               strategy = fmd.getValueStrategy();
 3538   
 3539           // we can handle non-native strategies without the store manager
 3540           switch (strategy) {
 3541               case ValueStrategies.UUID_HEX:
 3542                   return UUIDHexSeq.getInstance();
 3543               case ValueStrategies.UUID_STRING:
 3544                   return UUIDStringSeq.getInstance();
 3545               case ValueStrategies.SEQUENCE:
 3546                   SequenceMetaData smd = (fmd == null)
 3547                       ? meta.getIdentitySequenceMetaData()
 3548                       : fmd.getValueSequenceMetaData();
 3549                   return smd.getInstance(_loader);
 3550               default:
 3551                   // use store manager for native sequence
 3552                   if (fmd == null) {
 3553                       // this will return a sequence even for app id classes,
 3554                       // which is what we want for backwards-compatibility
 3555                       return _store.getDataStoreIdSequence(meta);
 3556                   }
 3557                   return _store.getValueSequence(fmd);
 3558           }
 3559       }
 3560   
 3561       ///////////
 3562       // Locking
 3563       ///////////
 3564   
 3565       public void lock(Object obj, OpCallbacks call) {
 3566           if (obj == null)
 3567               return;
 3568   
 3569           beginOperation(true); // have to sync or lock level always NONE
 3570           try {
 3571               lock(obj, _fc.getWriteLockLevel(), _fc.getLockTimeout(), call);
 3572           } finally {
 3573               endOperation();
 3574           }
 3575       }
 3576   
 3577       public void lock(Object obj, int level, int timeout, OpCallbacks call) {
 3578           if (obj == null)
 3579               return;
 3580   
 3581           beginOperation(true);
 3582           try {
 3583               assertActiveTransaction();
 3584   
 3585               StateManagerImpl sm = getStateManagerImpl(obj, true);
 3586               if ((processArgument(OpCallbacks.OP_LOCK, obj, sm, call)
 3587                   & OpCallbacks.ACT_RUN) == 0)
 3588                   return;
 3589               if (sm == null || !sm.isPersistent())
 3590                   return;
 3591   
 3592               _lm.lock(sm, level, timeout, null);
 3593               sm.readLocked(level, level); // use same level for future write
 3594           } catch (OpenJPAException ke) {
 3595               throw ke;
 3596           } catch (RuntimeException re) {
 3597               throw new GeneralException(re);
 3598           } finally {
 3599               endOperation();
 3600           }
 3601       }
 3602   
 3603       public void lockAll(Collection objs, OpCallbacks call) {
 3604           if (objs.isEmpty())
 3605               return;
 3606   
 3607           beginOperation(true); // have to sync or lock level always NONE
 3608           try {
 3609               lockAll(objs, _fc.getWriteLockLevel(), _fc.getLockTimeout(),
 3610                   call);
 3611           } finally {
 3612               endOperation();
 3613           }
 3614       }
 3615   
 3616       public void lockAll(Collection objs, int level, int timeout,
 3617           OpCallbacks call) {
 3618           if (objs.isEmpty())
 3619               return;
 3620           if (objs.size() == 1) {
 3621               lock(objs.iterator().next(), level, timeout, call);
 3622               return;
 3623           }
 3624   
 3625           beginOperation(true);
 3626           try {
 3627               assertActiveTransaction();
 3628   
 3629               Collection sms = new ArrayList(objs.size());
 3630               Object obj;
 3631               StateManagerImpl sm;
 3632               for (Iterator itr = objs.iterator(); itr.hasNext();) {
 3633                   obj = itr.next();
 3634                   if (obj == null)
 3635                       continue;
 3636   
 3637                   sm = getStateManagerImpl(obj, true);
 3638                   if ((processArgument(OpCallbacks.OP_LOCK, obj, sm, call)
 3639                       & OpCallbacks.ACT_RUN) == 0)
 3640                       continue;
 3641                   if (sm != null && sm.isPersistent())
 3642                       sms.add(sm);
 3643               }
 3644   
 3645               _lm.lockAll(sms, level, timeout, null);
 3646               for (Iterator itr = sms.iterator(); itr.hasNext();)
 3647                   ((StateManagerImpl) itr.next()).readLocked(level, level);
 3648           } catch (OpenJPAException ke) {
 3649               throw ke;
 3650           } catch (RuntimeException re) {
 3651               throw new GeneralException(re);
 3652           } finally {
 3653               endOperation();
 3654           }
 3655       }
 3656   
 3657       //////////////
 3658       // Connection
 3659       //////////////
 3660   
 3661       public boolean cancelAll() {
 3662           // this method does not lock, since we want to allow a different
 3663           // thread to be able to cancel on a locked-up persistence manager
 3664   
 3665           assertOpen();
 3666           try {
 3667               // if we're flushing, have to set rollback only -- do this before we
 3668               // attempt to cancel, because otherwise the cancel might case the
 3669               // transaction to complete before we have a chance to set the
 3670               // rollback only flag
 3671               if ((_flags & FLAG_STORE_FLUSHING) != 0)
 3672                   setRollbackOnlyInternal(new UserException());
 3673               return _store.cancelAll();
 3674           } catch (OpenJPAException ke) {
 3675               throw ke;
 3676           } catch (RuntimeException re) {
 3677               throw new StoreException(re);
 3678           }
 3679       }
 3680   
 3681       public Object getConnection() {
 3682           assertOpen();
 3683           if (!_conf.supportedOptions().contains
 3684               (_conf.OPTION_DATASTORE_CONNECTION))
 3685               throw new UnsupportedException(_loc.get("conn-not-supported"));
 3686   
 3687           return _store.getClientConnection();
 3688       }
 3689   
 3690       public boolean hasConnection() {
 3691           assertOpen();
 3692           return (_flags & FLAG_RETAINED_CONN) != 0;
 3693       }
 3694   
 3695       /**
 3696        * Tell store to retain connection if we haven't already.
 3697        */
 3698       private void retainConnection() {
 3699           if ((_flags & FLAG_RETAINED_CONN) == 0) {
 3700               _store.retainConnection();
 3701               _flags |= FLAG_RETAINED_CONN;
 3702           }
 3703       }
 3704   
 3705       /**
 3706        * Tell store to release connection if we have retained one.
 3707        */
 3708       private void releaseConnection() {
 3709           if ((_flags & FLAG_RETAINED_CONN) != 0) {
 3710               _store.releaseConnection();
 3711               _flags &= ~FLAG_RETAINED_CONN;
 3712           }
 3713       }
 3714   
 3715       /////////
 3716       // Cache
 3717       /////////
 3718   
 3719       public Collection getManagedObjects() {
 3720           beginOperation(false);
 3721           try {
 3722               return new ManagedObjectCollection(getManagedStates());
 3723           } finally {
 3724               endOperation();
 3725           }
 3726       }
 3727   
 3728       public Collection getTransactionalObjects() {
 3729           beginOperation(false);
 3730           try {
 3731               return new ManagedObjectCollection(getTransactionalStates());
 3732           } finally {
 3733               endOperation();
 3734           }
 3735       }
 3736   
 3737       public Collection getPendingTransactionalObjects() {
 3738           beginOperation(false);
 3739           try {
 3740               return new ManagedObjectCollection
 3741                   (getPendingTransactionalStates());
 3742           } finally {
 3743               endOperation();
 3744           }
 3745       }
 3746   
 3747       public Collection getDirtyObjects() {
 3748           beginOperation(false);
 3749           try {
 3750               return new ManagedObjectCollection(getDirtyStates());
 3751           } finally {
 3752               endOperation();
 3753           }
 3754       }
 3755   
 3756       public boolean getOrderDirtyObjects() {
 3757           return _orderDirty;
 3758       }
 3759   
 3760       public void setOrderDirtyObjects(boolean order) {
 3761           _orderDirty = order;
 3762       }
 3763   
 3764       /**
 3765        * Return a copy of all managed state managers.
 3766        */
 3767       protected Collection getManagedStates() {
 3768           return _cache.copy();
 3769       }
 3770   
 3771       /**
 3772        * Return a copy of all transactional state managers.
 3773        */
 3774       protected Collection getTransactionalStates() {
 3775           if (!hasTransactionalObjects())
 3776               return Collections.EMPTY_LIST;
 3777           return _transCache.copy();
 3778       }
 3779   
 3780       /**
 3781        * Whether or not there are any transactional objects in the current
 3782        * persistence context. If there are any instances with untracked state,
 3783        * this method will cause those instances to be scanned.
 3784        */
 3785       private boolean hasTransactionalObjects() {
 3786           _cache.dirtyCheck();
 3787           return _transCache != null;
 3788       }
 3789   
 3790       /**
 3791        * Return a copy of all dirty state managers.
 3792        */
 3793       protected Collection getDirtyStates() {
 3794           if (!hasTransactionalObjects())
 3795               return Collections.EMPTY_LIST;
 3796   
 3797           return _transCache.copyDirty();
 3798       }
 3799   
 3800       /**
 3801        * Return a copy of all state managers which will become
 3802        * transactional upon the next transaction.
 3803        */
 3804       protected Collection getPendingTransactionalStates() {
 3805           if (_pending == null)
 3806               return Collections.EMPTY_LIST;
 3807           return new ArrayList(_pending);
 3808       }
 3809   
 3810       /**
 3811        * Set the cached StateManager for the instance that had the given oid.
 3812        * This method must not be called multiple times for new instances.
 3813        *
 3814        * @param id the id previously used by the instance
 3815        * @param sm the state manager for the instance; if the state
 3816        * manager is transient, we'll stop managing the instance;
 3817        * if it has updated its oid, we'll re-cache under the new oid
 3818        * @param status one of our STATUS constants describing why we're
 3819        * setting the state manager
 3820        */
 3821       void setStateManager(Object id, StateManagerImpl sm, int status) {
 3822           lock();
 3823           try {
 3824               switch (status) {
 3825                   case STATUS_INIT:
 3826                       _cache.add(sm);
 3827                       break;
 3828                   case STATUS_TRANSIENT:
 3829                       _cache.remove(id, sm);
 3830                       break;
 3831                   case STATUS_OID_ASSIGN:
 3832                       assignObjectId(_cache, id, sm);
 3833                       break;
 3834                   case STATUS_COMMIT_NEW:
 3835                       _cache.commitNew(id, sm);
 3836                       break;
 3837                   default:
 3838                       throw new InternalException();
 3839               }
 3840           }
 3841           finally {
 3842               unlock();
 3843           }
 3844       }
 3845   
 3846       /**
 3847        * Notify the broker that the given state manager should
 3848        * be added to the set of instances involved in the current transaction.
 3849        */
 3850       void addToTransaction(StateManagerImpl sm) {
 3851           // we only add clean instances now; dirty instances are added in
 3852           // the setDirty callback
 3853           if (sm.isDirty())
 3854               return;
 3855   
 3856           lock();
 3857           try {
 3858               if (!hasTransactionalObjects())
 3859                   _transCache = new TransactionalCache(_orderDirty);
 3860               _transCache.addClean(sm);
 3861           } finally {
 3862               unlock();
 3863           }
 3864       }
 3865   
 3866       /**
 3867        * Notify the persistence manager that the given state manager should
 3868        * be removed from the set of instances involved in the current transaction.
 3869        */
 3870       void removeFromTransaction(StateManagerImpl sm) {
 3871           lock();
 3872           try {
 3873               if (_transCache != null)
 3874                   // intentional direct access; we don't want to recompute
 3875                   // dirtiness while removing instances from the transaction
 3876                   _transCache.remove(sm);
 3877               if (_derefCache != null && !sm.isPersistent())
 3878                   _derefCache.remove(sm);
 3879           } finally {
 3880               unlock();
 3881           }
 3882       }
 3883   
 3884       /**
 3885        * Notification that the given instance has been dirtied. This
 3886        * notification is given when an object first transitions to a dirty state,
 3887        * and every time the object is modified by the user thereafter.
 3888        */
 3889       void setDirty(StateManagerImpl sm, boolean firstDirty) {
 3890           if (sm.isPersistent())
 3891               _flags |= FLAG_FLUSH_REQUIRED;
 3892   
 3893           if (_savepoints != null && !_savepoints.isEmpty()) {
 3894               if (_savepointCache == null)
 3895                   _savepointCache = new HashSet();
 3896               _savepointCache.add(sm);
 3897           }
 3898   
 3899           if (firstDirty && sm.isTransactional()) {
 3900               lock();
 3901               try {
 3902                   // cache dirty instance
 3903                   if (!hasTransactionalObjects())
 3904                       _transCache = new TransactionalCache(_orderDirty);
 3905                   _transCache.addDirty(sm);
 3906   
 3907                   // also record that the class is dirty
 3908                   if (sm.isNew()) {
 3909                       if (_persistedClss == null)
 3910                           _persistedClss = new HashSet();
 3911                       _persistedClss.add(sm.getMetaData().getDescribedType());
 3912                   } else if (sm.isDeleted()) {
 3913                       if (_deletedClss == null)
 3914                           _deletedClss = new HashSet();
 3915                       _deletedClss.add(sm.getMetaData().getDescribedType());
 3916                   } else {
 3917                       if (_updatedClss == null)
 3918                           _updatedClss = new HashSet();
 3919                       _updatedClss.add(sm.getMetaData().getDescribedType());
 3920                   }
 3921   
 3922                   // if tracking changes and this instance wasn't already dirty,
 3923                   // add to changed set; we use this for detecting instances that
 3924                   // enter the transaction during pre store
 3925                   if ((_flags & FLAG_PRESTORING) != 0) {
 3926                       if (_transAdditions == null)
 3927                           _transAdditions = new HashSet();
 3928                       _transAdditions.add(sm);
 3929                   }
 3930               } finally {
 3931                   unlock();
 3932               }
 3933           }
 3934       }
 3935   
 3936       /**
 3937        * Notify the broker that the given state manager should
 3938        * be added to the set of instances that will become transactional
 3939        * on the next transaction
 3940        */
 3941       void addToPendingTransaction(StateManagerImpl sm) {
 3942           lock();
 3943           try {
 3944               if (_pending == null)
 3945                   _pending = new HashSet();
 3946               _pending.add(sm);
 3947           } finally {
 3948               unlock();
 3949           }
 3950       }
 3951   
 3952       /**
 3953        * Notify the persistence manager that the given state manager should
 3954        * be removed from the set of instances involved in the next transaction.
 3955        */
 3956       void removeFromPendingTransaction(StateManagerImpl sm) {
 3957           lock();
 3958           try {
 3959               if (_pending != null)
 3960                   _pending.remove(sm);
 3961               if (_derefCache != null && !sm.isPersistent())
 3962                   _derefCache.remove(sm);
 3963           } finally {
 3964               unlock();
 3965           }
 3966       }
 3967   
 3968       /**
 3969        * Add a dereferenced dependent object to the persistence manager's cache.
 3970        * On flush, these objects will be deleted.
 3971        */
 3972       void addDereferencedDependent(StateManagerImpl sm) {
 3973           lock();
 3974           try {
 3975               // if we're in the middle of flush and introducing more derefs
 3976               // via instance callbacks, add them to the special additions set
 3977               if ((_flags & FLAG_DEREFDELETING) != 0) {
 3978                   if (_derefAdditions == null)
 3979                       _derefAdditions = new HashSet();
 3980                   _derefAdditions.add(sm);
 3981               } else {
 3982                   if (_derefCache == null)
 3983                       _derefCache = new HashSet();
 3984                   _derefCache.add(sm);
 3985               }
 3986           }
 3987           finally {
 3988               unlock();
 3989           }
 3990       }
 3991   
 3992       /**
 3993        * Remove the given previously dereferenced dependent object from the
 3994        * cache. It is now referenced.
 3995        */
 3996       void removeDereferencedDependent(StateManagerImpl sm) {
 3997           lock();
 3998           try {
 3999               boolean removed = false;
 4000               if (_derefAdditions != null)
 4001                   removed = _derefAdditions.remove(sm);
 4002               if (!removed && (_derefCache == null || !_derefCache.remove(sm)))
 4003                   throw new InvalidStateException(_loc.get("not-derefed",
 4004                       Exceptions.toString(sm.getManagedInstance()))).
 4005                       setFailedObject(sm.getManagedInstance()).
 4006                       setFatal(true);
 4007           } finally {
 4008               unlock();
 4009           }
 4010       }
 4011   
 4012       public void dirtyType(Class cls) {
 4013           if (cls == null)
 4014               return;
 4015   
 4016           beginOperation(false);
 4017           try {
 4018               if (_updatedClss == null)
 4019                   _updatedClss = new HashSet();
 4020               _updatedClss.add(cls);
 4021           } finally {
 4022               endOperation();
 4023           }
 4024       }
 4025   
 4026       public Collection getPersistedTypes() {
 4027           if (_persistedClss == null || _persistedClss.isEmpty())
 4028               return Collections.EMPTY_LIST;
 4029           return Collections.unmodifiableCollection(_persistedClss);
 4030       }
 4031   
 4032       public Collection getUpdatedTypes() {
 4033           if (_updatedClss == null || _updatedClss.isEmpty())
 4034               return Collections.EMPTY_LIST;
 4035           return Collections.unmodifiableCollection(_updatedClss);
 4036       }
 4037   
 4038       public Collection getDeletedTypes() {
 4039           if (_deletedClss == null || _deletedClss.isEmpty())
 4040               return Collections.EMPTY_LIST;
 4041           return Collections.unmodifiableCollection(_deletedClss);
 4042       }
 4043   
 4044       ///////////
 4045       // Closing
 4046       ///////////
 4047   
 4048       public boolean isClosed() {
 4049           return _closed;
 4050       }
 4051   
 4052       public boolean isCloseInvoked() {
 4053           return _closed || (_flags & FLAG_CLOSE_INVOKED) != 0;
 4054       }
 4055   
 4056       public void close() {
 4057           beginOperation(false);
 4058           try {
 4059               // throw an exception if closing in an active local trans
 4060               if (!_managed && (_flags & FLAG_ACTIVE) != 0)
 4061                   throw new InvalidStateException(_loc.get("active"));
 4062   
 4063               // only close if not active; if active managed trans wait
 4064               // for completion
 4065               _flags |= FLAG_CLOSE_INVOKED;
 4066   
 4067               if ((_flags & FLAG_ACTIVE) == 0)
 4068                   free();
 4069           } finally {
 4070               endOperation();
 4071           }
 4072       }
 4073   
 4074       /**
 4075        * Free the resources used by this persistence manager.
 4076        */
 4077       protected void free() {
 4078           RuntimeException err = null;
 4079           if ((_autoDetach & DETACH_CLOSE) != 0) {
 4080               try {
 4081                   detachAllInternal(_call);
 4082               } catch (RuntimeException re) {
 4083                   err = re;
 4084               }
 4085           }
 4086   
 4087           _sync = null;
 4088           _userObjects = null;
 4089           _cache.clear();
 4090           _transCache = null;
 4091           _persistedClss = null;
 4092           _updatedClss = null;
 4093           _deletedClss = null;
 4094           _derefCache = null;
 4095           _pending = null;
 4096           _loader = null;
 4097           _transEventManager = null;
 4098           _lifeEventManager = null;
 4099   
 4100           OpenJPASavepoint save;
 4101           while (_savepoints != null && !_savepoints.isEmpty()) {
 4102               save =
 4103                   (OpenJPASavepoint) _savepoints.remove(_savepoints.size() - 1);
 4104               save.release(false);
 4105           }
 4106           _savepoints = null;
 4107           _savepointCache = null;
 4108   
 4109           if (_queries != null) {
 4110               for (Iterator itr = _queries.iterator(); itr.hasNext();) {
 4111                   try {
 4112                       ((Query) itr.next()).closeResources();
 4113                   } catch (RuntimeException re) {
 4114                   }
 4115               }
 4116               _queries = null;
 4117           }
 4118   
 4119           if (_extents != null) {
 4120               Extent e;
 4121               for (Iterator itr = _extents.iterator(); itr.hasNext();) {
 4122                   e = (Extent) itr.next();
 4123                   try {
 4124                       e.closeAll();
 4125                   } catch (RuntimeException re) {
 4126                   }
 4127               }
 4128               _extents = null;
 4129           }
 4130   
 4131           try { releaseConnection(); } catch (RuntimeException re) {}
 4132   
 4133           _lm.close();
 4134           _store.close();
 4135           _flags = 0;
 4136           _closed = true;
 4137           if (_log.isTraceEnabled())
 4138               _closedException = new IllegalStateException();
 4139   
 4140           _factory.releaseBroker(this);
 4141   
 4142           if (err != null)
 4143               throw err;
 4144       }
 4145   
 4146       ///////////////////
 4147       // Synchronization
 4148       ///////////////////
 4149   
 4150       public void lock() {
 4151           if (_lock != null)
 4152               _lock.lock();
 4153       }
 4154   
 4155       public void unlock() {
 4156           if (_lock != null)
 4157               _lock.unlock();
 4158       }
 4159   
 4160       ////////////////////
 4161       // State management
 4162       ////////////////////
 4163   
 4164       public Object newInstance(Class cls) {
 4165           assertOpen();
 4166   
 4167           if (!cls.isInterface() && Modifier.isAbstract(cls.getModifiers()))
 4168               throw new UnsupportedOperationException(_loc.get
 4169                   ("new-abstract", cls).getMessage());
 4170   
 4171           // 1.5 doesn't initialize classes without a true Class.forName
 4172           if (!PCRegistry.isRegistered(cls)) {
 4173               try {
 4174                   Class.forName(cls.getName(), true, 
 4175                       (ClassLoader) AccessController.doPrivileged(
 4176                           J2DoPrivHelper.getClassLoaderAction(cls)));
 4177               } catch (Throwable t) {
 4178               }
 4179           }
 4180   
 4181           if (_conf.getMetaDataRepositoryInstance().getMetaData(cls,
 4182               getClassLoader(), false) == null)
 4183               throw new IllegalArgumentException(
 4184                   _loc.get("no-interface-metadata", cls.getName()).getMessage());
 4185   
 4186           try {
 4187               return PCRegistry.newInstance(cls, null, false);
 4188           } catch (IllegalStateException ise) {
 4189               IllegalArgumentException iae =
 4190                   new IllegalArgumentException(ise.getMessage());
 4191               iae.setStackTrace(ise.getStackTrace());
 4192               throw iae;
 4193           }
 4194       }
 4195   
 4196       public Object getObjectId(Object obj) {
 4197           assertOpen();
 4198           if (ImplHelper.isManageable(obj))
 4199               return (ImplHelper.toPersistenceCapable(obj, _conf))
 4200                   .pcFetchObjectId();
 4201           return null;
 4202       }
 4203   
 4204       public int getLockLevel(Object o) {
 4205           assertOpen();
 4206           if (o == null)
 4207               return LockLevels.LOCK_NONE;
 4208   
 4209           OpenJPAStateManager sm = getStateManager(o);
 4210           if (sm == null)
 4211               return LockLevels.LOCK_NONE;
 4212           return getLockManager().getLockLevel(sm);
 4213       }
 4214   
 4215       public Object getVersion(Object obj) {
 4216           assertOpen();
 4217           if (ImplHelper.isManageable(obj))
 4218               return (ImplHelper.toPersistenceCapable(obj, _conf)).pcGetVersion();
 4219           return null;
 4220       }
 4221   
 4222       public boolean isDirty(Object obj) {
 4223           assertOpen();
 4224           if (ImplHelper.isManageable(obj)) {
 4225               PersistenceCapable pc = ImplHelper.toPersistenceCapable(obj, _conf);
 4226               return pc.pcIsDirty();
 4227           }
 4228           return false;
 4229       }
 4230   
 4231       public boolean isTransactional(Object obj) {
 4232           assertOpen();
 4233           if (ImplHelper.isManageable(obj))
 4234               return (ImplHelper.toPersistenceCapable(obj, _conf))
 4235                   .pcIsTransactional();
 4236           return false;
 4237       }
 4238   
 4239       public boolean isPersistent(Object obj) {
 4240           assertOpen();
 4241           if (ImplHelper.isManageable(obj))
 4242               return (ImplHelper.toPersistenceCapable(obj, _conf)).pcIsPersistent();
 4243           return false;
 4244       }
 4245   
 4246       public boolean isNew(Object obj) {
 4247           assertOpen();
 4248           if (ImplHelper.isManageable(obj))
 4249               return (ImplHelper.toPersistenceCapable(obj, _conf)).pcIsNew();
 4250           return false;
 4251       }
 4252   
 4253       public boolean isDeleted(Object obj) {
 4254           assertOpen();
 4255           if (ImplHelper.isManageable(obj))
 4256               return (ImplHelper.toPersistenceCapable(obj, _conf)).pcIsDeleted();
 4257           return false;
 4258       }
 4259   
 4260       public boolean isDetached(Object obj) {
 4261           if (!(ImplHelper.isManageable(obj)))
 4262               return false;
 4263   
 4264           PersistenceCapable pc = ImplHelper.toPersistenceCapable(obj, _conf);
 4265           Boolean detached = pc.pcIsDetached();
 4266           if (detached != null)
 4267               return detached.booleanValue();
 4268   
 4269           // last resort: instance is detached if it has a store record
 4270           ClassMetaData meta = _conf.getMetaDataRepositoryInstance().
 4271               getMetaData(ImplHelper.getManagedInstance(pc).getClass(),
 4272                   _loader, true);
 4273           Object oid = ApplicationIds.create(pc, meta);
 4274           if (oid == null)
 4275               return false;
 4276           return find(oid, null, EXCLUDE_ALL, null, 0) != null;
 4277       }
 4278   
 4279       public OpenJPAStateManager getStateManager(Object obj) {
 4280           assertOpen();
 4281           return getStateManagerImpl(obj, false);
 4282       }
 4283   
 4284       /**
 4285        * Return the state manager for the given instance, or null.
 4286        *
 4287        * @param assertThisContext if true, thow an exception if the given
 4288        * object is managed by another broker
 4289        */
 4290       protected StateManagerImpl getStateManagerImpl(Object obj,
 4291           boolean assertThisContext) {
 4292           if (ImplHelper.isManageable(obj)) {
 4293               PersistenceCapable pc = ImplHelper.toPersistenceCapable(obj, _conf);
 4294               if (pc.pcGetGenericContext() == this)
 4295                   return (StateManagerImpl) pc.pcGetStateManager();
 4296               if (assertThisContext && pc.pcGetGenericContext() != null)
 4297                   throw new UserException(_loc.get("not-managed",
 4298                       Exceptions.toString(obj))).setFailedObject(obj);
 4299           }
 4300           return null;
 4301       }
 4302   
 4303       /**
 4304        * Return the state manager for the given oid.
 4305        *
 4306        * @param allowNew if true, objects made persistent in the current
 4307        * transaction will be included in the search; if
 4308        * multiple new objects match the given oid, it is
 4309        * undefined which will be returned
 4310        */
 4311       protected StateManagerImpl getStateManagerImplById(Object oid,
 4312           boolean allowNew) {
 4313           return _cache.getById(oid, allowNew);
 4314       }
 4315   
 4316       /**
 4317        * Return the given instance as a {@link PersistenceCapable}.
 4318        * If the instance is not manageable throw the proper exception.
 4319        */
 4320       protected PersistenceCapable assertPersistenceCapable(Object obj) {
 4321           if (obj == null)
 4322               return null;
 4323           if (ImplHelper.isManageable(obj))
 4324               return ImplHelper.toPersistenceCapable(obj, _conf);
 4325   
 4326           // check for different instances of the PersistenceCapable interface
 4327           // and throw a better error that mentions the class loaders
 4328           Class[] intfs = obj.getClass().getInterfaces();
 4329           for (int i = 0; intfs != null && i < intfs.length; i++) {
 4330               if (intfs[i].getName().equals(PersistenceCapable.class.getName())) {
 4331                   throw new UserException(_loc.get("pc-loader-different",
 4332                       Exceptions.toString(obj),
 4333                       (ClassLoader) AccessController.doPrivileged(
 4334                           J2DoPrivHelper.getClassLoaderAction(
 4335                               PersistenceCapable.class)),
 4336                       (ClassLoader) AccessController.doPrivileged(
 4337                           J2DoPrivHelper.getClassLoaderAction(intfs[i]))))
 4338                       .setFailedObject(obj);
 4339               }
 4340           }
 4341   
 4342           // not enhanced
 4343           throw new UserException(_loc.get("pc-cast",
 4344               Exceptions.toString(obj))).setFailedObject(obj);
 4345       }
 4346   
 4347       /////////
 4348       // Utils
 4349       /////////
 4350       /**
 4351        * Throw an exception if the context is closed.  The exact message and
 4352        * content of the exception varies whether TRACE is enabled or not.
 4353        */
 4354       public void assertOpen() {
 4355           if (_closed) {
 4356               if (_closedException == null)  // TRACE not enabled
 4357                   throw new InvalidStateException(_loc.get("closed-notrace"))
 4358                           .setFatal(true);
 4359               else {
 4360                   OpenJPAException e = new InvalidStateException(
 4361                       _loc.get("closed"), _closedException).setFatal(true);
 4362                   e.setCause(_closedException);
 4363                   throw e;
 4364               }
 4365           }
 4366       }
 4367   
 4368       public void assertActiveTransaction() {
 4369           if ((_flags & FLAG_ACTIVE) == 0)
 4370               throw new NoTransactionException(_loc.get("not-active"));
 4371       }
 4372   
 4373       /**
 4374        * Throw exception if a transaction-related operation is attempted and
 4375        * no transaction is active.
 4376        */
 4377       private void assertTransactionOperation() {
 4378           if ((_flags & FLAG_ACTIVE) == 0)
 4379               throw new InvalidStateException(_loc.get("not-active"));
 4380       }
 4381   
 4382       public void assertNontransactionalRead() {
 4383           if ((_flags & FLAG_ACTIVE) == 0 && !_nontransRead)
 4384               throw new InvalidStateException(_loc.get("non-trans-read"));
 4385       }
 4386   
 4387       public void assertWriteOperation() {
 4388           if ((_flags & FLAG_ACTIVE) == 0 && (!_nontransWrite
 4389               || (_autoDetach & DETACH_NONTXREAD) != 0))
 4390               throw new NoTransactionException(_loc.get("write-operation"));
 4391       }
 4392   
 4393       /**
 4394        * Return an object not found exception containing nested exceptions
 4395        * for all of the given failed objects.
 4396        */
 4397       private static ObjectNotFoundException newObjectNotFoundException
 4398           (Collection failed) {
 4399           Throwable[] t = new Throwable[failed.size()];
 4400           int idx = 0;
 4401           for (Iterator itr = failed.iterator(); itr.hasNext(); idx++)
 4402               t[idx] = new ObjectNotFoundException(itr.next());
 4403           return new ObjectNotFoundException(failed, t);
 4404       }
 4405   
 4406       ////////////////////////////////
 4407       // FindCallbacks implementation
 4408       ////////////////////////////////
 4409   
 4410       public Object processArgument(Object oid) {
 4411           return oid;
 4412       }
 4413   
 4414       public Object processReturn(Object oid, OpenJPAStateManager sm) {
 4415           return (sm == null) ? null : sm.getManagedInstance();
 4416       }
 4417   
 4418       private void writeObject(ObjectOutputStream out) throws IOException {
 4419           assertOpen();
 4420           lock();
 4421           try {
 4422               if (isActive()) {
 4423                   if (!getOptimistic())
 4424                       throw new InvalidStateException(
 4425                           _loc.get("cant-serialize-pessimistic-broker"));
 4426                   if (hasFlushed())
 4427                       throw new InvalidStateException(
 4428                           _loc.get("cant-serialize-flushed-broker"));
 4429                   if (hasConnection())
 4430                       throw new InvalidStateException(
 4431                           _loc.get("cant-serialize-connected-broker"));
 4432               }
 4433   
 4434               try {
 4435                   _isSerializing = true;
 4436                   out.writeObject(_factory.getPoolKey());
 4437                   out.defaultWriteObject();
 4438               } finally {
 4439                   _isSerializing = false;
 4440               }
 4441           } finally {
 4442               unlock();
 4443           }
 4444       }
 4445   
 4446       private void readObject(ObjectInputStream in)
 4447           throws ClassNotFoundException, IOException {
 4448           Object factoryKey = in.readObject();
 4449           AbstractBrokerFactory factory =
 4450               AbstractBrokerFactory.getPooledFactoryForKey(factoryKey);
 4451   
 4452           // this needs to happen before defaultReadObject so that it's
 4453           // available for calls to broker.getConfiguration() during
 4454           // StateManager deserialization
 4455           _conf = factory.getConfiguration();
 4456           
 4457           in.defaultReadObject();
 4458           factory.initializeBroker(_managed, _connRetainMode, this, true);
 4459   
 4460           // re-initialize the lock if needed.
 4461           setMultithreaded(_multithreaded);
 4462   
 4463           if (isActive() && _runtime instanceof LocalManagedRuntime)
 4464               ((LocalManagedRuntime) _runtime).begin();
 4465       }
 4466   
 4467       /**
 4468        * Whether or not this broker is in the midst of being serialized.
 4469        *
 4470        * @since 1.1.0 
 4471        */
 4472       boolean isSerializing() {
 4473           return _isSerializing;
 4474       }
 4475   
 4476       /**
 4477        * Transactional cache that holds soft refs to clean instances.
 4478        */
 4479       static class TransactionalCache
 4480           implements Set, Serializable {
 4481   
 4482           private final boolean _orderDirty;
 4483           private Set _dirty = null;
 4484           private Set _clean = null;
 4485   
 4486           public TransactionalCache(boolean orderDirty) {
 4487               _orderDirty = orderDirty;
 4488           }
 4489   
 4490           /**
 4491            * Return a copy of all transactional state managers.
 4492            */
 4493           public Collection copy() {
 4494               if (isEmpty())
 4495                   return Collections.EMPTY_LIST;
 4496   
 4497               // size may not be entirely accurate due to refs expiring, so
 4498               // manually copy each object; doesn't matter this way if size too
 4499               // big by some
 4500               List copy = new ArrayList(size());
 4501               if (_dirty != null)
 4502                   for (Iterator itr = _dirty.iterator(); itr.hasNext();)
 4503                       copy.add(itr.next());
 4504               if (_clean != null)
 4505                   for (Iterator itr = _clean.iterator(); itr.hasNext();)
 4506                       copy.add(itr.next());
 4507               return copy;
 4508           }
 4509   
 4510           /**
 4511            * Return a copy of all dirty state managers.
 4512            */
 4513           public Collection copyDirty() {
 4514               if (_dirty == null || _dirty.isEmpty())
 4515                   return Collections.EMPTY_LIST;
 4516               return new ArrayList(_dirty);
 4517           }
 4518   
 4519           /**
 4520            * Transfer the given instance from the dirty cache to the clean cache.
 4521            */
 4522           public void flushed(StateManagerImpl sm) {
 4523               if (sm.isDirty() && _dirty != null && _dirty.remove(sm))
 4524                   addCleanInternal(sm);
 4525           }
 4526   
 4527           /**
 4528            * Add the given instance to the clean cache.
 4529            */
 4530           public void addClean(StateManagerImpl sm) {
 4531               if (addCleanInternal(sm) && _dirty != null)
 4532                   _dirty.remove(sm);
 4533           }
 4534   
 4535           private boolean addCleanInternal(StateManagerImpl sm) {
 4536               if (_clean == null)
 4537                   _clean = new ReferenceHashSet(ReferenceHashSet.SOFT);
 4538               return _clean.add(sm);
 4539           }
 4540   
 4541           /**
 4542            * Add the given instance to the dirty cache.
 4543            */
 4544           public void addDirty(StateManagerImpl sm) {
 4545               if (_dirty == null) {
 4546                   if (_orderDirty)
 4547                       _dirty = MapBackedSet.decorate(new LinkedMap());
 4548                   else
 4549                       _dirty = new HashSet();
 4550               }
 4551               if (_dirty.add(sm))
 4552                   removeCleanInternal(sm);
 4553           }
 4554   
 4555           /**
 4556            * Remove the given instance from the cache.
 4557            */
 4558           public boolean remove(StateManagerImpl sm) {
 4559               return removeCleanInternal(sm)
 4560                   || (_dirty != null && _dirty.remove(sm));
 4561           }
 4562   
 4563           private boolean removeCleanInternal(StateManagerImpl sm) {
 4564               return _clean != null && _clean.remove(sm);
 4565           }
 4566   
 4567           public Iterator iterator() {
 4568               IteratorChain chain = new IteratorChain();
 4569               if (_dirty != null && !_dirty.isEmpty())
 4570                   chain.addIterator(_dirty.iterator());
 4571               if (_clean != null && !_clean.isEmpty())
 4572                   chain.addIterator(_clean.iterator());
 4573               return chain;
 4574           }
 4575   
 4576           public boolean contains(Object obj) {
 4577               return (_dirty != null && _dirty.contains(obj))
 4578                   || (_clean != null && _clean.contains(obj));
 4579           }
 4580   
 4581           public boolean containsAll(Collection coll) {
 4582               for (Iterator itr = coll.iterator(); itr.hasNext();)
 4583                   if (!contains(itr.next()))
 4584                       return false;
 4585               return true;
 4586           }
 4587   
 4588           public void clear() {
 4589               if (_dirty != null)
 4590                   _dirty = null;
 4591               if (_clean != null)
 4592                   _clean = null;
 4593           }
 4594   
 4595           public boolean isEmpty() {
 4596               return (_dirty == null || _dirty.isEmpty())
 4597                   && (_clean == null || _clean.isEmpty());
 4598           }
 4599   
 4600           public int size() {
 4601               int size = 0;
 4602               if (_dirty != null)
 4603                   size += _dirty.size();
 4604               if (_clean != null)
 4605                   size += _clean.size();
 4606               return size;
 4607           }
 4608   
 4609           public boolean add(Object obj) {
 4610               throw new UnsupportedOperationException();
 4611           }
 4612   
 4613           public boolean addAll(Collection coll) {
 4614               throw new UnsupportedOperationException();
 4615           }
 4616   
 4617           public boolean remove(Object obj) {
 4618               throw new UnsupportedOperationException();
 4619           }
 4620   
 4621           public boolean removeAll(Collection coll) {
 4622               throw new UnsupportedOperationException();
 4623           }
 4624   
 4625           public boolean retainAll(Collection c) {
 4626               throw new UnsupportedOperationException();
 4627           }
 4628   
 4629           public Object[] toArray() {
 4630               throw new UnsupportedOperationException();
 4631           }
 4632   
 4633           public Object[] toArray(Object[] arr) {
 4634               throw new UnsupportedOperationException();
 4635           }
 4636       }
 4637   
 4638       /**
 4639        * Unique id for state managers of new datastore instances without assigned
 4640        * object ids.
 4641        */
 4642       private static class StateManagerId
 4643           implements Serializable {
 4644   
 4645           public static final String STRING_PREFIX = "openjpasm:";
 4646   
 4647           private static long _generator = 0;
 4648   
 4649           private final int _bhash;
 4650           private final long _id;
 4651   
 4652           public static StateManagerId newInstance(Broker b) {
 4653               return new StateManagerId(System.identityHashCode(b), _generator++);
 4654           }
 4655   
 4656           private StateManagerId(int bhash, long id) {
 4657               _bhash = bhash;
 4658               _id = id;
 4659           }
 4660   
 4661           public StateManagerId(String str) {
 4662               str = str.substring(STRING_PREFIX.length());
 4663               int idx = str.indexOf(':');
 4664               _bhash = Integer.parseInt(str.substring(0, idx));
 4665               _id = Long.parseLong(str.substring(idx + 1));
 4666           }
 4667   
 4668           public boolean equals(Object other) {
 4669               if (other == this)
 4670                   return true;
 4671               if (!(other instanceof StateManagerId))
 4672                   return false;
 4673               StateManagerId sid = (StateManagerId) other;
 4674               return _bhash == sid._bhash && _id == sid._id;
 4675           }
 4676   
 4677           public int hashCode() {
 4678               return (int) (_id ^ (_id >>> 32));
 4679           }
 4680   
 4681           public String toString() {
 4682               return STRING_PREFIX + _bhash + ":" + _id;
 4683           }
 4684       }
 4685   
 4686       /**
 4687        * Collection type that holds state managers but whose interface deals
 4688        * with the corresponding managed objects.
 4689        */
 4690       private static class ManagedObjectCollection
 4691           extends AbstractCollection {
 4692   
 4693           private final Collection _states;
 4694   
 4695           public ManagedObjectCollection(Collection states) {
 4696               _states = states;
 4697           }
 4698   
 4699           public Collection getStateManagers() {
 4700               return _states;
 4701           }
 4702   
 4703           public int size() {
 4704               return _states.size();
 4705           }
 4706   
 4707           public Iterator iterator() {
 4708               return new Iterator() {
 4709                   private final Iterator _itr = _states.iterator();
 4710   
 4711                   public boolean hasNext() {
 4712                       return _itr.hasNext();
 4713                   }
 4714   
 4715                   public Object next() {
 4716                       return ((OpenJPAStateManager) _itr.next()).
 4717                           getManagedInstance();
 4718                   }
 4719   
 4720                   public void remove() {
 4721                       throw new UnsupportedException();
 4722                   }
 4723               };
 4724           }
 4725       }
 4726   
 4727       /**
 4728        * Assign the object id to the cache. Exception will be
 4729        * thrown if the id already exists in the cache. 
 4730        */
 4731       protected void assignObjectId(Object cache, Object id, 
 4732           StateManagerImpl sm) {
 4733           ((ManagedCache) cache).assignObjectId(id, sm); 
 4734       }
 4735   
 4736       /** 
 4737        * This method makes sure we don't already have the instance cached
 4738        */
 4739       protected void checkForDuplicateId(Object id, Object obj) {
 4740           StateManagerImpl other = getStateManagerImplById(id, false);
 4741           if (other != null && !other.isDeleted() && !other.isNew())
 4742               throw new ObjectExistsException(_loc.get("cache-exists",
 4743                   obj.getClass().getName(), id)).setFailedObject(obj);
 4744       }
 4745   }

Save This Page
Home » apache-openjpa-1.1.0-source » org.apache.openjpa » kernel » [javadoc | source]