001/** 002 * Copyright 2005-2016 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 org.apache.commons.lang.StringUtils; 019import org.apache.log4j.Logger; 020import org.apache.log4j.MDC; 021import org.apache.ojb.broker.OptimisticLockException; 022import org.apache.struts.Globals; 023import org.apache.struts.action.Action; 024import org.apache.struts.action.ActionForm; 025import org.apache.struts.action.ActionForward; 026import org.apache.struts.action.ActionMapping; 027import org.apache.struts.action.InvalidCancelException; 028import org.apache.struts.action.RequestProcessor; 029import org.apache.struts.config.FormBeanConfig; 030import org.apache.struts.config.ForwardConfig; 031import org.apache.struts.util.RequestUtils; 032import org.kuali.rice.core.api.CoreApiServiceLocator; 033import org.kuali.rice.core.api.config.property.ConfigurationService; 034import org.kuali.rice.core.api.util.RiceConstants; 035import org.kuali.rice.core.api.util.RiceKeyConstants; 036import org.kuali.rice.kns.exception.FileUploadLimitExceededException; 037import org.kuali.rice.kns.service.KNSServiceLocator; 038import org.kuali.rice.kns.service.SessionDocumentService; 039import org.kuali.rice.kns.util.ErrorContainer; 040import org.kuali.rice.kns.util.InfoContainer; 041import org.kuali.rice.kns.util.KNSConstants; 042import org.kuali.rice.kns.util.KNSGlobalVariables; 043import org.kuali.rice.kns.util.WarningContainer; 044import org.kuali.rice.kns.util.WebUtils; 045import org.kuali.rice.kns.web.EditablePropertiesHistoryHolder; 046import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase; 047import org.kuali.rice.kns.web.struts.form.KualiForm; 048import org.kuali.rice.kns.web.struts.form.pojo.PojoForm; 049import org.kuali.rice.krad.UserSession; 050import org.kuali.rice.krad.document.Document; 051import org.kuali.rice.krad.exception.ValidationException; 052import org.kuali.rice.krad.util.GlobalVariables; 053import org.kuali.rice.krad.util.KRADConstants; 054import org.kuali.rice.krad.util.KRADUtils; 055import org.kuali.rice.krad.util.MessageMap; 056import org.springframework.transaction.PlatformTransactionManager; 057import org.springframework.transaction.TransactionStatus; 058import org.springframework.transaction.support.TransactionCallback; 059import org.springframework.transaction.support.TransactionTemplate; 060import org.springmodules.orm.ojb.OjbOperationException; 061 062import javax.servlet.ServletException; 063import javax.servlet.http.HttpServletRequest; 064import javax.servlet.http.HttpServletResponse; 065import javax.servlet.http.HttpSession; 066import java.io.IOException; 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 ActionForward forward = processActionPerform(request, response, action, form, mapping); 217 218 if (forward != null) { 219 if (forward.getRedirect() && forward.getName()!= null && forward.getName().equals(KRADConstants.KRAD_INITIATED_DOCUMENT_VIEW_NAME)) { 220 LOG.info("Attempt to open a document with a status of \"Initiated\" detected"); 221 return; 222 } 223 // ProcessDefinition the returned ActionForward instance 224 processForwardConfig(request, response, forward); 225 } 226 } 227 } 228 229 230 /** 231 * This method gets the document number from the request. The request should have been processed already 232 * before this is called if it is multipart. 233 * 234 * @param request 235 * @return the document number, or null if one can't be found in the request. 236 */ 237 private String getDocumentNumber(HttpServletRequest request) { 238 String documentNumber = request.getParameter(KRADConstants.DOCUMENT_DOCUMENT_NUMBER); 239 240 // from lookup pages. 241 if (documentNumber == null) { 242 documentNumber = request.getParameter(KRADConstants.DOC_NUM); 243 } 244 245 if (documentNumber == null) { 246 documentNumber = request.getParameter("documentId"); 247 } 248 249 return documentNumber; 250 } 251 252 /** 253 * Hooks into populate process to call form populate method if form is an 254 * instanceof PojoForm. 255 */ 256 @Override 257 protected void processPopulate(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping) throws ServletException { 258 if (form instanceof KualiForm) { 259 // Add the ActionForm to GlobalVariables 260 // This will allow developers to retrieve both the Document and any 261 // request parameters that are not 262 // part of the Form and make them available in ValueFinder classes 263 // and other places where they are needed. 264 KNSGlobalVariables.setKualiForm((KualiForm) form); 265 } 266 267 // if not PojoForm, call struts populate 268 if (!(form instanceof PojoForm)) { 269 super.processPopulate(request, response, form, mapping); 270 return; 271 } 272 273 final String previousRequestGuid = request.getParameter(KualiRequestProcessor.PREVIOUS_REQUEST_EDITABLE_PROPERTIES_GUID_PARAMETER_NAME); 274 275 ((PojoForm)form).clearEditablePropertyInformation(); 276 ((PojoForm)form).registerStrutsActionMappingScope(mapping.getScope()); 277 278 String multipart = mapping.getMultipartClass(); 279 if (multipart != null) { 280 request.setAttribute(Globals.MULTIPART_KEY, multipart); 281 } 282 283 form.setServlet(this.servlet); 284 form.reset(mapping, request); 285 286 ((PojoForm)form).setPopulateEditablePropertiesGuid(previousRequestGuid); 287 // call populate on ActionForm 288 ((PojoForm) form).populate(request); 289 request.setAttribute("UnconvertedValues", ((PojoForm) form).getUnconvertedValues().keySet()); 290 request.setAttribute("UnconvertedHash", ((PojoForm) form).getUnconvertedValues()); 291 } 292 293 /** 294 * Hooks into validate to catch any errors from the populate, and translate 295 * the ErrorMap to ActionMessages. 296 */ 297 @Override 298 protected boolean processValidate(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping) throws IOException, ServletException, InvalidCancelException { 299 300 // skip form validate if we had errors from populate 301 if (GlobalVariables.getMessageMap().hasNoErrors()) { 302 if (form == null) { 303 return (true); 304 } 305 // Was this request cancelled? 306 if (request.getAttribute(Globals.CANCEL_KEY) != null) { 307 if (LOG.isDebugEnabled()) { 308 LOG.debug(" Cancelled transaction, skipping validation"); 309 } 310 return (true); 311 } 312 313 // Has validation been turned off for this mapping? 314 if (!mapping.getValidate()) { 315 return (true); 316 } 317 318 // call super to call forms validate 319 super.processValidate(request, response, form, mapping); 320 } 321 322 publishMessages(request); 323 if (!GlobalVariables.getMessageMap().hasNoErrors()) { 324 // Special handling for multipart request 325 if (form.getMultipartRequestHandler() != null) { 326 if (LOG.isDebugEnabled()) { 327 LOG.debug(" Rolling back multipart request"); 328 } 329 form.getMultipartRequestHandler().rollback(); 330 } 331 332 // Fix state that could be incorrect because of validation failure 333 if (form instanceof PojoForm) { 334 ((PojoForm) form).processValidationFail(); 335 } 336 337 // Was an input path (or forward) specified for this mapping? 338 String input = mapping.getInput(); 339 if (input == null) { 340 if (LOG.isDebugEnabled()) { 341 LOG.debug(" Validation failed but no input form available"); 342 } 343 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, getInternal().getMessage("noInput", mapping.getPath())); 344 return (false); 345 } 346 347 if (moduleConfig.getControllerConfig().getInputForward()) { 348 ForwardConfig forward = mapping.findForward(input); 349 processForwardConfig(request, response, forward); 350 } else { 351 internalModuleRelativeForward(input, request, response); 352 } 353 354 return (false); 355 } 356 return true; 357 } 358 359 /** 360 * Checks for return from a lookup or question, and restores the action form 361 * stored under the request parameter docFormKey. 362 */ 363 @Override 364 protected ActionForm processActionForm(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) { 365 366 String documentNumber = getDocumentNumber(request); 367 if (documentNumber != null) { MDC.put(MDC_DOC_ID, documentNumber); } 368 369 UserSession userSession = (UserSession) request.getSession().getAttribute(KRADConstants.USER_SESSION_KEY); 370 371 String docFormKey = request.getParameter(KRADConstants.DOC_FORM_KEY); 372 String methodToCall = request.getParameter(KRADConstants.DISPATCH_REQUEST_PARAMETER); 373 String refreshCaller = request.getParameter(KRADConstants.REFRESH_CALLER); 374// String searchListRequestKey = request.getParameter(KRADConstants.SEARCH_LIST_REQUEST_KEY); 375 String documentWebScope = request.getParameter(KRADConstants.DOCUMENT_WEB_SCOPE); 376 377 if (mapping.getPath().startsWith(KRADConstants.REFRESH_MAPPING_PREFIX) || KRADConstants.RETURN_METHOD_TO_CALL.equalsIgnoreCase(methodToCall) || 378 KRADConstants.QUESTION_REFRESH.equalsIgnoreCase(refreshCaller) || KRADConstants.TEXT_AREA_REFRESH.equalsIgnoreCase(refreshCaller) || KRADConstants 379 .SESSION_SCOPE.equalsIgnoreCase(documentWebScope)) { 380 ActionForm form = null; 381 // check for search result storage and clear 382 GlobalVariables.getUserSession().removeObjectsByPrefix(KRADConstants.SEARCH_LIST_KEY_PREFIX); 383 384 // We put different type of forms such as document form, lookup form 385 // in session but we only store document form in 386 // database. 387 if (userSession.retrieveObject(docFormKey) != null) { 388 LOG.debug("getDecomentForm KualiDocumentFormBase from session"); 389 form = (ActionForm) userSession.retrieveObject(docFormKey); 390 } else if (StringUtils.isNotBlank(documentNumber)) { 391 form = getSessionDocumentService().getDocumentForm(documentNumber, docFormKey, userSession, request.getRemoteAddr()); 392 } 393 request.setAttribute(mapping.getAttribute(), form); 394 if (!KRADConstants.SESSION_SCOPE.equalsIgnoreCase(documentWebScope)) { 395 userSession.removeObject(docFormKey); 396 } 397 // we should check whether this is a multipart request because we 398 // could have had a combination of query parameters and a multipart 399 // request 400 String contentType = request.getContentType(); 401 String method = request.getMethod(); 402 if (("POST".equalsIgnoreCase(method) && contentType != null && contentType.startsWith("multipart/form-data"))) { 403 // this method parses the multipart request and adds new 404 // non-file parameters into the request 405 WebUtils.getMultipartParameters(request, null, form, mapping); 406 } 407 // The form can be null if the document is not a session document 408 if (form != null) { 409 return form; 410 } 411 } 412 413 // Rice has the ability to limit file upload sizes on a per-form basis, 414 // so the max upload sizes may be accessed by calling methods on 415 // PojoFormBase. 416 // This requires that we are able know the file upload size limit (i.e. 417 // retrieve a form instance) before we parse a mulitpart request. 418 ActionForm form = super.processActionForm(request, response, mapping); 419 420 // for sessiondocument with multipart request 421 String contentType = request.getContentType(); 422 String method = request.getMethod(); 423 424 if ("GET".equalsIgnoreCase(method) && StringUtils.isNotBlank(methodToCall) && form instanceof PojoForm && 425 ((PojoForm) form).getMethodToCallsToBypassSessionRetrievalForGETRequests().contains(methodToCall)) { 426 return createNewActionForm(mapping, request); 427 } 428 429 // if we have a multipart request, parse it and return the stored form 430 // from session if the doc form key is not blank. If it is blank, then 431 // we just return the form 432 // generated from the superclass processActionForm method. Either way, 433 // we need to parse the mulitpart request now so that we may determine 434 // what the value of the doc form key is. 435 // This is generally against the contract of processActionForm, because 436 // processPopulate should be responsible for parsing the mulitpart 437 // request, but we need to parse it now 438 // to determine the doc form key value. 439 if (("POST".equalsIgnoreCase(method) && contentType != null && contentType.startsWith("multipart/form-data"))) { 440 WebUtils.getMultipartParameters(request, null, form, mapping); 441 docFormKey = request.getParameter(KRADConstants.DOC_FORM_KEY); 442 documentWebScope = request.getParameter(KRADConstants.DOCUMENT_WEB_SCOPE); 443 444 documentNumber = getDocumentNumber(request); 445 446 if (KRADConstants.SESSION_SCOPE.equalsIgnoreCase(documentWebScope) || 447 (form instanceof KualiDocumentFormBase && WebUtils 448 .isDocumentSession(((KualiDocumentFormBase) form).getDocument(), 449 (KualiDocumentFormBase) form))) { 450 451 Object userSessionObject = userSession.retrieveObject(docFormKey); 452 if ( userSessionObject != null && userSessionObject instanceof ActionForm ) { 453 LOG.debug("getDocumentForm KualiDocumentFormBase from session"); 454 form = (ActionForm) userSessionObject; 455 } else { 456 ActionForm tempForm = getSessionDocumentService().getDocumentForm(documentNumber, docFormKey, userSession, request.getRemoteAddr()); 457 if ( tempForm != null ) { 458 form = tempForm; 459 } 460 } 461 462 request.setAttribute(mapping.getAttribute(), form); 463 if (form != null) { 464 return form; 465 } 466 } 467 } 468 return form; 469 } 470 471 /** 472 * Hook into action perform to handle errors in the error map and catch 473 * exceptions. 474 * 475 * <p> 476 * A transaction is started prior to the execution of the action. This 477 * allows for the action code to execute efficiently without the need for 478 * using PROPAGATION_SUPPORTS in the transaction definitions. The 479 * PROPAGATION_SUPPORTS propagation type does not work well with JTA. 480 */ 481 @Override 482 protected ActionForward processActionPerform(final HttpServletRequest request, final HttpServletResponse response, final Action action, final ActionForm form, final ActionMapping mapping) throws IOException, ServletException { 483 try { 484 TransactionTemplate template = new TransactionTemplate(getTransactionManager()); 485 ActionForward forward = null; 486 try { 487 forward = (ActionForward) template.execute(new TransactionCallback() { 488 public Object doInTransaction(TransactionStatus status) { 489 ActionForward actionForward = null; 490 try { 491 actionForward = action.execute(mapping, form, request, response); 492 } catch (Exception e) { 493 if (e.getMessage()!= null && e.getMessage().equals(KRADConstants.KRAD_INITIATED_DOCUMENT_VIEW_NAME)) { 494 ConfigurationService kualiConfigurationService = CoreApiServiceLocator 495 .getKualiConfigurationService(); 496 StringBuffer sb = new StringBuffer(); 497 sb.append(kualiConfigurationService.getPropertyValueAsString(KRADConstants.KRAD_URL_KEY)); 498 sb.append(kualiConfigurationService.getPropertyValueAsString(KRADConstants.KRAD_INITIATED_DOCUMENT_URL_KEY)); 499 return new ActionForward(KRADConstants.KRAD_INITIATED_DOCUMENT_VIEW_NAME, sb.toString() ,true); 500 } 501 // the doInTransaction method has no means for 502 // throwing exceptions, so we will wrap the 503 // exception in 504 // a RuntimeException and re-throw. The one caveat 505 // here is that this will always result in 506 // the 507 // transaction being rolled back (since 508 // WrappedRuntimeException is a runtime exception). 509 throw new WrappedRuntimeException(e); 510 } 511 if (status.isRollbackOnly()) { 512 // this means that the struts action execution 513 // caused the transaction to rollback, we want to 514 // go ahead 515 // and trigger the rollback by throwing an exception 516 // here but then return the action forward 517 // from this method 518 throw new WrappedActionForwardRuntimeException(actionForward); 519 } 520 return actionForward; 521 } 522 }); 523 } catch (WrappedActionForwardRuntimeException e) { 524 forward = e.getActionForward(); 525 } 526 527 publishMessages(request); 528 saveMessages(request); 529 saveAuditErrors(request); 530 531 if (form instanceof PojoForm) { 532 if (((PojoForm)form).getEditableProperties() == null 533 || ((PojoForm)form).getEditableProperties().isEmpty()) { 534 EditablePropertiesHistoryHolder holder = (EditablePropertiesHistoryHolder) GlobalVariables.getUserSession().getObjectMap().get( 535 KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME); 536 if (holder == null) { 537 holder = new EditablePropertiesHistoryHolder(); 538 } 539 540 final String guid = holder.addEditablePropertiesToHistory(((PojoForm)form).getEditableProperties()); 541 ((PojoForm)form).setActionEditablePropertiesGuid(guid); 542 GlobalVariables.getUserSession().addObject(KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME, holder); 543 } 544 } 545 546 return forward; 547 548 } catch (Exception e) { 549 if (e instanceof WrappedRuntimeException) { 550 e = (Exception) e.getCause(); 551 } 552 if (e instanceof ValidationException) { 553 // add a generic error message if there are none 554 if (GlobalVariables.getMessageMap().hasNoErrors()) { 555 556 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, RiceKeyConstants.ERROR_CUSTOM, e.getMessage()); 557 } 558 559 if (form instanceof PojoForm) { 560 if (((PojoForm)form).getEditableProperties() == null 561 || ((PojoForm)form).getEditableProperties().isEmpty()) { 562 EditablePropertiesHistoryHolder holder = (EditablePropertiesHistoryHolder) GlobalVariables.getUserSession().getObjectMap().get( 563 KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME); 564 if (holder == null) { 565 holder = new EditablePropertiesHistoryHolder(); 566 } 567 568 final String guid = holder.addEditablePropertiesToHistory(((PojoForm)form).getEditableProperties()); 569 ((PojoForm)form).setActionEditablePropertiesGuid(guid); 570 GlobalVariables.getUserSession().addObject(KRADConstants.EDITABLE_PROPERTIES_HISTORY_HOLDER_ATTR_NAME, holder); 571 } 572 } 573 // display error messages and return to originating page 574 publishMessages(request); 575 return mapping.findForward(RiceConstants.MAPPING_BASIC); 576 } 577 578 publishMessages(request); 579 580 return (processException(request, response, e, form, mapping)); 581 } 582 } 583 584 private static class WrappedActionForwardRuntimeException extends RuntimeException { 585 private ActionForward actionForward; 586 587 public WrappedActionForwardRuntimeException(ActionForward actionForward) { 588 this.actionForward = actionForward; 589 } 590 591 public ActionForward getActionForward() { 592 return actionForward; 593 } 594 } 595 596 /** 597 * Adds more detailed logging for unhandled exceptions 598 * 599 * @see org.apache.struts.action.RequestProcessor#processException(HttpServletRequest, 600 * HttpServletResponse, Exception, ActionForm, ActionMapping) 601 */ 602 @Override 603 protected ActionForward processException(HttpServletRequest request, HttpServletResponse response, Exception exception, ActionForm form, ActionMapping mapping) throws IOException, ServletException { 604 ActionForward actionForward = null; 605 606 try { 607 actionForward = super.processException(request, response, exception, form, mapping); 608 } catch (IOException e) { 609 logException(e); 610 throw e; 611 } catch (ServletException e) { 612 // special case, to make OptimisticLockExceptions easier to read 613 Throwable rootCause = e.getRootCause(); 614 if (rootCause instanceof OjbOperationException) { 615 OjbOperationException ooe = (OjbOperationException) rootCause; 616 617 Throwable subcause = ooe.getCause(); 618 if (subcause instanceof OptimisticLockException) { 619 OptimisticLockException ole = (OptimisticLockException) subcause; 620 621 StringBuffer message = new StringBuffer(e.getMessage()); 622 623 Object sourceObject = ole.getSourceObject(); 624 if (sourceObject != null) { 625 message.append(" (sourceObject is "); 626 message.append(sourceObject.getClass().getName()); 627 message.append(")"); 628 } 629 630 e = new ServletException(message.toString(), rootCause); 631 } 632 } 633 634 logException(e); 635 throw e; 636 } 637 return actionForward; 638 } 639 640 private void logException(Exception e) { 641 LOG.error("unhandled exception thrown by KualiRequestProcessor.processActionPerform", e); 642 } 643 644 /** 645 * Checks for errors in the error map and transforms them to struts action 646 * messages then stores in the request. 647 */ 648 private void publishMessages(HttpServletRequest request) { 649 MessageMap errorMap = GlobalVariables.getMessageMap(); 650 if (!errorMap.hasNoErrors()) { 651 ErrorContainer errorContainer = new ErrorContainer(errorMap); 652 653 request.setAttribute("ErrorContainer", errorContainer); 654 request.setAttribute(Globals.ERROR_KEY, errorContainer.getRequestErrors()); 655 request.setAttribute("ErrorPropertyList", errorContainer.getErrorPropertyList()); 656 } 657 658 if (errorMap.hasWarnings()) { 659 WarningContainer warningsContainer = new WarningContainer(errorMap); 660 661 request.setAttribute("WarningContainer", warningsContainer); 662 request.setAttribute("WarningActionMessages", warningsContainer.getRequestMessages()); 663 request.setAttribute("WarningPropertyList", warningsContainer.getMessagePropertyList()); 664 } 665 666 if (errorMap.hasInfo()) { 667 InfoContainer infoContainer = new InfoContainer(errorMap); 668 669 request.setAttribute("InfoContainer", infoContainer); 670 request.setAttribute("InfoActionMessages", infoContainer.getRequestMessages()); 671 request.setAttribute("InfoPropertyList", infoContainer.getMessagePropertyList()); 672 } 673 } 674 675 /** 676 * Checks for messages in GlobalVariables and places list in request 677 * attribute. 678 */ 679 private void saveMessages(HttpServletRequest request) { 680 if (!KNSGlobalVariables.getMessageList().isEmpty()) { 681 request.setAttribute(KRADConstants.GLOBAL_MESSAGES, KNSGlobalVariables.getMessageList().toActionMessages()); 682 } 683 } 684 685 /** 686 * Checks for messages in GlobalVariables and places list in request 687 * attribute. 688 */ 689 private void saveAuditErrors(HttpServletRequest request) { 690 if (!KNSGlobalVariables.getAuditErrorMap().isEmpty()) { 691 request.setAttribute(KNSConstants.AUDIT_ERRORS, KNSGlobalVariables.getAuditErrorMap()); 692 } 693 } 694 695 /** 696 * A simple exception that allows us to wrap an exception that is thrown out 697 * of a transaction template. 698 */ 699 @SuppressWarnings("serial") 700 private static class WrappedRuntimeException extends RuntimeException { 701 public WrappedRuntimeException(Exception e) { 702 super(e); 703 } 704 } 705 706 /** 707 * @return the sessionDocumentService 708 */ 709 public SessionDocumentService getSessionDocumentService() { 710 if ( sessionDocumentService == null ) { 711 sessionDocumentService = KNSServiceLocator.getSessionDocumentService(); 712 } 713 return this.sessionDocumentService; 714 } 715 716 /** 717 * @return the transactionManager 718 */ 719 public PlatformTransactionManager getTransactionManager() { 720 if ( transactionManager == null ) { 721 transactionManager = KNSServiceLocator.getTransactionManager(); 722 } 723 return this.transactionManager; 724 } 725 726 private ActionForm createNewActionForm(ActionMapping mapping, HttpServletRequest request) { 727 String name = mapping.getName(); 728 FormBeanConfig config = moduleConfig.findFormBeanConfig(name); 729 if (config == null) { 730 log.warn("No FormBeanConfig found under '" + name + "'"); 731 return (null); 732 } 733 ActionForm instance = RequestUtils.createActionForm(config, servlet); 734 if ("request".equals(mapping.getScope())) { 735 request.setAttribute(mapping.getAttribute(), instance); 736 } else { 737 HttpSession session = request.getSession(); 738 session.setAttribute(mapping.getAttribute(), instance); 739 } 740 return instance; 741 } 742}