Source code: org/infohazard/maverick/flow/AbstractControllerFactory.java
1 /*
2 * $Id: AbstractControllerFactory.java,v 1.1 2004/06/27 17:42:14 eelco12 Exp $
3 * $Source: /cvsroot/mav/maverick/src/java/org/infohazard/maverick/flow/AbstractControllerFactory.java,v $
4 */
5
6 package org.infohazard.maverick.flow;
7
8 import java.util.Map;
9
10 import javax.servlet.ServletConfig;
11 import javax.servlet.ServletException;
12
13 import org.infohazard.maverick.util.XML;
14 import org.jdom.Element;
15
16 /**
17 * Base class for controller factories.
18 * Creates (what appears to be) a singleton controller object appropriate
19 * for the type of controller specified in the XML, including single-use
20 * controllers. If no controller is specified, a special do-nothing
21 * controller is returned. If you want to use a custom controller factory,
22 * it is advisable to extend from this class and override either the
23 * interface method (createController) or override one or more of the
24 * template methods (getControllerClass, getControllerInstance and
25 * initializeController).
26 *
27 * @author Jeff Schnitzer
28 * @author Eelco Hillenius
29 */
30 public abstract class AbstractControllerFactory implements ControllerFactory
31 {
32 /**
33 * xml attribute of controller class.
34 */
35 protected static final String ATTR_CONTROLLER_CLASS = "class";
36
37 /**
38 * Dummy controller class.
39 */
40 static class NullController implements ControllerSingleton
41 {
42 /** initialize. */
43 public void init(Element controllerNode) {}
44
45 /** cmd method impl. */
46 public String go(ControllerContext cctx) throws ServletException
47 { return "NO CONTROLLER DEFINED"; }
48 }
49
50 /**
51 * dummy controller.
52 */
53 protected static final Controller nullController = new NullController();
54
55 /**
56 * Initialize: this method does nothing; override to customize.
57 * @see org.infohazard.maverick.flow.ControllerFactory#init(org.jdom.Element, javax.servlet.ServletConfig)
58 */
59 public void init(Element factoryNode, ServletConfig servletCfg) throws ConfigException
60 {
61 // no nada
62 }
63
64 /**
65 * Creates (what appears to be) a singleton controller object appropriate
66 * for the type of controller specified in the XML, including single-use
67 * controllers. If no controller is specified, a special do-nothing
68 * controller is returned.
69 * @see org.infohazard.maverick.flow.ControllerFactory#createController(org.jdom.Element)
70 */
71 public Controller createController(Element controllerNode) throws ConfigException
72 {
73 if (controllerNode == null) // if no controller is defined, return the dummy impl
74 {
75 return nullController;
76 }
77
78 // get the class of the controller node
79 Class controllerClass = getControllerClass(controllerNode);
80 // create the proper instance based on the class
81 Controller controller = getControllerInstance(controllerNode, controllerClass);
82 // initialize the controller based on the implementation
83 initializeController(controllerNode, controller);
84
85 return controller;
86 }
87
88 /**
89 * Get the controller class from the configuration.
90 * @param controllerNode the xml node of the controller
91 * @return Class the class from the configuration or null if the controller node was null
92 * @throws ConfigException
93 */
94 protected Class getControllerClass(Element controllerNode) throws ConfigException
95 {
96 if(controllerNode == null) return null;
97
98 String className = controllerNode.getAttributeValue(ATTR_CONTROLLER_CLASS);
99 if (className == null || className.trim().equals(""))
100 {
101 throw new ConfigException(
102 "Controller element must have " + ATTR_CONTROLLER_CLASS
103 + " attribute: " + XML.toString(controllerNode));
104 }
105
106 Class controllerClass;
107 try
108 {
109 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
110 if (classLoader == null)
111 {
112 classLoader = AbstractControllerFactory.class.getClassLoader();
113 }
114 controllerClass = classLoader.loadClass(className);
115 }
116 catch (ClassNotFoundException ex) { throw new ConfigException(ex); }
117 return controllerClass;
118 }
119
120 /**
121 * Create a controller instance (or a decorator) based on the controller class
122 * and the controller node.
123 * @param controllerNode xml node of the controller
124 * @param controllerClass the class of the controller
125 * @return Controller a controller or a controller decorator
126 * @throws ConfigException
127 */
128 protected Controller getControllerInstance(
129 Element controllerNode,
130 Class controllerClass)
131 throws ConfigException
132 {
133 Controller controller = null;
134 try
135 {
136 if (ControllerSingleton.class.isAssignableFrom(controllerClass))
137 {
138 controller = createSingletonController(controllerNode, controllerClass);
139 }
140 else
141 {
142 controller = createThrowawayController(controllerNode, controllerClass);
143 }
144 }
145 catch (InstantiationException ex) { throw new ConfigException(ex); }
146 catch (IllegalAccessException ex) { throw new ConfigException(ex); }
147
148 // decorate the controller if needed
149 controller = decorateController(controllerNode, controller);
150
151 return controller;
152 }
153
154 /**
155 * If needed, decorate the controller.
156 * @param controllerNode xml node of controller
157 * @param controller the controller instance.
158 * @return Controller the original controller or a decorator
159 * @throws ConfigException
160 */
161 protected Controller decorateController(
162 Element controllerNode,
163 Controller controller)
164 throws ConfigException
165 {
166 Controller decorated = controller;
167 Map params = XML.getParams(controllerNode);
168 if (params != null) // if we have params, create a decorator for the controller
169 {
170 decorated = new ControllerWithParams(controller, params);
171 }
172 return decorated;
173 }
174
175 /**
176 * Create a singleton controller instance.
177 * @param controllerNode controller node
178 * @param controllerClass controller class
179 * @return Controller instance
180 * @throws ConfigException
181 * @throws InstantiationException
182 * @throws IllegalAccessException
183 */
184 protected Controller createSingletonController(
185 Element controllerNode,
186 Class controllerClass)
187 throws ConfigException,
188 InstantiationException,
189 IllegalAccessException
190 {
191 return (ControllerSingleton)controllerClass.newInstance();
192 }
193
194 /**
195 * Create a throwaway controller; this implementation creates
196 * an instance of ThrowawayControllerAdapter.
197 * @param controllerNode controller node
198 * @param controllerClass controller class
199 * @return Controller instance
200 * @throws ConfigException
201 * @throws InstantiationException
202 * @throws IllegalAccessException
203 */
204 protected Controller createThrowawayController(
205 Element controllerNode,
206 Class controllerClass)
207 throws ConfigException,
208 InstantiationException,
209 IllegalAccessException
210 {
211 return new ThrowawayControllerAdapter(controllerClass);
212 }
213
214 /**
215 * initialize the controller if it needs to be done. This implementation
216 * just looks if the given controller (or embedded controller in case
217 * the given controller is a decorator) is of type ControllerSingleton and,
218 * if it is, it's init method is called.
219 * @param controllerNode the xml node of the controller
220 * @param controller the controller instance
221 * @throws ConfigException
222 */
223 protected void initializeController(
224 Element controllerNode,
225 Controller controller)
226 throws ConfigException
227 {
228 controller = getControllerUndecorated(controller);
229 if(controller instanceof ControllerSingleton)
230 {
231 ((ControllerSingleton)controller).init(controllerNode);
232 }
233 // else do nothing
234 }
235
236 /**
237 * Get the 'real' controller instance unwrapped.
238 * @param controller the controller or wrapper.
239 * @return Controller the 'real' controller instance unwrapped.
240 */
241 protected Controller getControllerUndecorated(Controller controller)
242 {
243 Controller concreteController = controller;
244 if(controller instanceof ControllerWithParams)
245 {
246 concreteController = ((ControllerWithParams)controller).getDecorated();
247 }
248 return concreteController;
249 }
250 }