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.ArrayList;
030: import java.util.Collection;
031: import java.util.HashMap;
032: import java.util.Iterator;
033: import java.util.List;
034: import java.util.Map;
035:
036: /**
037: * Required transaction support to record a thread's active
038: * {@link Subscriber} and maintain attached ChangeReports.
039: * <p>
040: * May be extended to add additional functionality to a Subscriber.
041: * @see org.cougaar.core.blackboard.Subscriber#newTransaction()
042: */
043: public class Transaction {
044: // instantiable stuff
045: protected Subscriber subscriber;
046:
047: public Transaction(Subscriber s) {
048: subscriber = s;
049: }
050:
051: /** a map of object to List (of outstanding changes) */
052: private Map _map = null;
053:
054: private final Map map() {
055: if (_map == null) {
056: _map = new HashMap(5);
057: }
058: return _map;
059: }
060:
061: /**
062: * Note a ChangeReport for the next {@link #getChangeReports}.
063: * <p>
064: * May be called by anyone (inside a Transaction) wishing to
065: * publish a detailed ChangeReport on an object.
066: *
067: * @see ActiveSubscriptionObject#changingInBlackboard
068: */
069: public final static void noteChangeReport(Object o, ChangeReport cr) {
070: Transaction t = getCurrentTransaction();
071: if (t != null) {
072: t.private_noteChangeReport(o, cr);
073: } else {
074: System.err
075: .println("Warning: ChangeReport added out of transaction on "
076: + o + ":\n\t" + cr);
077: Thread.dumpStack();
078: }
079: }
080:
081: /**
082: * Note a Collection of ChangeReports for the next {@link
083: * #getChangeReports}.
084: * <p>
085: * May be called by anyone (inside a Transaction) wishing to publish a
086: * detailed set of ChangeReports on an object.
087: *
088: * @see ActiveSubscriptionObject#changingInBlackboard
089: */
090: public final static void noteChangeReport(Object o, Collection c) {
091: Transaction t = getCurrentTransaction();
092: if (t != null) {
093: t.private_noteChangeReport(o, c);
094: } else {
095: System.err
096: .println("Warning: ChangeReport added out of transaction on "
097: + o + ":\n\t" + c);
098: Thread.dumpStack();
099: }
100: }
101:
102: private final synchronized void private_noteChangeReport(Object o,
103: ChangeReport r) {
104: Map m = map();
105: List changes = (List) m.get(o);
106: if (changes == null) {
107: changes = new ArrayList(3);
108: m.put(o, changes);
109: }
110: changes.add(r);
111: }
112:
113: /** Bulk version of noteChangeReport */
114: private final synchronized void private_noteChangeReport(Object o,
115: Collection r) {
116: Map m = map();
117: List changes = (List) m.get(o);
118: if (changes == null) {
119: changes = new ArrayList(r.size());
120: m.put(o, changes);
121: }
122: changes.addAll(r);
123: }
124:
125: /**
126: * Used by {@link Subscriber} to take {@link ChangeReport}s noted
127: * by {@link #noteChangeReport(Object,ChangeReport)} and merge them
128: * with the optional change reports specified in the plugin's {@link
129: * org.cougaar.core.service.BlackboardService#publishChange}.
130: * <p>
131: * Calling this method should atomically return the List and
132: * clear the stored value. Implementations may also want to
133: * keep track of the changing thread to avoid changing the object
134: * simultaneously in different transactions. <p>
135: * <p>
136: * Plugins must <em>never</em> call this or changes will not be propagated.
137: * <p>
138: * The List returned (if any) may not be reused, as the infrastructure
139: * can and will modify it for its purposes.
140: *
141: * @return A List of ChangeReport instances or null. Implementations
142: * are encouraged to return null if no trackable changes were made, rather
143: * than an empty List.
144: * @see ChangeReport
145: */
146: public synchronized final List getChangeReports(Object o) {
147: // be careful not to create map unless we need to...
148: if (_map == null)
149: return null;
150: List l = (List) _map.get(o);
151: if (l != null) {
152: _map.remove(o);
153: }
154: return l;
155: }
156:
157: /**
158: * Called by {@link Subscriber} to check for changes made to objects
159: * which hadn't actually been publishChanged.
160: */
161: synchronized final Map getChangeMap() {
162: Map m = _map;
163: _map = null;
164: return m;
165: }
166:
167: private static final ThreadLocal transactionTable = new ThreadLocal();
168:
169: /** Register a transaction as open */
170: public final static void open(Transaction t) {
171: Transaction o = (Transaction) transactionTable.get();
172: if (o != null) {
173: throw new RuntimeException(
174: "Attempt to open a nested transaction:\n"
175: + "\tPrevious was: " + o + "\n"
176: + "\tNext is: " + t);
177: }
178: transactionTable.set(t);
179: }
180:
181: /** Register a transaction as closed */
182: public final static void close(Transaction t) {
183: Transaction o = (Transaction) transactionTable.get();
184: if (o != t) {
185: throw new RuntimeException(
186: "Attempt to close a transaction inappropriately:\n"
187: + "\tPrevious was: " + o + "\n"
188: + "\tNext is: " + t);
189: }
190: transactionTable.set(null);
191: }
192:
193: /** get the current Transaction. */
194: public final static Transaction getCurrentTransaction() {
195: return (Transaction) transactionTable.get();
196: }
197: }
|