Chapter 52. MOP: Metaobject Protocol

52.1. Implementation: a Meta-Object Protocol

ProActive is built on top of a metaobject protocol (MOP) that permits reification of method invocation and constructor call. As this MOP is not limited to the implementation of our transparent remote objects library, it also provides an open framework for implementing powerful libraries for the Java language.

As for any other element of ProActive, this MOP is entirely written in Java and does not require any modification or extension to the Java Virtual Machine, as opposed to other metaobject protocols for Java {Kleinoeder96}. It makes extensive use of the Java Reflection API, thus requiring JDK 1.1 or higher. JDK 1.2 is required in order to suppress default Java language access control checks when executing reified non-public method or constructor calls.

52.2. Principles

If the programmer wants to implement a new metabehavior using our metaobject protocol, he or she has to write both a concrete (as opposed to abstract) class and an interface. The concrete class provides an implementation for the metabehavior he or she wants to achieve while the interface contains its declarative part.

The concrete class implements interface Proxy and provides an implementation for the given behavior through the method reify:

 public Object reify (MethodCall c) throws Throwable; 

This method takes a reified call as a parameter and returns the value returned by the execution of this reified call. Automatic wrapping and unwrapping of primitive types is provided. If the execution of the call completes abruptly by throwing an exception, it is propagated to the calling method, just as if the call had not been reified.

The interface that holds the declarative part of the metabehavior has to be a subinterface of Reflect (the root interface for all metabehaviors implemented using ProActive). The purpose of this interface is to declare the name of the proxy class that implements the given behavior. Then, any instance of a class implementing this interface will be automatically created with a proxy that implements this behavior, provided that this instance is not created using the standard new keyword but through a special static method: MOP.newInstance. This is the only required modification to the application code. Another static method, MOP.newWrapper, adds a proxy to an already-existing object; the turnActive function of ProActive, for example, is implemented through this feature.

52.3. Example of a different metabehavior: EchoProxy

Here's the implementation of a very simple yet useful metabehavior: for each reified call, the name of the invoked method is printed out on the standard output stream and the call is then executed. This may be a starting point for building debugging or profiling environments.

class EchoProxy extends Object implements Proxy {
  // here are constructor and variables declaration
  // [...]
  public Object reify (MethodCall c) throws Throwable {
      System.out.println (c.getMethodName());
      return c.execute (targetObject);
  }
}
interface Echo extends Reflect {
  public String PROXY_CLASS= 'EchoProxy';
}

52.3.1. Instantiating with the metabehavior

Instantiating an object of any class with this metabehavior can be done in three different ways: instantiation-based, class-based or object-based. Let's say we want to instantiate a Vector object with an Echo behavior.

  • Standard Java code would be:

     Vector v = new Vector(3); 
    
  • ProActive code, with instantiation-based declaration of the metabehavior (the last parameter is null because we do not have any additional parameter to pass to the proxy):

      Object[] params = {new Integer (3)};
      Vector v = (Vector) MOP.newInstance('Vector', params, 'EchoProxy', null);
    
  • with class-based declaration:

      public class MyVector extends Vector implements Echo {}
      Object[] params = {new Integer (3)} ;
      Vector v = (Vector) MOP.newInstance('Vector', params, null);
    
  • with object-based declaration:

      Vector v = new Vector (3);
      v=(Vector) MOP.newWrapper('EchoProxy',v);
    

    This is the only way to give a metabehavior to an object that is created in a place where we cannot edit source code. A typical example could be an object returned by a method that is part of an API distributed as a JAR file, without source code. Please note that, when using newWrapper, the invocation of the constructor of the class Vector is not reified.

52.4. The Reflect interface

All the interfaces used for declaring metabehaviors inherit directly or indirectly from Reflect. This leads to a hierarchy of metabehaviors such as shown in the figure below.

Metabehavior hierarchy

Figure 52.1. Metabehavior hierarchy

Reflect Interface and sub-interfaces diagram

Note that ImplicitActive inherits from Active to highlight the fact that implicit synchronization somewhere always relies on some hidden explicit mechanism. Interfaces inheriting from Reflect can thus be logically grouped and assembled using multiple inheritance in order to build new metabehaviors out of existing ones.

52.5. Limitations

Due to its commitment to be a 100% Java library, the MOP has a few limitations:

  • Calls sent to instances of final classes (which includes all arrays) cannot be reified.

  • Primitive types cannot be reified because they are not instance of a standard class.

  • Final classes (which includes all arrays) cannot be reified because they cannot be subclassed.