Top

Interceptors

AT Interceptors can be generally intented as extension points to Aria Templates classes. Their main purpouse is to provide pre and post processing on class methods implemented from a specific interface, both synchronous and asynchronous ones. They are basically implemented as JavaScript functions.

Originally introduced for flow controllers, interceptors can be used against any AT Javascript interface. Make sure you read the necessary documentation about Aria Templates JavaScript classes and interfaces before reading further.

Indeed, if a class C implements an interface I, it then becomes possible for any other class to define an interceptor on an instance of C.

When doing so, each time a method is executed on C (assuming it is defined in I), then all defined interceptors will be notified and will be given the chance to execute extra logic before or after the method on C gets executed.

Additionally, if the method on C is asynchronous, the interceptors can also execute some logic after its callback has been called. Interceptors can also prevent the call of the original method if needed or change the parameters or return value.

General considerations

  • An interceptor is similar to an event listener: it is an Aria Templates callback that can be added or removed from an object for a given interface. An interceptor can be added to an object obj for an interface by a call to obj.$addInterceptor(interfaceName, interceptorCallback). It can be removed by a call to obj.$removeInterceptors(interfaceName, scope, fn).
  • Interceptor callbacks registered on an object obj for an interface MyInterface are called when a method is called through the MyInterface interface wrapper, or the interface wrapper of one of its super interfaces.
  • Interceptor callbacks are called multiple times:
    • CallBegin - before the corresponding method is called on the whole object,
    • CallEnd - after the corresponding method returns,
    • Callback - if the method is asynchronous, when the callback is called, before the call of the normal callback. Note that "Callback" can sometimes be called before "CallEnd" if the method calls its callback synchronously.
  • For CallBegin, interceptor callbacks are called in the order in which they registered with $addInterceptor, and for both CallEnd and Callback they are called in the reverse order.

Interceptor parameter

The type of the first parameter given to interceptor callbacks is described below, depending on the moment of the call. If a property is marked as "in", it means that the property is intended to be read by the interceptor callback. If a property is marked as "out", it means that the property can be changed by the interceptor callback.

  • CallBegin
File /core/interceptors/ParameterTypes.txt
 {

   step: "CallBegin" /* (in) */,
   method:           /* (in) method name in the interface */,
   args:             /* (in/out) array of the arguments which will be used for the call of the method */,
   cancelDefault:    /* (out) if set to true, the call to the method is canceled, and the wrapper returns returnValue. */,
   returnValue:      /* (out) if cancelDefault is true, this is the value returned by the wrapper */

 }
  • CallEnd
File /core/interceptors/ParameterTypes.txt
 {

   step: "CallEnd" /* (in) */,
   method:         /* (in) method name in the interface */,
   args:           /* (in) array of the arguments which were used for the call of the method */,
   returnValue:    /* (in/out) the value returned by the method, and the one which will be returned by the wrapper */

 }
  • Callback
File /core/interceptors/ParameterTypes.txt
 {

   step: "Callback" /* (in) */,
   method:          /* (in) method name in the interface */,
   args:            /* (in) array of the arguments which were used for the call of the method */,
   callbackResult:  /* (in/out) the value received as the first parameter of the wrapper callback
                       and to be sent as the first parameter of the real callback */
   cancelDefault:   /* (out) if set to true, the call of the real callback is canceled, and the wrapper callback
                       returns returnValue. */,
   returnValue:     /* (out) if cancelDefault is true, this is the value returned by the wrapper callback */
   callback:        /* (in) This parameter contains the real callback specified by the caller
                       (may also be a callback wrapper to notify next interceptor, if several interceptors are registered).
                       It can be used by the interceptor if it needs to do some asynchronous action at the end of the asynchronous
                       action from the module controller.
                       "If the interceptor uses this callback, it must set cancelDefault to true, so that the callback is not
                       called twice (both by the framework and the interceptor)." */,

 }

Interceptor calls sequence diagrams

Synchronous Method Call

The following sequence diagram visualizes the calls chain in the simple case of a Synchronous method:

  • add an interceptor for a specific interface
  • intercepted method execution
  • interceptor calls (Begin, End)

Interceptors - Sync Call.png

Asynchronous Method Call

The following sequence diagram visualizes the calls chain in the simple case of a Asynchronous method:

  • add an interceptor for a specific interface
  • interceptor calls (Begin, End, CallBack)
  • interaction with the backend server system

Interceptors - ASync Call.png

Example

Basically the whole interceptors mechanism can be properly setup by the following steps:

  • You have to define an interface; this is required as interceptors are bound to a specific interface
  • You have to implement the above interface
  • You have to register one or more interceptors on a specific interface

Interface definition

Here is the example of a simple interface definition with two functions, mySimpleFunction and myAsynchronousFunction.

File /core/interceptors/MyInterface.js
Aria.interfaceDefinition({

  $classpath : 'x.z.MyInterface',
  $interface : {
    mySimpleFunction : function () {},
    myAsynchronousFunction : {
      $type : "Function",
      $callbackParam : 0
    }
  }

});

Interface implementation

Here is an implementation of the previous interface:

File /core/interceptors/MyClass.js
Aria.classDefinition({

  $classpath : 'x.z.MyClass',
  $constructor : function () {},
  $implements : ['x.z.MyInterface'],
  $prototype : {
    mySimpleFunction : function () {
      alert('mySimpleFunction is called.');
    },
    myAsynchronousFunction : function (callback) {
      alert('myAsynchronousFunction is called.');
      aria.core.IO.asyncRequest({
        url : "myAction", // relative path pointing to a dynamic or static resource, for instance create a
                  // text file "something.txt"
        callback : {
          fn : this._returnFromServer,
          scope : this,
          args : {
            callback : callback
          }
        }
      });
    },
    _returnFromServer : function (res, args) {
      alert('An answer (to the request done in myAsynchronousFunction) has been received from the server: '
          + res.data);
      this.$callback(args.callback);
    }
  }

});

Interceptor registration

Here is an example of how to register an interceptor on an instance of the previous object with the previous interface and how to unregister it:

File /core/interceptors/Interceptor.js
var myObj = new x.z.MyClass();

var myItf = myObj.$interface('x.z.MyInterface');

var myInterceptor = function (param) {
  alert("Interceptor: " + param.method + " [" + param.step + "].");
};

myObj.$addInterceptor('x.z.MyInterface', myInterceptor);

myItf.mySimpleFunction();
myObj.mySimpleFunction();

var myCallback = function () {
  alert("myCallback is called.");

  myObj.$removeInterceptors('x.z.MyInterface', null, myInterceptor);
  myItf.mySimpleFunction();
};

myItf.myAsynchronousFunction(myCallback);

When the above piece of code is run, the following alerts will be displayed (in this order):

  • For myItf.mySimpleFunction():
    • Interceptor: mySimpleFunction [CallBegin]
    • mySimpleFunction is called
    • Interceptor: mySimpleFunction [CallEnd]
  • For myObj.mySimpleFunction():
    • mySimpleFunction is called
  • For myItf.myAsynchronousFunction(myCallback):
    • Interceptor: myAsynchronousFunction [CallBegin]
    • myAsynchronousFunction is called
    • Interceptor: myAsynchronousFunction [CallEnd]
    • An answer (to the request done in myAsynchronousFunction) has been received from the server: null
    • Interceptor: myAsynchronousFunction [Callback]
    • myCallback is called.
  • For myItf.mySimpleFunction(myCallback) in myCallback:
    • mySimpleFunction is called
This page was last modified on 2 July 2012, at 16:43 and has been viewed 1,096 times.