/*
* Copyright (c) 1998 - 2005 Versant Corporation
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Versant Corporation - initial API and implementation
*/
package com.versant.core.jdo;
import com.versant.core.common.Debug;
import com.versant.core.common.*;
import com.versant.core.jdo.sco.VersantSCOCollection;
import com.versant.core.metadata.*;
import com.versant.core.jdo.query.mem.MemQueryCompiler;
import javax.jdo.*;
import javax.jdo.spi.JDOImplHelper;
import javax.jdo.spi.PersistenceCapable;
import javax.jdo.spi.StateManager;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.lang.reflect.Modifier;
import java.lang.ref.Reference;
import java.sql.Connection;
import java.util.*;
import com.versant.core.common.BindingSupportImpl;
//import com.versant.core.vds.util.Loid;
import com.versant.core.storagemanager.StorageManager;
import com.versant.core.storagemanager.ApplicationContext;
/**
* This is a global StateManager instance for a PersistenceManagerImp. It will
* assign a state managing object to a managed PersistenceCapable instance.
*/
public final class VersantPersistenceManagerImp
implements VersantPersistenceManager, ApplicationContext, Transaction, PersistenceContext
, XAResource, Synchronization {
/**
* Double linked list of dirty instances.
*
* @see PCStateMan#next
* @see PCStateMan#prev
*/
private PCStateMan txDirtyListHead;
private PCStateMan txDirtyListTail;
public VersantPersistenceManagerImp prev;
public VersantPersistenceManagerImp next;
public boolean idle;
/**
* The list of instances that has been marked for delete in the current tx.
*/
private final DeletePacket toBeDeleted;
/**
* This is used to transport the dirty stuff to the server.
*/
public final StatesToStore storeOidStateContainer;
/**
* while busy with a retrieve graph this will be set to true
*/
private boolean retrieveing;
/**
* This is the instances that has been retrieved already.
*/
private final Set retrieveSet = new HashSet();
/**
* The user object set by a client.
*/
private Object userObject;
/**
* A Transaction may be associated with an Synchronization instance. If so this will be set to
* the supplied intstance
*
* @see #setSynchronization
*/
private Synchronization synchronizationInstance;
/**
* This is a cheat to quickly get the sm for a pc instance.
* When getPM is called on the stateManager it sets itself to the
*/
public PCStateMan requestedPCState;
public final ModelMetaData modelMetaData;
private LocalPMCache cache;
public final JDOImplHelper jdoImplHelper = JDOImplHelper.getInstance();
/**
* Indication if tx is active.
*/
private boolean transactionActive;
private boolean busyWithRollback;
/**
* The counter that is used in creating NewObjectOID.
*/
private int counter = 0;
private boolean retainValues;
private boolean restoreValues;
private boolean optimistic;
private boolean nontransactionalRead;
private boolean nontransactionalWrite;
private boolean ignoreCache;
private boolean multithreaded;
/**
* If PersistenceManager is closed.
*/
private boolean closed;
/**
* The factory that created this pm.
*/
private final VersantPMFInternal pmf;
private final MemQueryCompiler memQueryCompiler;
/**
* This will force the instance to be thrown away by the pool.
*/
private boolean mustNotPool;
private boolean inPool;
/**
* This is the proxy that is used for all client access.
* This is used to decouple us from any instances left lying around.
* This proxy ref must be reset to a new instance every time that it
* is taken from the pool to be given out to a client.
*/
private PMProxy proxy;
/**
* This is a bitset of all dirty classes.
*/
private final CmdBitSet dirtyCmdBits;
private final StorageManager sm;
/**
* This variable is used to control the strict adherence to the spec. when
* transaction commits. If false then instances read in the tx but which is
* p-nontx will be hollowed.
*/
private boolean strict;
private boolean interceptDfgFieldAccess = true;
private boolean checkModelConsistencyOnCommit;
public static final String LANGUAGE_SQL = "SQL";
public static final String LANGUAGE_EJBQL = "EJBQL";
// These fields keep track of OIDs/instances of objectid-class'es and
// classes to evict from l2 cache if the transaction commits (epc = Evict
// Post Commit).
private Object[] epcObjects;
private int epcObjectCount;
private int[] epcClasses;
private int epcClassCount;
private boolean[] epcClassPresent;
private boolean epcAll;
private LifecycleListenerManager listeners;
private Reference activeReference;
public VersantPersistenceManagerImp(VersantPMFInternal pmf,
ModelMetaData modelMetaData, StorageManager sm,
LocalPMCache jdoManagedCache,
MemQueryCompiler memQueryCompiler) {
this.pmf = pmf;
this.modelMetaData = modelMetaData;
this.sm = sm;
this.storeOidStateContainer = new StatesToStore(modelMetaData);
this.toBeDeleted = new DeletePacket(modelMetaData);
dirtyCmdBits = new CmdBitSet(modelMetaData);
this.cache = jdoManagedCache;
this.cache.setPm(this);
this.memQueryCompiler = memQueryCompiler;
}
private void createProxy() {
if (multithreaded) {
proxy = new SynchronizedPMProxy(this);
} else {
proxy = new UnsynchronizedPMProxy(this);
}
}
public boolean isInterceptDfgFieldAccess() {
return interceptDfgFieldAccess;
}
public void setInterceptDfgFieldAccess(boolean on) {
if (interceptDfgFieldAccess == on) return;
cache.setInterceptDfgFieldAccess(
interceptDfgFieldAccess = on);
}
public boolean isStrict() {
return strict;
}
public void setStrict(boolean strict) {
this.strict = strict;
}
public PMProxy getProxy() {
return proxy;
}
public boolean isInPool() {
return inPool;
}
public void setMustNotPool(boolean mustNotPool) {
this.mustNotPool = mustNotPool;
}
public boolean isMustNotPool() {
return mustNotPool;
}
/**
* This is called by the pool instance the moment comes in or out of the
* pool.
*/
public void setInPool(boolean inPool) {
this.inPool = inPool;
if (inPool) {
proxy = null;
} else {
createProxy();
if (managed) managedClosed = false;
}
}
/**
* Reset the proxy so that if a client has a weak/soft ref to pm he will
* get a closed exception. Then return us to our PMF.
*/
protected void finalize() throws Throwable {
boolean txWasActive = transactionActive;
if (transactionActive) {
rollback();
}
proxy.resetPM();
pmf.pmClosedNotification(this, true, txWasActive);
}
/**
* Sets the master on the detail in a Master/Detail relationship. If
* removeFromCurrentMaster is true then the detail is removed from its
* current master (if any and if different).
*/
public void setMasterOnDetail(PersistenceCapable detail, int managedFieldNo,
PersistenceCapable master, boolean removeFromCurrentMaster) {
getInternalSM(detail).setMaster(managedFieldNo, master,
removeFromCurrentMaster);
}
/**
* Get a collection field from a pc. This is used to implement many-to-many
* relationships.
*/
public VersantSCOCollection getCollectionField(PersistenceCapable pc,
int fieldNo) {
PCStateMan sm = getInternalSM(pc);
return (VersantSCOCollection)sm.getObjectField(null, fieldNo, null);
}
public Object getObjectField(PersistenceCapable pc,
int fieldNo) {
PCStateMan sm = getInternalSM(pc);
return sm.getObjectField(null, fieldNo, null);
}
public String getConnectionURL(String dataStore) {
try {
return pmf.getConnectionURL();
} catch (Exception e) {
handleException(e);
return null;
}
}
public void cancelQueryExecution() {
// todo implement jdoConnection.cancelQueryExecution();
}
public String getConnectionDriverName(String dataStore) {
try {
return pmf.getConnectionDriverName();
} catch (Exception e) {
handleException(e);
return null;
}
}
public Connection getJdbcConnection(String datastore) {
if (!transactionActive) {
throw BindingSupportImpl.getInstance().invalidOperation(
"A JDBC Connection may only be obtained within a active JDO transaction.");
}
try {
return (Connection)sm.getDatastoreConnection();
} catch (Exception e) {
handleException(e);
return null;
}
}
/**
*
*/
public void flushIfDepOn(int[] cmdBits) {
if (cmdBits != null && dirtyCmdBits.containsAny(cmdBits)) flushRetainState();
}
public void loadFetchGroup(Object pc, String name) {
try {
if (!(pc instanceof PersistenceCapable)) {
String msg;
if (pc == null) {
msg = "Instance is null";
} else {
msg = "Instance is not a persistence class: " +
pc.getClass().getName();
}
throw BindingSupportImpl.getInstance().runtime(msg);
}
PCStateMan sm = getInternalSM((PersistenceCapable)pc);
if (sm == null) {
throw BindingSupportImpl.getInstance().invalidOperation("Instance is not managed by JDO (it is transient): " +
Utils.toString(pc));
}
sm.loadFetchGroup(name);
} catch (RuntimeException e) {
handleException(e);
}
}
public boolean isRetainValues() {
return retainValues;
}
public boolean isRestoreValues() {
return restoreValues;
}
public boolean isOptimistic() {
return optimistic;
}
public boolean isNontransactionalRead() {
return nontransactionalRead;
}
public boolean isNontransactionalWrite() {
return nontransactionalWrite;
}
public boolean isIgnoreCache() {
return ignoreCache;
}
public void evict(Object pc) {
try {
if (pc == null) return;
PersistenceCapable persistenceCapable = checkPersCapable(pc);
PersistenceManager pm = persistenceCapable.jdoGetPersistenceManager();
// This instance is not managed. So return.
if (pm == null) return;
checkPM(pm, proxy);
getInternalSM(persistenceCapable).evict();
} catch (Exception e) {
handleException(e);
}
}
public void evictAll() {
try {
cache.evict();
} catch (Exception e) {
handleException(e);
}
}
public void evictAll(Object[] pcs) {
for (int i = pcs.length - 1; i >= 0; i--) {
evict(pcs[i]);
}
}
public void evictAll(Collection pcs) {
try {
Object[] persistenceCapables = new Object[pcs.size()];
pcs.toArray(persistenceCapables);
for (int i = persistenceCapables.length - 1; i >= 0; i--) {
evict(persistenceCapables[i]);
}
} catch (Exception e) {
handleException(e);
}
}
public Object getObjectId(Object pc) {
try {
if (pc == null) {
throw BindingSupportImpl.getInstance().invalidOperation(
"The supplied Object param is null");
}
if (!(pc instanceof PersistenceCapable))
// pmPreCheck throws exception in this case
{
return null;
}
PCStateMan pcStateObject = pmPreCheck(pc);
if (pcStateObject != null) {
return pcStateObject.getObjectId(null);
} else {
return null;
}
} catch (RuntimeException e) {
handleException(e);
return null;
}
}
public Object getExternalOID(OID oid) {
PCStateMan sm = cache.getByOID(oid, false);
if (sm != null) {
return sm.getObjectId(null);
} else {
ClassMetaData classMetaData = oid.getAvailableClassMetaData();
if (classMetaData.identityType == MDStatics.IDENTITY_TYPE_DATASTORE) {
return new VersantOid(oid, modelMetaData, oid.isResolved());
} else if (classMetaData.identityType == MDStatics.IDENTITY_TYPE_APPLICATION) {
Object pcID = jdoImplHelper.newObjectIdInstance(
classMetaData.cls);
oid.populateObjectIdClassInstance(pcID);
return pcID;
} else {
throw BindingSupportImpl.getInstance().unsupported();
}
}
}
public Object getObjectByIDString(String value, boolean toValidate) {
return getObjectByIDString(value, toValidate, true);
}
public Object getObjectByIDString(String value, boolean toValidate,
boolean resolved) {
try {
OID oid = modelMetaData.newOIDFromIDString(value, resolved);
return getObjectById(oid, toValidate);
} catch (Exception e) {
handleException(e);
return null;
}
}
public Object getTransactionalObjectId(Object pc) {
try {
return getObjectId(pc);
} catch (Exception e) {
handleException(e);
return null;
}
}
// public void batchDelete(Query q, Object[] params) {
// VesantQueryImp clientQuery = (VesantQueryImp) q;
// clientQuery.getQParamsForBatchProc(params);
// throw new NotImplementedException();
// }
public boolean isClosed() {
checkInPool();
if (!managed) {
return closed;
} else {
return managedClosed;
}
}
/**
* This is called by the user to close the pm. If in a managed environment the managedClosed
* flag will be set.
* <p/>
* If pooling is enabled the the instance must be reset to
* be put back in the pool.
* <p/>
* If pooling is not enabled then the instance must be reset to avoid mem leaks.
* The instance will never be used again.
* <p/>
* The current transaction will be rolled back if active.
* This means that synchronization events will be fired.
*/
public synchronized void close() {
checkClosed();
if (!managed) {
checkCloseWithActiveTx();
boolean txWasActive = transactionActive;
if (transactionActive) {
rollback();
}
proxy.resetPM();
pmf.pmClosedNotification(this, false, txWasActive);
} else {
managedClosed = true;
}
}
private void checkCloseWithActiveTx() {
if (transactionActive && !pmf.isAllowPmCloseWithTxOpen()) {
throw BindingSupportImpl.getInstance().invalidOperation(
"May not close 'PersistenceManager' with active transaction");
}
}
/**
* Destroy the PM. It cannot be reused after this call.
*/
public void destroy() {
try {
sm.reset();
resetEpcFields();
reset();
cache.clear();
this.closed = true;
} catch (Exception e) {
handleException(e);
}
}
public boolean isActualClosed() {
return closed;
}
public Transaction currentTransaction() {
return this;
}
public void refresh(Object pc) {
try {
/**
* if pc is transient / non-managed then return.
*/
if (isTransient(pc)) return;
pmPreCheck(pc).refresh();
} catch (Exception e) {
handleException(e);
}
}
public void setPmCacheRefType(Object pc, int type) {
pmPreCheck(pc).cacheEntry.changeToRefType(cache.queue, type);
}
public void setPmCacheRefType(Object[] pcs, int type) {
for (int i = 0; i < pcs.length; i++) {
if (pcs[i] != null) setPmCacheRefType(pcs[i], type);
}
}
public void setPmCacheRefType(Collection col, int type) {
for (Iterator iterator = col.iterator(); iterator.hasNext();) {
Object o = iterator.next();
if (o != null) setPmCacheRefType(o, type);
}
}
public void setPmCacheRefType(int type) {
cache.setCurrentRefType(type);
}
public int getPmCacheRefType() {
return cache.getCurrentRefType();
}
public void refreshAll(Object[] pcs) {
try {
for (int i = 0; i < pcs.length; i++) {
refresh(pcs[i]);
}
} catch (Exception e) {
handleException(e);
}
}
public void refreshAll(Collection pcs) {
if (pcs instanceof QueryResult) {
cache.setOverWriteMode(true);
try {
Iterator iter = ((QueryResult)pcs).createInternalIterNoFlush();
while (iter.hasNext()) iter.next();
} catch (Exception e) {
handleException(e);
} finally {
cache.setOverWriteMode(false);
}
} else {
try {
for (Iterator iterator = pcs.iterator(); iterator.hasNext();) {
refresh(iterator.next());
}
} catch (Exception e) {
handleException(e);
}
}
}
public void refreshAll() {
try {
if (transactionActive) {
cache.doRefresh(strict);
}
} catch (Exception e) {
handleException(e);
}
}
public Query newQuery() {
try {
return new VersantQueryImp(proxy);
} catch (Exception e) {
handleException(e);
return null;
}
}
public Query newQuery(Object compiled) {
// if (compiled instanceof JavaQuery) {
// return newQuery((JavaQuery) compiled);
// }
try {
VersantQueryImp other = null;
try {
other = (VersantQueryImp)compiled;
} catch (Exception e) {
throw BindingSupportImpl.getInstance().invalidOperation(
"The supplied instance is not supported to re-create a query from.");
}
return new VersantQueryImp(proxy, other);
} catch (RuntimeException e) {
handleException(e);
return null;
}
}
// public Query newQuery(JavaQuery javaQuery) {
// try {
// JavaQueryParams query = null;
// try {
// query = (JavaQueryParams) javaQuery;
// } catch (ClassCastException e) {
// throw BindingSupportImpl.getInstance().invalidOperation(
// "JavaQuery has not been enhanced.");
// }
// VesantQueryImp clientQuery = new VesantQueryImp(proxy);
// clientQuery.setClass(query.getQueryClass());
// clientQuery.setFilter(query.getFilter());
// clientQuery.setOrdering(query.getOrdering());
// clientQuery.declareParameters(query.getParameters());
// clientQuery.declareVariables(query.getVariables());
// return clientQuery;
// } catch (Exception e) {
// handleException(e);
// return null;
// }
// }
//
// public Query newNamedQuery(Class cls, String queryName) {
// return null;
// }
public Query newQuery(String language, Object query) {
try {
if (language != null) {
language = language.toUpperCase();
}
if (LANGUAGE_SQL.equals(language)) {
VersantQueryImp clientQuery = new VersantQueryImp(proxy,
QueryDetails.LANGUAGE_SQL);
clientQuery.setFilter((String)query);
return clientQuery;
} else if (LANGUAGE_EJBQL.equals(language)) {
VersantQueryImp clientQuery = new VersantQueryImp(proxy,
QueryDetails.LANGUAGE_EJBQL);
clientQuery.setFilter((String)query);
return clientQuery;
} else {
return newQuery(query);
}
} catch (Exception e) {
handleException(e);
return null;
}
}
public Query newQuery(Class cls) {
try {
VersantQueryImp clientQuery = new VersantQueryImp(proxy);
clientQuery.setClass(cls);
return clientQuery;
} catch (Exception e) {
handleException(e);
return null;
}
}
public Query newQuery(Extent extent) {
try {
VersantQueryImp clientQuery = new VersantQueryImp(proxy);
clientQuery.setCandidates(extent);
return clientQuery;
} catch (Exception e) {
handleException(e);
return null;
}
}
public Query newQuery(Extent extent, String filter) {
try {
VersantQueryImp clientQuery = new VersantQueryImp(proxy);
clientQuery.setCandidates(extent);
clientQuery.setFilter(filter);
return clientQuery;
} catch (Exception e) {
handleException(e);
return null;
}
}
public Query newQuery(Class cls, Collection cln) {
try {
VersantQueryImp clientQuery = new VersantQueryImp(proxy);
clientQuery.setClass(cls);
clientQuery.setCandidates(cln);
return clientQuery;
} catch (Exception e) {
handleException(e);
return null;
}
}
public Query newQuery(Class cls, String filter) {
try {
VersantQueryImp clientQuery = new VersantQueryImp(proxy);
clientQuery.setClass(cls);
clientQuery.setFilter(filter);
return clientQuery;
} catch (Exception e) {
handleException(e);
return null;
}
}
public Query newQuery(Class cls, Collection cln, String filter) {
try {
VersantQueryImp clientQuery = new VersantQueryImp(proxy);
clientQuery.setClass(cls);
clientQuery.setCandidates(cln);
clientQuery.setFilter(filter);
return clientQuery;
} catch (Exception e) {
handleException(e);
return null;
}
}
public Class getObjectIdClass(Class cls) {
try {
if (cls == null) {
return null;
// throw new JDOUserException("The supplied Class param is null");
}
if (!PersistenceCapable.class.isAssignableFrom(cls)) {
return null;
}
if (Modifier.isAbstract(cls.getModifiers())) {
return null;
}
ClassMetaData cmd = modelMetaData.getClassMetaData(cls);
if (cmd == null) {
throw BindingSupportImpl.getInstance().invalidOperation(
"The class is not specified as " + PersistenceCapable.class.getName() + " for the application");
}
if (cmd.identityType == MDStatics.IDENTITY_TYPE_APPLICATION) {
return cmd.objectIdClass;
} else {
return VersantOid.class;
}
} catch (RuntimeException e) {
handleException(e);
return null;
}
}
public Object newObjectIdInstance(Class cls, String s) {
return newObjectIdInstance(cls, s, true);
}
public Object newObjectIdInstance(Class cls, String s, boolean resolved) {
try {
if (s == null || s.length() == 0) {
throw BindingSupportImpl.getInstance().invalidOperation(
"Please supply an non-null, non-empty String");
}
if (cls == null) { // assume datastore identity
return new VersantOid(
modelMetaData.newOIDFromIDString(s, resolved),
modelMetaData, resolved);
}
ClassMetaData cmd = modelMetaData.getClassMetaData(cls);
if (cmd == null) {
throw BindingSupportImpl.getInstance().invalidOperation("There is no metadata registered for class '" +
cls.getName() + "'");
}
if (cmd.identityType == MDStatics.IDENTITY_TYPE_APPLICATION) {
return jdoImplHelper.newObjectIdInstance(cls, s);
} else if (cmd.identityType == MDStatics.IDENTITY_TYPE_DATASTORE) {
return new VersantOid(
modelMetaData.newOIDFromIDString(s, resolved),
modelMetaData, resolved);
} else {
throw BindingSupportImpl.getInstance().invalidOperation("Class '" + cls.getName() +
" uses non-durable identity");
}
} catch (RuntimeException e) {
handleException(e);
return null;
}
}
public void retrieve(Object o) {
try {
PCStateMan pcStateObject = pmPreCheck(o);
if (pcStateObject != null) {
try {
if (Debug.DEBUG) {
if (retrieveing) {
throw BindingSupportImpl.getInstance().internal(
"Retrieveing is already set");
}
}
retrieveing = true;
if (Debug.DEBUG) {
if (retrieveSet.contains(pcStateObject.pc)) {
throw BindingSupportImpl.getInstance().internal(
"RetrieveSet already contains pc");
}
}
retrieveSet.add(pcStateObject.pc);
pcStateObject.retrieve(this);
} finally {
retrieveing = false;
retrieveSet.clear();
}
}
} catch (RuntimeException e) {
if (BindingSupportImpl.getInstance().isOwnInternalException(e)) {
handleException(e);
} else {
throw e;
}
}
}
public void retrieveImp(Object o) {
if (o == null) return;
PCStateMan pcStateObject = pmPreCheck(o);
if (pcStateObject != null &&
!retrieveSet.contains(pcStateObject.pc)) {
retrieveSet.add(pcStateObject.pc);
pcStateObject.retrieve(this);
}
}
public void retrieveAllImp(Object[] toRetrieve) {
for (int i = 0; i < toRetrieve.length; i++) {
retrieveImp(toRetrieve[i]);
}
}
public void retrieveAllImp(Collection toRetrieve) {
retrieveAllImp(toRetrieve.toArray());
}
public void retrieveAll(Collection collection) {
try {
retrieveAll(collection.toArray());
} catch (Exception e) {
handleException(e);
}
}
public void retrieveAll(Collection collection, boolean b) {
retrieveAll(collection);
}
public void retrieveAll(Object[] objects, boolean b) {
retrieveAll(objects);
}
public void retrieveAll(Object[] objects) {
try {
try {
if (Debug.DEBUG) {
if (retrieveing) {
throw BindingSupportImpl.getInstance().internal(
"Retrieveing is already set");
}
}
retrieveing = true;
for (int i = 0; i < objects.length; i++) {
retrieveImp(objects[i]);
}
} finally {
retrieveing = false;
retrieveSet.clear();
}
} catch (RuntimeException e) {
if (BindingSupportImpl.getInstance().isOwnInternalException(e)) {
handleException(e);
} else {
throw e;
}
}
}
/**
* todo keep extent's around instead of creating a new one.
*
* @param persistenceCapableClass
* @param subclasses
* @return
*/
public Extent getExtent(Class persistenceCapableClass, boolean subclasses) {
try {
return new ExtentImp(persistenceCapableClass, subclasses, proxy);
} catch (Exception e) {
handleException(e);
return null;
}
}
/**
* Util method to return the fetchgroup index.
*/
private int getFgIndex(OID nOID, String fetchGroupName) {
int fgIndex = 0;
FetchGroup fg = nOID.getAvailableClassMetaData().getFetchGroup(
fetchGroupName);
if (fg != null) {
fgIndex = fg.index;
}
return fgIndex;
}
public Object getObjectById(Object oid, boolean validate) {
if (oid == null) {
throw BindingSupportImpl.getInstance().invalidOperation(
"The supplied oid is null");
}
PersistenceCapable pc = null;
try {
OID nOID = extractOID(oid);
PCStateMan stateMan = cache.getByOID(nOID, true);
if (stateMan == null) {
if (!nOID.isNew()) {
/**
* A call to getObjectById(id, false) with a where
* (idType == appId) and (oid.cmd.usekeygen == true)
* will always be validated against the db. This is done
* because an inconsistent mapping
* may arise.
*
* Scenario: A new instance of this type is created and made
* persistent. This instance is now in the
* managed cache with a internal tmp oid.
* A call to getObjectById(id, false) is made with a id
* value of 2 for example. This instance will also
* end up in the managed cache with a real app id with value 2.
* On a call to commit the keygen will allocate a value to
* the new id instance. If this value is also
* assigned the value '2' then there will both instances
* wants the same id but the are already assigned
* different pc instance values.
*
* Also added check for an unresolved oid that is in a hierarchy.
* Such oids are also always resolved.
* This could be changed later to keep such an instance in an
* initialised state.
*/
if (validate
|| (nOID.getAvailableClassMetaData().useKeyGen && nOID.getAvailableClassMetaData(
).identityType == MDStatics.IDENTITY_TYPE_APPLICATION)
|| (!nOID.isResolved() && nOID.getAvailableClassMetaData(
).isInHeirachy())) {
checkNonTxRead();
OID nOIDcopy = nOID.copy();
stateMan = getStateMan(nOIDcopy, 0, -1, -1, validate);
if (stateMan == null) {
throw BindingSupportImpl.getInstance().objectNotFound(
"No row for " +
nOID.getAvailableClassMetaData().storeClass + " " + nOID.toSString());
}
pc = stateMan.pc;
stateMan.loadDFGIntoPC(this);
} else {
//create the sm
PCStateMan sm = getStateObject();
sm.init(nOID, this);
pc = sm.pc;
cache.add(sm);
}
} else {
throw BindingSupportImpl.getInstance().objectNotFound(
"No row for " + nOID.toSString());
}
} else {
pc = stateMan.pc;
if (validate && !stateMan.isTransactional(null)) {
checkNonTxRead();
if (stateMan.isHollow()) {
stateMan.loadDfgFromHollow();
} else {
getState(stateMan.oid, null, 0, -1, -1, true);
}
}
}
return pc;
} catch (Exception e) {
handleException(e);
return null;
}
}
public OID extractOID(Object oid) {
OID nOID;
if (oid instanceof VersantOid) {
nOID = modelMetaData.convertJDOGenieOIDtoOID((VersantOid)oid);
nOID = convertNewToActual(nOID);
} else if (oid instanceof OID) {
nOID = convertNewToActual((OID)oid);
} else {
nOID = modelMetaData.convertFromAppIdToOID(oid);
}
return nOID;
}
/**
* This is use by State implementations when they need to retrieve an
* instance. This will typically be a result of graph navigation.
* The oid parameter is not used when an embedded reference field is
* being retrieved.
*/
public Object getObjectByIdForState(OID oid, int stateFieldNo,
int navClassIndex, OID fromOID) {
try {
PersistenceCapable pc;
FieldMetaData fmd = fromOID.getAvailableClassMetaData().stateFields[stateFieldNo];
if (fmd.embedded) {
//create a managed instance of the embedded reference
PCStateMan owningSM = cache.getByOID(fromOID, false);
if (fmd.nullIndicatorFmd != null) {
if (owningSM.state.isFieldNullorZero(fmd.nullIndicatorFmd.stateFieldNo)) {
return null;
}
}
EmbeddedStateManager embeddedSm = owningSM.createEmbeddedSM(fmd);
pc = embeddedSm.pc;
} else {
PCStateMan stateMan = cache.getByOID(oid, true);
if (stateMan == null) {
stateMan = getStateMan(oid, 0, stateFieldNo, navClassIndex,
false);
if (stateMan != null) {
pc = stateMan.pc;
stateMan.loadDFGIntoPC(this);
} else {
pc = null;
}
} else {
pc = stateMan.pc;
if (!stateMan.isTransactional(null)) {
checkNonTxRead();
//TODO why loadDfg. Is it not better to leave the state hollow
if (stateMan.isHollow()) stateMan.loadDfgFromHollow();
}
}
}
return pc;
} catch (Exception e) {
handleException(e);
return null;
}
}
public final void checkNonTxRead() {
if (!(transactionActive || nontransactionalRead)) {
throw BindingSupportImpl.getInstance().invalidOperation(
"Must set nonTransactionalRead to true");
}
}
public final void checkNonTxWrite() {
if (!(transactionActive || nontransactionalWrite)) {
throw BindingSupportImpl.getInstance().invalidOperation(
"Must set nonTransactionalWrite to true");
}
}
public boolean doBeforeState(boolean isTransient, boolean isTransactional) {
return ((restoreValues || isTransient || isTransactional) && transactionActive);
}
private OID convertNewToActual(OID oid) {
if (!oid.isNew()) {
oid.getAvailableClassMetaData();
return oid;
}
return oid.getAvailableOID();
}
public void makePersistent(final Object o) {
try {
if (o == null) {
throw BindingSupportImpl.getInstance().invalidOperation(
"makePersistent called with null object");
}
checkActiveTx();
PCStateMan root = txDirtyListHead;
makeReachablePersistent(o);
if (root == null) {
root = txDirtyListTail;
} else {
root = root.next;
}
for (; root != null; root = root.next) root.addRefs();
} catch (Exception e) {
handleException(e);
}
}
/**
* Make an instance found in a reachability search persistent. This skips
* some checks done by makePersistent for speed. Note that this does not
* add reachable instances.<p>
*/
public void makeReachablePersistent(final Object o) {
if (o == null) return;
PersistenceCapable pc = checkPersCapable(o);
PersistenceManager pm = pc.jdoGetPersistenceManager();
if (pm == null) {
reManage(pc, assignOID(pc), false);
} else {
PMProxy pmProxy = (PMProxy)pm;
if (pmProxy.getRealPM() != this) {
throw BindingSupportImpl.getInstance().invalidOperation("Object is managed by " + pm + " (this is " + proxy + "): " +
pc.getClass() + ": " + Utils.toString(pc));
}
}
}
public void makePersistentAll(final Object[] pcs) {
try {
ArrayList failed = null;
for (int i = 0; i < pcs.length; i++) {
Object pc = pcs[i];
try {
makePersistent(pc);
} catch (RuntimeException e) {
if (BindingSupportImpl.getInstance().isOwnException(e)) {
if (BindingSupportImpl.getInstance().getFailedObject(e) == null) {
e = BindingSupportImpl.getInstance().exception(
e.getMessage(), e, pc);
}
if (failed == null) failed = new ArrayList();
failed.add(e);
} else {
throw e;
}
}
}
if (failed != null) {
int n = failed.size();
if (n == 1) {
throw (Exception)failed.get(0);
} else {
Throwable[] a = new Throwable[n];
failed.toArray(a);
throw BindingSupportImpl.getInstance().exception(
n + " instances failed to persist",
a);
}
}
} catch (Exception e) {
handleException(e);
}
}
public void makePersistentAll(final Collection pcs) {
try {
ArrayList failed = null;
for (Iterator i = pcs.iterator(); i.hasNext();) {
Object pc = i.next();
try {
makePersistent(pc);
} catch (RuntimeException e) {
if (BindingSupportImpl.getInstance().isOwnException(e)) {
if (BindingSupportImpl.getInstance().getFailedObject(e) == null) {
e = BindingSupportImpl.getInstance().exception(
e.getMessage(), e, pc);
}
if (failed == null) failed = new ArrayList();
failed.add(e);
} else {
throw e;
}
}
}
if (failed != null) {
int n = failed.size();
if (n == 1) {
throw (Exception)failed.get(0);
} else {
Throwable[] a = new Throwable[n];
failed.toArray(a);
throw BindingSupportImpl.getInstance().exception(
n + " instances failed to persist",
a);
}
}
} catch (Exception e) {
handleException(e);
}
}
/**
* @param pcs
*/
public void deletePersistentAll(Object[] pcs) {
try {
for (int i = 0; i < pcs.length; i++) {
deletePersistent(pcs[i]);
}
} catch (Exception e) {
handleException(e);
}
}
/**
* @param pc
*/
public void deletePersistent(Object pc) {
checkActiveTx();
if (isTransient(pc)) {
throw BindingSupportImpl.getInstance().invalidOperation(
"The instance is transient");
}
try {
pmPreCheck(pc).deletePersistent();
} catch (Exception e) {
handleException(e);
}
}
/**
* @param pcs
*/
public void deletePersistentAll(Collection pcs) {
try {
for (Iterator iterator = pcs.iterator(); iterator.hasNext();) {
deletePersistent(iterator.next());
}
} catch (Exception e) {
handleException(e);
}
}
/**
* TODO: Remove the instance from the weak caches.
*
* @param pc
*/
public void makeTransient(Object pc) {
checkPersCapable(pc);
try {
if (isTransient(pc)) {
//the instance is already transient.
return;
}
pmPreCheck(pc).makeTransient();
} catch (Exception e) {
handleException(e);
}
}
public void makeTransientRecursive(Object pc) {
checkPersCapable(pc);
try {
if (isTransient(pc)) {
//the instance is already transient.
return;
}
pmPreCheck(pc).makeTransientRecursive();
} catch (Exception e) {
handleException(e);
}
}
/**
* TODO: Remove the instance from the weak caches.
*
* @param pcs
*/
public void makeTransientAll(Object[] pcs) {
try {
Map failed = new HashMap();
for (int i = 0; i < pcs.length; i++) {
Object pc = pcs[i];
try {
makeTransient(pc);
} catch (Exception e) {
failed.put(pc, e);
}
}
if (failed.size() > 0) {
throw BindingSupportImpl.getInstance().invalidOperation(
"Errors occured with makeTransientAll: " + failed);
}
} catch (RuntimeException e) {
handleException(e);
}
}
/**
* TODO: Remove the instance from the weak caches.
*
* @param pcs
*/
public void makeTransientAll(Collection pcs) {
try {
if (pcs == null) return;
makeTransientAll(pcs.toArray());
} catch (Exception e) {
handleException(e);
}
}
private final boolean isTransient(Object pc) {
if (JDOHelper.getPersistenceManager(pc) == null) {
return true;
}
return false;
}
public void makeTransactional(Object pc) {
try {
if (JDOHelper.getPersistenceManager(pc) == null) {
//this is a transient instance
reManage((PersistenceCapable)pc,
assignOID((PersistenceCapable)pc), true);
}
PCStateMan pcStateObject = pmPreCheck(pc);
pcStateObject.makeTransactional();
} catch (Exception e) {
handleException(e);
}
}
public void makeTransactionalAll(Object[] pcs) {
if (pcs == null) return;
try {
Map failed = new HashMap(pcs.length);
for (int i = 0; i < pcs.length; i++) {
Object pc = pcs[i];
try {
makeTransactional(pc);
} catch (Exception e) {
failed.put(pc, e);
}
}
if (failed.size() > 0) {
throw BindingSupportImpl.getInstance().invalidOperation(
"Errors occured with makePersistentAll:" + failed);
}
} catch (RuntimeException e) {
handleException(e);
}
}
public void makeTransactionalAll(Collection pcs) {
if (pcs == null) return;
try {
makeTransactionalAll(pcs.toArray());
} catch (Exception e) {
handleException(e);
}
}
private final void makeNonTransactionalImp(Object pc) {
PCStateMan pcStateObject = pmPreCheck(pc);
pcStateObject.makeNonTransactional();
}
public void makeNontransactional(Object pc) {
try {
makeNonTransactionalImp(pc);
} catch (Exception e) {
handleException(e);
}
}
public void makeNontransactionalAll(Object[] pcs) {
try {
for (int i = 0; i < pcs.length; i++) {
makeNonTransactionalImp(pcs[i]);
}
} catch (Exception e) {
handleException(e);
}
}
public void makeNontransactionalAll(Collection pcs) {
for (Iterator iterator = pcs.iterator(); iterator.hasNext();) {
makeNonTransactionalImp(iterator.next());
}
}
public void setUserObject(Object o) {
try {
sm.setUserObject(userObject = o);
} catch (Exception e) {
handleException(e);
}
}
public Object getUserObject() {
return userObject;
}
public PersistenceManagerFactory getPersistenceManagerFactory() {
return pmf;
}
public void setMultithreaded(boolean flag) {
// Changing from multithreaded false to true is not allowed. This must
// be done at the PMF level before the PM is created.
if (flag && !multithreaded) {
throw BindingSupportImpl.getInstance().invalidOperation("PM.setMultithreaded(true) is not allowed if the PM " +
"was created from a PMF with multithreaded false");
}
// ignore the true -> false transition as the extra synchronization
// has only a performance impact
}
public boolean getMultithreaded() {
return multithreaded;
}
public void setMultithreadedImp(boolean flag) {
multithreaded = flag;
createProxy();
}
public void setIgnoreCache(boolean flag) {
this.ignoreCache = flag;
}
public boolean getIgnoreCache() {
return this.ignoreCache;
}
/**
* Does a preCheck on a object claimed to be PersistanceCapable and managed by
* this pm.
*/
private final PCStateMan pmPreCheck(final Object pc) {
return pmPreCheck(checkPersCapable(pc));
}
private PCStateMan pmPreCheck(PersistenceCapable pc) {
if (!checkManagedBy(pc)) {
return null;
}
PCStateMan pcState = getInternalSM(pc);
if (Debug.DEBUG) {
if (pcState == null && JDOHelper.getPersistenceManager(pc) != null) {
throw BindingSupportImpl.getInstance().internal(
"The pm is set on instance but is not in weak list");
}
}
return pcState;
}
private final boolean checkManagedBy(PersistenceCapable pc) {
PMProxy pm = (PMProxy)pc.jdoGetPersistenceManager();
if (pm != null) {
if (pm == proxy) return true;
throw BindingSupportImpl.getInstance().invalidOperation("Object is managed by " + pm + " (this is " + proxy + "): " +
pc.getClass() + ": " + Utils.toString(pc));
}
return false;
}
private final void checkActiveTx() {
if (!isActive()) {
throw BindingSupportImpl.getInstance().invalidOperation(
"No active transaction.");
}
}
public void begin() {
if (managed) {
throw BindingSupportImpl.getInstance().invalidOperation(
"May not call begin in managed transaction environment");
}
beginImp();
}
private void beginImp() {
if (transactionActive) {
throw BindingSupportImpl.getInstance().invalidOperation(
"The transaction is already active");
}
try {
transactionActive = true;
sm.begin(optimistic);
if (!optimistic && !interceptDfgFieldAccess) {
cache.setInterceptDfgFieldAccess(true);
}
} catch (Exception e) {
handleException(e);
}
}
/**
* This is for client to rollback the connection. This will try to
* orderly rollback the data. The pm will be reset if an internal exception
* happens during rollback. The pm must be reset as new to avoid inconsistent behaviour.
* <p/>
* NB
* TODO ensure that all tx instances are rolledback properly.
* If for instance the are already removed out of the tx list at commit
* time then they can not undergo a rollback.
*/
public void rollback() {
if (Debug.DEBUG) {
System.out.println(
">>>>>>>>>>> JdoGeniePersistenceManagerImp.rollback <<<<<<<<<<<<<<<");
}
if (managed) {
throw BindingSupportImpl.getInstance().invalidOperation(
"May not call rollback in managed transaction environment");
}
rollbackImp();
}
/**
* Reset this PM after an internal error of some kind. This just flags
* it to not go back in the pool.
*/
private void fatalReset() {
mustNotPool = true;
}
/**
* This is called by JDOConnectionImpProxy when it handles a
* JDOFatalException from a call to the JDOConnection.
*/
public void rollbackForFatalExceptionInJDOConnectionProxy() {
if (managed || !transactionActive) return;
rollbackImp();
}
/**
* This is an orderly rollback.
*/
private void rollbackImp() {
if (busyWithRollback) return;
checkActiveTx();
resetEpcFields();
try {
busyWithRollback = true;
if (synchronizationInstance != null) {
synchronizationInstance.afterCompletion(
Status.STATUS_ROLLING_BACK);
}
try {
cache.doRollback(retainValues);
sm.rollback();
} finally {
reset();
}
if (synchronizationInstance != null) {
synchronizationInstance.afterCompletion(
Status.STATUS_ROLLEDBACK);
}
} catch (Exception e) {
fatalReset();
if (BindingSupportImpl.getInstance().isOwnException(e)) {
throw (RuntimeException)e;
} else {
throw BindingSupportImpl.getInstance().internal(e.getMessage(),
e);
}
} finally {
transactionActive = false;
busyWithRollback = false;
}
}
public void setRestoreValues(boolean b) {
checkPropChange();
this.restoreValues = b;
}
public boolean getRestoreValues() {
return restoreValues;
}
/**
* Check the consistency of all instances in the local cache. Currently
* this makes sure that all birectional relationships have been completed
* properly (both sides in sync) but other checks may will be added in
* future. This method is very slow and should only be used for debugging
* during development.
*
* @see #setCheckModelConsistencyOnCommit(boolean)
*/
public void checkModelConsistency() {
cache.checkModelConsistency();
}
/**
* Add the OID and State for deletion.
*/
public void addForDelete(OID oid, State state) {
if (Debug.DEBUG) {
// make sure untyped OIDs are not added
if (oid.getAvailableClassMetaData() == null) {
BindingSupportImpl.getInstance().internal("oid is untyped: " +
oid);
}
}
toBeDeleted.add(oid, state);
}
/**
* This calls commit on all the transactional objects. It ensures
* that the instances undergo the correct state changes.
*/
public void commit() {
if (managed) {
throw BindingSupportImpl.getInstance().invalidOperation(
"May not call commit in managed transaction environment");
}
if (!transactionActive) {
throw BindingSupportImpl.getInstance().invalidOperation(
"Transaction is not active");
}
internalCommit(false);
}
private void dumpTxDirtyList(String msg) {
System.out.println("--- txDirtyListHead: " + msg);
for (PCStateMan o = txDirtyListTail; o != null; o = o.next) {
System.out.println(o.pc.getClass().getName() + "@" +
Integer.toHexString(System.identityHashCode(o.pc)) +
": " + o.pc);
}
System.out.println("---");
}
private void internalCommit(boolean phase) {
StatesReturned sc = null;
if (synchronizationInstance != null) {
synchronizationInstance.beforeCompletion();
synchronizationInstance.afterCompletion(Status.STATUS_COMMITTING);
}
try {
prepareForStore(true);
sc = sm.store(storeOidStateContainer, toBeDeleted, retainValues,
phase
? StorageManager.STORE_OPTION_PREPARE
: StorageManager.STORE_OPTION_COMMIT,
false);
resetEpcFields();
updateOIDsAndDoAutoS(sc);
cache.doCommit(retainValues);
invokePostStore();
if (!phase) {
if (synchronizationInstance != null) {
synchronizationInstance.afterCompletion(
Status.STATUS_COMMITTED);
}
}
reset();
transactionActive = false;
} catch (Exception e) {
handleException(e);
} finally {
if (sc != null) {
sc.clear();
}
}
}
/**
* Prepare to store all dirty instances for a commit or flush. This finds
* all reachable instances and invokes preStore lifecycle listeners and
* jdoPreStore instance callbacks.
*/
private void prepareForStore(boolean commit) {
boolean preStoreCalled = false;
for (PCStateMan i = txDirtyListTail; i != null; i = i.next) {
i.addRefs();
preStoreCalled |= i.doJDOPreStore(listeners);
}
for (; preStoreCalled;) {
PCStateMan root = txDirtyListHead;
for (PCStateMan i = txDirtyListTail; i != null; i = i.next) {
i.addRefs();
}
if (root == txDirtyListHead) break;
preStoreCalled = false;
for (PCStateMan i = root; i != null; i = i.next) {
preStoreCalled |= i.doJDOPreStore(listeners);
}
}
storeOidStateContainer.clear();
toBeDeleted.clear();
for (PCStateMan i = txDirtyListTail; i != null; i = i.next) {
i.prepareCommitOrFlush(commit);
}
if (checkModelConsistencyOnCommit) {
checkModelConsistency();
}
if (commit) { // fill in the epc stuff
storeOidStateContainer.epcAll = epcAll;
storeOidStateContainer.epcOids = epcObjectCount > 0
? modelMetaData.convertToOID(epcObjects, epcObjectCount)
: null;
storeOidStateContainer.epcClasses = epcClasses;
storeOidStateContainer.epcClassCount = epcClassCount;
}
}
private void invokePostStore() {
if (listeners != null && listeners.hasPostStoreListeners()) {
for (PCStateMan i = txDirtyListTail; i != null; i = i.next) {
i.firePostStore(listeners);
}
}
}
/**
* This flushes everything to the store. After this state interogation
* will not be correct and the behaviour is not inline with the spec.
* This is used for big batch procedures that needs to free the memory.
*/
public void flush() {
StatesReturned sc = null;
try {
prepareForStore(true);
sc = sm.store(storeOidStateContainer, toBeDeleted,
false, StorageManager.STORE_OPTION_FLUSH, true);
updateOIDsAndDoAutoS(sc);
cache.doCommit(retainValues);
invokePostStore();
resetEpcFields();
reset();
} catch (Exception x) {
handleException(x);
} finally {
if (sc != null) {
sc.clear();
}
}
}
public void flush(boolean retainValues) {
if (retainValues) {
flushRetainState();
} else {
flush();
}
}
public List versantAllDirtyInstances() {
if (txDirtyListTail == null) return Collections.EMPTY_LIST;
List l = new ArrayList();
for (PCStateMan i = txDirtyListTail; i != null; i = i.next) {
l.add(i.pc);
}
return l;
}
/**
* This is used to flush the current changes to the store.
* All state interogation will still work after this is called.
* The data is just flushed to the store for queries to work.
* <p/>
* The implication of this is that the datastore connection will be pinned
* to the jdoConnection for the life time of the transaction.
* <p/>
* This method will be a no-op if the transaction is not active.
*/
public void flushRetainState() {
if (!transactionActive) return;
StatesReturned sc = null;
storeOidStateContainer.clear();
toBeDeleted.clear();
try {
prepareForStore(false);
if (storeOidStateContainer.isEmpty() && toBeDeleted.size() == 0) {
return;
}
sc = sm.store(storeOidStateContainer, toBeDeleted, true,
StorageManager.STORE_OPTION_FLUSH, false);
updateOIDsAndDoAutoS(sc);
for (PCStateMan i = txDirtyListTail; i != null; i = i.next) {
i.flushCommit();
}
invokePostStore();
storeOidStateContainer.clear();
toBeDeleted.clear();
} catch (Exception x) {
handleException(x);
} finally {
try {
sc.clear();
} catch (Exception e) {
//ignore
}
}
}
/**
* This will do everything except actually commit to the store.
*/
public void phaseCommit1() {
internalCommit(true);
}
/**
* Do the actual commit on the store.
*/
public void phaseCommit2() {
sm.commit();
if (synchronizationInstance != null) {
synchronizationInstance.afterCompletion(Status.STATUS_COMMITTED);
}
}
public boolean isActive() {
return transactionActive;
}
public final boolean isActiveDS() {
return transactionActive && !optimistic;
}
public void setNontransactionalRead(boolean nontransactionalRead) {
checkPropChange();
this.nontransactionalRead = nontransactionalRead;
}
public boolean getNontransactionalRead() {
return nontransactionalRead;
}
public void setNontransactionalWrite(boolean nontransactionalWrite) {
checkPropChange();
this.nontransactionalWrite = nontransactionalWrite;
}
public boolean getNontransactionalWrite() {
return nontransactionalWrite;
}
public void setRetainValues(boolean retainValues) {
// checkPropChange();
this.retainValues = retainValues;
}
/**
* Check for changing props in active tx.
*/
private final void checkPropChange() {
if (transactionActive) {
throw BindingSupportImpl.getInstance().invalidOperation(
"May not be changed in active transaction");
}
}
public boolean getRetainValues() {
return retainValues;
}
public void setOptimistic(boolean optimistic) {
checkPropChange();
this.optimistic = optimistic;
}
public boolean getOptimistic() {
return optimistic;
}
public void setSynchronization(Synchronization sync) {
this.synchronizationInstance = sync;
}
public Synchronization getSynchronization() {
return synchronizationInstance;
}
public PersistenceManager getPersistenceManager() {
return VersantPersistenceManagerImp.this;
}
//==================================Transaction imp end=======================
/**
* Create a new oid for a pc instance.
*/
private NewObjectOID assignOID(PersistenceCapable pc) {
ClassMetaData cmd = modelMetaData.getClassMetaData(pc.getClass());
if (cmd == null) {
throw BindingSupportImpl.getInstance().invalidOperation(
"There is no metadata registered for " + pc.getClass());
}
if (cmd.instancesNotAllowed) {
throw BindingSupportImpl.getInstance().invalidOperation(
"Instances of " + cmd.qname + " may not be persistent or managed");
}
NewObjectOID oid = cmd.createNewObjectOID();
oid.init(++counter);
return oid;
}
/**
* This is called by the JDOManagedCache when an instance is added
* but it is not already managed.
*/
public PCStateMan reManage(OID oid, State state) {
if (oid.getAvailableClassMetaData() == null) {
// replace untyped OID with a real one since we have a state
OID tmp = state.getClassMetaData(modelMetaData).createOID(true);
tmp.setLongPrimaryKey(oid.getLongPrimaryKey());
oid = tmp;
}
PCStateMan stateObject = getStateObject();
stateObject.init(oid, state.getClassMetaData(modelMetaData), state, this);
return stateObject;
}
private final void reManage(PersistenceCapable pc, OID oid,
boolean isTransactional) {
PCStateMan sm = getStateObject();
pc.jdoReplaceStateManager(createStateManagerProxy(sm));
sm.init(pc, oid, isTransactional);
sm.getRealOIDIfAppId();
// check for app identity instance with same pk as instance already in
// local pm cache
if (!isTransactional
&& sm.getClassMetaData().identityType == MDStatics.IDENTITY_TYPE_APPLICATION) {
OID realOID = ((NewObjectOID)sm.oid).realOID;
if (realOID != null && cache.contains(realOID)) {
// throwing duplicateKey would be problematic for
// java, because duplicateKey is fatal
throw BindingSupportImpl.getInstance().
runtime("Instance of " + sm.getClassMetaData().qname +
" with identity '" + realOID + "' already exists in" +
" the local PM cache");
}
}
//The refs to the wrapper is kept around to avoid gc of the instance
cache.add(sm);
if (Debug.DEBUG) {
if (sm.cacheEntry == null) {
throw BindingSupportImpl.getInstance().internal(
"cacheEntry must be initialized");
}
}
}
/**
* Create a proxy for a PCStateMan or return it as is if no proxy is
* needed. This is used to synchronize StateManager access when
* multithreading is required.
*/
public StateManager createStateManagerProxy(PCStateMan sm) {
if (multithreaded) {
return new SynchronizedStateManagerProxy(proxy, sm);
} else {
return sm;
}
}
/**
* add an transactional instance to the list.
*
* @param stateObject
*/
public void addTxStateObject(PCStateMan stateObject) {
if (Debug.DEBUG) {
if (!stateObject.isTx()) {
throw BindingSupportImpl.getInstance().internal(
"The instance is not Transactional");
}
}
if (stateObject.isDirty()) {
if (!stateObject.isInDirtyList(txDirtyListHead)) {
addTxDirty(stateObject);
}
} else {
if (stateObject.isInDirtyList(txDirtyListHead)) {
removeTxDirty(stateObject);
}
}
}
/**
* Add pc to the dirty list.
*/
private void addTxDirty(PCStateMan pc) {
if (txDirtyListHead == null) {
txDirtyListHead = txDirtyListTail = pc;
pc.next = null;
pc.prev = null;
} else {
pc.prev = txDirtyListHead;
txDirtyListHead.next = pc;
pc.next = null;
txDirtyListHead = pc;
}
dirtyCmdBits.add(pc.getClassMetaData());
pc.inDirtyList = true;
}
/**
* Remove pc from the dirty list.
*/
private void removeTxDirty(PCStateMan pc) {
if (Debug.DEBUG) {
if (!pc.isInDirtyList(txDirtyListHead)) {
throw BindingSupportImpl.getInstance().internal(
"not in dirty list: " + pc);
}
}
if (txDirtyListTail == pc) {
txDirtyListTail = pc.next;
} else {
pc.prev.next = pc.next;
}
if (txDirtyListHead == pc) {
txDirtyListHead = pc.prev;
} else {
pc.next.prev = pc.prev;
}
dirtyCmdBits.remove(pc.getClassMetaData());
}
/**
* Clear the dirty list.
*/
public void clearTxDirtyList() {
for (PCStateMan i = txDirtyListTail; i != null;) {
PCStateMan next = i.next;
i.prev = null;
i.next = null;
i = next;
dirtyCmdBits.clear();
}
txDirtyListHead = txDirtyListTail = null;
}
public void removeTxStateObject(PCStateMan stateObject) {
if (stateObject.isInDirtyList(txDirtyListHead)) {
removeTxDirty(stateObject);
}
}
private final void reset() {
storeOidStateContainer.clear();
clearTxDirtyList();
}
/**
* This will reset the pm to be returned to the pool.
*/
public void resetForPooling() {
if (Debug.DEBUG) {
if (transactionActive) {
throw BindingSupportImpl.getInstance().fatal(
"The tx must be inactive");
}
}
managed = false;
cache.clear();
sm.reset();
sm.setUserObject(userObject = null);
synchronizationInstance = null;
resetEpcFields();
reset();
}
/**
* Check to see if the pm has not been closed by the user.
*/
private void checkClosed() {
checkInPool();
if (closed) {
throw BindingSupportImpl.getInstance().invalidOperation(
"The 'PersistenceManager' is already closed");
}
}
public PersistenceManager getPersistenceManager(PersistenceCapable pc) {
return this;
}
private PCStateMan getStateObject() {
return new PCStateMan(cache, modelMetaData, proxy);
}
// public Collection mapFrom(Object data, Class cls) {
// if (data instanceof ResultSet) {
// return null;
// } else {
// throw BindingSupportImpl.getInstance().unsupported();
// }
// }
//
// public Collection mapFrom(Object data, Class cls, String[] customMapping) {
// if (data instanceof ResultSet) {
// return null;
// } else {
// throw BindingSupportImpl.getInstance().unsupported();
// }
// }
//============================debug methods ============================================================================
public boolean isPNonTx(Object pc) {
return pmPreCheck(pc).isPNonTx();
}
public boolean isPClean(Object pc) {
return pmPreCheck(pc).isPClean();
}
public boolean isPNew(Object pc) {
return pmPreCheck(pc).isPNew();
}
public boolean isPNewDeleted(Object pc) {
return pmPreCheck(pc).isPNewDeleted();
}
public boolean isPDeleted(Object pc) {
return pmPreCheck(pc).isPDeleted();
}
public boolean isTClean(Object pc) {
return pmPreCheck(pc).isTClean();
}
public boolean isTDirty(Object pc) {
return pmPreCheck(pc).isTDirty();
}
public boolean isPDirty(Object pc) {
return pmPreCheck(pc).isPDirty();
}
/**
* Return the internal oid representation for this pc instance.
* This oid is the actual oid and not a clone. If pc is null then null
* is returned.
*/
public OID getInternalOID(final PersistenceCapable pc) {
if (pc == null) return null;
return getInternalSM(pc).oid;
}
/**
* This is used internally to obtain the PCStateObject for a PersistanceCapable
* instance. This instance must be managed by this pm.
*/
public PCStateMan getInternalSM(final PersistenceCapable pc) {
if (pc == null) return null;
requestedPCState = null;
pc.jdoGetPersistenceManager();
if (requestedPCState == null) {
throw BindingSupportImpl.getInstance().internal("Instance not managed: " +
pc.getClass().getName() + "@" +
Integer.toHexString(System.identityHashCode(pc)) + ": " +
pc.toString());
}
return requestedPCState;
}
public PCStateMan getSMIfManaged(PersistenceCapable pc) {
if (pc == null) return null;
requestedPCState = null;
pc.jdoGetPersistenceManager();
return requestedPCState;
}
/**
* This is used internally to obtain the PCStateObject for a PersistanceCapable
* instance. This instance must be managed by this pm.
*/
public PCStateMan getInternalSM(OID oid) {
if (oid == null) return null;
return getInternalSM((PersistenceCapable)getObjectById(oid, false));
}
/**
* This is used for debug. Not to be used else where.
*/
public State getInternaleState(PersistenceCapable pc) {
PCStateMan pcState = getInternalSM(pc);
if (pcState == null) return null;
return pcState.state;
}
public void dump(OID oid) {
if (Debug.DEBUG) {
PCStateMan pcStateObject = cache.getByOID(oid, true);
if (pcStateObject != null) {
pcStateObject.dump();
} else {
if (Debug.DEBUG) {
Debug.OUT.println("######## null for dump#######");
}
}
}
}
/**
* Are there any dirty instances?
*/
public boolean isDirty() {
return txDirtyListHead != null;
}
/**
* This is used from getObjectByID where the object by id is not in the managed cache.
* If there there is no jdo instance for the supplied instance then null is returned.
*
* @param ignoreLocalPmCache This is used to indicate that we should not check the
* if the state is in the localCache on the serverside. The serverSide might
* have a ref to the local cache.
*/
private PCStateMan getStateMan(OID aOID, int fgIndex, int fieldNo,
int navClassIndex, boolean ignoreLocalPmCache) {
StatesReturned container = null;
try {
container = getStateJdoConnection(aOID, null, fgIndex,
aOID.getAvailableClassId(),
ignoreLocalPmCache, fieldNo, navClassIndex);
if (Debug.DEBUG) {
if (container.get(container.getDirectOID()) == NULLState.NULL_STATE) {
//this instance does not exist in db
if (container.size() != 1) {
throw BindingSupportImpl.getInstance().internal("Then directOid of the container is null " +
"so there should not be any other instances");
}
}
}
return addAndReturnFirstDirect(container);
} finally {
if (container != null) container.clear();
}
}
/**
* This translates the old JDOConnection style call into a StorageManager
* call. It should be nuked at some point.
*/
private StatesReturned getStateJdoConnection(OID oid, State current,
int fetchGroup, int classId, boolean ignoreLocalPmCache,
int fieldNo, int navClassIndex) {
// todo use ignoreLocalPmCache to modify context
if (classId < 0) classId = oid.getAvailableClassId();
ClassMetaData cmd = oid.getAvailableClassMetaData();
FetchGroup fg = cmd == null ? null : cmd.getFetchGroup(fetchGroup, classId);
FieldMetaData triggerField;
if (navClassIndex >= 0) { // navigated stateFieldNo
ClassMetaData navCmd = modelMetaData.classes[navClassIndex];
triggerField = navCmd.stateFields[fieldNo];
} else if (fieldNo >= 0 && cmd != null) { // missing absFieldNo
triggerField = cmd.stateFields[cmd.absToRel[fieldNo]];
} else {
triggerField = null;
}
return sm.fetch(this, oid, current, fg, triggerField);
}
/**
* This is called by sm's if they require a field. This will update the
* local cache with the requested data (State instances). The returned
* container reference must be kept until all the required State instances
* in the local cache have been referenced or they might be GCed.
* This is important for collections of PC and other fields that
* involve fetching State instances and loading them into the local cache.
* They are only hard referenced when the SCO instance has been created.
*/
public StatesReturned getState(OID oid, State current, int fgi, int fieldNo,
int navClassIndex, boolean ignoreLocalPmCache) {
StatesReturned container = getStateJdoConnection(oid,
current == null || current.isEmpty() ? null : current,
fgi, oid.getAvailableClassId(),
ignoreLocalPmCache, fieldNo, navClassIndex);
if (Debug.DEBUG) {
if (container.get(container.getDirectOID()) == NULLState.NULL_STATE) {
//this instance does not exist in db
if (container.size() != 1) {
throw BindingSupportImpl.getInstance().internal("Then directOid of the container is null " +
"so there should not be any other instances");
}
}
}
addToCache(container);
return container;
}
/**
* This is called by sm's when doing a refresh. It will ensure that the
* data comes from the database when in a datastore transaction.
*/
public void getStateForRefresh(OID oid, State current, int fgi) {
StatesReturned container = null;
try {
container = getStateJdoConnection(oid,
current == null || current.isEmpty() ? null : current,
fgi, oid.getAvailableClassId(), true,
-1, -1);
addToCache(container);
} finally {
if (container != null) container.clear();
}
}
private void fillCacheWith(OIDArray oids, int fgi, int stateFieldNo,
int navClassIndex) {
StatesReturned container = null;
try {
FieldMetaData triggerField;
if (navClassIndex >= 0) { // navigated stateFieldNo
ClassMetaData navCmd = modelMetaData.classes[navClassIndex];
triggerField = navCmd.stateFields[stateFieldNo];
} else {
triggerField = null;
}
container = sm.fetch(this, oids, triggerField);
addToCache(container);
} finally {
if (container != null) container.clear();
}
}
/**
* This is the pm is not the current PM.
*/
private static final void checkPM(PersistenceManager otherPM,
PersistenceManager currentPM) {
if (otherPM != null && otherPM != currentPM) {
throw BindingSupportImpl.getInstance().invalidOperation(
"The instance is not managed by this PersistenceManager");
}
}
private static final PersistenceCapable checkPersCapable(Object o) {
try {
return (PersistenceCapable)o;
} catch (ClassCastException e) {
throw BindingSupportImpl.getInstance().invalidOperation("The supplied instance is not of type " +
PersistenceCapable.class.getName() +
" (" + o.getClass().getName() + ")");
}
}
/**
* <<<<<<< VersantPersistenceManagerImp.java
* This util method is used by collection types to preload their pc
* entries. It tests to determine if the states refered to by the oids is
* in the managed cache. If not they must be bulk loaded from server.
* The scenario in which this is likely to happen is when the collection
* is not in the default fetch group and the state is in cache with the
* collection filled in. If this collection field is read then the
* pcstateman will determine that the stateField is filled and hence not
* ask the server for it.
*/
public void checkToPreFetch(Object[] oids, int stateFieldNo,
int navClassIndex) {
if (oids != null && oids.length > 0 && oids[0] != null
&& cache.getByOID((OID)oids[0], true) == null) {
OIDArray oidArray = new OIDArray();
for (int i = 0; i < oids.length && oids[i] != null; i++) {
oidArray.add((OID)oids[i]);
}
fillCacheWith(oidArray, 0, stateFieldNo, navClassIndex);
}
}
/**
* This does general exception handling. It ensures that a rollback
* is done for Fatal exceptions.
* CR:
* If called with an instance of JDOUserException it's a no-op
* and the passed instance is thrown.
*/
private final void handleException(Exception x) {
if (BindingSupportImpl.getInstance().isOwnFatalException(x) && isActive()) {
try {
rollbackImp();
} catch (Exception e) {
// discard as we are already busy processing an earlier
// exception
}
}
if (BindingSupportImpl.getInstance().isOwnInternalException(x)) {
fatalReset();
throw (RuntimeException)x;
} else if (BindingSupportImpl.getInstance().isOwnException(x)) {
throw (RuntimeException)x;
} else {
fatalReset();
throw BindingSupportImpl.getInstance().internal(x.getMessage(), x);
}
}
/**
* Convert all PC, VersantOid and objectid-class params to OIDs. This
* makes it possible to pass an OID instead of a PC instance for PC
* parameters.
*/
public void convertPcParamsToOID(Object[] params) {
if (params == null) return;
int n = params.length;
for (int i = 0; i < n; i++) {
Object param = params[i];
if (param == null) continue;
if (param instanceof Collection) {
List l = new ArrayList((Collection)param);
for (int j = 0; j < l.size(); j++) {
Object o = (Object)l.get(j);
o = convertPcParamsToOIDImp(o, i);
if (o instanceof OID) {
l.set(j, o);
}
}
params[i] = l;
} else {
params[i] = convertPcParamsToOIDImp(param, i);
}
}
}
private Object convertPcParamsToOIDImp(Object param, int paramIndex) {
if (param == null) return param;
if (param instanceof PersistenceCapable) {
PersistenceCapable pc = (PersistenceCapable)param;
if (pc.jdoGetPersistenceManager() != proxy) {
if (pc.jdoGetPersistenceManager() != null) {
throw BindingSupportImpl.getInstance().invalidOperation("PC parameter " + paramIndex + " is managed by " + pc.jdoGetPersistenceManager() +
" (this is " + proxy + "): " +
pc.getClass() + ": " + Utils.toString(pc));
} else {
throw BindingSupportImpl.getInstance().invalidOperation("PC parameter " + paramIndex + " is transient: " +
pc.getClass() + ": " + Utils.toString(pc));
}
}
param = getInternalOID((PersistenceCapable)param);
} else if (param instanceof VersantOid) {
// datastore identity OID parameter
OID oid = modelMetaData.convertJDOGenieOIDtoOID((VersantOid)param);
param = convertNewToActual(oid);
} else {
ClassMetaData cmd =
modelMetaData.getClassMetaDataForObjectIdClass(
param.getClass());
if (cmd != null) { // app identity objectid-class parameter
OID oid = cmd.createOID(false);
oid.fillFromPK(param);
param = oid;
}
}
return param;
}
//==========================XAResource impl==================================
private int txTimeout = 3000;
private Xid xid = null;
private int txState;
public static final int TX_INACTIVE = 0;
public static final int TX_STARTED = 1;
public static final int TX_FAIL = 2;
public static final int TX_PREPARED = 4;
public static final int TX_SUSPENDED = 8;
/**
* If the pm is managed by a TransactionManager.
*/
private boolean managed;
/**
* This is a flag to indicate if it was closed by the client in a managed env.
*/
private boolean managedClosed = false;
/**
* Is this pm managed by a container.
*
* @return
*/
public boolean isManaged() {
return managed;
}
/**
* set the state of the pm to be in a container.
*
* @param managed
*/
public void setManaged(boolean managed) {
this.managed = managed;
this.managedClosed = false;
}
/**
* Called to associate the resource with a transaction.
* <p/>
* If the flags argument is {@link #TMNOFLAGS}, the transaction must not
* previously have been seen by this resource manager, or an
* {@link javax.transaction.xa.XAException} with error code XAER_DUPID will be thrown.
* <p/>
* If the flags argument is {@link #TMJOIN}, the resource will join a
* transaction previously seen by its resource manager.
* <p/>
* If the flags argument is {@link #TMRESUME} the resource will
* resume the transaction association that was suspended with
* end(TMSUSPEND).
*
* @param xid The id of the transaction to associate with.
* @param flags Must be either {@link #TMNOFLAGS}, {@link #TMJOIN}
* or {@link #TMRESUME}.
* @throws javax.transaction.xa.XAException
* If an error occurred.
*/
public void start(Xid xid, int flags) throws XAException {
if (Debug.DEBUG) {
Debug.OUT.println("***** JdoGeniePersistenceManagerImp.start ***** flag = "
+ getFlagString(flags) + " for \n" + this);
System.out.println("xid = " + xid);
}
checkInPool();
switch (flags) {
case TMNOFLAGS:
case TMJOIN:
begin(xid);
break;
case TMRESUME:
if (checkId(xid)) {
resume(xid);
}
break;
default:
throw new XAException(
"Unsupported state for method start state = " + flags);
}
}
private boolean checkId(Xid xid) {
if (!this.xid.equals(xid)) {
// throw new JDOFatalInternalException("The check id failed");
return false;
}
return true;
}
private void resume(Xid xid) throws XAException {
checkInPool();
if (this.txState != TX_SUSPENDED) {
throw new XAException(
"Could resume a transaction that was not suspended");
}
this.txState = TX_STARTED;
}
private void begin(Xid xid) throws XAException {
checkInPool();
if (this.txState == TX_INACTIVE) {
this.xid = xid;
try {
beginImp();
this.txState = TX_STARTED;
} catch (Exception e) {
throw new XAException(
"Could not begin a transaction : " + e.getMessage());
}
} else if (this.txState == TX_STARTED
|| this.txState == TX_PREPARED
|| this.txState == TX_SUSPENDED) {
// Transaction on this pm has started already. Since beans will
// share this pm, there will be multple calls to begin.
return;
} else {
throw new XAException(
"Could not begin a transaction in state = " + txState);
}
}
/**
* Prepare to commit the work done on this resource in the given
* transaction.
* <p/>
* This method cannot return a status indicating that the transaction
* should be rolled back. If the resource wants the transaction to
* be rolled back, it should throw an <code>XAException</code> at the
* caller.
*
* @param xid The id of the transaction to prepare to commit work for.
* @return Either {@link #XA_OK} or {@link #XA_RDONLY}.
* @throws javax.transaction.xa.XAException
* If an error occurred.
*/
public int prepare(Xid xid) throws XAException {
if (Debug.DEBUG) {
Debug.OUT.println(
"***** JdoGeniePersistenceManagerImp.prepare *****");
}
checkInPool();
if (checkId(xid)) {
if (txState == TX_STARTED) {
try {
internalCommit(true);
this.txState = TX_PREPARED;
return XA_OK;
} catch (Exception ex) {
ex.printStackTrace();
throw new XAException(
"Could not prepare commit : " + ex.getMessage());
}
} else if (this.txState == TX_PREPARED || this.txState == TX_SUSPENDED) {
return XA_OK;
} else {
throw new XAException(
"Wrong state to commit phase one on : state = " + this.txState);
}
}
return XA_OK;
}
/**
* Commit the work done on this resource in the given transaction.
* <p/>
* If the <code>onePhase</code> argument is true, one-phase
* optimization is being used, and the {@link #prepare(Xid) prepare}
* method must not have been called for this transaction.
* Otherwise, this is the second phase of the two-phase commit protocol.
*
* @param xid The id of the transaction to commit work for.
* @param onePhase If true, the transaction manager is using one-phase
* optimization.
* @throws javax.transaction.xa.XAException
* If an error occurred.
*/
public void commit(Xid xid, boolean onePhase) throws XAException {
if (Debug.DEBUG) {
Debug.OUT.println("***** JdoGeniePersistenceManagerImp.commit ***** onePhase: " + onePhase
+ " for \n" + this);
}
checkInPool();
if (checkId(xid)) {
try {
if (onePhase && txState == TX_STARTED) {
internalCommit(false);
} else if (this.txState == TX_PREPARED) {
phaseCommit2();
} else if (this.txState == TX_INACTIVE) {
return;
} else {
throw new XAException("Unable to commit unexpected state: state = " +
txState + " for xid = " + xid);
}
this.txState = TX_INACTIVE;
this.xid = null;
} catch (XAException ex) {
ex.printStackTrace();
throw ex;
} catch (Exception ex) {
ex.printStackTrace();
throw new XAException("Could not commit : " + ex.getMessage());
}
}
}
private void checkInPool() {
if (inPool) {
throw BindingSupportImpl.getInstance().fatal(
"The pm is in the pool");
}
}
public boolean isInTx() {
return this.txState != TX_INACTIVE;
}
/**
* Roll back the work done on this resource in the given transaction.
*
* @param xid The id of the transaction to rollback for.
* @throws javax.transaction.xa.XAException
* If an error occurred.
*/
public void rollback(Xid xid) throws XAException {
if (Debug.DEBUG) {
Debug.OUT.println(
"***** JdoGeniePersistenceManagerImp.rollback *****");
}
checkInPool();
if (checkId(xid)) {
try {
if (this.txState != TX_INACTIVE) {
rollbackImp();
this.txState = TX_INACTIVE;
}
this.xid = null;
} catch (Exception e) {
throw new XAException("Could not rollback: " + e.getMessage());
}
}
}
/**
* Called to disassociate the resource from a transaction.
* <p/>
* If the flags argument is {@link #TMSUCCESS}, the portion of work
* was done sucessfully.
* <p/>
* If the flags argument is {@link #TMFAIL}, the portion of work
* failed. The resource manager may mark the transaction for
* rollback only to avoid the transaction being committed.
* <p/>
* If the flags argument is {@link #TMSUSPEND} the resource will
* temporarily suspend the transaction association. The transaction
* must later be re-associated by giving the {@link #TMRESUME} state
* to the {@link #start(Xid,int) start} method.
*
* @param xid The id of the transaction to disassociate from.
* @param flags Must be either {@link #TMSUCCESS}, {@link #TMFAIL}
* or {@link #TMSUSPEND}.
* @throws javax.transaction.xa.XAException
* If an error occurred.
*/
public void end(Xid xid, int flags) throws XAException {
if (Debug.DEBUG) {
Debug.OUT.println("***** JdoGeniePersistenceManagerImp.end ***** flag: " + getFlagString(
flags)
+ " for \n" + this);
System.out.println("xid = " + xid);
}
checkInPool();
if (checkId(xid)) {
switch (flags) {
case TMSUCCESS:
this.txState = TX_STARTED;
break;
case TMFAIL:
this.txState = TX_FAIL;
break;
case TMSUSPEND:
this.txState = TX_SUSPENDED;
break;
default:
throw new XAException(
"Unable to end transaction = " + xid + " unhandled flag = " + flags);
}
}
}
private String getFlagString(int flag) {
switch (flag) {
case 8388608:
return "TMENDRSCAN";
case 536870912:
return "TMFAIL";
case 2097152:
return "TMJOIN";
case 0:
return "TMNOFLAGS";
case 1073741824:
return "TMONEPHASE";
case 134217728:
return "TMRESUME";
case 16777216:
return "TMSTARTRSCAN";
case 67108864:
return "TMSUCCESS";
case 33554432:
return "TMSUSPEND";
default:
return "UNKNOWN";
}
}
/**
* Tells the resource manager to forget about a heuristic decision.
*
* @param xid The id of the transaction that was ended with a heuristic
* decision.
* @throws javax.transaction.xa.XAException
* If an error occurred.
*/
public void forget(Xid xid) throws XAException {
if (Debug.DEBUG) {
Debug.OUT.println(
"***** JdoGeniePersistenceManagerImp.forget *****");
}
checkInPool();
if (this.xid.equals(xid)) {
this.txState = TX_STARTED;
}
}
/**
* Get the current transaction timeout value for this resource.
*
* @return The current timeout value, in seconds.
* @throws javax.transaction.xa.XAException
* If an error occurred.
*/
public int getTransactionTimeout() throws XAException {
if (Debug.DEBUG) {
Debug.OUT.println(
"***** JdoGeniePersistenceManagerImp.getTransactionTimeout *****");
}
checkInPool();
return txTimeout;
}
/**
* This method does not check for inPool because Weblogic seems to keep
* a ref around of used ones to try and re-use it. This happens even though
* it was delisted from transaction.
* <p/>
* Tells the caller if this resource has the same resource manager
* as the argument resource.
* <p/>
* The transaction manager needs this method to be able to decide
* if the {@link #start(Xid,int) start} method should be given the
* {@link #TMJOIN} state.
*
* @throws javax.transaction.xa.XAException
* If an error occurred.
*/
public boolean isSameRM(XAResource xaResource) throws XAException {
if (Debug.DEBUG) {
Debug.OUT.println(
"***** JdoGeniePersistenceManagerImp.isSameRM *****");
Debug.OUT.println("***** isSame: this = " + this);
Debug.OUT.println("***** isSame: other = " + this);
}
return xaResource == this;
}
/**
* Return a list of transactions that are in a prepared or heuristically
* state.
* <p/>
* This method looks not only at the resource it is invoked on, but
* also on all other resources managed by the same resource manager.
* It is intended to be used by the application server when recovering
* after a server crash.
* <p/>
* A recovery scan is done with one or more calls to this method.
* At the first call, {@link #TMSTARTRSCAN} must be in the
* <code>state</code> argument to indicate that the scan should be started.
* During the recovery scan, the resource manager maintains an internal
* cursor that keeps track of the progress of the recovery scan.
* To end the recovery scan, the {@link #TMENDRSCAN} must be passed
* in the <code>state</code> argument.
*
* @param flag Must be either {@link #TMNOFLAGS}, {@link #TMSTARTRSCAN},
* {@link #TMENDRSCAN} or <code>TMSTARTRSCAN|TMENDRSCAN</code>.
* @return An array of zero or more transaction ids.
* @throws javax.transaction.xa.XAException
* If an error occurred.
*/
public Xid[] recover(int flag) throws XAException {
if (Debug.DEBUG) {
Debug.OUT.println("***** JdoGeniePersistenceManagerImp.recover ***** flag = " + getFlagString(
flag));
}
checkInPool();
Xid[] xids = txState == TX_PREPARED ? new Xid[]{xid} : null;
return xids;
}
/**
* Set the transaction timeout value for this resource.
* <p/>
* If the <code>seconds</code> argument is <code>0</code>, the
* timeout value is set to the default timeout value of the resource
* manager.
* <p/>
* Not all resource managers support setting the timeout value.
* If the resource manager does not support setting the timeout
* value, it should return false.
*
* @param seconds The timeout value, in seconds.
* @return True if the timeout value could be set, otherwise false.
* @throws javax.transaction.xa.XAException
* If an error occurred.
*/
public boolean setTransactionTimeout(int seconds) throws XAException {
if (Debug.DEBUG) {
Debug.OUT.println(
"***** JdoGeniePersistenceManagerImp.setTransactionTimeout *****");
}
checkInPool();
if (seconds < -1) {
return false;
} else {
this.txTimeout = seconds;
}
return true;
}
//====================Synchronization imp==============================
public void afterCompletion(int status) {
if (Debug.DEBUG) {
Debug.OUT.println(
"***** JdoGeniePersistenceManagerImp.afterCompletion *****");
}
proxy.resetPM();
pmf.pmClosedNotification(this, false, false);
}
public void beforeCompletion() {
if (Debug.DEBUG) {
Debug.OUT.println(
"***** JdoGeniePersistenceManagerImp.beforeCompletion *****");
}
}
/**
* Set the locking mode for datastore transactions. This method may only
* be called when no transaction is active. You can set the default value
* for this property using the Workbench or edit your .jdogenie file
* directly (datastore.tx.locking property).
*
* @see #LOCKING_NONE
* @see #LOCKING_FIRST
* @see #LOCKING_ALL
*/
public void setDatastoreTxLocking(int mode) {
int policy;
switch (mode) {
case VersantPersistenceManager.LOCKING_NONE:
policy = StorageManager.LOCK_POLICY_NONE;
break;
case VersantPersistenceManager.LOCKING_FIRST:
policy = StorageManager.LOCK_POLICY_FIRST;
break;
case VersantPersistenceManager.LOCKING_ALL:
policy = StorageManager.LOCK_POLICY_ALL;
break;
default:
throw BindingSupportImpl.getInstance().invalidOperation(
"Invalid datastoreTxLocking mode: " + mode);
}
sm.setLockingPolicy(policy);
}
/**
* Get the locking mode for datastore transactions.
*/
public int getDatastoreTxLocking() {
switch (sm.getLockingPolicy()) {
case StorageManager.LOCK_POLICY_NONE:
return VersantPersistenceManager.LOCKING_NONE;
case StorageManager.LOCK_POLICY_FIRST:
return VersantPersistenceManager.LOCKING_FIRST;
case StorageManager.LOCK_POLICY_ALL:
return VersantPersistenceManager.LOCKING_ALL;
}
return VersantPersistenceManager.LOCKING_NONE;
}
public void logEvent(int level, String description, int ms) {
sm.logEvent(level, description, ms);
}
/**
* Return the instance for oid if it is present in the local PM cache
* otherwise return null. Note that the instances might still be hollow
* and touching its fields will cause a fetch from the level 2 cache
* or database.
*
* @see #isHollow(Object)
*/
public Object getObjectByIdFromCache(Object oid) {
if (oid == null) {
throw BindingSupportImpl.getInstance().invalidOperation(
"The oid is null");
}
try {
PCStateMan stateMan = cache.getByOID(extractOID(oid),
true);
return stateMan == null ? null : stateMan.pc;
} catch (Exception e) {
handleException(e);
return null;
}
}
/**
* Is the instance hollow? Hollow instances are managed but their fields
* have not been loaded from the level 2 cache or database.
*
* @see #getObjectByIdFromCache(Object)
*/
public boolean isHollow(Object pc) {
PCStateMan pcStateObject = pmPreCheck(pc);
return pcStateObject != null && pcStateObject.isHollow();
}
/**
* Does the instance have an identity? New instances are only assigned
* an identity on commit or flush or when the application executes an
* operation that requires the identity.
*/
public boolean hasIdentity(Object pc) {
return !pmPreCheck(pc).oid.isNew();
}
/**
* Process the ReferenceQueue for the local cache. This is called when
* a query fetches data from the server to ensure that long running
* read-only transactions do not leak SoftReferences.
*/
public void processLocalCacheReferenceQueue() {
cache.processReferenceQueue();
}
/**
* This method makes detached copies of the parameter instances and returns
* the copies as the result of the method. The order of instances in the
* parameter Collection's iteration corresponds to the order of corresponding
* instances in the returned Collection's iteration.
* <p/>
* The Collection of instances is first made persistent, and the reachability
* algorithm is run on the instances. This ensures that the closure of all
* of the instances in the the parameter Collection is persistent.
* <p/>
* For each instance in the parameter Collection, a corresponding detached
* copy is created. Each field in the persistent instance is handled based on
* its type and whether the field is contained in the fetch group for the
* persistence-capable class. If there are duplicates in the parameter
* Collection, the corresponding detached copy is used for each such duplicate.
*/
public Collection versantDetachCopy(Collection pcs, String fetchGroup) {
if (pcs instanceof QueryResult) {
//this is done to resolve the queryresult to avoid extra queries
// when iterating over it
pcs.size();
}
if (fetchGroup == null) fetchGroup = FetchGroup.DFG_NAME;
for (Iterator pcIt = pcs.iterator(); pcIt.hasNext();) {
Object o = pcIt.next();
if (o == null || !(o instanceof VersantDetachable)) continue;
VersantDetachable pc = (VersantDetachable)o;
requestedPCState = null;
pc.jdoGetPersistenceManager();
if (requestedPCState == null) {
makePersistent(pc);
}
}
flushRetainState();
// load the container with the initial root collection
DetachStateContainer dsc = new DetachStateContainer(this);
for (Iterator pcIt = pcs.iterator(); pcIt.hasNext();) {
PersistenceCapable pc = (PersistenceCapable)pcIt.next();
if (pc == null) continue;
PCStateMan sm = getInternalSM(pc);
ClassMetaData cmd = sm.state.getClassMetaData(modelMetaData);
FetchGroup fg = cmd.getFetchGroup(fetchGroup);
if (fg == null) {
fg = cmd.fetchGroups[0];
}
dsc.add(sm.oid, sm.state, fg);
}
// find all states reachable via the fetch group
for (; dsc.hasNextFetchGroup();) {
State state = dsc.getNextFetchGroupState();
FetchGroup fg = dsc.getNextFetchGroup();
OID oid = dsc.getNextFetchGroupOID();
// if we have to load the fetch group then there is no need to check
// any of its fields - all of the States referenced by the fetch
// group will have been added to the StatesReturned on the
// server and will be added to our dcs
if (!oid.isNew() && (state == null || !state.containsFetchGroup(fg))) {
StatesReturned con = getStateForDetach(oid, fg.index);
addToDetachStateContainer(con, dsc);
state = con.get(oid);
addToCache(con);
dsc.add(oid, state, fg);
}
ClassMetaData cmd = state.getClassMetaData(modelMetaData);
state.addFetchGroupStatesToDCS(fg, dsc, this, oid, cmd);
}
dsc.createPcClasses(modelMetaData);
ArrayList copyList = new ArrayList(pcs.size());
for (Iterator pcIt = pcs.iterator(); pcIt.hasNext();) {
Object o = pcIt.next();
if (o == null || !(o instanceof VersantDetachable)) {
copyList.add(null);
continue;
}
VersantDetachable pc = (VersantDetachable)o;
requestedPCState = null;
pc.jdoGetPersistenceManager();
if (requestedPCState == null) {
copyList.add(pc);
}
copyList.add(dsc.getDetachCopy(pc));
}
return copyList;
}
public List versantCollectReachable(Collection roots, String fetchGroup) {
if (fetchGroup == null) {
fetchGroup = FetchGroup.REF_NAME;
}
checkActiveTx();
Set result = new HashSet();
List todo = new ArrayList(roots);
while (todo.size() > 0) // non-recursive!
{
Object o = todo.remove(0);
String currentFetchGroup = fetchGroup;
if (o instanceof Object[]) { // added by sm.collectReachable
Object[] tmp = (Object[])o;
currentFetchGroup = (String)tmp[1];
o = tmp[0];
}
if (o == null || !(o instanceof PersistenceCapable)) continue;
PersistenceCapable pc = (PersistenceCapable)o;
requestedPCState = null;
pc.jdoGetPersistenceManager();
if (requestedPCState == null) {
makePersistent(pc);
}
PCStateMan sm = pmPreCheck(pc);
if (!result.contains(sm) && !sm.isDeleted(pc)) {
if (currentFetchGroup != null)
// nextFetchGroup was defined for the referencing field
{
sm.collectReachable(currentFetchGroup, todo);
}
result.add(sm);
}
}
List pcresult = new ArrayList();
for (Iterator it = result.iterator(); it.hasNext();) {
pcresult.add(((PCStateMan)it.next()).pc);
}
return pcresult;
}
/**
* This is called by sm's if they require a field.
* This will update the managedCache with the requested data.
*/
public StatesReturned getStateForDetach(OID oid, int fgi) {
return getStateJdoConnection(oid, null, fgi,
oid.getAvailableClassId(), false,
-1, -1);
}
public State getStateFromLocalCacheById(Object oid) {
if (oid == null) {
throw BindingSupportImpl.getInstance().invalidOperation(
"The supplied oid is null");
}
try {
OID nOID = extractOID(oid);
PCStateMan stateMan = cache.getByOID(nOID, true);
if (stateMan == null) {
return null;
}
return stateMan.state;
} catch (Exception e) {
handleException(e);
return null;
}
}
/**
* This util method is used by collection types to preload their pc
* entries. It tests to determine if the states refered to by the oids is
* in the managed cache. If not they must be bulk loaded from server.
* The scenario in which this is likely to happen is when the collection
* is not in the default fetch group and the state is in cache with the
* collection filled in. If this collection field is read then the
* pcstateman will determine that the stateField is filled and hence not
* ask the server for it.
*/
public int getObjectsById(Object[] oids, int length, Object[] data,
int stateFieldNo, int classMetaDataIndex) {
if (oids == null || oids.length <= 0) return 0;
OIDArray oidArray = new OIDArray();
for (int i = 0; i < length; i++) {
Object o = oids[i];
// if (o == null) {
// length = i;
// break;
// }
if (!(o instanceof OID)) {
data[i] = o;
continue;
}
OID oid = (OID)o;
PCStateMan pcStateMan = cache.getByOID(oid, true);
if (pcStateMan != null) {
data[i] = pcStateMan.pc;
} else {
oidArray.add(oid);
data[i] = null;
}
}
if (!oidArray.isEmpty()) {
FieldMetaData triggerField;
if (classMetaDataIndex >= 0) { // navigated stateFieldNo
ClassMetaData navCmd = modelMetaData.classes[classMetaDataIndex];
triggerField = navCmd.stateFields[stateFieldNo];
} else {
triggerField = null;
}
StatesReturned container = sm.fetch(this, oidArray, triggerField);
// keep a reference to each of the returned PCStateMan's so
// that they are not GCed before we reference them
PCStateMan[] nogc = addToCacheAndManage(container);
for (int i = 0; i < length; i++) {
if (data[i] == null) {
data[i] = cache.getByOID((OID)oids[i], true).pc;
}
}
if (nogc == null) {
// dummy code to keep IDE happy and to hopefully prevent
// any overzealous optimization from removing nogc
}
}
return length;
}
/**
* Construct a new query instance with the given candidate class from a
* named query. The query name given must be the name of a query defined
* in metadata. The metadata is searched for the specified name.
* This is a JDO 2 preview feature.
*/
public Query versantNewNamedQuery(Class cls, String queryName) {
try {
ClassMetaData cmd = modelMetaData.getClassMetaData(cls);
if (cmd == null) {
throw BindingSupportImpl.getInstance().invalidOperation("Class " + cls.getName() +
" is not persistent");
}
QueryDetails qp = cmd.getNamedQuery(queryName);
if (qp == null) {
throw BindingSupportImpl.getInstance().invalidOperation("No query called '" + queryName +
"' has been defined in the meta data for Class " +
cmd.qname);
}
return new VersantQueryImp(proxy, qp);
} catch (Exception e) {
handleException(e);
return null;
}
}
/**
* Must bidirectional relationships be checked for consistency
* on commit or flush?
*/
public boolean isCheckModelConsistencyOnCommit() {
return checkModelConsistencyOnCommit;
}
public void setCheckModelConsistencyOnCommit(boolean on) {
this.checkModelConsistencyOnCommit = on;
}
/**
* This method applies the changes contained in the collection of detached
* instances to the corresponding persistent instances in the cache and
* returns a collection of persistent instances that exactly corresponds to
* the parameter instances. The order of instances in the parameter
* Collection's iteration corresponds to the order of corresponding
* instances in the returned Collection's iteration.
* <p/>
* Changes made to instances while detached are applied to the corresponding
* persistent instances in the cache. New instances associated with the
* detached instances are added to the persistent instances in the
* corresponding place.
*/
public Collection versantAttachCopy(Collection detached,
boolean makeTransactional) {
return versantAttachCopy(detached, makeTransactional, false);
}
/**
* This method applies the changes contained in the collection of detached
* instances to the corresponding persistent instances in the cache and
* returns a collection of persistent instances that exactly corresponds to
* the parameter instances. The order of instances in the parameter
* Collection's iteration corresponds to the order of corresponding
* instances in the returned Collection's iteration.
* <p/>
* Changes made to instances while detached are applied to the corresponding
* persistent instances in the cache. New instances associated with the
* detached instances are added to the persistent instances in the
* corresponding place.
*
* @param detached VersantDetachable objects to attach in the current
* transaction
* @param shallow attach only the objects in 'detached' Collection and not
* reachable objects if true.
*/
public Collection versantAttachCopy(Collection detached,
boolean makeTransactional, boolean shallow) {
AttachStateContainer asc = new AttachStateContainer(this);
for (Iterator pcIt = detached.iterator(); pcIt.hasNext();) {
VersantDetachable detachable = (VersantDetachable)pcIt.next();
if (detachable == null) continue;
asc.addVersantDetachable(detachable);
}
if (!shallow) {
AttachNavStateManager ansm = new AttachNavStateManager(asc);
for (int c = 0; c < asc.getDetachedSize(); c++) {
VersantDetachable detachable = asc.getVersantDetachable(c);
if (detachable.jdoGetPersistenceManager() != null) continue;
detachable.jdoReplaceStateManager(ansm);
ClassMetaData cmd = modelMetaData.getClassMetaData(
detachable.getClass());
for (; cmd != null; cmd = cmd.pcSuperMetaData) {
for (int i = 0; i < cmd.fields.length; i++) {
FieldMetaData field = cmd.fields[i];
if (field.fake) continue;
switch (field.category) {
case FieldMetaData.CATEGORY_REF:
case FieldMetaData.CATEGORY_POLYREF:
case FieldMetaData.CATEGORY_COLLECTION:
case FieldMetaData.CATEGORY_MAP:
case FieldMetaData.CATEGORY_ARRAY:
detachable.jdoProvideField(
field.managedFieldNo);
}
}
}
}
}
AttachCopyStateManager acsm = new AttachCopyStateManager(this);
for (int c = 0; c < asc.getDetachedSize(); c++) {
OID oid = asc.getOID(c);
VersantDetachable detachable = asc.getVersantDetachable(c);
if (detachable.jdoGetPersistenceManager() != null) continue;
boolean notNew = !oid.isNew();
if (notNew && !detachable.versantIsDirty()) continue;
detachable.jdoReplaceStateManager(acsm);
PCStateMan sm = getInternalSM(oid);
if (notNew && sm.state.isHollow()) {
StatesReturned con = getStateForDetach(oid, 0);
addToCache(con);
}
acsm.setState(sm.state);
if (notNew) {
Object dVersion = detachable.versantGetVersion();
Object aVersion = sm.getOptimisticLockingValue();
if (aVersion == null && sm.getClassMetaData().optimisticLockingField != null) {
throw BindingSupportImpl.getInstance().internal("Optimistic locking value not available for "
+ sm.getClassMetaData().qname);
}
if (aVersion != null && !aVersion.equals(dVersion)) {
throw BindingSupportImpl.getInstance().concurrentUpdate(
"The object (" + oid.toStringImp() +
") has been updated since its been detached " +
"(current='" + aVersion + "', detached='" + dVersion + "')", oid);
}
}
ClassMetaData cmd = modelMetaData.getClassMetaData(
detachable.getClass());
for (; cmd != null; cmd = cmd.pcSuperMetaData) {
for (int i = 0; i < cmd.fields.length; i++) {
FieldMetaData fmd = cmd.fields[i];
if (fmd.fake) continue;
if (notNew && !detachable.versantIsDirty(
fmd.getManagedFieldNo())) {
continue;
}
switch (fmd.category) {
case FieldMetaData.CATEGORY_SIMPLE:
case FieldMetaData.CATEGORY_POLYREF:
case FieldMetaData.CATEGORY_EXTERNALIZED:
case FieldMetaData.CATEGORY_REF:
case FieldMetaData.CATEGORY_COLLECTION:
case FieldMetaData.CATEGORY_ARRAY:
case FieldMetaData.CATEGORY_MAP:
acsm.setFieldMetaData(fmd);
int managedFieldNo = fmd.managedFieldNo;
detachable.jdoProvideField(managedFieldNo);
if (notNew) {
sm.makeDirty(detachable, managedFieldNo);
}
}
}
}
if (notNew) {
sm.resetLoadedFields();
sm.setLoadRequired();
}
}
Collection deleted = asc.getDeleted();
for (Iterator it = deleted.iterator(); it.hasNext();) {
try {
Object o = getObjectById(it.next(), true);
deletePersistent(o);
} catch (JDOObjectNotFoundException e) {
// Do Nothing
}
}
ArrayList attached = new ArrayList();
for (Iterator pcIt = detached.iterator(); pcIt.hasNext();) {
VersantDetachable pc = (VersantDetachable)pcIt.next();
if (pc == null) {
attached.add(pc);
continue;
}
Object newPC = getObjectById(getOID(pc), false);
attached.add(newPC);
}
return attached;
}
OID getOID(VersantDetachable detachable) {
Object oid = detachable.versantGetOID();
if (oid == null) {
PersistenceManager pm = detachable.jdoGetPersistenceManager();
if (pm != null) {
if (pm instanceof PMProxy) {
pm = ((PMProxy)pm).getRealPM();
}
if (pm instanceof VersantPersistenceManagerImp) {
OID internalOID = ((VersantPersistenceManagerImp)pm).getInternalOID(
detachable);
detachable.versantSetOID(getExternalOID(internalOID));
return internalOID;
} else {
throw BindingSupportImpl.getInstance().runtime(
"Can't attach a managed " +
"instance that is not managed by Versant Open Access");
}
}
PCStateMan sm = getStateObject();
NewObjectOID nOID = assignOID(detachable);
sm.init(detachable, nOID, this);
cache.add(sm);
detachable.versantSetOID(nOID);
return sm.oid.getAvailableOID();
} else {
return extractOID(oid);
}
}
final PCStateMan getStateManager(Object o) {
return cache.getByOID(
extractOID(((PersistenceCapable)o).jdoGetObjectId()), false);
}
final PCStateMan getStateManagerById(Object oid) {
return cache.getByOID(extractOID(oid), false);
}
public void setRetainConnectionInOptTx(boolean on) {
sm.setConnectionPolicy(on
? StorageManager.CON_POLICY_PIN_FOR_TX
: StorageManager.CON_POLICY_RELEASE);
}
/**
* Clear all epc fields.
*/
private void resetEpcFields() {
epcAll = false;
epcObjects = null;
epcObjectCount = 0;
epcClasses = null;
epcClassCount = 0;
epcClassPresent = null;
}
public void evictFromL2CacheAfterCommit(Object o) {
checkActiveTx();
evictFromL2CacheAfterCommitImp(o);
}
/**
* Version of {@link #evictFromL2CacheAfterCommit} without the active tx
* check for interna use.
*/
public void evictFromL2CacheAfterCommitImp(Object o) {
if (epcAll) return;
if (o instanceof PersistenceCapable) {
PCStateMan sm = pmPreCheck((PersistenceCapable)o);
if (sm == null || sm.isNew(null)) {
return; // no need to evict transient or new objects
}
o = sm.oid;
}
if (epcObjects == null) {
epcObjects = new Object[4];
} else if (epcObjectCount == epcObjects.length) {
Object[] a = new Object[epcObjects.length * 3 / 2 + 1];
System.arraycopy(epcObjects, 0, a, 0, epcObjects.length);
epcObjects = a;
}
epcObjects[epcObjectCount++] = o;
}
public void evictAllFromL2CacheAfterCommit(final Object[] data) {
checkActiveTx();
int n = data.length;
if (n == 0 || epcAll) return;
ensureCapacityEpcObjects(n);
for (int i = 0; i < n; i++) {
Object o = data[i];
if (o instanceof PersistenceCapable) {
PCStateMan sm = pmPreCheck((PersistenceCapable)o);
if (sm == null || sm.isNew(null)) {
continue; // no need to evict transient or new objects
}
o = sm.oid;
}
epcObjects[epcObjectCount++] = o;
}
}
public void evictAllFromL2CacheAfterCommit(final Collection data) {
checkActiveTx();
int n = data.size();
if (n == 0 || epcAll) return;
ensureCapacityEpcObjects(n);
for (Iterator i = data.iterator(); i.hasNext();) {
Object o = i.next();
if (o instanceof PersistenceCapable) {
PCStateMan sm = pmPreCheck((PersistenceCapable)o);
if (sm == null || sm.isNew(null)) {
continue; // no need to evict transient or new objects
}
o = sm.oid;
}
epcObjects[epcObjectCount++] = o;
}
}
private void ensureCapacityEpcObjects(int delta) {
if (epcObjects == null) {
epcObjects = new Object[delta + 4];
} else if (epcObjectCount + delta >= epcObjects.length) {
Object[] a = new Object[epcObjectCount + delta];
System.arraycopy(epcObjects, 0, a, 0, epcObjects.length);
epcObjects = a;
}
}
public void evictAllFromL2CacheAfterCommit(final Class cls,
final boolean includeSubclasses) {
checkActiveTx();
ClassMetaData cmd = modelMetaData.getClassMetaData(cls);
if (cmd == null) {
BindingSupportImpl.getInstance().runtime(
"Class is not persistent: " + cls);
}
evictAllFromL2CacheAfterCommitImp(cmd, includeSubclasses);
}
private void evictAllFromL2CacheAfterCommitImp(ClassMetaData cmd,
final boolean includeSubclasses) {
if (epcAll) return;
if (epcClasses == null) {
epcClasses = new int[4];
epcClassPresent = new boolean[modelMetaData.classes.length];
}
int ci = cmd.index;
if (!epcClassPresent[ci]) {
if (epcClassCount == epcClasses.length) {
int[] a = new int[epcClasses.length * 2];
System.arraycopy(epcClasses, 0, a, 0, epcClasses.length);
epcClasses = a;
}
epcClasses[epcClassCount++] = ci;
epcClassPresent[ci] = true;
}
if (includeSubclasses) {
ClassMetaData[] subs = cmd.pcSubclasses;
if (subs != null) {
for (int i = subs.length - 1; i >= 0; i--) {
evictAllFromL2CacheAfterCommitImp(subs[i],
includeSubclasses);
}
}
}
}
public void evictAllFromL2CacheAfterCommit() {
checkActiveTx();
epcAll = true;
epcObjects = null;
epcObjectCount = 0;
epcClasses = null;
epcClassCount = 0;
epcClassPresent = null;
}
public Object getOptimisticLockingValue(Object o) {
return getInternalSM((PersistenceCapable)o).getOptimisticLockingValue();
}
public void setListeners(LifecycleListenerManager listeners) {
this.listeners = listeners;
}
public void addLifecycleListener(LifecycleListener listener,
Class[] classes) {
if (classes != null) {
BindingSupportImpl.getInstance().runtime("Support for non-null " +
"classes parameter has not been implemented");
}
if (listeners == null) {
listeners = new LifecycleListenerManager(listener);
} else {
listeners = listeners.add(listener);
}
}
public void removeLifecycleListener(LifecycleListener listener) {
if (listeners == null) {
return;
}
listeners = listeners.remove(listener);
}
/**
* Fire a delete to all of the LifecycleListener's for o.
*/
public void fireDelete(ClassMetaData cmd, Object o) {
if (listeners != null) {
listeners.fireDelete(o);
}
}
/**
* Get our StorageManager.
*/
public StorageManager getStorageManager() {
return sm;
}
public LocalPMCache getCache() {
return cache;
}
public void setCache(LocalPMCache cache) {
this.cache = cache;
}
public MemQueryCompiler getMemQueryCompiler() {
return memQueryCompiler;
}
public Reference getActiveReference() {
return activeReference;
}
public void setActiveReference(Reference activeReference) {
this.activeReference = activeReference;
}
/**
* Iterate through all the oid-state pairs returned from the server
* for a store operation and update the local states.
* This will also bring back real oids for new oids involved.
*/
private void updateOIDsAndDoAutoS(StatesReturned container) {
for (Iterator i = container.iterator(); i.hasNext(); ) {
EntrySet.Entry e = (EntrySet.Entry)i.next();
OID oid = (OID)e.getKey();
State state = (State)e.getValue();
// This gets the sm with the oid from the container.
// This is done for the case of a newOID which is not
// mapped in the cache via its real oid.
// The sm may not be null because the only way for it to have been sent
// to the server for commit was if it was dirty and hence we must have
// a hard refs to it.
PCStateMan sm;
if (oid instanceof NewObjectOID) {
sm = cache.getByNewObjectOID((NewObjectOID)oid);
if (sm == null) {
sm = cache.getByOID(oid, false);
if (sm == null) continue;
} else {
((NewObjectOID)sm.oid).setRealOid(oid.getRealOID());
}
} else {
sm = cache.getByOID(oid, false);
if (sm == null) continue;
}
// if the state returned is not null then it contains auto set fields that
// needs must be updated the sm's current state.
// this must only happen if retainValues is set to true and the state
// contains autoset fields. version number is a autoset field.
if (state != null) {
sm.updateAutoFields(state);
}
}
}
/**
* Add all the OIDs and States in the container to the cache.
*/
public void addToCache(StatesReturned container) {
for (Iterator i = container.iterator(); i.hasNext(); ) {
EntrySet.Entry e = (EntrySet.Entry)i.next();
cache.addStateOnly((OID)e.getKey(), (State)e.getValue());
}
}
/**
* Add all the OIDs and States in the container to the cache. The
* PCStateMan's are kept and returned to prevent the new instances
* being GCed before they are referenced.
*/
private PCStateMan[] addToCacheAndManage(StatesReturned container) {
PCStateMan[] smArray = new PCStateMan[1];
PCStateMan[] ans = new PCStateMan[container.size()];
int c = 0;
for (Iterator i = container.iterator(); i.hasNext(); ) {
EntrySet.Entry e = (EntrySet.Entry)i.next();
ans[c++] = cache.add((OID)e.getKey(), (State)e.getValue(), smArray);
}
return ans;
}
/**
* Add all the OIDs and States in the container to the cache and return
* the PCStateMan for the first direct State. This method is specifically written
* to keep a hard ref to created sm so that it can not be gc'd before it is returned.
*/
private PCStateMan addAndReturnFirstDirect(StatesReturned container) {
OID firstDirectOID = container.getDirectOID();
if (firstDirectOID == null) {
return null;
}
PCStateMan[] sm = new PCStateMan[1];
cache.add(firstDirectOID, container.get(firstDirectOID), sm);
for (Iterator i = container.iterator(); i.hasNext(); ) {
EntrySet.Entry e = (EntrySet.Entry)i.next();
cache.addStateOnly((OID)e.getKey(), (State)e.getValue());
}
return sm[0];
}
/**
* Add all the OIDs and States in the container to dcs.
*/
private void addToDetachStateContainer(StatesReturned container,
DetachStateContainer dcs) {
for (Iterator i = container.iterator(); i.hasNext(); ) {
EntrySet.Entry e = (EntrySet.Entry)i.next();
dcs.add((OID)e.getKey(), (State)e.getValue());
}
}
/**
* The StorageManager calls this method to check if we need prefetched
* data or not.
*/
public boolean isStateRequired(OID oid, FetchGroup fetchGroup) {
State state = cache.getStateByOID(oid);
if (state != null) {
if (fetchGroup == null) {
fetchGroup = state.getClassMetaData().fetchGroups[0];
}
return !state.containsFetchGroup(fetchGroup);
}
return true;
}
}
|