0001: /*
0002: * Copyright 2004 (C) TJDO.
0003: * All rights reserved.
0004: *
0005: * This software is distributed under the terms of the TJDO License version 1.0.
0006: * See the terms of the TJDO License in the documentation provided with this software.
0007: *
0008: * $Id: StateManagerImpl.java,v 1.19 2004/01/25 22:31:16 jackknifebarber Exp $
0009: */
0010:
0011: package com.triactive.jdo.state;
0012:
0013: import com.triactive.jdo.AbstractFieldManager;
0014: import com.triactive.jdo.FieldManager;
0015: import com.triactive.jdo.GenericFieldManager;
0016: import com.triactive.jdo.PersistenceManager;
0017: import com.triactive.jdo.SCO;
0018: import com.triactive.jdo.model.ClassMetaData;
0019: import com.triactive.jdo.model.FieldMetaData;
0020: import com.triactive.jdo.sco.SCOProcessor;
0021: import com.triactive.jdo.store.StoreManager;
0022: import java.io.PrintWriter;
0023: import java.lang.reflect.Method;
0024: import java.util.Arrays;
0025: import java.util.Collection;
0026: import java.util.Map;
0027: import javax.jdo.InstanceCallbacks;
0028: import javax.jdo.JDOFatalInternalException;
0029: import javax.jdo.JDOFatalUserException;
0030: import javax.jdo.JDOUnsupportedOptionException;
0031: import javax.jdo.JDOUserException;
0032: import javax.jdo.Transaction;
0033: import javax.jdo.spi.JDOImplHelper;
0034: import javax.jdo.spi.PersistenceCapable;
0035: import javax.jdo.spi.StateManager;
0036: import org.apache.log4j.Category;
0037:
0038: /**
0039: * This class implements the StateManager.
0040: *
0041: * @author <a href="mailto:mmartin5@austin.rr.com">Mike Martin</a>
0042: * @version $Revision: 1.19 $
0043: */
0044:
0045: public class StateManagerImpl implements com.triactive.jdo.StateManager {
0046: private static final Category LOG = Category
0047: .getInstance(StateManagerImpl.class);
0048: private static final JDOImplHelper helper;
0049: private static final StateFieldManager hollowFieldManager = new StateFieldManager();
0050:
0051: private PersistenceManager myPM;
0052: private PersistenceCapable myPC;
0053: private Transaction myTX;
0054: private Object myID;
0055: private LifeCycleState myLC;
0056: private byte jdoFlags;
0057: private PersistenceCapable savedImage = null;
0058: private byte savedFlags;
0059: private boolean[] savedLoadedFields = null;
0060:
0061: private ClassMetaData cmd;
0062: private StoreManager srm;
0063: private int fieldCount;
0064:
0065: private boolean inserting = false;
0066: private boolean dirty = false;
0067: private boolean deleting = false;
0068: private boolean flushing = false;
0069: private boolean changingState = false;
0070: private boolean postLoadPending = false;
0071: private boolean disconnecting = false;
0072: private boolean[] dirtyFields;
0073: private boolean[] loadedFields;
0074: private boolean[] defaultFetchGroupFields;
0075: private boolean[] secondClassMutableFields;
0076: private int[] allFieldNumbers;
0077: private int[] persistentFieldNumbers;
0078: private int[] defaultFetchGroupFieldNumbers;
0079: private int[] secondClassMutableFieldNumbers;
0080:
0081: private FieldManager currFM = null;
0082:
0083: static {
0084: try {
0085: helper = JDOImplHelper.getInstance();
0086: } catch (SecurityException e) {
0087: throw new JDOFatalUserException(
0088: "Insufficient access granted to com.triactive.jdo.*",
0089: e);
0090: }
0091: }
0092:
0093: /**
0094: * Constructs a state manager to manage an existing persistence-capable
0095: * instance that is not persistent.
0096: * All mutable SCO fields are wrapped in suitable wrapper objects and the
0097: * PC instance transitions to a TransientClean state.
0098: *
0099: * @param pm
0100: * The persistence manager controlling this state manager.
0101: * @param pc
0102: * The instance to be managed.
0103: */
0104:
0105: public StateManagerImpl(PersistenceManager pm, PersistenceCapable pc) {
0106: myPM = pm;
0107: myTX = pm.currentTransaction();
0108: srm = myPM.getStoreManager();
0109: myPC = pc;
0110: myID = null;
0111: myLC = LifeCycleState.getLifeCycleState(LifeCycleState.T_CLEAN);
0112: jdoFlags = PersistenceCapable.READ_OK;
0113: cmd = ClassMetaData.forClass(pc.getClass());
0114:
0115: initialize();
0116:
0117: for (int i = 0; i < fieldCount; ++i)
0118: loadedFields[i] = true;
0119:
0120: myPC.jdoReplaceStateManager(this );
0121: myPC.jdoReplaceFlags();
0122:
0123: replaceSCOFields();
0124: }
0125:
0126: /**
0127: * Constructs a state manager to manage a new, hollow instance having the
0128: * given object ID.
0129: * This constructor is used for creating new instances of existing
0130: * persistent objects (i.e. via pm.getObjectById()).
0131: *
0132: * @param pm
0133: * The persistence manager controlling this state manager.
0134: * @param pcClass
0135: * The class of the new instance to be created.
0136: * @param id
0137: * The JDO identity of the object.
0138: */
0139:
0140: public StateManagerImpl(PersistenceManager pm, Class pcClass,
0141: Object id) {
0142: myPM = pm;
0143: myTX = pm.currentTransaction();
0144: srm = myPM.getStoreManager();
0145: myID = id;
0146: myLC = LifeCycleState.getLifeCycleState(LifeCycleState.HOLLOW);
0147: jdoFlags = PersistenceCapable.LOAD_REQUIRED;
0148: cmd = ClassMetaData.forClass(pcClass);
0149:
0150: initialize();
0151:
0152: myPC = helper.newInstance(pcClass, this );
0153: }
0154:
0155: private void initialize() {
0156: fieldCount = cmd.getInheritedFieldCount() + cmd.getFieldCount();
0157:
0158: dirtyFields = new boolean[fieldCount];
0159: loadedFields = (boolean[]) cmd.getTransactionalFieldFlags()
0160: .clone();
0161: defaultFetchGroupFields = cmd.getDefaultFetchGroupFieldFlags();
0162: secondClassMutableFields = cmd
0163: .getSecondClassMutableFieldFlags();
0164:
0165: allFieldNumbers = cmd.getAllFieldNumbers();
0166: persistentFieldNumbers = cmd.getPersistentFieldNumbers();
0167: defaultFetchGroupFieldNumbers = cmd
0168: .getDefaultFetchGroupFieldNumbers();
0169: secondClassMutableFieldNumbers = cmd
0170: .getSecondClassMutableFieldNumbers();
0171: }
0172:
0173: /**
0174: * Passes all SCO field values through replaceFields(), which will wrap any
0175: * unwrapped values with an appropriate SCO wrapper.
0176: */
0177: void replaceSCOFields() {
0178: StateFieldManager scoFieldValues = new StateFieldManager();
0179:
0180: provideFields(secondClassMutableFieldNumbers, scoFieldValues);
0181: replaceFields(secondClassMutableFieldNumbers, scoFieldValues);
0182: }
0183:
0184: Object wrapSCOInstance(int field, Object value) {
0185: if (value == null
0186: || (value instanceof SCO && ((SCO) value).getOwner() == myPC))
0187: return value;
0188: else {
0189: FieldMetaData fmd = cmd.getFieldAbsolute(field);
0190: Class fieldType = fmd.getType();
0191:
0192: SCOProcessor scoProc = SCOProcessor.forFieldType(fieldType);
0193:
0194: if (scoProc == null)
0195: throw new JDOUserException(
0196: "Class not supported as a second-class object: "
0197: + fieldType.getName());
0198:
0199: return scoProc.newSCOInstance(myPC, fmd.getName(), value);
0200: }
0201: }
0202:
0203: /**
0204: * Calls unsetOwner() on all SCO fields.
0205: */
0206: private void disownSCOFields() {
0207: provideFields(secondClassMutableFieldNumbers,
0208: new AbstractFieldManager() {
0209: public void storeObjectField(int fieldNumber,
0210: Object value) {
0211: if (value instanceof SCO)
0212: ((SCO) value).unsetOwner();
0213: }
0214: });
0215: }
0216:
0217: private boolean isDFGLoaded() {
0218: for (int i = 0; i < defaultFetchGroupFieldNumbers.length; ++i) {
0219: if (!loadedFields[defaultFetchGroupFieldNumbers[i]])
0220: return false;
0221: }
0222:
0223: return true;
0224: }
0225:
0226: void enlistInTransaction() {
0227: myPM.enlistInTransaction(this );
0228:
0229: if (jdoFlags == PersistenceCapable.LOAD_REQUIRED
0230: && isDFGLoaded()) {
0231: /*
0232: * A transactional object whose DFG fields are loaded does not need
0233: * to contact us in order to read those fields.
0234: */
0235: jdoFlags = PersistenceCapable.READ_OK;
0236: myPC.jdoReplaceFlags();
0237: }
0238: }
0239:
0240: void evictFromTransaction() {
0241: myPM.evictFromTransaction(this );
0242:
0243: if (jdoFlags == PersistenceCapable.READ_OK
0244: && myLC.isPersistent()) {
0245: /*
0246: * A non-transactional object needs to contact us on any field read no
0247: * matter what fields are loaded.
0248: */
0249: jdoFlags = PersistenceCapable.LOAD_REQUIRED;
0250: myPC.jdoReplaceFlags();
0251: }
0252: }
0253:
0254: void saveFields() {
0255: savedFlags = jdoFlags;
0256: savedLoadedFields = (boolean[]) loadedFields.clone();
0257: savedImage = myPC.jdoNewInstance(this );
0258: savedImage.jdoCopyFields(myPC, allFieldNumbers);
0259:
0260: for (int i = 0; i < secondClassMutableFieldNumbers.length; ++i) {
0261: int field = secondClassMutableFieldNumbers[i];
0262:
0263: /*
0264: * Note: SCO fields in the saved image are clones and therefore
0265: * unowned. If/when they are later restored the individual life-
0266: * cycle states decide whether or not they must be rewrapped to get
0267: * reowned (if going Transient they don't, otherwise they do).
0268: */
0269: Object value = provideField(savedImage, field);
0270:
0271: if (value != null) {
0272: if (value instanceof SCO)
0273: value = ((SCO) value).clone();
0274: else {
0275: Class c = value.getClass();
0276:
0277: try {
0278: Method m = c.getMethod("clone", null);
0279:
0280: if (m != null)
0281: value = m.invoke(value, null);
0282: } catch (Exception e) {
0283: throw new JDOFatalInternalException(
0284: "SCO class not cloneable: "
0285: + c.getName());
0286: }
0287: }
0288:
0289: replaceField(savedImage, field, value);
0290: }
0291: }
0292: }
0293:
0294: void restoreFields() {
0295: if (savedImage != null) {
0296: loadedFields = savedLoadedFields;
0297: jdoFlags = savedFlags;
0298: myPC.jdoReplaceFlags();
0299: myPC.jdoCopyFields(savedImage, allFieldNumbers);
0300:
0301: clearDirtyFlags();
0302: discardSavedFields();
0303: }
0304: }
0305:
0306: void discardSavedFields() {
0307: savedFlags = 0;
0308: savedLoadedFields = null;
0309: savedImage = null;
0310: }
0311:
0312: void clearPersistentFields() {
0313: try {
0314: if (myPC instanceof InstanceCallbacks)
0315: ((InstanceCallbacks) myPC).jdoPreClear();
0316: } finally {
0317: replaceFieldsInternal(persistentFieldNumbers,
0318: hollowFieldManager);
0319: clearLoadedFlags();
0320: clearDirtyFlags();
0321: }
0322: }
0323:
0324: private void clearLoadedFlags() {
0325: jdoFlags = PersistenceCapable.LOAD_REQUIRED;
0326: myPC.jdoReplaceFlags();
0327: System.arraycopy(cmd.getTransactionalFieldFlags(), 0,
0328: loadedFields, 0, loadedFields.length);
0329: }
0330:
0331: private void clearDirtyFlags() {
0332: dirty = false;
0333: clearFlags(dirtyFields);
0334: }
0335:
0336: /**
0337: * Returns an array of integers containing the indices of all elements in
0338: * <tt>flags</tt> that are set to <tt>state</tt>.
0339: */
0340:
0341: private static int[] getFlagsSetTo(boolean[] flags, boolean state) {
0342: int[] temp = new int[flags.length];
0343: int j = 0;
0344:
0345: for (int i = 0; i < flags.length; i++) {
0346: if (flags[i] == state)
0347: temp[j++] = i;
0348: }
0349:
0350: if (j != 0) {
0351: int[] fieldNumbers = new int[j];
0352: System.arraycopy(temp, 0, fieldNumbers, 0, j);
0353:
0354: return fieldNumbers;
0355: } else
0356: return null;
0357: }
0358:
0359: /**
0360: * Returns an array of integers containing the indices of all elements in
0361: * <tt>flags</tt> whose index occurs in <tt>indices</tt> and whose value is
0362: * <tt>state</tt>.
0363: */
0364:
0365: private static int[] getFlagsSetTo(boolean[] flags, int[] indices,
0366: boolean state) {
0367: int[] temp = new int[indices.length];
0368: int j = 0;
0369:
0370: for (int i = 0; i < indices.length; i++) {
0371: if (flags[indices[i]] == state)
0372: temp[j++] = indices[i];
0373: }
0374:
0375: if (j != 0) {
0376: int[] fieldNumbers = new int[j];
0377: System.arraycopy(temp, 0, fieldNumbers, 0, j);
0378:
0379: return fieldNumbers;
0380: } else
0381: return null;
0382: }
0383:
0384: private static void clearFlags(boolean[] flags) {
0385: for (int i = 0; i < flags.length; i++)
0386: flags[i] = false;
0387: }
0388:
0389: private boolean disconnectClone(PersistenceCapable pc) {
0390: if (pc != myPC) {
0391: if (LOG.isDebugEnabled())
0392: LOG.debug("Disconnecting clone " + toJVMIDString(pc)
0393: + " from " + this );
0394:
0395: /*
0396: * Reset jdoFlags in the clone to PersistenceCapable.READ_WRITE_OK
0397: * and clear its state manager.
0398: */
0399: byte myJdoFlags = jdoFlags;
0400: jdoFlags = PersistenceCapable.READ_WRITE_OK;
0401:
0402: try {
0403: pc.jdoReplaceFlags();
0404: } finally {
0405: jdoFlags = myJdoFlags;
0406: }
0407:
0408: pc.jdoReplaceStateManager(null);
0409: return true;
0410: } else
0411: return false;
0412: }
0413:
0414: /**
0415: * The StateManager uses this method to supply the value of jdoFlags to the
0416: * associated PersistenceCapable instance.
0417: *
0418: * @param pc
0419: * the calling PersistenceCapable instance
0420: * @return
0421: * the value of jdoFlags to be stored in the PersistenceCapable instance
0422: */
0423:
0424: public byte replacingFlags(PersistenceCapable pc) {
0425: // If this is a clone, return READ_WRITE_OK.
0426: if (pc != myPC)
0427: return PersistenceCapable.READ_WRITE_OK;
0428: else
0429: return jdoFlags;
0430: }
0431:
0432: /**
0433: * Return the PersistenceManager that owns this instance.
0434: *
0435: * @param pc
0436: * the calling PersistenceCapable instance
0437: * @return
0438: * the PersistenceManager that owns this instance
0439: */
0440:
0441: public javax.jdo.PersistenceManager getPersistenceManager(
0442: PersistenceCapable pc) {
0443: if (disconnectClone(pc))
0444: return null;
0445: else {
0446: this .myPM.hereIsStateManager(this , myPC);
0447: return myPM;
0448: }
0449: }
0450:
0451: private void postWriteField(int field) {
0452: /*
0453: * If we've written a field in the middle of inserting or flushing it
0454: * must be due to jdoPreStore(). If inserting, we haven't actually done
0455: * the INSERT yet so we don't want to mark anything as dirty, which
0456: * would make us want to do an UPDATE later.
0457: */
0458: if (myLC.isPersistent() && !inserting) {
0459: dirty = true;
0460: dirtyFields[field] = true;
0461: loadedFields[field] = true;
0462:
0463: /*
0464: * If flushing, to avoid an infinite recursion we don't notify the
0465: * PM or call flush().
0466: */
0467: if (!flushing) {
0468: if (myTX.isActive())
0469: myPM.markDirty(this );
0470: else
0471: flush();
0472: }
0473: }
0474: }
0475:
0476: /**
0477: * Marks the given field dirty.
0478: */
0479:
0480: public void makeDirty(int field) {
0481: transitionWriteField();
0482: postWriteField(field);
0483: }
0484:
0485: /**
0486: * Mark the associated PersistenceCapable field dirty.
0487: *
0488: * @param pc the calling PersistenceCapable instance
0489: * @param fieldName the name of the field
0490: */
0491:
0492: public void makeDirty(PersistenceCapable pc, String fieldName) {
0493: if (!disconnectClone(pc)) {
0494: int fieldNumber = cmd.getAbsoluteFieldNumber(fieldName);
0495:
0496: if (fieldNumber == -1)
0497: throw new JDOUserException("No such field '"
0498: + fieldName + "' in class "
0499: + cmd.getPCClass().getName());
0500:
0501: makeDirty(fieldNumber);
0502: }
0503: }
0504:
0505: public Object getObjectId() {
0506: return myID;
0507: }
0508:
0509: public StoreManager getStoreManager() {
0510: return srm;
0511: }
0512:
0513: public PersistenceManager getPersistenceManager() {
0514: return myPM;
0515: }
0516:
0517: /**
0518: * Return the object representing the JDO identity of the calling instance.
0519: *
0520: * According to the JDO specification, if the JDO identity is being changed
0521: * in the current transaction, this method returns the JDO identify as of the
0522: * beginning of the transaction.
0523: *
0524: * @param pc the calling PersistenceCapable instance
0525: * @return the object representing the JDO identity of the calling instance
0526: */
0527:
0528: public Object getObjectId(PersistenceCapable pc) {
0529: if (disconnectClone(pc))
0530: return null;
0531: else
0532: return myID;
0533: }
0534:
0535: /**
0536: * Replace the current value of jdoStateManager.
0537: *
0538: * <P>This method is called by the PersistenceCapable whenever
0539: * jdoReplaceStateManager is called and there is already
0540: * an owning StateManager. This is a security precaution
0541: * to ensure that the owning StateManager is the only
0542: * source of any change to its reference in the PersistenceCapable.</p>
0543: *
0544: * @return the new value for the jdoStateManager
0545: * @param pc the calling PersistenceCapable instance
0546: * @param sm the proposed new value for the jdoStateManager
0547: */
0548:
0549: public StateManager replacingStateManager(PersistenceCapable pc,
0550: StateManager sm) {
0551: if (myLC == null)
0552: throw new JDOFatalInternalException("Null LifeCycleState");
0553:
0554: if (pc == myPC) {
0555: if (sm != null)
0556: throw new JDOFatalInternalException(
0557: "Attempted to replace with a different state manager");
0558: if (!disconnecting)
0559: throw new JDOFatalInternalException(
0560: "Attempted to clear state manager from other than disconnect()");
0561:
0562: if (LOG.isDebugEnabled())
0563: LOG.debug("Clearing state manager for "
0564: + toJVMIDString(pc));
0565:
0566: return null;
0567: } else if (pc == savedImage)
0568: return null;
0569: else
0570: return sm;
0571: }
0572:
0573: /**
0574: * Return the object representing the JDO identity
0575: * of the calling instance. If the JDO identity is being changed in
0576: * the current transaction, this method returns the current identity as
0577: * changed in the transaction.
0578: *
0579: * @param pc the calling PersistenceCapable instance
0580: * @return the object representing the JDO identity of the calling instance
0581: */
0582:
0583: public Object getTransactionalObjectId(PersistenceCapable pc) {
0584: return getObjectId(pc);
0585: }
0586:
0587: /**
0588: * Tests whether this object is dirty.
0589: *
0590: * Instances that have been modified, deleted, or newly
0591: * made persistent in the current transaction return true.
0592: *
0593: * <P>Transient nontransactional instances return false (JDO spec), but the
0594: * TriActive implementation does not currently support the transient
0595: * transactional state.
0596: *
0597: * @see PersistenceCapable#jdoMakeDirty(String fieldName)
0598: * @param pc the calling PersistenceCapable instance
0599: * @return true if this instance has been modified in the current transaction.
0600: */
0601:
0602: public boolean isDirty(PersistenceCapable pc) {
0603: if (disconnectClone(pc))
0604: return false;
0605: else
0606: return myLC.isDirty();
0607: }
0608:
0609: /**
0610: * Tests whether this object is transactional.
0611: *
0612: * Instances that respect transaction boundaries return true. These instances
0613: * include transient instances made transactional as a result of being the
0614: * target of a makeTransactional method call; newly made persistent or deleted
0615: * persistent instances; persistent instances read in data store
0616: * transactions; and persistent instances modified in optimistic transactions.
0617: *
0618: * <P>Transient nontransactional instances return false.
0619: *
0620: * @param pc the calling PersistenceCapable instance
0621: * @return true if this instance is transactional.
0622: */
0623:
0624: public boolean isTransactional(PersistenceCapable pc) {
0625: if (disconnectClone(pc))
0626: return false;
0627: else
0628: return myLC.isTransactional();
0629: }
0630:
0631: /**
0632: * Tests whether this object is persistent.
0633: *
0634: * Instances whose state is stored in the data store return true.
0635: *
0636: * <P>Transient instances return false.
0637: *
0638: * @see PersistenceManager#makePersistent(Object pc)
0639: * @param pc the calling PersistenceCapable instance
0640: * @return true if this instance is persistent.
0641: */
0642:
0643: public boolean isPersistent(PersistenceCapable pc) {
0644: if (disconnectClone(pc))
0645: return false;
0646: else
0647: return myLC.isPersistent();
0648: }
0649:
0650: /**
0651: * Tests whether this object has been newly made persistent.
0652: *
0653: * Instances that have been made persistent in the current transaction
0654: * return true.
0655: *
0656: * <P>Transient instances return false.
0657: *
0658: * @see PersistenceManager#makePersistent(Object pc)
0659: * @param pc the calling PersistenceCapable instance
0660: * @return true if this instance was made persistent
0661: * in the current transaction.
0662: */
0663:
0664: public boolean isNew(PersistenceCapable pc) {
0665: if (disconnectClone(pc))
0666: return false;
0667: else
0668: return myLC.isNew();
0669: }
0670:
0671: /**
0672: * Tests whether this object has been deleted.
0673: *
0674: * Instances that have been deleted in the current transaction return true.
0675: *
0676: * <P>Transient instances return false.
0677: *
0678: * @see PersistenceManager#deletePersistent(Object pc)
0679: * @param pc the calling PersistenceCapable instance
0680: * @return true if this instance was deleted
0681: * in the current transaction.
0682: */
0683:
0684: public boolean isDeleted(PersistenceCapable pc) {
0685: if (disconnectClone(pc))
0686: return false;
0687: else
0688: return myLC.isDeleted();
0689: }
0690:
0691: /**
0692: * Called whenever the default fetch group fields have all been loaded.
0693: * Updates jdoFlags and calls jdoPostLoad() as appropriate.
0694: * <p>
0695: * If it's called in the midst of a life-cycle transition both actions will
0696: * be deferred until the transition is complete.
0697: * <em>This deferral is important</em>.
0698: * Without it, we could enter user code (jdoPostLoad()) while still making
0699: * a state transition, and that way lies madness.
0700: * <p>
0701: * As an example, consider a jdoPostLoad() that calls other enhanced methods
0702: * that read fields (jdoPostLoad() itself is not enhanced). A P_NONTRANS
0703: * object accessed within a transaction would produce the following infinite
0704: * loop:
0705: * <p>
0706: * <blockquote><pre>
0707: * isLoaded()
0708: * transitionReadField()
0709: * refreshLoadedFields()
0710: * jdoPostLoad()
0711: * isLoaded()
0712: * ...
0713: * </pre></blockquote>
0714: * <p>
0715: * because the transition from P_NONTRANS to P_CLEAN can never be completed.
0716: */
0717:
0718: private void postLoad() {
0719: if (changingState)
0720: postLoadPending = true;
0721: else {
0722: /*
0723: * A transactional object whose DFG fields are loaded does not need
0724: * to contact us in order to read those fields, so we can safely set
0725: * READ_OK.
0726: *
0727: * A non-transactional object needs to notify us on all field reads
0728: * so that we can decide whether or not any transition should occur,
0729: * so we leave the flags at LOAD_REQUIRED.
0730: */
0731: if (jdoFlags == PersistenceCapable.LOAD_REQUIRED
0732: && myLC.isTransactional()) {
0733: jdoFlags = PersistenceCapable.READ_OK;
0734: myPC.jdoReplaceFlags();
0735: }
0736:
0737: if (myPC instanceof InstanceCallbacks)
0738: ((InstanceCallbacks) myPC).jdoPostLoad();
0739: }
0740: }
0741:
0742: /**
0743: * Validates that the instance exists in the data store.
0744: * Called by pm.getObjectById() when validate == true.
0745: */
0746:
0747: public void validate() {
0748: if (!myLC.isTransactional()) {
0749: /*
0750: * We're either Hollow or PNonTrans. If a TX is active and there
0751: * are any DFG fields then a retrieve() does what we want, i.e. it
0752: * validates existence and makes us PClean. Otherwise we do the
0753: * minimum lookup operation and leave our state as it is.
0754: */
0755: if (myTX.isActive()
0756: && defaultFetchGroupFieldNumbers.length != 0)
0757: retrieve(true);
0758: else
0759: srm.lookup(this );
0760: }
0761: }
0762:
0763: /**
0764: * Offers the specified pre-fetched fields to the state manager.
0765: * If the instance is in a state that can benefit from newly available field
0766: * values, the fields are replaced in the instance and a state change occurs
0767: * as though the instance itself had read a field.
0768: * Called by pm.getObjectById() when given prefetched fields.
0769: */
0770:
0771: public void offerPrefetchedFields(int[] fieldNumbers,
0772: FieldManager fieldManager) {
0773: if (!myLC.isTransactional()) {
0774: /* We're either Hollow or PNonTrans. Accept the fields. */
0775: transitionReadField();
0776:
0777: boolean dfgWasAlreadyLoaded = isDFGLoaded();
0778:
0779: replaceFields(fieldNumbers, fieldManager);
0780:
0781: if (!dfgWasAlreadyLoaded && isDFGLoaded())
0782: postLoad();
0783: }
0784: }
0785:
0786: /**
0787: * Fetchs from the database all fields in the default fetch group not
0788: * already loaded.
0789: * Called by, or immediately after, life-cycle transitions.
0790: */
0791:
0792: void loadDFGFields() {
0793: int[] fieldNumbers = getFlagsSetTo(loadedFields,
0794: defaultFetchGroupFieldNumbers, false);
0795:
0796: if (fieldNumbers != null) {
0797: srm.fetch(this , fieldNumbers);
0798: postLoad();
0799: }
0800: }
0801:
0802: /**
0803: * Fetch a given field from the database. Do NOT call with a default fetch
0804: * group field.
0805: *
0806: * @param fieldNumber the field number of the field to fetch.
0807: */
0808:
0809: private void loadNonDFGField(int fieldNumber) {
0810: srm.fetch(this , new int[] { fieldNumber });
0811: }
0812:
0813: /**
0814: * Fetchs from the database all fields not currently loaded.
0815: * Called by life-cycle transitions.
0816: */
0817:
0818: void loadUnloadedFields() {
0819: int[] fieldNumbers = getFlagsSetTo(loadedFields, false);
0820:
0821: if (fieldNumbers != null) {
0822: boolean dfgWasAlreadyLoaded = isDFGLoaded();
0823:
0824: srm.fetch(this , fieldNumbers);
0825:
0826: /* If the DFG was not already loaded, it is now. */
0827: if (!dfgWasAlreadyLoaded)
0828: postLoad();
0829: }
0830: }
0831:
0832: /**
0833: * Refreshes from the database all fields currently loaded.
0834: * Called by life-cycle transitions.
0835: */
0836:
0837: void refreshLoadedFields() {
0838: int[] fieldNumbers = getFlagsSetTo(loadedFields, true);
0839:
0840: if (fieldNumbers != null) {
0841: clearDirtyFlags();
0842: clearLoadedFlags();
0843:
0844: srm.fetch(this , fieldNumbers);
0845:
0846: if (isDFGLoaded())
0847: postLoad();
0848: }
0849: }
0850:
0851: /**
0852: * Guarantee that the serializable transactional and persistent fields
0853: * are loaded into the instance. This method is called by the generated
0854: * jdoPreSerialize method prior to serialization of the instance.
0855: *
0856: * @param pc the calling PersistenceCapable instance
0857: */
0858:
0859: public void preSerialize(PersistenceCapable pc) {
0860: if (disconnectClone(pc))
0861: return;
0862:
0863: retrieve(false);
0864: }
0865:
0866: /**
0867: * Return true if the field is cached in the calling instance.
0868: * <P>In the TriActive implementation of this method, isLoaded() will always
0869: * return true. If the field is not loaded, it will be loaded as a side effect
0870: * of the call to this method. If it is in the default fetch group, the default
0871: * fetch group, including this field, will be loaded.
0872: *
0873: * @param pc the calling PersistenceCapable instance
0874: * @param field the absolute field number
0875: * @return always returns true (this implementation)
0876: */
0877:
0878: public boolean isLoaded(PersistenceCapable pc, int field) {
0879: if (disconnectClone(pc))
0880: return true;
0881: else {
0882: transitionReadField();
0883:
0884: if (!loadedFields[field]) {
0885: if (defaultFetchGroupFields[field])
0886: loadDFGFields();
0887: else
0888: loadNonDFGField(field);
0889: }
0890:
0891: return true;
0892: }
0893: }
0894:
0895: /**
0896: * Reads the current value of the specified field.
0897: * This has the same effect on the state of the instance as if user code
0898: * attempted to read the field.
0899: *
0900: * @param field
0901: * The field number to read.
0902: * @return
0903: * The value of the field. Primitives are boxed in appropriate
0904: * java.lang wrapper classes.
0905: */
0906:
0907: public Object getField(int field) {
0908: isLoaded(myPC, field);
0909: return provideField(myPC, field);
0910: }
0911:
0912: /**
0913: * Updates the current value of the specified field.
0914: * This has the same effect on the state of the instance as if user code
0915: * attempted to write the field.
0916: *
0917: * @param field
0918: * The field number to write.
0919: * @param currentValue
0920: * The current value of the field. Used to determine whether or not
0921: * the write is redundant. If necessary, the value can be obtained
0922: * using {@link #getField}.
0923: * @param newValue
0924: * The new value of the field. Primitives must be boxed in appropriate
0925: * java.lang wrapper classes.
0926: */
0927:
0928: public void setField(int field, Object currentValue, Object newValue) {
0929: if (!loadedFields[field] || currentValue != newValue)
0930: writeObjectField(field, currentValue, newValue);
0931: }
0932:
0933: /**
0934: * This method is called by the associated PersistenceCapable if the
0935: * value for the specified field is not cached (i.e., StateManager.isLoaded()
0936: * fails). In this implementation of the StateManager, isLoaded() has a
0937: * side effect of loading unloaded information and will always return true.
0938: * As such, this method should never be called.
0939: */
0940:
0941: public boolean getBooleanField(PersistenceCapable pc, int field,
0942: boolean currentValue) {
0943: throw new JDOUnsupportedOptionException("Method not supported");
0944: }
0945:
0946: /**
0947: * This method is called by the associated PersistenceCapable if the
0948: * value for the specified field is not cached (i.e., StateManager.isLoaded()
0949: * fails). In this implementation of the StateManager, isLoaded() has a
0950: * side effect of loading unloaded information and will always return true.
0951: * As such, this method should never be called.
0952: */
0953:
0954: public byte getByteField(PersistenceCapable pc, int field,
0955: byte currentValue) {
0956: throw new JDOUnsupportedOptionException("Method not supported");
0957: }
0958:
0959: /**
0960: * This method is called by the associated PersistenceCapable if the
0961: * value for the specified field is not cached (i.e., StateManager.isLoaded()
0962: * fails). In this implementation of the StateManager, isLoaded() has a
0963: * side effect of loading unloaded information and will always return true.
0964: * As such, this method should never be called.
0965: */
0966:
0967: public char getCharField(PersistenceCapable pc, int field,
0968: char currentValue) {
0969: throw new JDOUnsupportedOptionException("Method not supported");
0970: }
0971:
0972: /**
0973: * This method is called by the associated PersistenceCapable if the
0974: * value for the specified field is not cached (i.e., StateManager.isLoaded()
0975: * fails). In this implementation of the StateManager, isLoaded() has a
0976: * side effect of loading unloaded information and will always return true.
0977: * As such, this method should never be called.
0978: */
0979:
0980: public double getDoubleField(PersistenceCapable pc, int field,
0981: double currentValue) {
0982: throw new JDOUnsupportedOptionException("Method not supported");
0983: }
0984:
0985: /**
0986: * This method is called by the associated PersistenceCapable if the
0987: * value for the specified field is not cached (i.e., StateManager.isLoaded()
0988: * fails). In this implementation of the StateManager, isLoaded() has a
0989: * side effect of loading unloaded information and will always return true.
0990: * As such, this method should never be called.
0991: */
0992:
0993: public float getFloatField(PersistenceCapable pc, int field,
0994: float currentValue) {
0995: throw new JDOUnsupportedOptionException("Method not supported");
0996: }
0997:
0998: /**
0999: * This method is called by the associated PersistenceCapable if the
1000: * value for the specified field is not cached (i.e., StateManager.isLoaded()
1001: * fails). In this implementation of the StateManager, isLoaded() has a
1002: * side effect of loading unloaded information and will always return true.
1003: * As such, this method should never be called.
1004: */
1005:
1006: public int getIntField(PersistenceCapable pc, int field,
1007: int currentValue) {
1008: throw new JDOUnsupportedOptionException("Method not supported");
1009: }
1010:
1011: /**
1012: * This method is called by the associated PersistenceCapable if the
1013: * value for the specified field is not cached (i.e., StateManager.isLoaded()
1014: * fails). In this implementation of the StateManager, isLoaded() has a
1015: * side effect of loading unloaded information and will always return true.
1016: * As such, this method should never be called.
1017: */
1018:
1019: public long getLongField(PersistenceCapable pc, int field,
1020: long currentValue) {
1021: throw new JDOUnsupportedOptionException("Method not supported");
1022: }
1023:
1024: /**
1025: * This method is called by the associated PersistenceCapable if the
1026: * value for the specified field is not cached (i.e., StateManager.isLoaded()
1027: * fails). In this implementation of the StateManager, isLoaded() has a
1028: * side effect of loading unloaded information and will always return true.
1029: * As such, this method should never be called.
1030: */
1031:
1032: public short getShortField(PersistenceCapable pc, int field,
1033: short currentValue) {
1034: throw new JDOUnsupportedOptionException("Method not supported");
1035: }
1036:
1037: /**
1038: * This method is called by the associated PersistenceCapable if the
1039: * value for the specified field is not cached (i.e., StateManager.isLoaded()
1040: * fails). In this implementation of the StateManager, isLoaded() has a
1041: * side effect of loading unloaded information and will always return true.
1042: * As such, this method should never be called.
1043: */
1044:
1045: public String getStringField(PersistenceCapable pc, int field,
1046: String currentValue) {
1047: throw new JDOUnsupportedOptionException("Method not supported");
1048: }
1049:
1050: /**
1051: * This method is called by the associated PersistenceCapable if the
1052: * value for the specified field is not cached (i.e., StateManager.isLoaded()
1053: * fails). In this implementation of the StateManager, isLoaded() has a
1054: * side effect of loading unloaded information and will always return true.
1055: * As such, this method should never be called.
1056: */
1057:
1058: public Object getObjectField(PersistenceCapable pc, int field,
1059: Object currentValue) {
1060: throw new JDOUnsupportedOptionException("Method not supported");
1061: }
1062:
1063: /**
1064: * Called by the various setXXXField() methods once it's established that
1065: * the field really deserves to be written.
1066: * Makes the state transition and replaces the field value in the PC
1067: * instance.
1068: */
1069:
1070: private void writeField(int field, Object newValue) {
1071: transitionWriteField();
1072: replaceField(myPC, field, newValue);
1073: postWriteField(field);
1074: }
1075:
1076: /**
1077: * Called by setObjectField() once it's established that the field really
1078: * deserves to be written.
1079: * Makes the state transition and replaces the field value in the PC
1080: * instance.
1081: * Wraps SCO fields if needed.
1082: */
1083:
1084: private void writeObjectField(int field, Object currentValue,
1085: Object newValue) {
1086: transitionWriteField();
1087:
1088: if (secondClassMutableFields[field]) {
1089: if (currentValue instanceof SCO)
1090: ((SCO) currentValue).unsetOwner();
1091:
1092: newValue = wrapSCOInstance(field, newValue);
1093: }
1094:
1095: replaceField(myPC, field, newValue);
1096: postWriteField(field);
1097: }
1098:
1099: /**
1100: * This method is called by the associated PersistenceCapable when the
1101: * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1102: *
1103: * @param pc the calling PersistenceCapable instance
1104: * @param field the field number
1105: * @param currentValue the current value of the field
1106: * @param newValue the new value for the field
1107: */
1108:
1109: public void setBooleanField(PersistenceCapable pc, int field,
1110: boolean currentValue, boolean newValue) {
1111: if (pc != myPC) {
1112: replaceField(pc, field, newValue ? Boolean.TRUE
1113: : Boolean.FALSE);
1114: disconnectClone(pc);
1115: } else {
1116: if (!loadedFields[field] || currentValue != newValue)
1117: writeField(field, newValue ? Boolean.TRUE
1118: : Boolean.FALSE);
1119: }
1120: }
1121:
1122: /**
1123: * This method is called by the associated PersistenceCapable when the
1124: * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1125: *
1126: * @param pc the calling PersistenceCapable instance
1127: * @param field the field number
1128: * @param currentValue the current value of the field
1129: * @param newValue the new value for the field
1130: */
1131:
1132: public void setByteField(PersistenceCapable pc, int field,
1133: byte currentValue, byte newValue) {
1134: if (pc != myPC) {
1135: replaceField(pc, field, new Byte(newValue));
1136: disconnectClone(pc);
1137: } else {
1138: if (!loadedFields[field] || currentValue != newValue)
1139: writeField(field, new Byte(newValue));
1140: }
1141: }
1142:
1143: /**
1144: * This method is called by the associated PersistenceCapable when the
1145: * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1146: *
1147: * @param pc the calling PersistenceCapable instance
1148: * @param field the field number
1149: * @param currentValue the current value of the field
1150: * @param newValue the new value for the field
1151: */
1152:
1153: public void setCharField(PersistenceCapable pc, int field,
1154: char currentValue, char newValue) {
1155: if (pc != myPC) {
1156: replaceField(pc, field, new Character(newValue));
1157: disconnectClone(pc);
1158: } else {
1159: if (!loadedFields[field] || currentValue != newValue)
1160: writeField(field, new Character(newValue));
1161: }
1162: }
1163:
1164: /**
1165: * This method is called by the associated PersistenceCapable when the
1166: * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1167: *
1168: * @param pc the calling PersistenceCapable instance
1169: * @param field the field number
1170: * @param currentValue the current value of the field
1171: * @param newValue the new value for the field
1172: */
1173:
1174: public void setDoubleField(PersistenceCapable pc, int field,
1175: double currentValue, double newValue) {
1176: if (pc != myPC) {
1177: replaceField(pc, field, new Double(newValue));
1178: disconnectClone(pc);
1179: } else {
1180: if (!loadedFields[field] || currentValue != newValue)
1181: writeField(field, new Double(newValue));
1182: }
1183: }
1184:
1185: /**
1186: * This method is called by the associated PersistenceCapable when the
1187: * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1188: *
1189: * @param pc the calling PersistenceCapable instance
1190: * @param field the field number
1191: * @param currentValue the current value of the field
1192: * @param newValue the new value for the field
1193: */
1194:
1195: public void setFloatField(PersistenceCapable pc, int field,
1196: float currentValue, float newValue) {
1197: if (pc != myPC) {
1198: replaceField(pc, field, new Float(newValue));
1199: disconnectClone(pc);
1200: } else {
1201: if (!loadedFields[field] || currentValue != newValue)
1202: writeField(field, new Float(newValue));
1203: }
1204: }
1205:
1206: /**
1207: * This method is called by the associated PersistenceCapable when the
1208: * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1209: *
1210: * @param pc the calling PersistenceCapable instance
1211: * @param field the field number
1212: * @param currentValue the current value of the field
1213: * @param newValue the new value for the field
1214: */
1215:
1216: public void setIntField(PersistenceCapable pc, int field,
1217: int currentValue, int newValue) {
1218: if (pc != myPC) {
1219: replaceField(pc, field, new Integer(newValue));
1220: disconnectClone(pc);
1221: } else {
1222: if (!loadedFields[field] || currentValue != newValue)
1223: writeField(field, new Integer(newValue));
1224: }
1225: }
1226:
1227: /**
1228: * This method is called by the associated PersistenceCapable when the
1229: * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1230: *
1231: * @param pc the calling PersistenceCapable instance
1232: * @param field the field number
1233: * @param currentValue the current value of the field
1234: * @param newValue the new value for the field
1235: */
1236:
1237: public void setLongField(PersistenceCapable pc, int field,
1238: long currentValue, long newValue) {
1239: if (pc != myPC) {
1240: replaceField(pc, field, new Long(newValue));
1241: disconnectClone(pc);
1242: } else {
1243: if (!loadedFields[field] || currentValue != newValue)
1244: writeField(field, new Long(newValue));
1245: }
1246: }
1247:
1248: /**
1249: * This method is called by the associated PersistenceCapable when the
1250: * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1251: *
1252: * @param pc the calling PersistenceCapable instance
1253: * @param field the field number
1254: * @param currentValue the current value of the field
1255: * @param newValue the new value for the field
1256: */
1257:
1258: public void setShortField(PersistenceCapable pc, int field,
1259: short currentValue, short newValue) {
1260: if (pc != myPC) {
1261: replaceField(pc, field, new Short(newValue));
1262: disconnectClone(pc);
1263: } else {
1264: if (!loadedFields[field] || currentValue != newValue)
1265: writeField(field, new Short(newValue));
1266: }
1267: }
1268:
1269: /**
1270: * This method is called by the associated PersistenceCapable when the
1271: * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1272: *
1273: * @param pc the calling PersistenceCapable instance
1274: * @param field the field number
1275: * @param currentValue the current value of the field
1276: * @param newValue the new value for the field
1277: */
1278:
1279: public void setStringField(PersistenceCapable pc, int field,
1280: String currentValue, String newValue) {
1281: if (pc != myPC) {
1282: replaceField(pc, field, newValue);
1283: disconnectClone(pc);
1284: } else {
1285: if (!loadedFields[field] || !equals(currentValue, newValue))
1286: writeField(field, newValue);
1287: }
1288: }
1289:
1290: /**
1291: * This method is called by the associated PersistenceCapable when the
1292: * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1293: *
1294: * @param pc the calling PersistenceCapable instance
1295: * @param field the field number
1296: * @param currentValue the current value of the field
1297: * @param newValue the new value for the field
1298: */
1299:
1300: public void setObjectField(PersistenceCapable pc, int field,
1301: Object currentValue, Object newValue) {
1302: if (pc != myPC) {
1303: replaceField(pc, field, newValue);
1304: disconnectClone(pc);
1305: } else {
1306: if (!loadedFields[field] || currentValue != newValue)
1307: writeObjectField(field, currentValue, newValue);
1308: }
1309: }
1310:
1311: /**
1312: * Compares two objects for equality, where one or both of the object
1313: * references may be null.
1314: *
1315: * @return <code>true</code> if the objects are both <code>null</code> or
1316: * compare equal according to their equals() method,
1317: * <code>false</code> otherwise.
1318: */
1319:
1320: private static boolean equals(Object o1, Object o2) {
1321: return o1 == null ? (o2 == null) : o1.equals(o2);
1322: }
1323:
1324: /**
1325: * This method is called from the associated PersistenceCapable when its
1326: * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1327: * to provide the value of the specified field to the StateManager.
1328: *
1329: * @param pc the calling PersistenceCapable instance
1330: * @param field the field number
1331: * @param currentValue the current value of the field
1332: */
1333:
1334: public void providedBooleanField(PersistenceCapable pc, int field,
1335: boolean currentValue) {
1336: currFM.storeBooleanField(field, currentValue);
1337: }
1338:
1339: /**
1340: * This method is called from the associated PersistenceCapable when its
1341: * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1342: * to provide the value of the specified field to the StateManager.
1343: *
1344: * @param pc the calling PersistenceCapable instance
1345: * @param field the field number
1346: * @param currentValue the current value of the field
1347: */
1348:
1349: public void providedByteField(PersistenceCapable pc, int field,
1350: byte currentValue) {
1351: currFM.storeByteField(field, currentValue);
1352: }
1353:
1354: /**
1355: * This method is called from the associated PersistenceCapable when its
1356: * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1357: * to provide the value of the specified field to the StateManager.
1358: *
1359: * @param pc the calling PersistenceCapable instance
1360: * @param field the field number
1361: * @param currentValue the current value of the field
1362: */
1363:
1364: public void providedCharField(PersistenceCapable pc, int field,
1365: char currentValue) {
1366: currFM.storeCharField(field, currentValue);
1367: }
1368:
1369: /**
1370: * This method is called from the associated PersistenceCapable when its
1371: * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1372: * to provide the value of the specified field to the StateManager.
1373: *
1374: * @param pc the calling PersistenceCapable instance
1375: * @param field the field number
1376: * @param currentValue the current value of the field
1377: */
1378:
1379: public void providedDoubleField(PersistenceCapable pc, int field,
1380: double currentValue) {
1381: currFM.storeDoubleField(field, currentValue);
1382: }
1383:
1384: /**
1385: * This method is called from the associated PersistenceCapable when its
1386: * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1387: * to provide the value of the specified field to the StateManager.
1388: *
1389: * @param pc the calling PersistenceCapable instance
1390: * @param field the field number
1391: * @param currentValue the current value of the field
1392: */
1393:
1394: public void providedFloatField(PersistenceCapable pc, int field,
1395: float currentValue) {
1396: currFM.storeFloatField(field, currentValue);
1397: }
1398:
1399: /**
1400: * This method is called from the associated PersistenceCapable when its
1401: * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1402: * to provide the value of the specified field to the StateManager.
1403: *
1404: * @param pc the calling PersistenceCapable instance
1405: * @param field the field number
1406: * @param currentValue the current value of the field
1407: */
1408:
1409: public void providedIntField(PersistenceCapable pc, int field,
1410: int currentValue) {
1411: currFM.storeIntField(field, currentValue);
1412: }
1413:
1414: /**
1415: * This method is called from the associated PersistenceCapable when its
1416: * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1417: * to provide the value of the specified field to the StateManager.
1418: *
1419: * @param pc the calling PersistenceCapable instance
1420: * @param field the field number
1421: * @param currentValue the current value of the field
1422: */
1423:
1424: public void providedLongField(PersistenceCapable pc, int field,
1425: long currentValue) {
1426: currFM.storeLongField(field, currentValue);
1427: }
1428:
1429: /**
1430: * This method is called from the associated PersistenceCapable when its
1431: * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1432: * to provide the value of the specified field to the StateManager.
1433: *
1434: * @param pc the calling PersistenceCapable instance
1435: * @param field the field number
1436: * @param currentValue the current value of the field
1437: */
1438:
1439: public void providedShortField(PersistenceCapable pc, int field,
1440: short currentValue) {
1441: currFM.storeShortField(field, currentValue);
1442: }
1443:
1444: /**
1445: * This method is called from the associated PersistenceCapable when its
1446: * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1447: * to provide the value of the specified field to the StateManager.
1448: *
1449: * @param pc the calling PersistenceCapable instance
1450: * @param field the field number
1451: * @param currentValue the current value of the field
1452: */
1453:
1454: public void providedStringField(PersistenceCapable pc, int field,
1455: String currentValue) {
1456: currFM.storeStringField(field, currentValue);
1457: }
1458:
1459: /**
1460: * This method is called from the associated PersistenceCapable when its
1461: * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1462: * to provide the value of the specified field to the StateManager.
1463: *
1464: * @param pc the calling PersistenceCapable instance
1465: * @param field the field number
1466: * @param currentValue the current value of the field
1467: */
1468:
1469: public void providedObjectField(PersistenceCapable pc, int field,
1470: Object currentValue) {
1471: currFM.storeObjectField(field, currentValue);
1472: }
1473:
1474: /**
1475: * This method is invoked by the PersistenceCapable object's
1476: * jdoReplaceField() method to refresh the value of a boolean field.
1477: *
1478: * @param pc the calling PersistenceCapable instance
1479: * @param field the field number
1480: * @return the new value for the field
1481: */
1482:
1483: public boolean replacingBooleanField(PersistenceCapable pc,
1484: int field) {
1485: boolean value = currFM.fetchBooleanField(field);
1486: loadedFields[field] = true;
1487:
1488: return value;
1489: }
1490:
1491: /**
1492: * This method is invoked by the PersistenceCapable object's
1493: * jdoReplaceField() method to refresh the value of a byte field.
1494: *
1495: * @param obj the calling PersistenceCapable instance
1496: * @param field the field number
1497: * @return the new value for the field
1498: */
1499:
1500: public byte replacingByteField(PersistenceCapable obj, int field) {
1501: byte value = currFM.fetchByteField(field);
1502: loadedFields[field] = true;
1503:
1504: return value;
1505: }
1506:
1507: /**
1508: * This method is invoked by the PersistenceCapable object's
1509: * jdoReplaceField() method to refresh the value of a char field.
1510: *
1511: * @param obj the calling PersistenceCapable instance
1512: * @param field the field number
1513: * @return the new value for the field
1514: */
1515:
1516: public char replacingCharField(PersistenceCapable obj, int field) {
1517: char value = currFM.fetchCharField(field);
1518: loadedFields[field] = true;
1519:
1520: return value;
1521: }
1522:
1523: /**
1524: * This method is invoked by the PersistenceCapable object's
1525: * jdoReplaceField() method to refresh the value of a double field.
1526: *
1527: * @param obj the calling PersistenceCapable instance
1528: * @param field the field number
1529: * @return the new value for the field
1530: */
1531:
1532: public double replacingDoubleField(PersistenceCapable obj, int field) {
1533: double value = currFM.fetchDoubleField(field);
1534: loadedFields[field] = true;
1535:
1536: return value;
1537: }
1538:
1539: /**
1540: * This method is invoked by the PersistenceCapable object's
1541: * jdoReplaceField() method to refresh the value of a float field.
1542: *
1543: * @param obj the calling PersistenceCapable instance
1544: * @param field the field number
1545: * @return the new value for the field
1546: */
1547:
1548: public float replacingFloatField(PersistenceCapable obj, int field) {
1549: float value = currFM.fetchFloatField(field);
1550: loadedFields[field] = true;
1551:
1552: return value;
1553: }
1554:
1555: /**
1556: * This method is invoked by the PersistenceCapable object's
1557: * jdoReplaceField() method to refresh the value of a int field.
1558: *
1559: * @param obj the calling PersistenceCapable instance
1560: * @param field the field number
1561: * @return the new value for the field
1562: */
1563:
1564: public int replacingIntField(PersistenceCapable obj, int field) {
1565: int value = currFM.fetchIntField(field);
1566: loadedFields[field] = true;
1567:
1568: return value;
1569: }
1570:
1571: /**
1572: * This method is invoked by the PersistenceCapable object's
1573: * jdoReplaceField() method to refresh the value of a long field.
1574: *
1575: * @param obj the calling PersistenceCapable instance
1576: * @param field the field number
1577: * @return the new value for the field
1578: */
1579:
1580: public long replacingLongField(PersistenceCapable obj, int field) {
1581: long value = currFM.fetchLongField(field);
1582: loadedFields[field] = true;
1583:
1584: return value;
1585: }
1586:
1587: /**
1588: * This method is invoked by the PersistenceCapable object's
1589: * jdoReplaceField() method to refresh the value of a short field.
1590: *
1591: * @param obj the calling PersistenceCapable instance
1592: * @param field the field number
1593: * @return the new value for the field
1594: */
1595:
1596: public short replacingShortField(PersistenceCapable obj, int field) {
1597: short value = currFM.fetchShortField(field);
1598: loadedFields[field] = true;
1599:
1600: return value;
1601: }
1602:
1603: /**
1604: * This method is invoked by the PersistenceCapable object's
1605: * jdoReplaceField() method to refresh the value of a String field.
1606: *
1607: * @param obj the calling PersistenceCapable instance
1608: * @param field the field number
1609: * @return the new value for the field
1610: */
1611:
1612: public String replacingStringField(PersistenceCapable obj, int field) {
1613: String value = currFM.fetchStringField(field);
1614: loadedFields[field] = true;
1615:
1616: return value;
1617: }
1618:
1619: /**
1620: * This method is invoked by the PersistenceCapable object's
1621: * jdoReplaceField() method to refresh the value of an Object field.
1622: *
1623: * @param obj the calling PersistenceCapable instance
1624: * @param field the field number
1625: * @return the new value for the field
1626: */
1627:
1628: public Object replacingObjectField(PersistenceCapable obj, int field) {
1629: Object value = currFM.fetchObjectField(field);
1630: loadedFields[field] = true;
1631:
1632: return value;
1633: }
1634:
1635: public PersistenceCapable getObject() {
1636: return myPC;
1637: }
1638:
1639: private synchronized Object provideField(PersistenceCapable pc,
1640: int fieldNumber) {
1641: Object obj;
1642:
1643: FieldManager prevFM = currFM;
1644: currFM = new StateFieldManager();
1645:
1646: try {
1647: pc.jdoProvideField(fieldNumber);
1648: return currFM.fetchObjectField(fieldNumber);
1649: } finally {
1650: currFM = prevFM;
1651: }
1652: }
1653:
1654: private synchronized void replaceField(PersistenceCapable pc,
1655: int fieldNumber, final Object value) {
1656: FieldManager prevFM = currFM;
1657: currFM = new GenericFieldManager() {
1658: public Object fetchObjectField(int field) {
1659: return value;
1660: }
1661:
1662: public void storeObjectField(int field, Object value) {
1663: } // not possible
1664: };
1665:
1666: try {
1667: pc.jdoReplaceField(fieldNumber);
1668: } finally {
1669: currFM = prevFM;
1670: }
1671: }
1672:
1673: /**
1674: * Called from the StoreManager after StoreManager.update() is called to obtain
1675: * updated values from the PersistenceCapable associated with this StateManager.
1676: *
1677: * @param fieldNumbers
1678: * An array of field numbers to be updated by the Store
1679: * @param fm
1680: * The updated values are stored in this object. This object is only valid
1681: * for the duration of this call.
1682: */
1683:
1684: public synchronized void provideFields(int fieldNumbers[],
1685: FieldManager fm) {
1686: FieldManager prevFM = currFM;
1687: currFM = fm;
1688:
1689: try {
1690: myPC.jdoProvideFields(fieldNumbers);
1691: } finally {
1692: currFM = prevFM;
1693: }
1694: }
1695:
1696: /**
1697: * Called to refresh data in the PersistenceCapable object associated with
1698: * this StateManager.
1699: * <p>
1700: * Unwrapped values for SCO fields are wrapped with an appropriate SCO
1701: * wrapper object.
1702: *
1703: * @param fieldNumbers
1704: * An array of field numbers to be refreshed.
1705: * @param fm
1706: * Provides the updated values. This object is only used for the
1707: * duration of the call.
1708: */
1709:
1710: public synchronized void replaceFields(int fieldNumbers[],
1711: FieldManager fm) {
1712: replaceFieldsInternal(fieldNumbers, new SCOWrapper(fm, this ,
1713: secondClassMutableFields));
1714: }
1715:
1716: private synchronized void replaceFieldsInternal(int fieldNumbers[],
1717: FieldManager fm) {
1718: FieldManager prevFM = currFM;
1719: currFM = fm;
1720:
1721: try {
1722: myPC.jdoReplaceFields(fieldNumbers);
1723: } finally {
1724: currFM = prevFM;
1725: }
1726: }
1727:
1728: private void preStateChange() {
1729: changingState = true;
1730: }
1731:
1732: private void postStateChange() {
1733: changingState = false;
1734:
1735: if (postLoadPending) {
1736: postLoadPending = false;
1737: postLoad();
1738: }
1739: }
1740:
1741: /**
1742: * Makes the instance persistent.
1743: * If the object is already persistent, this method does nothing.
1744: * Otherwise, a new object ID for the instance is obtained from the store
1745: * manager and the object is inserted in the data store.
1746: * <p>
1747: * Any failure will leave the instance in its previous life-cycle state.
1748: *
1749: * @return
1750: * <code>true</code> if the object was successfully made persistent,
1751: * <code>false</code> if the object was already persistent.
1752: */
1753:
1754: public boolean makePersistent() {
1755: if (myLC.isPersistent())
1756: return false;
1757:
1758: LifeCycleState oldLC = myLC;
1759: preStateChange();
1760: try {
1761: myLC = myLC.transitionMakePersistent(this , myTX);
1762: } finally {
1763: postStateChange();
1764: }
1765:
1766: boolean succeeded = false;
1767:
1768: try {
1769: myID = srm.newObjectID(myPC.getClass());
1770:
1771: if (inserting)
1772: throw new JDOFatalInternalException(
1773: "makePersistent() called recursively");
1774:
1775: inserting = true;
1776:
1777: try {
1778: if (myPC instanceof InstanceCallbacks)
1779: ((InstanceCallbacks) myPC).jdoPreStore();
1780:
1781: srm.insert(this );
1782: } finally {
1783: inserting = false;
1784: }
1785:
1786: succeeded = true;
1787: } finally {
1788: if (!succeeded) {
1789: preStateChange();
1790: try {
1791: myLC = myLC.transitionRollbackState(this , oldLC);
1792: } finally {
1793: postStateChange();
1794: }
1795:
1796: myID = null;
1797: }
1798: }
1799:
1800: myPM.dataStoreModified();
1801:
1802: return true;
1803: }
1804:
1805: private void transitionReadField() {
1806: preStateChange();
1807: try {
1808: myLC = myLC.transitionReadField(this , myTX);
1809: } finally {
1810: postStateChange();
1811: }
1812: }
1813:
1814: private void transitionWriteField() {
1815: preStateChange();
1816: try {
1817: myLC = myLC.transitionWriteField(this , myTX);
1818: } finally {
1819: postStateChange();
1820: }
1821: }
1822:
1823: public void makeTransactional() {
1824: preStateChange();
1825: try {
1826: myLC = myLC.transitionMakeTransactional(this , myTX);
1827: } finally {
1828: postStateChange();
1829: }
1830: }
1831:
1832: public void makeNontransactional() {
1833: preStateChange();
1834: try {
1835: myLC = myLC.transitionMakeNontransactional(this );
1836: } finally {
1837: postStateChange();
1838: }
1839: }
1840:
1841: public void makeTransient() {
1842: preStateChange();
1843: try {
1844: myLC = myLC.transitionMakeTransient(this );
1845: } finally {
1846: postStateChange();
1847: }
1848: }
1849:
1850: public void evict() {
1851: preStateChange();
1852: try {
1853: myLC = myLC.transitionEvict(this );
1854: } finally {
1855: postStateChange();
1856: }
1857: }
1858:
1859: public void refresh() {
1860: preStateChange();
1861: try {
1862: myLC = myLC.transitionRefresh(this , myTX);
1863: } finally {
1864: postStateChange();
1865: }
1866: }
1867:
1868: public void retrieve(boolean DFGOnly) {
1869: preStateChange();
1870: try {
1871: myLC = myLC.transitionRetrieve(this , myTX, DFGOnly);
1872: } finally {
1873: postStateChange();
1874: }
1875: }
1876:
1877: /**
1878: * This method is invoked when a commit is performed in a Transaction
1879: * involving the PersistenceCapable managed by this StateManager
1880: */
1881:
1882: public void postCommit() {
1883: preStateChange();
1884: try {
1885: myLC = myLC.transitionCommit(this , myTX);
1886: } finally {
1887: postStateChange();
1888: }
1889: }
1890:
1891: /**
1892: * This method is invoked when a rollback is performed in a Transaction
1893: * involving the PersistenceCapable managed by this StateManager.
1894: */
1895:
1896: public void preRollback() {
1897: preStateChange();
1898: try {
1899: myLC = myLC.transitionRollback(this , myTX);
1900: } finally {
1901: postStateChange();
1902: }
1903: }
1904:
1905: void preDelete() {
1906: if (myPC instanceof InstanceCallbacks)
1907: ((InstanceCallbacks) myPC).jdoPreDelete();
1908: }
1909:
1910: /**
1911: * Deletes the instance.
1912: * If the object is already deleted, this method does nothing.
1913: * Otherwise, the object is removed from the data store.
1914: * <p>
1915: * Any failure will leave the instance in its previous life-cycle state.
1916: */
1917:
1918: public void deletePersistent() {
1919: if (myLC.isDeleted())
1920: return;
1921:
1922: if (deleting)
1923: throw new JDOFatalInternalException(
1924: "deletePersistent() called recursively");
1925:
1926: deleting = true;
1927:
1928: try {
1929: LifeCycleState oldLC = myLC;
1930: preStateChange();
1931: try {
1932: myLC = myLC.transitionDeletePersistent(this , myTX);
1933: } finally {
1934: postStateChange();
1935: }
1936:
1937: boolean succeeded = false;
1938:
1939: try {
1940: srm.delete(this );
1941: clearLoadedFlags();
1942:
1943: succeeded = true;
1944: } finally {
1945: if (!succeeded) {
1946: preStateChange();
1947: try {
1948: myLC = myLC
1949: .transitionRollbackState(this , oldLC);
1950: } finally {
1951: postStateChange();
1952: }
1953: }
1954: }
1955: } finally {
1956: deleting = false;
1957: }
1958:
1959: myPM.dataStoreModified();
1960: }
1961:
1962: /**
1963: * Flushes any dirty fields to the data store.
1964: *
1965: * <p>Note that "dirty" in this case is not equated to being in the P_DIRTY
1966: * state. The P_DIRTY state means that at least one field in the object has
1967: * been written by the user during the current transaction, whereas for the
1968: * purposes of this method, a field is "dirty" if it's been written by the
1969: * user but not yet updated in the data store. The difference is, it's
1970: * possible for an object's state to be P_DIRTY, yet have no "dirty" fields
1971: * because flush() has been called at least once during the transaction.
1972: */
1973:
1974: public void flush() {
1975: if (dirty) {
1976: if (flushing)
1977: throw new JDOFatalInternalException(
1978: "flush() re-entered");
1979:
1980: flushing = true;
1981:
1982: try {
1983: if (myPC instanceof InstanceCallbacks && !deleting)
1984: ((InstanceCallbacks) myPC).jdoPreStore();
1985:
1986: int[] dirtyFieldNumbers = getFlagsSetTo(dirtyFields,
1987: true);
1988:
1989: if (dirtyFieldNumbers == null)
1990: throw new JDOFatalInternalException(
1991: "dirty == true but no fields are marked dirty");
1992:
1993: srm.update(this , dirtyFieldNumbers);
1994:
1995: clearDirtyFlags();
1996: } finally {
1997: flushing = false;
1998: }
1999:
2000: myPM.dataStoreModified();
2001: }
2002: }
2003:
2004: void disconnect() {
2005: if (LOG.isDebugEnabled())
2006: LOG.debug("Disconnecting " + toJVMIDString(myPC) + " from "
2007: + this );
2008:
2009: disownSCOFields();
2010: myPM.removeStateManager(this );
2011: jdoFlags = PersistenceCapable.READ_WRITE_OK;
2012: myPC.jdoReplaceFlags();
2013:
2014: disconnecting = true;
2015:
2016: try {
2017: myPC.jdoReplaceStateManager(null);
2018: } finally {
2019: disconnecting = false;
2020: }
2021:
2022: discardSavedFields();
2023: myPM = null;
2024: myPC = null;
2025: myID = null;
2026: myLC = null;
2027: cmd = null;
2028: srm = null;
2029: }
2030:
2031: public String toString() {
2032: if (myPC == null)
2033: return "StateManager@" + System.identityHashCode(this )
2034: + "(disconnected)";
2035: else {
2036: String pcClassName = myPC.getClass().getName();
2037:
2038: return "StateManager:"
2039: + pcClassName.substring(pcClassName
2040: .lastIndexOf('.') + 1) + '@'
2041: + System.identityHashCode(myPC) + '(' + myID + ')';
2042: }
2043: }
2044:
2045: /* ------------------------ debugging helper methods -------------------- */
2046:
2047: private static String toJVMIDString(Object obj) {
2048: if (obj == null)
2049: return "null";
2050: else
2051: return obj.getClass().getName() + '@'
2052: + System.identityHashCode(obj);
2053: }
2054:
2055: private static String booleanArrayToString(boolean[] ba) {
2056: if (ba == null)
2057: return "null";
2058:
2059: StringBuffer sb = new StringBuffer("[");
2060:
2061: for (int i = 0; i < ba.length; ++i)
2062: sb.append(ba[i] ? 'Y' : 'N');
2063:
2064: sb.append(']');
2065:
2066: return sb.toString();
2067: }
2068:
2069: private static String intArrayToString(int[] ia) {
2070: if (ia == null)
2071: return "null";
2072:
2073: StringBuffer sb = new StringBuffer("[");
2074:
2075: for (int i = 0; i < ia.length; ++i) {
2076: if (i > 0)
2077: sb.append(',');
2078:
2079: sb.append(ia[i]);
2080: }
2081:
2082: sb.append(']');
2083:
2084: return sb.toString();
2085: }
2086:
2087: private static String jdoFlagsToString(byte flags) {
2088: switch (flags) {
2089: case PersistenceCapable.LOAD_REQUIRED:
2090: return "LOAD_REQUIRED";
2091: case PersistenceCapable.READ_OK:
2092: return "READ_OK";
2093: case PersistenceCapable.READ_WRITE_OK:
2094: return "READ_WRITE_OK";
2095: default:
2096: return "???";
2097: }
2098: }
2099:
2100: private static void dumpPC(PersistenceCapable pc, PrintWriter out) {
2101: out.println(toJVMIDString(pc));
2102:
2103: if (pc == null)
2104: return;
2105:
2106: out.print("jdoStateManager = ");
2107: out.println(peekField(pc, "jdoStateManager"));
2108: out.print("jdoFlags = ");
2109: Object flagsObj = peekField(pc, "jdoFlags");
2110:
2111: if (flagsObj instanceof Byte)
2112: out
2113: .println(jdoFlagsToString(((Byte) flagsObj)
2114: .byteValue()));
2115: else
2116: out.println(flagsObj);
2117:
2118: Class c = pc.getClass();
2119:
2120: do {
2121: String[] fieldNames = helper.getFieldNames(c);
2122:
2123: for (int i = 0; i < fieldNames.length; ++i) {
2124: out.print(fieldNames[i]);
2125: out.print(" = ");
2126: out.println(peekField(pc, fieldNames[i]));
2127: }
2128:
2129: c = c.getSuperclass();
2130: } while (c != null
2131: && PersistenceCapable.class.isAssignableFrom(c));
2132: }
2133:
2134: private static Object peekField(Object obj, String fieldName) {
2135: try {
2136: /*
2137: * This doesn't work due to security problems but you get the idea.
2138: * I'm trying to get field values directly without going through
2139: * the provideField machinery.
2140: */
2141: Object value = obj.getClass().getDeclaredField(fieldName)
2142: .get(obj);
2143:
2144: if (value instanceof PersistenceCapable)
2145: return toJVMIDString(value);
2146: else
2147: return value;
2148: } catch (Exception e) {
2149: return e.toString();
2150: }
2151: }
2152:
2153: public void dump(PrintWriter out) {
2154: out.print("myPM = ");
2155: out.println(myPM);
2156: out.print("myTX = ");
2157: out.println(myTX);
2158: out.print("myID = ");
2159: out.println(myID);
2160: out.print("myLC = ");
2161: out.println(myLC);
2162: out.print("cmd = ");
2163: out.println(cmd);
2164: out.print("srm = ");
2165: out.println(srm);
2166: out.print("fieldCount = ");
2167: out.println(fieldCount);
2168: out.print("dirty = ");
2169: out.println(dirty);
2170: out.print("flushing = ");
2171: out.println(flushing);
2172: out.print("changingState = ");
2173: out.println(changingState);
2174: out.print("postLoadPending = ");
2175: out.println(postLoadPending);
2176: out.print("disconnecting = ");
2177: out.println(disconnecting);
2178: out.print("dirtyFields = ");
2179: out.println(booleanArrayToString(dirtyFields));
2180: out.print("defaultFetchGroupFields = ");
2181: out.println(booleanArrayToString(defaultFetchGroupFields));
2182: out.print("secondClassMutableFields = ");
2183: out.println(booleanArrayToString(secondClassMutableFields));
2184: out.print("allFieldNumbers = ");
2185: out.println(intArrayToString(allFieldNumbers));
2186: out.print("persistentFieldNumbers = ");
2187: out.println(intArrayToString(persistentFieldNumbers));
2188: out.print("defaultFetchGroupFieldNumbers = ");
2189: out.println(intArrayToString(defaultFetchGroupFieldNumbers));
2190: out.print("secondClassMutableFieldNumbers = ");
2191: out.println(intArrayToString(secondClassMutableFieldNumbers));
2192:
2193: out.println();
2194: out.print("jdoFlags = ");
2195: out.println(jdoFlagsToString(jdoFlags));
2196: out.print("loadedFields = ");
2197: out.println(booleanArrayToString(loadedFields));
2198: out.print("myPC = ");
2199: dumpPC(myPC, out);
2200:
2201: out.println();
2202: out.print("savedFlags = ");
2203: out.println(jdoFlagsToString(savedFlags));
2204: out.print("savedLoadedFields = ");
2205: out.println(booleanArrayToString(savedLoadedFields));
2206: out.print("savedImage = ");
2207: dumpPC(savedImage, out);
2208: }
2209: }
|