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