Programming Language Processors in Java Compilers and Interpreters.9780130257864.25356
On Processors, Compilers and @Configurations
-
Upload
netcetera -
Category
Technology
-
view
1.243 -
download
1
description
Transcript of On Processors, Compilers and @Configurations
On Processors, Compilers and @Configurations
Michael Pellaton
Netcetera
189
2
AGENDA
> Spring @Configuration classes
> An Annotation Processor for Validation
> Unit Testing the Annotation Processor
3
Code Examples
The code in this presentation is simplified and doesn’t compile.
Full length, compiling version of the code:
http://github.com/pellaton/jazoon2012-talk
4
Spring @Configuration Classes
> Java-based Spring application context configuration
> An alternative to XML or annotation based configurations
> Example:
@Configuration
public class ExampleConfiguration {
@Bean
public Example exampleBean() {
return new ExampleImpl();
}
}
5
Let’s Read the Javadoc…
@Configuration public class ExampleConfiguration { @Bean public Example exampleBean() { return new ExampleImpl(); } }
Must not
be Private
Must not
return void
Must not
be final
Must not
be final
Must have a visible
no-arg constructor
should be static if returning
a BeanFactoryPostProcessor
...
...
6
What If There’s Something Wrong?
Exception in thread "main"
org...BeanDefinitionParsingException:
Configuration problem: @Configuration class
'ExampleConfiguration' may not be final.
Remove the final modifier to continue.
7
Your Developers
8
Annotation Processors
> Annotations can be processed at
– Runtime reflection (methods on Class, Method & Co)
– Compile time annotation processor
> JSR-269 “Pluggable Annotation Processing API”
– Introduced with Java 6
– Replaces “Annotation Processing Tool APT” of Java 5
9
JSR-269 Annotation Processors
> A Java compiler plugin
> An implementation of Processor
> Interacts through ProcessingEnvironment injected in init()
– Messenger to emit compiler messages
– Filer to create classes and resources
> Works with the Mirror (Model) API
– Element represents a static Java language-level construct
(class, package, method, …)
– TypeMirror represents a Java type
> process() is called in rounds to process an annotation on a type
– Access to current compilation/processing state through RoundEnvironment
– May claim an annotation: subsequent processors are not called
10
The Validating Annotation Processor
@SupportedSourceVersion(SourceVersion.RELEASE_7) @SupportedAnnotationTypes(value = "org...Configuration") public class JazoonProcessor extends AbstractProcessor { private Elements elementUtils; private Messager messager; private TypeElement configurationTypeElement; public synchronized void init(ProcessingEnvironment env) { elementUtils = env.getElementUtils(); messager = env.getMessager();
configurationTypeElement = elementUtils.getTypeElement( "org.springframework...Configuration"); }
11
The Validating Annotation Processor
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
if (!roundEnv.errorRaised() && !roundEnv.processingOver()) {
for (TypeElement ann : annotations) {
for (Element element : roundEnv.getElementsAnnotatedWith(ann)) {
if (element instanceof TypeElement) {
processElement((TypeElement) element);
}
}
}
}
// don’t consume the annotation type
return false;
}
12
The Validating Annotation Processor
private void processElement(TypeElement typeElement) { for (AnnotationMirror ann : typeElement.getAnnotationMirrors()) { Element annTypeElement = ann.getAnnotationType().asElement(); if (annTypeElement.equals(configurationTypeElement)) { if (typeElement.getModifiers().contains(Modifier.FINAL)) { messager.printMessage(Kind.ERROR,
"@Configuration classes must not be final.", typeElement); } } } }
13
The Result
14
The Result
$ mvn clean compile
...
[ERROR] Failed to execute goal ...: compile
Compilation failure:
[ERROR] /.../ExampleConfiguration.java:[7,13] error: @Configuration classes must not be final.
[ERROR] /.../ExampleConfiguration.java:[10,16] error: @Bean methods must not be private.
15
Your Developer
How about
unit testing
your code?
16
The Java Programming Language Compiler API
> Idea: A unit test that compiles (faulty) classes and checks
the compiler output produced by the annotation processor
> JSR-199 The Java Programming Language Compiler API
(Java 6)
> Programmatic interface to the Java compiler
> Requires a JDK
17
The Java Compiler API : Main Components
> The JavaCompiler is the
– interface to the Java compiler
– Factory for CompilationTasks
– Instance is obtained through ToolProvider
> The CompilationTask is a future of a compilation task
> Source and class files are read and written using the
JavaFileManager
> A message emitted during compilation is called a
Diagnostic
> Diagnostics are stored by the DiagnosticCollector
18
The Java Compiler API : Main Components
CompilationTask
ToolProvider
JavaCompiler
JavaFileManager Diagnostic DiagnosticCollector
emits listens reads/writes
java/class files
holds
creates
creates
19
Compiling Classes in Unit Tests for Processors
/** Tests the detection of final @Configuration classes. */
@Test
public void finalConfigurationClass() throws IOException {
List<Diagnostic> diagnostics =
AnnotationProcessorTestCompiler.compileClass(
"/com/github/pellaton/jazoon2012/FinalTestConfiguration.java",
new JazoonProcessor());
DiagnosticsAssert.assertContainsSingleMessage(Kind.ERROR, 16,
diagnostics);
}
20
Compiling Classes in Unit Tests for Processors
public static List<Diagnostic> compileClass(String clazzName,
Processor processor) {
DiagnosticCollector<> collector = new DiagnosticCollector<>();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
Locale locale = Locale.getDefault();
StandardJavaFileManager fileManager =
compiler.getStandardFileManager(collector, locale, null);
Iterable<? extends JavaFileObject> compilationUnits =
fileManager.getJavaFileObjectsFromFiles(classToCompile);
// the infrastructure is now ready
21
Compiling Classes in Unit Tests for Processors
// compile the class and return the result
CompilationTask task = compiler.getTask(null, fileManager,
collector, "-proc:only", null, compilationUnits);
task.setProcessors(processor);
task.call();
return collector.getDiagnostics();
}
22
The Result
23
Conclusion
> Annotation processors can be used to extend the tooling and build beyond the
traditional purposes like code generation
> To unit test an annotation processor, employ the Java compiler API
> @Configuration and other annotation-based frameworks:
try hard to find problems as early as possible
Michael Pellaton www.netcetera.com
Netcetera [email protected]
Examples & Links: github.com/pellaton/jazoon2012-talk