001: /*
002: * $Id: TilesPlugin.java 471754 2006-11-06 14:55:09Z husted $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: package org.apache.struts.tiles;
023:
024: import java.util.Map;
025:
026: import javax.servlet.ServletContext;
027: import javax.servlet.ServletException;
028: import javax.servlet.UnavailableException;
029:
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032: import org.apache.struts.action.ActionServlet;
033: import org.apache.struts.action.PlugIn;
034: import org.apache.struts.action.RequestProcessor;
035: import org.apache.struts.chain.ComposableRequestProcessor;
036: import org.apache.struts.config.ControllerConfig;
037: import org.apache.struts.config.ModuleConfig;
038: import org.apache.struts.config.PlugInConfig;
039: import org.apache.struts.util.RequestUtils;
040:
041: /**
042: * Tiles Plugin used to initialize Tiles.
043: * This plugin is to be used with Struts 1.1 in association with
044: * {@link TilesRequestProcessor}.
045: * <br>
046: * This plugin creates one definition factory for each Struts-module. The definition factory
047: * configuration is read first from 'web.xml' (backward compatibility), then it is
048: * overloaded with values found in the plugin property values.
049: * <br>
050: * The plugin changes the Struts configuration by specifying a {@link TilesRequestProcessor} as
051: * request processor. If you want to use your own RequestProcessor,
052: * it should subclass TilesRequestProcessor.
053: * <br>
054: * This plugin can also be used to create one single factory for all modules.
055: * This behavior is enabled by specifying <code>moduleAware=false</code> in each
056: * plugin properties. In this case, the definition factory
057: * configuration file is read by the first Tiles plugin to be initialized. The order is
058: * determined by the order of modules declaration in web.xml. The first module
059: * is always the default one if it exists.
060: * The plugin should be declared in each struts-config.xml file in order to
061: * properly initialize the request processor.
062: * @since Struts 1.1
063: */
064: public class TilesPlugin implements PlugIn {
065:
066: /**
067: * Commons Logging instance.
068: */
069: protected static Log log = LogFactory.getLog(TilesPlugin.class);
070:
071: /**
072: * Is the factory module aware?
073: */
074: protected boolean moduleAware = false;
075:
076: /**
077: * Tiles util implementation classname. This property can be set
078: * by user in the plugin declaration.
079: */
080: protected String tilesUtilImplClassname = null;
081:
082: /**
083: * Associated definition factory.
084: */
085: protected DefinitionsFactory definitionFactory = null;
086:
087: /**
088: * The plugin config object provided by the ActionServlet initializing
089: * this plugin.
090: */
091: protected PlugInConfig currentPlugInConfigObject = null;
092:
093: /**
094: * Get the module aware flag.
095: * @return <code>true</code>: user wants a single factory instance,
096: * <code>false:</code> user wants multiple factory instances (one per module with Struts)
097: */
098: public boolean isModuleAware() {
099: 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: // Create factory config object
129: DefinitionsFactoryConfig factoryConfig = readFactoryConfig(
130: servlet, moduleConfig);
131:
132: // Set the module name in the config. This name will be used to compute
133: // the name under which the factory is stored.
134: factoryConfig.setFactoryName(moduleConfig.getPrefix());
135:
136: // Set RequestProcessor class
137: this .initRequestProcessorClass(moduleConfig);
138:
139: this .initTilesUtil();
140:
141: this .initDefinitionsFactory(servlet.getServletContext(),
142: moduleConfig, factoryConfig);
143: }
144:
145: /**
146: * Set TilesUtil implementation according to properties 'tilesUtilImplClassname'
147: * and 'moduleAware'. These properties are taken into account only once. A
148: * side effect is that only the values set in the first initialized plugin are
149: * effectively taken into account.
150: * @throws ServletException
151: */
152: private void initTilesUtil() throws ServletException {
153:
154: if (TilesUtil.isTilesUtilImplSet()) {
155: return;
156: }
157:
158: // Check if user has specified a TilesUtil implementation classname or not.
159: // If no implementation is specified, check if user has specified one
160: // shared single factory for all module, or one factory for each module.
161:
162: if (this .getTilesUtilImplClassname() == null) {
163:
164: if (isModuleAware()) {
165: TilesUtil
166: .setTilesUtil(new TilesUtilStrutsModulesImpl());
167: } else {
168: TilesUtil.setTilesUtil(new TilesUtilStrutsImpl());
169: }
170:
171: } else { // A classname is specified for the tilesUtilImp, use it.
172: try {
173: TilesUtilStrutsImpl impl = (TilesUtilStrutsImpl) RequestUtils
174: .applicationClass(getTilesUtilImplClassname())
175: .newInstance();
176: TilesUtil.setTilesUtil(impl);
177:
178: } catch (ClassCastException ex) {
179: throw new ServletException(
180: "Can't set TilesUtil implementation to '"
181: + getTilesUtilImplClassname()
182: + "'. TilesUtil implementation should be a subclass of '"
183: + TilesUtilStrutsImpl.class.getName()
184: + "'");
185:
186: } catch (Exception ex) {
187: throw new ServletException(
188: "Can't set TilesUtil implementation.", 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(ServletContext servletContext,
202: ModuleConfig moduleConfig,
203: DefinitionsFactoryConfig factoryConfig)
204: throws ServletException {
205:
206: // Check if a factory already exist for this module
207: definitionFactory = ((TilesUtilStrutsImpl) TilesUtil
208: .getTilesUtil()).getDefinitionsFactory(servletContext,
209: moduleConfig);
210:
211: if (definitionFactory != null) {
212: log.info("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: // Create configurable factory
222: try {
223: definitionFactory = TilesUtil.createDefinitionsFactory(
224: servletContext, factoryConfig);
225:
226: } catch (DefinitionsFactoryException ex) {
227: log
228: .error("Can't create Tiles definition factory for module '"
229: + moduleConfig.getPrefix() + "'.");
230:
231: throw new ServletException(ex);
232: }
233:
234: log.info("Tiles definition factory loaded for module '"
235: + moduleConfig.getPrefix() + "'.");
236: }
237:
238: /**
239: * End plugin.
240: */
241: public void destroy() {
242: definitionFactory.destroy();
243: definitionFactory = null;
244: }
245:
246: /**
247: * Create FactoryConfig and initialize it from web.xml and struts-config.xml.
248: *
249: * @param servlet ActionServlet that is managing all the modules
250: * in this web application.
251: * @param config ModuleConfig for the module with which
252: * this plugin is associated.
253: * @exception ServletException if this <code>PlugIn</code> cannot
254: * be successfully initialized.
255: */
256: protected DefinitionsFactoryConfig readFactoryConfig(
257: ActionServlet servlet, ModuleConfig config)
258: throws ServletException {
259:
260: // Create tiles definitions config object
261: DefinitionsFactoryConfig factoryConfig = new DefinitionsFactoryConfig();
262: // Get init parameters from web.xml files
263: try {
264: DefinitionsUtil.populateDefinitionsFactoryConfig(
265: factoryConfig, servlet.getServletConfig());
266:
267: } catch (Exception ex) {
268: if (log.isDebugEnabled()) {
269: log.debug("", ex);
270: }
271: ex.printStackTrace();
272: throw new UnavailableException(
273: "Can't populate DefinitionsFactoryConfig class from 'web.xml': "
274: + ex.getMessage());
275: }
276:
277: // Get init parameters from struts-config.xml
278: try {
279: Map strutsProperties = findStrutsPlugInConfigProperties(
280: servlet, config);
281: factoryConfig.populate(strutsProperties);
282:
283: } catch (Exception ex) {
284: if (log.isDebugEnabled()) {
285: log.debug("", ex);
286: }
287:
288: throw new UnavailableException(
289: "Can't populate DefinitionsFactoryConfig class from '"
290: + config.getPrefix()
291: + "/struts-config.xml':" + ex.getMessage());
292: }
293:
294: return factoryConfig;
295: }
296:
297: /**
298: * Find original properties set in the Struts PlugInConfig object.
299: * First, we need to find the index of this plugin. Then we retrieve the array of configs
300: * and then the object for this plugin.
301: * @param servlet ActionServlet that is managing all the modules
302: * in this web application.
303: * @param config ModuleConfig for the module with which
304: * this plug in is associated.
305: *
306: * @exception ServletException if this <code>PlugIn</code> cannot
307: * be successfully initialized.
308: */
309: protected Map findStrutsPlugInConfigProperties(
310: ActionServlet servlet, ModuleConfig config)
311: throws ServletException {
312:
313: return currentPlugInConfigObject.getProperties();
314: }
315:
316: /**
317: * Set RequestProcessor to appropriate Tiles {@link RequestProcessor}.
318: * First, check if a RequestProcessor is specified. If yes, check if it extends
319: * the appropriate {@link TilesRequestProcessor} class. If not, set processor class to
320: * TilesRequestProcessor.
321: *
322: * @param config ModuleConfig for the module with which
323: * this plugin is associated.
324: * @throws ServletException On errors.
325: */
326: protected void initRequestProcessorClass(ModuleConfig config)
327: throws ServletException {
328:
329: String tilesProcessorClassname = TilesRequestProcessor.class
330: .getName();
331: ControllerConfig ctrlConfig = config.getControllerConfig();
332: String configProcessorClassname = ctrlConfig
333: .getProcessorClass();
334:
335: // Check if specified classname exist
336: Class configProcessorClass;
337: try {
338: configProcessorClass = RequestUtils
339: .applicationClass(configProcessorClassname);
340:
341: } catch (ClassNotFoundException ex) {
342: log
343: .fatal("Can't set TilesRequestProcessor: bad class name '"
344: + configProcessorClassname + "'.");
345: throw new ServletException(ex);
346: }
347:
348: // Check to see if request processor uses struts-chain. If so,
349: // no need to replace the request processor.
350: if (ComposableRequestProcessor.class
351: .isAssignableFrom(configProcessorClass)) {
352: return;
353: }
354:
355: // Check if it is the default request processor or Tiles one.
356: // If true, replace by Tiles' one.
357: if (configProcessorClassname.equals(RequestProcessor.class
358: .getName())
359: || configProcessorClassname
360: .endsWith(tilesProcessorClassname)) {
361:
362: ctrlConfig.setProcessorClass(tilesProcessorClassname);
363: return;
364: }
365:
366: // Check if specified request processor is compatible with Tiles.
367: Class tilesProcessorClass = TilesRequestProcessor.class;
368: if (!tilesProcessorClass.isAssignableFrom(configProcessorClass)) {
369: // Not compatible
370: String msg = "TilesPlugin : Specified RequestProcessor not compatible with TilesRequestProcessor";
371: if (log.isFatalEnabled()) {
372: log.fatal(msg);
373: }
374: throw new ServletException(msg);
375: }
376: }
377:
378: /**
379: * Set Tiles util implemention classname.
380: * If this property is set, the flag <code>moduleAware</code> will not be used anymore.
381: * @param tilesUtilImplClassname Classname.
382: */
383: public void setTilesUtilImplClassname(String tilesUtilImplClassname) {
384: this .tilesUtilImplClassname = tilesUtilImplClassname;
385: }
386:
387: /**
388: * Get Tiles util implemention classname.
389: * @return The classname or <code>null</code> if none is set.
390: */
391: public String getTilesUtilImplClassname() {
392: return tilesUtilImplClassname;
393: }
394:
395: /**
396: * Method used by the ActionServlet initializing this plugin.
397: * Set the plugin config object read from module config.
398: * @param plugInConfigObject PlugInConfig.
399: */
400: public void setCurrentPlugInConfigObject(
401: PlugInConfig plugInConfigObject) {
402: this.currentPlugInConfigObject = plugInConfigObject;
403: }
404:
405: }
|