AOP in JavaScript using Humax Framework

Facet Programming

Humax introduces Facet from version 0.2 onwards to support both aspect orientated programming and programming with metadata.

This section presents a simple inventory management example of Facet with Humax. This example introduces the syntax for writing aspects.

Note that the example code has been written based on Humax framework. If you are not familiar with some code, please visit Humax Web Framework at SourceForge.net.

Inventory Management Application

The sample we have used here is an inventory management which enables to fill and/or purchase stocks.

Humax.declareNamespace("HxTest");

//Inventory Class
//Constructor
HxTest.Inventory = function()
{
// Fields
this._capacity = 10;
this._available = 0;
this._reorderLevel = 3;
// Declare a multicast delegate called StockAlert -
//If you do not know delegates, visit tutorial
// section at http://humax.sourceforge.net
this.StockAlert = new Humax.MulticastDelegate("StockAlert",
Function("message, ordered, available",""));
// Declare an event called OnStockAlert
this.OnStockAlert = new Humax.Event(this.StockAlert);
}

HxTest.Inventory.prototype =
{
fillInventory : function(quantity)
{
if(this._available + quantity <= this._capacity)
{
this._available += quantity;
}else
{
// if inventory is full, raise the OnStockAlert event
this.OnStockAlert(Humax.EventAction.Trigger,
"Stock full", quantity,this._available);
}
},

orderItem : function(quantity)
{
if(quantity <= (this._available - this._reorderLevel))
{
this._available -= quantity;
}else
{
// if inventory reached minimum reorder level, raise the OnStockAlert event
this.OnStockAlert(Humax.EventAction.Trigger,
"Fill the stock, reached minium reorder level", quantity,this._available);
}
},
getAvailableStockCount : function() {return this._available;}
}

// declare the class Inventory as Humax compatible
Humax.declareClass("HxTest.Inventory",HxTest.Inventory);

The Inventory class has two primary methods fillInventory and orderItem. The _available and _reorderLevel fields are involved in filling and ordering in the inventory. The fillInventory and orderItem methods are used to fill and order items in inventory respectively. Both methods raise OnStockAlert event when the inventory size is changed.

Running the Application

In a html page, you can place the above code into html->head->script block. Add the following HTML declaration in the page.

<table border="0"><tbody><tr><td>Quantity to fill</td><td><input id="fillTextBox" type="text" /></td><td><input id="fillButton" type="button" onclick="fillButton_onclick()" value="Fill" /></td></tr><tr><td>Quantity to order</td><td><input id="orderTextBox" type="text" /></td><td><input id="orderButton" type="button" onclick="orderButton_onclick()" value="Order" /></td></tr></tbody></table>
<div>Inventory capacity: 10</div>

Add the following event handlers in html->head->script block. Also, a variable myInventory is declared for keeping Inventory instance global.

var myInventory = null;

function page_onload()
{
myInventory = new HxTest.Inventory();
myInventory.OnStockAlert(Humax.EventAction.AttachHandler,
stockAlertHandler)
}

function fillButton_onclick()
{
document.getElementById("output").innerHTML = "";
myInventory.fillInventory(
parseInt(document.getElementById("fillTextBox").value)
);
document.getElementById("status").innerHTML =
"Available Stock: " + myInventory.getAvailableStockCount();
}

function orderButton_onclick()
{
document.getElementById("output").innerHTML = "";
myInventory.orderItem(parseInt(document.getElementById("orderTextBox").value));
document.getElementById("status").innerHTML =
"Available Stock: " + myInventory.getAvailableStockCount();
}

function stockAlertHandler(message, ordered, available)
{
document.getElementById("output").innerHTML =
message + " Ordered:" + ordered.toString() + " Available: " + available.toString();
}

The page load event handler instantiates myInventory and attach stockAlertHandler as one of the handler of OnStockAlert event. The fillButton_onclick calls fillInventory method and displays the available stock to UI. The orderButton_onclick calls orderItem method and displays the available to the UI. The stockAlertHandler displays the messages, currently how many has been ordered and how many available in inventory on UI whenever a fill or order is invalid.

First Facet

As we know that AOP can be achieved by Humax Facet. We will now proceed with the development of our first facet. This facet monitors each filled item by displaying message before and after the fillInventory method in Inventory class. Add the following script below to above event handlers.

HxTest.InventoryValidatorFacet = function()
{
//calls base class "Humax.Facet" constructor implementation
this.base();
//Facet usage
this.$usage = Humax.FacetUsage.AllowMultiple |
Humax.FacetUsage.AllowAdvices;
}

The $usage and $target Fields

Like a Humax class declaration, you can declare a facet declaration using function constructor. The above code declare the facet InventoryValidatorFacet. In the constructor, it calls base class constructor implementation and specifies the target usage of this facet. The base class protected member $usage allows you to control the manner in which it is used by setting FacetUsage enumeration. In the above code, the facet InventoryValidatorFacet can be specified more than one for a given element using AllowMultiple enumeration value. The AllowAdvices should be specified if we want to use this facet to allow advices. Surprised!

Basically facet is an approach used in Humax for both meta-data programming and AOP. By default, a facet is only a container for metadata. As we know that Advice is a mean of specifying code to run at a join point. Even though you have implemented an aspect, without specifying AllowAdvices enumeration, your facet cannot be treated as Aspect.

In addition to $usage, a facet provides one more protected field named$target. This allows you to specify the type of join point if your facet is an aspect or application element if your facet is a metadata on which it is valid to apply a facet. The base class by default specifies Humax classes and interfaces. Note that by default a facet usage is AllowMultiple.

In the above code we specified that the we are going to use this facet as aspect which can be specified more than one for a give element. And it targets only classes and interfaces.

Now we are going to implement this aspect. Add the following code below to the facet declaration:

HxTest.InventoryValidatorFacet.prototype = 
{
beforeFillInventory : function(sourceObject, joinPointOriginalArgs)
{
alert("Going to fill");
},

afterFillInventory : function(sourceObject, joinPointOriginalArgs)
{
alert(joinPointOriginalArgs[0] + " item(s) has been filled");
},

attachHandlers : function()
{
//calls the base class "Humax.Facet" method attachHandlers
// to initiate before/after/around advice delegates
this.callBaseMethod("attachHandlers");
this.BeforeAdvice(Humax.EventAction.AttachHandler,
this.beforeFillInventory, this);
this.AfterAdvice(Humax.EventAction.AttachHandler,
this.afterFillInventory, this);
}
}

Humax.declareClass("HxTest.InventoryValidatorFacet",
HxTest.InventoryValidatorFacet, Humax.Facet);
// apply the facet on Inventory class to the member "fillInventory"
Humax.applyFacet(new HxTest.InventoryValidatorFacet(),
HxTest.Inventory, "fillInventory");

The above facet has three methods beforeFillInventory, afterFillInventory and attachHandlers. The first two methods are actually for our own facet methods. These methods will be called before and after the execution of myInventory.fillInventory() method. The beforeFillInventory method displays pop-up the message "Going to fill" and afterFillInventory method displays pop-up the message "No of items has been filled".

BeforeAdvice, AfterAdvice and AroundAdvice Events

The base class Facet provides three events BeforeAdvice, AfterAdvice and AroundAdvice to specify before, after and around advices of an aspect respectively.

The BeforeAdvice and AfterAdvice events expect their handlers signature as "sourceObject, joinPointOriginalArgs" where sourceObject contains the actual object on which the current advice going to execute; joinPointOriginalArgs contains the arguments passed to the target method. In the above code, the sourceObject is myInventory and joinPointOriginalArgs is the arguments actually passed tofillInventory() methods.

The AroundAdvice is different from before and after advices. It expect their handlers signature as "sourceObject, sourceMethod, joinPointOriginalArgs". The next section will explain this in detail.

attachHandlers Method

The attachHandlers is the member of Facet class which is used to specify which implementation of the aspect should be executed on a particular advice. This method first calls the base class implementation.

Note that it is must to call base class attachHandlers implementation for doing some initialization work.

In the above implementation, we added the beforeFillOrder to before advice and afterFillOrder to after advice.

Humax provides applyFacet() method to add a facet to a Humax type. This is the way to specify point cut. The first argument should be a valid Facet instance and second argument should be target type on which this facet is going to apply. The third argument should be the actual pointcut. In this case, it is fillInventory().

Note: Unlike other AOP frameworks, the current implementation of Facet does not allow you to specify pointcuts in a query like or primitive pointcut designators patterns. For example, one such pointcut looks like execution(* set*(*)) means that this pointcut matches a method-execution join point, if the method name starts with "set" and there is exactly one argument of any type. Instead, you should specify the memebers name in variable length argument or array of members as third argument.

Now execute the application.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“In order to understand recursion, one must first understand recursion.”