View Javadoc

1   package cz.cuni.amis.pogamut.base.agent.impl;
2   
3   import java.util.HashMap;
4   import java.util.HashSet;
5   import java.util.Map;
6   import java.util.Set;
7   import java.util.concurrent.TimeUnit;
8   import java.util.logging.Level;
9   
10  import cz.cuni.amis.introspection.Folder;
11  import cz.cuni.amis.introspection.java.ReflectionObjectFolder;
12  import cz.cuni.amis.pogamut.base.agent.IAgent;
13  import cz.cuni.amis.pogamut.base.agent.IAgentId;
14  import cz.cuni.amis.pogamut.base.agent.component.event.AgentEvents;
15  import cz.cuni.amis.pogamut.base.agent.exceptions.AgentException;
16  import cz.cuni.amis.pogamut.base.agent.jmx.AgentJMXComponents;
17  import cz.cuni.amis.pogamut.base.agent.jmx.IJMXEnabled;
18  import cz.cuni.amis.pogamut.base.agent.state.WaitForAgentStateChange;
19  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentState;
20  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStateFailed;
21  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStateFailing;
22  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStateInstantiated;
23  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStatePaused;
24  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStatePausing;
25  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStateResumed;
26  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStateResuming;
27  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStateStarted;
28  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStateStartedPaused;
29  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStateStarting;
30  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStateStartingPaused;
31  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStateStopped;
32  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStateStopping;
33  import cz.cuni.amis.pogamut.base.agent.state.level0.IAgentState;
34  import cz.cuni.amis.pogamut.base.agent.state.level1.IAgentStateDown;
35  import cz.cuni.amis.pogamut.base.agent.state.level1.IAgentStateGoingDown;
36  import cz.cuni.amis.pogamut.base.agent.state.level1.IAgentStateGoingUp;
37  import cz.cuni.amis.pogamut.base.agent.state.level1.IAgentStateUp;
38  import cz.cuni.amis.pogamut.base.agent.state.level2.IAgentStateFailed;
39  import cz.cuni.amis.pogamut.base.agent.state.level2.IAgentStateFailing;
40  import cz.cuni.amis.pogamut.base.agent.state.level2.IAgentStateInstantiated;
41  import cz.cuni.amis.pogamut.base.agent.state.level2.IAgentStatePaused;
42  import cz.cuni.amis.pogamut.base.agent.state.level2.IAgentStatePausing;
43  import cz.cuni.amis.pogamut.base.agent.state.level2.IAgentStateResuming;
44  import cz.cuni.amis.pogamut.base.agent.state.level2.IAgentStateRunning;
45  import cz.cuni.amis.pogamut.base.agent.state.level2.IAgentStateStarting;
46  import cz.cuni.amis.pogamut.base.agent.state.level2.IAgentStateStartingPaused;
47  import cz.cuni.amis.pogamut.base.agent.state.level2.IAgentStateStopped;
48  import cz.cuni.amis.pogamut.base.agent.state.level2.IAgentStateStopping;
49  import cz.cuni.amis.pogamut.base.agent.state.level3.IAgentStateStarted;
50  import cz.cuni.amis.pogamut.base.component.IComponent;
51  import cz.cuni.amis.pogamut.base.component.bus.IComponentBus;
52  import cz.cuni.amis.pogamut.base.component.bus.IComponentEventListener;
53  import cz.cuni.amis.pogamut.base.component.bus.event.IFatalErrorEvent;
54  import cz.cuni.amis.pogamut.base.component.bus.event.IPausedEvent;
55  import cz.cuni.amis.pogamut.base.component.bus.event.IPausingEvent;
56  import cz.cuni.amis.pogamut.base.component.bus.event.IResetEvent;
57  import cz.cuni.amis.pogamut.base.component.bus.event.IResumedEvent;
58  import cz.cuni.amis.pogamut.base.component.bus.event.IResumingEvent;
59  import cz.cuni.amis.pogamut.base.component.bus.event.IStartedEvent;
60  import cz.cuni.amis.pogamut.base.component.bus.event.IStartingEvent;
61  import cz.cuni.amis.pogamut.base.component.bus.event.IStartingPausedEvent;
62  import cz.cuni.amis.pogamut.base.component.bus.event.IStoppedEvent;
63  import cz.cuni.amis.pogamut.base.component.bus.event.IStoppingEvent;
64  import cz.cuni.amis.pogamut.base.component.bus.event.impl.FatalErrorEvent;
65  import cz.cuni.amis.pogamut.base.component.controller.ComponentController;
66  import cz.cuni.amis.pogamut.base.component.exception.ComponentCantPauseException;
67  import cz.cuni.amis.pogamut.base.component.exception.ComponentCantResumeException;
68  import cz.cuni.amis.pogamut.base.component.exception.ComponentCantStartException;
69  import cz.cuni.amis.pogamut.base.component.exception.ComponentCantStopException;
70  import cz.cuni.amis.pogamut.base.component.lifecyclebus.ILifecycleBus;
71  import cz.cuni.amis.pogamut.base.utils.jmx.FolderToIJMXEnabledAdapter;
72  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
73  import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
74  import cz.cuni.amis.utils.ExceptionToString;
75  import cz.cuni.amis.utils.NullCheck;
76  import cz.cuni.amis.utils.collections.HashSetClass;
77  import cz.cuni.amis.utils.exception.PogamutException;
78  import cz.cuni.amis.utils.exception.PogamutInterruptedException;
79  import cz.cuni.amis.utils.flag.Flag;
80  import cz.cuni.amis.utils.flag.ImmutableFlag;
81  import cz.cuni.amis.utils.flag.WaitForFlagChange;
82  import cz.cuni.amis.utils.token.IToken;
83  
84  /**
85   * Abstract agent class, provides basic interface for the agent implementing its lifecycle methods + introducing JMX.
86   * <p><p>
87   * Agent has fully implemented lifecycle methods such as {@link AbstractAgent#start()}, {@link AbstractAgent#startPaused()}
88   * or {@link AbstractAgent#stop()}. It also listens for {@link IFatalErrorEvent} on its bus in order to terminate itself
89   * in case of any errors. Also, if all components of the agent stops themselves, the agent is stopped as well.
90   * If you do not like this behavior, email me to jakub.gemrot@gmail.com and I will make this optional.
91   * <p><p>
92   * Note that the Pogamut agent is designed to be a multicomponent beast. And by component we're meaning objects
93   * that implements {@link IComponent} registers itself into {@link AbstractAgent#getEventBus()} owned by the agent
94   * and adhering to the lifecycle provided either by {@link ComponentController} or 
95   * {@link ILifecycleBus#addLifecycleManagement(IToken, cz.cuni.amis.pogamut.base.component.controller.IComponentControlHelper, cz.cuni.amis.pogamut.base.component.controller.ComponentDependencies)}.
96   * <p>
97   * These components (if correctly configured, which all native components are) starts together with the call of {@link AbstractAgent#start()}
98   * or {@link AbstractAgent#startPaused()}, as well as stopping themselves in the case of {@link AbstractAgent#stop()}.
99   * <p><p>
100  * This model seems to defy the OOP, in fact it is its very incarnation applying principles or "separation of concerns",
101  * "modular design", whatever you would like to call it. Many native components are designed the way they interact
102  * with each other only via interfaces, they do not depend on each other directly thus greatly enabling anybody to
103  * hack inside any internal component of any Pogamut agent.
104  * <p><p>
105  * <b>JMX</b>
106  * <p><p>
107  * We're purposely using neither inheritance here nor decorator (or such
108  * wrappers) because we need JMX to be usable by every descendant of the class
109  * (so we don't want to have another JMX class branch, nor wrapper that denies
110  * the inheritance by its nature).
111  * <p>
112  * To keep the JMX-specific methods at minimum in the agent class we're grouping them into the
113  * private inner class object JMXComponents that can be accessed via getJMX()
114  * method. This object maintain also a list of IJMXEnabled components that
115  * should be enabled when the whole JMX feature is being enabled.
116  * <p><p>
117  * Usage:
118  * <p>
119  * <i>To start JMX on the agent:</i> getJMX().enableJMX(mBeanServer, domain)
120  * <p>
121  * <i>To add another JMX component to the agent:</i>
122  * getJMX().addComponent(component)
123  * </p>
124  * 
125  * @author Jimmy
126  */
127 public abstract class AbstractAgent implements IAgent {
128 
129     /**
130      * Name of the root introspection folder.
131      */
132     public static final String INTROSPECTION_ROOT_NAME = "root";
133 
134     /**
135      * Log category name used for {@link AbstractAgent#log}.
136      */
137 	public static final String LOG_CATEGORY_NAME = "Agent";
138     
139 	/**
140 	 * Logger that is used by the Pogamut (that means by the class hierarchy
141 	 * starting from AbstractAgent).
142 	 */
143 	private IAgentLogger logger = null;
144 	
145 	/**
146 	 * Agent's log category, goes under category name {@link AbstractAgent#LOG_CATEGORY_NAME}.
147 	 */
148 	protected LogCategory log = null;
149 
150 	/**
151 	 * State of the agent - it is a flag so you may attach listeners to it.
152 	 */
153 	private Flag<IAgentState> agentState = new Flag<IAgentState>(new AgentStateInstantiated("Just created."));
154 
155 	/**
156 	 * Wraps a few methods to one place so it won't plague the public method
157 	 * space of the agent. (Make things a bit clear...).
158 	 * <p><p>
159 	 * Contains list of IJMXEnabled components that should be enabled when the
160 	 * whole JMX feature of the agent is fired up.
161 	 * <p><p>
162 	 * Lazy initialization upon calling getJMX().
163 	 * <p><p>
164 	 * We're direct-accessing the field in-case we need to know whether the JMX has been already initialized or not
165 	 * comparing it against <i>null</i>.
166 	 */
167 	private AgentJMXComponents jmx = null;
168 
169 	private Object jmxMutex = new Object();
170 	
171 	/**
172 	 * Contains system event bus, that is used to propagate agent-system events (usually start/stop/fatal error messages).
173 	 */
174 	private IComponentBus eventBus;
175 	
176 	/**
177 	 * Introspection folder of the agent.
178 	 */
179 	private Folder folder = null;
180 			
181 	/**
182 	 * Gateway for sending events into the event bus.
183 	 */
184 	protected AgentEvents events;
185 	
186 	/**
187 	 * Agent id.
188 	 */
189 	private IAgentId agentId;
190 	
191 	/**
192 	 * Running components of the agent.
193 	 */
194 	private Map<IToken, IComponent> runningComponents = new HashMap<IToken, IComponent>();
195 	
196 	/**
197 	 * Components, the agent depends on.
198 	 */
199 	private HashSetClass stopDependencyClass = new HashSetClass();
200 	
201 	/**
202 	 * Components' ids, the agent depends on.
203 	 */
204 	private Set<IToken> stopDependencyToken = new HashSet<IToken>();
205 		
206 	private IComponentEventListener<IStartedEvent> startedEventListener = new IComponentEventListener<IStartedEvent>() {
207 
208 		@Override
209 		public void notify(IStartedEvent event) {
210 			if (event.getSource() == null) return;
211 			if (event.getSource() == AbstractAgent.this) return;
212 			synchronized(runningComponents) {
213 				IComponent component = runningComponents.get(event.getSource().getComponentId());
214 				if (component == null) {
215 					// ADDING NEW COMOPNENT TO THE AGENT
216 					if (log.isLoggable(Level.FINE)) log.fine("Component " + event.getSource() + " started.");
217 					componentStarted(event);
218 				} else {
219 					if (component == event.getSource()) {
220 						if (log.isLoggable(Level.WARNING)) log.warning("Component " + event.getSource() + " has started more than once.");
221 					} else {
222 						throw new AgentException("Component id clash, two instances of components have the same component id = " + event.getSource().getComponentId(), this);				
223 					}
224 				}
225 			}
226 		}
227 		
228 	};
229 	
230 	private IComponentEventListener<IPausedEvent> pausedEventListener = new IComponentEventListener<IPausedEvent>() {
231 
232 		@Override
233 		public void notify(IPausedEvent event) {
234 			if (event.getSource() == null) return;
235 			if (event.getSource() == AbstractAgent.this) return;
236 			synchronized(runningComponents) {
237 				IComponent component = runningComponents.get(event.getSource().getComponentId());
238 				if (component == null) {
239 					// ADDING NEW COMOPNENT TO THE AGENT
240 					if (log.isLoggable(Level.FINE)) log.fine("Component " + event.getSource() + " started.");
241 					componentStarted(event);
242 				} else {
243 					if (component != event.getSource()) {
244 						throw new AgentException("Component id clash, two instances of components have the same component id = " + event.getSource().getComponentId(), this);				
245 					}
246 				}
247 			}
248 		}
249 		
250 	};
251 	
252 	private IComponentEventListener<IStoppingEvent> stoppingEventListener = new IComponentEventListener<IStoppingEvent>() {
253 
254 		@Override
255 		public void notify(IStoppingEvent event) {
256 			componentStopping(event);
257 		}
258 		
259 	};
260 	
261 	private IComponentEventListener<IStoppedEvent> stoppedEventListener = new IComponentEventListener<IStoppedEvent>() {
262 
263 		@Override
264 		public void notify(IStoppedEvent event) {
265 			synchronized(runningComponents) {
266 				IComponent component = runningComponents.get(event.getSource().getComponentId());
267 				if (component == null) {
268 					if (log.isLoggable(Level.WARNING)) log.warning("Component " + event.getSource() + " stopped, but it has never reported that it started.");
269 				} else {
270 					if (component == event.getSource()) {
271 						// REMOVING COMPONENT FROM THE AGENT
272 						if (log.isLoggable(Level.WARNING)) log.warning("Component " + event.getSource() + " has stopped.");
273 						componentStopped(event);
274 					} else {
275 						throw new AgentException("Component id clash, two instances of components have the same component id = " + event.getSource().getComponentId(), this);					
276 					}
277 				}
278 			}
279 		}
280 		
281 	};
282 	
283 	private IComponentEventListener<IFatalErrorEvent> fatalErrorEventListener = new IComponentEventListener<IFatalErrorEvent>() {
284 
285 		@Override
286 		public void notify(IFatalErrorEvent event) {
287 			if (log.isLoggable(Level.SEVERE)) log.severe("Fatal error sensed: " + event);
288 			componentFatalError(event);
289 		}
290 		
291 	};
292 	
293 	private IComponentEventListener<IResetEvent> resetEventListener = new IComponentEventListener<IResetEvent>() {
294 
295 		@Override
296 		public void notify(IResetEvent event) {
297 			resetEvent(event);
298 		}
299 		
300 	};
301 	
302 	/**
303 	 * Introspection folder with properties and other subfolders obtained from
304 	 * this agent.
305 	 * @param agentId unique id of the agent
306 	 * @param eventBus agent's event bus system
307 	 * @param logger agent's logger, used to obtain {@link AgentName} instance
308 	 */
309 	public AbstractAgent(IAgentId agentId, IComponentBus eventBus, IAgentLogger logger) {
310 		this.logger = logger;
311 		NullCheck.check(this.logger, "logger");
312 		this.agentId = agentId;
313 		NullCheck.check(this.agentId, "agentId");
314 		this.eventBus = eventBus;
315 		NullCheck.check(this.eventBus, "eventBus");
316 		this.log = this.logger.getCategory(LOG_CATEGORY_NAME);
317 		NullCheck.check(this.log, "logger.getCategory()");
318 		
319 		if (log.isLoggable(Level.INFO)) log.info("Initializing " + getClass().getSimpleName() + ", id: " + this.agentId.getToken());
320 		
321 		this.events = new AgentEvents(this.eventBus, this, log);
322 		
323 		this.eventBus.addEventListener(IStartedEvent.class,    startedEventListener);
324 		this.eventBus.addEventListener(IPausedEvent.class,     pausedEventListener);
325 		this.eventBus.addEventListener(IStoppingEvent.class,   stoppingEventListener);
326 		this.eventBus.addEventListener(IStoppedEvent.class,    stoppedEventListener);
327 		this.eventBus.addEventListener(IFatalErrorEvent.class, fatalErrorEventListener);
328 		this.eventBus.addEventListener(IResetEvent.class,      resetEventListener);
329 	}	
330 	
331 	@Override
332     public boolean equals(Object other) {
333         if (other == null) return false;
334         if (!(other instanceof AbstractAgent)) return false;
335         AbstractAgent otherAgent = (AbstractAgent) other;
336         return this.agentId.equals(otherAgent.getComponentId());
337     }
338     
339     @Override
340 	public int hashCode() {
341     	return this.agentId.hashCode();
342     }
343 
344 	////
345 	//
346 	// INTERFACE METHOD IMPLEMENTATIONS
347 	//
348 	////
349 	
350     @Override
351 	public IAgentLogger getLogger() {
352 		return this.logger;
353 	}
354 
355     @Override
356 	public ImmutableFlag<IAgentState> getState() {
357 		return agentState.getImmutable();
358 	}
359     
360     /**
361      * Returns true if the agent is in one of 'states'.
362      * @param states
363      * @return
364      */
365     public boolean inState(Class<?>... states) {
366     	Class<?> agentState = getState().getFlag().getClass();
367     	for (Class<?> state : states) {
368     		if (state.isAssignableFrom(agentState)) return true;
369     	}
370     	return false;
371     }
372     
373     /**
374      * Returns true if the agent is not in any of 'states'.
375      * @param states
376      * @return
377      */
378     public boolean notInState(Class<?>... states) {
379     	Class<?> agentState = getState().getFlag().getClass();
380     	for (Class<?> state : states) {
381     		if (state.isAssignableFrom(agentState)) return false;
382     	}
383     	return true;
384     }
385     
386     @Override
387 	public IComponentBus getEventBus() {
388 		return eventBus;
389 	}
390     
391     @Override
392     public String getName() {
393     	return agentId.getName().getFlag();
394     }
395 	
396 	@Override
397 	public IAgentId getComponentId() {
398 		return agentId;
399 	}
400 	
401 	/**
402 	 * Returns log category of the agent, used by agent lifecycle management methods itself.
403 	 */
404 	public LogCategory getLog() {
405 		return log;
406 	}
407 		
408 	private void startFailedTest() {
409 		if (inState(IAgentStateFailed.class)) {
410 			throw new ComponentCantStartException("Agent has been killed during initialization.", log, this);
411 		}
412 	}
413 
414 	private boolean startMethodCalled = false;
415 	
416 	/**
417 	 * Starts the agent.
418 	 * <p><p>
419 	 * 1) switches state to {@link IAgentStateStarting}<p>
420 	 * 2) broadcasts {@link IStartingEvent} (transactional)<p>
421 	 * 3) calls startAgent()<p>
422 	 * 3) broadcasts {@link IStartedEvent} (transactional)<p>
423 	 * 4) switches state to {@link IAgentStateStarted}<p>
424 	 * <p><p>
425 	 * Every agent component that wants to start together with the agent should do so during (2 or 3) and broadcast eventTransactionl({@link IStartedEvent} / {@link IPausedEvent}).
426 	 * <p><p>
427 	 * Prevents recursion.
428 	 * 
429 	 * @throws ComponentCantStartException
430 	 */
431 	@Override
432 	public final synchronized void start() throws ComponentCantStartException {
433 		if (startMethodCalled) return;
434 	
435 		if (inState(IAgentStateGoingUp.class, IAgentStateUp.class)) return;
436 		if (notInState(IAgentStateDown.class)) {
437 			throw new ComponentCantStartException("Agent can't start, it is in wrong state (" + getState().getFlag() + ") stop() or kill() agent before start()ing.", log, this);
438 		}
439 		
440 		startMethodCalled = true;
441 		try {
442 			if (log.isLoggable(Level.WARNING)) log.warning("Starting agent " + getComponentId().getToken());
443 	    	if (jmx != null) {
444 	    		// reregister JMX components again
445 	    		getJMX().registerJMX();
446 	    	}
447 			if (!eventBus.isRunning()) {
448 				if (log.isLoggable(Level.WARNING)) log.warning("Event bus is not running, resetting.");
449 				eventBus.reset();
450 				if (!eventBus.isRunning()) { 
451 					throw new ComponentCantStartException("Event bus reset()ed but it's still not running.", log, this);
452 				}
453 			}
454 			setState(new AgentStateStarting("Sending 'starting' event."));
455 			startFailedTest();
456 			events.startingTransactional();
457 			startFailedTest();
458 			setState(new AgentStateStarting("Calling startAgent()."));
459 			startFailedTest();
460 			startAgent();
461 			startFailedTest();
462 			setState(new AgentStateStarting("Sending 'started' event."));
463 			startFailedTest();
464 			events.startedTransactional();
465 			startFailedTest();
466 			setState(new AgentStateStarted("Agent has started."));
467 			startFailedTest();
468 			if (log.isLoggable(Level.INFO)) log.info(runningComponents.size() + " component" + (runningComponents.size() > 1 ? "s" : "") + " has started along with the agent.");
469 		} catch (Exception e) {
470 			if (!inState(IAgentStateFailed.class) && !events.fatalError("Can't start the agent", e)) {
471 				componentFatalError(new FatalErrorEvent(this, "agent's fatal error not propagated"));
472 			}
473 			if (e instanceof ComponentCantStartException) throw (ComponentCantStartException)e;
474 			throw new ComponentCantStartException("Can't start: " + e.getMessage(), e, log, this);
475 		} finally {
476 			startMethodCalled = false;
477 		}
478 	}
479 	
480 	/**
481 	 * Starts the agent into paused state.
482 	 * <p><p>
483 	 * 1) switches state to {@link IAgentStateStartingPaused}<p>
484 	 * 2) broadcasts {@link IStartingPausedEvent} (transactional)<p>
485 	 * 3) calls startAgent()<p>
486 	 * 3) broadcasts {@link IPausedEvent} (transactional)<p>
487 	 * 4) switches state to {@link IAgentStatePaused}<p>
488 	 * <p><p>
489 	 * Every agent component that wants to start together with the agent should do so during (2 or 3) and broadcast eventTransactionl({@link IStartedEvent} / {@link IPausedEvent}).
490 	 * <p><p>
491 	 * Prevents recursion.
492 	 * 
493 	 * @throws ComponentCantStartException
494 	 */
495 	@Override
496 	public final synchronized void startPaused() throws ComponentCantStartException {
497 		if (startMethodCalled) return;
498 	
499 		if (inState(IAgentStateGoingUp.class, IAgentStateUp.class)) return;
500 		if (notInState(IAgentStateDown.class)) {
501 			throw new ComponentCantStartException("Agent can't start, it is in wrong state (" + getState().getFlag() + ") stop() or kill() agent before start()ing.", log, this);
502 		}
503 		
504 		startMethodCalled = true;
505 		try {
506 			if (log.isLoggable(Level.WARNING)) log.warning("Starting-paused agent " + getComponentId().getToken());
507 	    	if (jmx != null) {
508 	    		// reregister JMX components again
509 	    		getJMX().registerJMX();
510 	    	}
511 			if (!eventBus.isRunning()) {
512 				if (log.isLoggable(Level.WARNING)) log.warning("Event bus is not running, resetting.");
513 				eventBus.reset();
514 				if (!eventBus.isRunning()) { 
515 					throw new ComponentCantStartException("Event bus reset()ed but it's still not running.", log, this);
516 				}
517 			}
518 			setState(new AgentStateStartingPaused("Sending 'starting-paused' event."));
519 			startFailedTest();
520 			events.startingPausedTransactional();
521 			startFailedTest();
522 			setState(new AgentStateStartingPaused("Calling startPausedAgent()."));
523 			startFailedTest();
524 			startPausedAgent();
525 			startFailedTest();
526 			setState(new AgentStateStartingPaused("Sending 'paused' event."));
527 			startFailedTest();
528 			events.pausedTransactional();
529 			startFailedTest();
530 			setState(new AgentStateStartedPaused("Agent has started into paused state."));
531 			startFailedTest();
532 			if (log.isLoggable(Level.INFO)) log.info(runningComponents.size() + " component" + (runningComponents.size() > 1 ? "s" : "") + " has started along with the agent.");
533 		} catch (Exception e) {
534 			if (!inState(IAgentStateFailed.class) && !events.fatalError("Can't start-paused the agent", e)) {
535 				componentFatalError(new FatalErrorEvent(this, "agent's fatal error not propagated"));
536 			}
537 			if (e instanceof ComponentCantStartException) throw (ComponentCantStartException)e;
538 			throw new ComponentCantStartException("Can't start-paused: " + e.getMessage(), e, log, this);
539 		} finally {
540 			startMethodCalled = false;
541 		}
542 	}
543 	
544 	/**
545 	 * Called before any {@link AgentEvents#stopping()} event is broadcast. Hook that allows you to implement just-before-death stuff
546 	 * before any of agent component actually dies.
547 	 */
548 	protected void preStopAgent() {		
549 	}
550 	
551 	/**
552 	 * Stops the agent.
553 	 * <p><p>
554 	 * 1) switches state to {@link IAgentStateStopping}<p>
555 	 * 2) broadcasts {@link IStoppingEvent} (transactional)<p>
556 	 * 3) calls stopAgent()<p>
557 	 * 4) checks whether all components has stopped, if not - performs kill().
558 	 * 5) broadcasts {@link IStoppedEvent} (transactional)<p>
559 	 * 6) switches state to {@link IAgentStateStopped}<p>
560 	 * <p>
561 	 * Every agent's component that is still running must stop itself during (2) by broadcasting 'eventTransactional({@link IStoppedEvent})'.
562 	 * <p><p>
563 	 * If there will be some component that remains started after the propagation of {@link IStoppingEvent} the method
564 	 * will perform kill() method (counts as fatal error) and throws {@link AgentException}.
565 	 * <p><p>
566 	 * Prevents recursion.
567 	 * 
568 	 * @throws ComponentCantStopException
569 	 */
570 	@Override
571 	public final synchronized void stop() throws ComponentCantStopException {
572 		if (stopMethodCalled) return;
573 		
574 		if (inState(IAgentStateGoingDown.class, IAgentStateDown.class, IAgentStateInstantiated.class)) return;
575 		if (inState(IAgentStateGoingUp.class)) {
576 			throw new ComponentCantStopException("stop() requested during initialization, kill() the agent.", log, this);
577 		}
578 		if (notInState(IAgentStateUp.class)) {
579 			throw new ComponentCantStopException("Agent can't stop, it is in wrong state (" + getState().getFlag() + "), call start() first.", log, this);
580 		}
581 		
582 		stopMethodCalled = true;
583 		try {
584 			if (log.isLoggable(Level.WARNING)) log.warning("Stopping agent " + getComponentId().getToken());
585 			setState(new AgentStateStopping("stop() requested, calling preStopAgent()"));
586 			preStopAgent();
587 			setState(new AgentStateStopping("stop() requested, sending 'stopping' event"));
588 			events.stoppingTransactional();
589 			setState(new AgentStateStopping("Calling stopAgent()."));
590 			stopAgent();
591 			if (runningComponents.size() > 1) {
592 				StringBuffer sb = new StringBuffer();
593 				boolean first = true;
594 				for (IComponent component : runningComponents.values()) {
595 					if (component == this) continue;
596 					if (first) first = false;
597 					else sb.append(", ");
598 					sb.append(component.getComponentId().getToken());
599 					sb.append(":");
600 					sb.append(component);
601 				}
602 				ComponentCantStopException e = new ComponentCantStopException("Not all components has stopped along with the agent - components that did not send stopped event (id:toString): " + sb.toString(), log, this); 
603 				if (!events.fatalError(e)) {
604 					componentFatalError(new FatalErrorEvent(this, "agent's fatal error not propagated"));
605 				}
606 				throw e;
607 			}
608 			setState(new AgentStateStopping("Sending 'stopped' event."));
609 			events.stoppedTransactional();
610 			setState(new AgentStateStopped("Agent stopped."));
611 			if (jmx != null) {
612 				getJMX().unregisterJMX();
613 			}
614 			getLogger().removeDefaultNetworkHandler();
615 		} catch (Exception e) {
616 			if (!events.fatalError("Can't stop the agent.", e)) {
617 				componentFatalError(new FatalErrorEvent(this, "agent's fatal error not propagated"));
618 			}
619 			if (e instanceof ComponentCantStopException) throw ((ComponentCantStopException)e);
620 			throw new ComponentCantStopException("Can't stop.", e, log, this);
621 		} finally {
622 			stopMethodCalled = false;
623 			System.gc();
624 		}
625 	}
626 	
627 	private boolean stopMethodCalled = false;
628 	
629 	/**
630 	 * Called before any {@link AgentEvents#fatalError(String)} event is broadcast. Hook that allows you to implement just-before-death stuff
631 	 * before any of agent component actually dies.
632 	 */
633 	protected void preKillAgent() {		
634 	}
635 	
636 	/**
637 	 * Method that requests the agent to be killed - this counts as fatal error as well.
638 	 * <p><p>
639 	 * 1) switches state to {@link IAgentStateFailed}<p>
640 	 * 2) broadcast {@link IFatalErrorEvent}<p>
641 	 * 3) calls killAgent()<p>
642 	 * <p><p>
643 	 * This should be used to ultimately kill the agent in a ruthless way - components will usually perform some dirty tricks
644 	 * to kill themselves.
645 	 * <p><p>
646 	 * Prevents recursion.
647 	 * <p><p>
648 	 * <b>NEVER THROWS EXCEPTION</b>
649 	 */
650 	@Override
651 	public final synchronized void kill() {
652 		if (killMethodCalled) return;
653 		killMethodCalled = true;
654 		try {
655 			if (inState(IAgentStateFailing.class, IAgentStateFailed.class)) return; 
656 			try {
657 				if (log.isLoggable(Level.SEVERE)) log.severe("Killing agent " + getComponentId().getToken());
658 			} finally {
659 				try {
660 					preKillAgent();
661 				} finally {
662 					try {
663 						setState(new AgentStateFailing("kill() requested, sending fatal error event."));
664 					} finally {
665 						try {
666 							events.fatalError("agent kill() requested");
667 						} finally {
668 							try {
669 								setState(new AgentStateFailing("Calling killAgent()."));
670 							} finally {
671 								try {
672 									innerKillAgent();
673 								} finally {
674 									// CODE AFTER THIS POINT MUST BE PROPAGATED TO componentFatalError() as WELL!!!
675 									try {
676 										setState(new AgentStateFailed("Agent killed."));								
677 									} finally {
678 										if (jmx != null) {
679 											getJMX().unregisterJMX();
680 										}
681 									}
682 								}
683 							}
684 						}
685 					}
686 				}
687 			}
688 		} catch (Exception e) {
689 			if (e instanceof PogamutException) {
690 				((PogamutException) e).logExceptionOnce(log);
691 			} else {
692 				if (log.isLoggable(Level.SEVERE)) log.severe(ExceptionToString.process(e));
693 			}
694 		} finally { 
695 			killMethodCalled = false;
696 			System.gc();
697 		}
698 	}
699 	
700 	private boolean killMethodCalled = false;
701 		
702 	/**
703 	 * Pauses the agent - working only if the agent is in {@link IAgentStateRunning}
704 	 * <p><p>
705 	 * 1) switches state to {@link IAgentStatePausing}
706 	 * 2) broadcasts {@link IPausingEvent} (transactional)<p>
707 	 * 3) calls pauseAgent()<p>
708 	 * 4) broadcasts {@link IPausedEvent} (transactional)<p>
709 	 * 5) switches state to {@link IAgentPaused}
710 	 * <p><p>
711 	 * Prevents recursion.
712 	 * 
713 	 * @throws ComponentCantPauseException
714 	 */
715 	@Override
716 	public final synchronized void pause() throws ComponentCantPauseException {
717 		if (pauseMethodCalled) return;
718 		
719 		if (inState(IAgentStatePaused.class)) return;
720 		if (notInState(IAgentStateRunning.class)) {
721 			throw new ComponentCantPauseException("Agent can't pause, it is not in the running state but " + getState().getFlag() + ".", log, this);
722 		}
723 		
724 		pauseMethodCalled = true;
725 		try {
726 			setState(new AgentStatePausing("Sending 'pausing' event."));
727 			events.pausingTransactional();
728 			setState(new AgentStatePausing("Calling pauseAgent()."));
729 			pauseAgent();
730 			setState(new AgentStatePausing("Sending 'paused' event."));
731 			events.pausedTransactional();
732 			setState(new AgentStatePaused("Agent paused."));
733 		} catch (Exception e) {			
734 			if (!events.fatalError("Can't pause the agent", e)) {
735 				componentFatalError(new FatalErrorEvent(this, "agent's fatal error not propagated"));
736 			}
737 			if (e instanceof ComponentCantPauseException) throw ((ComponentCantPauseException)e);
738 			throw new ComponentCantPauseException("Can't pause.", e, log, this);
739 		} finally {
740 			pauseMethodCalled = false;
741 		}
742 	}
743 	
744 	private boolean pauseMethodCalled = false;
745 	
746 	/**
747 	 * Resumes the agent - working only if the agent is in {@link IAgentStatePaused}
748 	 * <p><p>
749 	 * 1) switches state to {@link IAgentStateResuming}<p>
750 	 * 2) broadcasts {@link IResumingEvent} (transactional)<p>
751 	 * 3) calls resumeAgent()<p>
752 	 * 4) broadcasts {@link IResumedEvent} (transactional)<p>
753 	 * 5) switches state to {@link IAgentStateRunning}
754 	 * <p><p>
755 	 * Prevents recursion.
756 	 * 
757 	 * @throws ComponentCantResumeException
758 	 */
759 	@Override
760 	public final synchronized void resume() throws ComponentCantResumeException {
761 		if (resumeMethodCalled) return;
762 		
763 		if (inState(IAgentStateRunning.class)) return;
764 		if (notInState(IAgentStatePaused.class)) {
765 			throw new ComponentCantResumeException("Agent can't resume, it is not in the paused state but " + getState().getFlag() + ".", log, this);				
766 		}
767 		
768 		resumeMethodCalled = true;
769 		try {
770 			setState(new AgentStateResuming("resume() requested, sending 'resuming' event."));
771 			events.resumingTransactional();
772 			setState(new AgentStateResuming("Calling resumeAgent()."));
773 			resumeAgent();
774 			setState(new AgentStateResuming("Sending 'resumed' event."));
775 			events.resumedTransactional();
776 			setState(new AgentStateResumed("Agent resumed."));
777 		} catch (Exception e) {			
778 			if (!events.fatalError("Can't resume the agent.", e)) {
779 				componentFatalError(new FatalErrorEvent(this, "agent's fatal error not propagated"));
780 			}
781 			if (e instanceof ComponentCantResumeException) throw ((ComponentCantResumeException)e);
782 			throw new ComponentCantResumeException("Can't resume.", e, log, this);
783 		} finally {
784 			resumeMethodCalled = false;
785 		}
786 	}
787 	
788 	private boolean resumeMethodCalled = false;
789 	
790 	////
791 	//
792 	// NEW PUBLIC METHODS
793 	//
794 	////
795 	
796 	/**
797 	 * Returns support class for the JMX feature of the agent. You may use it to
798 	 * register new JMX components of the agent or for enabling of the whole
799 	 * feature.
800 	 * 
801 	 * @return
802 	 */
803 	public final AgentJMXComponents getJMX() {
804 		if (jmx == null) {
805 			synchronized(jmxMutex) {
806 				if (jmx == null) {
807 					jmx = createAgentJMX();
808 					addJMXComponents();
809 				}
810 			}
811 		}
812 		return jmx;
813 	}
814 
815     /**
816      * This method is designed to wait for the agent to reach state 'awaitAgentState' (usually used with {@link IAgentStateUp}.
817      * <p><p>
818      * The call on this method will blocks until this instance of agent switches to the desired state.
819      * Nevertheless - if the agent switches itself to the {@link IAgentStateDown} state, it returns null.
820      * (if it is not the state you are awaiting for of course). If you find this unsuitable, use {@link WaitForAgentStateChange} directly.
821      * 
822      * @param awaitAgentState
823      * 
824      * @throws AgentException
825      */
826     public IAgentState awaitState(final Class awaitAgentState) throws AgentException {
827     	IAgentState state = getState().getFlag();
828     	if (awaitAgentState.isAssignableFrom(state.getClass())) return state;
829 		state = new WaitForFlagChange<IAgentState>(agentState, new WaitForFlagChange.IAccept<IAgentState>() {
830 
831 			@Override
832 			public boolean accept(IAgentState flagValue) {
833 				return awaitAgentState.isAssignableFrom(flagValue.getClass()) || flagValue instanceof IAgentStateDown;
834 			}
835 			
836 		}).await();    		
837    		if (awaitAgentState.isAssignableFrom(state.getClass())) return state;
838     	if (state instanceof IAgentStateDown) return null;
839     	throw new PogamutException("Agent is in unexpected state, not IAgentStateUp nor IAgentStateDown but " + state + ".", log, this);
840     }
841     
842     /**
843      * This method is designed to wait for the agent's initialization until till 'timeoutMillis'.
844      * <p><p>
845      * The call on this method will blocks until this instance of agent switches to the {@link IAgentStateUp}.
846      * Nevertheless - if the agent switches itself to the {@link IAgentStateDown} state, it returns null.  If you find this unsuitable, use {@link WaitForAgentStateChange} directly.
847      * <p><p>
848      * The method also returns null in the case of timeout.
849 	 *
850      * @param awaitAgentState 
851      * @param timeoutMillis how long we should wait for the agent to ini
852      * 
853      * @throws AgentException 
854      * @throws PogamutInterruptedException
855      */
856     public IAgentState awaitState(final Class awaitAgentState, long timeoutMillis) throws AgentException {
857     	IAgentState state = getState().getFlag();
858     	if (awaitAgentState.isAssignableFrom(state.getClass())) return state;
859 		state = new WaitForFlagChange<IAgentState>(agentState, new WaitForFlagChange.IAccept<IAgentState>() {
860 
861 			@Override
862 			public boolean accept(IAgentState flagValue) {
863 				return awaitAgentState.isAssignableFrom(flagValue.getClass()) || flagValue instanceof IAgentStateDown;
864 			}
865 			
866 		}).await(timeoutMillis, TimeUnit.MILLISECONDS);    		
867    		if (state == null) {
868    			return null;
869    		}
870    		if (awaitAgentState.isAssignableFrom(state.getClass())) return state;
871     	if (state instanceof IAgentStateDown) return null;
872     	throw new PogamutException("Agent is in unexpected state, not IAgentStateUp nor IAgentStateDown but " + state + ".", log, this);
873     }
874 
875     @Override
876     final public Folder getIntrospection() {
877     	 if(folder == null) {
878              folder = createIntrospection();
879          }
880         return folder;
881     }
882 
883     /**
884      * Create introspection root object.
885      * @return
886      */
887     protected Folder createIntrospection() {
888         return new ReflectionObjectFolder(INTROSPECTION_ROOT_NAME, this);
889     }
890     
891     ////
892     //
893     // PROTECTED - AGENT CONTROL METHODS
894     //
895     ////
896     
897     /**
898      * Called during start() method - override to provide custom starting behavior of the agent.
899      * <p><p>
900      * <b>WARNING:</b> DO NOT CALL ON YOUR OWN, ALWAYS USE PUBLIC INTERFACE (start()), but that should not be needed!
901      * <p><p>
902 	 * If you override this method <b>don't forget</b> to call
903 	 * <i>super.startAgent()</i> as the first method.
904      */
905     protected void startAgent() {
906     }
907     
908     /**
909      * Called during startPaused() method - override to provide custom starting-paused behavior of the agent.
910      * <p><p>
911      * <b>WARNING:</b> DO NOT CALL ON YOUR OWN, ALWAYS USE PUBLIC INTERFACE (startPaused()), but that should not be needed!
912      * <p><p>
913 	 * If you override this method <b>don't forget</b> to call
914 	 * <i>super.startPausedAgent()</i> as the first method.
915      */
916     protected void startPausedAgent() {
917     }
918     
919     /**
920      * Called during stop() method - override to provide custom stopping behavior of the agent.
921      * <p><p>
922      * <b>WARNING:</b> DO NOT CALL ON YOUR OWN, ALWAYS USE PUBLIC INTERFACE (stop()).
923      * <p><p>
924 	 * If you override this method <b>don't forget</b> to call
925 	 * <i>super.stopAgent()</i> as the first method.
926      */
927     protected void stopAgent() {    	
928     }
929     
930     /**
931      * Called during kill() method - override to provide custom ruthless stopping (killing) behavior of the agent.
932      * <p><p>
933      * <b>WARNING:</b> DO NOT CALL ON YOUR OWN, ALWAYS USE PUBLIC INTERFACE (kill()).
934      * <p><p>
935 	 * If you override this method <b>don't forget</b> to call
936 	 * <i>super.killAgent()</i> as the first method.
937      */
938     protected void killAgent() {    	
939     }
940     
941     /**
942      * This method is called to do some clean-up before actual {@link AbstractAgent#killAgent()} method is called.
943      */
944     private void innerKillAgent() {
945     	try {
946         	runningComponents.clear();          	
947     	} finally {
948     		killAgent();
949     	}
950     }
951     
952     /**
953      * Called during pause() method - override to provide custom pausing behavior of the agent.
954      * <p><p>
955      * <b>WARNING:</b> DO NOT CALL ON YOUR OWN, ALWAYS USE PUBLIC INTERFACE (pause()).
956      * <p><p>
957 	 * If you override this method <b>don't forget</b> to call
958 	 * <i>super.pauseAgent()</i> as the first method.
959      */
960     protected void pauseAgent() {
961     }
962     
963     /**
964      * Called during resume() method - override to provide custom resuming behavior of the agent.
965      * <p><p>
966      * <b>WARNING:</b> DO NOT CALL ON YOUR OWN, ALWAYS USE PUBLIC INTERFACE (resume()).
967      * <p><p>
968 	 * If you override this method <b>don't forget</b> to call
969 	 * <i>super.resumeAgent()</i> as the first method.
970      */
971     protected void resumeAgent() {
972     }
973     
974     /**
975      * Called whenever the {@link IComponentBus} broadcast {@link IResetEvent} to reset all agent's components as well
976      * as an agent. Clean up your private data structure, get ready to be started again.
977      * <p><p>
978      * <b>WARNING:</b> DO NOT CALL ON YOUR OWN, CALLED FROM THE {@link AbstractAgent#resetEvent(IResetEvent)} AUTOMATICALLY.
979      * <p><p>
980 	 * If you override this method <b>don't forget</b> to call
981 	 * <i>super.resetAgent()</i> as the first method.
982      */
983     protected void resetAgent() {
984     	
985     }
986     
987     ////
988     //
989     //  PROTECTED - EVENTS HANDLING METHODS
990     //
991     ////
992     
993 	/**
994 	 * Called whenever some component that was not started before broadcasts {@link IStartedEvent}
995 	 * <p><p>
996 	 * If you override this method <b>don't forget</b> to call
997 	 * <i>super.componentStarted(event)</i> as the first method.
998 	 * 
999 	 * @param event
1000 	 */
1001 	protected synchronized void componentStarted(IStartedEvent event) {
1002 		// ADD COMPONENT INTO AGENT'S COMPONENT
1003 		synchronized(runningComponents) {
1004 			if (runningComponents.containsKey(event.getSource().getComponentId())) return;
1005 			runningComponents.put(event.getSource().getComponentId(), event.getSource());
1006 		}
1007 		// PROBE JMX
1008 		if (event.getSource() instanceof IJMXEnabled) {
1009 			synchronized(getJMX()) {
1010 				jmx.addComponent((IJMXEnabled) event.getSource());
1011 			}
1012 		}
1013 	}
1014 	
1015 	/**
1016 	 * Called whenever some component that was not started before broadcasts {@link IPausedEvent}
1017 	 * <p><p>
1018 	 * If you override this method <b>don't forget</b> to call
1019 	 * <i>super.componentStarted(event)</i> as the first method.
1020 	 * 
1021 	 * @param event
1022 	 */
1023 	protected synchronized void componentStarted(IPausedEvent event) {
1024 		// ADD COMPONENT INTO AGENT'S COMPONENT
1025 		synchronized(runningComponents) {
1026 			if (runningComponents.containsKey(event.getSource().getComponentId())) return;
1027 			runningComponents.put(event.getSource().getComponentId(), event.getSource());
1028 		}
1029 		// PROBE JMX
1030 		if (event.getSource() instanceof IJMXEnabled) {
1031 			synchronized(getJMX()) {
1032 				jmx.addComponent((IJMXEnabled) event.getSource());
1033 			}
1034 		}
1035 	}
1036 
1037 	/**
1038 	 * Called whenever some component broadcasts {@link IStoppingEvent}
1039 	 * <p><p>
1040 	 * If you override this method <b>don't forget</b> to call
1041 	 * <i>super.componentStopping(event)</i> as the first method.
1042 	 * 
1043 	 * @param event
1044 	 */
1045 	protected synchronized void componentStopping(IStoppingEvent event) {
1046 		if (stopMethodCalled) return;
1047 		synchronized(stopDependencyToken) {
1048 			if (stopDependencyToken.contains(event.getSource().getComponentId())) {
1049 				if (log.isLoggable(Level.WARNING)) log.warning("Component " + event.getSource().getComponentId().getToken() + " that the agent depends on is stopping, stopping agent as well.");
1050 				stop();
1051 				return;
1052 			}
1053 		}
1054 		synchronized(stopDependencyClass) {
1055 			Class dependency = stopDependencyClass.containsClass(event.getSource().getClass());
1056 			if (dependency != null) {
1057 				if (log.isLoggable(Level.WARNING)) log.warning("Component of class " + dependency.getSimpleName() + " (id: " + event.getSource().getComponentId().getToken() + ") that tghe agent depends on is stopping, stopping agent as well.");
1058 				stop();
1059 				return;
1060 			}
1061 		}
1062 	}
1063 	
1064 	/**
1065 	 * Called whenever component that was running broadcasts {@link IStoppedEvent}.
1066 	 * <p><p>
1067 	 * If you override this method <b>don't forget</b> to call
1068 	 * <i>super.componentStopped(event)</i> as the first method.
1069 	 * 
1070 	 * @param event
1071 	 */
1072 	protected synchronized void componentStopped(IStoppedEvent event) {
1073 		// REMOVE FROM RUNNING
1074 		synchronized(runningComponents) {
1075 			runningComponents.remove(event.getSource().getComponentId());
1076 			if (runningComponents.size() == 0) {
1077 				if (log.isLoggable(Level.WARNING)) log.warning("All agent's components has stopped. Stopping agent as well.");
1078 				stop();
1079 			}
1080 		}
1081 	}
1082 	
1083 	/**
1084 	 * Called whenever some comopnent broadcasts {@link IFatalErrorEvent}.
1085 	 * <p><p>
1086 	 * If you override this method <b>don't forget</b> to call
1087 	 * <i>super.fatalError(event)</i> as the first method.
1088 	 * 
1089 	 * @param event
1090 	 */
1091 	protected void componentFatalError(IFatalErrorEvent event) {
1092 		if (inState(IAgentStateFailing.class)) return; // prevent recursion
1093 		try {
1094 			setState(new AgentStateFailing(event.getMessage() + ", calling killAgent()."));
1095 		} finally {
1096 			try {
1097 				preKillAgent();
1098 			} finally {
1099 				try {
1100 					innerKillAgent();
1101 				} finally {
1102 					// CODE AFTER THIS POINT MUST BE PROPAGETED TO kill() AS WELL !!!
1103 					try {
1104 						setState(new AgentStateFailed(event.getMessage()));								
1105 					} finally {
1106 						if (jmx != null) {
1107 							getJMX().unregisterJMX();
1108 						}
1109 					}				
1110 				}
1111 			}
1112 		}
1113 	}
1114 	
1115 	protected void resetEvent(IResetEvent event) {
1116 		resetAgent();
1117 	}
1118 	
1119 	////
1120 	//
1121 	// PROTECTED - AGENT INNER METHODS
1122 	// 
1123 	////
1124 	
1125 	protected AgentJMXComponents createAgentJMX() {
1126 		return new AgentJMXComponents(this);
1127 	}
1128     
1129     protected void addDependency(IComponent component) {
1130     	NullCheck.check(component, "component");
1131     	addDependency(component.getComponentId());
1132     }
1133     
1134     protected void addDependency(Class componentClass) {
1135     	NullCheck.check(componentClass, "componentClass");
1136     	synchronized(stopDependencyClass) {
1137     		stopDependencyClass.add(componentClass);
1138     	}
1139     }
1140     
1141     protected void addDependency(IToken componentId) {
1142     	NullCheck.check(componentId, "componentId");
1143     	synchronized(stopDependencyToken) {
1144     		stopDependencyToken.add(componentId);
1145     	}
1146     }
1147 	
1148 	/**
1149 	 * Sets the state of the agent ... note that the flag is private field so
1150 	 * you can't change it directly.
1151 	 * 
1152 	 * @param state
1153 	 */
1154 	protected void setState(AgentState state) {		
1155 		synchronized(agentState) {
1156 			if (log.isLoggable(Level.FINER)) log.finer("Agent state is going to be switched to: " + state.toString());
1157 			this.agentState.setFlag(state);
1158 			if (log.isLoggable(Level.INFO)) log.info("Agent state switched to: " + state.toString());
1159 		}		
1160 	}
1161 	
1162     /**
1163 	 * Called when AgentJMX (field jmx) is instantiated to populate it with
1164 	 * agent's JMX enabled components.
1165 	 * <p><p>
1166 	 * Currently two components are added:
1167 	 * <ol>
1168 	 * <li>agent's logger</li>
1169 	 * <li>agent's introspection</li>
1170 	 * </ol>
1171 	 * <p><p>
1172 	 * If you override this method <b>don't forget</b> to call
1173 	 * <i>super.addJMXComponents()</i> as the first method.
1174 	 * <p><p>
1175 	 * Note that you don't need to override this method to introduce new jmx components if and only if:<p>
1176 	 * 1) you do not need the component before the agent starts up<p>
1177 	 * 2) your JMX component is also an {@link IComponent} that starts together with the agent<p>
1178 	 * If (2) holds, the component will be added to the 'jmx' by the agent automatically and if jmx is already started
1179 	 * it will start it as well. 
1180 	 */
1181 	protected void addJMXComponents() {
1182 		jmx.addComponent(logger);
1183 		jmx.addComponent(new FolderToIJMXEnabledAdapter(getIntrospection()));
1184 	}
1185 	
1186 	@Override
1187 	public String toString() {
1188 		if (this == null) return "AbstractAgent[constructing]";
1189 		else return getClass().getSimpleName() + "[" + getName() + "]";
1190 	}
1191     
1192 }