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