Handling ExceptionsThe standard Servlet mechanism for handling exceptions that occur on the server is to specify an error-page entry in the deployment descriptor (web.xml file) and allow the container to redirect to the configured resource. For example: <error-page> <exception-type>javax.faces.application.ViewExpiredException</exception-type> <location>/faces/viewExpired.xhtml</location> </error-page> This mechanism works fine as long as the exception makes it up to the servlet container. If the exception is handled in some other way and doesn't bubble up to the container, it renders the error-page strategy ineffective. With JSF 2 and ICEfaces, you can decide how you want to handle exceptions both on the server and the client. Server-side - ExceptionHandlersWith JSF 2, there is now a facility for hooking into the lifecycle and customizing how exceptions are handled. This is done using an ExceptionHandlerFactory and one or more custom ExceptionHandlers. By default, Mojarra provides implementations of both a standard ExceptionHandler for normal requests as well as an Ajax implementation for Ajax requests Exceptions for non-Ajax requests can be handled using the error-page facility. If no error page is specified then a default page is created JSF and sent back to the client. Exceptions for Ajax requests are contained within Ajax responses and do not trickle up to the container so the error-page strategy is not used. If you'd like to change this behavior in some way, you can write your own ExceptionHandler and deal with the exceptions on the server as you see fit. It's relatively simple to do. ICEfaces currently uses the technique described here to emit SessionExpired exceptions instead of ViewExpiredExceptions when appropriate.
1) Create one or more implementations of ExceptionHandlersExceptionHandlers are request-scoped resources - that is, there is a new one for each request. Exceptions that occur during a JSF lifecycle are generally queued to be handled by one or more of the ExceptionHandlers. Normally, you'd create an ExceptionHandler that extends ExceptionHandlerWrapper. The interesting work of finding the exceptions you're interested in and then processing those might look like this: public class MyExceptionHandler extends ExceptionHandlerWrapper { private ExceptionHandler wrapped; public MyExceptionHandler(ExceptionHandler wrapped) { this.wrapped = wrapped; } public javax.faces.context.ExceptionHandler getWrapped() { return wrapped; } public void handle() throws FacesException { Iterator events = getUnhandledExceptionQueuedEvents().iterator(); //Iterate through the queued exceptions while (events.hasNext()) { ExceptionQueuedEvent event = (ExceptionQueuedEvent) events.next(); ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource(); Throwable t = context.getException(); //See if it's an exception I'm interested in if (t instanceof MyCustomException) { try { //do something exciting with the exception } finally { //remove it if you processed it events.remove(); } } //Let the next ExceptionHandler(s) deal with the others getWrapped().handle(); } } } 2) Create an implementation of ExceptionHandlerFactoryExceptionHandlers are created by ExceptionHandlerFactories. The basic mechanism is to create an ExceptionHandlerFactory that links into the chain of existing ExceptionHandlerFactories. Your factory is used to create the appropriate ExceptionHandler for the request. Because it's a wrapper, we can chain them together by passing in the delegate when we create the instance. For example: public class MyExceptionHandlerFactory extends ExceptionHandlerFactory { ExceptionHandlerFactory delegateFactory; public MyExceptionHandlerFactory(ExceptionHandlerFactory delegateFactory) { this.delegateFactory = delegateFactory; } public ExceptionHandler getExceptionHandler() { return new MyExceptionHandler(delegateFactory.getExceptionHandler()); } } 3) Then you need to register your factory with JSF. You can add an entry into the faces-config.xml of the .war that includes your custom ExceptionHandler(s):<factory> <exception-handler-factory> org.company.faces.context.MyExceptionHandlerFactory </exception-handler-factory> </factory> 4) Then, in your application, exceptions of interest can be done like this:FacesContext fc = FacesContext.getCurrentInstance(); ExceptionQueuedEventContext ctxt = new ExceptionQueuedEventContext(fc, new MyCustomException("my bad") ); Application app = fc.getApplication(); app.publishEvent(fc, ExceptionQueuedEvent.class, ctxt); Client-side - JavaScript CallbacksWhen an exception does occur during an Ajax request, JSF will process those exceptions and send them back as Ajax responses. With stock JSF, you can register a callback and handle these errors as you see fit. This is done using the JavaScript API provided by JSF: //Assign your error handling function to a variable var jsfErrorCallback = function handleError(error) { //Handle all errors by simply redirecting to an error page window.location.href = "./generalError.xhtml"; } //Register your error handler as a callback jsf.ajax.addOnError(jsfErrorCallback); ICEfaces uses this same technique. By default, when ICEfaces is active on the page and an error comes back as an Ajax response, a small popup is displayed with a short description of the error. For example, if a session times out and you attempt to interact with the page it will cause a ViewExpiredException when JSF attempts to restore the view: If you would like to do something else with these errors, you can use much the same technique as described above: //Assign your error handling function to a variable var iceErrorCallback = function iceHandleError(statusCode, responseTxt, responseDOM) { //Handle all errors by simply redirecting to an error page window.location.href = "./generalError.xhtml"; } //Safely check if ICEfaces is available if (ice) { //Turn off the popups as we plan to handle this ourselves ice.configuration.disableDefaultIndicators = true; //Register your error handler as a callback ice.onServerError(iceErrorCallback); } When any of the default popups comes up there is also a transparent overlay rendered underneath. This overlay has a CSS class assigned so that it can be styled in concordance with the rest of the application. By default the CSS styles are as follows: .ice-status-indicator-overlay { position: absolute; background-color: white; z-index: 28000; opacity: 0.22; filter: alpha(opacity = 22); } Providing a stylesheet with new definitions for ice-status-indicator-overlay CSS class should be enough to override the default styling. |
Handling Exceptions
© Copyright 2021 ICEsoft Technologies Canada Corp.