001: /*
002: * <copyright>
003: *
004: * Copyright 2001-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: package org.cougaar.logistics.plugin.trans.base;
027:
028: import java.util.ArrayList;
029: import java.util.Collection;
030: import java.util.Date;
031: import java.util.Enumeration;
032: import java.util.HashMap;
033: import java.util.Iterator;
034: import java.util.List;
035: import java.util.Map;
036:
037: import org.cougaar.core.mts.MessageAddress;
038: import org.cougaar.core.util.UniqueObject;
039:
040: import org.cougaar.planning.ldm.PlanningFactory;
041: import org.cougaar.planning.ldm.plan.Plan;
042: import org.cougaar.planning.ldm.plan.PlanElement;
043: import org.cougaar.planning.ldm.plan.PrepositionalPhrase;
044: import org.cougaar.planning.ldm.plan.Allocation;
045: import org.cougaar.planning.ldm.plan.AllocationResult;
046: import org.cougaar.planning.ldm.plan.SubTaskResult;
047: import org.cougaar.planning.ldm.plan.Expansion;
048: import org.cougaar.planning.ldm.plan.Workflow;
049: import org.cougaar.planning.ldm.plan.NewWorkflow;
050: import org.cougaar.planning.ldm.plan.Task;
051: import org.cougaar.planning.ldm.plan.NewTask;
052: import org.cougaar.planning.ldm.plan.AspectType;
053: import org.cougaar.planning.ldm.plan.Schedule;
054:
055: import org.cougaar.planning.ldm.asset.Asset;
056:
057: import org.cougaar.glm.ldm.asset.Deck;
058: import org.cougaar.glm.ldm.asset.PhysicalAsset;
059: import org.cougaar.lib.callback.UTILAllocationListener;
060: import org.cougaar.lib.callback.UTILAllocationCallback;
061: import org.cougaar.lib.callback.UTILAssetListener;
062: import org.cougaar.lib.callback.UTILAssetCallback;
063: import org.cougaar.lib.callback.UTILExpansionListener;
064: import org.cougaar.lib.callback.UTILExpansionCallback;
065: import org.cougaar.lib.callback.UTILExpandableTaskCallback;
066: import org.cougaar.lib.callback.UTILFilterCallback;
067: import org.cougaar.lib.callback.UTILGenericListener;
068: import org.cougaar.lib.filter.UTILBufferingPluginAdapter;
069: import org.cougaar.lib.util.UTILPluginException;
070: import org.cougaar.lib.util.UTILAllocate;
071:
072: import org.cougaar.logistics.plugin.trans.GLMTransConst;
073: import org.cougaar.logistics.plugin.trans.base.SequentialScheduleElement;
074: import org.cougaar.core.util.UID;
075: import org.cougaar.util.UnaryPredicate;
076:
077: /**
078: ** Base class that orchestrates sequential backwards planning.
079: **
080: ** Handles the state transitions of each schedule element that makes up the schedule
081: ** attached to each parent task.
082: **/
083: public abstract class SequentialPlannerPlugin extends
084: UTILBufferingPluginAdapter implements UTILAllocationListener,
085: UTILAssetListener, UTILExpansionListener {
086: protected List delayedTasks = new ArrayList();
087:
088: Map childToParentUID = new HashMap();
089: Map taskToSSE = new HashMap();
090: boolean tryToReplanOverlaps = false;
091: protected long waitTime = 10000; // millis
092:
093: public void localSetup() {
094: super .localSetup();
095:
096: try {
097: if (getMyParams().hasParam("tryToReplanOverlaps")) {
098: tryToReplanOverlaps = getMyParams().getBooleanParam(
099: "tryToReplanOverlaps");
100: }
101: } catch (Exception e) {
102: warn("got really unexpected exception " + e);
103: }
104: }
105:
106: // Plug in creates both Expansions and Allocations so it listens for both
107: public void setupFilters() {
108: super .setupFilters();
109:
110: if (isInfoEnabled())
111: info(getName() + " : Filtering for generic Assets...");
112: addFilter(myAssetCallback = createAssetCallback());
113:
114: if (isInfoEnabled())
115: info(getName() + " : Filtering for Expansions...");
116: addFilter(myExpansionCallback = createExpansionCallback());
117:
118: if (isInfoEnabled())
119: info(getName() + " : Filtering for Allocations...");
120: addFilter(myAllocCallback = createAllocCallback());
121: }
122:
123: protected UTILExpandableTaskCallback myInputTaskCallback;
124:
125: protected UTILFilterCallback getInputTaskCallback() {
126: return myInputTaskCallback;
127: }
128:
129: protected UTILFilterCallback createThreadCallback(
130: UTILGenericListener bufferingThread) {
131: if (isInfoEnabled())
132: info(getName() + " : Filtering for Naked Tasks...");
133:
134: myInputTaskCallback = new UTILExpandableTaskCallback(
135: bufferingThread, logger);
136: return myInputTaskCallback;
137: }
138:
139: public boolean interestingTask(Task t) {
140: return true;
141: }
142:
143: protected void execute() {
144: super .execute();
145:
146: if (!delayedTasks.isEmpty()) {
147: processTasks(new ArrayList());
148: }
149: }
150:
151: /**
152: * If necessary subordinates have not reported yet, accumulates tasks into
153: * a delayedTasks list, and asks to be kicked again in 10 seconds, by which
154: * time hopefully the subordinates have reported.
155: *
156: * Solves the race condition between tasks showing up and subordinates showing up.
157: * @param tasks to process
158: */
159: public void processTasks(List tasks) {
160: if (!allNecessaryAssetsReported()) { // if need subordinates aren't there yet, way 10 seconds
161: delayedTasks.addAll(tasks);
162:
163: if (logger.isInfoEnabled()) {
164: logger
165: .info(getName()
166: + " - necessary subords have not reported, so waiting "
167: + waitTime + " millis to process "
168: + delayedTasks.size() + " tasks.");
169: }
170:
171: examineBufferAgainIn(waitTime); // wait 10 seconds and check again
172: } else { // ok, all subords are here, lets go!
173: if (logger.isInfoEnabled()) {
174: logger
175: .info(getName()
176: + " - all necessary subords have reported, so processing "
177: + tasks.size() + " tasks.");
178: }
179:
180: tasks.addAll(delayedTasks);
181: delayedTasks.clear();
182:
183: tasks = getPrunedTaskList(tasks);
184:
185: for (int i = 0; i < tasks.size(); i++) {
186: if (isDebugEnabled())
187: debug(getName()
188: + ".processTasks calling handleTask - "
189: + (Task) tasks.get(i));
190:
191: handleTask((Task) tasks.get(i));
192: }
193: }
194: }
195:
196: /** probably unnecessary */
197: protected List getPrunedTaskList(List tasks) {
198: java.util.List prunedTasks = new java.util.ArrayList(tasks
199: .size());
200:
201: Collection removed = myInputTaskCallback.getSubscription()
202: .getRemovedCollection();
203:
204: for (Iterator iter = tasks.iterator(); iter.hasNext();) {
205: Task task = (Task) iter.next();
206: if (removed.contains(task)) {
207: if (isWarnEnabled()) {
208: warn("ignoring task on removed list "
209: + task.getUID());
210: }
211: } else
212: prunedTasks.add(task);
213: }
214: return prunedTasks;
215: }
216:
217: protected boolean allNecessaryAssetsReported() {
218: return true;
219: }
220:
221: /**
222: * handleTask creates an empty schedule and attaches it to the parent task. It also creates
223: * an expansion which starts empty and initiates a "planning cycle".
224: */
225: public void handleTask(Task t) {
226: if (isDebugEnabled())
227: debug(getName()
228: + ".handleTask, adding schedule prep to task " + t);
229:
230: prepHelper.addPrepToTask(t, prepHelper.makePrepositionalPhrase(
231: ldmf, GLMTransConst.SequentialSchedule,
232: createEmptyPlan(t)));
233:
234: publishChange(t); // we added a prep, should publish change it
235:
236: Workflow wf = makeEmptyWorkflow(t);
237: Expansion exp = ldmf.createExpansion(t.getPlan(), t, wf, null);
238: publishAdd(exp);
239:
240: turnCrank(t);
241: }
242:
243: private Workflow makeEmptyWorkflow(Task t) {
244: NewWorkflow wf = ldmf.newWorkflow();
245: wf.setParentTask(t);
246: return wf;
247: }
248:
249: /**
250: * turnCrank is a basic planning cycle. It looks through all the elements in the empty
251: * schedule and if one is ready to be planned it plans it. It also places the parenttask in a
252: * hashtable so it can be easily retrieved later when the subtask is succesfully allocated.
253: *
254: * Operates on the parent task of a backwards-planning expansion, e.g. the parent task
255: * of a typical Conus->Air/Sea->Theater triplet workflow.
256: *
257: * @param task parent task of expansion
258: */
259: public void turnCrank(Task task) {
260: if (isDebugEnabled())
261: debug(getName() + "---Turning Crank: S " + task.getUID());
262:
263: PrepositionalPhrase prep = prepHelper.getPrepNamed(task,
264: GLMTransConst.SequentialSchedule);
265:
266: if (prep == null) {
267: error(getName() + ".turnCrank - ERROR - no prep named "
268: + GLMTransConst.SequentialSchedule + " on task "
269: + task);
270: return;
271: }
272:
273: Schedule sched = (Schedule) prep.getIndirectObject();
274: Enumeration en = sched.getAllScheduleElements();
275: while (en.hasMoreElements()) {
276: SequentialScheduleElement spe = (SequentialScheduleElement) en
277: .nextElement();
278: if (isDebugEnabled())
279: debug(getName() + "------spe " + spe + " is planned: "
280: + spe.isPlanned() + " is ready "
281: + spe.isReady());
282: if ((!spe.isPlanned()) && (spe.isReady())) {
283: if (spe.getTask() != null) {
284: error("huh? there already is a task "
285: + spe.getTask().getUID() + " for " + spe);
286: }
287:
288: Task subtask = spe.planMe(this );
289: attachSubtask(subtask, spe);
290: childToParentUID.put(subtask.getUID().toString(), task);
291: }
292: }
293: if (isDebugEnabled())
294: debug(getName() + "---Turning Crank: E" + task.getUID());
295: }
296:
297: /**
298: * attachSubtask adds a created subtask to the parent task's expansion.
299: * remembers subtask->schedule element mapping in a map.
300: * sets the task pointer on the schedule element
301: */
302: protected void attachSubtask(Task subtask,
303: SequentialScheduleElement spe) {
304: enterHash(subtask.getUID().toString(), spe);
305:
306: // ScheduleElement now knows which task it has made
307: spe.setTask(subtask);
308:
309: // Not needed down the line
310: prepHelper.removePrepNamed(subtask,
311: GLMTransConst.SequentialSchedule);
312:
313: // don't do this - drags info into downstream agents
314: // add back pointer to spe
315: /*
316: prepHelper.addPrepToTask(subtask, prepHelper.makePrepositionalPhrase(ldmf,
317: GLMTransConst.SCHEDULE_ELEMENT,
318: spe));
319: */
320:
321: Task parentTask = spe.getParentTask();
322: Expansion exp = (Expansion) parentTask.getPlanElement();
323: if (exp == null) {
324: if (isInfoEnabled()) {
325: info("attachSubtask - task "
326: + parentTask.getUID()
327: + "'s plan element is missing, so skipping trying to process subtask "
328: + subtask.getUID()
329: + " must be in process of rescinds?");
330: }
331: return;
332: }
333: NewWorkflow wf = (NewWorkflow) exp.getWorkflow();
334:
335: // So initial creation doesn't get added and changed in same pass
336: boolean workflowWasEmpty = !wf.getTasks().hasMoreElements();
337:
338: wf.addTask(subtask);
339:
340: // if (wf.getTasksIDs().length > 3) {
341: // error ("parent " + parentTask.getUID ()+ " has " + wf.getTasksIDs ().length + " subtasks?");
342: // }
343:
344: ((NewTask) subtask).setWorkflow(wf);
345: ((NewTask) subtask).setParentTask(parentTask);
346: publishAdd(subtask);
347: if (!workflowWasEmpty) {
348: publishChange(exp);
349: } // else Workflow is empty, so publishAdd already done
350: }
351:
352: /**
353: * createEmptyPlan creates an empty schedule (there are no tasks
354: * yet in the workflow) made up of custom schedule elements. These
355: * schedule elements define a <code>planMe</code> method that adds a
356: * subtask to the parent task's workflow. Generally for each schedule
357: * element in the schedule returned here, there will be a subtask added
358: * to the workflow.
359: *
360: * @see org.cougaar.logistics.plugin.trans.base.SequentialScheduleElement#planMe
361: * @param parent referenced by sequential schedule elements in the returned schedule
362: * @return Schedule of sequential schedule elements that represent the steps of the backwards planning
363: */
364: public abstract Schedule createEmptyPlan(Task parent);
365:
366: public void handleIllFormedTask(Task t) {
367: reportIllFormedTask(t);
368: publishAdd(expandHelper.makeFailedExpansion(null, ldmf, t));
369: }
370:
371: /**********************/
372: /*** Asset Listener ***/
373: /**********************/
374:
375: protected UTILAssetCallback myAssetCallback;
376:
377: protected UTILAssetCallback createAssetCallback() {
378: return new UTILAssetCallback(this , logger);
379: }
380:
381: protected UTILAssetCallback getAssetCallback() {
382: return myAssetCallback;
383: }
384:
385: public boolean interestingAsset(Asset a) {
386: return true;
387: }
388:
389: public void handleNewAssets(Enumeration e) {
390: }
391:
392: public void handleChangedAssets(Enumeration e) {
393: }
394:
395: protected final Iterator getAssets() {
396: Collection assets = getAssetCallback().getSubscription()
397: .getCollection();
398:
399: if (assets.size() != 0) {
400: return assets.iterator();
401: }
402: return null;
403: }
404:
405: /**************************/
406: /*** Expansion Listener ***/
407: /**************************/
408:
409: protected UTILExpansionCallback myExpansionCallback;
410:
411: protected UTILExpansionCallback createExpansionCallback() {
412: return new UTILExpansionCallback(this , logger);
413: }
414:
415: protected UTILExpansionCallback getExpansionCallback() {
416: return myExpansionCallback;
417: }
418:
419: public boolean interestingExpandedTask(Task t) {
420: return interestingTask(t);
421: }
422:
423: public void handleFailedExpansion(Expansion exp,
424: List failedSubTaskResults) {
425: if (isInfoEnabled())
426: info(getName() + "---handleFailedExpansion: S");
427: reportChangedExpansion(exp);
428:
429: if (failedSubTaskResults.size() == 0)
430: error(getName() + " - empty list of failed subtasks?");
431:
432: // Go through the list of failed subtasks
433: Iterator failed_it = failedSubTaskResults.iterator();
434: while (failed_it.hasNext()) {
435: // Get the next failed task
436: SubTaskResult str = (SubTaskResult) failed_it.next();
437: Task failed_e_task = str.getTask();
438:
439: if (isInfoEnabled()) {
440: info(getName()
441: + ".handleFailedExpansion - Failed task : "
442: + failed_e_task + "\n\twith pe "
443: + failed_e_task.getPlanElement());
444: info("\nPref-Aspect comparison : ");
445: expandHelper.showPlanElement(failed_e_task);
446: }
447: }
448: if (isInfoEnabled())
449: info(getName() + "---handleFailedExpansion: E");
450: }
451:
452: public void handleConstraintViolation(Expansion exp,
453: List violatedConstraints) {
454: throw new UTILPluginException(
455: getName(),
456: "handleConstraintViolation : expansion "
457: + exp
458: + " has violated constraints that were ignored.");
459: }
460:
461: public boolean wantToChangeExpansion(Expansion exp) {
462: return false;
463: }
464:
465: public void changeExpansion(Expansion exp) {
466: }
467:
468: public void publishChangedExpansion(Expansion exp) {
469: publishChange(exp);
470: }
471:
472: /**
473: * Mainly just calls updateAllocationResult on <code>exp</code>
474: * Also prints debug info when tasks fail.
475: */
476: public void reportChangedExpansion(Expansion exp) {
477: if (isDebugEnabled()) {
478: debug(getName()
479: + "---reportChangedExpansion: S - reporting changed expansion to superior.");
480: }
481:
482: Task task = exp.getTask();
483: Schedule sched = null;
484:
485: if (!prepHelper.hasPrepNamed(task,
486: GLMTransConst.SequentialSchedule)) {
487: updateAllocationResult(exp);
488: return;
489: }
490:
491: try {
492: sched = (Schedule) prepHelper.getPrepNamed(task,
493: GLMTransConst.SequentialSchedule)
494: .getIndirectObject();
495: } catch (Exception e) {
496: error(getName()
497: + ".reportChangedExpansion - ERROR - no prep "
498: + GLMTransConst.SequentialSchedule + " on " + task);
499: return;
500: }
501:
502: Enumeration en = sched.getAllScheduleElements();
503: while (en.hasMoreElements()) {
504: SequentialScheduleElement spe = (SequentialScheduleElement) en
505: .nextElement();
506: AllocationResult reportedResult = exp.getReportedResult();
507: boolean isFailure = (reportedResult != null) ? !reportedResult
508: .isSuccess()
509: : false;
510:
511: if (!spe.isPlanned() && !isFailure) {
512: if (isDebugEnabled())
513: debug(getName()
514: + "---reportChangedExpansion: E (not updating Alloc b/c interim change)");
515: return;
516: }
517: }
518:
519: if (isDebugEnabled())
520: debug(getName()
521: + "---reportChangedExpansion: E (updating Alloc)");
522: updateAllocationResult(exp);
523: }
524:
525: public void handleSuccessfulExpansion(Expansion exp,
526: List successfulSubtasks) {
527: if (isDebugEnabled()) {
528: AllocationResult estAR = exp.getEstimatedResult();
529: AllocationResult repAR = exp.getReportedResult();
530: String est = "e null/";
531: String rep = " r null";
532: if (estAR != null)
533: est = " e " + (estAR.isSuccess() ? "S" : "F") + " - "
534: + (int) (estAR.getConfidenceRating() * 100.0)
535: + "% /";
536: if (repAR != null)
537: rep = " r " + (repAR.isSuccess() ? "S" : "F") + " - "
538: + (int) (repAR.getConfidenceRating() * 100.0)
539: + "%";
540:
541: debug(getName() + " : got successful expansion for task "
542: + exp.getTask().getUID() + est + rep);
543: }
544: }
545:
546: /***************************/
547: /*** Allocation Listener ***/
548: /***************************/
549:
550: protected UTILAllocationCallback myAllocCallback;
551:
552: protected UTILAllocationCallback createAllocCallback() {
553: return new UTILAllocationCallback(this , logger);
554: }
555:
556: protected UTILAllocationCallback getAllocCallback() {
557: return myAllocCallback;
558: }
559:
560: public boolean interestingNotification(Task t) {
561: boolean interest = interestingTask(t);
562: if (isDebugEnabled()) {
563: if (interest) {
564: debug(getName() + ": noticing expansion I made of "
565: + t.getUID() + " changed.");
566: } else {
567: debug(getName()
568: + ": ignoring expansion made by GLMTransTranscomExpander of "
569: + t.getUID());
570: }
571: }
572: return interest;
573: }
574:
575: public boolean needToRescind(Allocation alloc) {
576: return false;
577: }
578:
579: public boolean handleRescindedAlloc(Allocation alloc) {
580: return false;
581: }
582:
583: public void handleRemovedAlloc(Allocation alloc) {
584: if (isDebugEnabled()) {
585: String unit = "Undefined";//(prepHelper.hasPrepNamed(alloc.getTask (), Constants.Preposition.FOR)) ?
586: //("" + prepHelper.getPrepNamed(alloc.getTask (), Constants.Preposition.FOR)) : "nonUnit";
587: debug(getName()
588: + ".handleRemovedAlloc : alloc was removed for task "
589: + alloc.getTask().getUID() + " w/ d.o. "
590: + alloc.getTask().getDirectObject() + " from "
591: + unit);
592: }
593:
594: if (isInfoEnabled()) {
595: info("got remove alloc for task "
596: + alloc.getTask().getUID() + " verb "
597: + alloc.getTask().getVerb());
598: }
599:
600: String key = alloc.getTask().getUID().toString();
601: Object something = childToParentUID.remove(key);
602: if (something == null) {
603: if (isInfoEnabled()) {
604: info(getName() + " - no task with uid "
605: + alloc.getTask().getUID()
606: + " in child->parent map?");
607: }
608: }
609:
610: something = taskToSSE.remove(key);
611: if (something == null) {
612: if (isInfoEnabled()) {
613: info(getName() + " - no task with uid "
614: + alloc.getTask().getUID()
615: + " in task->schedule element map?");
616: }
617: }
618: }
619:
620: public void publishRemovalOfAllocation(Allocation alloc) {
621: if (isDebugEnabled())
622: debug(getName() + " : removing allocation for task "
623: + alloc.getTask().getUID());
624:
625: try {
626: publishRemove(alloc);
627: } catch (Exception e) {
628: if (isDebugEnabled())
629: debug(getName()
630: + " : publishRemovalOfAllocation - got reset claim exception, ignoring...");
631: }
632: }
633:
634: /**
635: * When an allocation is succesful you ignore it unless it is a meaningful allocation.
636: * Meaningful is defined as having a non-null reported result and a highest confidence rating.
637: * This would indicate that an allocation to an actual resource has been done. If there is
638: * a successful allocation the schedule element corresponding to the task will be grabbed and
639: * finishPlan will be run on it. Then another planning cycle will begin.
640: *
641: * @param alloc that has been changed (i.e. a downstream agent has sent back an allocation result)
642: */
643: public void handleSuccessfulAlloc(Allocation alloc) {
644: if (isDebugEnabled()) {
645: String assetInfo = (alloc.getAsset() instanceof PhysicalAsset) ? " is physical asset "
646: : " is not physical, is "
647: + alloc.getAsset().getClass();
648: debug(getName()
649: + "---handleSuccessfulAlloc: task allocated to "
650: + alloc.getAsset().getUID() + " " + assetInfo);
651: }
652: AllocationResult AR = alloc.getReportedResult() == null ? alloc
653: .getEstimatedResult() : alloc.getReportedResult();
654: if (!AR.isSuccess()) {
655: if (isInfoEnabled())
656: info(getName()
657: + ".handleSuccessfulAlloc - WARNING : planning of leg for task "
658: + alloc.getTask().getUID()
659: + " failed, not continuing to next leg.");
660: return;
661: }
662: double conf = 0;
663: if (alloc.getReportedResult() != null)
664: conf = alloc.getReportedResult().getConfidenceRating();
665:
666: if (isInfoEnabled()) {
667: info("successful alloc " + alloc.getUID() + " task "
668: + alloc.getTask().getUID() + " verb "
669: + alloc.getTask().getVerb() + " conf " + conf);
670: }
671:
672: if ((alloc.getReportedResult() != null && alloc
673: .getReportedResult().getConfidenceRating() >= UTILAllocate.HIGHEST_CONFIDENCE)
674: || alloc.getAsset() instanceof PhysicalAsset
675: || alloc.getAsset() instanceof Deck) {
676: // if (isDebugEnabled()) debug(getName () + "------non-null reported result with highest confidence");
677: Task allocTask = alloc.getTask();
678: String uid = allocTask.getUID().toString();
679:
680: if (isDebugEnabled()) {
681: debug(getName()
682: + ".handleSuccessfulAlloc - considering finishing planning for allocation's task "
683: + uid);
684: }
685:
686: SequentialScheduleElement sse = null;
687: Task parenttask = getParentTask(allocTask, uid);
688: if (parenttask == null) {
689: if (isInfoEnabled()) {
690: info(getName()
691: + ".handleSuccessfulAlloc - no parent of task "
692: + uid
693: + " - must be during rescinds. Skipping seq. planning.");
694: }
695: } else {
696: sse = getElement(allocTask, parenttask, uid);
697: }
698:
699: if (sse == null) {
700: if (parenttask != null) {
701: if (isInfoEnabled()) {
702: info(getName()
703: + ".handleSuccessfulAlloc - could not find seq. schedule element for task "
704: + uid);
705: }
706: } else if (isInfoEnabled()) {
707: info(getName()
708: + ".handleSuccessfulAlloc - no parent task of task "
709: + uid
710: + " must be during rescinds. Skipping seq. planning.");
711: }
712: } else if (!sse.isPlanned()) {
713: if (isInfoEnabled()) {
714: info(getName()
715: + ".handleSuccessfulAlloc - finishing planning of element "
716: + sse + " b/c task completed : " + uid);
717: }
718: sse.finishPlan(alloc, this );
719:
720: //Task parenttask = getParentTask(t, uid);
721: if (parenttask == null) {
722: if (isInfoEnabled()) {
723: info(getName()
724: + ".handleSuccessfulAlloc - no parent task of task "
725: + uid
726: + " - must be during rescinds. Skipping seq. planning.");
727: }
728: } else {
729: // this subtask is done, check to see if another subtask that depended on it can now be planned
730: turnCrank(parenttask);
731: }
732: } else {
733:
734: long returnedStart = (long) AR
735: .getValue(AspectType.START_TIME);
736:
737: if (isInfoEnabled()) {
738: info("planned alloc " + alloc.getUID() + " task "
739: + uid + " compare sse start "
740: + sse.getStartDate() + " vs AR start "
741: + new Date(returnedStart));
742: }
743:
744: // did the reported time get earlier?
745: if (tryToReplanOverlaps) {
746: if (returnedStart < sse.getStartDate().getTime()) {
747: if (isInfoEnabled()) {
748: info(getName()
749: + ".handleSuccessfulAlloc - resetting start and end times of the element "
750: + sse
751: + " b/c alloc results changed for task "
752: + uid);
753: }
754: sse.finishPlan(alloc, this );
755:
756: // find tasks that depended on this one and replan them
757: replanDependingTasks(parenttask, returnedStart);
758: }
759: }
760: }
761: }
762:
763: if (isDebugEnabled()) {
764: debug(getName()
765: + "---handleSuccessfulAlloc: E of processing for "
766: + alloc.getAsset().getUID());
767: }
768: }
769:
770: /**
771: * re-examine the workflow to see if the any tasks overlap the time
772: * of the recently changed allocation, if they do, replan tasks
773: * that depend on it.
774: */
775: protected void replanDependingTasks(Task parentTask, long beforeTime) {
776: }
777:
778: /**
779: * Gets the parent task for the child task with the UID uid.
780: * Uses a map to look up parents of child - if no key in the map,
781: * does a blackboard query (CPU expensive!).
782: *
783: * deals with post-rehydration state
784: */
785: public Task getParentTask(Task child, String uid) {
786: Task parenttask = (Task) childToParentUID.get(uid);
787:
788: if (parenttask != null)
789: return parenttask;
790:
791: // we rehydrated, so map is empty...
792:
793: UID parentUID = child.getParentTaskUID();
794:
795: Collection stuff = blackboard.query(new TaskCatcher(parentUID));
796:
797: if (stuff.isEmpty())
798: return null;
799:
800: parenttask = (Task) stuff.iterator().next();
801: childToParentUID.put(child.getUID().toString(), parenttask);
802:
803: return parenttask;
804: }
805:
806: /**
807: * Finds the schedule element for the <code>child</code> task in the taskToSSE map.
808: * If the map is empty (after rehydration) we look up the schedule element in the
809: * parent task's schedule.
810: *
811: * If we were to attach the schedule element to the child task (which would be
812: * the easier approach) we would drag the dependencies and all the tasks hanging
813: * off them into the downstream agent, which sucks from a memory point-of-view.
814: *
815: * Deals with post-rehydration state by looking at parent task.
816: * @param child task to look for matching SequentialScheduleElement
817: * @param parent of child task (where we look for the schedule elements)
818: * @param uid of child task
819: * @return SequentialScheduleElement that has the planning info for <code>child</code>
820: */
821: protected SequentialScheduleElement getElement(Task child,
822: Task parent, String uid) {
823: if (isDebugEnabled()) {
824: debug(getName() + ".getElement - getting schedule for "
825: + uid + " parent " + parent.getUID());
826: }
827:
828: SequentialScheduleElement sse = (SequentialScheduleElement) taskToSSE
829: .get(uid);
830:
831: if (sse != null) {
832: return sse;
833: } else {
834: if (isInfoEnabled()) {
835: info(getName()
836: + ".getElement - no schedule element for "
837: + uid + " in hash with " + taskToSSE.size()
838: + " elements.");
839: }
840: }
841:
842: // in case of rehydration, re-enter mapping into hash map
843:
844: PrepositionalPhrase prep = prepHelper.getPrepNamed(parent,
845: GLMTransConst.SequentialSchedule);
846:
847: if (prep == null) {
848: error(getName() + ".getElement - no prep named "
849: + GLMTransConst.SequentialSchedule + " on task "
850: + parent);
851: return null;
852: }
853:
854: Schedule sched = (Schedule) prep.getIndirectObject();
855:
856: for (Enumeration en = sched.getAllScheduleElements(); en
857: .hasMoreElements();) {
858: SequentialScheduleElement spe = (SequentialScheduleElement) en
859: .nextElement();
860: if (spe.getTask() == child) {
861: if (isInfoEnabled()) {
862: info(getName()
863: + ".getElement - found schedule for "
864: + spe.getTask().getUID());
865: }
866:
867: sse = spe;
868: } else {
869: if (isInfoEnabled() && (spe != null)
870: && (spe.getTask() != null) && (child != null)) {
871: info(getName() + ".getElement - schedule task "
872: + spe.getTask().getUID()
873: + " != examined task " + child.getUID());
874: }
875: }
876: }
877:
878: if (sse == null) {
879: if (isInfoEnabled()) {
880: info(getName()
881: + ".getElement - no schedule element for "
882: + child.getUID());
883: }
884: } else
885: enterHash(child.getUID().toString(), sse);
886:
887: return sse;
888: }
889:
890: /**
891: * for post-rehydration phase
892: * @see #getParentTask
893: */
894: class TaskCatcher implements UnaryPredicate {
895: UID parentUID;
896:
897: public TaskCatcher(UID parentUID) {
898: this .parentUID = parentUID;
899: }
900:
901: public boolean execute(Object obj) {
902: boolean isTask = (obj instanceof Task);
903: if (!isTask)
904: return false;
905: return ((Task) obj).getUID().equals(parentUID);
906: }
907: }
908:
909: /*** Set of dumb functions to get around incredibly annoying Java Compiler bugs ***/
910: // The following just allow protected plugin stuff to be called from the custom schedude
911: // elements. It should probably be replaced with a delegate.
912: public PlanningFactory publicGetFactory() {
913: return ldmf;
914: }
915:
916: public Plan publicGetRealityPlan() {
917: return realityPlan;
918: }
919:
920: public void publicPublishChange(Object o) {
921: if (isDebugEnabled())
922: debug(getName() + " - publicPublishChange on : "
923: + (UniqueObject) o);
924: publishChange(o);
925: }
926:
927: public void publicPublishAdd(Object o) {
928: if (isDebugEnabled())
929: debug(getName() + " - publicPublishAdd of : "
930: + (UniqueObject) o);
931: publishAdd(o);
932: }
933:
934: public MessageAddress publicGetMessageAddress() {
935: return getAgentIdentifier();
936: }
937:
938: public String publicGetMyClusterName() {
939: return myClusterName;
940: }
941:
942: /*** connect SSE to task so that you can do SSE processing when task returns ***/
943: public void enterHash(String key, SequentialScheduleElement obj) {
944: taskToSSE.put(key, obj);
945: }
946:
947: public Map getChildToParentUID() {
948: return childToParentUID;
949: }
950: }
|