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: package org.cougaar.planning.ldm.lps;
027:
028: import java.util.ArrayList;
029: import java.util.Collection;
030: import java.util.Iterator;
031: import java.util.Vector;
032:
033: import org.cougaar.core.blackboard.Directive;
034: import org.cougaar.core.domain.LogicProvider;
035: import org.cougaar.core.domain.MessageLogicProvider;
036: import org.cougaar.core.domain.RootPlan;
037: import org.cougaar.core.logging.LoggingServiceWithPrefix;
038: import org.cougaar.core.mts.MessageAddress;
039: import org.cougaar.planning.ldm.LogPlan;
040: import org.cougaar.planning.ldm.PlanningFactory;
041: import org.cougaar.planning.ldm.asset.Asset;
042: import org.cougaar.planning.ldm.asset.LocalPG;
043: import org.cougaar.planning.ldm.asset.PropertyGroup;
044: import org.cougaar.planning.ldm.asset.PropertyGroupSchedule;
045: import org.cougaar.planning.ldm.plan.AssetAssignment;
046: import org.cougaar.planning.ldm.plan.AssignedAvailabilityElement;
047: import org.cougaar.planning.ldm.plan.AssignedRelationshipElement;
048: import org.cougaar.planning.ldm.plan.HasRelationships;
049: import org.cougaar.planning.ldm.plan.NewRelationshipSchedule;
050: import org.cougaar.planning.ldm.plan.NewRoleSchedule;
051: import org.cougaar.planning.ldm.plan.NewSchedule;
052: import org.cougaar.planning.ldm.plan.Relationship;
053: import org.cougaar.planning.ldm.plan.RelationshipSchedule;
054: import org.cougaar.planning.ldm.plan.Role;
055: import org.cougaar.planning.ldm.plan.Schedule;
056: import org.cougaar.planning.ldm.plan.ScheduleElement;
057: import org.cougaar.util.Enumerator;
058: import org.cougaar.util.MutableTimeSpan;
059: import org.cougaar.util.TimeSpan;
060: import org.cougaar.util.UnaryPredicate;
061: import org.cougaar.util.log.Logger;
062: import org.cougaar.util.log.Logging;
063:
064: /**
065: * take an incoming AssetAssignment Directive and
066: * add Asset to the LogPlan w/side-effect of also disseminating to
067: * other subscribers.
068: **/
069: public class ReceiveAssetLP implements LogicProvider,
070: MessageLogicProvider {
071: private static Logger logBase = Logging
072: .getLogger(ReceiveAssetLP.class);
073:
074: private final RootPlan rootplan;
075: private final LogPlan logplan;
076: private final PlanningFactory ldmf;
077: private final MessageAddress self;
078: private final Logger logger;
079:
080: private static TimeSpan ETERNITY = new MutableTimeSpan();
081:
082: public ReceiveAssetLP(RootPlan rootplan, LogPlan logplan,
083: PlanningFactory ldmf, MessageAddress self) {
084: this .rootplan = rootplan;
085: this .logplan = logplan;
086: this .ldmf = ldmf;
087: this .self = self;
088: logger = new LoggingServiceWithPrefix(logBase, self.toString()
089: + ": ");
090: }
091:
092: public void init() {
093: }
094:
095: /**
096: * Adds Assets to LogPlan... Side-effect = other subscribers
097: * also updated.
098: **/
099: public void execute(Directive dir, Collection changes) {
100: if (dir instanceof AssetAssignment) {
101: AssetAssignment aa = (AssetAssignment) dir;
102: if (logger.isDebugEnabled())
103: logger.debug("Received " + aa);
104: receiveAssetAssignment(aa);
105: }
106: }
107:
108: private final static boolean related(Asset a) {
109: return (a instanceof HasRelationships);
110: }
111:
112: private void receiveAssetAssignment(AssetAssignment aa) {
113: // figure out the assignee
114: Asset assigneeT = aa.getAssignee();// assignee from message
115: Asset assigneeL = logplan.findAsset(assigneeT); // local assignee instance
116:
117: if (assigneeL == null) {
118: logger.error("Unable to find receiving asset " + assigneeT
119: + " in " + self);
120: return;
121: }
122:
123: // figure out the asset being transferred
124: Asset assetT = aa.getAsset(); // asset from message
125: Asset assetL = logplan.findAsset(assetT);// local instance of asset
126:
127: Asset asset = assetL;
128:
129: if (asset == null) {
130: if (aa.isUpdate() || aa.isRepeat()) {
131: logger
132: .error("Received Update Asset Transfer, but cannot find original "
133: + aa);
134: }
135: asset = createLocalAsset(assetT);
136: }
137:
138: boolean changeRelationshipRequired = updateRelationships(aa,
139: asset, assigneeL);
140:
141: boolean changeAvailabilityRequired = fixAvailSchedule(aa,
142: asset, assigneeL);
143:
144: boolean changePGRequired = false;
145:
146: if (assetL != null) {
147: // If we already had a matching asset - update with property groups
148: // from the asset transfer.
149: changePGRequired = updatePG(assetT, assetL);
150: }
151:
152: if (logger.isDebugEnabled()) {
153: logger
154: .debug("ReceiveAssetLP: changeRelationshipRequired = "
155: + changeRelationshipRequired
156: + " changeAvailabilityRequired = "
157: + changeAvailabilityRequired
158: + " changePGRequired = "
159: + changePGRequired
160: + " for AssetAssignment - "
161: + aa
162: + " transfering asset - "
163: + asset
164: + " receiving asset - " + assigneeL);
165: }
166:
167: // publish the add or change
168: if (assetL == null) { // add it if it wasn't already there
169: rootplan.add(asset);
170: } else if (changeRelationshipRequired) {
171: Collection changeReports = new ArrayList();
172: changeReports
173: .add(new RelationshipSchedule.RelationshipScheduleChangeReport());
174: rootplan.change(asset, changeReports);
175: } else if (changeAvailabilityRequired || changePGRequired) {
176: rootplan.change(asset, null);
177: }
178: }
179:
180: private void removeExistingRelationships(
181: Collection aaRelationships,
182: NewRelationshipSchedule transferringSchedule,
183: NewRelationshipSchedule receivingSchedule) {
184: HasRelationships receivingAsset = receivingSchedule
185: .getHasRelationships();
186:
187: for (Iterator aaIterator = aaRelationships.iterator(); aaIterator
188: .hasNext();) {
189: Relationship relationship = (Relationship) aaIterator
190: .next();
191:
192: Role role = (relationship.getA().equals(receivingAsset)) ? relationship
193: .getRoleA()
194: : relationship.getRoleB();
195:
196: Collection remove = transferringSchedule
197: .getMatchingRelationships(role, receivingAsset,
198: ETERNITY);
199:
200: // The relationship between the receiving and transferring assets should
201: // be represented with identical Relationships in the two
202: // RelationshipSchedules.
203: transferringSchedule.removeAll(remove);
204: receivingSchedule.removeAll(remove);
205: }
206: }
207:
208: // Update availability info for the transferred asset
209: // AvailableSchedule reflects availability within the current agent
210: private boolean fixAvailSchedule(AssetAssignment aa, Asset asset,
211: final Asset assignee) {
212: if (aa.getSchedule() == null) {
213: return false;
214: }
215:
216: NewSchedule availSchedule = (NewSchedule) asset
217: .getRoleSchedule().getAvailableSchedule();
218:
219: if (availSchedule == null) {
220: availSchedule = ldmf.newAssignedAvailabilitySchedule();
221: ((NewRoleSchedule) asset.getRoleSchedule())
222: .setAvailableSchedule(availSchedule);
223: }
224: boolean change = false;
225:
226: synchronized (availSchedule) {
227:
228: // Find all existing entries which refer to the receiving asset
229: Collection currentAvailability = availSchedule
230: .filter(new UnaryPredicate() {
231: public boolean execute(Object o) {
232: return ((o instanceof AssignedAvailabilityElement) && (((AssignedAvailabilityElement) o)
233: .getAssignee().equals(assignee)));
234: }
235: });
236:
237: if (related(asset) && related(assignee)) {
238:
239: //Construct aggregate avail info from the relationship schedule
240: RelationshipSchedule relationshipSchedule = ((HasRelationships) asset)
241: .getRelationshipSchedule();
242: Collection matchingRelationships = relationshipSchedule
243: .getMatchingRelationships(
244: (HasRelationships) assignee, ETERNITY);
245:
246: // If any relationships, construct a single avail element with the
247: // min start and max end
248: if (!matchingRelationships.isEmpty()) {
249: Schedule matchingRelationshipsSchedule = ldmf
250: .newSchedule(new Enumerator(
251: matchingRelationships));
252: AssignedAvailabilityElement aggregateAvailability = ldmf
253: .newAssignedAvailabilityElement(assignee,
254: matchingRelationshipsSchedule
255: .getStartTime(),
256: matchingRelationshipsSchedule
257: .getEndTime());
258: // Compare to existing entries - only change if required.
259: if (!currentAvailability.isEmpty()) {
260: Schedule currentSchedule = ldmf
261: .newSchedule(new Enumerator(
262: currentAvailability));
263: if ((currentSchedule.getStartTime() != matchingRelationshipsSchedule
264: .getStartTime())
265: || (currentSchedule.getEndTime() != matchingRelationshipsSchedule
266: .getEndTime())) {
267: availSchedule
268: .removeAll(currentAvailability);
269: availSchedule.add(aggregateAvailability);
270: } else {
271: // No change required
272: return false;
273: }
274: } else {
275: availSchedule.add(aggregateAvailability);
276: change = true;
277: }
278: }
279: } else {
280: if (((aa.isUpdate() || aa.isRepeat()) && (!currentAvailability
281: .isEmpty()))
282: && (currentAvailability.size() == aa
283: .getSchedule().size())) {
284:
285: // Compare to existing entries - only change if required.
286: Schedule currentSchedule = ldmf
287: .newSchedule(new Enumerator(
288: currentAvailability));
289:
290: for (Iterator localIterator = new ArrayList(
291: currentSchedule).iterator(), aaIterator = new ArrayList(
292: aa.getSchedule()).iterator(); localIterator
293: .hasNext();) {
294: ScheduleElement localElement = (ScheduleElement) localIterator
295: .next();
296: ScheduleElement aaElement = (ScheduleElement) aaIterator
297: .next();
298:
299: // compare timespan
300: if ((localElement.getStartTime() != aaElement
301: .getStartTime())
302: || (localElement.getEndTime() != aaElement
303: .getEndTime())) {
304: availSchedule
305: .removeAll(currentAvailability);
306: change = true;
307: break;
308: }
309: }
310: } else {
311: change = true;
312: }
313:
314: if (change) {
315: //Don't iterate over schedule directly because Schedule doesn't
316: //support iterator().
317: for (Iterator iterator = new ArrayList(aa
318: .getSchedule()).iterator(); iterator
319: .hasNext();) {
320: ScheduleElement avail = (ScheduleElement) iterator
321: .next();
322: availSchedule.add(ldmf
323: .newAssignedAvailabilityElement(
324: assignee, avail.getStartTime(),
325: avail.getEndTime()));
326: }
327: }
328: }
329: } // end sync block
330:
331: return change;
332: }
333:
334: protected Collection convertToRelationships(AssetAssignment aa,
335: Asset transferring, Asset receiving) {
336: if ((aa.getSchedule() == null) || (!related(transferring))
337: || (!related(receiving))) {
338: return new ArrayList();
339: }
340:
341: ArrayList relationships = new ArrayList(aa.getSchedule().size());
342:
343: //Safe because aaSchedule should be an AssignedRelationshipSchedule -
344: // supports iterator(). (Assumption is that AssignedRelationshipSchedule
345: // is only used by LPs.)
346: for (Iterator iterator = aa.getSchedule().iterator(); iterator
347: .hasNext();) {
348: AssignedRelationshipElement aaRelationship = (AssignedRelationshipElement) iterator
349: .next();
350:
351: Relationship localRelationship = ldmf.newRelationship(
352: aaRelationship, transferring, receiving);
353: relationships.add(localRelationship);
354: }
355:
356: return relationships;
357: }
358:
359: protected Asset createLocalAsset(Asset aaAsset) {
360: // Clone to ensure that we don't end up with cross agent asset
361: // references
362: Asset asset = ldmf.cloneInstance(aaAsset);
363:
364: if (related(asset)) {
365: HasRelationships hasRelationships = (HasRelationships) asset;
366: hasRelationships.setLocal(false);
367: hasRelationships.setRelationshipSchedule(ldmf
368: .newRelationshipSchedule(hasRelationships));
369: }
370:
371: return asset;
372: }
373:
374: protected boolean localScheduleUpdateRequired(AssetAssignment aa,
375: Collection aaRelationships, Asset assetL, Asset assigneeL) {
376: if ((aa.getSchedule() == null) || (!related(assetL))
377: || (!related(assigneeL))) {
378: return false;
379: }
380:
381: if (!aa.isUpdate() && !aa.isRepeat()) {
382: return true;
383: }
384:
385: if (!aa.getAsset().equals(assetL)) {
386: throw new IllegalArgumentException(
387: "ReceiveAssetLP.localScheduleUpdateRequired()"
388: + " attempt to compare different Assets - "
389: + aa.getAsset() + " != " + assetL);
390: }
391:
392: if (!aa.getAssignee().equals(assigneeL)) {
393: throw new IllegalArgumentException(
394: "ReceiveAssetLP.localScheduleUpdateRequired()"
395: + " attempt to compare different Assets - "
396: + aa.getAssignee() + " != " + assigneeL);
397: }
398:
399: RelationshipSchedule assetLRelationshipSchedule = ((HasRelationships) assetL)
400: .getRelationshipSchedule();
401:
402: RelationshipSchedule assigneeLRelationshipSchedule = ((HasRelationships) assigneeL)
403: .getRelationshipSchedule();
404:
405: for (Iterator iterator = aaRelationships.iterator(); iterator
406: .hasNext();) {
407: final Relationship relationship = (Relationship) iterator
408: .next();
409:
410: Collection matchingAssetL = assetLRelationshipSchedule
411: .getMatchingRelationships(new UnaryPredicate() {
412: public boolean execute(Object obj) {
413: Relationship matchCandidate = (Relationship) obj;
414: return (relationship.equals(matchCandidate));
415: }
416: });
417:
418: Collection matchingAssigneeL = assigneeLRelationshipSchedule
419: .getMatchingRelationships(new UnaryPredicate() {
420: public boolean execute(Object obj) {
421: Relationship matchCandidate = (Relationship) obj;
422: return (relationship.equals(matchCandidate));
423: }
424: });
425:
426: if (matchingAssetL.isEmpty() || matchingAssigneeL.isEmpty()) {
427: return true;
428: }
429: }
430:
431: return false;
432: }
433:
434: protected boolean updatePG(Asset aaAsset, Asset localAsset) {
435: Vector transferredPGs = aaAsset.fetchAllProperties();
436: boolean update = false;
437:
438: for (Iterator pgIterator = transferredPGs.iterator(); pgIterator
439: .hasNext();) {
440: Object next = pgIterator.next();
441:
442: //Don't overwrite LocalPGs
443: if (!(next instanceof LocalPG)) {
444: if (next instanceof PropertyGroup) {
445: PropertyGroup transferredPG = (PropertyGroup) next;
446: PropertyGroup localPG = localAsset
447: .searchForPropertyGroup(transferredPG
448: .getPrimaryClass());
449: if ((localPG == null)
450: || (!localPG.equals(transferredPG))) {
451: if (logger.isDebugEnabled()) {
452: logger
453: .debug("ReceiveAssetLP: pgs not equal "
454: + " localPG = "
455: + localPG
456: + " transferredPG = "
457: + transferredPG);
458: }
459: localAsset.addOtherPropertyGroup(transferredPG);
460: update = true;
461: }
462: } else if (next instanceof PropertyGroupSchedule) {
463: PropertyGroupSchedule transferredPGSchedule = (PropertyGroupSchedule) next;
464: PropertyGroupSchedule localPGSchedule = localAsset
465: .searchForPropertyGroupSchedule(transferredPGSchedule
466: .getClass());
467: if ((localPGSchedule == null)
468: || (!localPGSchedule
469: .equals(transferredPGSchedule))) {
470: if (logger.isDebugEnabled()) {
471: logger
472: .debug("ReceiveAssetLP: pgschedules not equal "
473: + " localPGSchedule = "
474: + localPGSchedule
475: + " transferredPG = "
476: + transferredPGSchedule);
477: }
478: localAsset
479: .addOtherPropertyGroupSchedule(transferredPGSchedule);
480: update = true;
481: }
482: } else {
483: logger
484: .error("ReceiveAssetLP: unrecognized property type - "
485: + next
486: + " - on transferred asset "
487: + localAsset);
488: }
489: }
490: }
491:
492: return update;
493: }
494:
495: protected boolean updateRelationships(AssetAssignment aa,
496: Asset localAsset, Asset localAssignee) {
497: Collection aaRelationships = convertToRelationships(aa,
498: localAsset, localAssignee);
499:
500: boolean update = localScheduleUpdateRequired(aa,
501: aaRelationships, localAsset, localAssignee);
502:
503: //Only munge relationships pertinent to the transfer - requires that
504: //both receiving and transferring assets have relationship schedules
505: if (update) {
506: HasRelationships dob = ((HasRelationships) localAsset);
507: NewRelationshipSchedule rs_a = (NewRelationshipSchedule) dob
508: .getRelationshipSchedule();
509:
510: HasRelationships iob = ((HasRelationships) localAssignee);
511: NewRelationshipSchedule rs_assignee = (NewRelationshipSchedule) iob
512: .getRelationshipSchedule();
513:
514: if (aa.isUpdate() || aa.isRepeat()) {
515: removeExistingRelationships(aaRelationships, rs_a,
516: rs_assignee);
517: }
518:
519: // Add relationships from the AssetAssignment
520: rs_a.addAll(aaRelationships);
521: rs_assignee.addAll(aaRelationships);
522:
523: Collection changeReports = new ArrayList();
524: changeReports
525: .add(new RelationshipSchedule.RelationshipScheduleChangeReport());
526: rootplan.change(localAssignee, changeReports);
527:
528: if (logger.isDebugEnabled()) {
529: logger.debug("ReceiveAssetLP: updateRelationships for "
530: + localAssignee + " with AssetAssignment - "
531: + aa);
532: }
533: }
534:
535: return update;
536: }
537:
538: protected boolean updateSchedules(AssetAssignment aa,
539: Asset localAsset, Asset localAssignee) {
540: boolean updateRelationships = updateRelationships(aa,
541: localAsset, localAssignee);
542:
543: boolean updateAvailSchedule = fixAvailSchedule(aa, localAsset,
544: localAssignee);
545:
546: return updateAvailSchedule || updateRelationships;
547: }
548: }
|