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 ActionForm form = processActionForm(request, response, mapping);
208 processPopulate(request, response, form, mapping);
209
210
211 Action action = processActionCreate(request, response, mapping);
212
213 if (action != null) {
214
215 ActionForward forward = processActionPerform(request, response, action, form, mapping);
216
217 if (forward != null) {
218
219 processForwardConfig(request, response, forward);
220 }
221 }
222 }
223
224
225
226
227
228
229
230
231
232 private String getDocumentNumber(HttpServletRequest request) {
233 String documentNumber = request.getParameter(KRADConstants.DOCUMENT_DOCUMENT_NUMBER);
234
235
236 if (documentNumber == null) {
237 documentNumber = request.getParameter(KRADConstants.DOC_NUM);
238 }
239
240 if (documentNumber == null) {
241 documentNumber = request.getParameter("documentId");
242 }
243
244 return documentNumber;
245 }
246
247
248
249
250
251 @Override
252 protected void processPopulate(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping) throws ServletException {
253 if (form instanceof KualiForm) {
254
255
256
257
258
259 KNSGlobalVariables.setKualiForm((KualiForm) form);
260 }
261
262
263 if (!(form instanceof PojoForm)) {
264 super.processPopulate(request, response, form, mapping);
265 return;
266 }
267
268 final String previousRequestGuid = request.getParameter(KualiRequestProcessor.PREVIOUS_REQUEST_EDITABLE_PROPERTIES_GUID_PARAMETER_NAME);
269
270 ((PojoForm)form).clearEditablePropertyInformation();
271 ((PojoForm)form).registerStrutsActionMappingScope(mapping.getScope());
272
273 String multipart = mapping.getMultipartClass();
274 if (multipart != null) {
275 request.setAttribute(Globals.MULTIPART_KEY, multipart);
276 }
277
278 form.setServlet(this.servlet);
279 form.reset(mapping, request);
280
281 ((PojoForm)form).setPopulateEditablePropertiesGuid(previousRequestGuid);
282
283 ((PojoForm) form).populate(request);
284 request.setAttribute("UnconvertedValues", ((PojoForm) form).getUnconvertedValues().keySet());
285 request.setAttribute("UnconvertedHash", ((PojoForm) form).getUnconvertedValues());
286 }
287
288
289
290
291
292 @Override
293 protected boolean processValidate(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping) throws IOException, ServletException, InvalidCancelException {
294
295
296 if (GlobalVariables.getMessageMap().hasNoErrors()) {
297 if (form == null) {
298 return (true);
299 }
300
301 if (request.getAttribute(Globals.CANCEL_KEY) != null) {
302 if (LOG.isDebugEnabled()) {
303 LOG.debug(" Cancelled transaction, skipping validation");
304 }
305 return (true);
306 }
307
308
309 if (!mapping.getValidate()) {
310 return (true);
311 }
312
313
314 super.processValidate(request, response, form, mapping);
315 }
316
317 publishMessages(request);
318 if (!GlobalVariables.getMessageMap().hasNoErrors()) {
319
320 if (form.getMultipartRequestHandler() != null) {
321 if (LOG.isDebugEnabled()) {
322 LOG.debug(" Rolling back multipart request");
323 }
324 form.getMultipartRequestHandler().rollback();
325 }
326
327
328 if (form instanceof PojoForm) {
329 ((PojoForm) form).processValidationFail();
330 }
331
332
333 String input = mapping.getInput();
334 if (input == null) {
335 if (LOG.isDebugEnabled()) {
336 LOG.debug(" Validation failed but no input form available");
337 }
338 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, getInternal().getMessage("noInput", mapping.getPath()));
339 return (false);
340 }
341
342 if (moduleConfig.getControllerConfig().getInputForward()) {
343 ForwardConfig forward = mapping.findForward(input);
344 processForwardConfig(request, response, forward);
345 } else {
346 internalModuleRelativeForward(input, request, response);
347 }
348
349 return (false);
350 }
351 return true;
352 }
353
354
355
356
357
358 @Override
359 protected ActionForm processActionForm(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) {
360
361 String documentNumber = getDocumentNumber(request);
362 if (documentNumber != null) { MDC.put(MDC_DOC_ID, documentNumber); }
363
364 UserSession userSession = (UserSession) request.getSession().getAttribute(KRADConstants.USER_SESSION_KEY);
365
366 String docFormKey = request.getParameter(KRADConstants.DOC_FORM_KEY);
367 String methodToCall = request.getParameter(KRADConstants.DISPATCH_REQUEST_PARAMETER);
368 String refreshCaller = request.getParameter(KRADConstants.REFRESH_CALLER);
369
370 String documentWebScope = request.getParameter(KRADConstants.DOCUMENT_WEB_SCOPE);
371
372 if (mapping.getPath().startsWith(KRADConstants.REFRESH_MAPPING_PREFIX) || KRADConstants.RETURN_METHOD_TO_CALL.equalsIgnoreCase(methodToCall) ||
373 KRADConstants.QUESTION_REFRESH.equalsIgnoreCase(refreshCaller) || KRADConstants.TEXT_AREA_REFRESH.equalsIgnoreCase(refreshCaller) || KRADConstants
374 .SESSION_SCOPE.equalsIgnoreCase(documentWebScope)) {
375 ActionForm form = null;
376
377 GlobalVariables.getUserSession().removeObjectsByPrefix(KRADConstants.SEARCH_LIST_KEY_PREFIX);
378
379
380
381
382 if (userSession.retrieveObject(docFormKey) != null) {
383 LOG.debug("getDecomentForm KualiDocumentFormBase from session");
384 form = (ActionForm) userSession.retrieveObject(docFormKey);
385 } else if (StringUtils.isNotBlank(documentNumber)) {
386 form = getSessionDocumentService().getDocumentForm(documentNumber, docFormKey, userSession, request.getRemoteAddr());
387 }
388 request.setAttribute(mapping.getAttribute(), form);
389 if (!KRADConstants.SESSION_SCOPE.equalsIgnoreCase(documentWebScope)) {
390 userSession.removeObject(docFormKey);
391 }
392
393
394
395 String contentType = request.getContentType();
396 String method = request.getMethod();
397 if (("POST".equalsIgnoreCase(method) && contentType != null && contentType.startsWith("multipart/form-data"))) {
398
399
400 WebUtils.getMultipartParameters(request, null, form, mapping);
401 }
402
403 if (form != null) {
404 return form;
405 }
406 }
407
408
409
410
411
412
413 ActionForm form = super.processActionForm(request, response, mapping);
414
415
416 String contentType = request.getContentType();
417 String method = request.getMethod();
418
419 if ("GET".equalsIgnoreCase(method) && StringUtils.isNotBlank(methodToCall) && form instanceof PojoForm &&
420 ((PojoForm) form).getMethodToCallsToBypassSessionRetrievalForGETRequests().contains(methodToCall)) {
421 return createNewActionForm(mapping, request);
422 }
423
424
425
426
427
428
429
430
431
432
433
434 if (("POST".equalsIgnoreCase(method) && contentType != null && contentType.startsWith("multipart/form-data"))) {
435 WebUtils.getMultipartParameters(request, null, form, mapping);
436 docFormKey = request.getParameter(KRADConstants.DOC_FORM_KEY);
437 documentWebScope = request.getParameter(KRADConstants.DOCUMENT_WEB_SCOPE);
438
439 documentNumber = getDocumentNumber(request);
440
441 if (KRADConstants.SESSION_SCOPE.equalsIgnoreCase(documentWebScope) ||
442 (form instanceof KualiDocumentFormBase && WebUtils
443 .isDocumentSession(((KualiDocumentFormBase) form).getDocument(),
444 (KualiDocumentFormBase) form))) {
445
446 Object userSessionObject = userSession.retrieveObject(docFormKey);
447 if ( userSessionObject != null && userSessionObject instanceof ActionForm ) {
448 LOG.debug("getDocumentForm KualiDocumentFormBase from session");
449 form = (ActionForm) userSessionObject;
450 } else {
451 ActionForm tempForm = getSessionDocumentService().getDocumentForm(documentNumber, docFormKey, userSession, request.getRemoteAddr());
452 if ( tempForm != null ) {
453 form = tempForm;
454 }
455 }
456
457 request.setAttribute(mapping.getAttribute(), form);
458 if (form != null) {
459 return form;
460 }
461 }
462 }
463 return form;
464 }
465
466
467
468
469
470
471
472
473
474
475
476 @Override
477 protected ActionForward processActionPerform(final HttpServletRequest request, final HttpServletResponse response, final Action action, final ActionForm form, final ActionMapping mapping) throws IOException, ServletException {
478 try {
479 TransactionTemplate template = new TransactionTemplate(getTransactionManager());
480 ActionForward forward = null;
481 try {
482 forward = (ActionForward) template.execute(new TransactionCallback() {
483 public Object doInTransaction(TransactionStatus status) {
484 ActionForward actionForward = null;
485 try {
486 actionForward = action.execute(mapping, form, request, response);
487 } catch (Exception e) {
488
489
490
491
492
493
494
495
496 throw new WrappedRuntimeException(e);
497 }
498 if (status.isRollbackOnly()) {
499
500
501
502
503
504
505 throw new WrappedActionForwardRuntimeException(actionForward);
506 }
507 return actionForward;
508 }
509 });
510 } catch (WrappedActionForwardRuntimeException e) {
511 forward = e.getActionForward();
512 }
513
514 publishMessages(request);
515 saveMessages(request);
516 saveAuditErrors(request);
517
518 if (form instanceof PojoForm) {
519 if (((PojoForm)form).getEditableProperties() == null
520 || ((PojoForm)form).getEditableProperties().isEmpty()) {
521 EditablePropertiesHistoryHolder holder = (EditablePropertiesHistoryHolder) GlobalVariables.getUserSession().getObjectMap().get(
522 KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME);
523 if (holder == null) {
524 holder = new EditablePropertiesHistoryHolder();
525 }
526
527 final String guid = holder.addEditablePropertiesToHistory(((PojoForm)form).getEditableProperties());
528 ((PojoForm)form).setActionEditablePropertiesGuid(guid);
529 GlobalVariables.getUserSession().addObject(KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME, holder);
530 }
531 }
532
533 return forward;
534
535 } catch (Exception e) {
536 if (e instanceof WrappedRuntimeException) {
537 e = (Exception) e.getCause();
538 }
539 if (e instanceof ValidationException) {
540
541 if (GlobalVariables.getMessageMap().hasNoErrors()) {
542
543 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, RiceKeyConstants.ERROR_CUSTOM, e.getMessage());
544 }
545
546 if (form instanceof PojoForm) {
547 if (((PojoForm)form).getEditableProperties() == null
548 || ((PojoForm)form).getEditableProperties().isEmpty()) {
549 EditablePropertiesHistoryHolder holder = (EditablePropertiesHistoryHolder) GlobalVariables.getUserSession().getObjectMap().get(
550 KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME);
551 if (holder == null) {
552 holder = new EditablePropertiesHistoryHolder();
553 }
554
555 final String guid = holder.addEditablePropertiesToHistory(((PojoForm)form).getEditableProperties());
556 ((PojoForm)form).setActionEditablePropertiesGuid(guid);
557 GlobalVariables.getUserSession().addObject(KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME, holder);
558 }
559 }
560
561 publishMessages(request);
562 return mapping.findForward(RiceConstants.MAPPING_BASIC);
563 }
564
565 publishMessages(request);
566
567 return (processException(request, response, e, form, mapping));
568 }
569 }
570
571 private static class WrappedActionForwardRuntimeException extends RuntimeException {
572 private ActionForward actionForward;
573
574 public WrappedActionForwardRuntimeException(ActionForward actionForward) {
575 this.actionForward = actionForward;
576 }
577
578 public ActionForward getActionForward() {
579 return actionForward;
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 }