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: package org.cougaar.community.manager;
027:
028: import java.util.Collection;
029: import java.util.Collections;
030: import java.util.HashMap;
031: import java.util.HashSet;
032: import java.util.Iterator;
033: import java.util.Map;
034: import java.util.Set;
035:
036: import javax.naming.NamingEnumeration;
037: import javax.naming.NamingException;
038: import javax.naming.directory.Attribute;
039: import javax.naming.directory.Attributes;
040: import javax.naming.directory.BasicAttributes;
041: import javax.naming.directory.DirContext;
042: import javax.naming.directory.ModificationItem;
043:
044: import org.cougaar.util.log.Logger;
045:
046: import org.cougaar.core.service.community.Community;
047: import org.cougaar.core.service.community.CommunityResponse;
048: import org.cougaar.core.service.community.Entity;
049: import org.cougaar.core.service.wp.Callback;
050: import org.cougaar.core.service.wp.Response;
051:
052: import org.cougaar.community.CommunityServiceConstants;
053: import org.cougaar.community.CommunityImpl;
054: import org.cougaar.community.CommunityResponseImpl;
055: import org.cougaar.community.CommunityUtils;
056:
057: /**
058: * Base class for CommunityManager that can manager one or more communities.
059: * Handles requests to join and leave a community. Disseminates community
060: * descriptors to community members and other interested agents.
061: */
062: public abstract class AbstractCommunityManager implements
063: CommunityManager, CommunityServiceConstants {
064:
065: protected Logger logger;
066: protected Map communities = Collections
067: .synchronizedMap(new HashMap());
068: protected String agentName;
069: protected CommunityAccessManager accessManager;
070:
071: /**
072: * Adds a community to be managed by this community manager.
073: * @param community Community to manage
074: */
075: public void manageCommunity(Community community) {
076: manageCommunity(community, null);
077: }
078:
079: /**
080: * Adds a community to be managed by this community manager.
081: *
082: * @param community
083: * Community to manage
084: * @param callback
085: * Callback to invoke on completion
086: */
087: public void manageCommunity(final Community community,
088: final Callback callback) {
089: if (logger.isDebugEnabled()) {
090: logger.debug(agentName + ": manageCommunity: community="
091: + community.getName());
092: }
093: final String communityName = community.getName();
094:
095: if (!isManager(communityName)) {
096: assertCommunityManagerRole(communityName, new Callback() {
097:
098: public void execute(Response resp) {
099: Response.Bind respBind = (Response.Bind) resp;
100: if (respBind.didBind()) {
101: if (logger.isDebugEnabled()) {
102: logger.debug(agentName + ": addCommunity:"
103: + " name=" + community.getName());
104: }
105: if (community.getAttributes() == null) {
106: community
107: .setAttributes(new BasicAttributes());
108: }
109: CommunityUtils.setAttribute(community
110: .getAttributes(), "CommunityManager",
111: agentName);
112: communities.put(communityName,
113: ((CommunityImpl) community).clone());
114: Set targets = new HashSet();
115: for (Iterator it = community.getEntities()
116: .iterator(); it.hasNext();) {
117: targets.add(((Entity) it.next()).getName());
118: }
119: addTargets(communityName, targets);
120: }
121: if (callback != null) {
122: callback.execute(resp);
123: }
124: }
125:
126: });
127:
128: }
129: }
130:
131: public CommunityResponse processRequest(String source,
132: String communityName, int reqType, Entity entity,
133: ModificationItem[] attrMods) {
134: CommunityResponseImpl resp = (CommunityResponseImpl) handleRequest(
135: source, communityName, reqType, entity, attrMods);
136: if (resp.getContent() != null) {
137: //resp.setContent(((CommunityImpl) resp.getContent()).clone());
138: resp.setContent(resp.getContent());
139: }
140: return resp;
141: }
142:
143: protected synchronized CommunityResponse handleRequest(
144: String source, String communityName, int reqType,
145: Entity entity, ModificationItem[] attrMods) {
146: if (logger.isDebugEnabled()) {
147: logger.debug(agentName + ": processRequest:" + " source="
148: + source + " community=" + communityName
149: + " reqType=" + reqType + " entity=" + entity
150: + " attrMods=" + attrMods);
151: }
152: if (accessManager != null
153: && !accessManager.authorize(communityName, source,
154: reqType, entity != null ? entity.getName()
155: : communityName, attrMods)) {
156: if (logger.isWarnEnabled()) {
157: logger.warn(agentName + ": Authorization Failure:"
158: + " community=" + communityName + " source="
159: + source + " request=" + reqType + " target="
160: + entity);
161: }
162: return new CommunityResponseImpl(CommunityResponse.FAIL,
163: null);
164: } else {
165: if (isManager(communityName)) {
166: CommunityImpl community = (CommunityImpl) communities
167: .get(communityName);
168: boolean result = true;
169: switch (reqType) {
170: case JOIN:
171: if (entity != null) {
172: String entitiesBeforeAdd = "";
173: if (logger.isDetailEnabled()) {
174: entitiesBeforeAdd = entityNames(community
175: .getEntities());
176: }
177: community.addEntity(entity);
178: if (logger.isDebugEnabled()) {
179: logger.debug(agentName + ": Add entity:"
180: + " community="
181: + community.getName() + " entity="
182: + entity + " members="
183: + community.getEntities().size());
184: }
185: if (logger.isDetailEnabled()) {
186: logger.detail(agentName
187: + ": Add entity:"
188: + " community="
189: + community.getName()
190: + " entity="
191: + entity.getName()
192: + " before="
193: + entitiesBeforeAdd
194: + " after="
195: + entityNames(community
196: .getEntities()));
197: }
198: addTargets(communityName, Collections
199: .singleton(source));
200: distributeUpdates(communityName);
201: } else {
202: result = false;
203: }
204: break;
205: case LEAVE:
206: if (entity != null
207: && community.hasEntity(entity.getName())) {
208: String entitiesBeforeRemove = "";
209: if (logger.isDetailEnabled()) {
210: entitiesBeforeRemove = entityNames(community
211: .getEntities());
212: }
213: community.removeEntity(entity.getName());
214: if (logger.isDebugEnabled()) {
215: logger.debug(agentName + ": Remove entity:"
216: + " community="
217: + community.getName() + " entity="
218: + entity + " members="
219: + community.getEntities().size());
220: }
221: if (logger.isDetailEnabled()) {
222: logger.detail(agentName
223: + ": Remove entity:"
224: + " community="
225: + community.getName()
226: + " entity="
227: + entity.getName()
228: + " before="
229: + entitiesBeforeRemove
230: + " after="
231: + entityNames(community
232: .getEntities()));
233: }
234: distributeUpdates(communityName);
235: } else {
236: result = false;
237: }
238: break;
239: case GET_COMMUNITY_DESCRIPTOR:
240: addTargets(communityName, Collections
241: .singleton(source));
242: break;
243: case MODIFY_ATTRIBUTES:
244: if (logger.isDebugEnabled()) {
245: logger.debug(agentName + ": Modify attributes:"
246: + " community=" + community.getName()
247: + " source=" + source
248: + " affectedEntity=" + entity);
249: }
250: if (entity == null
251: || community.getName().equals(
252: entity.getName())) {
253: // modify community attributes
254: Attributes attrs = community.getAttributes();
255: if (logger.isDetailEnabled()) {
256: logger
257: .debug(agentName
258: + ": Modifying community attributes:"
259: + " community="
260: + community.getName()
261: + " before="
262: + attrsToString(attrs));
263: }
264: applyAttrMods(attrs, attrMods);
265: if (logger.isDetailEnabled()) {
266: logger
267: .debug(agentName
268: + ": Modifying community attributes:"
269: + " community="
270: + community.getName()
271: + " after="
272: + attrsToString(attrs));
273: }
274: distributeUpdates(communityName);
275: } else {
276: // modify attributes of a community entity
277: entity = community.getEntity(entity.getName());
278: if (entity != null) {
279: Attributes attrs = entity.getAttributes();
280: if (logger.isDetailEnabled()) {
281: logger
282: .detail(agentName
283: + ": Modifying entity attributes:"
284: + " community="
285: + community.getName()
286: + " entity="
287: + entity.getName()
288: + " before="
289: + attrsToString(attrs));
290: }
291: applyAttrMods(attrs, attrMods);
292: if (logger.isDetailEnabled()) {
293: logger
294: .detail(agentName
295: + ": Modifying entity attributes:"
296: + " community="
297: + community.getName()
298: + " entity="
299: + entity.getName()
300: + " after="
301: + attrsToString(attrs));
302: }
303: distributeUpdates(communityName);
304: }
305: }
306: break;
307: }
308: community.setLastUpdate(System.currentTimeMillis());
309: return new CommunityResponseImpl(
310: result ? CommunityResponse.SUCCESS
311: : CommunityResponse.FAIL,
312: result ? community : null);
313: } else {
314: if (logger.isDetailEnabled()) {
315: logger.detail(agentName
316: + ": Not community manager:"
317: + " community=" + communityName
318: + " source=" + source + " request="
319: + reqType);
320: }
321: return new CommunityResponseImpl(
322: CommunityResponse.TIMEOUT, null);
323: }
324: }
325: }
326:
327: /**
328: * Apply attribute modifications.
329: * @param attrs Attributes to be modified
330: * @param mods Changes
331: */
332: protected void applyAttrMods(Attributes attrs,
333: ModificationItem[] mods) {
334: for (int i = 0; i < mods.length; i++) {
335: switch (mods[i].getModificationOp()) {
336: case DirContext.ADD_ATTRIBUTE:
337: Attribute newAttr = mods[i].getAttribute();
338: if (newAttr == null) {
339: continue;
340: }
341: Attribute oldAttr = attrs.get(newAttr.getID());
342: if (oldAttr == null) {
343: attrs.put(newAttr);
344: } else {
345: try {
346: NamingEnumeration en = newAttr.getAll();
347: while (en.hasMore()) {
348: oldAttr.add(en.next());
349: }
350: } catch (NamingException e) {
351: if (logger.isWarnEnabled()) {
352: logger.warn("Unable to add attribute", e);
353: }
354: }
355: }
356: break;
357: case DirContext.REPLACE_ATTRIBUTE:
358: attrs.remove(mods[i].getAttribute().getID());
359: attrs.put(mods[i].getAttribute());
360: break;
361: case DirContext.REMOVE_ATTRIBUTE:
362: attrs.remove(mods[i].getAttribute().getID());
363: break;
364: }
365: }
366: }
367:
368: // Converts a collection of Entities to a compact string representation of names
369: protected String entityNames(Collection entities) {
370: StringBuffer sb = new StringBuffer("[");
371: for (Iterator it = entities.iterator(); it.hasNext();) {
372: Entity entity = (Entity) it.next();
373: sb.append(entity.getName() + (it.hasNext() ? "," : ""));
374: }
375: return (sb.append("]").toString());
376: }
377:
378: /**
379: * Creates a string representation of an Attribute set.
380: * @param attrs Attributes to convert to String
381: * @return String representation of attributes
382: */
383: protected String attrsToString(Attributes attrs) {
384: StringBuffer sb = new StringBuffer("[");
385: try {
386: for (NamingEnumeration en = attrs.getAll(); en.hasMore();) {
387: Attribute attr = (Attribute) en.next();
388: sb.append(attr.getID() + "=(");
389: for (NamingEnumeration enum1 = attr.getAll(); enum1
390: .hasMore();) {
391: sb.append((String) enum1.next());
392: if (enum1.hasMore())
393: sb.append(",");
394: else
395: sb.append(")");
396: }
397: if (en.hasMore())
398: sb.append(",");
399: }
400: sb.append("]");
401: } catch (NamingException ne) {
402: }
403: return sb.toString();
404: }
405:
406: /**
407: * Tests whether this agent is the manager for the specified community.
408: * @param communityName String
409: * @return boolean
410: */
411: abstract protected boolean isManager(String communityName);
412:
413: /**
414: * Add agents to distribution list for community updates.
415: * @param communityName Name of community
416: * @param targets Set of agent names to add to distribution
417: */
418: abstract protected void addTargets(String communityName, Set targets);
419:
420: /**
421: * Remove agents from distribution list for community updates.
422: * @param communityName Name of community
423: * @param targets Set of agent names to remove
424: */
425: abstract protected void removeTargets(String communityName,
426: Set targets);
427:
428: /**
429: * Send updated Community info to agents on distribution.
430: * @param communityName Name of community
431: */
432: abstract protected void distributeUpdates(String communityName);
433:
434: /**
435: * Asserts community manager role.
436: * @param communityName Community to manage
437: */
438: abstract protected void assertCommunityManagerRole(
439: String communityName);
440:
441: /**
442: * asserts a community manager role in the WP and invokes a callback when the bind
443: * is complete
444: *
445: * @param communityName Community to manage
446: * @param callback callback to invoke on completion
447: */
448: abstract protected void assertCommunityManagerRole(
449: String communityName, Callback callback);
450:
451: }
|