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.BeanDescriptor;
030: import java.beans.BeanInfo;
031: import java.beans.EventSetDescriptor;
032: import java.beans.IntrospectionException;
033: import java.beans.MethodDescriptor;
034: import java.beans.PropertyChangeListener;
035: import java.beans.PropertyChangeSupport;
036: import java.beans.PropertyDescriptor;
037: import java.io.IOException;
038: import java.io.ObjectInputStream;
039: import java.io.ObjectOutputStream;
040: import java.util.ArrayList;
041: import java.util.Collection;
042: import java.util.Date;
043: import java.util.Enumeration;
044:
045: import org.cougaar.core.blackboard.ActiveSubscriptionObject;
046: import org.cougaar.core.blackboard.BlackboardException;
047: import org.cougaar.core.blackboard.Claimable;
048: import org.cougaar.core.blackboard.PublishableAdapter;
049: import org.cougaar.core.blackboard.Subscriber;
050: import org.cougaar.core.blackboard.Blackboard;
051: import org.cougaar.core.blackboard.Transaction;
052: import org.cougaar.core.persist.ActivePersistenceObject;
053: import org.cougaar.core.util.UID;
054: import org.cougaar.planning.ldm.asset.Asset;
055: import org.cougaar.util.log.Logger;
056: import org.cougaar.util.log.Logging;
057:
058: /** PlanElement Implementation
059: * PlanElements represent the association of a Plan, a Task,
060: * and a Disposition (where a Disposition is either
061: * an Allocation, an Expansion, an Aggregation, or an AssetTransfer).
062: * A Disposition (as defined above) are subclasses of PlanElement.
063: * PlanElements make a Plan. For Example, a task "move 15 tanks..." with
064: * an Allocation(an Asset, estimated penalty and estimated schedule)
065: * of 15 HETs could represent a PlanElement.
066: **/
067:
068: public abstract class PlanElementImpl extends PublishableAdapter
069: implements PlanElement, NewPlanElement, PEforCollections,
070: ScheduleElement, ActiveSubscriptionObject,
071: ActivePersistenceObject, BeanInfo {
072:
073: private static final long serialVersionUID = -3303746652987764635L;
074:
075: protected transient Task task; // changed to transient : Persistence
076: //protected Plan plan;
077:
078: private UID uid;
079:
080: /**
081: * There are four allocation results:
082: * estAR is set by the plugin often as a copy of the reported
083: * rcvAR is computed from downstream results (e.g. by workflow aggregators)
084: * obsAR is set from observed events (event monitor)
085: * repAR is the merge of obsAR and rcvAR and is lazily evaluated
086: **/
087: protected AllocationResult obsAR = null;
088: protected AllocationResult repAR = null;
089: protected AllocationResult rcvAR = null;
090: protected AllocationResult estAR = null;
091:
092: protected transient boolean notify = false;
093:
094: private static final Logger logger = Logging
095: .getLogger(PlanElement.class);
096:
097: //no-arg constructor
098: public PlanElementImpl() {
099: }
100:
101: public PlanElementImpl(UID uid) {
102: this .uid = uid;
103: }
104:
105: //constructor that takes both a plan and a task object
106: public PlanElementImpl(Plan p, Task t) {
107: //plan = p;
108: setTask(t);
109: }
110:
111: public void setUID(UID uid) {
112: this .uid = uid;
113: }
114:
115: public UID getUID() {
116: return uid;
117: }
118:
119: //PlanElement interface implementations
120:
121: /**
122: * @return Plan the plan this planelement is a part of
123: **/
124: public Plan getPlan() {
125: return PlanImpl.REALITY;
126: }
127:
128: /** This returns the Task of the PlanElement.
129: * @return Task
130: **/
131:
132: public Task getTask() {
133: return task;
134: }
135:
136: // ClaimableHolder interface implementation
137: public Claimable getClaimable() {
138: Task t = getTask();
139: if (t != null && t instanceof Claimable) {
140: return ((Claimable) t);
141: }
142: return null;
143: }
144:
145: // NewPlanElement interface implementations
146:
147: /** This sets the Task of the PlanElement.
148: * Also sets the planelement of the task
149: * @param t
150: **/
151:
152: public void setTask(Task t) {
153: if (task != null) {
154: logger.error("planelement.setTask from " + task + " to "
155: + t, new Throwable());
156: }
157: task = t;
158: }
159:
160: /**
161: * Sets the Task of the PlanElement. This method differs from
162: * setTask in that it is expected that the PlanElement is already
163: * attached to a Task so the Task and PlanElement are rewired
164: * accordingly.
165: * @param t - The new Task that the PlanElement is referencing.
166: **/
167: public void resetTask(Task t) {
168: Task oldTask = getTask();
169: logger.error("planelement.resetTask from " + oldTask + " to "
170: + t, new Throwable());
171: if (oldTask != null) {
172: ((TaskImpl) oldTask).privately_resetPlanElement();
173: }
174: setTask(t);
175: }
176:
177: /** @param p - set the plan this planelement is a part of */
178: public void setPlan(Plan p) {
179: //plan = p;
180: }
181:
182: /** Returns the estimated allocation result that is related to performing
183: * the Task.
184: * @return AllocationResult
185: **/
186:
187: public AllocationResult getEstimatedResult() {
188: return estAR;
189: }
190:
191: /** Returns the reported allocation result.
192: * @return AllocationResult
193: **/
194: public AllocationResult getReportedResult() {
195: if (repAR == null) {
196: if (rcvAR == null) {
197: repAR = obsAR;
198: } else if (obsAR == null) {
199: repAR = rcvAR;
200: } else {
201: repAR = new AllocationResult(obsAR, rcvAR);
202: }
203: }
204: return repAR;
205: }
206:
207: /** Returns the received allocation result.
208: * @return AllocationResult
209: **/
210: public AllocationResult getReceivedResult() {
211: return rcvAR;
212: }
213:
214: /** Returns the observed allocation result.
215: * @return AllocationResult
216: **/
217: public AllocationResult getObservedResult() {
218: return obsAR;
219: }
220:
221: /** Set the estimated allocation result so that a notification will
222: * propagate up another level.
223: * @param estimatedresult
224: **/
225: public void setEstimatedResult(AllocationResult estimatedresult) {
226: estAR = estimatedresult;
227: Transaction.noteChangeReport(this ,
228: new PlanElement.EstimatedResultChangeReport());
229: setNotification(true);
230: }
231:
232: /**
233: * CALLED BY INFRASTRUCTURE ONLY - AFTER RESULTS HAVE BEEN COMPUTED ACROSS TASKS.
234: * @param rcvres the new received AllocationResult object associated with this pe
235: */
236: public void setReceivedResult(AllocationResult rcvres) {
237: rcvAR = rcvres;
238: repAR = null; // Need to recompute this
239: Transaction.noteChangeReport(this ,
240: new PlanElement.ReportedResultChangeReport());
241: }
242:
243: /** @deprecated use setReceivedResult **/
244: public void setReportedResult(AllocationResult repres) {
245: throw new UnsupportedOperationException(
246: "Use setReceivedResult instead");
247: }
248:
249: /**
250: * Set or update the observed AllocationResult. Should be called
251: * only by the event monitor.
252: * @param obsres the new observed AllocationResult object associated with this pe
253: **/
254: public void setObservedResult(AllocationResult obsres) {
255: obsAR = obsres;
256: repAR = null; // Need to recompute this
257: Transaction.noteChangeReport(this ,
258: new PlanElement.ObservedResultChangeReport());
259: Transaction.noteChangeReport(this ,
260: new PlanElement.ReportedResultChangeReport());
261: }
262:
263: // implement TimeSpan
264:
265: public long getStartTime() {
266: AllocationResult ar = estAR;
267: if (ar != null) {
268: if (ar.isDefined(AspectType.START_TIME)) {
269: return (long) ar.getValue(AspectType.START_TIME);
270: }
271: }
272: return MIN_VALUE;
273: }
274:
275: public long getEndTime() {
276: AllocationResult ar = estAR;
277: if (ar != null) {
278: if (ar.isDefined(AspectType.END_TIME)) {
279: return (long) ar.getValue(AspectType.END_TIME);
280: }
281: }
282: return MAX_VALUE;
283: }
284:
285: public boolean shouldDoNotification() {
286: return notify;
287: }
288:
289: public void setNotification(boolean v) {
290: notify = v;
291: }
292:
293: // ScheduleElement implementation
294: /** Start date is a millisecond-precision, inclusive time of start.
295: * @return Date Start time for the task
296: **/
297: public Date getStartDate() {
298: return new Date(getStartTime());
299: }
300:
301: /** End Date is millisecond-precision, <em>exclusive</em> time of end.
302: * @return Date End time for the task
303: **/
304: public Date getEndDate() {
305: return new Date(getEndTime());
306: }
307:
308: /** is the Date on or after the start time and strictly before the end time?
309: * @return boolean whether the date is included in this time interval.
310: **/
311: public boolean included(Date date) {
312: return included(date.getTime());
313: }
314:
315: /** is the time on or after the start time and strictly before the end time?
316: * @return boolean whether the time is included in this time interval
317: **/
318: public boolean included(long time) {
319: return ((time >= getStartTime()) && (time < getEndTime()));
320: }
321:
322: /** Does the scheduleelement overlap (not merely abut) the schedule?
323: * @return boolean whether schedules overlap
324: **/
325: public boolean overlapSchedule(ScheduleElement se) {
326: long tstime = se.getStartTime();
327: long tetime = se.getEndTime();
328:
329: return (tstime < getEndTime() && tetime > getStartTime());
330: }
331:
332: /** Does the scheduleElement meet/abut the schedule?
333: **/
334: public boolean abutSchedule(ScheduleElement se) {
335: long tstime = se.getStartTime();
336: long tetime = se.getEndTime();
337:
338: return (tstime == getEndTime() || tetime == getStartTime());
339: }
340:
341: // If the planelement is either an allocation or an assettransfer, add the
342: // planelement to the respective Asset's RoleSchedule.
343: protected void addToRoleSchedule(Asset asset) {
344: Asset roleasset = asset;
345: if (roleasset != null) {
346: RoleScheduleImpl rsi = (RoleScheduleImpl) roleasset
347: .getRoleSchedule();
348: rsi.add(this );
349: } else {
350: System.err
351: .println("\n WARNING - could not add PlanElement to roleschedule");
352: }
353: }
354:
355: protected void removeFromRoleSchedule(Asset asset) {
356: Asset roleasset = asset;
357: if (roleasset != null) {
358: RoleScheduleImpl rsi = (RoleScheduleImpl) roleasset
359: .getRoleSchedule();
360: rsi.remove(this );
361: } else {
362: System.err
363: .println("\n WARNING - could not remove PlanElement from roleschedule");
364: }
365: }
366:
367: private void writeObject(ObjectOutputStream stream)
368: throws IOException {
369:
370: stream.defaultWriteObject();
371:
372: stream.writeObject(task);
373: if (stream instanceof org.cougaar.core.persist.PersistenceOutputStream) {
374: stream.writeObject(myAnnotation);
375: }
376: }
377:
378: private void readObject(ObjectInputStream stream)
379: throws ClassNotFoundException, IOException {
380:
381: stream.defaultReadObject();
382:
383: task = (Task) stream.readObject();
384: if (stream instanceof org.cougaar.core.persist.PersistenceInputStream) {
385: myAnnotation = (Annotation) stream.readObject();
386: }
387: pcs = new PropertyChangeSupport(this );
388: }
389:
390: public String toString() {
391: return "[PE #" + task.getUID() + " -> " + "]";
392: }
393:
394: // ActiveSubscriptionObject
395: public void addingToBlackboard(Subscriber s, boolean commit) {
396: Blackboard.getTracker().checkpoint(commit, getTask(),
397: "getPlanElement");
398: if (!commit)
399: return;
400:
401: Task t = getTask();
402: Date comdate = t.getCommitmentDate();
403: if (comdate != null) {
404: // make sure the current planning time is before commitment time
405: long curTime = s.getClient().currentTimeMillis();
406: // Could allow a 5 second buffer perhaps?
407: // IE: if (curTime > comdate.getTime() + 5000)
408: if (curTime > comdate.getTime()) {
409: // its after the commitment time - shouldn't publish the object
410: // But for now we do so anyhow
411: logger.warn("publishAdd of " + this + " "
412: + (curTime - comdate.getTime())
413: + " millis past commitmenttime " + comdate
414: + " at curTime: " + (new Date(curTime))
415: + " by Subscriber " + s);
416: }
417: }
418:
419: PlanElement existingPE = t.getPlanElement();
420: BlackboardException e = null;
421: if (existingPE == null) {
422: ((TaskImpl) t).privately_setPlanElement(this );
423: } else if (existingPE == this ) {
424: e = new BlackboardException(
425: "publishAdd of miswired PlanElement (task already wired to this PE): "
426: + this );
427: } else {
428: e = new BlackboardException(
429: "publishAdd of miswired PlanElement (task already has other PE): "
430: + existingPE);
431: }
432: if (e != null) {
433: logger.error("PlanElement.addingToBlackboard", e);
434: throw e;
435: }
436: }
437:
438: public void changingInBlackboard(Subscriber s, boolean commit) {
439: }
440:
441: public void removingFromBlackboard(Subscriber s, boolean commit) {
442: Blackboard.getTracker().checkpoint(commit, getTask(),
443: "getPlanElement");
444: if (!commit)
445: return;
446:
447: Task t = getTask();
448: ((TaskImpl) t).privately_resetPlanElement();
449: }
450:
451: // ActivePersistenceObject
452: public boolean skipUnpublishedPersist(Logger logger) {
453: logger.error("Omitting PlanElement not on blackboard: " + this );
454: return true;
455: }
456:
457: public void checkRehydration(Logger logger) {
458: /* // currently a no-op
459: if (this instanceof AssetTransfer) {
460: } else {
461: Task task = getTask();
462: if (task != null) {
463: PlanElement taskPE = task.getPlanElement();
464: if (taskPE != this) {
465: // if (logger.isWarnEnabled()) logger.warn("Bad " + getClass().getName() + ": getTask()=" + task + " task.getPlanElement()=" + taskPE);
466: }
467: } else {
468: // if (logger.isWarnEnabled()) logger.warn("Bad " + getClass().getName() + ": getTask()=null");
469: }
470: }
471: */
472: }
473:
474: public void postRehydration(Logger logger) {
475: if (logger.isDebugEnabled()) {
476: logger.debug("Rehydrated plan element: " + this );
477: }
478:
479: TaskImpl task = (TaskImpl) getTask();
480: if (task != null) {
481: PlanElement taskPE = task.getPlanElement();
482: if (taskPE != this ) {
483: if (taskPE != null) {
484: task.privately_resetPlanElement();
485: logger.warn("resetPlanElement of " + task + " to "
486: + this + " (was " + taskPE + ")");
487: }
488:
489: task.privately_setPlanElement(this ); // These links can get severed during rehydration
490: }
491: }
492: }
493:
494: /** reset asset role-schedules post-rehydration **/
495: protected void fixAsset(Asset asset) {
496: // Compute role-schedules
497: RoleScheduleImpl rsi = (RoleScheduleImpl) asset
498: .getRoleSchedule();
499: rsi.add(this );
500: }
501:
502: // Should match BasePersistence.hc(o), without compile dependency
503: protected static String hc(Object o) {
504: return (Integer.toHexString(System.identityHashCode(o)) + " " + (o == null ? "<null>"
505: : o.toString()));
506: }
507:
508: //
509: // annotation
510: //
511: private transient Annotation myAnnotation = null;
512:
513: public void setAnnotation(Annotation pluginAnnotation) {
514: myAnnotation = pluginAnnotation;
515: }
516:
517: public Annotation getAnnotation() {
518: return myAnnotation;
519: }
520:
521: //dummy PropertyChangeSupport for the Jess Interpreter.
522: public transient PropertyChangeSupport pcs = new PropertyChangeSupport(
523: this );
524:
525: public void addPropertyChangeListener(PropertyChangeListener pcl) {
526: pcs.addPropertyChangeListener(pcl);
527: }
528:
529: public void removePropertyChangeListener(PropertyChangeListener pcl) {
530: pcs.removePropertyChangeListener(pcl);
531: }
532:
533: // beaninfo - duplicate of SelfDescribingBeanInfo because
534: // java doesn't allow multiple inheritence of implementation.
535:
536: public BeanDescriptor getBeanDescriptor() {
537: return null;
538: }
539:
540: public int getDefaultPropertyIndex() {
541: return -1;
542: }
543:
544: public EventSetDescriptor[] getEventSetDescriptors() {
545: return null;
546: }
547:
548: public int getDefaultEventIndex() {
549: return -1;
550: }
551:
552: public MethodDescriptor[] getMethodDescriptors() {
553: return null;
554: }
555:
556: public BeanInfo[] getAdditionalBeanInfo() {
557: return null;
558: }
559:
560: public java.awt.Image getIcon(int iconKind) {
561: return null;
562: }
563:
564: private static final PropertyDescriptor[] _emptyPD = new PropertyDescriptor[0];
565:
566: public PropertyDescriptor[] getPropertyDescriptors() {
567: Collection pds = new ArrayList();
568: try {
569: addPropertyDescriptors(pds);
570: } catch (IntrospectionException ie) {
571: System.err
572: .println("Warning: Caught exception while introspecting on "
573: + this .getClass());
574: ie.printStackTrace();
575: }
576: return (PropertyDescriptor[]) pds.toArray(_emptyPD);
577: }
578:
579: protected void addPropertyDescriptors(Collection c)
580: throws IntrospectionException {
581: c.add(new PropertyDescriptor("uid", PlanElementImpl.class,
582: "getUID", null));
583: //c.add(new PropertyDescriptor("plan", PlanElementImpl.class, "getPlan", null));
584: c.add(new PropertyDescriptor("task", PlanElementImpl.class,
585: "getTask", null));
586: c.add(new PropertyDescriptor("estimatedResult",
587: PlanElementImpl.class, "getEstimatedResult",
588: "setEstimatedResult"));
589: c.add(new PropertyDescriptor("reportedResult",
590: PlanElementImpl.class, "getReportedResult", null));
591: }
592: }
|