You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 13 Next »

Error CSS Stylesheet macro - Import URL 'http://felix.apache.org/ipojo/site/superfish.css' is not on the allowlist. If you want to include this content, contact your Confluence administrator to request adding this URL to the Allowlist.
Error CSS Stylesheet macro - Import URL 'http://felix.apache.org/ipojo/site/style.css' is not on the allowlist. If you want to include this content, contact your Confluence administrator to request adding this URL to the Allowlist.

Dive into the iPOJO Manipulation depths

iPOJO (primitive) components are based on a bytecode manipulation. Don't be afraid, you don't have to be fluent in bytecode to understand ☺. This page explains how the manipulation works and how your class file is transformed. This manipulation takes care to NOT change the behavior of the class.

Why manipulating bundles

The iPOJO manipulation goals are twofold:
• Preparing classes to be managed at runtime
• Translating XML metadata into an internal format avoiding runtime XML parsing

iPOJO follows container principles and more specially follows the inversion of control and dependency injections. So, iPOJO container manages the class instantiation, and injection (service, properties and so on...). In order to inject values and supervise the execution inside component implementation class, iPOJO provides an interception and injection framework. One part of this framework is implemented as a class manipulation.

Once manipulated, iPOJO manipulation process translates the XML metadata and annotations in an internal syntax. This avoids embedding a XML parser at runtime.

Manipulation of the bytecode

For each class used as an implementation class, the manipulation process prepares the class to be managed at runtime. Basically, this means interception methods and field accesses. This manipulation is not dependent of the component type metadata. A class will always results in the same final class regardless of the metadata. This allows using a class as the implementation of several component types. As illustrated in the following image, the manipulated class instance interacts with an instance manager which delegates to plugged handlers. The metadata impacts the set of plugged handler. So, it does not impact neither the manipulated class, nor the instance manager (which is the iPOJO container foundation).

The following image explains what happens during the manipulation.

First, each manipulated class implements the POJO interface. This interface introduces a method allowing getting a reference on the container of the class. So, you can detect that an object is managed by iPOJO by checking if the object implements this interface.
Moreover, a special field, named __IM is injected. At runtime, this field contains a reference on the container object (InstanceManager.
For each field, getter and setter method are generated. They are called __get$FIELD_NAME and __set$FIELD_NAME. Moreover a boolean field is injected named __F$FIELD_NAME. This flag enables or disables the container delegation for this field. When, the delegation is disabled, the field is managed as a regular field: getter and setter methods just return and set the field value. When the delegation is enabled, the field never receives a value. The container stores the field value. The getter method asks to the container to get the value. The setter method asks to the container to update the stored value. All the field accesses (except ones from specific constructors) of the class are adapted to call the getter or the setter method.
In order to intercept methods, each method is substituted by a generated method. In fact, each method is renamed to __METHOD_NAME, and becomes private. The original method is replaced by iPOJO code. This code allows method interception. As for fields, the method delegation is enabled or disabled by an injected Boolean field. If the method has not to be intercepted, the iPOJO method (replacing the method) just calls the original method. Otherwise, the iPOJO method notifies the container of method entries, exits, and potential errors.
Finally, the constructors are also manipulated. Empty constructors and constructors receiving a BundleContext object are manipulated as others methods. However, they receive another argument: the InstanceManager. This instance manager is set just after the call to the super constructor. While setting the instance manager, all flags enabling / disabling delegation are set. This allows using injected values inside the constructor code itself. Constructors receiving other kind of arguments are not manipulated, and so can be used to create non-managed instance. In this case, fields won't be delegated, and methods won't be intercepted.

The annotation special case

The manipulation has a special behavior when a visible (at runtime) method or constructor annotation is detected. As the methods are replaced by private methods, the annotations are moved to the iPOJO replacements such as in:

public class MyClass {
  @MyAnnotation
  public void myMethod() {
     //my code here
 }
}

is transformed into:

public class MyClass {
  @MyAnnotation
  public void myMethod() {
     // iPojo code here
 }
  private void _myMethod() {
   // my code here
 } 

Avoiding XML at runtime

iPOJO does not use XML at runtime. The manipulation process replaces the XML metadata by an internal format (between LISP and XML). The XML metadata are translated and written inside the bundle manifest. Moreover, the component type declarations (<component> XML element) are completed with manipulation metadata. Those metadata are computed during the bytecode manipulation and avoid using reflection at runtime (requiring loading the class) to get class elements (such as available fields, methods, implemented interfaces, and so on).
If you want to see how XML metadata are tortured, just open the manifest file of a manipulated bundle, and look at the iPOJO-COMPONENTS entry...

  • No labels