Welcome to jMDA

To generate software automatically has been a strong ambition since the early days of software development.

jMDA is a new approach in this area. It streamlines proven, widely known and accepted open source technologies into a most comprehensible and easy to use set of Java libraries that are extremely powerful and flexible at the same time. The main purpose of jMDA is

  • to leverage a comprehensible and easy to use modelling environment,

  • to provide convenient and complete access to modelling information and

  • to make available easy to use software generator facilities.

The introduction will briefly explain the main drivers behind this project, the jMDA book provides more detailed information about the most important concepts and the open source software is available here.

annotation processing rounds explained

You can download everything you need for the examples from here and here

Annotation Processing Rounds Explained

jMDA tasks (de.jmda.mproc.task.Task) facilitate JSR 269 annotation processing significantly. Together with the jMDA processing utilities (de.jmda.mproc.ProcessingUtilities) they are like rabbit holes into the wonderland of annotation processing. jMDA provides different task types to address typical use cases for annotation processing.

But let's first explain why we see two “Hello World!” lines in our example. The reason is that javac controls annotation processing in processing rounds. There are always at least two rounds started by javac and the process method of jMDA tasks will be called for each of them. The last round is reserved for clean up purposes.

So how many rounds will be started by javac? This depends on the following circumstances:
  • If no supported annotations can be found by javac no processing round will be started. You don't have to worry much about this if you use jMDA type elements tasks (explained in more detail below).
  • If supported annotations can be found by javac at least two rounds will be started. You can find out if the last round is running by calling ProcessingUtilities.isProcessingOver().
  • If your code produces files using ProcessingUtilities.getFiler() during a processing round another processing round will be started for these files. This continues until no further files are produced.
The following example demonstrates the latter two cases:

package de.jmda.sample.mproc.task;

import static de.jmda.mproc.ProcessingUtilities.getFiler;
// … further import statements omitted

public class JUTTypeElementsTaskProcessingRounds
{
  private class TypeElementsTask extends AbstractTypeElementsTaskTypes
  {
    private int round = 0;
    private boolean produce;

    public TypeElementsTask(Set<? extends Class<?>> types, boolean produce)
    {
      super(types);
      this.produce = produce;
    }

    @Override
    public boolean execute() throws TaskException
    {
      StringBuffer sb = sb("round: " + round + ", produce: " + produce);

      round++;

      if (produce)
      {
        try
        {
          JavaFileObject javaFileObject = getFiler().createSourceFile("EmptyClass");

          Writer writer = javaFileObject.openWriter();
          writer.append("class EmptyClass {}");
          writer.close();

          sb.append(", produced file");
        }
        catch (IOException e)
        {
          throw new TaskException("failure creating source file", e);
        }
        produce = false;
      }

      if (isProcessingOver())
      {
        sb.append(", last round - goodbye");
      }

      System.out.println(sb);

      return false;
    }
  }

  @Test
  public void testProduceFalse() throws IOException
  {
    TypeElementsTask task = new TypeElementsTask(asSet(String.class), false);
    TaskRunner.run(task);
    assertEquals("wrong number of rounds", 2, task.round);
  }

  @Test
  public void testProduceTrue() throws IOException
  {
    TypeElementsTask task = new TypeElementsTask(asSet(String.class), true);
    TaskRunner.run(task);
    assertEquals("wrong number of rounds", 3, task.round);
  }
}
Table 1: Processing Rounds Example
The output of the first test is:

round: 0, produce: false
round: 1, produce: false, last round - goodbye

In the first test the task is configured to not produce a file (see constructor call). This way the resulting number of rounds is 2.

The output of the second test is:

round: 0, produce: true, produced file
round: 1, produce: false
round: 2, produce: false, last round - goodbye

Here we have three rounds. In this test the task is configured to produce a file (see constructor call again). The file is produced in the first round and then the production of files is stopped by setting produce to false inside the execute method. After execute returns from its first invocation javac starts another round. This time no file is produced because produce is set to false from the previous round. Then javac starts the final round and the task says goodbye.

I hope this shows clearly how annotation processing is controlled by javac.

No comments:

Post a Comment