Home > Flex, tutorial > Fabrication: Intro to using Interceptors

Fabrication: Intro to using Interceptors

April 12th, 2009

In one of my previous articles, I spoke about how to use Fabrication, or at least one portion of Fabrication (respondTo and reactTo). Given that there are other really cool things about Fabricaiton, I felt it deserved some more attention. One thing Fabrication supports are interceptors. Interceptors allow you to stop the normal flow of your notification to your commands or mediators. You can stop the notification, alter them, even send a different notification in its place. In this article, I will be focusing on a few different uses for interceptors.

What real world applications could an interceptor have? If we want to do something, why use an interceptor? Why not just put that logic in the same spot where we fire off the notification? One nice real world use interceptors could have is for logging purposes. If we are making data requests and we want to output what is being sent out with each request, we can use an interceptor to do it. Now we could just drop that logging information right before we make our request, but then we are putting logging logic where it doesnt belong. If we separate it out into a interceptor, not only is it separated out, but we can also just disable it if we dont want to see it anymore.

First make sure you get your environment setup. Download these libraries if you dont already have it setup.

After you have added them to your build path, you are ready to get started.

Here is what we are going to build.
fabricationapp
In this fake application, I have setup two String filters (one called first name, the other called last name). I also included a date chooser. This will our ‘request criteria’ for our purposes. Of course, it is not hooked into anything like a database or webservice since this is just for examples sake. Notice there are two buttons. These are going to represent different request objects that we are calling. Once again, for examples sake, there are no grids or charts here. I just wanted something to show two different objects for our logging example.

Here is our interface used for the request objects.

public interface IRequestObject{
    function get requestDataType():String;
    function get date():Date;
    function set date(d:Date):void;
    function get filter1():String;
    function set filter1(filter:String):void;
    function get filter2():String;
    function set filter2(filter:String):void;
}

I will use an “Abstract class” (note: actionscript does not have real abstract classes) to set the default behavior for the getters and setters, then I will have a ChartDataRequest and GridDataRequest object to make our calls. They just look like this:

  public class ChartDataRequest extends AbstractDataRequest{
    private static const TYPE:String = "Chart Data Request";
 
    override public function get requestDataType():String{
      return TYPE;
    }
  }
  public class GridDataRequest extends AbstractDataRequest{
    private static const TYPE:String="Grid Data Request";
 
    override public function get requestDataType():String{
      return TYPE;
    }
  }

Nothing special at all here. Now that that is out of the way, here is the Fabrication part that you are here for. This is going to be our debugging interceptor.

  public class DebugInterceptor extends AbstractInterceptor
  {
    override public function intercept():void{
      var request:IRequestObject = notification.getBody() as IRequestObject;
      trace("***************");
      trace("Debug information for the following class");
      trace("Type : " + request.requestDataType);
      trace("Date Range : " + request.date);
      trace("Filter 1 : " + request.filter1);
      trace("Filter 2 : " + request.filter2);
      trace("***************");
    }
  }

All we have to do is make sure that our class extends the AbstractInterceptor and that it overrides the intercept method. The notification property is set within the AbstractInterceptor. Our IRequestObject (a grid or chart request) is set as the body. After that, trace statements are used to output the results. So how do we make our interceptor get called? Here is our StartUpCommand.

    public class FabricationExampleStartUpCommand extends SimpleFabricationCommand{
        override public function execute(notification:INotification):void{
            registerMediator(new FabricationExampleMediator(notification.getBody()));
            fabFacade.registerInterceptor(FabricationConstants.RETRIEVEDATA, DebugInterceptor);
        }
    }

After the mediator is register, the DebugInterceptor is then registered. What this is saying is “Whenever the notification for FabricationConstants.RETRIEVEDATA is sent, use this to intercept it”. Looking at the mediator, you will see how it gets set and called.

public class FabricationExampleMediator extends FlexMediator{
    public static const NAME:String = "FabricationExampleMediator";
    public function FabricationExampleMediator(viewComponent:Object=null){
        super(NAME, viewComponent);
    }
 
    override public function onRegister():void{
        super.onRegister();
    }
    public function respondToPopulateRequest(note:INotification):void{
        var request:IRequestObject = note.getBody() as IRequestObject;
        request.filter1 = fabricationExample.filter1.text;
        request.filter2 = fabricationExample.filter2.text;
        request.date = fabricationExample.dateChooser.selectedDate;
        sendNotification(FabricationConstants.RETRIEVEDATA,request);
    }
 
    public function reactToChartSubmitButtonClick(e:MouseEvent):void{
        sendNotification(FabricationConstants.POPULATEREQUEST,new ChartDataRequest());
    }
    public function reactToGridSubmitButtonClick(e:MouseEvent):void{
        sendNotification(FabricationConstants.POPULATEREQUEST,new GridDataRequest());
    }
 
    public function get chartSubmitButton():Button{
        return  fabricationExample.chartSubmitButton as Button;
    }
    public function get gridSubmitButton():Button{
        return  fabricationExample.gridSubmitButton as Button;
    }
 
    public function get confirmation():Text{
        return fabricationExample.confirmation as Text;
    }
    public function respondToRetrieveData(note:INotification):void{
        confirmation.text = "Data came back at " + new Date();
    }
 
    public function get fabricationExample():FabricationExample{
        return viewComponent as FabricationExample;
    }
}

We have the reactTos setup for our buttons click events. When a button is clicked, it will send out a notification to populate the request with the associated object. This will get picked up by our respondToPopulateRequest function. It grabs the IRequest object attached to it, populates it, then sends it off with the Retrieve Data notifcation. Normally it would go straight to the respondToRetrieveData function, but here it will go to our interceptor first. If you debug the application (or if you have flash tracer installed in firefox), you should get an output similar to this after selecting a date and clicking the grid button:

***************
Debug information for the following class
Type : Grid Data Request
Date Range : Mon Apr 13 00:00:00 GMT-0400 2009
Filter 1 : First Name
Filter 2 : Last Name
***************

Following that, you will get a message below saying “Data came back at whatever the current time is”. And that is about it.

Now I mentioned that we are also able to stop the notifications as well with these interceptors. Try running the app again, but don’t select a date range. In the output, you will get the word ‘null’. You dont want your application searching for null dates. You probably also want them to at least enter a last name. We can use another interceptor to handle all this verification for us.

  public class VerificationInterceptor extends AbstractInterceptor
  {
    override public function intercept():void{
      var request:IRequestObject = notification.getBody() as IRequestObject;
      if (request.filter1.length == 0 || request.filter2.length == 0){
        sendNotification(FabricationConstants.ABORTED,"must filter on both first name and last name");
        abort();
      }
      if (request.date == null){
        sendNotification(FabricationConstants.ABORTED,"must select a date");
        abort();
      }
    }
  }

This will be used to verify that the data going out on our request is sufficient. We want to make sure that our request have both a first and last name, and a date is selected. If not, we are going to send out an Abort notification and then abort the whole process. If it hits that abort() function, it will never make it to the respondToRetrieveData. Instead, the Aborted notification will be fired off with the corresponding error.

Make sure you add this line to your StartUpCommand:

fabFacade.registerInterceptor(FabricationConstants.RETRIEVEDATA, VerificationInterceptor);

and this to your Mediator:

public function respondToAborted(note:INotification):void{
   confirmation.text = "The data request failed due to the following reason: " + note.getBody();
}

Now if you fail to enter the correct criteria for your request, you will get the error message instead.

Interceptors can provide a great deal of useful functionality to our applications. These are just a few examples of how they can be used. Not only that, but it once again helps us separate out logic that does not need to be in specific places. If I wanted to use the logger in a normal object, I would have to put that logic into the actual request call. With this, the request call doesn’t know anything about it, which is perfect.

Here is the source

Marcel Flex, tutorial , , ,