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