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 java.io.IOException;
24 import java.io.Serializable;
25 import java.util.HashMap;
26 import java.util.Locale;
27
28 import jakarta.servlet.RequestDispatcher;
29 import jakarta.servlet.ServletContext;
30 import jakarta.servlet.ServletException;
31 import jakarta.servlet.http.HttpServletRequest;
32 import jakarta.servlet.http.HttpServletResponse;
33 import jakarta.servlet.http.HttpSession;
34
35 import org.apache.struts.Globals;
36 import org.apache.struts.config.ActionConfig;
37 import org.apache.struts.config.ExceptionConfig;
38 import org.apache.struts.config.ForwardConfig;
39 import org.apache.struts.config.ModuleConfig;
40 import org.apache.struts.upload.MultipartRequestWrapper;
41 import org.apache.struts.util.MessageResources;
42 import org.apache.struts.util.RequestUtils;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46
47
48
49
50
51
52
53
54
55
56 public class RequestProcessor implements Serializable {
57 private static final long serialVersionUID = -6430999735913386425L;
58
59
60
61
62
63
64
65 public static final String INCLUDE_PATH_INFO =
66 "jakarta.servlet.include.path_info";
67
68
69
70
71
72
73 public static final String INCLUDE_SERVLET_PATH =
74 "jakarta.servlet.include.servlet_path";
75
76
77
78
79 private transient final Logger log =
80 LoggerFactory.getLogger(RequestProcessor.class);
81
82
83
84
85
86
87
88
89 protected HashMap<String, Action> actions = new HashMap<>();
90
91
92
93
94
95 protected ModuleConfig moduleConfig = null;
96
97
98
99
100 protected ActionServlet servlet = null;
101
102
103
104
105
106
107 public void destroy() {
108 synchronized (this.actions) {
109 for (Action action : this.actions.values()) {
110 action.setServlet(null);
111 }
112
113 this.actions.clear();
114 }
115
116 this.servlet = null;
117 }
118
119
120
121
122
123
124
125
126 public void init(ActionServlet servlet, ModuleConfig moduleConfig)
127 throws ServletException {
128 synchronized (actions) {
129 actions.clear();
130 }
131
132 this.servlet = servlet;
133 this.moduleConfig = moduleConfig;
134 }
135
136
137
138
139
140
141
142
143
144
145
146 public void process(HttpServletRequest request, HttpServletResponse response)
147 throws IOException, ServletException {
148
149 request = processMultipart(request);
150
151
152 String path = processPath(request, response);
153
154 if (path == null) {
155 return;
156 }
157
158 log.debug("Processing a '{}' for path '{}'",
159 request.getMethod(), path);
160
161
162 processLocale(request, response);
163
164
165 processContent(request, response);
166 processNoCache(request, response);
167
168
169 if (!processPreprocess(request, response)) {
170 return;
171 }
172
173 this.processCachedMessages(request, response);
174
175
176 ActionMapping mapping = processMapping(request, response, path);
177
178 if (mapping == null) {
179 return;
180 }
181
182
183 if (!processRoles(request, response, mapping)) {
184 return;
185 }
186
187
188 ActionForm form = processActionForm(request, response, mapping);
189
190 processPopulate(request, response, form, mapping);
191
192
193 try {
194 if (!processValidate(request, response, form, mapping)) {
195 return;
196 }
197 } catch (InvalidCancelException e) {
198 ActionForward forward = processException(request, response, e, form, mapping);
199 processForwardConfig(request, response, forward);
200 return;
201 } catch (IOException e) {
202 throw e;
203 } catch (ServletException e) {
204 throw e;
205 }
206
207
208 if (!processForward(request, response, mapping)) {
209 return;
210 }
211
212 if (!processInclude(request, response, mapping)) {
213 return;
214 }
215
216
217 Action action = processActionCreate(request, response, mapping);
218
219 if (action == null) {
220 return;
221 }
222
223
224 ActionForward forward =
225 processActionPerform(request, response, action, form, mapping);
226
227
228 processForwardConfig(request, response, forward);
229 }
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244 protected Action processActionCreate(HttpServletRequest request,
245 HttpServletResponse response, ActionMapping mapping)
246 throws IOException {
247
248 String className = mapping.getType();
249
250 log.debug(" Looking for Action instance for class {}", className);
251
252
253
254
255 Action instance;
256
257 synchronized (actions) {
258
259 instance = actions.get(className);
260
261 if (instance != null) {
262 log.trace(" Returning existing Action instance");
263
264 return (instance);
265 }
266
267
268 log.trace(" Creating new Action instance");
269
270 try {
271 instance = (Action) RequestUtils.applicationInstance(className);
272
273
274
275 } catch (Exception e) {
276 log.atError()
277 .setMessage(() -> getInternal().getMessage("actionCreate",
278 mapping.getPath(), mapping.toString()))
279 .setCause(e).log();
280
281 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
282 getInternal().getMessage("actionCreate", mapping.getPath()));
283
284 return (null);
285 }
286
287 actions.put(className, instance);
288
289 if (instance.getServlet() == null) {
290 instance.setServlet(this.servlet);
291 }
292 }
293
294 return (instance);
295 }
296
297
298
299
300
301
302
303
304
305
306
307
308 protected ActionForm processActionForm(HttpServletRequest request,
309 HttpServletResponse response, ActionMapping mapping) {
310
311 ActionForm instance =
312 RequestUtils.createActionForm(request, mapping, moduleConfig,
313 servlet);
314
315 if (instance == null) {
316 return (null);
317 }
318
319
320 log.debug(" Storing ActionForm bean instance in scope '{}' "
321 + "under attribute key '{}'",
322 mapping.getScope(), mapping.getAttribute());
323
324 if ("request".equals(mapping.getScope())) {
325 request.setAttribute(mapping.getAttribute(), instance);
326 } else {
327 HttpSession session = request.getSession();
328
329 session.setAttribute(mapping.getAttribute(), instance);
330 }
331
332 return (instance);
333 }
334
335
336
337
338
339
340
341
342
343
344
345
346 protected void processForwardConfig(HttpServletRequest request,
347 HttpServletResponse response, ForwardConfig forward)
348 throws IOException, ServletException {
349 if (forward == null) {
350 return;
351 }
352
353 log.debug("processForwardConfig({})", forward);
354
355 String forwardPath = forward.getPath();
356 String uri;
357
358
359 String actionIdPath = RequestUtils.actionIdURL(forward, request, servlet);
360 if (actionIdPath != null) {
361 forwardPath = actionIdPath;
362 ForwardConfig actionIdForward = new ForwardConfig(forward);
363 actionIdForward.setPath(actionIdPath);
364 forward = actionIdForward;
365 }
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
394
395
396
397
398
399
400
401
402
403
404
405
406
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
421
422
423
424
425
426
427
428
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
460
461
462
463
464
465
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
479
480
481
482
483
484
485
486
487
488
489
490
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.atWarn().log(() -> 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
527
528
529
530
531
532
533
534
535
536
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
549 String actionIdPath = RequestUtils.actionIdURL(forward, this.moduleConfig, this.servlet);
550 if (actionIdPath != null) {
551 forward = actionIdPath;
552 }
553
554 internalModuleRelativeForward(forward, request, response);
555
556 return (false);
557 }
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572 protected boolean processInclude(HttpServletRequest request,
573 HttpServletResponse response, ActionMapping mapping)
574 throws IOException, ServletException {
575
576 String include = mapping.getInclude();
577
578 if (include == null) {
579 return (true);
580 }
581
582
583 String actionIdPath = RequestUtils.actionIdURL(include, this.moduleConfig, this.servlet);
584 if (actionIdPath != null) {
585 include = actionIdPath;
586 }
587
588 internalModuleRelativeInclude(include, request, response);
589
590 return (false);
591 }
592
593
594
595
596
597
598
599
600
601
602 protected void processLocale(HttpServletRequest request,
603 HttpServletResponse response) {
604
605 if (!moduleConfig.getControllerConfig().getLocale()) {
606 return;
607 }
608
609
610 HttpSession session = request.getSession();
611
612 if (session.getAttribute(Globals.LOCALE_KEY) != null) {
613 return;
614 }
615
616
617 Locale locale = request.getLocale();
618
619 if (locale != null) {
620 log.debug(" Setting user locale '{}'", locale);
621
622 session.setAttribute(Globals.LOCALE_KEY, locale);
623 }
624 }
625
626
627
628
629
630
631
632
633
634
635
636
637
638 protected ActionMapping processMapping(HttpServletRequest request,
639 HttpServletResponse response, String path)
640 throws IOException {
641
642 ActionMapping mapping =
643 (ActionMapping) moduleConfig.findActionConfig(path);
644
645
646 if (mapping != null) {
647 request.setAttribute(Globals.MAPPING_KEY, mapping);
648
649 return (mapping);
650 }
651
652
653 ActionConfig[] configs = moduleConfig.findActionConfigs();
654
655 for (int i = 0; i < configs.length; i++) {
656 if (configs[i].getUnknown()) {
657 mapping = (ActionMapping) configs[i];
658 request.setAttribute(Globals.MAPPING_KEY, mapping);
659
660 return (mapping);
661 }
662 }
663
664
665 String msg = getInternal().getMessage("processInvalid");
666
667 log.error("{} {}", msg, path);
668 response.sendError(HttpServletResponse.SC_NOT_FOUND, msg);
669
670 return null;
671 }
672
673
674
675
676
677
678
679
680
681 protected HttpServletRequest processMultipart(HttpServletRequest request) {
682 if (!"POST".equalsIgnoreCase(request.getMethod())) {
683 return (request);
684 }
685
686 String contentType = request.getContentType();
687
688 if ((contentType != null)
689 && contentType.startsWith("multipart/form-data")) {
690 return (new MultipartRequestWrapper(request));
691 } else {
692 return (request);
693 }
694 }
695
696
697
698
699
700
701
702
703
704
705 protected void processNoCache(HttpServletRequest request,
706 HttpServletResponse response) {
707 if (moduleConfig.getControllerConfig().getNocache()) {
708 response.setHeader("Pragma", "No-cache");
709 response.setHeader("Cache-Control", "no-cache,no-store,max-age=0");
710 response.setDateHeader("Expires", 1);
711 }
712 }
713
714
715
716
717
718
719
720
721
722
723
724
725 protected String processPath(HttpServletRequest request,
726 HttpServletResponse response)
727 throws IOException {
728 String path;
729
730
731 if (request.getAttribute(Globals.ORIGINAL_URI_KEY) == null) {
732 request.setAttribute(Globals.ORIGINAL_URI_KEY, request.getServletPath());
733 }
734
735
736 path = (String) request.getAttribute(INCLUDE_PATH_INFO);
737
738 if (path == null) {
739 path = request.getPathInfo();
740 }
741
742 if ((path != null) && (path.length() > 0)) {
743 return (path);
744 }
745
746
747 path = (String) request.getAttribute(INCLUDE_SERVLET_PATH);
748
749 if (path == null) {
750 path = request.getServletPath();
751 }
752
753 String prefix = moduleConfig.getPrefix();
754
755 if (!path.startsWith(prefix)) {
756 String msg = getInternal().getMessage("processPath");
757
758 log.error("{} {}", msg, request.getRequestURI());
759 response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
760
761 return null;
762 }
763
764 path = path.substring(prefix.length());
765
766 int slash = path.lastIndexOf("/");
767 int period = path.lastIndexOf(".");
768
769 if ((period >= 0) && (period > slash)) {
770 path = path.substring(0, period);
771 }
772
773 return (path);
774 }
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789 protected void processPopulate(HttpServletRequest request,
790 HttpServletResponse response, ActionForm form, ActionMapping mapping)
791 throws ServletException {
792 if (form == null) {
793 return;
794 }
795
796
797 log.debug(" Populating bean properties from this request");
798
799 form.setServlet(this.servlet);
800 form.reset(mapping, request);
801
802 if (mapping.getMultipartClass() != null) {
803 request.setAttribute(Globals.MULTIPART_KEY,
804 mapping.getMultipartClass());
805 }
806
807 RequestUtils.populate(form, mapping.getPrefix(), mapping.getSuffix(),
808 request);
809
810
811 if ((request.getParameter(Globals.CANCEL_PROPERTY) != null)
812 || (request.getParameter(Globals.CANCEL_PROPERTY_X) != null)) {
813 request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
814 }
815 }
816
817
818
819
820
821
822
823
824
825
826
827
828 protected boolean processPreprocess(HttpServletRequest request,
829 HttpServletResponse response) {
830 return (true);
831 }
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847 protected boolean processRoles(HttpServletRequest request,
848 HttpServletResponse response, ActionMapping mapping)
849 throws IOException, ServletException {
850
851 String[] roles = mapping.getRoleNames();
852
853 if ((roles == null) || (roles.length < 1)) {
854 return (true);
855 }
856
857
858 for (int i = 0; i < roles.length; i++) {
859 if (request.isUserInRole(roles[i])) {
860 log.debug(" User '{}' has role '{}', granting access",
861 request.getRemoteUser(), roles[i]);
862
863 return (true);
864 }
865 }
866
867
868 log.debug(" User '{}' does not have any required role, denying access",
869 request.getRemoteUser());
870
871 response.sendError(HttpServletResponse.SC_FORBIDDEN,
872 getInternal().getMessage("notAuthorized", mapping.getPath()));
873
874 return (false);
875 }
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897 protected boolean processValidate(HttpServletRequest request,
898 HttpServletResponse response, ActionForm form, ActionMapping mapping)
899 throws IOException, ServletException, InvalidCancelException {
900 if (form == null) {
901 return (true);
902 }
903
904
905 if (!mapping.getValidate()) {
906 return (true);
907 }
908
909
910
911
912
913 if (request.getAttribute(Globals.CANCEL_KEY) != null) {
914 if (mapping.getCancellable()) {
915 log.debug(" Cancelled transaction, skipping validation");
916 return (true);
917 } else {
918 request.removeAttribute(Globals.CANCEL_KEY);
919 throw new InvalidCancelException();
920 }
921 }
922
923
924 log.debug(" Validating input form properties");
925
926 ActionMessages errors = form.validate(mapping, request);
927
928 if ((errors == null) || errors.isEmpty()) {
929 log.trace(" No errors detected, accepting input");
930
931 return (true);
932 }
933
934
935 if (form.getMultipartRequestHandler() != null) {
936 log.trace(" Rolling back multipart request");
937
938 form.getMultipartRequestHandler().rollback();
939 }
940
941
942 String input = mapping.getInput();
943
944 if (input == null) {
945 log.trace(" Validation failed but no input form available");
946
947 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
948 getInternal().getMessage("noInput", mapping.getPath()));
949
950 return (false);
951 }
952
953
954 log.debug(" Validation failed, returning to '{}'", input);
955
956 request.setAttribute(Globals.ERROR_KEY, errors);
957
958 if (moduleConfig.getControllerConfig().getInputForward()) {
959 ForwardConfig forward = mapping.findForward(input);
960
961 processForwardConfig(request, response, forward);
962 } else {
963 internalModuleRelativeForward(input, request, response);
964 }
965
966 return (false);
967 }
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983 protected void internalModuleRelativeForward(String uri,
984 HttpServletRequest request, HttpServletResponse response)
985 throws IOException, ServletException {
986
987 uri = moduleConfig.getPrefix() + uri;
988
989
990
991 log.debug(" Delegating via forward to '{}'", uri);
992
993 doForward(uri, request, response);
994 }
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010 protected void internalModuleRelativeInclude(String uri,
1011 HttpServletRequest request, HttpServletResponse response)
1012 throws IOException, ServletException {
1013
1014 uri = moduleConfig.getPrefix() + uri;
1015
1016
1017
1018 log.debug(" Delegating via include to '{}'", uri);
1019
1020 doInclude(uri, request, response);
1021 }
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035 protected void doForward(String uri, HttpServletRequest request,
1036 HttpServletResponse response)
1037 throws IOException, ServletException {
1038 RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);
1039
1040 if (rd == null) {
1041 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1042 getInternal().getMessage("requestDispatcher", uri));
1043
1044 return;
1045 }
1046
1047 rd.forward(request, response);
1048 }
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062 protected void doInclude(String uri, HttpServletRequest request,
1063 HttpServletResponse response)
1064 throws IOException, ServletException {
1065 RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);
1066
1067 if (rd == null) {
1068 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1069 getInternal().getMessage("requestDispatcher", uri));
1070
1071 return;
1072 }
1073
1074 rd.include(request, response);
1075 }
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086 protected MessageResources getInternal() {
1087 return (servlet.getInternal());
1088 }
1089
1090
1091
1092
1093
1094
1095
1096 protected ServletContext getServletContext() {
1097 return (servlet.getServletContext());
1098 }
1099 }