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