on processors, compilers and @configurations

Post on 27-Jun-2015

1.243 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

Jazoon talk on Java Annotation processors

TRANSCRIPT

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 michael.pellaton@netcetera.ch

Examples & Links: github.com/pellaton/jazoon2012-talk

top related