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.servicediscovery.lp;
028:
029: import java.util.ArrayList;
030: import java.util.Collection;
031: import java.util.Collections;
032: import java.util.Iterator;
033: import java.util.Vector;
034:
035: import org.cougaar.core.blackboard.EnvelopeTuple;
036: import org.cougaar.core.domain.EnvelopeLogicProvider;
037: import org.cougaar.core.domain.LogicProvider;
038: import org.cougaar.core.domain.RootPlan;
039: import org.cougaar.core.mts.MessageAddress;
040: import org.cougaar.planning.ldm.LogPlan;
041: import org.cougaar.planning.ldm.PlanningFactory;
042: import org.cougaar.planning.ldm.asset.Asset;
043: import org.cougaar.planning.ldm.asset.LocalPG;
044: import org.cougaar.planning.ldm.asset.PropertyGroup;
045: import org.cougaar.planning.ldm.asset.PropertyGroupSchedule;
046: import org.cougaar.planning.ldm.plan.HasRelationships;
047: import org.cougaar.planning.ldm.plan.Preference;
048: import org.cougaar.planning.ldm.plan.RelationshipSchedule;
049: import org.cougaar.servicediscovery.SDFactory;
050: import org.cougaar.servicediscovery.description.ServiceContractRelationship;
051: import org.cougaar.servicediscovery.transaction.ServiceContractRelay;
052: import org.cougaar.util.UnaryPredicate;
053: import org.cougaar.util.log.Logger;
054: import org.cougaar.util.log.Logging;
055:
056: /** ServiceContractLP is a "LogPlan Logic Provider":
057: *
058: * it provides the logic to keep Org Asset RelationshipSchedules
059: * in synch with their ServiceContracts.
060: * Relies on RelayLP to handle restart reconciliation.
061: **/
062:
063: public class ServiceContractLP implements LogicProvider,
064: EnvelopeLogicProvider {
065: private static Logger logger = Logging
066: .getLogger(ServiceContractLP.class);
067: private final LogPlan logplan;
068: private final RootPlan rootplan;
069: private final MessageAddress self;
070: private final PlanningFactory ldmf;
071:
072: public ServiceContractLP(LogPlan logplan, RootPlan rootplan,
073: MessageAddress self, PlanningFactory ldmf) {
074: this .logplan = logplan;
075: this .rootplan = rootplan;
076: this .self = self;
077: this .ldmf = ldmf;
078: }
079:
080: public void init() {
081: }
082:
083: /**
084: * @param o the Envelopetuple,
085: * where tuple.object
086: * == PlanElement with an Allocation to an cluster ADDED to LogPlan
087: *
088: * If the test returned true i.e. it was an ServiceContractRelay,
089: * update local org assets and relationship schedules
090: **/
091: public void execute(EnvelopeTuple o, Collection changes) {
092: Object obj = o.getObject();
093: if (obj instanceof ServiceContractRelay) {
094: ServiceContractRelay relay = (ServiceContractRelay) obj;
095:
096: // Is this the providing agent?
097: Asset provider = relay.getProvider();
098: if ((provider != null)
099: && (provider.getClusterPG().getMessageAddress()
100: .equals(self))) {
101: localProviderUpdate(o, relay);
102: }
103:
104: // If this the client agent
105: Asset client = relay.getClient();
106: if ((client != null)
107: && (client.getClusterPG().getMessageAddress()
108: .equals(self))
109: && (relay.getServiceContract() != null)) {
110: if (logger.isDebugEnabled()) {
111: logger.debug(self + ": Received " + relay);
112: }
113: localClientUpdate(o, relay);
114: }
115: }
116: }
117:
118: // Existing Relay logic should suffice.
119:
120: private void localProviderUpdate(EnvelopeTuple tuple,
121: ServiceContractRelay relay) {
122: Asset provider = logplan.findAsset(relay.getProvider());
123: if (provider == null) {
124: logger.error(self
125: + ": unable to process ServiceContractRelay - "
126: + relay.getUID() + " provider - "
127: + relay.getProvider()
128: + " - is not local to this agent.");
129: return;
130: } else if (provider == relay.getProvider()) {
131: logger.error(self
132: + ": Assets in ServiceContractRelay must be "
133: + " clones. ServiceContractRelay - "
134: + relay.getUID()
135: + " - references assets in the log plan.");
136: return;
137: }
138:
139: Asset client = relay.getClient();
140: Asset localClient = logplan.findAsset(client);
141:
142: if (localClient == null) {
143: if (tuple.isRemove()) {
144: logger
145: .error(self
146: + ": unable to process remove of ServiceContractRelay - "
147: + relay.getUID()
148: + " client does not already exist on this agent.");
149: return;
150: }
151:
152: client = ldmf.cloneInstance(client);
153: if (related(client)) {
154: ((HasRelationships) client)
155: .setRelationshipSchedule(ldmf
156: .newRelationshipSchedule((HasRelationships) client));
157: }
158: } else {
159: client = localClient;
160:
161: if (localClient == relay.getClient()) {
162: logger.error(self
163: + ": Assets in ServiceContractRelay must be "
164: + " clones. ServiceContractRelay - "
165: + relay.getUID()
166: + " - references assets in the log plan.");
167: return;
168: }
169: }
170:
171: boolean updateRelationships = false;
172:
173: if (related(provider) && related(client)) {
174: if (logger.isDebugEnabled()) {
175: logger.debug(self + ": localProviderUpdate() relay = "
176: + relay + " tuple.isAdd() = " + tuple.isAdd()
177: + " tuple.isChange() = " + tuple.isChange()
178: + " tuple.isRemove() = " + tuple.isRemove());
179: }
180: updateRelationships = tuple.isAdd() || tuple.isRemove();
181:
182: Collection localRelationships = (tuple.isRemove()) ? Collections.EMPTY_LIST
183: : convertToLocalRelationships(relay,
184: (HasRelationships) provider,
185: (HasRelationships) client);
186:
187: if (tuple.isChange()) {
188: updateRelationships = (localScheduleUpdateRequired(
189: localRelationships, (HasRelationships) provider) || localScheduleUpdateRequired(
190: localRelationships, (HasRelationships) client));
191: }
192:
193: if (logger.isDebugEnabled()) {
194: logger
195: .debug(self
196: + ": localProviderUpdate() updateRelationships = "
197: + updateRelationships + " for " + relay
198: + " uid = " + relay.getUID());
199: }
200:
201: if (updateRelationships) {
202: removeExistingRelationships(relay,
203: (HasRelationships) provider,
204: (HasRelationships) client);
205:
206: addRelationships(localRelationships,
207: (HasRelationships) provider,
208: (HasRelationships) client);
209: }
210: }
211:
212: if (updateRelationships) {
213: Collection changes = new ArrayList();
214: changes
215: .add(new RelationshipSchedule.RelationshipScheduleChangeReport());
216: if (logger.isInfoEnabled()) {
217: logger.info(self
218: + ": localProviderUpdate() changed provider :"
219: + provider);
220: }
221: rootplan.change(provider, changes); // change this to root plan
222: }
223:
224: if (localClient == null) {
225: rootplan.add(client);
226: } else if (updateRelationships) {
227: Collection changes = new ArrayList();
228: changes
229: .add(new RelationshipSchedule.RelationshipScheduleChangeReport());
230: if (logger.isInfoEnabled()) {
231: logger.info(self
232: + ": localProviderUpdate() changed client :"
233: + client);
234: }
235: rootplan.change(client, changes);
236: }
237:
238: // Clear client and provider relationship, role and available schedules to
239: // ensure that there are no references to other organizations.
240: clearSchedule(relay.getClient());
241: clearSchedule(relay.getProvider());
242: }
243:
244: private void localClientUpdate(EnvelopeTuple tuple,
245: ServiceContractRelay relay) {
246: if (logger.isDebugEnabled()) {
247: logger.debug(self + ": localClientUpdate() relay = "
248: + relay + " tuple.isAdd() = " + tuple.isAdd()
249: + " tuple.isChange() = " + tuple.isChange()
250: + " tuple.isRemove() = " + tuple.isRemove());
251: }
252:
253: Asset client = logplan.findAsset(relay.getClient()); // local client instance
254:
255: if (client == null) {
256: logger.error(self
257: + ": unable to process ServiceContractRelay - "
258: + relay.getUID() + " client - " + relay.getClient()
259: + " - is not local to this agent.");
260: return;
261: } else if (client == relay.getClient()) {
262: logger.error(self
263: + ": Assets in ServiceContractRelay must be "
264: + " clones. ServiceContractRelay - "
265: + relay.getUID()
266: + " - references assets in the log plan.");
267: return;
268: }
269:
270: // figure out the asset being transferred
271: Asset provider = logplan.findAsset(relay.getProvider());
272: boolean newProvider = (provider == null);
273:
274: if (newProvider) {
275: if (tuple.isRemove()) {
276: logger
277: .error(self
278: + ": unable to process remove of ServiceContractRelay - "
279: + relay.getUID()
280: + " provider does not already exist on this agent.");
281: return;
282: }
283:
284: // Clone to ensure that we don't end up with cross cluster asset
285: // references
286: provider = ldmf.cloneInstance(relay.getProvider());
287: if (related(provider)) {
288: HasRelationships hasRelationships = (HasRelationships) provider;
289: hasRelationships.setLocal(false);
290: hasRelationships.setRelationshipSchedule(ldmf
291: .newRelationshipSchedule(hasRelationships));
292: }
293: } else {
294: if (provider == relay.getProvider()) {
295: logger.error(self
296: + ": Assets in ServiceContractRelay must be "
297: + " clones. ServiceContractRelay - "
298: + relay.getUID()
299: + " - references assets in the log plan.");
300: return;
301: }
302: }
303:
304: boolean updateRelationships = false;
305:
306: //Only munge relationships pertinent to the transfer - requires that
307: //both receiving and transferring assets have relationship schedules
308: if (related(provider) && related(client)) {
309: Collection localRelationships = (tuple.isRemove()) ? Collections.EMPTY_LIST
310: : convertToLocalRelationships(relay,
311: (HasRelationships) provider,
312: (HasRelationships) client);
313: updateRelationships = (tuple.isRemove())
314: || (localScheduleUpdateRequired(localRelationships,
315: (HasRelationships) provider) || localScheduleUpdateRequired(
316: localRelationships,
317: (HasRelationships) client));
318:
319: if (updateRelationships) {
320: removeExistingRelationships(relay,
321: (HasRelationships) provider,
322: (HasRelationships) client);
323:
324: if (!tuple.isRemove()) {
325: addRelationships(localRelationships,
326: (HasRelationships) provider,
327: (HasRelationships) client);
328: }
329:
330: if (logger.isInfoEnabled()) {
331: logger.info(self
332: + ": localClientUpdate() changed client: "
333: + client);
334: }
335:
336: Collection changeReports = new ArrayList();
337: changeReports
338: .add(new RelationshipSchedule.RelationshipScheduleChangeReport());
339: rootplan.change(client, changeReports);
340: }
341: }
342:
343: boolean updatePGs = false;
344:
345: if (!newProvider) {
346: // If we already had a matching asset - update with property groups
347: // from the asset transfer.
348: Vector transferredPGs = relay.getProvider()
349: .fetchAllProperties();
350:
351: for (Iterator pgIterator = transferredPGs.iterator(); pgIterator
352: .hasNext();) {
353: Object next = pgIterator.next();
354:
355: //Don't propagate LocalPGs
356: if (!(next instanceof LocalPG)) {
357: if (next instanceof PropertyGroup) {
358: PropertyGroup transferredPG = (PropertyGroup) next;
359: PropertyGroup localPG = provider
360: .searchForPropertyGroup(transferredPG
361: .getPrimaryClass());
362: if ((localPG == null)
363: || (!localPG.equals(transferredPG))) {
364: if (logger.isDebugEnabled()) {
365: logger
366: .debug(self
367: + ": localProviderUpdate() pgs not equal "
368: + " localPG = "
369: + localPG
370: + " transferredPG = "
371: + transferredPG);
372: }
373: provider
374: .addOtherPropertyGroup((PropertyGroup) next);
375: updatePGs = true;
376: } else if (next instanceof PropertyGroupSchedule) {
377: PropertyGroupSchedule transferredPGSchedule = (PropertyGroupSchedule) next;
378: PropertyGroupSchedule localPGSchedule = provider
379: .searchForPropertyGroupSchedule(transferredPGSchedule
380: .getClass());
381: if ((localPGSchedule == null)
382: || (!localPGSchedule
383: .equals(transferredPGSchedule))) {
384: if (logger.isDebugEnabled()) {
385: logger
386: .debug(self
387: + ": localProviderUpdate() pgschedules not equal "
388: + " localPGSchedule = "
389: + localPGSchedule
390: + " transferredPG = "
391: + transferredPGSchedule);
392: }
393: provider
394: .addOtherPropertyGroupSchedule((PropertyGroupSchedule) next);
395: updatePGs = true;
396: } else {
397: logger
398: .error(self
399: + ": unrecognized property type - "
400: + next
401: + " - on provider "
402: + provider);
403: }
404: }
405: }
406: }
407: }
408: }
409:
410: if (logger.isDebugEnabled()) {
411: logger.debug(self
412: + ": localClientUpdate() updateRelationships = "
413: + updateRelationships + "updatePGs = " + updatePGs);
414: }
415:
416: // publish the added or changed provider
417: if (newProvider) { // add it if it wasn't already there
418: rootplan.add(provider);
419: } else {
420: if (updateRelationships) {
421: Collection changeReports = new ArrayList();
422: changeReports
423: .add(new RelationshipSchedule.RelationshipScheduleChangeReport());
424: if (logger.isInfoEnabled()) {
425: logger
426: .info(self
427: + ": localClientUpdate() changed provider: "
428: + provider);
429: }
430: rootplan.change(provider, changeReports);
431: } else if (updatePGs) {
432: if (logger.isInfoEnabled())
433: logger
434: .info(self
435: + ": localClientUpdate() changed provider: "
436: + provider);
437: rootplan.change(provider, null);
438: }
439: }
440: }
441:
442: private final static boolean related(Asset a) {
443: return (a instanceof HasRelationships);
444: }
445:
446: private boolean localScheduleUpdateRequired(
447: Collection localRelationships, HasRelationships localAsset) {
448:
449: Collection localMatches = new ArrayList();
450: RelationshipSchedule localRelationshipSchedule = localAsset
451: .getRelationshipSchedule();
452:
453: // Find all existing elationships for the same service contract
454: for (Iterator iterator = localRelationships.iterator(); iterator
455: .hasNext();) {
456: final ServiceContractRelationship relationship = (ServiceContractRelationship) iterator
457: .next();
458:
459: Collection matching = localRelationshipSchedule
460: .getMatchingRelationships(new UnaryPredicate() {
461: public boolean execute(Object obj) {
462: if (obj instanceof ServiceContractRelationship) {
463: return ((ServiceContractRelationship) obj)
464: .getServiceContractUID()
465: .equals(
466: relationship
467: .getServiceContractUID());
468: } else {
469: return false;
470: }
471: }
472: });
473:
474: localMatches.addAll(matching);
475: }
476:
477: if (localMatches.size() != localRelationships.size()) {
478: return true;
479: }
480:
481: // Compare entries
482: for (Iterator iterator = localRelationships.iterator(); iterator
483: .hasNext();) {
484: ServiceContractRelationship relationship = (ServiceContractRelationship) iterator
485: .next();
486: boolean found = false;
487:
488: for (Iterator existingIterator = localMatches.iterator(); existingIterator
489: .hasNext();) {
490: ServiceContractRelationship existing = (ServiceContractRelationship) existingIterator
491: .next();
492:
493: if (relationship.equals(existing)) {
494: found = true;
495: localMatches.remove(existing);
496: break;
497: }
498: }
499:
500: if (!found) {
501: return true;
502: }
503: }
504:
505: return false;
506: }
507:
508: private void addRelationships(Collection localRelationships,
509: HasRelationships provider, HasRelationships client) {
510:
511: RelationshipSchedule providerSchedule = provider
512: .getRelationshipSchedule();
513: providerSchedule.addAll(localRelationships);
514:
515: RelationshipSchedule clientSchedule = client
516: .getRelationshipSchedule();
517: clientSchedule.addAll(localRelationships);
518: }
519:
520: private void removeExistingRelationships(
521: final ServiceContractRelay relay,
522: HasRelationships provider, HasRelationships client) {
523:
524: RelationshipSchedule clientSchedule = client
525: .getRelationshipSchedule();
526: RelationshipSchedule providerSchedule = provider
527: .getRelationshipSchedule();
528:
529: Collection remove = providerSchedule
530: .getMatchingRelationships(new UnaryPredicate() {
531: public boolean execute(Object obj) {
532: if (obj instanceof ServiceContractRelationship) {
533: return ((ServiceContractRelationship) obj)
534: .getServiceContractUID().equals(
535: relay.getUID());
536: } else {
537: return false;
538: }
539: }
540: });
541:
542: providerSchedule.removeAll(remove);
543:
544: remove = clientSchedule
545: .getMatchingRelationships(new UnaryPredicate() {
546: public boolean execute(Object obj) {
547: if (obj instanceof ServiceContractRelationship) {
548: return ((ServiceContractRelationship) obj)
549: .getServiceContractUID().equals(
550: relay.getUID());
551: } else {
552: return false;
553: }
554: }
555: });
556:
557: clientSchedule.removeAll(remove);
558: }
559:
560: protected Collection convertToLocalRelationships(
561: ServiceContractRelay relay, HasRelationships localProvider,
562: HasRelationships client) {
563: Collection relationships = new ArrayList();
564:
565: if (relay.getServiceContract().isRevoked()) {
566: return relationships;
567: }
568:
569: ServiceContractRelationship relationship = SDFactory
570: .newServiceContractRelationship(relay, localProvider,
571: client);
572: if (relationship != null) {
573: relationships.add(relationship);
574: }
575: return relationships;
576: }
577:
578: // Clear relationship, role and availble schedules to ensure that there
579: // are no dangling references to other organizations.
580: private void clearSchedule(Asset asset) {
581: if (related(asset)) {
582: ((HasRelationships) asset).setRelationshipSchedule(null);
583: }
584:
585: if (asset.getRoleSchedule() != null) {
586: asset.getRoleSchedule().clear();
587:
588: if (asset.getRoleSchedule().getAvailableSchedule() != null) {
589: asset.getRoleSchedule().getAvailableSchedule().clear();
590: }
591: }
592: }
593: }
|