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