001/** 002 * Copyright 2005-2012 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.kns.web.struts.action; 017 018import java.io.IOException; 019 020import javax.servlet.ServletException; 021import javax.servlet.http.HttpServletRequest; 022import javax.servlet.http.HttpServletResponse; 023import javax.servlet.http.HttpSession; 024 025import org.apache.commons.lang.StringUtils; 026import org.apache.log4j.Logger; 027import org.apache.log4j.MDC; 028import org.apache.ojb.broker.OptimisticLockException; 029import org.apache.struts.Globals; 030import org.apache.struts.action.Action; 031import org.apache.struts.action.ActionForm; 032import org.apache.struts.action.ActionForward; 033import org.apache.struts.action.ActionMapping; 034import org.apache.struts.action.InvalidCancelException; 035import org.apache.struts.action.RequestProcessor; 036import org.apache.struts.config.FormBeanConfig; 037import org.apache.struts.config.ForwardConfig; 038import org.apache.struts.util.RequestUtils; 039import org.kuali.rice.core.api.util.RiceConstants; 040import org.kuali.rice.core.api.util.RiceKeyConstants; 041import org.kuali.rice.kns.exception.FileUploadLimitExceededException; 042import org.kuali.rice.kns.service.KNSServiceLocator; 043import org.kuali.rice.kns.service.SessionDocumentService; 044import org.kuali.rice.kns.util.ErrorContainer; 045import org.kuali.rice.kns.util.InfoContainer; 046import org.kuali.rice.kns.util.KNSConstants; 047import org.kuali.rice.kns.util.KNSGlobalVariables; 048import org.kuali.rice.kns.util.WarningContainer; 049import org.kuali.rice.kns.util.WebUtils; 050import org.kuali.rice.kns.web.EditablePropertiesHistoryHolder; 051import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase; 052import org.kuali.rice.kns.web.struts.form.KualiForm; 053import org.kuali.rice.kns.web.struts.form.pojo.PojoForm; 054import org.kuali.rice.krad.UserSession; 055import org.kuali.rice.krad.document.Document; 056import org.kuali.rice.krad.exception.ValidationException; 057import org.kuali.rice.krad.service.KRADServiceLocatorInternal; 058import org.kuali.rice.krad.util.GlobalVariables; 059import org.kuali.rice.krad.util.KRADConstants; 060import org.kuali.rice.krad.util.KRADUtils; 061import org.kuali.rice.krad.util.MessageMap; 062import org.springframework.transaction.PlatformTransactionManager; 063import org.springframework.transaction.TransactionStatus; 064import org.springframework.transaction.support.TransactionCallback; 065import org.springframework.transaction.support.TransactionTemplate; 066import org.springmodules.orm.ojb.OjbOperationException; 067 068/** 069 * This class handles setup of user session and restoring of action form. 070 * 071 * 072 */ 073public class KualiRequestProcessor extends RequestProcessor { 074 075 private static final String MDC_DOC_ID = "docId"; 076 private static final String PREVIOUS_REQUEST_EDITABLE_PROPERTIES_GUID_PARAMETER_NAME = "actionEditablePropertiesGuid"; 077 078 private static Logger LOG = Logger.getLogger(KualiRequestProcessor.class); 079 080 private SessionDocumentService sessionDocumentService; 081 private PlatformTransactionManager transactionManager; 082 083 @Override 084 public void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { 085 if ( LOG.isInfoEnabled() ) { 086 LOG.info(new StringBuffer("Started processing request: '").append(request.getRequestURI()).append("' w/ query string: '").append(request.getQueryString()).append("'")); 087 } 088 089 try { 090 strutsProcess(request, response); 091 } catch (FileUploadLimitExceededException e) { 092 ActionForward actionForward = processException(request, response, e, e.getActionForm(), e.getActionMapping()); 093 processForwardConfig(request, response, actionForward); 094 } finally { 095 KNSGlobalVariables.setKualiForm(null); 096 } 097 098 try { 099 ActionForm form = WebUtils.getKualiForm(request); 100 101 if (form != null && form instanceof KualiDocumentFormBase) { 102 String docId = ((KualiDocumentFormBase) form).getDocId(); 103 if (docId != null) { MDC.put(MDC_DOC_ID, docId); } 104 } 105 106 String refreshCaller = request.getParameter(KRADConstants.REFRESH_CALLER); 107 if (form!=null && KualiDocumentFormBase.class.isAssignableFrom(form.getClass()) 108 && !KRADConstants.QUESTION_REFRESH.equalsIgnoreCase(refreshCaller)) { 109 KualiDocumentFormBase docForm = (KualiDocumentFormBase) form; 110 Document document = docForm.getDocument(); 111 String docFormKey = docForm.getFormKey(); 112 113 UserSession userSession = (UserSession) request.getSession().getAttribute(KRADConstants.USER_SESSION_KEY); 114 115 if (WebUtils.isDocumentSession(document, docForm)) { 116 getSessionDocumentService().setDocumentForm(docForm, userSession, request.getRemoteAddr()); 117 } 118 119 Boolean exitingDocument = (Boolean) request.getAttribute(KRADConstants.EXITING_DOCUMENT); 120 121 if (exitingDocument != null && exitingDocument.booleanValue()) { 122 // remove KualiDocumentFormBase object from session and 123 // table. 124 getSessionDocumentService().purgeDocumentForm(docForm.getDocument().getDocumentNumber(), docFormKey, userSession, request.getRemoteAddr()); 125 } 126 } 127 128 if ( LOG.isInfoEnabled() ) { 129 LOG.info(new StringBuffer("Finished processing request: '").append(request.getRequestURI()).append("' w/ query string: '").append(request.getQueryString()).append("'")); 130 } 131 132 } finally { 133 // MDC docId key is set above, and also during super.process() in the call to processActionForm 134 MDC.remove(MDC_DOC_ID); 135 } 136 137 } 138 139 @Override 140 protected boolean processPreprocess(HttpServletRequest request, HttpServletResponse response) { 141 final UserSession session = KRADUtils.getUserSessionFromRequest(request); 142 143 if (session == null) { 144 throw new IllegalStateException("the user session has not been established"); 145 } 146 GlobalVariables.setUserSession(session); 147 KNSGlobalVariables.clear(); 148 return true; 149 } 150 151 /** 152 * <p>ProcessDefinition an <code>HttpServletRequest</code> and create the 153 * corresponding <code>HttpServletResponse</code> or dispatch 154 * to another resource.</p> 155 * 156 * @param request The servlet request we are processing 157 * @param response The servlet response we are creating 158 * 159 * @exception IOException if an input/output error occurs 160 * @exception ServletException if a processing exception occurs 161 */ 162 public void strutsProcess(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { 163 164 // Wrap multipart requests with a special wrapper 165 request = processMultipart(request); 166 167 // Identify the path component we will use to select a mapping 168 String path = processPath(request, response); 169 if (path == null) { 170 return; 171 } 172 173 if (log.isDebugEnabled()) { 174 log.debug("Processing a '" + request.getMethod() + 175 "' for path '" + path + "'"); 176 } 177 178 // Select a Locale for the current user if requested 179 processLocale(request, response); 180 181 // Set the content type and no-caching headers if requested 182 processContent(request, response); 183 processNoCache(request, response); 184 185 // General purpose preprocessing hook 186 if (!processPreprocess(request, response)) { 187 return; 188 } 189 190 this.processCachedMessages(request, response); 191 192 // Identify the mapping for this request 193 ActionMapping mapping = processMapping(request, response, path); 194 if (mapping == null) { 195 return; 196 } 197 198 // Check for any role required to perform this action 199 if (!processRoles(request, response, mapping)) { 200 return; 201 } 202 203 processFormActionAndForward(request, response, mapping); 204 205 } 206 207 public void processFormActionAndForward(final HttpServletRequest request, final HttpServletResponse response, final ActionMapping mapping) throws ServletException, IOException { 208 ActionForm form = processActionForm(request, response, mapping); 209 processPopulate(request, response, form, mapping); 210 211 // Create or acquire the Action instance to process this request 212 Action action = processActionCreate(request, response, mapping); 213 214 if (action != null) { 215 // Call the Action instance itself 216 final ActionForward forward = processActionPerform(request, response, action, form, mapping); 217 218 if (forward != null) { 219 // ProcessDefinition the returned ActionForward instance 220 TransactionTemplate template = new TransactionTemplate(getTransactionManager()); 221 try { 222 template.execute(new TransactionCallback<Object>() { 223 @Override 224 public Object doInTransaction(TransactionStatus status) { 225 try { 226 processForwardConfig(request, response, forward); 227 } 228 catch (IOException ex) { 229 throw new RuntimeException(ex); 230 } 231 catch (ServletException ex) { 232 throw new RuntimeException(ex); 233 } 234 return null; 235 } 236 }); 237 } catch ( RuntimeException ex ) { 238 if ( ex.getCause() instanceof IOException ) { 239 throw (IOException)ex.getCause(); 240 } else if ( ex.getCause() instanceof ServletException ) { 241 throw (ServletException)ex.getCause(); 242 } 243 throw ex; 244 } 245 } 246 } 247 } 248 249 250 /** 251 * This method gets the document number from the request. The request should have been processed already 252 * before this is called if it is multipart. 253 * 254 * @param request 255 * @return the document number, or null if one can't be found in the request. 256 */ 257 private String getDocumentNumber(HttpServletRequest request) { 258 String documentNumber = request.getParameter(KRADConstants.DOCUMENT_DOCUMENT_NUMBER); 259 260 // from lookup pages. 261 if (documentNumber == null) { 262 documentNumber = request.getParameter(KRADConstants.DOC_NUM); 263 } 264 265 if (documentNumber == null) { 266 documentNumber = request.getParameter("documentId"); 267 } 268 269 return documentNumber; 270 } 271 272 /** 273 * Hooks into populate process to call form populate method if form is an 274 * instanceof PojoForm. 275 */ 276 @Override 277 protected void processPopulate(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping) throws ServletException { 278 if (form instanceof KualiForm) { 279 // Add the ActionForm to GlobalVariables 280 // This will allow developers to retrieve both the Document and any 281 // request parameters that are not 282 // part of the Form and make them available in ValueFinder classes 283 // and other places where they are needed. 284 KNSGlobalVariables.setKualiForm((KualiForm) form); 285 } 286 287 // if not PojoForm, call struts populate 288 if (!(form instanceof PojoForm)) { 289 super.processPopulate(request, response, form, mapping); 290 return; 291 } 292 293 final String previousRequestGuid = request.getParameter(KualiRequestProcessor.PREVIOUS_REQUEST_EDITABLE_PROPERTIES_GUID_PARAMETER_NAME); 294 295 ((PojoForm)form).clearEditablePropertyInformation(); 296 ((PojoForm)form).registerStrutsActionMappingScope(mapping.getScope()); 297 298 String multipart = mapping.getMultipartClass(); 299 if (multipart != null) { 300 request.setAttribute(Globals.MULTIPART_KEY, multipart); 301 } 302 303 form.setServlet(this.servlet); 304 form.reset(mapping, request); 305 306 ((PojoForm)form).setPopulateEditablePropertiesGuid(previousRequestGuid); 307 // call populate on ActionForm 308 ((PojoForm) form).populate(request); 309 request.setAttribute("UnconvertedValues", ((PojoForm) form).getUnconvertedValues().keySet()); 310 request.setAttribute("UnconvertedHash", ((PojoForm) form).getUnconvertedValues()); 311 } 312 313 /** 314 * Hooks into validate to catch any errors from the populate, and translate 315 * the ErrorMap to ActionMessages. 316 */ 317 @Override 318 protected boolean processValidate(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping) throws IOException, ServletException, InvalidCancelException { 319 320 // skip form validate if we had errors from populate 321 if (GlobalVariables.getMessageMap().hasNoErrors()) { 322 if (form == null) { 323 return (true); 324 } 325 // Was this request cancelled? 326 if (request.getAttribute(Globals.CANCEL_KEY) != null) { 327 if (LOG.isDebugEnabled()) { 328 LOG.debug(" Cancelled transaction, skipping validation"); 329 } 330 return (true); 331 } 332 333 // Has validation been turned off for this mapping? 334 if (!mapping.getValidate()) { 335 return (true); 336 } 337 338 // call super to call forms validate 339 super.processValidate(request, response, form, mapping); 340 } 341 342 publishMessages(request); 343 if (!GlobalVariables.getMessageMap().hasNoErrors()) { 344 // Special handling for multipart request 345 if (form.getMultipartRequestHandler() != null) { 346 if (LOG.isDebugEnabled()) { 347 LOG.debug(" Rolling back multipart request"); 348 } 349 form.getMultipartRequestHandler().rollback(); 350 } 351 352 // Fix state that could be incorrect because of validation failure 353 if (form instanceof PojoForm) { 354 ((PojoForm) form).processValidationFail(); 355 } 356 357 // Was an input path (or forward) specified for this mapping? 358 String input = mapping.getInput(); 359 if (input == null) { 360 if (LOG.isDebugEnabled()) { 361 LOG.debug(" Validation failed but no input form available"); 362 } 363 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, getInternal().getMessage("noInput", mapping.getPath())); 364 return (false); 365 } 366 367 if (moduleConfig.getControllerConfig().getInputForward()) { 368 ForwardConfig forward = mapping.findForward(input); 369 processForwardConfig(request, response, forward); 370 } else { 371 internalModuleRelativeForward(input, request, response); 372 } 373 374 return (false); 375 } 376 return true; 377 } 378 379 /** 380 * Checks for return from a lookup or question, and restores the action form 381 * stored under the request parameter docFormKey. 382 */ 383 @Override 384 protected ActionForm processActionForm(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) { 385 386 String documentNumber = getDocumentNumber(request); 387 if (documentNumber != null) { MDC.put(MDC_DOC_ID, documentNumber); } 388 389 UserSession userSession = (UserSession) request.getSession().getAttribute(KRADConstants.USER_SESSION_KEY); 390 391 String docFormKey = request.getParameter(KRADConstants.DOC_FORM_KEY); 392 String methodToCall = request.getParameter(KRADConstants.DISPATCH_REQUEST_PARAMETER); 393 String refreshCaller = request.getParameter(KRADConstants.REFRESH_CALLER); 394// String searchListRequestKey = request.getParameter(KRADConstants.SEARCH_LIST_REQUEST_KEY); 395 String documentWebScope = request.getParameter(KRADConstants.DOCUMENT_WEB_SCOPE); 396 397 if (mapping.getPath().startsWith(KRADConstants.REFRESH_MAPPING_PREFIX) || KRADConstants.RETURN_METHOD_TO_CALL.equalsIgnoreCase(methodToCall) || 398 KRADConstants.QUESTION_REFRESH.equalsIgnoreCase(refreshCaller) || KRADConstants.TEXT_AREA_REFRESH.equalsIgnoreCase(refreshCaller) || KRADConstants 399 .SESSION_SCOPE.equalsIgnoreCase(documentWebScope)) { 400 ActionForm form = null; 401 // check for search result storage and clear 402 GlobalVariables.getUserSession().removeObjectsByPrefix(KRADConstants.SEARCH_LIST_KEY_PREFIX); 403 404 // We put different type of forms such as document form, lookup form 405 // in session but we only store document form in 406 // database. 407 if (userSession.retrieveObject(docFormKey) != null) { 408 LOG.debug("getDecomentForm KualiDocumentFormBase from session"); 409 form = (ActionForm) userSession.retrieveObject(docFormKey); 410 } else if (StringUtils.isNotBlank(documentNumber)) { 411 form = getSessionDocumentService().getDocumentForm(documentNumber, docFormKey, userSession, request.getRemoteAddr()); 412 } 413 request.setAttribute(mapping.getAttribute(), form); 414 if (!KRADConstants.SESSION_SCOPE.equalsIgnoreCase(documentWebScope)) { 415 userSession.removeObject(docFormKey); 416 } 417 // we should check whether this is a multipart request because we 418 // could have had a combination of query parameters and a multipart 419 // request 420 String contentType = request.getContentType(); 421 String method = request.getMethod(); 422 if (("POST".equalsIgnoreCase(method) && contentType != null && contentType.startsWith("multipart/form-data"))) { 423 // this method parses the multipart request and adds new 424 // non-file parameters into the request 425 WebUtils.getMultipartParameters(request, null, form, mapping); 426 } 427 // The form can be null if the document is not a session document 428 if (form != null) { 429 return form; 430 } 431 } 432 433 // Rice has the ability to limit file upload sizes on a per-form basis, 434 // so the max upload sizes may be accessed by calling methods on 435 // PojoFormBase. 436 // This requires that we are able know the file upload size limit (i.e. 437 // retrieve a form instance) before we parse a mulitpart request. 438 ActionForm form = super.processActionForm(request, response, mapping); 439 440 // for sessiondocument with multipart request 441 String contentType = request.getContentType(); 442 String method = request.getMethod(); 443 444 if ("GET".equalsIgnoreCase(method) && StringUtils.isNotBlank(methodToCall) && form instanceof PojoForm && 445 ((PojoForm) form).getMethodToCallsToBypassSessionRetrievalForGETRequests().contains(methodToCall)) { 446 return createNewActionForm(mapping, request); 447 } 448 449 // if we have a multipart request, parse it and return the stored form 450 // from session if the doc form key is not blank. If it is blank, then 451 // we just return the form 452 // generated from the superclass processActionForm method. Either way, 453 // we need to parse the mulitpart request now so that we may determine 454 // what the value of the doc form key is. 455 // This is generally against the contract of processActionForm, because 456 // processPopulate should be responsible for parsing the mulitpart 457 // request, but we need to parse it now 458 // to determine the doc form key value. 459 if (("POST".equalsIgnoreCase(method) && contentType != null && contentType.startsWith("multipart/form-data"))) { 460 WebUtils.getMultipartParameters(request, null, form, mapping); 461 docFormKey = request.getParameter(KRADConstants.DOC_FORM_KEY); 462 documentWebScope = request.getParameter(KRADConstants.DOCUMENT_WEB_SCOPE); 463 464 documentNumber = getDocumentNumber(request); 465 466 if (KRADConstants.SESSION_SCOPE.equalsIgnoreCase(documentWebScope) || 467 (form instanceof KualiDocumentFormBase && WebUtils 468 .isDocumentSession(((KualiDocumentFormBase) form).getDocument(), 469 (KualiDocumentFormBase) form))) { 470 471 Object userSessionObject = userSession.retrieveObject(docFormKey); 472 if ( userSessionObject != null && userSessionObject instanceof ActionForm ) { 473 LOG.debug("getDocumentForm KualiDocumentFormBase from session"); 474 form = (ActionForm) userSessionObject; 475 } else { 476 ActionForm tempForm = getSessionDocumentService().getDocumentForm(documentNumber, docFormKey, userSession, request.getRemoteAddr()); 477 if ( tempForm != null ) { 478 form = tempForm; 479 } 480 } 481 482 request.setAttribute(mapping.getAttribute(), form); 483 if (form != null) { 484 return form; 485 } 486 } 487 } 488 return form; 489 } 490 491 /** 492 * Hook into action perform to handle errors in the error map and catch 493 * exceptions. 494 * 495 * <p> 496 * A transaction is started prior to the execution of the action. This 497 * allows for the action code to execute efficiently without the need for 498 * using PROPAGATION_SUPPORTS in the transaction definitions. The 499 * PROPAGATION_SUPPORTS propagation type does not work well with JTA. 500 */ 501 @Override 502 protected ActionForward processActionPerform(final HttpServletRequest request, final HttpServletResponse response, final Action action, final ActionForm form, final ActionMapping mapping) throws IOException, ServletException { 503 try { 504 TransactionTemplate template = new TransactionTemplate(getTransactionManager()); 505 ActionForward forward = null; 506 try { 507 forward = (ActionForward) template.execute(new TransactionCallback() { 508 @Override 509 public Object doInTransaction(TransactionStatus status) { 510 ActionForward actionForward = null; 511 try { 512 actionForward = action.execute(mapping, form, request, response); 513 } catch (Exception e) { 514 // the doInTransaction method has no means for 515 // throwing exceptions, so we will wrap the 516 // exception in 517 // a RuntimeException and re-throw. The one caveat 518 // here is that this will always result in 519 // the 520 // transaction being rolled back (since 521 // WrappedRuntimeException is a runtime exception). 522 throw new WrappedRuntimeException(e); 523 } 524 if (status.isRollbackOnly()) { 525 // this means that the struts action execution 526 // caused the transaction to rollback, we want to 527 // go ahead 528 // and trigger the rollback by throwing an exception 529 // here but then return the action forward 530 // from this method 531 throw new WrappedActionForwardRuntimeException(actionForward); 532 } 533 return actionForward; 534 } 535 }); 536 } catch (WrappedActionForwardRuntimeException e) { 537 forward = e.getActionForward(); 538 } 539 540 publishMessages(request); 541 saveMessages(request); 542 saveAuditErrors(request); 543 544 if (form instanceof PojoForm) { 545 if (((PojoForm)form).getEditableProperties() == null 546 || ((PojoForm)form).getEditableProperties().isEmpty()) { 547 EditablePropertiesHistoryHolder holder = (EditablePropertiesHistoryHolder) GlobalVariables.getUserSession().getObjectMap().get( 548 KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME); 549 if (holder == null) { 550 holder = new EditablePropertiesHistoryHolder(); 551 } 552 553 final String guid = holder.addEditablePropertiesToHistory(((PojoForm)form).getEditableProperties()); 554 ((PojoForm)form).setActionEditablePropertiesGuid(guid); 555 GlobalVariables.getUserSession().addObject(KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME, holder); 556 } 557 } 558 559 return forward; 560 561 } catch (Exception e) { 562 if (e instanceof WrappedRuntimeException) { 563 e = (Exception) e.getCause(); 564 } 565 if (e instanceof ValidationException) { 566 // add a generic error message if there are none 567 if (GlobalVariables.getMessageMap().hasNoErrors()) { 568 569 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, RiceKeyConstants.ERROR_CUSTOM, e.getMessage()); 570 } 571 572 if (form instanceof PojoForm) { 573 if (((PojoForm)form).getEditableProperties() == null 574 || ((PojoForm)form).getEditableProperties().isEmpty()) { 575 EditablePropertiesHistoryHolder holder = (EditablePropertiesHistoryHolder) GlobalVariables.getUserSession().getObjectMap().get( 576 KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME); 577 if (holder == null) { 578 holder = new EditablePropertiesHistoryHolder(); 579 } 580 581 final String guid = holder.addEditablePropertiesToHistory(((PojoForm)form).getEditableProperties()); 582 ((PojoForm)form).setActionEditablePropertiesGuid(guid); 583 GlobalVariables.getUserSession().addObject(KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME, holder); 584 } 585 } 586 // display error messages and return to originating page 587 publishMessages(request); 588 return mapping.findForward(RiceConstants.MAPPING_BASIC); 589 } 590 591 publishMessages(request); 592 593 return (processException(request, response, e, form, mapping)); 594 } 595 } 596 597 private static class WrappedActionForwardRuntimeException extends RuntimeException { 598 private ActionForward actionForward; 599 600 public WrappedActionForwardRuntimeException(ActionForward actionForward) { 601 this.actionForward = actionForward; 602 } 603 604 public ActionForward getActionForward() { 605 return actionForward; 606 } 607 } 608 609 /** 610 * Adds more detailed logging for unhandled exceptions 611 * 612 * @see org.apache.struts.action.RequestProcessor#processException(HttpServletRequest, 613 * HttpServletResponse, Exception, ActionForm, ActionMapping) 614 */ 615 @Override 616 protected ActionForward processException(HttpServletRequest request, HttpServletResponse response, Exception exception, ActionForm form, ActionMapping mapping) throws IOException, ServletException { 617 ActionForward actionForward = null; 618 619 try { 620 actionForward = super.processException(request, response, exception, form, mapping); 621 } catch (IOException e) { 622 logException(e); 623 throw e; 624 } catch (ServletException e) { 625 // special case, to make OptimisticLockExceptions easier to read 626 Throwable rootCause = e.getRootCause(); 627 if (rootCause instanceof OjbOperationException) { 628 OjbOperationException ooe = (OjbOperationException) rootCause; 629 630 Throwable subcause = ooe.getCause(); 631 if (subcause instanceof OptimisticLockException) { 632 OptimisticLockException ole = (OptimisticLockException) subcause; 633 634 StringBuffer message = new StringBuffer(e.getMessage()); 635 636 Object sourceObject = ole.getSourceObject(); 637 if (sourceObject != null) { 638 message.append(" (sourceObject is "); 639 message.append(sourceObject.getClass().getName()); 640 message.append(")"); 641 } 642 643 e = new ServletException(message.toString(), rootCause); 644 } 645 } 646 647 logException(e); 648 throw e; 649 } 650 return actionForward; 651 } 652 653 private void logException(Exception e) { 654 LOG.error("unhandled exception thrown by KualiRequestProcessor.processActionPerform", e); 655 } 656 657 /** 658 * Checks for errors in the error map and transforms them to struts action 659 * messages then stores in the request. 660 */ 661 private void publishMessages(HttpServletRequest request) { 662 MessageMap errorMap = GlobalVariables.getMessageMap(); 663 if (!errorMap.hasNoErrors()) { 664 ErrorContainer errorContainer = new ErrorContainer(errorMap); 665 666 request.setAttribute("ErrorContainer", errorContainer); 667 request.setAttribute(Globals.ERROR_KEY, errorContainer.getRequestErrors()); 668 request.setAttribute("ErrorPropertyList", errorContainer.getErrorPropertyList()); 669 } 670 671 if (errorMap.hasWarnings()) { 672 WarningContainer warningsContainer = new WarningContainer(errorMap); 673 674 request.setAttribute("WarningContainer", warningsContainer); 675 request.setAttribute("WarningActionMessages", warningsContainer.getRequestMessages()); 676 request.setAttribute("WarningPropertyList", warningsContainer.getMessagePropertyList()); 677 } 678 679 if (errorMap.hasInfo()) { 680 InfoContainer infoContainer = new InfoContainer(errorMap); 681 682 request.setAttribute("InfoContainer", infoContainer); 683 request.setAttribute("InfoActionMessages", infoContainer.getRequestMessages()); 684 request.setAttribute("InfoPropertyList", infoContainer.getMessagePropertyList()); 685 } 686 } 687 688 /** 689 * Checks for messages in GlobalVariables and places list in request 690 * attribute. 691 */ 692 private void saveMessages(HttpServletRequest request) { 693 if (!KNSGlobalVariables.getMessageList().isEmpty()) { 694 request.setAttribute(KRADConstants.GLOBAL_MESSAGES, KNSGlobalVariables.getMessageList().toActionMessages()); 695 } 696 } 697 698 /** 699 * Checks for messages in GlobalVariables and places list in request 700 * attribute. 701 */ 702 private void saveAuditErrors(HttpServletRequest request) { 703 if (!KNSGlobalVariables.getAuditErrorMap().isEmpty()) { 704 request.setAttribute(KNSConstants.AUDIT_ERRORS, KNSGlobalVariables.getAuditErrorMap()); 705 } 706 } 707 708 /** 709 * A simple exception that allows us to wrap an exception that is thrown out 710 * of a transaction template. 711 */ 712 @SuppressWarnings("serial") 713 private static class WrappedRuntimeException extends RuntimeException { 714 public WrappedRuntimeException(Exception e) { 715 super(e); 716 } 717 } 718 719 /** 720 * @return the sessionDocumentService 721 */ 722 public SessionDocumentService getSessionDocumentService() { 723 if ( sessionDocumentService == null ) { 724 sessionDocumentService = KNSServiceLocator.getSessionDocumentService(); 725 } 726 return this.sessionDocumentService; 727 } 728 729 /** 730 * @return the transactionManager 731 */ 732 public PlatformTransactionManager getTransactionManager() { 733 if ( transactionManager == null ) { 734 transactionManager = KRADServiceLocatorInternal.getTransactionManager(); 735 } 736 return this.transactionManager; 737 } 738 739 private ActionForm createNewActionForm(ActionMapping mapping, HttpServletRequest request) { 740 String name = mapping.getName(); 741 FormBeanConfig config = moduleConfig.findFormBeanConfig(name); 742 if (config == null) { 743 log.warn("No FormBeanConfig found under '" + name + "'"); 744 return (null); 745 } 746 ActionForm instance = RequestUtils.createActionForm(config, servlet); 747 if ("request".equals(mapping.getScope())) { 748 request.setAttribute(mapping.getAttribute(), instance); 749 } else { 750 HttpSession session = request.getSession(); 751 session.setAttribute(mapping.getAttribute(), instance); 752 } 753 return instance; 754 } 755}