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