1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.struts.tiles;
20
21 import java.util.Map;
22
23 import javax.servlet.ServletContext;
24 import javax.servlet.ServletException;
25 import javax.servlet.UnavailableException;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.struts.action.ActionServlet;
30 import org.apache.struts.action.PlugIn;
31 import org.apache.struts.action.RequestProcessor;
32 import org.apache.struts.chain.ComposableRequestProcessor;
33 import org.apache.struts.config.ControllerConfig;
34 import org.apache.struts.config.ModuleConfig;
35 import org.apache.struts.config.PlugInConfig;
36 import org.apache.struts.util.RequestUtils;
37
38 /***
39 * Tiles Plugin used to initialize Tiles.
40 * This plugin is to be used with Struts 1.1 in association with
41 * {@link TilesRequestProcessor}.
42 * <br>
43 * This plugin creates one definition factory for each Struts-module. The definition factory
44 * configuration is read first from 'web.xml' (backward compatibility), then it is
45 * overloaded with values found in the plugin property values.
46 * <br>
47 * The plugin changes the Struts configuration by specifying a {@link TilesRequestProcessor} as
48 * request processor. If you want to use your own RequestProcessor,
49 * it should subclass TilesRequestProcessor.
50 * <br>
51 * This plugin can also be used to create one single factory for all modules.
52 * This behavior is enabled by specifying <code>moduleAware=false</code> in each
53 * plugin properties. In this case, the definition factory
54 * configuration file is read by the first Tiles plugin to be initialized. The order is
55 * determined by the order of modules declaration in web.xml. The first module
56 * is always the default one if it exists.
57 * The plugin should be declared in each struts-config.xml file in order to
58 * properly initialize the request processor.
59 * @since Struts 1.1
60 */
61 public class TilesPlugin implements PlugIn {
62
63 /***
64 * Commons Logging instance.
65 */
66 protected static Log log = LogFactory.getLog(TilesPlugin.class);
67
68 /***
69 * Is the factory module aware?
70 */
71 protected boolean moduleAware = false;
72
73 /***
74 * Tiles util implementation classname. This property can be set
75 * by user in the plugin declaration.
76 */
77 protected String tilesUtilImplClassname = null;
78
79 /***
80 * Associated definition factory.
81 */
82 protected DefinitionsFactory definitionFactory = null;
83
84 /***
85 * The plugin config object provided by the ActionServlet initializing
86 * this plugin.
87 */
88 protected PlugInConfig currentPlugInConfigObject=null;
89
90 /***
91 * Get the module aware flag.
92 * @return <code>true</code>: user wants a single factory instance,
93 * <code>false:</code> user wants multiple factory instances (one per module with Struts)
94 */
95 public boolean isModuleAware() {
96 return moduleAware;
97 }
98
99 /***
100 * Set the module aware flag.
101 * This flag is only meaningful if the property <code>tilesUtilImplClassname</code> is not
102 * set.
103 * @param moduleAware <code>true</code>: user wants a single factory instance,
104 * <code>false:</code> user wants multiple factory instances (one per module with Struts)
105 */
106 public void setModuleAware(boolean moduleAware) {
107 this.moduleAware = moduleAware;
108 }
109
110 /***
111 * <p>Receive notification that the specified module is being
112 * started up.</p>
113 *
114 * @param servlet ActionServlet that is managing all the modules
115 * in this web application.
116 * @param moduleConfig ModuleConfig for the module with which
117 * this plugin is associated.
118 *
119 * @exception ServletException if this <code>PlugIn</code> cannot
120 * be successfully initialized.
121 */
122 public void init(ActionServlet servlet, ModuleConfig moduleConfig)
123 throws ServletException {
124
125
126 DefinitionsFactoryConfig factoryConfig =
127 readFactoryConfig(servlet, moduleConfig);
128
129
130
131 factoryConfig.setFactoryName(moduleConfig.getPrefix());
132
133
134 this.initRequestProcessorClass(moduleConfig);
135
136 this.initTilesUtil();
137
138 this.initDefinitionsFactory(servlet.getServletContext(), moduleConfig, factoryConfig);
139 }
140
141 /***
142 * Set TilesUtil implementation according to properties 'tilesUtilImplClassname'
143 * and 'moduleAware'. These properties are taken into account only once. A
144 * side effect is that only the values set in the first initialized plugin are
145 * effectively taken into account.
146 * @throws ServletException
147 */
148 private void initTilesUtil() throws ServletException {
149
150 if (TilesUtil.isTilesUtilImplSet()) {
151 return;
152 }
153
154
155
156
157
158 if (this.getTilesUtilImplClassname() == null) {
159
160 if (isModuleAware()) {
161 TilesUtil.setTilesUtil(new TilesUtilStrutsModulesImpl());
162 } else {
163 TilesUtil.setTilesUtil(new TilesUtilStrutsImpl());
164 }
165
166 } else {
167 try {
168 TilesUtilStrutsImpl impl =
169 (TilesUtilStrutsImpl) RequestUtils
170 .applicationClass(getTilesUtilImplClassname())
171 .newInstance();
172 TilesUtil.setTilesUtil(impl);
173
174 } catch (ClassCastException ex) {
175 throw new ServletException(
176 "Can't set TilesUtil implementation to '"
177 + getTilesUtilImplClassname()
178 + "'. TilesUtil implementation should be a subclass of '"
179 + TilesUtilStrutsImpl.class.getName()
180 + "'");
181
182 } catch (Exception ex) {
183 throw new ServletException(
184 "Can't set TilesUtil implementation.",
185 ex);
186 }
187 }
188
189 }
190
191 /***
192 * Initialize the DefinitionsFactory this module will use.
193 * @param servletContext
194 * @param moduleConfig
195 * @param factoryConfig
196 * @throws ServletException
197 */
198 private void initDefinitionsFactory(
199 ServletContext servletContext,
200 ModuleConfig moduleConfig,
201 DefinitionsFactoryConfig factoryConfig)
202 throws ServletException {
203
204
205 definitionFactory =
206 ((TilesUtilStrutsImpl) TilesUtil.getTilesUtil()).getDefinitionsFactory(
207 servletContext,
208 moduleConfig);
209
210 if (definitionFactory != null) {
211 log.info(
212 "Factory already exists for module '"
213 + moduleConfig.getPrefix()
214 + "'. The factory found is from module '"
215 + definitionFactory.getConfig().getFactoryName()
216 + "'. No new creation.");
217
218 return;
219 }
220
221
222 try {
223 definitionFactory =
224 TilesUtil.createDefinitionsFactory(
225 servletContext,
226 factoryConfig);
227
228 } catch (DefinitionsFactoryException ex) {
229 log.error(
230 "Can't create Tiles definition factory for module '"
231 + moduleConfig.getPrefix()
232 + "'.");
233
234 throw new ServletException(ex);
235 }
236
237 log.info(
238 "Tiles definition factory loaded for module '"
239 + moduleConfig.getPrefix()
240 + "'.");
241 }
242
243 /***
244 * End plugin.
245 */
246 public void destroy() {
247 definitionFactory.destroy();
248 definitionFactory = null;
249 }
250
251 /***
252 * Create FactoryConfig and initialize it from web.xml and struts-config.xml.
253 *
254 * @param servlet ActionServlet that is managing all the modules
255 * in this web application.
256 * @param config ModuleConfig for the module with which
257 * this plugin is associated.
258 * @exception ServletException if this <code>PlugIn</code> cannot
259 * be successfully initialized.
260 */
261 protected DefinitionsFactoryConfig readFactoryConfig(
262 ActionServlet servlet,
263 ModuleConfig config)
264 throws ServletException {
265
266
267 DefinitionsFactoryConfig factoryConfig = new DefinitionsFactoryConfig();
268
269 try {
270 DefinitionsUtil.populateDefinitionsFactoryConfig(
271 factoryConfig,
272 servlet.getServletConfig());
273
274 } catch (Exception ex) {
275 if (log.isDebugEnabled()){
276 log.debug("", ex);
277 }
278 ex.printStackTrace();
279 throw new UnavailableException(
280 "Can't populate DefinitionsFactoryConfig class from 'web.xml': "
281 + ex.getMessage());
282 }
283
284
285 try {
286 Map strutsProperties = findStrutsPlugInConfigProperties(servlet, config);
287 factoryConfig.populate(strutsProperties);
288
289 } catch (Exception ex) {
290 if (log.isDebugEnabled()) {
291 log.debug("", ex);
292 }
293
294 throw new UnavailableException(
295 "Can't populate DefinitionsFactoryConfig class from '"
296 + config.getPrefix()
297 + "/struts-config.xml':"
298 + ex.getMessage());
299 }
300
301 return factoryConfig;
302 }
303
304 /***
305 * Find original properties set in the Struts PlugInConfig object.
306 * First, we need to find the index of this plugin. Then we retrieve the array of configs
307 * and then the object for this plugin.
308 * @param servlet ActionServlet that is managing all the modules
309 * in this web application.
310 * @param config ModuleConfig for the module with which
311 * this plug in is associated.
312 *
313 * @exception ServletException if this <code>PlugIn</code> cannot
314 * be successfully initialized.
315 */
316 protected Map findStrutsPlugInConfigProperties(
317 ActionServlet servlet,
318 ModuleConfig config)
319 throws ServletException {
320
321 return currentPlugInConfigObject.getProperties();
322 }
323
324 /***
325 * Set RequestProcessor to appropriate Tiles {@link RequestProcessor}.
326 * First, check if a RequestProcessor is specified. If yes, check if it extends
327 * the appropriate {@link TilesRequestProcessor} class. If not, set processor class to
328 * TilesRequestProcessor.
329 *
330 * @param config ModuleConfig for the module with which
331 * this plugin is associated.
332 * @throws ServletException On errors.
333 */
334 protected void initRequestProcessorClass(ModuleConfig config)
335 throws ServletException {
336
337 String tilesProcessorClassname = TilesRequestProcessor.class.getName();
338 ControllerConfig ctrlConfig = config.getControllerConfig();
339 String configProcessorClassname = ctrlConfig.getProcessorClass();
340
341
342 Class configProcessorClass;
343 try {
344 configProcessorClass =
345 RequestUtils.applicationClass(configProcessorClassname);
346
347 } catch (ClassNotFoundException ex) {
348 log.fatal(
349 "Can't set TilesRequestProcessor: bad class name '"
350 + configProcessorClassname
351 + "'.");
352 throw new ServletException(ex);
353 }
354
355
356
357 if (ComposableRequestProcessor.class.isAssignableFrom(configProcessorClass)) {
358 return;
359 }
360
361
362
363 if (configProcessorClassname.equals(RequestProcessor.class.getName())
364 || configProcessorClassname.endsWith(tilesProcessorClassname)) {
365
366 ctrlConfig.setProcessorClass(tilesProcessorClassname);
367 return;
368 }
369
370
371 Class tilesProcessorClass = TilesRequestProcessor.class;
372 if (!tilesProcessorClass.isAssignableFrom(configProcessorClass)) {
373
374 String msg =
375 "TilesPlugin : Specified RequestProcessor not compatible with TilesRequestProcessor";
376 if (log.isFatalEnabled()) {
377 log.fatal(msg);
378 }
379 throw new ServletException(msg);
380 }
381 }
382
383 /***
384 * Set Tiles util implemention classname.
385 * If this property is set, the flag <code>moduleAware</code> will not be used anymore.
386 * @param tilesUtilImplClassname Classname.
387 */
388 public void setTilesUtilImplClassname(String tilesUtilImplClassname) {
389 this.tilesUtilImplClassname = tilesUtilImplClassname;
390 }
391
392 /***
393 * Get Tiles util implemention classname.
394 * @return The classname or <code>null</code> if none is set.
395 */
396 public String getTilesUtilImplClassname() {
397 return tilesUtilImplClassname;
398 }
399
400 /***
401 * Method used by the ActionServlet initializing this plugin.
402 * Set the plugin config object read from module config.
403 * @param plugInConfigObject PlugInConfig.
404 */
405 public void setCurrentPlugInConfigObject(PlugInConfig plugInConfigObject) {
406 this.currentPlugInConfigObject = plugInConfigObject;
407 }
408
409 }