View Javadoc

1   package cz.cuni.amis.pogamut.base.factory.guice;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   
6   import com.google.inject.AbstractModule;
7   import com.google.inject.Injector;
8   import com.google.inject.Module;
9   import com.google.inject.util.Modules;
10  
11  import cz.cuni.amis.pogamut.base.agent.IAgent;
12  import cz.cuni.amis.pogamut.base.agent.IAgentId;
13  import cz.cuni.amis.pogamut.base.agent.impl.AbstractAgent;
14  import cz.cuni.amis.pogamut.base.agent.impl.AgentId;
15  import cz.cuni.amis.pogamut.base.agent.params.IAgentParameters;
16  import cz.cuni.amis.pogamut.base.agent.utils.runner.impl.AgentRunner;
17  import cz.cuni.amis.pogamut.base.component.bus.ComponentBus;
18  import cz.cuni.amis.pogamut.base.component.bus.IComponentBus;
19  import cz.cuni.amis.pogamut.base.utils.guice.AdaptableProvider;
20  import cz.cuni.amis.pogamut.base.utils.guice.AgentScope;
21  import cz.cuni.amis.pogamut.base.utils.guice.AgentScoped;
22  import cz.cuni.amis.pogamut.base.utils.guice.AgentTeamScoped;
23  import cz.cuni.amis.pogamut.base.utils.guice.IAgentScope;
24  import cz.cuni.amis.pogamut.base.utils.logging.AgentLogger;
25  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
26  import cz.cuni.amis.utils.NullCheck;
27  
28  /**
29   * GuiceAgentModule, implementation of {@link AbstractModule}, provides a way to hierarchically specify the bindings
30   * for interfaces and classes.
31   * <p><p>
32   * The module is a place where you assemble the pieces of the dependency puzzle. You're specifying which implementor
33   * should be created for specific interface, etc. This sounds good but then you find out that it is somehow hard
34   * to override once set bindings.
35   * <p><p>
36   * The {@link GuiceAgentModule} solves this by providing {@link GuiceAgentModule#addModule(AbstractModule)} method
37   * that should be called from {@link GuiceAgentModule#configureModules()} (see their javadocs). The {@link GuiceAgentModule#configureModules()}
38   * is meant to be overridden in every descendant of the {@link GuiceAgentModule} where it should call <i>super.configureModules()</i>
39   * and than add another module to the queue via {@link GuiceAgentModule#addModule(AbstractModule)}. We're simply collecting
40   * respective modules in every <i>configureModules()</i> implementation. These collected modules are than applied inside
41   * standard Guice's method {@link GuiceAgentModule#configure()} where they are applied in the order they have been added (that's
42   * why you have to call <i>super.configureModules</i> as a first command in the descendants).   
43   * <p><p>
44   * Additionally, we're introducing {@link AgentScope} under annotation {@link AgentScoped} (concrete scope implementation can
45   * be changed by in descendants via overriding {@link GuiceAgentModule#createAgentScope()}) and convenient providers for {@link IAgentId}
46   * and {@link IAgentParameters} exposed via {@link GuiceAgentModule#getAgentIdProvider()} and {@link GuiceAgentModule#getAgentParamsProvider()}.
47   * <p><p>
48   * <p><p>
49   * <b>IMPORTANT</b> the {@link GuiceAgentModule} introduces public method {@link GuiceAgentModule#prepareNewAgent(IAgentParameters)} that
50   * is meant to configure run-time dependencies inside the module before another agent is instantiated (i.e., for passing run-time
51   * parameters such as {@link IAgentParameters}. The method contains only one parameter - PARAMS, therefore it forces you to create
52   * new descendants of {@link IAgentParameters} if you want to introduce new run-time parameters (which follows the philosophy that
53   * every {@link AbstractAgent} implementation should also defines: 1) own parameters ({@link IAgentParameters} descendants), 2) own module ({@link GuiceAgentModule} descendants), 3) own runners ({@link AgentRunner} descendants).<p>
54   * <b>NOTE</b> that this method <b>MUST BE CALLED</b> before the factory creates another agent (but rest assured, it's already done in {@link GuiceAgentFactory} for 
55   * you automatically}).
56   * <p><p>
57   * <p><p>
58   * FINALLY the module is providing basic bindings that are always needed for {@link AbstractAgent}
59   * <table>
60   * <tr><th>Mapped class</th>                    <th>  </th> <th>Target</th>                          <th>Description</th></tr>
61   * 
62   * <tr><td>{@link IComponentBus}</td>           <td>-></td> <td>{@link ComponentBus}</td>            <td>Agent bus synchronizing starting/stopping/etc. events.</td></tr>
63   * <tr><td>{@link IAgentId}</td>                <td>-></td> <td>provided by the {@link GuiceAgentModule#agentIdProvider}.</td>
64   *                                                                                                   <td>Id that is provided during runtime, you may use {@link AgentId} implementation of {@link IAgentId}.</td></tr>
65   * <tr><td>{@link IAgentParameters}</td>        <td>-></td> <td>provided by the {@link GuiceAgentModule#agentParamsProvider}.</td>
66   * <tr><td>{@link IAgentLogger}</td>            <td>-></td> <td>{@link AgentLogger}</td>             <td>Takes care about logging.</td></tr>
67   * </table>
68   * <p><p>
69   * To have <b>successful module</b> the descendant <b>must specify</b> these <b>missing bindings</b>:
70   * <table>
71   * <tr><th>Mapped class</th>                    <th>Description</th></tr>
72   * 
73   * <tr><td>{@link IAgent}</td>                  <td>Agent that should be instantiated (preferable descendant of {@link AbstractAgent}.</td></tr>
74   * </table>
75   * ... plus all newly introduced dependencies (by various implementors of mentioned interfaces).<p>
76   * ... <b>don't forget to call super.configureModules()</b> in the subclasses. ;-)
77   *  
78   * 
79   * @author Jimmy
80   * @param PARAMS
81   */
82  public class GuiceAgentModule<PARAMS extends IAgentParameters> extends AbstractModule {
83  
84  	/**
85  	 * Agent scope used to hold instances annotated with {@link AgentScoped}.
86  	 */
87  	private IAgentScope agentScope;
88  
89  	/**
90  	 * Agent-team scope used to hold instances annotated with {@link AgentTeamScoped}.
91  	 */
92  	private IAgentScope agentTeamScope;
93  	
94  	/**
95  	 * Provider for the {@link IAgentId} run-time dependence.
96  	 */
97  	private AdaptableProvider<IAgentId> agentIdProvider = new AdaptableProvider(null);
98  	
99  	/**
100 	 * Provider for the {@link IAgentParameters} run-time dependencies.
101 	 */
102 	private AdaptableProvider<PARAMS> agentParamsProvider = new AdaptableProvider(null);
103 	
104 	/**
105 	 * List of modules that are joined together when the module is used to provide the {@link Injector}.
106 	 */
107 	private List<Module> modules = new ArrayList<Module>();
108 		
109 	/**
110 	 * Initializes {@link GuiceAgentModule#agentScope} via {@link GuiceAgentModule#createAgentScope()}.
111 	 */
112 	public GuiceAgentModule() {
113 		agentScope = createAgentScope();
114 		NullCheck.check(this.agentScope, "createAgentScope()");
115 		agentTeamScope = createAgentTeamScope();
116 		NullCheck.check(this.agentTeamScope, "createAgentTeamScope()");		
117 	}
118 	
119 	/**
120 	 * Must be called before another agent instance can be created. It clears the {@link GuiceAgentModule#agentScope}
121 	 * and binds {@link IAgentParameters#getAgentId()} to the {@link GuiceAgentModule#agentIdProvider}.
122 	 * <p><p>
123 	 * Whenever you create your own {@link IAgentParameters} you may need to override this method to utilize your new
124 	 * run-time dependencies. In such case, always call <i>super.prepareNewAgent(agentParameters)</i> as a first command.
125 	 * 
126 	 * @param agentParameters
127 	 */
128 	public void prepareNewAgent(PARAMS agentParameters) {
129 		NullCheck.check(agentParameters, "agentParameters");
130 		NullCheck.check(agentParameters.getAgentId(), "agentParameters.getAgentId()");
131 		agentScope.clearScope();
132 		agentIdProvider.set(agentParameters.getAgentId());
133 		agentParamsProvider.set(agentParameters);
134 	}
135 	
136 	/**
137 	 * Adds next modules containing new bindings that extend (and/or override) previous bindings.
138 	 * <p><p>
139 	 * Designed to be used from {@link GuiceAgentModule#configureModules()}.
140 	 * 
141 	 * @param module
142 	 */
143 	protected final void addModule(AbstractModule module) {
144 		this.modules.add(module);
145 	}
146 	
147 	/**
148 	 * Meant to introduce new {@link AbstractModule} into the module's queue {@link GuiceAgentModule#modules} via {@link GuiceAgentModule#addModule(AbstractModule)}.
149 	 * <p><p>
150 	 * See {@link GuiceAgentModule#configureModules()} source code for the example (utilizes anonymous class instantiation, 
151 	 * instantiating {@link AbstractModule} where you only have to override {@link AbstractModule#configure()} method where
152 	 * you use {@link AbstractModule#bind(Class)} method to specify the bindings).
153 	 */
154 	protected void configureModules() {		
155 		addModule(
156 			new AbstractModule() {
157 				@Override
158 				protected void configure() {
159 					bind(IComponentBus.class).to(ComponentBus.class);
160 					bind(IAgentId.class).toProvider(getAgentIdProvider());
161 					bind(IAgentParameters.class).toProvider(getAgentParamsProvider());
162 					bind(IAgentLogger.class).to(AgentLogger.class);										
163 				}				
164 			}
165 		);
166 	}
167 	
168 	/**
169 	 * Method called from the {@link GuiceAgentModule#GuiceAgentModule()} to initialize the {@link GuiceAgentModule#agentScope},
170 	 * override if you need you own {@link AgentScope} implementation.
171 	 * @return
172 	 */
173 	protected IAgentScope createAgentScope() {
174 		return new AgentScope();
175 	}
176 	
177 	/**
178 	 * Method called from the {@link GuiceAgentModule#GuiceAgentModule()} to initialize the {@link GuiceAgentModule#agentTeamScope},
179 	 * override if you need you own {@link AgentScope} implementation.
180 	 * @return
181 	 */
182 	protected IAgentScope createAgentTeamScope() {
183 		return new AgentScope();
184 	}
185 	
186 	/**
187 	 * AgentScope that is holding agent-scope-singletons (classes annotated with {@link AgentScoped}).
188 	 * <p><p>
189 	 * Use {@link AgentScope#clearScope()} to release the objects thus preparing the scope for the next initialization
190 	 * of the {@link IAgent} (automatically called from {@link GuiceAgentModule#prepareNewAgent(IAgentParameters)}.
191 	 * @return
192 	 */
193 	public IAgentScope getAgentScope() {
194 		return agentScope;
195 	}
196 	
197 	/**
198 	 * AgentTeamScope that is holding agent-team-scope-singletons (classes annotated with {@link AgentTeamScoped}).
199 	 * <p><p>
200 	 * Use {@link IAgentScope#clearScope()} to release the objects thus preparing the scope for the next team initialization.
201 	 * @return
202 	 */
203 	public IAgentScope getAgentTeamScope() {
204 		return agentTeamScope;
205 	}
206 	
207 	/**
208 	 * Returns a provider for the {@link IAgentId} interface. Use when utilizing descendants of {@link IAgentId}
209 	 * to provide the same instance for new interface/implementors.
210 	 * 
211 	 * @return
212 	 */
213 	protected AdaptableProvider<IAgentId> getAgentIdProvider() {
214 		return agentIdProvider;
215 	}
216 	
217 	/**
218 	 * Returns a provider for the {@link IAgentParameters} interface. Use when utilizing descendants of {@link IAgentParameters}
219 	 * to provide the same instace for new interface/implementors.
220 	 * 
221 	 * @return
222 	 */
223 	protected AdaptableProvider<PARAMS> getAgentParamsProvider() {
224 		return agentParamsProvider;
225 	}
226 
227 	/**
228 	 * Binds {@link GuiceAgentModule#agentScope} into the module and then it iterates over {@link GuiceAgentModule#modules} and
229 	 * adds all their bindings to the module - each module always overrides previous ones (uses {@link Modules#override(Module...)}).
230 	 * <p><p>
231 	 * The advantage over classical {@link AbstractModule#configure()} method is that you may easily re-bind already bound classes
232 	 * (which is unachievable by simple subclassing). 
233 	 */
234 	@Override
235 	protected final void configure() {
236 		configureModules();
237 		bindScope(AgentScoped.class, this.agentScope);
238 		bindScope(AgentTeamScoped.class, this.agentTeamScope);
239 		if (modules.size() == 0) {
240 			throw new IllegalStateException("There is no module defined, nobody has ever called addModule() method to introduce new bindings for the module.");
241 		}
242 		Module actual = modules.get(0);
243 		for (int i = 1; i < modules.size(); ++i) {
244 			actual = Modules.override(actual).with(modules.get(i));
245 		}
246 		install(actual);
247 	}
248 	
249 }