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.callback;
028:
029: import java.util.ArrayList;
030: import java.util.Enumeration;
031: import java.util.Iterator;
032: import java.util.List;
033:
034: import org.cougaar.planning.ldm.plan.AllocationResult;
035: import org.cougaar.planning.ldm.plan.Expansion;
036: import org.cougaar.planning.ldm.plan.NewExpansion;
037: import org.cougaar.planning.ldm.plan.SubTaskResult;
038: import org.cougaar.util.UnaryPredicate;
039: import org.cougaar.util.log.Logger;
040:
041: /**
042: * <pre>
043: * Filters for expansions, testing if the parent task
044: * is interesting. Calls the listener's handleExpansion
045: * method (which gives the expansion to the listener).
046: *
047: * Intended use is for expanders that should listen to
048: * their products : expansions.
049: *
050: * Allocators should use one of the WorkflowCallbacks.
051: *
052: * </pre>
053: */
054:
055: public class UTILExpansionCallback extends UTILFilterCallbackAdapter {
056: public UTILExpansionCallback(UTILExpansionListener listener,
057: Logger logger) {
058: super (listener, logger);
059: }
060:
061: /**
062: * Looks for expansions with interesting parent tasks
063: */
064: protected UnaryPredicate getPredicate() {
065: return new UnaryPredicate() {
066: public boolean execute(Object o) {
067: if (o instanceof Expansion) {
068: Expansion exp = (Expansion) o;
069: if (((UTILExpansionListener) myListener)
070: .interestingExpandedTask(exp.getTask())) {
071: return true;
072: }
073: }
074: return false;
075: }
076: };
077: }
078:
079: /**
080: * Expanders are generally only interested in expansions
081: * that have changed as a result of their reported alloc.
082: * results changing.
083: *
084: * New and removed expansions can be safely ignored.
085: */
086: public void reactToChangedFilter() {
087: // Map seenExpsMap = new HashMap ();
088:
089: Enumeration changedExps = mySub.getChangedList();
090:
091: while (changedExps.hasMoreElements()) {
092: Expansion exp = (Expansion) changedExps.nextElement();
093:
094: // if (seenExpsMap.get (exp) == null) {
095: reactToChangedExpansion(exp);
096: // seenExpsMap.put (exp, exp);
097: // } else if (logger.isDebugEnabled())
098: // logger.info ("UTILExpansionCallback : " +
099: // "Duplicate changed exp for task " +
100: // exp.getTask ().getUID () + " ignored.");
101: }
102:
103: if (logger.isDebugEnabled()) {
104: Enumeration removedExps = mySub.getRemovedList();
105: if (removedExps.hasMoreElements()) {
106: Expansion exp = (Expansion) removedExps.nextElement();
107: logger.debug(myListener.getClass()
108: + " saw removed expansion for task "
109: + exp.getTask().getUID());
110: }
111: }
112: }
113:
114: /**
115: * <pre>
116: * Defines protocol for dealing with Expansions as they change over time.
117: *
118: * This follows the expansion lifecycle. When an expansion fails, we get an initial
119: * shot at fixing it. If we can replace all the failed subtasks, then the expansion can
120: * be called "fixed." If it has been fixed, don't report it as failed (we can't change
121: * the reported alloc result, even if we replace all the tasks in the workflow).
122: * If there are still some subtasks in the workflow that we could
123: * not replace, then the expansion is a failure and should be reported as such.
124: *
125: * Uses getSubTaskResults, which erases it's logger.info after being called. (Another call
126: * will give an empty list.) It reports on subtask alloc results that have changed.
127: *
128: * This breaks down into 6 steps (= each if statement below):
129: *
130: * 1) Looks for failed subtasks on the expansion, and if finds any, passes them
131: * to listener. handleFailedExpansion gets a chance at removing the failed subtasks
132: * from the expansion's workflow and replacing them with tasks with different prefs.
133: * By default, if the expander can't fix this expansion, it should just report the
134: * failure. If the expander can only fix some of the tasks and not all, it should
135: * report as failure.
136: *
137: * 2) Then if no subtask failed, but there was a constraint violation, the
138: * listener is told of which constraints were violated.
139: *
140: * 3) If no subtasks failed, and no constraint was violated, the listener still
141: * has the option of changing the expansion, and if it wants to, will get passed
142: * the expansion.
143: *
144: * 4) If some subtasks could not be fixed, we may want to report the failed expansion
145: * The problem is that we may then report the failed expansion twice, since we report
146: * it once if it fails and we can't do anything about it in handleFailedExpansion, and
147: * then again after the report in handleFailedExpansion. (The Expansion has changed
148: * with the report->estimated flip (I think)). The second time, no subtask results
149: * are marked as changed.
150: *
151: * 5) If it's a success, report it.
152: *
153: * 6) If it's reported as a failure, but has no subtasks that are failures, the
154: * expansion has been handled previously, and we can ignore it.
155: *
156: * So for now (01/11/00), we don't automaticaly report this. We may want to revisit
157: * later.
158: *
159: * BOZO - 01/11/00 GWFV
160: *
161: * </pre>
162: * @param exp Expansion to examine
163: */
164: public void reactToChangedExpansion(Expansion exp) {
165: UTILExpansionListener listener = (UTILExpansionListener) myListener;
166:
167: if (exp.getReportedResult() == null)
168: return;
169:
170: if (exp.getReportedResult().isSuccess()) {
171: if (exp.getWorkflow().constraintViolation()) {
172: if (logger.isInfoEnabled())
173: logger.info(listener.getClass() + " Expansion "
174: + exp.getUID() + " violated constraints.");
175: List violatedList = enumToList(exp.getWorkflow()
176: .getViolatedConstraints());
177: listener.handleConstraintViolation(exp, violatedList);
178: } else if (listener.wantToChangeExpansion(exp)) {
179: if (logger.isInfoEnabled())
180: logger.info(listener.getClass() + " Expansion "
181: + exp.getUID()
182: + " successful, but wants to be changed.");
183: // NOTE that if the listener wants to change the expansion,
184: // it is responsible for calling or not calling
185: // handleSuccessfulExpansion
186: listener.changeExpansion(exp);
187: listener.publishChangedExpansion(exp);
188: } else {
189: if (logger.isDebugEnabled())
190: logger.debug(listener.getClass() + " Expansion "
191: + exp.getUID() + " was successful.");
192: listener.reportChangedExpansion(exp);
193:
194: List subtaskResults = ((NewExpansion) exp)
195: .getSubTaskResults();
196:
197: /** only use the following if getSubTaskResults is expensive */
198: /*
199: List subtaskResults;
200: if (listener.wantsSuccessfulExpResults ())
201: subtaskResults = ((NewExpansion) exp).getSubTaskResults();
202: else
203: subtaskResults = new ArrayList ();
204: */
205:
206: listener.handleSuccessfulExpansion(exp, subtaskResults);
207: }
208: } else {
209: // Why cache the results? Because getSubTaskResults is evil -- calling
210: // it clears its contents. Something one would not normally expect
211: // from something called getXXX. If it said getAndClearXXX you might
212: // have a chance to catch it before it becomes a nasty bug.
213:
214: List subtaskResults = ((NewExpansion) exp)
215: .getSubTaskResults();
216: if (logger.isDebugEnabled())
217: logger.debug(listener.getClass()
218: + " - reportChangedExpansion " + exp.getUID());
219:
220: List failedSubTaskResults = getFailedSubTaskResults(subtaskResults);
221:
222: if (!failedSubTaskResults.isEmpty()) {
223: if (logger.isInfoEnabled())
224: logger.info(listener.getClass() + " Expansion "
225: + exp.getUID() + " task "
226: + exp.getTask().getUID() + " failed.");
227: listener.handleFailedExpansion(exp,
228: failedSubTaskResults);
229: } else if (exp.getWorkflow().constraintViolation()) {
230: if (logger.isInfoEnabled())
231: logger.info(listener.getClass() + " Expansion "
232: + exp.getUID() + " violated constraints.");
233: List violatedList = enumToList(exp.getWorkflow()
234: .getViolatedConstraints());
235: listener.handleConstraintViolation(exp, violatedList);
236: } else if (getNumFailedSubTasks(subtaskResults) > 0) {
237: if (logger.isInfoEnabled())
238: logger
239: .info(listener.getClass()
240: + " Expansion "
241: + exp.getUID()
242: + " task "
243: + exp.getTask().getUID()
244: + " had failed subtasks, "
245: + "but all have been seen before (NO REPORT!)");
246: // BOZO - GWFV 01/11/00 - We may have to revisit this.
247: // Some cases where this may not be what we want.
248:
249: // GWFV 01/23/00 The story continues -- without the line below, we would
250: // ignore failed expansions, as they sometimes had no changed subTaskResults,
251: // despite the fact that we had never seen them before.
252: // So for the moment, the report is back in.
253:
254: listener.reportChangedExpansion(exp);
255: } else {
256: if (logger.isDebugEnabled())
257: logger
258: .debug(listener.getClass()
259: + " Expansion "
260: + exp.getUID()
261: + " task "
262: + exp.getTask().getUID()
263: + " - failed expansion, but has been handled already.");
264: }
265: }
266: }
267:
268: /**
269: * @param subtaskResults All subtask results
270: * @return List of SubTaskResult objects, all of which have newly failed.
271: */
272: protected List getFailedSubTaskResults(List subtaskResults) {
273: List result = new ArrayList();
274: int n = 0;
275: for (Iterator iter = subtaskResults.iterator(); iter.hasNext();) {
276: SubTaskResult stres = (SubTaskResult) iter.next();
277: boolean didFail = false;
278: if (stres.getAllocationResult() != null)
279: didFail = !stres.getAllocationResult().isSuccess();
280: else if (logger.isInfoEnabled())
281: logger.info("getFailedSubtasks - null AR for subtask "
282: + stres.getTask());
283:
284: if (didFail)
285: n++;
286: if (didFail && stres.hasChanged())
287: result.add(stres);
288: }
289:
290: if (logger.isInfoEnabled() && (n > 0))
291: logger.info(this + " : getFailedSubtasks - " + n
292: + " failed subtasks, " + result.size()
293: + " changed.");
294:
295: return result;
296: }
297:
298: /**
299: * If an expansion is altered after it is reported as failed,
300: * the reported alloc result will remain marked as failed, even
301: * after the failed subtasks have been removed.
302: *
303: * @param subtaskResults All subtask results
304: * @return number of failed sub tasks
305: */
306: protected int getNumFailedSubTasks(List subtaskResults) {
307: int n = 0;
308: for (Iterator iter = subtaskResults.iterator(); iter.hasNext();) {
309: SubTaskResult stres = (SubTaskResult) iter.next();
310: AllocationResult allocResult = stres.getAllocationResult();
311: if (allocResult != null && !allocResult.isSuccess())
312: n++;
313: }
314:
315: return n;
316: }
317:
318: protected List enumToList(Enumeration en) {
319: List result = new ArrayList();
320: while (en.hasMoreElements())
321: result.add(en.nextElement());
322: return result;
323: }
324: }
|