org.apache.struts.tiles.TilesPlugin Maven / Gradle / Ivy
/*
* $Id: TilesPlugin.java 471754 2006-11-06 14:55:09Z husted $
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.struts.tiles;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.PlugIn;
import org.apache.struts.action.RequestProcessor;
import org.apache.struts.chain.ComposableRequestProcessor;
import org.apache.struts.config.ControllerConfig;
import org.apache.struts.config.ModuleConfig;
import org.apache.struts.config.PlugInConfig;
import org.apache.struts.util.RequestUtils;
/**
* Tiles Plugin used to initialize Tiles.
* This plugin is to be used with Struts 1.1 in association with
* {@link TilesRequestProcessor}.
*
* This plugin creates one definition factory for each Struts-module. The definition factory
* configuration is read first from 'web.xml' (backward compatibility), then it is
* overloaded with values found in the plugin property values.
*
* The plugin changes the Struts configuration by specifying a {@link TilesRequestProcessor} as
* request processor. If you want to use your own RequestProcessor,
* it should subclass TilesRequestProcessor.
*
* This plugin can also be used to create one single factory for all modules.
* This behavior is enabled by specifying moduleAware=false
in each
* plugin properties. In this case, the definition factory
* configuration file is read by the first Tiles plugin to be initialized. The order is
* determined by the order of modules declaration in web.xml. The first module
* is always the default one if it exists.
* The plugin should be declared in each struts-config.xml file in order to
* properly initialize the request processor.
* @since Struts 1.1
*/
public class TilesPlugin implements PlugIn {
/**
* Commons Logging instance.
*/
protected static Log log = LogFactory.getLog(TilesPlugin.class);
/**
* Is the factory module aware?
*/
protected boolean moduleAware = false;
/**
* Tiles util implementation classname. This property can be set
* by user in the plugin declaration.
*/
protected String tilesUtilImplClassname = null;
/**
* Associated definition factory.
*/
protected DefinitionsFactory definitionFactory = null;
/**
* The plugin config object provided by the ActionServlet initializing
* this plugin.
*/
protected PlugInConfig currentPlugInConfigObject=null;
/**
* Get the module aware flag.
* @return true
: user wants a single factory instance,
* false:
user wants multiple factory instances (one per module with Struts)
*/
public boolean isModuleAware() {
return moduleAware;
}
/**
* Set the module aware flag.
* This flag is only meaningful if the property tilesUtilImplClassname
is not
* set.
* @param moduleAware true
: user wants a single factory instance,
* false:
user wants multiple factory instances (one per module with Struts)
*/
public void setModuleAware(boolean moduleAware) {
this.moduleAware = moduleAware;
}
/**
* Receive notification that the specified module is being
* started up.
*
* @param servlet ActionServlet that is managing all the modules
* in this web application.
* @param moduleConfig ModuleConfig for the module with which
* this plugin is associated.
*
* @exception ServletException if this PlugIn
cannot
* be successfully initialized.
*/
public void init(ActionServlet servlet, ModuleConfig moduleConfig)
throws ServletException {
// Create factory config object
DefinitionsFactoryConfig factoryConfig =
readFactoryConfig(servlet, moduleConfig);
// Set the module name in the config. This name will be used to compute
// the name under which the factory is stored.
factoryConfig.setFactoryName(moduleConfig.getPrefix());
// Set RequestProcessor class
this.initRequestProcessorClass(moduleConfig);
this.initTilesUtil();
this.initDefinitionsFactory(servlet.getServletContext(), moduleConfig, factoryConfig);
}
/**
* Set TilesUtil implementation according to properties 'tilesUtilImplClassname'
* and 'moduleAware'. These properties are taken into account only once. A
* side effect is that only the values set in the first initialized plugin are
* effectively taken into account.
* @throws ServletException
*/
private void initTilesUtil() throws ServletException {
if (TilesUtil.isTilesUtilImplSet()) {
return;
}
// Check if user has specified a TilesUtil implementation classname or not.
// If no implementation is specified, check if user has specified one
// shared single factory for all module, or one factory for each module.
if (this.getTilesUtilImplClassname() == null) {
if (isModuleAware()) {
TilesUtil.setTilesUtil(new TilesUtilStrutsModulesImpl());
} else {
TilesUtil.setTilesUtil(new TilesUtilStrutsImpl());
}
} else { // A classname is specified for the tilesUtilImp, use it.
try {
TilesUtilStrutsImpl impl =
(TilesUtilStrutsImpl) RequestUtils
.applicationClass(getTilesUtilImplClassname())
.newInstance();
TilesUtil.setTilesUtil(impl);
} catch (ClassCastException ex) {
throw new ServletException(
"Can't set TilesUtil implementation to '"
+ getTilesUtilImplClassname()
+ "'. TilesUtil implementation should be a subclass of '"
+ TilesUtilStrutsImpl.class.getName()
+ "'");
} catch (Exception ex) {
throw new ServletException(
"Can't set TilesUtil implementation.",
ex);
}
}
}
/**
* Initialize the DefinitionsFactory this module will use.
* @param servletContext
* @param moduleConfig
* @param factoryConfig
* @throws ServletException
*/
private void initDefinitionsFactory(
ServletContext servletContext,
ModuleConfig moduleConfig,
DefinitionsFactoryConfig factoryConfig)
throws ServletException {
// Check if a factory already exist for this module
definitionFactory =
((TilesUtilStrutsImpl) TilesUtil.getTilesUtil()).getDefinitionsFactory(
servletContext,
moduleConfig);
if (definitionFactory != null) {
log.info(
"Factory already exists for module '"
+ moduleConfig.getPrefix()
+ "'. The factory found is from module '"
+ definitionFactory.getConfig().getFactoryName()
+ "'. No new creation.");
return;
}
// Create configurable factory
try {
definitionFactory =
TilesUtil.createDefinitionsFactory(
servletContext,
factoryConfig);
} catch (DefinitionsFactoryException ex) {
log.error(
"Can't create Tiles definition factory for module '"
+ moduleConfig.getPrefix()
+ "'.");
throw new ServletException(ex);
}
log.info(
"Tiles definition factory loaded for module '"
+ moduleConfig.getPrefix()
+ "'.");
}
/**
* End plugin.
*/
public void destroy() {
definitionFactory.destroy();
definitionFactory = null;
}
/**
* Create FactoryConfig and initialize it from web.xml and struts-config.xml.
*
* @param servlet ActionServlet that is managing all the modules
* in this web application.
* @param config ModuleConfig for the module with which
* this plugin is associated.
* @exception ServletException if this PlugIn
cannot
* be successfully initialized.
*/
protected DefinitionsFactoryConfig readFactoryConfig(
ActionServlet servlet,
ModuleConfig config)
throws ServletException {
// Create tiles definitions config object
DefinitionsFactoryConfig factoryConfig = new DefinitionsFactoryConfig();
// Get init parameters from web.xml files
try {
DefinitionsUtil.populateDefinitionsFactoryConfig(
factoryConfig,
servlet.getServletConfig());
} catch (Exception ex) {
if (log.isDebugEnabled()){
log.debug("", ex);
}
ex.printStackTrace();
throw new UnavailableException(
"Can't populate DefinitionsFactoryConfig class from 'web.xml': "
+ ex.getMessage());
}
// Get init parameters from struts-config.xml
try {
Map strutsProperties = findStrutsPlugInConfigProperties(servlet, config);
factoryConfig.populate(strutsProperties);
} catch (Exception ex) {
if (log.isDebugEnabled()) {
log.debug("", ex);
}
throw new UnavailableException(
"Can't populate DefinitionsFactoryConfig class from '"
+ config.getPrefix()
+ "/struts-config.xml':"
+ ex.getMessage());
}
return factoryConfig;
}
/**
* Find original properties set in the Struts PlugInConfig object.
* First, we need to find the index of this plugin. Then we retrieve the array of configs
* and then the object for this plugin.
* @param servlet ActionServlet that is managing all the modules
* in this web application.
* @param config ModuleConfig for the module with which
* this plug in is associated.
*
* @exception ServletException if this PlugIn
cannot
* be successfully initialized.
*/
protected Map findStrutsPlugInConfigProperties(
ActionServlet servlet,
ModuleConfig config)
throws ServletException {
return currentPlugInConfigObject.getProperties();
}
/**
* Set RequestProcessor to appropriate Tiles {@link RequestProcessor}.
* First, check if a RequestProcessor is specified. If yes, check if it extends
* the appropriate {@link TilesRequestProcessor} class. If not, set processor class to
* TilesRequestProcessor.
*
* @param config ModuleConfig for the module with which
* this plugin is associated.
* @throws ServletException On errors.
*/
protected void initRequestProcessorClass(ModuleConfig config)
throws ServletException {
String tilesProcessorClassname = TilesRequestProcessor.class.getName();
ControllerConfig ctrlConfig = config.getControllerConfig();
String configProcessorClassname = ctrlConfig.getProcessorClass();
// Check if specified classname exist
Class configProcessorClass;
try {
configProcessorClass =
RequestUtils.applicationClass(configProcessorClassname);
} catch (ClassNotFoundException ex) {
log.fatal(
"Can't set TilesRequestProcessor: bad class name '"
+ configProcessorClassname
+ "'.");
throw new ServletException(ex);
}
// Check to see if request processor uses struts-chain. If so,
// no need to replace the request processor.
if (ComposableRequestProcessor.class.isAssignableFrom(configProcessorClass)) {
return;
}
// Check if it is the default request processor or Tiles one.
// If true, replace by Tiles' one.
if (configProcessorClassname.equals(RequestProcessor.class.getName())
|| configProcessorClassname.endsWith(tilesProcessorClassname)) {
ctrlConfig.setProcessorClass(tilesProcessorClassname);
return;
}
// Check if specified request processor is compatible with Tiles.
Class tilesProcessorClass = TilesRequestProcessor.class;
if (!tilesProcessorClass.isAssignableFrom(configProcessorClass)) {
// Not compatible
String msg =
"TilesPlugin : Specified RequestProcessor not compatible with TilesRequestProcessor";
if (log.isFatalEnabled()) {
log.fatal(msg);
}
throw new ServletException(msg);
}
}
/**
* Set Tiles util implemention classname.
* If this property is set, the flag moduleAware
will not be used anymore.
* @param tilesUtilImplClassname Classname.
*/
public void setTilesUtilImplClassname(String tilesUtilImplClassname) {
this.tilesUtilImplClassname = tilesUtilImplClassname;
}
/**
* Get Tiles util implemention classname.
* @return The classname or null
if none is set.
*/
public String getTilesUtilImplClassname() {
return tilesUtilImplClassname;
}
/**
* Method used by the ActionServlet initializing this plugin.
* Set the plugin config object read from module config.
* @param plugInConfigObject PlugInConfig.
*/
public void setCurrentPlugInConfigObject(PlugInConfig plugInConfigObject) {
this.currentPlugInConfigObject = plugInConfigObject;
}
}