KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > beehive > netui > pageflow > internal > DefaultExceptionsHandler


1 /*
2  * Copyright 2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * $Header:$
17  */

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 JavaDoc;
35 import javax.servlet.http.HttpServletResponse JavaDoc;
36 import javax.servlet.ServletContext JavaDoc;
37 import javax.servlet.ServletException JavaDoc;
38 import javax.servlet.ServletRequest JavaDoc;
39 import javax.servlet.ServletResponse JavaDoc;
40 import javax.servlet.jsp.el.ELException JavaDoc;
41 import java.lang.reflect.InvocationTargetException JavaDoc;
42 import java.lang.reflect.UndeclaredThrowableException JavaDoc;
43 import java.lang.reflect.Method JavaDoc;
44 import java.util.Locale JavaDoc;
45 import java.util.Collection JavaDoc;
46 import java.util.Iterator JavaDoc;
47 import java.io.IOException JavaDoc;
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 JavaDoc CACHEID_EXCEPTION_HANDLER_METHODS = "_netui:exceptionHandlers";
72     
73     private transient PageFlowEventReporter _eventReporter;
74     
75
76     public DefaultExceptionsHandler( ServletContext JavaDoc servletContext )
77     {
78         init( null, null, servletContext );
79         _eventReporter = AdapterManager.getServletContainerAdapter( servletContext ).getEventReporter();
80     }
81
82     public void reinit( ServletContext JavaDoc servletContext )
83     {
84         super.reinit( servletContext );
85         _eventReporter = AdapterManager.getServletContainerAdapter( servletContext ).getEventReporter();
86     }
87
88     public ActionForward handleException( FlowControllerHandlerContext context, Throwable JavaDoc ex,
89                                           ActionMapping actionMapping, ActionForm form )
90             throws IOException JavaDoc, ServletException JavaDoc
91     {
92         FlowController flowController = context.getFlowController();
93         ServletRequest JavaDoc request = context.getRequest();
94         ServletResponse JavaDoc response = context.getResponse();
95         
96         if ( _log.isInfoEnabled() )
97         {
98             _log.info( "Handling uncaught Throwable " + ex.getClass().getName() );
99         }
100         
101         // Callback to the event reporter.
102
_eventReporter.exceptionRaised( ex, actionMapping, form, flowController, request, response );
103         long startTime = System.currentTimeMillis();
104     
105         //
106
// Look up the ExceptionConfig that's associated with this Throwable.
107
//
108
Class JavaDoc exClass = ex.getClass();
109         ExceptionConfig exceptionConfig = null;
110         if ( actionMapping != null )
111         {
112             exceptionConfig = actionMapping.findException( exClass );
113         }
114         else
115         {
116             // If the mapping was null (i.e., the exception happened before we got the action mapping), look for the
117
// exception only in the module config.
118
exceptionConfig = getExceptionConfig( exClass, flowController.getModuleConfig() );
119         }
120         
121         //
122
// If there was no applicable exception handler in the current ModuleConfig, look in Global.app's module.
123
//
124
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                     // This is the module that will be handling the exception. Ensure that its message resources are
138
// initialized.
139
assert request instanceof HttpServletRequest JavaDoc : request.getClass().getName();
140                     InternalUtils.selectModule( flowController.getModuleConfig().getPrefix(),
141                                                ( HttpServletRequest JavaDoc ) request, getServletContext() );
142                     PageFlowRequestWrapper.get( request ).setCurrentFlowController( flowController );
143                 }
144             }
145             
146             actionMapping = null; // This action mapping isn't relevant if we found the exception elsewhere.
147
}
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             //
158
// First, see if it should be handled by invoking a handler method.
159
//
160
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             // Callback to the event reporter.
172
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 JavaDoc ) throw ( ServletException JavaDoc ) ex;
187             if ( ex instanceof IOException JavaDoc ) throw ( IOException JavaDoc ) ex;
188             throw new ServletException JavaDoc( ex );
189         }
190         
191         return null;
192     }
193     
194     public Throwable JavaDoc unwrapException( FlowControllerHandlerContext context, Throwable JavaDoc ex )
195     {
196         //
197
// If the exception was thrown in a method we called through reflection, it will be an
198
// InvocationTargetException. Unwrap it. Do the same for the UndeclaredThrowable exceptions thrown when
199
// invoking methods through dynamic proxies.
200
//
201
if ( ex instanceof InvocationTargetException JavaDoc )
202         {
203             return unwrapException( context, ( ( InvocationTargetException JavaDoc ) ex ).getTargetException() );
204         }
205         
206         if ( ex instanceof UndeclaredThrowableException JavaDoc )
207         {
208             return unwrapException( context, ( ( UndeclaredThrowableException JavaDoc ) ex ).getUndeclaredThrowable() );
209         }
210         
211         if ( ex instanceof ServletException JavaDoc )
212         {
213             ServletException JavaDoc servletException = ( ServletException JavaDoc ) ex;
214             Throwable JavaDoc 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 JavaDoc ex, ActionMapping actionMapping )
222     {
223         //
224
// Put the exception in a place where Struts/NetUI tags will find it.
225
//
226
context.getRequest().setAttribute( Globals.EXCEPTION_KEY, ex );
227     }
228     
229     protected ExceptionConfig getExceptionConfig( Class JavaDoc 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                 // Loop again for our superclass (if any)
240
exceptionType = exceptionType.getSuperclass();
241             }
242         }
243         
244         return config;
245     }
246     
247     protected FlowController getFallbackFlowController( FlowController originalFlowController, Class JavaDoc exClass,
248                                                         ServletRequest JavaDoc request, ServletResponse JavaDoc response,
249                                                         ServletContext JavaDoc servletContext )
250     {
251         if ( originalFlowController instanceof PageFlowController )
252         {
253             Collection JavaDoc/*< SharedFlowController >*/ sharedFlows =
254                     ( ( PageFlowController ) originalFlowController ).getSharedFlows().values();
255             
256             for ( Iterator JavaDoc 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 JavaDoc : request.getClass().getName();
264         SharedFlowController globalApp = PageFlowUtils.getGlobalApp( ( HttpServletRequest JavaDoc ) request );
265         if ( globalApp != null && checkForExceptionConfig( globalApp, exClass, request ) ) return globalApp;
266         return null;
267     }
268     
269     private boolean checkForExceptionConfig( SharedFlowController sf, Class JavaDoc exClass, ServletRequest JavaDoc 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 JavaDoc throwable,
290                                                          ExceptionConfig exceptionConfig, ActionMapping actionMapping,
291                                                          ActionForm form )
292         throws IOException JavaDoc, ServletException JavaDoc
293     {
294         String JavaDoc handlerClassName = exceptionConfig.getHandler();
295         
296         try
297         {
298             //
299
// Get the exception-handler class and delegate to it.
300
//
301
assert context.getRequest() instanceof HttpServletRequest JavaDoc : "don't support ServletRequest currently.";
302             assert context.getResponse() instanceof HttpServletResponse JavaDoc : "don't support ServletResponse currently.";
303             HttpServletRequest JavaDoc request = ( HttpServletRequest JavaDoc ) context.getRequest();
304             HttpServletResponse JavaDoc response = ( HttpServletResponse JavaDoc ) context.getResponse();
305             ExceptionHandler handler = ( ExceptionHandler ) RequestUtils.applicationInstance( handlerClassName );
306             Exception JavaDoc ex = throwable instanceof Exception JavaDoc ? ( Exception JavaDoc ) throwable : new Exception JavaDoc( throwable );
307             ActionForward result = handler.execute( ex, exceptionConfig, actionMapping, form, request, response );
308                     
309             //
310
// See if the path is really relative to the webapp root, not relative to the module. Struts doesn't by default
311
// support paths that are webapp-relative.
312
//
313
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 JavaDoc e )
327         {
328             _log.error( "Could not find exception-handler class " + handlerClassName, e );
329             throw new ServletException JavaDoc( e );
330         }
331         catch ( InstantiationException JavaDoc e )
332         {
333             _log.error( "Could not create instance of exception-handler class " + handlerClassName, e );
334             throw new ServletException JavaDoc( e );
335         }
336         catch ( IllegalAccessException JavaDoc e )
337         {
338             _log.error( "Could not create instance of exception-handler class " + handlerClassName, e );
339             throw new ServletException JavaDoc( e );
340         }
341     }
342     
343     protected ActionForward invokeExceptionHandlerMethod( FlowControllerHandlerContext context, Throwable JavaDoc ex,
344                                                           PageFlowExceptionConfig exceptionConfig, ActionForm form,
345                                                           ActionMapping actionMapping )
346         throws IOException JavaDoc, ServletException JavaDoc
347     {
348         assert context.getRequest() instanceof HttpServletRequest JavaDoc : "don't support ServletRequest currently.";
349         assert context.getResponse() instanceof HttpServletResponse JavaDoc : "don't support ServletResponse currently.";
350         HttpServletRequest JavaDoc request = ( HttpServletRequest JavaDoc ) context.getRequest();
351         HttpServletResponse JavaDoc response = ( HttpServletResponse JavaDoc ) context.getResponse();
352         FlowController flowController = context.getFlowController();
353         String JavaDoc methodName = exceptionConfig.getHandler();
354         Object JavaDoc unwrappedFormBean = InternalUtils.unwrapFormBean( form );
355         Method JavaDoc method = getExceptionHandlerMethod( context, methodName, ex, unwrappedFormBean );
356
357         if ( method != null )
358         {
359             // First see if there's a hard-coded message set.
360
String JavaDoc message = exceptionConfig.getDefaultMessage();
361             ActionMessage error = null;
362             
363             if ( message != null )
364             {
365                 error = new ExpressionMessage( message, new Object JavaDoc[]{ ex.getMessage() } );
366                 
367                 try
368                 {
369                     // The message may be an expression. Evaluate it.
370
message = InternalExpressionUtils.evaluateMessage( message, form, request, getServletContext() );
371                 }
372                 catch ( ELException JavaDoc 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                 // No hard-coded message. Get the message based on the message key.
382
String JavaDoc messageKey = exceptionConfig.getKey();
383                         
384                 if ( messageKey != null && messageKey.length() > 0 )
385                 {
386                     message = getMessage( context, messageKey, null, null );
387                 }
388             }
389             
390             //
391
// Expose the exception to the errors tag.
392
//
393
String JavaDoc 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             //
404
// This shouldn't happen except in out-of-date-class situations. JpfChecker
405
// should prevent this at compilation time.
406
//
407
String JavaDoc err;
408             if ( form != null )
409             {
410                 err= Bundle.getString( "PageFlow_MissingExceptionHandlerWithForm",
411                                        new Object JavaDoc[]{ 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 JavaDoc[]{ flowController.getDisplayName(), err } );
420             return null;
421         }
422     }
423     
424     protected static void storeException( HttpServletRequest JavaDoc request, String JavaDoc key, ActionMessage error, String JavaDoc 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 JavaDoc getMessage( FlowControllerHandlerContext context, String JavaDoc messageKey, String JavaDoc bundle, Object JavaDoc[] args )
440     {
441         if ( bundle == null ) bundle = Globals.MESSAGES_KEY;
442         
443         ServletRequest JavaDoc 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 JavaDoc userLocale =
453                 request instanceof HttpServletRequest JavaDoc
454                 ? FlowController.retrieveUserLocale( ( HttpServletRequest JavaDoc ) 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 JavaDoc ex )
468     {
469         _log.error( "Unhandled Page Flow Exception", ex );
470
471         try
472         {
473             //
474
// PageFlowExceptions know what to do in the unhandled case.
475
//
476
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 JavaDoc ioEx )
485         {
486             _log.error( ioEx.getMessage(), ioEx );
487         }
488         
489         return false;
490     }
491     
492     /**
493      * Get an Exception handler method.
494      *
495      * @param methodName the name of the method to get.
496      * @param ex the Exception that is to be handled.
497      * @return the Method with the given name that handles the given Exception, or <code>null</code>
498      * if none matches.
499      */

500     protected Method JavaDoc getExceptionHandlerMethod( FlowControllerHandlerContext context, String JavaDoc methodName, Throwable JavaDoc ex,
501                                                 Object JavaDoc formBean )
502     {
503         FlowController flowController = context.getFlowController();
504         String JavaDoc cacheKey = methodName + '/' + ex.getClass().getName();
505         ClassLevelCache cache = ClassLevelCache.getCache( flowController.getClass() );
506         Method JavaDoc method = ( Method JavaDoc ) cache.get( CACHEID_EXCEPTION_HANDLER_METHODS, cacheKey );
507         
508         if ( method != null )
509         {
510             return method;
511         }
512      
513         Class JavaDoc flowControllerClass = flowController.getClass();
514         for ( Class JavaDoc exClass = ex.getClass(); exClass != null; exClass = exClass.getSuperclass() )
515         {
516             Class JavaDoc[] args = new Class JavaDoc[]{ exClass, String JavaDoc.class, String JavaDoc.class, Object JavaDoc.class };
517             Method JavaDoc foundMethod = InternalUtils.lookupMethod( flowControllerClass, methodName, args );
518             
519             //
520
// If we didn't find an exception-handler with the right signature, look for the deprecated signature with
521
// FormData as the last argument.
522
//
523
if ( foundMethod == null && ( formBean == null || formBean instanceof FormData ) )
524             {
525                 args = new Class JavaDoc[]{ exClass, String JavaDoc.class, String JavaDoc.class, FormData.class };
526                 foundMethod = InternalUtils.lookupMethod( flowControllerClass, methodName, args );
527             }
528             
529             //
530
// If we didn't find an exception-handler with the right signature, look for the deprecated signature with
531
// ActionForm as the last argument.
532
//
533
if ( foundMethod == null && ( formBean == null || formBean instanceof ActionForm ) )
534             {
535                 args = new Class JavaDoc[]{ exClass, String JavaDoc.class, String JavaDoc.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