View Javadoc

1   package cz.cuni.amis.pogamut.base.component.controller;
2   
3   import java.util.HashSet;
4   import java.util.Set;
5   import java.util.concurrent.TimeUnit;
6   import java.util.logging.Level;
7   import java.util.logging.Logger;
8   
9   import cz.cuni.amis.pogamut.base.component.IComponent;
10  import cz.cuni.amis.pogamut.base.component.exception.ComponentKilledException;
11  import cz.cuni.amis.utils.NullCheck;
12  import cz.cuni.amis.utils.flag.Flag;
13  import cz.cuni.amis.utils.flag.ImmutableFlag;
14  import cz.cuni.amis.utils.flag.WaitForFlagChange;
15  import cz.cuni.amis.utils.flag.WaitForFlagChange.IAccept;
16  import cz.cuni.amis.utils.token.IToken;
17  import cz.cuni.amis.utils.token.Tokens;
18  
19  /**
20   * Abstract class implementing some methods from {@link IComponentControllerBase}. 
21   * <p><p>
22   * Namely:
23   * <ul>
24   * <li>{@link AbstractComponentControllerBase#getComponent()}</li>
25   * <li>{@link AbstractComponentControllerBase#getComponentControl()}</li>
26   * <li>{@link AbstractComponentControllerBase#isRunning()}</li>
27   * <li>{@link AbstractComponentControllerBase#isPaused()}</li>
28   * <li>{@link AbstractComponentControllerBase#awaitState(ComponentState...)}</li>
29   * <li>{@link AbstractComponentControllerBase#awaitState(long, ComponentState...)}</li>
30   * <li>{@link AbstractComponentControllerBase#getState()}</li>
31   * <li>{@link AbstractComponentControllerBase#inState(ComponentState...)}</li>
32   * <li>{@link AbstractComponentControllerBase#notInState(ComponentState...)}</li>
33   * </ul>
34   * And all methods from {@link IComponent}.
35   * <ul>
36   * <li>{@link AbstractComponentControllerBase#getComponent()}</li>
37   * <li>{@link AbstractComponentControllerBase#getComponentControl()}</li>
38   * <li>{@link AbstractComponentControllerBase#isRunning()}</li>
39   * <li>{@link AbstractComponentControllerBase#isPaused()}</li>
40   * <li>{@link AbstractComponentControllerBase#awaitState(ComponentState...)}</li>
41   * <li>{@link AbstractComponentControllerBase#awaitState(long, ComponentState...)}</li>
42   * <li>{@link AbstractComponentControllerBase#getState()}</li>
43   * <li>{@link AbstractComponentControllerBase#inState(ComponentState...)}</li>
44   * <li>{@link AbstractComponentControllerBase#notInState(ComponentState...)}</li>
45   * <li>
46   * </ul>
47   * 
48   * <p><p>
49   * Suitable for creating custom {@link IComponentController}s or {@link ISharedComponentController}s.
50   * 
51   * @author Jimmy
52   */
53  public abstract class AbstractComponentControllerBase<COMPONENT extends IComponent> implements IComponentControllerBase<COMPONENT> {
54  
55  	//
56  	// COMPONENT STATE AWAIT CLASS
57  	//
58  	
59  	/**
60  	 * Used for filtering states we're awaiting on.
61  	 */
62  	protected static class AwaitState implements IAccept<ComponentState> {
63  		
64  		Set<ComponentState> awaiting = new HashSet<ComponentState>();
65  		
66  		public AwaitState(ComponentState... states) {
67  			for (ComponentState state : states) {
68  				awaiting.add(state);
69  			}
70  		}
71  
72  		@Override
73  		public boolean accept(ComponentState flagValue) {
74  			return awaiting.contains(flagValue);
75  		}
76  		
77  	};
78  	
79  	//
80  	// FIELDS
81  	//
82  	
83  	/**
84  	 * Unique (in context of one agent) id of this controller.
85  	 */
86  	protected IToken controllerId;
87  
88  	/**
89  	 * Method providing means for direct control of the {@link AbstractComponentControllerBase#component}. This object is passed from the outside
90  	 * of the controller, usually created by the component itself.
91  	 */
92  	protected IComponentControlHelper control;
93  	
94  	/**
95  	 * Component controlled by this controller.
96  	 */
97  	protected COMPONENT component;
98  
99  	/**
100 	 * Log used by the class, is never null.
101 	 */
102 	protected Logger log;
103 
104 	/**
105 	 * State of the controlled component.
106 	 * <p><p>
107 	 * Use {@link ComponentController#setState(ComponentState)} to alter the value of the flag.
108 	 * <p><p>
109 	 * Should not be set manually, use {@link AbstractComponentControllerBase#setState(ComponentState)} instead.
110 	 */
111 	protected Flag<ComponentState> componentState = new Flag<ComponentState>(ComponentState.INSTANTIATED);
112 
113 	/**
114 	 * Tells whether the controller sends events about the state of the
115 	 * component, i.e., whether it should automatically send starting/stopping
116 	 * events or not.
117 	 * <p><p>
118 	 * DEFAULT: TRUE (the controller is broadcasting events as default)
119 	 * <p><p>
120 	 * Exception: {@link IComponentControllerBase#fatalError(String)} and {@link IComponentControllerBase#fatalError(String, Throwable)}
121 	 * must always send fatal error!
122 	 */
123 	protected boolean broadcastingEvents = true;
124 
125 	/**
126 	 * Initialize the controller. This constructor is auto-creating generic {@link AbstractComponentControllerBase#controllerId}
127 	 * (adding suffix "-controller" to the {@link IComponent#getComponentId()}.
128 	 * 
129 	 * @param component
130 	 * @param componentControlHelper
131 	 * @param log
132 	 */
133 	public AbstractComponentControllerBase(COMPONENT component, IComponentControlHelper componentControlHelper, Logger log) {
134 		// save arguments
135 		this.log = log;
136 		NullCheck.check(this.log, "log");
137 		this.component = component;		
138 		NullCheck.check(this.component, "component");
139 		this.control = componentControlHelper;
140 		NullCheck.check(this.control, "componentControlHelper");	
141 		
142 		// create default ID
143 		this.controllerId = Tokens.get(component.getComponentId().getToken() + "-controller");
144 	}
145 	
146 	/**
147 	 * Initialize controller with specific componentControllerId.
148 	 * 
149 	 * @param componentControllerId
150 	 * @param component
151 	 * @param componentControlHelper
152 	 * @param log
153 	 */
154 	public AbstractComponentControllerBase(IToken componentControllerId, COMPONENT component, IComponentControlHelper componentControlHelper, Logger log) {
155 		this(component, componentControlHelper, log);
156 		this.controllerId = componentControllerId;
157 		NullCheck.check(controllerId, "componentControllerId");
158 	}
159 	
160 	//
161 	//
162 	// PUBLIC INTERFACE - IComponent
163 	//
164 	//
165 	
166 	@Override
167 	public String toString() {
168 		if (this == null) return "AbstractComponentControllerBase";
169 		if (getComponentId() == null) return this.getClass().getSimpleName();
170 		return this.getClass().getSimpleName()+ "[" + getComponentId().getToken() + "]";
171 	}
172 	
173 	@Override
174 	public IToken getComponentId() {
175 		return controllerId;
176 	}
177 	
178 	public Logger getLog() {
179 		return log;
180 	}
181 	
182 	@Override
183 	public COMPONENT getComponent() {
184 		return component;
185 	}
186 	
187 	//
188 	//
189 	// PUBLIC INTERFACE - IComponentControllerBase
190 	//
191 	//
192 	
193 	@Override
194 	public boolean isBroadcastingEvents() {
195 		return broadcastingEvents;
196 	}
197 	
198 	@Override
199 	public void setBroadcastingEvents(boolean broadcastingEvents) {
200 		this.broadcastingEvents = broadcastingEvents;
201 	}
202 
203 	@Override
204 	public IComponentControlHelper getComponentControl() {
205 		return control;
206 	}
207 	
208 	@Override
209 	public boolean isRunning() {
210 		return inState(
211 				  ComponentState.PAUSING, ComponentState.PAUSED,
212 				  ComponentState.RESUMING, ComponentState.RUNNING
213 			   );
214 	}
215 	
216 	@Override
217 	public boolean isPaused() {
218 		return inState(
219 				  ComponentState.STARTING_PAUSED, ComponentState.PAUSED, ComponentState.PAUSING, ComponentState.RESUMING
220 			   );
221 	}
222 	
223 	@Override
224 	public ImmutableFlag<ComponentState> getState() {
225 		return componentState.getImmutable();
226 	}
227 	
228 	@Override
229 	public boolean inState(ComponentState... states) {
230 		return ComponentState.inside(componentState.getFlag(), states);
231 	}
232 	
233 	@Override
234 	public boolean notInState(ComponentState... states) {
235 		return ComponentState.notInside(componentState.getFlag(), states);
236 	}
237 	
238 	@Override
239 	public ComponentState awaitState(ComponentState... states) throws ComponentKilledException {
240 		ComponentState resultState;
241 		resultState = new WaitForFlagChange<ComponentState>(componentState, new AwaitState(states)).await();
242 		if (ComponentState.inside(resultState, ComponentState.KILLING, ComponentState.KILLED) &&
243 		    !ComponentState.partOf(states, ComponentState.KILLING, ComponentState.KILLED)) {
244 			throw new ComponentKilledException(component, this);	
245 		}
246 		return resultState;
247 	}
248 	
249 	@Override
250 	public ComponentState awaitState(long timeoutMillis, ComponentState... states) throws ComponentKilledException {
251 		ComponentState resultState;
252 		resultState = new WaitForFlagChange<ComponentState>(componentState, new AwaitState(states)).await(timeoutMillis, TimeUnit.MILLISECONDS);
253 		if (ComponentState.inside(resultState, ComponentState.KILLING, ComponentState.KILLED) &&
254 		    !ComponentState.partOf(states, ComponentState.KILLING, ComponentState.KILLED)) {
255 			throw new ComponentKilledException(component, this);	
256 		}
257 		return resultState;
258 	}
259 	
260 	//
261 	//
262 	// UTILITY METHODS
263 	//
264 	//
265 	
266 	/**
267 	 * Returns component id or null.
268 	 * @param component
269 	 * @return
270 	 */
271 	protected String id(IComponent component) {
272 		if (component == null) return "null";
273 		return component.getComponentId().getToken();
274 	}
275 	
276 	/**
277 	 * Changes the {@link AbstractComponentControllerBase#componentState} to desired state.
278 	 * @param state
279 	 */
280 	protected void setState(ComponentState state) {
281 		if (state == this.componentState.getFlag()) return;
282 		if (log.isLoggable(Level.FINEST)) log.finest("Switching to " + state + ".");
283 		this.componentState.setFlag(state);
284 		if (log.isLoggable(Level.INFO)) log.info("In state " + state + ".");
285 	}
286 	
287 }