Create Reflective Objects

Up to this point of the tutorial, we have seen how to apply aspects to classes, altering their structure and/or behavior. But sometimes it is useful to alter the structure and/or behaviour of just one instance. If we only were limited to what we have seen we would have to create a empty subclass and apply the links to it. To avoid this kind of problems, Reflex provides an utility method Reflex.createObject, that is able to create a reflective instance of a class applying a especified set of links to it.

API

The signature of the method is:

public static <T> T createObject(
        String aClassName,
        Object[] aArgs,
        ConfigurationObject aConfObject
)

The first parameter is the class name we want to get an instance from. Then, the arguments to create the object must be specified. Finally, a ConfigurationObject instance that is where the links to apply are specified.

Let us review the (more interesting) part of the API of the ConfigurationObject class:

public SLink addSMetaobject(SMetaobject aSMetaobject)

This method allow us to add a structural metaobject to the configuration. Notice that we do not have to specify a ClassSelector (as in Links.get(..)) because the class is explicitly provided when we invoke the Reflex.createObject method.

It returns a SLink that is associated with the specified structural metaobject.

public BLink addBMetaobjectDefinition(
        MODefinition aMODef,
        Class aOp,
        OperationSelector aOpSelector
)

This method allows to add a metaobject definition for the operation and operation selector specified. As in the addSMetaobject method, we do not have to specify the ClassSelector nor the Hookset.

It returns the BLink assosiated with the specified metaobject definition. This BLink instance can be used to configure the different attributes of the relation: the control, the call descriptor, etc.

Example of Usage

In the Reflex distribution there is a complete example that uses all the methods listed above to create a reflective instance. The code can be found here.

Nevertheless, we are not going to review that example because we want to use one of the aspects we have seen in this tutorial to demostrate the use of this particuar API. We will take the tracing aspect as our example. We will apply it to just one instace of the Fibonacci class:

MODefinition theMO = new MODefinition.SharedMO(System.out);
 
ConfigurationObject theConfigObject = new ConfigurationObject();
theConfigObject.addBMetaobjectDefinition(
        theMO,
        MsgReceive.class,
        new DeclaredInOS("reflex.examples.fibonacci.app.Fibonacci")
);
 
BLink theLink = theConfigObject.getBLinkFor(theMO);
theLink.setControl(Control.BEFORE_AFTER);
theLink.setCall(
        Control.BEFORE,
        System.out.getClass().getName(), "println", new BeforeParameter())
);
theLink.setCall(
        Control.AFTER,
        System.out.getClass().getName(), "println", new AfterParameter())
);

Here we use the same metaobject definition and link configuration as in the original example. The difference is that we do not have to:

  • Specify a ClassSelector: because it is explicitly derived from the Reflex.createObject call
  • Define the BLink: because it is automatically created by the ConfigurationObject.

Then if we use this object:

System.out.println("--------------- Create Object -----------------------------");
Fibonacci theInstance = Reflex.createObject(
        Fibonacci.class, null, theConfigObject
);
System.out.println("fib[5] = " + theInstance.get(5));

We should see output like the following:Before message receive:

get([5])
Before message receive: get([4])
Before message receive: get([3])
Before message receive: get([2])
Before message receive: get([1])
After message receive: get is returning: [1]
Before message receive: get([0])
...
After message receive: get is returning: [5]
fib[5] = 5

However, if we invoke the method over a normal instace:

System.out.println("--------------- Normal Object -----------------------------");
theInstance = new Fibonacci();
System.out.println("fib[5] = " + theInstance.get(5));

We do not get any tracing:

--------------- Normal Object -------------------------------
fib[5] = 5

The complete code of this class (we do not need link providers here because the links are defined at runtime) is here. To run the example, the command is:

Windows
    %java -classpath
            "lib\javassist.jar;build\reflex-core.jar;build\reflex-examples.jar"
            reflex.examples.tutorial.CreateObjectTracingAspect

Linux
    %java -classpath
            "lib/javassist.jar:build/reflex-core.jar:build/reflex-examples.jar"
            reflex.examples.tutorial.CreateObjectTracingAspect

Limitations

There are some limitations for this part of Reflex that are not present if we use link providers. All of them are related to the way the Reflex.createObject method is implemented: It creates a subclass and applies the links to it. Because of this:

  • The specified class must not be declared final.
  • The operations must be callee-side. At this moment, there are two operations of this kind: MsgReceive and Creation.