AJS_DefMgr

Motivation

All three of the AJS object's client-components (AJS_Validator, AJS_Logger and AJS_ODL) operate according to the same principle, in that they accept user-defined 'definition objects' that describe the methods that should be validated, logged, or which should serve as triggers for the loading of resources on demand.

In addition to supporting the immediate application of a given definition object, they also allow you to place definition objects in a queue for subsequent application. Moreover, AJS_Validator and AJS_Logger support the concept of suspension and resumption of validation and logging activity respectively, which requires the ability to iterate through each definition that has been applied so far.

It would be inelegant, redundant and therefore inefficient for the client components to replicate the implementation of this common functionality internally, and so AspectJS implements it as an auxiliary component called AJS_DefMgr (AspectJS DEFinition ManaGeR).

You do not need to understand AJS_DefMgr to use the client components of AspectJS, as they handle all the details internally, and so you need only pass a reference to createAJS_DefMgr to the relevant client-component creation function. Moreover, you do not need to understand definition objects at all if working directly with the AJS object. Given this, you do not need to read the brief user-guide for AJS_DefMgr unless you have your own use for the component.

You are, however, advised strongly to read this overview when consulting the user guides for AJS_Validator, AJS_Logger or AJS_ODL, as it considers points that are common to all three client components, and most of the information here is not repeated in those treatises.

Contents Motivation
Direct Application
Queued Application
Queue Dynamics
No Method-Name Validation
Applying Definition Objects to Constructors

Direct Application

There are two ways of applying a definition object. The first is by using the relevant apply... method of the client component in question, where the component uses a DefMgr object to assist in the application process, and to maintain a list of the methods to which a given definition has been applied.

When called, a given apply... method accepts three arguments. These are:

  1. A mandatory reference to the object to which the definition in question should be applied.
  2. A mandatory reference to the definition object itself.
  3. An optional MethodOwner string, which should be the name of the object (it's up to you what you call it – if you provide a value, the argument is used only in exception notifications).

In the case of AJS_Validator and AJS_Logger, and as pointed out above, the use of a DefMgr allows suspension and resumption of the effect of the definition objects in question. It also allows AJS_Validator to ensure that a given ValidationDef is not applied multiple times to the same method, and with AJS_Logger it prevents application of the same LogDef to the same method through the same logging object. Similarly, with AJS_ODL, it prevents multiple applications of a given LoadDef to the same method for the same resource-type (AJS_ODL uses a different DefMgr for each LoadMethod that it supports).

See the relevant sections in the AJS_Validator , AJS_Logger and AJS_ODL user guides for specific information on these points.

Note also that DefMgr objects do support the removal of definitions from their applied-definitions sets, which is a feature that AJS_ODL needs. But you cannot do this in the context of the DefMgrs that the other AJS client-components employ, because they do not give you access to those internal objects.

Queued Application

The second way of applying definition objects involves the use of the corresponding push... method of a given client component, and these also accept the same three arguments as their apply... counterparts.

When invoked, the push... method in question adds the definition object you supply to an internal queue that is maintained by the DefMgr in question, where it remains until a subsequent call to the corresponding apply-Validation-/Log-/Load-DefQueue method. Using the services of the relevant DefMgr, that method then applies each definition in the queue to its respective method-owner object, in the order in which the definitions were pushed onto the queue. The DefMgr empties the queue during this process, and adds each definition to the corresponding list of applied-definitions (a given DefMgr always has one pending-definitions and one applied-definitions set).

Queued application is of value in that the definitions you employ may rely on the prior existence of other objects. For example, it may be that a preLogger function you use can be created only by calling a method of an existing object, and this may require a particular evaluation-order in respect of the JavaScript source-files that your application employs. Other constraints may render it tedious, if not impossible, to enforce such an evaluation-order, or you may wish to control the cardinality of the affix functions that different AJS-object clients apply. Given this, placing all definitions (or a proportion at least) in the application into a queue, after which you apply them en-masse, enables you to avoid such headaches.

Queue Dynamics

Pending definition-object queues operate in a well-defined manner. To enumerate the relevant points:

First: you cannot push a definition to a queue, where one or more of the methods indicated by that definition (either explicitly, or by default when the definition object in question has no MethodNames property) are covered by other definitions in that queue. Nor can you push a definition when one of the methods it concerns can be found the corresponding applied-definitions set.

Second: you cannot remove a given definition from a given queue once it has been pushed to that queue. That is: none of the client components' Push...Def methods have a corresponding 'Pop...Def'. The AJS object does permit the removal of affix functions from a given interceptee, but there is no compelling case for making such functionality available in the client-component interfaces.

Third: the methods indicated in a given definition need be function-properties of the MethodOwner only at the time that the definition is applied. This means that you can push a given definition to a given client, where the method owner to which it relates has yet to acquire one or more of the methods that the definition indicates.

Fourth: if, at the time of application, a method indicated by the MethodNames property (if present) of a given definition does not correspond to a function-property of the MethodOwner in question, then the DefMgr in question will call the exception handler that the client object passed it (the exception handler that you provided when you called, for example createAJS_Validator). It will also remove the offending definition-object from the pending-definitions queue, and so any methods to which the definition has not been applied will remain unaffected. Furthermore, any definitions that were behind the offending definition-object in the queue will remain there until the relevant apply... method of the corresponding client-component is called again. (In other words: a non-existent-method exception stalls the application process.)

Fifth: given that a pending-definition queue empties as its definitions are applied, further calls to the corresponding apply... client-method do not cause existing applied-definitions to be re-applied. Moreover, you are free to push additional definition objects onto a given queue at any time, and to call the corresponding apply... method, as many times as needed as your application executes.

No Method-Name Validation

Note that neither the client components nor the AJS_DefMgr objects they employ validate the method-names held in the MethodNames properties of definition objects. This is because object-properties in JavaScript can be created and accessed using 'square bracket' notation, and this means that property-names (and hence method-names) can be composed of characters that would otherwise be illegal.

For example, it is possible to create and invoke a method thus:

   var MyObj = { };

   MyObj["123_CanYouSee"] = function () { }; // Define...

   MyObj["123_CanYouSee"] ();                // ...And call

...Whereas either of the following will fail:

   MyObj.123_CanYouSee = function () { };    // No way.
   MyObj.123_CanYouSee ();                   // Ain't happening either.

Given this, any validation logic would be forced to accept essentially anything, and so would be redundant. Moreover, you may have a real need to use AspectJS in conjunction with such method names, where validating for names that work only with the 'dot' membership-syntax would restrict the library's scope in that respect.

Applying Definition Objects to Constructors

Note that when using AJS_Validator, AJS_Logger or AJS_ODL, you can apply a definition to any method in an application, other than constructors. The AJS-object user guide goes into this in depth, but to summarise here: intercepting calls to a constructor does not work, and will cause otherwise-working code to fail. Given this, and as the AJS-object user guide states, the (somewhat awkward) solution in this case is to wrap the call to a given constructor in a forwarding-method (which could be a member of the Global object), and to apply the definition to that.