1 package com.sri.common.controller;
2
3 import com.jcorporate.expresso.core.controller.Controller;
4 import com.jcorporate.expresso.core.controller.ControllerException;
5 import com.jcorporate.expresso.core.controller.ControllerRequest;
6 import com.jcorporate.expresso.core.controller.ControllerResponse;
7 import com.jcorporate.expresso.core.controller.ExpressoRequest;
8 import com.jcorporate.expresso.core.controller.ExpressoResponse;
9 import com.jcorporate.expresso.core.controller.ServletControllerRequest;
10 import com.jcorporate.expresso.core.controller.ServletControllerResponse;
11 import com.jcorporate.expresso.core.controller.State;
12 import com.jcorporate.expresso.core.db.DBException;
13 import com.jcorporate.expresso.core.dbobj.SchemaFactory;
14 import com.sri.common.ComponentSchema;
15 import org.picocontainer.MutablePicoContainer;
16 import org.picocontainer.defaults.DefaultPicoContainer;
17
18 import java.util.ArrayList;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Set;
23
24 /***
25 * The component controller provides the home for two lightweight
26 * containers and a link to a third. They are shown here in the diagram:
27 * <img src="doc-files/container-hierarchy.gif" alt="Container Hierarchy Diagram" border="0"/>
28 * <p>The lowest level, the request container is build during the prePerform and
29 * postPerform states. The Request/Response objects are dumped in this particular
30 * container.</p>
31 * <p>The next level up, the ControllerContainer contains all the step handlers
32 * and other appropriate classes for the controller component. This may include
33 * <tt>Monitor</tt> objects, etc.</p>
34 * <p>The Controller-level container is first instantiated upon construction
35 * of the controller</p>
36 * <p>The final level container is contained in the Schema and may be used
37 * as a factory for DataObjects, Controllers, and the like.</p>
38 * <p>The Schema container is created at the startup of the servlet context</p>
39 *
40 * @author Michael Rimov
41 * @version 1.0
42 */
43 abstract public class AbstractComponentController extends AbstractDBController implements ComponentServiceLocator {
44
45 /***
46 * Component that has state handlers registered by container.
47 */
48 private MutablePicoContainer myContainer;
49
50 private final Class mySchemaClass;
51
52 private boolean registeredWithParent = false;
53
54 /***
55 * A set of all registered state handlers.
56 */
57 private Set stateHandlerStates = new HashSet();
58
59 /***
60 * The mutable pico container is stored here. In your own derived classes,
61 * if you wish to modify the request-level container, override prePerform, first
62 * call super.prePerform(...), then grab the REQUEST_CONTAINER from the
63 * request context.
64 */
65 public static final String REQUEST_CONTAINER = AbstractComponentController.class.getName() + "RequestContainer";
66
67
68 /***
69 * Constructor that takes a concrete schema instance as its parameter. Use
70 * SchemaFactory to create the instance of the component.
71 *
72 * @param mySchemaClass ComponentSchema
73 */
74 public AbstractComponentController(final Class mySchemaClass) {
75 super();
76
77 this.setSchema(mySchemaClass);
78
79 this.mySchemaClass = mySchemaClass;
80
81 }
82
83 /***
84 * Template method that allows sub-controllers to register any particular objects
85 * they wish.
86 *
87 * @param container MutablePicoContainer the mutable pico container instance.
88 */
89 protected void initializeControllerContainer(final MutablePicoContainer container) {
90
91 }
92
93 /***
94 * Allows adding a state handler by combining adding a state and registering
95 * the component instance in the container instance.
96 *
97 * @param stateName String the name of the state.
98 * @param stateDescription String the name of the instance.
99 * @param stateHandler Class the handler instance.
100 * @return State
101 */
102 protected State addStateHandler(final String stateName, final String stateDescription, final Class stateHandler) {
103 assert stateName != null;
104 assert stateDescription != null;
105 assert stateHandler != null;
106
107
108 State newState = new State(stateName, stateDescription);
109 super.addState(newState);
110
111
112 registerNewComponent(stateName, stateHandler);
113
114
115 stateHandlerStates.add(stateName);
116
117 return newState;
118 }
119
120
121 private List registrationCommands = new ArrayList();
122
123 /***
124 * Registers the state handler in the controller-level container.
125 *
126 * @param stateName String the state name.
127 * @param stateHandler Class the State handler interface.
128 */
129 private void registerNewComponent(final String stateName, final Class stateHandler) {
130 registrationCommands.add(new RegistrationCommand(stateName, stateHandler));
131 }
132
133 /***
134 * Template Method, allowing a subclass to into actions after the
135 * invocation of any state in this controller.
136 *
137 * @param nextState the state to be performed
138 * @param request the request object
139 * @param response the response object
140 * @throws ControllerException upon error.
141 */
142 protected synchronized void postPerform(final State nextState, final ExpressoRequest request,
143 final ExpressoResponse response) throws ControllerException {
144
145 super.postPerform(nextState, request, response);
146
147
148 MutablePicoContainer requestContainer = (MutablePicoContainer) request.getSession().getAttribute(
149 REQUEST_CONTAINER);
150 if (requestContainer == null) {
151 return;
152 }
153
154
155 myContainer.removeChildContainer(requestContainer);
156
157 request.getSession().removeAttribute(REQUEST_CONTAINER);
158
159 }
160
161 /***
162 * Template Method, allowing a subclass to do an action after any state
163 * in this controller is performed.
164 *
165 * @param nextState the state to be performed
166 * @param request the request object
167 * @param response the response object
168 * @throws ControllerException upon error.
169 */
170 protected synchronized void prePerform(final State nextState, final ExpressoRequest request,
171 final ExpressoResponse response) throws ControllerException {
172
173 super.prePerform(nextState, request, response);
174
175 if (!registeredWithParent) {
176 registerMyContainerWithSchema();
177 }
178
179
180 MutablePicoContainer requestContainer = new DefaultPicoContainer(myContainer);
181
182
183 requestContainer.registerComponentInstance(ExpressoRequest.class, request);
184 requestContainer.registerComponentInstance(ExpressoResponse.class, response);
185
186 if (request instanceof ControllerRequest && response instanceof ControllerResponse) {
187 requestContainer.registerComponentInstance(ControllerRequest.class, request);
188 requestContainer.registerComponentInstance(ControllerResponse.class, response);
189 }
190
191 if (request instanceof ServletControllerRequest) {
192 requestContainer.registerComponentInstance(ServletControllerRequest.class, request);
193 }
194
195 if (response instanceof ServletControllerResponse) {
196 requestContainer.registerComponentInstance(ServletControllerResponse.class, response);
197 }
198
199 try {
200 request.getSession().setAttribute(REQUEST_CONTAINER, requestContainer);
201 } catch (ControllerException ex) {
202 myContainer.removeChildContainer(requestContainer);
203 }
204
205 }
206
207 /***
208 * Initialize the container if it hasn't been already done.
209 */
210 private synchronized void registerMyContainerWithSchema() {
211 if (!registeredWithParent) {
212 myContainer = new DefaultPicoContainer(getMyComponentSchema().getApplicationContainer());
213 getMyComponentSchema().getApplicationContainer().addChildContainer(myContainer);
214
215
216
217 myContainer.registerComponentInstance(Controller.class, this);
218
219 for (Iterator i = this.registrationCommands.iterator(); i.hasNext();) {
220 RegistrationCommand oneCommand = (RegistrationCommand) i.next();
221 myContainer.registerComponentImplementation(oneCommand.getState(), oneCommand.getHandler());
222 }
223
224
225 initializeControllerContainer(myContainer);
226 registeredWithParent = true;
227 }
228 }
229
230 /***
231 * Grab the component schema instance -- factory method.
232 *
233 * @return ComponentSchema
234 */
235 private ComponentSchema getMyComponentSchema() {
236 return (ComponentSchema) SchemaFactory.getInstance().getSchema(
237 mySchemaClass.getName());
238 }
239
240 /***
241 * This is the method where all of the real work of the controller is
242 * done.
243 *
244 * @param newState The new state to transition to
245 * @param myRequest The calling controllerRequest object.
246 * @return a newly instantiated ControllerResponse for this state.
247 * @throws ControllerException on Error
248 */
249 public ExpressoResponse newExpressoState(final String newState,
250 final ExpressoRequest myRequest) throws ControllerException {
251
252 if (!stateHandlerStates.contains(newState)) {
253
254 return super.newExpressoState(newState, myRequest);
255 } else {
256 ExpressoResponse response = super.newExpressoState(newState, myRequest);
257 StateHandler stateHandler = (StateHandler) myContainer.getComponentInstance(newState);
258 try {
259 stateHandler.handleRequest(myRequest, response);
260 } catch (DBException ex) {
261 throw new ControllerException("Database Error handling Controller state", ex);
262 }
263
264
265
266 postPerform(this.getState(newState), myRequest, response);
267 return response;
268 }
269
270 }
271
272 /***
273 * Retrieve the application locator. Example Usage in a subclass statehandler.
274 * <p><code>this.locator().locate(Cache.class);</code></p>
275 *
276 * @return ComponentServiceLocator instance.
277 * @throws IllegalStateException if the attached schema does not implement
278 * the ComponentServiceLocator interface.
279 */
280 protected ComponentServiceLocator locator() {
281 if (!registeredWithParent) {
282 registerMyContainerWithSchema();
283 }
284 return this;
285 }
286
287 /***
288 * An inner class for registering commands in a list.
289 *
290 * @author Michael Rimov
291 */
292 private static class RegistrationCommand {
293 private final String state;
294
295 private final Class handler;
296
297 public RegistrationCommand(String commandState, Class commandHandler) {
298 state = commandState;
299 handler = commandHandler;
300 }
301
302 public Class getHandler() {
303 return handler;
304 }
305
306 public String getState() {
307 return state;
308 }
309
310 }
311
312
313 /***
314 * Retrieves the desired service defined by the key. Due to API issues,
315 * this retrieves the Controller-Level services, rather than the
316 * request level services.
317 *
318 * @param serviceKey Object usually the service interface class, but may
319 * be other object.
320 * @return Object.
321 * @throws NoSuchServiceException if unable to locate the service.
322 * @throws IllegalArgumentException if service key is null.
323 */
324 public Object locate(Object serviceKey) throws NoSuchServiceException, IllegalArgumentException {
325 if (serviceKey == null) {
326 throw new IllegalArgumentException("Service Key Cannot Be Null");
327 }
328
329
330 Object returnValue = this.myContainer.getComponentInstance(serviceKey);
331 if (returnValue == null) {
332 throw new NoSuchServiceException("Could not find service with key: " + serviceKey.toString());
333 }
334
335 return returnValue;
336 }
337
338
339 }