0001: /*
0002: * Copyright (c) 1998 - 2005 Versant Corporation
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * Versant Corporation - initial API and implementation
0010: */
0011: package com.versant.core.jdo;
0012:
0013: import com.versant.core.common.Debug;
0014: import com.versant.core.common.*;
0015: import com.versant.core.jdo.sco.VersantSCOCollection;
0016: import com.versant.core.metadata.*;
0017: import com.versant.core.jdo.query.mem.MemQueryCompiler;
0018:
0019: import javax.jdo.*;
0020: import javax.jdo.spi.JDOImplHelper;
0021: import javax.jdo.spi.PersistenceCapable;
0022: import javax.jdo.spi.StateManager;
0023:
0024: import javax.transaction.Status;
0025: import javax.transaction.Synchronization;
0026: import javax.transaction.xa.XAException;
0027: import javax.transaction.xa.XAResource;
0028: import javax.transaction.xa.Xid;
0029:
0030: import java.lang.reflect.Modifier;
0031: import java.lang.ref.Reference;
0032: import java.sql.Connection;
0033: import java.util.*;
0034:
0035: import com.versant.core.common.BindingSupportImpl; //import com.versant.core.vds.util.Loid;
0036: import com.versant.core.storagemanager.StorageManager;
0037: import com.versant.core.storagemanager.ApplicationContext;
0038:
0039: /**
0040: * This is a global StateManager instance for a PersistenceManagerImp. It will
0041: * assign a state managing object to a managed PersistenceCapable instance.
0042: */
0043: public final class VersantPersistenceManagerImp implements
0044: VersantPersistenceManager, ApplicationContext, Transaction,
0045: PersistenceContext, XAResource, Synchronization {
0046:
0047: /**
0048: * Double linked list of dirty instances.
0049: *
0050: * @see PCStateMan#next
0051: * @see PCStateMan#prev
0052: */
0053: private PCStateMan txDirtyListHead;
0054: private PCStateMan txDirtyListTail;
0055:
0056: public VersantPersistenceManagerImp prev;
0057: public VersantPersistenceManagerImp next;
0058: public boolean idle;
0059:
0060: /**
0061: * The list of instances that has been marked for delete in the current tx.
0062: */
0063: private final DeletePacket toBeDeleted;
0064: /**
0065: * This is used to transport the dirty stuff to the server.
0066: */
0067: public final StatesToStore storeOidStateContainer;
0068: /**
0069: * while busy with a retrieve graph this will be set to true
0070: */
0071: private boolean retrieveing;
0072: /**
0073: * This is the instances that has been retrieved already.
0074: */
0075: private final Set retrieveSet = new HashSet();
0076:
0077: /**
0078: * The user object set by a client.
0079: */
0080: private Object userObject;
0081:
0082: /**
0083: * A Transaction may be associated with an Synchronization instance. If so this will be set to
0084: * the supplied intstance
0085: *
0086: * @see #setSynchronization
0087: */
0088:
0089: private Synchronization synchronizationInstance;
0090:
0091: /**
0092: * This is a cheat to quickly get the sm for a pc instance.
0093: * When getPM is called on the stateManager it sets itself to the
0094: */
0095: public PCStateMan requestedPCState;
0096: public final ModelMetaData modelMetaData;
0097: private LocalPMCache cache;
0098: public final JDOImplHelper jdoImplHelper = JDOImplHelper
0099: .getInstance();
0100:
0101: /**
0102: * Indication if tx is active.
0103: */
0104: private boolean transactionActive;
0105: private boolean busyWithRollback;
0106:
0107: /**
0108: * The counter that is used in creating NewObjectOID.
0109: */
0110: private int counter = 0;
0111:
0112: private boolean retainValues;
0113: private boolean restoreValues;
0114: private boolean optimistic;
0115: private boolean nontransactionalRead;
0116: private boolean nontransactionalWrite;
0117: private boolean ignoreCache;
0118: private boolean multithreaded;
0119:
0120: /**
0121: * If PersistenceManager is closed.
0122: */
0123: private boolean closed;
0124:
0125: /**
0126: * The factory that created this pm.
0127: */
0128: private final VersantPMFInternal pmf;
0129:
0130: private final MemQueryCompiler memQueryCompiler;
0131:
0132: /**
0133: * This will force the instance to be thrown away by the pool.
0134: */
0135: private boolean mustNotPool;
0136: private boolean inPool;
0137:
0138: /**
0139: * This is the proxy that is used for all client access.
0140: * This is used to decouple us from any instances left lying around.
0141: * This proxy ref must be reset to a new instance every time that it
0142: * is taken from the pool to be given out to a client.
0143: */
0144: private PMProxy proxy;
0145:
0146: /**
0147: * This is a bitset of all dirty classes.
0148: */
0149: private final CmdBitSet dirtyCmdBits;
0150: private final StorageManager sm;
0151:
0152: /**
0153: * This variable is used to control the strict adherence to the spec. when
0154: * transaction commits. If false then instances read in the tx but which is
0155: * p-nontx will be hollowed.
0156: */
0157: private boolean strict;
0158: private boolean interceptDfgFieldAccess = true;
0159:
0160: private boolean checkModelConsistencyOnCommit;
0161:
0162: public static final String LANGUAGE_SQL = "SQL";
0163: public static final String LANGUAGE_EJBQL = "EJBQL";
0164:
0165: // These fields keep track of OIDs/instances of objectid-class'es and
0166: // classes to evict from l2 cache if the transaction commits (epc = Evict
0167: // Post Commit).
0168: private Object[] epcObjects;
0169: private int epcObjectCount;
0170: private int[] epcClasses;
0171: private int epcClassCount;
0172: private boolean[] epcClassPresent;
0173: private boolean epcAll;
0174:
0175: private LifecycleListenerManager listeners;
0176:
0177: private Reference activeReference;
0178:
0179: public VersantPersistenceManagerImp(VersantPMFInternal pmf,
0180: ModelMetaData modelMetaData, StorageManager sm,
0181: LocalPMCache jdoManagedCache,
0182: MemQueryCompiler memQueryCompiler) {
0183: this .pmf = pmf;
0184: this .modelMetaData = modelMetaData;
0185: this .sm = sm;
0186: this .storeOidStateContainer = new StatesToStore(modelMetaData);
0187: this .toBeDeleted = new DeletePacket(modelMetaData);
0188: dirtyCmdBits = new CmdBitSet(modelMetaData);
0189: this .cache = jdoManagedCache;
0190: this .cache.setPm(this );
0191: this .memQueryCompiler = memQueryCompiler;
0192: }
0193:
0194: private void createProxy() {
0195: if (multithreaded) {
0196: proxy = new SynchronizedPMProxy(this );
0197: } else {
0198: proxy = new UnsynchronizedPMProxy(this );
0199: }
0200: }
0201:
0202: public boolean isInterceptDfgFieldAccess() {
0203: return interceptDfgFieldAccess;
0204: }
0205:
0206: public void setInterceptDfgFieldAccess(boolean on) {
0207: if (interceptDfgFieldAccess == on)
0208: return;
0209: cache.setInterceptDfgFieldAccess(interceptDfgFieldAccess = on);
0210: }
0211:
0212: public boolean isStrict() {
0213: return strict;
0214: }
0215:
0216: public void setStrict(boolean strict) {
0217: this .strict = strict;
0218: }
0219:
0220: public PMProxy getProxy() {
0221: return proxy;
0222: }
0223:
0224: public boolean isInPool() {
0225: return inPool;
0226: }
0227:
0228: public void setMustNotPool(boolean mustNotPool) {
0229: this .mustNotPool = mustNotPool;
0230: }
0231:
0232: public boolean isMustNotPool() {
0233: return mustNotPool;
0234: }
0235:
0236: /**
0237: * This is called by the pool instance the moment comes in or out of the
0238: * pool.
0239: */
0240: public void setInPool(boolean inPool) {
0241: this .inPool = inPool;
0242: if (inPool) {
0243: proxy = null;
0244: } else {
0245: createProxy();
0246: if (managed)
0247: managedClosed = false;
0248: }
0249: }
0250:
0251: /**
0252: * Reset the proxy so that if a client has a weak/soft ref to pm he will
0253: * get a closed exception. Then return us to our PMF.
0254: */
0255: protected void finalize() throws Throwable {
0256: boolean txWasActive = transactionActive;
0257: if (transactionActive) {
0258: rollback();
0259: }
0260: proxy.resetPM();
0261: pmf.pmClosedNotification(this , true, txWasActive);
0262: }
0263:
0264: /**
0265: * Sets the master on the detail in a Master/Detail relationship. If
0266: * removeFromCurrentMaster is true then the detail is removed from its
0267: * current master (if any and if different).
0268: */
0269: public void setMasterOnDetail(PersistenceCapable detail,
0270: int managedFieldNo, PersistenceCapable master,
0271: boolean removeFromCurrentMaster) {
0272: getInternalSM(detail).setMaster(managedFieldNo, master,
0273: removeFromCurrentMaster);
0274: }
0275:
0276: /**
0277: * Get a collection field from a pc. This is used to implement many-to-many
0278: * relationships.
0279: */
0280: public VersantSCOCollection getCollectionField(
0281: PersistenceCapable pc, int fieldNo) {
0282: PCStateMan sm = getInternalSM(pc);
0283: return (VersantSCOCollection) sm.getObjectField(null, fieldNo,
0284: null);
0285: }
0286:
0287: public Object getObjectField(PersistenceCapable pc, int fieldNo) {
0288: PCStateMan sm = getInternalSM(pc);
0289: return sm.getObjectField(null, fieldNo, null);
0290: }
0291:
0292: public String getConnectionURL(String dataStore) {
0293: try {
0294: return pmf.getConnectionURL();
0295: } catch (Exception e) {
0296: handleException(e);
0297: return null;
0298: }
0299: }
0300:
0301: public void cancelQueryExecution() {
0302: // todo implement jdoConnection.cancelQueryExecution();
0303: }
0304:
0305: public String getConnectionDriverName(String dataStore) {
0306: try {
0307: return pmf.getConnectionDriverName();
0308: } catch (Exception e) {
0309: handleException(e);
0310: return null;
0311: }
0312: }
0313:
0314: public Connection getJdbcConnection(String datastore) {
0315: if (!transactionActive) {
0316: throw BindingSupportImpl
0317: .getInstance()
0318: .invalidOperation(
0319: "A JDBC Connection may only be obtained within a active JDO transaction.");
0320: }
0321: try {
0322: return (Connection) sm.getDatastoreConnection();
0323: } catch (Exception e) {
0324: handleException(e);
0325: return null;
0326: }
0327: }
0328:
0329: /**
0330: *
0331: */
0332: public void flushIfDepOn(int[] cmdBits) {
0333: if (cmdBits != null && dirtyCmdBits.containsAny(cmdBits))
0334: flushRetainState();
0335: }
0336:
0337: public void loadFetchGroup(Object pc, String name) {
0338: try {
0339: if (!(pc instanceof PersistenceCapable)) {
0340: String msg;
0341: if (pc == null) {
0342: msg = "Instance is null";
0343: } else {
0344: msg = "Instance is not a persistence class: "
0345: + pc.getClass().getName();
0346: }
0347: throw BindingSupportImpl.getInstance().runtime(msg);
0348: }
0349: PCStateMan sm = getInternalSM((PersistenceCapable) pc);
0350: if (sm == null) {
0351: throw BindingSupportImpl.getInstance()
0352: .invalidOperation(
0353: "Instance is not managed by JDO (it is transient): "
0354: + Utils.toString(pc));
0355: }
0356: sm.loadFetchGroup(name);
0357: } catch (RuntimeException e) {
0358: handleException(e);
0359: }
0360: }
0361:
0362: public boolean isRetainValues() {
0363: return retainValues;
0364: }
0365:
0366: public boolean isRestoreValues() {
0367: return restoreValues;
0368: }
0369:
0370: public boolean isOptimistic() {
0371: return optimistic;
0372: }
0373:
0374: public boolean isNontransactionalRead() {
0375: return nontransactionalRead;
0376: }
0377:
0378: public boolean isNontransactionalWrite() {
0379: return nontransactionalWrite;
0380: }
0381:
0382: public boolean isIgnoreCache() {
0383: return ignoreCache;
0384: }
0385:
0386: public void evict(Object pc) {
0387: try {
0388: if (pc == null)
0389: return;
0390: PersistenceCapable persistenceCapable = checkPersCapable(pc);
0391: PersistenceManager pm = persistenceCapable
0392: .jdoGetPersistenceManager();
0393: // This instance is not managed. So return.
0394: if (pm == null)
0395: return;
0396: checkPM(pm, proxy);
0397: getInternalSM(persistenceCapable).evict();
0398: } catch (Exception e) {
0399: handleException(e);
0400: }
0401: }
0402:
0403: public void evictAll() {
0404: try {
0405: cache.evict();
0406: } catch (Exception e) {
0407: handleException(e);
0408: }
0409: }
0410:
0411: public void evictAll(Object[] pcs) {
0412: for (int i = pcs.length - 1; i >= 0; i--) {
0413: evict(pcs[i]);
0414: }
0415: }
0416:
0417: public void evictAll(Collection pcs) {
0418: try {
0419: Object[] persistenceCapables = new Object[pcs.size()];
0420: pcs.toArray(persistenceCapables);
0421: for (int i = persistenceCapables.length - 1; i >= 0; i--) {
0422: evict(persistenceCapables[i]);
0423: }
0424: } catch (Exception e) {
0425: handleException(e);
0426: }
0427: }
0428:
0429: public Object getObjectId(Object pc) {
0430: try {
0431: if (pc == null) {
0432: throw BindingSupportImpl.getInstance()
0433: .invalidOperation(
0434: "The supplied Object param is null");
0435: }
0436:
0437: if (!(pc instanceof PersistenceCapable))
0438: // pmPreCheck throws exception in this case
0439: {
0440: return null;
0441: }
0442:
0443: PCStateMan pcStateObject = pmPreCheck(pc);
0444: if (pcStateObject != null) {
0445: return pcStateObject.getObjectId(null);
0446: } else {
0447: return null;
0448: }
0449: } catch (RuntimeException e) {
0450: handleException(e);
0451: return null;
0452: }
0453: }
0454:
0455: public Object getExternalOID(OID oid) {
0456: PCStateMan sm = cache.getByOID(oid, false);
0457: if (sm != null) {
0458: return sm.getObjectId(null);
0459: } else {
0460: ClassMetaData classMetaData = oid
0461: .getAvailableClassMetaData();
0462: if (classMetaData.identityType == MDStatics.IDENTITY_TYPE_DATASTORE) {
0463: return new VersantOid(oid, modelMetaData, oid
0464: .isResolved());
0465: } else if (classMetaData.identityType == MDStatics.IDENTITY_TYPE_APPLICATION) {
0466: Object pcID = jdoImplHelper
0467: .newObjectIdInstance(classMetaData.cls);
0468: oid.populateObjectIdClassInstance(pcID);
0469: return pcID;
0470: } else {
0471: throw BindingSupportImpl.getInstance().unsupported();
0472: }
0473: }
0474: }
0475:
0476: public Object getObjectByIDString(String value, boolean toValidate) {
0477: return getObjectByIDString(value, toValidate, true);
0478: }
0479:
0480: public Object getObjectByIDString(String value, boolean toValidate,
0481: boolean resolved) {
0482: try {
0483: OID oid = modelMetaData.newOIDFromIDString(value, resolved);
0484: return getObjectById(oid, toValidate);
0485: } catch (Exception e) {
0486: handleException(e);
0487: return null;
0488: }
0489: }
0490:
0491: public Object getTransactionalObjectId(Object pc) {
0492: try {
0493: return getObjectId(pc);
0494: } catch (Exception e) {
0495: handleException(e);
0496: return null;
0497: }
0498: }
0499:
0500: // public void batchDelete(Query q, Object[] params) {
0501: // VesantQueryImp clientQuery = (VesantQueryImp) q;
0502: // clientQuery.getQParamsForBatchProc(params);
0503: // throw new NotImplementedException();
0504: // }
0505:
0506: public boolean isClosed() {
0507: checkInPool();
0508: if (!managed) {
0509: return closed;
0510: } else {
0511: return managedClosed;
0512: }
0513: }
0514:
0515: /**
0516: * This is called by the user to close the pm. If in a managed environment the managedClosed
0517: * flag will be set.
0518: * <p/>
0519: * If pooling is enabled the the instance must be reset to
0520: * be put back in the pool.
0521: * <p/>
0522: * If pooling is not enabled then the instance must be reset to avoid mem leaks.
0523: * The instance will never be used again.
0524: * <p/>
0525: * The current transaction will be rolled back if active.
0526: * This means that synchronization events will be fired.
0527: */
0528: public synchronized void close() {
0529: checkClosed();
0530: if (!managed) {
0531: checkCloseWithActiveTx();
0532: boolean txWasActive = transactionActive;
0533: if (transactionActive) {
0534: rollback();
0535: }
0536: proxy.resetPM();
0537: pmf.pmClosedNotification(this , false, txWasActive);
0538: } else {
0539: managedClosed = true;
0540: }
0541: }
0542:
0543: private void checkCloseWithActiveTx() {
0544: if (transactionActive && !pmf.isAllowPmCloseWithTxOpen()) {
0545: throw BindingSupportImpl
0546: .getInstance()
0547: .invalidOperation(
0548:
0549: "May not close 'PersistenceManager' with active transaction");
0550:
0551: }
0552: }
0553:
0554: /**
0555: * Destroy the PM. It cannot be reused after this call.
0556: */
0557: public void destroy() {
0558: try {
0559: sm.reset();
0560: resetEpcFields();
0561: reset();
0562: cache.clear();
0563: this .closed = true;
0564: } catch (Exception e) {
0565: handleException(e);
0566: }
0567: }
0568:
0569: public boolean isActualClosed() {
0570: return closed;
0571: }
0572:
0573: public Transaction currentTransaction() {
0574: return this ;
0575: }
0576:
0577: public void refresh(Object pc) {
0578: try {
0579: /**
0580: * if pc is transient / non-managed then return.
0581: */
0582: if (isTransient(pc))
0583: return;
0584: pmPreCheck(pc).refresh();
0585: } catch (Exception e) {
0586: handleException(e);
0587: }
0588: }
0589:
0590: public void setPmCacheRefType(Object pc, int type) {
0591: pmPreCheck(pc).cacheEntry.changeToRefType(cache.queue, type);
0592: }
0593:
0594: public void setPmCacheRefType(Object[] pcs, int type) {
0595: for (int i = 0; i < pcs.length; i++) {
0596: if (pcs[i] != null)
0597: setPmCacheRefType(pcs[i], type);
0598: }
0599: }
0600:
0601: public void setPmCacheRefType(Collection col, int type) {
0602: for (Iterator iterator = col.iterator(); iterator.hasNext();) {
0603: Object o = iterator.next();
0604: if (o != null)
0605: setPmCacheRefType(o, type);
0606: }
0607: }
0608:
0609: public void setPmCacheRefType(int type) {
0610: cache.setCurrentRefType(type);
0611: }
0612:
0613: public int getPmCacheRefType() {
0614: return cache.getCurrentRefType();
0615: }
0616:
0617: public void refreshAll(Object[] pcs) {
0618: try {
0619: for (int i = 0; i < pcs.length; i++) {
0620: refresh(pcs[i]);
0621: }
0622: } catch (Exception e) {
0623: handleException(e);
0624: }
0625: }
0626:
0627: public void refreshAll(Collection pcs) {
0628: if (pcs instanceof QueryResult) {
0629: cache.setOverWriteMode(true);
0630: try {
0631: Iterator iter = ((QueryResult) pcs)
0632: .createInternalIterNoFlush();
0633: while (iter.hasNext())
0634: iter.next();
0635: } catch (Exception e) {
0636: handleException(e);
0637: } finally {
0638: cache.setOverWriteMode(false);
0639: }
0640: } else {
0641: try {
0642: for (Iterator iterator = pcs.iterator(); iterator
0643: .hasNext();) {
0644: refresh(iterator.next());
0645: }
0646: } catch (Exception e) {
0647: handleException(e);
0648: }
0649: }
0650: }
0651:
0652: public void refreshAll() {
0653: try {
0654: if (transactionActive) {
0655: cache.doRefresh(strict);
0656: }
0657: } catch (Exception e) {
0658: handleException(e);
0659: }
0660: }
0661:
0662: public Query newQuery() {
0663: try {
0664: return new VersantQueryImp(proxy);
0665: } catch (Exception e) {
0666: handleException(e);
0667: return null;
0668: }
0669: }
0670:
0671: public Query newQuery(Object compiled) {
0672: // if (compiled instanceof JavaQuery) {
0673: // return newQuery((JavaQuery) compiled);
0674: // }
0675: try {
0676: VersantQueryImp other = null;
0677: try {
0678: other = (VersantQueryImp) compiled;
0679: } catch (Exception e) {
0680: throw BindingSupportImpl
0681: .getInstance()
0682: .invalidOperation(
0683: "The supplied instance is not supported to re-create a query from.");
0684: }
0685: return new VersantQueryImp(proxy, other);
0686: } catch (RuntimeException e) {
0687: handleException(e);
0688: return null;
0689: }
0690: }
0691:
0692: // public Query newQuery(JavaQuery javaQuery) {
0693: // try {
0694: // JavaQueryParams query = null;
0695: // try {
0696: // query = (JavaQueryParams) javaQuery;
0697: // } catch (ClassCastException e) {
0698: // throw BindingSupportImpl.getInstance().invalidOperation(
0699: // "JavaQuery has not been enhanced.");
0700: // }
0701: // VesantQueryImp clientQuery = new VesantQueryImp(proxy);
0702: // clientQuery.setClass(query.getQueryClass());
0703: // clientQuery.setFilter(query.getFilter());
0704: // clientQuery.setOrdering(query.getOrdering());
0705: // clientQuery.declareParameters(query.getParameters());
0706: // clientQuery.declareVariables(query.getVariables());
0707: // return clientQuery;
0708: // } catch (Exception e) {
0709: // handleException(e);
0710: // return null;
0711: // }
0712: // }
0713: //
0714: // public Query newNamedQuery(Class cls, String queryName) {
0715: // return null;
0716: // }
0717:
0718: public Query newQuery(String language, Object query) {
0719: try {
0720: if (language != null) {
0721: language = language.toUpperCase();
0722: }
0723: if (LANGUAGE_SQL.equals(language)) {
0724: VersantQueryImp clientQuery = new VersantQueryImp(
0725: proxy, QueryDetails.LANGUAGE_SQL);
0726: clientQuery.setFilter((String) query);
0727: return clientQuery;
0728:
0729: } else if (LANGUAGE_EJBQL.equals(language)) {
0730: VersantQueryImp clientQuery = new VersantQueryImp(
0731: proxy, QueryDetails.LANGUAGE_EJBQL);
0732: clientQuery.setFilter((String) query);
0733: return clientQuery;
0734: } else {
0735: return newQuery(query);
0736: }
0737: } catch (Exception e) {
0738: handleException(e);
0739: return null;
0740: }
0741: }
0742:
0743: public Query newQuery(Class cls) {
0744: try {
0745: VersantQueryImp clientQuery = new VersantQueryImp(proxy);
0746: clientQuery.setClass(cls);
0747: return clientQuery;
0748: } catch (Exception e) {
0749: handleException(e);
0750: return null;
0751: }
0752: }
0753:
0754: public Query newQuery(Extent extent) {
0755: try {
0756: VersantQueryImp clientQuery = new VersantQueryImp(proxy);
0757: clientQuery.setCandidates(extent);
0758: return clientQuery;
0759: } catch (Exception e) {
0760: handleException(e);
0761: return null;
0762: }
0763: }
0764:
0765: public Query newQuery(Extent extent, String filter) {
0766: try {
0767: VersantQueryImp clientQuery = new VersantQueryImp(proxy);
0768: clientQuery.setCandidates(extent);
0769: clientQuery.setFilter(filter);
0770: return clientQuery;
0771: } catch (Exception e) {
0772: handleException(e);
0773: return null;
0774: }
0775: }
0776:
0777: public Query newQuery(Class cls, Collection cln) {
0778: try {
0779: VersantQueryImp clientQuery = new VersantQueryImp(proxy);
0780: clientQuery.setClass(cls);
0781: clientQuery.setCandidates(cln);
0782: return clientQuery;
0783: } catch (Exception e) {
0784: handleException(e);
0785: return null;
0786: }
0787: }
0788:
0789: public Query newQuery(Class cls, String filter) {
0790: try {
0791: VersantQueryImp clientQuery = new VersantQueryImp(proxy);
0792: clientQuery.setClass(cls);
0793: clientQuery.setFilter(filter);
0794: return clientQuery;
0795: } catch (Exception e) {
0796: handleException(e);
0797: return null;
0798: }
0799: }
0800:
0801: public Query newQuery(Class cls, Collection cln, String filter) {
0802: try {
0803: VersantQueryImp clientQuery = new VersantQueryImp(proxy);
0804: clientQuery.setClass(cls);
0805: clientQuery.setCandidates(cln);
0806: clientQuery.setFilter(filter);
0807: return clientQuery;
0808: } catch (Exception e) {
0809: handleException(e);
0810: return null;
0811: }
0812: }
0813:
0814: public Class getObjectIdClass(Class cls) {
0815: try {
0816: if (cls == null) {
0817: return null;
0818: // throw new JDOUserException("The supplied Class param is null");
0819: }
0820: if (!PersistenceCapable.class.isAssignableFrom(cls)) {
0821: return null;
0822: }
0823: if (Modifier.isAbstract(cls.getModifiers())) {
0824: return null;
0825: }
0826: ClassMetaData cmd = modelMetaData.getClassMetaData(cls);
0827: if (cmd == null) {
0828: throw BindingSupportImpl.getInstance()
0829: .invalidOperation(
0830: "The class is not specified as "
0831: + PersistenceCapable.class
0832: .getName()
0833: + " for the application");
0834: }
0835: if (cmd.identityType == MDStatics.IDENTITY_TYPE_APPLICATION) {
0836: return cmd.objectIdClass;
0837: } else {
0838: return VersantOid.class;
0839: }
0840: } catch (RuntimeException e) {
0841: handleException(e);
0842: return null;
0843: }
0844: }
0845:
0846: public Object newObjectIdInstance(Class cls, String s) {
0847: return newObjectIdInstance(cls, s, true);
0848: }
0849:
0850: public Object newObjectIdInstance(Class cls, String s,
0851: boolean resolved) {
0852: try {
0853: if (s == null || s.length() == 0) {
0854: throw BindingSupportImpl
0855: .getInstance()
0856: .invalidOperation(
0857: "Please supply an non-null, non-empty String");
0858: }
0859: if (cls == null) { // assume datastore identity
0860: return new VersantOid(modelMetaData.newOIDFromIDString(
0861: s, resolved), modelMetaData, resolved);
0862: }
0863: ClassMetaData cmd = modelMetaData.getClassMetaData(cls);
0864: if (cmd == null) {
0865: throw BindingSupportImpl.getInstance()
0866: .invalidOperation(
0867: "There is no metadata registered for class '"
0868: + cls.getName() + "'");
0869: }
0870: if (cmd.identityType == MDStatics.IDENTITY_TYPE_APPLICATION) {
0871: return jdoImplHelper.newObjectIdInstance(cls, s);
0872: } else if (cmd.identityType == MDStatics.IDENTITY_TYPE_DATASTORE) {
0873: return new VersantOid(modelMetaData.newOIDFromIDString(
0874: s, resolved), modelMetaData, resolved);
0875: } else {
0876: throw BindingSupportImpl.getInstance()
0877: .invalidOperation(
0878: "Class '" + cls.getName()
0879: + " uses non-durable identity");
0880: }
0881: } catch (RuntimeException e) {
0882: handleException(e);
0883: return null;
0884: }
0885: }
0886:
0887: public void retrieve(Object o) {
0888: try {
0889: PCStateMan pcStateObject = pmPreCheck(o);
0890: if (pcStateObject != null) {
0891: try {
0892: if (Debug.DEBUG) {
0893: if (retrieveing) {
0894: throw BindingSupportImpl
0895: .getInstance()
0896: .internal(
0897: "Retrieveing is already set");
0898: }
0899: }
0900: retrieveing = true;
0901: if (Debug.DEBUG) {
0902: if (retrieveSet.contains(pcStateObject.pc)) {
0903: throw BindingSupportImpl
0904: .getInstance()
0905: .internal(
0906: "RetrieveSet already contains pc");
0907: }
0908: }
0909: retrieveSet.add(pcStateObject.pc);
0910: pcStateObject.retrieve(this );
0911: } finally {
0912: retrieveing = false;
0913: retrieveSet.clear();
0914: }
0915: }
0916: } catch (RuntimeException e) {
0917: if (BindingSupportImpl.getInstance()
0918: .isOwnInternalException(e)) {
0919: handleException(e);
0920: } else {
0921: throw e;
0922: }
0923: }
0924: }
0925:
0926: public void retrieveImp(Object o) {
0927: if (o == null)
0928: return;
0929: PCStateMan pcStateObject = pmPreCheck(o);
0930: if (pcStateObject != null
0931: && !retrieveSet.contains(pcStateObject.pc)) {
0932: retrieveSet.add(pcStateObject.pc);
0933: pcStateObject.retrieve(this );
0934: }
0935: }
0936:
0937: public void retrieveAllImp(Object[] toRetrieve) {
0938: for (int i = 0; i < toRetrieve.length; i++) {
0939: retrieveImp(toRetrieve[i]);
0940: }
0941: }
0942:
0943: public void retrieveAllImp(Collection toRetrieve) {
0944: retrieveAllImp(toRetrieve.toArray());
0945: }
0946:
0947: public void retrieveAll(Collection collection) {
0948: try {
0949: retrieveAll(collection.toArray());
0950: } catch (Exception e) {
0951: handleException(e);
0952: }
0953: }
0954:
0955: public void retrieveAll(Collection collection, boolean b) {
0956: retrieveAll(collection);
0957: }
0958:
0959: public void retrieveAll(Object[] objects, boolean b) {
0960: retrieveAll(objects);
0961: }
0962:
0963: public void retrieveAll(Object[] objects) {
0964: try {
0965: try {
0966: if (Debug.DEBUG) {
0967: if (retrieveing) {
0968: throw BindingSupportImpl.getInstance()
0969: .internal("Retrieveing is already set");
0970: }
0971: }
0972: retrieveing = true;
0973: for (int i = 0; i < objects.length; i++) {
0974: retrieveImp(objects[i]);
0975: }
0976: } finally {
0977: retrieveing = false;
0978: retrieveSet.clear();
0979: }
0980: } catch (RuntimeException e) {
0981: if (BindingSupportImpl.getInstance()
0982: .isOwnInternalException(e)) {
0983: handleException(e);
0984: } else {
0985: throw e;
0986: }
0987: }
0988: }
0989:
0990: /**
0991: * todo keep extent's around instead of creating a new one.
0992: *
0993: * @param persistenceCapableClass
0994: * @param subclasses
0995: * @return
0996: */
0997: public Extent getExtent(Class persistenceCapableClass,
0998: boolean subclasses) {
0999: try {
1000: return new ExtentImp(persistenceCapableClass, subclasses,
1001: proxy);
1002: } catch (Exception e) {
1003: handleException(e);
1004: return null;
1005: }
1006: }
1007:
1008: /**
1009: * Util method to return the fetchgroup index.
1010: */
1011: private int getFgIndex(OID nOID, String fetchGroupName) {
1012: int fgIndex = 0;
1013: FetchGroup fg = nOID.getAvailableClassMetaData().getFetchGroup(
1014: fetchGroupName);
1015: if (fg != null) {
1016: fgIndex = fg.index;
1017: }
1018: return fgIndex;
1019: }
1020:
1021: public Object getObjectById(Object oid, boolean validate) {
1022: if (oid == null) {
1023: throw BindingSupportImpl.getInstance().invalidOperation(
1024: "The supplied oid is null");
1025: }
1026:
1027: PersistenceCapable pc = null;
1028: try {
1029: OID nOID = extractOID(oid);
1030: PCStateMan stateMan = cache.getByOID(nOID, true);
1031: if (stateMan == null) {
1032: if (!nOID.isNew()) {
1033: /**
1034: * A call to getObjectById(id, false) with a where
1035: * (idType == appId) and (oid.cmd.usekeygen == true)
1036: * will always be validated against the db. This is done
1037: * because an inconsistent mapping
1038: * may arise.
1039: *
1040: * Scenario: A new instance of this type is created and made
1041: * persistent. This instance is now in the
1042: * managed cache with a internal tmp oid.
1043: * A call to getObjectById(id, false) is made with a id
1044: * value of 2 for example. This instance will also
1045: * end up in the managed cache with a real app id with value 2.
1046: * On a call to commit the keygen will allocate a value to
1047: * the new id instance. If this value is also
1048: * assigned the value '2' then there will both instances
1049: * wants the same id but the are already assigned
1050: * different pc instance values.
1051: *
1052: * Also added check for an unresolved oid that is in a hierarchy.
1053: * Such oids are also always resolved.
1054: * This could be changed later to keep such an instance in an
1055: * initialised state.
1056: */
1057: if (validate
1058: || (nOID.getAvailableClassMetaData().useKeyGen && nOID
1059: .getAvailableClassMetaData().identityType == MDStatics.IDENTITY_TYPE_APPLICATION)
1060: || (!nOID.isResolved() && nOID
1061: .getAvailableClassMetaData()
1062: .isInHeirachy())) {
1063: checkNonTxRead();
1064: OID nOIDcopy = nOID.copy();
1065: stateMan = getStateMan(nOIDcopy, 0, -1, -1,
1066: validate);
1067:
1068: if (stateMan == null) {
1069: throw BindingSupportImpl
1070: .getInstance()
1071: .objectNotFound(
1072: "No row for "
1073: + nOID
1074: .getAvailableClassMetaData().storeClass
1075: + " "
1076: + nOID.toSString());
1077: }
1078:
1079: pc = stateMan.pc;
1080: stateMan.loadDFGIntoPC(this );
1081: } else {
1082: //create the sm
1083: PCStateMan sm = getStateObject();
1084: sm.init(nOID, this );
1085: pc = sm.pc;
1086: cache.add(sm);
1087: }
1088: } else {
1089: throw BindingSupportImpl.getInstance()
1090: .objectNotFound(
1091: "No row for " + nOID.toSString());
1092: }
1093: } else {
1094: pc = stateMan.pc;
1095: if (validate && !stateMan.isTransactional(null)) {
1096: checkNonTxRead();
1097: if (stateMan.isHollow()) {
1098: stateMan.loadDfgFromHollow();
1099: } else {
1100: getState(stateMan.oid, null, 0, -1, -1, true);
1101: }
1102: }
1103: }
1104: return pc;
1105: } catch (Exception e) {
1106: handleException(e);
1107: return null;
1108: }
1109: }
1110:
1111: public OID extractOID(Object oid) {
1112: OID nOID;
1113: if (oid instanceof VersantOid) {
1114: nOID = modelMetaData
1115: .convertJDOGenieOIDtoOID((VersantOid) oid);
1116: nOID = convertNewToActual(nOID);
1117: } else if (oid instanceof OID) {
1118: nOID = convertNewToActual((OID) oid);
1119: } else {
1120: nOID = modelMetaData.convertFromAppIdToOID(oid);
1121: }
1122: return nOID;
1123: }
1124:
1125: /**
1126: * This is use by State implementations when they need to retrieve an
1127: * instance. This will typically be a result of graph navigation.
1128: * The oid parameter is not used when an embedded reference field is
1129: * being retrieved.
1130: */
1131: public Object getObjectByIdForState(OID oid, int stateFieldNo,
1132: int navClassIndex, OID fromOID) {
1133: try {
1134: PersistenceCapable pc;
1135: FieldMetaData fmd = fromOID.getAvailableClassMetaData().stateFields[stateFieldNo];
1136: if (fmd.embedded) {
1137: //create a managed instance of the embedded reference
1138: PCStateMan owningSM = cache.getByOID(fromOID, false);
1139: if (fmd.nullIndicatorFmd != null) {
1140: if (owningSM.state
1141: .isFieldNullorZero(fmd.nullIndicatorFmd.stateFieldNo)) {
1142: return null;
1143: }
1144: }
1145: EmbeddedStateManager embeddedSm = owningSM
1146: .createEmbeddedSM(fmd);
1147: pc = embeddedSm.pc;
1148: } else {
1149: PCStateMan stateMan = cache.getByOID(oid, true);
1150: if (stateMan == null) {
1151: stateMan = getStateMan(oid, 0, stateFieldNo,
1152: navClassIndex, false);
1153: if (stateMan != null) {
1154: pc = stateMan.pc;
1155: stateMan.loadDFGIntoPC(this );
1156: } else {
1157: pc = null;
1158: }
1159: } else {
1160: pc = stateMan.pc;
1161: if (!stateMan.isTransactional(null)) {
1162: checkNonTxRead();
1163: //TODO why loadDfg. Is it not better to leave the state hollow
1164: if (stateMan.isHollow())
1165: stateMan.loadDfgFromHollow();
1166: }
1167: }
1168: }
1169: return pc;
1170: } catch (Exception e) {
1171: handleException(e);
1172: return null;
1173: }
1174: }
1175:
1176: public final void checkNonTxRead() {
1177: if (!(transactionActive || nontransactionalRead)) {
1178: throw BindingSupportImpl.getInstance().invalidOperation(
1179: "Must set nonTransactionalRead to true");
1180: }
1181: }
1182:
1183: public final void checkNonTxWrite() {
1184: if (!(transactionActive || nontransactionalWrite)) {
1185: throw BindingSupportImpl.getInstance().invalidOperation(
1186: "Must set nonTransactionalWrite to true");
1187: }
1188: }
1189:
1190: public boolean doBeforeState(boolean isTransient,
1191: boolean isTransactional) {
1192: return ((restoreValues || isTransient || isTransactional) && transactionActive);
1193: }
1194:
1195: private OID convertNewToActual(OID oid) {
1196: if (!oid.isNew()) {
1197: oid.getAvailableClassMetaData();
1198: return oid;
1199: }
1200: return oid.getAvailableOID();
1201: }
1202:
1203: public void makePersistent(final Object o) {
1204: try {
1205: if (o == null) {
1206: throw BindingSupportImpl
1207: .getInstance()
1208: .invalidOperation(
1209: "makePersistent called with null object");
1210: }
1211: checkActiveTx();
1212: PCStateMan root = txDirtyListHead;
1213: makeReachablePersistent(o);
1214: if (root == null) {
1215: root = txDirtyListTail;
1216: } else {
1217: root = root.next;
1218: }
1219:
1220: for (; root != null; root = root.next)
1221: root.addRefs();
1222: } catch (Exception e) {
1223: handleException(e);
1224: }
1225: }
1226:
1227: /**
1228: * Make an instance found in a reachability search persistent. This skips
1229: * some checks done by makePersistent for speed. Note that this does not
1230: * add reachable instances.<p>
1231: */
1232: public void makeReachablePersistent(final Object o) {
1233: if (o == null)
1234: return;
1235: PersistenceCapable pc = checkPersCapable(o);
1236: PersistenceManager pm = pc.jdoGetPersistenceManager();
1237: if (pm == null) {
1238: reManage(pc, assignOID(pc), false);
1239: } else {
1240: PMProxy pmProxy = (PMProxy) pm;
1241: if (pmProxy.getRealPM() != this ) {
1242: throw BindingSupportImpl.getInstance()
1243: .invalidOperation(
1244: "Object is managed by " + pm
1245: + " (this is " + proxy + "): "
1246: + pc.getClass() + ": "
1247: + Utils.toString(pc));
1248: }
1249: }
1250: }
1251:
1252: public void makePersistentAll(final Object[] pcs) {
1253: try {
1254: ArrayList failed = null;
1255: for (int i = 0; i < pcs.length; i++) {
1256: Object pc = pcs[i];
1257: try {
1258: makePersistent(pc);
1259: } catch (RuntimeException e) {
1260: if (BindingSupportImpl.getInstance()
1261: .isOwnException(e)) {
1262: if (BindingSupportImpl.getInstance()
1263: .getFailedObject(e) == null) {
1264: e = BindingSupportImpl.getInstance()
1265: .exception(e.getMessage(), e, pc);
1266: }
1267: if (failed == null)
1268: failed = new ArrayList();
1269: failed.add(e);
1270: } else {
1271: throw e;
1272: }
1273: }
1274: }
1275: if (failed != null) {
1276: int n = failed.size();
1277: if (n == 1) {
1278: throw (Exception) failed.get(0);
1279: } else {
1280: Throwable[] a = new Throwable[n];
1281: failed.toArray(a);
1282: throw BindingSupportImpl.getInstance().exception(
1283: n + " instances failed to persist", a);
1284: }
1285: }
1286: } catch (Exception e) {
1287: handleException(e);
1288: }
1289: }
1290:
1291: public void makePersistentAll(final Collection pcs) {
1292: try {
1293: ArrayList failed = null;
1294: for (Iterator i = pcs.iterator(); i.hasNext();) {
1295: Object pc = i.next();
1296: try {
1297: makePersistent(pc);
1298: } catch (RuntimeException e) {
1299: if (BindingSupportImpl.getInstance()
1300: .isOwnException(e)) {
1301: if (BindingSupportImpl.getInstance()
1302: .getFailedObject(e) == null) {
1303: e = BindingSupportImpl.getInstance()
1304: .exception(e.getMessage(), e, pc);
1305: }
1306: if (failed == null)
1307: failed = new ArrayList();
1308: failed.add(e);
1309: } else {
1310: throw e;
1311: }
1312: }
1313: }
1314: if (failed != null) {
1315: int n = failed.size();
1316: if (n == 1) {
1317: throw (Exception) failed.get(0);
1318: } else {
1319: Throwable[] a = new Throwable[n];
1320: failed.toArray(a);
1321: throw BindingSupportImpl.getInstance().exception(
1322: n + " instances failed to persist", a);
1323: }
1324: }
1325: } catch (Exception e) {
1326: handleException(e);
1327: }
1328: }
1329:
1330: /**
1331: * @param pcs
1332: */
1333: public void deletePersistentAll(Object[] pcs) {
1334: try {
1335: for (int i = 0; i < pcs.length; i++) {
1336: deletePersistent(pcs[i]);
1337: }
1338: } catch (Exception e) {
1339: handleException(e);
1340: }
1341: }
1342:
1343: /**
1344: * @param pc
1345: */
1346: public void deletePersistent(Object pc) {
1347: checkActiveTx();
1348: if (isTransient(pc)) {
1349: throw BindingSupportImpl.getInstance().invalidOperation(
1350: "The instance is transient");
1351: }
1352: try {
1353: pmPreCheck(pc).deletePersistent();
1354: } catch (Exception e) {
1355: handleException(e);
1356: }
1357: }
1358:
1359: /**
1360: * @param pcs
1361: */
1362: public void deletePersistentAll(Collection pcs) {
1363: try {
1364: for (Iterator iterator = pcs.iterator(); iterator.hasNext();) {
1365: deletePersistent(iterator.next());
1366: }
1367: } catch (Exception e) {
1368: handleException(e);
1369: }
1370: }
1371:
1372: /**
1373: * TODO: Remove the instance from the weak caches.
1374: *
1375: * @param pc
1376: */
1377: public void makeTransient(Object pc) {
1378: checkPersCapable(pc);
1379: try {
1380: if (isTransient(pc)) {
1381: //the instance is already transient.
1382: return;
1383: }
1384: pmPreCheck(pc).makeTransient();
1385: } catch (Exception e) {
1386: handleException(e);
1387: }
1388: }
1389:
1390: public void makeTransientRecursive(Object pc) {
1391: checkPersCapable(pc);
1392: try {
1393: if (isTransient(pc)) {
1394: //the instance is already transient.
1395: return;
1396: }
1397: pmPreCheck(pc).makeTransientRecursive();
1398: } catch (Exception e) {
1399: handleException(e);
1400: }
1401: }
1402:
1403: /**
1404: * TODO: Remove the instance from the weak caches.
1405: *
1406: * @param pcs
1407: */
1408: public void makeTransientAll(Object[] pcs) {
1409: try {
1410: Map failed = new HashMap();
1411: for (int i = 0; i < pcs.length; i++) {
1412: Object pc = pcs[i];
1413: try {
1414: makeTransient(pc);
1415: } catch (Exception e) {
1416: failed.put(pc, e);
1417: }
1418: }
1419: if (failed.size() > 0) {
1420: throw BindingSupportImpl.getInstance()
1421: .invalidOperation(
1422: "Errors occured with makeTransientAll: "
1423: + failed);
1424: }
1425: } catch (RuntimeException e) {
1426: handleException(e);
1427: }
1428: }
1429:
1430: /**
1431: * TODO: Remove the instance from the weak caches.
1432: *
1433: * @param pcs
1434: */
1435: public void makeTransientAll(Collection pcs) {
1436: try {
1437: if (pcs == null)
1438: return;
1439: makeTransientAll(pcs.toArray());
1440: } catch (Exception e) {
1441: handleException(e);
1442: }
1443: }
1444:
1445: private final boolean isTransient(Object pc) {
1446: if (JDOHelper.getPersistenceManager(pc) == null) {
1447: return true;
1448: }
1449: return false;
1450: }
1451:
1452: public void makeTransactional(Object pc) {
1453: try {
1454: if (JDOHelper.getPersistenceManager(pc) == null) {
1455: //this is a transient instance
1456: reManage((PersistenceCapable) pc,
1457: assignOID((PersistenceCapable) pc), true);
1458: }
1459: PCStateMan pcStateObject = pmPreCheck(pc);
1460: pcStateObject.makeTransactional();
1461: } catch (Exception e) {
1462: handleException(e);
1463: }
1464: }
1465:
1466: public void makeTransactionalAll(Object[] pcs) {
1467: if (pcs == null)
1468: return;
1469: try {
1470: Map failed = new HashMap(pcs.length);
1471: for (int i = 0; i < pcs.length; i++) {
1472: Object pc = pcs[i];
1473: try {
1474: makeTransactional(pc);
1475: } catch (Exception e) {
1476: failed.put(pc, e);
1477: }
1478: }
1479: if (failed.size() > 0) {
1480: throw BindingSupportImpl.getInstance()
1481: .invalidOperation(
1482: "Errors occured with makePersistentAll:"
1483: + failed);
1484: }
1485: } catch (RuntimeException e) {
1486: handleException(e);
1487: }
1488: }
1489:
1490: public void makeTransactionalAll(Collection pcs) {
1491: if (pcs == null)
1492: return;
1493: try {
1494: makeTransactionalAll(pcs.toArray());
1495: } catch (Exception e) {
1496: handleException(e);
1497: }
1498: }
1499:
1500: private final void makeNonTransactionalImp(Object pc) {
1501: PCStateMan pcStateObject = pmPreCheck(pc);
1502: pcStateObject.makeNonTransactional();
1503: }
1504:
1505: public void makeNontransactional(Object pc) {
1506: try {
1507: makeNonTransactionalImp(pc);
1508: } catch (Exception e) {
1509: handleException(e);
1510: }
1511: }
1512:
1513: public void makeNontransactionalAll(Object[] pcs) {
1514: try {
1515: for (int i = 0; i < pcs.length; i++) {
1516: makeNonTransactionalImp(pcs[i]);
1517: }
1518: } catch (Exception e) {
1519: handleException(e);
1520: }
1521: }
1522:
1523: public void makeNontransactionalAll(Collection pcs) {
1524: for (Iterator iterator = pcs.iterator(); iterator.hasNext();) {
1525: makeNonTransactionalImp(iterator.next());
1526: }
1527: }
1528:
1529: public void setUserObject(Object o) {
1530: try {
1531: sm.setUserObject(userObject = o);
1532: } catch (Exception e) {
1533: handleException(e);
1534: }
1535: }
1536:
1537: public Object getUserObject() {
1538: return userObject;
1539: }
1540:
1541: public PersistenceManagerFactory getPersistenceManagerFactory() {
1542: return pmf;
1543: }
1544:
1545: public void setMultithreaded(boolean flag) {
1546: // Changing from multithreaded false to true is not allowed. This must
1547: // be done at the PMF level before the PM is created.
1548: if (flag && !multithreaded) {
1549: throw BindingSupportImpl
1550: .getInstance()
1551: .invalidOperation(
1552: "PM.setMultithreaded(true) is not allowed if the PM "
1553: + "was created from a PMF with multithreaded false");
1554: }
1555: // ignore the true -> false transition as the extra synchronization
1556: // has only a performance impact
1557: }
1558:
1559: public boolean getMultithreaded() {
1560: return multithreaded;
1561: }
1562:
1563: public void setMultithreadedImp(boolean flag) {
1564: multithreaded = flag;
1565: createProxy();
1566: }
1567:
1568: public void setIgnoreCache(boolean flag) {
1569: this .ignoreCache = flag;
1570: }
1571:
1572: public boolean getIgnoreCache() {
1573: return this .ignoreCache;
1574: }
1575:
1576: /**
1577: * Does a preCheck on a object claimed to be PersistanceCapable and managed by
1578: * this pm.
1579: */
1580: private final PCStateMan pmPreCheck(final Object pc) {
1581: return pmPreCheck(checkPersCapable(pc));
1582:
1583: }
1584:
1585: private PCStateMan pmPreCheck(PersistenceCapable pc) {
1586: if (!checkManagedBy(pc)) {
1587: return null;
1588: }
1589: PCStateMan pcState = getInternalSM(pc);
1590: if (Debug.DEBUG) {
1591: if (pcState == null
1592: && JDOHelper.getPersistenceManager(pc) != null) {
1593: throw BindingSupportImpl
1594: .getInstance()
1595: .internal(
1596: "The pm is set on instance but is not in weak list");
1597: }
1598: }
1599: return pcState;
1600: }
1601:
1602: private final boolean checkManagedBy(PersistenceCapable pc) {
1603: PMProxy pm = (PMProxy) pc.jdoGetPersistenceManager();
1604: if (pm != null) {
1605: if (pm == proxy)
1606: return true;
1607: throw BindingSupportImpl.getInstance().invalidOperation(
1608: "Object is managed by " + pm + " (this is " + proxy
1609: + "): " + pc.getClass() + ": "
1610: + Utils.toString(pc));
1611: }
1612: return false;
1613: }
1614:
1615: private final void checkActiveTx() {
1616: if (!isActive()) {
1617: throw BindingSupportImpl.getInstance().invalidOperation(
1618: "No active transaction.");
1619: }
1620: }
1621:
1622: public void begin() {
1623: if (managed) {
1624: throw BindingSupportImpl
1625: .getInstance()
1626: .invalidOperation(
1627: "May not call begin in managed transaction environment");
1628: }
1629: beginImp();
1630: }
1631:
1632: private void beginImp() {
1633: if (transactionActive) {
1634: throw BindingSupportImpl.getInstance().invalidOperation(
1635: "The transaction is already active");
1636: }
1637: try {
1638: transactionActive = true;
1639: sm.begin(optimistic);
1640: if (!optimistic && !interceptDfgFieldAccess) {
1641: cache.setInterceptDfgFieldAccess(true);
1642: }
1643: } catch (Exception e) {
1644: handleException(e);
1645: }
1646: }
1647:
1648: /**
1649: * This is for client to rollback the connection. This will try to
1650: * orderly rollback the data. The pm will be reset if an internal exception
1651: * happens during rollback. The pm must be reset as new to avoid inconsistent behaviour.
1652: * <p/>
1653: * NB
1654: * TODO ensure that all tx instances are rolledback properly.
1655: * If for instance the are already removed out of the tx list at commit
1656: * time then they can not undergo a rollback.
1657: */
1658: public void rollback() {
1659: if (Debug.DEBUG) {
1660: System.out
1661: .println(">>>>>>>>>>> JdoGeniePersistenceManagerImp.rollback <<<<<<<<<<<<<<<");
1662: }
1663: if (managed) {
1664: throw BindingSupportImpl
1665: .getInstance()
1666: .invalidOperation(
1667: "May not call rollback in managed transaction environment");
1668: }
1669: rollbackImp();
1670: }
1671:
1672: /**
1673: * Reset this PM after an internal error of some kind. This just flags
1674: * it to not go back in the pool.
1675: */
1676: private void fatalReset() {
1677: mustNotPool = true;
1678: }
1679:
1680: /**
1681: * This is called by JDOConnectionImpProxy when it handles a
1682: * JDOFatalException from a call to the JDOConnection.
1683: */
1684: public void rollbackForFatalExceptionInJDOConnectionProxy() {
1685: if (managed || !transactionActive)
1686: return;
1687: rollbackImp();
1688: }
1689:
1690: /**
1691: * This is an orderly rollback.
1692: */
1693: private void rollbackImp() {
1694: if (busyWithRollback)
1695: return;
1696: checkActiveTx();
1697: resetEpcFields();
1698: try {
1699: busyWithRollback = true;
1700:
1701: if (synchronizationInstance != null) {
1702: synchronizationInstance
1703: .afterCompletion(Status.STATUS_ROLLING_BACK);
1704: }
1705:
1706: try {
1707: cache.doRollback(retainValues);
1708: sm.rollback();
1709: } finally {
1710: reset();
1711: }
1712:
1713: if (synchronizationInstance != null) {
1714: synchronizationInstance
1715: .afterCompletion(Status.STATUS_ROLLEDBACK);
1716: }
1717:
1718: } catch (Exception e) {
1719: fatalReset();
1720: if (BindingSupportImpl.getInstance().isOwnException(e)) {
1721: throw (RuntimeException) e;
1722: } else {
1723: throw BindingSupportImpl.getInstance().internal(
1724: e.getMessage(), e);
1725: }
1726: } finally {
1727: transactionActive = false;
1728: busyWithRollback = false;
1729: }
1730: }
1731:
1732: public void setRestoreValues(boolean b) {
1733: checkPropChange();
1734: this .restoreValues = b;
1735: }
1736:
1737: public boolean getRestoreValues() {
1738: return restoreValues;
1739: }
1740:
1741: /**
1742: * Check the consistency of all instances in the local cache. Currently
1743: * this makes sure that all birectional relationships have been completed
1744: * properly (both sides in sync) but other checks may will be added in
1745: * future. This method is very slow and should only be used for debugging
1746: * during development.
1747: *
1748: * @see #setCheckModelConsistencyOnCommit(boolean)
1749: */
1750: public void checkModelConsistency() {
1751: cache.checkModelConsistency();
1752: }
1753:
1754: /**
1755: * Add the OID and State for deletion.
1756: */
1757: public void addForDelete(OID oid, State state) {
1758: if (Debug.DEBUG) {
1759: // make sure untyped OIDs are not added
1760: if (oid.getAvailableClassMetaData() == null) {
1761: BindingSupportImpl.getInstance().internal(
1762: "oid is untyped: " + oid);
1763: }
1764: }
1765: toBeDeleted.add(oid, state);
1766: }
1767:
1768: /**
1769: * This calls commit on all the transactional objects. It ensures
1770: * that the instances undergo the correct state changes.
1771: */
1772: public void commit() {
1773: if (managed) {
1774: throw BindingSupportImpl
1775: .getInstance()
1776: .invalidOperation(
1777: "May not call commit in managed transaction environment");
1778: }
1779: if (!transactionActive) {
1780: throw BindingSupportImpl.getInstance().invalidOperation(
1781: "Transaction is not active");
1782: }
1783: internalCommit(false);
1784: }
1785:
1786: private void dumpTxDirtyList(String msg) {
1787: System.out.println("--- txDirtyListHead: " + msg);
1788: for (PCStateMan o = txDirtyListTail; o != null; o = o.next) {
1789: System.out.println(o.pc.getClass().getName()
1790: + "@"
1791: + Integer
1792: .toHexString(System.identityHashCode(o.pc))
1793: + ": " + o.pc);
1794: }
1795: System.out.println("---");
1796: }
1797:
1798: private void internalCommit(boolean phase) {
1799: StatesReturned sc = null;
1800:
1801: if (synchronizationInstance != null) {
1802: synchronizationInstance.beforeCompletion();
1803: synchronizationInstance
1804: .afterCompletion(Status.STATUS_COMMITTING);
1805: }
1806:
1807: try {
1808: prepareForStore(true);
1809:
1810: sc = sm
1811: .store(
1812: storeOidStateContainer,
1813: toBeDeleted,
1814: retainValues,
1815: phase ? StorageManager.STORE_OPTION_PREPARE
1816: : StorageManager.STORE_OPTION_COMMIT,
1817: false);
1818:
1819: resetEpcFields();
1820: updateOIDsAndDoAutoS(sc);
1821: cache.doCommit(retainValues);
1822:
1823: invokePostStore();
1824:
1825: if (!phase) {
1826:
1827: if (synchronizationInstance != null) {
1828: synchronizationInstance
1829: .afterCompletion(Status.STATUS_COMMITTED);
1830: }
1831:
1832: }
1833:
1834: reset();
1835: transactionActive = false;
1836: } catch (Exception e) {
1837: handleException(e);
1838: } finally {
1839: if (sc != null) {
1840: sc.clear();
1841: }
1842: }
1843: }
1844:
1845: /**
1846: * Prepare to store all dirty instances for a commit or flush. This finds
1847: * all reachable instances and invokes preStore lifecycle listeners and
1848: * jdoPreStore instance callbacks.
1849: */
1850: private void prepareForStore(boolean commit) {
1851: boolean preStoreCalled = false;
1852: for (PCStateMan i = txDirtyListTail; i != null; i = i.next) {
1853:
1854: i.addRefs();
1855: preStoreCalled |= i.doJDOPreStore(listeners);
1856: }
1857: for (; preStoreCalled;) {
1858: PCStateMan root = txDirtyListHead;
1859: for (PCStateMan i = txDirtyListTail; i != null; i = i.next) {
1860:
1861: i.addRefs();
1862: }
1863: if (root == txDirtyListHead)
1864: break;
1865: preStoreCalled = false;
1866: for (PCStateMan i = root; i != null; i = i.next) {
1867: preStoreCalled |= i.doJDOPreStore(listeners);
1868: }
1869: }
1870:
1871: storeOidStateContainer.clear();
1872: toBeDeleted.clear();
1873: for (PCStateMan i = txDirtyListTail; i != null; i = i.next) {
1874: i.prepareCommitOrFlush(commit);
1875: }
1876: if (checkModelConsistencyOnCommit) {
1877: checkModelConsistency();
1878: }
1879:
1880: if (commit) { // fill in the epc stuff
1881: storeOidStateContainer.epcAll = epcAll;
1882: storeOidStateContainer.epcOids = epcObjectCount > 0 ? modelMetaData
1883: .convertToOID(epcObjects, epcObjectCount)
1884: : null;
1885: storeOidStateContainer.epcClasses = epcClasses;
1886: storeOidStateContainer.epcClassCount = epcClassCount;
1887: }
1888: }
1889:
1890: private void invokePostStore() {
1891: if (listeners != null && listeners.hasPostStoreListeners()) {
1892: for (PCStateMan i = txDirtyListTail; i != null; i = i.next) {
1893: i.firePostStore(listeners);
1894: }
1895: }
1896: }
1897:
1898: /**
1899: * This flushes everything to the store. After this state interogation
1900: * will not be correct and the behaviour is not inline with the spec.
1901: * This is used for big batch procedures that needs to free the memory.
1902: */
1903: public void flush() {
1904: StatesReturned sc = null;
1905: try {
1906: prepareForStore(true);
1907:
1908: sc = sm.store(storeOidStateContainer, toBeDeleted, false,
1909: StorageManager.STORE_OPTION_FLUSH, true);
1910:
1911: updateOIDsAndDoAutoS(sc);
1912:
1913: cache.doCommit(retainValues);
1914:
1915: invokePostStore();
1916:
1917: resetEpcFields();
1918: reset();
1919: } catch (Exception x) {
1920: handleException(x);
1921: } finally {
1922: if (sc != null) {
1923: sc.clear();
1924: }
1925: }
1926: }
1927:
1928: public void flush(boolean retainValues) {
1929: if (retainValues) {
1930: flushRetainState();
1931: } else {
1932: flush();
1933: }
1934: }
1935:
1936: public List versantAllDirtyInstances() {
1937: if (txDirtyListTail == null)
1938: return Collections.EMPTY_LIST;
1939: List l = new ArrayList();
1940: for (PCStateMan i = txDirtyListTail; i != null; i = i.next) {
1941: l.add(i.pc);
1942: }
1943: return l;
1944: }
1945:
1946: /**
1947: * This is used to flush the current changes to the store.
1948: * All state interogation will still work after this is called.
1949: * The data is just flushed to the store for queries to work.
1950: * <p/>
1951: * The implication of this is that the datastore connection will be pinned
1952: * to the jdoConnection for the life time of the transaction.
1953: * <p/>
1954: * This method will be a no-op if the transaction is not active.
1955: */
1956: public void flushRetainState() {
1957: if (!transactionActive)
1958: return;
1959: StatesReturned sc = null;
1960:
1961: storeOidStateContainer.clear();
1962: toBeDeleted.clear();
1963: try {
1964: prepareForStore(false);
1965:
1966: if (storeOidStateContainer.isEmpty()
1967: && toBeDeleted.size() == 0) {
1968: return;
1969: }
1970:
1971: sc = sm.store(storeOidStateContainer, toBeDeleted, true,
1972: StorageManager.STORE_OPTION_FLUSH, false);
1973:
1974: updateOIDsAndDoAutoS(sc);
1975: for (PCStateMan i = txDirtyListTail; i != null; i = i.next) {
1976: i.flushCommit();
1977: }
1978:
1979: invokePostStore();
1980:
1981: storeOidStateContainer.clear();
1982: toBeDeleted.clear();
1983: } catch (Exception x) {
1984: handleException(x);
1985: } finally {
1986: try {
1987: sc.clear();
1988: } catch (Exception e) {
1989: //ignore
1990: }
1991: }
1992: }
1993:
1994: /**
1995: * This will do everything except actually commit to the store.
1996: */
1997: public void phaseCommit1() {
1998: internalCommit(true);
1999: }
2000:
2001: /**
2002: * Do the actual commit on the store.
2003: */
2004: public void phaseCommit2() {
2005: sm.commit();
2006:
2007: if (synchronizationInstance != null) {
2008: synchronizationInstance
2009: .afterCompletion(Status.STATUS_COMMITTED);
2010: }
2011:
2012: }
2013:
2014: public boolean isActive() {
2015: return transactionActive;
2016: }
2017:
2018: public final boolean isActiveDS() {
2019: return transactionActive && !optimistic;
2020: }
2021:
2022: public void setNontransactionalRead(boolean nontransactionalRead) {
2023: checkPropChange();
2024: this .nontransactionalRead = nontransactionalRead;
2025: }
2026:
2027: public boolean getNontransactionalRead() {
2028: return nontransactionalRead;
2029: }
2030:
2031: public void setNontransactionalWrite(boolean nontransactionalWrite) {
2032: checkPropChange();
2033: this .nontransactionalWrite = nontransactionalWrite;
2034: }
2035:
2036: public boolean getNontransactionalWrite() {
2037: return nontransactionalWrite;
2038: }
2039:
2040: public void setRetainValues(boolean retainValues) {
2041: // checkPropChange();
2042: this .retainValues = retainValues;
2043: }
2044:
2045: /**
2046: * Check for changing props in active tx.
2047: */
2048: private final void checkPropChange() {
2049: if (transactionActive) {
2050: throw BindingSupportImpl.getInstance().invalidOperation(
2051: "May not be changed in active transaction");
2052: }
2053: }
2054:
2055: public boolean getRetainValues() {
2056: return retainValues;
2057: }
2058:
2059: public void setOptimistic(boolean optimistic) {
2060: checkPropChange();
2061: this .optimistic = optimistic;
2062: }
2063:
2064: public boolean getOptimistic() {
2065: return optimistic;
2066: }
2067:
2068: public void setSynchronization(Synchronization sync) {
2069: this .synchronizationInstance = sync;
2070: }
2071:
2072: public Synchronization getSynchronization() {
2073: return synchronizationInstance;
2074: }
2075:
2076: public PersistenceManager getPersistenceManager() {
2077: return VersantPersistenceManagerImp.this ;
2078: }
2079:
2080: //==================================Transaction imp end=======================
2081:
2082: /**
2083: * Create a new oid for a pc instance.
2084: */
2085: private NewObjectOID assignOID(PersistenceCapable pc) {
2086: ClassMetaData cmd = modelMetaData.getClassMetaData(pc
2087: .getClass());
2088: if (cmd == null) {
2089: throw BindingSupportImpl.getInstance().invalidOperation(
2090: "There is no metadata registered for "
2091: + pc.getClass());
2092: }
2093: if (cmd.instancesNotAllowed) {
2094: throw BindingSupportImpl.getInstance().invalidOperation(
2095: "Instances of " + cmd.qname
2096: + " may not be persistent or managed");
2097: }
2098: NewObjectOID oid = cmd.createNewObjectOID();
2099: oid.init(++counter);
2100: return oid;
2101: }
2102:
2103: /**
2104: * This is called by the JDOManagedCache when an instance is added
2105: * but it is not already managed.
2106: */
2107: public PCStateMan reManage(OID oid, State state) {
2108: if (oid.getAvailableClassMetaData() == null) {
2109: // replace untyped OID with a real one since we have a state
2110: OID tmp = state.getClassMetaData(modelMetaData).createOID(
2111: true);
2112: tmp.setLongPrimaryKey(oid.getLongPrimaryKey());
2113: oid = tmp;
2114: }
2115: PCStateMan stateObject = getStateObject();
2116: stateObject.init(oid, state.getClassMetaData(modelMetaData),
2117: state, this );
2118: return stateObject;
2119: }
2120:
2121: private final void reManage(PersistenceCapable pc, OID oid,
2122: boolean isTransactional) {
2123: PCStateMan sm = getStateObject();
2124: pc.jdoReplaceStateManager(createStateManagerProxy(sm));
2125: sm.init(pc, oid, isTransactional);
2126: sm.getRealOIDIfAppId();
2127:
2128: // check for app identity instance with same pk as instance already in
2129: // local pm cache
2130: if (!isTransactional
2131: && sm.getClassMetaData().identityType == MDStatics.IDENTITY_TYPE_APPLICATION) {
2132: OID realOID = ((NewObjectOID) sm.oid).realOID;
2133: if (realOID != null && cache.contains(realOID)) {
2134:
2135: // throwing duplicateKey would be problematic for
2136: // java, because duplicateKey is fatal
2137: throw BindingSupportImpl.getInstance().runtime(
2138: "Instance of " + sm.getClassMetaData().qname
2139: + " with identity '" + realOID
2140: + "' already exists in"
2141: + " the local PM cache");
2142:
2143: }
2144: }
2145:
2146: //The refs to the wrapper is kept around to avoid gc of the instance
2147: cache.add(sm);
2148: if (Debug.DEBUG) {
2149: if (sm.cacheEntry == null) {
2150: throw BindingSupportImpl.getInstance().internal(
2151: "cacheEntry must be initialized");
2152: }
2153: }
2154: }
2155:
2156: /**
2157: * Create a proxy for a PCStateMan or return it as is if no proxy is
2158: * needed. This is used to synchronize StateManager access when
2159: * multithreading is required.
2160: */
2161: public StateManager createStateManagerProxy(PCStateMan sm) {
2162: if (multithreaded) {
2163: return new SynchronizedStateManagerProxy(proxy, sm);
2164: } else {
2165: return sm;
2166: }
2167: }
2168:
2169: /**
2170: * add an transactional instance to the list.
2171: *
2172: * @param stateObject
2173: */
2174: public void addTxStateObject(PCStateMan stateObject) {
2175: if (Debug.DEBUG) {
2176: if (!stateObject.isTx()) {
2177: throw BindingSupportImpl.getInstance().internal(
2178: "The instance is not Transactional");
2179: }
2180: }
2181:
2182: if (stateObject.isDirty()) {
2183: if (!stateObject.isInDirtyList(txDirtyListHead)) {
2184: addTxDirty(stateObject);
2185: }
2186: } else {
2187: if (stateObject.isInDirtyList(txDirtyListHead)) {
2188: removeTxDirty(stateObject);
2189: }
2190: }
2191: }
2192:
2193: /**
2194: * Add pc to the dirty list.
2195: */
2196: private void addTxDirty(PCStateMan pc) {
2197: if (txDirtyListHead == null) {
2198: txDirtyListHead = txDirtyListTail = pc;
2199: pc.next = null;
2200: pc.prev = null;
2201: } else {
2202: pc.prev = txDirtyListHead;
2203: txDirtyListHead.next = pc;
2204: pc.next = null;
2205: txDirtyListHead = pc;
2206: }
2207: dirtyCmdBits.add(pc.getClassMetaData());
2208: pc.inDirtyList = true;
2209: }
2210:
2211: /**
2212: * Remove pc from the dirty list.
2213: */
2214: private void removeTxDirty(PCStateMan pc) {
2215: if (Debug.DEBUG) {
2216: if (!pc.isInDirtyList(txDirtyListHead)) {
2217: throw BindingSupportImpl.getInstance().internal(
2218: "not in dirty list: " + pc);
2219: }
2220: }
2221: if (txDirtyListTail == pc) {
2222: txDirtyListTail = pc.next;
2223: } else {
2224: pc.prev.next = pc.next;
2225: }
2226: if (txDirtyListHead == pc) {
2227: txDirtyListHead = pc.prev;
2228: } else {
2229: pc.next.prev = pc.prev;
2230: }
2231: dirtyCmdBits.remove(pc.getClassMetaData());
2232: }
2233:
2234: /**
2235: * Clear the dirty list.
2236: */
2237: public void clearTxDirtyList() {
2238: for (PCStateMan i = txDirtyListTail; i != null;) {
2239: PCStateMan next = i.next;
2240: i.prev = null;
2241: i.next = null;
2242: i = next;
2243: dirtyCmdBits.clear();
2244: }
2245: txDirtyListHead = txDirtyListTail = null;
2246: }
2247:
2248: public void removeTxStateObject(PCStateMan stateObject) {
2249: if (stateObject.isInDirtyList(txDirtyListHead)) {
2250: removeTxDirty(stateObject);
2251: }
2252: }
2253:
2254: private final void reset() {
2255: storeOidStateContainer.clear();
2256: clearTxDirtyList();
2257: }
2258:
2259: /**
2260: * This will reset the pm to be returned to the pool.
2261: */
2262: public void resetForPooling() {
2263: if (Debug.DEBUG) {
2264: if (transactionActive) {
2265: throw BindingSupportImpl.getInstance().fatal(
2266: "The tx must be inactive");
2267: }
2268: }
2269: managed = false;
2270: cache.clear();
2271: sm.reset();
2272: sm.setUserObject(userObject = null);
2273:
2274: synchronizationInstance = null;
2275:
2276: resetEpcFields();
2277: reset();
2278: }
2279:
2280: /**
2281: * Check to see if the pm has not been closed by the user.
2282: */
2283: private void checkClosed() {
2284: checkInPool();
2285: if (closed) {
2286: throw BindingSupportImpl.getInstance().invalidOperation(
2287:
2288: "The 'PersistenceManager' is already closed");
2289:
2290: }
2291: }
2292:
2293: public PersistenceManager getPersistenceManager(
2294: PersistenceCapable pc) {
2295: return this ;
2296: }
2297:
2298: private PCStateMan getStateObject() {
2299: return new PCStateMan(cache, modelMetaData, proxy);
2300: }
2301:
2302: // public Collection mapFrom(Object data, Class cls) {
2303: // if (data instanceof ResultSet) {
2304: // return null;
2305: // } else {
2306: // throw BindingSupportImpl.getInstance().unsupported();
2307: // }
2308: // }
2309: //
2310: // public Collection mapFrom(Object data, Class cls, String[] customMapping) {
2311: // if (data instanceof ResultSet) {
2312: // return null;
2313: // } else {
2314: // throw BindingSupportImpl.getInstance().unsupported();
2315: // }
2316: // }
2317:
2318: //============================debug methods ============================================================================
2319:
2320: public boolean isPNonTx(Object pc) {
2321: return pmPreCheck(pc).isPNonTx();
2322: }
2323:
2324: public boolean isPClean(Object pc) {
2325: return pmPreCheck(pc).isPClean();
2326: }
2327:
2328: public boolean isPNew(Object pc) {
2329: return pmPreCheck(pc).isPNew();
2330: }
2331:
2332: public boolean isPNewDeleted(Object pc) {
2333: return pmPreCheck(pc).isPNewDeleted();
2334: }
2335:
2336: public boolean isPDeleted(Object pc) {
2337: return pmPreCheck(pc).isPDeleted();
2338: }
2339:
2340: public boolean isTClean(Object pc) {
2341: return pmPreCheck(pc).isTClean();
2342: }
2343:
2344: public boolean isTDirty(Object pc) {
2345: return pmPreCheck(pc).isTDirty();
2346: }
2347:
2348: public boolean isPDirty(Object pc) {
2349: return pmPreCheck(pc).isPDirty();
2350: }
2351:
2352: /**
2353: * Return the internal oid representation for this pc instance.
2354: * This oid is the actual oid and not a clone. If pc is null then null
2355: * is returned.
2356: */
2357: public OID getInternalOID(final PersistenceCapable pc) {
2358: if (pc == null)
2359: return null;
2360: return getInternalSM(pc).oid;
2361: }
2362:
2363: /**
2364: * This is used internally to obtain the PCStateObject for a PersistanceCapable
2365: * instance. This instance must be managed by this pm.
2366: */
2367: public PCStateMan getInternalSM(final PersistenceCapable pc) {
2368: if (pc == null)
2369: return null;
2370: requestedPCState = null;
2371: pc.jdoGetPersistenceManager();
2372: if (requestedPCState == null) {
2373: throw BindingSupportImpl.getInstance().internal(
2374: "Instance not managed: "
2375: + pc.getClass().getName()
2376: + "@"
2377: + Integer.toHexString(System
2378: .identityHashCode(pc)) + ": "
2379: + pc.toString());
2380: }
2381: return requestedPCState;
2382: }
2383:
2384: public PCStateMan getSMIfManaged(PersistenceCapable pc) {
2385: if (pc == null)
2386: return null;
2387: requestedPCState = null;
2388: pc.jdoGetPersistenceManager();
2389: return requestedPCState;
2390: }
2391:
2392: /**
2393: * This is used internally to obtain the PCStateObject for a PersistanceCapable
2394: * instance. This instance must be managed by this pm.
2395: */
2396: public PCStateMan getInternalSM(OID oid) {
2397: if (oid == null)
2398: return null;
2399: return getInternalSM((PersistenceCapable) getObjectById(oid,
2400: false));
2401: }
2402:
2403: /**
2404: * This is used for debug. Not to be used else where.
2405: */
2406: public State getInternaleState(PersistenceCapable pc) {
2407: PCStateMan pcState = getInternalSM(pc);
2408: if (pcState == null)
2409: return null;
2410: return pcState.state;
2411: }
2412:
2413: public void dump(OID oid) {
2414: if (Debug.DEBUG) {
2415: PCStateMan pcStateObject = cache.getByOID(oid, true);
2416: if (pcStateObject != null) {
2417: pcStateObject.dump();
2418: } else {
2419: if (Debug.DEBUG) {
2420: Debug.OUT.println("######## null for dump#######");
2421: }
2422: }
2423: }
2424: }
2425:
2426: /**
2427: * Are there any dirty instances?
2428: */
2429: public boolean isDirty() {
2430: return txDirtyListHead != null;
2431: }
2432:
2433: /**
2434: * This is used from getObjectByID where the object by id is not in the managed cache.
2435: * If there there is no jdo instance for the supplied instance then null is returned.
2436: *
2437: * @param ignoreLocalPmCache This is used to indicate that we should not check the
2438: * if the state is in the localCache on the serverside. The serverSide might
2439: * have a ref to the local cache.
2440: */
2441: private PCStateMan getStateMan(OID aOID, int fgIndex, int fieldNo,
2442: int navClassIndex, boolean ignoreLocalPmCache) {
2443: StatesReturned container = null;
2444: try {
2445: container = getStateJdoConnection(aOID, null, fgIndex, aOID
2446: .getAvailableClassId(), ignoreLocalPmCache,
2447: fieldNo, navClassIndex);
2448:
2449: if (Debug.DEBUG) {
2450: if (container.get(container.getDirectOID()) == NULLState.NULL_STATE) {
2451: //this instance does not exist in db
2452: if (container.size() != 1) {
2453: throw BindingSupportImpl
2454: .getInstance()
2455: .internal(
2456: "Then directOid of the container is null "
2457: + "so there should not be any other instances");
2458: }
2459: }
2460: }
2461:
2462: return addAndReturnFirstDirect(container);
2463: } finally {
2464: if (container != null)
2465: container.clear();
2466: }
2467: }
2468:
2469: /**
2470: * This translates the old JDOConnection style call into a StorageManager
2471: * call. It should be nuked at some point.
2472: */
2473: private StatesReturned getStateJdoConnection(OID oid,
2474: State current, int fetchGroup, int classId,
2475: boolean ignoreLocalPmCache, int fieldNo, int navClassIndex) {
2476: // todo use ignoreLocalPmCache to modify context
2477:
2478: if (classId < 0)
2479: classId = oid.getAvailableClassId();
2480: ClassMetaData cmd = oid.getAvailableClassMetaData();
2481: FetchGroup fg = cmd == null ? null : cmd.getFetchGroup(
2482: fetchGroup, classId);
2483:
2484: FieldMetaData triggerField;
2485: if (navClassIndex >= 0) { // navigated stateFieldNo
2486: ClassMetaData navCmd = modelMetaData.classes[navClassIndex];
2487: triggerField = navCmd.stateFields[fieldNo];
2488: } else if (fieldNo >= 0 && cmd != null) { // missing absFieldNo
2489: triggerField = cmd.stateFields[cmd.absToRel[fieldNo]];
2490: } else {
2491: triggerField = null;
2492: }
2493:
2494: return sm.fetch(this , oid, current, fg, triggerField);
2495: }
2496:
2497: /**
2498: * This is called by sm's if they require a field. This will update the
2499: * local cache with the requested data (State instances). The returned
2500: * container reference must be kept until all the required State instances
2501: * in the local cache have been referenced or they might be GCed.
2502: * This is important for collections of PC and other fields that
2503: * involve fetching State instances and loading them into the local cache.
2504: * They are only hard referenced when the SCO instance has been created.
2505: */
2506: public StatesReturned getState(OID oid, State current, int fgi,
2507: int fieldNo, int navClassIndex, boolean ignoreLocalPmCache) {
2508: StatesReturned container = getStateJdoConnection(oid,
2509: current == null || current.isEmpty() ? null : current,
2510: fgi, oid.getAvailableClassId(), ignoreLocalPmCache,
2511: fieldNo, navClassIndex);
2512:
2513: if (Debug.DEBUG) {
2514: if (container.get(container.getDirectOID()) == NULLState.NULL_STATE) {
2515: //this instance does not exist in db
2516: if (container.size() != 1) {
2517: throw BindingSupportImpl
2518: .getInstance()
2519: .internal(
2520: "Then directOid of the container is null "
2521: + "so there should not be any other instances");
2522: }
2523: }
2524: }
2525: addToCache(container);
2526: return container;
2527: }
2528:
2529: /**
2530: * This is called by sm's when doing a refresh. It will ensure that the
2531: * data comes from the database when in a datastore transaction.
2532: */
2533: public void getStateForRefresh(OID oid, State current, int fgi) {
2534: StatesReturned container = null;
2535: try {
2536: container = getStateJdoConnection(oid, current == null
2537: || current.isEmpty() ? null : current, fgi, oid
2538: .getAvailableClassId(), true, -1, -1);
2539: addToCache(container);
2540: } finally {
2541: if (container != null)
2542: container.clear();
2543: }
2544: }
2545:
2546: private void fillCacheWith(OIDArray oids, int fgi,
2547: int stateFieldNo, int navClassIndex) {
2548: StatesReturned container = null;
2549: try {
2550: FieldMetaData triggerField;
2551: if (navClassIndex >= 0) { // navigated stateFieldNo
2552: ClassMetaData navCmd = modelMetaData.classes[navClassIndex];
2553: triggerField = navCmd.stateFields[stateFieldNo];
2554: } else {
2555: triggerField = null;
2556: }
2557: container = sm.fetch(this , oids, triggerField);
2558: addToCache(container);
2559: } finally {
2560: if (container != null)
2561: container.clear();
2562: }
2563: }
2564:
2565: /**
2566: * This is the pm is not the current PM.
2567: */
2568: private static final void checkPM(PersistenceManager otherPM,
2569: PersistenceManager currentPM) {
2570: if (otherPM != null && otherPM != currentPM) {
2571: throw BindingSupportImpl
2572: .getInstance()
2573: .invalidOperation(
2574: "The instance is not managed by this PersistenceManager");
2575: }
2576: }
2577:
2578: private static final PersistenceCapable checkPersCapable(Object o) {
2579: try {
2580: return (PersistenceCapable) o;
2581: } catch (ClassCastException e) {
2582: throw BindingSupportImpl.getInstance().invalidOperation(
2583: "The supplied instance is not of type "
2584: + PersistenceCapable.class.getName() + " ("
2585: + o.getClass().getName() + ")");
2586: }
2587: }
2588:
2589: /**
2590: * <<<<<<< VersantPersistenceManagerImp.java
2591: * This util method is used by collection types to preload their pc
2592: * entries. It tests to determine if the states refered to by the oids is
2593: * in the managed cache. If not they must be bulk loaded from server.
2594: * The scenario in which this is likely to happen is when the collection
2595: * is not in the default fetch group and the state is in cache with the
2596: * collection filled in. If this collection field is read then the
2597: * pcstateman will determine that the stateField is filled and hence not
2598: * ask the server for it.
2599: */
2600: public void checkToPreFetch(Object[] oids, int stateFieldNo,
2601: int navClassIndex) {
2602: if (oids != null && oids.length > 0 && oids[0] != null
2603: && cache.getByOID((OID) oids[0], true) == null) {
2604: OIDArray oidArray = new OIDArray();
2605: for (int i = 0; i < oids.length && oids[i] != null; i++) {
2606: oidArray.add((OID) oids[i]);
2607: }
2608: fillCacheWith(oidArray, 0, stateFieldNo, navClassIndex);
2609: }
2610: }
2611:
2612: /**
2613: * This does general exception handling. It ensures that a rollback
2614: * is done for Fatal exceptions.
2615: * CR:
2616: * If called with an instance of JDOUserException it's a no-op
2617: * and the passed instance is thrown.
2618: */
2619: private final void handleException(Exception x) {
2620: if (BindingSupportImpl.getInstance().isOwnFatalException(x)
2621: && isActive()) {
2622: try {
2623: rollbackImp();
2624: } catch (Exception e) {
2625: // discard as we are already busy processing an earlier
2626: // exception
2627: }
2628: }
2629: if (BindingSupportImpl.getInstance().isOwnInternalException(x)) {
2630: fatalReset();
2631: throw (RuntimeException) x;
2632: } else if (BindingSupportImpl.getInstance().isOwnException(x)) {
2633: throw (RuntimeException) x;
2634: } else {
2635: fatalReset();
2636: throw BindingSupportImpl.getInstance().internal(
2637: x.getMessage(), x);
2638: }
2639: }
2640:
2641: /**
2642: * Convert all PC, VersantOid and objectid-class params to OIDs. This
2643: * makes it possible to pass an OID instead of a PC instance for PC
2644: * parameters.
2645: */
2646: public void convertPcParamsToOID(Object[] params) {
2647: if (params == null)
2648: return;
2649: int n = params.length;
2650: for (int i = 0; i < n; i++) {
2651: Object param = params[i];
2652: if (param == null)
2653: continue;
2654: if (param instanceof Collection) {
2655: List l = new ArrayList((Collection) param);
2656: for (int j = 0; j < l.size(); j++) {
2657: Object o = (Object) l.get(j);
2658: o = convertPcParamsToOIDImp(o, i);
2659: if (o instanceof OID) {
2660: l.set(j, o);
2661: }
2662: }
2663: params[i] = l;
2664: } else {
2665: params[i] = convertPcParamsToOIDImp(param, i);
2666: }
2667: }
2668: }
2669:
2670: private Object convertPcParamsToOIDImp(Object param, int paramIndex) {
2671: if (param == null)
2672: return param;
2673: if (param instanceof PersistenceCapable) {
2674: PersistenceCapable pc = (PersistenceCapable) param;
2675: if (pc.jdoGetPersistenceManager() != proxy) {
2676: if (pc.jdoGetPersistenceManager() != null) {
2677: throw BindingSupportImpl
2678: .getInstance()
2679: .invalidOperation(
2680: "PC parameter "
2681: + paramIndex
2682: + " is managed by "
2683: + pc
2684: .jdoGetPersistenceManager()
2685: + " (this is " + proxy
2686: + "): " + pc.getClass()
2687: + ": " + Utils.toString(pc));
2688: } else {
2689: throw BindingSupportImpl.getInstance()
2690: .invalidOperation(
2691: "PC parameter " + paramIndex
2692: + " is transient: "
2693: + pc.getClass() + ": "
2694: + Utils.toString(pc));
2695: }
2696: }
2697: param = getInternalOID((PersistenceCapable) param);
2698: } else if (param instanceof VersantOid) {
2699: // datastore identity OID parameter
2700: OID oid = modelMetaData
2701: .convertJDOGenieOIDtoOID((VersantOid) param);
2702: param = convertNewToActual(oid);
2703: } else {
2704: ClassMetaData cmd = modelMetaData
2705: .getClassMetaDataForObjectIdClass(param.getClass());
2706: if (cmd != null) { // app identity objectid-class parameter
2707: OID oid = cmd.createOID(false);
2708: oid.fillFromPK(param);
2709: param = oid;
2710: }
2711: }
2712: return param;
2713: }
2714:
2715: //==========================XAResource impl==================================
2716:
2717: private int txTimeout = 3000;
2718: private Xid xid = null;
2719:
2720: private int txState;
2721: public static final int TX_INACTIVE = 0;
2722: public static final int TX_STARTED = 1;
2723: public static final int TX_FAIL = 2;
2724: public static final int TX_PREPARED = 4;
2725: public static final int TX_SUSPENDED = 8;
2726:
2727: /**
2728: * If the pm is managed by a TransactionManager.
2729: */
2730: private boolean managed;
2731: /**
2732: * This is a flag to indicate if it was closed by the client in a managed env.
2733: */
2734: private boolean managedClosed = false;
2735:
2736: /**
2737: * Is this pm managed by a container.
2738: *
2739: * @return
2740: */
2741: public boolean isManaged() {
2742: return managed;
2743: }
2744:
2745: /**
2746: * set the state of the pm to be in a container.
2747: *
2748: * @param managed
2749: */
2750: public void setManaged(boolean managed) {
2751: this .managed = managed;
2752: this .managedClosed = false;
2753: }
2754:
2755: /**
2756: * Called to associate the resource with a transaction.
2757: * <p/>
2758: * If the flags argument is {@link #TMNOFLAGS}, the transaction must not
2759: * previously have been seen by this resource manager, or an
2760: * {@link javax.transaction.xa.XAException} with error code XAER_DUPID will be thrown.
2761: * <p/>
2762: * If the flags argument is {@link #TMJOIN}, the resource will join a
2763: * transaction previously seen by its resource manager.
2764: * <p/>
2765: * If the flags argument is {@link #TMRESUME} the resource will
2766: * resume the transaction association that was suspended with
2767: * end(TMSUSPEND).
2768: *
2769: * @param xid The id of the transaction to associate with.
2770: * @param flags Must be either {@link #TMNOFLAGS}, {@link #TMJOIN}
2771: * or {@link #TMRESUME}.
2772: * @throws javax.transaction.xa.XAException
2773: * If an error occurred.
2774: */
2775:
2776: public void start(Xid xid, int flags) throws XAException {
2777: if (Debug.DEBUG) {
2778: Debug.OUT
2779: .println("***** JdoGeniePersistenceManagerImp.start ***** flag = "
2780: + getFlagString(flags) + " for \n" + this );
2781: System.out.println("xid = " + xid);
2782: }
2783: checkInPool();
2784: switch (flags) {
2785: case TMNOFLAGS:
2786: case TMJOIN:
2787: begin(xid);
2788: break;
2789: case TMRESUME:
2790: if (checkId(xid)) {
2791: resume(xid);
2792: }
2793: break;
2794: default:
2795: throw new XAException(
2796: "Unsupported state for method start state = "
2797: + flags);
2798: }
2799: }
2800:
2801: private boolean checkId(Xid xid) {
2802: if (!this .xid.equals(xid)) {
2803: // throw new JDOFatalInternalException("The check id failed");
2804: return false;
2805: }
2806: return true;
2807: }
2808:
2809: private void resume(Xid xid) throws XAException {
2810: checkInPool();
2811: if (this .txState != TX_SUSPENDED) {
2812: throw new XAException(
2813: "Could resume a transaction that was not suspended");
2814: }
2815: this .txState = TX_STARTED;
2816: }
2817:
2818: private void begin(Xid xid) throws XAException {
2819: checkInPool();
2820: if (this .txState == TX_INACTIVE) {
2821: this .xid = xid;
2822: try {
2823: beginImp();
2824: this .txState = TX_STARTED;
2825: } catch (Exception e) {
2826: throw new XAException(
2827: "Could not begin a transaction : "
2828: + e.getMessage());
2829: }
2830: } else if (this .txState == TX_STARTED
2831: || this .txState == TX_PREPARED
2832: || this .txState == TX_SUSPENDED) {
2833: // Transaction on this pm has started already. Since beans will
2834: // share this pm, there will be multple calls to begin.
2835: return;
2836: } else {
2837: throw new XAException(
2838: "Could not begin a transaction in state = "
2839: + txState);
2840: }
2841: }
2842:
2843: /**
2844: * Prepare to commit the work done on this resource in the given
2845: * transaction.
2846: * <p/>
2847: * This method cannot return a status indicating that the transaction
2848: * should be rolled back. If the resource wants the transaction to
2849: * be rolled back, it should throw an <code>XAException</code> at the
2850: * caller.
2851: *
2852: * @param xid The id of the transaction to prepare to commit work for.
2853: * @return Either {@link #XA_OK} or {@link #XA_RDONLY}.
2854: * @throws javax.transaction.xa.XAException
2855: * If an error occurred.
2856: */
2857:
2858: public int prepare(Xid xid) throws XAException {
2859: if (Debug.DEBUG) {
2860: Debug.OUT
2861: .println("***** JdoGeniePersistenceManagerImp.prepare *****");
2862: }
2863: checkInPool();
2864: if (checkId(xid)) {
2865: if (txState == TX_STARTED) {
2866: try {
2867: internalCommit(true);
2868: this .txState = TX_PREPARED;
2869: return XA_OK;
2870: } catch (Exception ex) {
2871: ex.printStackTrace();
2872: throw new XAException("Could not prepare commit : "
2873: + ex.getMessage());
2874: }
2875: } else if (this .txState == TX_PREPARED
2876: || this .txState == TX_SUSPENDED) {
2877: return XA_OK;
2878: } else {
2879: throw new XAException(
2880: "Wrong state to commit phase one on : state = "
2881: + this .txState);
2882: }
2883: }
2884: return XA_OK;
2885: }
2886:
2887: /**
2888: * Commit the work done on this resource in the given transaction.
2889: * <p/>
2890: * If the <code>onePhase</code> argument is true, one-phase
2891: * optimization is being used, and the {@link #prepare(Xid) prepare}
2892: * method must not have been called for this transaction.
2893: * Otherwise, this is the second phase of the two-phase commit protocol.
2894: *
2895: * @param xid The id of the transaction to commit work for.
2896: * @param onePhase If true, the transaction manager is using one-phase
2897: * optimization.
2898: * @throws javax.transaction.xa.XAException
2899: * If an error occurred.
2900: */
2901:
2902: public void commit(Xid xid, boolean onePhase) throws XAException {
2903: if (Debug.DEBUG) {
2904: Debug.OUT
2905: .println("***** JdoGeniePersistenceManagerImp.commit ***** onePhase: "
2906: + onePhase + " for \n" + this );
2907: }
2908: checkInPool();
2909: if (checkId(xid)) {
2910: try {
2911: if (onePhase && txState == TX_STARTED) {
2912: internalCommit(false);
2913: } else if (this .txState == TX_PREPARED) {
2914: phaseCommit2();
2915: } else if (this .txState == TX_INACTIVE) {
2916: return;
2917: } else {
2918: throw new XAException(
2919: "Unable to commit unexpected state: state = "
2920: + txState + " for xid = " + xid);
2921: }
2922:
2923: this .txState = TX_INACTIVE;
2924: this .xid = null;
2925: } catch (XAException ex) {
2926: ex.printStackTrace();
2927: throw ex;
2928: } catch (Exception ex) {
2929: ex.printStackTrace();
2930: throw new XAException("Could not commit : "
2931: + ex.getMessage());
2932: }
2933: }
2934: }
2935:
2936: private void checkInPool() {
2937: if (inPool) {
2938: throw BindingSupportImpl.getInstance().fatal(
2939: "The pm is in the pool");
2940: }
2941: }
2942:
2943: public boolean isInTx() {
2944: return this .txState != TX_INACTIVE;
2945: }
2946:
2947: /**
2948: * Roll back the work done on this resource in the given transaction.
2949: *
2950: * @param xid The id of the transaction to rollback for.
2951: * @throws javax.transaction.xa.XAException
2952: * If an error occurred.
2953: */
2954:
2955: public void rollback(Xid xid) throws XAException {
2956: if (Debug.DEBUG) {
2957: Debug.OUT
2958: .println("***** JdoGeniePersistenceManagerImp.rollback *****");
2959: }
2960: checkInPool();
2961: if (checkId(xid)) {
2962: try {
2963: if (this .txState != TX_INACTIVE) {
2964: rollbackImp();
2965: this .txState = TX_INACTIVE;
2966: }
2967: this .xid = null;
2968: } catch (Exception e) {
2969: throw new XAException("Could not rollback: "
2970: + e.getMessage());
2971: }
2972: }
2973: }
2974:
2975: /**
2976: * Called to disassociate the resource from a transaction.
2977: * <p/>
2978: * If the flags argument is {@link #TMSUCCESS}, the portion of work
2979: * was done sucessfully.
2980: * <p/>
2981: * If the flags argument is {@link #TMFAIL}, the portion of work
2982: * failed. The resource manager may mark the transaction for
2983: * rollback only to avoid the transaction being committed.
2984: * <p/>
2985: * If the flags argument is {@link #TMSUSPEND} the resource will
2986: * temporarily suspend the transaction association. The transaction
2987: * must later be re-associated by giving the {@link #TMRESUME} state
2988: * to the {@link #start(Xid,int) start} method.
2989: *
2990: * @param xid The id of the transaction to disassociate from.
2991: * @param flags Must be either {@link #TMSUCCESS}, {@link #TMFAIL}
2992: * or {@link #TMSUSPEND}.
2993: * @throws javax.transaction.xa.XAException
2994: * If an error occurred.
2995: */
2996:
2997: public void end(Xid xid, int flags) throws XAException {
2998: if (Debug.DEBUG) {
2999: Debug.OUT
3000: .println("***** JdoGeniePersistenceManagerImp.end ***** flag: "
3001: + getFlagString(flags) + " for \n" + this );
3002: System.out.println("xid = " + xid);
3003: }
3004: checkInPool();
3005: if (checkId(xid)) {
3006: switch (flags) {
3007: case TMSUCCESS:
3008: this .txState = TX_STARTED;
3009: break;
3010: case TMFAIL:
3011: this .txState = TX_FAIL;
3012: break;
3013: case TMSUSPEND:
3014: this .txState = TX_SUSPENDED;
3015: break;
3016: default:
3017: throw new XAException("Unable to end transaction = "
3018: + xid + " unhandled flag = " + flags);
3019: }
3020: }
3021: }
3022:
3023: private String getFlagString(int flag) {
3024: switch (flag) {
3025: case 8388608:
3026: return "TMENDRSCAN";
3027: case 536870912:
3028: return "TMFAIL";
3029: case 2097152:
3030: return "TMJOIN";
3031: case 0:
3032: return "TMNOFLAGS";
3033: case 1073741824:
3034: return "TMONEPHASE";
3035: case 134217728:
3036: return "TMRESUME";
3037: case 16777216:
3038: return "TMSTARTRSCAN";
3039: case 67108864:
3040: return "TMSUCCESS";
3041: case 33554432:
3042: return "TMSUSPEND";
3043: default:
3044: return "UNKNOWN";
3045: }
3046: }
3047:
3048: /**
3049: * Tells the resource manager to forget about a heuristic decision.
3050: *
3051: * @param xid The id of the transaction that was ended with a heuristic
3052: * decision.
3053: * @throws javax.transaction.xa.XAException
3054: * If an error occurred.
3055: */
3056:
3057: public void forget(Xid xid) throws XAException {
3058: if (Debug.DEBUG) {
3059: Debug.OUT
3060: .println("***** JdoGeniePersistenceManagerImp.forget *****");
3061: }
3062: checkInPool();
3063: if (this .xid.equals(xid)) {
3064: this .txState = TX_STARTED;
3065: }
3066: }
3067:
3068: /**
3069: * Get the current transaction timeout value for this resource.
3070: *
3071: * @return The current timeout value, in seconds.
3072: * @throws javax.transaction.xa.XAException
3073: * If an error occurred.
3074: */
3075:
3076: public int getTransactionTimeout() throws XAException {
3077: if (Debug.DEBUG) {
3078: Debug.OUT
3079: .println("***** JdoGeniePersistenceManagerImp.getTransactionTimeout *****");
3080: }
3081: checkInPool();
3082: return txTimeout;
3083: }
3084:
3085: /**
3086: * This method does not check for inPool because Weblogic seems to keep
3087: * a ref around of used ones to try and re-use it. This happens even though
3088: * it was delisted from transaction.
3089: * <p/>
3090: * Tells the caller if this resource has the same resource manager
3091: * as the argument resource.
3092: * <p/>
3093: * The transaction manager needs this method to be able to decide
3094: * if the {@link #start(Xid,int) start} method should be given the
3095: * {@link #TMJOIN} state.
3096: *
3097: * @throws javax.transaction.xa.XAException
3098: * If an error occurred.
3099: */
3100:
3101: public boolean isSameRM(XAResource xaResource) throws XAException {
3102: if (Debug.DEBUG) {
3103: Debug.OUT
3104: .println("***** JdoGeniePersistenceManagerImp.isSameRM *****");
3105: Debug.OUT.println("***** isSame: this = " + this );
3106: Debug.OUT.println("***** isSame: other = " + this );
3107: }
3108: return xaResource == this ;
3109: }
3110:
3111: /**
3112: * Return a list of transactions that are in a prepared or heuristically
3113: * state.
3114: * <p/>
3115: * This method looks not only at the resource it is invoked on, but
3116: * also on all other resources managed by the same resource manager.
3117: * It is intended to be used by the application server when recovering
3118: * after a server crash.
3119: * <p/>
3120: * A recovery scan is done with one or more calls to this method.
3121: * At the first call, {@link #TMSTARTRSCAN} must be in the
3122: * <code>state</code> argument to indicate that the scan should be started.
3123: * During the recovery scan, the resource manager maintains an internal
3124: * cursor that keeps track of the progress of the recovery scan.
3125: * To end the recovery scan, the {@link #TMENDRSCAN} must be passed
3126: * in the <code>state</code> argument.
3127: *
3128: * @param flag Must be either {@link #TMNOFLAGS}, {@link #TMSTARTRSCAN},
3129: * {@link #TMENDRSCAN} or <code>TMSTARTRSCAN|TMENDRSCAN</code>.
3130: * @return An array of zero or more transaction ids.
3131: * @throws javax.transaction.xa.XAException
3132: * If an error occurred.
3133: */
3134:
3135: public Xid[] recover(int flag) throws XAException {
3136: if (Debug.DEBUG) {
3137: Debug.OUT
3138: .println("***** JdoGeniePersistenceManagerImp.recover ***** flag = "
3139: + getFlagString(flag));
3140: }
3141: checkInPool();
3142: Xid[] xids = txState == TX_PREPARED ? new Xid[] { xid } : null;
3143: return xids;
3144: }
3145:
3146: /**
3147: * Set the transaction timeout value for this resource.
3148: * <p/>
3149: * If the <code>seconds</code> argument is <code>0</code>, the
3150: * timeout value is set to the default timeout value of the resource
3151: * manager.
3152: * <p/>
3153: * Not all resource managers support setting the timeout value.
3154: * If the resource manager does not support setting the timeout
3155: * value, it should return false.
3156: *
3157: * @param seconds The timeout value, in seconds.
3158: * @return True if the timeout value could be set, otherwise false.
3159: * @throws javax.transaction.xa.XAException
3160: * If an error occurred.
3161: */
3162:
3163: public boolean setTransactionTimeout(int seconds)
3164: throws XAException {
3165: if (Debug.DEBUG) {
3166: Debug.OUT
3167: .println("***** JdoGeniePersistenceManagerImp.setTransactionTimeout *****");
3168: }
3169: checkInPool();
3170: if (seconds < -1) {
3171: return false;
3172: } else {
3173: this .txTimeout = seconds;
3174: }
3175: return true;
3176: }
3177:
3178: //====================Synchronization imp==============================
3179:
3180: public void afterCompletion(int status) {
3181: if (Debug.DEBUG) {
3182: Debug.OUT
3183: .println("***** JdoGeniePersistenceManagerImp.afterCompletion *****");
3184: }
3185: proxy.resetPM();
3186: pmf.pmClosedNotification(this , false, false);
3187: }
3188:
3189: public void beforeCompletion() {
3190: if (Debug.DEBUG) {
3191: Debug.OUT
3192: .println("***** JdoGeniePersistenceManagerImp.beforeCompletion *****");
3193: }
3194: }
3195:
3196: /**
3197: * Set the locking mode for datastore transactions. This method may only
3198: * be called when no transaction is active. You can set the default value
3199: * for this property using the Workbench or edit your .jdogenie file
3200: * directly (datastore.tx.locking property).
3201: *
3202: * @see #LOCKING_NONE
3203: * @see #LOCKING_FIRST
3204: * @see #LOCKING_ALL
3205: */
3206: public void setDatastoreTxLocking(int mode) {
3207: int policy;
3208: switch (mode) {
3209: case VersantPersistenceManager.LOCKING_NONE:
3210: policy = StorageManager.LOCK_POLICY_NONE;
3211: break;
3212: case VersantPersistenceManager.LOCKING_FIRST:
3213: policy = StorageManager.LOCK_POLICY_FIRST;
3214: break;
3215: case VersantPersistenceManager.LOCKING_ALL:
3216: policy = StorageManager.LOCK_POLICY_ALL;
3217: break;
3218: default:
3219: throw BindingSupportImpl.getInstance().invalidOperation(
3220: "Invalid datastoreTxLocking mode: " + mode);
3221: }
3222: sm.setLockingPolicy(policy);
3223: }
3224:
3225: /**
3226: * Get the locking mode for datastore transactions.
3227: */
3228: public int getDatastoreTxLocking() {
3229: switch (sm.getLockingPolicy()) {
3230: case StorageManager.LOCK_POLICY_NONE:
3231: return VersantPersistenceManager.LOCKING_NONE;
3232: case StorageManager.LOCK_POLICY_FIRST:
3233: return VersantPersistenceManager.LOCKING_FIRST;
3234: case StorageManager.LOCK_POLICY_ALL:
3235: return VersantPersistenceManager.LOCKING_ALL;
3236: }
3237: return VersantPersistenceManager.LOCKING_NONE;
3238: }
3239:
3240: public void logEvent(int level, String description, int ms) {
3241: sm.logEvent(level, description, ms);
3242: }
3243:
3244: /**
3245: * Return the instance for oid if it is present in the local PM cache
3246: * otherwise return null. Note that the instances might still be hollow
3247: * and touching its fields will cause a fetch from the level 2 cache
3248: * or database.
3249: *
3250: * @see #isHollow(Object)
3251: */
3252: public Object getObjectByIdFromCache(Object oid) {
3253: if (oid == null) {
3254: throw BindingSupportImpl.getInstance().invalidOperation(
3255: "The oid is null");
3256: }
3257: try {
3258: PCStateMan stateMan = cache.getByOID(extractOID(oid), true);
3259: return stateMan == null ? null : stateMan.pc;
3260: } catch (Exception e) {
3261: handleException(e);
3262: return null;
3263: }
3264: }
3265:
3266: /**
3267: * Is the instance hollow? Hollow instances are managed but their fields
3268: * have not been loaded from the level 2 cache or database.
3269: *
3270: * @see #getObjectByIdFromCache(Object)
3271: */
3272: public boolean isHollow(Object pc) {
3273: PCStateMan pcStateObject = pmPreCheck(pc);
3274: return pcStateObject != null && pcStateObject.isHollow();
3275: }
3276:
3277: /**
3278: * Does the instance have an identity? New instances are only assigned
3279: * an identity on commit or flush or when the application executes an
3280: * operation that requires the identity.
3281: */
3282: public boolean hasIdentity(Object pc) {
3283: return !pmPreCheck(pc).oid.isNew();
3284: }
3285:
3286: /**
3287: * Process the ReferenceQueue for the local cache. This is called when
3288: * a query fetches data from the server to ensure that long running
3289: * read-only transactions do not leak SoftReferences.
3290: */
3291: public void processLocalCacheReferenceQueue() {
3292: cache.processReferenceQueue();
3293: }
3294:
3295: /**
3296: * This method makes detached copies of the parameter instances and returns
3297: * the copies as the result of the method. The order of instances in the
3298: * parameter Collection's iteration corresponds to the order of corresponding
3299: * instances in the returned Collection's iteration.
3300: * <p/>
3301: * The Collection of instances is first made persistent, and the reachability
3302: * algorithm is run on the instances. This ensures that the closure of all
3303: * of the instances in the the parameter Collection is persistent.
3304: * <p/>
3305: * For each instance in the parameter Collection, a corresponding detached
3306: * copy is created. Each field in the persistent instance is handled based on
3307: * its type and whether the field is contained in the fetch group for the
3308: * persistence-capable class. If there are duplicates in the parameter
3309: * Collection, the corresponding detached copy is used for each such duplicate.
3310: */
3311: public Collection versantDetachCopy(Collection pcs,
3312: String fetchGroup) {
3313: if (pcs instanceof QueryResult) {
3314: //this is done to resolve the queryresult to avoid extra queries
3315: // when iterating over it
3316: pcs.size();
3317: }
3318:
3319: if (fetchGroup == null)
3320: fetchGroup = FetchGroup.DFG_NAME;
3321: for (Iterator pcIt = pcs.iterator(); pcIt.hasNext();) {
3322: Object o = pcIt.next();
3323: if (o == null || !(o instanceof VersantDetachable))
3324: continue;
3325: VersantDetachable pc = (VersantDetachable) o;
3326: requestedPCState = null;
3327: pc.jdoGetPersistenceManager();
3328: if (requestedPCState == null) {
3329: makePersistent(pc);
3330: }
3331: }
3332: flushRetainState();
3333: // load the container with the initial root collection
3334: DetachStateContainer dsc = new DetachStateContainer(this );
3335: for (Iterator pcIt = pcs.iterator(); pcIt.hasNext();) {
3336: PersistenceCapable pc = (PersistenceCapable) pcIt.next();
3337: if (pc == null)
3338: continue;
3339: PCStateMan sm = getInternalSM(pc);
3340: ClassMetaData cmd = sm.state
3341: .getClassMetaData(modelMetaData);
3342: FetchGroup fg = cmd.getFetchGroup(fetchGroup);
3343: if (fg == null) {
3344: fg = cmd.fetchGroups[0];
3345: }
3346: dsc.add(sm.oid, sm.state, fg);
3347: }
3348:
3349: // find all states reachable via the fetch group
3350: for (; dsc.hasNextFetchGroup();) {
3351: State state = dsc.getNextFetchGroupState();
3352: FetchGroup fg = dsc.getNextFetchGroup();
3353: OID oid = dsc.getNextFetchGroupOID();
3354:
3355: // if we have to load the fetch group then there is no need to check
3356: // any of its fields - all of the States referenced by the fetch
3357: // group will have been added to the StatesReturned on the
3358: // server and will be added to our dcs
3359: if (!oid.isNew()
3360: && (state == null || !state.containsFetchGroup(fg))) {
3361: StatesReturned con = getStateForDetach(oid, fg.index);
3362: addToDetachStateContainer(con, dsc);
3363: state = con.get(oid);
3364: addToCache(con);
3365: dsc.add(oid, state, fg);
3366: }
3367: ClassMetaData cmd = state.getClassMetaData(modelMetaData);
3368: state.addFetchGroupStatesToDCS(fg, dsc, this , oid, cmd);
3369: }
3370: dsc.createPcClasses(modelMetaData);
3371: ArrayList copyList = new ArrayList(pcs.size());
3372: for (Iterator pcIt = pcs.iterator(); pcIt.hasNext();) {
3373: Object o = pcIt.next();
3374: if (o == null || !(o instanceof VersantDetachable)) {
3375: copyList.add(null);
3376: continue;
3377: }
3378: VersantDetachable pc = (VersantDetachable) o;
3379: requestedPCState = null;
3380: pc.jdoGetPersistenceManager();
3381: if (requestedPCState == null) {
3382: copyList.add(pc);
3383: }
3384: copyList.add(dsc.getDetachCopy(pc));
3385: }
3386: return copyList;
3387: }
3388:
3389: public List versantCollectReachable(Collection roots,
3390: String fetchGroup) {
3391: if (fetchGroup == null) {
3392: fetchGroup = FetchGroup.REF_NAME;
3393: }
3394: checkActiveTx();
3395:
3396: Set result = new HashSet();
3397: List todo = new ArrayList(roots);
3398:
3399: while (todo.size() > 0) // non-recursive!
3400: {
3401: Object o = todo.remove(0);
3402: String currentFetchGroup = fetchGroup;
3403: if (o instanceof Object[]) { // added by sm.collectReachable
3404: Object[] tmp = (Object[]) o;
3405: currentFetchGroup = (String) tmp[1];
3406: o = tmp[0];
3407: }
3408:
3409: if (o == null || !(o instanceof PersistenceCapable))
3410: continue;
3411: PersistenceCapable pc = (PersistenceCapable) o;
3412:
3413: requestedPCState = null;
3414: pc.jdoGetPersistenceManager();
3415: if (requestedPCState == null) {
3416: makePersistent(pc);
3417: }
3418: PCStateMan sm = pmPreCheck(pc);
3419: if (!result.contains(sm) && !sm.isDeleted(pc)) {
3420: if (currentFetchGroup != null)
3421: // nextFetchGroup was defined for the referencing field
3422: {
3423: sm.collectReachable(currentFetchGroup, todo);
3424: }
3425: result.add(sm);
3426: }
3427: }
3428: List pcresult = new ArrayList();
3429: for (Iterator it = result.iterator(); it.hasNext();) {
3430: pcresult.add(((PCStateMan) it.next()).pc);
3431: }
3432: return pcresult;
3433: }
3434:
3435: /**
3436: * This is called by sm's if they require a field.
3437: * This will update the managedCache with the requested data.
3438: */
3439: public StatesReturned getStateForDetach(OID oid, int fgi) {
3440: return getStateJdoConnection(oid, null, fgi, oid
3441: .getAvailableClassId(), false, -1, -1);
3442: }
3443:
3444: public State getStateFromLocalCacheById(Object oid) {
3445: if (oid == null) {
3446: throw BindingSupportImpl.getInstance().invalidOperation(
3447: "The supplied oid is null");
3448: }
3449: try {
3450: OID nOID = extractOID(oid);
3451: PCStateMan stateMan = cache.getByOID(nOID, true);
3452: if (stateMan == null) {
3453: return null;
3454: }
3455: return stateMan.state;
3456: } catch (Exception e) {
3457: handleException(e);
3458: return null;
3459: }
3460: }
3461:
3462: /**
3463: * This util method is used by collection types to preload their pc
3464: * entries. It tests to determine if the states refered to by the oids is
3465: * in the managed cache. If not they must be bulk loaded from server.
3466: * The scenario in which this is likely to happen is when the collection
3467: * is not in the default fetch group and the state is in cache with the
3468: * collection filled in. If this collection field is read then the
3469: * pcstateman will determine that the stateField is filled and hence not
3470: * ask the server for it.
3471: */
3472: public int getObjectsById(Object[] oids, int length, Object[] data,
3473: int stateFieldNo, int classMetaDataIndex) {
3474: if (oids == null || oids.length <= 0)
3475: return 0;
3476: OIDArray oidArray = new OIDArray();
3477: for (int i = 0; i < length; i++) {
3478: Object o = oids[i];
3479: // if (o == null) {
3480: // length = i;
3481: // break;
3482: // }
3483: if (!(o instanceof OID)) {
3484: data[i] = o;
3485: continue;
3486: }
3487: OID oid = (OID) o;
3488: PCStateMan pcStateMan = cache.getByOID(oid, true);
3489: if (pcStateMan != null) {
3490: data[i] = pcStateMan.pc;
3491: } else {
3492: oidArray.add(oid);
3493: data[i] = null;
3494: }
3495: }
3496: if (!oidArray.isEmpty()) {
3497: FieldMetaData triggerField;
3498: if (classMetaDataIndex >= 0) { // navigated stateFieldNo
3499: ClassMetaData navCmd = modelMetaData.classes[classMetaDataIndex];
3500: triggerField = navCmd.stateFields[stateFieldNo];
3501: } else {
3502: triggerField = null;
3503: }
3504: StatesReturned container = sm.fetch(this , oidArray,
3505: triggerField);
3506: // keep a reference to each of the returned PCStateMan's so
3507: // that they are not GCed before we reference them
3508: PCStateMan[] nogc = addToCacheAndManage(container);
3509: for (int i = 0; i < length; i++) {
3510: if (data[i] == null) {
3511: data[i] = cache.getByOID((OID) oids[i], true).pc;
3512: }
3513: }
3514: if (nogc == null) {
3515: // dummy code to keep IDE happy and to hopefully prevent
3516: // any overzealous optimization from removing nogc
3517: }
3518: }
3519: return length;
3520: }
3521:
3522: /**
3523: * Construct a new query instance with the given candidate class from a
3524: * named query. The query name given must be the name of a query defined
3525: * in metadata. The metadata is searched for the specified name.
3526: * This is a JDO 2 preview feature.
3527: */
3528: public Query versantNewNamedQuery(Class cls, String queryName) {
3529: try {
3530: ClassMetaData cmd = modelMetaData.getClassMetaData(cls);
3531: if (cmd == null) {
3532: throw BindingSupportImpl.getInstance()
3533: .invalidOperation(
3534: "Class " + cls.getName()
3535: + " is not persistent");
3536: }
3537: QueryDetails qp = cmd.getNamedQuery(queryName);
3538: if (qp == null) {
3539: throw BindingSupportImpl
3540: .getInstance()
3541: .invalidOperation(
3542: "No query called '"
3543: + queryName
3544: + "' has been defined in the meta data for Class "
3545: + cmd.qname);
3546: }
3547: return new VersantQueryImp(proxy, qp);
3548: } catch (Exception e) {
3549: handleException(e);
3550: return null;
3551: }
3552: }
3553:
3554: /**
3555: * Must bidirectional relationships be checked for consistency
3556: * on commit or flush?
3557: */
3558: public boolean isCheckModelConsistencyOnCommit() {
3559: return checkModelConsistencyOnCommit;
3560: }
3561:
3562: public void setCheckModelConsistencyOnCommit(boolean on) {
3563: this .checkModelConsistencyOnCommit = on;
3564: }
3565:
3566: /**
3567: * This method applies the changes contained in the collection of detached
3568: * instances to the corresponding persistent instances in the cache and
3569: * returns a collection of persistent instances that exactly corresponds to
3570: * the parameter instances. The order of instances in the parameter
3571: * Collection's iteration corresponds to the order of corresponding
3572: * instances in the returned Collection's iteration.
3573: * <p/>
3574: * Changes made to instances while detached are applied to the corresponding
3575: * persistent instances in the cache. New instances associated with the
3576: * detached instances are added to the persistent instances in the
3577: * corresponding place.
3578: */
3579: public Collection versantAttachCopy(Collection detached,
3580: boolean makeTransactional) {
3581: return versantAttachCopy(detached, makeTransactional, false);
3582: }
3583:
3584: /**
3585: * This method applies the changes contained in the collection of detached
3586: * instances to the corresponding persistent instances in the cache and
3587: * returns a collection of persistent instances that exactly corresponds to
3588: * the parameter instances. The order of instances in the parameter
3589: * Collection's iteration corresponds to the order of corresponding
3590: * instances in the returned Collection's iteration.
3591: * <p/>
3592: * Changes made to instances while detached are applied to the corresponding
3593: * persistent instances in the cache. New instances associated with the
3594: * detached instances are added to the persistent instances in the
3595: * corresponding place.
3596: *
3597: * @param detached VersantDetachable objects to attach in the current
3598: * transaction
3599: * @param shallow attach only the objects in 'detached' Collection and not
3600: * reachable objects if true.
3601: */
3602: public Collection versantAttachCopy(Collection detached,
3603: boolean makeTransactional, boolean shallow) {
3604: AttachStateContainer asc = new AttachStateContainer(this );
3605: for (Iterator pcIt = detached.iterator(); pcIt.hasNext();) {
3606: VersantDetachable detachable = (VersantDetachable) pcIt
3607: .next();
3608: if (detachable == null)
3609: continue;
3610: asc.addVersantDetachable(detachable);
3611: }
3612: if (!shallow) {
3613: AttachNavStateManager ansm = new AttachNavStateManager(asc);
3614: for (int c = 0; c < asc.getDetachedSize(); c++) {
3615: VersantDetachable detachable = asc
3616: .getVersantDetachable(c);
3617: if (detachable.jdoGetPersistenceManager() != null)
3618: continue;
3619: detachable.jdoReplaceStateManager(ansm);
3620: ClassMetaData cmd = modelMetaData
3621: .getClassMetaData(detachable.getClass());
3622: for (; cmd != null; cmd = cmd.pcSuperMetaData) {
3623: for (int i = 0; i < cmd.fields.length; i++) {
3624: FieldMetaData field = cmd.fields[i];
3625: if (field.fake)
3626: continue;
3627: switch (field.category) {
3628: case FieldMetaData.CATEGORY_REF:
3629: case FieldMetaData.CATEGORY_POLYREF:
3630: case FieldMetaData.CATEGORY_COLLECTION:
3631: case FieldMetaData.CATEGORY_MAP:
3632: case FieldMetaData.CATEGORY_ARRAY:
3633: detachable
3634: .jdoProvideField(field.managedFieldNo);
3635: }
3636: }
3637: }
3638: }
3639: }
3640:
3641: AttachCopyStateManager acsm = new AttachCopyStateManager(this );
3642: for (int c = 0; c < asc.getDetachedSize(); c++) {
3643: OID oid = asc.getOID(c);
3644: VersantDetachable detachable = asc.getVersantDetachable(c);
3645: if (detachable.jdoGetPersistenceManager() != null)
3646: continue;
3647: boolean notNew = !oid.isNew();
3648: if (notNew && !detachable.versantIsDirty())
3649: continue;
3650: detachable.jdoReplaceStateManager(acsm);
3651: PCStateMan sm = getInternalSM(oid);
3652: if (notNew && sm.state.isHollow()) {
3653: StatesReturned con = getStateForDetach(oid, 0);
3654: addToCache(con);
3655: }
3656: acsm.setState(sm.state);
3657: if (notNew) {
3658: Object dVersion = detachable.versantGetVersion();
3659: Object aVersion = sm.getOptimisticLockingValue();
3660: if (aVersion == null
3661: && sm.getClassMetaData().optimisticLockingField != null) {
3662: throw BindingSupportImpl.getInstance().internal(
3663: "Optimistic locking value not available for "
3664: + sm.getClassMetaData().qname);
3665: }
3666: if (aVersion != null && !aVersion.equals(dVersion)) {
3667: throw BindingSupportImpl
3668: .getInstance()
3669: .concurrentUpdate(
3670: "The object ("
3671: + oid.toStringImp()
3672: + ") has been updated since its been detached "
3673: + "(current='" + aVersion
3674: + "', detached='"
3675: + dVersion + "')", oid);
3676: }
3677: }
3678: ClassMetaData cmd = modelMetaData
3679: .getClassMetaData(detachable.getClass());
3680: for (; cmd != null; cmd = cmd.pcSuperMetaData) {
3681: for (int i = 0; i < cmd.fields.length; i++) {
3682: FieldMetaData fmd = cmd.fields[i];
3683: if (fmd.fake)
3684: continue;
3685: if (notNew
3686: && !detachable.versantIsDirty(fmd
3687: .getManagedFieldNo())) {
3688: continue;
3689: }
3690: switch (fmd.category) {
3691: case FieldMetaData.CATEGORY_SIMPLE:
3692: case FieldMetaData.CATEGORY_POLYREF:
3693: case FieldMetaData.CATEGORY_EXTERNALIZED:
3694: case FieldMetaData.CATEGORY_REF:
3695: case FieldMetaData.CATEGORY_COLLECTION:
3696: case FieldMetaData.CATEGORY_ARRAY:
3697: case FieldMetaData.CATEGORY_MAP:
3698: acsm.setFieldMetaData(fmd);
3699: int managedFieldNo = fmd.managedFieldNo;
3700: detachable.jdoProvideField(managedFieldNo);
3701: if (notNew) {
3702: sm.makeDirty(detachable, managedFieldNo);
3703: }
3704: }
3705: }
3706: }
3707: if (notNew) {
3708: sm.resetLoadedFields();
3709: sm.setLoadRequired();
3710: }
3711: }
3712: Collection deleted = asc.getDeleted();
3713: for (Iterator it = deleted.iterator(); it.hasNext();) {
3714: try {
3715: Object o = getObjectById(it.next(), true);
3716: deletePersistent(o);
3717: } catch (JDOObjectNotFoundException e) {
3718: // Do Nothing
3719: }
3720: }
3721: ArrayList attached = new ArrayList();
3722: for (Iterator pcIt = detached.iterator(); pcIt.hasNext();) {
3723: VersantDetachable pc = (VersantDetachable) pcIt.next();
3724: if (pc == null) {
3725: attached.add(pc);
3726: continue;
3727: }
3728: Object newPC = getObjectById(getOID(pc), false);
3729: attached.add(newPC);
3730: }
3731: return attached;
3732: }
3733:
3734: OID getOID(VersantDetachable detachable) {
3735: Object oid = detachable.versantGetOID();
3736: if (oid == null) {
3737: PersistenceManager pm = detachable
3738: .jdoGetPersistenceManager();
3739: if (pm != null) {
3740: if (pm instanceof PMProxy) {
3741: pm = ((PMProxy) pm).getRealPM();
3742: }
3743: if (pm instanceof VersantPersistenceManagerImp) {
3744: OID internalOID = ((VersantPersistenceManagerImp) pm)
3745: .getInternalOID(detachable);
3746: detachable
3747: .versantSetOID(getExternalOID(internalOID));
3748: return internalOID;
3749: } else {
3750: throw BindingSupportImpl
3751: .getInstance()
3752: .runtime(
3753: "Can't attach a managed "
3754: + "instance that is not managed by Versant Open Access");
3755: }
3756: }
3757: PCStateMan sm = getStateObject();
3758: NewObjectOID nOID = assignOID(detachable);
3759: sm.init(detachable, nOID, this );
3760: cache.add(sm);
3761: detachable.versantSetOID(nOID);
3762: return sm.oid.getAvailableOID();
3763: } else {
3764: return extractOID(oid);
3765: }
3766: }
3767:
3768: final PCStateMan getStateManager(Object o) {
3769: return cache.getByOID(extractOID(((PersistenceCapable) o)
3770: .jdoGetObjectId()), false);
3771: }
3772:
3773: final PCStateMan getStateManagerById(Object oid) {
3774: return cache.getByOID(extractOID(oid), false);
3775: }
3776:
3777: public void setRetainConnectionInOptTx(boolean on) {
3778: sm
3779: .setConnectionPolicy(on ? StorageManager.CON_POLICY_PIN_FOR_TX
3780: : StorageManager.CON_POLICY_RELEASE);
3781: }
3782:
3783: /**
3784: * Clear all epc fields.
3785: */
3786: private void resetEpcFields() {
3787: epcAll = false;
3788: epcObjects = null;
3789: epcObjectCount = 0;
3790: epcClasses = null;
3791: epcClassCount = 0;
3792: epcClassPresent = null;
3793: }
3794:
3795: public void evictFromL2CacheAfterCommit(Object o) {
3796: checkActiveTx();
3797: evictFromL2CacheAfterCommitImp(o);
3798: }
3799:
3800: /**
3801: * Version of {@link #evictFromL2CacheAfterCommit} without the active tx
3802: * check for interna use.
3803: */
3804: public void evictFromL2CacheAfterCommitImp(Object o) {
3805: if (epcAll)
3806: return;
3807: if (o instanceof PersistenceCapable) {
3808: PCStateMan sm = pmPreCheck((PersistenceCapable) o);
3809: if (sm == null || sm.isNew(null)) {
3810: return; // no need to evict transient or new objects
3811: }
3812: o = sm.oid;
3813: }
3814: if (epcObjects == null) {
3815: epcObjects = new Object[4];
3816: } else if (epcObjectCount == epcObjects.length) {
3817: Object[] a = new Object[epcObjects.length * 3 / 2 + 1];
3818: System.arraycopy(epcObjects, 0, a, 0, epcObjects.length);
3819: epcObjects = a;
3820: }
3821: epcObjects[epcObjectCount++] = o;
3822: }
3823:
3824: public void evictAllFromL2CacheAfterCommit(final Object[] data) {
3825: checkActiveTx();
3826: int n = data.length;
3827: if (n == 0 || epcAll)
3828: return;
3829: ensureCapacityEpcObjects(n);
3830: for (int i = 0; i < n; i++) {
3831: Object o = data[i];
3832: if (o instanceof PersistenceCapable) {
3833: PCStateMan sm = pmPreCheck((PersistenceCapable) o);
3834: if (sm == null || sm.isNew(null)) {
3835: continue; // no need to evict transient or new objects
3836: }
3837: o = sm.oid;
3838: }
3839: epcObjects[epcObjectCount++] = o;
3840: }
3841: }
3842:
3843: public void evictAllFromL2CacheAfterCommit(final Collection data) {
3844: checkActiveTx();
3845: int n = data.size();
3846: if (n == 0 || epcAll)
3847: return;
3848: ensureCapacityEpcObjects(n);
3849: for (Iterator i = data.iterator(); i.hasNext();) {
3850: Object o = i.next();
3851: if (o instanceof PersistenceCapable) {
3852: PCStateMan sm = pmPreCheck((PersistenceCapable) o);
3853: if (sm == null || sm.isNew(null)) {
3854: continue; // no need to evict transient or new objects
3855: }
3856: o = sm.oid;
3857: }
3858: epcObjects[epcObjectCount++] = o;
3859: }
3860: }
3861:
3862: private void ensureCapacityEpcObjects(int delta) {
3863: if (epcObjects == null) {
3864: epcObjects = new Object[delta + 4];
3865: } else if (epcObjectCount + delta >= epcObjects.length) {
3866: Object[] a = new Object[epcObjectCount + delta];
3867: System.arraycopy(epcObjects, 0, a, 0, epcObjects.length);
3868: epcObjects = a;
3869: }
3870: }
3871:
3872: public void evictAllFromL2CacheAfterCommit(final Class cls,
3873: final boolean includeSubclasses) {
3874: checkActiveTx();
3875: ClassMetaData cmd = modelMetaData.getClassMetaData(cls);
3876: if (cmd == null) {
3877: BindingSupportImpl.getInstance().runtime(
3878: "Class is not persistent: " + cls);
3879: }
3880: evictAllFromL2CacheAfterCommitImp(cmd, includeSubclasses);
3881: }
3882:
3883: private void evictAllFromL2CacheAfterCommitImp(ClassMetaData cmd,
3884: final boolean includeSubclasses) {
3885: if (epcAll)
3886: return;
3887: if (epcClasses == null) {
3888: epcClasses = new int[4];
3889: epcClassPresent = new boolean[modelMetaData.classes.length];
3890: }
3891: int ci = cmd.index;
3892: if (!epcClassPresent[ci]) {
3893: if (epcClassCount == epcClasses.length) {
3894: int[] a = new int[epcClasses.length * 2];
3895: System
3896: .arraycopy(epcClasses, 0, a, 0,
3897: epcClasses.length);
3898: epcClasses = a;
3899: }
3900: epcClasses[epcClassCount++] = ci;
3901: epcClassPresent[ci] = true;
3902: }
3903: if (includeSubclasses) {
3904: ClassMetaData[] subs = cmd.pcSubclasses;
3905: if (subs != null) {
3906: for (int i = subs.length - 1; i >= 0; i--) {
3907: evictAllFromL2CacheAfterCommitImp(subs[i],
3908: includeSubclasses);
3909: }
3910: }
3911: }
3912: }
3913:
3914: public void evictAllFromL2CacheAfterCommit() {
3915: checkActiveTx();
3916: epcAll = true;
3917: epcObjects = null;
3918: epcObjectCount = 0;
3919: epcClasses = null;
3920: epcClassCount = 0;
3921: epcClassPresent = null;
3922: }
3923:
3924: public Object getOptimisticLockingValue(Object o) {
3925: return getInternalSM((PersistenceCapable) o)
3926: .getOptimisticLockingValue();
3927: }
3928:
3929: public void setListeners(LifecycleListenerManager listeners) {
3930: this .listeners = listeners;
3931: }
3932:
3933: public void addLifecycleListener(LifecycleListener listener,
3934: Class[] classes) {
3935: if (classes != null) {
3936: BindingSupportImpl
3937: .getInstance()
3938: .runtime(
3939: "Support for non-null "
3940: + "classes parameter has not been implemented");
3941: }
3942: if (listeners == null) {
3943: listeners = new LifecycleListenerManager(listener);
3944: } else {
3945: listeners = listeners.add(listener);
3946: }
3947: }
3948:
3949: public void removeLifecycleListener(LifecycleListener listener) {
3950: if (listeners == null) {
3951: return;
3952: }
3953: listeners = listeners.remove(listener);
3954: }
3955:
3956: /**
3957: * Fire a delete to all of the LifecycleListener's for o.
3958: */
3959: public void fireDelete(ClassMetaData cmd, Object o) {
3960: if (listeners != null) {
3961: listeners.fireDelete(o);
3962: }
3963: }
3964:
3965: /**
3966: * Get our StorageManager.
3967: */
3968: public StorageManager getStorageManager() {
3969: return sm;
3970: }
3971:
3972: public LocalPMCache getCache() {
3973: return cache;
3974: }
3975:
3976: public void setCache(LocalPMCache cache) {
3977: this .cache = cache;
3978: }
3979:
3980: public MemQueryCompiler getMemQueryCompiler() {
3981: return memQueryCompiler;
3982: }
3983:
3984: public Reference getActiveReference() {
3985: return activeReference;
3986: }
3987:
3988: public void setActiveReference(Reference activeReference) {
3989: this .activeReference = activeReference;
3990: }
3991:
3992: /**
3993: * Iterate through all the oid-state pairs returned from the server
3994: * for a store operation and update the local states.
3995: * This will also bring back real oids for new oids involved.
3996: */
3997: private void updateOIDsAndDoAutoS(StatesReturned container) {
3998: for (Iterator i = container.iterator(); i.hasNext();) {
3999: EntrySet.Entry e = (EntrySet.Entry) i.next();
4000: OID oid = (OID) e.getKey();
4001: State state = (State) e.getValue();
4002:
4003: // This gets the sm with the oid from the container.
4004: // This is done for the case of a newOID which is not
4005: // mapped in the cache via its real oid.
4006: // The sm may not be null because the only way for it to have been sent
4007: // to the server for commit was if it was dirty and hence we must have
4008: // a hard refs to it.
4009: PCStateMan sm;
4010: if (oid instanceof NewObjectOID) {
4011: sm = cache.getByNewObjectOID((NewObjectOID) oid);
4012: if (sm == null) {
4013: sm = cache.getByOID(oid, false);
4014: if (sm == null)
4015: continue;
4016: } else {
4017: ((NewObjectOID) sm.oid)
4018: .setRealOid(oid.getRealOID());
4019: }
4020: } else {
4021: sm = cache.getByOID(oid, false);
4022: if (sm == null)
4023: continue;
4024: }
4025:
4026: // if the state returned is not null then it contains auto set fields that
4027: // needs must be updated the sm's current state.
4028: // this must only happen if retainValues is set to true and the state
4029: // contains autoset fields. version number is a autoset field.
4030: if (state != null) {
4031: sm.updateAutoFields(state);
4032: }
4033: }
4034: }
4035:
4036: /**
4037: * Add all the OIDs and States in the container to the cache.
4038: */
4039: public void addToCache(StatesReturned container) {
4040: for (Iterator i = container.iterator(); i.hasNext();) {
4041: EntrySet.Entry e = (EntrySet.Entry) i.next();
4042: cache.addStateOnly((OID) e.getKey(), (State) e.getValue());
4043: }
4044: }
4045:
4046: /**
4047: * Add all the OIDs and States in the container to the cache. The
4048: * PCStateMan's are kept and returned to prevent the new instances
4049: * being GCed before they are referenced.
4050: */
4051: private PCStateMan[] addToCacheAndManage(StatesReturned container) {
4052: PCStateMan[] smArray = new PCStateMan[1];
4053: PCStateMan[] ans = new PCStateMan[container.size()];
4054: int c = 0;
4055: for (Iterator i = container.iterator(); i.hasNext();) {
4056: EntrySet.Entry e = (EntrySet.Entry) i.next();
4057: ans[c++] = cache.add((OID) e.getKey(),
4058: (State) e.getValue(), smArray);
4059: }
4060: return ans;
4061: }
4062:
4063: /**
4064: * Add all the OIDs and States in the container to the cache and return
4065: * the PCStateMan for the first direct State. This method is specifically written
4066: * to keep a hard ref to created sm so that it can not be gc'd before it is returned.
4067: */
4068: private PCStateMan addAndReturnFirstDirect(StatesReturned container) {
4069: OID firstDirectOID = container.getDirectOID();
4070: if (firstDirectOID == null) {
4071: return null;
4072: }
4073: PCStateMan[] sm = new PCStateMan[1];
4074: cache.add(firstDirectOID, container.get(firstDirectOID), sm);
4075: for (Iterator i = container.iterator(); i.hasNext();) {
4076: EntrySet.Entry e = (EntrySet.Entry) i.next();
4077: cache.addStateOnly((OID) e.getKey(), (State) e.getValue());
4078: }
4079: return sm[0];
4080: }
4081:
4082: /**
4083: * Add all the OIDs and States in the container to dcs.
4084: */
4085: private void addToDetachStateContainer(StatesReturned container,
4086: DetachStateContainer dcs) {
4087: for (Iterator i = container.iterator(); i.hasNext();) {
4088: EntrySet.Entry e = (EntrySet.Entry) i.next();
4089: dcs.add((OID) e.getKey(), (State) e.getValue());
4090: }
4091: }
4092:
4093: /**
4094: * The StorageManager calls this method to check if we need prefetched
4095: * data or not.
4096: */
4097: public boolean isStateRequired(OID oid, FetchGroup fetchGroup) {
4098: State state = cache.getStateByOID(oid);
4099: if (state != null) {
4100: if (fetchGroup == null) {
4101: fetchGroup = state.getClassMetaData().fetchGroups[0];
4102: }
4103: return !state.containsFetchGroup(fetchGroup);
4104: }
4105: return true;
4106: }
4107:
4108: }
|