1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.kns.web.struts.action;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.apache.log4j.Logger;
20 import org.apache.log4j.MDC;
21 import org.apache.ojb.broker.OptimisticLockException;
22 import org.apache.struts.Globals;
23 import org.apache.struts.action.Action;
24 import org.apache.struts.action.ActionForm;
25 import org.apache.struts.action.ActionForward;
26 import org.apache.struts.action.ActionMapping;
27 import org.apache.struts.action.InvalidCancelException;
28 import org.apache.struts.action.RequestProcessor;
29 import org.apache.struts.config.FormBeanConfig;
30 import org.apache.struts.config.ForwardConfig;
31 import org.apache.struts.util.RequestUtils;
32 import org.kuali.rice.core.api.util.RiceConstants;
33 import org.kuali.rice.core.api.util.RiceKeyConstants;
34 import org.kuali.rice.kns.exception.FileUploadLimitExceededException;
35 import org.kuali.rice.kns.service.KNSServiceLocator;
36 import org.kuali.rice.kns.service.SessionDocumentService;
37 import org.kuali.rice.kns.util.ErrorContainer;
38 import org.kuali.rice.kns.util.InfoContainer;
39 import org.kuali.rice.kns.util.KNSConstants;
40 import org.kuali.rice.kns.util.KNSGlobalVariables;
41 import org.kuali.rice.kns.util.WarningContainer;
42 import org.kuali.rice.kns.util.WebUtils;
43 import org.kuali.rice.kns.web.EditablePropertiesHistoryHolder;
44 import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
45 import org.kuali.rice.kns.web.struts.form.KualiForm;
46 import org.kuali.rice.kns.web.struts.form.pojo.PojoForm;
47 import org.kuali.rice.krad.UserSession;
48 import org.kuali.rice.krad.document.Document;
49 import org.kuali.rice.krad.exception.ValidationException;
50 import org.kuali.rice.krad.service.KRADServiceLocatorInternal;
51 import org.kuali.rice.krad.util.GlobalVariables;
52 import org.kuali.rice.krad.util.KRADConstants;
53 import org.kuali.rice.krad.util.KRADUtils;
54 import org.kuali.rice.krad.util.MessageMap;
55 import org.springframework.transaction.PlatformTransactionManager;
56 import org.springframework.transaction.TransactionStatus;
57 import org.springframework.transaction.support.TransactionCallback;
58 import org.springframework.transaction.support.TransactionTemplate;
59 import org.springmodules.orm.ojb.OjbOperationException;
60
61 import javax.servlet.ServletException;
62 import javax.servlet.http.HttpServletRequest;
63 import javax.servlet.http.HttpServletResponse;
64 import javax.servlet.http.HttpSession;
65 import java.io.IOException;
66
67
68
69
70
71
72 public class KualiRequestProcessor extends RequestProcessor {
73
74 private static final String MDC_DOC_ID = "docId";
75 private static final String PREVIOUS_REQUEST_EDITABLE_PROPERTIES_GUID_PARAMETER_NAME = "actionEditablePropertiesGuid";
76
77 private static Logger LOG = Logger.getLogger(KualiRequestProcessor.class);
78
79 private SessionDocumentService sessionDocumentService;
80 private PlatformTransactionManager transactionManager;
81
82 @Override
83 public void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
84 if ( LOG.isInfoEnabled() ) {
85 LOG.info(new StringBuffer("Started processing request: '").append(request.getRequestURI()).append("' w/ query string: '").append(request.getQueryString()).append("'"));
86 }
87
88 try {
89 strutsProcess(request, response);
90 } catch (FileUploadLimitExceededException e) {
91 ActionForward actionForward = processException(request, response, e, e.getActionForm(), e.getActionMapping());
92 processForwardConfig(request, response, actionForward);
93 } finally {
94 KNSGlobalVariables.setKualiForm(null);
95 }
96
97 try {
98 ActionForm form = WebUtils.getKualiForm(request);
99
100 if (form != null && form instanceof KualiDocumentFormBase) {
101 String docId = ((KualiDocumentFormBase) form).getDocId();
102 if (docId != null) { MDC.put(MDC_DOC_ID, docId); }
103 }
104
105 String refreshCaller = request.getParameter(KRADConstants.REFRESH_CALLER);
106 if (form!=null && KualiDocumentFormBase.class.isAssignableFrom(form.getClass())
107 && !KRADConstants.QUESTION_REFRESH.equalsIgnoreCase(refreshCaller)) {
108 KualiDocumentFormBase docForm = (KualiDocumentFormBase) form;
109 Document document = docForm.getDocument();
110 String docFormKey = docForm.getFormKey();
111
112 UserSession userSession = (UserSession) request.getSession().getAttribute(KRADConstants.USER_SESSION_KEY);
113
114 if (WebUtils.isDocumentSession(document, docForm)) {
115 getSessionDocumentService().setDocumentForm(docForm, userSession, request.getRemoteAddr());
116 }
117
118 Boolean exitingDocument = (Boolean) request.getAttribute(KRADConstants.EXITING_DOCUMENT);
119
120 if (exitingDocument != null && exitingDocument.booleanValue()) {
121
122
123 getSessionDocumentService().purgeDocumentForm(docForm.getDocument().getDocumentNumber(), docFormKey, userSession, request.getRemoteAddr());
124 }
125 }
126
127 if ( LOG.isInfoEnabled() ) {
128 LOG.info(new StringBuffer("Finished processing request: '").append(request.getRequestURI()).append("' w/ query string: '").append(request.getQueryString()).append("'"));
129 }
130
131 } finally {
132
133 MDC.remove(MDC_DOC_ID);
134 }
135
136 }
137
138 @Override
139 protected boolean processPreprocess(HttpServletRequest request, HttpServletResponse response) {
140 final UserSession session = KRADUtils.getUserSessionFromRequest(request);
141
142 if (session == null) {
143 throw new IllegalStateException("the user session has not been established");
144 }
145 GlobalVariables.setUserSession(session);
146 KNSGlobalVariables.clear();
147 return true;
148 }
149
150
151
152
153
154
155
156
157
158
159
160
161 public void strutsProcess(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
162
163
164 request = processMultipart(request);
165
166
167 String path = processPath(request, response);
168 if (path == null) {
169 return;
170 }
171
172 if (log.isDebugEnabled()) {
173 log.debug("Processing a '" + request.getMethod() +
174 "' for path '" + path + "'");
175 }
176
177
178 processLocale(request, response);
179
180
181 processContent(request, response);
182 processNoCache(request, response);
183
184
185 if (!processPreprocess(request, response)) {
186 return;
187 }
188
189 this.processCachedMessages(request, response);
190
191
192 ActionMapping mapping = processMapping(request, response, path);
193 if (mapping == null) {
194 return;
195 }
196
197
198 if (!processRoles(request, response, mapping)) {
199 return;
200 }
201
202 processFormActionAndForward(request, response, mapping);
203
204 }
205
206 public void processFormActionAndForward(final HttpServletRequest request, final HttpServletResponse response, final ActionMapping mapping) throws ServletException, IOException {
207 TransactionTemplate template = new TransactionTemplate(getTransactionManager());
208 try {
209 template.execute(new TransactionCallback() {
210 @Override
211 public Object doInTransaction(TransactionStatus status) {
212 try {
213
214 ActionForm form = processActionForm(request, response, mapping);
215 processPopulate(request, response, form, mapping);
216
217
218 try {
219 if (!processValidate(request, response, form, mapping)) {
220 return null;
221 }
222 } catch (InvalidCancelException e) {
223 ActionForward forward = processException(request, response, e, form, mapping);
224 processForwardConfig(request, response, forward);
225 return null;
226 } catch (IOException e) {
227 throw e;
228 } catch (ServletException e) {
229 throw e;
230 }
231
232
233 if (!processForward(request, response, mapping)) {
234 return null;
235 }
236
237 if (!processInclude(request, response, mapping)) {
238 return null;
239 }
240
241
242 Action action = processActionCreate(request, response, mapping);
243 if (action == null) {
244 return null;
245 }
246
247
248 ActionForward forward = processActionPerform(request, response, action, form, mapping);
249
250
251 processForwardConfig(request, response, forward);
252 } catch (Exception e) {
253
254
255
256
257
258
259
260
261 throw new WrappedRuntimeException(e);
262 }
263 return null;
264 }
265 });
266 } catch (WrappedRuntimeException wre) {
267 throw new RuntimeException(wre.getCause());
268 }
269 }
270
271
272
273
274
275
276
277
278
279 private String getDocumentNumber(HttpServletRequest request) {
280 String documentNumber = request.getParameter(KRADConstants.DOCUMENT_DOCUMENT_NUMBER);
281
282
283 if (documentNumber == null) {
284 documentNumber = request.getParameter(KRADConstants.DOC_NUM);
285 }
286
287 if (documentNumber == null) {
288 documentNumber = request.getParameter("documentId");
289 }
290
291 return documentNumber;
292 }
293
294
295
296
297
298 @Override
299 protected void processPopulate(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping) throws ServletException {
300 if (form instanceof KualiForm) {
301
302
303
304
305
306 KNSGlobalVariables.setKualiForm((KualiForm) form);
307 }
308
309
310 if (!(form instanceof PojoForm)) {
311 super.processPopulate(request, response, form, mapping);
312 return;
313 }
314
315 final String previousRequestGuid = request.getParameter(KualiRequestProcessor.PREVIOUS_REQUEST_EDITABLE_PROPERTIES_GUID_PARAMETER_NAME);
316
317 ((PojoForm)form).clearEditablePropertyInformation();
318 ((PojoForm)form).registerStrutsActionMappingScope(mapping.getScope());
319
320 String multipart = mapping.getMultipartClass();
321 if (multipart != null) {
322 request.setAttribute(Globals.MULTIPART_KEY, multipart);
323 }
324
325 form.setServlet(this.servlet);
326 form.reset(mapping, request);
327
328 ((PojoForm)form).setPopulateEditablePropertiesGuid(previousRequestGuid);
329
330 ((PojoForm) form).populate(request);
331 request.setAttribute("UnconvertedValues", ((PojoForm) form).getUnconvertedValues().keySet());
332 request.setAttribute("UnconvertedHash", ((PojoForm) form).getUnconvertedValues());
333 }
334
335
336
337
338
339 @Override
340 protected boolean processValidate(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping) throws IOException, ServletException, InvalidCancelException {
341
342
343 if (GlobalVariables.getMessageMap().hasNoErrors()) {
344 if (form == null) {
345 return (true);
346 }
347
348 if (request.getAttribute(Globals.CANCEL_KEY) != null) {
349 if (LOG.isDebugEnabled()) {
350 LOG.debug(" Cancelled transaction, skipping validation");
351 }
352 return (true);
353 }
354
355
356 if (!mapping.getValidate()) {
357 return (true);
358 }
359
360
361 super.processValidate(request, response, form, mapping);
362 }
363
364 publishMessages(request);
365 if (!GlobalVariables.getMessageMap().hasNoErrors()) {
366
367 if (form.getMultipartRequestHandler() != null) {
368 if (LOG.isDebugEnabled()) {
369 LOG.debug(" Rolling back multipart request");
370 }
371 form.getMultipartRequestHandler().rollback();
372 }
373
374
375 if (form instanceof PojoForm) {
376 ((PojoForm) form).processValidationFail();
377 }
378
379
380 String input = mapping.getInput();
381 if (input == null) {
382 if (LOG.isDebugEnabled()) {
383 LOG.debug(" Validation failed but no input form available");
384 }
385 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, getInternal().getMessage("noInput", mapping.getPath()));
386 return (false);
387 }
388
389 if (moduleConfig.getControllerConfig().getInputForward()) {
390 ForwardConfig forward = mapping.findForward(input);
391 processForwardConfig(request, response, forward);
392 } else {
393 internalModuleRelativeForward(input, request, response);
394 }
395
396 return (false);
397 }
398 return true;
399 }
400
401
402
403
404
405 @Override
406 protected ActionForm processActionForm(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) {
407
408 String documentNumber = getDocumentNumber(request);
409 if (documentNumber != null) { MDC.put(MDC_DOC_ID, documentNumber); }
410
411 UserSession userSession = (UserSession) request.getSession().getAttribute(KRADConstants.USER_SESSION_KEY);
412
413 String docFormKey = request.getParameter(KRADConstants.DOC_FORM_KEY);
414 String methodToCall = request.getParameter(KRADConstants.DISPATCH_REQUEST_PARAMETER);
415 String refreshCaller = request.getParameter(KRADConstants.REFRESH_CALLER);
416
417 String documentWebScope = request.getParameter(KRADConstants.DOCUMENT_WEB_SCOPE);
418
419 if (mapping.getPath().startsWith(KRADConstants.REFRESH_MAPPING_PREFIX) || KRADConstants.RETURN_METHOD_TO_CALL.equalsIgnoreCase(methodToCall) ||
420 KRADConstants.QUESTION_REFRESH.equalsIgnoreCase(refreshCaller) || KRADConstants.TEXT_AREA_REFRESH.equalsIgnoreCase(refreshCaller) || KRADConstants
421 .SESSION_SCOPE.equalsIgnoreCase(documentWebScope)) {
422 ActionForm form = null;
423
424 GlobalVariables.getUserSession().removeObjectsByPrefix(KRADConstants.SEARCH_LIST_KEY_PREFIX);
425
426
427
428
429 if (userSession.retrieveObject(docFormKey) != null) {
430 LOG.debug("getDecomentForm KualiDocumentFormBase from session");
431 form = (ActionForm) userSession.retrieveObject(docFormKey);
432 } else if (StringUtils.isNotBlank(documentNumber)) {
433 form = getSessionDocumentService().getDocumentForm(documentNumber, docFormKey, userSession, request.getRemoteAddr());
434 }
435 request.setAttribute(mapping.getAttribute(), form);
436 if (!KRADConstants.SESSION_SCOPE.equalsIgnoreCase(documentWebScope)) {
437 userSession.removeObject(docFormKey);
438 }
439
440
441
442 String contentType = request.getContentType();
443 String method = request.getMethod();
444 if (("POST".equalsIgnoreCase(method) && contentType != null && contentType.startsWith("multipart/form-data"))) {
445
446
447 WebUtils.getMultipartParameters(request, null, form, mapping);
448 }
449
450 if (form != null) {
451 return form;
452 }
453 }
454
455
456
457
458
459
460 ActionForm form = super.processActionForm(request, response, mapping);
461
462
463 String contentType = request.getContentType();
464 String method = request.getMethod();
465
466 if ("GET".equalsIgnoreCase(method) && StringUtils.isNotBlank(methodToCall) && form instanceof PojoForm &&
467 ((PojoForm) form).getMethodToCallsToBypassSessionRetrievalForGETRequests().contains(methodToCall)) {
468 return createNewActionForm(mapping, request);
469 }
470
471
472
473
474
475
476
477
478
479
480
481 if (("POST".equalsIgnoreCase(method) && contentType != null && contentType.startsWith("multipart/form-data"))) {
482 WebUtils.getMultipartParameters(request, null, form, mapping);
483 docFormKey = request.getParameter(KRADConstants.DOC_FORM_KEY);
484 documentWebScope = request.getParameter(KRADConstants.DOCUMENT_WEB_SCOPE);
485
486 documentNumber = getDocumentNumber(request);
487
488 if (KRADConstants.SESSION_SCOPE.equalsIgnoreCase(documentWebScope) ||
489 (form instanceof KualiDocumentFormBase && WebUtils
490 .isDocumentSession(((KualiDocumentFormBase) form).getDocument(),
491 (KualiDocumentFormBase) form))) {
492
493 Object userSessionObject = userSession.retrieveObject(docFormKey);
494 if ( userSessionObject != null && userSessionObject instanceof ActionForm ) {
495 LOG.debug("getDocumentForm KualiDocumentFormBase from session");
496 form = (ActionForm) userSessionObject;
497 } else {
498 ActionForm tempForm = getSessionDocumentService().getDocumentForm(documentNumber, docFormKey, userSession, request.getRemoteAddr());
499 if ( tempForm != null ) {
500 form = tempForm;
501 }
502 }
503
504 request.setAttribute(mapping.getAttribute(), form);
505 if (form != null) {
506 return form;
507 }
508 }
509 }
510 return form;
511 }
512
513
514
515
516
517
518
519
520
521
522
523 @Override
524 protected ActionForward processActionPerform(final HttpServletRequest request, final HttpServletResponse response, final Action action, final ActionForm form, final ActionMapping mapping) throws IOException, ServletException {
525 try {
526
527 ActionForward forward = action.execute(mapping, form, request, response);
528
529 publishMessages(request);
530 saveMessages(request);
531 saveAuditErrors(request);
532
533 if (form instanceof PojoForm) {
534 if (((PojoForm)form).getEditableProperties() == null
535 || ((PojoForm)form).getEditableProperties().isEmpty()) {
536 EditablePropertiesHistoryHolder holder = (EditablePropertiesHistoryHolder) GlobalVariables.getUserSession().getObjectMap().get(
537 KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME);
538 if (holder == null) {
539 holder = new EditablePropertiesHistoryHolder();
540 }
541
542 final String guid = holder.addEditablePropertiesToHistory(((PojoForm)form).getEditableProperties());
543 ((PojoForm)form).setActionEditablePropertiesGuid(guid);
544 GlobalVariables.getUserSession().addObject(KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME, holder);
545 }
546 }
547
548 return forward;
549
550 } catch (Exception e) {
551 if (e instanceof WrappedRuntimeException) {
552 e = (Exception) e.getCause();
553 }
554 if (e instanceof ValidationException) {
555
556 if (GlobalVariables.getMessageMap().hasNoErrors()) {
557
558 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, RiceKeyConstants.ERROR_CUSTOM, e.getMessage());
559 }
560
561 if (form instanceof PojoForm) {
562 if (((PojoForm)form).getEditableProperties() == null
563 || ((PojoForm)form).getEditableProperties().isEmpty()) {
564 EditablePropertiesHistoryHolder holder = (EditablePropertiesHistoryHolder) GlobalVariables.getUserSession().getObjectMap().get(
565 KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME);
566 if (holder == null) {
567 holder = new EditablePropertiesHistoryHolder();
568 }
569
570 final String guid = holder.addEditablePropertiesToHistory(((PojoForm)form).getEditableProperties());
571 ((PojoForm)form).setActionEditablePropertiesGuid(guid);
572 GlobalVariables.getUserSession().addObject(KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME, holder);
573 }
574 }
575
576 publishMessages(request);
577 return mapping.findForward(RiceConstants.MAPPING_BASIC);
578 }
579
580 publishMessages(request);
581
582 return (processException(request, response, e, form, mapping));
583 }
584 }
585
586
587
588
589
590
591
592 @Override
593 protected ActionForward processException(HttpServletRequest request, HttpServletResponse response, Exception exception, ActionForm form, ActionMapping mapping) throws IOException, ServletException {
594 ActionForward actionForward = null;
595
596 try {
597 actionForward = super.processException(request, response, exception, form, mapping);
598 } catch (IOException e) {
599 logException(e);
600 throw e;
601 } catch (ServletException e) {
602
603 Throwable rootCause = e.getRootCause();
604 if (rootCause instanceof OjbOperationException) {
605 OjbOperationException ooe = (OjbOperationException) rootCause;
606
607 Throwable subcause = ooe.getCause();
608 if (subcause instanceof OptimisticLockException) {
609 OptimisticLockException ole = (OptimisticLockException) subcause;
610
611 StringBuffer message = new StringBuffer(e.getMessage());
612
613 Object sourceObject = ole.getSourceObject();
614 if (sourceObject != null) {
615 message.append(" (sourceObject is ");
616 message.append(sourceObject.getClass().getName());
617 message.append(")");
618 }
619
620 e = new ServletException(message.toString(), rootCause);
621 }
622 }
623
624 logException(e);
625 throw e;
626 }
627 return actionForward;
628 }
629
630 private void logException(Exception e) {
631 LOG.error("unhandled exception thrown by KualiRequestProcessor.processActionPerform", e);
632 }
633
634
635
636
637
638 private void publishMessages(HttpServletRequest request) {
639 MessageMap errorMap = GlobalVariables.getMessageMap();
640 if (!errorMap.hasNoErrors()) {
641 ErrorContainer errorContainer = new ErrorContainer(errorMap);
642
643 request.setAttribute("ErrorContainer", errorContainer);
644 request.setAttribute(Globals.ERROR_KEY, errorContainer.getRequestErrors());
645 request.setAttribute("ErrorPropertyList", errorContainer.getErrorPropertyList());
646 }
647
648 if (errorMap.hasWarnings()) {
649 WarningContainer warningsContainer = new WarningContainer(errorMap);
650
651 request.setAttribute("WarningContainer", warningsContainer);
652 request.setAttribute("WarningActionMessages", warningsContainer.getRequestMessages());
653 request.setAttribute("WarningPropertyList", warningsContainer.getMessagePropertyList());
654 }
655
656 if (errorMap.hasInfo()) {
657 InfoContainer infoContainer = new InfoContainer(errorMap);
658
659 request.setAttribute("InfoContainer", infoContainer);
660 request.setAttribute("InfoActionMessages", infoContainer.getRequestMessages());
661 request.setAttribute("InfoPropertyList", infoContainer.getMessagePropertyList());
662 }
663 }
664
665
666
667
668
669 private void saveMessages(HttpServletRequest request) {
670 if (!KNSGlobalVariables.getMessageList().isEmpty()) {
671 request.setAttribute(KRADConstants.GLOBAL_MESSAGES, KNSGlobalVariables.getMessageList().toActionMessages());
672 }
673 }
674
675
676
677
678
679 private void saveAuditErrors(HttpServletRequest request) {
680 if (!KNSGlobalVariables.getAuditErrorMap().isEmpty()) {
681 request.setAttribute(KNSConstants.AUDIT_ERRORS, KNSGlobalVariables.getAuditErrorMap());
682 }
683 }
684
685
686
687
688
689 @SuppressWarnings("serial")
690 private static class WrappedRuntimeException extends RuntimeException {
691 public WrappedRuntimeException(Exception e) {
692 super(e);
693 }
694 }
695
696
697
698
699 public SessionDocumentService getSessionDocumentService() {
700 if ( sessionDocumentService == null ) {
701 sessionDocumentService = KNSServiceLocator.getSessionDocumentService();
702 }
703 return this.sessionDocumentService;
704 }
705
706
707
708
709 public PlatformTransactionManager getTransactionManager() {
710 if ( transactionManager == null ) {
711 transactionManager = KRADServiceLocatorInternal.getTransactionManager();
712 }
713 return this.transactionManager;
714 }
715
716 private ActionForm createNewActionForm(ActionMapping mapping, HttpServletRequest request) {
717 String name = mapping.getName();
718 FormBeanConfig config = moduleConfig.findFormBeanConfig(name);
719 if (config == null) {
720 log.warn("No FormBeanConfig found under '" + name + "'");
721 return (null);
722 }
723 ActionForm instance = RequestUtils.createActionForm(config, servlet);
724 if ("request".equals(mapping.getScope())) {
725 request.setAttribute(mapping.getAttribute(), instance);
726 } else {
727 HttpSession session = request.getSession();
728 session.setAttribute(mapping.getAttribute(), instance);
729 }
730 return instance;
731 }
732 }