001: /*
002: * This file is part of the Echo Web Application Framework (hereinafter "Echo").
003: * Copyright (C) 2002-2005 NextApp, Inc.
004: *
005: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
006: *
007: * The contents of this file are subject to the Mozilla Public License Version
008: * 1.1 (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: * http://www.mozilla.org/MPL/
011: *
012: * Software distributed under the License is distributed on an "AS IS" basis,
013: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
014: * for the specific language governing rights and limitations under the
015: * License.
016: *
017: * Alternatively, the contents of this file may be used under the terms of
018: * either the GNU General Public License Version 2 or later (the "GPL"), or
019: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
020: * in which case the provisions of the GPL or the LGPL are applicable instead
021: * of those above. If you wish to allow use of your version of this file only
022: * under the terms of either the GPL or the LGPL, and not to allow others to
023: * use your version of this file under the terms of the MPL, indicate your
024: * decision by deleting the provisions above and replace them with the notice
025: * and other provisions required by the GPL or the LGPL. If you do not delete
026: * the provisions above, a recipient may use your version of this file under
027: * the terms of any one of the MPL, the GPL or the LGPL.
028: */
029:
030: package nextapp.echo2.webcontainer.util;
031:
032: import java.io.IOException;
033: import java.io.ObjectInputStream;
034: import java.io.ObjectOutputStream;
035: import java.io.Serializable;
036: import java.lang.ref.Reference;
037: import java.lang.ref.ReferenceQueue;
038: import java.lang.ref.WeakReference;
039: import java.util.HashMap;
040: import java.util.HashSet;
041: import java.util.Iterator;
042: import java.util.Map;
043: import java.util.Set;
044:
045: import nextapp.echo2.app.RenderIdSupport;
046:
047: /**
048: * A table which provides an identifier-to-object mapping, with the objects
049: * being weakly referenced (i.e., the fact that they are held within this table
050: * will not prevent them from being garbage collected).
051: */
052: public class IdTable implements Serializable {
053:
054: private boolean hasHardReferences = false;
055: private transient Map idToReferenceMap = new HashMap();
056: private transient ReferenceQueue referenceQueue = new ReferenceQueue();
057:
058: /**
059: * Registers an object with the <code>IdTable</code>
060: *
061: * @param object the object to identify
062: */
063: public void register(RenderIdSupport object) {
064: purge();
065: String id = object.getRenderId();
066: WeakReference weakReference;
067: synchronized (idToReferenceMap) {
068: if (!idToReferenceMap.containsKey(id)) {
069: weakReference = new WeakReference(object,
070: referenceQueue);
071: idToReferenceMap.put(id, weakReference);
072: }
073: }
074: }
075:
076: /**
077: * Retrieves the object associated with the specified identifier.
078: *
079: * @param id the identifier
080: * @return the object (or null, if the object is not in the queue, perhaps
081: * due to having been dereferenced and garbage collected)
082: */
083: public Object getObject(String id) {
084: purge();
085: WeakReference weakReference;
086: synchronized (idToReferenceMap) {
087: weakReference = (WeakReference) idToReferenceMap.get(id);
088: }
089: if (weakReference == null) {
090: return null;
091: }
092: Object object = weakReference.get();
093: return object;
094: }
095:
096: /**
097: * Purges dereferenced/garbage collected entries from the
098: * <code>IdTable</code>.
099: */
100: private void purge() {
101: // Convert any hard references to weak references.
102: if (hasHardReferences) {
103: synchronized (idToReferenceMap) {
104: Iterator idIt = idToReferenceMap.keySet().iterator();
105: while (idIt.hasNext()) {
106: String id = (String) idIt.next();
107: Object object = idToReferenceMap.get(id);
108: if (!(object instanceof WeakReference)) {
109: WeakReference weakReference = new WeakReference(
110: object, referenceQueue);
111: idToReferenceMap.put(id, weakReference);
112: }
113: }
114: hasHardReferences = false;
115: }
116: }
117:
118: // Purge weak references that are no longer hard referenced elsewhere.
119: Reference reference = referenceQueue.poll();
120: if (reference == null) {
121: // No such references exist.
122: return;
123: }
124: Set referenceSet = new HashSet();
125: while (reference != null) {
126: referenceSet.add(reference);
127: reference = referenceQueue.poll();
128: }
129:
130: synchronized (idToReferenceMap) {
131: Iterator idIt = idToReferenceMap.keySet().iterator();
132: while (idIt.hasNext()) {
133: String id = (String) idIt.next();
134: if (referenceSet.contains(idToReferenceMap.get(id))) {
135: idIt.remove();
136: }
137: }
138: }
139: }
140:
141: /**
142: * @see java.io.Serializable
143: *
144: * Writes objects directly into values of Map as straight references.
145: * The values will be changed to <code>WeakReference</code>s when
146: * purge() is called.
147: */
148: private void readObject(ObjectInputStream in) throws IOException,
149: ClassNotFoundException {
150: in.defaultReadObject();
151:
152: idToReferenceMap = new HashMap();
153: referenceQueue = new ReferenceQueue();
154:
155: String id = (String) in.readObject();
156: if (id != null) {
157: // Hard references will be written.
158: hasHardReferences = true;
159:
160: // Load map and store objects as hard references.
161: while (id != null) {
162: RenderIdSupport object = (RenderIdSupport) in
163: .readObject();
164: idToReferenceMap.put(id, object);
165: id = (String) in.readObject();
166: }
167: }
168: }
169:
170: /**
171: * @see java.io.Serializable
172: */
173: private void writeObject(ObjectOutputStream out) throws IOException {
174: out.defaultWriteObject();
175: Iterator it = idToReferenceMap.keySet().iterator();
176: while (it.hasNext()) {
177: String id = (String) it.next();
178: out.writeObject(id);
179: Object object = idToReferenceMap.get(id);
180: if (object instanceof WeakReference) {
181: object = ((WeakReference) object).get();
182: }
183: out.writeObject(object);
184: }
185: // Write null to specify end of object.
186: out.writeObject(null);
187: }
188: }
|