1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts.action;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22 import org.apache.struts.Globals;
23 import org.apache.struts.config.ActionConfig;
24 import org.apache.struts.config.ExceptionConfig;
25 import org.apache.struts.config.ForwardConfig;
26 import org.apache.struts.config.ModuleConfig;
27 import org.apache.struts.upload.MultipartRequestWrapper;
28 import org.apache.struts.util.MessageResources;
29 import org.apache.struts.util.RequestUtils;
30
31 import javax.servlet.RequestDispatcher;
32 import javax.servlet.ServletContext;
33 import javax.servlet.ServletException;
34 import javax.servlet.http.HttpServletRequest;
35 import javax.servlet.http.HttpServletResponse;
36 import javax.servlet.http.HttpSession;
37
38 import java.io.IOException;
39
40 import java.util.HashMap;
41 import java.util.Iterator;
42 import java.util.Locale;
43
44 /***
45 * <p><strong>RequestProcessor</strong> contains the processing logic that the
46 * {@link ActionServlet} performs as it receives each servlet request from the
47 * container. You can customize the request processing behavior by subclassing
48 * this class and overriding the method(s) whose behavior you are interested
49 * in changing.</p>
50 *
51 * @version $Rev: 421119 $ $Date: 2006-07-11 21:49:11 -0700 (Tue, 11 Jul 2006) $
52 * @since Struts 1.1
53 */
54 public class RequestProcessor {
55
56
57 /***
58 * <p>The request attribute under which the path information is stored for
59 * processing during a <code>RequestDispatcher.include</code> call.</p>
60 */
61 public static final String INCLUDE_PATH_INFO =
62 "javax.servlet.include.path_info";
63
64 /***
65 * <p>The request attribute under which the servlet path information is
66 * stored for processing during a <code>RequestDispatcher.include</code>
67 * call.</p>
68 */
69 public static final String INCLUDE_SERVLET_PATH =
70 "javax.servlet.include.servlet_path";
71
72 /***
73 * <p>Commons Logging instance.</p>
74 */
75 protected static Log log = LogFactory.getLog(RequestProcessor.class);
76
77
78
79 /***
80 * <p>The set of <code>Action</code> instances that have been created and
81 * initialized, keyed by the fully qualified Java class name of the
82 * <code>Action</code> class.</p>
83 */
84 protected HashMap actions = new HashMap();
85
86 /***
87 * <p>The <code>ModuleConfiguration</code> with which we are
88 * associated.</p>
89 */
90 protected ModuleConfig moduleConfig = null;
91
92 /***
93 * <p>The servlet with which we are associated.</p>
94 */
95 protected ActionServlet servlet = null;
96
97
98
99 /***
100 * <p>Clean up in preparation for a shutdown of this application.</p>
101 */
102 public void destroy() {
103 synchronized (this.actions) {
104 Iterator actions = this.actions.values().iterator();
105
106 while (actions.hasNext()) {
107 Action action = (Action) actions.next();
108
109 action.setServlet(null);
110 }
111
112 this.actions.clear();
113 }
114
115 this.servlet = null;
116 }
117
118 /***
119 * <p>Initialize this request processor instance.</p>
120 *
121 * @param servlet The ActionServlet we are associated with
122 * @param moduleConfig The ModuleConfig we are associated with.
123 * @throws ServletException If an error occor during initialization
124 */
125 public void init(ActionServlet servlet, ModuleConfig moduleConfig)
126 throws ServletException {
127 synchronized (actions) {
128 actions.clear();
129 }
130
131 this.servlet = servlet;
132 this.moduleConfig = moduleConfig;
133 }
134
135 /***
136 * <p>Process an <code>HttpServletRequest</code> and create the
137 * corresponding <code>HttpServletResponse</code> or dispatch to another
138 * resource.</p>
139 *
140 * @param request The servlet request we are processing
141 * @param response The servlet response we are creating
142 * @throws IOException if an input/output error occurs
143 * @throws ServletException if a processing exception occurs
144 */
145 public void process(HttpServletRequest request, HttpServletResponse response)
146 throws IOException, ServletException {
147
148 request = processMultipart(request);
149
150
151 String path = processPath(request, response);
152
153 if (path == null) {
154 return;
155 }
156
157 if (log.isDebugEnabled()) {
158 log.debug("Processing a '" + request.getMethod() + "' for path '"
159 + path + "'");
160 }
161
162
163 processLocale(request, response);
164
165
166 processContent(request, response);
167 processNoCache(request, response);
168
169
170 if (!processPreprocess(request, response)) {
171 return;
172 }
173
174 this.processCachedMessages(request, response);
175
176
177 ActionMapping mapping = processMapping(request, response, path);
178
179 if (mapping == null) {
180 return;
181 }
182
183
184 if (!processRoles(request, response, mapping)) {
185 return;
186 }
187
188
189 ActionForm form = processActionForm(request, response, mapping);
190
191 processPopulate(request, response, form, mapping);
192
193
194 try {
195 if (!processValidate(request, response, form, mapping)) {
196 return;
197 }
198 } catch (InvalidCancelException e) {
199 ActionForward forward = processException(request, response, e, form, mapping);
200 processForwardConfig(request, response, forward);
201 return;
202 } catch (IOException e) {
203 throw e;
204 } catch (ServletException e) {
205 throw e;
206 }
207
208
209 if (!processForward(request, response, mapping)) {
210 return;
211 }
212
213 if (!processInclude(request, response, mapping)) {
214 return;
215 }
216
217
218 Action action = processActionCreate(request, response, mapping);
219
220 if (action == null) {
221 return;
222 }
223
224
225 ActionForward forward =
226 processActionPerform(request, response, action, form, mapping);
227
228
229 processForwardConfig(request, response, forward);
230 }
231
232
233
234 /***
235 * <p>Return an <code>Action</code> instance that will be used to process
236 * the current request, creating a new one if necessary.</p>
237 *
238 * @param request The servlet request we are processing
239 * @param response The servlet response we are creating
240 * @param mapping The mapping we are using
241 * @return An <code>Action</code> instance that will be used to process
242 * the current request.
243 * @throws IOException if an input/output error occurs
244 */
245 protected Action processActionCreate(HttpServletRequest request,
246 HttpServletResponse response, ActionMapping mapping)
247 throws IOException {
248
249 String className = mapping.getType();
250
251 if (log.isDebugEnabled()) {
252 log.debug(" Looking for Action instance for class " + className);
253 }
254
255
256
257
258 Action instance;
259
260 synchronized (actions) {
261
262 instance = (Action) actions.get(className);
263
264 if (instance != null) {
265 if (log.isTraceEnabled()) {
266 log.trace(" Returning existing Action instance");
267 }
268
269 return (instance);
270 }
271
272
273 if (log.isTraceEnabled()) {
274 log.trace(" Creating new Action instance");
275 }
276
277 try {
278 instance = (Action) RequestUtils.applicationInstance(className);
279
280
281
282 } catch (Exception e) {
283 log.error(getInternal().getMessage("actionCreate",
284 mapping.getPath()), e);
285
286 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
287 getInternal().getMessage("actionCreate", mapping.getPath()));
288
289 return (null);
290 }
291
292 actions.put(className, instance);
293 }
294
295 if (instance.getServlet() == null) {
296 instance.setServlet(this.servlet);
297 }
298
299 return (instance);
300 }
301
302 /***
303 * <p>Retrieve and return the <code>ActionForm</code> associated with this
304 * mapping, creating and retaining one if necessary. If there is no
305 * <code>ActionForm</code> associated with this mapping, return
306 * <code>null</code>.</p>
307 *
308 * @param request The servlet request we are processing
309 * @param response The servlet response we are creating
310 * @param mapping The mapping we are using
311 * @return The <code>ActionForm</code> associated with this mapping.
312 */
313 protected ActionForm processActionForm(HttpServletRequest request,
314 HttpServletResponse response, ActionMapping mapping) {
315
316 ActionForm instance =
317 RequestUtils.createActionForm(request, mapping, moduleConfig,
318 servlet);
319
320 if (instance == null) {
321 return (null);
322 }
323
324
325 if (log.isDebugEnabled()) {
326 log.debug(" Storing ActionForm bean instance in scope '"
327 + mapping.getScope() + "' under attribute key '"
328 + mapping.getAttribute() + "'");
329 }
330
331 if ("request".equals(mapping.getScope())) {
332 request.setAttribute(mapping.getAttribute(), instance);
333 } else {
334 HttpSession session = request.getSession();
335
336 session.setAttribute(mapping.getAttribute(), instance);
337 }
338
339 return (instance);
340 }
341
342 /***
343 * <p>Forward or redirect to the specified destination, by the specified
344 * mechanism. This method uses a <code>ForwardConfig</code> object
345 * instead an <code>ActionForward</code>.</p>
346 *
347 * @param request The servlet request we are processing
348 * @param response The servlet response we are creating
349 * @param forward The ForwardConfig controlling where we go next
350 * @throws IOException if an input/output error occurs
351 * @throws ServletException if a servlet exception occurs
352 */
353 protected void processForwardConfig(HttpServletRequest request,
354 HttpServletResponse response, ForwardConfig forward)
355 throws IOException, ServletException {
356 if (forward == null) {
357 return;
358 }
359
360 if (log.isDebugEnabled()) {
361 log.debug("processForwardConfig(" + forward + ")");
362 }
363
364 String forwardPath = forward.getPath();
365 String uri;
366
367
368
369 if (forwardPath.startsWith("/")) {
370
371 uri = RequestUtils.forwardURL(request, forward, null);
372 } else {
373 uri = forwardPath;
374 }
375
376 if (forward.getRedirect()) {
377
378 if (uri.startsWith("/")) {
379 uri = request.getContextPath() + uri;
380 }
381
382 response.sendRedirect(response.encodeRedirectURL(uri));
383 } else {
384 doForward(uri, request, response);
385 }
386 }
387
388
389
390
391
392 /***
393 * <P>Ask the specified <code>Action</code> instance to handle this
394 * request. Return the <code>ActionForward</code> instance (if any)
395 * returned by the called <code>Action</code> for further processing.
396 * </P>
397 *
398 * @param request The servlet request we are processing
399 * @param response The servlet response we are creating
400 * @param action The Action instance to be used
401 * @param form The ActionForm instance to pass to this Action
402 * @param mapping The ActionMapping instance to pass to this Action
403 * @return The <code>ActionForward</code> instance (if any) returned by
404 * the called <code>Action</code>.
405 * @throws IOException if an input/output error occurs
406 * @throws ServletException if a servlet exception occurs
407 */
408 protected ActionForward processActionPerform(HttpServletRequest request,
409 HttpServletResponse response, Action action, ActionForm form,
410 ActionMapping mapping)
411 throws IOException, ServletException {
412 try {
413 return (action.execute(mapping, form, request, response));
414 } catch (Exception e) {
415 return (processException(request, response, e, form, mapping));
416 }
417 }
418
419 /***
420 * <p>Removes any <code>ActionMessages</code> object stored in the session
421 * under <code>Globals.MESSAGE_KEY</code> and <code>Globals.ERROR_KEY</code>
422 * if the messages' <code>isAccessed</code> method returns true. This
423 * allows messages to be stored in the session, display one time, and be
424 * released here.</p>
425 *
426 * @param request The servlet request we are processing.
427 * @param response The servlet response we are creating.
428 * @since Struts 1.2
429 */
430 protected void processCachedMessages(HttpServletRequest request,
431 HttpServletResponse response) {
432 HttpSession session = request.getSession(false);
433
434 if (session == null) {
435 return;
436 }
437
438
439 ActionMessages messages =
440 (ActionMessages) session.getAttribute(Globals.MESSAGE_KEY);
441
442 if (messages != null) {
443 if (messages.isAccessed()) {
444 session.removeAttribute(Globals.MESSAGE_KEY);
445 }
446 }
447
448
449 messages = (ActionMessages) session.getAttribute(Globals.ERROR_KEY);
450
451 if (messages != null) {
452 if (messages.isAccessed()) {
453 session.removeAttribute(Globals.ERROR_KEY);
454 }
455 }
456 }
457
458 /***
459 * <p>Set the default content type (with optional character encoding) for
460 * all responses if requested. <strong>NOTE</strong> - This header will
461 * be overridden automatically if a <code>RequestDispatcher.forward</code>
462 * call is ultimately invoked.</p>
463 *
464 * @param request The servlet request we are processing
465 * @param response The servlet response we are creating
466 */
467 protected void processContent(HttpServletRequest request,
468 HttpServletResponse response) {
469 String contentType =
470 moduleConfig.getControllerConfig().getContentType();
471
472 if (contentType != null) {
473 response.setContentType(contentType);
474 }
475 }
476
477 /***
478 * <p>Ask our exception handler to handle the exception. Return the
479 * <code>ActionForward</code> instance (if any) returned by the called
480 * <code>ExceptionHandler</code>.</p>
481 *
482 * @param request The servlet request we are processing
483 * @param response The servlet response we are processing
484 * @param exception The exception being handled
485 * @param form The ActionForm we are processing
486 * @param mapping The ActionMapping we are using
487 * @return The <code>ActionForward</code> instance (if any) returned by
488 * the called <code>ExceptionHandler</code>.
489 * @throws IOException if an input/output error occurs
490 * @throws ServletException if a servlet exception occurs
491 */
492 protected ActionForward processException(HttpServletRequest request,
493 HttpServletResponse response, Exception exception, ActionForm form,
494 ActionMapping mapping)
495 throws IOException, ServletException {
496
497 ExceptionConfig config = mapping.findException(exception.getClass());
498
499 if (config == null) {
500 log.warn(getInternal().getMessage("unhandledException",
501 exception.getClass()));
502
503 if (exception instanceof IOException) {
504 throw (IOException) exception;
505 } else if (exception instanceof ServletException) {
506 throw (ServletException) exception;
507 } else {
508 throw new ServletException(exception);
509 }
510 }
511
512
513 try {
514 ExceptionHandler handler =
515 (ExceptionHandler) RequestUtils.applicationInstance(config
516 .getHandler());
517
518 return (handler.execute(exception, config, mapping, form, request,
519 response));
520 } catch (Exception e) {
521 throw new ServletException(e);
522 }
523 }
524
525 /***
526 * <p>Process a forward requested by this mapping (if any). Return
527 * <code>true</code> if standard processing should continue, or
528 * <code>false</code> if we have already handled this request.</p>
529 *
530 * @param request The servlet request we are processing
531 * @param response The servlet response we are creating
532 * @param mapping The ActionMapping we are using
533 * @return <code>true</code> to continue normal processing;
534 * <code>false</code> if a response has been created.
535 * @throws IOException if an input/output error occurs
536 * @throws ServletException if a servlet exception occurs
537 */
538 protected boolean processForward(HttpServletRequest request,
539 HttpServletResponse response, ActionMapping mapping)
540 throws IOException, ServletException {
541
542 String forward = mapping.getForward();
543
544 if (forward == null) {
545 return (true);
546 }
547
548 internalModuleRelativeForward(forward, request, response);
549
550 return (false);
551 }
552
553 /***
554 * <p>Process an include requested by this mapping (if any). Return
555 * <code>true</code> if standard processing should continue, or
556 * <code>false</code> if we have already handled this request.</p>
557 *
558 * @param request The servlet request we are processing
559 * @param response The servlet response we are creating
560 * @param mapping The ActionMapping we are using
561 * @return <code>true</code> to continue normal processing;
562 * <code>false</code> if a response has been created.
563 * @throws IOException if an input/output error occurs
564 * @throws ServletException if thrown by invoked methods
565 */
566 protected boolean processInclude(HttpServletRequest request,
567 HttpServletResponse response, ActionMapping mapping)
568 throws IOException, ServletException {
569
570 String include = mapping.getInclude();
571
572 if (include == null) {
573 return (true);
574 }
575
576 internalModuleRelativeInclude(include, request, response);
577
578 return (false);
579 }
580
581 /***
582 * <p>Automatically select a <code>Locale</code> for the current user, if
583 * requested. <strong>NOTE</strong> - configuring Locale selection will
584 * trigger the creation of a new <code>HttpSession</code> if
585 * necessary.</p>
586 *
587 * @param request The servlet request we are processing
588 * @param response The servlet response we are creating
589 */
590 protected void processLocale(HttpServletRequest request,
591 HttpServletResponse response) {
592
593 if (!moduleConfig.getControllerConfig().getLocale()) {
594 return;
595 }
596
597
598 HttpSession session = request.getSession();
599
600 if (session.getAttribute(Globals.LOCALE_KEY) != null) {
601 return;
602 }
603
604
605 Locale locale = request.getLocale();
606
607 if (locale != null) {
608 if (log.isDebugEnabled()) {
609 log.debug(" Setting user locale '" + locale + "'");
610 }
611
612 session.setAttribute(Globals.LOCALE_KEY, locale);
613 }
614 }
615
616 /***
617 * <p>Select the mapping used to process the selection path for this
618 * request. If no mapping can be identified, create an error response and
619 * return <code>null</code>.</p>
620 *
621 * @param request The servlet request we are processing
622 * @param response The servlet response we are creating
623 * @param path The portion of the request URI for selecting a mapping
624 * @return The mapping used to process the selection path for this
625 * request.
626 * @throws IOException if an input/output error occurs
627 */
628 protected ActionMapping processMapping(HttpServletRequest request,
629 HttpServletResponse response, String path)
630 throws IOException {
631
632 ActionMapping mapping =
633 (ActionMapping) moduleConfig.findActionConfig(path);
634
635
636 if (mapping != null) {
637 request.setAttribute(Globals.MAPPING_KEY, mapping);
638
639 return (mapping);
640 }
641
642
643 ActionConfig[] configs = moduleConfig.findActionConfigs();
644
645 for (int i = 0; i < configs.length; i++) {
646 if (configs[i].getUnknown()) {
647 mapping = (ActionMapping) configs[i];
648 request.setAttribute(Globals.MAPPING_KEY, mapping);
649
650 return (mapping);
651 }
652 }
653
654
655 String msg = getInternal().getMessage("processInvalid");
656
657 log.error(msg + " " + path);
658 response.sendError(HttpServletResponse.SC_NOT_FOUND, msg);
659
660 return null;
661 }
662
663 /***
664 * <p>If this is a multipart request, wrap it with a special wrapper.
665 * Otherwise, return the request unchanged.</p>
666 *
667 * @param request The HttpServletRequest we are processing
668 * @return A wrapped request, if the request is multipart; otherwise the
669 * original request.
670 */
671 protected HttpServletRequest processMultipart(HttpServletRequest request) {
672 if (!"POST".equalsIgnoreCase(request.getMethod())) {
673 return (request);
674 }
675
676 String contentType = request.getContentType();
677
678 if ((contentType != null)
679 && contentType.startsWith("multipart/form-data")) {
680 return (new MultipartRequestWrapper(request));
681 } else {
682 return (request);
683 }
684 }
685
686 /***
687 * <p>Set the no-cache headers for all responses, if requested.
688 * <strong>NOTE</strong> - This header will be overridden automatically if
689 * a <code>RequestDispatcher.forward</code> call is ultimately
690 * invoked.</p>
691 *
692 * @param request The servlet request we are processing
693 * @param response The servlet response we are creating
694 */
695 protected void processNoCache(HttpServletRequest request,
696 HttpServletResponse response) {
697 if (moduleConfig.getControllerConfig().getNocache()) {
698 response.setHeader("Pragma", "No-cache");
699 response.setHeader("Cache-Control", "no-cache,no-store,max-age=0");
700 response.setDateHeader("Expires", 1);
701 }
702 }
703
704 /***
705 * <p>Identify and return the path component (from the request URI) that
706 * we will use to select an <code>ActionMapping</code> with which to
707 * dispatch. If no such path can be identified, create an error response
708 * and return <code>null</code>.</p>
709 *
710 * @param request The servlet request we are processing
711 * @param response The servlet response we are creating
712 * @return The path that will be used to select an action mapping.
713 * @throws IOException if an input/output error occurs
714 */
715 protected String processPath(HttpServletRequest request,
716 HttpServletResponse response)
717 throws IOException {
718 String path;
719
720
721 path = (String) request.getAttribute(INCLUDE_PATH_INFO);
722
723 if (path == null) {
724 path = request.getPathInfo();
725 }
726
727 if ((path != null) && (path.length() > 0)) {
728 return (path);
729 }
730
731
732 path = (String) request.getAttribute(INCLUDE_SERVLET_PATH);
733
734 if (path == null) {
735 path = request.getServletPath();
736 }
737
738 String prefix = moduleConfig.getPrefix();
739
740 if (!path.startsWith(prefix)) {
741 String msg = getInternal().getMessage("processPath");
742
743 log.error(msg + " " + request.getRequestURI());
744 response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
745
746 return null;
747 }
748
749 path = path.substring(prefix.length());
750
751 int slash = path.lastIndexOf("/");
752 int period = path.lastIndexOf(".");
753
754 if ((period >= 0) && (period > slash)) {
755 path = path.substring(0, period);
756 }
757
758 return (path);
759 }
760
761 /***
762 * <p>Populate the properties of the specified <code>ActionForm</code>
763 * instance from the request parameters included with this request. In
764 * addition, request attribute <code>Globals.CANCEL_KEY</code> will be set
765 * if the request was submitted with a button created by
766 * <code>CancelTag</code>.</p>
767 *
768 * @param request The servlet request we are processing
769 * @param response The servlet response we are creating
770 * @param form The ActionForm instance we are populating
771 * @param mapping The ActionMapping we are using
772 * @throws ServletException if thrown by RequestUtils.populate()
773 */
774 protected void processPopulate(HttpServletRequest request,
775 HttpServletResponse response, ActionForm form, ActionMapping mapping)
776 throws ServletException {
777 if (form == null) {
778 return;
779 }
780
781
782 if (log.isDebugEnabled()) {
783 log.debug(" Populating bean properties from this request");
784 }
785
786 form.setServlet(this.servlet);
787 form.reset(mapping, request);
788
789 if (mapping.getMultipartClass() != null) {
790 request.setAttribute(Globals.MULTIPART_KEY,
791 mapping.getMultipartClass());
792 }
793
794 RequestUtils.populate(form, mapping.getPrefix(), mapping.getSuffix(),
795 request);
796
797
798 if ((request.getParameter(Globals.CANCEL_PROPERTY) != null)
799 || (request.getParameter(Globals.CANCEL_PROPERTY_X) != null)) {
800 request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
801 }
802 }
803
804 /***
805 * <p>General-purpose preprocessing hook that can be overridden as
806 * required by subclasses. Return <code>true</code> if you want standard
807 * processing to continue, or <code>false</code> if the response has
808 * already been completed. The default implementation does nothing.</p>
809 *
810 * @param request The servlet request we are processing
811 * @param response The servlet response we are creating
812 * @return <code>true</code> to continue normal processing;
813 * <code>false</code> if a response has been created.
814 */
815 protected boolean processPreprocess(HttpServletRequest request,
816 HttpServletResponse response) {
817 return (true);
818 }
819
820 /***
821 * <p>If this action is protected by security roles, make sure that the
822 * current user possesses at least one of them. Return <code>true</code>
823 * to continue normal processing, or <code>false</code> if an appropriate
824 * response has been created and processing should terminate.</p>
825 *
826 * @param request The servlet request we are processing
827 * @param response The servlet response we are creating
828 * @param mapping The mapping we are using
829 * @return <code>true</code> to continue normal processing;
830 * <code>false</code> if a response has been created.
831 * @throws IOException if an input/output error occurs
832 * @throws ServletException if a servlet exception occurs
833 */
834 protected boolean processRoles(HttpServletRequest request,
835 HttpServletResponse response, ActionMapping mapping)
836 throws IOException, ServletException {
837
838 String[] roles = mapping.getRoleNames();
839
840 if ((roles == null) || (roles.length < 1)) {
841 return (true);
842 }
843
844
845 for (int i = 0; i < roles.length; i++) {
846 if (request.isUserInRole(roles[i])) {
847 if (log.isDebugEnabled()) {
848 log.debug(" User '" + request.getRemoteUser()
849 + "' has role '" + roles[i] + "', granting access");
850 }
851
852 return (true);
853 }
854 }
855
856
857 if (log.isDebugEnabled()) {
858 log.debug(" User '" + request.getRemoteUser()
859 + "' does not have any required role, denying access");
860 }
861
862 response.sendError(HttpServletResponse.SC_FORBIDDEN,
863 getInternal().getMessage("notAuthorized", mapping.getPath()));
864
865 return (false);
866 }
867
868 /***
869 * <p>If this request was not cancelled, and the request's {@link
870 * ActionMapping} has not disabled validation, call the
871 * <code>validate</code> method of the specified {@link ActionForm}, and
872 * forward to the input path if there were any errors. Return
873 * <code>true</code> if we should continue processing, or
874 * <code>false</code> if we have already forwarded control back to the
875 * input form.</p>
876 *
877 * @param request The servlet request we are processing
878 * @param response The servlet response we are creating
879 * @param form The ActionForm instance we are populating
880 * @param mapping The ActionMapping we are using
881 * @return <code>true</code> to continue normal processing;
882 * <code>false</code> if a response has been created.
883 * @throws IOException if an input/output error occurs
884 * @throws ServletException if a servlet exception occurs
885 * @throws InvalidCancelException if a cancellation is attempted
886 * without the proper action configuration.
887 */
888 protected boolean processValidate(HttpServletRequest request,
889 HttpServletResponse response, ActionForm form, ActionMapping mapping)
890 throws IOException, ServletException, InvalidCancelException {
891 if (form == null) {
892 return (true);
893 }
894
895
896 if (!mapping.getValidate()) {
897 return (true);
898 }
899
900
901
902
903
904 if (request.getAttribute(Globals.CANCEL_KEY) != null) {
905 if (mapping.getCancellable()) {
906 if (log.isDebugEnabled()) {
907 log.debug(" Cancelled transaction, skipping validation");
908 }
909 return (true);
910 } else {
911 request.removeAttribute(Globals.CANCEL_KEY);
912 throw new InvalidCancelException();
913 }
914 }
915
916
917 if (log.isDebugEnabled()) {
918 log.debug(" Validating input form properties");
919 }
920
921 ActionMessages errors = form.validate(mapping, request);
922
923 if ((errors == null) || errors.isEmpty()) {
924 if (log.isTraceEnabled()) {
925 log.trace(" No errors detected, accepting input");
926 }
927
928 return (true);
929 }
930
931
932 if (form.getMultipartRequestHandler() != null) {
933 if (log.isTraceEnabled()) {
934 log.trace(" Rolling back multipart request");
935 }
936
937 form.getMultipartRequestHandler().rollback();
938 }
939
940
941 String input = mapping.getInput();
942
943 if (input == null) {
944 if (log.isTraceEnabled()) {
945 log.trace(" Validation failed but no input form available");
946 }
947
948 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
949 getInternal().getMessage("noInput", mapping.getPath()));
950
951 return (false);
952 }
953
954
955 if (log.isDebugEnabled()) {
956 log.debug(" Validation failed, returning to '" + input + "'");
957 }
958
959 request.setAttribute(Globals.ERROR_KEY, errors);
960
961 if (moduleConfig.getControllerConfig().getInputForward()) {
962 ForwardConfig forward = mapping.findForward(input);
963
964 processForwardConfig(request, response, forward);
965 } else {
966 internalModuleRelativeForward(input, request, response);
967 }
968
969 return (false);
970 }
971
972 /***
973 * <p>Do a module relative forward to specified URI using request
974 * dispatcher. URI is relative to the current module. The real URI is
975 * compute by prefixing the module name.</p> <p>This method is used
976 * internally and is not part of the public API. It is advised to not use
977 * it in subclasses. </p>
978 *
979 * @param uri Module-relative URI to forward to
980 * @param request Current page request
981 * @param response Current page response
982 * @throws IOException if an input/output error occurs
983 * @throws ServletException if a servlet exception occurs
984 * @since Struts 1.1
985 */
986 protected void internalModuleRelativeForward(String uri,
987 HttpServletRequest request, HttpServletResponse response)
988 throws IOException, ServletException {
989
990 uri = moduleConfig.getPrefix() + uri;
991
992
993
994 if (log.isDebugEnabled()) {
995 log.debug(" Delegating via forward to '" + uri + "'");
996 }
997
998 doForward(uri, request, response);
999 }
1000
1001 /***
1002 * <p>Do a module relative include to specified URI using request
1003 * dispatcher. URI is relative to the current module. The real URI is
1004 * compute by prefixing the module name.</p> <p>This method is used
1005 * internally and is not part of the public API. It is advised to not use
1006 * it in subclasses.</p>
1007 *
1008 * @param uri Module-relative URI to include
1009 * @param request Current page request
1010 * @param response Current page response
1011 * @throws IOException if an input/output error occurs
1012 * @throws ServletException if a servlet exception occurs
1013 * @since Struts 1.1
1014 */
1015 protected void internalModuleRelativeInclude(String uri,
1016 HttpServletRequest request, HttpServletResponse response)
1017 throws IOException, ServletException {
1018
1019 uri = moduleConfig.getPrefix() + uri;
1020
1021
1022
1023 if (log.isDebugEnabled()) {
1024 log.debug(" Delegating via include to '" + uri + "'");
1025 }
1026
1027 doInclude(uri, request, response);
1028 }
1029
1030 /***
1031 * <p>Do a forward to specified URI using a <code>RequestDispatcher</code>.
1032 * This method is used by all internal method needing to do a
1033 * forward.</p>
1034 *
1035 * @param uri Context-relative URI to forward to
1036 * @param request Current page request
1037 * @param response Current page response
1038 * @throws IOException if an input/output error occurs
1039 * @throws ServletException if a servlet exception occurs
1040 * @since Struts 1.1
1041 */
1042 protected void doForward(String uri, HttpServletRequest request,
1043 HttpServletResponse response)
1044 throws IOException, ServletException {
1045 RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);
1046
1047 if (rd == null) {
1048 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1049 getInternal().getMessage("requestDispatcher", uri));
1050
1051 return;
1052 }
1053
1054 rd.forward(request, response);
1055 }
1056
1057 /***
1058 * <p>Do an include of specified URI using a <code>RequestDispatcher</code>.
1059 * This method is used by all internal method needing to do an
1060 * include.</p>
1061 *
1062 * @param uri Context-relative URI to include
1063 * @param request Current page request
1064 * @param response Current page response
1065 * @throws IOException if an input/output error occurs
1066 * @throws ServletException if a servlet exception occurs
1067 * @since Struts 1.1
1068 */
1069 protected void doInclude(String uri, HttpServletRequest request,
1070 HttpServletResponse response)
1071 throws IOException, ServletException {
1072 RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);
1073
1074 if (rd == null) {
1075 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1076 getInternal().getMessage("requestDispatcher", uri));
1077
1078 return;
1079 }
1080
1081 rd.include(request, response);
1082 }
1083
1084
1085
1086 /***
1087 * <p>Return the <code>MessageResources</code> instance containing our
1088 * internal message strings.</p>
1089 *
1090 * @return The <code>MessageResources</code> instance containing our
1091 * internal message strings.
1092 */
1093 protected MessageResources getInternal() {
1094 return (servlet.getInternal());
1095 }
1096
1097 /***
1098 * <p>Return the <code>ServletContext</code> for the web application in
1099 * which we are running.</p>
1100 *
1101 * @return The <code>ServletContext</code> for the web application.
1102 */
1103 protected ServletContext getServletContext() {
1104 return (servlet.getServletContext());
1105 }
1106 }