1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.struts.util;
22
23 import org.apache.commons.beanutils.BeanUtils;
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.apache.struts.Globals;
27 import org.apache.struts.action.ActionForm;
28 import org.apache.struts.action.ActionMapping;
29 import org.apache.struts.action.ActionServlet;
30 import org.apache.struts.action.ActionServletWrapper;
31 import org.apache.struts.config.ActionConfig;
32 import org.apache.struts.config.FormBeanConfig;
33 import org.apache.struts.config.ForwardConfig;
34 import org.apache.struts.config.ModuleConfig;
35 import org.apache.struts.upload.MultipartRequestHandler;
36 import org.apache.struts.upload.MultipartRequestWrapper;
37
38 import javax.servlet.ServletContext;
39 import javax.servlet.ServletException;
40 import javax.servlet.http.HttpServletRequest;
41 import javax.servlet.http.HttpSession;
42
43 import java.net.MalformedURLException;
44 import java.net.URL;
45
46 import java.util.Collections;
47 import java.util.Enumeration;
48 import java.util.HashMap;
49 import java.util.Hashtable;
50 import java.util.Locale;
51 import java.util.Map;
52
53 /**
54 * <p>General purpose utility methods related to processing a servlet request
55 * in the Struts controller framework.</p>
56 *
57 * @version $Rev: 471754 $ $Date: 2006-11-06 08:55:09 -0600 (Mon, 06 Nov 2006) $
58 */
59 public class RequestUtils {
60
61
62 /**
63 * <p>Commons Logging instance.</p>
64 */
65 protected static Log log = LogFactory.getLog(RequestUtils.class);
66
67
68
69 /**
70 * <p>Create and return an absolute URL for the specified context-relative
71 * path, based on the server and context information in the specified
72 * request.</p>
73 *
74 * @param request The servlet request we are processing
75 * @param path The context-relative path (must start with '/')
76 * @return absolute URL based on context-relative path
77 * @throws MalformedURLException if we cannot create an absolute URL
78 */
79 public static URL absoluteURL(HttpServletRequest request, String path)
80 throws MalformedURLException {
81 return (new URL(serverURL(request), request.getContextPath() + path));
82 }
83
84 /**
85 * <p>Return the <code>Class</code> object for the specified fully
86 * qualified class name, from this web application's class loader.</p>
87 *
88 * @param className Fully qualified class name to be loaded
89 * @return Class object
90 * @throws ClassNotFoundException if the class cannot be found
91 */
92 public static Class applicationClass(String className)
93 throws ClassNotFoundException {
94 return applicationClass(className, null);
95 }
96
97 /**
98 * <p>Return the <code>Class</code> object for the specified fully
99 * qualified class name, from this web application's class loader.</p>
100 *
101 * @param className Fully qualified class name to be loaded
102 * @param classLoader The desired classloader to use
103 * @return Class object
104 * @throws ClassNotFoundException if the class cannot be found
105 */
106 public static Class applicationClass(String className,
107 ClassLoader classLoader)
108 throws ClassNotFoundException {
109 if (classLoader == null) {
110
111 classLoader = Thread.currentThread().getContextClassLoader();
112
113 if (classLoader == null) {
114 classLoader = RequestUtils.class.getClassLoader();
115 }
116 }
117
118
119 return (classLoader.loadClass(className));
120 }
121
122 /**
123 * <p>Return a new instance of the specified fully qualified class name,
124 * after loading the class from this web application's class loader. The
125 * specified class <strong>MUST</strong> have a public zero-arguments
126 * constructor.</p>
127 *
128 * @param className Fully qualified class name to use
129 * @return new instance of class
130 * @throws ClassNotFoundException if the class cannot be found
131 * @throws IllegalAccessException if the class or its constructor is not
132 * accessible
133 * @throws InstantiationException if this class represents an abstract
134 * class, an interface, an array class, a
135 * primitive type, or void
136 * @throws InstantiationException if this class has no zero-arguments
137 * constructor
138 */
139 public static Object applicationInstance(String className)
140 throws ClassNotFoundException, IllegalAccessException,
141 InstantiationException {
142 return applicationInstance(className, null);
143 }
144
145 /**
146 * <p>Return a new instance of the specified fully qualified class name,
147 * after loading the class from this web application's class loader. The
148 * specified class <strong>MUST</strong> have a public zero-arguments
149 * constructor.</p>
150 *
151 * @param className Fully qualified class name to use
152 * @param classLoader The desired classloader to use
153 * @return new instance of class
154 * @throws ClassNotFoundException if the class cannot be found
155 * @throws IllegalAccessException if the class or its constructor is not
156 * accessible
157 * @throws InstantiationException if this class represents an abstract
158 * class, an interface, an array class, a
159 * primitive type, or void
160 * @throws InstantiationException if this class has no zero-arguments
161 * constructor
162 */
163 public static Object applicationInstance(String className,
164 ClassLoader classLoader)
165 throws ClassNotFoundException, IllegalAccessException,
166 InstantiationException {
167 return (applicationClass(className, classLoader).newInstance());
168 }
169
170 /**
171 * <p>Create (if necessary) and return an <code>ActionForm</code> instance
172 * appropriate for this request. If no <code>ActionForm</code> instance
173 * is required, return <code>null</code>.</p>
174 *
175 * @param request The servlet request we are processing
176 * @param mapping The action mapping for this request
177 * @param moduleConfig The configuration for this module
178 * @param servlet The action servlet
179 * @return ActionForm instance associated with this request
180 */
181 public static ActionForm createActionForm(HttpServletRequest request,
182 ActionMapping mapping, ModuleConfig moduleConfig, ActionServlet servlet) {
183
184 String attribute = mapping.getAttribute();
185
186 if (attribute == null) {
187 return (null);
188 }
189
190
191 String name = mapping.getName();
192 FormBeanConfig config = moduleConfig.findFormBeanConfig(name);
193
194 if (config == null) {
195 log.warn("No FormBeanConfig found under '" + name + "'");
196
197 return (null);
198 }
199
200 ActionForm instance =
201 lookupActionForm(request, attribute, mapping.getScope());
202
203
204 if ((instance != null) && config.canReuse(instance)) {
205 return (instance);
206 }
207
208 return createActionForm(config, servlet);
209 }
210
211 private static ActionForm lookupActionForm(HttpServletRequest request,
212 String attribute, String scope) {
213
214 if (log.isDebugEnabled()) {
215 log.debug(" Looking for ActionForm bean instance in scope '"
216 + scope + "' under attribute key '" + attribute + "'");
217 }
218
219 ActionForm instance = null;
220 HttpSession session = null;
221
222 if ("request".equals(scope)) {
223 instance = (ActionForm) request.getAttribute(attribute);
224 } else {
225 session = request.getSession();
226 instance = (ActionForm) session.getAttribute(attribute);
227 }
228
229 return (instance);
230 }
231
232 /**
233 * <p>Create and return an <code>ActionForm</code> instance appropriate to
234 * the information in <code>config</code>.</p>
235 *
236 * <p>Does not perform any checks to see if an existing ActionForm exists
237 * which could be reused.</p>
238 *
239 * @param config The configuration for the Form bean which is to be
240 * created.
241 * @param servlet The action servlet
242 * @return ActionForm instance associated with this request
243 */
244 public static ActionForm createActionForm(FormBeanConfig config,
245 ActionServlet servlet) {
246 if (config == null) {
247 return (null);
248 }
249
250 ActionForm instance = null;
251
252
253 try {
254 instance = config.createActionForm(servlet);
255
256 if (log.isDebugEnabled()) {
257 log.debug(" Creating new "
258 + (config.getDynamic() ? "DynaActionForm" : "ActionForm")
259 + " instance of type '" + config.getType() + "'");
260 log.trace(" --> " + instance);
261 }
262 } catch (Throwable t) {
263 log.error(servlet.getInternal().getMessage("formBean",
264 config.getType()), t);
265 }
266
267 return (instance);
268 }
269
270 /**
271 * <p>Retrieves the servlet mapping pattern for the specified {@link ActionServlet}.</p>
272 *
273 * @return the servlet mapping
274 * @see Globals#SERVLET_KEY
275 * @since Struts 1.3.6
276 */
277 public static String getServletMapping(ActionServlet servlet) {
278 ServletContext servletContext = servlet.getServletConfig().getServletContext();
279 return (String)servletContext.getAttribute(Globals.SERVLET_KEY);
280 }
281
282 /**
283 * <p>Look up and return current user locale, based on the specified
284 * parameters.</p>
285 *
286 * @param request The request used to lookup the Locale
287 * @param locale Name of the session attribute for our user's Locale. If
288 * this is <code>null</code>, the default locale key is
289 * used for the lookup.
290 * @return current user locale
291 * @since Struts 1.2
292 */
293 public static Locale getUserLocale(HttpServletRequest request, String locale) {
294 Locale userLocale = null;
295 HttpSession session = request.getSession(false);
296
297 if (locale == null) {
298 locale = Globals.LOCALE_KEY;
299 }
300
301
302 if (session != null) {
303 userLocale = (Locale) session.getAttribute(locale);
304 }
305
306 if (userLocale == null) {
307
308 userLocale = request.getLocale();
309 }
310
311 return userLocale;
312 }
313
314 /**
315 * <p>Populate the properties of the specified JavaBean from the specified
316 * HTTP request, based on matching each parameter name against the
317 * corresponding JavaBeans "property setter" methods in the bean's class.
318 * Suitable conversion is done for argument types as described under
319 * <code>convert()</code>.</p>
320 *
321 * @param bean The JavaBean whose properties are to be set
322 * @param request The HTTP request whose parameters are to be used to
323 * populate bean properties
324 * @throws ServletException if an exception is thrown while setting
325 * property values
326 */
327 public static void populate(Object bean, HttpServletRequest request)
328 throws ServletException {
329 populate(bean, null, null, request);
330 }
331
332 /**
333 * <p>Populate the properties of the specified JavaBean from the specified
334 * HTTP request, based on matching each parameter name (plus an optional
335 * prefix and/or suffix) against the corresponding JavaBeans "property
336 * setter" methods in the bean's class. Suitable conversion is done for
337 * argument types as described under <code>setProperties</code>.</p>
338 *
339 * <p>If you specify a non-null <code>prefix</code> and a non-null
340 * <code>suffix</code>, the parameter name must match
341 * <strong>both</strong> conditions for its value(s) to be used in
342 * populating bean properties. If the request's content type is
343 * "multipart/form-data" and the method is "POST", the
344 * <code>HttpServletRequest</code> object will be wrapped in a
345 * <code>MultipartRequestWrapper</code object.</p>
346 *
347 * @param bean The JavaBean whose properties are to be set
348 * @param prefix The prefix (if any) to be prepend to bean property names
349 * when looking for matching parameters
350 * @param suffix The suffix (if any) to be appended to bean property
351 * names when looking for matching parameters
352 * @param request The HTTP request whose parameters are to be used to
353 * populate bean properties
354 * @throws ServletException if an exception is thrown while setting
355 * property values
356 */
357 public static void populate(Object bean, String prefix, String suffix,
358 HttpServletRequest request)
359 throws ServletException {
360
361 HashMap properties = new HashMap();
362
363
364 Enumeration names = null;
365
366
367 Map multipartParameters = null;
368
369 String contentType = request.getContentType();
370 String method = request.getMethod();
371 boolean isMultipart = false;
372
373 if (bean instanceof ActionForm) {
374 ((ActionForm) bean).setMultipartRequestHandler(null);
375 }
376
377 MultipartRequestHandler multipartHandler = null;
378 if ((contentType != null)
379 && (contentType.startsWith("multipart/form-data"))
380 && (method.equalsIgnoreCase("POST"))) {
381
382 ActionServletWrapper servlet;
383
384 if (bean instanceof ActionForm) {
385 servlet = ((ActionForm) bean).getServletWrapper();
386 } else {
387 throw new ServletException("bean that's supposed to be "
388 + "populated from a multipart request is not of type "
389 + "\"org.apache.struts.action.ActionForm\", but type "
390 + "\"" + bean.getClass().getName() + "\"");
391 }
392
393
394 multipartHandler = getMultipartHandler(request);
395
396 if (multipartHandler != null) {
397 isMultipart = true;
398
399
400 servlet.setServletFor(multipartHandler);
401 multipartHandler.setMapping((ActionMapping) request
402 .getAttribute(Globals.MAPPING_KEY));
403
404
405 multipartHandler.handleRequest(request);
406
407
408 Boolean maxLengthExceeded =
409 (Boolean) request.getAttribute(MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED);
410
411 if ((maxLengthExceeded != null)
412 && (maxLengthExceeded.booleanValue())) {
413 ((ActionForm) bean).setMultipartRequestHandler(multipartHandler);
414 return;
415 }
416
417
418 multipartParameters =
419 getAllParametersForMultipartRequest(request,
420 multipartHandler);
421 names = Collections.enumeration(multipartParameters.keySet());
422 }
423 }
424
425 if (!isMultipart) {
426 names = request.getParameterNames();
427 }
428
429 while (names.hasMoreElements()) {
430 String name = (String) names.nextElement();
431 String stripped = name;
432
433 if (prefix != null) {
434 if (!stripped.startsWith(prefix)) {
435 continue;
436 }
437
438 stripped = stripped.substring(prefix.length());
439 }
440
441 if (suffix != null) {
442 if (!stripped.endsWith(suffix)) {
443 continue;
444 }
445
446 stripped =
447 stripped.substring(0, stripped.length() - suffix.length());
448 }
449
450 Object parameterValue = null;
451
452 if (isMultipart) {
453 parameterValue = multipartParameters.get(name);
454 } else {
455 parameterValue = request.getParameterValues(name);
456 }
457
458
459
460 if (!(stripped.startsWith("org.apache.struts."))) {
461 properties.put(stripped, parameterValue);
462 }
463 }
464
465
466 try {
467 BeanUtils.populate(bean, properties);
468 } catch (Exception e) {
469 throw new ServletException("BeanUtils.populate", e);
470 } finally {
471 if (multipartHandler != null) {
472
473
474
475
476 ((ActionForm) bean).setMultipartRequestHandler(multipartHandler);
477 }
478 }
479 }
480
481 /**
482 * <p>Try to locate a multipart request handler for this request. First,
483 * look for a mapping-specific handler stored for us under an attribute.
484 * If one is not present, use the global multipart handler, if there is
485 * one.</p>
486 *
487 * @param request The HTTP request for which the multipart handler should
488 * be found.
489 * @return the multipart handler to use, or null if none is found.
490 * @throws ServletException if any exception is thrown while attempting to
491 * locate the multipart handler.
492 */
493 private static MultipartRequestHandler getMultipartHandler(
494 HttpServletRequest request)
495 throws ServletException {
496 MultipartRequestHandler multipartHandler = null;
497 String multipartClass =
498 (String) request.getAttribute(Globals.MULTIPART_KEY);
499
500 request.removeAttribute(Globals.MULTIPART_KEY);
501
502
503 if (multipartClass != null) {
504 try {
505 multipartHandler =
506 (MultipartRequestHandler) applicationInstance(multipartClass);
507 } catch (ClassNotFoundException cnfe) {
508 log.error("MultipartRequestHandler class \"" + multipartClass
509 + "\" in mapping class not found, "
510 + "defaulting to global multipart class");
511 } catch (InstantiationException ie) {
512 log.error("InstantiationException when instantiating "
513 + "MultipartRequestHandler \"" + multipartClass + "\", "
514 + "defaulting to global multipart class, exception: "
515 + ie.getMessage());
516 } catch (IllegalAccessException iae) {
517 log.error("IllegalAccessException when instantiating "
518 + "MultipartRequestHandler \"" + multipartClass + "\", "
519 + "defaulting to global multipart class, exception: "
520 + iae.getMessage());
521 }
522
523 if (multipartHandler != null) {
524 return multipartHandler;
525 }
526 }
527
528 ModuleConfig moduleConfig =
529 ModuleUtils.getInstance().getModuleConfig(request);
530
531 multipartClass = moduleConfig.getControllerConfig().getMultipartClass();
532
533
534 if (multipartClass != null) {
535 try {
536 multipartHandler =
537 (MultipartRequestHandler) applicationInstance(multipartClass);
538 } catch (ClassNotFoundException cnfe) {
539 throw new ServletException("Cannot find multipart class \""
540 + multipartClass + "\"" + ", exception: "
541 + cnfe.getMessage());
542 } catch (InstantiationException ie) {
543 throw new ServletException(
544 "InstantiationException when instantiating "
545 + "multipart class \"" + multipartClass + "\", exception: "
546 + ie.getMessage());
547 } catch (IllegalAccessException iae) {
548 throw new ServletException(
549 "IllegalAccessException when instantiating "
550 + "multipart class \"" + multipartClass + "\", exception: "
551 + iae.getMessage());
552 }
553
554 if (multipartHandler != null) {
555 return multipartHandler;
556 }
557 }
558
559 return multipartHandler;
560 }
561
562 /**
563 * <p>Create a <code>Map</code> containing all of the parameters supplied
564 * for a multipart request, keyed by parameter name. In addition to text
565 * and file elements from the multipart body, query string parameters are
566 * included as well.</p>
567 *
568 * @param request The (wrapped) HTTP request whose parameters are
569 * to be added to the map.
570 * @param multipartHandler The multipart handler used to parse the
571 * request.
572 * @return the map containing all parameters for this multipart request.
573 */
574 private static Map getAllParametersForMultipartRequest(
575 HttpServletRequest request, MultipartRequestHandler multipartHandler) {
576 Map parameters = new HashMap();
577 Hashtable elements = multipartHandler.getAllElements();
578 Enumeration e = elements.keys();
579
580 while (e.hasMoreElements()) {
581 String key = (String) e.nextElement();
582
583 parameters.put(key, elements.get(key));
584 }
585
586 if (request instanceof MultipartRequestWrapper) {
587 request =
588 (HttpServletRequest) ((MultipartRequestWrapper) request)
589 .getRequest();
590 e = request.getParameterNames();
591
592 while (e.hasMoreElements()) {
593 String key = (String) e.nextElement();
594
595 parameters.put(key, request.getParameterValues(key));
596 }
597 } else {
598 log.debug("Gathering multipart parameters for unwrapped request");
599 }
600
601 return parameters;
602 }
603
604 /**
605 * <p>Compute the printable representation of a URL, leaving off the
606 * scheme/host/port part if no host is specified. This will typically be
607 * the case for URLs that were originally created from relative or
608 * context-relative URIs.</p>
609 *
610 * @param url URL to render in a printable representation
611 * @return printable representation of a URL
612 */
613 public static String printableURL(URL url) {
614 if (url.getHost() != null) {
615 return (url.toString());
616 }
617
618 String file = url.getFile();
619 String ref = url.getRef();
620
621 if (ref == null) {
622 return (file);
623 } else {
624 StringBuffer sb = new StringBuffer(file);
625
626 sb.append('#');
627 sb.append(ref);
628
629 return (sb.toString());
630 }
631 }
632
633 /**
634 * <p>Return the context-relative URL that corresponds to the specified
635 * {@link ActionConfig}, relative to the module associated with the
636 * current modules's {@link ModuleConfig}.</p>
637 *
638 * @param request The servlet request we are processing
639 * @param action ActionConfig to be evaluated
640 * @param pattern URL pattern used to map the controller servlet
641 * @return context-relative URL relative to the module
642 * @since Struts 1.1
643 */
644 public static String actionURL(HttpServletRequest request,
645 ActionConfig action, String pattern) {
646 StringBuffer sb = new StringBuffer();
647
648 if (pattern.endsWith("/*")) {
649 sb.append(pattern.substring(0, pattern.length() - 2));
650 sb.append(action.getPath());
651 } else if (pattern.startsWith("*.")) {
652 ModuleConfig appConfig =
653 ModuleUtils.getInstance().getModuleConfig(request);
654
655 sb.append(appConfig.getPrefix());
656 sb.append(action.getPath());
657 sb.append(pattern.substring(1));
658 } else {
659 throw new IllegalArgumentException(pattern);
660 }
661
662 return sb.toString();
663 }
664
665 /**
666 * <p>Return the context-relative URL that corresponds to the specified
667 * <code>ForwardConfig</code>. The URL is calculated based on the
668 * properties of the {@link ForwardConfig} instance as follows:</p>
669 *
670 * <ul>
671 *
672 *
673 * <li>If the <code>contextRelative</code> property is set, it is assumed
674 * that the <code>path</code> property contains a path that is already
675 * context-relative:
676 *
677 * <ul>
678 *
679 * <li>If the <code>path</code> property value starts with a slash, it is
680 * returned unmodified.</li> <li>If the <code>path</code> property value
681 * does not start with a slash, a slash is prepended.</li>
682 *
683 * </ul></li>
684 *
685 * <li>Acquire the <code>forwardPattern</code> property from the
686 * <code>ControllerConfig</code> for the application module used to
687 * process this request. If no pattern was configured, default to a
688 * pattern of <code>$M$P</code>, which is compatible with the hard-coded
689 * mapping behavior in Struts 1.0.</li>
690 *
691 * <li>Process the acquired <code>forwardPattern</code>, performing the
692 * following substitutions:
693 *
694 * <ul>
695 *
696 * <li><strong>$M</strong> - Replaced by the module prefix for the
697 * application module processing this request.</li>
698 *
699 * <li><strong>$P</strong> - Replaced by the <code>path</code> property of
700 * the specified {@link ForwardConfig}, prepended with a slash if it does
701 * not start with one.</li>
702 *
703 * <li><strong>$$</strong> - Replaced by a single dollar sign
704 * character.</li>
705 *
706 * <li><strong>$x</strong> (where "x" is any charater not listed above) -
707 * Silently omit these two characters from the result value. (This has
708 * the side effect of causing all other $+letter combinations to be
709 * reserved.)</li>
710 *
711 * </ul></li>
712 *
713 * </ul>
714 *
715 * @param request The servlet request we are processing
716 * @param forward ForwardConfig to be evaluated
717 * @return context-relative URL
718 * @since Struts 1.1
719 */
720 public static String forwardURL(HttpServletRequest request,
721 ForwardConfig forward) {
722 return forwardURL(request, forward, null);
723 }
724
725 /**
726 * <p>Return the context-relative URL that corresponds to the specified
727 * <code>ForwardConfig</code>. The URL is calculated based on the
728 * properties of the {@link ForwardConfig} instance as follows:</p>
729 *
730 * <ul>
731 *
732 * <li>If the <code>contextRelative</code> property is set, it is assumed
733 * that the <code>path</code> property contains a path that is already
734 * context-relative: <ul>
735 *
736 * <li>If the <code>path</code> property value starts with a slash, it is
737 * returned unmodified.</li> <li>If the <code>path</code> property value
738 * does not start with a slash, a slash is prepended.</li>
739 *
740 * </ul></li>
741 *
742 * <li>Acquire the <code>forwardPattern</code> property from the
743 * <code>ControllerConfig</code> for the application module used to
744 * process this request. If no pattern was configured, default to a
745 * pattern of <code>$M$P</code>, which is compatible with the hard-coded
746 * mapping behavior in Struts 1.0.</li>
747 *
748 * <li>Process the acquired <code>forwardPattern</code>, performing the
749 * following substitutions: <ul> <li><strong>$M</strong> - Replaced by the
750 * module prefix for the application module processing this request.</li>
751 *
752 * <li><strong>$P</strong> - Replaced by the <code>path</code> property of
753 * the specified {@link ForwardConfig}, prepended with a slash if it does
754 * not start with one.</li>
755 *
756 * <li><strong>$$</strong> - Replaced by a single dollar sign
757 * character.</li>
758 *
759 * <li><strong>$x</strong> (where "x" is any charater not listed above) -
760 * Silently omit these two characters from the result value. (This has
761 * the side effect of causing all other $+letter combinations to be
762 * reserved.)</li>
763 *
764 * </ul></li></ul>
765 *
766 * @param request The servlet request we are processing
767 * @param forward ForwardConfig to be evaluated
768 * @param moduleConfig Base forward on this module config.
769 * @return context-relative URL
770 * @since Struts 1.2
771 */
772 public static String forwardURL(HttpServletRequest request,
773 ForwardConfig forward, ModuleConfig moduleConfig) {
774
775 if (moduleConfig == null) {
776 moduleConfig = ModuleUtils.getInstance().getModuleConfig(request);
777 }
778
779 String path = forward.getPath();
780
781
782 String prefix = moduleConfig.getPrefix();
783
784
785 if (forward.getModule() != null) {
786 prefix = forward.getModule();
787
788 if ("/".equals(prefix)) {
789 prefix = "";
790 }
791 }
792
793 StringBuffer sb = new StringBuffer();
794
795
796 String forwardPattern =
797 moduleConfig.getControllerConfig().getForwardPattern();
798
799 if (forwardPattern == null) {
800
801 sb.append(prefix);
802
803
804 if (!path.startsWith("/")) {
805 sb.append("/");
806 }
807
808 sb.append(path);
809 } else {
810 boolean dollar = false;
811
812 for (int i = 0; i < forwardPattern.length(); i++) {
813 char ch = forwardPattern.charAt(i);
814
815 if (dollar) {
816 switch (ch) {
817 case 'M':
818 sb.append(prefix);
819
820 break;
821
822 case 'P':
823
824
825 if (!path.startsWith("/")) {
826 sb.append("/");
827 }
828
829 sb.append(path);
830
831 break;
832
833 case '$':
834 sb.append('$');
835
836 break;
837
838 default:
839 ;
840 }
841
842 dollar = false;
843
844 continue;
845 } else if (ch == '$') {
846 dollar = true;
847 } else {
848 sb.append(ch);
849 }
850 }
851 }
852
853 return (sb.toString());
854 }
855
856 /**
857 * <p>Return the URL representing the current request. This is equivalent
858 * to <code>HttpServletRequest.getRequestURL</code> in Servlet 2.3.</p>
859 *
860 * @param request The servlet request we are processing
861 * @return URL representing the current request
862 * @throws MalformedURLException if a URL cannot be created
863 */
864 public static URL requestURL(HttpServletRequest request)
865 throws MalformedURLException {
866 StringBuffer url = requestToServerUriStringBuffer(request);
867
868 return (new URL(url.toString()));
869 }
870
871 /**
872 * <p>Return the URL representing the scheme, server, and port number of
873 * the current request. Server-relative URLs can be created by simply
874 * appending the server-relative path (starting with '/') to this.</p>
875 *
876 * @param request The servlet request we are processing
877 * @return URL representing the scheme, server, and port number of the
878 * current request
879 * @throws MalformedURLException if a URL cannot be created
880 */
881 public static URL serverURL(HttpServletRequest request)
882 throws MalformedURLException {
883 StringBuffer url = requestToServerStringBuffer(request);
884
885 return (new URL(url.toString()));
886 }
887
888 /**
889 * <p>Return the string representing the scheme, server, and port number
890 * of the current request. Server-relative URLs can be created by simply
891 * appending the server-relative path (starting with '/') to this.</p>
892 *
893 * @param request The servlet request we are processing
894 * @return URL representing the scheme, server, and port number of the
895 * current request
896 * @since Struts 1.2.0
897 */
898 public static StringBuffer requestToServerUriStringBuffer(
899 HttpServletRequest request) {
900 StringBuffer serverUri =
901 createServerUriStringBuffer(request.getScheme(),
902 request.getServerName(), request.getServerPort(),
903 request.getRequestURI());
904
905 return serverUri;
906 }
907
908 /**
909 * <p>Return <code>StringBuffer</code> representing the scheme, server,
910 * and port number of the current request. Server-relative URLs can be
911 * created by simply appending the server-relative path (starting with
912 * '/') to this.</p>
913 *
914 * @param request The servlet request we are processing
915 * @return URL representing the scheme, server, and port number of the
916 * current request
917 * @since Struts 1.2.0
918 */
919 public static StringBuffer requestToServerStringBuffer(
920 HttpServletRequest request) {
921 return createServerStringBuffer(request.getScheme(),
922 request.getServerName(), request.getServerPort());
923 }
924
925 /**
926 * <p>Return <code>StringBuffer</code> representing the scheme, server,
927 * and port number of the current request.</p>
928 *
929 * @param scheme The scheme name to use
930 * @param server The server name to use
931 * @param port The port value to use
932 * @return StringBuffer in the form scheme: server: port
933 * @since Struts 1.2.0
934 */
935 public static StringBuffer createServerStringBuffer(String scheme,
936 String server, int port) {
937 StringBuffer url = new StringBuffer();
938
939 if (port < 0) {
940 port = 80;
941 }
942
943 url.append(scheme);
944 url.append("://");
945 url.append(server);
946
947 if ((scheme.equals("http") && (port != 80))
948 || (scheme.equals("https") && (port != 443))) {
949 url.append(':');
950 url.append(port);
951 }
952
953 return url;
954 }
955
956 /**
957 * <p>Return <code>StringBuffer</code> representing the scheme, server,
958 * and port number of the current request.</p>
959 *
960 * @param scheme The scheme name to use
961 * @param server The server name to use
962 * @param port The port value to use
963 * @param uri The uri value to use
964 * @return StringBuffer in the form scheme: server: port
965 * @since Struts 1.2.0
966 */
967 public static StringBuffer createServerUriStringBuffer(String scheme,
968 String server, int port, String uri) {
969 StringBuffer serverUri = createServerStringBuffer(scheme, server, port);
970
971 serverUri.append(uri);
972
973 return serverUri;
974 }
975
976 /**
977 * <p>Returns the true path of the destination action if the specified forward
978 * is an action-aliased URL. This method version forms the URL based on
979 * the current request; selecting the current module if the forward does not
980 * explicitly contain a module path.</p>
981 *
982 * @param forward the forward config
983 * @param request the current request
984 * @param servlet the servlet handling the current request
985 * @return the context-relative URL of the action if the forward has an action identifier; otherwise <code>null</code>.
986 * @since Struts 1.3.6
987 */
988 public static String actionIdURL(ForwardConfig forward, HttpServletRequest request, ActionServlet servlet) {
989 ModuleConfig moduleConfig = null;
990 if (forward.getModule() != null) {
991 String prefix = forward.getModule();
992 moduleConfig = ModuleUtils.getInstance().getModuleConfig(prefix, servlet.getServletContext());
993 } else {
994 moduleConfig = ModuleUtils.getInstance().getModuleConfig(request);
995 }
996 return actionIdURL(forward.getPath(), moduleConfig, servlet);
997 }
998
999 /**
1000 * <p>Returns the true path of the destination action if the specified forward
1001 * is an action-aliased URL. This method version forms the URL based on
1002 * the specified module.
1003 *
1004 * @param originalPath the action-aliased path
1005 * @param moduleConfig the module config for this request
1006 * @param servlet the servlet handling the current request
1007 * @return the context-relative URL of the action if the path has an action identifier; otherwise <code>null</code>.
1008 * @since Struts 1.3.6
1009 */
1010 public static String actionIdURL(String originalPath, ModuleConfig moduleConfig, ActionServlet servlet) {
1011 if (originalPath.startsWith("http") || originalPath.startsWith("/")) {
1012 return null;
1013 }
1014
1015
1016
1017 String actionId = null;
1018 String qs = null;
1019 int qpos = originalPath.indexOf("?");
1020 if (qpos == -1) {
1021 actionId = originalPath;
1022 } else {
1023 actionId = originalPath.substring(0, qpos);
1024 qs = originalPath.substring(qpos);
1025 }
1026
1027
1028 ActionConfig actionConfig = moduleConfig.findActionConfigId(actionId);
1029 if (actionConfig == null) {
1030 if (log.isDebugEnabled()) {
1031 log.debug("No actionId found for " + actionId);
1032 }
1033 return null;
1034 }
1035
1036 String path = actionConfig.getPath();
1037 String mapping = RequestUtils.getServletMapping(servlet);
1038 StringBuffer actionIdPath = new StringBuffer();
1039
1040
1041 if (mapping.startsWith("*")) {
1042 actionIdPath.append(path);
1043 actionIdPath.append(mapping.substring(1));
1044 } else if (mapping.startsWith("/")) {
1045 mapping = mapping.substring(0, mapping.length() - 1);
1046 if (mapping.endsWith("/") && path.startsWith("/")) {
1047 actionIdPath.append(mapping);
1048 actionIdPath.append(path.substring(1));
1049 } else {
1050 actionIdPath.append(mapping);
1051 actionIdPath.append(path);
1052 }
1053 } else {
1054 log.warn("Unknown servlet mapping pattern");
1055 actionIdPath.append(path);
1056 }
1057
1058
1059 if (qs != null) {
1060 actionIdPath.append(qs);
1061 }
1062
1063
1064 if (log.isDebugEnabled()) {
1065 log.debug(originalPath + " unaliased to " + actionIdPath.toString());
1066 }
1067 return actionIdPath.toString();
1068 }
1069 }