/* * $Id$ * * 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.chain; import org.apache.commons.beanutils.ConstructorUtils; import org.apache.commons.chain.Catalog; import org.apache.commons.chain.CatalogFactory; import org.apache.commons.chain.Command; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.struts.action.ActionServlet; import org.apache.struts.action.RequestProcessor; import org.apache.struts.chain.contexts.ActionContext; import org.apache.struts.chain.contexts.ServletActionContext; import org.apache.struts.config.ControllerConfig; import org.apache.struts.config.ModuleConfig; import org.apache.struts.upload.MultipartRequestWrapper; import org.apache.struts.util.RequestUtils; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.UnavailableException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.Constructor; /** *
ComposableRequestProcessor uses the Chain Of Resposibility design * pattern (as implemented by the commons-chain package in Jakarta Commons) to * support external configuration of command chains to be used. It is * configured via the following context initialization parameters:
* *Cache for constructor discovered by setActionContextClass method. *
*/ private static final Class[] SERVLET_ACTION_CONTEXT_CTOR_SIGNATURE = new Class[] { ServletContext.class, HttpServletRequest.class, HttpServletResponse.class }; /** *Token for ActionContext clazss so that it can be stored in the * ControllerConfig.
*/ public static final String ACTION_CONTEXT_CLASS = "ACTION_CONTEXT_CLASS"; /** *The Log instance for this class.
The {@link CatalogFactory} from which catalog containing the the * base request-processing {@link Command} will be retrieved.
*/ protected CatalogFactory catalogFactory = null; /** *The {@link Catalog} containing all of the available command chains * for this module. */ protected Catalog catalog = null; /** *
The {@link Command} to be executed for each request.
*/ protected Command command = null; /** *ActionContext class as cached by createActionContextInstance * method.
*/ private Class actionContextClass; /** *ActionContext constructor as cached by createActionContextInstance * method.
*/ private Constructor servletActionContextConstructor = null; // ---------------------------------------------------------- Public Methods /** *Clean up in preparation for a shutdown of this application.
*/ public void destroy() { super.destroy(); catalogFactory = null; catalog = null; command = null; actionContextClass = null; servletActionContextConstructor = null; } /** *Initialize this request processor instance.
* * @param servlet The ActionServlet we are associated with * @param moduleConfig The ModuleConfig we are associated with. * @throws ServletException If an error occurs during initialization */ public void init(ActionServlet servlet, ModuleConfig moduleConfig) throws ServletException { LOG.info( "Initializing composable request processor for module prefix '" + moduleConfig.getPrefix() + "'"); super.init(servlet, moduleConfig); initCatalogFactory(servlet, moduleConfig); ControllerConfig controllerConfig = moduleConfig.getControllerConfig(); String catalogName = controllerConfig.getCatalog(); catalog = this.catalogFactory.getCatalog(catalogName); if (catalog == null) { throw new ServletException("Cannot find catalog '" + catalogName + "'"); } String commandName = controllerConfig.getCommand(); command = catalog.getCommand(commandName); if (command == null) { throw new ServletException("Cannot find command '" + commandName + "'"); } this.setActionContextClassName(controllerConfig.getProperty( ACTION_CONTEXT_CLASS)); } /** *Set and cache ActionContext class.
If there is a custom * class provided and if it uses our "preferred" constructor, cache a * reference to that constructor rather than looking it up every time. *
* * @param actionContextClass The ActionContext class to process */ private void setActionContextClass(Class actionContextClass) { this.actionContextClass = actionContextClass; if (actionContextClass != null) { this.servletActionContextConstructor = ConstructorUtils.getAccessibleConstructor(actionContextClass, SERVLET_ACTION_CONTEXT_CTOR_SIGNATURE); } else { this.servletActionContextConstructor = null; } } /** *Make sure that the specified className identfies a
* class which can be found and which implements the
* ActionContext interface.
Establish the CatalogFactory which will be used to look up the * catalog which has the request processing command.
The base * implementation simply calls CatalogFactory.getInstance(), unless the * catalogFactory property of this object has already been set, in which * case it is not changed.
* * @param servlet The ActionServlet we are processing * @param moduleConfig The ModuleConfig we are processing */ protected void initCatalogFactory(ActionServlet servlet, ModuleConfig moduleConfig) { if (this.catalogFactory != null) { return; } this.catalogFactory = CatalogFactory.getInstance(); } /** *Process an HttpServletRequest and create the
* corresponding HttpServletResponse.
Provide the initialized ActionContext instance which
* will be used by this request. Internally, this simply calls
* createActionContextInstance followed by
* initializeActionContext.
Create a new instance of ActionContext according to
* configuration. If no alternative was specified at initialization, a
* new instance ServletActionContext is returned. If an
* alternative was specified using the ACTION_CONTEXT_CLASS
* property, then that value is treated as a classname, and an instance of
* that class is created. If that class implements the same constructor
* that ServletActionContext does, then that constructor will
* be used: ServletContext, HttpServletRequest,
* HttpServletResponse; otherwise, it is assumed that the class has
* a no-arguments constructor. If these constraints do not suit you,
* simply override this method in a subclass.
Set common properties on the given ActionContext
* instance so that commands in the chain can count on their presence.
* Note that while this method does not require that its argument be an
* instance of ServletActionContext, at this time many common
* Struts commands will be expecting to receive an ActionContext
* which is also a ServletActionContext.
If this is a multipart request, wrap it with a special wrapper. * Otherwise, return the request unchanged.
* * @param request The HttpServletRequest we are processing * @return Original or wrapped request as appropriate */ protected HttpServletRequest processMultipart(HttpServletRequest request) { if (!"POST".equalsIgnoreCase(request.getMethod())) { return (request); } String contentType = request.getContentType(); if ((contentType != null) && contentType.startsWith("multipart/form-data")) { return (new MultipartRequestWrapper(request)); } else { return (request); } } /** *Set the CatalogFactory instance which should be used to
* find the request-processing command. In the base implementation, if
* this value is not already set, then it will be initialized when {@link
* #initCatalogFactory} is called.