on processors, compilers and @configurations

24
On Processors, Compilers and @Configurations Michael Pellaton Netcetera 189

Upload: netcetera

Post on 27-Jun-2015

1.243 views

Category:

Technology


1 download

DESCRIPTION

Jazoon talk on Java Annotation processors

TRANSCRIPT

Page 1: On Processors, Compilers and @Configurations

On Processors, Compilers and @Configurations

Michael Pellaton

Netcetera

189

Page 2: On Processors, Compilers and @Configurations

2

AGENDA

> Spring @Configuration classes

> An Annotation Processor for Validation

> Unit Testing the Annotation Processor

Page 3: On Processors, Compilers and @Configurations

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

Page 4: On Processors, Compilers and @Configurations

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();

}

}

Page 5: On Processors, Compilers and @Configurations

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

...

...

Page 6: On Processors, Compilers and @Configurations

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.

Page 7: On Processors, Compilers and @Configurations

7

Your Developers

Page 8: On Processors, Compilers and @Configurations

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

Page 9: On Processors, Compilers and @Configurations

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

Page 10: On Processors, Compilers and @Configurations

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"); }

Page 11: On Processors, Compilers and @Configurations

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;

}

Page 12: On Processors, Compilers and @Configurations

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); } } } }

Page 13: On Processors, Compilers and @Configurations

13

The Result

Page 14: On Processors, Compilers and @Configurations

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.

Page 15: On Processors, Compilers and @Configurations

15

Your Developer

How about

unit testing

your code?

Page 16: On Processors, Compilers and @Configurations

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

Page 17: On Processors, Compilers and @Configurations

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

Page 18: On Processors, Compilers and @Configurations

18

The Java Compiler API : Main Components

CompilationTask

ToolProvider

JavaCompiler

JavaFileManager Diagnostic DiagnosticCollector

emits listens reads/writes

java/class files

holds

creates

creates

Page 19: On Processors, Compilers and @Configurations

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);

}

Page 20: On Processors, Compilers and @Configurations

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

Page 21: On Processors, Compilers and @Configurations

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();

}

Page 22: On Processors, Compilers and @Configurations

22

The Result

Page 23: On Processors, Compilers and @Configurations

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

Page 24: On Processors, Compilers and @Configurations

Michael Pellaton www.netcetera.com

Netcetera [email protected]

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