001: /*
002: * <copyright>
003: *
004: * Copyright 2001-2007 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.Iterator;
032: import java.util.List;
033:
034: import org.cougaar.bootstrap.SystemProperties;
035: import org.cougaar.core.component.BindingSite;
036: import org.cougaar.core.component.Component;
037: import org.cougaar.core.component.ServiceBroker;
038: import org.cougaar.core.mts.MessageAddress;
039: import org.cougaar.core.service.AgentIdentificationService;
040: import org.cougaar.core.service.AlarmService;
041: import org.cougaar.core.service.BlackboardService;
042: import org.cougaar.core.service.SchedulerService;
043: import org.cougaar.util.SyncTriggerModelImpl;
044: import org.cougaar.util.Trigger;
045: import org.cougaar.util.TriggerModel;
046: import org.cougaar.core.service.SuicideService;
047:
048: /**
049: * This component is a standard base class for {@link Component}s
050: * that use the {@link BlackboardService}.
051: * <p>
052: * Plugins are the most common example of such components.<br>
053: * <code>ComponentPlugin</code> is an extension of this class.
054: * <p>
055: * Create a derived class by implementing
056: * <tt>setupSubscriptions()</tt> and <tt>execute()</tt>.
057: * <p>
058: * Note that both "precycle()" and "cycle()" will be run by the
059: * scheduler. This means that the scheduling order <i>in relation to
060: * other scheduled Components</i> may be *random* (i.e. this
061: * BlackboardClientComponent might load first but be precycled last!). In
062: * general a Component should <b>not</b> make assumptions about the
063: * load or schedule ordering.
064: *
065: * @see org.cougaar.core.plugin.ComponentPlugin
066: */
067: public abstract class BlackboardClientComponent extends
068: org.cougaar.util.GenericStateModelAdapter implements Component,
069: BlackboardClient {
070: private static final boolean SET_THREAD_NAME = SystemProperties
071: .getBoolean(
072: "org.cougaar.core.blackboard.client.setThreadName",
073: true);
074:
075: private Object parameter = null;
076:
077: protected MessageAddress agentId;
078: private SchedulerService scheduler;
079: protected BlackboardService blackboard;
080: protected AlarmService alarmService;
081: protected AgentIdentificationService agentIdentificationService;
082: private SuicideService ss;
083: protected String blackboardClientName;
084:
085: private BindingSite bindingSite;
086: private ServiceBroker sb;
087:
088: private TriggerModel tm;
089: private SubscriptionWatcher watcher;
090:
091: public BlackboardClientComponent() {
092: }
093:
094: /**
095: * Called just after construction (via introspection) by the
096: * loader if a non-null parameter Object was specified by
097: * the ComponentDescription.
098: */
099: public void setParameter(Object param) {
100: parameter = param;
101: setBlackboardClientName(computeBlackboardClientName(param));
102: }
103:
104: /**
105: * @return the parameter set by {@link #setParameter}
106: */
107: public Object getParameter() {
108: return parameter;
109: }
110:
111: /**
112: * Get any Component parameters passed by the instantiator.
113: * @return The parameter specified
114: * if it was a collection, a collection with one element (the parameter) if
115: * it wasn't a collection, or an empty collection if the parameter wasn't
116: * specified.
117: */
118: public Collection getParameters() {
119: if (parameter == null) {
120: return new ArrayList(0);
121: } else {
122: if (parameter instanceof Collection) {
123: return (Collection) parameter;
124: } else {
125: List l = new ArrayList(1);
126: l.add(parameter);
127: return l;
128: }
129: }
130: }
131:
132: /**
133: * Binding site is set by reflection at creation-time.
134: */
135: public void setBindingSite(BindingSite bs) {
136: bindingSite = bs;
137: // set "sb" now, for backwards compatibility
138: sb = bs.getServiceBroker();
139: }
140:
141: /**
142: * Get the binding site, for subclass use.
143: */
144: protected BindingSite getBindingSite() {
145: return bindingSite;
146: }
147:
148: /**
149: * ServiceBroker is set by reflection at creation-time.
150: */
151: public void setServiceBroker(ServiceBroker sb) {
152: this .sb = sb;
153: }
154:
155: /**
156: * Get the ServiceBroker, for subclass use.
157: */
158: protected ServiceBroker getServiceBroker() {
159: return sb;
160: }
161:
162: // rely upon load-time introspection ("public void setX(X)") to
163: // set these services. don't worry about revokation.
164: public final void setSchedulerService(SchedulerService ss) {
165: scheduler = ss;
166: }
167:
168: public final void setBlackboardService(BlackboardService bs) {
169: blackboard = bs;
170: }
171:
172: public final void setAlarmService(AlarmService s) {
173: alarmService = s;
174: }
175:
176: public final void setAgentIdentificationService(
177: AgentIdentificationService ais) {
178: agentIdentificationService = ais;
179: MessageAddress an;
180: if ((ais != null)
181: && ((an = ais.getMessageAddress()) instanceof MessageAddress)) {
182: agentId = (MessageAddress) an;
183: // } else { // Revocation -- nothing more to do
184: }
185: }
186:
187: public void setSuicideService(SuicideService ss) {
188: this .ss = ss;
189: }
190:
191: /**
192: * Get the blackboard service, for subclass use.
193: */
194: protected BlackboardService getBlackboardService() {
195: return blackboard;
196: }
197:
198: /**
199: * Get the alarm service, for subclass use.
200: */
201: protected AlarmService getAlarmService() {
202: return alarmService;
203: }
204:
205: protected final void requestCycle() {
206: tm.trigger();
207: }
208:
209: //
210: // implement GenericStateModel:
211: //
212:
213: public void load() {
214: super .load();
215:
216: // create a blackboard watcher
217: this .watcher = new SubscriptionWatcher() {
218: public void signalNotify(int event) {
219: // gets called frequently as the blackboard objects change
220: super .signalNotify(event);
221: requestCycle();
222: }
223:
224: public String toString() {
225: return "ThinWatcher("
226: + BlackboardClientComponent.this .toString()
227: + ")";
228: }
229: };
230:
231: // create a callback for running this component
232: Trigger myTrigger = new Trigger() {
233: String compName = null;
234: private boolean didPrecycle = false;
235:
236: // no need to "sync" when using "SyncTriggerModel"
237: public void trigger() {
238: Thread currentThread = Thread.currentThread();
239: String savedName = null;
240: if (SET_THREAD_NAME) {
241: savedName = currentThread.getName();
242: if (compName == null)
243: compName = getBlackboardClientName();
244: currentThread.setName(compName);
245: }
246: awakened = watcher.clearSignal();
247: try {
248: if (didPrecycle) {
249: cycle();
250: } else {
251: didPrecycle = true;
252: precycle();
253: }
254: } finally {
255: awakened = false;
256: if (SET_THREAD_NAME) {
257: currentThread.setName(savedName);
258: }
259: }
260: }
261:
262: public String toString() {
263: return "Trigger("
264: + BlackboardClientComponent.this .toString()
265: + ")";
266: }
267: };
268:
269: // create the trigger model
270: this .tm = new SyncTriggerModelImpl(scheduler, myTrigger);
271:
272: // activate the blackboard watcher
273: blackboard.registerInterest(watcher);
274:
275: // activate the trigger model
276: //
277: // note that this jumps the gun a bit, since we'll request our
278: // precycle before starting, but that's the behavior the plugins
279: // currently expect.
280: tm.initialize();
281: tm.load();
282: }
283:
284: public void start() {
285: super .start();
286: tm.start();
287: // Tell the scheduler to run me at least this once
288: requestCycle();
289: }
290:
291: public void suspend() {
292: super .suspend();
293: tm.suspend();
294: }
295:
296: public void resume() {
297: super .resume();
298: tm.resume();
299: }
300:
301: public void stop() {
302: super .stop();
303: tm.stop();
304: }
305:
306: public void halt() {
307: super .halt();
308: tm.halt();
309: }
310:
311: public void unload() {
312: super .unload();
313: if (tm != null) {
314: tm.unload();
315: tm = null;
316: }
317: blackboard.unregisterInterest(watcher);
318: if (alarmService != null) {
319: sb.releaseService(this , AlarmService.class, alarmService);
320: alarmService = null;
321: }
322: if (blackboard != null) {
323: sb
324: .releaseService(this , BlackboardService.class,
325: blackboard);
326: blackboard = null;
327: }
328: if (scheduler != null) {
329: sb.releaseService(this , SchedulerService.class, scheduler);
330: scheduler = null;
331: }
332: }
333:
334: //
335: // implement basic "callback" actions
336: //
337:
338: protected void precycle() {
339: try {
340: blackboard.openTransaction();
341: setupSubscriptions();
342:
343: // run execute here so subscriptions don't miss out on the first
344: // batch in their subscription addedLists
345: execute(); // MIK: I don't like this!!!
346: } catch (Throwable t) {
347: if (ss != null)
348: ss.die(agentId, t);
349: } finally {
350: blackboard.closeTransaction();
351: }
352: }
353:
354: protected void cycle() {
355: // do stuff
356: try {
357: blackboard.openTransaction();
358: if (shouldExecute()) {
359: execute();
360: }
361: } catch (Throwable t) {
362: if (ss != null)
363: ss.die(agentId, t);
364: } finally {
365: blackboard.closeTransaction();
366: }
367: }
368:
369: protected boolean shouldExecute() {
370: return (wasAwakened() || blackboard.haveCollectionsChanged());
371: }
372:
373: /**
374: * Get the local agent's address.
375: *
376: * Also consider adding a "getMessageAddress()" method backed
377: * by the NodeIdentificationService.
378: */
379: protected MessageAddress getAgentIdentifier() {
380: return agentId;
381: }
382:
383: /** @deprecated Use getAgentIdentifier() */
384: protected MessageAddress getMessageAddress() {
385: return getAgentIdentifier();
386: }
387:
388: /**
389: * Called once after initialization, as a pre-{@link #execute}.
390: */
391: protected abstract void setupSubscriptions();
392:
393: /**
394: * Called every time this component is scheduled to run.
395: */
396: protected abstract void execute();
397:
398: /** storage for wasAwakened - only valid during cycle(). */
399: private boolean awakened = false;
400:
401: /**
402: * True IFF were we awakened explicitly (i.e. we were asked to run
403: * even if no subscription activity has happened).
404: * The value is valid only within the scope of the cycle() method.
405: */
406: protected final boolean wasAwakened() {
407: return awakened;
408: }
409:
410: //
411: // oddball methods required by BlackboardClient:
412: //
413:
414: // for BlackboardClient use
415: // DO NOT SYNCHRONIZE THIS METHOD; a deadlock will result.
416: // It doesn't matter if two threads compute the name.
417: public String getBlackboardClientName() {
418: if (blackboardClientName == null) {
419: setBlackboardClientName(computeBlackboardClientName(parameter));
420: }
421: return blackboardClientName;
422: }
423:
424: private void setBlackboardClientName(String newName) {
425: blackboardClientName = newName;
426: }
427:
428: private String computeBlackboardClientName(Object parameter) {
429: StringBuffer buf = new StringBuffer();
430: buf.append(getClass().getName());
431: if (parameter instanceof Collection) {
432: buf.append("[");
433: String sep = "";
434: for (Iterator params = ((Collection) parameter).iterator(); params
435: .hasNext();) {
436: buf.append(sep);
437: buf.append(params.next().toString());
438: sep = ",";
439: }
440: buf.append("]");
441: }
442: return buf.substring(0);
443: }
444:
445: public long currentTimeMillis() {
446: if (alarmService != null)
447: return alarmService.currentTimeMillis();
448: else
449: return System.currentTimeMillis();
450: }
451:
452: public String toString() {
453: return getBlackboardClientName();
454: }
455: }
|