001: /*
002: * <copyright>
003: *
004: * Copyright 2002-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.plugin;
028:
029: import java.util.Collection;
030: import java.util.Date;
031: import java.util.HashMap;
032: import java.util.Map;
033: import java.util.Iterator;
034:
035: import org.cougaar.core.blackboard.IncrementalSubscription;
036: import org.cougaar.core.plugin.ComponentPlugin;
037: import org.cougaar.core.service.DomainService;
038: import org.cougaar.core.service.LoggingService;
039:
040: import org.cougaar.planning.ldm.PlanningDomain;
041: import org.cougaar.planning.ldm.PlanningFactory;
042: import org.cougaar.planning.ldm.asset.Asset;
043: import org.cougaar.planning.ldm.plan.AspectType;
044: import org.cougaar.planning.ldm.plan.HasRelationships;
045: import org.cougaar.planning.ldm.plan.Preference;
046:
047: import org.cougaar.servicediscovery.SDDomain;
048: import org.cougaar.servicediscovery.SDFactory;
049: import org.cougaar.servicediscovery.description.ServiceContract;
050: import org.cougaar.servicediscovery.description.ServiceRequest;
051: import org.cougaar.servicediscovery.transaction.ProviderServiceContractRelay;
052: import org.cougaar.servicediscovery.transaction.ServiceContractRelay;
053:
054: import org.cougaar.util.TimeSpan;
055: import org.cougaar.util.UnaryPredicate;
056:
057: /**
058: * ServiceDiscovery Plugin at Provider that agrees to new incoming requests as is.
059: * It receives ServiceContractRelays, and creates new ServiceContracts that
060: * exactly match the request, sending that as the response back on the relay.
061: * The SD Relay will see the Contract, and fill in the necessary Relationship
062: * on the Entities, so that the relationship can be used, and Tasks allocated.
063: *<p>
064: * Extenders of this plugin might not agree to all requests, but for example might
065: * check a local capacity, or put time constraints on the contracts. More complex
066: * providers support contracts being revoked, for example.
067: *<p>
068: * This plugin is limited by: not looking at ProviderCapabilities or handling
069: * changes to them, and not handling changes to service contract requests.
070: */
071: public class AgreeableProviderPlugin extends ComponentPlugin {
072:
073: // Constants used to address Time preferences
074: private static final Integer START_TIME_KEY = new Integer(
075: AspectType.START_TIME);
076: private static final Integer END_TIME_KEY = new Integer(
077: AspectType.END_TIME);
078:
079: /** Subscription to the self Entity */
080: private IncrementalSubscription myLocalEntitySubscription;
081:
082: /** Subscription to service contract requests */
083: private IncrementalSubscription myServiceContractRelaySubscription;
084:
085: /** Name of the local agent */
086: private String myAgentName;
087:
088: // Services and factories
089: protected LoggingService myLoggingService;
090: protected DomainService myDomainService;
091: protected SDFactory mySDFactory;
092: protected PlanningFactory planningFactory;
093:
094: /**
095: * Subscription for ServiceContractRelays where the desired provider is me.
096: */
097: private UnaryPredicate myServiceContractRelayPred = new UnaryPredicate() {
098: public boolean execute(Object o) {
099: if (o instanceof ProviderServiceContractRelay) {
100: ServiceContractRelay relay = (ServiceContractRelay) o;
101: return (relay.getProviderName().equals(myAgentName));
102: } else {
103: return false;
104: }
105: }
106: };
107:
108: /** Subscription for the self Entity or Organization */
109: private UnaryPredicate myLocalEntityPred = new UnaryPredicate() {
110: public boolean execute(Object o) {
111: if (o instanceof Asset && o instanceof HasRelationships) {
112: HasRelationships entity = (HasRelationships) o;
113: if (entity.isLocal()) {
114: return true;
115: }
116: }
117: return false;
118: }
119: };
120:
121: /**
122: * Used by the binding utility through reflection to set my DomainService
123: */
124: public void setDomainService(DomainService domainService) {
125: myDomainService = domainService;
126: }
127:
128: /**
129: * Get a pointer to the DomainService, for creating factories.
130: */
131: public DomainService getDomainService() {
132: return myDomainService;
133: }
134:
135: /**
136: * Load less critical services, and get pointers to factoreis.
137: */
138: public void load() {
139: super .load();
140:
141: mySDFactory = (SDFactory) getDomainService().getFactory(
142: SDDomain.SD_NAME);
143: planningFactory = (PlanningFactory) getDomainService()
144: .getFactory(PlanningDomain.PLANNING_NAME);
145:
146: // Done with Domain service, so release it
147: getServiceBroker().releaseService(this , DomainService.class,
148: getDomainService());
149:
150: myLoggingService = (LoggingService) getServiceBroker()
151: .getService(this , LoggingService.class, null);
152:
153: if (myLoggingService == null)
154: myLoggingService = LoggingService.NULL;
155:
156: myAgentName = getAgentIdentifier().toString();
157: }
158:
159: /**
160: * Every load method should have an unload; Here we unload the LoggingService.
161: */
162: public void unload() {
163: if (myLoggingService != LoggingService.NULL)
164: getServiceBroker().releaseService(this ,
165: LoggingService.class, myLoggingService);
166:
167: super .unload();
168: }
169:
170: /**
171: * Create subscriptions to ServiceContractRelays and the self Asset.
172: */
173: protected void setupSubscriptions() {
174: myServiceContractRelaySubscription = (IncrementalSubscription) getBlackboardService()
175: .subscribe(myServiceContractRelayPred);
176: myLocalEntitySubscription = (IncrementalSubscription) getBlackboardService()
177: .subscribe(myLocalEntityPred);
178: }
179:
180: /**
181: * For each new ServiceContractRelay, reply with a Contract exactly matching what they requested.
182: */
183: public void execute() {
184: if (myServiceContractRelaySubscription.hasChanged()) {
185:
186: // For each new service contract request, handle it
187: Collection addedRelays = myServiceContractRelaySubscription
188: .getAddedCollection();
189: for (Iterator adds = addedRelays.iterator(); adds.hasNext();) {
190: ProviderServiceContractRelay relay = (ProviderServiceContractRelay) adds
191: .next();
192:
193: if (myLoggingService.isDebugEnabled()) {
194: myLoggingService
195: .debug("execute() new ServiceContractRelay - "
196: + relay);
197: }
198: handleServiceContractRelay(relay);
199: }
200:
201: // We do not handle changed or removed service contract requests....
202: if (myLoggingService.isDebugEnabled()) {
203:
204: Collection changedRelays = myServiceContractRelaySubscription
205: .getChangedCollection();
206: for (Iterator changes = changedRelays.iterator(); changes
207: .hasNext();) {
208: ProviderServiceContractRelay relay = (ProviderServiceContractRelay) changes
209: .next();
210:
211: myLoggingService
212: .debug("execute() ignoring modifed ServiceContractRelay - "
213: + relay);
214: }
215:
216: Collection removedRelays = myServiceContractRelaySubscription
217: .getRemovedCollection();
218: for (Iterator removes = removedRelays.iterator(); removes
219: .hasNext();) {
220: ProviderServiceContractRelay relay = (ProviderServiceContractRelay) removes
221: .next();
222:
223: myLoggingService
224: .debug("execute: ignoring removed ServiceContractRelay - "
225: + relay);
226: }
227: } // end of ifDebug
228: } // end of block for changed ServiceContractRelays
229: } // end of execute()
230:
231: /**
232: * Create a ServiceContract that matches the request, add it to the Relay, and publishChange the Relay.
233: * This is how the plugin responds to the customer. In this case, the response is always YES to whatever
234: * they asked. Other users might, for example, check the capacity of this provider in some way. Or
235: * confirm that the local self Entity has the requested Role.
236: *
237: * @param relay the service request relay to which we will respond
238: */
239: protected void handleServiceContractRelay(
240: ProviderServiceContractRelay relay) {
241: // Pull out the request
242: ServiceRequest serviceRequest = relay.getServiceRequest();
243: ServiceContract serviceContract = relay.getServiceContract();
244:
245: // Once a relay has a contract, it is done.
246: if (serviceContract != null) {
247: if (myLoggingService.isDebugEnabled())
248: myLoggingService
249: .debug("handleServiceContractRelay() relay = "
250: + relay
251: + " "
252: + relay.getUID()
253: + " already has a contract. Not handling changed/removed requests.");
254:
255: return;
256: }
257:
258: // Now handle the preferences on the Contract request we just received......
259:
260: // Make a copy of the preferences, to avoid modifying them in place.
261: // Note that in our example, these are basically none.
262: Map contractPreferences = copyPreferences(serviceRequest
263: .getServicePreferences());
264:
265: // Get a TimeSpan to represent when they wanted the service. In the pizza app, this is FOREVER.
266: TimeSpan requestedTimeSpan = SDFactory
267: .getTimeSpanFromPreferences(serviceRequest
268: .getServicePreferences());
269:
270: if (myLoggingService.isDebugEnabled()) {
271: myLoggingService
272: .debug("handleServiceContractRelay() relay = "
273: + relay
274: + " "
275: + relay.getUID()
276: + " requestedTimeSpan = "
277: + new Date(requestedTimeSpan.getStartTime())
278: + " "
279: + new Date(requestedTimeSpan.getEndTime()));
280: }
281:
282: // If the request was poorly formed, the timespan might be null....
283: if (requestedTimeSpan == null) {
284: myLoggingService
285: .error("handleServiceContractRelay() unable to satisfy service request - "
286: + relay.getServiceRequest()
287: + " - does not have start and/or end time preferences.");
288:
289: // Remove start/end preferences since provider can't meet request.
290: contractPreferences.remove(START_TIME_KEY);
291: contractPreferences.remove(END_TIME_KEY);
292: }
293:
294: // OK, we are ready to respond now...
295:
296: // Create a new service contract between the local Entity, to provide the requested role,
297: // with the preferences as requested on the Relay.
298: serviceContract = mySDFactory.newServiceContract(
299: getLocalEntity(), serviceRequest.getServiceRole(),
300: contractPreferences.values());
301: relay.setServiceContract(serviceContract);
302:
303: if (myLoggingService.isInfoEnabled()) {
304: myLoggingService
305: .info("added new ServiceContract on a relay from "
306: + relay.getUID() + " for the role "
307: + serviceRequest.getServiceRole());
308: }
309:
310: // This is where the Provider responds to the client. The SD LP will see the Relay,
311: // and construct the relationships at both agents -- we have a new relationship!
312: getBlackboardService().publishChange(relay);
313:
314: } // end of handleServiceContractRelay
315:
316: /**
317: * Get the local (self) Entity; the first Asset on the Entity subscription.
318: * @return the local (self) Asset, if any on our subscription
319: */
320: protected Asset getLocalEntity() {
321: for (Iterator iterator = myLocalEntitySubscription.iterator(); iterator
322: .hasNext();) {
323: return (Asset) iterator.next();
324: }
325:
326: return null;
327: }
328:
329: /**
330: * Deep copy a set of preferences, to avoid any modification errors.
331: * @param preferences to copy
332: * @return a Map of the exact same preferences.
333: */
334: private Map copyPreferences(Collection preferences) {
335: Map preferenceMap = new HashMap(preferences.size());
336:
337: for (Iterator iterator = preferences.iterator(); iterator
338: .hasNext();) {
339: Preference original = (Preference) iterator.next();
340:
341: Preference copy = planningFactory.newPreference(original
342: .getAspectType(), original.getScoringFunction(),
343: original.getWeight());
344: preferenceMap.put(new Integer(copy.getAspectType()), copy);
345: }
346:
347: return preferenceMap;
348: }
349: } // end of AgreeableProviderPlugin
|