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.blackboard;
028:
029: import java.util.Collection;
030: import java.util.HashSet;
031: import java.util.Iterator;
032: import java.util.Map;
033: import java.util.WeakHashMap;
034:
035: import org.cougaar.bootstrap.SystemProperties;
036: import org.cougaar.util.DynamicUnaryPredicate;
037: import org.cougaar.util.StackElements;
038: import org.cougaar.util.UnaryPredicate;
039:
040: /**
041: * A subscription that only tracks add/change/remove deltas.
042: * <p>
043: * See {@link CollectionSubscription} for a system property that identifies
044: * subscriptions that could potentially be changed to DeltaSubscriptions.
045: * <p>
046: * We subclass {@link IncrementalSubscription} to both reuse code and to make
047: * it easy for existing plugins to switch over to this class. For example, the
048: * plugin won't need to change its field declaration from:<pre>
049: * private IncrementalSubscription sub;
050: * </pre>
051: * to be "DeltaSubscription". The only change is the subscribe line:<pre>
052: * sub = (IncrementalSubscription)
053: * blackboard.subscribe(new DeltaSubscription(pred));
054: * </pre>
055: * The downside is a tiny cost per subscription for the pointer to the unused
056: * "real" backing collection.
057: *
058: * @property org.cougaar.core.blackboard.DeltaSubscription.enable
059: * Enable DeltaSubscription memory saving. This property exists so we can
060: * easily profile the memory advantage of DeltaSubscriptions when compared to
061: * using regular IncrementalSubscriptions.
062: * (defaults to true)
063: *
064: * @property org.cougaar.core.blackboard.DeltaSubscription.recordStack
065: * Track the creation point of every DeltaSubscription, so if it is misused
066: * as a CollectionSubscription the runtime exception will point to the original
067: * "subscribe" call.
068: * (defaults to false)
069: */
070: public class DeltaSubscription extends IncrementalSubscription {
071:
072: private static final String ENABLED_PROP = "org.cougaar.core.blackboard.DeltaSubscription.enable";
073: private static final boolean ENABLED = SystemProperties.getBoolean(
074: ENABLED_PROP, true);
075:
076: private static final String RECORD_STACK_PROP = "org.cougaar.core.blackboard.DeltaSubscription.recordStack";
077: private static final boolean RECORD_STACK = SystemProperties
078: .getBoolean(RECORD_STACK_PROP);
079:
080: private static final Collection BLOCKER_COLLECTION = new BlockerCollection();
081:
082: public DeltaSubscription(UnaryPredicate p) {
083: super (p, makeCollection());
084: if (ENABLED && p instanceof DynamicUnaryPredicate) {
085: throw new IllegalArgumentException("Dynamic predicate: "
086: + p);
087: }
088: }
089:
090: private static Collection makeCollection() {
091: if (!ENABLED) {
092: return new HashSet();
093: } else if (RECORD_STACK) {
094: return new TrackedCollection();
095: } else {
096: return BLOCKER_COLLECTION;
097: }
098: }
099:
100: public String toString() {
101: return "Delta Subscription";
102: }
103:
104: private static class BlockerCollection implements Collection {
105: // allow our CollectionSubscription to call these methods
106: public boolean add(Object o) {
107: return false;
108: }
109:
110: public boolean remove(Object o) {
111: return false;
112: }
113:
114: // block the rest
115: public int size() {
116: die();
117: return 0;
118: }
119:
120: public boolean isEmpty() {
121: die();
122: return false;
123: }
124:
125: public boolean contains(Object o) {
126: die();
127: return false;
128: }
129:
130: public Iterator iterator() {
131: die();
132: return null;
133: }
134:
135: public Object[] toArray() {
136: die();
137: return null;
138: }
139:
140: public Object[] toArray(Object a[]) {
141: die();
142: return null;
143: }
144:
145: public boolean containsAll(Collection c) {
146: die();
147: return false;
148: }
149:
150: public boolean addAll(Collection c) {
151: die();
152: return false;
153: }
154:
155: public boolean removeAll(Collection c) {
156: die();
157: return false;
158: }
159:
160: public boolean retainAll(Collection c) {
161: die();
162: return false;
163: }
164:
165: public void clear() {
166: die();
167: }
168:
169: public boolean equals(Object o) {
170: die();
171: return false;
172: }
173:
174: public int hashCode() {
175: die();
176: return 0;
177: }
178:
179: protected void die() {
180: // create error message
181: StringBuffer buf = new StringBuffer();
182: buf
183: .append("Invalid use of a DeltaSubscription as a Collection."
184: + "\nTo disable delta subscriptions, set -D"
185: + ENABLED_PROP + "=false");
186: Throwable t = getStack();
187: if (t == null) {
188: buf
189: .append("\nFor subscription creation stack info, run again with "
190: + "-D" + RECORD_STACK_PROP + "=true");
191: } else {
192: buf.append("\nCreated at");
193: StackTraceElement[] ste = t.getStackTrace();
194: for (int i = 0; i < ste.length; i++) {
195: buf.append("\n\tat ").append(ste[i]);
196: }
197: buf.append("\nObserved at:");
198: }
199: String msg = buf.toString();
200:
201: // throw exception
202: throw new UnsupportedOperationException(msg);
203: }
204:
205: protected Throwable getStack() {
206: return null;
207: }
208: }
209:
210: private static class TrackedCollection extends BlockerCollection {
211: private static final Map stacks = new WeakHashMap();
212: // keep the StackElements and not just the throwable, otherwise our cache
213: // won't work (since it's weak)
214: private final StackElements stack;
215:
216: public TrackedCollection() {
217: this .stack = captureStack();
218: }
219:
220: // enhance the "die()" message with our allocation point
221: protected Throwable getStack() {
222: return stack.getThrowable();
223: }
224:
225: private static StackElements captureStack() {
226: StackElements se = new StackElements(new Throwable());
227: synchronized (stacks) {
228: StackElements cached_se = (StackElements) stacks
229: .get(se);
230: if (cached_se == null) {
231: stacks.put(se, se);
232: } else {
233: se = cached_se;
234: }
235: }
236: return se;
237: }
238: }
239: }
|