0001: /*
0002: * $Id: RequestUtils.java 471754 2006-11-06 14:55:09Z husted $
0003: *
0004: * Licensed to the Apache Software Foundation (ASF) under one
0005: * or more contributor license agreements. See the NOTICE file
0006: * distributed with this work for additional information
0007: * regarding copyright ownership. The ASF licenses this file
0008: * to you under the Apache License, Version 2.0 (the
0009: * "License"); you may not use this file except in compliance
0010: * with the License. You may obtain a copy of the License at
0011: *
0012: * http://www.apache.org/licenses/LICENSE-2.0
0013: *
0014: * Unless required by applicable law or agreed to in writing,
0015: * software distributed under the License is distributed on an
0016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0017: * KIND, either express or implied. See the License for the
0018: * specific language governing permissions and limitations
0019: * under the License.
0020: */
0021: package org.apache.struts.util;
0022:
0023: import org.apache.commons.beanutils.BeanUtils;
0024: import org.apache.commons.logging.Log;
0025: import org.apache.commons.logging.LogFactory;
0026: import org.apache.struts.Globals;
0027: import org.apache.struts.action.ActionForm;
0028: import org.apache.struts.action.ActionMapping;
0029: import org.apache.struts.action.ActionServlet;
0030: import org.apache.struts.action.ActionServletWrapper;
0031: import org.apache.struts.config.ActionConfig;
0032: import org.apache.struts.config.FormBeanConfig;
0033: import org.apache.struts.config.ForwardConfig;
0034: import org.apache.struts.config.ModuleConfig;
0035: import org.apache.struts.upload.MultipartRequestHandler;
0036: import org.apache.struts.upload.MultipartRequestWrapper;
0037:
0038: import javax.servlet.ServletContext;
0039: import javax.servlet.ServletException;
0040: import javax.servlet.http.HttpServletRequest;
0041: import javax.servlet.http.HttpSession;
0042:
0043: import java.net.MalformedURLException;
0044: import java.net.URL;
0045:
0046: import java.util.Collections;
0047: import java.util.Enumeration;
0048: import java.util.HashMap;
0049: import java.util.Hashtable;
0050: import java.util.Locale;
0051: import java.util.Map;
0052:
0053: /**
0054: * <p>General purpose utility methods related to processing a servlet request
0055: * in the Struts controller framework.</p>
0056: *
0057: * @version $Rev: 471754 $ $Date: 2006-11-06 08:55:09 -0600 (Mon, 06 Nov 2006) $
0058: */
0059: public class RequestUtils {
0060: // ------------------------------------------------------- Static Variables
0061:
0062: /**
0063: * <p>Commons Logging instance.</p>
0064: */
0065: protected static Log log = LogFactory.getLog(RequestUtils.class);
0066:
0067: // --------------------------------------------------------- Public Methods
0068:
0069: /**
0070: * <p>Create and return an absolute URL for the specified context-relative
0071: * path, based on the server and context information in the specified
0072: * request.</p>
0073: *
0074: * @param request The servlet request we are processing
0075: * @param path The context-relative path (must start with '/')
0076: * @return absolute URL based on context-relative path
0077: * @throws MalformedURLException if we cannot create an absolute URL
0078: */
0079: public static URL absoluteURL(HttpServletRequest request,
0080: String path) throws MalformedURLException {
0081: return (new URL(serverURL(request), request.getContextPath()
0082: + path));
0083: }
0084:
0085: /**
0086: * <p>Return the <code>Class</code> object for the specified fully
0087: * qualified class name, from this web application's class loader.</p>
0088: *
0089: * @param className Fully qualified class name to be loaded
0090: * @return Class object
0091: * @throws ClassNotFoundException if the class cannot be found
0092: */
0093: public static Class applicationClass(String className)
0094: throws ClassNotFoundException {
0095: return applicationClass(className, null);
0096: }
0097:
0098: /**
0099: * <p>Return the <code>Class</code> object for the specified fully
0100: * qualified class name, from this web application's class loader.</p>
0101: *
0102: * @param className Fully qualified class name to be loaded
0103: * @param classLoader The desired classloader to use
0104: * @return Class object
0105: * @throws ClassNotFoundException if the class cannot be found
0106: */
0107: public static Class applicationClass(String className,
0108: ClassLoader classLoader) throws ClassNotFoundException {
0109: if (classLoader == null) {
0110: // Look up the class loader to be used
0111: classLoader = Thread.currentThread()
0112: .getContextClassLoader();
0113:
0114: if (classLoader == null) {
0115: classLoader = RequestUtils.class.getClassLoader();
0116: }
0117: }
0118:
0119: // Attempt to load the specified class
0120: return (classLoader.loadClass(className));
0121: }
0122:
0123: /**
0124: * <p>Return a new instance of the specified fully qualified class name,
0125: * after loading the class from this web application's class loader. The
0126: * specified class <strong>MUST</strong> have a public zero-arguments
0127: * constructor.</p>
0128: *
0129: * @param className Fully qualified class name to use
0130: * @return new instance of class
0131: * @throws ClassNotFoundException if the class cannot be found
0132: * @throws IllegalAccessException if the class or its constructor is not
0133: * accessible
0134: * @throws InstantiationException if this class represents an abstract
0135: * class, an interface, an array class, a
0136: * primitive type, or void
0137: * @throws InstantiationException if this class has no zero-arguments
0138: * constructor
0139: */
0140: public static Object applicationInstance(String className)
0141: throws ClassNotFoundException, IllegalAccessException,
0142: InstantiationException {
0143: return applicationInstance(className, null);
0144: }
0145:
0146: /**
0147: * <p>Return a new instance of the specified fully qualified class name,
0148: * after loading the class from this web application's class loader. The
0149: * specified class <strong>MUST</strong> have a public zero-arguments
0150: * constructor.</p>
0151: *
0152: * @param className Fully qualified class name to use
0153: * @param classLoader The desired classloader to use
0154: * @return new instance of class
0155: * @throws ClassNotFoundException if the class cannot be found
0156: * @throws IllegalAccessException if the class or its constructor is not
0157: * accessible
0158: * @throws InstantiationException if this class represents an abstract
0159: * class, an interface, an array class, a
0160: * primitive type, or void
0161: * @throws InstantiationException if this class has no zero-arguments
0162: * constructor
0163: */
0164: public static Object applicationInstance(String className,
0165: ClassLoader classLoader) throws ClassNotFoundException,
0166: IllegalAccessException, InstantiationException {
0167: return (applicationClass(className, classLoader).newInstance());
0168: }
0169:
0170: /**
0171: * <p>Create (if necessary) and return an <code>ActionForm</code> instance
0172: * appropriate for this request. If no <code>ActionForm</code> instance
0173: * is required, return <code>null</code>.</p>
0174: *
0175: * @param request The servlet request we are processing
0176: * @param mapping The action mapping for this request
0177: * @param moduleConfig The configuration for this module
0178: * @param servlet The action servlet
0179: * @return ActionForm instance associated with this request
0180: */
0181: public static ActionForm createActionForm(
0182: HttpServletRequest request, ActionMapping mapping,
0183: ModuleConfig moduleConfig, ActionServlet servlet) {
0184: // Is there a form bean associated with this mapping?
0185: String attribute = mapping.getAttribute();
0186:
0187: if (attribute == null) {
0188: return (null);
0189: }
0190:
0191: // Look up the form bean configuration information to use
0192: String name = mapping.getName();
0193: FormBeanConfig config = moduleConfig.findFormBeanConfig(name);
0194:
0195: if (config == null) {
0196: log.warn("No FormBeanConfig found under '" + name + "'");
0197:
0198: return (null);
0199: }
0200:
0201: ActionForm instance = lookupActionForm(request, attribute,
0202: mapping.getScope());
0203:
0204: // Can we recycle the existing form bean instance (if there is one)?
0205: if ((instance != null) && config.canReuse(instance)) {
0206: return (instance);
0207: }
0208:
0209: return createActionForm(config, servlet);
0210: }
0211:
0212: private static ActionForm lookupActionForm(
0213: HttpServletRequest request, String attribute, String scope) {
0214: // Look up any existing form bean instance
0215: if (log.isDebugEnabled()) {
0216: log
0217: .debug(" Looking for ActionForm bean instance in scope '"
0218: + scope
0219: + "' under attribute key '"
0220: + attribute + "'");
0221: }
0222:
0223: ActionForm instance = null;
0224: HttpSession session = null;
0225:
0226: if ("request".equals(scope)) {
0227: instance = (ActionForm) request.getAttribute(attribute);
0228: } else {
0229: session = request.getSession();
0230: instance = (ActionForm) session.getAttribute(attribute);
0231: }
0232:
0233: return (instance);
0234: }
0235:
0236: /**
0237: * <p>Create and return an <code>ActionForm</code> instance appropriate to
0238: * the information in <code>config</code>.</p>
0239: *
0240: * <p>Does not perform any checks to see if an existing ActionForm exists
0241: * which could be reused.</p>
0242: *
0243: * @param config The configuration for the Form bean which is to be
0244: * created.
0245: * @param servlet The action servlet
0246: * @return ActionForm instance associated with this request
0247: */
0248: public static ActionForm createActionForm(FormBeanConfig config,
0249: ActionServlet servlet) {
0250: if (config == null) {
0251: return (null);
0252: }
0253:
0254: ActionForm instance = null;
0255:
0256: // Create and return a new form bean instance
0257: try {
0258: instance = config.createActionForm(servlet);
0259:
0260: if (log.isDebugEnabled()) {
0261: log.debug(" Creating new "
0262: + (config.getDynamic() ? "DynaActionForm"
0263: : "ActionForm") + " instance of type '"
0264: + config.getType() + "'");
0265: log.trace(" --> " + instance);
0266: }
0267: } catch (Throwable t) {
0268: log.error(servlet.getInternal().getMessage("formBean",
0269: config.getType()), t);
0270: }
0271:
0272: return (instance);
0273: }
0274:
0275: /**
0276: * <p>Retrieves the servlet mapping pattern for the specified {@link ActionServlet}.</p>
0277: *
0278: * @return the servlet mapping
0279: * @see Globals#SERVLET_KEY
0280: * @since Struts 1.3.6
0281: */
0282: public static String getServletMapping(ActionServlet servlet) {
0283: ServletContext servletContext = servlet.getServletConfig()
0284: .getServletContext();
0285: return (String) servletContext
0286: .getAttribute(Globals.SERVLET_KEY);
0287: }
0288:
0289: /**
0290: * <p>Look up and return current user locale, based on the specified
0291: * parameters.</p>
0292: *
0293: * @param request The request used to lookup the Locale
0294: * @param locale Name of the session attribute for our user's Locale. If
0295: * this is <code>null</code>, the default locale key is
0296: * used for the lookup.
0297: * @return current user locale
0298: * @since Struts 1.2
0299: */
0300: public static Locale getUserLocale(HttpServletRequest request,
0301: String locale) {
0302: Locale userLocale = null;
0303: HttpSession session = request.getSession(false);
0304:
0305: if (locale == null) {
0306: locale = Globals.LOCALE_KEY;
0307: }
0308:
0309: // Only check session if sessions are enabled
0310: if (session != null) {
0311: userLocale = (Locale) session.getAttribute(locale);
0312: }
0313:
0314: if (userLocale == null) {
0315: // Returns Locale based on Accept-Language header or the server default
0316: userLocale = request.getLocale();
0317: }
0318:
0319: return userLocale;
0320: }
0321:
0322: /**
0323: * <p>Populate the properties of the specified JavaBean from the specified
0324: * HTTP request, based on matching each parameter name against the
0325: * corresponding JavaBeans "property setter" methods in the bean's class.
0326: * Suitable conversion is done for argument types as described under
0327: * <code>convert()</code>.</p>
0328: *
0329: * @param bean The JavaBean whose properties are to be set
0330: * @param request The HTTP request whose parameters are to be used to
0331: * populate bean properties
0332: * @throws ServletException if an exception is thrown while setting
0333: * property values
0334: */
0335: public static void populate(Object bean, HttpServletRequest request)
0336: throws ServletException {
0337: populate(bean, null, null, request);
0338: }
0339:
0340: /**
0341: * <p>Populate the properties of the specified JavaBean from the specified
0342: * HTTP request, based on matching each parameter name (plus an optional
0343: * prefix and/or suffix) against the corresponding JavaBeans "property
0344: * setter" methods in the bean's class. Suitable conversion is done for
0345: * argument types as described under <code>setProperties</code>.</p>
0346: *
0347: * <p>If you specify a non-null <code>prefix</code> and a non-null
0348: * <code>suffix</code>, the parameter name must match
0349: * <strong>both</strong> conditions for its value(s) to be used in
0350: * populating bean properties. If the request's content type is
0351: * "multipart/form-data" and the method is "POST", the
0352: * <code>HttpServletRequest</code> object will be wrapped in a
0353: * <code>MultipartRequestWrapper</code object.</p>
0354: *
0355: * @param bean The JavaBean whose properties are to be set
0356: * @param prefix The prefix (if any) to be prepend to bean property names
0357: * when looking for matching parameters
0358: * @param suffix The suffix (if any) to be appended to bean property
0359: * names when looking for matching parameters
0360: * @param request The HTTP request whose parameters are to be used to
0361: * populate bean properties
0362: * @throws ServletException if an exception is thrown while setting
0363: * property values
0364: */
0365: public static void populate(Object bean, String prefix,
0366: String suffix, HttpServletRequest request)
0367: throws ServletException {
0368: // Build a list of relevant request parameters from this request
0369: HashMap properties = new HashMap();
0370:
0371: // Iterator of parameter names
0372: Enumeration names = null;
0373:
0374: // Map for multipart parameters
0375: Map multipartParameters = null;
0376:
0377: String contentType = request.getContentType();
0378: String method = request.getMethod();
0379: boolean isMultipart = false;
0380:
0381: if (bean instanceof ActionForm) {
0382: ((ActionForm) bean).setMultipartRequestHandler(null);
0383: }
0384:
0385: MultipartRequestHandler multipartHandler = null;
0386: if ((contentType != null)
0387: && (contentType.startsWith("multipart/form-data"))
0388: && (method.equalsIgnoreCase("POST"))) {
0389: // Get the ActionServletWrapper from the form bean
0390: ActionServletWrapper servlet;
0391:
0392: if (bean instanceof ActionForm) {
0393: servlet = ((ActionForm) bean).getServletWrapper();
0394: } else {
0395: throw new ServletException(
0396: "bean that's supposed to be "
0397: + "populated from a multipart request is not of type "
0398: + "\"org.apache.struts.action.ActionForm\", but type "
0399: + "\"" + bean.getClass().getName()
0400: + "\"");
0401: }
0402:
0403: // Obtain a MultipartRequestHandler
0404: multipartHandler = getMultipartHandler(request);
0405:
0406: if (multipartHandler != null) {
0407: isMultipart = true;
0408:
0409: // Set servlet and mapping info
0410: servlet.setServletFor(multipartHandler);
0411: multipartHandler.setMapping((ActionMapping) request
0412: .getAttribute(Globals.MAPPING_KEY));
0413:
0414: // Initialize multipart request class handler
0415: multipartHandler.handleRequest(request);
0416:
0417: //stop here if the maximum length has been exceeded
0418: Boolean maxLengthExceeded = (Boolean) request
0419: .getAttribute(MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED);
0420:
0421: if ((maxLengthExceeded != null)
0422: && (maxLengthExceeded.booleanValue())) {
0423: ((ActionForm) bean)
0424: .setMultipartRequestHandler(multipartHandler);
0425: return;
0426: }
0427:
0428: //retrieve form values and put into properties
0429: multipartParameters = getAllParametersForMultipartRequest(
0430: request, multipartHandler);
0431: names = Collections.enumeration(multipartParameters
0432: .keySet());
0433: }
0434: }
0435:
0436: if (!isMultipart) {
0437: names = request.getParameterNames();
0438: }
0439:
0440: while (names.hasMoreElements()) {
0441: String name = (String) names.nextElement();
0442: String stripped = name;
0443:
0444: if (prefix != null) {
0445: if (!stripped.startsWith(prefix)) {
0446: continue;
0447: }
0448:
0449: stripped = stripped.substring(prefix.length());
0450: }
0451:
0452: if (suffix != null) {
0453: if (!stripped.endsWith(suffix)) {
0454: continue;
0455: }
0456:
0457: stripped = stripped.substring(0, stripped.length()
0458: - suffix.length());
0459: }
0460:
0461: Object parameterValue = null;
0462:
0463: if (isMultipart) {
0464: parameterValue = multipartParameters.get(name);
0465: } else {
0466: parameterValue = request.getParameterValues(name);
0467: }
0468:
0469: // Populate parameters, except "standard" struts attributes
0470: // such as 'org.apache.struts.action.CANCEL'
0471: if (!(stripped.startsWith("org.apache.struts."))) {
0472: properties.put(stripped, parameterValue);
0473: }
0474: }
0475:
0476: // Set the corresponding properties of our bean
0477: try {
0478: BeanUtils.populate(bean, properties);
0479: } catch (Exception e) {
0480: throw new ServletException("BeanUtils.populate", e);
0481: } finally {
0482: if (multipartHandler != null) {
0483: // Set the multipart request handler for our ActionForm.
0484: // If the bean isn't an ActionForm, an exception would have been
0485: // thrown earlier, so it's safe to assume that our bean is
0486: // in fact an ActionForm.
0487: ((ActionForm) bean)
0488: .setMultipartRequestHandler(multipartHandler);
0489: }
0490: }
0491: }
0492:
0493: /**
0494: * <p>Try to locate a multipart request handler for this request. First,
0495: * look for a mapping-specific handler stored for us under an attribute.
0496: * If one is not present, use the global multipart handler, if there is
0497: * one.</p>
0498: *
0499: * @param request The HTTP request for which the multipart handler should
0500: * be found.
0501: * @return the multipart handler to use, or null if none is found.
0502: * @throws ServletException if any exception is thrown while attempting to
0503: * locate the multipart handler.
0504: */
0505: private static MultipartRequestHandler getMultipartHandler(
0506: HttpServletRequest request) throws ServletException {
0507: MultipartRequestHandler multipartHandler = null;
0508: String multipartClass = (String) request
0509: .getAttribute(Globals.MULTIPART_KEY);
0510:
0511: request.removeAttribute(Globals.MULTIPART_KEY);
0512:
0513: // Try to initialize the mapping specific request handler
0514: if (multipartClass != null) {
0515: try {
0516: multipartHandler = (MultipartRequestHandler) applicationInstance(multipartClass);
0517: } catch (ClassNotFoundException cnfe) {
0518: log.error("MultipartRequestHandler class \""
0519: + multipartClass
0520: + "\" in mapping class not found, "
0521: + "defaulting to global multipart class");
0522: } catch (InstantiationException ie) {
0523: log
0524: .error("InstantiationException when instantiating "
0525: + "MultipartRequestHandler \""
0526: + multipartClass
0527: + "\", "
0528: + "defaulting to global multipart class, exception: "
0529: + ie.getMessage());
0530: } catch (IllegalAccessException iae) {
0531: log
0532: .error("IllegalAccessException when instantiating "
0533: + "MultipartRequestHandler \""
0534: + multipartClass
0535: + "\", "
0536: + "defaulting to global multipart class, exception: "
0537: + iae.getMessage());
0538: }
0539:
0540: if (multipartHandler != null) {
0541: return multipartHandler;
0542: }
0543: }
0544:
0545: ModuleConfig moduleConfig = ModuleUtils.getInstance()
0546: .getModuleConfig(request);
0547:
0548: multipartClass = moduleConfig.getControllerConfig()
0549: .getMultipartClass();
0550:
0551: // Try to initialize the global request handler
0552: if (multipartClass != null) {
0553: try {
0554: multipartHandler = (MultipartRequestHandler) applicationInstance(multipartClass);
0555: } catch (ClassNotFoundException cnfe) {
0556: throw new ServletException(
0557: "Cannot find multipart class \""
0558: + multipartClass + "\""
0559: + ", exception: " + cnfe.getMessage());
0560: } catch (InstantiationException ie) {
0561: throw new ServletException(
0562: "InstantiationException when instantiating "
0563: + "multipart class \"" + multipartClass
0564: + "\", exception: " + ie.getMessage());
0565: } catch (IllegalAccessException iae) {
0566: throw new ServletException(
0567: "IllegalAccessException when instantiating "
0568: + "multipart class \"" + multipartClass
0569: + "\", exception: " + iae.getMessage());
0570: }
0571:
0572: if (multipartHandler != null) {
0573: return multipartHandler;
0574: }
0575: }
0576:
0577: return multipartHandler;
0578: }
0579:
0580: /**
0581: * <p>Create a <code>Map</code> containing all of the parameters supplied
0582: * for a multipart request, keyed by parameter name. In addition to text
0583: * and file elements from the multipart body, query string parameters are
0584: * included as well.</p>
0585: *
0586: * @param request The (wrapped) HTTP request whose parameters are
0587: * to be added to the map.
0588: * @param multipartHandler The multipart handler used to parse the
0589: * request.
0590: * @return the map containing all parameters for this multipart request.
0591: */
0592: private static Map getAllParametersForMultipartRequest(
0593: HttpServletRequest request,
0594: MultipartRequestHandler multipartHandler) {
0595: Map parameters = new HashMap();
0596: Hashtable elements = multipartHandler.getAllElements();
0597: Enumeration e = elements.keys();
0598:
0599: while (e.hasMoreElements()) {
0600: String key = (String) e.nextElement();
0601:
0602: parameters.put(key, elements.get(key));
0603: }
0604:
0605: if (request instanceof MultipartRequestWrapper) {
0606: request = (HttpServletRequest) ((MultipartRequestWrapper) request)
0607: .getRequest();
0608: e = request.getParameterNames();
0609:
0610: while (e.hasMoreElements()) {
0611: String key = (String) e.nextElement();
0612:
0613: parameters.put(key, request.getParameterValues(key));
0614: }
0615: } else {
0616: log
0617: .debug("Gathering multipart parameters for unwrapped request");
0618: }
0619:
0620: return parameters;
0621: }
0622:
0623: /**
0624: * <p>Compute the printable representation of a URL, leaving off the
0625: * scheme/host/port part if no host is specified. This will typically be
0626: * the case for URLs that were originally created from relative or
0627: * context-relative URIs.</p>
0628: *
0629: * @param url URL to render in a printable representation
0630: * @return printable representation of a URL
0631: */
0632: public static String printableURL(URL url) {
0633: if (url.getHost() != null) {
0634: return (url.toString());
0635: }
0636:
0637: String file = url.getFile();
0638: String ref = url.getRef();
0639:
0640: if (ref == null) {
0641: return (file);
0642: } else {
0643: StringBuffer sb = new StringBuffer(file);
0644:
0645: sb.append('#');
0646: sb.append(ref);
0647:
0648: return (sb.toString());
0649: }
0650: }
0651:
0652: /**
0653: * <p>Return the context-relative URL that corresponds to the specified
0654: * {@link ActionConfig}, relative to the module associated with the
0655: * current modules's {@link ModuleConfig}.</p>
0656: *
0657: * @param request The servlet request we are processing
0658: * @param action ActionConfig to be evaluated
0659: * @param pattern URL pattern used to map the controller servlet
0660: * @return context-relative URL relative to the module
0661: * @since Struts 1.1
0662: */
0663: public static String actionURL(HttpServletRequest request,
0664: ActionConfig action, String pattern) {
0665: StringBuffer sb = new StringBuffer();
0666:
0667: if (pattern.endsWith("/*")) {
0668: sb.append(pattern.substring(0, pattern.length() - 2));
0669: sb.append(action.getPath());
0670: } else if (pattern.startsWith("*.")) {
0671: ModuleConfig appConfig = ModuleUtils.getInstance()
0672: .getModuleConfig(request);
0673:
0674: sb.append(appConfig.getPrefix());
0675: sb.append(action.getPath());
0676: sb.append(pattern.substring(1));
0677: } else {
0678: throw new IllegalArgumentException(pattern);
0679: }
0680:
0681: return sb.toString();
0682: }
0683:
0684: /**
0685: * <p>Return the context-relative URL that corresponds to the specified
0686: * <code>ForwardConfig</code>. The URL is calculated based on the
0687: * properties of the {@link ForwardConfig} instance as follows:</p>
0688: *
0689: * <ul>
0690: *
0691: *
0692: * <li>If the <code>contextRelative</code> property is set, it is assumed
0693: * that the <code>path</code> property contains a path that is already
0694: * context-relative:
0695: *
0696: * <ul>
0697: *
0698: * <li>If the <code>path</code> property value starts with a slash, it is
0699: * returned unmodified.</li> <li>If the <code>path</code> property value
0700: * does not start with a slash, a slash is prepended.</li>
0701: *
0702: * </ul></li>
0703: *
0704: * <li>Acquire the <code>forwardPattern</code> property from the
0705: * <code>ControllerConfig</code> for the application module used to
0706: * process this request. If no pattern was configured, default to a
0707: * pattern of <code>$M$P</code>, which is compatible with the hard-coded
0708: * mapping behavior in Struts 1.0.</li>
0709: *
0710: * <li>Process the acquired <code>forwardPattern</code>, performing the
0711: * following substitutions:
0712: *
0713: * <ul>
0714: *
0715: * <li><strong>$M</strong> - Replaced by the module prefix for the
0716: * application module processing this request.</li>
0717: *
0718: * <li><strong>$P</strong> - Replaced by the <code>path</code> property of
0719: * the specified {@link ForwardConfig}, prepended with a slash if it does
0720: * not start with one.</li>
0721: *
0722: * <li><strong>$$</strong> - Replaced by a single dollar sign
0723: * character.</li>
0724: *
0725: * <li><strong>$x</strong> (where "x" is any charater not listed above) -
0726: * Silently omit these two characters from the result value. (This has
0727: * the side effect of causing all other $+letter combinations to be
0728: * reserved.)</li>
0729: *
0730: * </ul></li>
0731: *
0732: * </ul>
0733: *
0734: * @param request The servlet request we are processing
0735: * @param forward ForwardConfig to be evaluated
0736: * @return context-relative URL
0737: * @since Struts 1.1
0738: */
0739: public static String forwardURL(HttpServletRequest request,
0740: ForwardConfig forward) {
0741: return forwardURL(request, forward, null);
0742: }
0743:
0744: /**
0745: * <p>Return the context-relative URL that corresponds to the specified
0746: * <code>ForwardConfig</code>. The URL is calculated based on the
0747: * properties of the {@link ForwardConfig} instance as follows:</p>
0748: *
0749: * <ul>
0750: *
0751: * <li>If the <code>contextRelative</code> property is set, it is assumed
0752: * that the <code>path</code> property contains a path that is already
0753: * context-relative: <ul>
0754: *
0755: * <li>If the <code>path</code> property value starts with a slash, it is
0756: * returned unmodified.</li> <li>If the <code>path</code> property value
0757: * does not start with a slash, a slash is prepended.</li>
0758: *
0759: * </ul></li>
0760: *
0761: * <li>Acquire the <code>forwardPattern</code> property from the
0762: * <code>ControllerConfig</code> for the application module used to
0763: * process this request. If no pattern was configured, default to a
0764: * pattern of <code>$M$P</code>, which is compatible with the hard-coded
0765: * mapping behavior in Struts 1.0.</li>
0766: *
0767: * <li>Process the acquired <code>forwardPattern</code>, performing the
0768: * following substitutions: <ul> <li><strong>$M</strong> - Replaced by the
0769: * module prefix for the application module processing this request.</li>
0770: *
0771: * <li><strong>$P</strong> - Replaced by the <code>path</code> property of
0772: * the specified {@link ForwardConfig}, prepended with a slash if it does
0773: * not start with one.</li>
0774: *
0775: * <li><strong>$$</strong> - Replaced by a single dollar sign
0776: * character.</li>
0777: *
0778: * <li><strong>$x</strong> (where "x" is any charater not listed above) -
0779: * Silently omit these two characters from the result value. (This has
0780: * the side effect of causing all other $+letter combinations to be
0781: * reserved.)</li>
0782: *
0783: * </ul></li></ul>
0784: *
0785: * @param request The servlet request we are processing
0786: * @param forward ForwardConfig to be evaluated
0787: * @param moduleConfig Base forward on this module config.
0788: * @return context-relative URL
0789: * @since Struts 1.2
0790: */
0791: public static String forwardURL(HttpServletRequest request,
0792: ForwardConfig forward, ModuleConfig moduleConfig) {
0793: //load the current moduleConfig, if null
0794: if (moduleConfig == null) {
0795: moduleConfig = ModuleUtils.getInstance().getModuleConfig(
0796: request);
0797: }
0798:
0799: String path = forward.getPath();
0800:
0801: //load default prefix
0802: String prefix = moduleConfig.getPrefix();
0803:
0804: //override prefix if supplied by forward
0805: if (forward.getModule() != null) {
0806: prefix = forward.getModule();
0807:
0808: if ("/".equals(prefix)) {
0809: prefix = "";
0810: }
0811: }
0812:
0813: StringBuffer sb = new StringBuffer();
0814:
0815: // Calculate a context relative path for this ForwardConfig
0816: String forwardPattern = moduleConfig.getControllerConfig()
0817: .getForwardPattern();
0818:
0819: if (forwardPattern == null) {
0820: // Performance optimization for previous default behavior
0821: sb.append(prefix);
0822:
0823: // smoothly insert a '/' if needed
0824: if (!path.startsWith("/")) {
0825: sb.append("/");
0826: }
0827:
0828: sb.append(path);
0829: } else {
0830: boolean dollar = false;
0831:
0832: for (int i = 0; i < forwardPattern.length(); i++) {
0833: char ch = forwardPattern.charAt(i);
0834:
0835: if (dollar) {
0836: switch (ch) {
0837: case 'M':
0838: sb.append(prefix);
0839:
0840: break;
0841:
0842: case 'P':
0843:
0844: // add '/' if needed
0845: if (!path.startsWith("/")) {
0846: sb.append("/");
0847: }
0848:
0849: sb.append(path);
0850:
0851: break;
0852:
0853: case '$':
0854: sb.append('$');
0855:
0856: break;
0857:
0858: default:
0859: ; // Silently swallow
0860: }
0861:
0862: dollar = false;
0863:
0864: continue;
0865: } else if (ch == '$') {
0866: dollar = true;
0867: } else {
0868: sb.append(ch);
0869: }
0870: }
0871: }
0872:
0873: return (sb.toString());
0874: }
0875:
0876: /**
0877: * <p>Return the URL representing the current request. This is equivalent
0878: * to <code>HttpServletRequest.getRequestURL</code> in Servlet 2.3.</p>
0879: *
0880: * @param request The servlet request we are processing
0881: * @return URL representing the current request
0882: * @throws MalformedURLException if a URL cannot be created
0883: */
0884: public static URL requestURL(HttpServletRequest request)
0885: throws MalformedURLException {
0886: StringBuffer url = requestToServerUriStringBuffer(request);
0887:
0888: return (new URL(url.toString()));
0889: }
0890:
0891: /**
0892: * <p>Return the URL representing the scheme, server, and port number of
0893: * the current request. Server-relative URLs can be created by simply
0894: * appending the server-relative path (starting with '/') to this.</p>
0895: *
0896: * @param request The servlet request we are processing
0897: * @return URL representing the scheme, server, and port number of the
0898: * current request
0899: * @throws MalformedURLException if a URL cannot be created
0900: */
0901: public static URL serverURL(HttpServletRequest request)
0902: throws MalformedURLException {
0903: StringBuffer url = requestToServerStringBuffer(request);
0904:
0905: return (new URL(url.toString()));
0906: }
0907:
0908: /**
0909: * <p>Return the string representing the scheme, server, and port number
0910: * of the current request. Server-relative URLs can be created by simply
0911: * appending the server-relative path (starting with '/') to this.</p>
0912: *
0913: * @param request The servlet request we are processing
0914: * @return URL representing the scheme, server, and port number of the
0915: * current request
0916: * @since Struts 1.2.0
0917: */
0918: public static StringBuffer requestToServerUriStringBuffer(
0919: HttpServletRequest request) {
0920: StringBuffer serverUri = createServerUriStringBuffer(request
0921: .getScheme(), request.getServerName(), request
0922: .getServerPort(), request.getRequestURI());
0923:
0924: return serverUri;
0925: }
0926:
0927: /**
0928: * <p>Return <code>StringBuffer</code> representing the scheme, server,
0929: * and port number of the current request. Server-relative URLs can be
0930: * created by simply appending the server-relative path (starting with
0931: * '/') to this.</p>
0932: *
0933: * @param request The servlet request we are processing
0934: * @return URL representing the scheme, server, and port number of the
0935: * current request
0936: * @since Struts 1.2.0
0937: */
0938: public static StringBuffer requestToServerStringBuffer(
0939: HttpServletRequest request) {
0940: return createServerStringBuffer(request.getScheme(), request
0941: .getServerName(), request.getServerPort());
0942: }
0943:
0944: /**
0945: * <p>Return <code>StringBuffer</code> representing the scheme, server,
0946: * and port number of the current request.</p>
0947: *
0948: * @param scheme The scheme name to use
0949: * @param server The server name to use
0950: * @param port The port value to use
0951: * @return StringBuffer in the form scheme: server: port
0952: * @since Struts 1.2.0
0953: */
0954: public static StringBuffer createServerStringBuffer(String scheme,
0955: String server, int port) {
0956: StringBuffer url = new StringBuffer();
0957:
0958: if (port < 0) {
0959: port = 80; // Work around java.net.URL bug
0960: }
0961:
0962: url.append(scheme);
0963: url.append("://");
0964: url.append(server);
0965:
0966: if ((scheme.equals("http") && (port != 80))
0967: || (scheme.equals("https") && (port != 443))) {
0968: url.append(':');
0969: url.append(port);
0970: }
0971:
0972: return url;
0973: }
0974:
0975: /**
0976: * <p>Return <code>StringBuffer</code> representing the scheme, server,
0977: * and port number of the current request.</p>
0978: *
0979: * @param scheme The scheme name to use
0980: * @param server The server name to use
0981: * @param port The port value to use
0982: * @param uri The uri value to use
0983: * @return StringBuffer in the form scheme: server: port
0984: * @since Struts 1.2.0
0985: */
0986: public static StringBuffer createServerUriStringBuffer(
0987: String scheme, String server, int port, String uri) {
0988: StringBuffer serverUri = createServerStringBuffer(scheme,
0989: server, port);
0990:
0991: serverUri.append(uri);
0992:
0993: return serverUri;
0994: }
0995:
0996: /**
0997: * <p>Returns the true path of the destination action if the specified forward
0998: * is an action-aliased URL. This method version forms the URL based on
0999: * the current request; selecting the current module if the forward does not
1000: * explicitly contain a module path.</p>
1001: *
1002: * @param forward the forward config
1003: * @param request the current request
1004: * @param servlet the servlet handling the current request
1005: * @return the context-relative URL of the action if the forward has an action identifier; otherwise <code>null</code>.
1006: * @since Struts 1.3.6
1007: */
1008: public static String actionIdURL(ForwardConfig forward,
1009: HttpServletRequest request, ActionServlet servlet) {
1010: ModuleConfig moduleConfig = null;
1011: if (forward.getModule() != null) {
1012: String prefix = forward.getModule();
1013: moduleConfig = ModuleUtils.getInstance().getModuleConfig(
1014: prefix, servlet.getServletContext());
1015: } else {
1016: moduleConfig = ModuleUtils.getInstance().getModuleConfig(
1017: request);
1018: }
1019: return actionIdURL(forward.getPath(), moduleConfig, servlet);
1020: }
1021:
1022: /**
1023: * <p>Returns the true path of the destination action if the specified forward
1024: * is an action-aliased URL. This method version forms the URL based on
1025: * the specified module.
1026: *
1027: * @param originalPath the action-aliased path
1028: * @param moduleConfig the module config for this request
1029: * @param servlet the servlet handling the current request
1030: * @return the context-relative URL of the action if the path has an action identifier; otherwise <code>null</code>.
1031: * @since Struts 1.3.6
1032: */
1033: public static String actionIdURL(String originalPath,
1034: ModuleConfig moduleConfig, ActionServlet servlet) {
1035: if (originalPath.startsWith("http")
1036: || originalPath.startsWith("/")) {
1037: return null;
1038: }
1039:
1040: // Split the forward path into the resource and query string;
1041: // it is possible a forward (or redirect) has added parameters.
1042: String actionId = null;
1043: String qs = null;
1044: int qpos = originalPath.indexOf("?");
1045: if (qpos == -1) {
1046: actionId = originalPath;
1047: } else {
1048: actionId = originalPath.substring(0, qpos);
1049: qs = originalPath.substring(qpos);
1050: }
1051:
1052: // Find the action of the given actionId
1053: ActionConfig actionConfig = moduleConfig
1054: .findActionConfigId(actionId);
1055: if (actionConfig == null) {
1056: if (log.isDebugEnabled()) {
1057: log.debug("No actionId found for " + actionId);
1058: }
1059: return null;
1060: }
1061:
1062: String path = actionConfig.getPath();
1063: String mapping = RequestUtils.getServletMapping(servlet);
1064: StringBuffer actionIdPath = new StringBuffer();
1065:
1066: // Form the path based on the servlet mapping pattern
1067: if (mapping.startsWith("*")) {
1068: actionIdPath.append(path);
1069: actionIdPath.append(mapping.substring(1));
1070: } else if (mapping.startsWith("/")) { // implied ends with a *
1071: mapping = mapping.substring(0, mapping.length() - 1);
1072: if (mapping.endsWith("/") && path.startsWith("/")) {
1073: actionIdPath.append(mapping);
1074: actionIdPath.append(path.substring(1));
1075: } else {
1076: actionIdPath.append(mapping);
1077: actionIdPath.append(path);
1078: }
1079: } else {
1080: log.warn("Unknown servlet mapping pattern");
1081: actionIdPath.append(path);
1082: }
1083:
1084: // Lastly add any query parameters (the ? is part of the query string)
1085: if (qs != null) {
1086: actionIdPath.append(qs);
1087: }
1088:
1089: // Return the path
1090: if (log.isDebugEnabled()) {
1091: log.debug(originalPath + " unaliased to "
1092: + actionIdPath.toString());
1093: }
1094: return actionIdPath.toString();
1095: }
1096: }
|