KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > jdo > spi > persistence > support > sqlstore > SQLStateManager


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24 /*
25  * SQLStateManager.java
26  *
27  * Created on March 3, 2000
28  */

29
30 package com.sun.jdo.spi.persistence.support.sqlstore;
31
32 import com.sun.jdo.api.persistence.support.*;
33 import com.sun.jdo.spi.persistence.support.sqlstore.ejb.EJBHelper;
34 import com.sun.jdo.spi.persistence.support.sqlstore.model.*;
35 import com.sun.jdo.spi.persistence.support.sqlstore.query.jqlc.QueryValueFetcher;
36 import com.sun.jdo.spi.persistence.support.sqlstore.sco.SqlTimestamp;
37 import com.sun.jdo.spi.persistence.support.sqlstore.sql.UpdateObjectDescImpl;
38 import com.sun.jdo.spi.persistence.support.sqlstore.state.LifeCycleState;
39 import com.sun.jdo.spi.persistence.support.sqlstore.state.PersistentNonTransactional;
40 import com.sun.jdo.spi.persistence.support.sqlstore.state.PersistentClean;
41 import com.sun.jdo.spi.persistence.support.sqlstore.state.Hollow;
42 import com.sun.jdo.spi.persistence.utility.I18NHelper;
43 import com.sun.jdo.spi.persistence.utility.NullSemaphore;
44 import com.sun.jdo.spi.persistence.utility.Semaphore;
45 import com.sun.jdo.spi.persistence.utility.SemaphoreImpl;
46 import com.sun.jdo.spi.persistence.utility.logging.Logger;
47
48 import java.lang.reflect.Field JavaDoc;
49 import java.lang.reflect.InvocationTargetException JavaDoc;
50 import java.lang.reflect.Method JavaDoc;
51 import java.sql.Timestamp JavaDoc;
52 import java.util.*;
53
54
55 /**
56  *
57  */

58 public class SQLStateManager implements Cloneable JavaDoc, StateManager, TestStateManager {
59
60     private static final int PRESENCE_MASK = 0;
61
62     private static final int SET_MASK = 1;
63
64     private static final int MAX_MASKS = 2;
65
66     private BitSet fieldMasks;
67
68     /** Array of Object. */
69     public ArrayList hiddenValues;
70
71     private ClassDesc persistenceConfig;
72
73     private PersistenceManager persistenceManager;
74
75     private PersistenceStore store;
76
77     private SQLStateManager beforeImage;
78
79     private Object JavaDoc persistentObject;
80
81     private Object JavaDoc objectId;
82
83     private LifeCycleState state;
84
85     /** This flag is used to disable updates due to dependency management. */
86     private static final short ST_UPDATE_DISABLED = 0x1;
87
88     private static final short ST_REGISTERED = 0x2;
89
90     private static final short ST_VISITED = 0x4;
91
92     private static final short ST_PREPARED_PHASE_II = 0x8;
93
94     private static final short ST_FIELD_TRACKING_INPROGRESS = 0x10;
95
96     private static final short ST_DELETE_INPROGRESS = 0x20;
97
98     private static final short ST_VALIDATION_FAILED = 0x40;
99
100     private short stateFlags;
101
102     // This instance is a replacement for a deleted instance with the same
103
// ObjectId.
104
private boolean isReplacementInstance = false;
105
106     // This instance needs to be registered with the global (weak) cache at
107
// rollback if it transitions to persistent (HOLLOW or P_NONTX) state.
108
private boolean needsRegisterAtRollback = false;
109
110     // This instance needs to be to be verified at the time it is removed
111
// from the global (weak) cache at rollback if it transitions to transient state.
112
private boolean needsVerifyAtDeregister = false;
113
114     // This flag is initially set to false and changed to true when the first
115
// operation (e.g. makePersistent, loadForRead, or getObjectById) succeeds.
116
private boolean valid = false;
117
118     /** Stores the updates to the associated object. */
119     private UpdateObjectDescImpl updateDesc;
120
121     /** Contains state managers depending on this object. */
122     private HashSet updatedForeignReferences;
123
124     /** Counts the foreign state managers this state manager depends on. */
125     private int referenceCount;
126
127     /** Serializes access to this StateManager. */
128     private final Semaphore lock;
129
130     /** The logger. */
131     private static Logger logger = LogHelperStateManager.getLogger();
132
133     /** I18N message handler. */
134     private final static ResourceBundle messages = I18NHelper.loadBundle(
135             SQLStateManager.class);
136
137     /** Name of the USE_BATCH property. */
138     public static final String JavaDoc USE_BATCH_PROPERTY =
139         "com.sun.jdo.spi.persistence.support.sqlstore.USE_BATCH"; // NOI18N
140

141     /**
142      * Property to swich on/off batching. Note, the default is true, meaning we
143      * try to do batching if the property is not specified.
144      */

145     private static final boolean USE_BATCH = Boolean.valueOf(
146         System.getProperty(USE_BATCH_PROPERTY, "true")).booleanValue(); // NOI18N
147

148     /**
149      * Construct a new SQLStateManager so that it locks or does not lock as
150      * per whether or not it is used in a managed environment.
151      */

152     public SQLStateManager(PersistenceStore store, ClassDesc persistenceConfig) {
153         this.store = store;
154         this.persistenceConfig = persistenceConfig;
155
156         if (EJBHelper.isManaged()) {
157             this.lock = new NullSemaphore("SQLStateManager"); // NOI18N
158
} else {
159             this.lock = new SemaphoreImpl("SQLStateManager"); // NOI18N
160
}
161     }
162
163     public synchronized void initialize(boolean persistentInDB) {
164         boolean xactActive = persistenceManager.isActiveTransaction();
165         boolean optimistic = persistenceManager.isOptimisticTransaction();
166         boolean nontransactionalRead = persistenceManager.isNontransactionalRead();
167         LifeCycleState oldstate = state;
168
169         if (state == null) {
170             if (persistentInDB == false) {
171                 // Hollow object aquired by PM.getObjectByOid() does not require
172
// to be persistent in DB
173
state = LifeCycleState.getLifeCycleState(LifeCycleState.HOLLOW);
174                 persistenceManager.setFlags(persistentObject, LOAD_REQUIRED);
175             } else {
176                 if (xactActive && !optimistic) {
177                     state = LifeCycleState.getLifeCycleState(LifeCycleState.P_CLEAN);
178                     persistenceManager.setFlags(persistentObject, READ_OK);
179                 } else {
180                     state = LifeCycleState.getLifeCycleState(LifeCycleState.P_NON_TX);
181                     persistenceManager.setFlags(persistentObject, LOAD_REQUIRED);
182                 }
183                 valid = true;
184             }
185         } else if (state.needMerge()) {
186             state = state.transitionReadField(optimistic, nontransactionalRead, xactActive);
187
188             // If we are in a state that requires the instance to be reloaded
189
// we need to set the jdoFlags to LOAD_REQUIRED to enable field mediation.
190
if (state.needsReload(optimistic, nontransactionalRead, xactActive)) {
191                 persistenceManager.setFlags(persistentObject, LOAD_REQUIRED);
192             } else {
193                 if (persistenceManager.getFlags(persistentObject) == LOAD_REQUIRED) {
194                     persistenceManager.setFlags(persistentObject, READ_OK);
195                 }
196             }
197         }
198
199         registerInstance(false, null, oldstate);
200     }
201
202     private void registerInstance(boolean throwDuplicateException,
203         ArrayList newlyRegisteredSMs, LifeCycleState oldstate) {
204
205         if ((stateFlags & ST_REGISTERED) == 0 || // not registered or
206
(oldstate != state && // state changed from clean to dirty or transactional type.
207
(oldstate == null || oldstate.isDirty() != state.isDirty() ||
208                     oldstate.isTransactional() != state.isTransactional()))) {
209
210             persistenceManager.registerInstance(this, getObjectId(), throwDuplicateException, false);
211             stateFlags |= ST_REGISTERED;
212             if (newlyRegisteredSMs != null) {
213                 if (!newlyRegisteredSMs.contains(this))
214                     newlyRegisteredSMs.add(this);
215             }
216         }
217     }
218
219     public void setPersistenceManager(com.sun.jdo.api.persistence.support.PersistenceManager pm) {
220         this.persistenceManager = (PersistenceManager) pm;
221     }
222
223     public void setPersistent(Object JavaDoc pc) {
224         this.persistentObject = pc;
225     }
226
227     public PersistenceStore getStore() {
228         return store;
229     }
230
231     public Object JavaDoc getPersistent() {
232         return persistentObject;
233     }
234
235     public PersistenceConfig getPersistenceConfig() {
236         return persistenceConfig;
237     }
238
239     private UpdateObjectDescImpl getUpdateDesc() {
240         if (updateDesc == null) {
241             updateDesc = (UpdateObjectDescImpl) store.getUpdateObjectDesc(
242                     persistenceConfig.getPersistenceCapableClass());
243         }
244
245         if (updateDesc.getConcurrency() == null) {
246             boolean optimistic = persistenceManager.isOptimisticTransaction();
247             updateDesc.setConcurrency(persistenceConfig.getConcurrency(optimistic));
248         }
249
250         return updateDesc;
251     }
252
253     private void unsetMaskBit(int index, int mask) {
254         if (fieldMasks == null) {
255             newFieldMasks();
256         } else {
257             if (index >= 0) {
258                 fieldMasks.clear(index + mask * persistenceConfig.maxFields);
259             } else {
260                 fieldMasks.clear(-(index + 1) + persistenceConfig.maxVisibleFields +
261                         mask * persistenceConfig.maxFields);
262             }
263         }
264     }
265
266     private void clearMask(int mask) {
267         if (fieldMasks != null) {
268             fieldMasks.clear(mask * persistenceConfig.maxFields,
269                     (mask+1) * persistenceConfig.maxFields);
270         }
271     }
272
273     private void setVisibleMaskBits(int mask) {
274         if (fieldMasks == null) {
275             newFieldMasks();
276         }
277
278         int offset = mask * persistenceConfig.maxFields;
279         fieldMasks.set(offset, offset + persistenceConfig.maxVisibleFields);
280     }
281
282     private BitSet getVisibleMaskBits(int mask) {
283         if (fieldMasks == null) {
284             newFieldMasks();
285         }
286
287         int offset = mask * persistenceConfig.maxFields;
288         return fieldMasks.get(offset, offset + persistenceConfig.maxVisibleFields);
289     }
290
291     private void newFieldMasks() {
292         this.fieldMasks = new BitSet(MAX_MASKS * persistenceConfig.maxFields);
293     }
294
295     public void setPresenceMaskBit(int index) {
296         if (fieldMasks == null) {
297             newFieldMasks();
298         }
299
300         if (index >= 0) {
301             fieldMasks.set(index + PRESENCE_MASK * persistenceConfig.maxFields);
302         } else {
303             fieldMasks.set(-(index + 1) + persistenceConfig.maxVisibleFields +
304                     PRESENCE_MASK * persistenceConfig.maxFields);
305         }
306     }
307
308     private void setSetMaskBit(int index) {
309         if (fieldMasks == null) {
310             newFieldMasks();
311         }
312
313         if (index >= 0) {
314             fieldMasks.set(index + SET_MASK * persistenceConfig.maxFields);
315         } else {
316             fieldMasks.set(-(index + 1) + persistenceConfig.maxVisibleFields +
317                     SET_MASK * persistenceConfig.maxFields);
318         }
319     }
320
321     public boolean getPresenceMaskBit(int index) {
322         if (fieldMasks == null) {
323             newFieldMasks();
324         }
325
326         if (index >= 0) {
327             return fieldMasks.get(index + PRESENCE_MASK * persistenceConfig.maxFields);
328         } else {
329             return fieldMasks.get(-(index + 1) + persistenceConfig.maxVisibleFields +
330                     PRESENCE_MASK * persistenceConfig.maxFields);
331         }
332     }
333
334     public boolean getSetMaskBit(int index) {
335         if (fieldMasks == null) {
336             newFieldMasks();
337         }
338
339         if (index >= 0) {
340             return fieldMasks.get(index + SET_MASK * persistenceConfig.maxFields);
341         } else {
342             return fieldMasks.get(-(index + 1) + persistenceConfig.maxVisibleFields +
343                     SET_MASK * persistenceConfig.maxFields);
344         }
345     }
346
347     public Object JavaDoc getHiddenValue(int index) {
348         // This method expects index to be negative for hidden fields.
349
if (index >= 0) {
350             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
351                     "core.statemanager.poshiddenindex", "" + index)); // NOI18N
352
}
353
354         int realIndex = -(index + 1);
355
356         if ((hiddenValues != null) && (realIndex < hiddenValues.size())) {
357             return hiddenValues.get(realIndex);
358         }
359
360         return null;
361     }
362
363     public void setHiddenValue(int index, Object JavaDoc value) {
364         // This method expects index to be negative for hidden fields.
365
if (index >= 0) {
366             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
367                     "core.statemanager.poshiddenindex", "" + index)); // NOI18N
368
}
369
370         int realIndex = -(index + 1);
371
372         if (hiddenValues == null) {
373             hiddenValues = new ArrayList();
374         }
375
376         for (int i = hiddenValues.size(); i <= realIndex; i++) {
377             hiddenValues.add(null);
378         }
379
380         hiddenValues.set(realIndex, value);
381     }
382
383     public synchronized void replaceObjectField(String JavaDoc fieldName, Object JavaDoc o) {
384         boolean debug = logger.isLoggable();
385
386         if (debug) {
387             Object JavaDoc[] items = new Object JavaDoc[] {fieldName, o.getClass().getName()};
388             logger.fine("sqlstore.sqlstatemanager.replaceobjectfield", items); // NOI18N
389
}
390
391         FieldDesc fieldDesc = persistenceConfig.getField(fieldName);
392         Object JavaDoc oldo = prepareSetField(fieldDesc, o);
393
394         if ((oldo instanceof SCO) && oldo != o) {
395             if (debug)
396                 logger.fine("sqlstore.sqlstatemanager.replaceobjectfield.unsetsco"); // NOI18N
397
((SCO) oldo).unsetOwner();
398         }
399     }
400
401     public synchronized void makeDirty(String JavaDoc fieldName) {
402         boolean debug = logger.isLoggable();
403
404         if (debug) {
405             logger.fine("sqlstore.sqlstatemanager.makedirty", fieldName); // NOI18N
406
}
407
408         FieldDesc fieldDesc = persistenceConfig.getField(fieldName);
409
410         // Save current value: if it is SCO object we need to replace it with the
411
// new value instead of new instance
412
Object JavaDoc oldo = fieldDesc.getValue(this);
413
414         prepareUpdateField(fieldDesc, null);
415
416         // Now adjust SCO instance
417
Object JavaDoc newo = fieldDesc.getValue(this);
418
419         if ((newo instanceof SCO) && oldo != newo) {
420             if (oldo instanceof SCOCollection) {
421                 if (debug) {
422                     logger.fine("sqlstore.sqlstatemanager.makedirty.fixscocollection"); // NOI18N
423
}
424
425                 ((SCOCollection) oldo).clearInternal();
426                 ((SCOCollection) oldo).addAllInternal((Collection) newo);
427             }
428
429             else if (oldo instanceof SCODate) {
430                 if (debug) {
431                     logger.fine("sqlstore.sqlstatemanager.makedirty.fixscodate"); // NOI18N
432
}
433
434                 long l = ((java.util.Date JavaDoc) newo).getTime();
435                 // Adjust nanoseconds if necessary:
436
int n = 0;
437
438                 if (newo instanceof Timestamp) {
439                     n = ((Timestamp) newo).getNanos();
440                 } else {
441                     n = (int) ((l % 1000) * 1000000);
442                 }
443
444                 if (oldo instanceof SqlTimestamp) {
445                     ((SCODate) oldo).setTimeInternal(l);
446                     ((SqlTimestamp) oldo).setNanosInternal(n);
447
448                 } else if (newo instanceof Timestamp) {
449                     ((SCODate) oldo).setTimeInternal(l + (n / 1000000));
450                 } else {
451                     ((SCODate) oldo).setTimeInternal(l);
452                 }
453             }
454
455             updateTrackedFields(fieldDesc, oldo, null);
456             fieldDesc.setValue(this, oldo);
457
458             // disconnect temp SCO instance
459
if (newo instanceof SCO)
460                 ((SCO) newo).unsetOwner();
461         }
462     }
463
464     /**
465      * This method is central to record changes to SCOCollections.
466      */

467     public void applyUpdates(String JavaDoc fieldName, SCOCollection c) {
468         boolean debug = logger.isLoggable();
469
470         if (debug) {
471             logger.fine("sqlstore.sqlstatemanager.applyupdates", fieldName); // NOI18N
472
}
473
474         FieldDesc fieldDesc = persistenceConfig.getField(fieldName);
475         if (fieldDesc instanceof ForeignFieldDesc) {
476             ArrayList removed = new ArrayList(c.getRemoved());
477             ArrayList added = new ArrayList(c.getAdded());
478
479             // We reset the collection to clear the added and removed list before calling
480
// processCollectionUpdates() which can throw an exception.
481
c.reset();
482             processCollectionUpdates((ForeignFieldDesc) fieldDesc, removed, added, null, true, false);
483         }
484         // else it is an ERROR?
485

486         if (debug) {
487             logger.fine("sqlstore.sqlstatemanager.applyupdates.exit"); // NOI18N
488
}
489     }
490
491     public void makePresent(String JavaDoc fieldName, Object JavaDoc value) {
492         boolean debug = logger.isLoggable();
493
494         if (debug) {
495             logger.fine("sqlstore.sqlstatemanager.makepresent", fieldName); // NOI18N
496
}
497
498         FieldDesc fieldDesc = persistenceConfig.getField(fieldName);
499         fieldDesc.setValue(this, value);
500         setPresenceMaskBit(fieldDesc.absoluteID);
501     }
502
503     public void setObjectId(Object JavaDoc objectId) {
504         // RESOLVE: do we allow to replace existing?
505
this.objectId = objectId;
506     }
507
508     public Object JavaDoc getObjectId() {
509         // Note: PM.getObjectId() makes copy of the actual object id.
510
if (objectId == null) {
511             Class JavaDoc oidClass = persistenceConfig.getOidClass();
512             Object JavaDoc oid = null;
513
514             try {
515                 oid = oidClass.newInstance();
516             } catch (Exception JavaDoc e) {
517                 throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
518                         "core.statemanager.cantnewoid", oidClass.getName()), e); // NOI18N
519
}
520
521             Field JavaDoc keyFields[] = persistenceConfig.getKeyFields();
522             String JavaDoc keyFieldNames[] = persistenceConfig.getKeyFieldNames();
523             for (int i = 0; i < keyFields.length; i++) {
524                 Field JavaDoc keyField = keyFields[i];
525                 try {
526                     FieldDesc fd = persistenceConfig.getField(keyFieldNames[i]);
527
528                     if (fd != null) {
529                         keyField.set(oid, fd.getValue(this));
530                     }
531
532                 } catch (IllegalAccessException JavaDoc e) {
533                     throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
534                             "core.statemanager.cantsetkeyfield", keyField.getName()), e); // NOI18N
535
}
536             }
537             objectId = oid;
538         }
539
540         return objectId;
541     }
542
543     private void makeAutoPersistent(Object JavaDoc pc) {
544         persistenceManager.makePersistent(pc);
545         SQLStateManager sm = (SQLStateManager) persistenceManager.getStateManager(pc);
546
547         sm.state = LifeCycleState.getLifeCycleState(LifeCycleState.AP_NEW);
548     }
549
550     /**
551      * Prepares the associated object to be stored in the datastore.
552      * This method is called by PersistenceManager.makePersistent().
553      * Thread synchronization is done in the persistence manager.
554      */

555     public void makePersistent(PersistenceManager pm, Object JavaDoc pc) {
556
557         boolean debug = logger.isLoggable();
558
559         if (debug) {
560             logger.fine("sqlstore.sqlstatemanager.makepersistence", // NOI18N
561
persistenceConfig.getPersistenceCapableClass().getName());
562         }
563
564         // If the instance is autopersistent, we simply transition it to persistent_new.
565
if (state != null) {
566             if (state.isAutoPersistent()) {
567                 state = state.transitionMakePersistent();
568             }
569             return;
570         }
571
572         this.persistenceManager = pm;
573         this.persistentObject = pc;
574
575         // Mark all the visible fields as present to prevent navigation and
576
// to allow us to create a before image that contains all the fields.
577
setVisibleMaskBits(PRESENCE_MASK);
578         getBeforeImage();
579
580         state = LifeCycleState.getLifeCycleState(LifeCycleState.P_NEW);
581
582         try {
583             registerInstance(true, null, null);
584         } catch (JDOException e) {
585             this.release();
586
587             throw e;
588         }
589
590         // We set the statemanager for the pc now so the instance is considered
591
// persistent. We need to do this in order for persistent-by-reachability to
592
// work properly in the case of self-referencing relationship.
593
pm.setStateManager(pc, this);
594         valid = true;
595
596         // Now that the state manager has been set in the pc, we need to
597
// synchronize it so other threads can't modify this instance while
598
// we perform the persistence-by-reachability algorithm.
599
try {
600             getLock();
601
602             // Make sure all the fields have been marked dirty.
603
Object JavaDoc obj = null;
604             ArrayList fields = persistenceConfig.fields;
605             for (int i = 0; i < fields.size(); i++) {
606                 FieldDesc f = (FieldDesc) fields.get(i);
607
608                 // In case of makePersistent, we skip all secondary tracked fields
609
// and use the primary to propagate changes. In addition, we take
610
// the policy that a tracked relationship field takes precedence
611
// over its primitive counterpart. In other words, we skip all
612
// primitive fields that also tracks relationship fields.
613
if ((f.sqlProperties & FieldDesc.PROP_SECONDARY_TRACKED_FIELD) > 0) {
614                     continue;
615                 }
616
617                 obj = f.getValue(this);
618
619                 if (f instanceof ForeignFieldDesc) {
620                     ForeignFieldDesc ff = (ForeignFieldDesc) f;
621                     ArrayList trackedFields = null;
622
623                     if (debug) {
624                         logger.fine("sqlstore.sqlstatemanager.processforeign", ff.getName()); // NOI18N
625
}
626
627                     if ((ff.sqlProperties & FieldDesc.PROP_PRIMARY_TRACKED_FIELD) > 0) {
628                         trackedFields = ff.getTrackedFields();
629                         Object JavaDoc theValue = obj;
630
631                         for (int j = 0; j < trackedFields.size(); j++) {
632                             FieldDesc tf = (FieldDesc) trackedFields.get(j);
633                             Object JavaDoc value = tf.getValue(this);
634
635                             if ((theValue != null) && (value != null) && (theValue != value)) {
636                                 if (needsVerifyAtDeregister) {
637                                     persistenceManager.deregisterInstance(getObjectId(), this);
638                                     needsVerifyAtDeregister = false;
639                                 } else {
640                                     persistenceManager.deregisterInstance(getObjectId());
641                                 }
642                                 this.release();
643                                 throw new JDOUserException(I18NHelper.getMessage(messages,
644                                         "core.statemanager.conflictingvalues", ff.getName(), tf.getName())); // NOI18N
645
} else if ((theValue == null) && (value != null)) {
646                                 theValue = value;
647                             }
648                         }
649
650                         if (theValue != obj) {
651                             obj = theValue;
652                             ff.setValue(this, obj);
653                         }
654                     }
655
656                     if (obj != null) {
657                         if (obj instanceof Collection) {
658                             if (((Collection) obj).size() > 0) {
659                                 ArrayList removed = null;
660                                 ArrayList added = new ArrayList((Collection) obj);
661                                 processCollectionUpdates(ff, removed, added, null, true, false);
662                             }
663                         } else {
664                             // null out this field to pretend we are setting this field for the first time
665
ff.setValue(this, null);
666
667                             updateObjectField(ff, obj, true, false);
668
669                             // now restore the value
670
ff.setValue(this, obj);
671                         }
672                     } else {
673                         // For a null managed collection relationship field, we replace it
674
// with an empty SCOCollection
675
if ((ff.getInverseRelationshipField() != null) && (ff.cardinalityUPB > 1)) {
676                             replaceCollection(ff, null);
677                         }
678                     }
679
680                     updateTrackedFields(ff, ff.getValue(this), null);
681                 } else {
682                     // We ignore primitive fields that also tracks relationship field
683
if ((f.sqlProperties & FieldDesc.PROP_TRACK_RELATIONSHIP_FIELD) > 0) {
684                         ArrayList trackedFields = f.getTrackedFields();
685                         boolean found = false;
686
687                         for (int j = trackedFields.size() - 1; j >= 0; j--) {
688                             FieldDesc tf = (FieldDesc) trackedFields.get(j);
689
690                             if (tf instanceof ForeignFieldDesc) {
691                                 if (tf.getValue(this) != null) {
692                                     found = true;
693                                     break;
694                                 }
695                             } else {
696                                 break;
697                             }
698                         }
699
700                         if (!found) {
701                             //f.setValue(this, null);
702
updateTrackedFields(f, obj, null);
703                             //f.setValue(this, obj);
704
}
705                     } else {
706                         updateTrackedFields(f, obj, null);
707                     }
708
709                     if ((f.sqlProperties & FieldDesc.PROP_RECORD_ON_UPDATE) > 0) {
710                         getUpdateDesc().recordUpdatedField((LocalFieldDesc) f);
711                     }
712                 }
713
714                 if (debug) {
715                     logger.fine("sqlstore.sqlstatemanager.makedirtyfield", f.getName()); // NOI18N
716
}
717
718                 setSetMaskBit(f.absoluteID);
719             }
720         } finally {
721             releaseLock();
722         }
723     }
724
725     /**
726      * Prepares the associated object for delete. This method is
727      * called by PersistenceManager.deletePersistent(). After
728      * nullifying the relationship fields, the instance transitions to
729      * deleted state.
730      */

731     public void deletePersistent() {
732         if (logger.isLoggable()) {
733              logger.fine("sqlstore.sqlstatemanager.deletepersistence", // NOI18N
734
persistenceConfig.getPersistenceCapableClass().getName());
735
736         }
737
738         // Why try try? The difference is in whether you try and then
739
// acquire/get, or acquire/get and then try.
740
// Prior to having acquireFieldUpdateLock, this code synchronized on
741
// a field, following that with the try for {get,release}Lock. That
742
// pattern of usage calls for that order.
743
// {acquire,release}FieldUpdateLock calls for the other order. We
744
// are close to FCS, so this strange situation persists for now
745
// (i.e., it ain't broken).
746

747         persistenceManager.acquireFieldUpdateLock();
748         try {
749             try {
750                 getLock();
751
752                 if (state.isDeleted()) {
753                     return;
754                 }
755
756                 deleteRelationships();
757
758                 LifeCycleState oldstate = state;
759                 state = state.transitionDeletePersistent();
760                 persistenceManager.setFlags(persistentObject, LOAD_REQUIRED);
761                 registerInstance(false, null, oldstate);
762             } finally {
763                 releaseLock();
764             }
765         } finally {
766             persistenceManager.releaseFieldUpdateLock();
767         }
768     }
769
770     /**
771      * Prepares the current instance for delete by nullifying all
772      * relationships. The deletion is propagated to relationship
773      * fields marked for cascade delete.
774      */

775     private void deleteRelationships() {
776         ArrayList foreignFields = persistenceConfig.foreignFields;
777         int size = foreignFields.size();
778         stateFlags |= ST_DELETE_INPROGRESS;
779
780         for (int i = 0; i < size; i++) {
781             ForeignFieldDesc ff = (ForeignFieldDesc) foreignFields.get(i);
782             ForeignFieldDesc irf = ff.getInverseRelationshipField();
783
784             // Skip this field if it is secondary.
785
if ((ff.sqlProperties & FieldDesc.PROP_SECONDARY_TRACKED_FIELD) > 0) {
786                 continue;
787             }
788
789             // Skip this field if it is not managed nor marked for cascade delete.
790
if ((ff.deleteAction != ForeignFieldDesc.ACT_CASCADE) && (irf == null)) {
791                 continue;
792             }
793
794             prepareUpdateField(ff, null);
795
796             if (ff.cardinalityUPB > 1) {
797                 Collection c = (Collection) ff.getValue(this);
798
799                 if (c != null) {
800                     ArrayList removed = new ArrayList(c);
801
802                     // For managed relationship or cascade delete, we need to call
803
// processCollectionUpdates() to set up the dependency. In case of
804
// managed relationship, the inverse relationship field should also
805
// be set to null.
806
processCollectionUpdates(ff, removed, null, null, true, false);
807
808                     if (c instanceof SCOCollection) {
809                         ((SCOCollection) c).clearInternal();
810                     } else {
811                         c.clear();
812                     }
813
814                     if (ff.deleteAction == ForeignFieldDesc.ACT_CASCADE) {
815                         Iterator iter = removed.iterator();
816
817                         while (iter.hasNext()) {
818                             Object JavaDoc obj = iter.next();
819
820                             if (obj != null) {
821                                 SQLStateManager sm = (SQLStateManager)
822                                         persistenceManager.getStateManager(obj);
823
824                                 // Ignore if this sm is in the process of being cascade
825
// deleted. This is to prevent infinite recursive in case
826
// of self-referencing relationship.
827
if ((sm != null) && !sm.isDeleted() &&
828                                         ((sm.stateFlags & ST_DELETE_INPROGRESS) == 0)) {
829                                     try {
830                                         persistenceManager.deletePersistent(obj);
831                                     } catch (Throwable JavaDoc e) {
832                                     }
833                                 }
834                             }
835                         }
836                     }
837                 }
838             } else {
839                 Object JavaDoc obj = ff.getValue(this);
840
841                 if (obj != null) {
842                     updateObjectField(ff, null, true, false);
843                     ff.setValue(this, null);
844
845                     if (ff.deleteAction == ForeignFieldDesc.ACT_CASCADE) {
846                         SQLStateManager sm = (SQLStateManager)
847                                 persistenceManager.getStateManager(obj);
848
849                         // Ignore if this sm is in the process of being cascade
850
// deleted. This is to prevent infinite recursive in case
851
// of self-referencing relationships.
852
if ((sm != null) && !sm.isDeleted() &&
853                                 ((sm.stateFlags & ST_DELETE_INPROGRESS) == 0)) {
854                             try {
855                                 persistenceManager.deletePersistent(obj);
856                             } catch (Throwable JavaDoc e) {
857                             }
858                         }
859                     }
860                 }
861             }
862         }
863
864         stateFlags &= ~ST_DELETE_INPROGRESS;
865     }
866
867     /**
868      * Stores the associated object in the datastore. This method is
869      * called by {@link PersistenceManager#beforeCompletion} on
870      * flush/commit. The specified state manager argument is used to
871      * determine whether the actual instance should be flushed
872      * immediately or whether batch update is possible.
873      *
874      * @param next Next state manager in the transaction cache.
875      */

876     public void updatePersistent(StateManager next) {
877         boolean debug = logger.isLoggable();
878
879         if ((stateFlags & ST_UPDATE_DISABLED) > 0) {
880             if (debug) {
881                 Object JavaDoc[] items = new Object JavaDoc[] {persistenceConfig.getPersistenceCapableClass().getName(),
882                                                persistentObject};
883                 logger.fine("sqlstore.sqlstatemanager.updatepersistent.skipped", items); // NOI18N
884
}
885             return;
886         }
887
888         try {
889             if (debug) {
890                  logger.fine("sqlstore.sqlstatemanager.updatepersistent", // NOI18N
891
persistenceConfig.getPersistenceCapableClass().getName());
892             }
893
894             ArrayList actions = new ArrayList();
895
896             // Get a list of actions to perform.
897
getUpdateActions(actions);
898
899             if (actions.size() == 1 && useBatch()) {
900                 // Batch update only if actions consists of a single action
901
UpdateObjectDesc updateDesc = (UpdateObjectDesc)actions.get(0);
902                 boolean immediateFlush = requiresImmediateFlush((SQLStateManager)next);
903
904                 if (debug && immediateFlush) {
905                     Object JavaDoc[] items = new Object JavaDoc[] {getPersistent(), (next != null) ? next.getPersistent() : null};
906                     logger.fine("sqlstore.sqlstatemanager.updatepersistent.immediateflush", items); // NOI18N
907
}
908
909                 store.executeBatch(persistenceManager, updateDesc, immediateFlush);
910             } else if (actions.size() > 0) {
911                 store.execute(persistenceManager, actions);
912             }
913
914             incrementVersion(actions);
915
916             if (debug) {
917                 logger.fine("sqlstore.sqlstatemanager.updatepersistent.exit"); // NOI18N
918
}
919         } catch (JDOException e) {
920             e.addFailedObject(persistentObject);
921             throw e;
922         } catch (Exception JavaDoc e) {
923             logger.throwing("sqlstore.SQLStateManager", "updatePersistent", e); // NOI18N
924
throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
925                     "core.generic.unknownexception"), e); // NOI18N
926
}
927     }
928
929     /**
930      * Increments the version for all state managers in
931      * <code>actions</code> registered for version consistency.
932      *
933      * @param actions List of updated state managers.
934      */

935     static private void incrementVersion(List actions) {
936
937         for (Iterator iter = actions.iterator(); iter.hasNext(); ) {
938             ((UpdateObjectDescImpl) iter.next()).incrementVersion();
939         }
940     }
941
942     /**
943      * Increments the version fields for this state manager. Instances
944      * mapped to multiple tables have got a version field for each table.
945      */

946     public void incrementVersion() {
947         LocalFieldDesc [] versionFields = persistenceConfig.getVersionFields();
948
949         for (int i = 0; i < versionFields.length; i++) {
950             versionFields[i].incrementValue(this);
951         }
952     }
953
954     /**
955      * @inheritDoc StateManager#hasVersionConsistency
956      */

957     public boolean hasVersionConsistency() {
958         return persistenceConfig.hasVersionConsistency();
959     }
960
961     /**
962      * @inheritDoc StateManager#verifyPersistent
963      */

964     public boolean verifyPersistent() {
965         assert persistenceConfig.hasVersionConsistency();
966         boolean verified = true;
967
968         if (state instanceof PersistentClean) {
969             RetrieveDesc verificationRD = persistenceConfig.getRetrieveDescForVerificationQuery(store);
970             LocalFieldDesc[] keyFields = persistenceConfig.getKeyFieldDescs();
971             LocalFieldDesc[] versionFields = persistenceConfig.getVersionFields();
972
973             // Please make sure that the order of parameter values is same as
974
// order of parameters as defined by ClassDesc#getRetrieveDescForVerificationQuery()
975
Object JavaDoc [] parameters = new Object JavaDoc[keyFields.length + versionFields.length];
976             copyValues(parameters, keyFields, 0);
977             copyValues(parameters, versionFields, keyFields.length);
978
979             // verificationRD requires parameters for pk field and version fields
980
Boolean JavaDoc result = (Boolean JavaDoc) store.
981                     retrieve(persistenceManager, verificationRD, new QueryValueFetcher(parameters));
982             verified = result.booleanValue();
983         }
984         return verified;
985     }
986
987     /**
988      * Returns true if batch update might be used to store the changes
989      * of this state manager.
990      *
991      * TODO: Because batched statements on Oracle don't return a
992      * valid success indicator, batching is disabled for Version
993      * Consistency.
994      */

995     private boolean useBatch() {
996         boolean result = false;
997
998         if (USE_BATCH) {
999             switch(state.getUpdateAction()) {
1000
1001            case ActionDesc.LOG_CREATE:
1002                result = !getUpdateDesc().hasChangedRelationships() &&
1003                         !getUpdateDesc().hasModifiedLobField();
1004                break;
1005            case ActionDesc.LOG_DESTROY:
1006            case ActionDesc.LOG_UPDATE:
1007                // Do not try to batch in optimitic tx for now. We need to
1008
// check for parallel updates, so the WHERE clause checks
1009
// the values from the beforeImage. We need a different SQL
1010
// statements for the null vs. non null case.
1011
result = !persistenceManager.isOptimisticTransaction() &&
1012                         !persistenceConfig.hasModifiedCheckAtCommitConsistency() &&
1013                         !getUpdateDesc().hasChangedRelationships() &&
1014                         !getUpdateDesc().hasModifiedLobField() &&
1015                         !hasVersionConsistency();
1016                break;
1017            default:
1018                result = false;
1019                break;
1020            }
1021        }
1022
1023        return result;
1024    }
1025
1026    /**
1027     * @inheritDoc StateManager#setVerificationFailed
1028     */

1029    public void setVerificationFailed() {
1030        if (hasVersionConsistency()) {
1031            stateFlags |= ST_VALIDATION_FAILED;
1032        }
1033    }
1034
1035    /**
1036     * @inheritDoc StateManager#getFailed
1037     */

1038    public boolean isVerificationFailed() {
1039        return (stateFlags & ST_VALIDATION_FAILED) > 0;
1040    }
1041
1042    /**
1043     * This method checks whether this StateManager instance needs to be
1044     * flushed immediately during beforeCompletion. A return of <code>false</code>
1045     * means the store manager is allowed to combine flushing of these two
1046     * instance in a single database roundtrip (e.g. by using batched updates).
1047     *
1048     * @param next Next state manager to be flushed.
1049     */

1050    private boolean requiresImmediateFlush(SQLStateManager next) {
1051        // There is no next SM =>
1052
// flush this sm immediately
1053
if (next == null)
1054            return true;
1055
1056        // The next StateManager has a different pc class =>
1057
// flush this sm immediately
1058
if (persistenceConfig != next.persistenceConfig)
1059            return true;
1060
1061        // The next StateManager represents a different update operation
1062
// INSERT/UPDATE/DELETE => flush this sm immediately
1063
if (state.getUpdateAction() != next.state.getUpdateAction())
1064            return true;
1065
1066        // If the next's flush is disabled, flush this sm
1067
if ((next.stateFlags & ST_UPDATE_DISABLED) > 0)
1068            return true;
1069
1070        // If next sm does not use batch update flush this sm
1071
if (!next.useBatch())
1072            return true;
1073
1074        // For updates, we need to check if the next sm updates the
1075
// same fields. If not, flush this sm
1076
if (getUpdateDesc().getUpdateAction() == ActionDesc.LOG_UPDATE &&
1077                persistenceConfig.hasLocalNonDFGFields()) {
1078
1079            if (!compareUpdatedFields(next))
1080                return true;
1081        }
1082
1083        // Now we can make use of batching w/o flushing
1084
return false;
1085    }
1086
1087    private boolean compareUpdatedFields(SQLStateManager next) {
1088        BitSet updFields = getVisibleMaskBits(SET_MASK);
1089        BitSet nextUpdFields = (next != null) ? next.getVisibleMaskBits(SET_MASK) : null;
1090
1091        return updFields.equals(nextUpdFields);
1092    }
1093
1094    public void refreshPersistent() {
1095        boolean debug = logger.isLoggable();
1096
1097        if (debug) {
1098            logger.fine("sqlstore.sqlstatemanager.refreshpersistent", // NOI18N
1099
persistenceConfig.getPersistenceCapableClass().getName());
1100        }
1101
1102        // Only refresh if the state allows it.
1103
if (state.isRefreshable()) {
1104            LifeCycleState oldstate = state;
1105            state = state.transitionRefreshPersistent();
1106            reload(null);
1107            registerInstance(false, null, oldstate);
1108        }
1109
1110        if (debug) {
1111            logger.fine("sqlstore.sqlstatemanager.refreshpersistent.exit"); // NOI18N
1112
}
1113    }
1114
1115    /**
1116     * Reloads the instance by delegating actual work, state transition, and
1117     * instance registration to {@link #reload(FieldDesc) reload(FieldDesc)}
1118     * With <code>null</code> as an argument. Called by
1119     * {@link PersistenceManager#getObjectById(Object, boolean)
1120     * PersistenceManager.getObjectById(Object, boolean)} with validate
1121     * flag set to <code>true</code>
1122     */

1123    public void reload() {
1124        boolean debug = logger.isLoggable(Logger.FINER);
1125
1126        if (debug) {
1127            logger.finer("sqlstore.sqlstatemanager.unconditionalreload", // NOI18N
1128
persistenceConfig.getPersistenceCapableClass().getName());
1129        }
1130
1131        persistenceManager.acquireShareLock();
1132
1133        try {
1134            getLock();
1135
1136            reload(null);
1137
1138        } finally {
1139            persistenceManager.releaseShareLock();
1140            releaseLock();
1141
1142            if (debug) {
1143                logger.finer("sqlstore.sqlstatemanager.unconditionalreload.exit"); // NOI18N
1144
}
1145        }
1146    }
1147
1148    /**
1149     * Reloads this SM from the state in the datastore, getting data for the
1150     * given field.
1151     * @param additionalField Field to be loaded.
1152     */

1153    private void reload(FieldDesc additionalField) {
1154        boolean debug = logger.isLoggable();
1155
1156        if (debug) {
1157            String JavaDoc fieldName =
1158                (additionalField != null) ? additionalField.getName() : null;
1159            logger.fine("sqlstore.sqlstatemanager.reload", // NOI18N
1160
persistenceConfig.getPersistenceCapableClass().getName(), fieldName);
1161        }
1162
1163        // Clear the fields PresenceMask so all the currently present
1164
// fields will be replaced.
1165
clearMask(PRESENCE_MASK);
1166
1167        // Need to mark the key fields as present
1168
markKeyFieldsPresent();
1169
1170        clearMask(SET_MASK);
1171
1172        LifeCycleState oldState = state;
1173        state = state.transitionReload(persistenceManager.isActiveTransaction());
1174
1175        if (!retrieveFromVersionConsistencyCache(additionalField)) {
1176            // Retrieve the instance from the data store, if this class
1177
// is not version consistent, or not found in the cache.
1178

1179            try {
1180                retrieve(additionalField);
1181            } catch (JDOException e) {
1182                // Reset the state if the instance couldn't be found.
1183
state = oldState;
1184                throw e;
1185            }
1186        }
1187        registerInstance(false, null, oldState);
1188
1189        if (persistenceManager.getFlags(persistentObject) == LOAD_REQUIRED) {
1190            persistenceManager.setFlags(persistentObject, READ_OK);
1191        }
1192
1193        if (debug) {
1194            logger.fine("sqlstore.sqlstatemanager.reload.exit"); // NOI18N
1195
}
1196    }
1197
1198    /**
1199     * Initialize this SM from the version consistency cache. If this
1200     * SM is in the cache and the additional field is not populated,
1201     * the field is retrieved from the store.
1202     * @param additionalField Field to be loaded.
1203     */

1204    private boolean retrieveFromVersionConsistencyCache(FieldDesc additionalField) {
1205        boolean rc =
1206            persistenceManager.initializeFromVersionConsistencyCache(this);
1207
1208        if (rc) {
1209
1210            // make sure additionalField is available
1211
if (additionalField != null
1212                    && !getPresenceMaskBit(additionalField.absoluteID)) {
1213
1214                realizeField(additionalField);
1215            }
1216        }
1217        return rc;
1218    }
1219
1220    /**
1221     * PersistenceManager calls this method to prepare a persistent
1222     * object for update. This is required for foreign fields only
1223     * as they could reference "regular" JDK Collections vs. SCO
1224     * Collections. Such process has the side-effect of causing more
1225     * objects to be registered with the transaction cache.
1226     */

1227    public void prepareToUpdatePhaseI() {
1228        boolean debug = logger.isLoggable();
1229
1230        if (debug) {
1231            logger.fine("sqlstore.sqlstatemanager.preparetoupdateph1", // NOI18N
1232
persistenceConfig.getPersistenceCapableClass().getName());
1233        }
1234
1235        int action = state.getUpdateAction();
1236
1237        if (action == ActionDesc.LOG_NOOP || action == ActionDesc.LOG_DESTROY) {
1238            // Nothing extra to do
1239
return;
1240        }
1241
1242        // Initialize UpdateDesc.
1243
getUpdateDesc();
1244
1245        ArrayList newlyRegisteredSMs = new ArrayList();
1246        ArrayList foreignFields = persistenceConfig.foreignFields;
1247        int size = foreignFields.size();
1248
1249        for (int i = 0; i < size; i++) {
1250            ForeignFieldDesc ff = (ForeignFieldDesc) foreignFields.get(i);
1251
1252            if ((ff.sqlProperties & FieldDesc.PROP_SECONDARY_TRACKED_FIELD) > 0) {
1253                continue;
1254            }
1255
1256            if ((ff.cardinalityUPB > 1) && (getSetMaskBit(ff.absoluteID) == true)) {
1257                Collection v = (Collection) ff.getValue(this);
1258
1259                if ((v != null) && (!(v instanceof SCO) || (((SCO) v).getOwner() == null)) &&
1260                        (v.size() > 0)) {
1261                    ArrayList removed = null;
1262                    ArrayList added = new ArrayList(v);
1263
1264                    processCollectionUpdates(ff, removed, added, newlyRegisteredSMs, true, false);
1265                }
1266            }
1267        }
1268
1269        // The newRegisteredSMs should contain a list of all the state managers that
1270
// are registered as the result of processCollectionUpdates.
1271
for (int i = 0; i < newlyRegisteredSMs.size(); i++) {
1272            SQLStateManager sm = (SQLStateManager) newlyRegisteredSMs.get(i);
1273
1274            sm.prepareToUpdatePhaseI();
1275        }
1276
1277        if (debug) {
1278            logger.fine("sqlstore.sqlstatemanager.preparetoupdateph1.exit"); // NOI18N
1279
}
1280    }
1281
1282    /**
1283     * This is the second phase of the commit processing. It populates phase3sms with all
1284     * the autopersistent instances that are no longer reachable from a persistent instance.
1285     *
1286     * @param phase3sms List containing autopersistent instances that are no longer reachable
1287     * from a persistent instance.
1288     */

1289    public void prepareToUpdatePhaseII(HashSet phase3sms) {
1290        boolean debug = logger.isLoggable();
1291
1292        if (debug) {
1293             logger.fine("sqlstore.sqlstatemanager.preparetoupdateph2", // NOI18N
1294
persistenceConfig.getPersistenceCapableClass().getName());
1295        }
1296
1297        // If this instance is autopersistent, we transition it into a pending state and
1298
// add it to phase3sms collection. Any instance in phase3sms collection may be removed
1299
// later if it becomes persistent.
1300
if (state.isAutoPersistent()) {
1301            state = state.transitionMakePending();
1302            phase3sms.add(this);
1303            return;
1304        }
1305
1306        if ((stateFlags & ST_PREPARED_PHASE_II) > 0) {
1307            return;
1308        }
1309
1310        stateFlags |= ST_PREPARED_PHASE_II;
1311
1312        if ((!state.isNew() && !state.isDirty()) || state.isDeleted()) {
1313            return;
1314        }
1315
1316        ArrayList foreignFields = persistenceConfig.foreignFields;
1317        int size = foreignFields.size();
1318
1319        // Walk the object graph starting from this instance and transition all
1320
// autopersistent instances to persistent and remove it from phase3sms.
1321
for (int i = 0; i < size; i++) {
1322            ForeignFieldDesc ff = (ForeignFieldDesc) foreignFields.get(i);
1323
1324            if (ff.cardinalityUPB <= 1) {
1325                if (getPresenceMaskBit(ff.absoluteID)) {
1326                    Object JavaDoc v = ff.getValue(this);
1327
1328                    if (v != null) {
1329                        transitionPersistent(v, phase3sms);
1330                    }
1331                }
1332            } else {
1333                Collection c = getCollectionValue(ff);
1334
1335                if (c != null) {
1336                    Iterator iter = c.iterator();
1337
1338                    while (iter.hasNext()) {
1339                        Object JavaDoc v = iter.next();
1340
1341                        transitionPersistent(v, phase3sms);
1342                    }
1343                }
1344            }
1345        }
1346
1347        if (debug) {
1348            logger.fine("sqlstore.sqlstatemanager.preparetoupdateph2.exit"); // NOI18N
1349
}
1350    }
1351
1352    /**
1353     * This is the third phase of commit processing. It sets up the delete dependencies among
1354     * all the autopersistent instances that have been flushed to the database.
1355     */

1356    public void prepareToUpdatePhaseIII() {
1357        boolean debug = logger.isLoggable();
1358
1359        if (debug) {
1360            logger.fine("sqlstore.sqlstatemanager.preparetoupdateph3", // NOI18N
1361
persistenceConfig.getPersistenceCapableClass().getName());
1362        }
1363
1364        if (!state.isPersistentInDataStore()) {
1365            // This object will not be written to the store. But we need to
1366
// make sure, that scheduled jointable entries aren't written either.
1367
// See UpdateQueryPlan#processJoinTables().
1368
if (updateDesc != null) {
1369                updateDesc.clearUpdatedJoinTableRelationships();
1370            }
1371
1372            // Finished for this instance.
1373
return;
1374        }
1375
1376        ArrayList foreignFields = persistenceConfig.foreignFields;
1377        int size = foreignFields.size();
1378
1379        // Sets up dependencies between this instance and all its relationship fields
1380
// that are autopersistent.
1381
for (int i = 0; i < size; i++) {
1382            ForeignFieldDesc ff = (ForeignFieldDesc) foreignFields.get(i);
1383
1384            if (ff.cardinalityUPB <= 1) {
1385                if (getPresenceMaskBit(ff.absoluteID)) {
1386                    Object JavaDoc v = ff.getValue(this);
1387
1388                    if (v != null) {
1389                        updateObjectField(ff, null, false, false);
1390                    }
1391                }
1392            } else {
1393                Collection c = getCollectionValue(ff);
1394
1395                if (c != null) {
1396                    if (c.size() > 0) {
1397                        ArrayList removed = new ArrayList(c);
1398                        ArrayList added = null;
1399
1400                        processCollectionUpdates(ff, removed, added, null, false, false);
1401                    }
1402                }
1403            }
1404        }
1405
1406        if (debug) {
1407            logger.fine("sqlstore.sqlstatemanager.preparetoupdateph3.exit"); // NOI18N
1408
}
1409    }
1410
1411    /**
1412     * Transitions the instance <code>pc</code> to persistent state if it's
1413     * autopersistent, and removes its state manager from the list
1414     * <code>phase3sms</code> of unreachable autopersistent
1415     * instances. The recursive call to <code>prepareToUpdatePhaseII</code>
1416     * removes the transitive closure of all state managers reachable from
1417     * <code>pc</code> from <code>phase3sms</code>. This method has got no
1418     * effects on transient instances.
1419     *
1420     * @param pc Instance becoming persistent and removed from <code>phase3sms</code>.
1421     * @param phase3sms List containing so far unreachable autopersistent instances.
1422     */

1423    private void transitionPersistent(Object JavaDoc pc, HashSet phase3sms) {
1424        SQLStateManager sm = (SQLStateManager) persistenceManager.getStateManager(pc);
1425
1426        // Need to check if the associated state manager is null, if
1427
// called with an object from a collection relationship field. If
1428
// the collection is not a SCO collection, it is possible that it
1429
// contains transient instances. No need to check for object
1430
// relationship fields.
1431
if (sm != null && sm.state.isAutoPersistent()) {
1432            sm.state = sm.state.transitionMakePersistent();
1433            phase3sms.remove(sm);
1434            sm.prepareToUpdatePhaseII(phase3sms);
1435        }
1436    }
1437
1438    /**
1439     * Returns the value of the collection relationship field
1440     * <code>ff</code>. For deferred SCOCollections, only the
1441     * objects added in the current transaction are returned.
1442     * This method may only be called for Collection fields!
1443     *
1444     * @param ff Collection relationship field.
1445     * @return The value of the collection relationship field
1446     * <code>ff</code>. For deferred SCOCollections, only the
1447     * objects added in the current transaction are returned.
1448     */

1449    private Collection getCollectionValue(ForeignFieldDesc ff) {
1450        Collection c = null;
1451        if (ff.cardinalityUPB > 1) {
1452            c = (Collection) ff.getValue(this);
1453            if (c != null && c instanceof SCOCollection) {
1454                SCOCollection sco = (SCOCollection) c;
1455                if (sco.isDeferred()) {
1456                    c = sco.getAdded();
1457                }
1458            }
1459        }
1460        return c;
1461    }
1462
1463    private void getUpdateActions(ArrayList actions) {
1464        if ((stateFlags & ST_VISITED) > 0) {
1465            return;
1466        }
1467
1468        int action = state.getUpdateAction();
1469
1470        if ((action == ActionDesc.LOG_NOOP) && (updateDesc == null)) {
1471            return;
1472        }
1473
1474        // Initialize updateDesc.
1475
getUpdateDesc();
1476
1477        updateDesc.setObjectInfo(getBeforeImage(), this, action);
1478
1479        if ((action == ActionDesc.LOG_DESTROY) || (action == ActionDesc.LOG_CREATE) ||
1480                updateDesc.hasUpdatedFields() || updateDesc.hasUpdatedJoinTableRelationships()) {
1481            actions.add(updateDesc);
1482        }
1483
1484        stateFlags |= ST_VISITED;
1485
1486        if (updatedForeignReferences != null) {
1487            Iterator iter = updatedForeignReferences.iterator();
1488
1489            while (iter.hasNext()) {
1490                SQLStateManager sm = ((UpdatedForeignReference) iter.next()).getStateManager();
1491
1492                if (sm.referenceCount == 1) {
1493                    sm.getUpdateActions(actions);
1494                }
1495
1496                sm.referenceCount--;
1497            }
1498        }
1499    }
1500
1501    public void release() {
1502        if (null != persistenceManager) {
1503
1504            // The persistenceManager can be null, for example if this
1505
// instance is used in the VersionConsistency cache.
1506
persistenceManager.setStateManager(persistentObject, null);
1507        }
1508
1509        persistentObject = null;
1510        objectId = null;
1511        persistenceManager = null;
1512        beforeImage = null;
1513        hiddenValues = null;
1514        updatedForeignReferences = null;
1515        updateDesc = null;
1516        persistenceConfig = null;
1517        store = null;
1518        valid = false;
1519    }
1520
1521    private void reset(boolean retainValues, boolean wasNew, boolean keepState) {
1522        boolean debug = logger.isLoggable();
1523
1524        if (debug) {
1525             Object JavaDoc[] items = new Object JavaDoc[] {Boolean.valueOf(retainValues),
1526                                            Boolean.valueOf(wasNew), Boolean.valueOf(keepState)};
1527             logger.fine("sqlstore.sqlstatemanager.reset", items); // NOI18N
1528

1529        }
1530
1531        if (state == null) {
1532            // make the instance transient.
1533

1534            if (!keepState) {
1535                persistenceManager.clearFields(persistentObject);
1536            }
1537
1538            // Need to set jdoFlag to READ_WRITE_OK for transient instance.
1539
persistenceManager.setFlags(persistentObject, READ_WRITE_OK);
1540
1541            if (needsVerifyAtDeregister) {
1542                persistenceManager.deregisterInstance(getObjectId(), this);
1543            } else {
1544                persistenceManager.deregisterInstance(getObjectId());
1545            }
1546            this.release();
1547        } else {
1548            // Reset the state manager for the next transaction.
1549
stateFlags = 0;
1550            beforeImage = null;
1551            updatedForeignReferences = null;
1552            referenceCount = 0;
1553
1554            if (updateDesc != null) {
1555                updateDesc.reset();
1556            }
1557
1558            // We retain the field values if retainValues is true or the state
1559
// is persistentNontransactional
1560
if (retainValues || (state instanceof PersistentNonTransactional)) {
1561                FieldDesc f = null;
1562                ArrayList fields = persistenceConfig.fields;
1563                for (int i = 0; i < fields.size(); i++) {
1564                    f = (FieldDesc) fields.get(i);
1565                    Object JavaDoc v = f.getValue(this);
1566
1567                    // For new objects mark null references as not set if the field is
1568
// not managed. This is to allow the field to be reloaded in the next
1569
// transaction because a relationship may exist in the database.
1570
if (wasNew && (f instanceof ForeignFieldDesc) &&
1571                            (v == null) && (((ForeignFieldDesc) f).getInverseRelationshipField() == null)) {
1572                        if (debug)
1573                            logger.fine("sqlstore.sqlstatemanager.unsetmask", f.getName()); // NOI18N
1574
unsetMaskBit(f.absoluteID, PRESENCE_MASK);
1575                    }
1576
1577                    // Replace java.util Collection and Date objects
1578
// with SCO instances:
1579
if ((v instanceof Collection) && !(v instanceof SCOCollection)
1580                            && !keepState) {
1581                        if (debug)
1582                            logger.fine("sqlstore.sqlstatemanager.resettingcollection"); // NOI18N
1583

1584                        replaceCollection((ForeignFieldDesc) f, (Collection) v);
1585
1586                        if (debug) {
1587                            logger.fine("sqlstore.sqlstatemanager.newtype", (f.getValue(this)).getClass()); // NOI18N
1588
}
1589                    } else if (v instanceof SCOCollection) {
1590                        ((SCOCollection) v).reset();
1591                    }
1592
1593                    // TO FIX!!!: this already replaces Date with SCO Date
1594
else if ((v instanceof java.util.Date JavaDoc) && !(v instanceof SCODate)
1595                            && !keepState) {
1596                        if (debug)
1597                            logger.fine("sqlstore.sqlstatemanager.resettingdate"); // NOI18N
1598

1599                        v = f.convertValue(v, this);
1600                        f.setValue(this, v);
1601                        if (debug) {
1602                            logger.fine("sqlstore.sqlstatemanager.newtype", (f.getValue(this)).getClass()); // NOI18N
1603
}
1604                    }
1605                }
1606
1607                // We need to set the jdoFlags to LOAD_REQUIRED instead of READ_OK in order to
1608
// have the StateManager intermediate access to this instance. The reason is
1609
// if the next transaction is pessimistic, the StateManager needs to reload
1610
// the instance. Note that the StateManager will not reload the instance
1611
// if the transaction is optimistic and the instance is p_nontransactional.
1612
persistenceManager.setFlags(persistentObject, LOAD_REQUIRED);
1613            } else {
1614                clearMask(PRESENCE_MASK);
1615                persistenceManager.clearFields(persistentObject);
1616
1617                // Need to mark the key fields as present
1618
markKeyFieldsPresent();
1619
1620                persistenceManager.setFlags(persistentObject, LOAD_REQUIRED);
1621            }
1622
1623            clearMask(SET_MASK);
1624            isReplacementInstance = false;
1625            needsRegisterAtRollback = false;
1626            needsVerifyAtDeregister = false;
1627        }
1628    }
1629
1630    /**
1631     * @return true if persistentObject has been flushed to db
1632     */

1633    public boolean isProcessed() {
1634        return (referenceCount == 0);
1635    }
1636
1637    public void flushed() {
1638        // reset the current state to the point where we can accept more updates.
1639
state = state.transitionFlushed();
1640
1641        clearMask(SET_MASK);
1642
1643        stateFlags &= ~ST_VISITED;
1644        stateFlags &= ~ST_UPDATE_DISABLED;
1645        stateFlags &= ~ST_REGISTERED;
1646
1647        // need to set the jdoFlags to LOAD_REQUIRED to allow updated to be tracked.
1648
persistenceManager.setFlags(persistentObject, LOAD_REQUIRED);
1649
1650        if (updatedForeignReferences != null) {
1651            updatedForeignReferences.clear();
1652        }
1653
1654        if (updateDesc != null) {
1655            updateDesc.reset();
1656        }
1657    }
1658
1659    public void commit(boolean retainValues) {
1660        boolean wasNew = (state.isNew() && !state.isDeleted());
1661        state = state.transitionCommit(retainValues);
1662        reset(retainValues, wasNew, false);
1663    }
1664
1665    public void rollback(boolean retainValues) {
1666        boolean wasNew = (state.isNew() && !state.isDeleted());
1667        boolean needsRestore = state.needsRestoreOnRollback(retainValues);
1668        state = state.transitionRollback(retainValues);
1669        boolean keepState = needsRestore;
1670
1671        // Only restore if there is a before image and the flag needsRestore is true.
1672
if ((beforeImage != null) && (needsRestore == true)) {
1673            copyFields(beforeImage, true, false);
1674
1675            // Keep the fields from being reset in reset()
1676
keepState = true;
1677        }
1678
1679        if (needsRegisterAtRollback && !isReplacementInstance) {
1680            persistenceManager.registerInstance(this, getObjectId());
1681        }
1682        reset(retainValues, wasNew, keepState);
1683    }
1684
1685    private void markKeyFieldsPresent() {
1686        ArrayList keyFields = persistenceConfig.getPrimaryTable().getKey().getFields();
1687
1688        for (int i = 0; i < keyFields.size(); i++) {
1689            LocalFieldDesc fd = (LocalFieldDesc) keyFields.get(i);
1690
1691            if (fd != null) {
1692                setPresenceMaskBit(fd.absoluteID);
1693            }
1694        }
1695    }
1696
1697    public void prepareGetField(int fieldID) {
1698        FieldDesc fieldDesc = persistenceConfig.getField(fieldID);
1699
1700        prepareGetField(fieldDesc, false, true);
1701    }
1702
1703    private void prepareGetField(FieldDesc fieldDesc) {
1704        prepareGetField(fieldDesc, true, false);
1705    }
1706
1707    /**
1708     * Loads the field described by <code>fieldDesc</code>. If the field is not
1709     * present in the instance, it will be loaded from the data store by calling
1710     * {@link #realizeField(FieldDesc)}. Depending on its lifecycle state,
1711     * the instance is registered in the transaction cache and the field
1712     * is marked as read.
1713     *
1714     * @param fieldDesc Field descriptor for the field to be loaded.
1715     */

1716    private void prepareGetField(FieldDesc fieldDesc, boolean internal, boolean acquireShareLock) {
1717        boolean debug = logger.isLoggable(Logger.FINEST);
1718
1719        if (debug) {
1720            logger.finest("sqlstore.sqlstatemanager.preparegetfield", fieldDesc.getName()); // NOI18N
1721
}
1722
1723        if (acquireShareLock) {
1724            persistenceManager.acquireShareLock();
1725        }
1726
1727        try {
1728            getLock();
1729
1730            boolean xactActive = persistenceManager.isActiveTransaction();
1731            boolean optimistic = persistenceManager.isOptimisticTransaction();
1732            boolean nontransactionalRead = persistenceManager.isNontransactionalRead();
1733
1734            if (state.needsReload(optimistic, nontransactionalRead, xactActive)) {
1735                reload(fieldDesc);
1736            }
1737
1738            LifeCycleState oldstate = state;
1739            state = state.transitionReadField(optimistic, nontransactionalRead, xactActive);
1740
1741            registerInstance(false, null, oldstate);
1742
1743            // Only allow dynamic navigation if we are not in the state that allows it.
1744
if (state.isNavigable() || !internal) {
1745                if (getPresenceMaskBit(fieldDesc.absoluteID) == false) {
1746                    realizeField(fieldDesc);
1747                }
1748            }
1749        } catch (JDOException e) {
1750            throw e;
1751        } catch (Exception JavaDoc e) {
1752            logger.log(Logger.FINE,"sqlstore.exception.log", e);
1753            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
1754                    "core.statemanager.getfieldfailed"), e); // NOI18N
1755
} finally {
1756            if (acquireShareLock) {
1757                persistenceManager.releaseShareLock();
1758            }
1759            releaseLock();
1760
1761            if (debug) {
1762                logger.finest("sqlstore.sqlstatemanager.preparegetfield.exit"); // NOI18N
1763
}
1764        }
1765    }
1766
1767    /**
1768     * Retrieves a field specified by <code>fieldDesc</code> from the data
1769     * store. If the field is part of a group then all unfetched fields
1770     * in the group are retrieved. realizeField is part of dynamic
1771     * navigation. The field is marked as present.
1772     *
1773     * @param fieldDesc The field descriptor of the field to be retrieved.<p>
1774     * Note: The <code>fieldDesc</code> parameter must not be null.
1775     */

1776    private void realizeField(FieldDesc fieldDesc) {
1777        assert fieldDesc != null;
1778
1779        boolean debug = logger.isLoggable();
1780
1781        if (debug) {
1782            logger.fine("sqlstore.sqlstatemanager.realizefield", fieldDesc.getName()); // NOI18N
1783
}
1784
1785        if (!persistenceConfig.isNavigable()) {
1786            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
1787                    "core.statemanager.notnavigable", // NOI18N
1788
fieldDesc.getName(), persistentObject.getClass().getName()));
1789        }
1790
1791        boolean fieldRealized = false;
1792
1793        if (fieldDesc instanceof ForeignFieldDesc) {
1794            ForeignFieldDesc ff = (ForeignFieldDesc) fieldDesc;
1795
1796            // We can do an enhancement if we are only getting a single
1797
// relationship field. Check if the field can be retrieved on
1798
// it's own and the relationship is not mapped to a join table.
1799
// The field can be retrieved on it's own if it's is in
1800
// an independent fetch group or not in a fetch group at all.
1801
// Independent fetch groups have group ids < FieldDesc.GROUP_NONE.
1802
if (ff.fetchGroup <= FieldDesc.GROUP_NONE
1803                    && persistenceConfig.getFetchGroup(ff.fetchGroup).size() <= 1
1804                 && !ff.useJoinTable()) {
1805
1806                fieldRealized = realizeForeignField(ff);
1807            }
1808        }
1809
1810        if (!fieldRealized) {
1811            retrieve(fieldDesc);
1812        }
1813
1814        if (debug) {
1815            logger.fine("sqlstore.sqlstatemanager.realizefield.exit"); // NOI18N
1816
}
1817    }
1818
1819    /**
1820     * For foreign fields we want to take advantage of knowing the
1821     * relationship key and only selecting the foreign rather than the
1822     * primary with the foreign attached. Most of the work is in
1823     * figuring out whether we can do that.
1824     *
1825     * @param foreignField The relationship field to be retrieved.
1826     * Following is true for this field.
1827     * <ul>
1828     * <li>It is part of an independent fetch group with only one
1829     * field in the fetch group or not part of any fetch group.</li>
1830     * <li>It is not mapped to a join table. </li>
1831     * </ul>
1832     * Note: The <code>foreignField</code> parameter must not be null.
1833     *
1834     * @return True, if relationship field has been retrieved, false otherwise.
1835     */

1836    private boolean realizeForeignField(ForeignFieldDesc foreignField) {
1837        assert foreignField != null;
1838
1839        boolean isPresent = false;
1840        boolean debug = logger.isLoggable();
1841
1842        if (debug) {
1843            logger.fine("sqlstore.sqlstatemanager.realizeforeignfield", // NOI18N
1844
foreignField.getName());
1845        }
1846
1847        // Check and see if all the values we need are present.
1848
for (int i = 0; i < foreignField.localFields.size(); i++) {
1849            LocalFieldDesc lf = (LocalFieldDesc) foreignField.localFields.get(i);
1850            isPresent = getPresenceMaskBit(lf.absoluteID);
1851
1852            if (!isPresent) {
1853                break;
1854            }
1855        }
1856
1857        if (isPresent) {
1858            // All the values we need are present. Wow. Now we'll have to
1859
// format a more specialized request and attach the object(s)
1860
// we get back to our managed object.
1861
populateForeignField(foreignField);
1862        }
1863
1864        if (debug) {
1865            logger.fine("sqlstore.sqlstatemanager.realizeforeignfield.exit", // NOI18N
1866
Boolean.valueOf(isPresent));
1867        }
1868
1869        return isPresent;
1870    }
1871
1872    /**
1873     * Retrieves the relationship.
1874     *
1875     * @param foreignField The relationship field to be retrieved.
1876     * Following is true for this field.
1877     * <ul>
1878     * <li>It is part of an independent fetch group with only one
1879     * field in the fetch group or not part of any fetch group.</li>
1880     * <li>It is not mapped to a join table. </li>
1881     * </ul>
1882     * Note: The <code>foreignField</code> must not be null.
1883     */

1884    private void populateForeignField(ForeignFieldDesc foreignField) {
1885        assert foreignField != null;
1886
1887        boolean foundInstance = false;
1888
1889        if (foreignField.hasForeignKey() && foreignField.isMappedToPk()) {
1890            // Cache lookup, returns an object.
1891
Object JavaDoc pc = getObjectById(foreignField, null, null, true);
1892
1893            if (foundInstance = (pc != null)) {
1894                foreignField.setValue(this, pc);
1895            }
1896        }
1897
1898        if (!foundInstance) {
1899            // Query lookup, returns a collection.
1900
Collection result = retrieveForeign(foreignField);
1901            attachQueryResult(foreignField, result);
1902        }
1903
1904        // Or in PRESENT bit for this field in the properties mask
1905
setPresenceMaskBit(foreignField.absoluteID);
1906    }
1907
1908    /**
1909     * Attaches the retrieved object(s) <code>queryResult</code> to the
1910     * relationship field <code>foreignField</code> of the instance
1911     * managed by this state manager.
1912     *
1913     * @param queryResult Retrieved value for the relationship field
1914     * <code>foreignField</code>.
1915     */

1916    private void attachQueryResult(ForeignFieldDesc foreignField,
1917                                   Collection queryResult) {
1918        assert foreignField != null;
1919
1920        // Attach the object(s) we got back to our managed object.
1921
// There are three cases:
1922
// 1) The foreign field contains a collection
1923
// 2) The foreign object doesn't exist (NIL)
1924
// 3) The foreign field is a reference to a single object
1925
if (foreignField.getComponentType() != null) {
1926            // Instantiate and populate a dynamic array, namely Collection.
1927
// NOTE: queryResult is null, if we didn't execute the retrieval!
1928
replaceCollection(foreignField, queryResult);
1929        } else if (queryResult == null || queryResult.size() == 0) {
1930            foreignField.setValue(this, null);
1931        } else {
1932            if (queryResult.size() > 1) {
1933                throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
1934                        "core.persistencestore.toomanyobjforcard1", // NOI18N
1935
persistenceConfig.getName(),
1936                        foreignField.getName(), "" + queryResult.size())); // NOI18N
1937
}
1938            Object JavaDoc v = queryResult.iterator().next();
1939            foreignField.setValue(this, v);
1940        }
1941    }
1942
1943    /**
1944     * Retrieves the relationship based on the values of the
1945     * relationship columns on the local side. Note: This method
1946     * assumes that the relationship key fields are loaded.
1947     *
1948     * RESOLVE:
1949     * The relationship might be mapped to an Unique Key.
1950     * Do databases constrain the Unique Key columns to be non null?
1951     *
1952     * @param foreignField The relationship field to be retrieved.
1953     * Following is true for this field.
1954     * <ul>
1955     * <li>It is part of an independent fetch group with only one
1956     * field in the fetch group or not part of any fetch group.</li>
1957     * <li>It is not mapped to a join table. </li>
1958     * </ul>
1959     * Note: The <code>foreignField</code> must not be null.
1960     *
1961     * @return Collection returned by the query. Null, if the relationship
1962     * is not set or the passed relationship field is null.
1963     * @see #retrieve
1964     */

1965    private Collection retrieveForeign(ForeignFieldDesc foreignField) {
1966        assert foreignField != null;
1967
1968        Collection result = null;
1969        boolean debug = logger.isLoggable();
1970
1971        if (debug) {
1972            logger.fine("sqlstore.sqlstatemanager.retrieveforeign", // NOI18N
1973
foreignField.getName());
1974        }
1975
1976        Object JavaDoc[] values = new Object JavaDoc[foreignField.localFields.size()];
1977        boolean isValidForeignKey = true;
1978
1979        for (int i = 0; i < foreignField.localFields.size(); i++) {
1980            FieldDesc flf = (FieldDesc) foreignField.localFields.get(i);
1981
1982            if (!getPresenceMaskBit(i)) {
1983                // throw exception
1984
}
1985
1986            if (getSetMaskBit(flf.absoluteID)) {
1987                // This is one reason why we must have a before image
1988
// on relationship changes!
1989
values[i] = flf.getValue(beforeImage);
1990            } else {
1991                values[i] = flf.getValue(this);
1992            }
1993
1994            // Make sure we have a valid query key.
1995
if (values[i] == null) {
1996                // The relationship must be null. No need to query!
1997
isValidForeignKey = false;
1998            }
1999        }
2000
2001        if (isValidForeignKey) {
2002            // Getting a new generated RD or a cached one.
2003
RetrieveDesc fdesc =
2004                    persistenceConfig.getRetrieveDescForFKQuery(foreignField, store);
2005            result = (Collection) store.retrieve(
2006                    persistenceManager, fdesc, new QueryValueFetcher(values));
2007        }
2008
2009        if (debug) {
2010            logger.fine("sqlstore.sqlstatemanager.retrieveforeign.exit"); // NOI18N
2011
}
2012
2013        return result;
2014    }
2015
2016    /**
2017     * The retrieve method gets a retrieve descriptor to retrieve the
2018     * desired field and adds constraints necessary to limit the
2019     * retrieval set to the source object runs the retrieve
2020     * descriptor against the store, the source object is connected to,
2021     * and then merges the results back into the source object.
2022     *
2023     * @param additionalField The additional field to be retrieved.<p>
2024     * Note: The <code>additionalField</code> might be null if we just
2025     * want to reload the instance.
2026     * @see #retrieveForeign
2027     */

2028    private void retrieve(FieldDesc additionalField) {
2029        boolean debug = logger.isLoggable();
2030
2031        if (debug) {
2032            String JavaDoc fieldName = (additionalField != null) ? additionalField.getName() : null;
2033            logger.fine("sqlstore.sqlstatemanager.retrieve", fieldName); // NOI18N
2034
}
2035
2036        LocalFieldDesc[] keyFields = persistenceConfig.getKeyFieldDescs();
2037        Object JavaDoc [] values = new Object JavaDoc[keyFields.length];
2038        copyValues(values, keyFields, 0);
2039
2040        // Getting a new generated RD or a cached one.
2041
RetrieveDesc rd = persistenceConfig.getRetrieveDescForPKQuery(additionalField, store);
2042        Collection result = (Collection) store.
2043                retrieve(persistenceManager, rd, new QueryValueFetcher(values));
2044
2045        if (result.size() > 1) {
2046            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
2047                    "core.statemanager.toomanyrows", // NOI18N
2048
persistenceConfig.getPersistenceCapableClass().getName()));
2049        } else if (result.size() < 1 || result.iterator().next() != persistentObject) {
2050
2051            // If there are no instances fetched, or the fetched instances is not the one
2052
// we asked for, it means that it is not found and we throw an exception
2053
throw new JDOObjectNotFoundException(I18NHelper.getMessage(messages,
2054                    "core.statemanager.objectnotfound"), // NOI18N
2055
new Object JavaDoc[]{persistentObject});
2056        }
2057
2058        if (debug) {
2059            logger.fine("sqlstore.sqlstatemanager.retrieve.exit"); // NOI18N
2060
}
2061    }
2062
2063    /**
2064     * Copies the value of <code>fields</code> into the array
2065     * <code>values</code>. The values are copied into the
2066     * array starting at index <code>startIndex</code>.
2067     *
2068     * @param values Array taking the values of <code>fields</code>.
2069     * @param fields Array of LocalFieldDesc.
2070     * @param startIndex Starting index into <code>values</code>.
2071     */

2072    private void copyValues(Object JavaDoc[] values, LocalFieldDesc[] fields, int startIndex) {
2073
2074        // The values array should be long enough to hold all the fields.
2075
assert values.length - startIndex >= fields.length;
2076
2077        for (int i = 0; i < fields.length; i++) {
2078            LocalFieldDesc field = fields[i];
2079            values[i + startIndex] = field.getValue(this);
2080
2081            // The field is not expected to be null.
2082
assert values[i + startIndex] != null;
2083        }
2084    }
2085
2086    public SQLStateManager getBeforeImage() {
2087
2088        // Do not try to get before image if not required
2089
if (beforeImage == null && isBeforeImageRequired()) {
2090            boolean debug = logger.isLoggable();
2091
2092            if (debug) {
2093                logger.fine("sqlstore.sqlstatemanager.getbeforeimage", // NOI18N
2094
persistenceConfig.getPersistenceCapableClass().getName());
2095            }
2096
2097            try {
2098                getLock();
2099
2100                // Make a copy of the persistentObject.
2101
beforeImage = copyPersistent();
2102            } finally {
2103                releaseLock();
2104            }
2105
2106            if (debug) {
2107                logger.fine("sqlstore.sqlstatemanager.getbeforeimage.exit"); // NOI18N
2108
}
2109        }
2110
2111        return beforeImage;
2112    }
2113
2114    public boolean isBeforeImageRequired() {
2115
2116        com.sun.jdo.api.persistence.support.Transaction t = persistenceManager.currentTransaction();
2117        // NOTE: We need to create a before image on relationship changes for two reasons:
2118
// (1) Relationship management assumes relationship fields to be loaded.
2119
// See prepareUpdateField.
2120
// (2) The before image is used in realizeField.
2121
boolean isBeforeImageRequired =
2122                persistenceManager.isOptimisticTransaction() ||
2123                getUpdateDesc().hasChangedRelationships() ||
2124                t.getRetainValues() || t.getRestoreValues() ||
2125                persistenceConfig.hasModifiedCheckAtCommitConsistency();
2126       if (logger.isLoggable(Logger.FINER)) {
2127            logger.finer("sqlstore.sqlstatemanager.isbeforeimagerequired", // NOI18N
2128
Boolean.valueOf(isBeforeImageRequired));
2129       }
2130       return isBeforeImageRequired;
2131    }
2132
2133    private SQLStateManager copyPersistent() {
2134        PersistenceManager pm = (PersistenceManager) getPersistenceManagerInternal();
2135        SQLStateManager newStateManager = (SQLStateManager) clone();
2136
2137        // Associate state manager with a new pc instance.
2138
pm.newInstance(newStateManager);
2139
2140        newStateManager.copyFields(this, true, true);
2141
2142        return newStateManager;
2143    }
2144
2145    /**
2146     * @inheritDoc StateManager#copyFields(StateManager source)
2147     * Does not copy relationship fields. Other fields are cloned while
2148     * copying as per {@link #cloneObjectMaybe(Object source)}.
2149     * @throws IllegalArgumentException if source is null, is
2150     * not <code>instanceof</code> SQLStateManager, or is not managing the
2151     * same type of persistent instance as this StateManager.
2152     */

2153    public void copyFields(StateManager source) {
2154        if (!(source instanceof SQLStateManager)) {
2155            String JavaDoc className =
2156                (source != null) ? source.getClass().getName() : null;
2157            throw new IllegalArgumentException JavaDoc(className);
2158        }
2159
2160        SQLStateManager sqlSource = (SQLStateManager) source;
2161
2162        if (persistenceConfig != sqlSource.getPersistenceConfig()) {
2163            Class JavaDoc thisPCClass =
2164                persistenceConfig.getPersistenceCapableClass();
2165            Class JavaDoc sourcePCClass =
2166                sqlSource.getPersistenceConfig().getPersistenceCapableClass();
2167            throw new IllegalArgumentException JavaDoc(
2168                I18NHelper.getMessage(
2169                    messages,
2170                    "core.statemanager.copyFields.mismatch", // NOI18N
2171
thisPCClass.getName(),
2172                    sourcePCClass.getName()));
2173        }
2174
2175        copyFields(sqlSource, false, true);
2176    }
2177
2178    /**
2179     * @inheritDoc StateManager#copyFields(StateManager source).
2180     * @param copyRelationships if true, then relationship fields are copied,
2181     * otherwise they are not copied.
2182     * @param clone if true, then the fields are cloned while copying,
2183     * otherwise both the <code>source</code> and this StateManager reference
2184     * the same field value.
2185     */

2186    private void copyFields(SQLStateManager source,
2187                            boolean copyRelationships,
2188                            boolean clone) {
2189        ArrayList fields = null;
2190
2191        // Reset the field masks, as the instance will
2192
// be populated with the state from the source.
2193
clearMask(PRESENCE_MASK);
2194        clearMask(SET_MASK);
2195
2196        for (int i = 0; i < 2; i++) {
2197            if (i == 0) {
2198                fields = persistenceConfig.fields;
2199            } else {
2200                fields = persistenceConfig.hiddenFields;
2201            }
2202
2203            for (int j = 0; (fields != null) && (j < fields.size()); j++) {
2204                FieldDesc f = (FieldDesc) fields.get(j);
2205
2206                if (!copyRelationships && f.isRelationshipField()) {
2207                    continue;
2208                }
2209
2210                if (source.getPresenceMaskBit(f.absoluteID)) {
2211                    Object JavaDoc value = f.getValue(source);
2212                    f.setValue(this, (clone) ? cloneObjectMaybe(value) : value);
2213                    setPresenceMaskBit(f.absoluteID);
2214                }
2215            }
2216        }
2217    }
2218
2219    private Object JavaDoc cloneObjectMaybe(Object JavaDoc source) {
2220        // RESOLVE: need to clone SCOCollection
2221

2222        if (source != null) {
2223            // RESOLVE: Should we clone byte[]???
2224
if ((source instanceof SCO)) {
2225                return ((SCO)source).cloneInternal();
2226
2227            } else if (!(source instanceof Number JavaDoc) &&
2228                    !(source instanceof String JavaDoc) &&
2229                    !(source instanceof Character JavaDoc) &&
2230                    !(source instanceof Boolean JavaDoc) &&
2231                    // RESOLVE: #javax.ejb package# !(source instanceof javax.ejb.EJBObject) &&
2232
!(source instanceof com.sun.jdo.api.persistence.support.PersistenceCapable) &&
2233                    !(source instanceof byte[])) {
2234                try {
2235                    Class JavaDoc type = source.getClass();
2236
2237                    if (!type.isArray()) {
2238                        Method JavaDoc m = type.getMethod("clone", (Class JavaDoc []) null); // NOI18N
2239

2240                        if (m != null) {
2241                            return m.invoke(source, (Object JavaDoc []) null);
2242                        }
2243                    } else {
2244                        Object JavaDoc srcArray[] = (Object JavaDoc[]) source;
2245                        Object JavaDoc dstArray[] = (Object JavaDoc[])
2246                                java.lang.reflect.Array.newInstance(type.getComponentType(), srcArray.length);
2247
2248                        for (int i = 0; i < srcArray.length; i++) {
2249                            dstArray[i] = srcArray[i];
2250                        }
2251                        return dstArray;
2252                    }
2253                } catch (NoSuchMethodException JavaDoc e) {
2254                    if (logger.isLoggable()) {
2255                        Object JavaDoc[] items = new Object JavaDoc[] {e, source.getClass().getName()};
2256                        logger.fine("sqlstore.sqlstatemanager.nosuchmethodexcep.clone", items); // NOI18N
2257
}
2258                } catch (InvocationTargetException JavaDoc e) {
2259                } catch (IllegalAccessException JavaDoc e) {
2260                }
2261            }
2262        }
2263
2264        return source;
2265    }
2266
2267    /**
2268     * Prepares the field described by <code>fieldDesc</code> for update.
2269     * This method is central to record changes to fields. The state
2270     * transitions to dirty. The instance is registered in the
2271     * transaction cache. If the field is set for the first time,
2272     * the before image is prepared and the field is marked as modified.
2273     * Updated local fields are added to the column list to be updated.
2274     *
2275     * @param fieldDesc Updated field.
2276     * @param newlyRegisteredSMs
2277     * State managers of autopersistent objects will be added to this list.
2278     * @see #prepareUpdateFieldSpecial
2279     */

2280    private void prepareUpdateField(FieldDesc fieldDesc, ArrayList newlyRegisteredSMs) {
2281
2282        // No updates for key fields. As this method is called by
2283
// loadForUpdate for _all_ DFG fields (to prepare a hollow
2284
// instance for update), we can't throw an exception here
2285
// like in prepareUpdateFieldSpecial.
2286
if (fieldDesc.isKeyField()) {
2287            return;
2288        }
2289
2290        getUpdateDesc().markRelationshipChange(fieldDesc);
2291
2292        boolean debug = logger.isLoggable();
2293
2294        if (debug) {
2295             Object JavaDoc[] items = new Object JavaDoc[] {fieldDesc.getName(),state};
2296             logger.fine("sqlstore.sqlstatemanager.prepareupdatefield", items); // NOI18N
2297
}
2298
2299        boolean optimistic = persistenceManager.isOptimisticTransaction();
2300        boolean xactActive = persistenceManager.isActiveTransaction();
2301        boolean nontransactionalRead = persistenceManager.isNontransactionalRead();
2302
2303        if (state.needsReload(optimistic, nontransactionalRead, xactActive)) {
2304            reload(fieldDesc);
2305        }
2306
2307        // State transition.
2308
// The state transition prevends field updates on deleted instances
2309
// and must always be executed. See PersistentDeleted.transitionWriteField().
2310
LifeCycleState oldstate = state;
2311        state = state.transitionWriteField(xactActive);
2312        registerInstance(false, newlyRegisteredSMs, oldstate);
2313
2314        if (state == oldstate && getSetMaskBit(fieldDesc.absoluteID) &&
2315                getPresenceMaskBit(fieldDesc.absoluteID)) {
2316            // The presence mask bit is NEVER set for deferred collections,
2317
// because deferred collections MUST always be reloaded.
2318
return;
2319        }
2320
2321        // We don't reload the field for a newly created instance.
2322
if (state.isBeforeImageUpdatable()) {
2323
2324            // Reload the field, as relationship management
2325
// assumes that relationship fields are always present.
2326
// See updateObjectField() or processCollectionupdates().
2327
if (!getPresenceMaskBit(fieldDesc.absoluteID)) {
2328                prepareGetField(fieldDesc);
2329            }
2330
2331            updateBeforeImage(fieldDesc, null);
2332        }
2333
2334        recordUpdatedField(fieldDesc);
2335
2336        if (debug) {
2337            logger.fine("sqlstore.sqlstatemanager.prepareupdatefield.exit"); // NOI18N
2338
}
2339    }
2340
2341    /**
2342     * Initializes the <code>beforeImage</code> and registers the before image
2343     * value if not null.
2344     *
2345     * @param fieldDesc Updated field.
2346     * @param value BeforeImage value, null if called from
2347     * {@link #prepareUpdateField}. If the value is null it will be retrieved
2348     * from the instance.
2349     * @see #prepareUpdateFieldSpecial
2350     */

2351    private void updateBeforeImage(FieldDesc fieldDesc, Object JavaDoc value) {
2352
2353        getBeforeImage();
2354
2355        if (beforeImage != null
2356                && !beforeImage.getPresenceMaskBit(fieldDesc.absoluteID)
2357                && (fieldDesc.sqlProperties & FieldDesc.PROP_LOG_ON_UPDATE) > 0) {
2358
2359            if (value == null) {
2360                value = fieldDesc.getValue(this);
2361            }
2362
2363            if (value != null) {
2364                if (logger.isLoggable(Logger.FINEST)) {
2365                    Object JavaDoc[] items = new Object JavaDoc[] {fieldDesc, value};
2366                    logger.finest("sqlstore.sqlstatemanager.updatebeforeimage", items); // NOI18N
2367
}
2368
2369                fieldDesc.setValue(beforeImage, cloneObjectMaybe(value));
2370                beforeImage.setPresenceMaskBit(fieldDesc.absoluteID);
2371            }
2372        }
2373    }
2374
2375    /**
2376     * Marks the field <code>fieldDesc</code> as set and schedules local fields
2377     * to be written to the data store.
2378     *
2379     * @param fieldDesc Updated field.
2380     */

2381    private void recordUpdatedField(FieldDesc fieldDesc) {
2382        boolean debug = logger.isLoggable(Logger.FINEST);
2383
2384        if (!fieldDesc.isRelationshipField() &&
2385                (fieldDesc.sqlProperties & FieldDesc.PROP_RECORD_ON_UPDATE) > 0) {
2386            if (debug) {
2387                logger.finest("sqlstore.sqlstatemanager.recordingfield", fieldDesc); // NOI18N
2388
}
2389            getUpdateDesc().recordUpdatedField((LocalFieldDesc) fieldDesc);
2390        }
2391
2392        if (debug) {
2393            logger.finest("sqlstore.sqlstatemanager.makedirtyfield", fieldDesc); // NOI18N
2394
}
2395        setSetMaskBit(fieldDesc.absoluteID);
2396    }
2397
2398    /**
2399     * This method adds the dependency between this StateManager
2400     * and the other.
2401     *
2402     * @param sm Second state manager.
2403     * @see StateManager#addDependency(StateManager sm)
2404     */

2405    public void addDependency(StateManager sm) {
2406
2407        if (logger.isLoggable()) {
2408            Object JavaDoc[] items = new Object JavaDoc[] {this, sm};
2409            logger.fine("sqlstore.sqlstatemanager.adddependency", items); // NOI18N
2410
}
2411
2412        // The simple solution is to call addUpdatedForeignReference()
2413
// internally. It might try to reregister both instances again,
2414
// but the caller should clean up the cache. e.g. the
2415
// PersistenceManager MUST replace the deleted StateManager
2416
// with the new instance in the weak cache AFTER this call.
2417

2418        SQLStateManager other = (SQLStateManager)sm;
2419        if (!state.isNew() || !state.isDeleted()) {
2420            // Do not need to add a dependency to a new-deleted instance as
2421
// its flush is a no-op any way.
2422

2423            // First parameter == null marks a non removable dependency.
2424
this.addUpdatedForeignReference(null, other);
2425        } else if ((other.stateFlags & ST_REGISTERED) == 0) {
2426
2427            // If we did not add a dependency, we still need to register the other
2428
// instance if it is not yet registered.
2429
persistenceManager.registerInstance(other, other.getObjectId(), false, true);
2430            other.stateFlags |= ST_REGISTERED;
2431        }
2432    }
2433
2434    /**
2435     * Resolves the dependencies for the instances waiting for this state manager.
2436     * Dependencies are registered instantly during the course of the transaction.
2437     * For this reason, the introduced dependencies must be checked, if they are
2438     * still valid at commit/flush. E.g. remove dependencies introduced on
2439     * relationship removal are only valid, if the removed instance is deleted.
2440     * <p>
2441     * This method checks the dependencies for all instances waiting for the
2442     * current state manager to be flushed to the store.
2443     */

2444    public void resolveDependencies() {
2445        if (logger.isLoggable()) {
2446            logger.fine("sqlstore.sqlstatemanager.resolvedependencies", this.getPersistent()); // NOI18N
2447
}
2448
2449        if (updatedForeignReferences != null) {
2450            Iterator iter = updatedForeignReferences.iterator();
2451
2452            while (iter.hasNext()) {
2453                final UpdatedForeignReference ufr = (UpdatedForeignReference) iter.next();
2454                final ForeignFieldDesc fieldDesc = ufr.getFieldDesc();
2455                final SQLStateManager foreignSM = ufr.getStateManager();
2456
2457                if (resolveDependency(fieldDesc, foreignSM)) {
2458                    foreignSM.removeDependency();
2459                    iter.remove();
2460                }
2461            }
2462        }
2463    }
2464
2465    /**
2466     * Tries to resolve the dependency between <code>this</code>
2467     * and <code>foreignSM</code> introduced on the update of
2468     * relationship field <code>fieldDesc</code>. There are three
2469     * kinds of dependencies:
2470     * <ul>
2471     * <li>Create dependency, see {@link #registerRemoveDependency}</li>
2472     * <li>Remove dependency, see {@link #registerCreateDependency}</li>
2473     * <li>Update dependency, see {@link #manageDependencyForObjectField}</li>
2474     * </ul>
2475     * The current implementation does not attempt to resolve
2476     * <em>Update dependencies</em>.
2477     *
2478     * @param fieldDesc Relationship field.
2479     * @param foreignSM Foreign state manager.
2480     * @return True, if the dependency between <code>this</code>
2481     * and <code>foreignSM</code> can be safely removed.
2482     */

2483    private boolean resolveDependency(ForeignFieldDesc fieldDesc,
2484                                      SQLStateManager foreignSM) {
2485        boolean removeDependency = false;
2486        Object JavaDoc pc = foreignSM.getPersistent();
2487
2488        if (!state.isPersistentInDataStore()) {
2489            // Check for a create dependency.
2490
// The create dependency is valid, if the instance is being created.
2491
if (state.getUpdateAction() != ActionDesc.LOG_CREATE) {
2492                // The instance is not persistent and will not be created.
2493
removeDependency = true;
2494            } else if (!checkRelationship(this, fieldDesc, pc)) {
2495                // No relationship between the two instances.
2496
removeDependency = true;
2497            }
2498        } else {
2499            // Not removable dependencies are marked by fieldDesc == null.
2500
// See delete/create with the same id dependency in addDependency
2501
// or update dependencies in manageDependencyForObjectField.
2502
// RESOLVE: Are update dependencies removable?
2503
if (fieldDesc != null) {
2504                // Check for a remove dependency.
2505
// The remove dependency is valid, if the formerly referred
2506
// instance is being removed.
2507
if (foreignSM.state.getUpdateAction() != ActionDesc.LOG_DESTROY) {
2508                    // The formerly referred instance will not be removed.
2509
removeDependency = true;
2510                } else if (fieldDesc.cardinalityUPB <= 1) {
2511                    // Don't check collection relationships, as collection
2512
// fields might not be populated in the before image.
2513
// RESOLVE: Collection relationships shouldn't be
2514
// checked for remove dependencies anyway.
2515
if (!checkRelationship(beforeImage, fieldDesc, pc)) {
2516                        // No previous relationship between the two instances.
2517
removeDependency = true;
2518                    }
2519                }
2520            }
2521        }
2522
2523        if (removeDependency && logger.isLoggable()) {
2524            Object JavaDoc[] items = new Object JavaDoc[] {this.getPersistent(), fieldDesc.getName(), pc};
2525            logger.fine("sqlstore.sqlstatemanager.resolvedependency", items); // NOI18N
2526
}
2527        return removeDependency;
2528    }
2529
2530    /**
2531     * Decrements the reference count and marks this instance
2532     * as updateable, if the reference count is zero.
2533     */

2534    private void removeDependency() {
2535        if (--referenceCount == 0) {
2536            stateFlags &= ~ST_UPDATE_DISABLED;
2537        }
2538    }
2539
2540    /**
2541     * Checks, if there is a relationship between the persistence
2542     * capable instance managed by state manager <code>sm</code> and
2543     * <code>pc</code> on field <code>fieldDesc</code>. If
2544     * <code>sm</code> represents a before image, the relationship was
2545     * reset in the current transaction.
2546     *
2547     * @param sm Either the before- or after image of the current state manager.
2548     * @param fieldDesc Relationship field.
2549     * @param pc Persistence capable instance checked for relationship.
2550     * @return True, if the persistence capable instances are (were)
2551     * related on the given field.
2552     */

2553    static private boolean checkRelationship(SQLStateManager sm,
2554                                             ForeignFieldDesc fieldDesc,
2555                                             Object JavaDoc pc) {
2556        boolean related = false;
2557
2558        if (fieldDesc != null && sm != null
2559                && sm.getPresenceMaskBit(fieldDesc.absoluteID)) {
2560
2561            if (fieldDesc.cardinalityUPB > 1) {
2562                // Checking directly for contains doesn't work for deferred SCOCollections.
2563
Collection c = sm.getCollectionValue(fieldDesc);
2564
2565                // Resulting collection can't be null because the presence mask is set.
2566
related = c.contains(pc);
2567            } else {
2568                related = fieldDesc.getValue(sm) == pc;
2569            }
2570        }
2571        return related;
2572    }
2573
2574    /**
2575     * Nullify the relationship in the data store before the _possible_ removal
2576     * of removedSM. To maintain referentional integrity constraints in the
2577     * database, the relationship to the removed instance has to be nullified,
2578     * before the removed object might be deleted. Since we use immediate
2579     * dependency management, we don't know, if the removed object is deleted
2580     * later on. The data store relationship has to be erased before the
2581     * removal. Immediate dependency management determines dependencies
2582     * immediatly when the relationship is set. In contrast the deferred
2583     * approach waits until the objects are flushed to the store. Set the
2584     * dependency only if both instances are already persistent.
2585     *
2586     * @param fieldDesc Updated relationship field.
2587     * @param removedSM State manager removed from the relationship.
2588     * @see #nullifyForeignKey(ForeignFieldDesc, SQLStateManager, ForeignFieldDesc, boolean)
2589     * @see #removeJoinTableEntry(ForeignFieldDesc, SQLStateManager, ForeignFieldDesc)
2590     */

2591    private void registerRemoveDependency(ForeignFieldDesc fieldDesc, SQLStateManager removedSM) {
2592        if (this.state.isPersistentInDataStore() &&
2593                removedSM.state.isPersistentInDataStore()) {
2594
2595            this.addUpdatedForeignReference(fieldDesc, removedSM);
2596        }
2597    }
2598
2599    /**
2600     * The referred object has to be written to the store before the
2601     * relationship can be set. To ensure referentional integrity
2602     * constraints in the database, the added object has to be written
2603     * to the store, before the relationship can be set. The same
2604     * dependency applies for relationships mapped to a jointable.
2605     *
2606     * @param inverseFieldDesc Updated inverse relationship field.
2607     * WE must pass the inverse field, because the dependency is
2608     * registered on the added state manager. See
2609     * {@link #registerRemoveDependency}.
2610     * @param addedSM State manager added to the relationship.
2611     * @see #setForeignKey(ForeignFieldDesc, SQLStateManager, ForeignFieldDesc)
2612     * @see #addJoinTableEntry(ForeignFieldDesc, SQLStateManager, ForeignFieldDesc)
2613     */

2614    private void registerCreateDependency(ForeignFieldDesc inverseFieldDesc, SQLStateManager addedSM) {
2615        if (!addedSM.state.isPersistentInDataStore()) {
2616            addedSM.addUpdatedForeignReference(inverseFieldDesc, this);
2617        }
2618    }
2619
2620    /**
2621     * Adds a dependency for state manager <code>sm</code>. State
2622     * manager <code>sm</code> must wait for <code>this</code> to be
2623     * flushed to the store before it can be written itself. State
2624     * manager <code>sm</code> is added to the list of objects
2625     * depending on <code>this</code> and will be notified when
2626     * <code>this</code> is written to the store. Store updates to
2627     * <code>sm</code> are disabled until then. This dependency is
2628     * established to maintain referential integrity conditions in the
2629     * data store.
2630     *
2631     * @param fieldDesc Updated relationship field.
2632     * @param sm Foreign state manager depending on <code>this</code>.
2633     */

2634    private void addUpdatedForeignReference(ForeignFieldDesc fieldDesc, SQLStateManager sm) {
2635
2636        // Avoid self-dependency.
2637
if (sm == this) {
2638            return;
2639        }
2640
2641        // IMPORTANT: The following check assumes that this StateManager needs to be
2642
// registered only if it has no updatedForeignReferences. Check if this causes
2643
// a problem after this instance has been flushed, and updatedForeignReferences
2644
// is now an empty collection.
2645
if (updatedForeignReferences == null) {
2646            updatedForeignReferences = new HashSet();
2647
2648            // Register this instance disregarding it's LifeCycle state.
2649
// Otherwise, its state may never be reset after the transaction commits.
2650
if ((stateFlags & ST_REGISTERED) == 0) {
2651                persistenceManager.registerInstance(this, getObjectId(), false, true);
2652                stateFlags |= ST_REGISTERED;
2653            }
2654        }
2655
2656        if (updatedForeignReferences.add(new UpdatedForeignReference(fieldDesc, sm))) {
2657            sm.stateFlags |= ST_UPDATE_DISABLED;
2658            sm.referenceCount++;
2659
2660            if (logger.isLoggable() ) {
2661                String JavaDoc fieldName = (fieldDesc != null) ? fieldDesc.getName() : null;
2662                Object JavaDoc[] items = new Object JavaDoc[] {this.persistentObject, fieldName,
2663                                               sm.persistentObject, new Integer JavaDoc(sm.referenceCount)};
2664                logger.fine("sqlstore.sqlstatemanager.addupdate", items); // NOI18N
2665
}
2666
2667            // Register this instance disregarding it's LifeCycle state.
2668
// Otherwise, its state may never be reset after the transaction commits.
2669
if ((sm.stateFlags & ST_REGISTERED) == 0) {
2670                persistenceManager.registerInstance(sm, sm.getObjectId(), false, true);
2671                sm.stateFlags |= ST_REGISTERED;
2672            }
2673        }
2674    }
2675
2676    /**
2677     * Removes the dependency from state manager <code>sm</code> on
2678     * <code>this</code>. State manager <code>sm</code> does not need to
2679     * wait for <code>this</code> to be flushed to the store. The dependency
2680     * was established to maintain referential integrity conditions in the
2681     * data store.
2682     *
2683     * @param fieldDesc Updated relationship field.
2684     * @param sm Foreign state manager removed from the dependency.
2685     */

2686    private void removeUpdatedForeignReference(ForeignFieldDesc fieldDesc, SQLStateManager sm) {
2687        if ((updatedForeignReferences == null) ||
2688                (updatedForeignReferences.size() == 0)) {
2689            return;
2690        }
2691
2692        if (updatedForeignReferences.remove(new UpdatedForeignReference(fieldDesc, sm))) {
2693            sm.referenceCount--;
2694
2695            if (logger.isLoggable()) {
2696                String JavaDoc fieldName = (fieldDesc != null) ? fieldDesc.getName() : null;
2697                Object JavaDoc[] items = new Object JavaDoc[] {this.persistentObject, fieldName,
2698                                               sm.persistentObject, new Integer JavaDoc(sm.referenceCount)};
2699                logger.fine("sqlstore.sqlstatemanager.removeupdate", items); // NOI18N
2700
}
2701
2702            if (sm.referenceCount == 0) {
2703                sm.stateFlags &= ~ST_UPDATE_DISABLED;
2704            }
2705        }
2706    }
2707
2708    /**
2709     * Handles relationship updates for the object side of a one-to-many or both
2710     * sides of a one-to-one relationship. This method processes i.e. the
2711     * Employee side of the Employee-Department, or both sides
2712     * Employee-Insurance relationship. Nullifies the relation on the instance
2713     * removed from the relationship. The relation is set on the added instance
2714     * <code>value</code>. Data store updates are scheduled. Updates the inverse
2715     * relationship side if <code>updateInverseRelationshipField</code> == true
2716     * and the relationship is mapped as bi-directional by the user.
2717     *
2718     * @param fieldDesc Updated relationship field.
2719     * @param addedObject Relationship object to be set.
2720     * @param updateInverseRelationshipField
2721     * True, if we need to update the inverse relationship side.
2722     * @param managedRelationshipInProgress True during relationship management.
2723     * @return Always true.
2724     * @exception JDOUserException
2725     * Thrown if the added object <code>addedObject</code> has already been deleted.
2726     */

2727    private boolean updateObjectField(ForeignFieldDesc fieldDesc,
2728                                      Object JavaDoc addedObject,
2729                                      boolean updateInverseRelationshipField,
2730                                      boolean managedRelationshipInProgress) {
2731
2732        boolean debug = logger.isLoggable();
2733
2734        if (debug) {
2735            Object JavaDoc[] items = new Object JavaDoc[] {fieldDesc.getName(),fieldDesc.getComponentType()};
2736            logger.fine("sqlstore.sqlstatemanager.updateobjfield", items); // NOI18N
2737
}
2738
2739        Object JavaDoc removedObject = fieldDesc.getValue(this);
2740
2741        // Don't do anything if the before and after value are the same.
2742
if (addedObject != removedObject) {
2743
2744            SQLStateManager addedSM = getAddedSM(addedObject, null);
2745            SQLStateManager removedSM = getRemovedSM(removedObject);
2746            SQLStateManager addedInverseFieldSM = null;
2747
2748            // If the new value is already deleted, we throw an exception.
2749
if (addedSM != null && addedSM.isDeleted()) {
2750                JDOUserException ex = new JDOUserException(I18NHelper.getMessage(messages,
2751                        "jdo.lifecycle.deleted.accessField")); // NOI18N
2752
ex.addFailedObject(addedObject);
2753                throw ex;
2754            }
2755
2756            ForeignFieldDesc inverseFieldDesc = fieldDesc.getInverseRelationshipField();
2757
2758            updateRelationshipInDataStore(fieldDesc, addedSM, removedSM,
2759                    inverseFieldDesc, managedRelationshipInProgress);
2760
2761            if (updateInverseRelationshipField && inverseFieldDesc != null) {
2762                addedInverseFieldSM = manageRelationshipForObjectField(inverseFieldDesc, addedSM, removedSM,
2763                        managedRelationshipInProgress);
2764            }
2765
2766            manageDependencyForObjectField(fieldDesc, addedSM, removedSM, addedInverseFieldSM);
2767        }
2768
2769        if (debug) {
2770            logger.fine("sqlstore.sqlstatemanager.updateobjfield.exit"); // NOI18N
2771
}
2772
2773        return true;
2774    }
2775
2776    /**
2777     * Updates the relationship in the data store. Updates the (hidden) local fields
2778     * corresponding to the foreign key columns if the relationship is mapped to a
2779     * foreign key. Jointable entries are scheduled for creation/removal if the
2780     * relationship is mapped to a jointable.
2781     *
2782     * @param fieldDesc Updated relationship field.
2783     * @param addedSM State manager of the added object.
2784     * @param removedSM State manager of the removed object.
2785     * @param inverseFieldDesc Inverse relationship field.
2786     * @param managedRelationshipInProgress
2787     * True during relationship management. We don't want to update the
2788     * relationship fields twice during relationship management.
2789     */

2790    private void updateRelationshipInDataStore(ForeignFieldDesc fieldDesc,
2791                                               SQLStateManager addedSM,
2792                                               SQLStateManager removedSM,
2793                                               ForeignFieldDesc inverseFieldDesc,
2794                                               boolean managedRelationshipInProgress) {
2795
2796        if (!fieldDesc.useJoinTable()) {
2797            processForeignKeys(fieldDesc, addedSM, removedSM, inverseFieldDesc,
2798                    managedRelationshipInProgress);
2799        } else {
2800            processJoinTableEntries(fieldDesc, addedSM, removedSM, inverseFieldDesc,
2801                    managedRelationshipInProgress);
2802        }
2803    }
2804
2805    /**
2806     * Updates the (hidden) local fields corresponding to the foreign key columns
2807     * if the relationship is mapped to a foreign key. The updates are written
2808     * to the store when the instance is flushed. For relationships mapped to
2809     * foreign keys, we always update the side with the foreign key.
2810     * The collection side never has the foreign key. Data store updates for
2811     * added objects are not processed twice during relationship management.
2812     *
2813     * Data store dependencies for the update operations are established.
2814     *
2815     * @param fieldDesc Updated relationship field.
2816     * @param addedSM State manager of the added object.
2817     * @param removedSM State manager of the removed object.
2818     * @param inverseFieldDesc Inverse relationship field.
2819     * @param managedRelationshipInProgress True during relationship management.
2820     */

2821    private void processForeignKeys(ForeignFieldDesc fieldDesc,
2822                                    SQLStateManager addedSM,
2823                                    SQLStateManager removedSM,
2824                                    ForeignFieldDesc inverseFieldDesc,
2825                                    boolean managedRelationshipInProgress) {
2826
2827        // If the fieldDesc property has the REF_INTEGRITY_UPDATES unset, it means we
2828
// need to update the other side of the relationship naming the foreign key fields.
2829
boolean updateOtherSide = (fieldDesc.sqlProperties & FieldDesc.PROP_REF_INTEGRITY_UPDATES) == 0;
2830
2831        if (updateOtherSide) {
2832            // Null out the foreign key on the removed object.
2833
if (removedSM != null) {
2834                removedSM.nullifyForeignKey(inverseFieldDesc, this, fieldDesc, false);
2835            }
2836            // Set the foreign key on the added object.
2837
// Don't set the fk twice during relationship management.
2838
if (addedSM != null && !managedRelationshipInProgress) {
2839                addedSM.setForeignKey(inverseFieldDesc, this, fieldDesc);
2840            }
2841        } else {
2842            // Null out the foreign key to the removed object.
2843
if (removedSM != null) {
2844                // Don't overwrite the foreign key, if both removedSM and
2845
// addedSM != null. See runtime test rel12 for an example.
2846
nullifyForeignKey(fieldDesc, removedSM, inverseFieldDesc, addedSM != null);
2847            }
2848            // Set the foreign key to the added object.
2849
// Don't set the fk twice during relationship management.
2850
if (addedSM != null && !managedRelationshipInProgress) {
2851                // See above!
2852
setForeignKey(fieldDesc, addedSM, inverseFieldDesc);
2853            }
2854        }
2855    }
2856
2857    /**
2858     * Schedules jointable entries for relationships mapped to jointables.
2859     * The actual creation/removal of the jointable entry is deferred until
2860     * flush. Data store updates for added objects are not processed twice
2861     * during relationship management.
2862     *
2863     * Data store dependencies for the update operations are established.
2864     *
2865     * @param fieldDesc Updated relationship field.
2866     * @param addedSM State manager of the added object.
2867     * @param removedSM State manager of the removed object.
2868     * @param inverseFieldDesc Inverse relationship field.
2869     * @param managedRelationshipInProgress True during relationship management.
2870     */

2871    private void processJoinTableEntries(ForeignFieldDesc fieldDesc,
2872                                         SQLStateManager addedSM,
2873                                         SQLStateManager removedSM,
2874                                         ForeignFieldDesc inverseFieldDesc,
2875                                         boolean managedRelationshipInProgress) {
2876
2877        // If the fieldDesc property has the REF_INTEGRITY_UPDATES unset,
2878
// it means we need to update the other side of the relationship.
2879
boolean updateOtherSide = (fieldDesc.sqlProperties & FieldDesc.PROP_REF_INTEGRITY_UPDATES) == 0;
2880
2881        if (updateOtherSide) {
2882            // Schedule the removal of the jointable entry to removedSM.
2883
if (removedSM != null) {
2884                removedSM.removeJoinTableEntry(inverseFieldDesc, this, fieldDesc);
2885            }
2886
2887            // Schedule the jointable entry to the added object.
2888
// Don't schedule the jointable entry twice during relationship management.
2889
if (addedSM != null && !managedRelationshipInProgress) {
2890                addedSM.addJoinTableEntry(inverseFieldDesc, this, fieldDesc);
2891            }
2892        } else {
2893            // Schedule the removal of the jointable entry to removedSM.
2894
if (removedSM != null) {
2895                // Contrary to foreign key relationships, we always have
2896
// to remove the previous jointable entry.
2897
removeJoinTableEntry(fieldDesc, removedSM, inverseFieldDesc);
2898            }
2899
2900            // Schedule the jointable entry to the added object.
2901
// Don't schedule the jointable entry twice during relationship management.
2902
if (addedSM != null && !managedRelationshipInProgress) {
2903                addJoinTableEntry(fieldDesc, addedSM, inverseFieldDesc);
2904            }
2905        }
2906    }
2907
2908    /**
2909     * Updates the (inverse) relationship field for <code>addedSM</code>
2910     * and <code>removedSM</code>.
2911     *
2912     * @param inverseFieldDesc Inverse relationship field.
2913     * @param addedSM State manager of the added object.
2914     * @param removedSM State manager of the removed object.
2915     * @param managedRelationshipInProgress True during relationship management.
2916     * @return State manager managing the previous value of addedSM's
2917     * relationship field.
2918     */

2919    private SQLStateManager manageRelationshipForObjectField(ForeignFieldDesc inverseFieldDesc,
2920                                                             SQLStateManager addedSM,
2921                                                             SQLStateManager removedSM,
2922                                                             boolean managedRelationshipInProgress) {
2923
2924        Object JavaDoc addedInverseFieldValue = null;
2925        SQLStateManager addedInverseFieldSM = null;
2926
2927        if (removedSM != null) {
2928            removedSM.removeRelationship(inverseFieldDesc, this);
2929        }
2930
2931        if (addedSM != null && !managedRelationshipInProgress) {
2932            addedInverseFieldValue = addedSM.addRelationship(inverseFieldDesc, this);
2933
2934            if (addedInverseFieldValue != null) {
2935                addedInverseFieldSM = (SQLStateManager)
2936                        persistenceManager.getStateManager(addedInverseFieldValue);
2937            }
2938        }
2939
2940        return addedInverseFieldSM;
2941    }
2942
2943    /**
2944     * Dependency management for database operations on the one-to-one
2945     * relationships. Establishes a dependency to nullify the foreign key on the
2946     * removed instance before the added instance's foreign key can be set. If
2947     * the relationship is mapped to a jointable, remove the jointable entry to
2948     * the removed instance before the jointable entry on the added can be
2949     * added. This update dependency is valid only for one-to-one relationships,
2950     * because only one-to-one relationships can be enforced by an unique
2951     * index on the foreign key. On the other hand, there's never a state
2952     * manager removed from the relationship for one-to-many relationships.
2953     *
2954     * We would like to do this in updateRelationshipInDataStore,
2955     * but we don't know the added instance's previous value in case of
2956     * updateOtherSide == false there. Here, we can handle this situation
2957     * at one place.
2958     *
2959     * @param fieldDesc Updated relationship field.
2960     * @param addedSM Added state manager.
2961     * @param removedSM Removed state manager.
2962     * @param addedInverseFieldSM
2963     * State manager removed from the relationship,
2964     * if the foreign key is on the local side.
2965     */

2966    private void manageDependencyForObjectField(ForeignFieldDesc fieldDesc,
2967                                                SQLStateManager addedSM,
2968                                                SQLStateManager removedSM,
2969                                                SQLStateManager addedInverseFieldSM) {
2970
2971        // If the fieldDesc property has the REF_INTEGRITY_UPDATES unset,
2972
// it means data store updates are scheduled on the other side of the relationship.
2973
boolean updateOtherSide = (fieldDesc.sqlProperties & FieldDesc.PROP_REF_INTEGRITY_UPDATES) == 0;
2974
2975        // Nullify the foreign key on the removed object before
2976
// the added object's foreign key can be set.
2977
if (updateOtherSide && removedSM != null && addedSM != null) {
2978
2979            // Add the dependency only if both objects involved
2980
// in the relationship being removed are already persistent.
2981
if (removedSM.state.isPersistentInDataStore()
2982                    && this.state.isPersistentInDataStore()) {
2983
2984                // First parameter == null marks a non removable dependency.
2985
// RESOLVE: Pass inverseFieldDesc here.
2986
removedSM.addUpdatedForeignReference(null, addedSM);
2987            }
2988        }
2989
2990        // If the foreign key is on this side, addedInverseFieldSM
2991
// corresponds to the removedSM and this is the addedSM!
2992
if (!updateOtherSide && addedInverseFieldSM != null) {
2993
2994            // Add the dependency only if both objects involved
2995
// in the relationship being removed are already persistent.
2996
if (addedInverseFieldSM.state.isPersistentInDataStore()
2997                    && addedSM.state.isPersistentInDataStore()) {
2998
2999                // First parameter == null marks a non removable dependency.
3000
// RESOLVE: Pass inverseFieldDesc here.
3001
addedInverseFieldSM.addUpdatedForeignReference(null, this);
3002            }
3003        }
3004    }
3005
3006    /**
3007     * Returns the added object's state manager. Transient objects become
3008     * "autopersistent" on the association to an already persistent instance and
3009     * are associated with a new state manager.
3010     *
3011     * If <code>newlyRegisteredSMs</code> is not null, the newly created state manager
3012     * is added to the list. This list is only non-null for the treatment of deferred
3013     * collection fields, which is done before the actual flush to the data store.
3014     * Autopersistence management for all other cases is handled sufficiently
3015     * in the makeAutoPersistent call.
3016     *
3017     * @param addedObject Object added to a relationship.
3018     * @param newlyRegisteredSMs
3019     * The state managers of autopersistent objects will be added to this list.
3020     * @return State manager for the added object. The statemanager will
3021     * be not null for all persistence capable objects != null.
3022     */

3023    private SQLStateManager getAddedSM(Object JavaDoc addedObject, ArrayList newlyRegisteredSMs) {
3024        SQLStateManager addedSM = null;
3025
3026        if (addedObject != null) {
3027            // Persistence by reachablity.
3028
if ((addedSM = (SQLStateManager) persistenceManager.getStateManager(addedObject)) == null) {
3029                makeAutoPersistent(addedObject);
3030                addedSM = (SQLStateManager) persistenceManager.getStateManager(addedObject);
3031
3032                // Add the newly created state manager to the newlyRegisteredSMs
3033
// list so we can do further processing on it.
3034
if (newlyRegisteredSMs != null && !newlyRegisteredSMs.contains(addedSM)) {
3035                    newlyRegisteredSMs.add(addedSM);
3036                }
3037            }
3038        }
3039        return addedSM;
3040    }
3041
3042    /**
3043     * Returns the removed object's state manager.
3044     *
3045     * @param removedObject Object removed to a relationship.
3046     * @return State manager for the removed object. The state manager might
3047     * be null even for objects != null (in case of JDK collections).
3048     * @see #removeCollectionRelationship
3049     */

3050    private SQLStateManager getRemovedSM(Object JavaDoc removedObject) {
3051        SQLStateManager removedSM = null;
3052
3053        if (removedObject != null) {
3054            removedSM = (SQLStateManager) persistenceManager.getStateManager(removedObject);
3055        }
3056        return removedSM;
3057    }
3058
3059    /**
3060     * Updates the relationship for the collection side of a one-to-many or
3061     * many-to-many relationship. Objects in <code>removedList</code> are
3062     * removed from the relation. The relation is set on all objects in
3063     * <code>addedList</code>. In case of user updates, relationship management
3064     * on "this" relationship side is done by the user, i.e. by
3065     * d.getEmployees().add(newEmp) for a Department d. This method is never
3066     * called during relationship management.
3067     *
3068     * @param fieldDesc Updated relationship field.
3069     * @param removedList List of objects to be removed from the relationship.
3070     * @param addedList List of objects to be added to the relationship.
3071     * @param newlyRegisteredSMs
3072     * List taking newly registered SMs for objects becoming autopersistent.
3073     * @param updateInverseRelationshipField
3074     * True, if we need to update the inverse relationship field.
3075     * @param managedRelationshipInProgress
3076     * True during relationship management. NOTE: This parameter is
3077     * always false, as the method is never called during relationship management.
3078     * @exception JDOUserException Thrown on failures in <code>afterList</code> handling.
3079     * @see #prepareSetField(FieldDesc,Object,boolean)
3080     */

3081    private void processCollectionUpdates(ForeignFieldDesc fieldDesc,
3082                                          ArrayList removedList,
3083                                          ArrayList addedList,
3084                                          ArrayList newlyRegisteredSMs,
3085                                          boolean updateInverseRelationshipField,
3086                                          boolean managedRelationshipInProgress) {
3087
3088        boolean debug = logger.isLoggable();
3089        ForeignFieldDesc inverseFieldDesc = fieldDesc.getInverseRelationshipField();
3090
3091        // RESOLVE: What if
3092
// * inverseFieldDesc is null?
3093
// * fieldDesc.cardinalityUPB == 1
3094

3095        if (debug) {
3096            Object JavaDoc[] items = new Object JavaDoc[] {removedList,addedList};
3097            logger.fine("sqlstore.sqlstatemanager.processcollectionupdate", items); // NOI18N
3098
}
3099
3100        // removedList contains the list of objects removed.
3101
if (removedList != null) {
3102            removeCollectionRelationship(fieldDesc, removedList, inverseFieldDesc,
3103                    updateInverseRelationshipField, managedRelationshipInProgress);
3104        }
3105
3106        // addedList contains the objects added.
3107
if (addedList != null) {
3108            addCollectionRelationship(fieldDesc, addedList, inverseFieldDesc,
3109                    newlyRegisteredSMs,
3110                    updateInverseRelationshipField, managedRelationshipInProgress);
3111        }
3112
3113        if (debug) {
3114            logger.fine("sqlstore.sqlstatemanager.processcollectionupdate.exit"); // NOI18N
3115
}
3116    }
3117
3118    /**
3119     * Nullifies the relationship for the objects removed from a collection relationship.
3120     *
3121     * @param fieldDesc Updated relationship field.
3122     * @param removedList List of objects to be removed from the relationship.
3123     * @param inverseFieldDesc Inverse relationship field.
3124     * @param updateInverseRelationshipField
3125     * True, if we need to update the inverse relationship side.
3126     * @param managedRelationshipInProgress
3127     * True during relationship management. NOTE: This parameter is always
3128     * false, as the method is never called during relationship management.
3129     * @see #processCollectionUpdates
3130     */

3131    private void removeCollectionRelationship(ForeignFieldDesc fieldDesc,
3132                                              ArrayList removedList,
3133                                              ForeignFieldDesc inverseFieldDesc,
3134                                              boolean updateInverseRelationshipField,
3135                                              boolean managedRelationshipInProgress) {
3136
3137        for (int i = 0; i < removedList.size(); i++) {
3138            SQLStateManager removedSM = getRemovedSM(removedList.get(i));
3139
3140            // removedSM == null can happen if the collection is non-SCO and contains
3141
// transient instances which don't become persistent until commit.
3142
if (removedSM != null) {
3143
3144                // The collection side never has the foreign key, i.e.
3145
// it's never processed during relationship management,
3146
// because data store updates are already done.
3147
if (!managedRelationshipInProgress) {
3148                    updateRelationshipInDataStore(fieldDesc, null, removedSM, inverseFieldDesc, false);
3149
3150                    // Relationship management
3151
if (updateInverseRelationshipField && inverseFieldDesc != null) {
3152                        removedSM.removeRelationship(inverseFieldDesc, this);
3153                    }
3154                }
3155            }
3156        }
3157    }
3158
3159    /**
3160     * Nullifies the (hidden) local fields corresponding to the foreign key columns
3161     * for relationship field <code>fieldDesc</code>. Usually the foreign key
3162     * columns are not mapped explicitly by the user. For this reason the runtime
3163     * creates hidden fields representing the foreign key columns internally. We
3164     * determine the local fields by iterating appropriate field list of either
3165     * <code>fieldDesc</code> or <code>inverseFieldDesc</code>.
3166     *
3167     * For dependency management, the removal of the foreign key has
3168     * to be nullified before the _possible_ removal of removedSM. The
3169     * same dependency applies to jointable relationships, see {@link
3170     * #registerRemoveDependency}.
3171     *
3172     * @param fieldDesc Updated relationship field.
3173     * @param removedSM State manager of the removed object.
3174     * @param inverseFieldDesc Inverse relationship field.
3175     * @param setDependencyOnly Only set the dependency between <code>this</code>
3176     * and <code>removedSM</code>.
3177     */

3178    private void nullifyForeignKey(ForeignFieldDesc fieldDesc,
3179                                   SQLStateManager removedSM,
3180                                   ForeignFieldDesc inverseFieldDesc,
3181                                   boolean setDependencyOnly) {
3182
3183        if (!isDeleted() && !setDependencyOnly) {
3184            // fieldDesc can be null for one-directional relationships. We are
3185
// only interested in the LocalFieldDescs for the foreign key columns,
3186
// which can also be retrieved from inverseFieldDesc.
3187
if (fieldDesc != null) {
3188                for (int i = 0; i < fieldDesc.localFields.size(); i++) {
3189                    LocalFieldDesc la = (LocalFieldDesc) fieldDesc.localFields.get(i);
3190
3191                    nullifyForeignKey(fieldDesc, la);
3192                }
3193            } else {
3194                for (int i = 0; i < inverseFieldDesc.foreignFields.size(); i++) {
3195                    LocalFieldDesc la = (LocalFieldDesc) inverseFieldDesc.foreignFields.get(i);
3196
3197                    nullifyForeignKey(fieldDesc, la);
3198                 }
3199            }
3200        }
3201
3202        // Nullify the foreign key before the _possible_ removal of removedSM.
3203
registerRemoveDependency(fieldDesc, removedSM);
3204    }
3205
3206    /**
3207     * Actually nullifies the local field <code>la</code> corresponding to a
3208     * foreign key column for the relationship update.
3209     * Fields tracking the foreign key field <code>la</code> are updated.
3210     *
3211     * @param fieldDesc Updated relationship field.
3212     * @param la Local field corresponding to a foreign key column.
3213     * @exception JDOUserException Is thrown, if the field to be updated
3214     * is a primary key field. We don't allow pk updates.
3215     */

3216    private void nullifyForeignKey(ForeignFieldDesc fieldDesc, LocalFieldDesc la) {
3217
3218        if (!getSetMaskBit(la.absoluteID)) {
3219            prepareUpdateField(la, null);
3220        }
3221
3222        JDOUserException pkUpdateEx = null;
3223
3224        if (la.isKeyField()) {
3225            try {
3226                assertPKUpdate(la, null);
3227            } catch (JDOUserException e) {
3228                // If the relationship being set to null
3229
// and the parent instance is being deleted, we will ignore
3230
// the exception thrown from assertPKUpdate(). The reason is
3231
// that we are really just trying to set up the dependency
3232
// and not modify the relationship itself. If we do not
3233
// ignore this exception, there will be no way to delete
3234
// the parent instance at all because we don't support
3235
// modifying primary key.
3236

3237                if (((stateFlags & ST_DELETE_INPROGRESS) == 0)) {
3238                    throw e;
3239                }
3240
3241                pkUpdateEx = e;
3242            }
3243        }
3244
3245        if (pkUpdateEx == null) {
3246            // As la tracks fieldDesc, fieldDesc is ignored in updateTrackedFields.
3247
updateTrackedFields(la, null, fieldDesc);
3248            la.setValue(this, null);
3249        }
3250    }
3251
3252    /**
3253     * Schedules the removal of the jointable entry between this and the
3254     * foreign state manager. A scheduled creation of the jointable entry
3255     * between these two objects is simply removed. The removal is
3256     * scheduled on the local side.
3257     *
3258     * For dependency management, the removal of the jointable entry has
3259     * to precede the _possible_ removal of removedSM. The same dependency applies
3260     * to foreign key relationships, see {@link #registerRemoveDependency}.
3261     *
3262     * RESOLVE: What happens, if a field descriptor is null, e.g. for one
3263     * way relationships, as the descriptors are taken as keys during scheduling?
3264     *
3265     * @param fieldDesc Updated relationship field. This field is mapped to a jointable.
3266     * @param removedSM State manager of the removed object.
3267     * @param inverseFieldDesc Inverse relationship field.
3268     * @see #prepareToUpdatePhaseIII
3269     */

3270    private void removeJoinTableEntry(ForeignFieldDesc fieldDesc,
3271                                      SQLStateManager removedSM,
3272                                      ForeignFieldDesc inverseFieldDesc) {
3273
3274        // Cleanup dependencies. We need to cleanup dependencies for
3275
// flushed autopersistent instances that aren't reachable
3276
// before commit. See runtime test Autopersistence.TestCase12.
3277
// The cleanup must be done for removals only, because the
3278
// only operations executed in prepareToUpdatePhaseIII are
3279
// removals.
3280
if (removedSM.state.isAutoPersistent() || this.state.isAutoPersistent()) {
3281            removedSM.removeUpdatedForeignReference(inverseFieldDesc, this);
3282            this.removeUpdatedForeignReference(fieldDesc, removedSM);
3283        }
3284
3285        // Remove scheduled creation on this side.
3286
if (fieldDesc != null && getUpdateDesc().removeUpdatedJoinTableRelationship(
3287                fieldDesc, removedSM, ActionDesc.LOG_CREATE) == false) {
3288
3289            // Remove scheduled creation on the other side.
3290
if (inverseFieldDesc == null || removedSM.getUpdateDesc().removeUpdatedJoinTableRelationship(
3291                    inverseFieldDesc, this, ActionDesc.LOG_CREATE) == false) {
3292
3293                // Schedule removal on this side.
3294
// The field descriptor taken as key must not be null, see above!
3295
getUpdateDesc().recordUpdatedJoinTableRelationship(
3296                        fieldDesc, this, removedSM, ActionDesc.LOG_DESTROY);
3297
3298                // Remove the jointable entry before the _possible_ removal of removedSM.
3299
registerRemoveDependency(fieldDesc, removedSM);
3300            }
3301        } else if (fieldDesc == null) {
3302            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
3303                    "core.statemanager.cantschedulejointable", // NOI18N
3304
this.getPersistenceConfig().getPersistenceCapableClass().getName(),
3305                    removedSM.getPersistenceConfig().getPersistenceCapableClass().getName()));
3306        }
3307    }
3308
3309    /**
3310     * Updates the relationship field <code>fieldDesc</code> between <code>this</code>
3311     * and <code>removedSM</code>.
3312     *
3313     * @param fieldDesc Updated relationship field. The field must be != null.
3314     * @param removedSM State manager of the removed object.
3315     */

3316    private void removeRelationship(ForeignFieldDesc fieldDesc, SQLStateManager removedSM) {
3317
3318        boolean isCollection = (fieldDesc.cardinalityUPB > 1);
3319
3320        if (!isCollection) {
3321            prepareUpdateFieldSpecial(fieldDesc, removedSM.persistentObject, false);
3322            updateTrackedFields(fieldDesc, null, null);
3323            fieldDesc.setValue(this, null);
3324        } else {
3325            try {
3326                prepareUpdateFieldSpecial(fieldDesc, null, true);
3327                SCOCollection c = (SCOCollection) fieldDesc.getValue(this);
3328                c.removeInternal(removedSM.persistentObject);
3329                updateTrackedFields(fieldDesc, c, null);
3330            } catch (ClassCastException JavaDoc e) {
3331                // ignore
3332
}
3333        }
3334    }
3335
3336    /**
3337     * Sets the relationship for the objects added to a collection relationship.
3338     *
3339     * @param fieldDesc Updated relationship field.
3340     * @param addedList List of objects to be added to the relationship.
3341     * @param inverseFieldDesc Inverse relationship field.
3342     * @param newlyRegisteredSMs
3343     * State managers for autopersistent objects will be added to this list.
3344     * @param updateInverseRelationshipField
3345     * True, if we need to update the inverse relationship side.
3346     * @param managedRelationshipInProgress
3347     * True during relationship management. NOTE: This parameter is always
3348     * false, as the method is never called during relationship management.
3349     * @exception JDOUserException Thrown if objects in <code>addedList</code> have been deleted.
3350     * @see #processCollectionUpdates
3351     */

3352    private void addCollectionRelationship(ForeignFieldDesc fieldDesc,
3353                                           ArrayList addedList,
3354                                           ForeignFieldDesc inverseFieldDesc,
3355                                           ArrayList newlyRegisteredSMs,
3356                                           boolean updateInverseRelationshipField,
3357                                           boolean managedRelationshipInProgress) {
3358
3359        JDOUserException ex = null;
3360
3361        for (int i = 0; i < addedList.size(); i++) {
3362            Object JavaDoc addedObject = addedList.get(i);
3363            SQLStateManager addedSM = getAddedSM(addedObject, newlyRegisteredSMs);
3364
3365            // addedSM == null can happen if the collection is non-SCO and contains
3366
// transient instances which don't become persistent until commit.
3367
if (addedSM != null) {
3368
3369                if (addedSM.isDeleted()) {
3370                    // For managed relationships, if the addedObject is deleted, we need
3371
// to throw an exception at the end and the exception should include
3372
// the deleted objects in its failedObjectArray.
3373
if (inverseFieldDesc != null) {
3374                        if (ex == null) {
3375                            ex = new JDOUserException(I18NHelper.getMessage(messages,
3376                                    "jdo.lifecycle.deleted.accessField")); // NOI18N
3377
}
3378
3379                        ex.addFailedObject(addedObject);
3380                    }
3381                    continue;
3382                }
3383
3384                // The collection side never has the foreign key, i.e.
3385
// it's never processed during relationship management,
3386
// because data store updates are already done at that time.
3387
if (!managedRelationshipInProgress) {
3388                    updateRelationshipInDataStore(fieldDesc, addedSM, null, inverseFieldDesc, false);
3389
3390                    // Relationship management
3391
if (updateInverseRelationshipField && inverseFieldDesc != null) {
3392                        addedSM.addRelationship(inverseFieldDesc, this);
3393                    }
3394                }
3395            }
3396        }
3397
3398        if (ex != null) {
3399            throw ex;
3400        }
3401    }
3402
3403    /**
3404     * Sets the foreign key corresponding to the relationship field
3405     * <code>fieldDesc</code>. Usually the foreign key columns are not mapped
3406     * explicitly by the user. For this reason the runtime creates hidden fields
3407     * representing the foreign key columns internally. We determine the local
3408     * fields by iterating appropriate field list of either
3409     * <code>fieldDesc</code> or <code>inverseFieldDesc</code>.
3410     *
3411     * To ensure referentional integrity constraints in the database,
3412     * the added object has to be written to the store, before the
3413     * foreign key can be set. The same dependency applies to relationships
3414     * mapped to jointables, see {@link #addJoinTableEntry}.
3415     *
3416     * @param fieldDesc Updated relationship field.
3417     * @param addedSM State manager of the added object.
3418     * @param inverseFieldDesc Inverse relationship field.
3419     */

3420    private void setForeignKey(ForeignFieldDesc fieldDesc,
3421                               SQLStateManager addedSM,
3422                               ForeignFieldDesc inverseFieldDesc) {
3423
3424        if (!isDeleted()) {
3425            // fieldDesc can be null for one-directional relationships. We are
3426
// only interested in the LocalFieldDescs for the foreign key columns,
3427
// which can also be retrieved from inverseFieldDesc.
3428
if (fieldDesc != null) {
3429                for (int i = 0; i < fieldDesc.localFields.size(); i++) {
3430                    LocalFieldDesc la = (LocalFieldDesc) fieldDesc.localFields.get(i);
3431                    LocalFieldDesc fa = (LocalFieldDesc) fieldDesc.foreignFields.get(i);
3432
3433                    setForeignKey(fieldDesc, la, fa.getValue(addedSM));
3434                }
3435            } else {
3436                for (int i = 0; i < inverseFieldDesc.foreignFields.size(); i++) {
3437                    LocalFieldDesc la = (LocalFieldDesc) inverseFieldDesc.foreignFields.get(i);
3438                    LocalFieldDesc fa = (LocalFieldDesc) inverseFieldDesc.localFields.get(i);
3439
3440                    setForeignKey(fieldDesc, la, fa.getValue(addedSM));
3441                }
3442            }
3443        }
3444
3445        // The referred object has to be written to the store before the foreign key can be set.
3446
registerCreateDependency(inverseFieldDesc, addedSM);
3447    }
3448
3449    /**
3450     * Actually sets the local field <code>la</code> corresponding to a foreign
3451     * key column to the new value for the relationship update. The new value
3452     * is taken from <code>fa</code>, which is typically a primary key field on
3453     * the other relationship side. Fields tracking the foreign key field
3454     * <code>la</code> are updated.
3455     *
3456     * @param fieldDesc Updated relationship field.
3457     * @param la Local field corresponding to a foreign key column.
3458     * @param faValue Value of the local field corresponding to the primary
3459     * key column on the other relationship side.
3460     * @exception JDOUserException Is thrown, if the field to be updated
3461     * is a primary key field. We don't allow pk updates.
3462     */

3463    private void setForeignKey(ForeignFieldDesc fieldDesc,
3464                               LocalFieldDesc la,
3465                               Object JavaDoc faValue) {
3466
3467        if (!getSetMaskBit(la.absoluteID)) {
3468            prepareUpdateField(la, null);
3469        }
3470
3471        if (la.isKeyField()) {
3472            assertPKUpdate(la, faValue);
3473        }
3474
3475        updateTrackedFields(la, faValue, fieldDesc);
3476        la.setValue(this, faValue);
3477    }
3478
3479    /**
3480     * Schedules the creation of a jointable entry between this and the added
3481     * state manager. A scheduled removal of the jointable entry between these
3482     * two is simply removed. The creation is scheduled on the local side.
3483     *
3484     * For dependency management, the side creating the jointable entry has
3485     * to wait for the other to become persistent. The same dependency applies
3486     * to foreign key relationships, see {@link #setForeignKey}.
3487     *
3488     * RESOLVE: What happens, if a field descriptor is null, e.g. for one
3489     * way relationships, as the descriptors are taken as keys during scheduling?
3490     *
3491     * @param fieldDesc Updated relationship field. This field is mapped to a jointable.
3492     * @param addedSM State manager of the added object.
3493     * @param inverseFieldDesc Inverse relationship field.
3494     */

3495    private void addJoinTableEntry(ForeignFieldDesc fieldDesc,
3496                                   SQLStateManager addedSM,
3497                                   ForeignFieldDesc inverseFieldDesc) {
3498
3499        // Cleanup dependencies.
3500
// Note: The following lines break deadlock detection for circular dependencies.
3501
//this.removeUpdatedForeignReference(addedSM);
3502
//addedSM.removeUpdatedForeignReference(this);
3503

3504        // Remove scheduled removal on this side.
3505
if (fieldDesc != null && getUpdateDesc().removeUpdatedJoinTableRelationship(
3506                fieldDesc, addedSM, ActionDesc.LOG_DESTROY) == false) {
3507
3508            // Remove scheduled removal on the other side.
3509
if (inverseFieldDesc == null || addedSM.getUpdateDesc().removeUpdatedJoinTableRelationship(
3510                    inverseFieldDesc, this, ActionDesc.LOG_DESTROY) == false) {
3511
3512                // Schedule creation on this side.
3513
// The field descriptor taken as key must not be null, see above!
3514
getUpdateDesc().recordUpdatedJoinTableRelationship(
3515                        fieldDesc, this, addedSM, ActionDesc.LOG_CREATE);
3516
3517                // The side creating the jointable entry has to wait for the other to become persistent.
3518
registerCreateDependency(inverseFieldDesc, addedSM);
3519            }
3520        } else if (fieldDesc == null) {
3521            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
3522                    "core.statemanager.cantschedulejointable", // NOI18N
3523
this.getPersistenceConfig().getPersistenceCapableClass().getName(),
3524                    addedSM.getPersistenceConfig().getPersistenceCapableClass().getName()));
3525        }
3526    }
3527
3528    /**
3529     * Updates the relationship field <code>fieldDesc</code> between <code>this</code>
3530     * and the state manager of the added object <code>addedSM</code>.
3531     *
3532     * @param fieldDesc Updated relationship field. The field must be != null.
3533     * @param addedSM State manager of the added object.
3534     * @return Field <code>fieldDesc</code>'s previous value.
3535     */

3536    private Object JavaDoc addRelationship(ForeignFieldDesc fieldDesc,
3537                                   SQLStateManager addedSM) {
3538
3539        Object JavaDoc previousValue = null;
3540        boolean isCollection = (fieldDesc.cardinalityUPB > 1);
3541
3542        if (!isCollection) {
3543            previousValue = prepareSetField(fieldDesc, addedSM.persistentObject, true);
3544        } else {
3545            try {
3546                prepareUpdateFieldSpecial(fieldDesc, null, true);
3547                SCOCollection c = (SCOCollection) fieldDesc.getValue(this);
3548
3549                // Note: c might be null during relationship management for a
3550
// self relationship, as in the Employee-Manager relation.
3551
// See runtime test AutoPersistence.TestCase31 for an example.
3552
if (c == null) {
3553                    replaceCollection(fieldDesc, null);
3554                    c = (SCOCollection) fieldDesc.getValue(this);
3555                }
3556
3557                c.addInternal(addedSM.persistentObject);
3558                updateTrackedFields(fieldDesc, c, null);
3559            } catch (ClassCastException JavaDoc e) {
3560                // ignore
3561
}
3562        }
3563
3564        return previousValue;
3565    }
3566
3567    /**
3568     * This is a special version of prepareUpdateField that does not do navigation
3569     * if a field is not loaded. We don't need to reload the field, because the
3570     * before image value is given as parameter! This method is mostly called
3571     * during relatioship management, as the before image value is already know
3572     * in this case.
3573     * <p>
3574     * The <code>createDeferredCollection</code> parameter should be true
3575     * only if <code>fieldDesc</code> corresponds to a collection field. Note:
3576     * <ul>
3577     * <li><code>createDeferredCollection</code> == false
3578     * ==> <code>beforeImageValue</code> must be non-null.</li>
3579     * <li><code>createDeferredCollection</code> == true
3580     * ==> <code>beforeImageValue</code> is null.</li>
3581     * </ul>.
3582     *
3583     * @param fieldDesc The field to be prepared.
3584     * @param beforeImageValue The before image value.
3585     * @param createDeferredCollection
3586     * Indicates whether to create a deferred SCOCollection. Deferred collections
3587     * are created during relationship management if the inverse field is not
3588     * loaded.
3589     * @see #prepareUpdateField
3590     */

3591    private synchronized void prepareUpdateFieldSpecial(FieldDesc fieldDesc,
3592                                                        Object JavaDoc beforeImageValue,
3593                                                        boolean createDeferredCollection) {
3594        if (fieldDesc.isKeyField()) {
3595            throw new JDOUnsupportedOptionException(I18NHelper.getMessage(messages,
3596                    "core.statemanager.nopkupdate")); // NOI18N
3597
}
3598
3599        getUpdateDesc().markRelationshipChange(fieldDesc);
3600
3601        boolean debug = logger.isLoggable();
3602
3603        if (debug) {
3604            Object JavaDoc[] items = new Object JavaDoc[] {fieldDesc.getName(),state};
3605            logger.fine("sqlstore.sqlstatemanager.prepareupdatefieldspl", items); // NOI18N
3606
}
3607
3608        boolean optimistic = persistenceManager.isOptimisticTransaction();
3609        boolean xactActive = persistenceManager.isActiveTransaction();
3610        boolean nontransactionalRead = persistenceManager.isNontransactionalRead();
3611
3612        if (state.needsReload(optimistic, nontransactionalRead, xactActive)) {
3613            if (!optimistic) {
3614                persistenceManager.clearFields(this.persistentObject);
3615            }
3616
3617            reload(null);
3618        }
3619
3620        LifeCycleState oldstate = state;
3621        state = state.transitionWriteField(xactActive);
3622        registerInstance(false, null, oldstate);
3623
3624        if (getSetMaskBit(fieldDesc.absoluteID)) {
3625            // Note: The set mask is set for all fields on make persistent.
3626
return;
3627        }
3628
3629        if (!getPresenceMaskBit(fieldDesc.absoluteID)) {
3630            if (!createDeferredCollection) {
3631                if (!(beforeImageValue instanceof SCOCollection) ||
3632                        !((SCOCollection) beforeImageValue).isDeferred()) {
3633
3634                    updateBeforeImage(fieldDesc, beforeImageValue);
3635
3636                    // Set the presence mask for a non deferred collection.
3637
setPresenceMaskBit(fieldDesc.absoluteID);
3638                }
3639            } else {
3640                // Deferred collection handling.
3641
if (!(fieldDesc instanceof ForeignFieldDesc) ||
3642                        (((ForeignFieldDesc) fieldDesc).cardinalityUPB <= 1)) {
3643                    //should throw an exception
3644
return;
3645                }
3646
3647                Object JavaDoc value = fieldDesc.getValue(this);
3648
3649                if (value == null) {
3650                    // If the collection field is null, we need to create a
3651
// deferred SCOCollection.
3652
SCOCollection c = (SCOCollection) persistenceManager.newCollectionInstanceInternal(
3653                            fieldDesc.getType(),
3654                            persistentObject,
3655                            fieldDesc.getName(),
3656                            fieldDesc.getComponentType(),
3657                            false,
3658                            10);
3659                    c.markDeferred();
3660                    fieldDesc.setValue(this, c);
3661                }
3662                // NOTE: We don't set the presence mask bit for deferred collections,
3663
// because deferred collections MUST be reloaded on the first read access!
3664
}
3665        }
3666
3667        recordUpdatedField(fieldDesc);
3668
3669        if (debug) {
3670            logger.fine("sqlstore.sqlstatemanager.prepareupdatefieldspl.exit"); // NOI18N
3671
}
3672    }
3673
3674    /**
3675     * Updates the values for fields tracking field
3676     * <code>fieldDesc</code>. Must be called before the new value
3677     * for field <code>fieldDesc</code> is actually set.<p>
3678     *
3679     * If called when setting the local fields mapped to the
3680     * relationship on relationship updates, the relationship field
3681     * tracked by <code>fieldDesc</code> must be ignored when
3682     * propagating the changes.<p>
3683     *
3684     * For overlapping pk/fk situations or if a fk column is
3685     * explicitly mapped to a visible field, the update of the local
3686     * field triggers the update of the relationship field tracking
3687     * the local field.
3688     *
3689     * @param fieldDesc Field whose tracked fields we wish to update.
3690     * @param value New value for the field.
3691     * @param fieldToIgnore Field to be ignored when propagating
3692     * changes. This is the relationship field tracked by field
3693     * <code>fieldDesc</code> if <code>fieldDesc</code> is a
3694     * <b>hidden</b> local field.
3695     */

3696    private void updateTrackedFields(FieldDesc fieldDesc,
3697                                     Object JavaDoc value,
3698                                     ForeignFieldDesc fieldToIgnore) {
3699
3700        ArrayList trackedFields = fieldDesc.getTrackedFields();
3701
3702        if (trackedFields == null) {
3703            return;
3704        }
3705
3706        boolean debug = logger.isLoggable(Logger.FINEST);
3707
3708        if (debug) {
3709            Object JavaDoc[] items = new Object JavaDoc[] {fieldDesc.getName(), value,
3710                                           ((fieldToIgnore != null) ? fieldToIgnore.getName() : null)};
3711            logger.finest("sqlstore.sqlstatemanager.updatetrackedfields", items); // NOI18N
3712
}
3713
3714        Object JavaDoc currentValue = fieldDesc.getValue(this);
3715        int size = trackedFields.size();
3716
3717        ArrayList fieldsToIgnore = ((fieldToIgnore != null) ? fieldToIgnore.getTrackedFields() : null);
3718
3719        if (fieldDesc instanceof ForeignFieldDesc) {
3720            // For tracked relationship fields, we simply set the new value.
3721
for (int i = 0; i < size; i++) {
3722                ForeignFieldDesc tf = (ForeignFieldDesc) trackedFields.get(i);
3723                prepareUpdateFieldSpecial(tf, currentValue, false);
3724                tf.setValue(this, value);
3725            }
3726        } else {
3727            Object JavaDoc previousValues[] = new Object JavaDoc[size];
3728            LocalFieldDesc primaryTrackedField = null;
3729            Object JavaDoc primaryTrackedFieldValue = null;
3730
3731            if ((fieldDesc.sqlProperties & FieldDesc.PROP_PRIMARY_TRACKED_FIELD) > 0) {
3732                primaryTrackedField = (LocalFieldDesc) fieldDesc;
3733                primaryTrackedFieldValue = value;
3734            }
3735
3736            for (int i = 0; i < size; i++) {
3737                FieldDesc tf = (FieldDesc) trackedFields.get(i);
3738
3739                if (tf instanceof LocalFieldDesc) {
3740                    Object JavaDoc convertedValue = null;
3741                    Object JavaDoc convertedCurrentValue = null;
3742
3743                    // RESOLVE: SCODate is problematic because convertValue unsets
3744
// the owner. The SCO to be used for restoring is broken.
3745
try {
3746                        convertedValue = tf.convertValue(value, this);
3747                        convertedCurrentValue = tf.convertValue(currentValue, this);
3748                    } catch (JDOUserException e) {
3749                        // We got a conversion error. We need to revert all
3750
// the tracked fields to their previous values.
3751
// NOTE: We don't have to revert relationship fields
3752
// because they come after all the primitive fields.
3753
for (int j = 0; j < i; j++) {
3754                            tf = (FieldDesc) trackedFields.get(j);
3755
3756                            tf.setValue(this, previousValues[j]);
3757                        }
3758
3759                        throw e;
3760                    }
3761
3762                    if ((tf.sqlProperties & FieldDesc.PROP_PRIMARY_TRACKED_FIELD) > 0) {
3763                        primaryTrackedField = (LocalFieldDesc) tf;
3764                        primaryTrackedFieldValue = convertedValue;
3765                    }
3766
3767                    prepareUpdateFieldSpecial(tf, convertedCurrentValue, false);
3768
3769                    // save the previous values for rollback
3770
previousValues[i] = tf.getValue(this);
3771
3772                    tf.setValue(this, convertedValue);
3773                } else {
3774                    // We bypass fieldToIgnore and its trackedFields
3775
if (((stateFlags & ST_FIELD_TRACKING_INPROGRESS) > 0)
3776                            || (tf == fieldToIgnore)
3777                            || ((fieldsToIgnore != null) && fieldsToIgnore.contains(tf))) {
3778                        continue;
3779                    }
3780
3781                    ForeignFieldDesc ftf = (ForeignFieldDesc) tf;
3782
3783                    Object JavaDoc pc = null;
3784
3785                    if (primaryTrackedFieldValue != null) {
3786                        pc = getObjectById(ftf, primaryTrackedField, primaryTrackedFieldValue, false);
3787                    }
3788
3789                    stateFlags |= ST_FIELD_TRACKING_INPROGRESS;
3790                    prepareSetField(ftf, pc);
3791                    stateFlags &= ~ST_FIELD_TRACKING_INPROGRESS;
3792                }
3793            }
3794        }
3795
3796        if (debug) {
3797            logger.finest("sqlstore.sqlstatemanager.updatetrackedfields.exit"); // NOI18N
3798
}
3799    }
3800
3801    /**
3802     * Looks up the object associated to this state manager on
3803     * relationship <code>ff</code> field in the persistence manager cache.
3804     * The method first constructs the related instance's object id by
3805     * calling {@link ForeignFieldDesc#createObjectId}. Then asks the
3806     * persistence manager to retrieve the object associated to this
3807     * id from it's caches. If the referred object is not found, the
3808     * instance returned by {@link PersistenceManager#getObjectById(Object)}
3809     * is Hollow. Hollow instances are ignored for navigation.
3810     *
3811     * @param ff Relationship to be retrieved. The relationship must have
3812     * an object ("to one side") value.
3813     * @param updatedField Updated local field mapped to this relationship.
3814     * @param value <code>updatedField</code>'s new value.
3815     * @param forNavigation If true, the lookup is executed for navigation.
3816     * @return Object found in the cache. Null, if the object wasn't found.
3817     */

3818    private Object JavaDoc getObjectById(ForeignFieldDesc ff,
3819                                 LocalFieldDesc updatedField,
3820                                 Object JavaDoc value,
3821                                 boolean forNavigation) {
3822        assert ff.cardinalityUPB <=1;
3823        // If called for navigation updatedField and value should be null.
3824
assert forNavigation ? updatedField == null && value == null : true;
3825
3826        Object JavaDoc rc = null;
3827        Object JavaDoc oid = ff.createObjectId(this, updatedField, value);
3828
3829        if (oid != null) {
3830            rc = persistenceManager.getObjectById(oid);
3831            LifeCycleState rcState = ((SQLStateManager)
3832                    ((PersistenceCapable) rc).jdoGetStateManager()).state;
3833
3834            if (forNavigation && (rcState instanceof Hollow)) {
3835                rc = null;
3836            }
3837        }
3838
3839        return rc;
3840    }
3841
3842    /**
3843     * Sets field <code>fieldDesc</code> to <code>value</code>.
3844     * The update of relationship fields is triggered by either calling
3845     * {@link #updateCollectionField} or {@link #updateObjectField},
3846     * depending on the field's cardinality.
3847     *
3848     * @param fieldDesc Field to be updated.
3849     * @param value New value.
3850     * @param managedRelationshipInProgress
3851     * True during relationship management.
3852     * @return Field <code>fieldDesc</code>'s previous value.
3853     */

3854    private Object JavaDoc doUpdateField(FieldDesc fieldDesc,
3855                                 Object JavaDoc value,
3856                                 boolean managedRelationshipInProgress) {
3857
3858        prepareUpdateField(fieldDesc, null);
3859
3860        if (fieldDesc instanceof ForeignFieldDesc) {
3861            ForeignFieldDesc ff = (ForeignFieldDesc) fieldDesc;
3862            if (ff.cardinalityUPB > 1) {
3863                updateCollectionField(ff, (Collection) value,
3864                        managedRelationshipInProgress);
3865            } else {
3866                updateObjectField(ff, value, true,
3867                        managedRelationshipInProgress);
3868            }
3869        }
3870
3871        updateTrackedFields(fieldDesc, value, null);
3872
3873        Object JavaDoc currentValue = fieldDesc.getValue(this);
3874        fieldDesc.setValue(this, value);
3875        return currentValue;
3876    }
3877
3878    private Object JavaDoc prepareSetField(int fieldID, Object JavaDoc value) {
3879        FieldDesc fieldDesc = persistenceConfig.getField(fieldID);
3880
3881        return prepareSetField(fieldDesc, value, false, true);
3882    }
3883
3884    private Object JavaDoc prepareSetField(FieldDesc fieldDesc, Object JavaDoc value) {
3885        return prepareSetField(fieldDesc, value, false, false);
3886    }
3887
3888    /**
3889     * Internal method setting the new value <code>value</code> for field
3890     * <code>fieldDesc</code> during relationship management. Only called on
3891     * relationship additions for object fields. This method is never called
3892     * for collection fields. Relationship management for collection fields
3893     * is done by deferred collections in {@link #addRelationship}.
3894     * Deferred collections are implemented in <code>SCOCollection</code>.
3895     * Relationship management on relationship removal is done in
3896     * {@link #removeRelationship} for both object and collection fields.
3897     *
3898     * @param fieldDesc Field to be updated.
3899     * @param value New value.
3900     * @param managedRelationshipInProgress Always true.
3901     * @return Field <code>fieldDesc</code>'s previous value.
3902     * @see com.sun.jdo.spi.persistence.support.sqlstore.SCOCollection
3903     */

3904    private Object JavaDoc prepareSetField(FieldDesc fieldDesc, Object JavaDoc value,
3905                                   boolean managedRelationshipInProgress) {
3906        return prepareSetField(fieldDesc, value, managedRelationshipInProgress, false);
3907    }
3908
3909    /**
3910     * Sets field <code>fieldDesc</code> by calling
3911     * {@link SQLStateManager#doUpdateField(FieldDesc, Object, boolean)}.
3912     *
3913     * @param fieldDesc Field to be updated.
3914     * @param value New value.
3915     * @param managedRelationshipInProgress
3916     * True during relationship management.
3917     * @param acquireShareLock Acquire a shared lock during the update.
3918     * @return Field <code>fieldDesc</code>'s previous value.
3919     */

3920    private Object JavaDoc prepareSetField(FieldDesc fieldDesc, Object JavaDoc value,
3921                                   boolean managedRelationshipInProgress,
3922                                   boolean acquireShareLock) {
3923        boolean debug = logger.isLoggable(Logger.FINEST);
3924
3925        if (debug) {
3926             logger.finest("sqlstore.sqlstatemanager.preparesetfield", fieldDesc.getName()); // NOI18N
3927
}
3928
3929        if (acquireShareLock) {
3930            persistenceManager.acquireShareLock();
3931        }
3932
3933        try {
3934            getLock();
3935
3936            if ((fieldDesc.sqlProperties & FieldDesc.PROP_READ_ONLY) > 0) {
3937                throw new JDOUserException(I18NHelper.getMessage(messages,
3938                        "core.statemanager.readonly", fieldDesc.getName(), // NOI18N
3939
persistentObject.getClass().getName()));
3940            }
3941
3942            // We need to lock fieldUpdateLock if there is a chance that
3943
// relationship field values might be affected. This is the case if
3944
// fieldDesc is a relationship field or it tracks other fields.
3945
if ((fieldDesc.getTrackedFields() != null) ||
3946                (fieldDesc instanceof ForeignFieldDesc)) {
3947
3948                persistenceManager.acquireFieldUpdateLock();
3949                try {
3950                    return doUpdateField(fieldDesc, value, managedRelationshipInProgress);
3951                } finally {
3952                    persistenceManager.releaseFieldUpdateLock();
3953                }
3954
3955            } else {
3956                    return doUpdateField(fieldDesc, value, managedRelationshipInProgress);
3957            }
3958        } catch (JDOException e) {
3959            throw e;
3960        } catch (Exception JavaDoc e) {
3961            logger.log(Logger.FINE,"sqlstore.exception.log", e);
3962            throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
3963                    "core.statemanager.setfieldfailed"), e); // NOI18N
3964
} finally {
3965            if (acquireShareLock) {
3966                persistenceManager.releaseShareLock();
3967            }
3968            releaseLock();
3969
3970            if (debug) {
3971                logger.finest("sqlstore.sqlstatemanager.preparesetfield.exit"); // NOI18N
3972
}
3973        }
3974    }
3975
3976    /**
3977     * Sets the new value for the collection field by calling
3978     * {@link #processCollectionUpdates}.
3979     *
3980     * @param fieldDesc Field descriptor of the field to be set.
3981     * @param value New value.
3982     * @param managedRelationshipInProgress
3983     * True during relationship management.
3984     */

3985    private void updateCollectionField(ForeignFieldDesc fieldDesc,
3986                                       Collection value,
3987                                       boolean managedRelationshipInProgress) {
3988        boolean debug = logger.isLoggable(Logger.FINEST);
3989
3990        if (debug) {
3991            Object JavaDoc[] items = new Object JavaDoc[] {value,((value == null)? "NO" : value.getClass().getName())}; // NOI18N
3992
logger.finest("sqlstore.sqlstatemanager.processforeignfield", items); // NOI18N
3993
}
3994
3995        Object JavaDoc currVal = fieldDesc.getValue(this);
3996
3997        // Do nothing if the current value is identical to the new value.
3998
if (currVal != value) {
3999            Object JavaDoc owner = null;
4000            ArrayList added = null;
4001            ArrayList removed = null;
4002
4003            // Verify SCO owner and fieldName if any
4004
if (value != null && value instanceof SCOCollection) {
4005                SCOCollection sco = (SCOCollection) value;
4006                owner = sco.getOwner();
4007
4008                if (owner == null) {
4009                    sco.setOwner(persistentObject, fieldDesc.getName(),
4010                        fieldDesc.getComponentType());
4011
4012                } else if (owner != persistentObject ||
4013                        !fieldDesc.getName().equals(sco.getFieldName())) {
4014
4015                        throw new JDOUserException(I18NHelper.getMessage(
4016                            messages, "core.statemanager.anotherowner"), // NOI18N
4017
new Object JavaDoc[]{owner, sco.getFieldName()});
4018                }
4019                // SCO should not behave as a JDK collection,
4020
// but become owned and tracked at setXXX operation.
4021
added = new ArrayList(value);
4022            }
4023
4024            Object JavaDoc befrVal = fieldDesc.getValue(beforeImage);
4025            if (currVal != null) {
4026                if (debug)
4027                    logger.finest("sqlstore.sqlstatemanager.processforeignfield.remove"); // NOI18N
4028

4029                // This is a setXXX (i.e. replace) operation, we need to
4030
// "remove" elements from the current SCOCollection and mark it as not used
4031

4032                if (((Collection) currVal).size() > 0) {
4033                    removed = new ArrayList((Collection) currVal);
4034                }
4035
4036                if (currVal instanceof SCOCollection) {
4037                    if (debug)
4038                        logger.finest("sqlstore.sqlstatemanager.processforeignfield.reset"); // NOI18N
4039
// SCOCollection: mark it as not used
4040
((SCO) currVal).unsetOwner();
4041                }
4042            } else if (getSetMaskBit(fieldDesc.absoluteID) == false && befrVal != null)
4043            // && value instanceof SCOCollection && owner != null)
4044
{
4045                if (debug)
4046                    logger.finest("sqlstore.sqlstatemanager.processforeignfield.remove_from_bi"); // NOI18N
4047
// Replace with SCOCollection: mark beforeImage as removed
4048

4049                if (((Collection) befrVal).size() > 0) {
4050                    removed = new ArrayList((Collection) befrVal);
4051                }
4052            }
4053
4054            processCollectionUpdates(fieldDesc, removed, added, null, true,
4055                managedRelationshipInProgress);
4056        }
4057    }
4058
4059    public Object JavaDoc clone() {
4060        SQLStateManager clone = new SQLStateManager(store, persistenceConfig);
4061        clone.persistenceManager = persistenceManager;
4062
4063        return clone;
4064    }
4065
4066    private void assertNotPK(int fieldNumber) {
4067        if (persistenceConfig.isPKField(fieldNumber))
4068            throw new JDOUnsupportedOptionException(I18NHelper.getMessage(messages,
4069                    "core.statemanager.nopkupdate")); // NOI18N
4070
}
4071
4072    private void assertPKUpdate(FieldDesc f, Object JavaDoc value) {
4073        Object JavaDoc currentValue = f.getValue(this);
4074        boolean throwException = false;
4075
4076        // We only throw an exception if the new value is actually different from
4077
// the current value.
4078
if ((value != null) && (currentValue != null)) {
4079            if (value.toString().compareTo(currentValue.toString()) != 0) {
4080                throwException = true;
4081            }
4082        } else if (value != currentValue) {
4083            throwException = true;
4084        }
4085
4086        if (throwException) {
4087            throw new JDOUnsupportedOptionException(I18NHelper.getMessage(messages,
4088                    "core.statemanager.nopkupdate")); // NOI18N
4089
}
4090    }
4091
4092    /**
4093     * ...
4094     */

4095    public com.sun.jdo.api.persistence.support.PersistenceManager getPersistenceManagerInternal() {
4096        return persistenceManager;
4097    }
4098
4099    /**
4100     * ...
4101     */

4102    public com.sun.jdo.api.persistence.support.PersistenceManager getPersistenceManager() {
4103        return (persistenceManager == null)? null : persistenceManager.getCurrentWrapper();
4104    }
4105
4106
4107    /**
4108     * ...
4109     */

4110    // !!! olsen: changed to return byte instead of void (->PC.jdoSetFlags())
4111
public byte setFlags(byte flags) {
4112        // RESOLVE: Need to verify that the flags are valid with the current
4113
// state of the state manager.
4114
return flags;
4115    }
4116
4117    /**
4118     * Triggers the state transition for READ and registers the
4119     * instance in the transaction cache.
4120     */

4121    public void loadForRead() {
4122        boolean debug = logger.isLoggable(Logger.FINER);
4123
4124        if (debug) {
4125            logger.finer("sqlstore.sqlstatemanager.loadforread"); // NOI18N
4126
}
4127
4128        persistenceManager.acquireShareLock();
4129
4130        try {
4131            getLock();
4132
4133            byte oldFlags = persistenceManager.getFlags(persistentObject);
4134
4135            // If the jdoFlag is either READ_OK or READ_WRITE_OK, that means another
4136
// thread might have already call loadForRead on this instance.
4137
if (oldFlags != LOAD_REQUIRED) {
4138                return;
4139            }
4140
4141            try {
4142                boolean xactActive = persistenceManager.isActiveTransaction();
4143                boolean optimistic = persistenceManager.isOptimisticTransaction();
4144                boolean nontransactionalRead = persistenceManager.isNontransactionalRead();
4145
4146                if (state.needsReload(optimistic, nontransactionalRead, xactActive)) {
4147                    reload(null);
4148                }
4149
4150                LifeCycleState oldstate = state;
4151                state = state.transitionReadField(optimistic, nontransactionalRead, xactActive);
4152                persistenceManager.setFlags(persistentObject, READ_OK);
4153                registerInstance(false, null, oldstate);
4154            } catch (JDOException e) {
4155                // restore the jdoFlags.
4156
persistenceManager.setFlags(persistentObject, oldFlags);
4157                throw e;
4158            }
4159        } finally {
4160            persistenceManager.releaseShareLock();
4161            releaseLock();
4162
4163            if (debug) {
4164                logger.finer("sqlstore.sqlstatemanager.loadforread.exit"); // NOI18N
4165
}
4166        }
4167    }
4168
4169    /**
4170     * Triggers the state transition for WRITE and registers the instance
4171     * in the transaction cache. Prepares all DFG fields for update.
4172     */

4173    public void loadForUpdate() {
4174        boolean debug = logger.isLoggable(Logger.FINER);
4175
4176        if (debug) {
4177            logger.finer("sqlstore.sqlstatemanager.loadforupdate"); // NOI18N
4178
}
4179
4180        persistenceManager.acquireShareLock();
4181
4182        try {
4183            getLock();
4184
4185            byte oldFlags = persistenceManager.getFlags(persistentObject);
4186
4187            // If the jdoFlags is already set to READ_WRITE_OK, it means that anther
4188
// thread has called loadForUpdate on this instance.
4189
if (oldFlags == READ_WRITE_OK) {
4190                return;
4191            }
4192
4193            persistenceManager.setFlags(persistentObject, READ_WRITE_OK);
4194
4195            ArrayList fields = persistenceConfig.fields;
4196
4197            try {
4198                // Mark all the fields in the dfg dirty.
4199
for (int i = 0; i < fields.size(); i++) {
4200                    FieldDesc f = (FieldDesc) fields.get(i);
4201
4202                    if (f.fetchGroup == FieldDesc.GROUP_DEFAULT) {
4203                        //prepareSetField(f, null);
4204
prepareUpdateField(f, null);
4205                    }
4206                }
4207            } catch (JDOException e) {
4208                // restore the jdoFlags.
4209
persistenceManager.setFlags(persistentObject, oldFlags);
4210                throw e;
4211            }
4212        } finally {
4213            persistenceManager.releaseShareLock();
4214            releaseLock();
4215
4216            if (debug) {
4217                logger.finer("sqlstore.sqlstatemanager.loadforupdate.exit"); // NOI18N
4218
}
4219        }
4220    }
4221
4222    /**
4223     * This method serves two purposes:
4224     * 1. If the field value is null or contains a non-SCOCollection instance, it
4225     * creates a new SCOCollection and populates with elements in c.
4226     * 2. If the field value is a SCOCollection instance, then if it is deferred,
4227     * it calls applyDeferredUpdates on the collection passing in c. Otherwise,
4228     * it clears the collection and repopulates with elements in c.
4229     */

4230    public synchronized void replaceCollection(ForeignFieldDesc ff, Collection c) {
4231        Collection collection = (Collection) ff.getValue(this);
4232
4233        SCOCollection scoCollection = null;
4234
4235        if ((collection == null) || !(collection instanceof SCO)) {
4236            scoCollection = (SCOCollection) persistenceManager.newCollectionInstanceInternal(
4237                    ff.getType(), persistentObject, ff.getName(),
4238                    ff.getComponentType(), false, ((c != null) ? c.size() : 0));
4239
4240            ff.setValue(this, scoCollection);
4241            scoCollection.addAllInternal(c);
4242        } else {
4243            scoCollection = (SCOCollection) collection;
4244
4245            if (scoCollection.isDeferred()) {
4246                scoCollection.applyDeferredUpdates(c);
4247
4248                // We need to mark all the tracked fields as present.
4249
ArrayList trackedFields = ff.getTrackedFields();
4250                if (trackedFields != null) {
4251                    for (int i = 0; i < trackedFields.size(); i++) {
4252                        ForeignFieldDesc tf = (ForeignFieldDesc) trackedFields.get(i);
4253
4254                        setPresenceMaskBit(tf.absoluteID);
4255                    }
4256                }
4257            } else {
4258                scoCollection.clearInternal();
4259                scoCollection.addAllInternal(c);
4260            }
4261        }
4262
4263        // Should not use old collection as SCO if any
4264
if (c != null && c instanceof SCO) {
4265            ((SCO) c).unsetOwner();
4266        }
4267    }
4268
4269    /**
4270     * For test purposes
4271     */

4272    protected LifeCycleState getCurrentState() {
4273        return state;
4274    }
4275
4276    // Status interrogation methods
4277
// For each one of these methods, there is a corresponding version
4278
// of it prefixed with jdo on the PersistenceCapable class. These
4279
// methods are used to query the state o an instance. For example,
4280
// when jdoIsReadReady is called on the PersistenceCapable
4281
// instance, the generated <code>jdoIsReadReady</code> will delegate the
4282
// status interrogation to the <code>StateManager</code> by call
4283
// <code>isReadReady()</code>.
4284

4285    /**
4286     * ...
4287     */

4288    public boolean isDirty() {
4289        if (state != null) {
4290            return state.isDirty();
4291        }
4292
4293        return false;
4294    }
4295
4296    /**
4297     * ...
4298     */

4299    public boolean isTransactional() {
4300        if (state != null) {
4301            return state.isTransactional();
4302        }
4303
4304        return false;
4305    }
4306
4307    /**
4308     * ...
4309     */

4310    public boolean isNew() {
4311        if (state != null) {
4312            return state.isNew();
4313        }
4314
4315        return false;
4316    }
4317
4318    /**
4319     * ...
4320     */

4321    public boolean isDeleted() {
4322        if (state != null) {
4323            return state.isDeleted();
4324        }
4325
4326        return false;
4327    }
4328
4329    /**
4330     * ...
4331     */

4332    public boolean isPersistent() {
4333        if (state != null) {
4334            return state.isPersistent();
4335        }
4336
4337        return false;
4338    }
4339
4340    /**
4341     * @inheritDoc
4342     */

4343    public boolean needsRegisterWithVersionConsistencyCache() {
4344        boolean rc = hasVersionConsistency();
4345        if (rc && state != null) {
4346            rc = state.isPersistent()
4347                    && state.isTransactional()
4348                    && !state.isNew()
4349                    && !state.isDirty()
4350                    && !state.isDeleted();
4351        }
4352
4353        return rc;
4354    }
4355
4356    /**
4357     * @inheritDoc
4358     */

4359    public boolean needsUpdateInVersionConsistencyCache() {
4360        boolean rc = hasVersionConsistency();
4361        if (rc && state != null) {
4362            rc = (state.isDirty()
4363                    || state.isNew()
4364                    || persistenceConfig.hasLocalNonDFGFields())
4365                 && !state.isDeleted();
4366        }
4367
4368        return rc;
4369    }
4370
4371    // Setter methods
4372
// These are methods for accessing the persistent field values
4373
// from the <code>StateManager</code>. The setter methods can also
4374
// serve as the hook for keeping track of changes made to the
4375
// <code>StateManager</code>.
4376

4377    public boolean setBooleanField(int fieldNumber, boolean value) {
4378        assertNotPK(fieldNumber);
4379        prepareSetField(fieldNumber, new Boolean JavaDoc(value));
4380        return value;
4381    }
4382
4383    public boolean[] setBooleanArrayField(int fieldNumber, boolean[] value) {
4384        prepareSetField(fieldNumber, null);
4385        return value;
4386    }
4387
4388    public byte setByteField(int fieldNumber, byte value) {
4389        assertNotPK(fieldNumber);
4390        prepareSetField(fieldNumber, new Byte JavaDoc(value));
4391        return value;
4392    }
4393
4394    public byte[] setByteArrayField(int fieldNumber, byte[] value) {
4395        prepareSetField(fieldNumber, null);
4396        return value;
4397    }
4398
4399    public short setShortField(int fieldNumber, short value) {
4400        assertNotPK(fieldNumber);
4401        prepareSetField(fieldNumber, new Short JavaDoc(value));
4402        return value;
4403    }
4404
4405    public short[] setShortArrayField(int fieldNumber, short[] value) {
4406        prepareSetField(fieldNumber, null);
4407        return value;
4408    }
4409
4410    public int setIntField(int fieldNumber, int value) {
4411        assertNotPK(fieldNumber);
4412        prepareSetField(fieldNumber, new Integer JavaDoc(value));
4413        return value;
4414    }
4415
4416    public int[] setIntArrayField(int fieldNumber, int[] value) {
4417        prepareSetField(fieldNumber, null);
4418        return value;
4419    }
4420
4421    public long setLongField(int fieldNumber, long value) {
4422        assertNotPK(fieldNumber);
4423        prepareSetField(fieldNumber, new Long JavaDoc(value));
4424        return value;
4425    }
4426
4427    public long[] setLongArrayField(int fieldNumber, long[] value) {
4428        prepareSetField(fieldNumber, null);
4429        return value;
4430    }
4431
4432    public char setCharField(int fieldNumber, char value) {
4433        assertNotPK(fieldNumber);
4434        prepareSetField(fieldNumber, null);
4435        return value;
4436    }
4437
4438    public char setCharArrayField(int fieldNumber, char value) {
4439        prepareSetField(fieldNumber, null);
4440        return value;
4441    }
4442
4443    public float setFloatField(int fieldNumber, float value) {
4444        assertNotPK(fieldNumber);
4445        prepareSetField(fieldNumber, new Float JavaDoc(value));
4446        return value;
4447    }
4448
4449    public float[] setFloatArrayField(int fieldNumber, float[] value) {
4450        prepareSetField(fieldNumber, null);
4451        return value;
4452    }
4453
4454    public double setDoubleField(int fieldNumber, double value) {
4455        assertNotPK(fieldNumber);
4456        prepareSetField(fieldNumber, new Double JavaDoc(value));
4457        return value;
4458    }
4459
4460    public double[] setDoubleArrayField(int fieldNumber, double[] value) {
4461        prepareSetField(fieldNumber, null);
4462        return value;
4463    }
4464
4465    public String JavaDoc setStringField(int fieldNumber, String JavaDoc value) {
4466        assertNotPK(fieldNumber);
4467        prepareSetField(fieldNumber, value);
4468        return value;
4469    }
4470
4471    public String JavaDoc[] setStringArrayField(int fieldNumber, String JavaDoc[] value) {
4472        prepareSetField(fieldNumber, null);
4473        return value;
4474    }
4475
4476    /**
4477     * This method sets object fields, e.g. relationship fields.
4478     */

4479    public Object JavaDoc setObjectField(int fieldNumber, Object JavaDoc value) {
4480        assertNotPK(fieldNumber);
4481        prepareSetField(fieldNumber, value);
4482        return value;
4483    }
4484
4485    public Object JavaDoc[] setObjectArrayField(int fieldNumber, Object JavaDoc[] value) {
4486        prepareSetField(fieldNumber, value);
4487        return value;
4488    }
4489
4490
4491    public boolean testIsLoaded(int fieldNumber) {
4492        return getPresenceMaskBit(fieldNumber);
4493    }
4494
4495    public boolean testIsLoaded(String JavaDoc fieldName) {
4496        FieldDesc f = persistenceConfig.getField(fieldName);
4497
4498        return testIsLoaded(f.absoluteID);
4499    }
4500
4501    public boolean testIsAutoPersistent() {
4502        return state.isAutoPersistent();
4503    }
4504
4505    /**
4506     * Marks this instance needs to require registering with the global (weak) cache
4507     * at rollback if it transitions to persistent state.
4508     * Used for replacing a deleted instance with the newly persistent with
4509     * the same object id.
4510     */

4511    public void markNotRegistered() {
4512        needsRegisterAtRollback = true;
4513    }
4514
4515    /**
4516     * Marks this instance as needs to be verified at the time it is removed from the
4517     * global (weak) cache at rollback if it transitions to transient state.
4518     */

4519    public void markVerifyAtDeregister() {
4520        needsVerifyAtDeregister = true;
4521    }
4522
4523    /**
4524     * Marks this instance as a replacement for a deleted instance with the same
4525     * ObjectId.
4526     */

4527    public void markReplacement() {
4528        isReplacementInstance = true;
4529    }
4530
4531    /**
4532     * Lock this instance.
4533     */

4534    // For consistency's sake, this should be changed to acquireLock.
4535
public void getLock() {
4536        lock.acquire();
4537    }
4538
4539    /**
4540     * Release lock.
4541     */

4542    public void releaseLock() {
4543        lock.release();
4544    }
4545
4546
4547    /**
4548     * Return value for valid flag.
4549     */

4550    public boolean isValid() {
4551        return valid;
4552    }
4553
4554    /**
4555     * Mark this StateManager as valid. Called before returning from
4556     * getObjectById.
4557     */

4558    public void setValid() {
4559        try {
4560            getLock();
4561            valid = true;
4562        } finally {
4563            releaseLock();
4564        }
4565    }
4566
4567    /**
4568     * This class stores a database dependency between the current and
4569     * a foreign state manager. To resolve dependencies before
4570     * commit/flush, remember the relationship field intorducing this
4571     * dependency.
4572     */

4573    private class UpdatedForeignReference {
4574        ForeignFieldDesc fieldDesc;
4575        SQLStateManager sm;
4576
4577        private UpdatedForeignReference(ForeignFieldDesc fieldDesc, SQLStateManager sm) {
4578            this.fieldDesc = fieldDesc;
4579            this.sm = sm;
4580        }
4581
4582        private ForeignFieldDesc getFieldDesc() {
4583            return fieldDesc;
4584        }
4585
4586        private SQLStateManager getStateManager() {
4587            return sm;
4588        }
4589
4590        public boolean equals(Object JavaDoc obj) {
4591            if (obj != null &&
4592                this.getClass().equals(obj.getClass())) {
4593                final UpdatedForeignReference other = (UpdatedForeignReference) obj;
4594
4595                return (this.fieldDesc == other.fieldDesc && this.sm == other.sm);
4596            }
4597            return (false);
4598        }
4599
4600        public int hashCode() {
4601            int hashCode = sm.hashCode();
4602            if (fieldDesc != null) {
4603                hashCode += fieldDesc.hashCode();
4604            }
4605            return hashCode;
4606        }
4607    }
4608}
4609
Popular Tags