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