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.lib.filter;
028:
029: import java.io.Serializable;
030: import java.util.ArrayList;
031: import java.util.Collection;
032: import java.util.Iterator;
033: import java.util.List;
034: import java.util.Vector;
035:
036: import org.cougaar.core.blackboard.IncrementalSubscription;
037: import org.cougaar.core.mts.MessageAddress;
038: import org.cougaar.core.plugin.ComponentPlugin;
039: import org.cougaar.core.service.LoggingService;
040: import org.cougaar.lib.callback.UTILFilterCallback;
041: import org.cougaar.lib.callback.UTILRehydrateReactor;
042: import org.cougaar.lib.param.Param;
043: import org.cougaar.lib.param.ParamMap;
044: import org.cougaar.lib.util.UTILAggregate;
045: import org.cougaar.lib.util.UTILAllocate;
046: import org.cougaar.lib.util.UTILAsset;
047: import org.cougaar.lib.util.UTILExpand;
048: import org.cougaar.lib.util.UTILParamTable;
049: import org.cougaar.lib.util.UTILPreference;
050: import org.cougaar.lib.util.UTILPrepPhrase;
051: import org.cougaar.lib.util.UTILVerify;
052: import org.cougaar.lib.xml.parser.ParamParser;
053: import org.cougaar.planning.ldm.PlanningFactory;
054: import org.cougaar.planning.ldm.plan.AllocationResult;
055: import org.cougaar.planning.ldm.plan.AuxiliaryQueryType;
056: import org.cougaar.planning.ldm.plan.NewTask;
057: import org.cougaar.planning.ldm.plan.NewWorkflow;
058: import org.cougaar.planning.ldm.plan.Plan;
059: import org.cougaar.planning.ldm.plan.PlanElement;
060: import org.cougaar.planning.ldm.plan.Task;
061: import org.cougaar.planning.service.LDMService;
062: import org.cougaar.util.UnaryPredicate;
063:
064: /**
065: * <pre>
066: * Implementation of UTILPlugin interface.
067: *
068: * parameters are read when the plugin is loaded.
069: *
070: * filterCallbacks are added just before the plugin
071: * thread is started.
072: * </pre>
073: */
074: public class UTILPluginAdapter extends ComponentPlugin implements
075: UTILPlugin {
076:
077: protected MessageAddress getOriginalAgentID() {
078: return null;
079: }
080:
081: protected boolean didSpawn() {
082: // old state-object code (bug 3513)
083: return false;
084: }
085:
086: /**
087: * This method is called before cycle is ever called to
088: * set up safe transactions for container subscriptions.
089: *
090: * setupFilters () is wrapped in an open/closeTransaction block.
091: */
092: protected final void setupSubscriptions() {
093: if (blackboard.didRehydrate())
094: justRehydrated();
095:
096: paramParser = new ParamParser();
097: getEnvData();
098: setInstanceVariables();
099:
100: preFilterSetup();
101:
102: setupFilters();
103:
104: localSetup();
105: }
106:
107: private LDMService ldmService = null;
108:
109: public final void setLDMService(LDMService s) {
110: ldmService = s;
111: }
112:
113: protected final LDMService getLDMService() {
114: return ldmService;
115: }
116:
117: /**
118: * set instance variables here that don't depend on
119: * env var parameters.
120: */
121: private void setInstanceVariables() {
122: ldmf = getLDMService().getFactory();
123: myClusterName = getAgentIdentifier().getAddress();
124: realityPlan = ldmf.getRealityPlan();
125: mySubscriptions = new Vector();
126: }
127:
128: /**
129: * A place to put setup actions that will occur before
130: * any filters are active.
131: *
132: * I.e. objects created here can be referenced in
133: * filterCallback listener methods.
134: *
135: * Default does nothing.
136: */
137: public void preFilterSetup() {
138: }
139:
140: /****************************************************************
141: ** Setup Filters...
142: **/
143:
144: /**
145: * <pre>
146: * The idea is to add subscriptions (via the filterCallback), and when
147: * they change, to have the callback react to the change, and tell
148: * the listener (many times the plugin) what to do.
149: *
150: * The intent is that all "on start-up" filters get added here.
151: *
152: * Note that there is no restraint on when a subclass can add a
153: * filter, as long as it doesn't happen before setupFilters ().
154: *
155: * </pre>
156: * @see #addFilter ()
157: */
158: public void setupFilters() {
159: }
160:
161: /**
162: * mySubscriptions is synchronized here and in cycle to make
163: * sure that no subscriptions are added while iterating over the
164: * list of current subscriptions (in cycle ()).
165: *
166: * This method can be called at any time, not just from setupFilters ().
167: *
168: * @see #setupFilters ()
169: */
170: public void addFilter(UTILFilterCallback callbackObj) {
171: if (callbackObj == null) {
172: if (isDebugEnabled())
173: debug("Adding null filter (THIS MAY BE OK.)");
174: return;
175: }
176:
177: synchronized (mySubscriptions) {
178: mySubscriptions.addElement(callbackObj);
179: }
180: }
181:
182: /**
183: * mySubscriptions is synchronized here and in cycle to make
184: * sure that no subscriptions are removed while iterating over the
185: * list of current subscriptions (in cycle ()).
186: *
187: * This method can be called at any time, not just from setupFilters ().
188: *
189: * BOZO - should make addFilter automatically do subscribe too.
190: * Not enough time now (9-4-99) -- should do later!!!
191: * Problem is if we want special containers to be used with subscriptions.
192: * A number of API changes would be necessary...
193: *
194: * @see #setupFilters ()
195: */
196: public void removeFilter(UTILFilterCallback callbackObj) {
197: synchronized (mySubscriptions) {
198: mySubscriptions.removeElement(callbackObj);
199: blackboard.unsubscribe(callbackObj.getSubscription());
200: }
201: }
202:
203: /**
204: * Implemented for UTILFilterCallbackListener
205: *
206: * This allows callbacks to make subscriptions.
207: */
208: public IncrementalSubscription subscribeFromCallback(
209: UnaryPredicate pred) {
210: if (isDebugEnabled())
211: debug(getName() + " : Subscribing to " + pred);
212:
213: return (IncrementalSubscription) blackboard.subscribe(pred);
214: }
215:
216: /**
217: * Implemented for UTILFilterCallbackListener
218: *
219: * This allows callbacks to make subscriptions with a special container.
220: */
221: public IncrementalSubscription subscribeFromCallback(
222: UnaryPredicate pred, Collection specialContainer) {
223: if (isDebugEnabled())
224: debug(getName() + " : Subscribing to " + pred);
225:
226: return (IncrementalSubscription) blackboard.subscribe(pred,
227: specialContainer);
228: }
229:
230: /**
231: * Allows child classes to read additional data from environment files.
232: *
233: * Derived classes should call super ().
234: **/
235: public void getEnvData() {
236:
237: // Don't care to clone the vector?
238: Vector myP;
239: if (getParameters() != null)
240: myP = new Vector(getParameters());
241: else
242: myP = new Vector();
243:
244: // create the parameter table
245: MessageAddress agentID = (didSpawn() ? getOriginalAgentID()
246: : getAgentIdentifier());
247:
248: myParams = createParamTable(myP, agentID);
249:
250: // set instance variables
251:
252: if (isInfoEnabled()) {
253: String optionalEnvFile = null;
254: try {
255: optionalEnvFile = myParams.getStringParam("envFile");
256: if (optionalEnvFile == null)
257: optionalEnvFile = myParams
258: .getStringParam("default_envFile");
259: } catch (Exception e) {
260: }
261: info(getClassName() + ".getEnvData : read param file <"
262: + optionalEnvFile + ">");
263: }
264:
265: if (isDebugEnabled())
266: debug(getName() + " - Params : " + myParams);
267: }
268:
269: /**
270: * Subclass to return a different ParamTable.
271: */
272: protected ParamMap createParamTable(Vector envParams,
273: MessageAddress ident) {
274: if (isDebugEnabled()) {
275: debug(getName()
276: + " - creating param table, identifier was "
277: + ident);
278: for (Iterator i = envParams.iterator(); i.hasNext();) {
279: String runtimeParam = (String) i.next();
280: Param p = paramParser.getParam(runtimeParam);
281: if (p != null) {
282: String name = p.getName();
283: debug("UTILPluginAdapter.createParamTable() - got param name "
284: + name + " with value " + p);
285: }
286: }
287: }
288: return new UTILParamTable(envParams, ident, logger);
289: }
290:
291: /** <pre>
292: * Place to put any local plugin startup initiallization.
293: * This is a good place to read local data from files.
294: *
295: * This is useful during integration when getting as much as debugrmation
296: * as possible at the point of failure is critical.
297: * </pre>
298: */
299: public void localSetup() {
300: try {
301: if (getMyParams().hasParam("skipLowConfidence"))
302: skipLowConfidence = getMyParams().getBooleanParam(
303: "skipLowConfidence");
304: else
305: skipLowConfidence = true;
306:
307: if (getMyParams().hasParam("HIGH_CONFIDENCE"))
308: HIGH_CONFIDENCE = getMyParams().getFloatParam(
309: "HIGH_CONFIDENCE");
310: else
311: HIGH_CONFIDENCE = 0.99d;
312: } catch (Exception e) {
313: }
314: }
315:
316: /**
317: * Accessor to param table. Child classes can use this to
318: * add additional parameters.
319: * @return ParamTable
320: */
321: public ParamMap getMyParams() {
322: return myParams;
323: }
324:
325: /**
326: * <pre>
327: * if the reported allocation result has been successfully calculated
328: * set the estimated equal to the reported to send a notification
329: * back up.
330: *
331: * Only reports results if the confidence is above a threshold.
332: * This threshold is currently 0.99. All changes will be reported
333: * if skipLowConfidence is set to false.
334: *
335: * Takes care not to copy nulls from reported aux query results into
336: * estimated fields.
337: * </pre>
338: */
339: public void updateAllocationResult(PlanElement cpe) {
340: if (isDebugEnabled())
341: debug(getName() + " : Received changed pe " + cpe.getUID()
342: + " for task " + cpe.getTask().getUID());
343: AllocationResult reportedresult = cpe.getReportedResult();
344: if (reportedresult != null) {
345: // compare entire allocationresults.
346: AllocationResult estimatedresult = cpe.getEstimatedResult();
347: double confidence = reportedresult.getConfidenceRating();
348: boolean nullEstimated = (estimatedresult == null);
349: // if we are not ignoring low confidence reported values
350: boolean highConfidence = (!skipLowConfidence || confidence > HIGH_CONFIDENCE);
351:
352: if (nullEstimated
353: || (highConfidence && (!estimatedresult
354: .isEqual(reportedresult)))) {
355: if (isDebugEnabled())
356: debug(getName()
357: + " : Swapping Alloc Results for task "
358: + cpe.getTask().getUID());
359: if (isInfoEnabled() && !reportedresult.isSuccess())
360: info(getName() + " : " + cpe.getTask().getUID()
361: + " failed to allocate.");
362:
363: cpe.setEstimatedResult(reportedresult);
364:
365: if (!(estimatedresult == null)) {
366: if (isDebugEnabled()) {
367: debug(getName() + " auxiliaryQueries for task "
368: + cpe.getTask().getUID());
369: for (int i = 0; i < AuxiliaryQueryType.LAST_AQTYPE + 1; i++) {
370: debug("\tEstimatedResult - " + i + ", "
371: + estimatedresult.auxiliaryQuery(i)
372: + "\tReportedResult - " + i + ", "
373: + reportedresult.auxiliaryQuery(i));
374: }
375: }
376: for (int i = 0; i < AuxiliaryQueryType.LAST_AQTYPE + 1; i++) {
377: if ((estimatedresult.auxiliaryQuery(i) != null)
378: && (reportedresult.auxiliaryQuery(i) == null)) {
379: cpe.getEstimatedResult()
380: .addAuxiliaryQueryInfo(
381: i,
382: estimatedresult
383: .auxiliaryQuery(i));
384: }
385: }
386: }
387: blackboard.publishChange(cpe);
388: }
389: } else if (!cpe.getTask().getSource().equals(
390: getAgentIdentifier())) {
391: info("unexpected : " + getName() + " : "
392: + cpe.getTask().getUID()
393: + " has a null reported allocation.");
394: }
395: }
396:
397: /**
398: * Called every time one of the filterCallback subscriptions
399: * change.
400: *
401: * What the plugin does in response to a changed subscription.
402: *
403: * Directs the filterCallback with the changed subscription
404: * to react to the change in some way.
405: */
406: protected void execute() {
407: if (isDebugEnabled())
408: debug(getName()
409: + " : cycle called (a subscription changed)");
410:
411: synchronized (mySubscriptions) {
412: for (int i = 0; i < mySubscriptions.size(); i++) {
413: UTILFilterCallback cb = (UTILFilterCallback) mySubscriptions
414: .elementAt(i);
415: if (blackboard.didRehydrate() && !checkedDidRehydrate) {
416: if (cb instanceof UTILRehydrateReactor) {
417: ((UTILRehydrateReactor) cb).reactToRehydrate();
418: // don't react to a changed filter, since react to rehydrate should
419: // already deal with new items in the container
420: continue;
421: }
422: }
423:
424: if (cb.getSubscription().hasChanged()) {
425: if (isDebugEnabled())
426: debug("\tFilter# " + i + " of Subscription "
427: + cb.getSubscription()
428: + " has changed.");
429: cb.reactToChangedFilter();
430: }
431: }
432:
433: checkedDidRehydrate = true; // so we don't get those Failed to find persisted state messages
434: }
435: }
436:
437: /**
438: * Automatic support for persistent state.
439: *
440: * Calls rehydrate if appropriate
441: */
442: protected void justRehydrated() {
443: if (isInfoEnabled())
444: info(getName() + ".justRehydrated.");
445:
446: persistentState = findState();
447:
448: // tell subclasses about rehydrated state
449: if (persistentState != null) {
450: if (isDebugEnabled())
451: debug(getName() + ".justRehydrated - found state.");
452: rehydrateState(persistentState.stuff);
453: } else {
454: if (isDebugEnabled())
455: debug(getName() + ".justRehydrated - no state found.");
456: }
457: }
458:
459: /**
460: * Call this method to add your state to what will be persisted
461: *
462: * Call from inside of a transaction.
463: */
464: protected void registerPersistentState(Object obj) {
465: if (blackboard.didRehydrate()) {
466: debug(getName()
467: + ".registerPersistentState - just rehydrated, "
468: + "so ignoring register request for " + obj);
469: return;
470: }
471:
472: if (persistentState == null) {
473: persistentState = findState();
474:
475: if (persistentState == null) {
476: persistentState = new PersistentState(getClassName()
477: + "_Persistent_State");
478: if (isDebugEnabled())
479: debug(getName()
480: + ".registerPersistentState - publishingState.");
481: publishAdd(persistentState);
482: }
483: }
484:
485: persistentState.stuff.add(obj);
486: }
487:
488: /** anything you added with register, you will be debugrmed about here upon rehydration */
489: protected void rehydrateState(List stuff) {
490: if (isDebugEnabled())
491: debug(getName() + ".rehydrate - got " + stuff.size()
492: + " persistent items.");
493: }
494:
495: protected PersistentState findState() {
496: Collection stuff = blackboard.query(new UnaryPredicate() {
497: public boolean execute(Object obj) {
498: boolean myState = (obj instanceof PersistentState);
499: if (!myState)
500: return false;
501: PersistentState state = (PersistentState) obj;
502: boolean match = state.name.startsWith(getClassName());
503:
504: debug(getName()
505: + "findState - found state! Comparing state name "
506: + state.name + " with " + getClassName()
507: + ((match) ? " MATCH! " : " no match"));
508:
509: return match;
510: }
511: });
512:
513: if (stuff.isEmpty())
514: return null;
515: else
516: return (PersistentState) stuff.iterator().next();
517: }
518:
519: protected final void publishAdd(Object o) {
520: getBlackboardService().publishAdd(o);
521: }
522:
523: protected final void publishRemove(Object o) {
524: getBlackboardService().publishRemove(o);
525: }
526:
527: protected final void publishChange(Object o) {
528: getBlackboardService().publishChange(o, null);
529: }
530:
531: /** @return the name of the cluster */
532: public String getClusterName() {
533: return myClusterName;
534: }
535:
536: /** utility function to get just the name of this class (no package qualification) */
537: protected String getClassName() {
538: return getClassName(this );
539: }
540:
541: /** utility function to get just the name of the class of an object (no package) */
542: protected String getClassName(Object obj) {
543: String classname = obj.getClass().getName();
544: int index = classname.lastIndexOf(".");
545: classname = classname.substring(index + 1, classname.length());
546: return classname;
547: }
548:
549: /**
550: * rely upon load-time introspection to set these services -
551: * don't worry about revokation.
552: */
553: public void setLoggingService(LoggingService bs) {
554: logger = bs;
555:
556: assetHelper = new UTILAsset(logger);
557: prepHelper = new UTILPrepPhrase(logger);
558: prefHelper = new UTILPreference(logger);
559: aggregateHelper = new UTILAggregate(logger);
560: allocHelper = new UTILAllocate(logger);
561: expandHelper = new UTILExpand(logger);
562: verifyHelper = new UTILVerify(logger);
563: }
564:
565: /**
566: * Get the logging service, for subclass use.
567: */
568: protected LoggingService getLoggingService() {
569: return logger;
570: }
571:
572: //
573: // specific "isEnabledFor(..)" shorthand methods:
574: //
575: protected boolean isInfoEnabled() {
576: return logger.isInfoEnabled();
577: }
578:
579: protected boolean isDebugEnabled() {
580: return logger.isDebugEnabled();
581: }
582:
583: protected boolean isWarnEnabled() {
584: return logger.isWarnEnabled();
585: }
586:
587: protected boolean isErrorEnabled() {
588: return logger.isErrorEnabled();
589: }
590:
591: protected boolean isFatalEnabled() {
592: return logger.isFatalEnabled();
593: }
594:
595: //
596: // specific "level" shorthand methods:
597: //
598:
599: /**
600: * Equivalent to "log(info, ..)".
601: */
602: protected void info(String message) {
603: logger.info(message);
604: }
605:
606: /**
607: * Equivalent to "log(debug, ..)".
608: */
609: protected void debug(String message) {
610: logger.debug(message);
611: }
612:
613: /**
614: * Equivalent to "log(WARN, ..)".
615: */
616: protected void warn(String message) {
617: logger.warn(message);
618: }
619:
620: /**
621: * Equivalent to "log(ERROR, ..)".
622: */
623: protected void error(String message) {
624: logger.error(message);
625: }
626:
627: /**
628: * Equivalent to "log(FATAL, ..)".
629: */
630: protected void fatal(String message) {
631: logger.fatal(message);
632: }
633:
634: /** @return cluster name and plugin name */
635: public String getName() {
636: if (myClusterName == null) {
637: myName = null;
638: myClusterName = getAgentIdentifier().getAddress();
639: }
640: if (myName == null)
641: myName = getClusterName() + "/" + getClassName();
642: return myName;
643: }
644:
645: /** holds persistent state, labeled with name of plugin */
646: private static class PersistentState implements Serializable {
647: public PersistentState(String name) {
648: this .name = name;
649: }
650:
651: String name;
652: List stuff = new ArrayList();
653: }
654:
655: protected Vector mySubscriptions;
656:
657: protected PlanningFactory ldmf;
658: protected String myClusterName;
659: protected Plan realityPlan;
660:
661: // .env vars
662: protected ParamMap myParams;
663: protected ParamParser paramParser;
664: protected boolean showinfoOnFailure;
665: protected boolean skipLowConfidence = true;
666: protected double HIGH_CONFIDENCE = 0.99d;
667: protected PersistentState persistentState;
668: protected String myName;
669: protected LoggingService logger;
670:
671: protected UTILAsset assetHelper;
672: protected UTILAggregate aggregateHelper;
673: protected UTILPreference prefHelper;
674: protected UTILPrepPhrase prepHelper;
675: protected UTILAllocate allocHelper;
676: protected UTILExpand expandHelper;
677: protected UTILVerify verifyHelper;
678:
679: protected boolean checkedDidRehydrate = false;
680: }
|