View Javadoc

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         //Create the state and register it.
108         State newState = new State(stateName, stateDescription);
109         super.addState(newState);
110 
111         //Register this state handler as a component.
112         registerNewComponent(stateName, stateHandler);
113 
114         //Register the state name as a 'componentized' state.
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         //Retrieve the pico-container from the request session.
148         MutablePicoContainer requestContainer = (MutablePicoContainer) request.getSession().getAttribute(
149                 REQUEST_CONTAINER);
150         if (requestContainer == null) {
151             return;
152         }
153 
154         //Remove the child container.
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         //Create the Container
180         MutablePicoContainer requestContainer = new DefaultPicoContainer(myContainer);
181 
182         //Register all the isntances of the component to this.
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             //Sub actions of this controller result in all references to controllers
216             //referring to this one.
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             //Call template method for other work.
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             // if we have NOT registered a state name, just use superclass handling scheme 
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             //Do postperform because since we use a state handler it doesn't
265             //get called normally.
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 }