001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.core.persist;
028:
029: import java.lang.ref.ReferenceQueue;
030: import java.util.ArrayList;
031: import java.util.Collection;
032: import java.util.Iterator;
033:
034: import org.cougaar.util.log.Logger;
035: import org.cougaar.util.GC;
036:
037: /**
038: * Identifies all objects that have been (or are about to be) written
039: * to persistence media. This purpose of this table is to remember
040: * objects that have persisted in previous persistence snapshots so
041: * that references to such objects in subsequent persistence deltas
042: * can be replaced with references to the previously persisted
043: * objects. This is similar to the wire handle of the Java
044: * serialization process (ObjectOutputStream), but applies across
045: * persistence snapshots whereas the wire handle applies within a
046: * single snapshot.
047: * <p>
048: * This class is essentially a hash table implementation. It differs
049: * from the java.util versions of hash tables in that the values are
050: * {@link WeakReference}s so that values to which there are no longer
051: * any references get removed from the table. WeakHashMap has weak
052: * keys, not weak values.
053: */
054: class IdentityTable {
055: static class MyArrayList extends ArrayList {
056: /**
057: * Strangely enough, ArrayLists cannot be resized; this extension
058: * adds setSize().
059: * @param newSize the new size.
060: */
061: public void setSize(int newSize) {
062: while (this .size() < newSize) {
063: this .add(null);
064: }
065: while (this .size() > newSize) {
066: this .remove(this .size() - 1);
067: }
068: }
069: }
070:
071: private int nextId = 0;
072:
073: private PersistenceAssociation[] table = new PersistenceAssociation[123];
074:
075: private int count = 0;
076:
077: private ReferenceQueue referenceQueue = new ReferenceQueue();
078:
079: private Collection rehydrationCollection = null;
080:
081: private Logger logger;
082:
083: /**
084: * This list keeps all the PersistenceAssociation objects indexed
085: * by their refId. Only the first encountered (last written) version
086: * is kept.
087: */
088: private MyArrayList persistentObjects = new MyArrayList();
089:
090: IdentityTable(Logger logger) {
091: this .logger = logger;
092: }
093:
094: private void processQueue() {
095: PersistenceAssociation pAssoc;
096: while ((pAssoc = (PersistenceAssociation) referenceQueue.poll()) != null) {
097: if (logger.isDetailEnabled())
098: logger.detail("processQueue removing " + pAssoc);
099: int hashIndex = (pAssoc.hash & 0x7fffffff) % table.length;
100: for (PersistenceAssociation x = table[hashIndex], prev = null;; prev = x, x = x.next) {
101: if (x == null) {
102: break; // Not found due to "clear()"
103: }
104: if (x == pAssoc) {
105: if (prev == null) {
106: table[hashIndex] = pAssoc.next;
107: } else {
108: prev.next = pAssoc.next;
109: }
110: count--;
111: break;
112: }
113: }
114: persistentObjects.set(pAssoc.getReferenceId().intValue(),
115: null);
116: }
117: }
118:
119: public void setRehydrationCollection(Collection list) {
120: rehydrationCollection = list;
121: if (list == null) {
122: GC.gc();
123: }
124: }
125:
126: public int getNextId() {
127: return nextId;
128: }
129:
130: public void setNextId(int newNextId) {
131: nextId = newNextId;
132: }
133:
134: /**
135: * Find the PersistenceAssociation for an object.
136: * @param object the object
137: * @return the PersistenceAssociation for the object
138: * @return null if the object has no current association
139: */
140: public PersistenceAssociation find(Object object) {
141: processQueue();
142: int hash = System.identityHashCode(object);
143: int hashIndex = (hash & 0x7fffffff) % table.length;
144: PersistenceAssociation pAssoc;
145: for (pAssoc = table[hashIndex]; pAssoc != null; pAssoc = pAssoc.next) {
146: if (pAssoc.get() == object) {
147: return pAssoc;
148: }
149: }
150: return null;
151: }
152:
153: private void rehash() {
154: PersistenceAssociation[] oldTable = table;
155: int newLength = table.length * 2 + 1;
156: table = new PersistenceAssociation[newLength];
157: for (int i = 0; i < oldTable.length; i++) {
158: PersistenceAssociation p = oldTable[i];
159: while (p != null) {
160: PersistenceAssociation n = p.next;
161: int hashIndex = (p.hash & 0x7fffffff) % newLength;
162: p.next = table[hashIndex];
163: table[hashIndex] = p;
164: p = n;
165: }
166: }
167: }
168:
169: /**
170: * Create a new PersistenceAssociation and enter it into the table.
171: * @param o the object of the PersistenceAssociation
172: * @param ref The PersistenceReference of the object.
173: * @return the new PersistenceAssociation
174: */
175: public PersistenceAssociation create(Object o,
176: PersistenceReference ref) {
177: if (count > table.length * 3 / 4) {
178: rehash();
179: }
180: PersistenceAssociation pAssoc = new PersistenceAssociation(o,
181: ref, referenceQueue);
182: if (rehydrationCollection != null) {
183: rehydrationCollection.add(o); // Make sure there is a reference to the object
184: }
185: int hashIndex = (pAssoc.hash & 0x7fffffff) % table.length;
186: pAssoc.next = table[hashIndex];
187: table[hashIndex] = pAssoc;
188: int ix = ref.intValue();
189: if (persistentObjects.size() <= ix)
190: persistentObjects.setSize(ix + 1);
191: if (persistentObjects.get(ix) != null) {
192: throw new IllegalArgumentException("Slot full: "
193: + persistentObjects.get(ix) + "<-" + pAssoc);
194: }
195: persistentObjects.set(ix, pAssoc);
196: count++;
197: return pAssoc;
198: }
199:
200: /**
201: * Assign an identity to the given object. Makes an entry in the
202: * identityTable if not already there and marks the entry as
203: * "needing to be written". The next time the
204: * ObjectOutputStream.replaceObject method is called for the object
205: * it will be written. Thereafter, a reference object will be
206: * written.
207: */
208: public PersistenceAssociation findOrCreate(Object o) {
209: PersistenceAssociation pAssoc = find(o);
210: if (pAssoc != null) {
211: return pAssoc;
212: }
213: return create(o, new PersistenceReference(nextId++));
214: }
215:
216: /**
217: * Get the PersistenceAssociation corresponding to a PersistenceReference.
218: * @param ref the PersistenceReference
219: * @return the corresponding PersistenceAssociation
220: * @return null if the is no corresponding PersistenceAssociation
221: */
222: public PersistenceAssociation get(PersistenceReference ref) {
223: return get(ref.intValue());
224: }
225:
226: /**
227: * Get the PersistenceAssociation at a particular index.
228: * @param ix the index
229: * @return the corresponding PersistenceAssociation
230: * @return null if the is no corresponding PersistenceAssociation
231: */
232: public PersistenceAssociation get(int ix) {
233: processQueue();
234: if (ix < 0 || ix >= persistentObjects.size())
235: return null;
236: return (PersistenceAssociation) persistentObjects.get(ix);
237: }
238:
239: public int size() {
240: return persistentObjects.size();
241: }
242:
243: public Iterator iterator() {
244: processQueue();
245: return new Iterator() {
246: Iterator iter = persistentObjects.iterator();
247: Object nextObject = null;
248:
249: public boolean hasNext() {
250: while (nextObject == null) {
251: if (!iter.hasNext()) {
252: return false;
253: }
254: nextObject = iter.next();
255: }
256: return true;
257: }
258:
259: public Object next() {
260: if (nextObject == null) {
261: return iter.next();
262: }
263: Object result = nextObject;
264: nextObject = null;
265: return result;
266: }
267:
268: public void remove() {
269: throw new UnsupportedOperationException(
270: "remove not supported");
271: }
272: };
273: }
274:
275: public void clear() {
276: table = new PersistenceAssociation[123];
277: count = 0;
278: setRehydrationCollection(null);
279: }
280: }
|