001: /*
002: * <copyright>
003: *
004: * Copyright 1997-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.node;
028:
029: import java.util.ArrayList;
030: import java.util.Collection;
031: import java.util.Collections;
032: import java.util.HashSet;
033: import java.util.Iterator;
034: import java.util.List;
035: import java.util.Map;
036: import java.util.Set;
037: import org.cougaar.bootstrap.SystemProperties;
038: import org.cougaar.core.agent.AgentContainer;
039: import org.cougaar.core.blackboard.BlackboardForAgent;
040: import org.cougaar.core.component.Component;
041: import org.cougaar.core.component.ComponentDescription;
042: import org.cougaar.core.component.ComponentDescriptions;
043: import org.cougaar.core.component.ServiceBroker;
044: import org.cougaar.core.component.ServiceProvider;
045: import org.cougaar.core.component.ServiceRevokedListener;
046: import org.cougaar.core.mts.MessageAddress;
047: import org.cougaar.core.persist.PersistenceClient;
048: import org.cougaar.core.persist.PersistenceIdentity;
049: import org.cougaar.core.persist.PersistenceService;
050: import org.cougaar.core.persist.RehydrationData;
051: import org.cougaar.core.service.AgentIdentificationService;
052: import org.cougaar.core.service.LoggingService;
053: import org.cougaar.util.GenericStateModelAdapter;
054:
055: /**
056: * This component loads the initial set of agents into the node and
057: * persists the names of dynamically added/removed agents.
058: *
059: * @property org.cougaar.core.node.ignoreRehydratedAgentList
060: * Ignore the list of agents from the rehydrated state of the
061: * NodeAgent, if any. Defaults to false. Set to true to disable
062: * this feature and always use the list of agents from the
063: * ComponentInitializerService.
064: */
065: public final class AgentLoader extends GenericStateModelAdapter
066: implements Component {
067:
068: public static final String IGNORE_REHYDRATED_AGENT_LIST_PROP = "org.cougaar.core.node.ignoreRehydratedAgentList";
069:
070: private static final boolean ignoreRehydratedAgentDescs = SystemProperties
071: .getBoolean(IGNORE_REHYDRATED_AGENT_LIST_PROP);
072:
073: private ServiceBroker sb;
074:
075: private List initialAgents;
076:
077: private LoggingService log;
078:
079: private ServiceBroker rootsb;
080: private AgentContainer agentContainer;
081:
082: private MessageAddress localAgent;
083:
084: private PersistenceService ps;
085: private PersistenceClient pc;
086:
087: private BlackboardForAgent bb;
088:
089: private RegisterAgentServiceProvider rasp;
090:
091: private boolean addingAgents;
092: private List initialDescs;
093:
094: private final Set activeAgentAddrs = new HashSet();
095:
096: public void setServiceBroker(ServiceBroker sb) {
097: this .sb = sb;
098: }
099:
100: /**
101: * Expecting a list of agent names.
102: */
103: public void setParameter(Object o) {
104: List l = (List) o;
105: int n = (l == null ? 0 : l.size());
106: for (int i = 0; i < n; i++) {
107: Object o1 = l.get(i);
108: if (!(o1 instanceof String)) {
109: throw new IllegalArgumentException("List element["
110: + i
111: + "/"
112: + n
113: + "] is "
114: + (o1 == null ? "null" : o1.getClass()
115: .getName()));
116: }
117: if (initialAgents == null) {
118: initialAgents = new ArrayList();
119: } else if (initialAgents.contains(o1)) {
120: // n^2 duplicate check, but n is small...
121: continue;
122: }
123: initialAgents.add(o1);
124: }
125: }
126:
127: /**
128: * Add Agents and their child Components (Plugins, etc) to this Node.
129: * <p>
130: * This first checks the persistence snapshot to resume the agents
131: * that were running. If there is no snapshot (or it has been
132: * disabled) then the component initializer service is used, which
133: * reads the list of agents from the configuration files (INI/XML/DB).
134: * <p>
135: * Note that the agents are added in bulk, which loads them in
136: * sequence in our thread.
137: */
138: public void load() {
139: super .load();
140:
141: log = (LoggingService) sb.getService(this ,
142: LoggingService.class, null);
143:
144: NodeControlService ncs = (NodeControlService) sb.getService(
145: this , NodeControlService.class, null);
146: if (ncs == null) {
147: throw new RuntimeException(
148: "Unable to obtain NodeControlService");
149: }
150: rootsb = ncs.getRootServiceBroker();
151: agentContainer = ncs.getRootContainer();
152: sb.releaseService(this , NodeControlService.class, ncs);
153:
154: localAgent = find_local_agent();
155:
156: register_persistence();
157:
158: bb = (BlackboardForAgent) sb.getService(this ,
159: BlackboardForAgent.class, null);
160: if (bb == null && log.isWarnEnabled()) {
161: log.warn("Unable to obtain BlackboardForAgent");
162: }
163:
164: // advertise our agent add/remove listener
165: rasp = new RegisterAgentServiceProvider();
166: rootsb.addService(RegisterAgentService.class, rasp);
167:
168: ComponentDescription[] agentDescs = null;
169:
170: // rehydrate list of agent descriptions
171: Object o = rehydrate();
172: if (o instanceof List) {
173: List l = (List) o;
174: agentDescs = (ComponentDescription[]) l
175: .toArray(new ComponentDescription[l.size()]);
176: if (log.isInfoEnabled()) {
177: log.info("Persistence snapshot contains a list of "
178: + agentDescs.length + " agents");
179: }
180: }
181: o = null;
182:
183: if (agentDescs != null && ignoreRehydratedAgentDescs) {
184: if (log.isInfoEnabled()) {
185: log.info("Ignoring rehydrated list of "
186: + agentDescs.length + " agents");
187: }
188: agentDescs = null;
189: }
190:
191: // Look for agents in the ComponentInitializerService
192: if (agentDescs == null) {
193: agentDescs = readAgentsFromConfig();
194: }
195:
196: addAgents(agentDescs);
197: }
198:
199: public void unload() {
200: super .unload();
201:
202: if (rasp != null) {
203: rootsb.revokeService(RegisterAgentService.class, rasp);
204: rasp = null;
205: }
206:
207: if (bb != null) {
208: sb.releaseService(this , BlackboardForAgent.class, bb);
209: bb = null;
210: }
211:
212: unregister_persistence();
213: }
214:
215: private MessageAddress find_local_agent() {
216: AgentIdentificationService ais = (AgentIdentificationService) sb
217: .getService(this , AgentIdentificationService.class,
218: null);
219: if (ais == null) {
220: return null;
221: }
222: MessageAddress ret = ais.getMessageAddress();
223: sb.releaseService(this , AgentIdentificationService.class, ais);
224: return ret;
225: }
226:
227: private ComponentDescription[] readAgentsFromConfig() {
228: ComponentDescription[] agentDescs = null;
229:
230: // backwards compatibility for non-XML configs!
231: try {
232: ComponentInitializerService cis = (ComponentInitializerService) sb
233: .getService(this ,
234: ComponentInitializerService.class, null);
235: boolean oldStyle = !cis.includesDefaultComponents();
236: if (oldStyle) {
237: if (log.isInfoEnabled()) {
238: log
239: .info("Asking component initializer service for agents");
240: }
241: // get the agents - this gives _anything_ below AgentManager,
242: // so must extract out just the .Agent's later
243: agentDescs = cis.getComponentDescriptions(localAgent
244: .getAddress(), "Node.AgentManager");
245: if (agentDescs == null) {
246: agentDescs = new ComponentDescription[0];
247: }
248: }
249: sb.releaseService(this , ComponentInitializerService.class,
250: cis);
251: if (oldStyle) {
252: return agentDescs;
253: }
254: } catch (Exception e) {
255: throw new Error("Couldn't initialize list of agents", e);
256: }
257:
258: if (initialAgents == null) {
259: if (log.isWarnEnabled()) {
260: log
261: .warn("Node " + localAgent
262: + " contains zero agents");
263: }
264: return new ComponentDescription[0];
265: }
266:
267: int n = initialAgents.size();
268:
269: if (log.isInfoEnabled()) {
270: log.info("Will add agents[" + n + "]: " + initialAgents);
271: }
272:
273: ComponentDescription[] ret = new ComponentDescription[n];
274:
275: for (int i = 0; i < n; i++) {
276: String name = (String) initialAgents.get(i);
277: ComponentDescription desc = new ComponentDescription(
278: "org.cougaar.core.agent.AgentImpl(" + name + ")",
279: "Node.AgentManager.Agent",
280: "org.cougaar.core.agent.AgentImpl", null, //codebase
281: Collections.singletonList(name), //params
282: null, //certificate
283: null, //lease
284: null, //policy
285: ComponentDescription.PRIORITY_COMPONENT);
286: ret[i] = desc;
287: }
288:
289: return ret;
290: }
291:
292: private void addAgents(ComponentDescription[] agentDescs) {
293: ComponentDescriptions cds = new ComponentDescriptions(
294: agentDescs);
295: List cdcs = cds
296: .extractInsertionPointComponent("Node.AgentManager.Agent");
297: if (log.isDebugEnabled()) {
298: log.debug("Adding " + cdcs.size() + " agents: " + cdcs);
299: }
300:
301: addingAgents = true;
302: initialDescs = cdcs;
303:
304: for (int i = 0, n = cdcs.size(); i < n; i++) {
305: ComponentDescription cd = (ComponentDescription) cdcs
306: .get(i);
307: try {
308: agentContainer.add(cd);
309: } catch (Exception e) {
310: log
311: .error("Unable to add agent "
312: + cd.getParameter()
313: + ", not loading agents: "
314: + cdcs.subList(i, n));
315: break;
316: }
317: }
318:
319: addingAgents = true;
320: initialDescs = null;
321: }
322:
323: private Object captureState() {
324: if (addingAgents) {
325: if (log.isInfoEnabled()) {
326: int n = (initialDescs == null ? 0 : initialDescs.size());
327: log.info("Asked to \"captureState\" while loading,"
328: + " which would find a partial list of agents,"
329: + " so instead return our initial agent list["
330: + n + "]");
331: if (log.isDebugEnabled()) {
332: log
333: .debug("initialDescs[" + n + "]="
334: + initialDescs);
335: }
336: }
337: return initialDescs;
338: }
339:
340: // FIXME replace with just the agent addrs?
341: //
342: // this should be possible once there's a fixed agent
343: // class and agent parameters are limited to the addr.
344:
345: // get the map of (addr -> desc)
346: Map agents = agentContainer.getAgents();
347: // remove ourselves
348: agents.remove(localAgent);
349: // convert to desc list
350: List ret = new ArrayList(agents.values());
351:
352: return ret;
353: }
354:
355: private void register_persistence() {
356: // get persistence
357: pc = new PersistenceClient() {
358: public PersistenceIdentity getPersistenceIdentity() {
359: String id = getClass().getName();
360: return new PersistenceIdentity(id);
361: }
362:
363: public List getPersistenceData() {
364: Object o = captureState();
365: // must return mutable list!
366: List l = new ArrayList(1);
367: l.add(o);
368: return l;
369: }
370: };
371: ps = (PersistenceService) sb.getService(pc,
372: PersistenceService.class, null);
373: }
374:
375: private void unregister_persistence() {
376: if (ps != null) {
377: sb.releaseService(pc, PersistenceService.class, ps);
378: ps = null;
379: pc = null;
380: }
381: }
382:
383: private void persistNow() {
384: if (addingAgents) {
385: if (log.isInfoEnabled()) {
386: log
387: .info("Still loading, delaying persistNow until active");
388: }
389: return;
390: }
391:
392: if (bb == null) {
393: if (log.isInfoEnabled()) {
394: log
395: .info("Unable to persistNow, BlackboardService is null");
396: }
397: return;
398: }
399:
400: if (log.isInfoEnabled()) {
401: log.info("Asking our blackboard to persist, to trigger a"
402: + " full node-agent snapshot that will contain the"
403: + " modified list of agents running on this node");
404: }
405: bb.persistNow();
406: if (log.isInfoEnabled()) {
407: log.info("Completed persistNow()");
408: }
409: }
410:
411: private Object rehydrate() {
412: RehydrationData rd = ps.getRehydrationData();
413: if (rd == null) {
414: if (log.isInfoEnabled()) {
415: log.info("No rehydration data found");
416: }
417: return null;
418: }
419:
420: // extract our ComponentDescriptions
421: List l = rd.getObjects();
422: rd = null;
423: int lsize = (l == null ? 0 : l.size());
424: if (lsize < 1) {
425: if (log.isInfoEnabled()) {
426: log.info("Invalid rehydration list? " + l);
427: }
428: return null;
429: }
430: Object o = l.get(0);
431: if (o == null) {
432: if (log.isInfoEnabled()) {
433: log.info("Null rehydration state?");
434: }
435: return null;
436: }
437:
438: if (log.isInfoEnabled()) {
439: log.info("Found rehydrated state");
440: if (log.isDetailEnabled()) {
441: log.detail("state is " + o);
442: }
443: }
444:
445: return o;
446: }
447:
448: private void add(MessageAddress addr) {
449: if (localAgent.equals(addr)) {
450: // ignore self
451: return;
452: }
453:
454: boolean changed;
455: synchronized (activeAgentAddrs) {
456: changed = activeAgentAddrs.add(addr);
457: }
458:
459: if (changed) {
460: persistNow();
461: }
462: }
463:
464: private void remove(MessageAddress addr) {
465: boolean changed;
466: synchronized (activeAgentAddrs) {
467: changed = activeAgentAddrs.remove(addr);
468: }
469:
470: if (changed) {
471: persistNow();
472: }
473: }
474:
475: private class RegisterAgentServiceProvider implements
476: ServiceProvider {
477:
478: private final RegisterAgentService myService = new RegisterAgentService() {
479: public void addAgent(MessageAddress addr) {
480: add(addr);
481: }
482:
483: public void removeAgent(MessageAddress addr) {
484: remove(addr);
485: }
486: };
487:
488: public Object getService(ServiceBroker sb, Object requestor,
489: Class serviceClass) {
490: if (serviceClass == RegisterAgentService.class) {
491: return myService;
492: } else {
493: throw new IllegalArgumentException(
494: "Can only provide RegisterAgentService!");
495: }
496: }
497:
498: public void releaseService(ServiceBroker sb, Object requestor,
499: Class serviceClass, Object service) {
500: }
501: }
502: }
|