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