1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.kernel;
20
21 import java.io.IOException;
22 import java.io.NotSerializableException;
23 import java.io.ObjectInputStream;
24 import java.io.ObjectOutput;
25 import java.io.ObjectOutputStream;
26 import java.io.Serializable;
27 import java.lang.reflect.Modifier;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.BitSet;
31 import java.util.Calendar;
32 import java.util.Comparator;
33 import java.util.Date;
34 import java.util.HashMap;
35 import java.util.Iterator;
36 import java.util.TimeZone;
37
38 import org.apache.commons.lang.StringUtils;
39 import org.apache.openjpa.conf.OpenJPAConfiguration;
40 import org.apache.openjpa.enhance.DynamicPersistenceCapable;
41 import org.apache.openjpa.enhance.FieldManager;
42 import org.apache.openjpa.enhance.ManagedInstanceProvider;
43 import org.apache.openjpa.enhance.PCRegistry;
44 import org.apache.openjpa.enhance.PersistenceCapable;
45 import org.apache.openjpa.enhance.RedefinitionHelper;
46 import org.apache.openjpa.enhance.StateManager;
47 import org.apache.openjpa.event.LifecycleEvent;
48 import org.apache.openjpa.event.LifecycleEventManager;
49 import org.apache.openjpa.lib.util.Localizer;
50 import org.apache.openjpa.meta.ClassMetaData;
51 import org.apache.openjpa.meta.FetchGroup;
52 import org.apache.openjpa.meta.FieldMetaData;
53 import org.apache.openjpa.meta.JavaTypes;
54 import org.apache.openjpa.meta.UpdateStrategies;
55 import org.apache.openjpa.meta.ValueMetaData;
56 import org.apache.openjpa.meta.ValueStrategies;
57 import org.apache.openjpa.util.ApplicationIds;
58 import org.apache.openjpa.util.Exceptions;
59 import org.apache.openjpa.util.ImplHelper;
60 import org.apache.openjpa.util.InternalException;
61 import org.apache.openjpa.util.InvalidStateException;
62 import org.apache.openjpa.util.ObjectNotFoundException;
63 import org.apache.openjpa.util.OpenJPAId;
64 import org.apache.openjpa.util.ProxyManager;
65 import org.apache.openjpa.util.RuntimeExceptionTranslator;
66 import org.apache.openjpa.util.UserException;
67 import serp.util.Numbers;
68
69 /**
70 * Implementation of the {@link OpenJPAStateManager} interface for use
71 * with this runtime. Each state manager manages the state of a single
72 * persistence capable instance. The state manager is also responsible for
73 * all communications about the instance to the {@link StoreManager}.
74 * The state manager uses the State pattern in both its interaction with
75 * the governed instance and its interaction with the broker.
76 * In its interactions with the persistence capable instance, it uses the
77 * {@link FieldManager} interface. Similarly, when interacting with the
78 * broker, it uses the {@link PCState} singleton that represents
79 * the current lifecycle state of the instance.
80 *
81 * @author Abe White
82 */
83 public class StateManagerImpl
84 implements OpenJPAStateManager, Serializable {
85
86 public static final int LOAD_FGS = 0;
87 public static final int LOAD_ALL = 1;
88 public static final int LOAD_SERIALIZE = 2;
89
90 private static final int FLAG_SAVE = 2 << 0;
91 private static final int FLAG_DEREF = 2 << 1;
92 private static final int FLAG_LOADED = 2 << 2;
93 private static final int FLAG_READ_LOCKED = 2 << 3;
94 private static final int FLAG_WRITE_LOCKED = 2 << 4;
95 private static final int FLAG_OID_ASSIGNED = 2 << 5;
96 private static final int FLAG_LOADING = 2 << 6;
97 private static final int FLAG_PRE_DELETING = 2 << 7;
98 private static final int FLAG_FLUSHED = 2 << 8;
99 private static final int FLAG_PRE_FLUSHED = 2 << 9;
100 private static final int FLAG_FLUSHED_DIRTY = 2 << 10;
101 private static final int FLAG_IMPL_CACHE = 2 << 11;
102 private static final int FLAG_INVERSES = 2 << 12;
103 private static final int FLAG_NO_UNPROXY = 2 << 13;
104 private static final int FLAG_VERSION_CHECK = 2 << 14;
105 private static final int FLAG_VERSION_UPDATE = 2 << 15;
106 private static final int FLAG_DETACHING = 2 << 16;
107
108 private static final Localizer _loc = Localizer.forPackage
109 (StateManagerImpl.class);
110
111 // information about the instance
112 private transient PersistenceCapable _pc = null;
113 private transient ClassMetaData _meta = null;
114 private BitSet _loaded = null;
115 private BitSet _dirty = null;
116 private BitSet _flush = null;
117 private int _flags = 0;
118
119 // id is the state manager identity; oid is the persistent identity. oid
120 // may be null for embedded and transient-transactional objects or new
121 // instances that haven't been assigned an oid. id is reassigned to oid
122 // on successful oid assignment (or flush completion if assignment is
123 // during flush)
124 private Object _id = null;
125 private Object _oid = null;
126
127 // the managing persistence manager and lifecycle state
128 private transient BrokerImpl _broker; // this is serialized specially
129 private PCState _state = PCState.TRANSIENT;
130
131 // the current and last loaded version indicators, and the lock object
132 private Object _version = null;
133 private Object _loadVersion = null;
134 private Object _lock = null;
135 private int _readLockLevel = -1;
136 private int _writeLockLevel = -1;
137
138 // delegates when providing/replacing instance data
139 private SingleFieldManager _single = null;
140 private SaveFieldManager _saved = null;
141 private FieldManager _fm = null;
142
143 // impldata; field impldata and intermediate data share the same array
144 private Object _impl = null;
145 private Object[] _fieldImpl = null;
146
147 // information about the owner of this instance, if it is embedded
148 private StateManagerImpl _owner = null;
149 private int _ownerIndex = -1;
150
151 /**
152 * Constructor; supply id, type metadata, and owning persistence manager.
153 */
154 protected StateManagerImpl(Object id, ClassMetaData meta,
155 BrokerImpl broker) {
156 _id = id;
157 _meta = meta;
158 _broker = broker;
159 _single = new SingleFieldManager(this, broker);
160
161 if (_meta.getIdentityType() == ClassMetaData.ID_UNKNOWN)
162 throw new UserException(_loc.get("meta-unknownid", _meta));
163 }
164
165 /**
166 * Set the owning state and field if this is an embedded instance.
167 */
168 void setOwner(StateManagerImpl owner, ValueMetaData ownerMeta) {
169 _owner = owner;
170 _ownerIndex = ownerMeta.getFieldMetaData().getIndex();
171 }
172
173 /**
174 * Whether this state manager is in the middle of a load.
175 */
176 boolean isLoading() {
177 return (_flags & FLAG_LOADING) > 0;
178 }
179
180 /**
181 * Whether this state manager is in the middle of a load initiated
182 * by outside code; for any internal methods that cause loading, the
183 * loading flag is set automatically.
184 */
185 void setLoading(boolean loading) {
186 if (loading)
187 _flags |= FLAG_LOADING;
188 else
189 _flags &= ~FLAG_LOADING;
190 }
191
192 /**
193 * Set or reset the lifecycle state of the managed instance. If the
194 * transactional state of the instance changes, it will be enlisted/
195 * delisted from the current transaction as necessary. The given
196 * state will be initialized after being set. If the given state
197 * is the same as the current state, this method will have no effect.
198 */
199 private void setPCState(PCState state) {
200 if (_state == state)
201 return;
202
203 lock();
204 try {
205 // notify the store manager that we're changing states; can veto
206 _broker.getStoreManager().beforeStateChange(this, _state, state);
207
208 // replace state
209 boolean wasDeleted = _state.isDeleted();
210 boolean wasDirty = _state.isDirty();
211 boolean wasPending = _state.isPendingTransactional();
212 _state = state;
213
214 // enlist/delist from transaction
215 if (_state.isTransactional()) {
216 _broker.addToTransaction(this);
217 if (_state.isDeleted() != wasDeleted)
218 _broker.setDirty(this, !wasDirty || isFlushed());
219 else if (_state.isDirty() && !wasDirty)
220 _broker.setDirty(this, true);
221 } else if (!wasPending && _state.isPendingTransactional())
222 _broker.addToPendingTransaction(this);
223 else if (wasPending && !_state.isPendingTransactional())
224 _broker.removeFromPendingTransaction(this);
225 else
226 _broker.removeFromTransaction(this);
227
228 // initialize
229 _state.initialize(this);
230 if (_state.isDeleted() && !wasDeleted)
231 fireLifecycleEvent(LifecycleEvent.AFTER_DELETE);
232 } finally {
233 unlock();
234 }
235 }
236
237 //////////////////////////////////////
238 // OpenJPAStateManager implementation
239 //////////////////////////////////////
240
241 public void initialize(Class cls, PCState state) {
242 // check to see if our current object id instance is the
243 // correct id type for the specified class; this is for cases
244 // when we have an application id hierarchy and we had set the
245 // metadata to a superclass id -- the subclass' id may be a
246 // different class, so we need to reset it
247 if (_meta.getDescribedType() != cls) {
248 ClassMetaData sub = _meta.getRepository().getMetaData
249 (cls, _broker.getClassLoader(), true);
250 if (_oid != null) {
251 if (_meta.getIdentityType() == ClassMetaData.ID_DATASTORE)
252 _oid = _broker.getStoreManager().copyDataStoreId(_oid,
253 sub);
254 else if (_meta.isOpenJPAIdentity())
255 _oid = ApplicationIds.copy(_oid, sub);
256 else if (sub.getObjectIdType() != _meta.getObjectIdType()) {
257 Object[] pkFields = ApplicationIds.toPKValues(_oid, _meta);
258 _oid = ApplicationIds.fromPKValues(pkFields, sub);
259 }
260 }
261 _meta = sub;
262 }
263
264 PersistenceCapable inst = PCRegistry.newInstance(cls, this, _oid, true);
265 if (inst == null) {
266 // the instance was null: check to see if the instance is
267 // abstract (as can sometimes be the case when the
268 // class discriminator strategy is not configured correctly)
269 if (Modifier.isAbstract(cls.getModifiers()))
270 throw new UserException(_loc.get("instantiate-abstract",
271 cls.getName(), _oid));
272 throw new InternalException();
273 }
274
275 initialize(inst, state);
276 }
277
278 /**
279 * Initialize with the given instance and state.
280 */
281 protected void initialize(PersistenceCapable pc, PCState state) {
282 if (pc == null)
283 throw new UserException(_loc.get("init-null-pc", _meta));
284 if (pc.pcGetStateManager() != null && pc.pcGetStateManager() != this)
285 throw new UserException(_loc.get("init-sm-pc",
286 Exceptions.toString(pc))).setFailedObject(pc);
287 pc.pcReplaceStateManager(this);
288
289 FieldMetaData[] fmds = _meta.getFields();
290 _loaded = new BitSet(fmds.length);
291 _flush = new BitSet(fmds.length);
292 _dirty = new BitSet(fmds.length);
293
294 for (int i = 0; i < fmds.length; i++) {
295 // mark primary key and non-persistent fields as loaded
296 if (fmds[i].isPrimaryKey()
297 || fmds[i].getManagement() != fmds[i].MANAGE_PERSISTENT)
298 _loaded.set(i);
299
300 // record whether there are any managed inverse fields
301 if (_broker.getInverseManager() != null
302 && fmds[i].getInverseMetaDatas().length > 0)
303 _flags |= FLAG_INVERSES;
304 }
305
306 pc.pcSetDetachedState(null);
307 _pc = pc;
308
309 if (_oid instanceof OpenJPAId)
310 ((OpenJPAId) _oid).setManagedInstanceType(_meta.getDescribedType());
311
312 // initialize our state and add ourselves to the broker's cache
313 setPCState(state);
314 _broker.setStateManager(_id, this, BrokerImpl.STATUS_INIT);
315 if (state == PCState.PNEW)
316 fireLifecycleEvent(LifecycleEvent.AFTER_PERSIST);
317
318 // if this is a non-tracking PC, add a hard ref to the appropriate data
319 // sets and give it an opportunity to make a state snapshot.
320 if (!isIntercepting()) {
321 saveFields(true);
322 if (!isNew())
323 RedefinitionHelper.assignLazyLoadProxies(this);
324 }
325 }
326
327 /**
328 * Whether or not data access in this instance is intercepted. This differs
329 * from {@link ClassMetaData#isIntercepting()} in that it checks for
330 * property access + subclassing in addition to the redefinition /
331 * enhancement checks.
332 *
333 * @since 1.0.0
334 */
335 public boolean isIntercepting() {
336 if (getMetaData().isIntercepting())
337 return true;
338 if (getMetaData().getAccessType() != ClassMetaData.ACCESS_FIELD
339 && _pc instanceof DynamicPersistenceCapable)
340 return true;
341
342 return false;
343 }
344
345 /**
346 * Fire the given lifecycle event to all listeners.
347 */
348 private boolean fireLifecycleEvent(int type) {
349 return _broker.fireLifecycleEvent(getManagedInstance(), null,
350 _meta, type);
351 }
352
353 public void load(FetchConfiguration fetch) {
354 load(fetch, LOAD_FGS, null, null, false);
355 }
356
357 /**
358 * Load the state of this instance based on the given fetch configuration
359 * and load mode. Return true if any data was loaded, false otherwise.
360 */
361 protected boolean load(FetchConfiguration fetch, int loadMode,
362 BitSet exclude, Object sdata, boolean forWrite) {
363 if (!forWrite && (!isPersistent() || isNew() || isDeleted()))
364 return false;
365
366 // if any fields being loaded, do state transitions for read
367 BitSet fields = getUnloadedInternal(fetch, loadMode, exclude);
368 boolean active = _broker.isActive();
369 if (!forWrite && fields != null)
370 beforeRead(-1);
371
372 // call load even if no fields are being loaded, because it takes
373 // care of checking if the DFG is loaded, making sure version info
374 // is loaded, etc
375 int lockLevel = calculateLockLevel(active, forWrite, fetch);
376 boolean ret = loadFields(fields, fetch, lockLevel, sdata);
377 obtainLocks(active, forWrite, lockLevel, fetch, sdata);
378 return ret;
379 }
380
381 public Object getManagedInstance() {
382 if (_pc instanceof ManagedInstanceProvider)
383 return ((ManagedInstanceProvider) _pc).getManagedInstance();
384 else
385 return _pc;
386 }
387
388 public PersistenceCapable getPersistenceCapable() {
389 return _pc;
390 }
391
392 public ClassMetaData getMetaData() {
393 return _meta;
394 }
395
396 public OpenJPAStateManager getOwner() {
397 return _owner;
398 }
399
400 public int getOwnerIndex() {
401 return _ownerIndex;
402 }
403
404 public boolean isEmbedded() {
405 return _owner != null;
406 }
407
408 public boolean isFlushed() {
409 return (_flags & FLAG_FLUSHED) > 0;
410 }
411
412 public boolean isFlushedDirty() {
413 return (_flags & FLAG_FLUSHED_DIRTY) > 0;
414 }
415
416 public BitSet getLoaded() {
417 return _loaded;
418 }
419
420 public BitSet getFlushed() {
421 return _flush;
422 }
423
424 public BitSet getDirty() {
425 return _dirty;
426 }
427
428 public BitSet getUnloaded(FetchConfiguration fetch) {
429 // collect fields to load from data store based on fetch configuration
430 BitSet fields = getUnloadedInternal(fetch, LOAD_FGS, null);
431 return (fields == null) ? new BitSet(0) : fields;
432 }
433
434 /**
435 * Internal version of {@link OpenJPAStateManager#getUnloaded} that avoids
436 * creating an empty bit set by returning null when there are no unloaded
437 * fields.
438 */
439 private BitSet getUnloadedInternal(FetchConfiguration fetch, int mode,
440 BitSet exclude) {
441 if (exclude == StoreContext.EXCLUDE_ALL)
442 return null;
443
444 BitSet fields = null;
445 FieldMetaData[] fmds = _meta.getFields();
446 boolean load;
447 for (int i = 0; i < fmds.length; i++) {
448 if (_loaded.get(i) || (exclude != null && exclude.get(i)))
449 continue;
450
451 switch (mode) {
452 case LOAD_SERIALIZE:
453 load = !fmds[i].isTransient();
454 break;
455 case LOAD_FGS:
456 load = fetch == null || fetch.requiresFetch(fmds[i])
457 != FetchConfiguration.FETCH_NONE;
458 break;
459 default: // LOAD_ALL
460 load = true;
461 }
462
463 if (load) {
464 if (fields == null)
465 fields = new BitSet(fmds.length);
466 fields.set(i);
467 }
468 }
469 return fields;
470 }
471
472 public StoreContext getContext() {
473 return _broker;
474 }
475
476 /**
477 * Managing broker.
478 */
479 BrokerImpl getBroker() {
480 return _broker;
481 }
482
483 public Object getId() {
484 return _id;
485 }
486
487 public Object getObjectId() {
488 StateManagerImpl sm = this;
489 while (sm.getOwner() != null)
490 sm = (StateManagerImpl) sm.getOwner();
491 return sm._oid;
492 }
493
494 public void setObjectId(Object oid) {
495 _oid = oid;
496 if (_pc != null && oid instanceof OpenJPAId)
497 ((OpenJPAId) oid).setManagedInstanceType(_meta.getDescribedType());
498 }
499
500 public boolean assignObjectId(boolean flush) {
501 lock();
502 try {
503 return assignObjectId(flush, false);
504 } finally {
505 unlock();
506 }
507 }
508
509 /**
510 * Ask store manager to assign our oid, optionally flushing and
511 * optionally recaching on the new oid.
512 */
513 private boolean assignObjectId(boolean flush, boolean preFlushing) {
514 if (_oid != null || isEmbedded() || !isPersistent())
515 return true;
516
517 if (_broker.getStoreManager().assignObjectId(this, preFlushing)) {
518 if (!preFlushing)
519 assertObjectIdAssigned(true);
520 } else if (flush)
521 _broker.flush();
522 else
523 return false;
524 return true;
525 }
526
527 /**
528 * Make sure we were assigned an oid, and perform actions to make it
529 * permanent.
530 *
531 * @param recache whether to recache ourself on the new oid
532 */
533 private void assertObjectIdAssigned(boolean recache) {
534 if (!isNew() || isDeleted() || isProvisional()
535 || (_flags & FLAG_OID_ASSIGNED) != 0)
536 return;
537 if (_oid == null) {
538 if (_meta.getIdentityType() == ClassMetaData.ID_DATASTORE)
539 throw new InternalException(Exceptions.toString
540 (getManagedInstance()));
541 _oid = ApplicationIds.create(_pc, _meta);
542 }
543
544 Object orig = _id;
545 _id = _oid;
546 if (recache) {
547 try {
548 _broker.setStateManager(orig, this,
549 BrokerImpl.STATUS_OID_ASSIGN);
550 } catch (RuntimeException re) {
551 _id = orig;
552 _oid = null;
553 throw re;
554 }
555 }
556 _flags |= FLAG_OID_ASSIGNED;
557 }
558
559 /**
560 * Assign the proper generated value to the given field based on its
561 * value-strategy.
562 */
563 private boolean assignField(int field, boolean preFlushing) {
564 OpenJPAStateManager sm = this;
565 while (sm.isEmbedded())
566 sm = sm.getOwner();
567 if (!sm.isNew() || sm.isFlushed() || sm.isDeleted())
568 return false;
569
570 // special-case oid fields, which require us to look inside the oid
571 // object
572 FieldMetaData fmd = _meta.getField(field);
573 if (fmd.getDeclaredTypeCode() == JavaTypes.OID) {
574 // try to shortcut if possible
575 if (_oid != null || isEmbedded() || !isPersistent())
576 return true;
577
578 // check embedded fields of oid for value strategy + default value
579 FieldMetaData[] pks = fmd.getEmbeddedMetaData().getFields();
580 OpenJPAStateManager oidsm = null;
581 boolean assign = false;
582 for (int i = 0; !assign && i < pks.length; i++) {
583 if (pks[i].getValueStrategy() == ValueStrategies.NONE)
584 continue;
585 if (oidsm == null)
586 oidsm = new ObjectIdStateManager(fetchObjectField(field),
587 this, fmd);
588 assign = oidsm.isDefaultValue(i);
589 }
590 return assign && assignObjectId(!preFlushing, preFlushing);
591 }
592
593 // Just return if there's no value generation strategy
594 if (fmd.getValueStrategy() == ValueStrategies.NONE)
595 return false;
596
597 // Throw exception if field already has a value assigned.
598 // @GeneratedValue overrides POJO initial values and setter methods
599 if (!fmd.isValueGenerated() && !isDefaultValue(field))
600 throw new InvalidStateException(_loc.get(
601 "existing-value-override-excep", fmd.getFullName(false)));
602
603 // for primary key fields, assign the object id and recache so that
604 // to the user, so it looks like the oid always matches the pk fields
605 if (fmd.isPrimaryKey() && !isEmbedded())
606 return assignObjectId(!preFlushing, preFlushing);
607
608 // for other fields just assign the field or flush if needed
609 if (_broker.getStoreManager().assignField(this, field, preFlushing)) {
610 fmd.setValueGenerated(true);
611 return true;
612 }
613 if (!preFlushing)
614 _broker.flush();
615 return !preFlushing;
616 }
617
618 public Object getLock() {
619 return _lock;
620 }
621
622 public void setLock(Object lock) {
623 _lock = lock;
624 }
625
626 public Object getVersion() {
627 return _version;
628 }
629
630 public void setVersion(Object version) {
631 _loadVersion = version;
632 assignVersionField(version);
633 }
634
635 Object getLoadVersion() {
636 return _loadVersion;
637 }
638
639 public void setNextVersion(Object version) {
640 assignVersionField(version);
641 }
642
643 private void assignVersionField(Object version) {
644 _version = version;
645 FieldMetaData vfield = _meta.getVersionField();
646 if (vfield != null)
647 store(vfield.getIndex(), JavaTypes.convert(version,
648 vfield.getTypeCode()));
649 }
650
651 public PCState getPCState() {
652 return _state;
653 }
654
655 public synchronized Object getImplData() {
656 return _impl;
657 }
658
659 public synchronized Object setImplData(Object data, boolean cacheable) {
660 Object old = _impl;
661 _impl = data;
662 if (cacheable && data != null)
663 _flags |= FLAG_IMPL_CACHE;
664 else
665 _flags &= ~FLAG_IMPL_CACHE;
666 return old;
667 }
668
669 public boolean isImplDataCacheable() {
670 return (_flags & FLAG_IMPL_CACHE) != 0;
671 }
672
673 public Object getImplData(int field) {
674 return getExtraFieldData(field, true);
675 }
676
677 public Object setImplData(int field, Object data) {
678 return setExtraFieldData(field, data, true);
679 }
680
681 public synchronized boolean isImplDataCacheable(int field) {
682 if (_fieldImpl == null || !_loaded.get(field))
683 return false;
684 if (_meta.getField(field).usesImplData() != null)
685 return false;
686 int idx = _meta.getExtraFieldDataIndex(field);
687 return idx != -1 && _fieldImpl[idx] != null;
688 }
689
690 public Object getIntermediate(int field) {
691 return getExtraFieldData(field, false);
692 }
693
694 public void setIntermediate(int field, Object data) {
695 setExtraFieldData(field, data, false);
696 }
697
698 /**
699 * Return the data from the proper index of the extra field data array.
700 */
701 private synchronized Object getExtraFieldData(int field, boolean isLoaded) {
702 // only return the field data if the field is in the right loaded
703 // state; otherwise we might return intermediate for impl data or
704 // vice versa
705 if (_fieldImpl == null || _loaded.get(field) != isLoaded)
706 return null;
707 int idx = _meta.getExtraFieldDataIndex(field);
708 return (idx == -1) ? null : _fieldImpl[idx];
709 }
710
711 /**
712 * Set the data from to proper index of the extra field data array.
713 */
714 private synchronized Object setExtraFieldData(int field, Object data,
715 boolean loaded) {
716 int idx = _meta.getExtraFieldDataIndex(field);
717 if (idx == -1)
718 throw new InternalException(String.valueOf(_meta.getField(field)));
719
720 Object old = (_fieldImpl == null) ? null : _fieldImpl[idx];
721 if (data != null) {
722 // cannot set if field in wrong loaded state
723 if (_loaded.get(field) != loaded)
724 throw new InternalException(String.valueOf(_meta.getField
725 (field)));
726
727 // set data
728 if (_fieldImpl == null)
729 _fieldImpl = new Object[_meta.getExtraFieldDataLength()];
730 _fieldImpl[idx] = data;
731 } else if (_fieldImpl != null && _loaded.get(field) == loaded)
732 _fieldImpl[idx] = null;
733 return old;
734 }
735
736 public Object fetch(int field) {
737 Object val = fetchField(field, false);
738 return _meta.getField(field).getExternalValue(val, _broker);
739 }
740
741 public Object fetchField(int field, boolean transitions) {
742 FieldMetaData fmd = _meta.getField(field);
743 if (fmd == null)
744 throw new UserException(_loc.get("no-field",
745 String.valueOf(field), getManagedInstance().getClass())).
746 setFailedObject(getManagedInstance());
747
748 // do normal state transitions
749 if (!fmd.isPrimaryKey() && transitions)
750 accessingField(field);
751
752 switch (fmd.getDeclaredTypeCode()) {
753 case JavaTypes.STRING:
754 return fetchStringField(field);
755 case JavaTypes.OBJECT:
756 return fetchObjectField(field);
757 case JavaTypes.BOOLEAN:
758 return (fetchBooleanField(field)) ? Boolean.TRUE
759 : Boolean.FALSE;
760 case JavaTypes.BYTE:
761 return new Byte(fetchByteField(field));
762 case JavaTypes.CHAR:
763 return new Character(fetchCharField(field));
764 case JavaTypes.DOUBLE:
765 return new Double(fetchDoubleField(field));
766 case JavaTypes.FLOAT:
767 return new Float(fetchFloatField(field));
768 case JavaTypes.INT:
769 return Numbers.valueOf(fetchIntField(field));
770 case JavaTypes.LONG:
771 return Numbers.valueOf(fetchLongField(field));
772 case JavaTypes.SHORT:
773 return new Short(fetchShortField(field));
774 default:
775 return fetchObjectField(field);
776 }
777 }
778
779 public void store(int field, Object val) {
780 val = _meta.getField(field).getFieldValue(val, _broker);
781 storeField(field, val);
782 }
783
784 public void storeField(int field, Object val) {
785 storeField(field, val, this);
786 }
787
788 /**
789 * <p>Checks whether or not <code>_pc</code> is dirty. In the cases where
790 * field tracking is not happening (see below), this method will do a
791 * state comparison to find whether <code>_pc</code> is dirty, and will
792 * update this instance with this information. In the cases where field
793 * tracking is happening, this method is a no-op.</p>
794 *
795 * <p>Fields are tracked for all classes that are run through the OpenJPA
796 * enhancer prior to or during deployment, and all classes (enhanced or
797 * unenhanced) in a Java 6 environment or newer.</p>
798 *
799 * <p>In a Java 5 VM or older:
800 * <br>- instances of unenhanced classes that use
801 * property access and obey the property access limitations are tracked
802 * when the instances are loaded from the database by OpenJPA, and are
803 * not tracked when the instances are created by application code.
804 * <br>- instances of unenhanced classes that use field access are
805 * never tracked.</p>
806 *
807 * @since 1.0.0
808 */
809 public void dirtyCheck() {
810 if (!needsDirtyCheck())
811 return;
812
813 SaveFieldManager saved = getSaveFieldManager();
814 if (saved == null)
815 throw new InternalException(_loc.get("no-saved-fields",
816 getMetaData().getDescribedType().getName()));
817
818 FieldMetaData[] fmds = getMetaData().getFields();
819 for (int i = 0; i < fmds.length; i++) {
820 // pk and version fields cannot be mutated; don't mark them
821 // as such. ##### validate?
822 if (!fmds[i].isPrimaryKey() && !fmds[i].isVersion()
823 && _loaded.get(i)) {
824 if (!saved.isFieldEqual(i, fetch(i))) {
825 dirty(i);
826 }
827 }
828 }
829 }
830
831 private boolean needsDirtyCheck() {
832 if (isIntercepting())
833 return false;
834 if (isDeleted())
835 return false;
836 if (isNew() && !isFlushed())
837 return false;
838 return true;
839 }
840
841 public Object fetchInitialField(int field) {
842 FieldMetaData fmd = _meta.getField(field);
843 if (_broker.getRestoreState() == RestoreState.RESTORE_NONE
844 && ((_flags & FLAG_INVERSES) == 0
845 || fmd.getInverseMetaDatas().length == 0))
846 throw new InvalidStateException(_loc.get("restore-unset"));
847
848 switch (fmd.getDeclaredTypeCode()) {
849 case JavaTypes.DATE:
850 case JavaTypes.CALENDAR:
851 case JavaTypes.ARRAY:
852 case JavaTypes.COLLECTION:
853 case JavaTypes.MAP:
854 case JavaTypes.OBJECT:
855 // if we're not saving mutable types, throw an exception
856 if (_broker.getRestoreState() != RestoreState.RESTORE_ALL
857 && ((_flags & FLAG_INVERSES) == 0
858 || fmd.getInverseMetaDatas().length == 0))
859 throw new InvalidStateException(_loc.get
860 ("mutable-restore-unset"));
861 }
862
863 lock();
864 try {
865 if (_saved == null || !_loaded.get(field) || !_dirty.get(field))
866 return fetchField(field, false);
867
868 // if the field is dirty but we never loaded it, we can't restore it
869 if (_saved.getUnloaded().get(field))
870 throw new InvalidStateException(_loc.get("initial-unloaded",
871 fmd));
872
873 provideField(_saved.getState(), _single, field);
874 return fetchField(_single, fmd);
875 } finally {
876 unlock();
877 }
878 }
879
880 /**
881 * Fetch the specified field from the specified field manager, wrapping it
882 * in an object if it's a primitive. A field should be provided to the
883 * field manager before this call is made.
884 */
885 private static Object fetchField(FieldManager fm, FieldMetaData fmd) {
886 int field = fmd.getIndex();
887 switch (fmd.getDeclaredTypeCode()) {
888 case JavaTypes.BOOLEAN:
889 return (fm.fetchBooleanField(field)) ? Boolean.TRUE
890 : Boolean.FALSE;
891 case JavaTypes.BYTE:
892 return new Byte(fm.fetchByteField(field));
893 case JavaTypes.CHAR:
894 return new Character(fm.fetchCharField(field));
895 case JavaTypes.DOUBLE:
896 return new Double(fm.fetchDoubleField(field));
897 case JavaTypes.FLOAT:
898 return new Float(fm.fetchFloatField(field));
899 case JavaTypes.INT:
900 return Numbers.valueOf(fm.fetchIntField(field));
901 case JavaTypes.LONG:
902 return Numbers.valueOf(fm.fetchLongField(field));
903 case JavaTypes.SHORT:
904 return new Short(fm.fetchShortField(field));
905 case JavaTypes.STRING:
906 return fm.fetchStringField(field);
907 default:
908 return fm.fetchObjectField(field);
909 }
910 }
911
912 public void setRemote(int field, Object value) {
913 lock();
914 try {
915 Boolean stat = dirty(field, Boolean.FALSE, false);
916 storeField(field, value, _single);
917 replaceField(_pc, _single, field);
918 postDirty(stat);
919 } finally {
920 unlock();
921 }
922 }
923
924 ////////////////////////
925 // Lifecycle operations
926 ////////////////////////
927
928 /**
929 * Notification that the object is about to be accessed.
930 *
931 * @param field the field number being read, or -1 if not a single
932 * field read
933 */
934 void beforeRead(int field) {
935 // allow unmediated reads of primary key fields
936 if (field != -1 && _meta.getField(field).isPrimaryKey())
937 return;
938
939 if (_broker.isActive() && !_broker.isTransactionEnding()) {
940 if (_broker.getOptimistic())
941 setPCState(_state.beforeOptimisticRead(this, field));
942 else
943 setPCState(_state.beforeRead(this, field));
944 } else if (_broker.getNontransactionalRead())
945 setPCState(_state.beforeNontransactionalRead(this, field));
946 else
947 throw new InvalidStateException(_loc.get("non-trans-read")).
948 setFailedObject(getManagedInstance());
949 }
950
951 /**
952 * Delegates to the current state.
953 *
954 * @see PCState#beforeFlush
955 */
956 void beforeFlush(int reason, OpCallbacks call) {
957 _state.beforeFlush(this, reason == BrokerImpl.FLUSH_LOGICAL, call);
958 }
959
960 /**
961 * Delegates to the current state.
962 *
963 * @see PCState#flush
964 */
965 void afterFlush(int reason) {
966 // nothing happens when we flush non-persistent states
967 if (!isPersistent())
968 return;
969
970 if (reason != BrokerImpl.FLUSH_ROLLBACK
971 && reason != BrokerImpl.FLUSH_LOGICAL) {
972 // analyze previous state for later
973 boolean wasNew = isNew();
974 boolean wasFlushed = isFlushed();
975 boolean wasDeleted = isDeleted();
976
977 // all dirty fields were flushed
978 _flush.or(_dirty);
979
980 // important to set flushed bit after calling _state.flush so
981 // that the state can tell whether this is the first flush
982 setPCState(_state.flush(this));
983 _flags |= FLAG_FLUSHED;
984 _flags &= ~FLAG_FLUSHED_DIRTY;
985
986 _flags &= ~FLAG_VERSION_CHECK;
987 _flags &= ~FLAG_VERSION_UPDATE;
988
989 // if this was an inc flush during which we had our identity
990 // assigned, tell the broker to cache us under our final oid
991 if (reason == BrokerImpl.FLUSH_INC)
992 assertObjectIdAssigned(true);
993
994 // if this object was stored with preFlush, do post-store callback
995 if ((_flags & FLAG_PRE_FLUSHED) > 0)
996 fireLifecycleEvent(LifecycleEvent.AFTER_STORE);
997
998 // do post-update as needed
999 if (wasNew && !wasFlushed)
1000 fireLifecycleEvent(LifecycleEvent.AFTER_PERSIST_PERFORMED);
1001 else if (wasDeleted)
1002 fireLifecycleEvent(LifecycleEvent.AFTER_DELETE_PERFORMED);
1003 else
1004 // updates and new-flushed with changes
1005 fireLifecycleEvent(LifecycleEvent.AFTER_UPDATE_PERFORMED);
1006 } else if (reason == BrokerImpl.FLUSH_ROLLBACK) {
1007 // revert to last loaded version and original oid
1008 assignVersionField(_loadVersion);
1009 if (isNew() && (_flags & FLAG_OID_ASSIGNED) == 0)
1010 _oid = null;
1011 }
1012 _flags &= ~FLAG_PRE_FLUSHED;
1013 }
1014
1015 /**
1016 * Delegates to the current state after checking the value
1017 * of the RetainState flag.
1018 *
1019 * @see PCState#commit
1020 * @see PCState#commitRetain
1021 */
1022 void commit() {
1023 // release locks before oid updated
1024 releaseLocks();
1025
1026 // update version and oid information
1027 setVersion(_version);
1028 _flags &= ~FLAG_FLUSHED;
1029 _flags &= ~FLAG_FLUSHED_DIRTY;
1030
1031 Object orig = _id;
1032 assertObjectIdAssigned(false);
1033
1034 boolean wasNew = isNew() && !isDeleted() && !isProvisional();
1035 if (_broker.getRetainState())
1036 setPCState(_state.commitRetain(this));
1037 else
1038 setPCState(_state.commit(this));
1039
1040 // ask the broker to re-cache us if we were new previously
1041 if (wasNew)
1042 _broker.setStateManager(orig, this, BrokerImpl.STATUS_COMMIT_NEW);
1043 }
1044
1045 /**
1046 * Delegates to the current state after checking the value
1047 * of the RetainState flag.
1048 *
1049 * @see PCState#rollback
1050 * @see PCState#rollbackRestore
1051 */
1052 void rollback() {
1053 // release locks
1054 releaseLocks();
1055 _flags &= ~FLAG_FLUSHED;
1056 _flags &= ~FLAG_FLUSHED_DIRTY;
1057 afterFlush(BrokerImpl.FLUSH_ROLLBACK);
1058
1059 if (_broker.getRestoreState() != RestoreState.RESTORE_NONE)
1060 setPCState(_state.rollbackRestore(this));
1061 else
1062 setPCState(_state.rollback(this));
1063 }
1064
1065 /**
1066 * Rollback state of the managed instance to the given savepoint.
1067 */
1068 void rollbackToSavepoint(SavepointFieldManager savepoint) {
1069 _state = savepoint.getPCState();
1070 BitSet loaded = savepoint.getLoaded();
1071 for (int i = 0, len = loaded.length(); i < len; i++) {
1072 if (loaded.get(i) && savepoint.restoreField(i)) {
1073 provideField(savepoint.getCopy(), savepoint, i);
1074 replaceField(_pc, savepoint, i);
1075 }
1076 }
1077 _loaded = loaded;
1078 _dirty = savepoint.getDirty();
1079 _flush = savepoint.getFlushed();
1080 _version = savepoint.getVersion();
1081 _loadVersion = savepoint.getLoadVersion();
1082 }
1083
1084 /**
1085 * Delegates to the current state.
1086 *
1087 * @see PCState#persist
1088 * @see Broker#persist
1089 */
1090 void persist() {
1091 setPCState(_state.persist(this));
1092 }
1093
1094 /**
1095 * Delegates to the current state.
1096 *
1097 * @see PCState#delete
1098 * @see Broker#delete
1099 */
1100 void delete() {
1101 setPCState(_state.delete(this));
1102 }
1103
1104 /**
1105 * Delegates to the current state.
1106 *
1107 * @see PCState#nontransactional
1108 * @see Broker#nontransactional
1109 */
1110 void nontransactional() {
1111 setPCState(_state.nontransactional(this));
1112 }
1113
1114 /**
1115 * Delegates to the current state.
1116 *
1117 * @see PCState#transactional
1118 * @see Broker#transactional
1119 */
1120 void transactional() {
1121 setPCState(_state.transactional(this));
1122 }
1123
1124 /**
1125 * Delegates to the current state.
1126 *
1127 * @see PCState#nonprovisional
1128 */
1129 void nonprovisional(boolean logical, OpCallbacks call) {
1130 setPCState(_state.nonprovisional(this, logical, call));
1131 }
1132
1133 /**
1134 * Delegates to the current state.
1135 *
1136 * @see PCState#release
1137 * @see Broker#release
1138 */
1139 void release(boolean unproxy) {
1140 release(unproxy, false);
1141 }
1142
1143 void release(boolean unproxy, boolean force) {
1144 // optimization for detach-in-place special case when fields are
1145 // already (un)proxied correctly
1146 if (!unproxy)
1147 _flags |= FLAG_NO_UNPROXY;
1148 try {
1149 if (force)
1150 setPCState(PCState.TRANSIENT);
1151 else
1152 setPCState(_state.release(this));
1153 } finally {
1154 _flags &= ~FLAG_NO_UNPROXY;
1155 }
1156 }
1157
1158 /**
1159 * Delegates to the current state.
1160 *
1161 * @see PCState#evict
1162 * @see Broker#evict
1163 */
1164 void evict() {
1165 setPCState(_state.evict(this));
1166 }
1167
1168 /**
1169 * Gather relations reachable from values using
1170 * {@link ValueMetaData#CASCADE_IMMEDIATE}.
1171 */
1172 void gatherCascadeRefresh(OpCallbacks call) {
1173 FieldMetaData[] fmds = _meta.getFields();
1174 for (int i = 0; i < fmds.length; i++) {
1175 if (!_loaded.get(i))
1176 continue;
1177
1178 if (fmds[i].getCascadeRefresh() == ValueMetaData.CASCADE_IMMEDIATE
1179 || fmds[i].getKey().getCascadeRefresh()
1180 == ValueMetaData.CASCADE_IMMEDIATE
1181 || fmds[i].getElement().getCascadeRefresh()
1182 == ValueMetaData.CASCADE_IMMEDIATE) {
1183 _single.storeObjectField(i, fetchField(i, false));
1184 _single.gatherCascadeRefresh(call);
1185 _single.clear();
1186 }
1187 }
1188 }
1189
1190 public boolean beforeRefresh(boolean refreshAll) {
1191 // note: all logic placed here rather than in the states for
1192 // optimization; this method public b/c used by remote package
1193
1194 // nothing to do for non persistent or new unflushed instances
1195 if (!isPersistent() || (isNew() && !isFlushed()))
1196 return false;
1197
1198 lock();
1199 try {
1200 // if dirty need to clear fields
1201 if (isDirty()) {
1202 clearFields();
1203 return true;
1204 }
1205
1206 // if some fields have been loaded but the instance is out of
1207 // date or this is part of a refreshAll() and we don't want to
1208 // take the extra hit to see if the instance is out of date, clear
1209 if (_loaded.length() > 0 && (refreshAll || isEmbedded()
1210 || !syncVersion(null))) {
1211 Object version = _version;
1212 clearFields();
1213
1214 // if syncVersion just replaced the version, reset it
1215 if (!refreshAll && !isEmbedded())
1216 setVersion(version);
1217 return true;
1218 }
1219 return false;
1220 } finally {
1221 unlock();
1222 }
1223 }
1224
1225 /**
1226 * Perform state transitions after refresh. This method is only
1227 * called if {@link #beforeRefresh} returns true.
1228 */
1229 void afterRefresh() {
1230 lock();
1231 try {
1232 // transition to clean or nontransactional depending on trans status
1233 if (!_broker.isActive())
1234 setPCState(_state.afterNontransactionalRefresh());
1235 else if (_broker.getOptimistic())
1236 setPCState(_state.afterOptimisticRefresh());
1237 else
1238 setPCState(_state.afterRefresh());
1239 } finally {
1240 unlock();
1241 }
1242 }
1243
1244 /**
1245 * Mark this object as a dereferenced dependent object.
1246 */
1247 void setDereferencedDependent(boolean deref, boolean notify) {
1248 if (!deref && (_flags & FLAG_DEREF) > 0) {
1249 if (notify)
1250 _broker.removeDereferencedDependent(this);
1251 _flags &= ~FLAG_DEREF;
1252 } else if (deref && (_flags & FLAG_DEREF) == 0) {
1253 _flags |= FLAG_DEREF;
1254 if (notify)
1255 _broker.addDereferencedDependent(this);
1256 }
1257 }
1258
1259 ///////////
1260 // Locking
1261 ///////////
1262
1263 /**
1264 * Notification that we've been read-locked. Pass in the level at which
1265 * we were locked and the level at which we should write lock ourselves
1266 * on dirty.
1267 */
1268 void readLocked(int readLockLevel, int writeLockLevel) {
1269 // make sure object is added to transaction so lock will get
1270 // cleared on commit/rollback
1271 if (readLockLevel != LockLevels.LOCK_NONE)
1272 transactional();
1273
1274 _readLockLevel = readLockLevel;
1275 _writeLockLevel = writeLockLevel;
1276 _flags |= FLAG_READ_LOCKED;
1277 _flags &= ~FLAG_WRITE_LOCKED;
1278 }
1279
1280 /**
1281 * Return the lock level to use when loading state.
1282 */
1283 private int calculateLockLevel(boolean active, boolean forWrite,
1284 FetchConfiguration fetch) {
1285 if (!active)
1286 return LockLevels.LOCK_NONE;
1287 if (fetch == null)
1288 fetch = _broker.getFetchConfiguration();
1289
1290 if (_readLockLevel == -1)
1291 _readLockLevel = fetch.getReadLockLevel();
1292 if (_writeLockLevel == -1)
1293 _writeLockLevel = fetch.getWriteLockLevel();
1294 return (forWrite) ? _writeLockLevel : _readLockLevel;
1295 }
1296
1297 /**
1298 * Make sure we're locked at the given level.
1299 */
1300 private void obtainLocks(boolean active, boolean forWrite, int lockLevel,
1301 FetchConfiguration fetch, Object sdata) {
1302 if (!active)
1303 return;
1304
1305 // if we haven't been locked yet, lock now at the given level
1306 int flag = (forWrite) ? FLAG_WRITE_LOCKED : FLAG_READ_LOCKED;
1307 if ((_flags & flag) == 0) {
1308 // make sure object is added to transaction so lock will get
1309 // cleared on commit/rollback
1310 if (lockLevel != LockLevels.LOCK_NONE)
1311 transactional();
1312
1313 if (fetch == null)
1314 fetch = _broker.getFetchConfiguration();
1315 _broker.getLockManager().lock(this, lockLevel,
1316 fetch.getLockTimeout(), sdata);
1317 _flags |= FLAG_READ_LOCKED;
1318 _flags |= flag;
1319 }
1320 }
1321
1322 /**
1323 * Release locks.
1324 */
1325 private void releaseLocks() {
1326 if (_lock != null)
1327 _broker.getLockManager().release(this);
1328 _readLockLevel = -1;
1329 _writeLockLevel = -1;
1330 _flags &= ~FLAG_READ_LOCKED;
1331 _flags &= ~FLAG_WRITE_LOCKED;
1332 }
1333
1334 ////////////////////////////////////////////
1335 // Implementation of StateManager interface
1336 ////////////////////////////////////////////
1337
1338 /**
1339 * @return whether or not unloaded fields should be closed.
1340 */
1341 public boolean serializing() {
1342 // if the broker is in the midst of a serialization, then no special
1343 // handling should be performed on the instance, and no subsequent
1344 // load should happen
1345 if (_broker.isSerializing())
1346 return false;
1347
1348 try {
1349 if (_meta.isDetachable())
1350 return DetachManager.preSerialize(this);
1351
1352 load(_broker.getFetchConfiguration(), LOAD_SERIALIZE, null, null,
1353 false);
1354 return false;
1355 } catch (RuntimeException re) {
1356 throw translate(re);
1357 }
1358 }
1359
1360 public boolean writeDetached(ObjectOutput out)
1361 throws IOException {
1362 BitSet idxs = new BitSet(_meta.getFields().length);
1363 lock();
1364 try {
1365 boolean detsm = DetachManager.writeDetachedState(this, out, idxs);
1366 if (detsm)
1367 _flags |= FLAG_DETACHING;
1368
1369 FieldMetaData[] fmds = _meta.getFields();
1370 for (int i = 0; i < fmds.length; i++) {
1371 if (fmds[i].isTransient())
1372 continue;
1373 provideField(_pc, _single, i);
1374 _single.serialize(out, !idxs.get(i));
1375 _single.clear();
1376 }
1377 return true;
1378 } catch (RuntimeException re) {
1379 throw translate(re);
1380 } finally {
1381 _flags &= ~FLAG_DETACHING;
1382 unlock();
1383 }
1384 }
1385
1386 public void proxyDetachedDeserialized(int idx) {
1387 // we don't serialize state manager impls
1388 throw new InternalException();
1389 }
1390
1391 public boolean isTransactional() {
1392 // special case for TCLEAN, which we want to appear non-trans to
1393 // internal code, but which publicly should be transactional
1394 return _state == PCState.TCLEAN || _state.isTransactional();
1395 }
1396
1397 public boolean isPendingTransactional() {
1398 return _state.isPendingTransactional();
1399 }
1400
1401 public boolean isProvisional() {
1402 return _state.isProvisional();
1403 }
1404
1405 public boolean isPersistent() {
1406 return _state.isPersistent();
1407 }
1408
1409 public boolean isNew() {
1410 return _state.isNew();
1411 }
1412
1413 public boolean isDeleted() {
1414 return _state.isDeleted();
1415 }
1416
1417 public boolean isDirty() {
1418 return _state.isDirty();
1419 }
1420
1421 public boolean isDetached() {
1422 return (_flags & FLAG_DETACHING) != 0;
1423 }
1424
1425 public Object getGenericContext() {
1426 return _broker;
1427 }
1428
1429 public Object fetchObjectId() {
1430 try {
1431 assignObjectId(true);
1432 if (_oid == null || !_broker.getConfiguration().
1433 getCompatibilityInstance().getCopyObjectIds())
1434 return _oid;
1435
1436 if (_meta.getIdentityType() == ClassMetaData.ID_DATASTORE)
1437 return _broker.getStoreManager().copyDataStoreId(_oid, _meta);
1438 return ApplicationIds.copy(_oid, _meta);
1439 } catch (RuntimeException re) {
1440 throw translate(re);
1441 }
1442 }
1443
1444 public Object getPCPrimaryKey(Object oid, int field) {
1445 FieldMetaData fmd = _meta.getField(field);
1446 Object pk = ApplicationIds.get(oid, fmd);
1447 if (pk == null)
1448 return null;
1449
1450 ClassMetaData relmeta = fmd.getDeclaredTypeMetaData();
1451 if (relmeta.getIdentityType() == ClassMetaData.ID_DATASTORE
1452 && fmd.getObjectIdFieldTypeCode() == JavaTypes.LONG)
1453 pk = _broker.getStoreManager().newDataStoreId(pk, relmeta);
1454 else if (relmeta.getIdentityType() == ClassMetaData.ID_APPLICATION
1455 && fmd.getObjectIdFieldType() != relmeta.getObjectIdType())
1456 pk = ApplicationIds.fromPKValues(new Object[] { pk }, relmeta);
1457 return _broker.find(pk, false, null);
1458 }
1459
1460 public byte replaceFlags() {
1461 // we always use load required so that we can detect when objects
1462 // are touched for locking or making transactional
1463 return PersistenceCapable.LOAD_REQUIRED;
1464 }
1465
1466 public StateManager replaceStateManager(StateManager sm) {
1467 return sm;
1468 }
1469
1470 public void accessingField(int field) {
1471 // possibly change state
1472 try {
1473 beforeRead(field);
1474 beforeAccessField(field);
1475 } catch (RuntimeException re) {
1476 throw translate(re);
1477 }
1478 }
1479
1480 /**
1481 * Load the given field before access.
1482 */
1483 protected void beforeAccessField(int field) {
1484 lock();
1485 try {
1486 boolean active = _broker.isActive();
1487 int lockLevel = calculateLockLevel(active, false, null);
1488 if (!_loaded.get(field))
1489 loadField(field, lockLevel, false, true);
1490 else
1491 assignField(field, false);
1492 obtainLocks(active, false, lockLevel, null, null);
1493 } catch (RuntimeException re) {
1494 throw translate(re);
1495 } finally {
1496 unlock();
1497 }
1498 }
1499
1500 public void dirty(String field) {
1501 FieldMetaData fmd = _meta.getField(field);
1502 if (fmd == null)
1503 throw translate(new UserException(_loc.get("no-field", field,
1504 ImplHelper.getManagedInstance(_pc).getClass()))
1505 .setFailedObject(getManagedInstance()));
1506
1507 dirty(fmd.getIndex(), null, true);
1508 }
1509
1510 public void dirty(int field) {
1511 dirty(field, null, true);
1512 }
1513
1514 /**
1515 * Make the given field dirty.
1516 *
1517 * @param mutate if null, may be an SCO mutation; if true, is certainly
1518 * a mutation (or at least treat as one)
1519 * @return {@link Boolean#FALSE} if this instance was already dirty,
1520 * <code>null</code> if it was dirty but not since flush, and
1521 * {@link Boolean#TRUE} if it was not dirty
1522 */
1523 private Boolean dirty(int field, Boolean mutate, boolean loadFetchGroup) {
1524 boolean locked = false;
1525 boolean newFlush = false;
1526 boolean clean = false;
1527 try {
1528 FieldMetaData fmd = _meta.getField(field);
1529 if (!isNew() || isFlushed()) {
1530 if (fmd.getUpdateStrategy() == UpdateStrategies.RESTRICT)
1531 throw new InvalidStateException(_loc.get
1532 ("update-restrict", fmd));
1533 if (fmd.getUpdateStrategy() == UpdateStrategies.IGNORE)
1534 return Boolean.FALSE;
1535 }
1536
1537 if (isEmbedded()) {
1538 // notify owner of change
1539 _owner.dirty(_ownerIndex, Boolean.TRUE, loadFetchGroup);
1540 }
1541
1542 // is this a direct mutation of an sco field?
1543 if (mutate == null) {
1544 switch (fmd.getDeclaredTypeCode()) {
1545 case JavaTypes.COLLECTION:
1546 case JavaTypes.MAP:
1547 case JavaTypes.ARRAY:
1548 case JavaTypes.DATE:
1549 case JavaTypes.CALENDAR:
1550 case JavaTypes.OBJECT:
1551 mutate = Boolean.TRUE;
1552 break;
1553 case JavaTypes.PC:
1554 mutate =
1555 (fmd.isEmbedded()) ? Boolean.TRUE : Boolean.FALSE;
1556 break;
1557 default:
1558 mutate = Boolean.FALSE; // not sco
1559 }
1560 }
1561
1562 // possibly change state
1563 boolean active = _broker.isActive();
1564 clean = !_state.isDirty(); // intentional direct access
1565
1566 // fire event fast before state change.
1567 if (clean)
1568 fireLifecycleEvent(LifecycleEvent.BEFORE_DIRTY);
1569 if (active) {
1570 if (_broker.getOptimistic())
1571 setPCState(_state.beforeOptimisticWrite(this, field,
1572 mutate.booleanValue()));
1573 else
1574 setPCState(_state.beforeWrite(this, field,
1575 mutate.booleanValue()));
1576 } else if (fmd.getManagement() == FieldMetaData.MANAGE_PERSISTENT) {
1577 if (isPersistent() && !_broker.getNontransactionalWrite())
1578 throw new InvalidStateException(_loc.get
1579 ("non-trans-write")).setFailedObject
1580 (getManagedInstance());
1581
1582 setPCState(_state.beforeNontransactionalWrite(this, field,
1583 mutate.booleanValue()));
1584 }
1585
1586 if ((_flags & FLAG_FLUSHED) != 0) {
1587 newFlush = (_flags & FLAG_FLUSHED_DIRTY) == 0;
1588 _flags |= FLAG_FLUSHED_DIRTY;
1589 }
1590
1591 lock();
1592 locked = true;
1593
1594 // note that the field is in need of flushing again, and tell the
1595 // broker too
1596 _flush.clear(field);
1597 _broker.setDirty(this, newFlush && !clean);
1598
1599 // save the field for rollback if needed
1600 saveField(field);
1601
1602 // dirty the field and mark loaded; load fetch group if needed
1603 int lockLevel = calculateLockLevel(active, true, null);
1604 if (!_dirty.get(field)) {
1605 setLoaded(field, true);
1606 _dirty.set(field);
1607
1608 // make sure the field's fetch group is loaded
1609 if (loadFetchGroup && isPersistent()
1610 && fmd.getManagement() == fmd.MANAGE_PERSISTENT)
1611 loadField(field, lockLevel, true, true);
1612 }
1613 obtainLocks(active, true, lockLevel, null, null);
1614 } catch (RuntimeException re) {
1615 throw translate(re);
1616 } finally {
1617 if (locked)
1618 unlock();
1619 }
1620
1621 if (clean)
1622 return Boolean.TRUE;
1623 if (newFlush) {
1624 // this event can be fired later cause we're already dirty.
1625 fireLifecycleEvent(LifecycleEvent.BEFORE_DIRTY_FLUSHED);
1626 return null;
1627 }
1628 return Boolean.FALSE;
1629 }
1630
1631 /**
1632 * Fire post-dirty events after field value changes.
1633 *
1634 * @param status return value from {@link #dirty(int, Boolean, boolean)}
1635 */
1636 private void postDirty(Boolean status) {
1637 if (Boolean.TRUE.equals(status))
1638 fireLifecycleEvent(LifecycleEvent.AFTER_DIRTY);
1639 else if (status == null)
1640 fireLifecycleEvent(LifecycleEvent.AFTER_DIRTY_FLUSHED);
1641 }
1642
1643 public void removed(int field, Object removed, boolean key) {
1644 if (removed == null)
1645 return;
1646
1647 try {
1648 // dereference dependent fields, delete embedded
1649 FieldMetaData fmd = _meta.getField(field);
1650 ValueMetaData vmd = (key) ? fmd.getKey() : fmd.getElement();
1651 if (vmd.isEmbeddedPC())
1652 _single.delete(vmd, removed, null);
1653 else if (vmd.getCascadeDelete() == ValueMetaData.CASCADE_AUTO)
1654 _single.dereferenceDependent(removed);
1655 } catch (RuntimeException re) {
1656 throw translate(re);
1657 }
1658 }
1659
1660 public Object newProxy(int field) {
1661 FieldMetaData fmd = _meta.getField(field);
1662 if (!fmd.isExternalized())
1663 return newFieldProxy(field);
1664
1665 switch (fmd.getTypeCode()) {
1666 case JavaTypes.DATE:
1667 if (fmd.getDeclaredType() == java.sql.Date.class)
1668 return new java.sql.Date(System.currentTimeMillis());
1669 if (fmd.getDeclaredType() == java.sql.Timestamp.class)
1670 return new java.sql.Timestamp(System.currentTimeMillis());
1671 if (fmd.getDeclaredType() == java.sql.Time.class)
1672 return new java.sql.Time(System.currentTimeMillis());
1673 return new Date();
1674 case JavaTypes.CALENDAR:
1675 return Calendar.getInstance();
1676 case JavaTypes.COLLECTION:
1677 return new ArrayList();
1678 case JavaTypes.MAP:
1679 return new HashMap();
1680 }
1681 return null;
1682 }
1683
1684 public Object newFieldProxy(int field) {
1685 FieldMetaData fmd = _meta.getField(field);
1686 ProxyManager mgr = _broker.getConfiguration().
1687 getProxyManagerInstance();
1688 Object init = fmd.getInitializer();
1689
1690 switch (fmd.getDeclaredTypeCode()) {
1691 case JavaTypes.DATE:
1692 return mgr.newDateProxy(fmd.getDeclaredType());
1693 case JavaTypes.CALENDAR:
1694 return mgr.newCalendarProxy(fmd.getDeclaredType(),
1695 init instanceof TimeZone ? (TimeZone) init : null);
1696 case JavaTypes.COLLECTION:
1697 return mgr.newCollectionProxy(fmd.getProxyType(),
1698 fmd.getElement().getDeclaredType(),
1699 init instanceof Comparator ? (Comparator) init : null);
1700 case JavaTypes.MAP:
1701 return mgr.newMapProxy(fmd.getProxyType(),
1702 fmd.getKey().getDeclaredType(),
1703 fmd.getElement().getDeclaredType(),
1704 init instanceof Comparator ? (Comparator) init : null);
1705 }
1706 return null;
1707 }
1708
1709 public boolean isDefaultValue(int field) {
1710 lock();
1711 try {
1712 _single.clear();
1713 provideField(_pc, _single, field);
1714 boolean ret = _single.isDefaultValue();
1715 _single.clear();
1716 return ret;
1717 } finally {
1718 unlock();
1719 }
1720 }
1721
1722 /////////////////////////////////////////////////////////
1723 // Record that the field is dirty (which might load DFG)
1724 /////////////////////////////////////////////////////////
1725
1726 public void settingBooleanField(PersistenceCapable pc, int field,
1727 boolean curVal, boolean newVal, int set) {
1728 if (set != SET_REMOTE) {
1729 if (newVal == curVal && _loaded.get(field))
1730 return;
1731 assertNoPrimaryKeyChange(field);
1732 }
1733
1734 lock();
1735 try {
1736 Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
1737 _single.storeBooleanField(field, newVal);
1738 replaceField(pc, _single, field);
1739 postDirty(stat);
1740 } finally {
1741 unlock();
1742 }
1743 }
1744
1745 public void settingByteField(PersistenceCapable pc, int field,
1746 byte curVal, byte newVal, int set) {
1747 if (set != SET_REMOTE) {
1748 if (newVal == curVal && _loaded.get(field))
1749 return;
1750 assertNoPrimaryKeyChange(field);
1751 }
1752
1753 lock();
1754 try {
1755 Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
1756 _single.storeByteField(field, newVal);
1757 replaceField(pc, _single, field);
1758 postDirty(stat);
1759 } finally {
1760 unlock();
1761 }
1762 }
1763
1764 public void settingCharField(PersistenceCapable pc, int field,
1765 char curVal, char newVal, int set) {
1766 if (set != SET_REMOTE) {
1767 if (newVal == curVal && _loaded.get(field))
1768 return;
1769 assertNoPrimaryKeyChange(field);
1770 }
1771
1772 lock();
1773 try {
1774 Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
1775 _single.storeCharField(field, newVal);
1776 replaceField(pc, _single, field);
1777 postDirty(stat);
1778 } finally {
1779 unlock();
1780 }
1781 }
1782
1783 public void settingDoubleField(PersistenceCapable pc, int field,
1784 double curVal, double newVal, int set) {
1785 if (set != SET_REMOTE) {
1786 if (newVal == curVal && _loaded.get(field))
1787 return;
1788 assertNoPrimaryKeyChange(field);
1789 }
1790
1791 lock();
1792 try {
1793 Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
1794 _single.storeDoubleField(field, newVal);
1795 replaceField(pc, _single, field);
1796 postDirty(stat);
1797 } finally {
1798 unlock();
1799 }
1800 }
1801
1802 public void settingFloatField(PersistenceCapable pc, int field,
1803 float curVal, float newVal, int set) {
1804 if (set != SET_REMOTE) {
1805 if (newVal == curVal && _loaded.get(field))
1806 return;
1807 assertNoPrimaryKeyChange(field);
1808 }
1809
1810 lock();
1811 try {
1812 Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
1813 _single.storeFloatField(field, newVal);
1814 replaceField(pc, _single, field);
1815 postDirty(stat);
1816 } finally {
1817 unlock();
1818 }
1819 }
1820
1821 public void settingIntField(PersistenceCapable pc, int field,
1822 int curVal, int newVal, int set) {
1823 if (set != SET_REMOTE) {
1824 if (newVal == curVal && _loaded.get(field))
1825 return;
1826 assertNoPrimaryKeyChange(field);
1827 }
1828
1829 lock();
1830 try {
1831 Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
1832 _single.storeIntField(field, newVal);
1833 replaceField(pc, _single, field);
1834 postDirty(stat);
1835 } finally {
1836 unlock();
1837 }
1838 }
1839
1840 public void settingLongField(PersistenceCapable pc, int field,
1841 long curVal, long newVal, int set) {
1842 if (set != SET_REMOTE) {
1843 if (newVal == curVal && _loaded.get(field))
1844 return;
1845 assertNoPrimaryKeyChange(field);
1846 }
1847
1848 lock();
1849 try {
1850 Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
1851 _single.storeLongField(field, newVal);
1852 replaceField(pc, _single, field);
1853 postDirty(stat);
1854 } finally {
1855 unlock();
1856 }
1857 }
1858
1859 public void settingObjectField(PersistenceCapable pc, int field,
1860 Object curVal, Object newVal, int set) {
1861 if (set != SET_REMOTE) {
1862 FieldMetaData fmd = _meta.getField(field);
1863 if (_loaded.get(field)) {
1864 if (newVal == curVal)
1865 return;
1866
1867 // only compare new to old values if the comparison is going to
1868 // be cheap -- don't compare collections, maps, UDTs
1869 switch (fmd.getDeclaredTypeCode()) {
1870 case JavaTypes.ARRAY:
1871 case JavaTypes.COLLECTION:
1872 case JavaTypes.MAP:
1873 case JavaTypes.PC:
1874 case JavaTypes.PC_UNTYPED:
1875 break;
1876 default:
1877 if (newVal != null && newVal.equals(curVal))
1878 return;
1879 }
1880 } else {
1881 // if this is a dependent unloaded field, make sure to load
1882 // it now
1883 if (fmd.getCascadeDelete() == ValueMetaData.CASCADE_AUTO
1884 || fmd.getKey().getCascadeDelete()
1885 == ValueMetaData.CASCADE_AUTO
1886 || fmd.getElement().getCascadeDelete()
1887 == ValueMetaData.CASCADE_AUTO)
1888 curVal = fetchObjectField(field);
1889 }
1890
1891 assertNoPrimaryKeyChange(field);
1892 if (fmd.getDeclaredTypeCode() == JavaTypes.OID)
1893 assertNotManagedObjectId(newVal);
1894 }
1895
1896 lock();
1897 try {
1898 Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
1899 if (set != SET_REMOTE) {
1900 _single.storeObjectField(field, curVal);
1901 _single.unproxy();
1902 _single.dereferenceDependent();
1903 _single.clear();
1904 }
1905 _single.storeObjectField(field, newVal);
1906 replaceField(pc, _single, field);
1907 postDirty(stat);
1908 } finally {
1909 unlock();
1910 }
1911 }
1912
1913 public void settingShortField(PersistenceCapable pc, int field,
1914 short curVal, short newVal, int set) {
1915 if (set != SET_REMOTE) {
1916 if (newVal == curVal && _loaded.get(field))
1917 return;
1918 assertNoPrimaryKeyChange(field);
1919 }
1920
1921 lock();
1922 try {
1923 Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
1924 _single.storeShortField(field, newVal);
1925 replaceField(pc, _single, field);
1926 postDirty(stat);
1927 } finally {
1928 unlock();
1929 }
1930 }
1931
1932 public void settingStringField(PersistenceCapable pc, int field,
1933 String curVal, String newVal, int set) {
1934 if (set != SET_REMOTE) {
1935 if (StringUtils.equals(newVal, curVal) && _loaded.get(field))
1936 return;
1937 assertNoPrimaryKeyChange(field);
1938 }
1939
1940 lock();
1941 try {
1942 Boolean stat = dirty(field, Boolean.FALSE, set == SET_USER);
1943 _single.storeStringField(field, newVal);
1944 replaceField(pc, _single, field);
1945 postDirty(stat);
1946 } finally {
1947 unlock();
1948 }
1949 }
1950
1951 /**
1952 * Disallows changing primary key fields for instances.
1953 */
1954 private void assertNoPrimaryKeyChange(int field) {
1955 if (_oid != null && _meta.getField(field).isPrimaryKey())
1956 throw translate(new InvalidStateException(_loc.get
1957 ("change-identity")).setFailedObject(getManagedInstance()));
1958 }
1959
1960 /**
1961 * Disallows setting an object id field to a managed instance.
1962 */
1963 void assertNotManagedObjectId(Object val) {
1964 if (val != null
1965 && (ImplHelper.toPersistenceCapable(val,
1966 getContext().getConfiguration())).pcGetGenericContext()!= null)
1967 throw translate(new InvalidStateException(_loc.get
1968 ("managed-oid", Exceptions.toString(val),
1969 Exceptions.toString(getManagedInstance()))).
1970 setFailedObject(getManagedInstance()));
1971 }
1972
1973 ////////////////////////////
1974 // Delegate to FieldManager
1975 ////////////////////////////
1976
1977 public void providedBooleanField(PersistenceCapable pc, int field,
1978 boolean curVal) {
1979 _fm.storeBooleanField(field, curVal);
1980 }
1981
1982 public void providedByteField(PersistenceCapable pc, int field,
1983 byte curVal) {
1984 _fm.storeByteField(field, curVal);
1985 }
1986
1987 public void providedCharField(PersistenceCapable pc, int field,
1988 char curVal) {
1989 _fm.storeCharField(field, curVal);
1990 }
1991
1992 public void providedDoubleField(PersistenceCapable pc, int field,
1993 double curVal) {
1994 _fm.storeDoubleField(field, curVal);
1995 }
1996
1997 public void providedFloatField(PersistenceCapable pc, int field,
1998 float curVal) {
1999 _fm.storeFloatField(field, curVal);
2000 }
2001
2002 public void providedIntField(PersistenceCapable pc, int field,
2003 int curVal) {
2004 _fm.storeIntField(field, curVal);
2005 }
2006
2007 public void providedLongField(PersistenceCapable pc, int field,
2008 long curVal) {
2009 _fm.storeLongField(field, curVal);
2010 }
2011
2012 public void providedObjectField(PersistenceCapable pc, int field,
2013 Object curVal) {
2014 _fm.storeObjectField(field, curVal);
2015 }
2016
2017 public void providedShortField(PersistenceCapable pc, int field,
2018 short curVal) {
2019 _fm.storeShortField(field, curVal);
2020 }
2021
2022 public void providedStringField(PersistenceCapable pc, int field,
2023 String curVal) {
2024 _fm.storeStringField(field, curVal);
2025 }
2026
2027 public boolean replaceBooleanField(PersistenceCapable pc, int field) {
2028 return _fm.fetchBooleanField(field);
2029 }
2030
2031 public byte replaceByteField(PersistenceCapable pc, int field) {
2032 return _fm.fetchByteField(field);
2033 }
2034
2035 public char replaceCharField(PersistenceCapable pc, int field) {
2036 return _fm.fetchCharField(field);
2037 }
2038
2039 public double replaceDoubleField(PersistenceCapable pc, int field) {
2040 return _fm.fetchDoubleField(field);
2041 }
2042
2043 public float replaceFloatField(PersistenceCapable pc, int field) {
2044 return _fm.fetchFloatField(field);
2045 }
2046
2047 public int replaceIntField(PersistenceCapable pc, int field) {
2048 return _fm.fetchIntField(field);
2049 }
2050
2051 public long replaceLongField(PersistenceCapable pc, int field) {
2052 return _fm.fetchLongField(field);
2053 }
2054
2055 public Object replaceObjectField(PersistenceCapable pc, int field) {
2056 return _fm.fetchObjectField(field);
2057 }
2058
2059 public short replaceShortField(PersistenceCapable pc, int field) {
2060 return _fm.fetchShortField(field);
2061 }
2062
2063 public String replaceStringField(PersistenceCapable pc, int field) {
2064 return _fm.fetchStringField(field);
2065 }
2066
2067 //////////////////////////////////
2068 // Implementation of FieldManager
2069 //////////////////////////////////
2070
2071 public boolean fetchBoolean(int field) {
2072 FieldMetaData fmd = _meta.getField(field);
2073 if (!fmd.isExternalized())
2074 return fetchBooleanField(field);
2075
2076 Object val = fetchField(field, false);
2077 return ((Boolean) fmd.getExternalValue(val, _broker)).booleanValue();
2078 }
2079
2080 public boolean fetchBooleanField(int field) {
2081 lock();
2082 try {
2083 if (!_loaded.get(field))
2084 loadField(field, LockLevels.LOCK_NONE, false, false);
2085
2086 provideField(_pc, _single, field);
2087 return _single.fetchBooleanField(field);
2088 } finally {
2089 unlock();
2090 }
2091 }
2092
2093 public byte fetchByte(int field) {
2094 FieldMetaData fmd = _meta.getField(field);
2095 if (!fmd.isExternalized())
2096 return fetchByteField(field);
2097
2098 Object val = fetchField(field, false);
2099 return ((Number) fmd.getExternalValue(val, _broker)).byteValue();
2100 }
2101
2102 public byte fetchByteField(int field) {
2103 lock();
2104 try {
2105 if (!_loaded.get(field))
2106 loadField(field, LockLevels.LOCK_NONE, false, false);
2107
2108 provideField(_pc, _single, field);
2109 return _single.fetchByteField(field);
2110 } finally {
2111 unlock();
2112 }
2113 }
2114
2115 public char fetchChar(int field) {
2116 FieldMetaData fmd = _meta.getField(field);
2117 if (!fmd.isExternalized())
2118 return fetchCharField(field);
2119
2120 Object val = fetchField(field, false);
2121 return ((Character) fmd.getExternalValue(val, _broker)).charValue();
2122 }
2123
2124 public char fetchCharField(int field) {
2125 lock();
2126 try {
2127 if (!_loaded.get(field))
2128 loadField(field, LockLevels.LOCK_NONE, false, false);
2129
2130 provideField(_pc, _single, field);
2131 return _single.fetchCharField(field);
2132 } finally {
2133 unlock();
2134 }
2135 }
2136
2137 public double fetchDouble(int field) {
2138 FieldMetaData fmd = _meta.getField(field);
2139 if (!fmd.isExternalized())
2140 return fetchDoubleField(field);
2141
2142 Object val = fetchField(field, false);
2143 return ((Number) fmd.getExternalValue(val, _broker)).doubleValue();
2144 }
2145
2146 public double fetchDoubleField(int field) {
2147 lock();
2148 try {
2149 if (!_loaded.get(field))
2150 loadField(field, LockLevels.LOCK_NONE, false, false);
2151
2152 provideField(_pc, _single, field);
2153 return _single.fetchDoubleField(field);
2154 } finally {
2155 unlock();
2156 }
2157 }
2158
2159 public float fetchFloat(int field) {
2160 FieldMetaData fmd = _meta.getField(field);
2161 if (!fmd.isExternalized())
2162 return fetchFloatField(field);
2163
2164 Object val = fetchField(field, false);
2165 return ((Number) fmd.getExternalValue(val, _broker)).floatValue();
2166 }
2167
2168 public float fetchFloatField(int field) {
2169 lock();
2170 try {
2171 if (!_loaded.get(field))
2172 loadField(field, LockLevels.LOCK_NONE, false, false);
2173
2174 provideField(_pc, _single, field);
2175 return _single.fetchFloatField(field);
2176 } finally {
2177 unlock();
2178 }
2179 }
2180
2181 public int fetchInt(int field) {
2182 FieldMetaData fmd = _meta.getField(field);
2183 if (!fmd.isExternalized())
2184 return fetchIntField(field);
2185
2186 Object val = fetchField(field, false);
2187 return ((Number) fmd.getExternalValue(val, _broker)).intValue();
2188 }
2189
2190 public int fetchIntField(int field) {
2191 lock();
2192 try {
2193 if (!_loaded.get(field))
2194 loadField(field, LockLevels.LOCK_NONE, false, false);
2195
2196 provideField(_pc, _single, field);
2197 return _single.fetchIntField(field);
2198 } finally {
2199 unlock();
2200 }
2201 }
2202
2203 public long fetchLong(int field) {
2204 FieldMetaData fmd = _meta.getField(field);
2205 if (!fmd.isExternalized())
2206 return fetchLongField(field);
2207
2208 Object val = fetchField(field, false);
2209 return ((Number) fmd.getExternalValue(val, _broker)).longValue();
2210 }
2211
2212 public long fetchLongField(int field) {
2213 lock();
2214 try {
2215 if (!_loaded.get(field))
2216 loadField(field, LockLevels.LOCK_NONE, false, false);
2217
2218 provideField(_pc, _single, field);
2219 return _single.fetchLongField(field);
2220 } finally {
2221 unlock();
2222 }
2223 }
2224
2225 public Object fetchObject(int field) {
2226 FieldMetaData fmd = _meta.getField(field);
2227 if (!fmd.isExternalized())
2228 return fetchObjectField(field);
2229
2230 Object val = fetchField(field, false);
2231 return fmd.getExternalValue(val, _broker);
2232 }
2233
2234 public Object fetchObjectField(int field) {
2235 lock();
2236 try {
2237 if (!_loaded.get(field))
2238 loadField(field, LockLevels.LOCK_NONE, false, false);
2239
2240 provideField(_pc, _single, field);
2241 return _single.fetchObjectField(field);
2242 } finally {
2243 unlock();
2244 }
2245 }
2246
2247 public short fetchShort(int field) {
2248 FieldMetaData fmd = _meta.getField(field);
2249 if (!fmd.isExternalized())
2250 return fetchShortField(field);
2251
2252 Object val = fetchField(field, false);
2253 return ((Number) fmd.getExternalValue(val, _broker)).shortValue();
2254 }
2255
2256 public short fetchShortField(int field) {
2257 lock();
2258 try {
2259 if (!_loaded.get(field))
2260 loadField(field, LockLevels.LOCK_NONE, false, false);
2261
2262 provideField(_pc, _single, field);
2263 return _single.fetchShortField(field);
2264 } finally {
2265 unlock();
2266 }
2267 }
2268
2269 public String fetchString(int field) {
2270 FieldMetaData fmd = _meta.getField(field);
2271 if (!fmd.isExternalized())
2272 return fetchStringField(field);
2273
2274 Object val = fetchField(field, false);
2275 return (String) fmd.getExternalValue(val, _broker);
2276 }
2277
2278 public String fetchStringField(int field) {
2279 lock();
2280 try {
2281 if (!_loaded.get(field))
2282 loadField(field, LockLevels.LOCK_NONE, false, false);
2283
2284 provideField(_pc, _single, field);
2285 return _single.fetchStringField(field);
2286 } finally {
2287 unlock();
2288 }
2289 }
2290
2291 public void storeBoolean(int field, boolean externalVal) {
2292 FieldMetaData fmd = _meta.getField(field);
2293 if (!fmd.isExternalized())
2294 storeBooleanField(field, externalVal);
2295 else {
2296 Object val = (externalVal) ? Boolean.TRUE : Boolean.FALSE;
2297 storeField(field, fmd.getFieldValue(val, _broker));
2298 }
2299 }
2300
2301 public void storeBooleanField(int field, boolean curVal) {
2302 lock();
2303 try {
2304 _single.storeBooleanField(field, curVal);
2305 replaceField(_pc, _single, field);
2306 setLoaded(field, true);
2307 postLoad(field, null);
2308 } finally {
2309 unlock();
2310 }
2311 }
2312
2313 public void storeByte(int field, byte externalVal) {
2314 FieldMetaData fmd = _meta.getField(field);
2315 if (!fmd.isExternalized())
2316 storeByteField(field, externalVal);
2317 else
2318 storeField(field, fmd.getFieldValue(new Byte(externalVal),
2319 _broker));
2320 }
2321
2322 public void storeByteField(int field, byte curVal) {
2323 lock();
2324 try {
2325 _single.storeByteField(field, curVal);
2326 replaceField(_pc, _single, field);
2327 setLoaded(field, true);
2328 postLoad(field, null);
2329 } finally {
2330 unlock();
2331 }
2332 }
2333
2334 public void storeChar(int field, char externalVal) {
2335 FieldMetaData fmd = _meta.getField(field);
2336 if (!fmd.isExternalized())
2337 storeCharField(field, externalVal);
2338 else
2339 storeField(field, fmd.getFieldValue(new Character(externalVal),
2340 _broker));
2341 }
2342
2343 public void storeCharField(int field, char curVal) {
2344 lock();
2345 try {
2346 _single.storeCharField(field, curVal);
2347 replaceField(_pc, _single, field);
2348 setLoaded(field, true);
2349 postLoad(field, null);
2350 } finally {
2351 unlock();
2352 }
2353 }
2354
2355 public void storeDouble(int field, double externalVal) {
2356 FieldMetaData fmd = _meta.getField(field);
2357 if (!fmd.isExternalized())
2358 storeDoubleField(field, externalVal);
2359 else
2360 storeField(field, fmd.getFieldValue(new Double(externalVal),
2361 _broker));
2362 }
2363
2364 public void storeDoubleField(int field, double curVal) {
2365 lock();
2366 try {
2367 _single.storeDoubleField(field, curVal);
2368 replaceField(_pc, _single, field);
2369 setLoaded(field, true);
2370 postLoad(field, null);
2371 } finally {
2372 unlock();
2373 }
2374 }
2375
2376 public void storeFloat(int field, float externalVal) {
2377 FieldMetaData fmd = _meta.getField(field);
2378 if (!fmd.isExternalized())
2379 storeFloatField(field, externalVal);
2380 else
2381 storeField(field, fmd.getFieldValue(new Float(externalVal),
2382 _broker));
2383 }
2384
2385 public void storeFloatField(int field, float curVal) {
2386 lock();
2387 try {
2388 _single.storeFloatField(field, curVal);
2389 replaceField(_pc, _single, field);
2390 setLoaded(field, true);
2391 postLoad(field, null);
2392 } finally {
2393 unlock();
2394 }
2395 }
2396
2397 public void storeInt(int field, int externalVal) {
2398 FieldMetaData fmd = _meta.getField(field);
2399 if (!fmd.isExternalized())
2400 storeIntField(field, externalVal);
2401 else
2402 storeField(field, fmd.getFieldValue(Numbers.valueOf(externalVal),
2403 _broker));
2404 }
2405
2406 public void storeIntField(int field, int curVal) {
2407 lock();
2408 try {
2409 _single.storeIntField(field, curVal);
2410 replaceField(_pc, _single, field);
2411 setLoaded(field, true);
2412 postLoad(field, null);
2413 } finally {
2414 unlock();
2415 }
2416 }
2417
2418 public void storeLong(int field, long externalVal) {
2419 FieldMetaData fmd = _meta.getField(field);
2420 if (!fmd.isExternalized())
2421 storeLongField(field, externalVal);
2422 else
2423 storeField(field, fmd.getFieldValue(Numbers.valueOf(externalVal),
2424 _broker));
2425 }
2426
2427 public void storeLongField(int field, long curVal) {
2428 lock();
2429 try {
2430 _single.storeLongField(field, curVal);
2431 replaceField(_pc, _single, field);
2432 setLoaded(field, true);
2433 postLoad(field, null);
2434 } finally {
2435 unlock();
2436 }
2437 }
2438
2439 public void storeObject(int field, Object externalVal) {
2440 FieldMetaData fmd = _meta.getField(field);
2441 externalVal = fmd.order(externalVal);
2442 if (!fmd.isExternalized())
2443 storeObjectField(field, externalVal);
2444 else
2445 storeField(field, fmd.getFieldValue(externalVal, _broker));
2446 }
2447
2448 public void storeObjectField(int field, Object curVal) {
2449 lock();
2450 try {
2451 _single.storeObjectField(field, curVal);
2452 _single.proxy(true, false);
2453 replaceField(_pc, _single, field);
2454 setLoaded(field, true);
2455 postLoad(field, null);
2456 } finally {
2457 unlock();
2458 }
2459 }
2460
2461 public void storeShort(int field, short externalVal) {
2462 FieldMetaData fmd = _meta.getField(field);
2463 if (!fmd.isExternalized())
2464 storeShortField(field, externalVal);
2465 else
2466 storeField(field, fmd.getFieldValue(new Short(externalVal),
2467 _broker));
2468 }
2469
2470 public void storeShortField(int field, short curVal) {
2471 lock();
2472 try {
2473 _single.storeShortField(field, curVal);
2474 replaceField(_pc, _single, field);
2475 setLoaded(field, true);
2476 postLoad(field, null);
2477 } finally {
2478 unlock();
2479 }
2480 }
2481
2482 public void storeString(int field, String externalVal) {
2483 FieldMetaData fmd = _meta.getField(field);
2484 if (!fmd.isExternalized())
2485 storeStringField(field, externalVal);
2486 else
2487 storeField(field, fmd.getFieldValue(externalVal, _broker));
2488 }
2489
2490 public void storeStringField(int field, String curVal) {
2491 lock();
2492 try {
2493 _single.storeStringField(field, curVal);
2494 replaceField(_pc, _single, field);
2495 setLoaded(field, true);
2496 postLoad(field, null);
2497 } finally {
2498 unlock();
2499 }
2500 }
2501
2502 /**
2503 * Store the given field value into the given field manager.
2504 */
2505 private void storeField(int field, Object val, FieldManager fm) {
2506 FieldMetaData fmd = _meta.getField(field);
2507 if (fmd == null)
2508 throw new UserException(_loc.get("no-field-index",
2509 String.valueOf(field), _meta.getDescribedType())).
2510 setFailedObject(getManagedInstance());
2511
2512 switch (fmd.getDeclaredTypeCode()) {
2513 case JavaTypes.BOOLEAN:
2514 boolean bool = val != null && ((Boolean) val).booleanValue();
2515 fm.storeBooleanField(field, bool);
2516 break;
2517 case JavaTypes.BYTE:
2518 byte b = (val == null) ? 0 : ((Number) val).byteValue();
2519 fm.storeByteField(field, b);
2520 break;
2521 case JavaTypes.CHAR:
2522 char c = (val == null) ? 0 : ((Character) val).charValue();
2523 fm.storeCharField(field, c);
2524 break;
2525 case JavaTypes.DOUBLE:
2526 double d = (val == null) ? 0 : ((Number) val).doubleValue();
2527 fm.storeDoubleField(field, d);
2528 break;
2529 case JavaTypes.FLOAT:
2530 float f = (val == null) ? 0 : ((Number) val).floatValue();
2531 fm.storeFloatField(field, f);
2532 break;
2533 case JavaTypes.INT:
2534 int i = (val == null) ? 0 : ((Number) val).intValue();
2535 fm.storeIntField(field, i);
2536 break;
2537 case JavaTypes.LONG:
2538 long l = (val == null) ? 0 : ((Number) val).longValue();
2539 fm.storeLongField(field, l);
2540 break;
2541 case JavaTypes.SHORT:
2542 short s = (val == null) ? 0 : ((Number) val).shortValue();
2543 fm.storeShortField(field, s);
2544 break;
2545 case JavaTypes.STRING:
2546 fm.storeStringField(field, (String) val);
2547 break;
2548 default:
2549 fm.storeObjectField(field, val);
2550 }
2551 }
2552
2553 /////////////
2554 // Utilities
2555 /////////////
2556
2557 /**
2558 * Erase the fact that this instance has been flushed.
2559 */
2560 void eraseFlush() {
2561 _flags &= ~FLAG_FLUSHED;
2562 _flags &= ~FLAG_FLUSHED_DIRTY;
2563
2564 int fmds = _meta.getFields().length;
2565 for (int i = 0; i < fmds; i++)
2566 _flush.clear(i);
2567 }
2568
2569 /**
2570 * Records that all instance fields are/are not loaded.
2571 * Primary key and non-persistent fields are not affected.
2572 */
2573 void setLoaded(boolean val) {
2574 FieldMetaData[] fmds = _meta.getFields();
2575 for (int i = 0; i < fmds.length; i++) {
2576 if (!fmds[i].isPrimaryKey()
2577 && fmds[i].getManagement() == fmds[i].MANAGE_PERSISTENT)
2578 setLoaded(i, val);
2579 }
2580 if (!val) {
2581 _flags &= ~FLAG_LOADED;
2582 setDirty(false);
2583 } else
2584 _flags |= FLAG_LOADED;
2585 }
2586
2587 /**
2588 * Records that all instance fields are/are not dirty,
2589 * and changes the flags of the instance accordingly.
2590 */
2591 void setDirty(boolean val) {
2592 FieldMetaData[] fmds = _meta.getFields();
2593 boolean update = !isNew() || isFlushed();
2594 for (int i = 0; i < fmds.length; i++) {
2595 if (val && (!update
2596 || fmds[i].getUpdateStrategy() != UpdateStrategies.IGNORE))
2597 _dirty.set(i);
2598 else if (!val) {
2599 // we never consider clean fields flushed; this also takes
2600 // care of clearing the flushed fields on commit/rollback
2601 _flush.clear(i);
2602 _dirty.clear(i);
2603 }
2604 }
2605
2606 if (val)
2607 _flags |= FLAG_LOADED;
2608 }
2609
2610 /**
2611 * Executes pre-clear callbacks, clears all managed fields, and calls the
2612 * {@link #setLoaded} method with a value of false. Primary key fields
2613 * are not cleared.
2614 */
2615 void clearFields() {
2616 if (!isIntercepting())
2617 return;
2618
2619 fireLifecycleEvent(LifecycleEvent.BEFORE_CLEAR);
2620
2621 // unproxy all fields
2622 unproxyFields();
2623
2624 lock();
2625 try {
2626 // clear non-pk fields
2627 FieldMetaData[] fmds = _meta.getFields();
2628 for (int i = 0; i < fmds.length; i++) {
2629 if (!fmds[i].isPrimaryKey() && fmds[i].getManagement()
2630 == FieldMetaData.MANAGE_PERSISTENT)
2631 replaceField(_pc, ClearFieldManager.getInstance(), i);
2632 }
2633
2634 // forget version info and impl data so we re-read next time
2635 setLoaded(false);
2636 _version = null;
2637 _loadVersion = null;
2638 if (_fieldImpl != null)
2639 Arrays.fill(_fieldImpl, null);
2640 } finally {
2641 unlock();
2642 }
2643
2644 fireLifecycleEvent(LifecycleEvent.AFTER_CLEAR);
2645 }
2646
2647 /**
2648 * Record that we should save any fields that change from this point
2649 * forward.
2650 */
2651 void saveFields(boolean immediate) {
2652 if (_broker.getRestoreState() == RestoreState.RESTORE_NONE
2653 && (_flags & FLAG_INVERSES) == 0)
2654 return;
2655
2656 _flags |= FLAG_SAVE;
2657 if (immediate) {
2658 for (int i = 0, len = _loaded.length(); i < len; i++)
2659 saveField(i);
2660 _flags &= ~FLAG_SAVE;
2661 }
2662 }
2663
2664 /**
2665 * If the field isn't already saved, saves the currently loaded field
2666 * state of the instance. The saved values can all be restored via
2667 * {@link #restoreFields}.
2668 */
2669 private void saveField(int field) {
2670 if ((_flags & FLAG_SAVE) == 0)
2671 return;
2672
2673 // if this is a managed inverse field, load it so we're sure to have
2674 // the original value
2675 if (!_loaded.get(field) && ((_flags & FLAG_INVERSES) != 0
2676 && _meta.getField(field).getInverseMetaDatas().length > 0))
2677 loadField(field, LockLevels.LOCK_NONE, false, false);
2678
2679 // don't bother creating the save field manager if we're not going to
2680 // save the old field value anyway
2681 if (_saved == null) {
2682 if (_loaded.get(field))
2683 _saved = new SaveFieldManager(this, null, _dirty);
2684 else
2685 return;
2686 }
2687
2688 // copy the field to save field manager; if the field is not directly
2689 // copyable, immediately provide and replace it via the save field
2690 // manager, which will copy the mutable value to prevent by-ref mods
2691 if (_saved.saveField(field)) {
2692 provideField(_pc, _saved, field);
2693 replaceField(_saved.getState(), _saved, field);
2694 }
2695 }
2696
2697 /**
2698 * Notification that the state will not need to be rolled back
2699 * to that of the last call to {@link #saveFields}.
2700 */
2701 void clearSavedFields() {
2702 if (isIntercepting()) {
2703 _flags &= ~FLAG_SAVE;
2704 _saved = null;
2705 }
2706 }
2707
2708 public SaveFieldManager getSaveFieldManager() {
2709 return _saved;
2710 }
2711
2712 /**
2713 * Rollback the state of the instance to the saved state from the
2714 * last call to {@link #saveFields}, or to default values if never saved.
2715 */
2716 void restoreFields() {
2717 lock();
2718 try {
2719 if (_saved == null) {
2720 if ((_flags & FLAG_SAVE) == 0)
2721 clearFields();
2722 else // only unloaded fields were dirtied
2723 _loaded.andNot(_loaded);
2724 }
2725 // we direct state transitions based on our own getRestoreState
2726 // method, but to decide whether to actually rollback field
2727 // values, we consult the broker for the user's setting
2728 else if (_broker.getRestoreState() != RestoreState.RESTORE_NONE) {
2729 // rollback all currently-loaded fields
2730 for (int i = 0, len = _loaded.length(); i < len; i++)
2731 if (_loaded.get(i) && _saved.restoreField(i))
2732 replaceField(_pc, _saved, i);
2733
2734 // rollback loaded set
2735 _loaded.andNot(_saved.getUnloaded());
2736 }
2737 }
2738 finally {
2739 unlock();
2740 }
2741 }
2742
2743 /**
2744 * Replaces all second class object fields with fresh proxied instances
2745 * containing the same information as the originals.
2746 */
2747 void proxyFields(boolean reset, boolean replaceNull) {
2748 // we only replace nulls if the runtime can't differentiate between
2749 // null and empty containers. we replace nulls in this case to
2750 // maintain consistency whether values are being retained or not
2751 if (replaceNull)
2752 replaceNull = !_broker.getConfiguration().supportedOptions().
2753 contains(OpenJPAConfiguration.OPTION_NULL_CONTAINER);
2754
2755 lock();
2756 try {
2757 for (int i = 0, len = _loaded.length(); i < len; i++) {
2758 if (_loaded.get(i)) {
2759 provideField(_pc, _single, i);
2760 if (_single.proxy(reset, replaceNull))
2761 replaceField(_pc, _single, i);
2762 else
2763 _single.clear();
2764 }
2765 }
2766 } finally {
2767 unlock();
2768 }
2769 }
2770
2771 /**
2772 * Unproxy all fields.
2773 */
2774 void unproxyFields() {
2775 if ((_flags & FLAG_NO_UNPROXY) != 0)
2776 return;
2777
2778 lock();
2779 try {
2780 for (int i = 0, len = _loaded.length(); i < len; i++) {
2781 provideField(_pc, _single, i);
2782 _single.unproxy();
2783 _single.releaseEmbedded();
2784 _single.clear();
2785 }
2786 }
2787 finally {
2788 unlock();
2789 }
2790 }
2791
2792 /**
2793 * Get ready for a flush. Persists all persistence-capable object fields,
2794 * and checks for illegal null values. Also assigns oids and field values
2795 * for all strategies that don't require flushing.
2796 */
2797 void preFlush(boolean logical, OpCallbacks call) {
2798 if ((_flags & FLAG_PRE_FLUSHED) != 0)
2799 return;
2800
2801 if (isPersistent()) {
2802 fireLifecycleEvent(LifecycleEvent.BEFORE_STORE);
2803 // BEFORE_PERSIST is handled during Broker.persist and Broker.attach
2804 if (isDeleted())
2805 fireLifecycleEvent(LifecycleEvent.BEFORE_DELETE);
2806 else if (!(isNew() && !isFlushed()))
2807 fireLifecycleEvent(LifecycleEvent.BEFORE_UPDATE);
2808 _flags |= FLAG_PRE_FLUSHED;
2809 }
2810
2811 lock();
2812 try {
2813 if (!logical)
2814 assignObjectId(false, true);
2815 for (int i = 0, len = _meta.getFields().length; i < len; i++) {
2816 if ((logical || !assignField(i, true)) && !_flush.get(i)
2817 && _dirty.get(i)) {
2818 provideField(_pc, _single, i);
2819 if (_single.preFlush(logical, call))
2820 replaceField(_pc, _single, i);
2821 else
2822 _single.clear();
2823 }
2824 }
2825
2826 dirtyCheck();
2827 } finally {
2828 unlock();
2829 }
2830 }
2831
2832 /**
2833 * Make callbacks for deletion.
2834 */
2835 void preDelete() {
2836 // set a flag while call pre delete callback so that user can't
2837 // get into infinite recursion by calling delete(this)
2838 // within his callback method
2839 if ((_flags & FLAG_PRE_DELETING) == 0) {
2840 _flags |= FLAG_PRE_DELETING;
2841 try {
2842 fireLifecycleEvent(LifecycleEvent.BEFORE_DELETE);
2843 } finally {
2844 _flags &= ~FLAG_PRE_DELETING;
2845 }
2846 }
2847 }
2848
2849 /**
2850 * Cascade deletes and dereference dependent fields.
2851 */
2852 void cascadeDelete(OpCallbacks call) {
2853 FieldMetaData[] fmds = _meta.getFields();
2854 for (int i = 0; i < fmds.length; i++) {
2855 if (fmds[i].getCascadeDelete() != ValueMetaData.CASCADE_NONE
2856 || fmds[i].getKey().getCascadeDelete()
2857 != ValueMetaData.CASCADE_NONE
2858 || fmds[i].getElement().getCascadeDelete()
2859 != ValueMetaData.CASCADE_NONE) {
2860 _single.storeObjectField(i, fetchField(i, false));
2861 _single.delete(call);
2862 _single.clear();
2863 }
2864 }
2865 }
2866
2867 /**
2868 * Called after an instance is persisted by a user through the broker.
2869 * Cascades the persist operation to fields marked
2870 * {@link ValueMetaData#CASCADE_IMMEDIATE}.
2871 */
2872 void cascadePersist(OpCallbacks call) {
2873 FieldMetaData[] fmds = _meta.getFields();
2874 for (int i = 0; i < fmds.length; i++) {
2875 if (!_loaded.get(i))
2876 continue;
2877
2878 if (fmds[i].getCascadePersist() == ValueMetaData.CASCADE_IMMEDIATE
2879 || fmds[i].getKey().getCascadePersist()
2880 == ValueMetaData.CASCADE_IMMEDIATE
2881 || fmds[i].getElement().getCascadePersist()
2882 == ValueMetaData.CASCADE_IMMEDIATE) {
2883 _single.storeObjectField(i, fetchField(i, false));
2884 _single.persist(call);
2885 _single.clear();
2886 }
2887 }
2888 }
2889
2890 /**
2891 * Load the given field set from the data store into the instance.
2892 * Return true if any data is loaded, false otherwise.
2893 */
2894 boolean loadFields(BitSet fields, FetchConfiguration fetch, int lockLevel,
2895 Object sdata) {
2896 // can't load version field from store
2897 if (fields != null) {
2898 FieldMetaData vfield = _meta.getVersionField();
2899 if (vfield != null)
2900 fields.clear(vfield.getIndex());
2901 }
2902
2903 boolean ret = false;
2904 setLoading(true);
2905 try {
2906 // if any fields given, load them
2907 int len = (fields == null) ? 0 : fields.length();
2908 if (len > 0) {
2909 if (fetch == null)
2910 fetch = _broker.getFetchConfiguration();
2911 if (!_broker.getStoreManager().load(this, fields, fetch,
2912 lockLevel, sdata)) {
2913 throw new ObjectNotFoundException(_loc.get("del-instance",
2914 _meta.getDescribedType(), _oid)).
2915 setFailedObject(getManagedInstance());
2916 }
2917 ret = true;
2918 }
2919
2920 // make sure version information has been set; version info must
2921 // always be set after the first state load or set (which is why
2922 // we do this even if no fields were loaded -- could be that this
2923 // method is being called after a field is set)... some instances
2924 // might not have version info, in which case this gets called
2925 // mutiple times; that should be ok too
2926 if (_loadVersion == null) {
2927 syncVersion(sdata);
2928 ret = ret || _loadVersion != null;
2929 }
2930 }
2931 finally {
2932 setLoading(false);
2933 }
2934
2935 // see if the dfg is now loaded; do this regardless of whether we
2936 // loaded any fields, cause may already have been loaded by
2937 // StoreManager during initialization
2938 postLoad(-1, fetch);
2939 return ret;
2940 }
2941
2942 /**
2943 * Load the given field's fetch group; the field itself may already be
2944 * loaded if it is being set by the user.
2945 */
2946 protected void loadField(int field, int lockLevel, boolean forWrite,
2947 boolean fgs) {
2948 FetchConfiguration fetch = _broker.getFetchConfiguration();
2949 FieldMetaData fmd = _meta.getField(field);
2950 BitSet fields = null;
2951
2952 // if this is a dfg field or we need to load our dfg, do so
2953 if (fgs && (_flags & FLAG_LOADED) == 0)
2954 fields = getUnloadedInternal(fetch, LOAD_FGS, null);
2955
2956 // check for load fetch group
2957 String lfg = fmd.getLoadFetchGroup();
2958 boolean lfgAdded = false;
2959 if (lfg != null) {
2960 FieldMetaData[] fmds = _meta.getFields();
2961 for (int i = 0; i < fmds.length; i++) {
2962 if (!_loaded.get(i) && (i == field
2963 || fmds[i].isInFetchGroup(lfg))) {
2964 if (fields == null)
2965 fields = new BitSet(fmds.length);
2966 fields.set(i);
2967 }
2968 }
2969
2970 // relation field is loaded with the load-fetch-group
2971 // but this addition must be reverted once the load is over
2972 if (!fetch.hasFetchGroup(lfg)) {
2973 fetch.addFetchGroup(lfg);
2974 lfgAdded = true;
2975 }
2976 } else if (fmd.isInDefaultFetchGroup() && fields == null) {
2977 // no load group but dfg: add dfg fields if we haven't already
2978 fields = getUnloadedInternal(fetch, LOAD_FGS, null);
2979 } else if (!_loaded.get(fmd.getIndex())) {
2980 // no load group or dfg: load individual field
2981 if (fields == null)
2982 fields = new BitSet();
2983 fields.set(fmd.getIndex());
2984 }
2985
2986 // call this method even if there are no unloaded fields; loadFields
2987 // takes care of things like loading version info and setting PC flags
2988 try {
2989 loadFields(fields, fetch, lockLevel, null);
2990 } finally {
2991 if (lfgAdded)
2992 fetch.removeFetchGroup(lfg);
2993 }
2994 }
2995
2996 /**
2997 * Helper method to provide the given field number to the given
2998 * field manager.
2999 */
3000 void provideField(PersistenceCapable pc, FieldManager store, int field) {
3001 FieldManager beforeFM = _fm;
3002 _fm = store;
3003 pc.pcProvideField(field);
3004 // Retaining original FM because of the possibility of reentrant calls
3005 _fm = beforeFM;
3006 }
3007
3008 /**
3009 * Helper method to replace the given field number to the given
3010 * field manager.
3011 */
3012 void replaceField(PersistenceCapable pc, FieldManager load, int field) {
3013 FieldManager beforeFM = _fm;
3014 _fm = load;
3015 pc.pcReplaceField(field);
3016 // Retaining original FM because of the possibility of reentrant calls
3017 _fm = beforeFM;
3018 }
3019
3020 /**
3021 * Mark the field as loaded or unloaded.
3022 */
3023 private void setLoaded(int field, boolean isLoaded) {
3024 // don't continue if loaded state is already correct; otherwise we
3025 // can end up clearing _fieldImpl when we shouldn't
3026 if (_loaded.get(field) == isLoaded)
3027 return;
3028
3029 // if loading, clear intermediate data; if unloading, clear impl data
3030 if (_fieldImpl != null) {
3031 int idx = _meta.getExtraFieldDataIndex(field);
3032 if (idx != -1)
3033 _fieldImpl[idx] = null;
3034 }
3035
3036 if (isLoaded)
3037 _loaded.set(field);
3038 else
3039 _loaded.clear(field);
3040 }
3041
3042 /**
3043 * Perform post-load steps, including the post load callback.
3044 * We have to check the dfg after all field loads because it might be
3045 * loaded in multiple steps when paging is involved; the initial load
3046 * might exclude some fields which are then immediately loaded in a
3047 * separate step before being returned to the user.
3048 *
3049 * @param field the field index that was loaded, or -1 to indicate
3050 * that a group of possibly unknown fields was loaded
3051 */
3052 private void postLoad(int field, FetchConfiguration fetch) {
3053 // no need for postLoad callback?
3054 if ((_flags & FLAG_LOADED) != 0)
3055 return;
3056
3057 // in the middle of a group load, after which this method will be
3058 // called again?
3059 if (field != -1 && isLoading())
3060 return;
3061
3062 // no listeners?
3063 LifecycleEventManager mgr = _broker.getLifecycleEventManager();
3064 if (mgr == null || !mgr.hasLoadListeners(getManagedInstance(), _meta))
3065 return;
3066
3067 if (fetch == null)
3068 fetch = _broker.getFetchConfiguration();
3069 // is this field a post-load field?
3070 if (field != -1) {
3071 FieldMetaData fmd = _meta.getField(field);
3072 if (fmd.isInDefaultFetchGroup()
3073 && fetch.hasFetchGroup(FetchGroup.NAME_DEFAULT)
3074 && postLoad(FetchGroup.NAME_DEFAULT, fetch))
3075 return;
3076 String[] fgs = fmd.getCustomFetchGroups();
3077 for (int i = 0; i < fgs.length; i++)
3078 if (fetch.hasFetchGroup(fgs[i]) && postLoad(fgs[i], fetch))
3079 return;
3080 } else {
3081 for (Iterator itr = fetch.getFetchGroups().iterator();
3082 itr.hasNext();) {
3083 if (postLoad((String) itr.next(), fetch))
3084 return;
3085 }
3086 }
3087 }
3088
3089 /**
3090 * Perform post-load actions if the given fetch group is a post-load group
3091 * and is fully loaded.
3092 */
3093 private boolean postLoad(String fgName, FetchConfiguration fetch) {
3094 FetchGroup fg = _meta.getFetchGroup(fgName);
3095 if (fg == null || !fg.isPostLoad())
3096 return false;
3097
3098 FieldMetaData[] fmds = _meta.getFields();
3099 for (int i = 0; i < fmds.length; i++)
3100 if (!_loaded.get(i) && fmds[i].isInFetchGroup(fgName))
3101 return false;
3102
3103 _flags |= FLAG_LOADED;
3104 _broker.fireLifecycleEvent(getManagedInstance(), fetch, _meta,
3105 LifecycleEvent.AFTER_LOAD);
3106 return true;
3107 }
3108
3109 /**
3110 * Synchronize our version object with the datastore.
3111 */
3112 private boolean syncVersion(Object sdata) {
3113 return _broker.getStoreManager().syncVersion(this, sdata);
3114 }
3115
3116 /**
3117 * Returns whether this instance needs a version check.
3118 */
3119 public boolean isVersionCheckRequired() {
3120 // explicit flag for version check
3121 if ((_flags & FLAG_VERSION_CHECK) != 0)
3122 return true;
3123
3124 if (!_broker.getOptimistic() && !_broker.getConfiguration().
3125 getCompatibilityInstance().getNonOptimisticVersionCheck())
3126 return false;
3127 return _state.isVersionCheckRequired(this);
3128 }
3129
3130 /**
3131 * Set whether this instance requires a version check on the next flush.
3132 */
3133 void setCheckVersion(boolean versionCheck) {
3134 if (versionCheck)
3135 _flags |= FLAG_VERSION_CHECK;
3136 else
3137 _flags &= ~FLAG_VERSION_CHECK;
3138 }
3139
3140 /**
3141 * Returns whether this instance needs a version update.
3142 */
3143 public boolean isVersionUpdateRequired() {
3144 return (_flags & FLAG_VERSION_UPDATE) > 0;
3145 }
3146
3147 /**
3148 * Set whether this instance requires a version update on the next flush.
3149 */
3150 void setUpdateVersion(boolean versionUpdate) {
3151 if (versionUpdate)
3152 _flags |= FLAG_VERSION_UPDATE;
3153 else
3154 _flags &= ~FLAG_VERSION_UPDATE;
3155 }
3156
3157 /**
3158 * Translate the given exception based on the broker's implicit behavior.
3159 * Translation only occurs if the exception is initiated by a user action
3160 * on an instance, and therefore will not be caught and translated by the
3161 * broker.
3162 */
3163 protected RuntimeException translate(RuntimeException re) {
3164 RuntimeExceptionTranslator trans = _broker.
3165 getInstanceExceptionTranslator();
3166 return (trans == null) ? re : trans.translate(re);
3167 }
3168
3169 /**
3170 * Lock the state manager if the multithreaded option is set.
3171 */
3172 protected void lock() {
3173 // use broker-level lock to avoid deadlock situations with the state
3174 // manager lock and broker lock being obtained in different orders
3175 _broker.lock();
3176 }
3177
3178 /**
3179 * Unlock the state manager.
3180 */
3181 protected void unlock ()
3182 {
3183 // use broker-level lock to avoid deadlock situations with the state
3184 // manager lock and broker lock being obtained in different orders
3185 _broker.unlock ();
3186 }
3187
3188 private void writeObject(ObjectOutputStream oos) throws IOException {
3189 oos.writeObject(_broker);
3190 oos.defaultWriteObject();
3191 oos.writeObject(_meta.getDescribedType());
3192 writePC(oos, _pc);
3193 }
3194
3195 /**
3196 * Write <code>pc</code> to <code>oos</code>, handling internal-form
3197 * serialization. <code>pc</code> must be of the same type that this
3198 * state manager manages.
3199 *
3200 * @since 1.1.0
3201 */
3202 void writePC(ObjectOutputStream oos, PersistenceCapable pc)
3203 throws IOException {
3204 if (!Serializable.class.isAssignableFrom(_meta.getDescribedType()))
3205 throw new NotSerializableException(
3206 _meta.getDescribedType().getName());
3207
3208 oos.writeObject(pc);
3209 }
3210
3211 private void readObject(ObjectInputStream in)
3212 throws IOException, ClassNotFoundException {
3213 _broker = (BrokerImpl) in.readObject();
3214 in.defaultReadObject();
3215
3216 // we need to store the class before the pc instance so that we can
3217 // create _meta before calling readPC(), which relies on _meta being
3218 // non-null when reconstituting ReflectingPC instances. Sadly, this
3219 // penalizes the serialization footprint of non-ReflectingPC SMs also.
3220 Class managedType = (Class) in.readObject();
3221 _meta = _broker.getConfiguration().getMetaDataRepositoryInstance()
3222 .getMetaData(managedType, null, true);
3223
3224 _pc = readPC(in);
3225 }
3226
3227 /**
3228 * Converts the deserialized <code>o</code> to a {@link PersistenceCapable}
3229 * instance appropriate for storing in <code>_pc</code>.
3230 *
3231 * @since 1.1.0
3232 */
3233 PersistenceCapable readPC(ObjectInputStream in)
3234 throws ClassNotFoundException, IOException {
3235 Object o = in.readObject();
3236
3237 if (o == null)
3238 return null;
3239
3240 PersistenceCapable pc;
3241 if (!(o instanceof PersistenceCapable))
3242 pc = ImplHelper.toPersistenceCapable(o, this);
3243 else
3244 pc = (PersistenceCapable) o;
3245
3246 pc.pcReplaceStateManager(this);
3247 return pc;
3248 }
3249 }