org.apache.struts.util.RequestUtils Maven / Gradle / Ivy
/*
* $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.util;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.struts.Globals;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionRedirect;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.ActionServletWrapper;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
/**
* General purpose utility methods related to processing a servlet request
* in the Struts controller framework.
*
* @version $Rev$ $Date$
*/
public class RequestUtils {
// ------------------------------------------------------- Static Variables
/**
* The {@code Log} instance for this class.
*/
private final static Logger LOG =
LoggerFactory.getLogger(RequestUtils.class);
/**
* Pattern matching 'class' access.
*/
protected static final Pattern CLASS_ACCESS_PATTERN = Pattern
.compile("(.*\\.|^|.*|\\[('|\"))class(\\.|('|\")]|\\[).*",
Pattern.CASE_INSENSITIVE);
// --------------------------------------------------------- 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.
*
* @param className Fully qualified class name to be loaded
* @return Class object
* @throws ClassNotFoundException if the class cannot be found
*/
public static Class> applicationClass(String className)
throws ClassNotFoundException {
return applicationClass(className, null);
}
/**
* Return the Class
object for the specified fully
* qualified class name, from this web application's class loader.
*
* @param className Fully qualified class name to be loaded
* @param classLoader The desired classloader to use
* @return Class object
* @throws ClassNotFoundException if the class cannot be found
*/
public static Class> applicationClass(String className,
ClassLoader classLoader)
throws ClassNotFoundException {
if (classLoader == null) {
// Look up the class loader to be used
classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = RequestUtils.class.getClassLoader();
}
}
// Attempt to load the specified class
return (classLoader.loadClass(className));
}
/**
* 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 {
try {
return applicationClass(className, classLoader)
.getDeclaredConstructor()
.newInstance();
} catch (IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException
| ClassNotFoundException e) {
InstantiationException e2 =
new InstantiationException("Error creating " + className + " instance");
e2.initCause(e);
throw e2;
}
}
/**
* Create (if necessary) and return an ActionForm
instance
* appropriate for this request. If no ActionForm
instance
* is required, return null
.
*
* @param request The servlet request we are processing
* @param mapping The action mapping for this request
* @param moduleConfig The configuration for this module
* @param servlet The action servlet
* @return ActionForm instance associated with this request
*/
public static ActionForm createActionForm(HttpServletRequest request,
ActionMapping mapping, ModuleConfig moduleConfig, ActionServlet servlet) {
// Is there a form bean associated with this mapping?
String attribute = mapping.getAttribute();
if (attribute == null) {
return (null);
}
// Look up the form bean configuration information to use
String name = mapping.getName();
FormBeanConfig config = moduleConfig.findFormBeanConfig(name);
if (config == null) {
LOG.warn("No FormBeanConfig found under '{}'", name);
return (null);
}
ActionForm instance =
lookupActionForm(request, attribute, mapping.getScope());
// Can we recycle the existing form bean instance (if there is one)?
if ((instance != null) && config.canReuse(instance)) {
return (instance);
}
return createActionForm(config, servlet);
}
private static ActionForm lookupActionForm(HttpServletRequest request,
String attribute, String scope) {
// Look up any existing form bean instance
LOG.debug(" Looking for ActionForm bean instance in scope '{}' "
+ "under attribute key '{}'", scope, attribute);
ActionForm instance = null;
HttpSession session = null;
if ("request".equals(scope)) {
instance = (ActionForm) request.getAttribute(attribute);
} else {
session = request.getSession();
instance = (ActionForm) session.getAttribute(attribute);
}
return (instance);
}
/**
* 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);
LOG.atDebug()
.setMessage(" Creating new {} instance of type '{}'")
.addArgument(() -> config.getDynamic() ? "DynaActionForm" : "ActionForm")
.addArgument(config.getType())
.log();
LOG.trace(" --> {}", instance);
} catch (Throwable t) {
LOG.atError()
.setMessage(() -> servlet.getInternal().getMessage("formBean",
config.getType()))
.setCause(t)
.log();
}
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 is null
, 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()
.
*
* @param bean The JavaBean whose properties are to be set
* @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, HttpServletRequest request)
throws ServletException {
populate(bean, null, null, request);
}
/**
* 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
object.
*
* @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 = 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);
}
// 2014/05/13 - CVE-2014-0114 security problem patch.
// Author: NTT DATA Corporation
if (CLASS_ACCESS_PATTERN.matcher(stripped).matches()) {
// this log output is only for detection of invalid parameters and not an integral part of the bug fix
LOG.trace("ignore parameter: paramName={}", stripped);
continue;
}
// 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 = e.nextElement();
String[] values = request.getParameterValues(name);
redirect.addParameter(name, values);
}
}
/**
* If the specified property has multiple FormFile objects, but the form
* bean type can only process a single FormFile object, then this method
* attempts to take this situation into account.
*
* @param bean the FormBean-object
* @param name the name of the property
* @param parameterValue the value of the property
*
* @return parameterValue with expected type
*
* @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;
}
final FormFile[] formFileValue = (FormFile[]) parameterValue;
try {
final Class> propertyType =
PropertyUtils.getPropertyType(bean, name);
if (propertyType == null) {
return parameterValue;
}
if (List.class.isAssignableFrom(propertyType)) {
return Arrays.asList(formFileValue);
}
if (FormFile.class.isAssignableFrom(propertyType)) {
switch (formFileValue.length) {
case 0:
LOG.error("FormFile-parameter \"{}\" for FormBean "
+ "\"{}\" has unexpected length of null!",
name, bean.getClass().getName());
return null;
case 1:
return formFileValue[0];
default:
LOG.error("FormFile-parameter \"{}\" for FormBean "
+ "\"{}\" should have an Array or a List as "
+ "method parameter!", name,
bean.getClass().getName());
return formFileValue[0];
}
}
} catch (IllegalAccessException | InvocationTargetException | 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 \"{}\" "
+ "in mapping class not found, "
+ "defaulting to global multipart class", multipartClass);
} catch (InstantiationException ie) {
LOG.error("InstantiationException when instantiating "
+ "MultipartRequestHandler \"{}\", "
+ "defaulting to global multipart class, exception: {}",
multipartClass, ie.getMessage());
} catch (IllegalAccessException iae) {
LOG.error("IllegalAccessException when instantiating "
+ "MultipartRequestHandler \"{}\", "
+ "defaulting to global multipart class, exception: {}",
multipartClass, 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.
*
* @param request The (wrapped) HTTP request whose parameters are
* to be added to the map.
* @param multipartHandler The multipart handler used to parse the
* request.
* @return the map containing all parameters for this multipart request.
*/
private static Map getAllParametersForMultipartRequest(
HttpServletRequest request, MultipartRequestHandler multipartHandler) {
final Map parameters =
new HashMap<>(multipartHandler.getAllElements());
if (request instanceof MultipartRequestWrapper) {
request =
(HttpServletRequest) ((MultipartRequestWrapper) request)
.getRequest();
parameters.putAll(request.getParameterMap());
} else {
LOG.debug("Gathering multipart parameters for unwrapped request");
}
return parameters;
}
/**
* 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 {
StringBuilder sb = new StringBuilder(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) {
StringBuilder sb = new StringBuilder();
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:
*
*
*
*
* - If the
contextRelative
property is set, it is assumed
* that the path
property contains a path that is already
* context-relative:
*
*
*
* - If the
path
property value starts with a slash, it is
* returned unmodified. - If the
path
property value
* does not start with a slash, a slash is prepended.
*
*
*
* - Acquire the
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.
*
* - Process the acquired
forwardPattern
, performing the
* following substitutions:
*
*
*
* - $M - Replaced by the module prefix for the
* application module processing this request.
*
* - $P - Replaced by the
path
property of
* the specified {@link ForwardConfig}, prepended with a slash if it does
* not start with one.
*
* - $$ - Replaced by a single dollar sign
* character.
*
* - $x (where "x" is any charater not listed above) -
* Silently omit these two characters from the result value. (This has
* the side effect of causing all other $+letter combinations to be
* reserved.)
*
*
*
*
*
* @param request The servlet request we are processing
* @param forward ForwardConfig to be evaluated
* @return context-relative URL
* @since Struts 1.1
*/
public static String forwardURL(HttpServletRequest request,
ForwardConfig forward) {
return forwardURL(request, forward, null);
}
/**
* 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:
*
*
*
* - If the
contextRelative
property is set, it is assumed
* that the path
property contains a path that is already
* context-relative:
*
* - If the
path
property value starts with a slash, it is
* returned unmodified. - If the
path
property value
* does not start with a slash, a slash is prepended.
*
*
*
* - Acquire the
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.
*
* - Process the acquired
forwardPattern
, performing the
* following substitutions: - $M - Replaced by the
* module prefix for the application module processing this request.
*
* - $P - Replaced by the
path
property of
* the specified {@link ForwardConfig}, prepended with a slash if it does
* not start with one.
*
* - $$ - Replaced by a single dollar sign
* character.
*
* - $x (where "x" is any charater not listed above) -
* Silently omit these two characters from the result value. (This has
* the side effect of causing all other $+letter combinations to be
* reserved.)
*
*
*
* @param request The servlet request we are processing
* @param forward ForwardConfig to be evaluated
* @param moduleConfig Base forward on this module config.
* @return context-relative URL
* @since Struts 1.2
*/
public static String forwardURL(HttpServletRequest request,
ForwardConfig forward, ModuleConfig moduleConfig) {
//load the current moduleConfig, if null
if (moduleConfig == null) {
moduleConfig = ModuleUtils.getInstance().getModuleConfig(request);
}
String path = forward.getPath();
//load default prefix
String prefix = moduleConfig.getPrefix();
//override prefix if supplied by forward
if (forward.getModule() != null) {
prefix = forward.getModule();
if ("/".equals(prefix)) {
prefix = "";
}
}
StringBuilder sb = new StringBuilder();
// Calculate a context relative path for this ForwardConfig
String forwardPattern =
moduleConfig.getControllerConfig().getForwardPattern();
if (forwardPattern == null) {
// Performance optimization for previous default behavior
sb.append(prefix);
// smoothly insert a '/' if needed
if (!path.startsWith("/")) {
sb.append("/");
}
sb.append(path);
} else {
boolean dollar = false;
for (int i = 0; i < forwardPattern.length(); i++) {
char ch = forwardPattern.charAt(i);
if (dollar) {
switch (ch) {
case 'M':
sb.append(prefix);
break;
case 'P':
// add '/' if needed
if (!path.startsWith("/")) {
sb.append("/");
}
sb.append(path);
break;
case '$':
sb.append('$');
break;
default:
; // Silently swallow
}
dollar = false;
continue;
} else if (ch == '$') {
dollar = true;
} else {
sb.append(ch);
}
}
}
return (sb.toString());
}
/**
* Return the URL representing the current request. This is equivalent
* to {@code HttpServletRequest.getRequestURL} in Servlet 2.3.
*
* @param request The servlet request we are processing
* @return URL representing the current request
* @throws MalformedURLException if a URL cannot be created
*/
public static URL requestURL(HttpServletRequest request)
throws MalformedURLException {
StringBuilder url = requestToServerUriStringBuilder(request);
return (new URL(url.toString()));
}
/**
* 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 {
StringBuilder url = requestToServerStringBuilder(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 StringBuilder requestToServerUriStringBuilder(
HttpServletRequest request) {
StringBuilder serverUri =
createServerUriStringBuilder(request.getScheme(),
request.getServerName(), request.getServerPort(),
request.getRequestURI());
return serverUri;
}
/**
* Return {@code StringBuilder} 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 StringBuilder requestToServerStringBuilder(
HttpServletRequest request) {
return createServerStringBuilder(request.getScheme(),
request.getServerName(), request.getServerPort());
}
/**
* Return {@code StringBuilder} representing the scheme, server,
* and port number of the current request.
*
* @param scheme The scheme name to use
* @param server The server name to use
* @param port The port value to use
* @return StringBuilder in the form scheme: server: port
* @since Struts 1.2.0
*/
public static StringBuilder createServerStringBuilder(String scheme,
String server, int port) {
StringBuilder url = new StringBuilder();
if (port < 0) {
port = 80; // Work around java.net.URL bug
}
url.append(scheme);
url.append("://");
url.append(server);
if ((scheme.equals("http") && (port != 80))
|| (scheme.equals("https") && (port != 443))) {
url.append(':');
url.append(port);
}
return url;
}
/**
* Return {@code StringBuilder} representing the scheme, server,
* and port number of the current request.
*
* @param scheme The scheme name to use
* @param server The server name to use
* @param port The port value to use
* @param uri The uri value to use
* @return StringBuilder in the form scheme: server: port
* @since Struts 1.2.0
*/
public static StringBuilder createServerUriStringBuilder(String scheme,
String server, int port, String uri) {
StringBuilder serverUri = createServerStringBuilder(scheme, server, port);
serverUri.append(uri);
return serverUri;
}
/**
* 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; otherwise null
.
* @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) {
LOG.debug("No actionId found for {}", actionId);
return null;
}
String path = actionConfig.getPath();
String mapping = RequestUtils.getServletMapping(servlet);
StringBuilder actionIdPath = new StringBuilder();
// 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
LOG.debug("{} unaliased to {}", originalPath, actionIdPath);
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);
}
}