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.planning.ldm.plan;
028:
029: import java.beans.PropertyChangeListener;
030: import java.beans.PropertyChangeSupport;
031: import java.io.IOException;
032: import java.io.ObjectInputStream;
033: import java.io.ObjectOutputStream;
034: import java.util.ArrayList;
035: import java.util.Date;
036: import java.util.Enumeration;
037: import java.util.HashSet;
038: import java.util.Iterator;
039: import java.util.List;
040: import java.util.ListIterator;
041: import java.util.Set;
042:
043: import org.cougaar.core.blackboard.ActiveSubscriptionObject;
044: import org.cougaar.core.blackboard.Blackboard;
045: import org.cougaar.core.blackboard.Subscriber;
046: import org.cougaar.core.blackboard.Transaction;
047: import org.cougaar.core.mts.MessageAddress;
048: import org.cougaar.core.persist.PersistenceStream;
049: import org.cougaar.core.util.UID;
050: import org.cougaar.planning.ldm.asset.Asset;
051: import org.cougaar.util.BackedEnumerator;
052: import org.cougaar.util.CallerTracker;
053: import org.cougaar.util.Empty;
054: import org.cougaar.util.Enumerator;
055: import org.cougaar.util.Filters;
056: import org.cougaar.util.log.Logger;
057: import org.cougaar.util.log.Logging;
058:
059: /** Implementation of Task.
060: * Tasks that were created by Expanders are part of a Workflow.
061: * Tasks are the basic unit of Planning Domain work.
062: */
063: public class TaskImpl extends PlanningDirectiveImpl implements Task,
064: NewTask, Cloneable, ActiveSubscriptionObject,
065: java.io.Serializable {
066: private static final Logger logger = Logging
067: .getLogger(TaskImpl.class);
068: static final long serialVersionUID = 3637651371788963470L;
069:
070: private Verb verb;
071: private transient Asset directObject; // changed to transient : Persistence
072: private transient List phrases = null; // changed to transient : Persistence
073: private transient Workflow workflow; // changed to transient : Persistence
074: private transient List preferences = null;
075: private byte priority = Priority.UNDEFINED;
076: private UID parentUID;
077: private UID uid = null;
078: // plan elements don't cross agent boundaries
079: private transient PlanElement myPE;
080: // initialize to null unitl we fully implement
081: private long commitmenttime = 0;
082: //private Date commitmentdate = null;
083: private boolean deleted = false;// Set to true when deletion occurs
084: private transient Set observableAspects;
085: // initialize with one slot = -1 in case its never filled in.
086: private static final int[] emptyAuxQTypes = { -1 };
087: private int[] auxqtypes = emptyAuxQTypes;
088:
089: public MessageAddress getOwner() {
090: return source;
091: }
092:
093: public UID getUID() {
094: return uid;
095: }
096:
097: public void setUID(UID uid) {
098: if (this .uid != null)
099: throw new IllegalArgumentException("UID already set");
100: this .uid = uid;
101: }
102:
103: /** Constructor that takes no args */
104: public TaskImpl(UID uid) {
105: this .uid = uid;
106: }
107:
108: /** empty constructor used by clone and externalizable */
109: public TaskImpl() {
110: }
111:
112: /** @return Verb verb or action of task (move from move 152 tanks)*/
113: public Verb getVerb() {
114: return verb;
115: }
116:
117: /** @param aVerb set the verb or action of a task*/
118: public void setVerb(Verb aVerb) {
119: if (aVerb == null) {
120: throw new IllegalArgumentException("Verb must be non-null");
121: }
122: verb = aVerb;
123: decacheTS();
124: }
125:
126: /** @return Asset - directObject of the task */
127: public Asset getDirectObject() {
128: return directObject;
129: }
130:
131: /** @param dobj - set the directObject*/
132: public void setDirectObject(Asset dobj) {
133: directObject = dobj;
134: decacheTS();
135: }
136:
137: /** @return Enum{PrepositionalPhrase} - The prepositional phrase(s) of the task */
138: public Enumeration getPrepositionalPhrases() {
139: if (phrases == null || phrases.size() == 0)
140: return Empty.enumeration;
141: else
142: return new Enumerator(phrases);
143: }
144:
145: public PrepositionalPhrase getPrepositionalPhrase(String preposition) {
146: if (phrases == null || preposition == null)
147: return null;
148:
149: //preposition = preposition.intern(); // so we can use == below
150:
151: int l = phrases.size();
152: for (int i = 0; i < l; i++) {
153: PrepositionalPhrase pp = (PrepositionalPhrase) phrases
154: .get(i);
155: String op = pp.getPreposition();
156: //if (preposition==op) return pp;
157: if (preposition.equals(op))
158: return pp;
159: }
160: return null;
161: }
162:
163: /**
164: * @note that any previous values will be dropped.
165: * @param enumOfPrepPhrase - set the prepositional phrases
166: */
167: public void setPrepositionalPhrases(Enumeration enumOfPrepPhrase) {
168: if (phrases == null) {
169: if (enumOfPrepPhrase.hasMoreElements()) // don't make one if there aren't elements
170: phrases = new ArrayList(2);
171: } else {
172: phrases.clear();
173: }
174:
175: if (enumOfPrepPhrase == null) {
176: throw new IllegalArgumentException(
177: "Task.setPrepositionalPhrases(Enum e): e must be an Enumeration");
178: }
179:
180: while (enumOfPrepPhrase.hasMoreElements()) {
181: Object pp = enumOfPrepPhrase.nextElement();
182: if (pp instanceof PrepositionalPhrase) {
183: phrases.add((PrepositionalPhrase) pp);
184: } else {
185: //buzzzzzzz... wrong answer - tryed to pass in a null!
186: String info = pp != null ? ", found a " + pp.getClass()
187: : " found a 'null'";
188: throw new IllegalArgumentException(
189: "Task.setPrepositionalPhrases(Enum e): "
190: + "all elements of e must be PrepositionalPhrases"
191: + info);
192: }
193: }
194:
195: Transaction.noteChangeReport(this ,
196: new Task.PrepositionChangeReport());
197:
198: decacheTS();
199:
200: }
201:
202: /**
203: * Set the prepositional phrase (note singularity)
204: * @note that any previous values will be dropped
205: * @param aPrepPhrase
206: * @deprecated Use setPrepositionalPhrases(PrepositionalPhrase) or addPrepositionalPhrase(PrepositionalPhrase) instead.
207: */
208: public void setPrepositionalPhrase(PrepositionalPhrase aPrepPhrase) {
209: setPrepositionalPhrases(aPrepPhrase);
210: }
211:
212: /**
213: * Set the prepositional phrase (note singularity)
214: * @note that any previous values will be dropped
215: * @param aPrepPhrase
216: */
217: public void setPrepositionalPhrases(PrepositionalPhrase aPrepPhrase) {
218: if (phrases == null)
219: phrases = new ArrayList(1);
220: else
221: phrases.clear();
222:
223: if (aPrepPhrase == null)
224: return;
225:
226: Transaction.noteChangeReport(this ,
227: new Task.PrepositionChangeReport());
228: phrases.add(aPrepPhrase);
229: decacheTS();
230: }
231:
232: /**
233: * Adds a PrepositionalPhrase to the list of PrepositionalPhrases.
234: * @param aPrepPhrase
235: */
236: public void addPrepositionalPhrase(PrepositionalPhrase aPrepPhrase) {
237: if (aPrepPhrase == null)
238: throw new IllegalArgumentException(
239: "addPrepositionalPhrase requires a non-null argument.");
240:
241: if (phrases == null)
242: phrases = new ArrayList(1);
243:
244: String prep = aPrepPhrase.getPreposition();
245:
246: boolean found = false;
247: for (ListIterator it = phrases.listIterator(); it.hasNext();) {
248: PrepositionalPhrase pp = (PrepositionalPhrase) it.next();
249: if (prep.equals(pp.getPreposition())) {
250: found = true;
251: it.set(aPrepPhrase);
252: break;
253: }
254: }
255: if (!found) {
256: phrases.add(aPrepPhrase);
257: }
258:
259: Transaction.noteChangeReport(this ,
260: new Task.PrepositionChangeReport());
261: decacheTS();
262: }
263:
264: /** @return Workflow that this task is a part of*/
265: public Workflow getWorkflow() {
266: return workflow;
267: }
268:
269: /** @param aWorkflow setWorkflow */
270: public void setWorkflow(Workflow aWorkflow) {
271: workflow = aWorkflow;
272: decacheTS();
273: }
274:
275: /** @return Task - return parent task*/
276: public UID getParentTaskUID() {
277: org.cougaar.core.blackboard.Blackboard.getTracker()
278: .checkAccess(this , "getParentTask");
279: return parentUID;
280: }
281:
282: /** @param pt */
283: public void setParentTask(Task pt) {
284: if (pt == null) {
285: parentUID = null;
286: } else {
287: parentUID = pt.getUID();
288: }
289: //decacheTS(); // no need since toString doesnt use parent
290: }
291:
292: public void setParentTaskUID(UID uid) {
293: parentUID = uid;
294: //decacheTS(); // no need since toString doesnt use parent
295: }
296:
297: /**
298: * Get the preferences on this task. We assume that if the caller
299: * has synchronized the task that he will enumerate the preferences
300: * safely so we return an ordinary Enumberation. Otherwise, we
301: * return an Enumeration backed by a copy of the preferences to
302: * avoid ConcurrentModificationExceptions. With debug logging turned
303: * on, callers that haven't synchronized the tasks are printed the
304: * first time they getPreferences.
305: * @return Enumeration{Preference}
306: **/
307: private static CallerTracker badCallers = CallerTracker
308: .getShallowTracker();
309:
310: public Enumeration getPreferences() {
311: boolean useBackedEnumerator = !Thread.holdsLock(this );
312: if (useBackedEnumerator && logger.isDebugEnabled()) {
313: Object caller = badCallers.isNewFrame();
314: logger.debug("Unsafe call to Task.getPreferences from "
315: + caller);
316: }
317:
318: synchronized (this ) {
319: if (preferences != null && preferences.size() > 0) {
320: // if we need extra protection...
321: if (useBackedEnumerator) {
322: return new BackedEnumerator(preferences);
323: } else {
324: return new Enumerator(preferences);
325: }
326: } else {
327: return Empty.enumeration;
328: }
329: }
330: }
331:
332: /** return the preference for the given aspect type
333: * will return null if there is not a preference defined for this aspect type
334: * @param aspect_type The Aspect referenced by the preference
335: * @return Preference
336: **/
337: public synchronized Preference getPreference(int aspect_type) {
338: if (preferences == null)
339: return null;
340: int l = preferences.size();
341: for (int i = 0; i < l; i++) {
342: Preference testpref = (Preference) preferences.get(i);
343: if (testpref.getAspectType() == aspect_type) {
344: return testpref;
345: }
346: }
347: return null;
348: }
349:
350: /** return the preferred value for a given aspect type
351: * from the defined preference (and scoring function)
352: * will return Double.NaN if there is not a preference defined for this aspect type
353: * @param aspect_type The Aspect referenced by the preference
354: * @return double
355: */
356: public double getPreferredValue(int aspect_type) {
357: double valueresult = Double.NaN;
358: Preference matchpref = this .getPreference(aspect_type);
359: if (matchpref != null) {
360: valueresult = matchpref.getScoringFunction().getBest()
361: .getValue();
362: }
363: return valueresult;
364: }
365:
366: /** Get a list of the requested AuxiliaryQueryTypes (int).
367: * @note if there are no types set, this will return an
368: * an array with one element equal to -1 .
369: * @see org.cougaar.planning.ldm.plan.AuxiliaryQueryType
370: */
371: public int[] getAuxiliaryQueryTypes() {
372: // return a copy
373: //return (int[])auxqtypes.clone();
374: return auxqtypes; // reduce consing at a cost of object security
375: }
376:
377: /** Set the collection of AuxiliaryQueryTypes that the task is
378: * requesting information on. This information will be returned in
379: * the AllocationResult of this task's disposition.
380: * @note that this method clears all previous types.
381: * @param thetypes A collection of defined AuxiliaryQueryTypes
382: * @see org.cougaar.planning.ldm.plan.AuxiliaryQueryType
383: */
384: public void setAuxiliaryQueryTypes(int[] thetypes) {
385: // check the array values
386: for (int cit = 0; cit < thetypes.length; cit++) {
387: int checktype = thetypes[cit];
388: if ((checktype < -1)
389: || (checktype > AuxiliaryQueryType.LAST_AQTYPE)) {
390: throw new IllegalArgumentException(
391: "Task.setAxiliaryQueryTypes(int[] thetypes) "
392: + "expects a collection of defined types (int) from org.cougaar.planning.ldm.plan.AuxiliaryQueryType");
393: }
394: }
395: //auxqtypes = (int[])thetypes.clone();
396: auxqtypes = thetypes; // reduce consing at a cost of object security
397: decacheTS();
398: }
399:
400: /** Get the priority of this task.
401: * @note that this should only be used when there are competing tasks
402: * from the SAME customer.
403: * @return byte The priority of this task
404: * @see org.cougaar.planning.ldm.plan.Priority
405: */
406: public byte getPriority() {
407: return priority;
408: }
409:
410: /** set the preferences on this task.
411: * @param thepreferences
412: */
413: public synchronized void setPreferences(Enumeration thepreferences) {
414: // clear prefs
415: if (preferences == null) {
416: if (thepreferences.hasMoreElements()) // do we actually need storage?
417: preferences = new ArrayList(2);
418: } else {
419: preferences.clear();
420: }
421:
422: while (thepreferences.hasMoreElements()) {
423: Preference p = (Preference) thepreferences.nextElement();
424: //preferences.add(p.clone());
425: preferences.add(p); // MT
426: Transaction.noteChangeReport(this ,
427: new Task.PreferenceChangeReport(p.getAspectType()));
428: }
429:
430: Transaction.noteChangeReport(this ,
431: new Task.PreferenceChangeReport());
432: decacheTS();
433: }
434:
435: /** ONLY for infrastructure! Compare the preferences from two
436: * tasks, updating this.preferences to match that.preferences
437: * only if needed.
438: * @return true IFF the preferences were changed in this.
439: **/
440: public synchronized boolean private_updatePreferences(TaskImpl that) {
441: // this synchronization is scary, but it should be ok since we
442: // should not have preference updates in both directions.
443: if (this == that)
444: return false;// if eq, cannot do anything useful.
445: synchronized (that) {
446: List fps = that.preferences;
447: if (fps == preferences)
448: return false; // if prefs are eq, bail out now.
449: if (preferences == null) {
450: // don't have to test for null, since we'd have caught it
451: // above in the prefs == test.
452: int l = fps.size();
453: preferences = new ArrayList(l);
454: for (Iterator i = fps.iterator(); i.hasNext();) {
455: //preferences.add(((Preference)i.next()).clone());
456: preferences.add((Preference) i.next());
457: }
458: Transaction.noteChangeReport(this ,
459: new Task.PreferenceChangeReport());
460: return true;
461: } else {
462: if (fps == null || fps.isEmpty()) {
463: if (preferences.isEmpty()) {
464: return false;
465: } else {
466: preferences.clear();
467: return true;
468: }
469: } else {
470: // hard case - see if they are equal first
471: if (preferences.equals(fps)) {
472: // they have the same elements in the same order
473: return false;
474: } else {
475: // they are different.
476: preferences.clear();
477: preferences.addAll(fps);
478: Transaction.noteChangeReport(this ,
479: new Task.PreferenceChangeReport());
480: return true;
481: }
482: }
483: }
484: }
485: }
486:
487: /** Set just one preference in the task's preference list **/
488: public synchronized void setPreference(Preference p) {
489: int at = p.getAspectType();
490: Preference old;
491: if (preferences == null) {
492: preferences = new ArrayList(1);
493: old = null;
494: } else {
495: old = (Preference) Filters.findElement(preferences,
496: PreferencePredicate.get(at));
497: if (old != null) {
498: preferences.remove(old);
499: }
500: }
501: preferences.add(p);
502: Transaction.noteChangeReport(this ,
503: new Task.PreferenceChangeReport(at, old));
504: decacheTS();
505: }
506:
507: /** add a preference to the already existing preference list
508: * @param aPreference
509: */
510: public void addPreference(Preference aPreference) {
511: setPreference(aPreference);
512: }
513:
514: /** Set the priority of this task.
515: * @note that this should only be used when there are competing tasks
516: * from the SAME customer.
517: * @param thepriority
518: * @see org.cougaar.planning.ldm.plan.Priority
519: */
520: public void setPriority(byte thepriority) {
521: priority = thepriority;
522: decacheTS();
523: }
524:
525: /** WARNING: This date may be null if it is undefined
526: * Get the Commitment date of this task.
527: * After this date, the task is not allowed to be rescinded
528: * or re-planned (change in preferences).
529: * @return Date
530: */
531: public Date getCommitmentDate() {
532: if (commitmenttime == 0)
533: return null;
534: if (commitmentdate == null)
535: commitmentdate = new Date(commitmenttime);
536: return commitmentdate;
537: }
538:
539: /** The task commitment date of a task represents the date past which the planning
540: * module will warn if the task is changed or removed. Commitment dates in the planning
541: * domain are used to note the last possible date that a task could be changed before
542: * the supplier has committed resources to fill or commit the task. E.g. If a supplier
543: * has an order and ship lead time of 3 days, then the task's commitment date should be
544: * atleast 3 days before the desired delivery date (usually represented with an end
545: * date preference).
546: */
547: private transient Date commitmentdate = null;
548:
549: /**
550: * Check to see if the current time is before the Commitment date.
551: * Will return true if we have not reached the commitment date.
552: * Will return true if the commitment date is undefined (null)
553: * Will return false if we have passed the commitment date.
554: * @param currentdate The current date.
555: * @return boolean
556: */
557: public boolean beforeCommitment(Date currentdate) {
558: long currenttime = currentdate.getTime();
559: if (commitmenttime > 0) {
560: if (currenttime < commitmenttime) {
561: return true;
562: }
563: } else {
564: // if the commitmentdate is not defined (null) return true
565: return true;
566: }
567: // if we made it to here the current time is after the commit time.
568: return false;
569: }
570:
571: /** Set the Commitment date of this task.
572: * After this date, the task is not allowed to be rescinded
573: * or re-planned (change in preferences).
574: * @param commitDate
575: */
576: public void setCommitmentDate(Date commitDate) {
577: commitmentdate = commitDate;
578: commitmenttime = commitDate.getTime();
579: decacheTS();
580: }
581:
582: public boolean isDeleted() {
583: return deleted;
584: }
585:
586: public void setDeleted(boolean newDeleted) {
587: deleted = newDeleted;
588: }
589:
590: public void addObservableAspect(int aspectType) {
591: if (observableAspects == null)
592: observableAspects = new HashSet(1);
593: observableAspects.add(new Integer(aspectType));
594: }
595:
596: public Enumeration getObservableAspects() {
597: if (observableAspects == null)
598: return Empty.enumeration;
599: return new Enumerator(observableAspects.iterator());
600: }
601:
602: private void decacheTS() {
603: cachedTS = null;
604: }
605:
606: private transient String cachedTS = null;
607:
608: // String that has the main slots of the task
609: public String toString() {
610: if (cachedTS != null)
611: return cachedTS;
612:
613: Object o;
614: StringBuffer buf = new StringBuffer();
615: buf.append("<Task src=");
616: buf.append((o = getSource()) == null ? "null" : o.toString());
617: buf.append(" uid=");
618: buf.append((o = getUID()) == null ? "null" : o.toString());
619: buf.append(" pUid=");
620: buf.append((o = getParentTaskUID()) == null ? "null" : o
621: .toString());
622: buf.append(" verb=");
623: buf.append((o = getVerb()) == null ? "null" : o.toString());
624: buf.append(" dObj=");
625: buf.append(directObject == null ? "null" : directObject
626: .toString());
627:
628: if (phrases != null && phrases.size() > 0) {
629: buf.append(" ");
630: buf.append(phrases.toString());
631: }
632: if (priority != Priority.UNDEFINED) {
633: buf.append(" ");
634: buf.append(priority);
635: }
636: if (commitmenttime != 0) {
637: buf.append(" ");
638: buf.append(getCommitmentDate().toString());
639: }
640: if (isDeleted()) {
641: buf.append(" deleted");
642: }
643: if (preferences != null && preferences.size() != 0) {
644: buf.append(" ");
645: buf.append(preferences.toString());
646: }
647: if (auxqtypes != null) {
648: int l = auxqtypes.length;
649: if (l > 0 && !(l == 1 && auxqtypes[0] == -1)) {
650: buf.append(" (");
651: buf.append(l);
652: buf.append(" AQ)");
653: }
654: }
655: buf.append(">");
656:
657: String ts = buf.toString();
658: cachedTS = ts;
659: return ts;
660: }
661:
662: /** serialize tasks making certain that references to other tasks and
663: * workflows are appropriately proxied.
664: */
665: private void writeObject(ObjectOutputStream stream)
666: throws IOException {
667: synchronized (this ) { // make sure the prefs aren't changing while writing
668: stream.defaultWriteObject();
669:
670: stream.writeObject(directObject);
671: stream.writeObject(phrases);
672: stream.writeObject(preferences);
673: }
674:
675: // if we're persisting, we'll write some additional bits
676: if (stream instanceof PersistenceStream) {
677: stream.writeObject(myAnnotation);
678: stream.writeObject(observableAspects);
679: }
680: }
681:
682: private void readObject(ObjectInputStream stream)
683: throws ClassNotFoundException, IOException {
684: stream.defaultReadObject();
685:
686: directObject = (Asset) stream.readObject();
687: phrases = (List) stream.readObject();
688: preferences = (List) stream.readObject();
689:
690: // if we're persisting, we'll write some additional bits
691: if (stream instanceof PersistenceStream) {
692: myAnnotation = (Annotation) stream.readObject();
693: observableAspects = (HashSet) stream.readObject();
694: }
695:
696: pcs = new PropertyChangeSupport(this );
697: }
698:
699: /**
700: * Returns PlanElement that this Task is associated with.
701: * Can be used to discern between expandable and non-expandable
702: * Tasks. If Task has no PlanElement associated with it, will
703: * return null.
704: * @note This method is for infrastructure use only - do not call from user code!
705: * @note When TaskImpl debug logging is enabled, will do additional
706: * checks to be sure it is being called in a reasonable context (transaction).
707: */
708: public synchronized PlanElement getPlanElement() {
709: checkTransaction("privately_getPlanElement");
710:
711: Blackboard.getTracker().checkAccess(this , "getPlanElement");
712: return myPE;
713: }
714:
715: /**
716: * This method sets the PlanElement associated with this Task.
717: * @note This method is for infrastructure use only - do not call from user code!
718: * @note When TaskImpl debug logging is enabled, will do additional
719: * checks to be sure it is being called in a reasonable context (transaction).
720: */
721: public synchronized void privately_setPlanElement(PlanElement pe) {
722: checkTransaction("privately_setPlanElement");
723:
724: if (logger.isWarnEnabled()) {
725: if (myPE != null) {
726: logger.warn("Re-disposing " + this + " from " + myPE
727: + " to " + pe + ".", new Throwable());
728: }
729: if (pe == null) {
730: logger
731: .warn(
732: "Setting "
733: + this
734: + ".planElement to null - privately_resetPlanElement should be used instead",
735: new Throwable());
736: }
737: }
738: myPE = pe;
739: }
740:
741: /**
742: * This method clears the PlanElement associated with this Task.
743: * @note This method is for infrastructure use only - do not call from user code!
744: * @note When TaskImpl debug logging is enabled, will do additional
745: * checks to be sure it is being called in a reasonable context (transaction).
746: **/
747: public synchronized void privately_resetPlanElement() {
748: checkTransaction("privately_resetPlanElement");
749:
750: myPE = null;
751: }
752:
753: private void checkTransaction(String acc) {
754: if (logger.isDebugEnabled()) {
755: if (Transaction.getCurrentTransaction() == null) {
756: Throwable t = new Throwable();
757: StackTraceElement[] st = t.getStackTrace();
758: if (st.length >= 3) {
759: //String cn = st[2].getClassName();
760: String mn = st[2].getMethodName();
761: if ("postRehydration".equals(mn) || //PlanElementImpl (really ok)
762: "getCompletionData".equals(mn) || // completionservlet (less interesting)
763: "getConfidence".equals(mn) || // CompletionCalculator (ditto)
764: "printTaskDetails".equals(mn) // PlanViewServlet
765: ) {
766: return;
767: }
768: }
769: logger.error("called task." + acc
770: + "() outside of Transaction", t);
771: }
772: }
773: }
774:
775: public boolean equals(Object ob) {
776: if (ob == this )
777: return true;
778: if (ob instanceof Task) {
779: return uid.equals(((Task) ob).getUID());
780: } else {
781: return false;
782: }
783: }
784:
785: public int hashCode() {
786: // just use the hashcode of the UID.
787: // this means Don't mix UIDs and Tasks in the same hash table...
788: return getUID().hashCode();
789: }
790:
791: public void setSource(MessageAddress asource) {
792: MessageAddress old = getSource();
793: if (old != null) {
794: if (!asource.equals(old)) {
795: logger.error("Bad task.setSource(" + asource + ") was "
796: + old, new Throwable());
797: }
798: } else {
799: super .setSource(asource);
800: }
801: }
802:
803: public void setDestination(MessageAddress dest) {
804: super .setDestination(dest);
805: if (!dest.equals(getSource())) {
806: logger.error("Suspicious task.setDestination(" + dest
807: + ") != " + getSource(), new Throwable());
808: }
809: }
810:
811: // Private setter without destination check
812: public void privately_setDestination(MessageAddress dest) {
813: super .setDestination(dest);
814: }
815:
816: public synchronized Object clone() {
817: // make sure the clone gets a new oid.
818:
819: TaskImpl nt = new TaskImpl();
820:
821: // directiveimpl
822: nt.setSource(getSource());
823: nt.setDestination(getDestination());
824: //nt.setPlan(getPlan());
825:
826: // duplicate the immutable parts
827: nt.setVerb(getVerb());
828: nt.setDirectObject(getDirectObject());
829: nt.setPrepositionalPhrases(getPrepositionalPhrases());
830: nt.setPreferences(getPreferences());
831: nt.setPriority(getPriority());
832: nt.setAuxiliaryQueryTypes(getAuxiliaryQueryTypes());
833: nt.setContext(getContext());
834:
835: // parent of the clone is our parent.
836: nt.setParentTaskUID(getParentTaskUID());
837:
838: // no point to doing these
839: // nt.setWorkflow(null);
840: // nt.privately_resetPlanElement();
841:
842: return nt;
843: }
844:
845: // new property reading methods returned by TaskImplBeanInfo
846: public String getParentTaskID() {
847: org.cougaar.core.blackboard.Blackboard.getTracker()
848: .checkAccess(this , "getParentTask");
849: return (parentUID == null) ? null : parentUID.toString();
850: }
851:
852: public String getVerbName() {
853: return getVerb().toString();
854: }
855:
856: public String getPlanName() {
857: return getPlan().getPlanName();
858: }
859:
860: private static final PrepositionalPhrase[] emptyPhrases = new PrepositionalPhrase[0];
861:
862: public PrepositionalPhrase[] getPrepositionalPhrasesAsArray() {
863: if (phrases == null || phrases.size() == 0) {
864: return emptyPhrases;
865: }
866: int l = phrases.size();
867: PrepositionalPhrase p[] = new PrepositionalPhrase[l];
868: for (int i = 0; i < l; i++) {
869: p[i] = (PrepositionalPhrase) phrases.get(i);
870: }
871: return p;
872: }
873:
874: public PrepositionalPhrase getPrepositionalPhraseFromArray(int i) {
875: if (phrases == null)
876: return null;
877: return (PrepositionalPhrase) phrases.get(i);
878: }
879:
880: private static final Preference[] emptyPreferences = new Preference[0];
881:
882: public synchronized Preference[] getPreferencesAsArray() {
883: int l;
884: if (preferences == null)
885: return emptyPreferences;
886: if ((l = preferences.size()) == 0)
887: return emptyPreferences;
888:
889: Preference p[] = new Preference[l];
890: for (int i = 0; i < l; i++) {
891: p[i] = (Preference) preferences.get(i);
892: }
893: return p;
894: }
895:
896: public synchronized Preference getPreferenceFromArray(int i) {
897: if (preferences == null)
898: return null;
899: return (Preference) preferences.get(i);
900: }
901:
902: public UID getPlanElementID() {
903: PlanElement pe = getPlanElement();
904: return (pe != null) ? pe.getUID() : null;
905: }
906:
907: // ActiveSubscriptionObject
908: public void addingToBlackboard(Subscriber s, boolean commit) {
909: if (!commit) {
910: return;
911: }
912: if (verb == null) {
913: throw new IllegalArgumentException("publishAdd of " + this
914: + " with null verb");
915: }
916: }
917:
918: public void changingInBlackboard(Subscriber s, boolean commit) {
919: if (!commit) {
920: return;
921: }
922: // execution monitoring / commitment time checks
923: if (commitmenttime > 0) {
924: long curTime = s.getClient().currentTimeMillis();
925: // Could allow a 5 second buffer perhaps?
926: // IE: if (curTime > commitmenttime + 5000)
927: if (curTime > commitmenttime) {
928: // its after the commitment time, should not publish the change
929: // For now, we do though
930: logger.warn("publishChange of " + this + " "
931: + (curTime - commitmenttime)
932: + " past commitmenttime " + getCommitmentDate()
933: + " at current time " + (new Date(curTime))
934: + " by Subscriber " + s);
935: }
936: }
937: }
938:
939: public void removingFromBlackboard(Subscriber s, boolean commit) {
940: if (!commit) {
941: return;
942: }
943:
944: NewWorkflow wf = (NewWorkflow) getWorkflow();
945: if (wf != null) {
946: synchronized (wf) {
947: for (Enumeration tasks = wf.getTasks(); tasks
948: .hasMoreElements();) {
949: if (tasks.nextElement() == this ) {
950: if (logger.isDebugEnabled()) {
951: logger.debug(
952: "Illegal publishRemove subtask still in a workflow: "
953: + this , new Throwable());
954: } else if (logger.isWarnEnabled()) {
955: logger
956: .warn("Illegal publishRemove subtask still in a workflow: "
957: + this );
958: }
959: wf.removeTask(this );
960: break;
961: }
962: }
963: }
964: }
965: }
966:
967: //dummy PropertyChangeSupport for the Jess Interpreter.
968: public transient PropertyChangeSupport pcs = new PropertyChangeSupport(
969: this );
970:
971: public void addPropertyChangeListener(PropertyChangeListener pcl) {
972: pcs.addPropertyChangeListener(pcl);
973: }
974:
975: public void removePropertyChangeListener(PropertyChangeListener pcl) {
976: pcs.removePropertyChangeListener(pcl);
977: }
978:
979: private Context myContext = null;
980:
981: public void setContext(Context context) {
982: myContext = context;
983: }
984:
985: public Context getContext() {
986: return myContext;
987: }
988:
989: private transient Annotation myAnnotation = null;
990:
991: public void setAnnotation(Annotation pluginAnnotation) {
992: myAnnotation = pluginAnnotation;
993: }
994:
995: public Annotation getAnnotation() {
996: return myAnnotation;
997: }
998:
999: }
|