001: /*
002: * <copyright>
003: *
004: * Copyright 2001-2004 Mobile Intelligence Corp
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.community;
028:
029: import org.cougaar.core.service.community.CommunityChangeEvent;
030: import org.cougaar.core.service.community.CommunityChangeListener;
031: import org.cougaar.core.service.community.CommunityResponse;
032: import org.cougaar.core.service.community.CommunityResponseListener;
033: import org.cougaar.core.service.community.CommunityService;
034: import org.cougaar.core.service.community.Community;
035:
036: import org.cougaar.community.CommunityResponseImpl;
037:
038: import org.cougaar.community.requests.AddChangeListener;
039: import org.cougaar.community.requests.CommunityRequest;
040: import org.cougaar.community.requests.GetCommunity;
041: import org.cougaar.community.requests.JoinCommunity;
042: import org.cougaar.community.requests.LeaveCommunity;
043: import org.cougaar.community.requests.ListParentCommunities;
044: import org.cougaar.community.requests.ModifyAttributes;
045: import org.cougaar.community.requests.RemoveChangeListener;
046: import org.cougaar.community.requests.SearchCommunity;
047:
048: import org.cougaar.community.init.CommunityInitializerService;
049: import org.cougaar.community.init.CommunityConfig;
050: import org.cougaar.community.init.EntityConfig;
051:
052: import java.util.Collection;
053: import java.util.Map;
054: import java.util.HashMap;
055: import java.util.Set;
056: import java.util.Iterator;
057: import java.util.HashSet;
058: import java.util.Vector;
059:
060: import org.cougaar.core.blackboard.IncrementalSubscription;
061: import org.cougaar.core.component.ServiceBroker;
062: import org.cougaar.core.plugin.ComponentPlugin;
063: import org.cougaar.core.service.LoggingService;
064: import org.cougaar.util.UnaryPredicate;
065:
066: import javax.naming.NamingEnumeration;
067: import javax.naming.NamingException;
068: import javax.naming.directory.Attributes;
069: import javax.naming.directory.Attribute;
070:
071: /**
072: * Plugin used by an agent to automatically create/join its initial
073: * communities during startup. The communities to create/join are defined
074: * in the communities.xml file found on the Cougaar config path. This
075: * plugin is listens for CommunityRequests published to blackboard and
076: * invokes applicable CommunityService methods.
077: */
078: public class CommunityPlugin extends ComponentPlugin {
079:
080: // Services used
081: protected LoggingService logger;
082: protected CommunityService communityService;
083:
084: protected String this Agent;
085:
086: /**
087: * Get required services and join startup communities.
088: */
089: protected void setupSubscriptions() {
090: logger = (LoggingService) getBindingSite().getServiceBroker()
091: .getService(this , LoggingService.class, null);
092: logger = org.cougaar.core.logging.LoggingServiceWithPrefix.add(
093: logger, agentId + ": ");
094: communityService = (CommunityService) getBindingSite()
095: .getServiceBroker().getService(this ,
096: CommunityService.class, null);
097: this Agent = agentId.toString();
098:
099: if (!blackboard.didRehydrate()) {
100: // Initial start
101: joinStartupCommunities(getCommunityConfigs());
102: }
103:
104: // Subscribe to CommunityRequests
105: communityRequestSub = (IncrementalSubscription) blackboard
106: .subscribe(communityRequestPredicate);
107:
108: }
109:
110: public void execute() {
111:
112: // Get community requests published by local components.
113: Collection communityRequests = communityRequestSub
114: .getAddedCollection();
115: for (Iterator it = communityRequests.iterator(); it.hasNext();) {
116: CommunityRequest cr = (CommunityRequest) it.next();
117: processCommunityRequest(cr);
118: }
119: }
120:
121: /**
122: * Get CommunityConfigs using CommunityInitializerService.
123: * @return Collection
124: */
125: private Collection getCommunityConfigs() {
126: Collection communityConfigs = null;
127: ServiceBroker sb = getBindingSite().getServiceBroker();
128: CommunityInitializerService cis = (CommunityInitializerService) sb
129: .getService(this , CommunityInitializerService.class,
130: null);
131: try {
132: communityConfigs = cis.getCommunityDescriptions(null);
133: } catch (Exception e) {
134: if (logger.isWarnEnabled()) {
135: logger
136: .warn("Unable to obtain community information for agent "
137: + this Agent);
138: }
139: } finally {
140: sb.releaseService(this , CommunityInitializerService.class,
141: cis);
142: }
143: return communityConfigs;
144: }
145:
146: /**
147: * Builds a collection of CommunityConfig objects for all parent communities.
148: * The community config information is retrieved from the
149: * CommunityInitializerService. The source of the community config data is
150: * typically an XML file (named "communities.xml" in config path).
151: * @param communityConfigs CommunityConfigs associated with society
152: * @return Collection of CommunityConfig objects defining parent communities
153: */
154: private Collection findMyStartupCommunities(
155: Collection communityConfigs) {
156: Collection startupCommunities = new Vector();
157: for (Iterator it = communityConfigs.iterator(); it.hasNext();) {
158: CommunityConfig cc = (CommunityConfig) it.next();
159: EntityConfig ec = cc.getEntity(this Agent);
160: if (ec != null) {
161: Attributes attrs = ec.getAttributes();
162: Attribute roles = attrs.get("Role");
163: if (roles != null && roles.contains("Member")) {
164: startupCommunities.add(cc);
165: }
166: }
167: }
168: return startupCommunities;
169: }
170:
171: /**
172: * Join (and optionally create) all parent communities. If the parent
173: * community does not exist this agent may attempt to create it
174: * based on community attributes set in community configuration.
175: * The agent will attempt to create the community if the
176: * community attribute "CommunityManager=" contains its name.
177: * If this attribute is not set the agent will also attempt to become the
178: * manager if the entity attribute "CanBeManager=" is undefined or set to true.
179: * @param communityConfigs List of all CommunityConfig objects associated
180: * with startup communities.
181: */
182: private void joinStartupCommunities(
183: final Collection communityConfigs) {
184: Collection myStartupCommunities = findMyStartupCommunities(communityConfigs);
185: for (Iterator it = myStartupCommunities.iterator(); it
186: .hasNext();) {
187: final CommunityConfig cc = (CommunityConfig) it.next();
188: final String communityName = cc.getName();
189: final EntityConfig ec = cc.getEntity(this Agent);
190: Set designatedManagers = getDesignatedManagers(cc);
191: if (logger.isDetailEnabled()) {
192: logger.detail("joinStartupCommunity :" + " agent="
193: + this Agent + " community=" + communityName
194: + " designatedManagers=" + designatedManagers
195: + " canBeManager=" + canBeManager(ec));
196: }
197:
198: // Submit join request to add self to community
199: final boolean createIfNotFound = (designatedManagers
200: .contains(ec.getName()) || (designatedManagers
201: .isEmpty() && canBeManager(ec)));
202: CommunityResponseListener crl = new CommunityResponseListener() {
203: public void getResponse(CommunityResponse resp) {
204: if (logger.isDebugEnabled()) {
205: logger
206: .debug("joinCommunity:" + " community="
207: + communityName + " agent="
208: + agentId + " result="
209: + resp.getStatusAsString());
210: }
211: switch (resp.getStatus()) {
212: case CommunityResponse.SUCCESS:
213: Community community = (Community) resp
214: .getContent();
215: if (hasAttribute(community.getAttributes(),
216: "CommunityManager", this Agent)) {
217: Map parentCommunities = listParents(
218: communityConfigs, communityName);
219: if (logger.isDebugEnabled()) {
220: logger.debug("Managing community "
221: + communityName + " parents="
222: + parentCommunities);
223: }
224: if (parentCommunities != null) {
225: joinParentCommunities(
226: parentCommunities,
227: communityName);
228: }
229: }
230: break;
231: }
232: }
233: };
234: communityService.joinCommunity(communityName, ec.getName(),
235: CommunityService.AGENT, ec.getAttributes(),
236: createIfNotFound, (createIfNotFound ? cc
237: .getAttributes() : null), crl);
238: }
239: }
240:
241: /**
242: * Adds a nested community to its parent community.
243: * @param parentCommunities Collection
244: * @param nestedCommunity String
245: */
246: protected void joinParentCommunities(Map parentCommunities,
247: final String nestedCommunity) {
248: for (Iterator it = parentCommunities.entrySet().iterator(); it
249: .hasNext();) {
250: Map.Entry me = (Map.Entry) it.next();
251: final String communityName = (String) me.getKey();
252: final Attributes memberAttrs = (Attributes) me.getValue();
253: communityService.joinCommunity(communityName,
254: nestedCommunity, CommunityService.COMMUNITY,
255: memberAttrs, false, null,
256: new CommunityResponseListener() {
257: public void getResponse(CommunityResponse resp) {
258: if (logger.isDebugEnabled()) {
259: logger.debug("joinParentCommunity:"
260: + " community=" + communityName
261: + " nestedCommunity="
262: + nestedCommunity + " result="
263: + resp.getStatusAsString());
264: }
265: }
266: });
267: }
268: }
269:
270: /**
271: * Create all communities that contain the specified community as
272: * a member.
273: * @param communityConfigs Collection
274: * @param communityName String
275: * @return Map containing parent community name and member attributes
276: */
277: private Map listParents(Collection communityConfigs,
278: String communityName) {
279: Map parents = new HashMap();
280: for (Iterator it = communityConfigs.iterator(); it.hasNext();) {
281: CommunityConfig cc = (CommunityConfig) it.next();
282: EntityConfig ec = cc.getEntity(communityName);
283: if (ec != null) {
284: Attributes attrs = ec.getAttributes();
285: Attribute type = attrs.get("EntityType");
286: if (type != null && type.contains("Community")) {
287: parents.put(cc.getName(), attrs);
288: }
289: }
290: }
291: return parents;
292: }
293:
294: /**
295: * Retrieves designated community manager(s) from community configuration.
296: * @param cc Config data associated with parent community
297: * @return Set of entity names
298: */
299: private Set getDesignatedManagers(CommunityConfig cc) {
300: Set managers = new HashSet();
301: try {
302: Attributes attrs = cc.getAttributes(); // get community attributes
303: if (attrs != null) {
304: Attribute attr = attrs.get("CommunityManager");
305: if (attr != null && attr.size() > 0
306: && ((String) attr.get()).trim().length() > 0) { // is a manager specified?
307: for (NamingEnumeration ne = attr.getAll(); ne
308: .hasMoreElements();) {
309: managers.add(ne.next());
310: }
311: }
312: }
313: } catch (NamingException ne) {
314: }
315: return managers;
316: }
317:
318: /**
319: * Determines if specified agent can become a community manager. A value
320: * of true is returned if the agent is a member of community and the
321: * attribute "CanBeManager=" is either undefined or is not equal to false.
322: * @param ec Config data associated with agent
323: * @return true if can be manager
324: */
325: private boolean canBeManager(EntityConfig ec) {
326: Attributes attrs = ec.getAttributes(); // get agent attributes
327: if (attrs == null)
328: return false; // no attributes, can't be a member or manager
329: if (!hasAttribute(attrs, "Role", "Member")
330: || !hasAttribute(attrs, "EntityType", "Agent"))
331: return false;
332: Attribute attr = attrs.get("CanBeManager");
333: if (attr == null) {
334: return true; // default to true if attr not specified
335: } else {
336: return (!attr.contains("No") && !attr.contains("False"));
337: }
338: }
339:
340: /**
341: * Handle CommunityRequests published to blackboard by invoking appropriate
342: * CommunityService method.
343: * @param cr CommunityRequest
344: */
345: private void processCommunityRequest(CommunityRequest cr) {
346: if (logger.isDebugEnabled()) {
347: logger.debug("Received CommunityRequest: " + cr);
348: }
349: if (cr instanceof GetCommunity) {
350: GetCommunity gcd = (GetCommunity) cr;
351: Community community = communityService.getCommunity(gcd
352: .getCommunityName(), new ResponseHandler(cr));
353: if (community != null) {
354: cr.setResponse(new CommunityResponseImpl(
355: CommunityResponse.SUCCESS, community));
356: blackboard.publishChange(cr);
357: }
358: } else if (cr instanceof ModifyAttributes) {
359: ModifyAttributes ma = (ModifyAttributes) cr;
360: communityService.modifyAttributes(ma.getCommunityName(), ma
361: .getEntityName(), ma.getModifications(),
362: new ResponseHandler(cr));
363: } else if (cr instanceof JoinCommunity) {
364: JoinCommunity jc = (JoinCommunity) cr;
365: communityService.joinCommunity(jc.getCommunityName(), jc
366: .getEntityName(), jc.getEntityType(), jc
367: .getEntityAttributes(), jc.createIfNotFound(), jc
368: .getCommunityAttributes(), new ResponseHandler(cr));
369: } else if (cr instanceof LeaveCommunity) {
370: LeaveCommunity lc = (LeaveCommunity) cr;
371: communityService.leaveCommunity(lc.getCommunityName(), lc
372: .getEntityName(), new ResponseHandler(cr));
373: } else if (cr instanceof SearchCommunity) {
374: SearchCommunity sc = (SearchCommunity) cr;
375: Collection results = communityService.searchCommunity(sc
376: .getCommunityName(), sc.getFilter(), sc
377: .isRecursiveSearch(), sc.getQualifier(),
378: new ResponseHandler(cr));
379: if (results != null) {
380: cr.setResponse(new CommunityResponseImpl(
381: CommunityResponse.SUCCESS, results));
382: blackboard.publishChange(cr);
383: }
384: } else if (cr instanceof ListParentCommunities) {
385: ListParentCommunities lpc = (ListParentCommunities) cr;
386: Collection parentNames = communityService
387: .listParentCommunities(lpc.getCommunityName(),
388: new ResponseHandler(cr));
389: if (parentNames != null) {
390: cr.setResponse(new CommunityResponseImpl(
391: CommunityResponse.SUCCESS, parentNames));
392: blackboard.publishChange(cr);
393: }
394: } else if (cr instanceof AddChangeListener) {
395: AddChangeListener acl = (AddChangeListener) cr;
396: communityService.addListener(acl.getChangeListener());
397: cr.setResponse(new CommunityResponseImpl(
398: CommunityResponse.SUCCESS, null));
399: blackboard.publishChange(cr);
400: } else if (cr instanceof RemoveChangeListener) {
401: RemoveChangeListener rcl = (RemoveChangeListener) cr;
402: communityService.removeListener(rcl.getChangeListener());
403: cr.setResponse(new CommunityResponseImpl(
404: CommunityResponse.SUCCESS, null));
405: blackboard.publishChange(cr);
406: } else {
407: if (logger.isWarnEnabled()) {
408: logger
409: .warn("Received unknown CommunityRequest - "
410: + cr);
411: }
412: }
413: }
414:
415: private boolean hasAttribute(Attributes attrs, String id,
416: String value) {
417: if (attrs != null) {
418: Attribute attr = attrs.get(id);
419: return (attr != null && attr.contains(value));
420: }
421: return false;
422: }
423:
424: private IncrementalSubscription communityRequestSub;
425: private static final UnaryPredicate communityRequestPredicate = new CommunityRequestPredicate();
426:
427: private static class CommunityRequestPredicate implements
428: UnaryPredicate {
429: public boolean execute(Object o) {
430: return (o instanceof CommunityRequest);
431: }
432: }
433:
434: // Updates CommunityRequest with response from CommunityService and
435: // publishes change to requester.
436: class ResponseHandler implements CommunityResponseListener {
437: CommunityRequest req;
438:
439: ResponseHandler(final CommunityRequest cr) {
440: req = cr;
441: if (cr instanceof SearchCommunity) {
442: final SearchCommunity sc = (SearchCommunity) cr;
443: communityService
444: .addListener(new CommunityChangeListener() {
445: public String getCommunityName() {
446: return cr.getCommunityName();
447: }
448:
449: public void communityChanged(
450: CommunityChangeEvent cce) {
451: Collection results = communityService
452: .searchCommunity(sc
453: .getCommunityName(), sc
454: .getFilter(), sc
455: .isRecursiveSearch(),
456: sc.getQualifier(), null);
457: if (results != null) {
458: req
459: .setResponse(new CommunityResponseImpl(
460: CommunityResponse.SUCCESS,
461: results));
462: blackboard.openTransaction();
463: blackboard.publishChange(req);
464: blackboard.closeTransaction();
465: }
466: }
467: });
468: }
469: }
470:
471: public void getResponse(CommunityResponse resp) {
472: req.setResponse(resp);
473: blackboard.openTransaction();
474: blackboard.publishChange(req);
475: blackboard.closeTransaction();
476: }
477: }
478:
479: }
|