001: /*
002: * <copyright>
003: *
004: * Copyright 2001-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.core.agent;
028:
029: import java.net.InetAddress;
030: import java.net.UnknownHostException;
031: import java.util.ArrayList;
032: import java.util.Collections;
033: import java.util.Iterator;
034: import java.util.LinkedHashMap;
035: import java.util.List;
036: import java.util.Map;
037: import java.util.Set;
038:
039: import org.cougaar.bootstrap.SystemProperties;
040: import org.cougaar.core.component.ComponentDescription;
041: import org.cougaar.core.component.ComponentDescriptions;
042: import org.cougaar.core.component.ContainerSupport;
043: import org.cougaar.core.component.Service;
044: import org.cougaar.core.component.ServiceBroker;
045: import org.cougaar.core.component.ServiceProvider;
046: import org.cougaar.core.component.StateTuple;
047: import org.cougaar.core.component.ViewService;
048: import org.cougaar.core.mts.MessageAddress;
049: import org.cougaar.core.node.ComponentInitializerService;
050: import org.cougaar.core.node.NodeControlService;
051: import org.cougaar.core.node.NodeIdentificationService;
052: import org.cougaar.util.Memo;
053: import org.cougaar.util.log.Logger;
054: import org.cougaar.util.log.Logging;
055:
056: /**
057: * A container for Agents.
058: * <p>
059: * Although the AgentManager can hold Components other than Agents,
060: * the default BinderFactory will only actually accept Agents and
061: * other Binders. If you want to load other sorts of components
062: * into AgentManager, you'll need to supply a Binder which knows
063: * how to bind your Component class.
064: *
065: * @property org.cougaar.core.node.InitializationComponent
066: * Which component should be loaded to advertise the
067: * ComponentInitializerService, which provides the agent
068: * component description configurations. Usually set to XML.
069: */
070: public class AgentManager extends ContainerSupport implements
071: AgentContainer {
072: public static final String INSERTION_POINT = "Node.AgentManager";
073:
074: private static final String AGENT_CLASSNAME = "org.cougaar.core.agent.AgentImpl";
075: private static final String AGENT_INSERTION_POINT = Agent.INSERTION_POINT;
076: private static final String BOOTSTRAP_CLASSNAME = "org.cougaar.core.agent.Bootstrap";
077:
078: private static final String NODE_AGENT_CLASSNAME_PROPERTY = "org.cougaar.core.node.classname";
079:
080: private List params;
081:
082: private ServiceProvider nodeIdentificationSP;
083: private ServiceProvider nodeControlSP;
084:
085: private boolean isNodeShuttingDown = false;
086: private final Object shutdownLock = new Object();
087:
088: public void setParameter(Object o) {
089: if (!(o instanceof List)) {
090: throw new IllegalArgumentException("Expecting a List, not "
091: + (o == null ? "null" : o.getClass().getName()));
092: }
093: params = (List) o;
094: }
095:
096: public void load() {
097: super .load();
098:
099: List ext = params;
100: params = null;
101:
102: String nodeName = getNodeName();
103:
104: add_node_identification_service(nodeName);
105:
106: add_node_control_service();
107:
108: addAll(getAgentBinderDescriptions(nodeName));
109:
110: add(getNodeAgentDescription(nodeName, ext));
111: }
112:
113: /**
114: * Unload Agent manager.
115: *
116: * @see org.cougaar.util.GenericStateModel#unload()
117: */
118: public void unload() {
119: revokeServices();
120: super .unload();
121: }
122:
123: private void revokeServices() {
124: if (nodeIdentificationSP != null) {
125: getServiceBroker().revokeService(
126: NodeIdentificationService.class,
127: nodeIdentificationSP);
128: nodeIdentificationSP = null;
129: }
130: if (nodeControlSP != null) {
131: getServiceBroker().revokeService(NodeControlService.class,
132: nodeControlSP);
133: nodeControlSP = null;
134: }
135: }
136:
137: protected String specifyContainmentPoint() {
138: return INSERTION_POINT;
139: }
140:
141: protected ComponentDescriptions findInitialComponentDescriptions() {
142: return null;
143: }
144:
145: //
146: // override "add(o)" to log exceptions:
147: //
148:
149: public boolean add(Object o) {
150: try {
151: return super .add(o);
152: } catch (RuntimeException re) {
153: Logging.getLogger(this .getClass()).error(
154: "Failed to add " + o + " to " + this , re);
155: return false;
156: }
157: }
158:
159: //
160: // methods used by "load()":
161: //
162:
163: private String getNodeName() {
164: String nodeName = SystemProperties
165: .getProperty("org.cougaar.node.name");
166: if (nodeName == null) {
167: try {
168: nodeName = InetAddress.getLocalHost().getHostName();
169: } catch (UnknownHostException uhe) {
170: throw new RuntimeException(
171: "Node name not specified and couldn't guess from local host name.",
172: uhe);
173: }
174: if (nodeName == null) {
175: throw new IllegalArgumentException(
176: "Node name not specified");
177: }
178: }
179: return nodeName;
180: }
181:
182: private void add_node_identification_service(String nodeName) {
183: final MessageAddress localNode = MessageAddress
184: .getMessageAddress(nodeName);
185: Class clazz = NodeIdentificationService.class;
186: Service service = new NodeIdentificationService() {
187: public MessageAddress getMessageAddress() {
188: return localNode;
189: }
190: };
191: nodeIdentificationSP = add_service(clazz, service);
192: }
193:
194: private void add_node_control_service() {
195: // instead of using the node's or our service broker as the
196: // "rootsb", we use our child service broker. All components
197: // and agents are loaded as our children, so this is should
198: // be fine.
199: final ServiceBroker rootsb = getChildServiceBroker();
200: // create a proxy for our agent manager, to prevent casting
201: final AgentContainer rootac = new AgentContainer() {
202: public boolean containsAgent(MessageAddress agentId) {
203: return AgentManager.this .containsAgent(agentId);
204: }
205:
206: public Set getAgentAddresses() {
207: return AgentManager.this .getAgentAddresses();
208: }
209:
210: public ComponentDescription getAgentDescription(
211: MessageAddress agentId) {
212: return AgentManager.this .getAgentDescription(agentId);
213: }
214:
215: public Map getAgents() {
216: return AgentManager.this .getAgents();
217: }
218:
219: public List getComponents() {
220: return AgentManager.this .getComponents();
221: }
222:
223: public void addAgent(MessageAddress agentId) {
224: AgentManager.this .addAgent(agentId);
225: }
226:
227: public void addAgent(MessageAddress agentId,
228: StateTuple tuple) {
229: AgentManager.this .addAgent(agentId, tuple);
230: }
231:
232: public boolean add(Object o) {
233: return AgentManager.this .add(o);
234: }
235:
236: public void removeAgent(MessageAddress agentId) {
237: AgentManager.this .removeAgent(agentId);
238: }
239:
240: public boolean remove(Object o) {
241: return AgentManager.this .remove(o);
242: }
243: };
244: Class clazz = NodeControlService.class;
245: Service service = new NodeControlService() {
246: public ServiceBroker getRootServiceBroker() {
247: return rootsb;
248: }
249:
250: public AgentContainer getRootContainer() {
251: return rootac;
252: }
253:
254: public void shutdown() {
255: shutdownNode();
256: }
257: };
258: nodeControlSP = add_service(clazz, service);
259: }
260:
261: private List getAgentBinderDescriptions(String nodeName) {
262: ServiceBroker csb = getChildServiceBroker();
263: ComponentDescriptions cds;
264: try {
265: ComponentInitializerService cis = (ComponentInitializerService) csb
266: .getService(this ,
267: ComponentInitializerService.class, null);
268: Logger logger = Logging.getLogger(AgentManager.class);
269: if (logger.isInfoEnabled()) {
270: logger
271: .info(nodeName
272: + " AgentManager.load about to look for CompDesc's"
273: + " of Agent Binders.");
274: }
275: // Get all items _below_ given insertion point.
276: // To get just binders, must use extract method later....
277: cds = new ComponentDescriptions(
278: cis.getComponentDescriptions(nodeName,
279: INSERTION_POINT));
280: csb.releaseService(this , ComponentInitializerService.class,
281: cis);
282: } catch (Exception e) {
283: throw new Error("Couldn't initialize AgentManager Binders"
284: + " with ComponentInitializerService ", e);
285: }
286:
287: return ComponentDescriptions.sort(cds
288: .extractInsertionPointComponent(INSERTION_POINT
289: + ".Binder"));
290: }
291:
292: private ComponentDescription getNodeAgentDescription(
293: String nodeName, List external_components) {
294: String classname = SystemProperties.getProperty(
295: NODE_AGENT_CLASSNAME_PROPERTY,
296: "org.cougaar.core.agent.AgentImpl");
297: List params = new ArrayList(1);
298: params.add(nodeName);
299: if (external_components != null) {
300: params.addAll(external_components);
301: }
302: ComponentDescription desc = new ComponentDescription(classname,
303: Agent.INSERTION_POINT, classname, null, //codebase
304: params, null, //certificate
305: null, //lease
306: null, //policy
307: ComponentDescription.PRIORITY_COMPONENT);
308: return desc;
309: }
310:
311: //
312: // Implement the "AgentContainer" API, primarily to support
313: // agent mobility
314: //
315:
316: public boolean containsAgent(MessageAddress agentId) {
317: return (getAgentDescription(agentId) != null);
318: }
319:
320: private Memo _getAgentAddressesMemo = Memo.get(new Memo.Function() {
321: public String toString() {
322: return "Memo<getAgentAddresses>";
323: }
324:
325: public Object eval(Object o) {
326: return Collections.unmodifiableSet(((Map) o).keySet());
327: }
328: });
329:
330: /** Return the current set of contained Agent's MessageAddresses */
331: public Set getAgentAddresses() {
332: // memorize to avoid consing a new Set each time.
333: return (Set) _getAgentAddressesMemo.eval(getAgents());
334: }
335:
336: public ComponentDescription getAgentDescription(
337: MessageAddress agentId) {
338: Map m = getAgents();
339: return (ComponentDescription) m.get(agentId);
340: }
341:
342: public List getComponents() { // better to use iterator()!
343: return super .listComponents();
344: }
345:
346: protected Iterator getComponentsIterator() {
347: return iterator();
348: }
349:
350: private Memo _getAgentsMemo = Memo.get(new Memo.Function() {
351: public String toString() {
352: return "Memo<getAgents>";
353: }
354:
355: public Object eval(Object o) {
356: List cds = (List) o;
357:
358: Map ret = new LinkedHashMap(cds.size());
359: for (Iterator iter = componentIterator(); iter.hasNext();) {
360: Object ob = iter.next();
361: ComponentDescription desc = (ComponentDescription) ob;
362: MessageAddress cid = cdToMa(desc);
363: if (cid != null) {
364: ret.put(cid, desc);
365: }
366: }
367: return Collections.unmodifiableMap(ret);
368: }
369: });
370:
371: /** return a map of MessageAddress to ComponentDescription which
372: * describes the current set of agents.
373: * The returned map is Unmodifiable and will be shared across multiple
374: * invocations.
375: */
376: public synchronized Map getAgents() {
377: return (Map) _getAgentsMemo.eval(getBoundComponentList());
378: }
379:
380: /** Interpret an Agent-level ComponentDescription to
381: * determine the agent name in the form of the MessageAddress of the agent.
382: * May return null if uninterpretable.
383: */
384: protected MessageAddress cdToMa(ComponentDescription desc) {
385: Object o = desc.getParameter();
386: MessageAddress cid = null;
387: if (o instanceof MessageAddress) {
388: cid = (MessageAddress) o;
389: } else if (o instanceof String) {
390: cid = MessageAddress.getMessageAddress((String) o);
391: } else if (o instanceof List) {
392: List l = (List) o;
393: if (l.size() > 0) {
394: Object o1 = l.get(0);
395: if (o1 instanceof MessageAddress) {
396: cid = (MessageAddress) o1;
397: } else if (o1 instanceof String) { // primary case
398: cid = MessageAddress.getMessageAddress((String) o1);
399: } else {
400: // shouldn't happen
401: // o1 unknown Object type!
402: Logger logger = Logging
403: .getLogger(AgentManager.class);
404: if (logger.isWarnEnabled())
405: logger
406: .warn("Unknown object class in ComponentDescription List at CID spot: "
407: + o1);
408: }
409: }
410: } else {
411: // sometimes we get a null ComponentDesc parameter (e.g. agent binder)
412: // so cid will be null. This is OK.
413: }
414:
415: return cid;
416: }
417:
418: public void addAgent(MessageAddress agentId) {
419: _addAgent(agentId, null);
420: }
421:
422: public void addAgent(MessageAddress agentId, StateTuple tuple) {
423: _addAgent(agentId, tuple);
424: }
425:
426: private void _addAgent(MessageAddress agentId, Object o) {
427: if (containsAgent(agentId)) {
428: // agent already exists
429: throw new RuntimeException("Agent " + agentId
430: + " already exists");
431: }
432:
433: ComponentDescription desc;
434: if (o instanceof StateTuple) {
435: StateTuple tuple = (StateTuple) o;
436: if (tuple.getState() != null) {
437: // no longer supported!
438: throw new UnsupportedOperationException(
439: "State must be null");
440: }
441: desc = tuple.getComponentDescription();
442: } else {
443: Object parameter;
444: if (o == null) {
445: parameter = agentId;
446: } else {
447: throw new RuntimeException("Invalid type: " + o);
448: }
449: desc = new ComponentDescription(AGENT_CLASSNAME,
450: AGENT_INSERTION_POINT, AGENT_CLASSNAME, null, // codebase
451: parameter, null, // certificate
452: null, // lease
453: null, // policy
454: ComponentDescription.PRIORITY_COMPONENT);
455: }
456:
457: // add the agent
458: if (!add(desc)) {
459: throw new RuntimeException("Agent " + agentId
460: + " returned \"false\"");
461: }
462:
463: // the agent has started and is now ACTIVE
464: }
465:
466: public void removeAgent(MessageAddress agentId) {
467: // find the agent's component description
468: ComponentDescription desc = getAgentDescription(agentId);
469: if (desc == null) {
470: // no such agent, or not loaded with a desc
471: throw new RuntimeException("Agent " + agentId
472: + " is not loaded");
473: }
474:
475: if (!remove(desc)) {
476: throw new RuntimeException("Unable to remove agent "
477: + agentId + ", \"remove()\" returned false");
478: }
479:
480: // the agent has been UNLOADED and removed
481: }
482:
483: /**
484: * Shuts down the local node.
485: */
486: protected void shutdownNode() {
487: synchronized (shutdownLock) {
488: if (isNodeShuttingDown) {
489: // Shut down sequence has already been initiated.
490: return;
491: }
492: isNodeShuttingDown = true;
493: }
494: Runnable r = new Runnable() {
495: public void run() {
496: // Remove all agents in reverse load order
497: //
498: // Note that we remove the nodeAgent last
499: List l = new ArrayList(getAgentAddresses());
500: for (int i = l.size() - 1; i >= 0; i--) {
501: Object oi = l.get(i);
502: if (oi instanceof MessageAddress) {
503: removeAgent((MessageAddress) oi);
504: }
505: }
506:
507: revokeServices();
508:
509: // For debugging purposes: verify all services have been revoked
510: ServiceBroker sb = getServiceBroker();
511: for (Iterator iter = sb.getCurrentServiceClasses(); iter
512: .hasNext();) {
513: Class cl = (Class) iter.next();
514: if (ViewService.class.isAssignableFrom(cl))
515: continue;
516: Logging.getLogger(this .getClass()).error(
517: "Service should have been revoked: "
518: + cl.getName());
519: }
520: }
521: };
522: (new Thread(r)).start();
523: }
524:
525: private ServiceProvider add_service(Class clazz, Service service) {
526: // we must use our service broker, otherwise our child components
527: // will not be able to block our services
528: ServiceBroker sb = getServiceBroker();
529: ServiceProvider sp = new SimpleServiceProvider(clazz, service);
530: if (!sb.addService(clazz, sp)) {
531: throw new RuntimeException("Unable to add service " + clazz);
532: }
533: return sp;
534: }
535:
536: private void revoke_service(Class clazz, ServiceProvider sp) {
537: if (sp != null) {
538: ServiceBroker sb = getServiceBroker();
539: sb.revokeService(clazz, sp);
540: }
541: }
542:
543: private static final class SimpleServiceProvider implements
544: ServiceProvider {
545: private final Class clazz;
546: private final Service service;
547:
548: public SimpleServiceProvider(Class clazz, Service service) {
549: this .clazz = clazz;
550: this .service = service;
551: }
552:
553: public Object getService(ServiceBroker sb, Object requestor,
554: Class serviceClass) {
555: if (clazz.isAssignableFrom(serviceClass)) {
556: return service;
557: } else {
558: return null;
559: }
560: }
561:
562: public void releaseService(ServiceBroker sb, Object requestor,
563: Class serviceClass, Object service) {
564: }
565: }
566: }
|