All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.openjpa.kernel.BrokerImpl Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.openjpa.kernel;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;

import javax.transaction.Status;
import javax.transaction.Synchronization;

import org.apache.openjpa.conf.Compatibility;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.datacache.DataCache;
import org.apache.openjpa.datacache.QueryCache;
import org.apache.openjpa.datacache.TypesChangedEvent;
import org.apache.openjpa.ee.ManagedRuntime;
import org.apache.openjpa.enhance.PCRegistry;
import org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.enhance.Reflection;
import org.apache.openjpa.event.LifecycleEvent;
import org.apache.openjpa.event.LifecycleEventManager;
import org.apache.openjpa.event.RemoteCommitEventManager;
import org.apache.openjpa.event.TransactionEvent;
import org.apache.openjpa.event.TransactionEventManager;
import org.apache.openjpa.instrumentation.InstrumentationManager;
import org.apache.openjpa.kernel.exps.ExpressionParser;
import org.apache.openjpa.lib.conf.Configurations;
import org.apache.openjpa.lib.instrumentation.InstrumentationLevel;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.util.ReferenceHashMap;
import org.apache.openjpa.lib.util.ReferenceHashSet;
import org.apache.openjpa.lib.util.ReferenceMap;
import org.apache.openjpa.lib.util.StringUtil;
import org.apache.openjpa.lib.util.collections.AbstractReferenceMap;
import org.apache.openjpa.lib.util.collections.IteratorChain;
import org.apache.openjpa.lib.util.collections.LinkedMap;
import org.apache.openjpa.lib.util.collections.MapBackedSet;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.MetaDataRepository;
import org.apache.openjpa.meta.SequenceMetaData;
import org.apache.openjpa.meta.ValueMetaData;
import org.apache.openjpa.meta.ValueStrategies;
import org.apache.openjpa.util.ApplicationIds;
import org.apache.openjpa.util.CallbackException;
import org.apache.openjpa.util.Exceptions;
import org.apache.openjpa.util.GeneralException;
import org.apache.openjpa.util.ImplHelper;
import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.InvalidStateException;
import org.apache.openjpa.util.NoTransactionException;
import org.apache.openjpa.util.ObjectExistsException;
import org.apache.openjpa.util.ObjectId;
import org.apache.openjpa.util.ObjectNotFoundException;
import org.apache.openjpa.util.OpenJPAException;
import org.apache.openjpa.util.OptimisticException;
import org.apache.openjpa.util.RuntimeExceptionTranslator;
import org.apache.openjpa.util.StoreException;
import org.apache.openjpa.util.UnsupportedException;
import org.apache.openjpa.util.UserException;
import org.apache.openjpa.util.WrappedException;
import org.apache.openjpa.validation.ValidatingLifecycleEventManager;

/**
 * Concrete {@link Broker}. The broker handles object-level behavior,
 * but leaves all interaction with the data store to a {@link StoreManager}
 * that must be supplied at initialization.
 *
 * @author Abe White
 */
public class BrokerImpl implements Broker, FindCallbacks, Cloneable, Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * Incremental flush.
     */
    protected static final int FLUSH_INC = 0;

    /**
     * Flush in preparation of commit.
     */
    protected static final int FLUSH_COMMIT = 1;

    /**
     * Flush to check consistency of cache, then immediately rollback changes.
     */
    protected static final int FLUSH_ROLLBACK = 2;

    /**
     * Run persistence-by-reachability and other flush-time operations without
     * accessing the database.
     */
    protected static final int FLUSH_LOGICAL = 3;

    static final int STATUS_INIT = 0;
    static final int STATUS_TRANSIENT = 1;
    static final int STATUS_OID_ASSIGN = 2;
    static final int STATUS_COMMIT_NEW = 3;

    private static final int FLAG_ACTIVE = 2 << 0;
    private static final int FLAG_STORE_ACTIVE = 2 << 1;
    private static final int FLAG_CLOSE_INVOKED = 2 << 2;
    private static final int FLAG_PRESTORING = 2 << 3;
    private static final int FLAG_DEREFDELETING = 2 << 4;
    private static final int FLAG_FLUSHING = 2 << 5;
    private static final int FLAG_STORE_FLUSHING = 2 << 6;
    private static final int FLAG_FLUSHED = 2 << 7;
    private static final int FLAG_FLUSH_REQUIRED = 2 << 8;
    private static final int FLAG_REMOTE_LISTENER = 2 << 9;
    private static final int FLAG_RETAINED_CONN = 2 << 10;
    private static final int FLAG_TRANS_ENDING = 2 << 11;

    private static final Object[] EMPTY_OBJECTS = new Object[0];

    private String _connectionFactoryName = "";
    private String _connectionFactory2Name = "";

    private static final Localizer _loc =
        Localizer.forPackage(BrokerImpl.class);

    //	the store manager in use; this may be a decorator such as a
    //	data cache store manager around the native store manager
    private transient DelegatingStoreManager _store = null;

    private FetchConfiguration _fc = null;
    private String _user = null;
    private String _pass = null;

    // these must be rebuilt by the facade layer during its deserialization
    private transient Log _log = null;
    private transient Compatibility _compat = null;
    private transient ManagedRuntime _runtime = null;
    private transient LockManager _lm = null;
    private transient InverseManager _im = null;
    private transient ReentrantLock _lock = null;
    private transient OpCallbacks _call = null;
    private transient RuntimeExceptionTranslator _extrans = null;
    private transient InstrumentationManager _instm = null;

    // ref to producing factory and configuration
    private transient AbstractBrokerFactory _factory = null;
    private transient OpenJPAConfiguration _conf = null;
    private transient MetaDataRepository _repo = null;

    // cache class loader associated with the broker
    private transient ClassLoader _loader = null;

    // user state
    private Synchronization _sync = null;
    private Map _userObjects = null;

    // managed object caches
    private ManagedCache _cache = null;
    private TransactionalCache _transCache = null;
    private Set _transAdditions = null;
    private Set _derefCache = null;
    private Set _derefAdditions = null;

    // these are used for method-internal state only
    private transient Map _loading = null;
    private transient Set _operating = null;
    private transient boolean _operatingDirty = true;

    private Set> _persistedClss = null;
    private Set> _updatedClss = null;
    private Set> _deletedClss = null;
    private Set _pending = null;
    private int findAllDepth = 0;

    // track instances that become transactional after the first savepoint
    // (the first uses the transactional cache)
    private Set _savepointCache = null;
    private LinkedMap _savepoints = null;
    private transient SavepointManager _spm = null;

    // track open queries and extents so we can free their resources on close
    private transient ReferenceHashSet _queries = null;
    private transient ReferenceHashSet _extents = null;

    // track operation stack depth. Transient because operations cannot
    // span serialization.
    private transient int _operationCount = 0;

    // options
    private boolean _nontransRead = false;
    private boolean _nontransWrite = false;
    private boolean _retainState = false;
    private int _autoClear = CLEAR_DATASTORE;
    private int _restoreState = RESTORE_IMMUTABLE;
    private boolean _optimistic = false;
    private boolean _ignoreChanges = false;
    private boolean _multithreaded = false;
    private boolean _managed = false;
    private boolean _syncManaged = false;
    private int _connRetainMode = CONN_RETAIN_DEMAND;
    private boolean _evictDataCache = false;
    private boolean _populateDataCache = true;
    private boolean _largeTransaction = false;
    private int _autoDetach = 0;
    private int _detachState = DETACH_LOADED;
    private boolean _detachedNew = true;
    private boolean _orderDirty = false;
    private boolean _cachePreparedQuery = true;
    private boolean _cacheFinderQuery = true;
    private boolean _suppressBatchOLELogging = false;
    private boolean _allowReferenceToSiblingContext = false;
    private boolean _postLoadOnMerge = false;

    // status
    private int _flags = 0;

    // this is not in status because it should not be serialized
    private transient boolean _isSerializing = false;

    // transient because closed brokers can't be serialized
    private transient boolean _closed = false;
    private transient RuntimeException _closedException = null;

    // event managers
    private TransactionEventManager _transEventManager = null;
    private int _transCallbackMode = 0;
    private LifecycleEventManager _lifeEventManager = null;
    private int _lifeCallbackMode = 0;

    private transient DetachManagerLite _dmLite;

    private transient boolean _initializeWasInvoked = false;
    private transient boolean _fromWriteBehindCallback = false;
    private LinkedList _fcs;

    // Set of supported property keys. The keys in this set correspond to bean-style setter methods
    // that can be set by reflection. The keys are not qualified by any prefix.
    private static Set _supportedPropertyNames;
    static {
        _supportedPropertyNames = new HashSet<>();
        _supportedPropertyNames.addAll(Arrays.asList(new String[] {
                "AutoClear",
                "AutoDetach",
                "CacheFinderQuery",
                "CachePreparedQuery",
                "DetachedNew",
                "DetachState",
                "EvictFromDataCache",
                "IgnoreChanges",
                "LifecycleListenerCallbackMode",
                "Multithreaded",
                "NontransactionalRead",
                "NontransactionalWrite",
                "Optimistic",
                "PopulateDataCache",
                "RestoreState",
                "RetainState",
                }));
    }

    private boolean _printParameters = false;
    private static final String PRINT_PARAMETERS_CONFIG_STR = "PrintParameters";

    /**
     * Set the persistence manager's authentication. This is the first
     * method called after construction.
     *
     * @param user the username this broker represents; used when pooling
     * brokers to make sure that a request to the factory for
     * a connection with an explicit user is delegated to a suitable broker
     * @param pass the password for the above user
     */
    public void setAuthentication(String user, String pass) {
        _user = user;
        _pass = pass;
    }

    /**
     * Initialize the persistence manager. This method is called
     * automatically by the factory before use.
     *
     * @param factory the factory used to create this broker
     * @param sm a concrete StoreManager implementation to
     * handle interaction with the data store
     * @param managed the transaction mode
     * @param connMode the connection retain mode
     * @param fromDeserialization whether this call happened because of a
     * deserialization or creation of a new BrokerImpl.
     */
    public void initialize(AbstractBrokerFactory factory,
        DelegatingStoreManager sm, boolean managed, int connMode,
        boolean fromDeserialization) {
        initialize(factory, sm, managed, connMode, fromDeserialization, false);
    }

    public void initialize(AbstractBrokerFactory factory,
        DelegatingStoreManager sm, boolean managed, int connMode,
        boolean fromDeserialization, boolean fromWriteBehindCallback) {
        _fromWriteBehindCallback = fromWriteBehindCallback;
        _initializeWasInvoked = true;
        _loader = AccessController.doPrivileged(
            J2DoPrivHelper.getContextClassLoaderAction());
        if (!fromDeserialization){
            _conf = factory.getConfiguration();
            _repo = _conf.getMetaDataRepositoryInstance();
        }
        _compat = _conf.getCompatibilityInstance();
        _factory = factory;
        _log = _conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);
        if (!fromDeserialization)
            _cache = new ManagedCache(this);
        // Force creation of a new operating set
        _operatingDirty = true;
        initializeOperatingSet();

        _connRetainMode = connMode;
        _managed = managed;
        if (managed)
            _runtime = _conf.getManagedRuntimeInstance();
        else
            _runtime = new LocalManagedRuntime(this);

        if (!fromDeserialization) {
            _lifeEventManager = _conf.getLifecycleEventManagerInstance();
            _transEventManager = new TransactionEventManager();
            int cmode = _repo.getMetaDataFactory().getDefaults().getCallbackMode();
            setLifecycleListenerCallbackMode(cmode);
            setTransactionListenerCallbackMode(cmode);

            // setup default options
            _factory.configureBroker(this);
        }

        // make sure to do this after configuring broker so that store manager
        // can look to broker configuration; we set both store and lock managers
        // before initializing them because they may each try to access the
        // other in their initialization
        _store = sm;
        _lm = _conf.newLockManagerInstance();
        _im = _conf.newInverseManagerInstance();
        _spm = _conf.getSavepointManagerInstance();
        _store.setContext(this);
        _lm.setContext(this);

        if (_connRetainMode == CONN_RETAIN_ALWAYS)
            retainConnection();
        if (!fromDeserialization) {
            _fc = _store.newFetchConfiguration();
            _fc.setContext(this);
        }

        _instm = _conf.getInstrumentationManagerInstance();
        if (_instm != null) {
            _instm.start(InstrumentationLevel.BROKER, this);
        }

        _dmLite = new DetachManagerLite(_conf);
        _printParameters =
            Boolean.parseBoolean(Configurations.parseProperties(_conf.getConnectionFactoryProperties()).getProperty(
                PRINT_PARAMETERS_CONFIG_STR, "false"));

        // do it before begin event otherwise transactional listeners can't use it, see @Auditable
        if (!fromDeserialization)
            _factory.addListeners(this);

        // synch with the global transaction in progress, if any
        if (_factory.syncWithManagedTransaction(this, false))
            beginInternal();
    }

    @SuppressWarnings("unchecked")
    private void initializeOperatingSet() {
        if(_operatingDirty) {
            _operatingDirty = false;
            _operating = MapBackedSet.mapBackedSet(new IdentityHashMap<>());
        }
    }

    /**
     * Gets the unmodifiable set of instances being operated.
     */
    protected Set getOperatingSet() {
    	return Collections.unmodifiableSet(_operating);
    }

    @Override
    public Object clone()
        throws CloneNotSupportedException {
        if (_initializeWasInvoked)
            throw new CloneNotSupportedException();
        else {
            return super.clone();
        }
    }

    /**
     * Create a {@link Map} to be used for the primary managed object cache.
     * Maps oids to state managers. By default, this creates a
     * {@link ReferenceMap} with soft values.
     */
    protected Map newManagedObjectCache() {
        return new ReferenceHashMap(
                AbstractReferenceMap.ReferenceStrength.HARD, AbstractReferenceMap.ReferenceStrength.SOFT);
    }

    //////////////////////////////////
    // Implementation of StoreContext
    //////////////////////////////////

    @Override
    public Broker getBroker() {
        return this;
    }

    //////////////
    // Properties
    //////////////

    @Override
    public void setImplicitBehavior(OpCallbacks call,
        RuntimeExceptionTranslator ex) {
        if (_call == null)
            _call = call;
        if (_extrans == null)
            _extrans = ex;
    }

    RuntimeExceptionTranslator getInstanceExceptionTranslator() {
        return (_operationCount == 0) ? _extrans : null;
    }

    @Override
    public BrokerFactory getBrokerFactory() {
        return _factory;
    }

    @Override
    public OpenJPAConfiguration getConfiguration() {
        return _conf;
    }

    @Override
    public FetchConfiguration getFetchConfiguration() {
        return _fc;
    }

    @Override
    public FetchConfiguration pushFetchConfiguration() {
		return pushFetchConfiguration(null);
    }

    @Override
    public FetchConfiguration pushFetchConfiguration(FetchConfiguration fc) {
        if (_fcs == null)
            _fcs = new LinkedList<>();
        _fcs.add(_fc);
        _fc = (FetchConfiguration) (fc != null ? fc : _fc).clone();
        return _fc;
    }

    @Override
    public void popFetchConfiguration() {
        if (_fcs == null || _fcs.isEmpty())
            throw new UserException(
                    _loc.get("fetch-configuration-stack-empty"));
        _fc = _fcs.removeLast();
    }

    @Override
    public int getConnectionRetainMode() {
        return _connRetainMode;
    }

    @Override
    public boolean isManaged() {
        return _managed;
    }

    @Override
    public ManagedRuntime getManagedRuntime() {
        return _runtime;
    }

    @Override
    public ClassLoader getClassLoader() {
        return _loader;
    }

    @Override
    public DelegatingStoreManager getStoreManager() {
        return _store;
    }

    @Override
    public LockManager getLockManager() {
        return _lm;
    }

    @Override
    public InverseManager getInverseManager() {
        return _im;
    }

    @Override
    public String getConnectionUserName() {
        return _user;
    }

    @Override
    public String getConnectionPassword() {
        return _pass;
    }

    @Override
    public boolean getMultithreaded() {
        return _multithreaded;
    }

    @Override
    public void setMultithreaded(boolean multithreaded) {
        assertOpen();
        _multithreaded = multithreaded;
        if (multithreaded && _lock == null)
            _lock = new ReentrantLock();
        else if (!multithreaded)
            _lock = null;
    }

    @Override
    public boolean getIgnoreChanges() {
        return _ignoreChanges;
    }

    @Override
    public void setIgnoreChanges(boolean val) {
        assertOpen();
        _ignoreChanges = val;
    }

    @Override
    public boolean getNontransactionalRead() {
        return _nontransRead;
    }

    @Override
    public void setNontransactionalRead(boolean val) {
        assertOpen();
        if ((_flags & FLAG_PRESTORING) != 0)
            throw new UserException(_loc.get("illegal-op-in-prestore"));

        // make sure the runtime supports it
        if (val && !_conf.supportedOptions().contains
            (OpenJPAConfiguration.OPTION_NONTRANS_READ))
            throw new UnsupportedException(_loc.get
                ("nontrans-read-not-supported"));

        _nontransRead = val;
    }

    @Override
    public boolean getNontransactionalWrite() {
        return _nontransWrite;
    }

    @Override
    public void setNontransactionalWrite(boolean val) {
        assertOpen();
        if ((_flags & FLAG_PRESTORING) != 0)
            throw new UserException(_loc.get("illegal-op-in-prestore"));

        _nontransWrite = val;
    }

    @Override
    public boolean getOptimistic() {
        return _optimistic;
    }

    @Override
    public void setOptimistic(boolean val) {
        assertOpen();
        if ((_flags & FLAG_ACTIVE) != 0)
            throw new InvalidStateException(_loc.get("trans-active",
                "Optimistic"));

        // make sure the runtime supports it
        if (val && !_conf.supportedOptions().contains(OpenJPAConfiguration.OPTION_OPTIMISTIC))
            throw new UnsupportedException(_loc.get
                ("optimistic-not-supported"));

        _optimistic = val;
    }

    @Override
    public int getRestoreState() {
        return _restoreState;
    }

    @Override
    public void setRestoreState(int val) {
        assertOpen();
        if ((_flags & FLAG_ACTIVE) != 0)
            throw new InvalidStateException(_loc.get("trans-active",
                "Restore"));

        _restoreState = val;
    }

    @Override
    public boolean getRetainState() {
        return _retainState;
    }

    @Override
    public void setRetainState(boolean val) {
        assertOpen();
        if ((_flags & FLAG_PRESTORING) != 0)
            throw new UserException(_loc.get("illegal-op-in-prestore"));
        _retainState = val;
    }

    @Override
    public int getAutoClear() {
        return _autoClear;
    }

    @Override
    public void setAutoClear(int val) {
        assertOpen();
        _autoClear = val;
    }

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