View Javadoc

1   package cz.cuni.amis.pogamut.base.component.controller;
2   
3   import java.util.HashMap;
4   import java.util.Map;
5   import java.util.logging.Level;
6   import java.util.logging.Logger;
7   
8   import cz.cuni.amis.pogamut.base.communication.mediator.IMediator;
9   import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView;
10  import cz.cuni.amis.pogamut.base.component.IComponent;
11  import cz.cuni.amis.pogamut.base.component.bus.IComponentBus;
12  import cz.cuni.amis.pogamut.base.component.bus.IComponentEvent;
13  import cz.cuni.amis.pogamut.base.component.bus.IComponentEventListener;
14  import cz.cuni.amis.pogamut.base.component.bus.event.ComponentBusEvents;
15  import cz.cuni.amis.pogamut.base.component.bus.event.IFatalErrorEvent;
16  import cz.cuni.amis.pogamut.base.component.bus.event.IPausedEvent;
17  import cz.cuni.amis.pogamut.base.component.bus.event.IPausingEvent;
18  import cz.cuni.amis.pogamut.base.component.bus.event.IResetEvent;
19  import cz.cuni.amis.pogamut.base.component.bus.event.IResumedEvent;
20  import cz.cuni.amis.pogamut.base.component.bus.event.IResumingEvent;
21  import cz.cuni.amis.pogamut.base.component.bus.event.IStartedEvent;
22  import cz.cuni.amis.pogamut.base.component.bus.event.IStartingEvent;
23  import cz.cuni.amis.pogamut.base.component.bus.event.IStartingPausedEvent;
24  import cz.cuni.amis.pogamut.base.component.bus.event.IStoppedEvent;
25  import cz.cuni.amis.pogamut.base.component.bus.event.IStoppingEvent;
26  import cz.cuni.amis.pogamut.base.component.bus.event.impl.FatalErrorEvent;
27  import cz.cuni.amis.pogamut.base.component.exception.ComponentCantPauseException;
28  import cz.cuni.amis.pogamut.base.component.exception.ComponentCantResumeException;
29  import cz.cuni.amis.pogamut.base.component.exception.ComponentCantStartException;
30  import cz.cuni.amis.pogamut.base.component.exception.ComponentCantStopException;
31  import cz.cuni.amis.pogamut.base.component.lifecyclebus.ILifecycleBus;
32  import cz.cuni.amis.utils.NullCheck;
33  import cz.cuni.amis.utils.token.IToken;
34  import cz.cuni.amis.utils.token.Tokens;
35  
36  /**
37   * Provides simple way for components to start/stop automatically based on the status of objects
38   * they depends on (e.g. {@link IWorldView} may start only if underlying {@link IMediator} has been started).
39   * <p><p>
40   * Dependents may be identified by {@link IToken} or {@link IComponent}.
41   * <p><p>
42   * Component controller ease the burden with starting/stopping the component in the right time and the broadcasting
43   * of appropriate events and tracking its {@link ComponentState}.
44   * <p><p>
45   * It allows you to specify starting-dependencies of your {@link IComponentControlHelper} allowing you to define the moment
46   * when the component should start/stop. Moreover - it automatically watching the {@link IComponentBus} for
47   * {@link IFatalErrorEvent} killing your component when a fatal event is caught (but it will call kill only iff fatal error is 
48   * produced by different component then the controlled one).
49   * <p><p>
50   * Additionally the controller is broadcasting starting/stopping events automatically.
51   * <p><p>
52   * If you wish to manually stop the component for whatever reason - call manualStop() method, it will
53   * broadcast {@link IStoppingEvent} and {@link IStopeedEvent} automatically.
54   * <p><p>
55   * <p>
56   * The controlled component goes through various states during its life-cycle.
57   * <p><p>
58   * Initial state of the component {@link ComponentState.RESETED}.
59   * <p><p>
60   * Usual life-cycle of the component is:<p>
61   * INSTANTIATED (or RESETED or STOPPED) -> STARTING -> RUNNING -> [PAUSING -> PAUSED -> RESUMING -> RUNNING] -> STOPPING -> STOPPED<p>
62   * Note that the component might be started again from the STOPPED state or may be stopped from PAUSED state.
63   * <p><p>
64   * Also the component might be started to paused state, the lifecycle is then is:<p>
65   * INSTANTIATED (or RESETED or STOPPED) -> STARTING_PAUSED -> PAUSED -> RESUMING -> RUNNING -> ....
66   * <p><p>
67   * If {@link IFatalErrorEvent} is got or raised by the component through {@link ComponentController#fatalError}, the state is switched 
68   * to -> KILLING -> KILLED and may continue only with -> RESETING -> RESETED.
69   * <p><p>
70   * Various corresponding methods from {@link IComponentControlHelper} are during state transitions.
71   * <p><p>
72   * INSTANTIATED (or RESETED or STOPPED) -> preStart() -> STARTING -> start() -> RUNNING -> prePause() -> PAUSING -> pause() -> PAUSED -> preResume() -> RESUMING -> resume() -> RUNNING -> preStop() -> STOPPING -> stop()
73   * -> STOPPED
74   * <p><p>
75   * (similarly for start-paused) INSTANTIATED (or RESETED or STOPPED) -> preStartPaused() -> STARTING_PAUSED -> startPaused() -> PAUSED -> ...
76   * <p><p>
77   * If {@link IFatalErrorEvent} is got (or raised by the controlled component), 
78   * the state is switched (and methods are called) ... -> KILLING -> kill() (not called in case of fatalError() method invocation) -> KILLED and
79   * then it may continue only with -> RESETING -> reset() -> RESETED.
80   * <p><p>
81   * Furthermore corresponding {@link IComponentEvent}s are broadcast to the underlying {@link IComponentBus}. Thus the complete
82   * life/event/method-cycle looks like this:
83   * <p><p>
84   * INSTANTIATED (or RESETED) -> STARTING -> {@link IStartingEvent} -> start() -> RUNNING -> {@link IStartedEvent} -> prePause() -> PAUSING -> {@link IPausingEvent}
85   * -> pause() -> PAUSED -> {@link IPausedEvent} -> RESUMING -> {@link IResumingEvent} -> resume() -> RUNNING -> {@link IResumedEvent} -> preStop()
86   * -> STOPPING -> {@link IStoppingEvent} -> stop() -> STOPPED -> {@link IStoppedEvent}
87   * <p><p>
88   * Last pieces of information:<p>
89   * 1) INSTANTIATED (or RESETED) -> STARTING + STOPPED -> STARTING transition is triggered only if dependencies are starting / has started.
90   * 2) RUNNING -> PAUSING transition is triggered whenever some dependency broadcasts {@link IPausingEvent} or {@link IPausedEvent}.<p>
91   * 3) PAUSED -> RESUMING transition is triggered when all dependencies are resuming / has resumed.<p>
92   * 4) RUNNING | PAUSED -> STOPPING transition is triggered whenever some dependency broadcasts {@links IStoppingEvent} or {@link IStoppedEvent} or
93   *    when the stop is manually required by calling {@link ComponentController#manualStop} method.<p>
94   * 5) any state -> KILLING transition is triggered whenever some component broadcasts {@link IFatalErrorEvent} or the component reports
95   *    that fatal error has happened through one of {@link ComponentController}.fatalError() method.<p>
96   * 6) kill() method is called only if OTHER component broadcasts {@link IFatalErrorEvent} - the kill() method is not called
97   *    when the fatal error is raised manually by the component via one of fatalError() methods.<p>
98   * 7) KILLED -> RESETING transition is triggered by {@link IResetEvent}<p>
99   * 8) there is a specific transition (STARTING | RUNNING | PAUSING | PAUSED | RESUMING ) -> STOPPING transition that may be triggered
100  *    by {@link IStoppingEvent} or {@link IStoppedEvent} or by manually calling {@link ComponentController#manualStop(String)} method
101  *    from within the component.
102  * 9) not mentioned transitions (in the whole javadoc) are non-existing (e.g. there is no such transition such as STARTING -> PAUSED,
103  *    etc.). Note that some transition can't even happen because {@link IComponenBus} is processing one event at time.<p>
104  * <p><p>
105  * The comopnent life-cycle looks complex but it is driven by simple idea that we have to control the process of starting/pausing/resuming/stopping of
106  * {@link IComponent}s. Hopefully it works like you would expect it to work.   
107  * <p><p><p>
108  * The controller is also {@link IComponent} but that is just a technical detail - whenever a fatal error happens in the logic
109  * of starting/stopping/pausing/resuming/etc. of components the controller raises the fatal error under own id.
110  * 
111  * @author Jimmy
112  */
113 public class ComponentController<COMPONENT extends IComponent> extends AbstractComponentControllerBase<COMPONENT> implements IComponentController<COMPONENT> {
114 
115 	/**
116 	 * Mutex used for synchronization.
117 	 */
118 	private Object ctrlMutex = new Object();
119 	
120 	/**
121 	 * Provided dependencies of the controlled component.
122 	 */
123 	private ComponentDependencies dependencies;
124 	
125 	/**
126 	 * Map tracking the states of dependencies.
127 	 */
128 	private Map<IToken, ComponentState> dependencyState = new HashMap<IToken, ComponentState>();
129 	
130 	/**
131 	 * Map tracking count of states of dependencies.
132 	 */
133 	private Map<ComponentState, Integer> stateCount = new HashMap<ComponentState, Integer>();
134 	
135 	/**
136 	 * Stores the fatal error that triggered the system failure.
137 	 */
138 	private IFatalErrorEvent lastFatalError = null;
139 	
140 	//
141 	// EVENT LISTENERS
142 	//
143 	
144 	private IComponentEventListener<IStartingEvent> startingListener = new IComponentEventListener<IStartingEvent>() {
145 
146 		@Override
147 		public void notify(IStartingEvent event) {
148 			startingEvent(event, false);
149 		}
150 		
151 	};
152 	
153 	private IComponentEventListener<IStartingPausedEvent> startingPausedListener = new IComponentEventListener<IStartingPausedEvent>() {
154 
155 		@Override
156 		public void notify(IStartingPausedEvent event) {
157 			startingPausedEvent(event);
158 		}
159 		
160 	};
161 	
162 	private IComponentEventListener<IStartedEvent> startedListener = new IComponentEventListener<IStartedEvent>() {
163 
164 		@Override
165 		public void notify(IStartedEvent event) {
166 			startedEvent(event);
167 		}
168 		
169 	};
170 	
171 	private IComponentEventListener<IPausingEvent> pausingListener = new IComponentEventListener<IPausingEvent>() {
172 
173 		@Override
174 		public void notify(IPausingEvent event) {
175 			pausingEvent(event, false);
176 		}
177 		
178 	};
179 	
180 	private IComponentEventListener<IPausedEvent> pausedListener = new IComponentEventListener<IPausedEvent>() {
181 
182 		@Override
183 		public void notify(IPausedEvent event) {
184 			pausedEvent(event);
185 		}
186 		
187 	};
188 	
189 	private IComponentEventListener<IResumingEvent> resumingListener = new IComponentEventListener<IResumingEvent>() {
190 
191 		@Override
192 		public void notify(IResumingEvent event) {
193 			resumingEvent(event, false);
194 		}
195 		
196 	};
197 	
198 	private IComponentEventListener<IResumedEvent> resumedListener = new IComponentEventListener<IResumedEvent>() {
199 
200 		@Override
201 		public void notify(IResumedEvent event) {
202 			resumedEvent(event);
203 		}
204 		
205 	};
206 	
207 	private IComponentEventListener<IStoppingEvent> stoppingListener = new IComponentEventListener<IStoppingEvent>() {
208 
209 		@Override
210 		public void notify(IStoppingEvent event) {
211 			stoppingEvent(event, false);
212 		}
213 		
214 	};
215 	
216 	private IComponentEventListener<IStoppedEvent> stoppedListener = new IComponentEventListener<IStoppedEvent>() {
217 
218 		@Override
219 		public void notify(IStoppedEvent event) {
220 			stoppedEvent(event);
221 		}
222 		
223 	};
224 	
225 	private IComponentEventListener<IFatalErrorEvent> fatalErrorListener = new IComponentEventListener<IFatalErrorEvent>() {
226 
227 		@Override
228 		public void notify(IFatalErrorEvent event) {
229 			fatalErrorEvent(event);
230 		}
231 		
232 	};
233 	
234 	private IComponentEventListener<IResetEvent> resetEventListener = new IComponentEventListener<IResetEvent>() {
235 
236 		@Override
237 		public void notify(IResetEvent event) {
238 			resetEvent(event);
239 		}
240 		
241 	};
242 	
243 	private IComponentBus bus;
244 
245 	private ComponentBusEvents componentEvents;	
246 	
247 	/**
248 	 * If you're using {@link ILifecycleBus} (not only {@link IComponentBus}, you may create this {@link ComponentController} even after some 'dependencies' has started
249 	 * as {@link ILifecycleBus} allows us to retrieve current state of dependencies, so we're able to start the component during the construction
250 	 * if dependencies are already met.
251 	 * 
252 	 * @param component controlled component
253 	 * @param componentControlHelper object controlling the 'component' (contains lifecycle methods which controls the component)
254 	 * @param bus bus of the component
255 	 * @param log logger for the class
256 	 * @param dependencyType type of the dependency (YOU MUST KNOW THE SEMANTICS OF THIS ENUM, see {@link ComponentDependencyType})
257 	 * @param dependencies {@link IToken} or {@link Class} of components the 'component' depends on
258 	 */
259 	public ComponentController(COMPONENT component, IComponentControlHelper componentControlHelper, ILifecycleBus bus, Logger log, ComponentDependencyType dependencyType, Object... dependencies) {
260 		this(component, componentControlHelper, bus, log, new ComponentDependencies(dependencyType, dependencies));
261 	}
262 	
263 	/**
264 	 * If you're using {@link ILifecycleBus} (not only {@link IComponentBus}, you may create this {@link ComponentController} even after some 'dependencies' has started
265 	 * as {@link ILifecycleBus} allows us to retrieve current state of dependencies, so we're able to start the component during the construction
266 	 * if dependencies are already met.
267 	 * 
268 	 * @param component controlled component
269 	 * @param componentControlHelper object controlling the 'component' (contains lifecycle methods which controls the component)
270 	 * @param bus bus of the component
271 	 * @param log logger for the class
272 	 * @param dependencies dependencies of the component
273 	 */
274 	public ComponentController(COMPONENT component, IComponentControlHelper componentControlHelper, ILifecycleBus bus, Logger log, ComponentDependencies dependencies) {
275 		this(component, componentControlHelper, (IComponentBus)bus, log, dependencies);
276 	}
277 
278 	/**
279 	 * If you use only {@link IComponentBus} (not {@link ILifecycleBus}, you must create this {@link ComponentController} before any of 'dependencies' is started
280 	 * as there is no way how to retrieve state of component from 'dependencies' so we will assume that all are in state {@link ComponentState#INSTANTIATED}.
281 	 * 
282 	 * @param component controlled component
283 	 * @param componentControlHelper object controlling the 'component' (contains lifecycle methods which controls the component)
284 	 * @param bus bus of the component
285 	 * @param log logger to be used by this class
286 	 * @param dependencyType type of the dependency (YOU MUST KNOW THE SEMANTICS OF THIS ENUM, see {@link ComponentDependencyType})
287 	 * @param dependencies {@link IToken} or {@link Class} of components the 'component' depends on
288 	 */
289 	public ComponentController(COMPONENT component, IComponentControlHelper componentControlHelper, IComponentBus bus, Logger log, ComponentDependencyType dependencyType, Object... dependencies) {
290 		this(component, componentControlHelper, bus, log, new ComponentDependencies(dependencyType, dependencies));
291 	}
292 	
293 	/**
294 	 * If you use only {@link IComponentBus} (not {@link ILifecycleBus}, you must create this {@link ComponentController} before any of 'dependencies' is started
295 	 * as there is no way how to retrieve state of component from 'dependencies' so we will assume that all are in state {@link ComponentState#INSTANTIATED}.
296 	 *  
297 	 * @param component controlled component
298 	 * @param componentControlHelper object controlling the 'component' (contains lifecycle methods which controls the component)
299 	 * @param bus bus of the component
300 	 * @param log logger to be used by this class
301 	 * @param dependencies dependencies of the component
302 	 */
303 	public ComponentController(COMPONENT component, IComponentControlHelper componentControlHelper, IComponentBus bus, Logger log, ComponentDependencies dependencies) {
304 		super(Tokens.get(component.getComponentId().getToken() + "-controller"), component, componentControlHelper, log);
305 		// FOLLOWING CODE COVERS ALSO ILifecycleBus INITIALIZATION
306 		
307 		// save private fields
308 		this.bus = bus;
309 		NullCheck.check(this.bus, "bus");
310 		this.dependencies = dependencies;
311 		NullCheck.check(this.dependencies, "dependencies");
312 		
313 		// register the component
314 		this.bus.register(component);
315 		// register the controller
316 		this.bus.register(this);
317 		
318 		// create event broadcasting objects
319 		this.componentEvents = new ComponentBusEvents(bus, this.component, log);
320 		
321 		// initialize state counts
322 		for (ComponentState state : ComponentState.values()) {
323 			stateCount.put(state, 0);
324 		}
325 		stateCount.put(ComponentState.INSTANTIATED, dependencies.getCount());
326 		
327 		// add initial dependency state
328 		for (IToken dependency : dependencies.getDependencies()) {
329 			dependencyState.put(dependency, ComponentState.INSTANTIATED);
330 		}
331 		
332 		// hook global listeners
333 		this.bus.addEventListener(IFatalErrorEvent.class, fatalErrorListener);
334 		this.bus.addEventListener(IResetEvent.class,      resetEventListener);
335 		
336 		synchronized(ctrlMutex) {
337 			// hook event listeners
338 			for (IToken dependency : dependencies.getDependencies()) {
339 				this.bus.addEventListener(IStartingEvent.class,       dependency, startingListener);
340 				this.bus.addEventListener(IStartingPausedEvent.class, dependency, startingPausedListener);
341 				this.bus.addEventListener(IStartedEvent.class,        dependency, startedListener);
342 				this.bus.addEventListener(IStoppingEvent.class,       dependency, stoppingListener);
343 				this.bus.addEventListener(IStoppedEvent.class,        dependency, stoppedListener);
344 				this.bus.addEventListener(IPausingEvent.class,        dependency, pausingListener);
345 				this.bus.addEventListener(IPausedEvent.class,         dependency, pausedListener);
346 				this.bus.addEventListener(IResumingEvent.class,       dependency, resumingListener);
347 				this.bus.addEventListener(IResumedEvent.class,        dependency, resumedListener);
348 			}	
349 		
350 			// if the bus is ILifecycleBus ... 
351 			if (bus instanceof ILifecycleBus) {
352 				// ... correctly initialize dependency states
353 				for (IToken dependency : dependencies.getDependencies()) {
354 					// add initial dependency state
355 					setDependencyState(dependency, ((ILifecycleBus)bus).getComponentState(dependency).getFlag());
356 				}
357 				
358 				// ... and check whether we should not start the component
359 				if (inState(ComponentState.INSTANTIATED, ComponentState.RESETED)) {
360 					// check whether we should not start the component
361 					if (dependencies.getType() == ComponentDependencyType.STARTS_WITH) {
362 						// STARTS_WITH
363 						if (getStateCount(ComponentState.STARTING, ComponentState.RESUMING, ComponentState.RUNNING) == dependencies.getCount()) {
364 							start(false);
365 						} else 
366 						if (getStateCount(ComponentState.STARTING, ComponentState.STARTING_PAUSED, ComponentState.PAUSING, ComponentState.PAUSED, ComponentState.RESUMING, ComponentState.RUNNING) == dependencies.getCount()) {
367 							startPaused(false);
368 						}
369 					} else {
370 						// STARTS_AFTER
371 						if (getStateCount(ComponentState.RUNNING) == dependencies.getCount()) {
372 							start(false);
373 						} else 
374 						if (getStateCount(ComponentState.PAUSED, ComponentState.RUNNING) == dependencies.getCount()) {
375 							startPaused(false);
376 						}
377 					}
378 				}
379 			}
380 		}
381 		
382 		if (log.isLoggable(Level.INFO)) log.info("In state " + componentState.getFlag() + ".");
383 	}
384 	
385 	//
386 	//
387 	// PUBLIC INTERFACE - IComponentControllerBase
388 	//
389 	//
390 	
391 	@Override
392 	public void setBroadcastingEvents(boolean broadcastingEvents) {
393 		super.setBroadcastingEvents(broadcastingEvents);
394 		componentEvents.setBroadcasting(broadcastingEvents);
395 	}
396 	
397 	@Override
398 	public IFatalErrorEvent getFatalError() {
399 		return lastFatalError;
400 	}
401 	
402 	@Override
403 	public void manualStart(String reason) {
404 		synchronized(ctrlMutex) {
405 			if (notInState(ComponentState.KILLED, ComponentState.STOPPED, ComponentState.RESETED, ComponentState.INSTANTIATED)) {
406 				if (log.isLoggable(Level.WARNING)) log.warning("Start requested, but the component is in state " + componentState.getFlag() + ", unsupported.");
407 			} else {
408 				if (log.isLoggable(Level.WARNING)) log.warning("Start requested.");
409 				if (inState(ComponentState.KILLED)) {
410 					if (log.isLoggable(Level.WARNING)) log.warning("Component is in state " + ComponentState.KILLED + ", resetting.");
411 					reset();
412 				}
413 				start(true);
414 			}
415 		}		
416 	}
417 	
418 	@Override
419 	public void manualStartPaused(String reason) {
420 		synchronized(ctrlMutex) {
421 			if (notInState(ComponentState.KILLED, ComponentState.STOPPED, ComponentState.RESETED, ComponentState.INSTANTIATED)) {
422 				if (log.isLoggable(Level.WARNING)) log.warning("Start-paused requested, but the component is in state " + componentState.getFlag() + ", unsupported.");
423 			} else {
424 				if (log.isLoggable(Level.WARNING)) log.warning("Start-paused requested.");
425 				if (inState(ComponentState.KILLED)) {
426 					if (log.isLoggable(Level.WARNING)) log.warning("Component is in state " + ComponentState.KILLED + ", resetting.");
427 					reset();
428 				}
429 				startPaused(true);
430 			}
431 		}		
432 	}
433 	
434 	@Override
435 	public void manualStop(String reason) {
436 		synchronized(ctrlMutex) {
437 			if (inState(ComponentState.RUNNING, ComponentState.PAUSED)) {
438 				if (log.isLoggable(Level.INFO)) log.info("Stop requested, reason: " + reason);
439 				stop(true);
440 			} else {
441 				if (log.isLoggable(Level.WARNING)) log.warning("Stop requested, but the component is in state " + componentState.getFlag() + ", unsupported.");
442 			}
443 		}
444 	}
445 	
446 	@Override
447 	public void manualKill(String reason) {
448 		if (log.isLoggable(Level.SEVERE)) log.severe("Kill requested, reason: " + reason);
449 		kill(true);		
450 	}
451 	
452 	@Override
453 	public void manualPause(String reason) {
454 		synchronized(ctrlMutex) {
455 			if (inState(ComponentState.RUNNING)) {
456 				if (log.isLoggable(Level.INFO)) log.info(id(component) + " pause requested, reason: " + reason);
457 				pause(true);
458 			} else {
459 				if (log.isLoggable(Level.WARNING)) log.warning(id(component) + " pause requested, but the component is in state " + componentState.getFlag() + ", unsupported.");
460 			}
461 		}
462 	}
463 	
464 	@Override
465 	public void manualResume(String reason) {
466 		synchronized(ctrlMutex) {
467 			if (inState(ComponentState.PAUSED)) {
468 				if (log.isLoggable(Level.INFO)) log.info(id(component) + " resume requested, reason: " + reason);
469 				resume(true);
470 			} else {
471 				if (log.isLoggable(Level.WARNING)) log.warning(id(component) + " resume requested, but the component is in state " + componentState.getFlag() + ", unsupported.");
472 			}
473 		}
474 	}
475 	
476 	@Override
477 	public void fatalError(String message) {
478 		fatalError(message, null);
479 	}
480 	
481 	@Override
482 	public void fatalError(String message, Throwable e) {
483 		// EARLY RETURN (do not dead-lock on multi fatal-errors)
484 		try {
485 			if (inState(ComponentState.KILLING, ComponentState.KILLED)) return;
486 		} catch (Exception e0) {
487 		}
488 		synchronized(ctrlMutex) {
489 			// OK, we're not in the state KILLING or KILLED ... meaning that one thread from fatal-error-threads reaches here 
490 			try {
491 				// Are we first here?
492 				if (inState(ComponentState.KILLING, ComponentState.KILLED)) return;
493 			} catch (Exception e0) {
494 			}
495 			// Yes we are ... let's mark the component to be killed soon.
496 			try { 
497 				setState(ComponentState.KILLING); 
498 			} catch (Exception e1) {
499 			}	
500 		}
501 		
502 		// WE ARE ALONE HERE!
503 		
504 		try {
505 			if (log.isLoggable(Level.SEVERE)) log.severe("Fatal error in " + id(component) + ": " + message);
506 		} catch (Exception e2) {
507 		}
508 		try {
509 			control.kill();
510 		} catch (Exception e3) {			
511 		}		
512 		try {
513 			lastFatalError = new FatalErrorEvent<IComponent>(component, message, e);
514 			// Important: DO NOT USE 'componentEvents' here! They may have been disabled
515 			this.bus.event(lastFatalError);
516 		} catch (Exception e5) {			
517 		}		
518 		try { 
519 			setState(ComponentState.KILLED); 
520 		} catch (Exception e6) {
521 		}
522 	}
523 	
524 	//
525 	//
526 	// PUBLIC INTERFACE - IComponentController
527 	//
528 	//
529 	
530 	@Override
531 	public boolean isDependent(IToken token) {
532 		return dependencies.isDependency(token);
533 	}
534 	
535 	@Override
536 	public boolean isDependent(IComponent component) {
537 		return dependencies.isDependency(component);
538 	}
539 	
540 	//
541 	//
542 	// IMPLEMENTATION
543 	//
544 	//
545 	
546 	/**
547 	 * Returns how many component we're depending on are in any of 'states'.
548 	 * 
549 	 * @param states which states are we counting
550 	 * @return total number of components (every counted component is in one of 'states')
551 	 */
552 	private int getStateCount(ComponentState... states) {
553 		int total = 0;
554 		for (ComponentState state : states) {
555 			total += stateCount.get(state);
556 		}
557 		if (total > dependencies.getCount()) {
558 			throw new IllegalStateException("Sum of ints from stateCount can't be greater than number of dependencies.");
559 		}
560 		return total;
561 	}
562 	
563 	private ComponentState getDependencyState(IComponent dependency) {
564 		return getDependencyState(dependency.getComponentId());
565 	}
566 	
567 	private ComponentState getDependencyState(IToken componentId) {
568 		return dependencyState.get(componentId);
569 	}
570 
571 	/**
572 	 * Changes tracked dependency state, returns whether the state has truly changed.
573 	 * @param dependency
574 	 * @param newState
575 	 * @return whether the state has changed
576 	 */
577 	private boolean setDependencyState(IComponent dependency, ComponentState newState) {
578 		return setDependencyState(dependency.getComponentId(), newState);
579 	}
580 	
581 	/**
582 	 * Changes tracked dependency state, returns whether the state has truly changed.
583 	 * @param dependency
584 	 * @param newState
585 	 * @return
586 	 */
587 	private boolean setDependencyState(IToken dependency, ComponentState newState) {
588 		ComponentState oldState = dependencyState.get(dependency);
589 		if (oldState == newState) return false;
590 		int count = stateCount.get(oldState);
591 		if (count <= 0) {
592 			throw new IllegalStateException("There should not be a dependency in state " + oldState + ", but still...");
593 		}
594 		stateCount.put(oldState, count-1);
595 		dependencyState.put(dependency, newState);
596 		count = stateCount.get(newState);
597 		stateCount.put(newState, count+1);
598 		if (count+1 > dependencies.getCount()) {
599 			throw new IllegalStateException("There are too many dependencies in state " + newState + ", more than is possible...");
600 		}
601 		return true;
602 	}
603 	
604 	private boolean dependencyInState(IComponent dependency, ComponentState... states) {
605 		return dependencyInState(dependency.getComponentId(), states);
606 	}
607 	
608 	private boolean dependencyInState(IToken componentId, ComponentState... states) {
609 		ComponentState state = dependencyState.get(componentId);
610 		if (state == null) return false;
611 		for (ComponentState s : states) {
612 			if (state == s) return true;
613 		}
614 		return false;
615 	}
616 	
617 	private boolean dependencyNotInState(IComponent dependency, ComponentState... states) {
618 		return dependencyInState(dependency.getComponentId());
619 	}
620 	
621 	private boolean dependencyNotInState(IToken componentId, ComponentState... states) {
622 		ComponentState state = dependencyState.get(componentId);
623 		if (state == null) return true;
624 		for (ComponentState s : states) {
625 			if (state == s) return false;
626 		}
627 		return true;
628 	}	
629 	
630 	//
631 	//
632 	// LEVEL 1 (calling only UTILITY or LEVEL 2 methods)
633 	//   --->  these methods are first methods that are called when lifecycle events of other components are received, they process every such event
634 	//         and deciding what to do next based on the state of other components and controlled component (+ providing sanity checks)
635 	//   --->  these methods are synchronizing access to lower level methods! 
636 	//
637 	//
638 	
639 	/**
640 	 * @param event may be {@link IStartingEvent} or {@link IStartedEvent}
641 	 * @param simulating whether we're simulating the event
642 	 */
643 	private void startingEvent(IComponentEvent event, boolean simulating) {
644 		synchronized(ctrlMutex) {
645 			NullCheck.check(event, "event");
646 			IComponent eventComponent = event.getSource();
647 			
648 			if (!isDependent(eventComponent)) return;
649 			
650 			if (simulating) {
651 				if (log.isLoggable(Level.FINER)) log.finer("Simulating " + id(eventComponent) + " starting event.");
652 			} else {
653 				if (log.isLoggable(Level.FINER)) log.finer("Received " + id(eventComponent) + " starting event.");
654 			}
655 			
656 			// SANITY CHECKS
657 			if (dependencyInState(eventComponent, ComponentState.STARTING)) {
658 				if (log.isLoggable(Level.WARNING)) log.warning(id(eventComponent) + " is broadcasting 'starting event' twice, ignoring.");
659 				return;
660 			}
661 			if (!dependencyInState(eventComponent, ComponentState.INSTANTIATED, ComponentState.RESETED, ComponentState.STOPPED)) {
662 				throw new ComponentCantStartException(id(eventComponent) + " is broadcasting 'starting event' while it is in state " + getDependencyState(eventComponent) + ", unsupported.", log, component);
663 			}		
664 			
665 			// alter the dependency state
666 			if (setDependencyState(eventComponent, ComponentState.STARTING)) {
667 				if (getStateCount(ComponentState.STARTING_PAUSED, ComponentState.PAUSING, ComponentState.PAUSED) == 0) {
668 					// GOING TO LEVEL 2
669 					startingChangedByStartingEvent(event);
670 				} else {
671 					// GOING TO LEVEL 2
672 					startingChangedByStartingEventButOneComponentIsStartingPausedOrPausingOrPaused(event);
673 				}
674 			}
675 		}
676 	}
677 	
678 	private void startedEvent(IStartedEvent event) {
679 		synchronized(ctrlMutex) {
680 			NullCheck.check(event, "event");
681 			IComponent eventComponent = event.getSource();
682 			if (!isDependent(eventComponent)) return;
683 			
684 			if (log.isLoggable(Level.FINER)) log.finer("Received " + id(eventComponent) + " started event.");
685 			
686 			// SANITY CHECKS
687 			if (dependencyInState(eventComponent, ComponentState.RUNNING)) {
688 				if (log.isLoggable(Level.WARNING)) log.warning(id(eventComponent) + " is broadcasting 'started event' while running, ignoring.");
689 				return;
690 			}
691 			if (dependencyInState(eventComponent, ComponentState.INSTANTIATED, ComponentState.RESETED, ComponentState.STOPPED)) {
692 				if (log.isLoggable(Level.WARNING)) log.warning(id(eventComponent) + " is broadcasting 'started event' but did not broadcast 'starting event' before, ill behavior, simulating the event.");
693 				startingEvent(event, true);
694 			}			
695 			if (!dependencyInState(eventComponent, ComponentState.STARTING)) {
696 				throw new ComponentCantStartException(id(eventComponent) + " is broadcasting 'started event' while it is in state " + getDependencyState(eventComponent) + ", unsupported.", log, component);
697 			}
698 
699 			// alter the dependency state
700 			if (setDependencyState(eventComponent, ComponentState.RUNNING)) {
701 				// GOING TO LEVEL 2
702 				runningChangedByStartedEvent(event);
703 			}
704 		}
705 	}
706 	
707 	/**
708 	 * @param event may be {@link IStartingPausedEvent} only
709 	 * @param simulating whether we're simulating the event
710 	 */
711 	private void startingPausedEvent(IComponentEvent event) {
712 		synchronized(ctrlMutex) {
713 			NullCheck.check(event, "event");
714 			IComponent eventComponent = event.getSource();
715 			
716 			if (!isDependent(eventComponent)) return;
717 			
718 			// SANITY CHECKS
719 			if (dependencyInState(eventComponent, ComponentState.STARTING)) {
720 				if (log.isLoggable(Level.WARNING)) log.warning(id(eventComponent) + " is broadcasting 'starting event' twice, ignoring.");
721 				return;
722 			}
723 			if (!dependencyInState(eventComponent, ComponentState.INSTANTIATED, ComponentState.RESETED, ComponentState.STOPPED)) {
724 				throw new ComponentCantStartException(id(eventComponent) + " is broadcasting 'starting event' while it is in state " + getDependencyState(eventComponent) + ", unsupported.", log, component);
725 			}			
726 			
727 			// alter the dependency state
728 			if (setDependencyState(eventComponent, ComponentState.STARTING_PAUSED)) {
729 				// GOING TO LEVEL 2
730 				startingChangedByStartingPausedEvent(event);
731 			}
732 		}
733 	}
734 	
735 	private void pausingEvent(IComponentEvent event, boolean simulating) {
736 		synchronized(ctrlMutex) {
737 			NullCheck.check(event, "event");
738 			IComponent eventComponent = event.getSource();
739 			if (!isDependent(eventComponent)) return;
740 				
741 			if (simulating) {
742 				if (log.isLoggable(Level.FINER)) log.finer("Simulating " + id(eventComponent) + " pausing event.");
743 			} else {
744 				if (log.isLoggable(Level.FINER)) log.finer("Received " + id(eventComponent) + " pausing event.");
745 			}
746 			
747 			// SANITY CHECKS
748 			if (dependencyInState(eventComponent, ComponentState.PAUSING)) {
749 				if (log.isLoggable(Level.WARNING)) log.warning(id(eventComponent) + " is broadcasting 'pausing event' twice, ignoring.");
750 				return;
751 			}			
752 			if (!dependencyInState(eventComponent, ComponentState.RUNNING)) {
753 				throw new ComponentCantPauseException(id(eventComponent) + " is broadcasting 'pausing event' while it is in state " + getDependencyState(eventComponent) + ", unsupported.", log, component);
754 			}
755 			
756 			// alter the dependency state
757 			if (setDependencyState(eventComponent, ComponentState.PAUSING)) {
758 				pausingChangedByPausingEvent(event);
759 			}
760 		}
761 	}
762 
763 	private void pausedEvent(IPausedEvent event) {
764 		synchronized(ctrlMutex) {
765 			NullCheck.check(event, "event");
766 			IComponent eventComponent = event.getSource();
767 			if (!isDependent(eventComponent)) return;
768 			
769 			if (log.isLoggable(Level.FINER)) log.finer("Received " + id(eventComponent) + " paused event.");
770 			
771 			// SANITY CHECKS
772 			if (dependencyInState(eventComponent, ComponentState.PAUSED)) {
773 				if (log.isLoggable(Level.WARNING)) log.warning(id(eventComponent) + " is broadcasting 'paused event' twice, ignoring.");
774 				return;
775 			}
776 			if (dependencyInState(eventComponent, ComponentState.RUNNING)) {
777 				if (log.isLoggable(Level.WARNING)) log.warning(id(eventComponent) + " is broadcasting 'paused event' but did not broadcast 'pausing event' before, ill behavior, simulating the event.");
778 				pausingEvent(event, true);
779 			}
780 			if (dependencyInState(eventComponent, ComponentState.STARTING_PAUSED)) {
781 				// alter the dependency state				
782 				if (setDependencyState(eventComponent, ComponentState.PAUSED)) {
783 					// GOING TO LEVEL 2
784 					pausedChangedByPausedEventAfterStartingPaused(event);
785 				}
786 			} else 
787 			if (dependencyInState(eventComponent, ComponentState.PAUSING)) {
788 				// alter the dependency state
789 				if (setDependencyState(eventComponent, ComponentState.PAUSED)) {
790 					// GOING TO LEVEL 2
791 					pausedChangedByPausedEvent(event);
792 				}
793 			} else {
794 				throw new ComponentCantPauseException(id(eventComponent) + " is broadcasting 'paused event' while it is in state " + getDependencyState(eventComponent) + ", unsupported.", log, component);
795 			}			
796 		}
797 	}
798 
799 	/**
800 	 * @param event may be {@link IStartingEvent} or {@link IStartedEvent}
801 	 * @param simulating whether we're simulating the event
802 	 */
803 	private void resumingEvent(IComponentEvent event, boolean simulating) {
804 		synchronized(ctrlMutex) {
805 			NullCheck.check(event, "event");
806 			IComponent eventComponent = event.getSource();
807 			if (!isDependent(eventComponent)) return;
808 			
809 			if (simulating) {
810 				if (log.isLoggable(Level.FINER)) log.finer("Simulating " + id(eventComponent) + " resuming event.");
811 			} else {
812 				if (log.isLoggable(Level.FINER)) log.finer("Received " + id(eventComponent) + " resuming event.");
813 			}
814 			
815 			// SANITY CHECKS
816 			if (dependencyInState(eventComponent, ComponentState.RESUMING)) {
817 				if (log.isLoggable(Level.WARNING)) log.warning(id(eventComponent) + " is broadcasting 'resuming event' twice, ignoring.");
818 				return;
819 			}
820 			if (!dependencyInState(eventComponent, ComponentState.PAUSED)) {
821 				throw new ComponentCantResumeException(id(eventComponent) + " is broadcasting 'resuming event' while it is in state " + getDependencyState(eventComponent) + ", unsupported.", log, component);
822 			}				
823 			
824 			// alter the dependency state
825 			if (setDependencyState(eventComponent, ComponentState.RESUMING)) {
826 				// GOING TO LEVEL 2
827 				resumingChangedByResumingEvent(event);
828 			}
829 		}
830 	}
831 	
832 	private void resumedEvent(IResumedEvent event) {
833 		synchronized(ctrlMutex) {
834 			NullCheck.check(event, "event");
835 			IComponent eventComponent = event.getSource();
836 			if (!isDependent(eventComponent)) return;
837 			
838 			if (log.isLoggable(Level.FINER)) log.finer("Received " + id(eventComponent) + " resumed event.");
839 			
840 			// SANITY CHECKS
841 			if (dependencyInState(eventComponent, ComponentState.RUNNING)) {
842 				if (log.isLoggable(Level.WARNING)) log.warning(id(eventComponent) + " is broadcasting 'resumed event' while running, ignoring.");
843 				return;
844 			}
845 			if (dependencyInState(eventComponent, ComponentState.PAUSED)) {
846 				if (log.isLoggable(Level.WARNING)) log.warning(id(eventComponent) + " is broadcasting 'resumed event' but did not broadcast 'resuming event' before, ill behavior, simulating the event.");
847 				resumingEvent(event, true);
848 			}
849 			if (!dependencyInState(eventComponent, ComponentState.RESUMING)) {
850 				throw new ComponentCantResumeException(id(eventComponent) + " is broadcasting 'resumed event' while it is in state " + getDependencyState(eventComponent) + ", unsupported.", log, component);
851 			}
852 			
853 			// alter the dependency state
854 			if (setDependencyState(eventComponent, ComponentState.RUNNING)) {
855 				// GOING TO LEVEL 2
856 				runningChangedByResumedEvent(event);
857 			}
858 		}
859 	}
860 
861 	private void stoppingEvent(IComponentEvent event, boolean simulating) {
862 		synchronized(ctrlMutex) {
863 			NullCheck.check(event, "event");
864 			IComponent eventComponent = event.getSource();
865 			if (!isDependent(eventComponent)) return;
866 				
867 			if (simulating) {
868 				if (log.isLoggable(Level.FINER)) log.finer("Simulating " + id(eventComponent) + " stopping event.");
869 			} else {
870 				if (log.isLoggable(Level.FINER)) log.finer("Received " + id(eventComponent) + " stopping event.");
871 			}
872 			
873 			// SANITY CHECKS
874 			if (dependencyInState(eventComponent, ComponentState.STOPPING)) {
875 				if (log.isLoggable(Level.WARNING)) log.warning(id(eventComponent) + " is broadcasting 'stopping event' twice, ignoring.");
876 				return;
877 			}
878 			if (!dependencyInState(eventComponent, ComponentState.PAUSED, ComponentState.PAUSING, ComponentState.RESUMING, ComponentState.RUNNING)) {
879 				throw new ComponentCantStopException(id(eventComponent) + " is broadcasting 'stopping event' while it is in state " + getDependencyState(eventComponent) + ", unsupported.", log, component);				
880 			}
881 			
882 			// alter the dependency state
883 			if (setDependencyState(eventComponent, ComponentState.STOPPING)) {
884 				// GOING TO LEVEL 2
885 				stoppingChangedByStoppingEvent(event);
886 			}
887 		}
888 	}
889 	
890 	private void stoppedEvent(IStoppedEvent event) {
891 		synchronized(ctrlMutex) {
892 			NullCheck.check(event, "event");
893 			IComponent eventComponent = event.getSource();
894 			if (!isDependent(eventComponent)) return;
895 			
896 			if (log.isLoggable(Level.FINER)) log.finer("Received " + id(eventComponent) + " stopped event.");
897 			
898 			// SANITY CHECKS
899 			if (dependencyInState(eventComponent, ComponentState.STOPPED)) {
900 				if (log.isLoggable(Level.WARNING)) log.warning(id(eventComponent) + " is broadcasting 'stopped event' twice, ignoring.");
901 				return;
902 			}
903 			if (dependencyInState(eventComponent, ComponentState.PAUSED, ComponentState.PAUSING, ComponentState.RESUMING, ComponentState.RUNNING)) {
904 				if (log.isLoggable(Level.WARNING)) log.warning(id(eventComponent) + " is broadcasting 'stopped event' but did not broadcast 'stopping event' before, ill behavior, simulating the event.");
905 				stoppingEvent(event, true);				
906 			}
907 			if (!dependencyInState(eventComponent, ComponentState.STOPPING)) {
908 				throw new ComponentCantStopException(id(eventComponent) + " is broadcasting 'stopped event' while it is in state " + getDependencyState(eventComponent) + ", unsupported.", log, component);
909 			}
910 			
911 			// alter the dependency state
912 			if (setDependencyState(eventComponent, ComponentState.STOPPED)) {
913 				// GOING TO LEVEL 2
914 				stoppedChangedByStoppedEvent(event);
915 			}
916 		}
917 	}
918 	
919 	private void fatalErrorEvent(IFatalErrorEvent event) {
920 		if (inState(ComponentState.KILLING, ComponentState.KILLED)) {
921 			// NOTHING TO SEE, MOVE ALONG ...
922 			return;
923 		}
924 		if (event.getSource() == component) {
925 			if (log.isLoggable(Level.FINER)) log.finer("Fatal error received from the controlled component, discarding.");
926 			return;
927 		}
928 		lastFatalError = event;
929 		if (log.isLoggable(Level.SEVERE)) log.severe("Received fatal error from " + id(event.getSource()) + ".");
930 		// GOING TO LEVEL 2
931 		componentFatalError();		
932 	}
933 	
934 	private void resetEvent(IResetEvent event) {
935 		synchronized(ctrlMutex) {
936 			if (log.isLoggable(Level.WARNING)) log.warning("Received reset event.");
937 			// GOING TO LEVEL 2
938 			componentReset();
939 		}
940 	}
941 
942 	//
943 	//
944 	// LEVEL 2 (Called by LEVEL 1 methods and calling only LEVEL 3 methods) 
945 	//   --->  these methods are called whenever lifecycle state ({@link ComponentState}) of some component we're depending on changes
946 	//   --->  the method names are uniformely named
947 	//              STATE_NAME ChangedBy EVENT
948 	//                \                    \
949 	//                 \                    +- EVENT = event that the component (we're depending on) has broadcast | or other explanation for the change
950 	//                  \
951 	//                   +- STATE_NAME = state which the component (we're depending on) has switched into 
952 	//
953 	//
954 	
955 	
956 	//
957 	// dependency has changed its STATE into STARTING state
958 	//
959 	
960 	/**
961 	 * @param event may be {@link IStartingEvent} or {@link IStartingPausedEvent}
962 	 */
963 	private void startingChangedByStartingEvent(IComponentEvent event) {
964 		if (dependencies.getType() == ComponentDependencyType.STARTS_AFTER) {
965 			return;
966 		}
967 		if (getStateCount(ComponentState.STARTING, ComponentState.RESUMING, ComponentState.RUNNING) == dependencies.getCount()) {
968 			if (inState(ComponentState.STOPPED, ComponentState.RESETED, ComponentState.INSTANTIATED)) {
969 				if (log.isLoggable(Level.INFO)) log.info("All dependencies are starting/resuming/has started, starting the component.");
970 				// GOING TO LEVEL 3
971 				start(false);
972 			} else {
973 				throw new ComponentCantStartException("All dependencies are starting/resuming/has started, but can't start the component, it's in an ill state " + componentState.getFlag() + ".", log, component);
974 			}
975 		} else
976 		if (getStateCount(ComponentState.STARTING, ComponentState.STARTING_PAUSED, ComponentState.PAUSING, ComponentState.PAUSED, ComponentState.RESUMING, ComponentState.RUNNING) == dependencies.getCount()) {
977 			if (inState(ComponentState.STOPPED, ComponentState.RESETED, ComponentState.INSTANTIATED)) {
978 				if (log.isLoggable(Level.INFO)) log.info("All dependencies are starting/resuming/has started but there are some which are in STARTING_PAUSED/PAUSING/PAUSED state, starting-paused the component.");
979 				// GOING TO LEVEL 3
980 				startPaused(false);
981 			} else {
982 				throw new ComponentCantStartException("All dependencies are starting/resuming/has started but there are some which are in STARTING_PAUSED/PAUSING/PAUSED state, but can't starting-paused the component, it's in an ill state " + componentState.getFlag() + ".", log, component);
983 			}
984 		}
985 	}
986 
987 	private void startingChangedByStartingEventButOneComponentIsStartingPausedOrPausingOrPaused(IComponentEvent event) {
988 		startingChangedByStartingEvent(event);
989 	}
990 	
991 	private void startingChangedByStartingPausedEvent(IComponentEvent event) {
992 		startingChangedByStartingEvent(event);
993 	}
994 	
995 	//
996 	// dependency has changed its STATE into RUNNING state
997 	//
998 	
999 	private void runningChangedByStartedEvent(IStartedEvent event) {
1000 		// no need to assess dependencies.getType() == ComponentDependencyType.STARTS_WITH case
1001 		// as this was already triggered by real/simulated starting event of the same component
1002 		if (dependencies.getType() == ComponentDependencyType.STARTS_WITH) {
1003 			return;
1004 		}
1005 		// we're assessing the situation dependencies.getType() == ComponentDependencyType.STARTS_AFTER
1006 		if (getStateCount(ComponentState.RUNNING) == dependencies.getCount()) {
1007 			if (inState(ComponentState.STOPPED, ComponentState.RESETED, ComponentState.INSTANTIATED)) {
1008 				if (log.isLoggable(Level.INFO)) log.info("All dependencies has started/resumed, starting the component.");
1009 				// GOING TO LEVEL 3
1010 				start(false);
1011 			} else {
1012 				throw new ComponentCantStartException("All dependencies has started/resumed, but can't start the component, it's in an ill state " + componentState.getFlag() + ".", log, component);
1013 			}
1014 		} else 
1015 		if (getStateCount(ComponentState.RUNNING, ComponentState.PAUSING, ComponentState.PAUSED) == dependencies.getCount()) {
1016 			if (inState(ComponentState.STOPPED, ComponentState.RESETED, ComponentState.INSTANTIATED)) {
1017 				if (log.isLoggable(Level.INFO)) log.info("All dependencies has started/resumed but there are some that is pausing/paused, starting-paused the component.");
1018 				// GOING TO LEVEL 3
1019 				startPaused(false);
1020 			} else {
1021 				throw new ComponentCantStartException("All dependencies has started/resumed, but there are some that is pausing/paused, but can't starting-paused the component, it's in an ill state " + componentState.getFlag() + ".", log, component);
1022 			}
1023 	}
1024 	}
1025 	
1026 	private void runningChangedByResumedEvent(IResumedEvent event) {
1027 		// no need to assess dependencies.getType() == ComponentDependencyType.STARTS_WITH case
1028 		// as this was already triggered by real/simulated starting event of the same component
1029 		if (dependencies.getType() == ComponentDependencyType.STARTS_WITH) {
1030 			return;
1031 		}
1032 		// we're assessing the situation dependencies.getType() == ComponentDependencyType.STARTS_AFTER
1033 		if (getStateCount(ComponentState.RUNNING) == dependencies.getCount()) {
1034 			if (inState(ComponentState.STOPPED, ComponentState.RESETED, ComponentState.INSTANTIATED)) {
1035 				if (log.isLoggable(Level.INFO)) log.info("All dependencies has started/resumed, starting the component.");
1036 				// GOING TO LEVEL 3
1037 				start(false);
1038 			} else 
1039 			if (inState(ComponentState.PAUSED)) {
1040 				if (log.isLoggable(Level.INFO)) log.info("All dependencies has started/resumed, resuming the component.");
1041 				// GOING TO LEVEL 3
1042 				resume(false);
1043 			} else {
1044 				throw new ComponentCantStartException("All dependencies has started/resumed, but can't start/resume the component, it's in an ill state " + componentState.getFlag() + ".", log, component);
1045 			}
1046 		} else
1047 		if (getStateCount(ComponentState.RUNNING, ComponentState.PAUSING, ComponentState.PAUSED) == dependencies.getCount()) {
1048 			if (inState(ComponentState.STOPPED, ComponentState.RESETED, ComponentState.INSTANTIATED)) {
1049 				if (log.isLoggable(Level.INFO)) log.info("All dependencies has started/resumed but some of them are pausing/paused, starting-paused the component.");
1050 				// GOING TO LEVEL 3
1051 				start(false);
1052 			}
1053 		}
1054 	}
1055 
1056 	//
1057 	// dependency has changed its STATE into PAUSING state
1058 	//
1059 	
1060 	private void pausingChangedByPausingEvent(IComponentEvent event) {
1061 		if (inState(ComponentState.RUNNING)) {
1062 			if (log.isLoggable(Level.INFO)) log.info("Dependency " + id(event.getSource()) + " is pausing, pausing the component.");
1063 			// GOING TO LEVEL 3
1064 			pause(false);
1065 		} else {
1066 			// pausing while this component is starting is solved elsewhere --> start() / startPaused() methods
1067 		}
1068 	}
1069 	
1070 	//
1071 	// dependency has changed its STATE into PAUSED state
1072 	//
1073 	
1074 	private void pausedChangedByPausedEvent(IPausedEvent event) {
1075 		if (inState(ComponentState.RUNNING)) {
1076 			if (log.isLoggable(Level.INFO)) log.info("Dependency " + id(event.getSource()) + " has paused, pausing the component.");
1077 			// GOING TO LEVEL 3
1078 			pause(false);
1079 		} else {
1080 			// paused while this component starting is solved elsewhere --> start() / startPaused() methods
1081 		}
1082 	}
1083 	
1084 	private void pausedChangedByPausedEventAfterStartingPaused(IPausedEvent event) {
1085 		// no need to assess dependencies.getType() == ComponentDependencyType.STARTS_WITH case
1086 		// as this was already triggered by real/simulated starting event of the same component
1087 		if (dependencies.getType() == ComponentDependencyType.STARTS_WITH) {
1088 			return;
1089 		}
1090 		// we're assessing the situation dependencies.getType() == ComponentDependencyType.STARTS_AFTER
1091 		if (getStateCount(ComponentState.RUNNING, ComponentState.PAUSED) == dependencies.getCount()) {
1092 			if (inState(ComponentState.STOPPED, ComponentState.RESETED, ComponentState.INSTANTIATED)) {
1093 				if (log.isLoggable(Level.INFO)) log.info("All dependencies has started/started into paused/are paused, starting-paused the component.");
1094 				// GOING TO LEVEL 3
1095 				startPaused(false);
1096 			} else {
1097 				throw new ComponentCantStartException("All dependencies has started/started into paused/are paused, but can't starting-paused the component, it's in an ill state " + componentState.getFlag() + ".", log, component);
1098 			}
1099 		}		
1100 	}
1101 	
1102 	//
1103 	// dependency has changed its STATE into RESUMING state
1104 	//
1105 	
1106 	private void resumingChangedByResumingEvent(IComponentEvent event) {
1107 		if (dependencies.getType() == ComponentDependencyType.STARTS_AFTER) {
1108 			return;
1109 		}
1110 		// we're assessing the situation dependencies.getType() == ComponentDependencyType.STARTS_WITH
1111 		if (getStateCount(ComponentState.STARTING, ComponentState.RESUMING, ComponentState.RUNNING) == dependencies.getCount()) {
1112 			if (inState(ComponentState.STOPPED, ComponentState.RESETED, ComponentState.INSTANTIATED)) {
1113 				if (log.isLoggable(Level.INFO)) log.info("All dependencies has started/resumed, starting the component.");
1114 				// GOING TO LEVEL 3
1115 				start(false);
1116 			} else 
1117 			if (inState(ComponentState.PAUSED)) {
1118 				if (log.isLoggable(Level.INFO)) log.info("All dependencies are starting/resuming/has started, resuming the component.");
1119 				// GOING TO LEVEL 3
1120 				resume(false);
1121 			} else {
1122 				throw new ComponentCantResumeException("All dependencies are starting/resuming/has started, but can't resume the component, it's in an ill state " + componentState.getFlag() + ".", log, component);
1123 			}
1124 		} else 
1125 		if (getStateCount(ComponentState.STARTING, ComponentState.RESUMING, ComponentState.RUNNING, ComponentState.STARTING_PAUSED, ComponentState.PAUSING, ComponentState.PAUSED) == dependencies.getCount()) {
1126 			if (inState(ComponentState.STOPPED, ComponentState.RESETED, ComponentState.INSTANTIATED)) {
1127 				if (log.isLoggable(Level.INFO)) log.info("All dependencies are starting/resuming/has started but there are some which are in STARTING_PAUSED/PAUSING/PAUSED state, starting-paused the component.");
1128 				// GOING TO LEVEL 3
1129 				startPaused(false);
1130 			}
1131 		}
1132 	}
1133 
1134 	//
1135 	// dependency has changed its STATE into STOPPING state
1136 	//
1137 	
1138 	private void stoppingChangedByStoppingEvent(IComponentEvent event) {
1139 		if (inState(ComponentState.PAUSED, ComponentState.PAUSING, ComponentState.RESUMING, ComponentState.RUNNING)) {
1140 			if (log.isLoggable(Level.INFO)) log.info("Dependency " + id(event.getSource()) + " is stopping, stopping the component.");
1141 			// GOING TO LEVEL 3
1142 			stop(false);
1143 		} else {
1144 			// stopping while this component is starting is solved elsewhere --> start() / startPaused() methods
1145 		}
1146 	}
1147 
1148 	//
1149 	// dependency has changed its STATE into STOPPED state
1150 	//
1151 	
1152 	private void stoppedChangedByStoppedEvent(IStoppedEvent event) {
1153 		if (inState(ComponentState.PAUSED, ComponentState.PAUSING, ComponentState.RESUMING, ComponentState.RUNNING)) {
1154 			if (log.isLoggable(Level.INFO)) log.info("Dependency " + id(event.getSource()) + " is stopping, stopping the component.");
1155 			// GOING TO LEVEL 3
1156 			stop(false);
1157 		} else {
1158 			// stopped while this component is starting is solved elsewhere --> start() / startPaused() methods
1159 		}
1160 	}
1161 	
1162 	//
1163 	// some component has broadcast FATAL ERROR
1164 	//
1165 	
1166 	private void componentFatalError() {
1167 		// WARNING: We may not be under ctrlMutex here!!!
1168 		switch(componentState.getFlag()) {
1169 		case INSTANTIATED:
1170 			if (log.isLoggable(Level.WARNING)) log.warning("Component is in instantiated state, won't call kill().");
1171 			return;
1172 		case RESETED:
1173 			if (log.isLoggable(Level.WARNING)) log.warning("Component is resetted, won't call kill().");
1174 			return;
1175 		case KILLED:
1176 			if (log.isLoggable(Level.WARNING)) log.warning("Component has been already killed, won't call kill().");
1177 			return;
1178 		}
1179 		// GOING TO LEVEL 3
1180 		kill(false);
1181 	}
1182 	
1183 	//
1184 	// RESET event received
1185 	//
1186 	
1187 	private void componentReset() {
1188 		switch(componentState.getFlag()) {
1189 		case INSTANTIATED:
1190 			if (log.isLoggable(Level.WARNING)) log.warning("Component is in instantiated state, won't call reset().");
1191 			return;
1192 		case RESETED:
1193 			if (log.isLoggable(Level.WARNING)) log.warning("Component is resetted, won't call reset().");
1194 			return;
1195 		case KILLED:
1196 			// GOING TO LEVEL 3
1197 			reset();
1198 			return;
1199 		default:
1200 			if (log.isLoggable(Level.WARNING)) log.warning("Reset event received but the component has not been killed! Current state is " + componentState.getFlag() + ", killing the component first!");
1201 			// GOING TO LEVEL 3			
1202 			kill(false);
1203 			if (log.isLoggable(Level.WARNING)) log.warning("And than, resetting it!");
1204 			// GOING TO LEVEL 3
1205 			reset();
1206 			return;
1207 		}
1208 	}
1209 	
1210 	//
1211 	//
1212 	// LEVEL 3 (Called by LEVEL 2 / manualXXX() methods and calling only UTILITY METHODS) 
1213 	//   -- methods are not checking whether they can perform requested operation! 
1214 	//
1215 	//
1216 	
1217 	private void start(boolean manual) {
1218 		setState(ComponentState.STARTING);
1219 		if (log.isLoggable(Level.FINE)) log.fine("Calling " + id(component) + ".preStart().");
1220 		control.preStart();
1221 		if (log.isLoggable(Level.FINE)) log.fine("Sending " + id(component) + " starting event (transactional).");
1222 		componentEvents.startingTransactional();
1223 		
1224 		if (log.isLoggable(Level.FINE)) log.fine(id(component) + " starting event (transactional) sent.");
1225 
1226 		if (!manual) {
1227 			// not manual?
1228 			// --> check the dependencies state count again as somebody may have stopped/paused
1229 			if (dependencies.getType() == ComponentDependencyType.STARTS_WITH) {
1230 				// STARTS_WITH case
1231 				if (getStateCount(ComponentState.STARTING, ComponentState.RESUMING, ComponentState.RUNNING) == dependencies.getCount()) {
1232 					// all is OK!					
1233 				} else
1234 				if (getStateCount(ComponentState.STARTING, ComponentState.RESUMING, ComponentState.RUNNING, ComponentState.PAUSING, ComponentState.PAUSED) == dependencies.getCount()) {
1235 					// some component we've been depending on is pausing / has paused
1236 					fatalError("Some components is pausing / has paused after starting-event of " + id(component) + ", unsupported state!");
1237 					return;
1238 				} else {
1239 					// some components has stopped
1240 					fatalError("Some components has stopped after starting-event of " + id(component) + ", unsupported state!");
1241 					return;
1242 				}
1243 			} else {
1244 				// STARTS_AFTER case
1245 				if (getStateCount(ComponentState.RUNNING) == dependencies.getCount()) {
1246 					// all is OK!					
1247 				} else
1248 				if (getStateCount(ComponentState.RUNNING, ComponentState.PAUSING, ComponentState.PAUSED) == dependencies.getCount()) {
1249 					// some component we've been depending on is pausing / has paused
1250 					fatalError("Some components is pausing / has paused after starting-event of " + id(component) + ", unsupported state!");
1251 					return;
1252 				} else {
1253 					// some components has stopped
1254 					fatalError("Some components has stopped after starting-event of " + id(component) + ", unsupported state!");
1255 					return;
1256 				}
1257 			}
1258 		}
1259 		
1260 		if (log.isLoggable(Level.FINE)) log.fine("Calling " + id(component) + ".start().");
1261 		control.start();
1262 		if (log.isLoggable(Level.INFO)) log.info(id(component) + ".start()ed.");
1263 		
1264 		setState(ComponentState.RUNNING);
1265 		if (log.isLoggable(Level.FINE)) log.fine("Sending " + id(component) + " started event (transactional).");
1266 		componentEvents.startedTransactional();
1267 	}
1268 	
1269 	private void startPaused(boolean manual) {
1270 		setState(ComponentState.STARTING_PAUSED);
1271 		if (log.isLoggable(Level.FINE)) log.fine("Calling " + id(component) + ".preStartPaused().");
1272 		control.preStartPaused();
1273 		if (log.isLoggable(Level.FINE)) log.fine("Sending " + id(component) + " starting-paused event (transactional).");
1274 		componentEvents.startingPausedTransactional();
1275 		
1276 		if (log.isLoggable(Level.FINE)) log.fine(id(component) + " starting-paused event (transactional) sent.");
1277 
1278 		if (!manual) {
1279 			// not manual?
1280 			// --> check the dependencies state count again as somebody may have stopped/paused
1281 			if (dependencies.getType() == ComponentDependencyType.STARTS_WITH) {
1282 				// STARTS_WITH case
1283 				if (getStateCount(ComponentState.STARTING, ComponentState.RESUMING, ComponentState.RUNNING) == dependencies.getCount()) {
1284 					// all components is starting/is resuming/running, some components that was pausing/has been paused is now resuming/resumed
1285 					fatalError("Some components is resuming / has resumed after starting-paused-event of " + id(component) + ", unsupported state!");
1286 					return;
1287 				} else
1288 				if (getStateCount(ComponentState.STARTING, ComponentState.STARTING_PAUSED, ComponentState.RESUMING, ComponentState.RUNNING, ComponentState.PAUSING, ComponentState.PAUSED) == dependencies.getCount()) {
1289 					// all is OK!					
1290 				} else {
1291 					// some components has stopped
1292 					fatalError("Some components has stopped after starting-paused-event of " + id(component) + ", unsupported state!");
1293 					return;
1294 				}
1295 			} else {
1296 				// STARTS_AFTER case
1297 				if (getStateCount(ComponentState.RUNNING) == dependencies.getCount()) {
1298 					// all components is starting/is resuming/running, some components that was pausing/has been paused is now resuming/resumed
1299 					fatalError("Some components is resuming / has resumed after starting-paused-event of " + id(component) + ", unsupported state!");
1300 					return;
1301 				} else
1302 				if (getStateCount(ComponentState.RUNNING, ComponentState.PAUSING, ComponentState.PAUSED) == dependencies.getCount()) {
1303 					// all is OK!					
1304 				} else {
1305 					// some components has stopped
1306 					fatalError("Some components has stopped after starting-paused-event of " + id(component) + ", unsupported state!");
1307 					return;
1308 				}
1309 			}
1310 		}
1311 		
1312 		if (log.isLoggable(Level.FINE)) log.fine("Calling " + id(component) + ".startPaused().");
1313 		control.startPaused();
1314 		if (log.isLoggable(Level.INFO)) log.info(id(component) + ".startPaused()ed.");
1315 		
1316 		setState(ComponentState.PAUSED);
1317 		if (log.isLoggable(Level.FINE)) log.fine("Sending " + id(component) + " paused event (transactional).");
1318 		componentEvents.pausedTransactional();		
1319 	}
1320 	
1321 	/**
1322 	 * @param manual whether the method has been called from manualStop() method
1323 	 */
1324 	private void pause(boolean manual) {
1325 		setState(ComponentState.PAUSING);
1326 		if (log.isLoggable(Level.FINE)) log.fine("Calling " + id(component) + ".prePause().");
1327 		control.prePause();
1328 		if (log.isLoggable(Level.FINE)) log.fine("Sending " + id(component) + " pausing event (transactional).");
1329 		componentEvents.pausingTransactional();
1330 		if (log.isLoggable(Level.FINE)) log.fine("Calling " + id(component) + ".pause().");
1331 		control.pause();
1332 		if (log.isLoggable(Level.INFO)) log.info(id(component) + ".paused()ed.");
1333 		setState(ComponentState.PAUSED);
1334 		if (log.isLoggable(Level.FINE)) log.fine("Sending " + id(component) + " paused event (transactional).");
1335 		componentEvents.pausedTransactional();
1336 	}
1337 	
1338 	/**
1339 	 * @param manual whether the method has been called from manualStop() method
1340 	 */
1341 	private void resume(boolean manual) {
1342 		setState(ComponentState.RESUMING);
1343 		if (log.isLoggable(Level.FINE)) log.fine("Calling " + id(component) + ".preResume().");
1344 		control.preResume();
1345 		if (log.isLoggable(Level.FINE)) log.fine("Sending " + id(component) + " resuming event (transactional).");
1346 		componentEvents.resumingTransactional();
1347 		if (log.isLoggable(Level.FINE)) log.fine("Calling " + id(component) + ".resume().");
1348 		control.resume();
1349 		if (log.isLoggable(Level.INFO)) log.info(id(component) + ".resum()ed.");
1350 		setState(ComponentState.RUNNING);
1351 		if (log.isLoggable(Level.FINE)) log.fine("Sending " + id(component) + " resumed event (transactional).");
1352 		componentEvents.resumedTransactional();
1353 	}
1354 
1355 	/**
1356 	 * @param manual whether the method has been called from manualStop() method
1357 	 */
1358 	private void stop(boolean manual) {
1359 		setState(ComponentState.STOPPING);
1360 		if (log.isLoggable(Level.FINE)) log.fine("Calling " + id(component) + ".preStop().");
1361 		control.preStop();
1362 		if (log.isLoggable(Level.FINE)) log.fine("Sending " + id(component) + " stopping event (transactional).");
1363 		componentEvents.stoppingTransactional();
1364 		if (log.isLoggable(Level.FINE)) log.fine("Calling " + id(component) + ".stop().");
1365 		control.stop();
1366 		if (log.isLoggable(Level.INFO)) log.info(id(component) + ".stop()ed.");
1367 		setState(ComponentState.STOPPED);
1368 		componentEvents.stoppedTransactional();
1369 	}
1370 	
1371 	/**
1372 	 * Will eat all exceptions...
1373 	 * @param manual
1374 	 */
1375 	private void kill(boolean manual) {
1376 		if (inState(ComponentState.KILLING, ComponentState.KILLED)) {
1377 			// NOTHING TO SEE, MOVE ALONG
1378 			return;
1379 		}
1380 		
1381 		synchronized(ctrlMutex) {
1382 			if (inState(ComponentState.KILLING, ComponentState.KILLED)) {
1383 				// NOTHING TO SEE, MOVE ALONG
1384 				return;
1385 			}
1386 			try {
1387 				setState(ComponentState.KILLING);
1388 			} catch (Exception e) {
1389 			}
1390 		}
1391 		
1392 		// WE'RE ALONE HERE!
1393 		
1394 		try {
1395 			if (log.isLoggable(Level.WARNING)) log.warning("Calling " + id(component) + ".kill().");
1396 		} catch (Exception e) {
1397 		}
1398 		try {
1399 			control.kill();
1400 		} catch(Exception e) {			
1401 		}
1402 		try {
1403 			if (log.isLoggable(Level.SEVERE)) log.severe(id(component) + ".kill()ed.");
1404 		} catch (Exception e) {
1405 		}
1406 		try {
1407 			setState(ComponentState.KILLED);
1408 		} catch (Exception e) {
1409 		}
1410 	}
1411 	
1412 	private void reset() {
1413 		setState(ComponentState.RESETTING);
1414 		if (log.isLoggable(Level.FINE)) log.fine("Reseting " + id(component) + "'s controller.");
1415 		resetController();
1416 		if (log.isLoggable(Level.INFO)) log.info(id(component) + "'s controller reseted.");
1417 		if (log.isLoggable(Level.FINE)) log.fine("Calling " + id(component) + ".reset().");
1418 		control.reset();
1419 		if (log.isLoggable(Level.INFO)) log.info(id(component) + ".reset()ed.");
1420 		setState(ComponentState.RESETED);
1421 	}
1422 	
1423 	private void resetController() {
1424 		for (IToken dependency : dependencies.getDependencies()) {
1425 			dependencyState.put(dependency, ComponentState.RESETED);
1426 		}
1427 		for (ComponentState state : ComponentState.values()) {
1428 			stateCount.put(state, 0);
1429 		}
1430 		stateCount.put(ComponentState.RESETED, dependencies.getCount());
1431 		lastFatalError = null;
1432 	}
1433 
1434 }