1 18 package org.apache.beehive.netui.pageflow.internal; 19 20 import org.apache.beehive.netui.util.internal.InternalStringBuilder; 21 22 import org.apache.struts.action.ActionForward; 23 import org.apache.struts.action.ActionMapping; 24 import org.apache.struts.action.ActionForm; 25 import org.apache.struts.action.ExceptionHandler; 26 import org.apache.struts.action.ActionMessage; 27 import org.apache.struts.action.ActionMessages; 28 import org.apache.struts.Globals; 29 import org.apache.struts.util.RequestUtils; 30 import org.apache.struts.util.MessageResources; 31 import org.apache.struts.config.ModuleConfig; 32 import org.apache.struts.config.ExceptionConfig; 33 34 import javax.servlet.http.HttpServletRequest ; 35 import javax.servlet.http.HttpServletResponse ; 36 import javax.servlet.ServletContext ; 37 import javax.servlet.ServletException ; 38 import javax.servlet.ServletRequest ; 39 import javax.servlet.ServletResponse ; 40 import javax.servlet.jsp.el.ELException ; 41 import java.lang.reflect.InvocationTargetException ; 42 import java.lang.reflect.UndeclaredThrowableException ; 43 import java.lang.reflect.Method ; 44 import java.util.Locale ; 45 import java.util.Collection ; 46 import java.util.Iterator ; 47 import java.io.IOException ; 48 49 import org.apache.beehive.netui.pageflow.config.PageFlowExceptionConfig; 50 import org.apache.beehive.netui.pageflow.FlowController; 51 import org.apache.beehive.netui.pageflow.FormData; 52 import org.apache.beehive.netui.pageflow.PageFlowController; 53 import org.apache.beehive.netui.pageflow.SharedFlowController; 54 import org.apache.beehive.netui.pageflow.PageFlowManagedObjectException; 55 import org.apache.beehive.netui.pageflow.PageFlowEventReporter; 56 import org.apache.beehive.netui.pageflow.ExpressionMessage; 57 import org.apache.beehive.netui.pageflow.PageFlowUtils; 58 import org.apache.beehive.netui.pageflow.handler.ExceptionsHandler; 59 import org.apache.beehive.netui.pageflow.handler.FlowControllerHandlerContext; 60 import org.apache.beehive.netui.util.Bundle; 61 import org.apache.beehive.netui.util.internal.cache.ClassLevelCache; 62 import org.apache.beehive.netui.util.logging.Logger; 63 64 65 public class DefaultExceptionsHandler 66 extends DefaultHandler 67 implements ExceptionsHandler 68 { 69 private static final Logger _log = Logger.getInstance( DefaultExceptionsHandler.class ); 70 71 private static final String CACHEID_EXCEPTION_HANDLER_METHODS = "_netui:exceptionHandlers"; 72 73 private transient PageFlowEventReporter _eventReporter; 74 75 76 public DefaultExceptionsHandler( ServletContext servletContext ) 77 { 78 init( null, null, servletContext ); 79 _eventReporter = AdapterManager.getServletContainerAdapter( servletContext ).getEventReporter(); 80 } 81 82 public void reinit( ServletContext servletContext ) 83 { 84 super.reinit( servletContext ); 85 _eventReporter = AdapterManager.getServletContainerAdapter( servletContext ).getEventReporter(); 86 } 87 88 public ActionForward handleException( FlowControllerHandlerContext context, Throwable ex, 89 ActionMapping actionMapping, ActionForm form ) 90 throws IOException , ServletException 91 { 92 FlowController flowController = context.getFlowController(); 93 ServletRequest request = context.getRequest(); 94 ServletResponse response = context.getResponse(); 95 96 if ( _log.isInfoEnabled() ) 97 { 98 _log.info( "Handling uncaught Throwable " + ex.getClass().getName() ); 99 } 100 101 _eventReporter.exceptionRaised( ex, actionMapping, form, flowController, request, response ); 103 long startTime = System.currentTimeMillis(); 104 105 Class exClass = ex.getClass(); 109 ExceptionConfig exceptionConfig = null; 110 if ( actionMapping != null ) 111 { 112 exceptionConfig = actionMapping.findException( exClass ); 113 } 114 else 115 { 116 exceptionConfig = getExceptionConfig( exClass, flowController.getModuleConfig() ); 119 } 120 121 if ( exceptionConfig == null ) 125 { 126 FlowController fallbackFC = 127 getFallbackFlowController( flowController, exClass, request, response, getServletContext() ); 128 129 if ( fallbackFC != null ) 130 { 131 flowController = fallbackFC; 132 context = new FlowControllerHandlerContext( request, response, flowController ); 133 exceptionConfig = getExceptionConfig( exClass, flowController.getModuleConfig() ); 134 135 if ( exceptionConfig != null ) 136 { 137 assert request instanceof HttpServletRequest : request.getClass().getName(); 140 InternalUtils.selectModule( flowController.getModuleConfig().getPrefix(), 141 ( HttpServletRequest ) request, getServletContext() ); 142 PageFlowRequestWrapper.get( request ).setCurrentFlowController( flowController ); 143 } 144 } 145 146 actionMapping = null; } 148 149 if ( exceptionConfig != null ) 150 { 151 if ( _log.isDebugEnabled() ) 152 { 153 _log.debug( "Found exception-config for exception " + exClass.getName() 154 + ": handler=" + exceptionConfig.getHandler() + ", path=" + exceptionConfig.getPath() ); 155 } 156 157 if ( exceptionConfig instanceof PageFlowExceptionConfig ) 161 { 162 PageFlowExceptionConfig pfExceptionConfig = ( PageFlowExceptionConfig ) exceptionConfig; 163 164 if ( pfExceptionConfig.isHandlerMethod() ) 165 { 166 return invokeExceptionHandlerMethod( context, ex, pfExceptionConfig, form, actionMapping ); 167 } 168 } 169 170 ActionForward ret = invokeExceptionHandlerClass( context, ex, exceptionConfig, actionMapping, form ); 171 long timeTaken = System.currentTimeMillis() - startTime; 173 _eventReporter.exceptionHandled( ex, actionMapping, form, flowController, request, response, ret, timeTaken ); 174 175 return ret; 176 } 177 178 if ( _log.isErrorEnabled() ) 179 { 180 InternalStringBuilder msg = new InternalStringBuilder( "Throwable " ).append( exClass.getName() ); 181 _log.error( msg.append( " unhandled by the current page flow (and any shared flow)" ).toString(), ex ); 182 } 183 184 if ( ! getRegisteredExceptionsHandler().eatUnhandledException( context, ex ) ) 185 { 186 if ( ex instanceof ServletException ) throw ( ServletException ) ex; 187 if ( ex instanceof IOException ) throw ( IOException ) ex; 188 throw new ServletException ( ex ); 189 } 190 191 return null; 192 } 193 194 public Throwable unwrapException( FlowControllerHandlerContext context, Throwable ex ) 195 { 196 if ( ex instanceof InvocationTargetException ) 202 { 203 return unwrapException( context, ( ( InvocationTargetException ) ex ).getTargetException() ); 204 } 205 206 if ( ex instanceof UndeclaredThrowableException ) 207 { 208 return unwrapException( context, ( ( UndeclaredThrowableException ) ex ).getUndeclaredThrowable() ); 209 } 210 211 if ( ex instanceof ServletException ) 212 { 213 ServletException servletException = ( ServletException ) ex; 214 Throwable rootCause = servletException.getRootCause(); 215 if ( rootCause != null ) return unwrapException( context, rootCause ); 216 } 217 218 return ex; 219 } 220 221 public void exposeException( FlowControllerHandlerContext context, Throwable ex, ActionMapping actionMapping ) 222 { 223 context.getRequest().setAttribute( Globals.EXCEPTION_KEY, ex ); 227 } 228 229 protected ExceptionConfig getExceptionConfig( Class exceptionType, ModuleConfig moduleConfig ) 230 { 231 ExceptionConfig config = null; 232 233 if ( moduleConfig != null ) 234 { 235 while ( config == null && exceptionType != null ) 236 { 237 config = moduleConfig.findExceptionConfig( exceptionType.getName() ); 238 239 exceptionType = exceptionType.getSuperclass(); 241 } 242 } 243 244 return config; 245 } 246 247 protected FlowController getFallbackFlowController( FlowController originalFlowController, Class exClass, 248 ServletRequest request, ServletResponse response, 249 ServletContext servletContext ) 250 { 251 if ( originalFlowController instanceof PageFlowController ) 252 { 253 Collection sharedFlows = 254 ( ( PageFlowController ) originalFlowController ).getSharedFlows().values(); 255 256 for ( Iterator ii = sharedFlows.iterator(); ii.hasNext(); ) 257 { 258 SharedFlowController sf = ( SharedFlowController ) ii.next(); 259 if ( checkForExceptionConfig( sf, exClass, request ) ) return sf; 260 } 261 } 262 263 assert request instanceof HttpServletRequest : request.getClass().getName(); 264 SharedFlowController globalApp = PageFlowUtils.getGlobalApp( ( HttpServletRequest ) request ); 265 if ( globalApp != null && checkForExceptionConfig( globalApp, exClass, request ) ) return globalApp; 266 return null; 267 } 268 269 private boolean checkForExceptionConfig( SharedFlowController sf, Class exClass, ServletRequest request ) 270 { 271 ModuleConfig mc = sf.getModuleConfig(); 272 ExceptionConfig ec = getExceptionConfig( exClass, mc ); 273 274 if ( ec != null ) 275 { 276 if ( _log.isDebugEnabled() ) 277 { 278 _log.debug( "Found exception-config for " + exClass.getName() + " in SharedFlowController " 279 + sf.getDisplayName() ); 280 } 281 282 InternalUtils.setCurrentModule( mc, request ); 283 return true; 284 } 285 286 return false; 287 } 288 289 protected ActionForward invokeExceptionHandlerClass( FlowControllerHandlerContext context, Throwable throwable, 290 ExceptionConfig exceptionConfig, ActionMapping actionMapping, 291 ActionForm form ) 292 throws IOException , ServletException 293 { 294 String handlerClassName = exceptionConfig.getHandler(); 295 296 try 297 { 298 assert context.getRequest() instanceof HttpServletRequest : "don't support ServletRequest currently."; 302 assert context.getResponse() instanceof HttpServletResponse : "don't support ServletResponse currently."; 303 HttpServletRequest request = ( HttpServletRequest ) context.getRequest(); 304 HttpServletResponse response = ( HttpServletResponse ) context.getResponse(); 305 ExceptionHandler handler = ( ExceptionHandler ) RequestUtils.applicationInstance( handlerClassName ); 306 Exception ex = throwable instanceof Exception ? ( Exception ) throwable : new Exception ( throwable ); 307 ActionForward result = handler.execute( ex, exceptionConfig, actionMapping, form, request, response ); 308 309 if ( exceptionConfig instanceof PageFlowExceptionConfig 314 && ( ( PageFlowExceptionConfig ) exceptionConfig ).isPathContextRelative() ) 315 { 316 result.setContextRelative( true ); 317 } 318 319 if ( _log.isDebugEnabled() ) 320 { 321 _log.debug( "Exception-handler: forward to " + result.getPath() ); 322 } 323 324 return result; 325 } 326 catch ( ClassNotFoundException e ) 327 { 328 _log.error( "Could not find exception-handler class " + handlerClassName, e ); 329 throw new ServletException ( e ); 330 } 331 catch ( InstantiationException e ) 332 { 333 _log.error( "Could not create instance of exception-handler class " + handlerClassName, e ); 334 throw new ServletException ( e ); 335 } 336 catch ( IllegalAccessException e ) 337 { 338 _log.error( "Could not create instance of exception-handler class " + handlerClassName, e ); 339 throw new ServletException ( e ); 340 } 341 } 342 343 protected ActionForward invokeExceptionHandlerMethod( FlowControllerHandlerContext context, Throwable ex, 344 PageFlowExceptionConfig exceptionConfig, ActionForm form, 345 ActionMapping actionMapping ) 346 throws IOException , ServletException 347 { 348 assert context.getRequest() instanceof HttpServletRequest : "don't support ServletRequest currently."; 349 assert context.getResponse() instanceof HttpServletResponse : "don't support ServletResponse currently."; 350 HttpServletRequest request = ( HttpServletRequest ) context.getRequest(); 351 HttpServletResponse response = ( HttpServletResponse ) context.getResponse(); 352 FlowController flowController = context.getFlowController(); 353 String methodName = exceptionConfig.getHandler(); 354 Object unwrappedFormBean = InternalUtils.unwrapFormBean( form ); 355 Method method = getExceptionHandlerMethod( context, methodName, ex, unwrappedFormBean ); 356 357 if ( method != null ) 358 { 359 String message = exceptionConfig.getDefaultMessage(); 361 ActionMessage error = null; 362 363 if ( message != null ) 364 { 365 error = new ExpressionMessage( message, new Object []{ ex.getMessage() } ); 366 367 try 368 { 369 message = InternalExpressionUtils.evaluateMessage( message, form, request, getServletContext() ); 371 } 372 catch ( ELException e ) 373 { 374 _log.error( "error while evaluating expression in exception-handler for " + ex.getClass().getName(), e ); 375 } 376 } 377 378 379 if ( message == null ) 380 { 381 String messageKey = exceptionConfig.getKey(); 383 384 if ( messageKey != null && messageKey.length() > 0 ) 385 { 386 message = getMessage( context, messageKey, null, null ); 387 } 388 } 389 390 String msgKey = exceptionConfig.getKey(); 394 if ( error == null ) error = new ActionMessage( msgKey, ex.getMessage() ); 395 storeException( request, msgKey, error, exceptionConfig.getScope() ); 396 397 return flowController.invokeExceptionHandler( method, ex, message, unwrappedFormBean, 398 form, actionMapping, request, response, 399 exceptionConfig.isReadonly() ); 400 } 401 else 402 { 403 String err; 408 if ( form != null ) 409 { 410 err= Bundle.getString( "PageFlow_MissingExceptionHandlerWithForm", 411 new Object []{ methodName, form.getClass().getName() } ); 412 } 413 else 414 { 415 err = Bundle.getString( "PageFlow_MissingExceptionHandler", methodName ); 416 } 417 418 InternalUtils.sendError( "PageFlow_Custom_Error", null, request, response, 419 new Object []{ flowController.getDisplayName(), err } ); 420 return null; 421 } 422 } 423 424 protected static void storeException( HttpServletRequest request, String key, ActionMessage error, String scope ) 425 { 426 ActionMessages errors = new ActionMessages(); 427 errors.add( key, error ); 428 429 if ( "request".equals( scope ) ) 430 { 431 request.setAttribute( Globals.ERROR_KEY, errors ); 432 } 433 else 434 { 435 request.getSession().setAttribute( Globals.ERROR_KEY, errors ); 436 } 437 } 438 439 protected String getMessage( FlowControllerHandlerContext context, String messageKey, String bundle, Object [] args ) 440 { 441 if ( bundle == null ) bundle = Globals.MESSAGES_KEY; 442 443 ServletRequest request = context.getRequest(); 444 MessageResources resources = InternalUtils.getMessageResources(bundle, request, getServletContext()); 445 446 if ( resources == null ) 447 { 448 _log.error( "Could not find message-resources for bundle " + bundle ); 449 return null; 450 } 451 452 Locale userLocale = 453 request instanceof HttpServletRequest 454 ? FlowController.retrieveUserLocale( ( HttpServletRequest ) request, null ) 455 : null; 456 457 if ( args == null ) 458 { 459 return resources.getMessage( userLocale, messageKey ); 460 } 461 else 462 { 463 return resources.getMessage( userLocale, messageKey, args ); 464 } 465 } 466 467 public boolean eatUnhandledException( FlowControllerHandlerContext context, Throwable ex ) 468 { 469 _log.error( "Unhandled Page Flow Exception", ex ); 470 471 try 472 { 473 boolean prodMode = AdapterManager.getServletContainerAdapter( getServletContext() ).isInProductionMode(); 477 478 if ( ! prodMode && ex instanceof PageFlowManagedObjectException ) 479 { 480 ( ( PageFlowManagedObjectException ) ex ).sendError( context.getRequest(), context.getResponse() ); 481 return true; 482 } 483 } 484 catch ( IOException ioEx ) 485 { 486 _log.error( ioEx.getMessage(), ioEx ); 487 } 488 489 return false; 490 } 491 492 500 protected Method getExceptionHandlerMethod( FlowControllerHandlerContext context, String methodName, Throwable ex, 501 Object formBean ) 502 { 503 FlowController flowController = context.getFlowController(); 504 String cacheKey = methodName + '/' + ex.getClass().getName(); 505 ClassLevelCache cache = ClassLevelCache.getCache( flowController.getClass() ); 506 Method method = ( Method ) cache.get( CACHEID_EXCEPTION_HANDLER_METHODS, cacheKey ); 507 508 if ( method != null ) 509 { 510 return method; 511 } 512 513 Class flowControllerClass = flowController.getClass(); 514 for ( Class exClass = ex.getClass(); exClass != null; exClass = exClass.getSuperclass() ) 515 { 516 Class [] args = new Class []{ exClass, String .class, String .class, Object .class }; 517 Method foundMethod = InternalUtils.lookupMethod( flowControllerClass, methodName, args ); 518 519 if ( foundMethod == null && ( formBean == null || formBean instanceof FormData ) ) 524 { 525 args = new Class []{ exClass, String .class, String .class, FormData.class }; 526 foundMethod = InternalUtils.lookupMethod( flowControllerClass, methodName, args ); 527 } 528 529 if ( foundMethod == null && ( formBean == null || formBean instanceof ActionForm ) ) 534 { 535 args = new Class []{ exClass, String .class, String .class, ActionForm.class }; 536 foundMethod = InternalUtils.lookupMethod( flowControllerClass, methodName, args ); 537 } 538 539 if ( foundMethod != null ) 540 { 541 if ( _log.isDebugEnabled() ) 542 { 543 _log.debug( "Found exception handler for " + exClass.getName() ); 544 } 545 546 foundMethod.setAccessible( true ); 547 cache.put( CACHEID_EXCEPTION_HANDLER_METHODS, cacheKey, foundMethod ); 548 return foundMethod; 549 } 550 else 551 { 552 if ( _log.isErrorEnabled() ) 553 { 554 InternalStringBuilder msg = new InternalStringBuilder( "Could not find exception handler method " ); 555 msg.append( methodName ).append( " for " ).append( exClass.getName() ).append( '.' ); 556 _log.error( msg.toString() ); 557 } 558 } 559 } 560 561 return null; 562 } 563 564 public ExceptionsHandler getRegisteredExceptionsHandler() 565 { 566 return ( ExceptionsHandler ) super.getRegisteredHandler(); 567 } 568 } 569 | Popular Tags |