Introduction
Before going anywhere, you must choose your templating engine. Currently, APT-Jelly has direct support for both
Jakarta Commons Jelly and Freemarker, and indirect support for Velocity
(through the merging capabilities of Jelly). The bottom line is that you'll probably want to choose Freemarker, unless you have a specific reason
for going with Jelly or Velocity. The examples in the documentation use Freemarker, but correlating examples in Jelly will be referenced where
practical. Unless explicitly stated, all APT-Jelly features and usages are applicable to both Freemarker and Jelly. For a more in-depth analysis
of your templating choices, see templating choices.
You will also want to be familiar with the Annotation Processing Tool (APT).
I'd suggest reading through the getting started page, and pay particular attention to the section on "how the apt tool operates" so as to be
familiar with what's going on in the background while generating artifacts and to be able to understand any error messages or warnings that may
turn up. You would also be well-advised to be familiar the APT Mirror
API not only to know the properties and structure of the objects available in the template data model, but also to be somewhat familiar with the
vocabulary of Java syntax.
So, shall we get started? As an admittedly contrived example, let's go through the process of generating a class that prints to
System.out a list of all classes and methods in a given source base.
Step 1: Creating the Template
We first write our template:
<@javaSource name="net.sf.jelly.apt.examples.ClassAndMethodPrinter">
package net.sf.jelly.apt.examples;
public class ClassAndMethodPrinter {
public static void main(String[] args) {
<@forAllTypes var="type">
System.out.println("${type.qualifiedName}");
<@forAllMethods var="method">
System.out.println("${type.qualifiedName}.${method.simpleName}");
</@forAllMethods>
</@forAllTypes>
}
}
</@javaSource>
Example Freemarker template for the ClassAndMethodPrinter. Click here to see
the corresponding Jelly template.
Pretty simple, no? You should be familiar with the different parts of the template. The "javaSource" directive declares that we are going to be
generating the source code for a Java class named "net.sf.jelly.apt.examples.ClassAndMethodPrinter". The body of this directive will be directed
to this Java source file, so this is where we see the Java code, including the package declaration, the class declaration, and the
main method that will do the strenuous work of printing the names of the classes and methods.
The "forAllTypes" directive iterates through each type declaration in the source base. By default, interfaces are excluded, so this will only iterate
through each class declaration. The "var" parameter on this directive indicates the name of the context variable that will be used as a reference to
the declaration over which we are currently iterating. (For more information on the "forAllTypes" directive, including how to include interfaces, see
the directive documentation.) The first thing we do for each class declaration is output the Java code that will
print out the name of the class to System.out. Note the use of the reference (sometimes called interpolation) to the
qualified name of the type.
We now iterate through each method in the current declaration with the "forAllMethods" directive (again, the context variable name is set with the "var"
parameter). We then output the Java code that writes the method to System.out in the form of
"fully.qualified.Class.[method name]".
And that's it. We close off the directives and save the file.
Step 2: Invoking APT
Now that the template is written, APT can be invoked as is described in the getting
started guide.
The name of the AnnotationProcessorFactory to use is "net.sf.jelly.apt.freemarker.FreemarkerProcessorFactory". (For a
jelly script, it's "net.sf.jelly.apt.APTJellyProcessorFactory".) The location of the template must also be passed in as a parameter using
either the "-Atemplate" option or the "-AtemplateURL" option. The first option is the path to
the template on the local filesystem. The second is a URL to the template. If both are specified, only the filesystem option will be used.
Please also note the following when invoking APT:
-
Because APT-Jelly doesn't specify the set of annotations that it processes (it processes all of them), invoking APT using the discovery procedure
is not an option. By design, APT-Jelly must be invoked using the "-factory" option. This is to eliminate any ambiguity
that could be caused when invoking APT-Jelly with other instances of AnnotationProcessorFactory that are on the path.
-
For now, APT-Jelly only processes one APT round. Any source files generated by APT-Jelly will not be processed in later rounds by APT-Jelly.
APT-Jelly itself has only a single jar, but depending on which templating engine you choose, you will need to include on the classpath the
dependencies for that engine. See the dependencies list for details. Since we're using Freemarker for our example,
the "freemarker" jar is the only dependency.
Of course, we also to include on our classpath any other jars that the source code itself depends on. For our simple example, we will assume that
our source code depends on another third-party libary that is packaged into a jar named "third-party.jar".
So as an example, let's assume our script is saved to the file "class-method-printer.fmt" and that the engine jars are named "apt-jelly.jar",
and "freemarker.jar" and put in the "lib" directory, as are all other dependant jars (in our case, just "third-party.jar").
To invoke our script on the source files found in the "src/java" directory, putting all generated artifacts into the "target/gen" directory, and
compiling all class files (including the class file associated with our generated source file) into the "target/classes" directory, we invoke
something like the following command (note it's all one line, and make sure the directory "target/classes" exists):
user@host:~>apt -cp lib/apt-jelly.jar:lib/freemarker.jar:lib/third-party.jar -s target/gen -factory
net.sf.jelly.apt.freemarker.FreemarkerProcessorFactory -Atemplate=class-method-printer.fmt -d target/classes src/java/*.java
If everything worked as it should, you'll can now invoke the main method on your generated class:
user@host:~>java -cp target/classes net.sf.jelly.apt.examples.ClassAndMethodPrinter
And you can see the generated source code in target/gen/net/sf/jelly/apt/examples/ClassAndMethodPrinter.java
NOTE: You probably noticed that the apt command is kind of verbose. You'll probably want to invoke apt using an Ant
task or IDE tool of some kind. How to do so is beyond the scope of this document, although you may be interested to know that there is a standard
ant task for APT being shipped with Ant 1.7. As of this writing, Ant 1.7 is still in beta, but the APT task works well.
Further Reading
|