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.List;
030:
031: import org.cougaar.util.UnaryPredicate;
032: import org.cougaar.util.log.Logger;
033: import org.cougaar.util.log.Logging;
034: import org.cougaar.util.CallerTracker;
035:
036: /**
037: * A filtered view of the blackboard that is updated between
038: * {@link org.cougaar.core.service.BlackboardService} transactions.
039: * <p>
040: * The view is defined by the {@link UnaryPredicate} filter's
041: * "execute" method.
042: *
043: * @see CollectionSubscription
044: * @see IncrementalSubscription
045: */
046: public abstract class Subscription {
047:
048: private static final Logger _logger = Logging
049: .getLogger(Subscription.class);
050:
051: /**
052: * Have we recieved our InitializeSubscriptionEnvelope yet?
053: * @see #apply(Envelope)
054: */
055: private boolean isInitialized = false;
056:
057: /** our Subscriber and interface to the blackboard */
058: protected Subscriber subscriber = null;
059:
060: /**
061: * set the Subscriber instance for the subscription. Should only be
062: * done by Subscriber.subscribe(), and will throw a RuntimeException
063: * if called more than once.
064: */
065: final void setSubscriber(Subscriber s) {
066: if (subscriber != null) {
067: throw new RuntimeException(
068: "Attempt to reset the Subscriber of " + this
069: + " to " + s + " from " + subscriber);
070: }
071: subscriber = s;
072:
073: // blackboard needs no delayed fill
074: if (subscriber instanceof Blackboard) {
075: //_logger.error("Preset InitializeSubscriptionEnvelope for "+
076: //this.predicate+" "+this.hashCode(), new Throwable());
077: setIsInitialized();
078: }
079: }
080:
081: /**
082: * @return the Subscriber instance which is the interface to the plugin.
083: */
084: public final Subscriber getSubscriber() {
085: return subscriber;
086: }
087:
088: /**
089: * Check to see if we're in a transaction for the named purpose if we
090: * have a subscription which supports transactions.
091: */
092: protected final void checkTransactionOK(String s) {
093: if (subscriber != null) {
094: subscriber.checkTransactionOK("hasChanged()");
095: }
096: }
097:
098: protected final void subscriberSignalExternalActivity() {
099: if (subscriber != null) {
100: subscriber.signalExternalActivity();
101: }
102: }
103:
104: /** The predicate that represents this subscription */
105: protected final UnaryPredicate predicate;
106:
107: /** stack tracker which selects the first frame that isn't core/lib stuff */
108: private static final CallerTracker pTracker = CallerTracker
109: .getPredicateTracker(new UnaryPredicate() {
110: public boolean execute(Object x) {
111: String sn = (String) x;
112: if (sn.startsWith("org.cougaar.core.")
113: || sn.startsWith("org.cougaar.lib.")) {
114: return false;
115: } else {
116: return true;
117: }
118: }
119: });
120:
121: /**
122: * @note Although allowed, use of DynamicUnaryPredicate can be extremely expensive
123: * and tends to create as many problems as it solves. When in pedantic mode,
124: * warning are emitted when DynamicUnaryPredicate is used. Disable Blackboard.PEDANTIC to quiet
125: * such warnings if you are sure you want to do this.
126: * @see Blackboard#PEDANTIC
127: */
128: public Subscription(UnaryPredicate p) {
129: if (Blackboard.PEDANTIC
130: && p instanceof org.cougaar.util.DynamicUnaryPredicate
131: && pTracker.isNew()) {
132: _logger.warn(
133: "Performance hit: use of DynamicUnaryPredicate "
134: + p, new Throwable());
135: }
136: if (p == null)
137: throw new IllegalArgumentException(
138: "Predicate must be non-null");
139: predicate = p;
140: }
141:
142: public String getName() {
143: return predicate.getClass().getName();
144: }
145:
146: /**
147: * Decide if the object is applicable to the subscription
148: * and make the appropriate changes.
149: * Called by Envelope.wrapperAdd to add an object to the
150: * subscription view.
151: * @param isVisible If FALSE will make the change quietly, e.g. after
152: * rehydration from persistence plugin.
153: * @return true IFF the subscription was changed as a result of the call.
154: */
155: boolean conditionalAdd(Object o, boolean isVisible) {
156: if (predicate.execute(o)) {
157: privateAdd(o, isVisible);
158: return true;
159: } else {
160: return false;
161: }
162: }
163:
164: /**
165: * Decide if the object is applicable to the subscription
166: * and make the appropriate changes.
167: * Called by Envelope.wrapperAdd to remove an object in the
168: * subscription view.
169: * @param isVisible If FALSE will make the change quietly, e.g. after
170: * rehydration from persistence plugin.
171: * @return true IFF the subscription was changed as a result of the call.
172: */
173: boolean conditionalRemove(Object o, boolean isVisible) {
174: if (predicate.execute(o)) {
175: privateRemove(o, isVisible);
176: return true;
177: } else {
178: return false;
179: }
180: }
181:
182: /**
183: * Decide if the object is applicable to the subscription
184: * and make the appropriate changes.
185: * Called by Envelope.wrapperAdd to mark as changed an object in the
186: * subscription view.
187: * @param changes a List of ChangeReport instances describing the changes
188: * made to the object. May be null.
189: * @param isVisible If FALSE will make the change quietly, e.g. after
190: * rehydration from persistence plugin.
191: * @return true IFF the subscription was changed as a result of the call.
192: */
193: boolean conditionalChange(Object o, List changes, boolean isVisible) {
194: if (predicate.execute(o)) {
195: privateChange(o, changes, isVisible);
196: return true;
197: } else {
198: return false;
199: }
200: }
201:
202: abstract protected void privateAdd(Object o, boolean isVisible);
203:
204: abstract protected void privateRemove(Object o, boolean isVisible);
205:
206: abstract protected void privateChange(Object o, List changes,
207: boolean isVisible);
208:
209: //
210: // Want to move them down to a SubscriptionWithDeltas interface
211: //
212:
213: // Change sets
214: protected boolean myHasChanged = false;
215:
216: /**
217: * Check to see if this subscription has changed since the
218: * last transaction.
219: * <p>
220: * To be precise, this indicates if there were any visible changes
221: * to the subscription contents (add/change/remove) in the interval
222: * between the current and the previous calls to
223: * Subscriber.openTransaction()
224: */
225: public final boolean hasChanged() {
226: checkTransactionOK("hasChanged()");
227: return myHasChanged;
228: }
229:
230: // changed to package-protected
231: protected final void setChanged(boolean changed) {
232: myHasChanged = changed;
233: }
234:
235: /**
236: * Called by Subscriber's transaction system to update the
237: * changes (and delta lists, if applicable) tracking.
238: * @see #hasChanged()
239: */
240: protected void resetChanges() {
241: setChanged(false);
242: }
243:
244: /**
245: * Apply a set of transactional changes to our subscription.
246: * Envelopes are ignored until a matching InitializeSubscriptionEnvelope
247: * has been received.
248: * @note The real work of applying the envelope to the subscription is accomplished
249: * by calling {@link #privateApply(Envelope)}.
250: */
251: public boolean apply(Envelope envelope) {
252: // if this is an ISE, check to see if it is ours!
253: if (envelope instanceof InitializeSubscriptionEnvelope) {
254: InitializeSubscriptionEnvelope ise = (InitializeSubscriptionEnvelope) envelope;
255: if (ise.getSubscription() == this ) {
256: if (isInitialized) {
257: _logger
258: .error("Received redundant InitializeSubscriptionEnvelope for "
259: + this .predicate);
260: } else {
261: if (_logger.isDebugEnabled()) {
262: _logger
263: .debug("Received InitializeSubscriptionEnvelope for "
264: + this .predicate);
265: }
266: setIsInitialized();
267: }
268: }
269: return false; // doesn't actually change the subscription in any case
270: } else {
271: if (isInitialized) {
272: return privateApply(envelope);
273: } else {
274: if (_logger.isInfoEnabled()) {
275: _logger.info("Dropped an envelope for "
276: + this .predicate);
277: }
278: return false;
279: }
280: }
281: }
282:
283: final void setIsInitialized() {
284: isInitialized = true;
285: }
286:
287: /** Fill the subscription with the initial contents. */
288: public void fill(Envelope envelope) {
289: // logically, just call apply(envelope), but we need to avoid the isInitialized checks.
290: if (privateApply(envelope)) {
291: setChanged(true);
292: subscriberSignalExternalActivity();
293: }
294: }
295:
296: protected final boolean privateApply(Envelope envelope) {
297: return envelope.applyToSubscription(this);
298: }
299:
300: }
|