0001: /*
0002: * <copyright>
0003: *
0004: * Copyright 2001-2004 Mobile Intelligence Corp under sponsorship of the Defense
0005: * Advanced Research Projects Agency (DARPA).
0006: *
0007: * You can redistribute this software and/or modify it under the terms of the
0008: * Cougaar Open Source License as published on the Cougaar Open Source Website
0009: * (www.cougaar.org).
0010: *
0011: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0012: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0013: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0014: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0015: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0016: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0017: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0018: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0019: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0020: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0021: * POSSIBILITY OF SUCH DAMAGE.
0022: *
0023: * </copyright>
0024: */
0025: package org.cougaar.community;
0026:
0027: import java.util.ArrayList;
0028: import java.util.Collection;
0029: import java.util.Collections;
0030: import java.util.HashSet;
0031: import java.util.Iterator;
0032: import java.util.List;
0033: import java.util.Set;
0034:
0035: import javax.naming.directory.Attribute;
0036: import javax.naming.directory.Attributes;
0037: import javax.naming.directory.BasicAttribute;
0038: import javax.naming.directory.BasicAttributes;
0039: import javax.naming.directory.DirContext;
0040: import javax.naming.directory.ModificationItem;
0041:
0042: import org.cougaar.community.manager.CommunityManager;
0043: import org.cougaar.community.manager.Request;
0044: import org.cougaar.core.mts.MessageAddress;
0045: import org.cougaar.core.service.community.Agent;
0046: import org.cougaar.core.service.community.Community;
0047: import org.cougaar.core.service.community.CommunityChangeEvent;
0048: import org.cougaar.core.service.community.CommunityChangeListener;
0049: import org.cougaar.core.service.community.CommunityResponse;
0050: import org.cougaar.core.service.community.CommunityResponseListener;
0051: import org.cougaar.core.service.community.CommunityService;
0052: import org.cougaar.core.service.community.Entity;
0053: import org.cougaar.core.service.community.FindCommunityCallback;
0054: import org.cougaar.core.service.wp.Callback;
0055: import org.cougaar.core.service.wp.Response;
0056: import org.cougaar.util.log.Logger;
0057:
0058: /**
0059: * Base class for implementations of CommunityService API. Methods that are used
0060: * for remote operations are abstract enabling the use of different
0061: * communication mechanisms.
0062: */
0063: public abstract class AbstractCommunityService implements
0064: CommunityService, java.io.Serializable {
0065:
0066: protected Logger log;
0067:
0068: protected static CommunityCache cache; // Shared community cache
0069:
0070: protected CommunityManager communityManager;
0071:
0072: protected String agentName;
0073:
0074: protected CommunityUpdateListener communityUpdateListener;
0075:
0076: protected CommunityMemberships myCommunities;
0077:
0078: protected MembershipWatcher membershipWatcher;
0079:
0080: /**
0081: * Request to create a community. If the specified community does not exist it
0082: * will be created and the caller will become the community manager. It the
0083: * community already exists a Community reference is obtained from its
0084: * community manager and returned.
0085: *
0086: * @param communityName
0087: * Name of community to create
0088: * @param attrs
0089: * Attributes to associate with new community
0090: * @param crl
0091: * Listener to receive response
0092: */
0093: public void createCommunity(String communityName, Attributes attrs,
0094: CommunityResponseListener crl) {
0095: joinCommunity(communityName, getAgentName(), AGENT,
0096: new BasicAttributes(), true, attrs, crl);
0097: }
0098:
0099: /**
0100: * Request to get a Community instance from local cache. If community is found
0101: * in cache a reference is returned by method call. If the community is not
0102: * found in the cache a null value is returned and the Community reference is
0103: * requested from the community manager. After the Community instance has been
0104: * obtained from the community manager the supplied CommunityResponseListener
0105: * callback is invoked to notify the requester. Note that the supplied
0106: * callback is not invoked if a non-null value is returned.
0107: *
0108: * @param communityName
0109: * Community of interest
0110: * @param crl
0111: * Listener to receive response after remote fetch
0112: * @return Community instance if found in cache or null if not found
0113: */
0114: public Community getCommunity(String communityName,
0115: CommunityResponseListener crl) {
0116: if (log.isDebugEnabled()) {
0117: log.debug(agentName + ": getCommunity:" + " community="
0118: + communityName + " inCache="
0119: + cache.contains(communityName));
0120: }
0121: if (cache.contains(communityName)) {
0122: Community value = cache.get(communityName);
0123: if (value == null) {
0124: crl.getResponse(new CommunityResponseImpl(
0125: CommunityResponse.FAIL, null));
0126: }
0127: return value;
0128: } else {
0129: queueCommunityRequest(communityName,
0130: Request.GET_COMMUNITY_DESCRIPTOR, null, null, crl,
0131: -1, // No timeout
0132: 0); // no delay
0133: return null;
0134: }
0135: }
0136:
0137: /**
0138: * Request to modify an Entity's attributes.
0139: *
0140: * @param communityName
0141: * Name of community
0142: * @param entityName
0143: * Name of affected Entity or null if modifying community attributes
0144: * @param mods
0145: * Attribute modifications
0146: * @param crl
0147: * Listener to receive response
0148: */
0149: public void modifyAttributes(String communityName,
0150: String entityName, ModificationItem[] mods,
0151: CommunityResponseListener crl) {
0152: if (log.isDebugEnabled()) {
0153: log.debug(agentName + ": modifyAttributes:" + " community="
0154: + communityName + " entity=" + entityName
0155: + " mods=" + mods);
0156: }
0157: Entity entity = entityName != null ? new EntityImpl(entityName)
0158: : null;
0159: final CommunityResponseListener wcrl = wrapResponse(
0160: Request.MODIFY_ATTRIBUTES, crl, communityName, entity);
0161: queueCommunityRequest(communityName, Request.MODIFY_ATTRIBUTES,
0162: entity, mods, wcrl, -1, // no timeout
0163: 0);
0164: }
0165:
0166: /**
0167: * Request to join a named community. If the specified community does not
0168: * exist it may be created in which case the caller becomes the community
0169: * manager. It the community doesn't exist and the caller has set the
0170: * "createIfNotFound flag to false the join request will be queued until the
0171: * community is found.
0172: *
0173: * @param communityName
0174: * Community to join
0175: * @param entityName
0176: * New member name
0177: * @param entityType
0178: * Type of member entity to create (AGENT or COMMUNITY)
0179: * @param entityAttrs
0180: * Attributes for new member
0181: * @param createIfNotFound
0182: * Create community if it doesn't exist, otherwise wait
0183: * @param newCommunityAttrs
0184: * Attributes for created community (used if createIfNotFound set to
0185: * true, otherwise ignored)
0186: * @param crl
0187: * Listener to receive response
0188: */
0189: public void joinCommunity(final String communityName,
0190: final String entityName, final int entityType,
0191: final Attributes entityAttrs, boolean createIfNotFound,
0192: final Attributes newCommunityAttrs, final long timeout,
0193: final CommunityResponseListener crl) {
0194: if (log.isDebugEnabled()) {
0195: log.debug(agentName + ": joinCommunity: " + " community="
0196: + communityName + " entity=" + entityName
0197: + " entityAttrs=" + entityAttrs
0198: + " createIfNotFound=" + createIfNotFound
0199: + " timeout=" + timeout);
0200: }
0201: switch (entityType) {
0202: case AGENT:
0203: final Agent agent = new AgentImpl(
0204: entityName != null ? entityName : agentName,
0205: entityAttrs);
0206: final CommunityResponseListener wcrl = wrapResponse(
0207: Request.JOIN, crl, communityName, agent);
0208: if (createIfNotFound) {
0209: if (createIfNotFound && !entityName.equals(agentName)) {
0210: if (log.isWarnEnabled()) {
0211: log.warn("Cannot create community by proxy. "
0212: + agentName
0213: + " cannot create community and set "
0214: + entityName
0215: + " as the manager. Will try to join "
0216: + communityName + " for " + entityName
0217: + " if the community already exists");
0218: }
0219: queueCommunityRequest(communityName, Request.JOIN,
0220: agent, null, wcrl, timeout, 0);
0221: }
0222: FindCommunityCallback fmcb = new FindCommunityCallback() {
0223:
0224: public void execute(String name) {
0225: if (name == null) { // community not found
0226: Community community = new CommunityImpl(
0227: communityName, newCommunityAttrs);
0228: communityManager.manageCommunity(community,
0229: new Callback() {
0230:
0231: public void execute(
0232: Response resp) {
0233: Response.Bind respBind = (Response.Bind) resp;
0234: if (respBind.didBind()) {
0235: queueCommunityRequest(
0236: communityName,
0237: Request.JOIN,
0238: agent, null,
0239: wcrl, timeout,
0240: 0);
0241: } else {
0242: queueCommunityRequest(
0243: communityName,
0244: Request.JOIN,
0245: agent, null,
0246: wcrl, timeout,
0247: 1000);
0248: }
0249: }
0250:
0251: });
0252: } else {
0253: queueCommunityRequest(communityName,
0254: Request.JOIN, agent, null, wcrl,
0255: timeout, 0);
0256: }
0257: }
0258: };
0259: findCommunity(communityName, fmcb, 0);
0260: } else {
0261: queueCommunityRequest(communityName, Request.JOIN,
0262: agent, null, wcrl, timeout, 0);
0263: }
0264: break;
0265: case COMMUNITY:
0266: // Submit join request to add nested community iff nested community
0267: // already exists
0268: if (createIfNotFound) {
0269: if (log.isWarnEnabled()) {
0270: log
0271: .warn("createIfNotFound true but value is ignored "
0272: + " for joining nested communities. "
0273: + entityName
0274: + " will only join "
0275: + communityName
0276: + " if it already exists");
0277: }
0278: }
0279: FindCommunityCallback fmcb = new FindCommunityCallback() {
0280:
0281: public void execute(final String nestedCommunityMgr) {
0282: if (nestedCommunityMgr != null) { // community found
0283: Community member = new CommunityImpl(
0284: entityName, entityAttrs);
0285: CommunityResponseListener wcrl = wrapResponse(
0286: Request.JOIN, crl, communityName,
0287: member);
0288: queueCommunityRequest(communityName,
0289: Request.JOIN, member, null, wcrl,
0290: timeout, 0);
0291: // Add "Parent" attribute to nested community
0292: Community nestedCommunity = getCommunity(
0293: entityName,
0294: new CommunityResponseListener() {
0295:
0296: public void getResponse(
0297: CommunityResponse resp) {
0298: if (resp.getStatus() == CommunityResponse.SUCCESS) {
0299: Community nestedCommunity = (Community) resp
0300: .getContent();
0301: addParentAttribute(
0302: nestedCommunity,
0303: communityName);
0304: }
0305: }
0306: });
0307: if (nestedCommunity != null) {
0308: addParentAttribute(nestedCommunity,
0309: communityName);
0310: }
0311: } else {
0312: // Failed request, nested community does not exist
0313: crl.getResponse(new CommunityResponseImpl(
0314: CommunityResponse.FAIL, null));
0315: }
0316: }
0317: };
0318: findCommunity(entityName, fmcb, 0); // Look for nested community
0319: break;
0320: default:
0321: // Failed request, unknown entity type
0322: crl.getResponse(new CommunityResponseImpl(
0323: CommunityResponse.FAIL, null));
0324: }
0325: }
0326:
0327: /**
0328: * Request to join a named community. If the specified community does not
0329: * exist it may be created in which case the caller becomes the community
0330: * manager. It the community doesn't exist and the caller has set the
0331: * "createIfNotFound flag to false the join request will be queued until the
0332: * community is found.
0333: *
0334: * @param communityName
0335: * Community to join
0336: * @param entityName
0337: * New member name
0338: * @param entityType
0339: * Type of member entity to create (AGENT or COMMUNITY)
0340: * @param entityAttrs
0341: * Attributes for new member
0342: * @param createIfNotFound
0343: * Create community if it doesn't exist, otherwise wait
0344: * @param newCommunityAttrs
0345: * Attributes for created community (used if createIfNotFound set to
0346: * true, otherwise ignored)
0347: * @param crl
0348: * Listener to receive response
0349: */
0350: public void joinCommunity(final String communityName,
0351: final String entityName, final int entityType,
0352: final Attributes entityAttrs, boolean createIfNotFound,
0353: final Attributes newCommunityAttrs,
0354: final CommunityResponseListener crl) {
0355: joinCommunity(communityName, entityName, entityType,
0356: entityAttrs, createIfNotFound, newCommunityAttrs, -1,
0357: crl);
0358: }
0359:
0360: // Scratchpad for pending callbacks to prevent multiple invocations
0361: private Set pendingCallbacks = Collections
0362: .synchronizedSet(new HashSet());
0363:
0364: /**
0365: * Wrap response enabling to be automatically resent in the event of a
0366: * timeout.
0367: *
0368: * @param type
0369: * int
0370: * @param crl
0371: * CommunityResponseListener
0372: * @param communityName
0373: * String
0374: * @param entity
0375: * Entity
0376: * @return CommunityResponseListener
0377: */
0378: private CommunityResponseListener wrapResponse(final int type,
0379: final CommunityResponseListener crl,
0380: final String communityName, final Entity entity) {
0381: if (crl == null)
0382: return null;
0383: membershipWatcher.addPendingOperation(communityName);
0384: return new CommunityResponseListener() {
0385:
0386: public void getResponse(final CommunityResponse resp) {
0387: switch (resp.getStatus()) {
0388: case CommunityResponse.SUCCESS:
0389: if (resp.getContent() == null) {
0390: final Community community = cache
0391: .get(communityName);
0392: if (type == CommunityServiceConstants.JOIN) {
0393: if (community != null
0394: && community.hasEntity(entity
0395: .getName())) {
0396: myCommunities
0397: .add(communityName, entity);
0398: membershipWatcher
0399: .removePendingOperation(communityName);
0400: ((CommunityResponseImpl) resp)
0401: .setContent(community);
0402: crl.getResponse(resp);
0403: } else {
0404: CommunityChangeListener ccl = new CommunityChangeListener() {
0405:
0406: public String getCommunityName() {
0407: return communityName;
0408: }
0409:
0410: public void communityChanged(
0411: CommunityChangeEvent cce) {
0412: synchronized (pendingCallbacks) {
0413: if (cce
0414: .getCommunity()
0415: .hasEntity(
0416: entity
0417: .getName())
0418: && pendingCallbacks
0419: .contains(this )) {
0420: removeListener(this );
0421: myCommunities.add(
0422: communityName,
0423: entity);
0424: membershipWatcher
0425: .removePendingOperation(communityName);
0426: ((CommunityResponseImpl) resp)
0427: .setContent(cce
0428: .getCommunity());
0429: crl.getResponse(resp);
0430: pendingCallbacks
0431: .remove(this );
0432: }
0433: }
0434: }
0435: };
0436: pendingCallbacks.add(ccl);
0437: addListener(ccl);
0438: }
0439: } else if (type == CommunityServiceConstants.LEAVE) {
0440: if (community != null
0441: && !community.hasEntity(entity
0442: .getName())) {
0443: myCommunities.remove(communityName,
0444: entity.getName());
0445: membershipWatcher
0446: .removePendingOperation(communityName);
0447: ((CommunityResponseImpl) resp)
0448: .setContent(community);
0449: crl.getResponse(resp);
0450: } else {
0451: CommunityChangeListener ccl = new CommunityChangeListener() {
0452:
0453: public String getCommunityName() {
0454: return communityName;
0455: }
0456:
0457: public void communityChanged(
0458: CommunityChangeEvent cce) {
0459: if (!cce
0460: .getCommunity()
0461: .hasEntity(
0462: entity
0463: .getName())
0464: && pendingCallbacks
0465: .contains(this )) {
0466: removeListener(this );
0467: myCommunities.remove(
0468: communityName,
0469: entity.getName());
0470: membershipWatcher
0471: .removePendingOperation(communityName);
0472: ((CommunityResponseImpl) resp)
0473: .setContent(cce
0474: .getCommunity());
0475: crl.getResponse(resp);
0476: pendingCallbacks
0477: .remove(this );
0478: }
0479: }
0480: };
0481: pendingCallbacks.add(ccl);
0482: addListener(ccl);
0483: }
0484: } else {
0485: if (community != null) {
0486: ((CommunityResponseImpl) resp)
0487: .setContent(community);
0488: crl.getResponse(resp);
0489: } else {
0490: CommunityChangeListener ccl = new CommunityChangeListener() {
0491:
0492: public String getCommunityName() {
0493: return communityName;
0494: }
0495:
0496: public void communityChanged(
0497: CommunityChangeEvent cce) {
0498: if (pendingCallbacks
0499: .contains(this )) {
0500: ((CommunityResponseImpl) resp)
0501: .setContent(cce
0502: .getCommunity());
0503: crl.getResponse(resp);
0504: pendingCallbacks
0505: .remove(this );
0506: removeListener(this );
0507: }
0508: }
0509: };
0510: pendingCallbacks.add(ccl);
0511: addListener(ccl);
0512: }
0513: }
0514: } else { // content != null
0515: if (type == CommunityServiceConstants.JOIN) {
0516: myCommunities.add(communityName, entity);
0517: } else if (type == CommunityServiceConstants.LEAVE) {
0518: myCommunities.remove(communityName, entity
0519: .getName());
0520: }
0521: membershipWatcher
0522: .removePendingOperation(communityName);
0523: crl.getResponse(resp);
0524: }
0525: break;
0526: case CommunityResponse.FAIL:
0527: membershipWatcher
0528: .removePendingOperation(communityName);
0529: crl.getResponse(resp);
0530: break;
0531: case CommunityResponse.TIMEOUT:
0532: if (log.isWarnEnabled()) {
0533: log.warn("Community request timeout:"
0534: + " type=" + type + " entity=" + entity
0535: + " community=" + communityName);
0536: }
0537: // retry ??
0538: /*
0539: * queueCommunityRequest(communityName, type, entity, null,
0540: * wrapResponse(type, crl, communityName, entity), -1, 10 * 1000); //
0541: * Requeue with delay
0542: */
0543:
0544: break;
0545:
0546: }
0547: }
0548: };
0549: }
0550:
0551: /**
0552: * Request to leave named community.
0553: *
0554: * @param communityName
0555: * Community to leave
0556: * @param entityName
0557: * Entity to remove from community
0558: * @param crl
0559: * Listener to receive response
0560: */
0561: public void leaveCommunity(final String communityName,
0562: final String entityName, final CommunityResponseListener crl) {
0563: leaveCommunity(communityName, entityName, -1, crl);
0564: }
0565:
0566: /**
0567: * Request to leave named community.
0568: *
0569: * @param communityName
0570: * Community to leave
0571: * @param entityName
0572: * Entity to remove from community
0573: * @param timeout
0574: * How long to attempt operation before giving up
0575: * @param crl
0576: * Listener to receive response
0577: */
0578: public void leaveCommunity(final String communityName,
0579: final String entityName, final long timeout,
0580: final CommunityResponseListener crl) {
0581: if (log.isDebugEnabled()) {
0582: log.debug(agentName + ": leaveCommunity: " + " community="
0583: + communityName + " entity=" + entityName);
0584: }
0585: final Entity member = new EntityImpl(entityName);
0586: final CommunityResponseListener wcrl = wrapResponse(
0587: Request.LEAVE, crl, communityName, member);
0588: Community community = getCommunity(communityName, null);
0589: if (community != null && community.hasEntity(entityName)) {
0590: Entity entity = community.getEntity(entityName);
0591: if (entity instanceof Agent) {
0592: // Leave request for this agent
0593: queueCommunityRequest(communityName, Request.LEAVE,
0594: member, null, wcrl, timeout, 0);
0595: } else { // Entity is a community
0596: findCommunity(communityName,
0597: new FindCommunityCallback() {
0598:
0599: public void execute(String managerName) {
0600: if (agentName.equals(managerName)) {
0601: queueCommunityRequest(
0602: communityName,
0603: Request.LEAVE, member,
0604: null, wcrl, timeout, 0);
0605: removeParentAttribute(getCommunity(
0606: entityName, null),
0607: communityName);
0608: } else {
0609: // Failed request, requestor not community manager
0610: crl
0611: .getResponse(new CommunityResponseImpl(
0612: CommunityResponse.FAIL,
0613: null));
0614: }
0615: }
0616: }, timeout);
0617: }
0618: } else {
0619: // Failed request, nested community or entity does not exist
0620: crl.getResponse(new CommunityResponseImpl(
0621: CommunityResponse.FAIL, null));
0622: }
0623: }
0624:
0625: /**
0626: * Adds "Parent=XXX" attribute to nested community.
0627: *
0628: * @param community
0629: * Community
0630: * @param parent
0631: * String
0632: */
0633: protected void addParentAttribute(Community community, String parent) {
0634: if (log.isDebugEnabled()) {
0635: log.debug(agentName + ": addParentAttribute: community="
0636: + community.getName());
0637: }
0638: Attributes attrs = community.getAttributes();
0639: Attribute attr = attrs.get("Parent");
0640: if (attr == null || !attr.contains(parent)) {
0641: int type = attr == null ? DirContext.ADD_ATTRIBUTE
0642: : DirContext.REPLACE_ATTRIBUTE;
0643: ModificationItem[] mods = new ModificationItem[] { new ModificationItem(
0644: type, new BasicAttribute("Parent", parent)) };
0645: queueCommunityRequest(community.getName(),
0646: Request.MODIFY_ATTRIBUTES, null, mods, null, -1, 0);
0647: }
0648: }
0649:
0650: /**
0651: * Removes "Parent=XXX" attribute from nested community.
0652: *
0653: * @param community
0654: * Community
0655: * @param parent
0656: * String
0657: */
0658: protected void removeParentAttribute(Community community,
0659: String parent) {
0660: if (log.isDebugEnabled()) {
0661: log.debug(agentName + ": removeParentAttribute: community="
0662: + community.getName());
0663: }
0664: Attributes attrs = community.getAttributes();
0665: Attribute attr = attrs.get("Parent");
0666: if (attr != null && attr.contains(parent)) {
0667: ModificationItem[] mods = new ModificationItem[] { new ModificationItem(
0668: DirContext.REMOVE_ATTRIBUTE, new BasicAttribute(
0669: "Parent", parent)) };
0670: queueCommunityRequest(community.getName(),
0671: Request.MODIFY_ATTRIBUTES, null, mods, null, -1, 0);
0672: }
0673: }
0674:
0675: /**
0676: * Initiates a community search operation. The results of the search are
0677: * immediately returned as part of the method call if the search can be
0678: * resolved using locally cached data. However, if the search requires
0679: * interaction with a remote community manager a null value is returned by the
0680: * method call and the search results are returned via the
0681: * CommunityResponseListener callback after the remote operation has been
0682: * completed. In the case where the search can be satisified using local data
0683: * (i.e., the method returns a non-null value) the CommunityResponseListener
0684: * is not invoked.
0685: *
0686: * @param communityName
0687: * Name of community to search
0688: * @param searchFilter
0689: * JNDI compliant search filter
0690: * @param recursiveSearch
0691: * True for recursive search into nested communities [false = search
0692: * top community only]
0693: * @param resultQualifier
0694: * Type of entities to return in result [ALL_ENTITIES, AGENTS_ONLY,
0695: * or COMMUNITIES_ONLY]
0696: * @param crl
0697: * Callback object to receive search results
0698: * @return Collection of Entity objects matching search criteria if available
0699: * in local cache. A null value is returned if cache doesn't contained
0700: * named community.
0701: */
0702: public Collection searchCommunity(final String communityName,
0703: final String searchFilter, final boolean recursiveSearch,
0704: final int resultQualifier,
0705: final CommunityResponseListener crl) {
0706: Collection results = null;
0707: if (communityName == null) {
0708: // Search all parent communities found in local cache
0709: results = cache.search(searchFilter);
0710: if (results == null)
0711: results = Collections.EMPTY_SET;
0712: } else {
0713: if (cache.contains(communityName)) {
0714: results = cache.search(communityName, searchFilter,
0715: resultQualifier, recursiveSearch);
0716: } else {
0717: final Set matches = new HashSet();
0718: FindCommunityCallback fmcb = new FindCommunityCallback() {
0719:
0720: public void execute(String name) {
0721: if (name != null) { // community exists, get a copy in local cache
0722: CommunityResponseListener getCommunityListener = new CommunityResponseListener() {
0723:
0724: public void getResponse(
0725: CommunityResponse resp) {
0726: if (resp.getStatus() == CommunityResponse.SUCCESS) {
0727: matches.addAll(cache.search(
0728: communityName,
0729: searchFilter,
0730: resultQualifier,
0731: recursiveSearch));
0732: }
0733: if (crl != null) {
0734: crl
0735: .getResponse(new CommunityResponseImpl(
0736: CommunityResponse.SUCCESS,
0737: matches));
0738: }
0739: }
0740: };
0741: if (getCommunity(communityName,
0742: getCommunityListener) != null) {
0743: matches.addAll(cache.search(
0744: communityName, searchFilter,
0745: resultQualifier,
0746: recursiveSearch));
0747: crl
0748: .getResponse(new CommunityResponseImpl(
0749: CommunityResponse.SUCCESS,
0750: matches));
0751: }
0752: } else { // community doesn't exist
0753: crl.getResponse(new CommunityResponseImpl(
0754: CommunityResponse.SUCCESS,
0755: Collections.EMPTY_SET));
0756: }
0757: }
0758: };
0759: findCommunity(communityName, fmcb, -1);
0760: }
0761: }
0762: if (log.isDebugEnabled()) {
0763: boolean inCache = cache.contains(communityName);
0764: log.debug(agentName
0765: + ": searchCommunity:"
0766: + " community="
0767: + communityName
0768: + " filter="
0769: + searchFilter
0770: + " recursive="
0771: + recursiveSearch
0772: + " qualifier="
0773: + resultQualifier
0774: + " inCache="
0775: + inCache
0776: + " results="
0777: + (results != null ? Integer.toString(results
0778: .size()) : null));
0779: }
0780: return results;
0781: }
0782:
0783: /**
0784: * Returns an array of community names of all communities of which caller is a
0785: * member.
0786: *
0787: * @param allLevels
0788: * Set to false if the list should contain only those communities in
0789: * which the caller is explicitly referenced. If true the list will
0790: * also include those communities in which the caller is implicitly a
0791: * member as a result of community nesting.
0792: * @return Array of community names
0793: */
0794: public String[] getParentCommunities(boolean allLevels) {
0795: return (String[]) cache.getAncestorNames(getAgentName(),
0796: allLevels).toArray(new String[0]);
0797: }
0798:
0799: /**
0800: * Requests a collection of community names identifying the communities that
0801: * contain the specified member. If the member name is null the immediate
0802: * parent communities for calling agent are returned. If member is the name of
0803: * a nested community the names of all immediate parent communities is
0804: * returned. The results are returned directly if the member name is null or
0805: * if a copy of the specified community is available in local cache.
0806: * Otherwise, the results will be returned in the CommunityResponseListener
0807: * callback in which case the method returns a null value.
0808: *
0809: * @param member
0810: * Member name (either null or name of a nested community)
0811: * @param crl
0812: * Listner to receive results if remote lookup is required
0813: * @return A collection of community names if operation can be resolved using
0814: * data from local cache, otherwise null
0815: */
0816: public Collection listParentCommunities(String member,
0817: String filter, CommunityResponseListener crl) {
0818: if (log.isDebugEnabled()) {
0819: log.debug("listParentCommunities:" + " member=" + member
0820: + " filter=" + filter + " hasCRL=" + (crl != null));
0821: }
0822: return listParentCommunities(member, crl);
0823: }
0824:
0825: /**
0826: * Add listener for CommunityChangeEvents.
0827: *
0828: * @param l
0829: * Listener
0830: */
0831: public void addListener(CommunityChangeListener l) {
0832: if (log.isDebugEnabled())
0833: log.debug(agentName + ": Adding CommunityChangeListener:"
0834: + " community=" + l.getCommunityName());
0835: cache.addListener(l);
0836: }
0837:
0838: /**
0839: * Remove listener for CommunityChangeEvents.
0840: *
0841: * @param l
0842: * Listener
0843: */
0844: public void removeListener(CommunityChangeListener l) {
0845: if (l != null) {
0846: cache.removeListener(l);
0847: }
0848: }
0849:
0850: /**
0851: * Performs attribute based search of community entities. This is a general
0852: * purpose search operation using a JNDI search filter. This method is
0853: * non-blocking. An empty Collection will be returned if the local cache is
0854: * empty. Updated search results can be obtained by using the addListener
0855: * method to receive change notifications.
0856: *
0857: * @param communityName
0858: * Name of community to search
0859: * @param filter
0860: * JNDI search filter
0861: * @return Collection of MessageAddress objects
0862: */
0863: public Collection search(final String communityName,
0864: final String filter) {
0865: if (log.isDebugEnabled()) {
0866: boolean inCache = cache.contains(communityName);
0867: if (log.isDebugEnabled()) {
0868: log.debug(agentName + ": search" + " community="
0869: + communityName + " filter=" + filter
0870: + " inCache=" + inCache);
0871: }
0872: if (inCache) {
0873: if (log.isDetailEnabled()) {
0874: log.detail(cache.get(communityName).toXml());
0875: }
0876: }
0877: }
0878: final Collection matches = new HashSet();
0879: if (cache.contains(communityName)) {
0880: matches.addAll(getMatches(communityName, filter));
0881: } else {
0882: Community community = getCommunity(communityName,
0883: new CommunityResponseListener() {
0884:
0885: public void getResponse(CommunityResponse resp) {
0886: if (resp.getContent() != null) {
0887: matches.addAll(getMatches(
0888: communityName, filter));
0889: }
0890: }
0891: });
0892: if (community != null) {
0893: matches.addAll(getMatches(communityName, filter));
0894: }
0895: }
0896: return matches;
0897: }
0898:
0899: /**
0900: * Returns a collection of MessageAddress objects for all agents from
0901: * specified community that match search filter.
0902: *
0903: * @param communityName
0904: * String
0905: * @param filter
0906: * String
0907: * @return Collection Collection of MessageAddress objects
0908: */
0909: protected Collection getMatches(String communityName, String filter) {
0910: Collection matches = new HashSet();
0911: if (cache.contains(communityName)) {
0912: Collection searchResults = cache.search(communityName,
0913: filter, Community.AGENTS_ONLY, true);
0914: for (Iterator it = searchResults.iterator(); it.hasNext();) {
0915: Entity e = (Entity) it.next();
0916: if (e instanceof Agent) {
0917: matches.add(MessageAddress.getMessageAddress(e
0918: .getName()));
0919: }
0920: }
0921: }
0922: return matches;
0923: }
0924:
0925: /**
0926: * Requests a collection of community names identifying the communities that
0927: * contain the specified member.
0928: *
0929: * @param member
0930: * Member name
0931: * @return A collection of community names
0932: */
0933: public Collection listParentCommunities(String member) {
0934: if (log.isDebugEnabled()) {
0935: log.debug("listParentCommunities:" + " member=" + member);
0936: }
0937: return cache.getAncestorNames(member, false);
0938: }
0939:
0940: /**
0941: * Requests a collection of community names identifying the communities that
0942: * contain the specified member and satisfy a given set of attributes.
0943: *
0944: * @param member
0945: * Member name
0946: * @param filter
0947: * Search filter defining community attributes
0948: * @return A collection of community names
0949: */
0950: public Collection listParentCommunities(String member, String filter) {
0951: if (log.isDebugEnabled()) {
0952: log.debug("listParentCommunities:" + " member=" + member
0953: + " filter=" + filter);
0954: }
0955: List matches = new ArrayList();
0956: Collection parentNames = listParentCommunities(member);
0957: Set communitiesMatchingFilter = cache.search(filter);
0958: if (communitiesMatchingFilter != null) {
0959: for (Iterator it = communitiesMatchingFilter.iterator(); it
0960: .hasNext();) {
0961: Community community = (Community) it.next();
0962: if (parentNames.contains(community.getName())) {
0963: matches.add(community.getName());
0964: }
0965: }
0966: }
0967: return matches;
0968: }
0969:
0970: /**
0971: * Handle response to community request returned by manager.
0972: *
0973: * @param resp
0974: * CommunityResponse from manager
0975: * @param listeners
0976: * CommunityResponseListeners to be notified
0977: */
0978: protected void handleResponse(final String communityName,
0979: final CommunityResponse resp, final Set listeners) {
0980: if (log.isDebugEnabled()) {
0981: log.debug(agentName + ": handleResponse: " + resp);
0982: }
0983: int status = resp.getStatus();
0984: Object content = resp.getContent();
0985: switch (status) {
0986: case CommunityResponse.SUCCESS:
0987: if (content != null && content instanceof Community) {
0988: Community community = (Community) content;
0989: // Update cached copy of community
0990: if (communityUpdateListener != null) {
0991: communityUpdateListener.updateCommunity(community);
0992: }
0993: // Replace community object in response with local reference from
0994: // cache
0995: ((CommunityResponseImpl) resp).setContent(cache
0996: .get(community.getName()));
0997: sendResponse(resp, listeners);
0998: } else {
0999: // Notify all listeners
1000: sendResponse(resp, listeners);
1001: }
1002: break;
1003: case CommunityResponse.TIMEOUT:
1004: // TODO: Retry??
1005: sendResponse(resp, listeners);
1006: break;
1007: case CommunityResponse.FAIL:
1008: sendResponse(resp, listeners);
1009: break;
1010: }
1011: }
1012:
1013: abstract public Collection listAllCommunities();
1014:
1015: abstract public void listAllCommunities(
1016: CommunityResponseListener crl);
1017:
1018: abstract protected void queueCommunityRequest(String communityName,
1019: int requestType, Entity entity,
1020: ModificationItem[] attrMods, CommunityResponseListener crl,
1021: long timeout, long delay);
1022:
1023: abstract protected String getAgentName();
1024:
1025: abstract protected void sendResponse(CommunityResponse resp,
1026: Set listeners);
1027:
1028: /**
1029: * Invokes callback when specified community is found.
1030: *
1031: * @param communityName
1032: * Name of community
1033: * @param fccb
1034: * Callback invoked after community is found or timeout has lapsed
1035: * @param timeout
1036: * Length of time (in milliseconds) to wait for community to be
1037: * located. A value of -1 disables the timeout.
1038: */
1039: abstract public void findCommunity(String communityName,
1040: FindCommunityCallback fccb, long timeout);
1041:
1042: }
|