001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.kernel;
020:
021: import java.io.Serializable;
022: import java.util.ArrayList;
023: import java.util.Collection;
024: import java.util.Collections;
025: import java.util.HashMap;
026: import java.util.HashSet;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.Map;
030:
031: import org.apache.openjpa.lib.util.Localizer;
032: import org.apache.openjpa.lib.util.ReferenceHashSet;
033: import org.apache.openjpa.util.Exceptions;
034: import org.apache.openjpa.util.InternalException;
035: import org.apache.openjpa.util.UserException;
036:
037: /**
038: * Cache of managed objects. Must be static for serialization reasons.
039: */
040: class ManagedCache implements Serializable {
041:
042: private static final Localizer _loc = Localizer
043: .forPackage(ManagedCache.class);
044:
045: private Map _main; // oid -> sm
046: private Map _conflicts = null; // conflict oid -> new sm
047: private Map _news = null; // tmp id -> new sm
048: private Collection _embeds = null; // embedded/non-persistent sms
049: private Collection _untracked = null; // hard refs to untracked sms
050: private BrokerImpl broker;
051:
052: /**
053: * Constructor; supply primary cache map.
054: */
055: ManagedCache(BrokerImpl broker) {
056: this .broker = broker;
057: _main = broker.newManagedObjectCache();
058: }
059:
060: /**
061: * Return the instance for the given oid, optionally allowing
062: * new instances.
063: */
064: public StateManagerImpl getById(Object oid, boolean allowNew) {
065: if (oid == null)
066: return null;
067:
068: // check main cache for oid
069: StateManagerImpl sm = (StateManagerImpl) _main.get(oid);
070: StateManagerImpl sm2;
071: if (sm != null) {
072: // if it's a new instance, we know it's the only match, because
073: // other pers instances override new instances in _cache
074: if (sm.isNew())
075: return (allowNew) ? sm : null;
076: if (!allowNew || !sm.isDeleted())
077: return sm;
078:
079: // sm is deleted; check conflict cache
080: if (_conflicts != null) {
081: sm2 = (StateManagerImpl) _conflicts.get(oid);
082: if (sm2 != null)
083: return sm2;
084: }
085: }
086:
087: // at this point sm is null or deleted; check the new cache for
088: // any matches. this allows us to match app id objects to new
089: // instances without permanant oids
090: if (allowNew && _news != null && !_news.isEmpty()) {
091: sm2 = (StateManagerImpl) _news.get(oid);
092: if (sm2 != null)
093: return sm2;
094: }
095: return sm;
096: }
097:
098: /**
099: * Call this method when a new state manager initializes itself.
100: */
101: public void add(StateManagerImpl sm) {
102: if (!sm.isIntercepting()) {
103: if (_untracked == null)
104: _untracked = new HashSet();
105: _untracked.add(sm);
106: }
107:
108: if (!sm.isPersistent() || sm.isEmbedded()) {
109: if (_embeds == null)
110: _embeds = new ReferenceHashSet(ReferenceHashSet.WEAK);
111: _embeds.add(sm);
112: return;
113: }
114:
115: // initializing new instance; put in new cache because won't have
116: // permanent oid yet
117: if (sm.isNew()) {
118: if (_news == null)
119: _news = new HashMap();
120: _news.put(sm.getId(), sm);
121: return;
122: }
123:
124: // initializing persistent instance; put in main cache
125: StateManagerImpl orig = (StateManagerImpl) _main.put(sm
126: .getObjectId(), sm);
127: if (orig != null) {
128: _main.put(sm.getObjectId(), orig);
129: throw new UserException(_loc.get("dup-load", sm
130: .getObjectId(), Exceptions.toString(orig
131: .getManagedInstance()))).setFailedObject(sm
132: .getManagedInstance());
133: }
134: }
135:
136: /**
137: * Remove the given state manager from the cache when it transitions
138: * to transient.
139: */
140: public void remove(Object id, StateManagerImpl sm) {
141: // if it has a permanent oid, remove from main / conflict cache,
142: // else remove from embedded/nontrans cache, and if not there
143: // remove from new cache
144: Object orig;
145: if (sm.getObjectId() != null) {
146: orig = _main.remove(id);
147: if (orig != sm) {
148: if (orig != null)
149: _main.put(id, orig); // put back
150: if (_conflicts != null) {
151: orig = _conflicts.remove(id);
152: if (orig != null && orig != sm)
153: _conflicts.put(id, orig); // put back
154: }
155: }
156: } else if ((_embeds == null || !_embeds.remove(sm))
157: && _news != null) {
158: orig = _news.remove(id);
159: if (orig != null && orig != sm)
160: _news.put(id, orig); // put back
161: }
162:
163: if (_untracked != null)
164: _untracked.remove(sm);
165: }
166:
167: /**
168: * An embedded or nonpersistent managed instance has been persisted.
169: */
170: public void persist(StateManagerImpl sm) {
171: if (_embeds != null)
172: _embeds.remove(sm);
173: }
174:
175: /**
176: * A new instance has just been assigned a permanent oid.
177: */
178: public void assignObjectId(Object id, StateManagerImpl sm) {
179: // if assigning oid, remove from new cache and put in primary; may
180: // not be in new cache if another new instance had same id
181: StateManagerImpl orig = null;
182: if (_news != null) {
183: orig = (StateManagerImpl) _news.remove(id);
184: if (orig != null && orig != sm)
185: _news.put(id, orig); // put back
186: }
187:
188: // put in main cache, but make sure we don't replace another
189: // instance with the same oid
190: orig = (StateManagerImpl) _main.put(sm.getObjectId(), sm);
191: if (orig != null) {
192: _main.put(sm.getObjectId(), orig);
193: if (!orig.isDeleted())
194: throw new UserException(_loc.get("dup-oid-assign", sm
195: .getObjectId(), Exceptions.toString(sm
196: .getManagedInstance()))).setFailedObject(sm
197: .getManagedInstance());
198:
199: // same oid as deleted instance; put in conflict cache
200: if (_conflicts == null)
201: _conflicts = new HashMap();
202: _conflicts.put(sm.getObjectId(), sm);
203: }
204: }
205:
206: /**
207: * A new instance has committed; recache under permanent oid.
208: */
209: public void commitNew(Object id, StateManagerImpl sm) {
210: // if the id didn't change, the instance was already assigned an
211: // id, but it could have been in conflict cache
212: StateManagerImpl orig;
213: if (sm.getObjectId() == id) {
214: orig = (_conflicts == null) ? null
215: : (StateManagerImpl) _conflicts.remove(id);
216: if (orig == sm) {
217: orig = (StateManagerImpl) _main.put(id, sm);
218: if (orig != null && !orig.isDeleted()) {
219: _main.put(sm.getObjectId(), orig);
220: throw new UserException(_loc.get("dup-oid-assign",
221: sm.getObjectId(), Exceptions.toString(sm
222: .getManagedInstance())))
223: .setFailedObject(sm.getManagedInstance())
224: .setFatal(true);
225: }
226: }
227: return;
228: }
229:
230: // oid changed, so it must previously have been a new instance
231: // without an assigned oid. remove it from the new cache; ok if
232: // we end up removing another instance with same id
233: if (_news != null)
234: _news.remove(id);
235:
236: // and put into main cache now that id is asssigned
237: orig = (StateManagerImpl) _main.put(sm.getObjectId(), sm);
238: if (orig != null && orig != sm && !orig.isDeleted()) {
239: // put back orig and throw error
240: _main.put(sm.getObjectId(), orig);
241: throw new UserException(_loc.get("dup-oid-assign", sm
242: .getObjectId(), Exceptions.toString(sm
243: .getManagedInstance()))).setFailedObject(
244: sm.getManagedInstance()).setFatal(true);
245: }
246: }
247:
248: /**
249: * Return a copy of all cached persistent objects.
250: */
251: public Collection copy() {
252: // proxies not included here because the state manager is always
253: // present in other caches too
254:
255: int size = _main.size();
256: if (_conflicts != null)
257: size += _conflicts.size();
258: if (_news != null)
259: size += _news.size();
260: if (_embeds != null)
261: size += _embeds.size();
262: if (size == 0)
263: return Collections.EMPTY_LIST;
264:
265: List copy = new ArrayList(size);
266: for (Iterator itr = _main.values().iterator(); itr.hasNext();)
267: copy.add(itr.next());
268: if (_conflicts != null && !_conflicts.isEmpty())
269: for (Iterator itr = _conflicts.values().iterator(); itr
270: .hasNext();)
271: copy.add(itr.next());
272: if (_news != null && !_news.isEmpty())
273: for (Iterator itr = _news.values().iterator(); itr
274: .hasNext();)
275: copy.add(itr.next());
276: if (_embeds != null && !_embeds.isEmpty())
277: for (Iterator itr = _embeds.iterator(); itr.hasNext();)
278: copy.add(itr.next());
279: return copy;
280: }
281:
282: /**
283: * Clear the cache.
284: */
285: public void clear() {
286: _main = broker.newManagedObjectCache();
287: if (_conflicts != null)
288: _conflicts = null;
289: if (_news != null)
290: _news = null;
291: if (_embeds != null)
292: _embeds = null;
293: if (_untracked != null)
294: _untracked = null;
295: }
296:
297: /**
298: * Clear new instances without permanent oids.
299: */
300: public void clearNew() {
301: if (_news != null)
302: _news = null;
303: }
304:
305: void dirtyCheck() {
306: if (_untracked == null)
307: return;
308:
309: for (Iterator iter = _untracked.iterator(); iter.hasNext();)
310: ((StateManagerImpl) iter.next()).dirtyCheck();
311: }
312: }
|