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.ObjectInputStream;
23 import java.io.ObjectOutputStream;
24 import java.io.Serializable;
25 import java.lang.reflect.Modifier;
26 import java.security.AccessController;
27 import java.util.AbstractCollection;
28 import java.util.ArrayList;
29 import java.util.BitSet;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.Iterator;
35 import java.util.LinkedList;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39 import javax.transaction.Status;
40 import javax.transaction.Synchronization;
41
42 import org.apache.commons.collections.iterators.IteratorChain;
43 import org.apache.commons.collections.map.IdentityMap;
44 import org.apache.commons.collections.map.LinkedMap;
45 import org.apache.commons.collections.set.MapBackedSet;
46 import org.apache.openjpa.conf.Compatibility;
47 import org.apache.openjpa.conf.OpenJPAConfiguration;
48 import org.apache.openjpa.datacache.DataCache;
49 import org.apache.openjpa.ee.ManagedRuntime;
50 import org.apache.openjpa.enhance.PCRegistry;
51 import org.apache.openjpa.enhance.PersistenceCapable;
52 import org.apache.openjpa.event.LifecycleEvent;
53 import org.apache.openjpa.event.LifecycleEventManager;
54 import org.apache.openjpa.event.RemoteCommitEventManager;
55 import org.apache.openjpa.event.TransactionEvent;
56 import org.apache.openjpa.event.TransactionEventManager;
57 import org.apache.openjpa.kernel.exps.ExpressionParser;
58 import org.apache.openjpa.lib.log.Log;
59 import org.apache.openjpa.lib.util.J2DoPrivHelper;
60 import org.apache.openjpa.lib.util.Localizer;
61 import org.apache.openjpa.lib.util.ReferenceHashMap;
62 import org.apache.openjpa.lib.util.ReferenceHashSet;
63 import org.apache.openjpa.lib.util.ReferenceMap;
64 import java.util.concurrent.locks.ReentrantLock;
65 import org.apache.openjpa.meta.ClassMetaData;
66 import org.apache.openjpa.meta.FieldMetaData;
67 import org.apache.openjpa.meta.MetaDataRepository;
68 import org.apache.openjpa.meta.SequenceMetaData;
69 import org.apache.openjpa.meta.ValueMetaData;
70 import org.apache.openjpa.meta.ValueStrategies;
71 import org.apache.openjpa.util.ApplicationIds;
72 import org.apache.openjpa.util.CallbackException;
73 import org.apache.openjpa.util.Exceptions;
74 import org.apache.openjpa.util.GeneralException;
75 import org.apache.openjpa.util.ImplHelper;
76 import org.apache.openjpa.util.InternalException;
77 import org.apache.openjpa.util.InvalidStateException;
78 import org.apache.openjpa.util.NoTransactionException;
79 import org.apache.openjpa.util.ObjectExistsException;
80 import org.apache.openjpa.util.ObjectId;
81 import org.apache.openjpa.util.ObjectNotFoundException;
82 import org.apache.openjpa.util.OpenJPAException;
83 import org.apache.openjpa.util.OptimisticException;
84 import org.apache.openjpa.util.RuntimeExceptionTranslator;
85 import org.apache.openjpa.util.StoreException;
86 import org.apache.openjpa.util.UnsupportedException;
87 import org.apache.openjpa.util.UserException;
88
89 /**
90 * Concrete {@link Broker}. The broker handles object-level behavior,
91 * but leaves all interaction with the data store to a {@link StoreManager}
92 * that must be supplied at initialization.
93 *
94 * @author Abe White
95 */
96 public class BrokerImpl
97 implements Broker, FindCallbacks, Cloneable, Serializable {
98
99 /**
100 * Incremental flush.
101 */
102 protected static final int FLUSH_INC = 0;
103
104 /**
105 * Flush in preparation of commit.
106 */
107 protected static final int FLUSH_COMMIT = 1;
108
109 /**
110 * Flush to check consistency of cache, then immediately rollback changes.
111 */
112 protected static final int FLUSH_ROLLBACK = 2;
113
114 /**
115 * Run persistence-by-reachability and other flush-time operations without
116 * accessing the database.
117 */
118 protected static final int FLUSH_LOGICAL = 3;
119
120 static final int STATUS_INIT = 0;
121 static final int STATUS_TRANSIENT = 1;
122 static final int STATUS_OID_ASSIGN = 2;
123 static final int STATUS_COMMIT_NEW = 3;
124
125 private static final int FLAG_ACTIVE = 2 << 0;
126 private static final int FLAG_STORE_ACTIVE = 2 << 1;
127 private static final int FLAG_CLOSE_INVOKED = 2 << 2;
128 private static final int FLAG_PRESTORING = 2 << 3;
129 private static final int FLAG_DEREFDELETING = 2 << 4;
130 private static final int FLAG_FLUSHING = 2 << 5;
131 private static final int FLAG_STORE_FLUSHING = 2 << 6;
132 private static final int FLAG_FLUSHED = 2 << 7;
133 private static final int FLAG_FLUSH_REQUIRED = 2 << 8;
134 private static final int FLAG_REMOTE_LISTENER = 2 << 9;
135 private static final int FLAG_RETAINED_CONN = 2 << 10;
136 private static final int FLAG_TRANS_ENDING = 2 << 11;
137
138 private static final Object[] EMPTY_OBJECTS = new Object[0];
139
140 private static final Localizer _loc =
141 Localizer.forPackage(BrokerImpl.class);
142
143 // the store manager in use; this may be a decorator such as a
144 // data cache store manager around the native store manager
145 private transient DelegatingStoreManager _store = null;
146
147 private FetchConfiguration _fc = null;
148 private String _user = null;
149 private String _pass = null;
150
151 // these must be rebuilt by the facade layer during its deserialization
152 private transient Log _log = null;
153 private transient Compatibility _compat = null;
154 private transient ManagedRuntime _runtime = null;
155 private transient LockManager _lm = null;
156 private transient InverseManager _im = null;
157 private transient ReentrantLock _lock = null;
158 private transient OpCallbacks _call = null;
159 private transient RuntimeExceptionTranslator _extrans = null;
160
161 // ref to producing factory and configuration
162 private transient AbstractBrokerFactory _factory = null;
163 private transient OpenJPAConfiguration _conf = null;
164
165 // cache class loader associated with the broker
166 private transient ClassLoader _loader = null;
167
168 // user state
169 private Synchronization _sync = null;
170 private Map _userObjects = null;
171
172 // managed object caches
173 private ManagedCache _cache = null;
174 private TransactionalCache _transCache = null;
175 private Set _transAdditions = null;
176 private Set _derefCache = null;
177 private Set _derefAdditions = null;
178
179 // these are used for method-internal state only
180 private transient Map _loading = null;
181 private transient Set _operating = null;
182
183 private Set _persistedClss = null;
184 private Set _updatedClss = null;
185 private Set _deletedClss = null;
186 private Set _pending = null;
187 private int findAllDepth = 0;
188
189 // track instances that become transactional after the first savepoint
190 // (the first uses the transactional cache)
191 private Set _savepointCache = null;
192 private LinkedMap _savepoints = null;
193 private transient SavepointManager _spm = null;
194
195 // track open queries and extents so we can free their resources on close
196 private transient ReferenceHashSet _queries = null;
197 private transient ReferenceHashSet _extents = null;
198
199 // track operation stack depth. Transient because operations cannot
200 // span serialization.
201 private transient int _operationCount = 0;
202
203 // options
204 private boolean _nontransRead = false;
205 private boolean _nontransWrite = false;
206 private boolean _retainState = false;
207 private int _autoClear = CLEAR_DATASTORE;
208 private int _restoreState = RESTORE_IMMUTABLE;
209 private boolean _optimistic = false;
210 private boolean _ignoreChanges = false;
211 private boolean _multithreaded = false;
212 private boolean _managed = false;
213 private boolean _syncManaged = false;
214 private int _connRetainMode = CONN_RETAIN_DEMAND;
215 private boolean _evictDataCache = false;
216 private boolean _populateDataCache = true;
217 private boolean _largeTransaction = false;
218 private int _autoDetach = 0;
219 private int _detachState = DETACH_LOADED;
220 private boolean _detachedNew = true;
221 private boolean _orderDirty = false;
222
223 // status
224 private int _flags = 0;
225
226 // this is not in status because it should not be serialized
227 private transient boolean _isSerializing = false;
228
229 // transient because closed brokers can't be serialized
230 private transient boolean _closed = false;
231 private transient RuntimeException _closedException = null;
232
233 // event managers
234 private TransactionEventManager _transEventManager = null;
235 private int _transCallbackMode = 0;
236 private LifecycleEventManager _lifeEventManager = null;
237 private int _lifeCallbackMode = 0;
238
239 private transient boolean _initializeWasInvoked = false;
240 private LinkedList _fcs;
241
242 /**
243 * Set the persistence manager's authentication. This is the first
244 * method called after construction.
245 *
246 * @param user the username this broker represents; used when pooling
247 * brokers to make sure that a request to the factory for
248 * a connection with an explicit user is delegated to a suitable broker
249 * @param pass the password for the above user
250 */
251 public void setAuthentication(String user, String pass) {
252 _user = user;
253 _pass = pass;
254 }
255
256 /**
257 * Initialize the persistence manager. This method is called
258 * automatically by the factory before use.
259 *
260 * @param factory the factory used to create this broker
261 * @param sm a concrete StoreManager implementation to
262 * handle interaction with the data store
263 * @param managed the transaction mode
264 * @param connMode the connection retain mode
265 * @param fromDeserialization whether this call happened because of a
266 * deserialization or creation of a new BrokerImpl.
267 */
268 public void initialize(AbstractBrokerFactory factory,
269 DelegatingStoreManager sm, boolean managed, int connMode,
270 boolean fromDeserialization) {
271 _initializeWasInvoked = true;
272 _loader = (ClassLoader) AccessController.doPrivileged(
273 J2DoPrivHelper.getContextClassLoaderAction());
274 if (!fromDeserialization)
275 _conf = factory.getConfiguration();
276 _compat = _conf.getCompatibilityInstance();
277 _factory = factory;
278 _log = _conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);
279 if (!fromDeserialization)
280 _cache = new ManagedCache(this);
281 initializeOperatingSet();
282 _connRetainMode = connMode;
283 _managed = managed;
284 if (managed)
285 _runtime = _conf.getManagedRuntimeInstance();
286 else
287 _runtime = new LocalManagedRuntime(this);
288
289 if (!fromDeserialization) {
290 _lifeEventManager = new LifecycleEventManager();
291 _transEventManager = new TransactionEventManager();
292 int cmode = _conf.getMetaDataRepositoryInstance().
293 getMetaDataFactory().getDefaults().getCallbackMode();
294 setLifecycleListenerCallbackMode(cmode);
295 setTransactionListenerCallbackMode(cmode);
296
297 // setup default options
298 _factory.configureBroker(this);
299 }
300
301 // make sure to do this after configuring broker so that store manager
302 // can look to broker configuration; we set both store and lock managers
303 // before initializing them because they may each try to access the
304 // other in thier initialization
305 _store = sm;
306 _lm = _conf.newLockManagerInstance();
307 _im = _conf.newInverseManagerInstance();
308 _spm = _conf.getSavepointManagerInstance();
309 _store.setContext(this);
310 _lm.setContext(this);
311
312 if (_connRetainMode == CONN_RETAIN_ALWAYS)
313 retainConnection();
314 if (!fromDeserialization) {
315 _fc = _store.newFetchConfiguration();
316 _fc.setContext(this);
317 }
318
319 // synch with the global transaction in progress, if any
320 if (_factory.syncWithManagedTransaction(this, false))
321 beginInternal();
322 }
323
324 private void initializeOperatingSet() {
325 _operating = MapBackedSet.decorate(new IdentityMap());
326 }
327
328 /**
329 * Gets the unmodifiable set of instances being operated.
330 */
331 protected Set getOperatingSet() {
332 return Collections.unmodifiableSet(_operating);
333 }
334
335 public Object clone()
336 throws CloneNotSupportedException {
337 if (_initializeWasInvoked)
338 throw new CloneNotSupportedException();
339 else {
340 return super.clone();
341 }
342 }
343
344 /**
345 * Create a {@link Map} to be used for the primary managed object cache.
346 * Maps oids to state managers. By default, this creates a
347 * {@link ReferenceMap} with soft values.
348 */
349 protected Map newManagedObjectCache() {
350 return new ReferenceHashMap(ReferenceMap.HARD, ReferenceMap.SOFT);
351 }
352
353 //////////////////////////////////
354 // Implementation of StoreContext
355 //////////////////////////////////
356
357 public Broker getBroker() {
358 return this;
359 }
360
361 //////////////
362 // Properties
363 //////////////
364
365 public void setImplicitBehavior(OpCallbacks call,
366 RuntimeExceptionTranslator ex) {
367 if (_call == null)
368 _call = call;
369 if (_extrans == null)
370 _extrans = ex;
371 }
372
373 RuntimeExceptionTranslator getInstanceExceptionTranslator() {
374 return (_operationCount == 0) ? _extrans : null;
375 }
376
377 public BrokerFactory getBrokerFactory() {
378 return _factory;
379 }
380
381 public OpenJPAConfiguration getConfiguration() {
382 return _conf;
383 }
384
385 public FetchConfiguration getFetchConfiguration() {
386 return _fc;
387 }
388
389 public FetchConfiguration pushFetchConfiguration() {
390 if (_fcs == null)
391 _fcs = new LinkedList();
392 _fcs.add(_fc);
393 _fc = (FetchConfiguration) _fc.clone();
394 return _fc;
395 }
396
397 public void popFetchConfiguration() {
398 if (_fcs == null || _fcs.isEmpty())
399 throw new UserException(_loc.get("fetch-configuration-stack-empty"));
400 _fc = (FetchConfiguration) _fcs.removeLast();
401 }
402
403 public int getConnectionRetainMode() {
404 return _connRetainMode;
405 }
406
407 public boolean isManaged() {
408 return _managed;
409 }
410
411 public ManagedRuntime getManagedRuntime() {
412 return _runtime;
413 }
414
415 public ClassLoader getClassLoader() {
416 return _loader;
417 }
418
419 public DelegatingStoreManager getStoreManager() {
420 return _store;
421 }
422
423 public LockManager getLockManager() {
424 return _lm;
425 }
426
427 public InverseManager getInverseManager() {
428 return _im;
429 }
430
431 public String getConnectionUserName() {
432 return _user;
433 }
434
435 public String getConnectionPassword() {
436 return _pass;
437 }
438
439 public boolean getMultithreaded() {
440 return _multithreaded;
441 }
442
443 public void setMultithreaded(boolean multithreaded) {
444 assertOpen();
445 _multithreaded = multithreaded;
446 if (multithreaded && _lock == null)
447 _lock = new ReentrantLock();
448 else if (!multithreaded)
449 _lock = null;
450 }
451
452 public boolean getIgnoreChanges() {
453 return _ignoreChanges;
454 }
455
456 public void setIgnoreChanges(boolean val) {
457 assertOpen();
458 _ignoreChanges = val;
459 }
460
461 public boolean getNontransactionalRead() {
462 return _nontransRead;
463 }
464
465 public void setNontransactionalRead(boolean val) {
466 assertOpen();
467 if ((_flags & FLAG_PRESTORING) != 0)
468 throw new UserException(_loc.get("illegal-op-in-prestore"));
469
470 // make sure the runtime supports it
471 if (val && !_conf.supportedOptions().contains
472 (_conf.OPTION_NONTRANS_READ))
473 throw new UnsupportedException(_loc.get
474 ("nontrans-read-not-supported"));
475
476 _nontransRead = val;
477 }
478
479 public boolean getNontransactionalWrite() {
480 return _nontransWrite;
481 }
482
483 public void setNontransactionalWrite(boolean val) {
484 assertOpen();
485 if ((_flags & FLAG_PRESTORING) != 0)
486 throw new UserException(_loc.get("illegal-op-in-prestore"));
487
488 _nontransWrite = val;
489 }
490
491 public boolean getOptimistic() {
492 return _optimistic;
493 }
494
495 public void setOptimistic(boolean val) {
496 assertOpen();
497 if ((_flags & FLAG_ACTIVE) != 0)
498 throw new InvalidStateException(_loc.get("trans-active",
499 "Optimistic"));
500
501 // make sure the runtime supports it
502 if (val && !_conf.supportedOptions().contains(_conf.OPTION_OPTIMISTIC))
503 throw new UnsupportedException(_loc.get
504 ("optimistic-not-supported"));
505
506 _optimistic = val;
507 }
508
509 public int getRestoreState() {
510 return _restoreState;
511 }
512
513 public void setRestoreState(int val) {
514 assertOpen();
515 if ((_flags & FLAG_ACTIVE) != 0)
516 throw new InvalidStateException(_loc.get("trans-active",
517 "Restore"));
518
519 _restoreState = val;
520 }
521
522 public boolean getRetainState() {
523 return _retainState;
524 }
525
526 public void setRetainState(boolean val) {
527 assertOpen();
528 if ((_flags & FLAG_PRESTORING) != 0)
529 throw new UserException(_loc.get("illegal-op-in-prestore"));
530 _retainState = val;
531 }
532
533 public int getAutoClear() {
534 return _autoClear;
535 }
536
537 public void setAutoClear(int val) {
538 assertOpen();
539 _autoClear = val;
540 }
541
542 public int getAutoDetach() {
543 return _autoDetach;
544 }
545
546 public void setAutoDetach(int detachFlags) {
547 assertOpen();
548 _autoDetach = detachFlags;
549 }
550
551 public void setAutoDetach(int detachFlag, boolean on) {
552 assertOpen();
553 if (on)
554 _autoDetach |= detachFlag;
555 else
556 _autoDetach &= ~detachFlag;
557 }
558
559 public int getDetachState() {
560 return _detachState;
561 }
562
563 public void setDetachState(int mode) {
564 assertOpen();
565 _detachState = mode;
566 }
567
568 public boolean isDetachedNew() {
569 return _detachedNew;
570 }
571
572 public void setDetachedNew(boolean isNew) {
573 assertOpen();
574 _detachedNew = isNew;
575 }
576
577 public boolean getSyncWithManagedTransactions() {
578 return _syncManaged;
579 }
580
581 public void setSyncWithManagedTransactions(boolean sync) {
582 assertOpen();
583 _syncManaged = sync;
584 }
585
586 public boolean getEvictFromDataCache() {
587 return _evictDataCache;
588 }
589
590 public void setEvictFromDataCache(boolean evict) {
591 assertOpen();
592 _evictDataCache = evict;
593 }
594
595 public boolean getPopulateDataCache() {
596 return _populateDataCache;
597 }
598
599 public void setPopulateDataCache(boolean cache) {
600 assertOpen();
601 _populateDataCache = cache;
602 }
603
604 public boolean isTrackChangesByType() {
605 return _largeTransaction;
606 }
607
608 public void setTrackChangesByType(boolean largeTransaction) {
609 assertOpen();
610 _largeTransaction = largeTransaction;
611 }
612
613 public Object getUserObject(Object key) {
614 beginOperation(false);
615 try {
616 return (_userObjects == null) ? null : _userObjects.get(key);
617 } finally {
618 endOperation();
619 }
620 }
621
622 public Object putUserObject(Object key, Object val) {
623 beginOperation(false);
624 try {
625 if (val == null)
626 return (_userObjects == null) ? null : _userObjects.remove(key);
627
628 if (_userObjects == null)
629 _userObjects = new HashMap();
630 return _userObjects.put(key, val);
631 } finally {
632 endOperation();
633 }
634 }
635
636 //////////
637 // Events
638 //////////
639
640 public void addLifecycleListener(Object listener, Class[] classes) {
641 beginOperation(false);
642 try {
643 _lifeEventManager.addListener(listener, classes);
644 } finally {
645 endOperation();
646 }
647 }
648
649 public void removeLifecycleListener(Object listener) {
650 beginOperation(false);
651 try {
652 _lifeEventManager.removeListener(listener);
653 } finally {
654 endOperation();
655 }
656 }
657
658 public int getLifecycleListenerCallbackMode() {
659 return _lifeCallbackMode;
660 }
661
662 public void setLifecycleListenerCallbackMode(int mode) {
663 beginOperation(false);
664 try {
665 _lifeCallbackMode = mode;
666 _lifeEventManager.setFailFast((mode & CALLBACK_FAIL_FAST) != 0);
667 } finally {
668 endOperation();
669 }
670 }
671
672 /**
673 * Give state managers access to the lifecycle event manager.
674 */
675 public LifecycleEventManager getLifecycleEventManager() {
676 return _lifeEventManager;
677 }
678
679 /**
680 * Fire given lifecycle event, handling any exceptions appropriately.
681 *
682 * @return whether events are being processed at this time
683 */
684 boolean fireLifecycleEvent(Object src, Object related, ClassMetaData meta,
685 int eventType) {
686 if (_lifeEventManager == null)
687 return false;
688 handleCallbackExceptions(_lifeEventManager.fireEvent(src, related,
689 meta, eventType), _lifeCallbackMode);
690 return true;
691 }
692
693 /**
694 * Take actions on callback exceptions depending on callback mode.
695 */
696 private void handleCallbackExceptions(Exception[] exceps, int mode) {
697 if (exceps.length == 0 || (mode & CALLBACK_IGNORE) != 0)
698 return;
699
700 OpenJPAException ce;
701 if (exceps.length == 1)
702 ce = new CallbackException(exceps[0]);
703 else
704 ce = new CallbackException(_loc.get("callback-err")).
705 setNestedThrowables(exceps);
706 if ((mode & CALLBACK_ROLLBACK) != 0 && (_flags & FLAG_ACTIVE) != 0) {
707 ce.setFatal(true);
708 setRollbackOnlyInternal(ce);
709 }
710 if ((mode & CALLBACK_LOG) != 0 && _log.isWarnEnabled())
711 _log.warn(ce);
712 if ((mode & CALLBACK_RETHROW) != 0)
713 throw ce;
714 }
715
716 public void addTransactionListener(Object tl) {
717 beginOperation(false);
718 try {
719 _transEventManager.addListener(tl);
720 if (tl instanceof RemoteCommitEventManager)
721 _flags |= FLAG_REMOTE_LISTENER;
722 } finally {
723 endOperation();
724 }
725 }
726
727 public void removeTransactionListener(Object tl) {
728 beginOperation(false);
729 try {
730 if (_transEventManager.removeListener(tl)
731 && (tl instanceof RemoteCommitEventManager))
732 _flags &= ~FLAG_REMOTE_LISTENER;
733 } finally {
734 endOperation();
735 }
736 }
737
738 public int getTransactionListenerCallbackMode() {
739 return _transCallbackMode;
740 }
741
742 public void setTransactionListenerCallbackMode(int mode) {
743 beginOperation(false);
744 try {
745 _transCallbackMode = mode;
746 _transEventManager.setFailFast((mode & CALLBACK_FAIL_FAST) != 0);
747 } finally {
748 endOperation();
749 }
750 }
751
752 /**
753 * Fire given transaction event, handling any exceptions appropriately.
754 */
755 private void fireTransactionEvent(TransactionEvent trans) {
756 if (_transEventManager != null)
757 handleCallbackExceptions(_transEventManager.fireEvent(trans),
758 _transCallbackMode);
759 }
760
761 ///////////
762 // Lookups
763 ///////////
764
765 public Object find(Object oid, boolean validate, FindCallbacks call) {
766 int flags = OID_COPY | OID_ALLOW_NEW | OID_NODELETED;
767 if (!validate)
768 flags |= OID_NOVALIDATE;
769 return find(oid, _fc, null, null, flags, call);
770 }
771
772 public Object find(Object oid, FetchConfiguration fetch, BitSet exclude,
773 Object edata, int flags) {
774 return find(oid, fetch, exclude, edata, flags, null);
775 }
776
777 /**
778 * Internal finder.
779 */
780 protected Object find(Object oid, FetchConfiguration fetch, BitSet exclude,
781 Object edata, int flags, FindCallbacks call) {
782 if (call == null)
783 call = this;
784 oid = call.processArgument(oid);
785 if (oid == null) {
786 if ((flags & OID_NOVALIDATE) == 0)
787 throw new ObjectNotFoundException(_loc.get("null-oid"));
788 return call.processReturn(oid, null);
789 }
790 if (fetch == null)
791 fetch = _fc;
792
793 beginOperation(true);
794 try {
795 assertNontransactionalRead();
796
797 // cached instance?
798 StateManagerImpl sm = getStateManagerImplById(oid,
799 (flags & OID_ALLOW_NEW) != 0 || hasFlushed());
800 if (sm != null) {
801 if (!requiresLoad(sm, true, fetch, edata, flags))
802 return call.processReturn(oid, sm);
803
804 if (!sm.isLoading()) {
805 // make sure all the configured fields are loaded; do this
806 // after making instance transactional for locking
807 if (!sm.isTransactional() && useTransactionalState(fetch))
808 sm.transactional();
809 boolean loaded;
810 try {
811 loaded = sm.load(fetch, StateManagerImpl.LOAD_FGS,
812 exclude, edata, false);
813 } catch (ObjectNotFoundException onfe) {
814 if ((flags & OID_NODELETED) != 0
815 || (flags & OID_NOVALIDATE) != 0)
816 throw onfe;
817 return call.processReturn(oid, null);
818 }
819
820 // if no data needed to be loaded and the user wants to
821 // validate, just make sure the object exists
822 if (!loaded && (flags & OID_NOVALIDATE) == 0
823 && _compat.getValidateTrueChecksStore()
824 && !sm.isTransactional()
825 && !_store.exists(sm, edata)) {
826 if ((flags & OID_NODELETED) == 0)
827 return call.processReturn(oid, null);
828 throw new ObjectNotFoundException(_loc.get
829 ("del-instance", sm.getManagedInstance(), oid)).
830 setFailedObject(sm.getManagedInstance());
831 }
832 }
833
834 // since the object was cached, we may need to upgrade lock
835 // if current level is higher than level of initial load
836 if ((_flags & FLAG_ACTIVE) != 0) {
837 int level = fetch.getReadLockLevel();
838 _lm.lock(sm, level, fetch.getLockTimeout(), edata);
839 sm.readLocked(level, fetch.getWriteLockLevel());
840 }
841 return call.processReturn(oid, sm);
842 }
843
844 // if there's no cached sm for a new/transient id type, we
845 // it definitely doesn't exist
846 if (oid instanceof StateManagerId)
847 return call.processReturn(oid, null);
848
849 // initialize a new state manager for the datastore instance
850 sm = newStateManagerImpl(oid, (flags & OID_COPY) != 0);
851 boolean load = requiresLoad(sm, false, fetch, edata, flags);
852 sm = initialize(sm, load, fetch, edata);
853 if (sm == null) {
854 if ((flags & OID_NOVALIDATE) != 0)
855 throw new ObjectNotFoundException(oid);
856 return call.processReturn(oid, null);
857 }
858
859 // make sure all configured fields were loaded
860 if (load) {
861 try {
862 sm.load(fetch, StateManagerImpl.LOAD_FGS, exclude,
863 edata, false);
864 } catch (ObjectNotFoundException onfe) {
865 if ((flags & OID_NODELETED) != 0
866 || (flags & OID_NOVALIDATE) != 0)
867 throw onfe;
868 return call.processReturn(oid, null);
869 }
870 }
871 return call.processReturn(oid, sm);
872 } catch (OpenJPAException ke) {
873 throw ke;
874 } catch (RuntimeException re) {
875 throw new GeneralException(re);
876 } finally {
877 endOperation();
878 }
879 }
880
881 /**
882 * Initialize a newly-constructed state manager.
883 */
884 protected StateManagerImpl initialize(StateManagerImpl sm, boolean load,
885 FetchConfiguration fetch, Object edata) {
886 if (!load) {
887 sm.initialize(sm.getMetaData().getDescribedType(),
888 PCState.HOLLOW);
889 } else {
890 PCState state = (useTransactionalState(fetch))
891 ? PCState.PCLEAN : PCState.PNONTRANS;
892 sm.setLoading(true);
893 try {
894 if (!_store.initialize(sm, state, fetch, edata))
895 return null;
896 } finally {
897 sm.setLoading(false);
898 }
899 }
900 return sm;
901 }
902
903 public Object[] findAll(Collection oids, boolean validate,
904 FindCallbacks call) {
905 int flags = OID_COPY | OID_ALLOW_NEW | OID_NODELETED;
906 if (!validate)
907 flags |= OID_NOVALIDATE;
908 return findAll(oids, _fc, null, null, flags, call);
909 }
910
911 public Object[] findAll(Collection oids, FetchConfiguration fetch,
912 BitSet exclude, Object edata, int flags) {
913 return findAll(oids, fetch, exclude, edata, flags, null);
914 }
915
916 /**
917 * Internal finder.
918 */
919 protected Object[] findAll(Collection oids, FetchConfiguration fetch,
920 BitSet exclude, Object edata, int flags, FindCallbacks call) {
921 findAllDepth ++;
922
923 // throw any exceptions for null oids up immediately
924 if (oids == null)
925 throw new NullPointerException("oids == null");
926 if ((flags & OID_NOVALIDATE) != 0 && oids.contains(null))
927 throw new UserException(_loc.get("null-oids"));
928
929 // we have to use a map of oid->sm rather than a simple
930 // array, so that we make sure not to create multiple sms for equivalent
931 // oids if the user has duplicates in the given array
932 if (_loading == null)
933 _loading = new HashMap((int) (oids.size() * 1.33 + 1));
934
935 if (call == null)
936 call = this;
937 if (fetch == null)
938 fetch = _fc;
939
940 beginOperation(true);
941 try {
942 assertNontransactionalRead();
943
944 // collection of state managers to pass to store manager
945 List load = null;
946 StateManagerImpl sm;
947 boolean initialized;
948 boolean transState = useTransactionalState(fetch);
949 Object obj, oid;
950 int idx = 0;
951 for (Iterator itr = oids.iterator(); itr.hasNext(); idx++) {
952 // if we've already seen this oid, skip repeats
953 obj = itr.next();
954 oid = call.processArgument(obj);
955 if (oid == null || _loading.containsKey(obj))
956 continue;
957
958 // if we don't have a cached instance or it is not transactional
959 // and is hollow or we need to validate, load it
960 sm = getStateManagerImplById(oid, (flags & OID_ALLOW_NEW) != 0
961 || hasFlushed());
962 initialized = sm != null;
963 if (!initialized)
964 sm = newStateManagerImpl(oid, (flags & OID_COPY) != 0);
965
966 _loading.put(obj, sm);
967 if (requiresLoad(sm, initialized, fetch, edata, flags)) {
968 transState = transState || useTransactionalState(fetch);
969 if (initialized && !sm.isTransactional() && transState)
970 sm.transactional();
971 if (load == null)
972 load = new ArrayList(oids.size() - idx);
973 load.add(sm);
974 } else if (!initialized)
975 sm.initialize(sm.getMetaData().getDescribedType(),
976 PCState.HOLLOW);
977 }
978
979 // pass all state managers in need of loading or validation to the
980 // store manager
981 if (load != null) {
982 PCState state = (transState) ? PCState.PCLEAN
983 : PCState.PNONTRANS;
984 Collection failed = _store.loadAll(load, state,
985 StoreManager.FORCE_LOAD_NONE, fetch, edata);
986
987 // set failed instances to null
988 if (failed != null && !failed.isEmpty()) {
989 if ((flags & OID_NOVALIDATE) != 0)
990 throw newObjectNotFoundException(failed);
991 for (Iterator itr = failed.iterator(); itr.hasNext();)
992 _loading.put(itr.next(), null);
993 }
994 }
995
996 // create results array; make sure all configured fields are
997 // loaded in each instance
998 Object[] results = new Object[oids.size()];
999 boolean active = (_flags & FLAG_ACTIVE) != 0;
1000 int level = fetch.getReadLockLevel();
1001 idx = 0;
1002 for (Iterator itr = oids.iterator(); itr.hasNext(); idx++) {
1003 oid = itr.next();
1004 sm = (StateManagerImpl) _loading.get(oid);
1005 if (sm != null && requiresLoad(sm, true, fetch, edata, flags)) {
1006 try {
1007 sm.load(fetch, StateManagerImpl.LOAD_FGS,
1008 exclude, edata, false);
1009 if (active) {
1010 _lm.lock(sm, level, fetch.getLockTimeout(), edata);
1011 sm.readLocked(level, fetch.getWriteLockLevel());
1012 }
1013 }
1014 catch (ObjectNotFoundException onfe) {
1015 if ((flags & OID_NODELETED) != 0
1016 || (flags & OID_NOVALIDATE) != 0)
1017 throw onfe;
1018 sm = null;
1019 }
1020 }
1021 results[idx] = call.processReturn(oid, sm);
1022 }
1023 return results;
1024 } catch (OpenJPAException ke) {
1025 throw ke;
1026 } catch (RuntimeException re) {
1027 throw new GeneralException(re);
1028 } finally {
1029 findAllDepth--;
1030 if (findAllDepth == 0)
1031 _loading = null;
1032 endOperation();
1033 }
1034 }
1035
1036 private boolean hasFlushed() {
1037 return (_flags & FLAG_FLUSHED) != 0;
1038 }
1039
1040 /**
1041 * Return whether the given instance needs loading before being returned
1042 * to the user.
1043 */
1044 private boolean requiresLoad(OpenJPAStateManager sm, boolean initialized,
1045 FetchConfiguration fetch, Object edata, int flags) {
1046 if (!fetch.requiresLoad())
1047 return false;
1048 if ((flags & OID_NOVALIDATE) == 0)
1049 return true;
1050 if (edata != null) // take advantage of existing result
1051 return true;
1052 if (initialized && sm.getPCState() != PCState.HOLLOW)
1053 return false;
1054 if (!initialized && sm.getMetaData().getPCSubclasses().length > 0)
1055 return true;
1056 return !_compat.getValidateFalseReturnsHollow();
1057 }
1058
1059 /**
1060 * Return whether to use a transactional state.
1061 */
1062 private boolean useTransactionalState(FetchConfiguration fetch) {
1063 return (_flags & FLAG_ACTIVE) != 0 && (!_optimistic
1064 || _autoClear == CLEAR_ALL
1065 || fetch.getReadLockLevel() != LOCK_NONE);
1066 }
1067
1068 public Object findCached(Object oid, FindCallbacks call) {
1069 if (call == null)
1070 call = this;
1071 oid = call.processArgument(oid);
1072 if (oid == null)
1073 return call.processReturn(oid, null);
1074
1075 beginOperation(true);
1076 try {
1077 StateManagerImpl sm = getStateManagerImplById(oid, true);
1078 return call.processReturn(oid, sm);
1079 } finally {
1080 endOperation();
1081 }
1082 }
1083
1084 public Class getObjectIdType(Class cls) {
1085 if (cls == null)
1086 return null;
1087
1088 beginOperation(false);
1089 try {
1090 ClassMetaData meta = _conf.getMetaDataRepositoryInstance().
1091 getMetaData(cls, _loader, false);
1092 if (meta == null
1093 || meta.getIdentityType() == ClassMetaData.ID_UNKNOWN)
1094 return null;
1095 if (meta.getIdentityType() == ClassMetaData.ID_APPLICATION)
1096 return meta.getObjectIdType();
1097
1098 return _store.getDataStoreIdType(meta);
1099 } catch (OpenJPAException ke) {
1100 throw ke;
1101 } catch (RuntimeException re) {
1102 throw new GeneralException(re);
1103 } finally {
1104 endOperation();
1105 }
1106 }
1107
1108 public Object newObjectId(Class cls, Object val) {
1109 if (val == null)
1110 return null;
1111
1112 beginOperation(false);
1113 try {
1114 ClassMetaData meta = _conf.getMetaDataRepositoryInstance().
1115 getMetaData(cls, _loader, true);
1116 switch (meta.getIdentityType()) {
1117 case ClassMetaData.ID_DATASTORE:
1118 // delegate to store manager for datastore ids
1119 if (val instanceof String
1120 && ((String) val).startsWith(StateManagerId.STRING_PREFIX))
1121 return new StateManagerId((String) val);
1122 return _store.newDataStoreId(val, meta);
1123 case ClassMetaData.ID_APPLICATION:
1124 if (ImplHelper.isAssignable(meta.getObjectIdType(),
1125 val.getClass())) {
1126 if (!meta.isOpenJPAIdentity()
1127 && meta.isObjectIdTypeShared())
1128 return new ObjectId(cls, val);
1129 return val;
1130 }
1131
1132 // stringified app id?
1133 if (val instanceof String
1134 && !_conf.getCompatibilityInstance().
1135 getStrictIdentityValues()
1136 && !Modifier.isAbstract(cls.getModifiers()))
1137 return PCRegistry.newObjectId(cls, (String) val);
1138
1139 Object[] arr = (val instanceof Object[]) ? (Object[]) val
1140 : new Object[]{ val };
1141 return ApplicationIds.fromPKValues(arr, meta);
1142 default:
1143 throw new UserException(_loc.get("meta-unknownid", cls));
1144 }
1145 } catch (OpenJPAException ke) {
1146 throw ke;
1147 } catch (ClassCastException cce) {
1148 throw new UserException(_loc.get("bad-id-value", val,
1149 val.getClass().getName(), cls)).setCause(cce);
1150 } catch (RuntimeException re) {
1151 throw new GeneralException(re);
1152 } finally {
1153 endOperation();
1154 }
1155 }
1156
1157 /**
1158 * Create a new state manager for the given oid.
1159 */
1160 private StateManagerImpl newStateManagerImpl(Object oid, boolean copy) {
1161 // see if we're in the process of loading this oid in a loadAll call
1162 StateManagerImpl sm;
1163 if (_loading != null) {
1164 sm = (StateManagerImpl) _loading.get(oid);
1165 if (sm != null && sm.getPersistenceCapable() == null)
1166 return sm;
1167 }
1168
1169 // find metadata for the oid
1170 Class pcType = _store.getManagedType(oid);
1171 MetaDataRepository repos = _conf.getMetaDataRepositoryInstance();
1172 ClassMetaData meta;
1173 if (pcType != null)
1174 meta = repos.getMetaData(pcType, _loader, true);
1175 else
1176 meta = repos.getMetaData(oid, _loader, true);
1177
1178 // copy the oid if needed
1179 if (copy && _compat.getCopyObjectIds()) {
1180 if (meta.getIdentityType() == ClassMetaData.ID_APPLICATION)
1181 oid = ApplicationIds.copy(oid, meta);
1182 else if (meta.getIdentityType() == ClassMetaData.ID_UNKNOWN)
1183 throw new UserException(_loc.get("meta-unknownid", meta));
1184 else
1185 oid = _store.copyDataStoreId(oid, meta);
1186 }
1187
1188 sm = newStateManagerImpl(oid, meta);
1189 sm.setObjectId(oid);
1190 return sm;
1191 }
1192
1193 /**
1194 * Create a state manager for the given oid and metadata.
1195 */
1196 protected StateManagerImpl newStateManagerImpl(Object oid,
1197 ClassMetaData meta) {
1198 return new StateManagerImpl(oid, meta, this);
1199 }
1200
1201 ///////////////
1202 // Transaction
1203 ///////////////
1204
1205 public void begin() {
1206 beginOperation(true);
1207 try {
1208 if ((_flags & FLAG_ACTIVE) != 0)
1209 throw new InvalidStateException(_loc.get("active"));
1210 _factory.syncWithManagedTransaction(this, true);
1211 beginInternal();
1212 } finally {
1213 endOperation();
1214 }
1215 }
1216
1217 /**
1218 * Notify the store manager of a transaction.
1219 */
1220 private void beginInternal() {
1221 try {
1222 beginStoreManagerTransaction(_optimistic);
1223 _flags |= FLAG_ACTIVE;
1224
1225 // start locking
1226 if (!_optimistic) {
1227 _fc.setReadLockLevel(_conf.getReadLockLevelConstant());
1228 _fc.setWriteLockLevel(_conf.getWriteLockLevelConstant());
1229 _fc.setLockTimeout(_conf.getLockTimeout());
1230 }
1231 _lm.beginTransaction();
1232
1233 if (_transEventManager.hasBeginListeners())
1234 fireTransactionEvent(new TransactionEvent(this,
1235 TransactionEvent.AFTER_BEGIN, null, null, null, null));
1236 } catch (OpenJPAException ke) {
1237 // if we already started the transaction, don't let it commit
1238 if ((_flags & FLAG_ACTIVE) != 0)
1239 setRollbackOnlyInternal(ke);
1240 throw ke.setFatal(true);
1241 } catch (RuntimeException re) {
1242 // if we already started the transaction, don't let it commit
1243 if ((_flags & FLAG_ACTIVE) != 0)
1244 setRollbackOnlyInternal(re);
1245 throw new StoreException(re).setFatal(true);
1246 }
1247
1248 if (_pending != null) {
1249 StateManagerImpl sm;
1250 for (Iterator it = _pending.iterator(); it.hasNext();) {
1251 sm = (StateManagerImpl) it.next();
1252 sm.transactional();
1253 if (sm.isDirty())
1254 setDirty(sm, true);
1255 }
1256 _pending = null;
1257 }
1258 }
1259
1260 public void beginStore() {
1261 beginOperation(true);
1262 try {
1263 assertTransactionOperation();
1264 if ((_flags & FLAG_STORE_ACTIVE) == 0)
1265 beginStoreManagerTransaction(false);
1266 } catch (OpenJPAException ke) {
1267 throw ke;
1268 } catch (RuntimeException re) {
1269 throw new StoreException(re);
1270 } finally {
1271 endOperation();
1272 }
1273 }
1274
1275 /**
1276 * Begin a store manager transaction.
1277 */
1278 private void beginStoreManagerTransaction(boolean optimistic) {
1279 if (!optimistic) {
1280 retainConnection();
1281 _store.begin();
1282 _flags |= FLAG_STORE_ACTIVE;
1283 } else {
1284 if (_connRetainMode == CONN_RETAIN_TRANS)
1285 retainConnection();
1286 _store.beginOptimistic();
1287 }
1288 }
1289
1290 /**
1291 * End the current store manager transaction. Throws an
1292 * exception to signal a forced rollback after failed commit, otherwise
1293 * returns any exception encountered during the end process.
1294 */
1295 private RuntimeException endStoreManagerTransaction(boolean rollback) {
1296 boolean forcedRollback = false;
1297 boolean releaseConn = false;
1298 RuntimeException err = null;
1299 try {
1300 if ((_flags & FLAG_STORE_ACTIVE) != 0) {
1301 releaseConn = _connRetainMode != CONN_RETAIN_ALWAYS;
1302 if (rollback)
1303 _store.rollback();
1304 else
1305 _store.commit();
1306 } else {
1307 releaseConn = _connRetainMode == CONN_RETAIN_TRANS;
1308 _store.rollbackOptimistic();
1309 }
1310 }
1311 catch (RuntimeException re) {
1312 if (!rollback) {
1313 forcedRollback = true;
1314 try { _store.rollback(); } catch (RuntimeException re2) {}
1315 }
1316 err = re;
1317 } finally {
1318 _flags &= ~FLAG_STORE_ACTIVE;
1319 }
1320
1321 if (releaseConn) {
1322 try {
1323 releaseConnection();
1324 } catch (RuntimeException re) {
1325 if (err == null)
1326 err = re;
1327 }
1328 }
1329
1330 if (forcedRollback)
1331 throw err;
1332 return err;
1333 }
1334
1335 public void commit() {
1336 beginOperation(false);
1337 try {
1338 assertTransactionOperation();
1339
1340 javax.transaction.Transaction trans =
1341 _runtime.getTransactionManager().getTransaction();
1342 if (trans == null)
1343 throw new InvalidStateException(_loc.get("null-trans"));
1344
1345 // this commit on the transaction will cause our
1346 // beforeCompletion method to be invoked
1347 trans.commit();
1348 } catch (OpenJPAException ke) {
1349 if (_log.isTraceEnabled())
1350 _log.trace(_loc.get("end-trans-error"), ke);
1351 throw ke;
1352 } catch (Exception e) {
1353 if (_log.isTraceEnabled())
1354 _log.trace(_loc.get("end-trans-error"), e);
1355 throw new StoreException(e);
1356 } finally {
1357 endOperation();
1358 }
1359 }
1360
1361 public void rollback() {
1362 beginOperation(false);
1363 try {
1364 assertTransactionOperation();
1365
1366 javax.transaction.Transaction trans =
1367 _runtime.getTransactionManager().getTransaction();
1368 if (trans != null)
1369 trans.rollback();
1370 } catch (OpenJPAException ke) {
1371 if (_log.isTraceEnabled())
1372 _log.trace(_loc.get("end-trans-error"), ke);
1373 throw ke;
1374 } catch (Exception e) {
1375 if (_log.isTraceEnabled())
1376 _log.trace(_loc.get("end-trans-error"), e);
1377 throw new StoreException(e);
1378 } finally {
1379 endOperation();
1380 }
1381 }
1382
1383 public boolean syncWithManagedTransaction() {
1384 assertOpen();
1385 lock();
1386 try {
1387 if ((_flags & FLAG_ACTIVE) != 0)
1388 return true;
1389 if (!_managed)
1390 throw new InvalidStateException(_loc.get("trans-not-managed"));
1391 if (_factory.syncWithManagedTransaction(this, false)) {
1392 beginInternal();
1393 return true;
1394 }
1395 return false;
1396 } finally {
1397 unlock();
1398 }
1399 }
1400
1401 public void commitAndResume() {
1402 endAndResume(true);
1403 }
1404
1405 public void rollbackAndResume() {
1406 endAndResume(false);
1407 }
1408
1409 private void endAndResume(boolean commit) {
1410 beginOperation(false);
1411 try {
1412 if (commit)
1413 commit();
1414 else
1415 rollback();
1416 begin();
1417 } finally {
1418 endOperation();
1419 }
1420 }
1421
1422 public boolean getRollbackOnly() {
1423 beginOperation(true);
1424 try {
1425 if ((_flags & FLAG_ACTIVE) == 0)
1426 return false;
1427
1428 javax.transaction.Transaction trans =
1429 _runtime.getTransactionManager().getTransaction();
1430 if (trans == null)
1431 return false;
1432 return trans.getStatus() == Status.STATUS_MARKED_ROLLBACK;
1433 } catch (OpenJPAException ke) {
1434 throw ke;
1435 } catch (Exception e) {
1436 throw new GeneralException(e);
1437 } finally {
1438 endOperation();
1439 }
1440 }
1441
1442 public Throwable getRollbackCause() {
1443 beginOperation(true);
1444 try {
1445 if ((_flags & FLAG_ACTIVE) == 0)
1446 return null;
1447
1448 javax.transaction.Transaction trans =
1449 _runtime.getTransactionManager().getTransaction();
1450 if (trans == null)
1451 return null;
1452 if (trans.getStatus() == Status.STATUS_MARKED_ROLLBACK)
1453 return _runtime.getRollbackCause();
1454
1455 return null;
1456 } catch (OpenJPAException ke) {
1457 throw ke;
1458 } catch (Exception e) {
1459 throw new GeneralException(e);
1460 } finally {
1461 endOperation();
1462 }
1463 }
1464
1465 public void setRollbackOnly() {
1466 setRollbackOnly(new UserException());
1467 }
1468
1469 public void setRollbackOnly(Throwable cause) {
1470 beginOperation(true);
1471 try {
1472 assertTransactionOperation();
1473 setRollbackOnlyInternal(cause);
1474 } finally {
1475 endOperation();
1476 }
1477 }
1478
1479 /**
1480 * Mark the current transaction as rollback-only.
1481 */
1482 private void setRollbackOnlyInternal(Throwable cause) {
1483 try {
1484 javax.transaction.Transaction trans =
1485 _runtime.getTransactionManager().getTransaction();
1486 if (trans == null)
1487 throw new InvalidStateException(_loc.get("null-trans"));
1488 // ensure tran is in a valid state to accept the setRollbackOnly
1489 int tranStatus = trans.getStatus();
1490 if ((tranStatus != Status.STATUS_NO_TRANSACTION)
1491 && (tranStatus != Status.STATUS_ROLLEDBACK)
1492 && (tranStatus != Status.STATUS_COMMITTED))
1493 _runtime.setRollbackOnly(cause);
1494 else if (_log.isTraceEnabled())
1495 _log.trace(_loc.get("invalid-tran-status", new Integer(
1496 tranStatus), "setRollbackOnly"));
1497 } catch (OpenJPAException ke) {
1498 throw ke;
1499 } catch (Exception e) {
1500 throw new GeneralException(e);
1501 }
1502 }
1503
1504 public void setSavepoint(String name) {
1505 beginOperation(true);
1506 try {
1507 assertActiveTransaction();
1508 if (_savepoints != null && _savepoints.containsKey(name))
1509 throw new UserException(_loc.get("savepoint-exists", name));
1510
1511 if (hasFlushed() && !_spm.supportsIncrementalFlush())
1512 throw new UnsupportedException(_loc.get
1513 ("savepoint-flush-not-supported"));
1514
1515 OpenJPASavepoint save = _spm.newSavepoint(name, this);
1516 if (_savepoints == null || _savepoints.isEmpty()) {
1517 save.save(getTransactionalStates());
1518 _savepoints = new LinkedMap();
1519 } else {
1520 if (_savepointCache == null)
1521 save.save(Collections.EMPTY_LIST);
1522 else {
1523 save.save(_savepointCache);
1524 _savepointCache.clear();
1525 }
1526 }
1527 _savepoints.put(name, save);
1528 } catch (OpenJPAException ke) {
1529 throw ke;
1530 } catch (Exception e) {
1531 throw new GeneralException(e);
1532 } finally {
1533 endOperation();
1534 }
1535 }
1536
1537 public void releaseSavepoint() {
1538 beginOperation(false);
1539 try {
1540 if (_savepoints == null || _savepoints.isEmpty())
1541 throw new UserException(_loc.get("no-lastsavepoint"));
1542 releaseSavepoint((String) _savepoints.get
1543 (_savepoints.size() - 1));
1544 } finally {
1545 endOperation();
1546 }
1547 }
1548
1549 public void releaseSavepoint(String savepoint) {
1550 beginOperation(false);
1551 try {
1552 assertActiveTransaction();
1553
1554 int index = (_savepoints == null) ? -1
1555 : _savepoints.indexOf(savepoint);
1556 if (index < 0)
1557 throw new UserException(_loc.get("no-savepoint", savepoint));
1558
1559 // clear old in reverse
1560 OpenJPASavepoint save;
1561 while (_savepoints.size() > index + 1) {
1562 save = (OpenJPASavepoint) _savepoints.remove
1563 (_savepoints.size() - 1);
1564 save.release(false);
1565 }
1566
1567 save = (OpenJPASavepoint) _savepoints.remove(index);
1568 save.release(true);
1569 if (_savepointCache != null)
1570 _savepointCache.clear();
1571 } catch (OpenJPAException ke) {
1572 throw ke;
1573 } catch (Exception e) {
1574 throw new GeneralException(e);
1575 } finally {
1576 endOperation();
1577 }
1578 }
1579
1580 public void rollbackToSavepoint() {
1581 beginOperation(false);
1582 try {
1583 if (_savepoints == null || _savepoints.isEmpty())
1584 throw new UserException(_loc.get("no-lastsavepoint"));
1585 rollbackToSavepoint((String) _savepoints.get
1586 (_savepoints.size() - 1));
1587 } finally {
1588 endOperation();
1589 }
1590 }
1591
1592 public void rollbackToSavepoint(String savepoint) {
1593 beginOperation(false);
1594 try {
1595 assertActiveTransaction();
1596
1597 int index = (_savepoints == null) ? -1
1598 : _savepoints.indexOf(savepoint);
1599 if (index < 0)
1600 throw new UserException(_loc.get("no-savepoint", savepoint));
1601
1602 // clear old in reverse
1603 OpenJPASavepoint save;
1604 while (_savepoints.size() > index + 1) {
1605 save = (OpenJPASavepoint) _savepoints.remove
1606 (_savepoints.size() - 1);
1607 save.release(false);
1608 }
1609
1610 save = (OpenJPASavepoint) _savepoints.remove(index);
1611 Collection saved = save.rollback(_savepoints.values());
1612 if (_savepointCache != null)
1613 _savepointCache.clear();
1614 if (hasTransactionalObjects()) {
1615 // build up a new collection of states
1616 TransactionalCache oldTransCache = _transCache;
1617 TransactionalCache newTransCache = new TransactionalCache
1618 (_orderDirty);
1619 _transCache = null;
1620
1621 // currently there is the assumption that incremental
1622 // flush is either a) not allowed, or b) required
1623 // pre-savepoint. this solves a number of issues including
1624 // storing flushed states as well as OID handling.
1625 // if future plugins do not follow this, we need to cache
1626 // more info per state
1627 SavepointFieldManager fm;
1628 StateManagerImpl sm;
1629 for (Iterator itr = saved.iterator(); itr.hasNext();) {
1630 fm = (SavepointFieldManager) itr.next();
1631 sm = fm.getStateManager();
1632 sm.rollbackToSavepoint(fm);
1633 oldTransCache.remove(sm);
1634 if (sm.isDirty())
1635 newTransCache.addDirty(sm);
1636 else
1637 newTransCache.addClean(sm);
1638 }
1639 for (Iterator itr = oldTransCache.iterator(); itr.hasNext();) {
1640 sm = (StateManagerImpl) itr.next();
1641 sm.rollback();
1642 removeFromTransaction(sm);
1643 }
1644 _transCache = newTransCache;
1645 }
1646 }
1647 catch (OpenJPAException ke) {
1648 throw ke;
1649 } catch (Exception e) {
1650 throw new GeneralException(e);
1651 } finally {
1652 endOperation();
1653 }
1654 }
1655
1656 public void flush() {
1657 beginOperation(true);
1658 try {
1659 // return silently if no trans is active, or if this is a reentrant
1660 // call, which can happen if the store manager tries to get an
1661 // auto-inc oid during flush
1662 if ((_flags & FLAG_ACTIVE) == 0
1663 || (_flags & FLAG_STORE_FLUSHING) != 0)
1664 return;
1665
1666 // make sure the runtime supports it
1667 if (!_conf.supportedOptions().contains(_conf.OPTION_INC_FLUSH))
1668 throw new UnsupportedException(_loc.get
1669 ("incremental-flush-not-supported"));
1670 if (_savepoints != null && !_savepoints.isEmpty()
1671 && !_spm.supportsIncrementalFlush())
1672 throw new UnsupportedException(_loc.get
1673 ("savepoint-flush-not-supported"));
1674
1675 try {
1676 flushSafe(FLUSH_INC);
1677 _flags |= FLAG_FLUSHED;
1678 } catch (OpenJPAException ke) {
1679 // rollback on flush error; objects may be in inconsistent state
1680 setRollbackOnly(ke);
1681 throw ke.setFatal(true);
1682 } catch (RuntimeException re) {
1683 // rollback on flush error; objects may be in inconsistent state
1684 setRollbackOnly(re);
1685 throw new StoreException(re).setFatal(true);
1686 }
1687 }
1688 finally {
1689 endOperation();
1690 }
1691 }
1692
1693 public void preFlush() {
1694 beginOperation(true);
1695 try {
1696 if ((_flags & FLAG_ACTIVE) != 0)
1697 flushSafe(FLUSH_LOGICAL);
1698 } finally {
1699 endOperation();
1700 }
1701 }
1702
1703 public void validateChanges() {
1704 beginOperation(true);
1705 try {
1706 // if no trans, just return; if active datastore trans, flush
1707 if ((_flags & FLAG_ACTIVE) == 0)
1708 return;
1709 if ((_flags & FLAG_STORE_ACTIVE) != 0) {
1710 flush();
1711 return;
1712 }
1713
1714 // make sure the runtime supports inc flush
1715 if (!_conf.supportedOptions().contains(_conf.OPTION_INC_FLUSH))
1716 throw new UnsupportedException(_loc.get
1717 ("incremental-flush-not-supported"));
1718
1719 try {
1720 flushSafe(FLUSH_ROLLBACK);
1721 } catch (OpenJPAException ke) {
1722 throw ke;
1723 } catch (RuntimeException re) {
1724 throw new StoreException(re);
1725 }
1726 }
1727 finally {
1728 endOperation();
1729 }
1730 }
1731
1732 public boolean isActive() {
1733 beginOperation(true);
1734 try {
1735 return (_flags & FLAG_ACTIVE) != 0;
1736 } finally {
1737 endOperation();
1738 }
1739 }
1740
1741 public boolean isStoreActive() {
1742 // we need to lock here, because we might be in the middle of an
1743 // atomic transaction process (e.g., commitAndResume)
1744 beginOperation(true);
1745 try {
1746 return (_flags & FLAG_STORE_ACTIVE) != 0;
1747 } finally {
1748 endOperation();
1749 }
1750 }
1751
1752 /**
1753 * Return whether the current transaction is ending, i.e. in the 2nd phase
1754 * of a commit or rollback
1755 */
1756 boolean isTransactionEnding() {
1757 return (_flags & FLAG_TRANS_ENDING) != 0;
1758 }
1759
1760 public boolean beginOperation(boolean syncTrans) {
1761 lock();
1762 try {
1763 assertOpen();
1764
1765 if (syncTrans && _operationCount == 0 && _syncManaged
1766 && (_flags & FLAG_ACTIVE) == 0)
1767 syncWithManagedTransaction();
1768 return _operationCount++ == 1;
1769 } catch (OpenJPAException ke) {
1770 unlock();
1771 throw ke;
1772 } catch (RuntimeException re) {
1773 unlock();
1774 throw new GeneralException(re);
1775 }
1776 }
1777
1778 /**
1779 * Mark the operation over. If outermost caller of stack, returns true
1780 * and will detach managed instances if necessary.
1781 */
1782 public boolean endOperation() {
1783 try {
1784 if (_operationCount == 1 && (_autoDetach & DETACH_NONTXREAD) != 0
1785 && (_flags & FLAG_ACTIVE) == 0) {
1786 detachAllInternal(null);
1787 }
1788 if (_operationCount < 1)
1789 throw new InternalException(_loc.get("multi-threaded-access"));
1790 return _operationCount == 1;
1791 } catch (OpenJPAException ke) {
1792 throw ke;
1793 } catch (RuntimeException re) {
1794 throw new GeneralException(re);
1795 } finally {
1796 _operationCount--;
1797 if (_operationCount == 0)
1798 initializeOperatingSet();
1799 unlock();
1800 }
1801 }
1802
1803 public Synchronization getSynchronization() {
1804 return _sync;
1805 }
1806
1807 public void setSynchronization(Synchronization sync) {
1808 assertOpen();
1809 _sync = sync;
1810 }
1811
1812 ///////////////////////////////////////////////
1813 // Implementation of Synchronization interface
1814 ///////////////////////////////////////////////
1815
1816 public void beforeCompletion() {
1817 beginOperation(false);
1818 try {
1819 // user-supplied synchronization
1820 if (_sync != null)
1821 _sync.beforeCompletion();
1822
1823 flushSafe(FLUSH_COMMIT);
1824 } catch (OpenJPAException ke) {
1825 if (_log.isTraceEnabled())
1826 _log.trace(_loc.get("end-trans-error"), ke);
1827 throw translateManagedCompletionException(ke);
1828 } catch (RuntimeException re) {
1829 if (_log.isTraceEnabled())
1830 _log.trace(_loc.get("end-trans-error"), re);
1831 throw translateManagedCompletionException(new StoreException(re));
1832 } finally {
1833 endOperation();
1834 }
1835 }
1836
1837 public void afterCompletion(int status) {
1838 beginOperation(false);
1839 try {
1840 assertActiveTransaction();
1841
1842 _flags |= FLAG_TRANS_ENDING;
1843 endTransaction(status);
1844 if (_sync != null)
1845 _sync.afterCompletion(status);
1846
1847 if ((_autoDetach & DETACH_COMMIT) != 0)
1848 detachAllInternal(null);
1849 else if (status == Status.STATUS_ROLLEDBACK
1850 && (_autoDetach & DETACH_ROLLBACK) != 0) {
1851 detachAllInternal(null);
1852 }
1853
1854 // in an ee context, it's possible that the user tried to close
1855 // us but we didn't actually close because we were waiting on this
1856 // transaction; if that's true, then close now
1857 if ((_flags & FLAG_CLOSE_INVOKED) != 0
1858 && _compat.getCloseOnManagedCommit())
1859 free();
1860 } catch (OpenJPAException ke) {
1861 if (_log.isTraceEnabled())
1862 _log.trace(_loc.get("end-trans-error"), ke);
1863 throw translateManagedCompletionException(ke);
1864 } catch (RuntimeException re) {
1865 if (_log.isTraceEnabled())
1866 _log.trace(_loc.get("end-trans-error"), re);
1867 throw translateManagedCompletionException(new StoreException(re));
1868 } finally {
1869 _flags &= ~FLAG_ACTIVE;
1870 _flags &= ~FLAG_FLUSHED;
1871 _flags &= ~FLAG_TRANS_ENDING;
1872
1873 // event manager nulled if freed broker
1874 if (_transEventManager != null
1875 && _transEventManager.hasEndListeners()) {
1876 fireTransactionEvent(new TransactionEvent(this,
1877 status == Status.STATUS_COMMITTED
1878 ? TransactionEvent.AFTER_COMMIT_COMPLETE
1879 : TransactionEvent.AFTER_ROLLBACK_COMPLETE,
1880 null, null, null, null));
1881 }
1882
1883 endOperation();
1884 }
1885 }
1886
1887 /**
1888 * If we're in a managed transaction, use our implicit behavior exception
1889 * translator to translate before/afterCompletion callback errors.
1890 */
1891 private RuntimeException translateManagedCompletionException
1892 (RuntimeException re) {
1893 return (!_managed || _extrans == null) ? re : _extrans.translate(re);
1894 }
1895
1896 /**
1897 * Flush safely, catching reentrant calls.
1898 */
1899 private void flushSafe(int reason) {
1900 if ((_flags & FLAG_FLUSHING) != 0)
1901 throw new InvalidStateException(_loc.get("reentrant-flush"));
1902
1903 _flags |= FLAG_FLUSHING;
1904 try {
1905 flush(reason);
1906 } finally {
1907 _flags &= ~FLAG_FLUSHING;
1908 }
1909 }
1910
1911 /**
1912 * Flush the transactional state to the data store. Subclasses that
1913 * customize commit behavior should override this method. The method
1914 * assumes that the persistence manager is locked, is not closed,
1915 * and has an active transaction.
1916 *
1917 * @param reason one of {@link #FLUSH_INC}, {@link #FLUSH_COMMIT},
1918 * {@link #FLUSH_ROLLBACK}, or {@link #FLUSH_LOGICAL}
1919 * @since 0.2.5
1920 */
1921 protected void flush(int reason) {
1922 // this will enlist proxied states as necessary so we know whether we
1923 // have anything to flush
1924 Collection transactional = getTransactionalStates();
1925
1926 // do we actually have to flush? only if our flags say so, or if
1927 // we have transaction listeners that need to be invoked for commit
1928 // (no need to invoke them on inc flush if nothing is dirty). we
1929 // special case the remote commit listener used by the datacache cause
1930 // we know it doesn't require the commit event when nothing changes
1931 boolean flush = (_flags & FLAG_FLUSH_REQUIRED) != 0;
1932 boolean listeners = (_transEventManager.hasFlushListeners()
1933 || _transEventManager.hasEndListeners())
1934 && ((_flags & FLAG_REMOTE_LISTENER) == 0
1935 || _transEventManager.getListeners().size() > 1);
1936 if (!flush && (reason != FLUSH_COMMIT || !listeners))
1937 return;
1938
1939 Collection mobjs = null;
1940 _flags |= FLAG_PRESTORING;
1941 try {
1942 if (flush) {
1943 // call pre store on all currently transactional objs
1944 for (Iterator itr = transactional.iterator(); itr.hasNext();)
1945 ((StateManagerImpl) itr.next()).beforeFlush(reason, _call);
1946 flushAdditions(transactional, reason);
1947 }
1948
1949 // hopefully now all dependent instances that are going to end
1950 // up referenced have been marked as such; delete unrefed
1951 // dependents
1952 _flags |= FLAG_DEREFDELETING;
1953 if (flush && _derefCache != null && !_derefCache.isEmpty()) {
1954 for (Iterator itr = _derefCache.iterator(); itr.hasNext();)
1955 deleteDeref((StateManagerImpl) itr.next());
1956 flushAdditions(transactional, reason);
1957 }
1958
1959 if (reason != FLUSH_LOGICAL) {
1960 // if no datastore transaction, start one; even if we don't
1961 // think we'll need to flush at this point, our transaction
1962 // listeners might introduce some dirty objects or interact
1963 // directly with the database
1964 if ((_flags & FLAG_STORE_ACTIVE) == 0)
1965 beginStoreManagerTransaction(false);
1966
1967 if ((_transEventManager.hasFlushListeners()
1968 || _transEventManager.hasEndListeners())
1969 && (flush || reason == FLUSH_COMMIT)) {
1970 // fire events
1971 mobjs = new ManagedObjectCollection(transactional);
1972 if (reason == FLUSH_COMMIT
1973 && _transEventManager.hasEndListeners()) {
1974 fireTransactionEvent(new TransactionEvent(this,
1975 TransactionEvent.BEFORE_COMMIT, mobjs,
1976 _persistedClss, _updatedClss, _deletedClss));
1977
1978 flushAdditions(transactional, reason);
1979 flush = (_flags & FLAG_FLUSH_REQUIRED) != 0;
1980 }
1981
1982 if (flush && _transEventManager.hasFlushListeners()) {
1983 fireTransactionEvent(new TransactionEvent(this,
1984 TransactionEvent.BEFORE_FLUSH, mobjs,
1985 _persistedClss, _updatedClss, _deletedClss));
1986 flushAdditions(transactional, reason);
1987 }
1988 }
1989 }
1990 }
1991 finally {
1992 _flags &= ~FLAG_PRESTORING;
1993 _flags &= ~FLAG_DEREFDELETING;
1994 _transAdditions = null;
1995 _derefAdditions = null;
1996
1997 // also clear derefed set; the deletes have been recorded
1998 if (_derefCache != null)
1999 _derefCache = null;
2000 }
2001
2002 // flush to store manager
2003 List exceps = null;
2004 try {
2005 if (flush && reason != FLUSH_LOGICAL) {
2006 _flags |= FLAG_STORE_FLUSHING;
2007 exceps = add(exceps,
2008 newFlushException(_store.flush(transactional)));
2009 }
2010 } finally {
2011 _flags &= ~FLAG_STORE_FLUSHING;
2012
2013 if (reason == FLUSH_ROLLBACK)
2014 exceps = add(exceps, endStoreManagerTransaction(true));
2015 else if (reason != FLUSH_LOGICAL)
2016 _flags &= ~FLAG_FLUSH_REQUIRED;
2017
2018 // mark states as flushed
2019 if (flush) {
2020 StateManagerImpl sm;
2021 for (Iterator itr = transactional.iterator(); itr.hasNext();) {
2022 sm = (StateManagerImpl) itr.next();
2023 try {
2024 // the state may have become transient, such as if
2025 // it is embedded and the owner has been deleted during
2026 // this flush process; bug #1100
2027 if (sm.getPCState() == PCState.TRANSIENT)
2028 continue;
2029
2030 sm.afterFlush(reason);
2031 if (reason == FLUSH_INC) {
2032 // if not about to clear trans cache for commit
2033 // anyway, re-cache dirty objects with default soft
2034 // refs; we don't need hard refs now that the
2035 // changes have been flushed
2036 sm.proxyFields(true, false);
2037 _transCache.flushed(sm);
2038 }
2039 } catch (Exception e) {
2040 exceps = add(exceps, e);
2041 }
2042 }
2043 }
2044 }
2045
2046 // throw any exceptions to shortcut listeners on fail
2047 throwNestedExceptions(exceps, true);
2048
2049 if (flush && reason != FLUSH_ROLLBACK && reason != FLUSH_LOGICAL
2050 && _transEventManager.hasFlushListeners()) {
2051 fireTransactionEvent(new TransactionEvent(this,
2052 TransactionEvent.AFTER_FLUSH, mobjs, _persistedClss,
2053 _updatedClss, _deletedClss));
2054 }
2055 }
2056
2057 /**
2058 * Flush newly-transactional objects.
2059 */
2060 private void flushAdditions(Collection transactional, int reason) {
2061 boolean loop;
2062 do {
2063 // flush new transactional instances; note logical or
2064 loop = flushTransAdditions(transactional, reason)
2065 | deleteDerefAdditions(_derefCache);
2066 } while (loop);
2067 }
2068
2069 /**
2070 * Flush transactional additions.
2071 */
2072 private boolean flushTransAdditions(Collection transactional, int reason) {
2073 if (_transAdditions == null || _transAdditions.isEmpty())
2074 return false;
2075
2076 // keep local transactional list copy up to date
2077 transactional.addAll(_transAdditions);
2078
2079 // copy the change set, then clear it for the next iteration
2080 StateManagerImpl[] states = (StateManagerImpl[]) _transAdditions.
2081 toArray(new StateManagerImpl[_transAdditions.size()]);
2082 _transAdditions = null;
2083
2084 for (int i = 0; i < states.length; i++)
2085 states[i].beforeFlush(reason, _call);
2086 return true;
2087 }
2088
2089 /**
2090 * Delete new dereferenced objects.
2091 */
2092 private boolean deleteDerefAdditions(Collection derefs) {
2093 if (_derefAdditions == null || _derefAdditions.isEmpty())
2094 return false;
2095
2096 // remember these additions in case one becomes derefed again later
2097 derefs.addAll(_derefAdditions);
2098
2099 StateManagerImpl[] states = (StateManagerImpl[]) _derefAdditions.
2100 toArray(new StateManagerImpl[_derefAdditions.size()]);
2101 _derefAdditions = null;
2102
2103 for (int i = 0; i < states.length; i++)
2104 deleteDeref(states[i]);
2105 return true;
2106 }
2107
2108 /**
2109 * Delete a dereferenced dependent.
2110 */
2111 private void deleteDeref(StateManagerImpl sm) {
2112 int action = processArgument(OpCallbacks.OP_DELETE,
2113 sm.getManagedInstance(), sm, null);
2114 if ((action & OpCallbacks.ACT_RUN) != 0)
2115 sm.delete();
2116 if ((action & OpCallbacks.ACT_CASCADE) != 0)
2117 sm.cascadeDelete(_call);
2118 }
2119
2120 /**
2121 * Determine the action to take based on the user's given callbacks and
2122 * our implicit behavior.
2123 */
2124 private int processArgument(int op, Object obj, OpenJPAStateManager sm,
2125 OpCallbacks call) {
2126 if (call != null)
2127 return call.processArgument(op, obj, sm);
2128 if (_call != null)
2129 return _call.processArgument(op, obj, sm);
2130 return OpCallbacks.ACT_RUN | OpCallbacks.ACT_CASCADE;
2131 }
2132
2133 /**
2134 * Throw the proper exception based on the given set of flush errors, or
2135 * do nothing if no errors occurred.
2136 */
2137 private OpenJPAException newFlushException(Collection exceps) {
2138 if (exceps == null || exceps.isEmpty())
2139 return null;
2140
2141 Throwable[] t = (Throwable[]) exceps.toArray
2142 (new Throwable[exceps.size()]);
2143 List failed = new ArrayList(t.length);
2144
2145 // create fatal exception with nested exceptions for all the failed
2146 // objects; if all OL exceptions, throw a top-level OL exception
2147 boolean opt = true;
2148 for (int i = 0; opt && i < t.length; i++) {
2149 opt = t[i] instanceof OptimisticException;
2150 if (opt) {
2151 Object f = ((OptimisticException) t[i]).getFailedObject();
2152 if (f != null)
2153 failed.add(f);
2154 }
2155 }
2156 if (opt && !failed.isEmpty())
2157 return new OptimisticException(failed, t);
2158 if (opt)
2159 return new OptimisticException(t);
2160 return new StoreException(_loc.get("rolled-back")).
2161 setNestedThrowables(t).setFatal(true);
2162 }
2163
2164 /**
2165 * End the current transaction, making appropriate state transitions.
2166 */
2167 protected void endTransaction(int status) {
2168 // if a data store transaction was in progress, do the
2169 // appropriate transaction change
2170 boolean rollback = status != Status.STATUS_COMMITTED;
2171 List exceps = null;
2172
2173 try {
2174 exceps = add(exceps, endStoreManagerTransaction(rollback));
2175 } catch (RuntimeException re) {
2176 rollback = true;
2177 exceps = add(exceps, re);
2178 }
2179
2180 // go back to default none lock level
2181 _fc.setReadLockLevel(LOCK_NONE);
2182 _fc.setWriteLockLevel(LOCK_NONE);
2183 _fc.setLockTimeout(-1);
2184
2185 Collection transStates;
2186 if (hasTransactionalObjects())
2187 transStates = _transCache;
2188 else
2189 transStates = Collections.EMPTY_LIST;
2190
2191 // fire after rollback/commit event
2192 Collection mobjs = null;
2193 if (_transEventManager.hasEndListeners()) {
2194 mobjs = new ManagedObjectCollection(transStates);
2195 int eventType = (rollback) ? TransactionEvent.AFTER_ROLLBACK
2196 : TransactionEvent.AFTER_COMMIT;
2197 fireTransactionEvent(new TransactionEvent(this, eventType, mobjs,
2198 _persistedClss, _updatedClss, _deletedClss));
2199 }
2200
2201 // null transactional caches now so that all the removeFromTransaction
2202 // calls as we transition each object don't have to do any work; don't
2203 // clear trans cache object because we still need the transStates
2204 // reference to it below
2205 _transCache = null;
2206 if (_persistedClss != null)
2207 _persistedClss = null;
2208 if (_updatedClss != null)
2209 _updatedClss = null;
2210 if (_deletedClss != null)
2211 _deletedClss = null;
2212
2213 // new cache would get cleared anyway during transitions, but doing so
2214 // immediately saves us some lookups
2215 _cache.clearNew();
2216
2217 // tell all derefed instances they're no longer derefed; we can't
2218 // rely on rollback and commit calls below cause some instances might
2219 // not be transactional
2220 if (_derefCache != null && !_derefCache.isEmpty()) {
2221 for (Iterator itr = _derefCache.iterator(); itr.hasNext();)
2222 ((StateManagerImpl) itr.next()).setDereferencedDependent
2223 (false, false);
2224 _derefCache = null;
2225 }
2226
2227 // peform commit or rollback state transitions on each instance
2228 StateManagerImpl sm;
2229 for (Iterator itr = transStates.iterator(); itr.hasNext();) {
2230 sm = (StateManagerImpl) itr.next();
2231 try {
2232 if (rollback) {
2233 // tell objects that may have been derefed then flushed
2234 // (and therefore deleted) to un-deref
2235 sm.setDereferencedDependent(false, false);
2236 sm.rollback();
2237 } else
2238 sm.commit();
2239 } catch (RuntimeException re) {
2240 exceps = add(exceps, re);
2241 }
2242 }
2243
2244 // notify the lock manager to clean up and release remaining locks
2245 _lm.endTransaction();
2246
2247 // clear old savepoints in reverse
2248 OpenJPASavepoint save;
2249 while (_savepoints != null && _savepoints.size() > 0) {
2250 save =
2251 (OpenJPASavepoint) _savepoints.remove(_savepoints.size() - 1);
2252 save.release(false);
2253 }
2254 _savepoints = null;
2255 _savepointCache = null;
2256
2257 // fire after state change event
2258 if (_transEventManager.hasEndListeners())
2259 fireTransactionEvent(new TransactionEvent(this, TransactionEvent.
2260 AFTER_STATE_TRANSITIONS, mobjs, null, null, null));
2261
2262 // now clear trans cache; keep cleared version rather than
2263 // null to avoid having to re-create the set later; more efficient
2264 if (transStates != Collections.EMPTY_LIST) {
2265 _transCache = (TransactionalCache) transStates;
2266 _transCache.clear();
2267 }
2268
2269 throwNestedExceptions(exceps, true);
2270 }
2271
2272 ////////////////////
2273 // Object lifecycle
2274 ////////////////////
2275
2276 public void persist(Object obj, OpCallbacks call) {
2277 persist(obj, null, true, call);
2278 }
2279
2280 public OpenJPAStateManager persist(Object obj, Object id,
2281 OpCallbacks call) {
2282 return persist(obj, id, true, call);
2283 }
2284
2285 public void persistAll(Collection objs, OpCallbacks call) {
2286 persistAll(objs, true, call);
2287 }
2288
2289 /**
2290 * Persist the given objects. Indicate whether this was an explicit persist
2291 * (PNEW) or a provisonal persist (PNEWPROVISIONAL).
2292 */
2293 public void persistAll(Collection objs, boolean explicit,
2294 OpCallbacks call) {
2295 if (objs.isEmpty())
2296 return;
2297
2298 beginOperation(true);
2299 List exceps = null;
2300 try {
2301 assertWriteOperation();
2302
2303 for (Iterator itr = objs.iterator(); itr.hasNext();) {
2304 try {
2305 persist(itr.next(), explicit, call);
2306 } catch (UserException ue) {
2307 exceps = add(exceps, ue);
2308 }
2309 }
2310 } finally {
2311 endOperation();
2312 }
2313 throwNestedExceptions(exceps, false);
2314 }
2315
2316 /**
2317 * If the given element is not null, add it to the given list,
2318 * creating the list if necessary.
2319 */
2320 private List add(List l, Object o) {
2321 if (o == null)
2322 return l;
2323 if (l == null)
2324 l = new LinkedList();
2325 l.add(o);
2326 return l;
2327 }
2328
2329 /**
2330 * Throw an exception wrapping the given nested exceptions.
2331 */
2332 private void throwNestedExceptions(List exceps, boolean datastore) {
2333 if (exceps == null || exceps.isEmpty())
2334 return;
2335 if (datastore && exceps.size() == 1)
2336 throw (RuntimeException) exceps.get(0);
2337
2338 boolean fatal = false;
2339 Throwable[] t = (Throwable[]) exceps.toArray
2340 (new Throwable[exceps.size()]);
2341 for (int i = 0; i < t.length; i++) {
2342 if (t[i] instanceof OpenJPAException
2343 && ((OpenJPAException) t[i]).isFatal())
2344 fatal = true;
2345 }
2346 OpenJPAException err;
2347 if (datastore)
2348 err = new StoreException(_loc.get("nested-exceps"));
2349 else
2350 err = new UserException(_loc.get("nested-exceps"));
2351 throw err.setNestedThrowables(t).setFatal(fatal);
2352 }
2353
2354 /**
2355 * Persist the given object. Indicate whether this was an explicit persist
2356 * (PNEW) or a provisonal persist (PNEWPROVISIONAL)
2357 */
2358 public void persist(Object obj, boolean explicit, OpCallbacks call) {
2359 persist(obj, null, explicit, call);
2360 }
2361
2362 /**
2363 * Persist the given object. Indicate whether this was an explicit persist
2364 * (PNEW) or a provisonal persist (PNEWPROVISIONAL).
2365 * See {@link Broker} for details on this method.
2366 */
2367 public OpenJPAStateManager persist(Object obj, Object id, boolean explicit,
2368 OpCallbacks call) {
2369 if (obj == null)
2370 return null;
2371
2372 beginOperation(true);
2373 try {
2374 assertWriteOperation();
2375
2376 StateManagerImpl sm = getStateManagerImpl(obj, true);
2377 if (!_operating.add(obj))
2378 return sm;
2379
2380 int action = processArgument(OpCallbacks.OP_PERSIST, obj, sm, call);
2381 if (action == OpCallbacks.ACT_NONE)
2382 return sm;
2383
2384 // ACT_CASCADE
2385 if ((action & OpCallbacks.ACT_RUN) == 0) {
2386 if (sm != null)
2387 sm.cascadePersist(call);
2388 else
2389 cascadeTransient(OpCallbacks.OP_PERSIST, obj, call,
2390 "persist");
2391 return sm;
2392 }
2393
2394 // ACT_RUN
2395 PersistenceCapable pc;
2396 if (sm != null) {
2397 if (sm.isDetached())
2398 throw new ObjectExistsException(_loc.get
2399 ("persist-detached", Exceptions.toString(obj))).
2400 setFailedObject(obj);
2401
2402 if (!sm.isEmbedded()) {
2403 sm.persist();
2404 _cache.persist(sm);
2405 if ((action & OpCallbacks.ACT_CASCADE) != 0)
2406 sm.cascadePersist(call);
2407 return sm;
2408 }
2409
2410 // an embedded field; notify the owner that the value has
2411 // changed by becoming independently persistent
2412 sm.getOwner().dirty(sm.getOwnerIndex());
2413 _cache.persist(sm);
2414 pc = sm.getPersistenceCapable();
2415 } else {
2416 pc = assertPersistenceCapable(obj);
2417 if (pc.pcIsDetached() == Boolean.TRUE)
2418 throw new ObjectExistsException(_loc.get
2419 ("persist-detached", Exceptions.toString(obj))).
2420 setFailedObject(obj);
2421 }
2422
2423 ClassMetaData meta = _conf.getMetaDataRepositoryInstance().
2424 getMetaData(obj.getClass(), _loader, true);
2425 fireLifecycleEvent(obj, null, meta, LifecycleEvent.BEFORE_PERSIST);
2426
2427 // create id for instance
2428 if (id == null) {
2429 if (meta.getIdentityType() == ClassMetaData.ID_APPLICATION)
2430 id = ApplicationIds.create(pc, meta);
2431 else if (meta.getIdentityType() == ClassMetaData.ID_UNKNOWN)
2432 throw new UserException(_loc.get("meta-unknownid", meta));
2433 else
2434 id = StateManagerId.newInstance(this);
2435 }
2436
2437 // make sure we don't already have the instance cached
2438 checkForDuplicateId(id, obj);
2439
2440 // if had embedded sm, null it
2441 if (sm != null)
2442 pc.pcReplaceStateManager(null);
2443
2444 // create new sm
2445 sm = new StateManagerImpl(id, meta, this);
2446 if ((_flags & FLAG_ACTIVE) != 0) {
2447 if (explicit)
2448 sm.initialize(pc, PCState.PNEW);
2449 else
2450 sm.initialize(pc, PCState.PNEWPROVISIONAL);
2451 } else
2452 sm.initialize(pc, PCState.PNONTRANSNEW);
2453 if ((action & OpCallbacks.ACT_CASCADE) != 0)
2454 sm.cascadePersist(call);
2455 return sm;
2456 } catch (OpenJPAException ke) {
2457 throw ke;
2458 } catch (RuntimeException re) {
2459 throw new GeneralException(re);
2460 } finally {
2461 endOperation();
2462 }
2463 }
2464
2465 /**
2466 * Temporarily manage the given instance in order to cascade the given
2467 * operation through it.
2468 */
2469 private void cascadeTransient(int op, Object obj, OpCallbacks call,
2470 String errOp) {
2471 PersistenceCapable pc = assertPersistenceCapable(obj);
2472
2473 // if using detached state manager, don't replace
2474 if (pc.pcGetStateManager() != null)
2475 throw newDetachedException(obj, errOp);
2476
2477 ClassMetaData meta = _conf.getMetaDataRepositoryInstance().
2478 getMetaData(obj.getClass(), _loader, true);
2479 StateManagerImpl sm = new StateManagerImpl(StateManagerId.
2480 newInstance(this), meta, this);
2481 sm.initialize(pc, PCState.TLOADED);
2482 try {
2483 switch (op) {
2484 case OpCallbacks.OP_PERSIST:
2485 sm.cascadePersist(call);
2486 break;
2487 case OpCallbacks.OP_DELETE:
2488 sm.cascadeDelete(call);
2489 break;
2490 case OpCallbacks.OP_REFRESH:
2491 sm.gatherCascadeRefresh(call);
2492 break;
2493 default:
2494 throw new InternalException(String.valueOf(op));
2495 }
2496 }
2497 finally {
2498 sm.release(true);
2499 }
2500 }
2501
2502 public void deleteAll(Collection objs, OpCallbacks call) {
2503 beginOperation(true);
2504 try {
2505 assertWriteOperation();
2506
2507 List exceps = null;
2508 Object obj;
2509 for (Iterator itr = objs.iterator(); itr.hasNext();) {
2510 try {
2511 obj = itr.next();
2512 if (obj != null)
2513 delete(obj, getStateManagerImpl(obj, true), call);
2514 } catch (UserException ue) {
2515 exceps = add(exceps, ue);
2516 }
2517 }
2518 throwNestedExceptions(exceps, false);
2519 } finally {
2520 endOperation();
2521 }
2522 }
2523
2524 public void delete(Object obj, OpCallbacks call) {
2525 if (obj == null)
2526 return;
2527
2528 beginOperation(true);
2529 try {
2530 assertWriteOperation();
2531 delete(obj, getStateManagerImpl(obj, true), call);
2532 } catch (OpenJPAException ke) {
2533 throw ke;
2534 } catch (RuntimeException re) {
2535 throw new GeneralException(re);
2536 } finally {
2537 endOperation();
2538 }
2539 }
2540
2541 /**
2542 * Internal delete.
2543 */
2544 void delete(Object obj, StateManagerImpl sm, OpCallbacks call) {
2545 if (!_operating.add(obj))
2546 return;
2547
2548 int action = processArgument(OpCallbacks.OP_DELETE, obj, sm, call);
2549 if (action == OpCallbacks.ACT_NONE)
2550 return;
2551
2552 // ACT_CASCADE
2553 if ((action & OpCallbacks.ACT_RUN) == 0) {
2554 if (sm != null)
2555 sm.cascadeDelete(call);
2556 else
2557 cascadeTransient(OpCallbacks.OP_DELETE, obj, call, "delete");
2558 return;
2559 }
2560
2561 // ACT_RUN
2562 if (sm != null) {
2563 if (sm.isDetached())
2564 throw newDetachedException(obj, "delete");
2565 if ((action & OpCallbacks.ACT_CASCADE) != 0)
2566 sm.cascadeDelete(call);
2567 sm.delete();
2568 } else if (assertPersistenceCapable(obj).pcIsDetached() == Boolean.TRUE)
2569 throw newDetachedException(obj, "delete");
2570 }
2571
2572 /**
2573 * Throw an exception indicating that the current action can't be
2574 * performed on a detached object.
2575 */
2576 private OpenJPAException newDetachedException(Object obj,
2577 String operation) {
2578 throw new UserException(_loc.get("bad-detached-op", operation,
2579 Exceptions.toString(obj))).setFailedObject(obj);
2580 }
2581
2582 public void releaseAll(Collection objs, OpCallbacks call) {
2583 beginOperation(false);
2584 try {
2585 List exceps = null;
2586 for (Iterator itr = objs.iterator(); itr.hasNext();) {
2587 try {
2588 release(itr.next(), call);
2589 } catch (UserException ue) {
2590 exceps = add(exceps, ue);
2591 }
2592 }
2593 throwNestedExceptions(exceps, false);
2594 } finally {
2595 endOperation();
2596 }
2597 }
2598
2599 public void release(Object obj, OpCallbacks call) {
2600 if (obj == null)
2601 return;
2602
2603 beginOperation(false);
2604 try {
2605 StateManagerImpl sm = getStateManagerImpl(obj, true);
2606 int action = processArgument(OpCallbacks.OP_RELEASE, obj, sm, call);
2607
2608 if (sm == null)
2609 return;
2610 if ((action & OpCallbacks.ACT_RUN) != 0 && sm.isPersistent()) {
2611 boolean pending = sm.isPendingTransactional();
2612 sm.release(true);
2613 if (pending)
2614 removeFromPendingTransaction(sm);
2615 }
2616 }
2617 catch (OpenJPAException ke) {
2618 throw ke;
2619 } catch (RuntimeException re) {
2620 throw new GeneralException(re);
2621 } finally {
2622 endOperation();
2623 }
2624 }
2625
2626 public OpenJPAStateManager embed(Object obj, Object id,
2627 OpenJPAStateManager owner, ValueMetaData ownerMeta) {
2628 beginOperation(true);
2629 try {
2630 StateManagerImpl orig = getStateManagerImpl(obj, true);
2631 if (orig != null) {
2632 // if already embedded, nothing to do
2633 if (orig.getOwner() == owner && orig.getMetaData().
2634 getEmbeddingMetaData() == ownerMeta)
2635 return orig;
2636
2637 // otherwise make sure pc is fully loaded for when we copy its
2638 // data below
2639 orig.load(_fc, StateManagerImpl.LOAD_ALL, null, null, false);
2640 }
2641
2642 // create new state manager with embedded metadata
2643 ClassMetaData meta = ownerMeta.getEmbeddedMetaData();
2644 if (meta == null)
2645 throw new InternalException(_loc.get("bad-embed", ownerMeta));
2646
2647 if (id == null)
2648 id = StateManagerId.newInstance(this);
2649
2650 StateManagerImpl sm = new StateManagerImpl(id, meta, this);
2651 sm.setOwner((StateManagerImpl) owner, ownerMeta);
2652
2653 PersistenceCapable copy;
2654 PCState state;
2655 Class type = meta.getDescribedType();
2656 if (obj != null) {
2657 // give copy and the original instance the same state manager
2658 // so that we can copy fields from one to the other
2659 StateManagerImpl copySM;
2660 PersistenceCapable pc;
2661 if (orig == null) {
2662 copySM = sm;
2663 pc = assertPersistenceCapable(obj);
2664 pc.pcReplaceStateManager(sm);
2665 } else {
2666 copySM = orig;
2667 pc = orig.getPersistenceCapable();
2668 }
2669
2670 try {
2671 // copy the instance. we do this even if it doesn't already
2672 // have a state manager in case it is later assigned to a
2673 // PC field; at that point it's too late to copy
2674 copy = PCRegistry.newInstance(type, copySM, false);
2675 int[] fields = new int[meta.getFields().length];
2676 for (int i = 0; i < fields.length; i++)
2677 fields[i] = i;
2678 copy.pcCopyFields(pc, fields);
2679 state = PCState.ECOPY;
2680 copy.pcReplaceStateManager(null);
2681 } finally {
2682 // if the instance didn't have a state manager to start,
2683 // revert it to being transient
2684 if (orig == null)
2685 pc.pcReplaceStateManager(null);
2686 }
2687 } else {
2688 copy = PCRegistry.newInstance(type, sm, false);
2689 if ((_flags & FLAG_ACTIVE) != 0 && !_optimistic)
2690 state = PCState.ECLEAN;
2691 else
2692 state = PCState.ENONTRANS;
2693 }
2694
2695 sm.initialize(copy, state);
2696 return sm;
2697 } catch (OpenJPAException ke) {
2698 throw ke;
2699 } catch (RuntimeException re) {
2700 throw new GeneralException(re);
2701 } finally {
2702 endOperation();
2703 }
2704 }
2705
2706 /**
2707 * If not already cached, create an empty copy of the given state
2708 * manager in the given state.
2709 */
2710 OpenJPAStateManager copy(OpenJPAStateManager copy, PCState state) {
2711 beginOperation(true);
2712 try {
2713 assertOpen();
2714 Object oid = copy.fetchObjectId();
2715 Class type = copy.getManagedInstance().getClass();
2716 if (oid == null)
2717 throw new InternalException();
2718 // cached instance?
2719 StateManagerImpl sm = null;
2720 if (!copy.isEmbedded())
2721 sm = getStateManagerImplById(oid, true);
2722 if (sm == null) {
2723 MetaDataRepository repos = _conf.
2724 getMetaDataRepositoryInstance();
2725 ClassMetaData meta = repos.getMetaData(type, _loader, true);
2726 // construct a new state manager with all info known
2727 sm = new StateManagerImpl(oid, meta, this);
2728 sm.setObjectId(oid);
2729 sm.initialize(sm.getMetaData().getDescribedType(), state);
2730 }
2731 return sm;
2732 } finally {
2733 endOperation();
2734 }
2735 }
2736
2737 public void refreshAll(Collection objs, OpCallbacks call) {
2738 if (objs.isEmpty())
2739 return;
2740
2741 beginOperation(true);
2742 try {
2743 assertNontransactionalRead();
2744
2745 for (Iterator itr = objs.iterator(); itr.hasNext();)
2746 gatherCascadeRefresh(itr.next(), call);
2747 if (_operating.isEmpty())
2748 return;
2749 if (_operating.size() == 1)
2750 refreshInternal(_operating.iterator().next(), call);
2751 else
2752 refreshInternal(_operating, call);
2753 } finally {
2754 endOperation();
2755 }
2756 }
2757
2758 public void refresh(Object obj, OpCallbacks call) {
2759 if (obj == null)
2760 return;
2761
2762 beginOperation(true);
2763 try {
2764 assertNontransactionalRead();
2765
2766 gatherCascadeRefresh(obj, call);
2767 if (_operating.isEmpty())
2768 return;
2769 if (_operating.size() == 1)
2770 refreshInternal(_operating.iterator().next(), call);
2771 else
2772 refreshInternal(_operating, call);
2773 } finally {
2774 endOperation();
2775 }
2776 }
2777
2778 /**
2779 * Gathers all objects reachable through cascade-refresh relations
2780 * into the operating set.
2781 */
2782 void gatherCascadeRefresh(Object obj, OpCallbacks call) {
2783 if (obj == null)
2784 return;
2785 if (!_operating.add(obj))
2786 return;
2787
2788 StateManagerImpl sm = getStateManagerImpl(obj, false);
2789 int action = processArgument(OpCallbacks.OP_REFRESH, obj, sm, call);
2790 if ((action & OpCallbacks.ACT_CASCADE) == 0)
2791 return;
2792
2793 if (sm != null)
2794 sm.gatherCascadeRefresh(call);
2795 else
2796 cascadeTransient(OpCallbacks.OP_REFRESH, obj, call, "refresh");
2797 }
2798
2799 /**
2800 * This method is called with the full set of objects reachable via
2801 * cascade-refresh relations from the user-given instances.
2802 */
2803 protected void refreshInternal(Collection objs, OpCallbacks call) {
2804 List exceps = null;
2805 try {
2806 // collect instances that need a refresh
2807 Collection load = null;
2808 StateManagerImpl sm;
2809 Object obj;
2810 for (Iterator itr = objs.iterator(); itr.hasNext();) {
2811 obj = itr.next();
2812 if (obj == null)
2813 continue;
2814
2815 try {
2816 sm = getStateManagerImpl(obj, true);
2817 if ((processArgument(OpCallbacks.OP_REFRESH, obj, sm, call)
2818 & OpCallbacks.ACT_RUN) == 0)
2819 continue;
2820
2821 if (sm != null) {
2822 if (sm.isDetached())
2823 throw newDetachedException(obj, "refresh");
2824 else if (sm.beforeRefresh(true)) {
2825 if (load == null)
2826 load = new ArrayList(objs.size());
2827 load.add(sm);
2828 }
2829 } else if (assertPersistenceCapable(obj).pcIsDetached()
2830 == Boolean.TRUE)
2831 throw newDetachedException(obj, "refresh");
2832 } catch (OpenJPAException ke) {
2833 exceps = add(exceps, ke);
2834 }
2835 }
2836
2837 // refresh all
2838 if (load != null) {
2839 Collection failed = _store.loadAll(load, null,
2840 StoreManager.FORCE_LOAD_REFRESH, _fc, null);
2841 if (failed != null && !failed.isEmpty())
2842 exceps = add(exceps, newObjectNotFoundException(failed));
2843
2844 // perform post-refresh transitions and make sure all fetch
2845 // group fields are loaded
2846 for (Iterator itr = load.iterator(); itr.hasNext();) {
2847 sm = (StateManagerImpl) itr.next();
2848 if (failed != null && failed.contains(sm.getId()))
2849 continue;
2850
2851 try {
2852 sm.afterRefresh();
2853 sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null,
2854 false);
2855 } catch (OpenJPAException ke) {
2856 exceps = add(exceps, ke);
2857 }
2858 }
2859 }
2860
2861 // now invoke postRefresh on all the instances
2862 for (Iterator itr = objs.iterator(); itr.hasNext();) {
2863 try {
2864 sm = getStateManagerImpl(itr.next(), true);
2865 if (sm != null && !sm.isDetached())
2866 fireLifecycleEvent(sm.getManagedInstance(), null,
2867 sm.getMetaData(), LifecycleEvent.AFTER_REFRESH);
2868 } catch (OpenJPAException ke) {
2869 exceps = add(exceps, ke);
2870 }
2871 }
2872 } catch (OpenJPAException ke) {
2873 throw ke;
2874 } catch (RuntimeException re) {
2875 throw new GeneralException(re);
2876 }
2877 throwNestedExceptions(exceps, false);
2878 }
2879
2880 /**
2881 * Optimization for single-object refresh.
2882 */
2883 protected void refreshInternal(Object obj, OpCallbacks call) {
2884 try {
2885 StateManagerImpl sm = getStateManagerImpl(obj, true);
2886 if ((processArgument(OpCallbacks.OP_REFRESH, obj, sm, call)
2887 & OpCallbacks.ACT_RUN) == 0)
2888 return;
2889
2890 if (sm != null) {
2891 if (sm.isDetached())
2892 throw newDetachedException(obj, "refresh");
2893 else if (sm.beforeRefresh(false)) {
2894 sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null, false);
2895 sm.afterRefresh();
2896 }
2897 fireLifecycleEvent(sm.getManagedInstance(), null,
2898 sm.getMetaData(), LifecycleEvent.AFTER_REFRESH);
2899 } else if (assertPersistenceCapable(obj).pcIsDetached()
2900 == Boolean.TRUE)
2901 throw newDetachedException(obj, "refresh");
2902 } catch (OpenJPAException ke) {
2903 throw ke;
2904 } catch (RuntimeException re) {
2905 throw new GeneralException(re);
2906 }
2907 }
2908
2909 public void retrieveAll(Collection objs, boolean dfgOnly,
2910 OpCallbacks call) {
2911 if (objs.isEmpty())
2912 return;
2913 if (objs.size() == 1) {
2914 retrieve(objs.iterator().next(), dfgOnly, call);
2915 return;
2916 }
2917
2918 List exceps = null;
2919 beginOperation(true);
2920 try {
2921 assertOpen();
2922 assertNontransactionalRead();
2923
2924 // collect all hollow instances for load
2925 Object obj;
2926 Collection load = null;
2927 StateManagerImpl sm;
2928 Collection sms = new ArrayList(objs.size());
2929 for (Iterator itr = objs.iterator(); itr.hasNext();) {
2930 obj = itr.next();
2931 if (obj == null)
2932 continue;
2933
2934 try {
2935 sm = getStateManagerImpl(obj, true);
2936 if ((processArgument(OpCallbacks.OP_RETRIEVE, obj, sm, call)
2937 & OpCallbacks.ACT_RUN) == 0)
2938 continue;
2939
2940 if (sm != null) {
2941 if (sm.isDetached())
2942 throw newDetachedException(obj, "retrieve");
2943 if (sm.isPersistent()) {
2944 sms.add(sm);
2945 if (sm.getPCState() == PCState.HOLLOW) {
2946 if (load == null)
2947 load = new ArrayList();
2948 load.add(sm);
2949 }
2950 }
2951 } else if (assertPersistenceCapable(obj).pcIsDetached()
2952 == Boolean.TRUE)
2953 throw newDetachedException(obj, "retrieve");
2954 } catch (UserException ue) {
2955 exceps = add(exceps, ue);
2956 }
2957 }
2958
2959 // load all hollow instances
2960 Collection failed = null;
2961 if (load != null) {
2962 int mode = (dfgOnly) ? _store.FORCE_LOAD_DFG
2963 : _store.FORCE_LOAD_ALL;
2964 failed = _store.loadAll(load, null, mode, _fc, null);
2965 if (failed != null && !failed.isEmpty())
2966 exceps = add(exceps, newObjectNotFoundException(failed));
2967 }
2968
2969 // retrieve all non-failed instances
2970 for (Iterator itr = sms.iterator(); itr.hasNext();) {
2971 sm = (StateManagerImpl) itr.next();
2972 if (failed != null && failed.contains(sm.getId()))
2973 continue;
2974
2975 int mode = (dfgOnly) ? StateManagerImpl.LOAD_FGS
2976 : StateManagerImpl.LOAD_ALL;
2977 try {
2978 sm.beforeRead(-1);
2979 sm.load(_fc, mode, null, null, false);
2980 } catch (OpenJPAException ke) {
2981 exceps = add(exceps, ke);
2982 }
2983 }
2984 } catch (OpenJPAException ke) {
2985 throw ke;
2986 } catch (RuntimeException re) {
2987 throw new GeneralException(re);
2988 } finally {
2989 endOperation();
2990 }
2991 throwNestedExceptions(exceps, false);
2992 }
2993
2994 public void retrieve(Object obj, boolean dfgOnly, OpCallbacks call) {
2995 if (obj == null)
2996 return;
2997
2998 beginOperation(true);
2999 try {
3000 assertOpen();
3001 assertNontransactionalRead();
3002
3003 StateManagerImpl sm = getStateManagerImpl(obj, true);
3004 if ((processArgument(OpCallbacks.OP_RETRIEVE, obj, sm, call)
3005 & OpCallbacks.ACT_RUN) == 0)
3006 return;
3007
3008 if (sm != null) {
3009 if (sm.isDetached())
3010 throw newDetachedException(obj, "retrieve");
3011 if (sm.isPersistent()) {
3012 int mode = (dfgOnly) ? StateManagerImpl.LOAD_FGS
3013 : StateManagerImpl.LOAD_ALL;
3014 sm.beforeRead(-1);
3015 sm.load(_fc, mode, null, null, false);
3016 }
3017 } else if (assertPersistenceCapable(obj).pcIsDetached()
3018 == Boolean.TRUE)
3019 throw newDetachedException(obj, "retrieve");
3020 } catch (OpenJPAException ke) {
3021 throw ke;
3022 } catch (RuntimeException re) {
3023 throw new GeneralException(re);
3024 } finally {
3025 endOperation();
3026 }
3027 }
3028
3029 public void evictAll(OpCallbacks call) {
3030 beginOperation(false);
3031 try {
3032 // evict all PClean and PNonTrans objects
3033 Collection c = getManagedStates();
3034 StateManagerImpl sm;
3035 for (Iterator itr = c.iterator(); itr.hasNext();) {
3036 sm = (StateManagerImpl) itr.next();
3037 if (sm.isPersistent() && !sm.isDirty())
3038 evict(sm.getManagedInstance(), call);
3039 }
3040 }
3041 finally {
3042 endOperation();
3043 }
3044 }
3045
3046 public void evictAll(Collection objs, OpCallbacks call) {
3047 List exceps = null;
3048 beginOperation(false);
3049 try {
3050 for (Iterator itr = objs.iterator(); itr.hasNext();) {
3051 try {
3052 evict(itr.next(), call);
3053 } catch (UserException ue) {
3054 exceps = add(exceps, ue);
3055 }
3056 }
3057 } finally {
3058 endOperation();
3059 }
3060 throwNestedExceptions(exceps, false);
3061 }
3062
3063 public void evictAll(Extent extent, OpCallbacks call) {
3064 if (extent == null)
3065 return;
3066
3067 beginOperation(false);
3068 try {
3069 // evict all PClean and PNonTrans objects in extent
3070 Collection c = getManagedStates();
3071 StateManagerImpl sm;
3072 Class cls;
3073 for (Iterator itr = c.iterator(); itr.hasNext();) {
3074 sm = (StateManagerImpl) itr.next();
3075 if (sm.isPersistent() && !sm.isDirty()) {
3076 cls = sm.getMetaData().getDescribedType();
3077 if (cls == extent.getElementType()
3078 || (extent.hasSubclasses()
3079 && extent.getElementType().isAssignableFrom(cls)))
3080 evict(sm.getManagedInstance(), call);
3081 }
3082 }
3083 } finally {
3084 endOperation();
3085 }
3086 }
3087
3088 public void evict(Object obj, OpCallbacks call) {
3089 if (obj == null)
3090 return;
3091
3092 beginOperation(false);
3093 try {
3094 StateManagerImpl sm = getStateManagerImpl(obj, true);
3095 if ((processArgument(OpCallbacks.OP_EVICT, obj, sm, call)
3096 & OpCallbacks.ACT_RUN) == 0)
3097 return;
3098 if (sm == null)
3099 return;
3100
3101 sm.evict();
3102 if (_evictDataCache && sm.getObjectId() != null) {
3103 DataCache cache = sm.getMetaData().getDataCache();
3104 if (cache != null)
3105 cache.remove(sm.getObjectId());
3106 }
3107 }
3108 catch (OpenJPAException ke) {
3109 throw ke;
3110 } catch (RuntimeException re) {
3111 throw new GeneralException(re);
3112 } finally {
3113 endOperation();
3114 }
3115 }
3116
3117 public Object detach(Object obj, OpCallbacks call) {
3118 if (obj == null)
3119 return null;
3120 if (call == null)
3121 call = _call;
3122
3123 beginOperation(true);
3124 try {
3125 return new DetachManager(this, false, call).detach(obj);
3126 } catch (OpenJPAException ke) {
3127 throw ke;
3128 } catch (RuntimeException re) {
3129 throw new GeneralException(re);
3130 } finally {
3131 endOperation();
3132 }
3133 }
3134
3135 public Object[] detachAll(Collection objs, OpCallbacks call) {
3136 if (objs == null)
3137 return null;
3138 if (objs.isEmpty())
3139 return EMPTY_OBJECTS;
3140 if (call == null)
3141 call = _call;
3142
3143 beginOperation(true);
3144 try {
3145 return new DetachManager(this, false, call).detachAll(objs);
3146 } catch (OpenJPAException ke) {
3147 throw ke;
3148 } catch (RuntimeException re) {
3149 throw new GeneralException(re);
3150 } finally {
3151 endOperation();
3152 }
3153 }
3154
3155 public void detachAll(OpCallbacks call) {
3156 detachAll(call, true);
3157 }
3158
3159 public void detachAll(OpCallbacks call, boolean flush) {
3160 beginOperation(true);
3161 try {
3162 // If a flush is desired (based on input parm), then check if the
3163 // "dirty" flag is set before calling flush().
3164 if ((flush) && ((_flags & FLAG_FLUSH_REQUIRED) != 0))
3165 flush();
3166 detachAllInternal(call);
3167 } catch (OpenJPAException ke) {
3168 throw ke;
3169 } catch (RuntimeException re) {
3170 throw new GeneralException(re);
3171 } finally {
3172 endOperation();
3173 }
3174 }
3175
3176 private void detachAllInternal(OpCallbacks call) {
3177 Collection states = getManagedStates();
3178 StateManagerImpl sm;
3179 for (Iterator itr = states.iterator(); itr.hasNext();) {
3180 sm = (StateManagerImpl) itr.next();
3181 if (!sm.isPersistent())
3182 itr.remove();
3183 else if (!sm.getMetaData().isDetachable()) {
3184 sm.release(true);
3185 itr.remove();
3186 }
3187 }
3188 if (states.isEmpty())
3189 return;
3190
3191 if (call == null)
3192 call = _call;
3193 new DetachManager(this, true, call).detachAll
3194 (new ManagedObjectCollection(states));
3195 }
3196
3197 public Object attach(Object obj, boolean copyNew, OpCallbacks call) {
3198 if (obj == null)
3199 return null;
3200
3201 beginOperation(true);
3202 try {
3203 // make sure not to try to set rollback only if this fails
3204 assertWriteOperation();
3205 try {
3206 return new AttachManager(this, copyNew, call).attach(obj);
3207 } catch (OptimisticException oe) {
3208 setRollbackOnly(oe);
3209 throw oe.setFatal(true);
3210 } catch (OpenJPAException ke) {
3211 throw ke;
3212 } catch (RuntimeException re) {
3213 throw new GeneralException(re);
3214 }
3215 }
3216 finally {
3217 endOperation();
3218 }
3219 }
3220
3221 public Object[] attachAll(Collection objs, boolean copyNew,
3222 OpCallbacks call) {
3223 if (objs == null)
3224 return null;
3225 if (objs.isEmpty())
3226 return EMPTY_OBJECTS;
3227
3228 beginOperation(true);
3229 try {
3230 // make sure not to try to set rollback only if this fails
3231 assertWriteOperation();
3232 try {
3233 return new AttachManager(this, copyNew, call).attachAll(objs);
3234 } catch (OptimisticException oe) {
3235 setRollbackOnly(oe);
3236 throw oe.setFatal(true);
3237 } catch (OpenJPAException ke) {
3238 throw ke;
3239 } catch (RuntimeException re) {
3240 throw new GeneralException(re);
3241 }
3242 }
3243 finally {
3244 endOperation();
3245 }
3246 }
3247
3248 public void nontransactionalAll(Collection objs, OpCallbacks call) {
3249 beginOperation(true);
3250 try {
3251 List exceps = null;
3252 for (Iterator itr = objs.iterator(); itr.hasNext();) {
3253 try {
3254 nontransactional(itr.next(), call);
3255 } catch (UserException ue) {
3256 exceps = add(exceps, ue);
3257 }
3258 }
3259 throwNestedExceptions(exceps, false);
3260 } finally {
3261 endOperation();
3262 }
3263 }
3264
3265 public void nontransactional(Object obj, OpCallbacks call) {
3266 if (obj == null)
3267 return;
3268
3269 beginOperation(true);
3270 try {
3271 StateManagerImpl sm = getStateManagerImpl(obj, true);
3272 if ((processArgument(OpCallbacks.OP_NONTRANSACTIONAL, obj, sm, call)
3273 & OpCallbacks.ACT_RUN) == 0)
3274 return;
3275 if (sm != null)
3276 sm.nontransactional();
3277 } catch (OpenJPAException ke) {
3278 throw ke;
3279 } catch (RuntimeException re) {
3280 throw new GeneralException(re);
3281 } finally {
3282 endOperation();
3283 }
3284 }
3285
3286 /**
3287 * Make the given instances transactional.
3288 */
3289 public void transactionalAll(Collection objs, boolean updateVersion,
3290 OpCallbacks call) {
3291 if (objs.isEmpty())
3292 return;
3293 if (objs.size() == 1) {
3294 transactional(objs.iterator().next(), updateVersion, call);
3295 return;
3296 }
3297
3298 beginOperation(true);
3299 try {
3300 // collect all hollow instances for load, and make unmananged
3301 // instances transient-transactional
3302 Collection load = null;
3303 Object obj;
3304 StateManagerImpl sm;
3305 ClassMetaData meta;
3306 Collection sms = new ArrayList(objs.size());
3307 List exceps = null;
3308 for (Iterator itr = objs.iterator(); itr.hasNext();) {
3309 obj = itr.next();
3310 if (obj == null)
3311 continue;
3312
3313 try {
3314 sm = getStateManagerImpl(obj, true);
3315 if ((processArgument(OpCallbacks.OP_TRANSACTIONAL, obj, sm,
3316 call) & OpCallbacks.ACT_RUN) == 0)
3317 continue;
3318
3319 if (sm == null) {
3320 // manage transient instance
3321 meta = _conf.getMetaDataRepositoryInstance().
3322 getMetaData(obj.getClass(), _loader, true);
3323
3324 sm = new StateManagerImpl
3325 (StateManagerId.newInstance(this), meta, this);
3326 sm.initialize(assertPersistenceCapable(obj),
3327 PCState.TCLEAN);
3328 } else if (sm.isPersistent()) {
3329 assertActiveTransaction();
3330 sms.add(sm);
3331 if (sm.getPCState() == PCState.HOLLOW) {
3332 if (load == null)
3333 load = new ArrayList();
3334 load.add(sm);
3335 }
3336
3337 sm.setCheckVersion(true);
3338 if (updateVersion)
3339 sm.setUpdateVersion(true);
3340 _flags |= FLAG_FLUSH_REQUIRED; // version check/up
3341 }
3342 }
3343 catch (UserException ue) {
3344 exceps = add(exceps, ue);
3345 }
3346 }
3347
3348 // load all hollow instances
3349 Collection failed = null;
3350 if (load != null) {
3351 failed = _store.loadAll(load, null, _store.FORCE_LOAD_NONE,
3352 _fc, null);
3353 if (failed != null && !failed.isEmpty())
3354 exceps = add(exceps,
3355 newObjectNotFoundException(failed));
3356 }
3357
3358 transactionalStatesAll(sms, failed, exceps);
3359 } catch (OpenJPAException ke) {
3360 throw ke;
3361 } catch (RuntimeException re) {
3362 throw new GeneralException(re);
3363 } finally {
3364 endOperation();
3365 }
3366 }
3367
3368 /**
3369 * Make the given instances transactional.
3370 */
3371 public void transactional(Object obj, boolean updateVersion,
3372 OpCallbacks call) {
3373 if (obj == null)
3374 return;
3375
3376 beginOperation(true);
3377 try {
3378 StateManagerImpl sm = getStateManagerImpl(obj, true);
3379 if ((processArgument(OpCallbacks.OP_TRANSACTIONAL, obj, sm, call)
3380 & OpCallbacks.ACT_RUN) == 0)
3381 return;
3382
3383 if (sm != null && sm.isPersistent()) {
3384 assertActiveTransaction();
3385 sm.transactional();
3386 sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null, false);
3387 sm.setCheckVersion(true);
3388 if (updateVersion)
3389 sm.setUpdateVersion(true);
3390 _flags |= FLAG_FLUSH_REQUIRED; // version check/up
3391 } else if (sm == null) {
3392 // manage transient instance
3393 ClassMetaData meta = _conf.getMetaDataRepositoryInstance().
3394 getMetaData(obj.getClass(), _loader, true);
3395 Object id = StateManagerId.newInstance(this);
3396 sm = new StateManagerImpl(id, meta, this);
3397 sm.initialize(assertPersistenceCapable(obj),
3398 PCState.TCLEAN);
3399 }
3400 }
3401 catch (OpenJPAException ke) {
3402 throw ke;
3403 } catch (RuntimeException re) {
3404 throw new GeneralException(re);
3405 } finally {
3406 endOperation();
3407 }
3408 }
3409
3410 /**
3411 * Transition the given state managers to transactional.
3412 */
3413 private void transactionalStatesAll(Collection sms, Collection failed,
3414 List exceps) {
3415 // make instances transactional and make sure they are loaded
3416 StateManagerImpl sm;
3417 for (Iterator itr = sms.iterator(); itr.hasNext();) {
3418 sm = (StateManagerImpl) itr.next();
3419 if (failed != null && failed.contains(sm.getId()))
3420 continue;
3421
3422 try {
3423 sm.transactional();
3424 sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null, false);
3425 } catch (OpenJPAException ke) {
3426 exceps = add(exceps, ke);
3427 }
3428 }
3429 throwNestedExceptions(exceps, false);
3430 }
3431
3432 /////////////////
3433 // Extent, Query
3434 /////////////////
3435
3436 public Extent newExtent(Class type, boolean subclasses) {
3437 return newExtent(type, subclasses, null);
3438 }
3439
3440 private Extent newExtent(Class type, boolean subclasses,
3441 FetchConfiguration fetch) {
3442 beginOperation(true);
3443 try {
3444 ExtentImpl extent = new ExtentImpl(this, type, subclasses, fetch);
3445 if (_extents == null)
3446 _extents = new ReferenceHashSet(ReferenceHashSet.WEAK);
3447 _extents.add(extent);
3448
3449 return extent;
3450 } catch (OpenJPAException ke) {
3451 throw ke;
3452 } catch (RuntimeException re) {
3453 throw new GeneralException(re);
3454 } finally {
3455 endOperation();
3456 }
3457 }
3458
3459 public Iterator extentIterator(Class type, boolean subclasses,
3460 FetchConfiguration fetch, boolean ignoreChanges) {
3461 Extent extent = newExtent(type, subclasses, fetch);
3462 extent.setIgnoreChanges(ignoreChanges);
3463 return extent.iterator();
3464 }
3465
3466 public Query newQuery(String lang, Class cls, Object query) {
3467 Query q = newQuery(lang, query);
3468 q.setCandidateType(cls, true);
3469 return q;
3470 }
3471
3472 public Query newQuery(String lang, Object query) {
3473 // common mistakes
3474 if (query instanceof Extent || query instanceof Class)
3475 throw new UserException(_loc.get("bad-new-query"));
3476
3477 beginOperation(false);
3478 try {
3479 StoreQuery sq = _store.newQuery(lang);
3480 if (sq == null) {
3481 ExpressionParser ep = QueryLanguages.parserForLanguage(lang);
3482 if (ep != null)
3483 sq = new ExpressionStoreQuery(ep);
3484 else if (QueryLanguages.LANG_METHODQL.equals(lang))
3485 sq = new MethodStoreQuery();
3486 else
3487 throw new UnsupportedException(lang);
3488 }
3489
3490 Query q = newQueryImpl(lang, sq);
3491 q.setIgnoreChanges(_ignoreChanges);
3492 if (query != null)
3493 q.setQuery(query);
3494
3495 // track queries
3496 if (_queries == null)
3497 _queries = new ReferenceHashSet(ReferenceHashSet.WEAK);
3498 _queries.add(q);
3499 return q;
3500 } catch (OpenJPAException ke) {
3501 throw ke;
3502 } catch (RuntimeException re) {
3503 throw new GeneralException(re);
3504 } finally {
3505 endOperation();
3506 }
3507 }
3508
3509 /**
3510 * Create a new query.
3511 */
3512 protected QueryImpl newQueryImpl(String lang, StoreQuery sq) {
3513 return new QueryImpl(this, lang, sq);
3514 }
3515
3516 public Seq getIdentitySequence(ClassMetaData meta) {
3517 if (meta == null)
3518 return null;
3519 return getSequence(meta, null);
3520 }
3521
3522 public Seq getValueSequence(FieldMetaData fmd) {
3523 if (fmd == null)
3524 return null;
3525 return getSequence(fmd.getDefiningMetaData(), fmd);
3526 }
3527
3528 /**
3529 * Return a sequence for the given class and optional field.
3530 */
3531 private Seq getSequence(ClassMetaData meta, FieldMetaData fmd) {
3532 // get sequence strategy from metadata
3533 int strategy;
3534 if (fmd == null)
3535 strategy = meta.getIdentityStrategy();
3536 else
3537 strategy = fmd.getValueStrategy();
3538
3539 // we can handle non-native strategies without the store manager
3540 switch (strategy) {
3541 case ValueStrategies.UUID_HEX:
3542 return UUIDHexSeq.getInstance();
3543 case ValueStrategies.UUID_STRING:
3544 return UUIDStringSeq.getInstance();
3545 case ValueStrategies.SEQUENCE:
3546 SequenceMetaData smd = (fmd == null)
3547 ? meta.getIdentitySequenceMetaData()
3548 : fmd.getValueSequenceMetaData();
3549 return smd.getInstance(_loader);
3550 default:
3551 // use store manager for native sequence
3552 if (fmd == null) {
3553 // this will return a sequence even for app id classes,
3554 // which is what we want for backwards-compatibility
3555 return _store.getDataStoreIdSequence(meta);
3556 }
3557 return _store.getValueSequence(fmd);
3558 }
3559 }
3560
3561 ///////////
3562 // Locking
3563 ///////////
3564
3565 public void lock(Object obj, OpCallbacks call) {
3566 if (obj == null)
3567 return;
3568
3569 beginOperation(true); // have to sync or lock level always NONE
3570 try {
3571 lock(obj, _fc.getWriteLockLevel(), _fc.getLockTimeout(), call);
3572 } finally {
3573 endOperation();
3574 }
3575 }
3576
3577 public void lock(Object obj, int level, int timeout, OpCallbacks call) {
3578 if (obj == null)
3579 return;
3580
3581 beginOperation(true);
3582 try {
3583 assertActiveTransaction();
3584
3585 StateManagerImpl sm = getStateManagerImpl(obj, true);
3586 if ((processArgument(OpCallbacks.OP_LOCK, obj, sm, call)
3587 & OpCallbacks.ACT_RUN) == 0)
3588 return;
3589 if (sm == null || !sm.isPersistent())
3590 return;
3591
3592 _lm.lock(sm, level, timeout, null);
3593 sm.readLocked(level, level); // use same level for future write
3594 } catch (OpenJPAException ke) {
3595 throw ke;
3596 } catch (RuntimeException re) {
3597 throw new GeneralException(re);
3598 } finally {
3599 endOperation();
3600 }
3601 }
3602
3603 public void lockAll(Collection objs, OpCallbacks call) {
3604 if (objs.isEmpty())
3605 return;
3606
3607 beginOperation(true); // have to sync or lock level always NONE
3608 try {
3609 lockAll(objs, _fc.getWriteLockLevel(), _fc.getLockTimeout(),
3610 call);
3611 } finally {
3612 endOperation();
3613 }
3614 }
3615
3616 public void lockAll(Collection objs, int level, int timeout,
3617 OpCallbacks call) {
3618 if (objs.isEmpty())
3619 return;
3620 if (objs.size() == 1) {
3621 lock(objs.iterator().next(), level, timeout, call);
3622 return;
3623 }
3624
3625 beginOperation(true);
3626 try {
3627 assertActiveTransaction();
3628
3629 Collection sms = new ArrayList(objs.size());
3630 Object obj;
3631 StateManagerImpl sm;
3632 for (Iterator itr = objs.iterator(); itr.hasNext();) {
3633 obj = itr.next();
3634 if (obj == null)
3635 continue;
3636
3637 sm = getStateManagerImpl(obj, true);
3638 if ((processArgument(OpCallbacks.OP_LOCK, obj, sm, call)
3639 & OpCallbacks.ACT_RUN) == 0)
3640 continue;
3641 if (sm != null && sm.isPersistent())
3642 sms.add(sm);
3643 }
3644
3645 _lm.lockAll(sms, level, timeout, null);
3646 for (Iterator itr = sms.iterator(); itr.hasNext();)
3647 ((StateManagerImpl) itr.next()).readLocked(level, level);
3648 } catch (OpenJPAException ke) {
3649 throw ke;
3650 } catch (RuntimeException re) {
3651 throw new GeneralException(re);
3652 } finally {
3653 endOperation();
3654 }
3655 }
3656
3657 //////////////
3658 // Connection
3659 //////////////
3660
3661 public boolean cancelAll() {
3662 // this method does not lock, since we want to allow a different
3663 // thread to be able to cancel on a locked-up persistence manager
3664
3665 assertOpen();
3666 try {
3667 // if we're flushing, have to set rollback only -- do this before we
3668 // attempt to cancel, because otherwise the cancel might case the
3669 // transaction to complete before we have a chance to set the
3670 // rollback only flag
3671 if ((_flags & FLAG_STORE_FLUSHING) != 0)
3672 setRollbackOnlyInternal(new UserException());
3673 return _store.cancelAll();
3674 } catch (OpenJPAException ke) {
3675 throw ke;
3676 } catch (RuntimeException re) {
3677 throw new StoreException(re);
3678 }
3679 }
3680
3681 public Object getConnection() {
3682 assertOpen();
3683 if (!_conf.supportedOptions().contains
3684 (_conf.OPTION_DATASTORE_CONNECTION))
3685 throw new UnsupportedException(_loc.get("conn-not-supported"));
3686
3687 return _store.getClientConnection();
3688 }
3689
3690 public boolean hasConnection() {
3691 assertOpen();
3692 return (_flags & FLAG_RETAINED_CONN) != 0;
3693 }
3694
3695 /**
3696 * Tell store to retain connection if we haven't already.
3697 */
3698 private void retainConnection() {
3699 if ((_flags & FLAG_RETAINED_CONN) == 0) {
3700 _store.retainConnection();
3701 _flags |= FLAG_RETAINED_CONN;
3702 }
3703 }
3704
3705 /**
3706 * Tell store to release connection if we have retained one.
3707 */
3708 private void releaseConnection() {
3709 if ((_flags & FLAG_RETAINED_CONN) != 0) {
3710 _store.releaseConnection();
3711 _flags &= ~FLAG_RETAINED_CONN;
3712 }
3713 }
3714
3715 /////////
3716 // Cache
3717 /////////
3718
3719 public Collection getManagedObjects() {
3720 beginOperation(false);
3721 try {
3722 return new ManagedObjectCollection(getManagedStates());
3723 } finally {
3724 endOperation();
3725 }
3726 }
3727
3728 public Collection getTransactionalObjects() {
3729 beginOperation(false);
3730 try {
3731 return new ManagedObjectCollection(getTransactionalStates());
3732 } finally {
3733 endOperation();
3734 }
3735 }
3736
3737 public Collection getPendingTransactionalObjects() {
3738 beginOperation(false);
3739 try {
3740 return new ManagedObjectCollection
3741 (getPendingTransactionalStates());
3742 } finally {
3743 endOperation();
3744 }
3745 }
3746
3747 public Collection getDirtyObjects() {
3748 beginOperation(false);
3749 try {
3750 return new ManagedObjectCollection(getDirtyStates());
3751 } finally {
3752 endOperation();
3753 }
3754 }
3755
3756 public boolean getOrderDirtyObjects() {
3757 return _orderDirty;
3758 }
3759
3760 public void setOrderDirtyObjects(boolean order) {
3761 _orderDirty = order;
3762 }
3763
3764 /**
3765 * Return a copy of all managed state managers.
3766 */
3767 protected Collection getManagedStates() {
3768 return _cache.copy();
3769 }
3770
3771 /**
3772 * Return a copy of all transactional state managers.
3773 */
3774 protected Collection getTransactionalStates() {
3775 if (!hasTransactionalObjects())
3776 return Collections.EMPTY_LIST;
3777 return _transCache.copy();
3778 }
3779
3780 /**
3781 * Whether or not there are any transactional objects in the current
3782 * persistence context. If there are any instances with untracked state,
3783 * this method will cause those instances to be scanned.
3784 */
3785 private boolean hasTransactionalObjects() {
3786 _cache.dirtyCheck();
3787 return _transCache != null;
3788 }
3789
3790 /**
3791 * Return a copy of all dirty state managers.
3792 */
3793 protected Collection getDirtyStates() {
3794 if (!hasTransactionalObjects())
3795 return Collections.EMPTY_LIST;
3796
3797 return _transCache.copyDirty();
3798 }
3799
3800 /**
3801 * Return a copy of all state managers which will become
3802 * transactional upon the next transaction.
3803 */
3804 protected Collection getPendingTransactionalStates() {
3805 if (_pending == null)
3806 return Collections.EMPTY_LIST;
3807 return new ArrayList(_pending);
3808 }
3809
3810 /**
3811 * Set the cached StateManager for the instance that had the given oid.
3812 * This method must not be called multiple times for new instances.
3813 *
3814 * @param id the id previously used by the instance
3815 * @param sm the state manager for the instance; if the state
3816 * manager is transient, we'll stop managing the instance;
3817 * if it has updated its oid, we'll re-cache under the new oid
3818 * @param status one of our STATUS constants describing why we're
3819 * setting the state manager
3820 */
3821 void setStateManager(Object id, StateManagerImpl sm, int status) {
3822 lock();
3823 try {
3824 switch (status) {
3825 case STATUS_INIT:
3826 _cache.add(sm);
3827 break;
3828 case STATUS_TRANSIENT:
3829 _cache.remove(id, sm);
3830 break;
3831 case STATUS_OID_ASSIGN:
3832 assignObjectId(_cache, id, sm);
3833 break;
3834 case STATUS_COMMIT_NEW:
3835 _cache.commitNew(id, sm);
3836 break;
3837 default:
3838 throw new InternalException();
3839 }
3840 }
3841 finally {
3842 unlock();
3843 }
3844 }
3845
3846 /**
3847 * Notify the broker that the given state manager should
3848 * be added to the set of instances involved in the current transaction.
3849 */
3850 void addToTransaction(StateManagerImpl sm) {
3851 // we only add clean instances now; dirty instances are added in
3852 // the setDirty callback
3853 if (sm.isDirty())
3854 return;
3855
3856 lock();
3857 try {
3858 if (!hasTransactionalObjects())
3859 _transCache = new TransactionalCache(_orderDirty);
3860 _transCache.addClean(sm);
3861 } finally {
3862 unlock();
3863 }
3864 }
3865
3866 /**
3867 * Notify the persistence manager that the given state manager should
3868 * be removed from the set of instances involved in the current transaction.
3869 */
3870 void removeFromTransaction(StateManagerImpl sm) {
3871 lock();
3872 try {
3873 if (_transCache != null)
3874 // intentional direct access; we don't want to recompute
3875 // dirtiness while removing instances from the transaction
3876 _transCache.remove(sm);
3877 if (_derefCache != null && !sm.isPersistent())
3878 _derefCache.remove(sm);
3879 } finally {
3880 unlock();
3881 }
3882 }
3883
3884 /**
3885 * Notification that the given instance has been dirtied. This
3886 * notification is given when an object first transitions to a dirty state,
3887 * and every time the object is modified by the user thereafter.
3888 */
3889 void setDirty(StateManagerImpl sm, boolean firstDirty) {
3890 if (sm.isPersistent())
3891 _flags |= FLAG_FLUSH_REQUIRED;
3892
3893 if (_savepoints != null && !_savepoints.isEmpty()) {
3894 if (_savepointCache == null)
3895 _savepointCache = new HashSet();
3896 _savepointCache.add(sm);
3897 }
3898
3899 if (firstDirty && sm.isTransactional()) {
3900 lock();
3901 try {
3902 // cache dirty instance
3903 if (!hasTransactionalObjects())
3904 _transCache = new TransactionalCache(_orderDirty);
3905 _transCache.addDirty(sm);
3906
3907 // also record that the class is dirty
3908 if (sm.isNew()) {
3909 if (_persistedClss == null)
3910 _persistedClss = new HashSet();
3911 _persistedClss.add(sm.getMetaData().getDescribedType());
3912 } else if (sm.isDeleted()) {
3913 if (_deletedClss == null)
3914 _deletedClss = new HashSet();
3915 _deletedClss.add(sm.getMetaData().getDescribedType());
3916 } else {
3917 if (_updatedClss == null)
3918 _updatedClss = new HashSet();
3919 _updatedClss.add(sm.getMetaData().getDescribedType());
3920 }
3921
3922 // if tracking changes and this instance wasn't already dirty,
3923 // add to changed set; we use this for detecting instances that
3924 // enter the transaction during pre store
3925 if ((_flags & FLAG_PRESTORING) != 0) {
3926 if (_transAdditions == null)
3927 _transAdditions = new HashSet();
3928 _transAdditions.add(sm);
3929 }
3930 } finally {
3931 unlock();
3932 }
3933 }
3934 }
3935
3936 /**
3937 * Notify the broker that the given state manager should
3938 * be added to the set of instances that will become transactional
3939 * on the next transaction
3940 */
3941 void addToPendingTransaction(StateManagerImpl sm) {
3942 lock();
3943 try {
3944 if (_pending == null)
3945 _pending = new HashSet();
3946 _pending.add(sm);
3947 } finally {
3948 unlock();
3949 }
3950 }
3951
3952 /**
3953 * Notify the persistence manager that the given state manager should
3954 * be removed from the set of instances involved in the next transaction.
3955 */
3956 void removeFromPendingTransaction(StateManagerImpl sm) {
3957 lock();
3958 try {
3959 if (_pending != null)
3960 _pending.remove(sm);
3961 if (_derefCache != null && !sm.isPersistent())
3962 _derefCache.remove(sm);
3963 } finally {
3964 unlock();
3965 }
3966 }
3967
3968 /**
3969 * Add a dereferenced dependent object to the persistence manager's cache.
3970 * On flush, these objects will be deleted.
3971 */
3972 void addDereferencedDependent(StateManagerImpl sm) {
3973 lock();
3974 try {
3975 // if we're in the middle of flush and introducing more derefs
3976 // via instance callbacks, add them to the special additions set
3977 if ((_flags & FLAG_DEREFDELETING) != 0) {
3978 if (_derefAdditions == null)
3979 _derefAdditions = new HashSet();
3980 _derefAdditions.add(sm);
3981 } else {
3982 if (_derefCache == null)
3983 _derefCache = new HashSet();
3984 _derefCache.add(sm);
3985 }
3986 }
3987 finally {
3988 unlock();
3989 }
3990 }
3991
3992 /**
3993 * Remove the given previously dereferenced dependent object from the
3994 * cache. It is now referenced.
3995 */
3996 void removeDereferencedDependent(StateManagerImpl sm) {
3997 lock();
3998 try {
3999 boolean removed = false;
4000 if (_derefAdditions != null)
4001 removed = _derefAdditions.remove(sm);
4002 if (!removed && (_derefCache == null || !_derefCache.remove(sm)))
4003 throw new InvalidStateException(_loc.get("not-derefed",
4004 Exceptions.toString(sm.getManagedInstance()))).
4005 setFailedObject(sm.getManagedInstance()).
4006 setFatal(true);
4007 } finally {
4008 unlock();
4009 }
4010 }
4011
4012 public void dirtyType(Class cls) {
4013 if (cls == null)
4014 return;
4015
4016 beginOperation(false);
4017 try {
4018 if (_updatedClss == null)
4019 _updatedClss = new HashSet();
4020 _updatedClss.add(cls);
4021 } finally {
4022 endOperation();
4023 }
4024 }
4025
4026 public Collection getPersistedTypes() {
4027 if (_persistedClss == null || _persistedClss.isEmpty())
4028 return Collections.EMPTY_LIST;
4029 return Collections.unmodifiableCollection(_persistedClss);
4030 }
4031
4032 public Collection getUpdatedTypes() {
4033 if (_updatedClss == null || _updatedClss.isEmpty())
4034 return Collections.EMPTY_LIST;
4035 return Collections.unmodifiableCollection(_updatedClss);
4036 }
4037
4038 public Collection getDeletedTypes() {
4039 if (_deletedClss == null || _deletedClss.isEmpty())
4040 return Collections.EMPTY_LIST;
4041 return Collections.unmodifiableCollection(_deletedClss);
4042 }
4043
4044 ///////////
4045 // Closing
4046 ///////////
4047
4048 public boolean isClosed() {
4049 return _closed;
4050 }
4051
4052 public boolean isCloseInvoked() {
4053 return _closed || (_flags & FLAG_CLOSE_INVOKED) != 0;
4054 }
4055
4056 public void close() {
4057 beginOperation(false);
4058 try {
4059 // throw an exception if closing in an active local trans
4060 if (!_managed && (_flags & FLAG_ACTIVE) != 0)
4061 throw new InvalidStateException(_loc.get("active"));
4062
4063 // only close if not active; if active managed trans wait
4064 // for completion
4065 _flags |= FLAG_CLOSE_INVOKED;
4066
4067 if ((_flags & FLAG_ACTIVE) == 0)
4068 free();
4069 } finally {
4070 endOperation();
4071 }
4072 }
4073
4074 /**
4075 * Free the resources used by this persistence manager.
4076 */
4077 protected void free() {
4078 RuntimeException err = null;
4079 if ((_autoDetach & DETACH_CLOSE) != 0) {
4080 try {
4081 detachAllInternal(_call);
4082 } catch (RuntimeException re) {
4083 err = re;
4084 }
4085 }
4086
4087 _sync = null;
4088 _userObjects = null;
4089 _cache.clear();
4090 _transCache = null;
4091 _persistedClss = null;
4092 _updatedClss = null;
4093 _deletedClss = null;
4094 _derefCache = null;
4095 _pending = null;
4096 _loader = null;
4097 _transEventManager = null;
4098 _lifeEventManager = null;
4099
4100 OpenJPASavepoint save;
4101 while (_savepoints != null && !_savepoints.isEmpty()) {
4102 save =
4103 (OpenJPASavepoint) _savepoints.remove(_savepoints.size() - 1);
4104 save.release(false);
4105 }
4106 _savepoints = null;
4107 _savepointCache = null;
4108
4109 if (_queries != null) {
4110 for (Iterator itr = _queries.iterator(); itr.hasNext();) {
4111 try {
4112 ((Query) itr.next()).closeResources();
4113 } catch (RuntimeException re) {
4114 }
4115 }
4116 _queries = null;
4117 }
4118
4119 if (_extents != null) {
4120 Extent e;
4121 for (Iterator itr = _extents.iterator(); itr.hasNext();) {
4122 e = (Extent) itr.next();
4123 try {
4124 e.closeAll();
4125 } catch (RuntimeException re) {
4126 }
4127 }
4128 _extents = null;
4129 }
4130
4131 try { releaseConnection(); } catch (RuntimeException re) {}
4132
4133 _lm.close();
4134 _store.close();
4135 _flags = 0;
4136 _closed = true;
4137 if (_log.isTraceEnabled())
4138 _closedException = new IllegalStateException();
4139
4140 _factory.releaseBroker(this);
4141
4142 if (err != null)
4143 throw err;
4144 }
4145
4146 ///////////////////
4147 // Synchronization
4148 ///////////////////
4149
4150 public void lock() {
4151 if (_lock != null)
4152 _lock.lock();
4153 }
4154
4155 public void unlock() {
4156 if (_lock != null)
4157 _lock.unlock();
4158 }
4159
4160 ////////////////////
4161 // State management
4162 ////////////////////
4163
4164 public Object newInstance(Class cls) {
4165 assertOpen();
4166
4167 if (!cls.isInterface() && Modifier.isAbstract(cls.getModifiers()))
4168 throw new UnsupportedOperationException(_loc.get
4169 ("new-abstract", cls).getMessage());
4170
4171 // 1.5 doesn't initialize classes without a true Class.forName
4172 if (!PCRegistry.isRegistered(cls)) {
4173 try {
4174 Class.forName(cls.getName(), true,
4175 (ClassLoader) AccessController.doPrivileged(
4176 J2DoPrivHelper.getClassLoaderAction(cls)));
4177 } catch (Throwable t) {
4178 }
4179 }
4180
4181 if (_conf.getMetaDataRepositoryInstance().getMetaData(cls,
4182 getClassLoader(), false) == null)
4183 throw new IllegalArgumentException(
4184 _loc.get("no-interface-metadata", cls.getName()).getMessage());
4185
4186 try {
4187 return PCRegistry.newInstance(cls, null, false);
4188 } catch (IllegalStateException ise) {
4189 IllegalArgumentException iae =
4190 new IllegalArgumentException(ise.getMessage());
4191 iae.setStackTrace(ise.getStackTrace());
4192 throw iae;
4193 }
4194 }
4195
4196 public Object getObjectId(Object obj) {
4197 assertOpen();
4198 if (ImplHelper.isManageable(obj))
4199 return (ImplHelper.toPersistenceCapable(obj, _conf))
4200 .pcFetchObjectId();
4201 return null;
4202 }
4203
4204 public int getLockLevel(Object o) {
4205 assertOpen();
4206 if (o == null)
4207 return LockLevels.LOCK_NONE;
4208
4209 OpenJPAStateManager sm = getStateManager(o);
4210 if (sm == null)
4211 return LockLevels.LOCK_NONE;
4212 return getLockManager().getLockLevel(sm);
4213 }
4214
4215 public Object getVersion(Object obj) {
4216 assertOpen();
4217 if (ImplHelper.isManageable(obj))
4218 return (ImplHelper.toPersistenceCapable(obj, _conf)).pcGetVersion();
4219 return null;
4220 }
4221
4222 public boolean isDirty(Object obj) {
4223 assertOpen();
4224 if (ImplHelper.isManageable(obj)) {
4225 PersistenceCapable pc = ImplHelper.toPersistenceCapable(obj, _conf);
4226 return pc.pcIsDirty();
4227 }
4228 return false;
4229 }
4230
4231 public boolean isTransactional(Object obj) {
4232 assertOpen();
4233 if (ImplHelper.isManageable(obj))
4234 return (ImplHelper.toPersistenceCapable(obj, _conf))
4235 .pcIsTransactional();
4236 return false;
4237 }
4238
4239 public boolean isPersistent(Object obj) {
4240 assertOpen();
4241 if (ImplHelper.isManageable(obj))
4242 return (ImplHelper.toPersistenceCapable(obj, _conf)).pcIsPersistent();
4243 return false;
4244 }
4245
4246 public boolean isNew(Object obj) {
4247 assertOpen();
4248 if (ImplHelper.isManageable(obj))
4249 return (ImplHelper.toPersistenceCapable(obj, _conf)).pcIsNew();
4250 return false;
4251 }
4252
4253 public boolean isDeleted(Object obj) {
4254 assertOpen();
4255 if (ImplHelper.isManageable(obj))
4256 return (ImplHelper.toPersistenceCapable(obj, _conf)).pcIsDeleted();
4257 return false;
4258 }
4259
4260 public boolean isDetached(Object obj) {
4261 if (!(ImplHelper.isManageable(obj)))
4262 return false;
4263
4264 PersistenceCapable pc = ImplHelper.toPersistenceCapable(obj, _conf);
4265 Boolean detached = pc.pcIsDetached();
4266 if (detached != null)
4267 return detached.booleanValue();
4268
4269 // last resort: instance is detached if it has a store record
4270 ClassMetaData meta = _conf.getMetaDataRepositoryInstance().
4271 getMetaData(ImplHelper.getManagedInstance(pc).getClass(),
4272 _loader, true);
4273 Object oid = ApplicationIds.create(pc, meta);
4274 if (oid == null)
4275 return false;
4276 return find(oid, null, EXCLUDE_ALL, null, 0) != null;
4277 }
4278
4279 public OpenJPAStateManager getStateManager(Object obj) {
4280 assertOpen();
4281 return getStateManagerImpl(obj, false);
4282 }
4283
4284 /**
4285 * Return the state manager for the given instance, or null.
4286 *
4287 * @param assertThisContext if true, thow an exception if the given
4288 * object is managed by another broker
4289 */
4290 protected StateManagerImpl getStateManagerImpl(Object obj,
4291 boolean assertThisContext) {
4292 if (ImplHelper.isManageable(obj)) {
4293 PersistenceCapable pc = ImplHelper.toPersistenceCapable(obj, _conf);
4294 if (pc.pcGetGenericContext() == this)
4295 return (StateManagerImpl) pc.pcGetStateManager();
4296 if (assertThisContext && pc.pcGetGenericContext() != null)
4297 throw new UserException(_loc.get("not-managed",
4298 Exceptions.toString(obj))).setFailedObject(obj);
4299 }
4300 return null;
4301 }
4302
4303 /**
4304 * Return the state manager for the given oid.
4305 *
4306 * @param allowNew if true, objects made persistent in the current
4307 * transaction will be included in the search; if
4308 * multiple new objects match the given oid, it is
4309 * undefined which will be returned
4310 */
4311 protected StateManagerImpl getStateManagerImplById(Object oid,
4312 boolean allowNew) {
4313 return _cache.getById(oid, allowNew);
4314 }
4315
4316 /**
4317 * Return the given instance as a {@link PersistenceCapable}.
4318 * If the instance is not manageable throw the proper exception.
4319 */
4320 protected PersistenceCapable assertPersistenceCapable(Object obj) {
4321 if (obj == null)
4322 return null;
4323 if (ImplHelper.isManageable(obj))
4324 return ImplHelper.toPersistenceCapable(obj, _conf);
4325
4326 // check for different instances of the PersistenceCapable interface
4327 // and throw a better error that mentions the class loaders
4328 Class[] intfs = obj.getClass().getInterfaces();
4329 for (int i = 0; intfs != null && i < intfs.length; i++) {
4330 if (intfs[i].getName().equals(PersistenceCapable.class.getName())) {
4331 throw new UserException(_loc.get("pc-loader-different",
4332 Exceptions.toString(obj),
4333 (ClassLoader) AccessController.doPrivileged(
4334 J2DoPrivHelper.getClassLoaderAction(
4335 PersistenceCapable.class)),
4336 (ClassLoader) AccessController.doPrivileged(
4337 J2DoPrivHelper.getClassLoaderAction(intfs[i]))))
4338 .setFailedObject(obj);
4339 }
4340 }
4341
4342 // not enhanced
4343 throw new UserException(_loc.get("pc-cast",
4344 Exceptions.toString(obj))).setFailedObject(obj);
4345 }
4346
4347 /////////
4348 // Utils
4349 /////////
4350 /**
4351 * Throw an exception if the context is closed. The exact message and
4352 * content of the exception varies whether TRACE is enabled or not.
4353 */
4354 public void assertOpen() {
4355 if (_closed) {
4356 if (_closedException == null) // TRACE not enabled
4357 throw new InvalidStateException(_loc.get("closed-notrace"))
4358 .setFatal(true);
4359 else {
4360 OpenJPAException e = new InvalidStateException(
4361 _loc.get("closed"), _closedException).setFatal(true);
4362 e.setCause(_closedException);
4363 throw e;
4364 }
4365 }
4366 }
4367
4368 public void assertActiveTransaction() {
4369 if ((_flags & FLAG_ACTIVE) == 0)
4370 throw new NoTransactionException(_loc.get("not-active"));
4371 }
4372
4373 /**
4374 * Throw exception if a transaction-related operation is attempted and
4375 * no transaction is active.
4376 */
4377 private void assertTransactionOperation() {
4378 if ((_flags & FLAG_ACTIVE) == 0)
4379 throw new InvalidStateException(_loc.get("not-active"));
4380 }
4381
4382 public void assertNontransactionalRead() {
4383 if ((_flags & FLAG_ACTIVE) == 0 && !_nontransRead)
4384 throw new InvalidStateException(_loc.get("non-trans-read"));
4385 }
4386
4387 public void assertWriteOperation() {
4388 if ((_flags & FLAG_ACTIVE) == 0 && (!_nontransWrite
4389 || (_autoDetach & DETACH_NONTXREAD) != 0))
4390 throw new NoTransactionException(_loc.get("write-operation"));
4391 }
4392
4393 /**
4394 * Return an object not found exception containing nested exceptions
4395 * for all of the given failed objects.
4396 */
4397 private static ObjectNotFoundException newObjectNotFoundException
4398 (Collection failed) {
4399 Throwable[] t = new Throwable[failed.size()];
4400 int idx = 0;
4401 for (Iterator itr = failed.iterator(); itr.hasNext(); idx++)
4402 t[idx] = new ObjectNotFoundException(itr.next());
4403 return new ObjectNotFoundException(failed, t);
4404 }
4405
4406 ////////////////////////////////
4407 // FindCallbacks implementation
4408 ////////////////////////////////
4409
4410 public Object processArgument(Object oid) {
4411 return oid;
4412 }
4413
4414 public Object processReturn(Object oid, OpenJPAStateManager sm) {
4415 return (sm == null) ? null : sm.getManagedInstance();
4416 }
4417
4418 private void writeObject(ObjectOutputStream out) throws IOException {
4419 assertOpen();
4420 lock();
4421 try {
4422 if (isActive()) {
4423 if (!getOptimistic())
4424 throw new InvalidStateException(
4425 _loc.get("cant-serialize-pessimistic-broker"));
4426 if (hasFlushed())
4427 throw new InvalidStateException(
4428 _loc.get("cant-serialize-flushed-broker"));
4429 if (hasConnection())
4430 throw new InvalidStateException(
4431 _loc.get("cant-serialize-connected-broker"));
4432 }
4433
4434 try {
4435 _isSerializing = true;
4436 out.writeObject(_factory.getPoolKey());
4437 out.defaultWriteObject();
4438 } finally {
4439 _isSerializing = false;
4440 }
4441 } finally {
4442 unlock();
4443 }
4444 }
4445
4446 private void readObject(ObjectInputStream in)
4447 throws ClassNotFoundException, IOException {
4448 Object factoryKey = in.readObject();
4449 AbstractBrokerFactory factory =
4450 AbstractBrokerFactory.getPooledFactoryForKey(factoryKey);
4451
4452 // this needs to happen before defaultReadObject so that it's
4453 // available for calls to broker.getConfiguration() during
4454 // StateManager deserialization
4455 _conf = factory.getConfiguration();
4456
4457 in.defaultReadObject();
4458 factory.initializeBroker(_managed, _connRetainMode, this, true);
4459
4460 // re-initialize the lock if needed.
4461 setMultithreaded(_multithreaded);
4462
4463 if (isActive() && _runtime instanceof LocalManagedRuntime)
4464 ((LocalManagedRuntime) _runtime).begin();
4465 }
4466
4467 /**
4468 * Whether or not this broker is in the midst of being serialized.
4469 *
4470 * @since 1.1.0
4471 */
4472 boolean isSerializing() {
4473 return _isSerializing;
4474 }
4475
4476 /**
4477 * Transactional cache that holds soft refs to clean instances.
4478 */
4479 static class TransactionalCache
4480 implements Set, Serializable {
4481
4482 private final boolean _orderDirty;
4483 private Set _dirty = null;
4484 private Set _clean = null;
4485
4486 public TransactionalCache(boolean orderDirty) {
4487 _orderDirty = orderDirty;
4488 }
4489
4490 /**
4491 * Return a copy of all transactional state managers.
4492 */
4493 public Collection copy() {
4494 if (isEmpty())
4495 return Collections.EMPTY_LIST;
4496
4497 // size may not be entirely accurate due to refs expiring, so
4498 // manually copy each object; doesn't matter this way if size too
4499 // big by some
4500 List copy = new ArrayList(size());
4501 if (_dirty != null)
4502 for (Iterator itr = _dirty.iterator(); itr.hasNext();)
4503 copy.add(itr.next());
4504 if (_clean != null)
4505 for (Iterator itr = _clean.iterator(); itr.hasNext();)
4506 copy.add(itr.next());
4507 return copy;
4508 }
4509
4510 /**
4511 * Return a copy of all dirty state managers.
4512 */
4513 public Collection copyDirty() {
4514 if (_dirty == null || _dirty.isEmpty())
4515 return Collections.EMPTY_LIST;
4516 return new ArrayList(_dirty);
4517 }
4518
4519 /**
4520 * Transfer the given instance from the dirty cache to the clean cache.
4521 */
4522 public void flushed(StateManagerImpl sm) {
4523 if (sm.isDirty() && _dirty != null && _dirty.remove(sm))
4524 addCleanInternal(sm);
4525 }
4526
4527 /**
4528 * Add the given instance to the clean cache.
4529 */
4530 public void addClean(StateManagerImpl sm) {
4531 if (addCleanInternal(sm) && _dirty != null)
4532 _dirty.remove(sm);
4533 }
4534
4535 private boolean addCleanInternal(StateManagerImpl sm) {
4536 if (_clean == null)
4537 _clean = new ReferenceHashSet(ReferenceHashSet.SOFT);
4538 return _clean.add(sm);
4539 }
4540
4541 /**
4542 * Add the given instance to the dirty cache.
4543 */
4544 public void addDirty(StateManagerImpl sm) {
4545 if (_dirty == null) {
4546 if (_orderDirty)
4547 _dirty = MapBackedSet.decorate(new LinkedMap());
4548 else
4549 _dirty = new HashSet();
4550 }
4551 if (_dirty.add(sm))
4552 removeCleanInternal(sm);
4553 }
4554
4555 /**
4556 * Remove the given instance from the cache.
4557 */
4558 public boolean remove(StateManagerImpl sm) {
4559 return removeCleanInternal(sm)
4560 || (_dirty != null && _dirty.remove(sm));
4561 }
4562
4563 private boolean removeCleanInternal(StateManagerImpl sm) {
4564 return _clean != null && _clean.remove(sm);
4565 }
4566
4567 public Iterator iterator() {
4568 IteratorChain chain = new IteratorChain();
4569 if (_dirty != null && !_dirty.isEmpty())
4570 chain.addIterator(_dirty.iterator());
4571 if (_clean != null && !_clean.isEmpty())
4572 chain.addIterator(_clean.iterator());
4573 return chain;
4574 }
4575
4576 public boolean contains(Object obj) {
4577 return (_dirty != null && _dirty.contains(obj))
4578 || (_clean != null && _clean.contains(obj));
4579 }
4580
4581 public boolean containsAll(Collection coll) {
4582 for (Iterator itr = coll.iterator(); itr.hasNext();)
4583 if (!contains(itr.next()))
4584 return false;
4585 return true;
4586 }
4587
4588 public void clear() {
4589 if (_dirty != null)
4590 _dirty = null;
4591 if (_clean != null)
4592 _clean = null;
4593 }
4594
4595 public boolean isEmpty() {
4596 return (_dirty == null || _dirty.isEmpty())
4597 && (_clean == null || _clean.isEmpty());
4598 }
4599
4600 public int size() {
4601 int size = 0;
4602 if (_dirty != null)
4603 size += _dirty.size();
4604 if (_clean != null)
4605 size += _clean.size();
4606 return size;
4607 }
4608
4609 public boolean add(Object obj) {
4610 throw new UnsupportedOperationException();
4611 }
4612
4613 public boolean addAll(Collection coll) {
4614 throw new UnsupportedOperationException();
4615 }
4616
4617 public boolean remove(Object obj) {
4618 throw new UnsupportedOperationException();
4619 }
4620
4621 public boolean removeAll(Collection coll) {
4622 throw new UnsupportedOperationException();
4623 }
4624
4625 public boolean retainAll(Collection c) {
4626 throw new UnsupportedOperationException();
4627 }
4628
4629 public Object[] toArray() {
4630 throw new UnsupportedOperationException();
4631 }
4632
4633 public Object[] toArray(Object[] arr) {
4634 throw new UnsupportedOperationException();
4635 }
4636 }
4637
4638 /**
4639 * Unique id for state managers of new datastore instances without assigned
4640 * object ids.
4641 */
4642 private static class StateManagerId
4643 implements Serializable {
4644
4645 public static final String STRING_PREFIX = "openjpasm:";
4646
4647 private static long _generator = 0;
4648
4649 private final int _bhash;
4650 private final long _id;
4651
4652 public static StateManagerId newInstance(Broker b) {
4653 return new StateManagerId(System.identityHashCode(b), _generator++);
4654 }
4655
4656 private StateManagerId(int bhash, long id) {
4657 _bhash = bhash;
4658 _id = id;
4659 }
4660
4661 public StateManagerId(String str) {
4662 str = str.substring(STRING_PREFIX.length());
4663 int idx = str.indexOf(':');
4664 _bhash = Integer.parseInt(str.substring(0, idx));
4665 _id = Long.parseLong(str.substring(idx + 1));
4666 }
4667
4668 public boolean equals(Object other) {
4669 if (other == this)
4670 return true;
4671 if (!(other instanceof StateManagerId))
4672 return false;
4673 StateManagerId sid = (StateManagerId) other;
4674 return _bhash == sid._bhash && _id == sid._id;
4675 }
4676
4677 public int hashCode() {
4678 return (int) (_id ^ (_id >>> 32));
4679 }
4680
4681 public String toString() {
4682 return STRING_PREFIX + _bhash + ":" + _id;
4683 }
4684 }
4685
4686 /**
4687 * Collection type that holds state managers but whose interface deals
4688 * with the corresponding managed objects.
4689 */
4690 private static class ManagedObjectCollection
4691 extends AbstractCollection {
4692
4693 private final Collection _states;
4694
4695 public ManagedObjectCollection(Collection states) {
4696 _states = states;
4697 }
4698
4699 public Collection getStateManagers() {
4700 return _states;
4701 }
4702
4703 public int size() {
4704 return _states.size();
4705 }
4706
4707 public Iterator iterator() {
4708 return new Iterator() {
4709 private final Iterator _itr = _states.iterator();
4710
4711 public boolean hasNext() {
4712 return _itr.hasNext();
4713 }
4714
4715 public Object next() {
4716 return ((OpenJPAStateManager) _itr.next()).
4717 getManagedInstance();
4718 }
4719
4720 public void remove() {
4721 throw new UnsupportedException();
4722 }
4723 };
4724 }
4725 }
4726
4727 /**
4728 * Assign the object id to the cache. Exception will be
4729 * thrown if the id already exists in the cache.
4730 */
4731 protected void assignObjectId(Object cache, Object id,
4732 StateManagerImpl sm) {
4733 ((ManagedCache) cache).assignObjectId(id, sm);
4734 }
4735
4736 /**
4737 * This method makes sure we don't already have the instance cached
4738 */
4739 protected void checkForDuplicateId(Object id, Object obj) {
4740 StateManagerImpl other = getStateManagerImplById(id, false);
4741 if (other != null && !other.isDeleted() && !other.isNew())
4742 throw new ObjectExistsException(_loc.get("cache-exists",
4743 obj.getClass().getName(), id)).setFailedObject(obj);
4744 }
4745 }