Features

Dynamic composition

The dynamic composition allows merging objects into a conglomerate. By doing so the objects can alter their state and behavior. Objects can arbitrarily join and disjoin the composite at runtime by calling $join and $disjoin operators. It results in very flexible structures which can be fixed or adapted at runtime. One object can declare that its field or method is context-overridable what means that once it becomes a part of a composite the member is overridden by a member of another so called dominant component.

Note:
A member is declared context-overridable (or submissive) by annotating it with @FromContext. A member is declared contextual (or dominant) by annotating it with @ToContext

Every component can play a role in the composite. This role is a label assigned by the $role operator. The role can be used for example in message routing or in Autocollecting.

The following example shows how a simple greeting composite can be assembled. The GreetingService class publishes two greeting methods sayHello and sayGoodBye. Both methods delegate the call to method printMessage. This an abstract method annotated with annotation FromContext. Calling this method causes emitting an internal message which can be intercepted by other component in the composite.

    public abstract class GreetingService {

      public void sayHello() {
          printMessage("Hello!");
      }

      public void sayGoodBye() {
          printMessage("Good Bye!");
      }

      @FromContext
      abstract void printMessage(String message);

    }

The purpose of the MessagePrinter class is intercepting printMessage invocations and printing the message to a java.io.PrintStream output stream. The output stream is held in context-overridable (submissive) field out. The default value, ie. the value used when there is no dominant counterpart in the composite, is System.out.

    public class MessagePrinter {

      @FromContext
      private PrintStream out = System.out;

      @ToContext
      public void printMessage(String message) {
          out.println(message);
      }

    }

By inserting an instance of the following class PrinterContext into the composite its field out gets published within the composite. It causes that the out field in MessagePrinter will be circumvented and in fact replaced by the out field from the PrinterContext.

    public class PrinterContext {

      // Overrides every field annotated with @FromContext
      // in all components in the composite
      @ToContext
      private PrintStream out;

      public PrinterContext(PrintStream out) {
         this.out = out;
      }

    }

Now we can assemble the composite. The $ operator is responsible for welding objects and creating so the primordial composite. First, it does a little magic: it looks to the left and finds out the type of the variable to which the result is assigned and creates its instance. In the following example the type is GreetingService. By having done so it has created the first component. The remaining components are taken from the arguments. Finally it creates a composite context and assigns it to each component. From this moment the components live in the companion of each other. The composite now contains two components GreetingService and MessagePrinter.

Calling methods sayHello and sayGoodBye causes invoking method printMessage in MessagePrinter. As there is no dominant component yet for the out field, this method uses the default output stream (System.out) for printing the message.

   public static void main(String[] args) throws FileNotFoundException {
      // The $ operator is a statically imported method
      // which can fuse objects into a composite.
      GreetingService greetServ = $(new MessagePrinter());
      greetServ.sayHello(); // the message goes to the console
      greetServ.sayGoodBye(); // the message goes to the console

Now we create the PrinterContext component which will override the out field in MessagePrinter. A new component can be joined with a composite by calling operator $join. This operator takes any component which already belongs to a composite and the new component.

      // Overriding the output stream to a file by joining
      // the printer context component
      FileOutputStream fos = new FileOutputStream(args[0]);
      PrintStream out = new PrintStream(fos);
      PrinterContext printerCtx = new PrinterContext(out);

      $join(printerCtx, greetServ);
      greetServ.sayHello(); // the message goes to the file
      greetServ.sayGoodBye(); // the message goes to the file

And as the final step we will remove the PrinterContext component from the composite by calling operator $disjoin. By removing this dominant component the MessagePrinter will print again to the default output stream, ie. to the console.

      $disjoin(printerCtx);
      greetServ.sayHello(); // the message goes to the console again
      greetServ.sayGoodBye(); // the message goes to the console again

   }

GreetingServiceApp.java SharingMethodTest ContextFieldsTest.java

Stackable interceptors (concerns, side-effects, constraints)

Interactions between components are mediated with the help of an internal messaging. It allows putting message interceptors between the sender and receiver. An interceptor can be any component whose method is marked with the @ToContext annotation and setting its mode attribute to one of the following values: side-effect, concern or mixin (default).

If there are more interceptors of the same message (method invocation) then the order is determined by the order of the components in the composite. An interceptor passes the message over to the next interceptor or the final receiver by calling the $next(...) operator.

Side-effects are interceptors that can change neither the input values (the message content) nor the output values (the message replies). Neither can they throw an exception. The system ensures such a behavior. Thus even though a component had changed the message or had thrown an exception it would have had no effect to the processing of the message because these behaviors would be shielded by the system. Side-effects can only do things which do not intefere with processing the message. A typical side-effect is logging or monitoring.

On the other hand, concerns are interceptors which are intended to modify the message and output values and are allowed to throw an exception. Moreover, they can avoid passing over the message to the next receivers. However, they should not produce any side-effect. A typical activity for concern can be an authorization, a transaction control or various filters. A special kind of concern are constraints that are specialized on validating input and output values.

Mixins are not in fact interceptors as they are the final destinations of the message. This is also the default value of the mode attribute in the ToContext annotation. So every component which has a method annotated with this annotations without specifying the mode attribute is considered as a mixin.

SideEffects Intro Concerns Intro Constraints Intro SideEffectTest.java MessageControlTest.java

Traits (mixins)

There is one interesting application of the stackable interceptors which has its origin in the Scala language. This is a concept of traits. A trait encapsulate field and method definitions, which can be reused by mixing them into classes. Unlike class inheritance, in which each class is allowed to inherit from just one superclass, a class can mix in any number of traits.

The following example shows how traits can change behavior of a class. The goal is to create a class representing a queue, which allows putting and getting integer numbers in the first-in/first-out manner. The behavior of the queue is declared in the IntQueue interface. The BasicIntQueue class implements it with the help of java.util.ArrayList. The traits are defined in three classes: Doubling, Incrementing and Filtering. Each trait intercepts a number before it is put to the queue. The intercepting method is method put from IntQueue, which is annotated with the ToContext(mode=InvocationMode.concern) annotation. As these traits modify the input they have to be marked as concerns.

Class Doubling doubles the value before it is put to the queue. Similarly, class Incrementig increments the value and class Filtering filters out negative numbers.

Next, two test methods are defined. In the first one a BasicQueue object is mixed with trait Doubling. In the second one a BasicQueue object is mixed with all three traits.

    public interface IntQueue {
      Integer get();
      void put(Integer x);
    }


    public static class BasicIntQueue implements IntQueue {
      private final ArrayList<Integer> buf = new ArrayList<Integer>();

      public Integer get() {
          return buf.remove(0);
      }

      public void put(Integer x) {
          buf.add(x);
      }
    }


    public abstract static class Doubling implements IntQueue {

      @ToContext(mode = InvocationMode.concern)
      public void put(Integer x) {
          $next(2 * x);
      }
    }

    public abstract static class Incrementing implements IntQueue {
      @ToContext(mode = InvocationMode.concern)
      public void put(Integer x) {
          $next(x + 1);
      }
    }

    public abstract static class Filtering implements IntQueue {
      @ToContext(mode = InvocationMode.concern)
      public void put(Integer x) {
          if (x >= 0) {
              $next(x);
          }
      }
    }

    ...

    public void testDoublingQueue() {
        IntQueue q = $((Doubling)$(), new BasicIntQueue());
        q.put(1);
        q.put(2);
        q.put(3);
        assertEquals(2, (int)q.get());
        assertEquals(4, (int)q.get());
        assertEquals(6, (int)q.get());
    }

    public void testDoublingIncrementingFilteringQueue() {
      IntQueue q = $((Doubling)$(), (Incrementing)$(), (Filtering)$(), new BasicIntQueue());
      q.put(-1);
      q.put(-2);
      q.put(3);
      q.put(4);
      assertEquals(7, (int)q.get());
      assertEquals(9, (int)q.get());
    }

Note:
In contrast to Scala, in Chaplin ACT a trait feature is not defined as a classifier (keyword trait). Instead, a method is marked as a concern interceptor. Thus, traits in Chaplin ACT are defined at the level of methods, while in Scala they are defined at the level of classifiers. It leads to an interesting feature of Chaplin ACT that for some methods a class can behave like a trait, while for the others it behaves like a standard class.
Note:
The example and the trait definition is taken from Programming in Scala by Martin Odersky, Lex Spoon and Bill Venners, Artima 2007, 2008.

SumTest.java IntQueueTest.java

Incomplete class instantiation

Chaplin ACT allows you to instantiate abstract classes. It may be useful for example in the unit testing as you do not have to create a subclass for testing the abstract class. However, there is another reason for it. There may be classes whose instances are not capable of an autonomous existence. Their essence arises only in a vicinity of other objects. Let's look at the following example. The ping object sends the pong message which is received by the pong object. It replies by sending the ping message and so on. Each object is not itself operable, however, by merging both objects together we get an operable entity (the composite) in which the objects interact.

  public class abstract Ping {

     public void ping() {
        System.out.println("ping");
        pong();
     }

     @FromContext
     abstract void pong();

  }

  public class abstract Pong {

     public void pong() {
        System.out.println("pong");
        ping();
     }

     @FromContext
     abstract void ping();

  }

  ...

  Ping ping = $();
  Pong pong = $();
  $$(ping, pong);

  // causes an infinit ping-pong exchange
  ping.ping();

Because the previous example leads to an infinite loop we can modify it as follows:

  public abstract static class Ping {

      private final int exchanges;
      private int counter;

      public Ping(int exchanges) {
          this.exchanges = exchanges;
      }

      public void ping() {
          if (counter == exchanges) {
              return;
          }
          counter++;
          System.out.println("ping");
          pong();
      }

      @FromContext
      abstract void pong();

    }

  ...

  // Create a Ping instance and pass an argument to its constructor
  Ping ping = $(5);
  ...

Now the Ping class replies by sending the pong message only as many times as the value in the exchanges field dictates. This modification also shows how to pass arguments to constructors of abstract classes.

ClassificationTest.java PingPongTest.java

Automerging

Sometimes it is useful to have something like a composite factory. Such a factory is a composite consisting of factory components whose products are to be merged into a monolitic product composite. Let's look at the following example where there is factory interface Factory having a single method createProduct. This method is supposed to create an instance of the Product interface featuring two methods: doX and doY. Next, there are to incomplete implementations of Product interface: A and B. For each product partial implementation there is a respective factory FactoryA and FactoryB. These factories use the $ operator which is able to instantiate abstract classes.

The composite made up by both FactoryA and FactoryB automatically implements Factory interface. By calling the createProduct on it a new composite is made which consists of both A and B instances. This composite automatically implements the Product interface. The proper functionality of the composite can be tested by calling both the doX and doY methods.

  interface Factory {
     Product createProduct();
  }


  interface Product {
     void doX();
     void doY();
  }

  abstract class A implements Product {
     public void doX() {
        ...
     }
  }

  abstract class B implements Product {
     public void doY() {
        ...
     }
  }

  class FactoryA implements Factory {
     public A createProduct() {
        return $();
     }
  }

  class FactoryB implements Factory {
     public B createProduct() {
        return $();
     }
  }

  ...

  Factory compositeFactory = $(new FactoryA(), new FactoryB());
  Product product = compositeFactory.createProduct();
  product.doX();
  product.doY();
Note:
In order that the automerging may work, the product classes must be so called domain classes. The domain classes are those classes processed by Chaplin ACT. The transformer takes a list of packages as a parameter which specifies that all classes belonging to theses packages (and subpackages) are to be processes.

AutoMergeTest.java

Autocollecting

Chaplin ACT allows that more components play the same role in a composite. Sometimes it may happen that one component wishes to know all components in the composite which play a certain role. In such a case the courious component can declare a context field of type java.util.List whose name equals to the name of the role of the components:

  class CuriousOfX {

     @FromContext
     private List<X> xRole;

     public void processAllX() {
        ...
     }

  }

  ...

  // Assembling the composite: (xRole=>x1, xRole=>x2, xRole=>x3, courious)
  CuriousOfX courious = $($role("xRole", x1), $role("xRole", x2), $role("xRole", x3));
  courious.processAllX();

The system collects all adequate components (ie. playing role xRole) and puts them into a list which is returned to the sender component. A similar behaviour would occur if the field were of type java.util.Set. Moreover, the type can be java.util.Map. In such a case the components are put into a map whereas the key is determined by first trying to retrieve the name metadata property (by means of the $name() operator), then by blindly calling a getName() method and finally toString() as the fallback.

AutoCollectTest.java

Injectable metadata

An arbitrary metadata can be attached to every domain object (ie. which is processed by Chaplin ACT). The transformer enhances every domain class by an implementation of the org.iqual.chaplin.Meta interface. This interface provides methods for accessing metadata attached to the object. This interface is not aimed to be called directly. Instead, one should use the $meta operators (statically imported from org.iqual.chaplin.DynaCastUtils).

Example - Assigning the creator of an object

  A a = new A();
  $meta(a, "creator", System.getProperty("user.name"));

  ...

  System.out.println("Object created by " + $meta(a, "creator"));

There are several predefined metadata: role and name which can be accessed by the $role and $name operators. The role metadata determines the role of the object in a composite. The name metadata determines a textual representation of the object.

Note:
Using interface classes as keys for the metadata is recognized as a good practice. In the case of the role metadata it is the org.iqual.chaplin.Role interface and in the case of the name metadata it is the org.iqual.chaplin.Name interface.

MetaTest.java

Nested composites

It sounds naturally that a composite can be nested in another composite. Nesting composites in Chaplin ACT is very straightforward. A supercomposite consisting of other composites is created in the same way as any other composite, ie. by means of the $ operator. The following example is a continuation of the Ping-Pong example from the paragraph Incomplete class instantiation. Now, I am going to create two ping-pong composites and fusing them into a supercomposite. Furthermore, each ping-pong composite contains another component which redirects the ping-pong exchanges to the other ping-pong composite once the number of exchanges reaches a specified value.

Class PingPongDirector is a trait, ie. a component intercepting ping message in the concern mode. For each ping-pong composite one PingPongDirector instance is included to it. It passes over the message to the next receivers until the number of exchanges reaches the specified number. Then it calls context method pong referring to the oposite court. By doing so, the ball is thrown to the other court where the process continues.

      public abstract static class PingPongDirector {

          private final int exchanges;
          private int counter;

          public PingPongDirector(int exchanges) {
              this.exchanges = exchanges;
          }

          @ToContext(mode = InvocationMode.concern)
          public void pong() {
              System.out.println("In court " + this);

              if (exchanges == counter) {
                  counter = 0;
                  // redirect the ball to the other court
                  ping();
              } else {
                  counter++;
                  $next();
              }
          }

          /**
           * Notice the $court variable which makes possible to
           * determine the message target at runtime. The value
           * of the variable is read from the metadata attached
           * to this object.
           */
          @FromContext(name = "../$court/")
          abstract void ping();

      }

      public void testCompositePingPong() {

          // prepare the first court
          Ping ping1 = $(5);
          Pong pong1 = $();
          PingPongDirector dir1 = $(3);
          $meta(dir1, "court", "c2");
          $$(dir1, ping1, pong1);

          // prepare the second court
          Ping ping2 = $(3);
          Pong pong2 = $();
          PingPongDirector dir2 = $(2);
          $meta(dir2, "court", "c1");
          $$(dir2, ping2, pong2);

          // fuse both courts court
          $$($role("c1", ping1), $role("c2", ping2));

          // start to play on the first court
          ping1.ping();

          assertEquals(5, ping1.counter);
          assertEquals(3, ping2.counter);
          assertEquals(1, dir1.counter);
          assertEquals(0, dir2.counter);

      }

The $$ operator works similarly to the $ one. The difference is that it returns no value, thus it makes the composite from the arguments only.

StructuredNameTest.java

Cross-type casting

With DynaCast you can override Java's typecasting system and perform your own dynamic conversions between arbitrary types. These conversions are handled by DynaCast handlers. Let's consider the following example in which some personal data is loaded from a file:

    @DynaCast(MyHandler.class)
    public static void main(String[] args) {

       // String->InputStream conversion
       InputStream inputStream = $("org/iqual/chaplin/example/dcast/simple/person.properties");

       // InputStream->Map conversion
       Map data = $(inputStream);
       // the following statement has the same effect
       //Map data = (Map)inputStream;

       System.out.print("My name is ");
       System.out.print(data.get("firstname"));
       System.out.print(" ");
       System.out.print(data.get("lastname"));
    }

At first sight the code looks very strange. It seems that I am going to convert a java.lang.String value, which carries the file path, to class java.io.InputStream. Then a next conversion happens when I am casting an input stream instance as a java.util.Map instance. Without DynaCast behind the scene the program would end up with a runtime exception complaining about incompatible types. However, DynaCast allows you to write such a code, as long as you provide a special DynaCast handler and associate it with the method in which you want to use dynamic type conversions. Being associated with the method the handler is responsible for perfoming these dynamic conversions. One associates the handler with a method by annotating it with @DynaCast annotation. The annotation carries a list of handler classes. A handler is not obliged to handle arbitrary type conversions. Instead a chain of handlers is created which is ordered in the same sequence as the handler classes appear in the annotation. If one handler cannot handle a conversion it is handed over to the next handler and so on. If no handler is able to handle the conversion the original input value is returned and DynaCast infrastructure attempts to cast the input value as the target type as if there were no DynaCast present.

SomeConversion.java

Thread-bound contexts

So far we have worked with the composite contexts. A composite context is bound to all components which form the composite. One may imagine this as if every component had a hidden field whose value is a reference pointing to the (composite) context.

However, it is not the only way for binding the context. It can be also bound to the current thread. Let's look at the following example:

    public abstract static class DumbClass {

       public void sayHello(int count) {
          for (int i = 0; i < count; i++) {
             printMessage("Hello " + i);
          }
       }

       @FromContext(DumbContext.class)
       protected abstract void printMessage(String message);
    }

The DumbClass exposes the sayHello method which repeatedly prints the "Hello" message. As it is a really dumb class it does not know how to print the message. Therefore it defers printing to the printMessage context method and expects the context does the job for it. The context method declaration is the abstract method declaration annotated with the @FromContext annotation. The annotation specifies the id of the thread-bound context which the method is supposed to run in. Normally, it is the context's class. Now let's look at the context class:

    public static class DumbContext extends SimpleContextHandler {

       final List<String> messages = new ArrayList<String>();

       public void printMessage(String message) {
          messages.add(message);
          System.out.println(message);
       }

    }

For the sake of simplicity I derive the DumbContext class from the org.iqual.chaplin.SimpleContextHandler utility class. This class delegates invocations of its invoke method, which belongs to the org.iqual.chaplin.ContextHandler interface, to the appropriate methods in the context class. It uses the duck-typing approach for recognition the methods in the context class to which the invocations are delegated.

    @DynaContext(id = DumbContext.class)
    public void testCallingMethodOnContext() {

       DumbClass dumb = $();
       dumb.sayHello(3);

       // test whether the context contains the messages sent from the dumb trait
       DumbContext ctx = $();

       assertEquals(3, ctx.messages.size());
       assertEquals("Hello 0", ctx.messages.get(0));
       assertEquals("Hello 1", ctx.messages.get(1));
       assertEquals("Hello 2", ctx.messages.get(2));

    }

The test method first instantiates the DumbClass class by using operator $. The test method calls the sayHello method. As the code is running in the DumbContex context, which owns such a printMessage method, the sayHello method succeeds in calling its printMessage context method. Then the test method verifies that the context method was really called by looking into the list of processed messages in the context object.

ContextMethodTest.java