The subject of this chapter, Interceptors, allow you to override/ extend the default behaviour of Baritus.
Interceptors provide a means to encapsulate cross-cutting code that is executed on pre-defined points in the line of execution. Interceptors are classes that implement one or more interfaces from the package 'nl.openedge.baritus.interceptors'.
Interceptors can be used to decorate the normal execution. Also, by throwing FlowExceptions, interceptors can alter the flow of execution. An interceptor can throw a FlowException if it wants Baritus to stop normal processing and go to the given declared view (using ReturnNowFlowException) such as 'error', or dispatch to an arbitrary - non declared - URL (using DispatchNowFlowException) location.
The following interceptors are available.
BeforeMakeFormBeanInterceptor. Registered instances will have their command method executed before the method makeFormBean is called.
BeforePopulationInterceptor. Registered instances will have their command method executed before population and validation is done.
PopulationErrorInterceptor. Registered instances will have their command method executed if the model failed to populate, or did not pass validation.
AfterPopulationInterceptor. Registered instances will have their command method executed before the normal action execution took place, but after the form bean was populated.
PerformExceptionInterceptor. Registered instances will have their command method executed if an unhandeld exception occured while executing the perform method.
AfterPerformInterceptor. Registered instances will have their command method executed after the normal action execution took place. That means that makeFormBean was called, the form was populated and - if that population was succesfull - the command method was called prior to this execution.
You cannot be sure that the form was populated successfully. Therefore it's dangerous and generally bad practice to rely on form properties that are populated from the http request. A good usage example: a lot of views need data to fill their dropdown lists etc. In this method, you could load that data and save it in the form (or as a request attribute if that's your style). As this method is allways executed, you have a guaranteed data delivery to your view, regardless the normal execution outcome of the control.
An interceptor can alter the line of execution by thowing a FlowException. An example of when to do this is: if you expect a session variable to be set, but there is none, you might want to redirect to a search page or an error page. Another example: on certain non handled exceptions that are thrown in the perform method, you might want to redirect to a specific error page that is different from the error page that is shown when population errors occured.
There are two types of FlowExceptions that can be thrown by interceptors.
ReturnNowFlowException. The property 'view' in this exception is used to return to Maverick as the view to display. In effect, this view must correspond to a declared view (like 'error' or 'detail') in your Maverick configuration.
DispatchNowFlowException. The property 'dispatchPath' is used to dispatch to directly (like 'errors/critical.jsp', 'mymacro.vm' or 'http://www.myserver.com/support'.
Finally, with FlowException property 'executeOtherInterceptors' you can set on the exception that indicates whether (some of the) other interceptors should be executed. FlowExceptions thrown by these interceptors are ignored.
Here is an example of how to use interceptors. In part of our imaginary application, we want to be sure that an object was saved in the session, and after we checked it is, we want to set that object as one of our form bean properties. If our object is not found however, we want to cancel the normal Baritus behaviour and do a redirect (or actually dispatch) to another location right away. Because we set that object in the form bean just before population takes place (but after the makeFormBean method was called), it can be populated by the request paremeters etc. as well.
First, there is our Interceptor.
public class LoadFooFromSessionInterceptor implements BeforePopulationInterceptor { /** * Before population takes place, we get our Foo object from the session, check * it's actually there (is not null) and set it in our form, so that it can be * populated as well. */ public void doBeforePopulation( ControllerContext cctx, FormBeanContext formBeanContext) throws ServletException, DispatchNowFlowException, ReturnNowFlowException { HttpServletRequest request = cctx.getRequest(); HttpSession session = request.getSession(); Foo ourFoo = (Foo) session.getAttribute(Constants.SESSION_KEY_FOO_OBJECT); if (ourFoo == null) { // ourFoo does not exist in the session... redirect to 'home.m' throw new DispatchNowFlowException("home.m"); } else { // we're ok, set in form and exit method normally FooBarForm form = (FooBarForm) formBeanContext.getBean(); form.setFoo(ourFoo); } } }
Then, we should register the interceptor in the controllers we want to use it.
public void init(Element controllerNode) throws ConfigException { addInterceptor(new LoadFooFromSessionInterceptor()); }
Baritus will now call this interceptor each time this control is used right after calling makeFormBean and just before population and validation takes place.
Just like you can do with validators, you can stack interceptors as well.
public void init(Element controllerNode) throws ConfigException { addInterceptor(new FooInterceptor()); addInterceptor(new BarInterceptor()); addInterceptor(new MooInterceptor()); }
In the above example, all interceptors can be of the same type(s), and will be called in order of registration. Thus, FooInterceptor is called first, BarInterceptor after that and MooInterceptor is called last.