/* * $Id: RequestUtils.java 727180 2008-12-16 21:54:10Z niallp $ * * 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.util; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.struts.Globals; import org.apache.struts.action.*; import org.apache.struts.config.ActionConfig; import org.apache.struts.config.FormBeanConfig; import org.apache.struts.config.ForwardConfig; import org.apache.struts.config.ModuleConfig; import org.apache.struts.upload.FormFile; import org.apache.struts.upload.MultipartRequestHandler; import org.apache.struts.upload.MultipartRequestWrapper; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.util.*; /** *
General purpose utility methods related to processing a servlet request * in the Struts controller framework.
* * @version $Rev: 727180 $ $Date: 2008-12-17 00:54:10 +0300 (Ср., 17 дек. 2008) $ */ public class RequestUtils { // ------------------------------------------------------- Static Variables /** *Commons Logging instance.
*/ protected static Log log = LogFactory.getLog(RequestUtils.class); // --------------------------------------------------------- Public Methods /** *Create and return an absolute URL for the specified context-relative * path, based on the server and context information in the specified * request.
* * @param request The servlet request we are processing * @param path The context-relative path (must start with '/') * @return absolute URL based on context-relative path * @throws MalformedURLException if we cannot create an absolute URL */ public static URL absoluteURL(HttpServletRequest request, String path) throws MalformedURLException { return (new URL(serverURL(request), request.getContextPath() + path)); } /** *Return the Class
object for the specified fully
* qualified class name, from this web application's class loader.
Return the Class
object for the specified fully
* qualified class name, from this web application's class loader.
Return a new instance of the specified fully qualified class name, * after loading the class from this web application's class loader. The * specified class MUST have a public zero-arguments * constructor.
* * @param className Fully qualified class name to use * @return new instance of class * @throws ClassNotFoundException if the class cannot be found * @throws IllegalAccessException if the class or its constructor is not * accessible * @throws InstantiationException if this class represents an abstract * class, an interface, an array class, a * primitive type, or void * @throws InstantiationException if this class has no zero-arguments * constructor */ public static Object applicationInstance(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException { return applicationInstance(className, null); } /** *Return a new instance of the specified fully qualified class name, * after loading the class from this web application's class loader. The * specified class MUST have a public zero-arguments * constructor.
* * @param className Fully qualified class name to use * @param classLoader The desired classloader to use * @return new instance of class * @throws ClassNotFoundException if the class cannot be found * @throws IllegalAccessException if the class or its constructor is not * accessible * @throws InstantiationException if this class represents an abstract * class, an interface, an array class, a * primitive type, or void * @throws InstantiationException if this class has no zero-arguments * constructor */ public static Object applicationInstance(String className, ClassLoader classLoader) throws ClassNotFoundException, IllegalAccessException, InstantiationException { return (applicationClass(className, classLoader).newInstance()); } /** *Create (if necessary) and return an ActionForm
instance
* appropriate for this request. If no ActionForm
instance
* is required, return null
.
Create and return an ActionForm
instance appropriate to
* the information in config
.
Does not perform any checks to see if an existing ActionForm exists * which could be reused.
* * @param config The configuration for the Form bean which is to be * created. * @param servlet The action servlet * @return ActionForm instance associated with this request */ public static ActionForm createActionForm(FormBeanConfig config, ActionServlet servlet) { if (config == null) { return (null); } ActionForm instance = null; // Create and return a new form bean instance try { instance = config.createActionForm(servlet); if (log.isDebugEnabled()) { log.debug(" Creating new " + (config.getDynamic() ? "DynaActionForm" : "ActionForm") + " instance of type '" + config.getType() + "'"); log.trace(" --> " + instance); } } catch (Throwable t) { log.error(servlet.getInternal().getMessage("formBean", config.getType()), t); } return (instance); } /** *Retrieves the servlet mapping pattern for the specified {@link ActionServlet}.
* * @return the servlet mapping * @see Globals#SERVLET_KEY * @since Struts 1.3.6 */ public static String getServletMapping(ActionServlet servlet) { ServletContext servletContext = servlet.getServletConfig().getServletContext(); return (String) servletContext.getAttribute(Globals.SERVLET_KEY); } /** *Look up and return current user locale, based on the specified * parameters.
* * @param request The request used to lookup the Locale * @param locale Name of the session attribute for our user's Locale. If * this isnull
, the default locale key is
* used for the lookup.
* @return current user locale
* @since Struts 1.2
*/
public static Locale getUserLocale(HttpServletRequest request, String locale) {
Locale userLocale = null;
HttpSession session = request.getSession(false);
if (locale == null) {
locale = Globals.LOCALE_KEY;
}
// Only check session if sessions are enabled
if (session != null) {
userLocale = (Locale) session.getAttribute(locale);
}
if (userLocale == null) {
// Returns Locale based on Accept-Language header or the server default
userLocale = request.getLocale();
}
return userLocale;
}
/**
* Populate the properties of the specified JavaBean from the specified
* HTTP request, based on matching each parameter name against the
* corresponding JavaBeans "property setter" methods in the bean's class.
* Suitable conversion is done for argument types as described under
* convert()
.
Populate the properties of the specified JavaBean from the specified
* HTTP request, based on matching each parameter name (plus an optional
* prefix and/or suffix) against the corresponding JavaBeans "property
* setter" methods in the bean's class. Suitable conversion is done for
* argument types as described under setProperties
.
If you specify a non-null prefix
and a non-null
* suffix
, the parameter name must match
* both conditions for its value(s) to be used in
* populating bean properties. If the request's content type is
* "multipart/form-data" and the method is "POST", the
* HttpServletRequest
object will be wrapped in a
* MultipartRequestWrapper
*
* @param bean The JavaBean whose properties are to be set
* @param prefix The prefix (if any) to be prepend to bean property names
* when looking for matching parameters
* @param suffix The suffix (if any) to be appended to bean property
* names when looking for matching parameters
* @param request The HTTP request whose parameters are to be used to
* populate bean properties
* @throws ServletException if an exception is thrown while setting
* property values
*/
public static void populate(Object bean, String prefix, String suffix,
HttpServletRequest request)
throws ServletException {
// Build a list of relevant request parameters from this request
HashMap properties = new HashMap();
// Iterator of parameter names
Enumeration names = null;
// Map for multipart parameters
Map multipartParameters = null;
String contentType = request.getContentType();
String method = request.getMethod();
boolean isMultipart = false;
if (bean instanceof ActionForm) {
((ActionForm) bean).setMultipartRequestHandler(null);
}
MultipartRequestHandler multipartHandler = null;
if ((contentType != null)
&& (contentType.startsWith("multipart/form-data"))
&& (method.equalsIgnoreCase("POST"))) {
// Get the ActionServletWrapper from the form bean
ActionServletWrapper servlet;
if (bean instanceof ActionForm) {
servlet = ((ActionForm) bean).getServletWrapper();
} else {
throw new ServletException("bean that's supposed to be "
+ "populated from a multipart request is not of type "
+ "\"org.apache.struts.action.ActionForm\", but type "
+ "\"" + bean.getClass().getName() + "\"");
}
// Obtain a MultipartRequestHandler
multipartHandler = getMultipartHandler(request);
if (multipartHandler != null) {
isMultipart = true;
// Set servlet and mapping info
servlet.setServletFor(multipartHandler);
multipartHandler.setMapping((ActionMapping) request
.getAttribute(Globals.MAPPING_KEY));
// Initialize multipart request class handler
multipartHandler.handleRequest(request);
//stop here if the maximum length has been exceeded
Boolean maxLengthExceeded =
(Boolean) request.getAttribute(MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED);
if ((maxLengthExceeded != null)
&& (maxLengthExceeded.booleanValue())) {
((ActionForm) bean).setMultipartRequestHandler(multipartHandler);
return;
}
//retrieve form values and put into properties
multipartParameters =
getAllParametersForMultipartRequest(request,
multipartHandler);
names = Collections.enumeration(multipartParameters.keySet());
}
}
if (!isMultipart) {
names = request.getParameterNames();
}
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
String stripped = name;
if (prefix != null) {
if (!stripped.startsWith(prefix)) {
continue;
}
stripped = stripped.substring(prefix.length());
}
if (suffix != null) {
if (!stripped.endsWith(suffix)) {
continue;
}
stripped =
stripped.substring(0, stripped.length() - suffix.length());
}
Object parameterValue = null;
if (isMultipart) {
parameterValue = multipartParameters.get(name);
parameterValue = rationalizeMultipleFileProperty(bean, name, parameterValue);
} else {
parameterValue = request.getParameterValues(name);
}
// Populate parameters, except "standard" struts attributes
// such as 'org.apache.struts.action.CANCEL'
if (!(stripped.startsWith("org.apache.struts."))) {
properties.put(stripped, parameterValue);
}
}
// Set the corresponding properties of our bean
try {
BeanUtils.populate(bean, properties);
} catch (Exception e) {
throw new ServletException("BeanUtils.populate", e);
} finally {
if (multipartHandler != null) {
// Set the multipart request handler for our ActionForm.
// If the bean isn't an ActionForm, an exception would have been
// thrown earlier, so it's safe to assume that our bean is
// in fact an ActionForm.
((ActionForm) bean).setMultipartRequestHandler(multipartHandler);
}
}
}
/**
*
Populates the parameters of the specified ActionRedirect from * the specified HTTP request.
* * @param redirect The ActionRedirect whose parameters are to be set * @param request The HTTP request whose parameters are to be used * @since Struts 1.4 */ public static void populate(ActionRedirect redirect, HttpServletRequest request) { assert (redirect != null) : "redirect is required"; assert (request != null) : "request is required"; Enumeration e = request.getParameterNames(); while (e.hasMoreElements()) { String name = (String) e.nextElement(); String[] values = request.getParameterValues(name); redirect.addParameter(name, values); } } /** *If the given form bean can accept multiple FormFile objects but the user only uploaded a single, then * the property will not match the form bean type. This method performs some simple checks to try to accommodate * that situation.
* * @param bean * @param name * @param parameterValue * @return * @throws ServletException if the introspection has any errors. */ private static Object rationalizeMultipleFileProperty(Object bean, String name, Object parameterValue) throws ServletException { if (!(parameterValue instanceof FormFile)) { return parameterValue; } FormFile formFileValue = (FormFile) parameterValue; try { Class propertyType = PropertyUtils.getPropertyType(bean, name); if (propertyType == null) { return parameterValue; } if (List.class.isAssignableFrom(propertyType)) { ArrayList list = new ArrayList(1); list.add(formFileValue); return list; } if (propertyType.isArray() && propertyType.getComponentType().equals(FormFile.class)) { return new FormFile[]{formFileValue}; } } catch (IllegalAccessException e) { throw new ServletException(e); } catch (InvocationTargetException e) { throw new ServletException(e); } catch (NoSuchMethodException e) { throw new ServletException(e); } // no changes return parameterValue; } /** *Try to locate a multipart request handler for this request. First, * look for a mapping-specific handler stored for us under an attribute. * If one is not present, use the global multipart handler, if there is * one.
* * @param request The HTTP request for which the multipart handler should * be found. * @return the multipart handler to use, or null if none is found. * @throws ServletException if any exception is thrown while attempting to * locate the multipart handler. */ private static MultipartRequestHandler getMultipartHandler( HttpServletRequest request) throws ServletException { MultipartRequestHandler multipartHandler = null; String multipartClass = (String) request.getAttribute(Globals.MULTIPART_KEY); request.removeAttribute(Globals.MULTIPART_KEY); // Try to initialize the mapping specific request handler if (multipartClass != null) { try { multipartHandler = (MultipartRequestHandler) applicationInstance(multipartClass); } catch (ClassNotFoundException cnfe) { log.error("MultipartRequestHandler class \"" + multipartClass + "\" in mapping class not found, " + "defaulting to global multipart class"); } catch (InstantiationException ie) { log.error("InstantiationException when instantiating " + "MultipartRequestHandler \"" + multipartClass + "\", " + "defaulting to global multipart class, exception: " + ie.getMessage()); } catch (IllegalAccessException iae) { log.error("IllegalAccessException when instantiating " + "MultipartRequestHandler \"" + multipartClass + "\", " + "defaulting to global multipart class, exception: " + iae.getMessage()); } if (multipartHandler != null) { return multipartHandler; } } ModuleConfig moduleConfig = ModuleUtils.getInstance().getModuleConfig(request); multipartClass = moduleConfig.getControllerConfig().getMultipartClass(); // Try to initialize the global request handler if (multipartClass != null) { try { multipartHandler = (MultipartRequestHandler) applicationInstance(multipartClass); } catch (ClassNotFoundException cnfe) { throw new ServletException("Cannot find multipart class \"" + multipartClass + "\"", cnfe); } catch (InstantiationException ie) { throw new ServletException( "InstantiationException when instantiating " + "multipart class \"" + multipartClass + "\"", ie); } catch (IllegalAccessException iae) { throw new ServletException( "IllegalAccessException when instantiating " + "multipart class \"" + multipartClass + "\"", iae); } if (multipartHandler != null) { return multipartHandler; } } return multipartHandler; } /** *Create a Map
containing all of the parameters supplied
* for a multipart request, keyed by parameter name. In addition to text
* and file elements from the multipart body, query string parameters are
* included as well.
Compute the printable representation of a URL, leaving off the * scheme/host/port part if no host is specified. This will typically be * the case for URLs that were originally created from relative or * context-relative URIs.
* * @param url URL to render in a printable representation * @return printable representation of a URL */ public static String printableURL(URL url) { if (url.getHost() != null) { return (url.toString()); } String file = url.getFile(); String ref = url.getRef(); if (ref == null) { return (file); } else { StringBuffer sb = new StringBuffer(file); sb.append('#'); sb.append(ref); return (sb.toString()); } } /** *Return the context-relative URL that corresponds to the specified * {@link ActionConfig}, relative to the module associated with the * current modules's {@link ModuleConfig}.
* * @param request The servlet request we are processing * @param action ActionConfig to be evaluated * @param pattern URL pattern used to map the controller servlet * @return context-relative URL relative to the module * @since Struts 1.1 */ public static String actionURL(HttpServletRequest request, ActionConfig action, String pattern) { StringBuffer sb = new StringBuffer(); if (pattern.endsWith("/*")) { sb.append(pattern.substring(0, pattern.length() - 2)); sb.append(action.getPath()); } else if (pattern.startsWith("*.")) { ModuleConfig appConfig = ModuleUtils.getInstance().getModuleConfig(request); sb.append(appConfig.getPrefix()); sb.append(action.getPath()); sb.append(pattern.substring(1)); } else { throw new IllegalArgumentException(pattern); } return sb.toString(); } /** *Return the context-relative URL that corresponds to the specified
* ForwardConfig
. The URL is calculated based on the
* properties of the {@link ForwardConfig} instance as follows:
contextRelative
property is set, it is assumed
* that the path
property contains a path that is already
* context-relative:
*
* path
property value starts with a slash, it is
* returned unmodified.path
property value
* does not start with a slash, a slash is prepended.forwardPattern
property from the
* ControllerConfig
for the application module used to
* process this request. If no pattern was configured, default to a
* pattern of $M$P
, which is compatible with the hard-coded
* mapping behavior in Struts 1.0.forwardPattern
, performing the
* following substitutions:
*
* path
property of
* the specified {@link ForwardConfig}, prepended with a slash if it does
* not start with one.Return the context-relative URL that corresponds to the specified
* ForwardConfig
. The URL is calculated based on the
* properties of the {@link ForwardConfig} instance as follows:
contextRelative
property is set, it is assumed
* that the path
property contains a path that is already
* context-relative: path
property value starts with a slash, it is
* returned unmodified.path
property value
* does not start with a slash, a slash is prepended.forwardPattern
property from the
* ControllerConfig
for the application module used to
* process this request. If no pattern was configured, default to a
* pattern of $M$P
, which is compatible with the hard-coded
* mapping behavior in Struts 1.0.forwardPattern
, performing the
* following substitutions: path
property of
* the specified {@link ForwardConfig}, prepended with a slash if it does
* not start with one.Return the URL representing the current request. This is equivalent
* to HttpServletRequest.getRequestURL
in Servlet 2.3.
Return the URL representing the scheme, server, and port number of * the current request. Server-relative URLs can be created by simply * appending the server-relative path (starting with '/') to this.
* * @param request The servlet request we are processing * @return URL representing the scheme, server, and port number of the * current request * @throws MalformedURLException if a URL cannot be created */ public static URL serverURL(HttpServletRequest request) throws MalformedURLException { StringBuffer url = requestToServerStringBuffer(request); return (new URL(url.toString())); } /** *Return the string representing the scheme, server, and port number * of the current request. Server-relative URLs can be created by simply * appending the server-relative path (starting with '/') to this.
* * @param request The servlet request we are processing * @return URL representing the scheme, server, and port number of the * current request * @since Struts 1.2.0 */ public static StringBuffer requestToServerUriStringBuffer( HttpServletRequest request) { StringBuffer serverUri = createServerUriStringBuffer(request.getScheme(), request.getServerName(), request.getServerPort(), request.getRequestURI()); return serverUri; } /** *Return StringBuffer
representing the scheme, server,
* and port number of the current request. Server-relative URLs can be
* created by simply appending the server-relative path (starting with
* '/') to this.
Return StringBuffer
representing the scheme, server,
* and port number of the current request.
Return StringBuffer
representing the scheme, server,
* and port number of the current request.
Returns the true path of the destination action if the specified forward * is an action-aliased URL. This method version forms the URL based on * the current request; selecting the current module if the forward does not * explicitly contain a module path.
* * @param forward the forward config * @param request the current request * @param servlet the servlet handling the current request * @return the context-relative URL of the action if the forward has an action identifier; otherwisenull
.
* @since Struts 1.3.6
*/
public static String actionIdURL(ForwardConfig forward, HttpServletRequest request, ActionServlet servlet) {
ModuleConfig moduleConfig = null;
if (forward.getModule() != null) {
String prefix = forward.getModule();
moduleConfig = ModuleUtils.getInstance().getModuleConfig(prefix, servlet.getServletContext());
} else {
moduleConfig = ModuleUtils.getInstance().getModuleConfig(request);
}
return actionIdURL(forward.getPath(), moduleConfig, servlet);
}
/**
* Returns the true path of the destination action if the specified forward
* is an action-aliased URL. This method version forms the URL based on
* the specified module.
*
* @param originalPath the action-aliased path
* @param moduleConfig the module config for this request
* @param servlet the servlet handling the current request
* @return the context-relative URL of the action if the path has an action identifier; otherwise null
.
* @since Struts 1.3.6
*/
public static String actionIdURL(String originalPath, ModuleConfig moduleConfig, ActionServlet servlet) {
if (originalPath.startsWith("http") || originalPath.startsWith("/")) {
return null;
}
// Split the forward path into the resource and query string;
// it is possible a forward (or redirect) has added parameters.
String actionId = null;
String qs = null;
int qpos = originalPath.indexOf("?");
if (qpos == -1) {
actionId = originalPath;
} else {
actionId = originalPath.substring(0, qpos);
qs = originalPath.substring(qpos);
}
// Find the action of the given actionId
ActionConfig actionConfig = moduleConfig.findActionConfigId(actionId);
if (actionConfig == null) {
if (log.isDebugEnabled()) {
log.debug("No actionId found for " + actionId);
}
return null;
}
String path = actionConfig.getPath();
String mapping = RequestUtils.getServletMapping(servlet);
StringBuffer actionIdPath = new StringBuffer();
// Form the path based on the servlet mapping pattern
if (mapping.startsWith("*")) {
actionIdPath.append(path);
actionIdPath.append(mapping.substring(1));
} else if (mapping.startsWith("/")) { // implied ends with a *
mapping = mapping.substring(0, mapping.length() - 1);
if (mapping.endsWith("/") && path.startsWith("/")) {
actionIdPath.append(mapping);
actionIdPath.append(path.substring(1));
} else {
actionIdPath.append(mapping);
actionIdPath.append(path);
}
} else {
log.warn("Unknown servlet mapping pattern");
actionIdPath.append(path);
}
// Lastly add any query parameters (the ? is part of the query string)
if (qs != null) {
actionIdPath.append(qs);
}
// Return the path
if (log.isDebugEnabled()) {
log.debug(originalPath + " unaliased to " + actionIdPath.toString());
}
return actionIdPath.toString();
}
/**
* Determines whether the current request is forwarded.
*
* @param request current HTTP request
* @return true if the request is forwarded; otherwise false
* @since Struts 1.4
*/
public static boolean isRequestForwarded(HttpServletRequest request) {
return (request.getAttribute("jakarta.servlet.forward.request_uri") != null);
}
/**
* Determines whether the current request is included.
*
* @param request current HTTP request
* @return true if the request is included; otherwise false
* @since Struts 1.4
*/
public static boolean isRequestIncluded(HttpServletRequest request) {
return (request.getAttribute("jakarta.servlet.include.request_uri") != null);
}
/**
* Verifies whether current request is forwarded from one action to
* another or not.
*
* @param request current HTTP request
* @return true if the request is chained; otherwise false
* @since Struts 1.4
*/
public static boolean isRequestChained(HttpServletRequest request) {
return (request.getAttribute(Globals.CHAIN_KEY) != null);
}
}