gwt and jsr 269's pluggable annotation processing api

48
PLUGGABLE ANNOTATION PROCESSING API GWT CON - 2015 - LTE CONSULTING ARNAUD TOURNIER

Upload: arnaud-tournier

Post on 15-Apr-2017

588 views

Category:

Software


1 download

TRANSCRIPT

PLUGGABLE ANNOTATIONPROCESSING APIGWT CON - 2015 - LTE CONSULTING

ARNAUD TOURNIER

ARNAUD TOURNIERPassionnate developper, trainer and architect at LTEConsulting.

Speaker at Devoxx, GWT.create, Paris/Toulouse JUG, etc...

Email : [email protected]

Twitter : @ltearno

Website : www.lteconsulting.fr

Full stack (x86_64 to JavaScript)

PRESENTATION AVAILABLE ONlteconsulting.fr/annotation-processing

And the demo project is available atgithub.com/ltearno/gwtcon-jsr269

GWT 3 WILL DROP GENERATORS !

JSR 269 TO THE

JSR 269 TO THERESCUE???

PLUGGABLE ANNOTATION PROCESSING API

PLUGGABLE ANNOTATION PROCESSING APICode generation in Java (source, byte-code and resource).

Integrated with the Java compiler.

Based on annotations. The developer's annotation processorreceives most of the program's AST.

USED FOR ?

USED FOR ?RPC stubs,Reflection stubs,UI generation,Configuration file generation,Code checkers, Build breakers,Dependency injection,Glue code generation,Your own needs !

IN THE GWT CONTEXT

IN THE GWT CONTEXTGWT 3 will abandon generators because the functionalityexists in standard Java : JSR 269.

Even with GWT 2.8 it makes sense to use it.

Causes migration problems, with most of the time quickresolution.

GOOD POINTS

GOOD POINTSAPI is easy to use.

Generated code is visible and debuggable,

Generated code is known before compilation so you canreference it directly (no GWT.create).

No overhead at runtime.

Does not depend on byte code : GWT compatible

BAD POINTS

BAD POINTSOnly annotated elements trigger processing. API makes itdifficult to coordinate processing of multiple classes overmultiple rounds (bad for incremental compilation).

Dependency to external resource is not managed either.

A BRIEF HISTORY

JAVADOC COMMENTS

JAVADOC COMMENTSXDoclet (2002)

/***** Account entity bean** @ejb.bean* name="bank/Account"* jndi-name="ejb/bank/Account"* primkey-field="id"* schema = "Customers"* ...*/public class MonBean { ... }

APT

APTIntroduced in JDK 5, wasremoved with Java 7 because it support new languageelements.

Annotation Processing Toolcan't

Runs outside of javac.

API includes com.sun.mirror packages.

PLUGGABLE ANNOTATION PROCESSING API

PLUGGABLE ANNOTATION PROCESSING APIFixes the sins of the past.

has been included since Java 6 (2006).JSR-269

Runs inside of javac.

API is able to welcome new language features.

HOW IT WORKS

HOW IT WORKSAnnotation processors must be registered.

Java source files are compiled during rounds.

Each round, processors are activated and receive theprogram's AST.

They can then generate files which will be part of the nextround.

When no file is generated during a round, real compilationhappens.

A SAMPLE

Let's say we want to develop a tool that automaticallygenerates UIs from any POJO...

DEMO'S PLAN

DEMO'S PLAN

WE WILL HAVE TO

WE WILL HAVE TOWrite an annotation,

Write an annotation processor,

Register our processor through SPI,

Package and use our library.

ANNOTATION CREATION

ANNOTATION CREATIONThis is the annotation we use to trigger the customannotation processing :

import java.lang.annotation.*;

@Target( { ElementType.METHOD } )@Retention( RetentionPolicy.SOURCE )public @interface AutoUi{}

ANNOTATION PROCESSOR IMPLEMENTATION

ANNOTATION PROCESSOR IMPLEMENTATION@SupportedAnnotationTypes({"fr.lteconsulting.AutoUi"})@SupportedSourceVersion(SourceVersion.RELEASE_8)public class AutoUiProcessor extends AbstractProcessor { @Override public boolean process( Set<TypeElement> annotations, RoundEnvironment round) { for(TypeElement element : round.getElementsAnnotatedWith(AutoUi.class)) { ... JavaFileObject javaFile = filer.createSourceFile(classFqn); Writer writer = javaFile.openWriter(); ... } return true; }}

REGISTERING THROUGH SPI

REGISTERING THROUGH SPIJava compiler searches annotation processors through SPI.

Add a file named META-INF/services/javax.annotation.processing.Processor containingthe annotation processors' fqn list :

fr.lteconsulting.AutoUiAnnotationProcessor

Other ways to register : javac has special flags. TheCompilationTask also has methods to set the processors to beused.

PACKAGING

PACKAGINGThe simplest way is to have the annotation and its processorin the same jar package.

Maven tip: dont forget to use the<compilerArgument>-proc:none</compilerArgument> options

USING THE PROCESSOR

USING THE PROCESSORIn a project with the processor's jar in the classpath, we canuse the annotation...

Eclipse tips :

Eclipse uses its own java compiler, JDT. Use m2e-apt toconfigure your project if you work with maven.Don't forget to close the processor project to have it activated.

THE POJO CLASS

THE POJO CLASS@AutoUipublic class Person { @Label("Name") private String firstName; private String lastName;

private int age;

// getters and setters}

THE GENERATED CLASS

THE GENERATED CLASSpublic class PersonAutoUi extends Composite { private final TextBox firstNameTextBox = new TextBox(); ...

public void setPerson(Person pojo) { ... }

public void updatePerson(Person pojo) { ... }

...}

USING THE GENERATED CLASS

USING THE GENERATED CLASSpublic class Application implements EntryPoint { public void onModuleLoad() { Person person = new Person(...);

// Use of the generated UI code PersonAutoUi editor = new PersonAutoUi();

// Feed the ui editor.setPerson(person);

// Update the pojo with the ui values updateButton.addClickHandler((e)->{ editor.updatePerson(person); });

RootPanel.get().add(editor); }}

HOW IS IT POSSIBLE ?

HOW IS IT POSSIBLE ?Using the not yet generated file is possible because the javacompiler deffers processing of theNotFoundSymbolException.

The error is raised at the end of the parsing and annotationprocessing process if the symbol has not been generated.

THE API : A BRIEF

THE API : A BRIEFINTRODUCTION

API OVERVIEW

API OVERVIEWFiler class : generate files (source, byte-code, resource)

Language Model classes : browse the program's structure,

Messager class : to communicate with the user,

Other tools : element and type tools

see javadoc of the javax.annotation.processing andjavax.lang.model packages.

API : JAVA SOURCE REPRESENTATION

API : JAVA SOURCE REPRESENTATIONElement : representation of a language construct (classdeclarations, methods, ...). Ex: getSimpleName(), ...

Supports all the language structures through theaccept(visitor) and getKind() methods.Hierarchical structure : getEnclosedElements(),getEnclosingElements().

TypeMirror : Type representation, almost like Class<?>

API : ACCESSING ELEMENTS

API : ACCESSING ELEMENTSElements are given as parameters in the process(...)method of the generator. Annotated elements are retrievedlike this :

round.getElementsAnnotatedWith(AutoUi.class).

All the classes parsed during the current round can beobtained with :

round.getRootElements().

Elements can also be retrieved with the utility methods :

elementsUtils.getTypeElement(fqn),and elementsUtils.getPackageElement(fqn).

API : FILER

API : FILERjavax.annotation.processing.Filer

Creating a new Java source

// obtains Filer from the abstract super classFiler filer = processingEnv.getFiler();

// create only create new filesJavaFileObject jfo = filer.createSourceFile(classFqn);

// compose your java fiPrintWriter pw = new PrintWriter( jfo );

API : MESSAGER

API : MESSAGEROutputs messages to the user.

Can also generate errors and break the build. Very handy toassert things on the code.

messager.printMessage(Kind.ERROR, "Cannot find an ID field !");

IDE integration : hints on the API for the user.

API : TOOLS

API : TOOLSMany tools can be retrieved from theprocessingEnvironment field of the AbstractProcessor

Filer getFiler();Messager getMessager();

Elements getElementUtils();Types getTypeUtils();

Map<String, String> getOptions();SourceVersion getSourceVersion();Locale getLocale();

Other static methods and classes are helpful :

ElementFilter, AbstractVisitors...

UNIT TESTS

TESTING A COMPILATION

TESTING A COMPILATIONJavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

// Oracle JDK: task can be cast into// com.sun.source.util.JavacTaskCompilationTask task = compiler.getTask(...);

// forces the processorstask.setProcessors(processors);

boolean successful = task.call();diagnosticCollector.getDiagnostics(); // structured logsfileManager.getOutputFiles();

COMPILE-TESTINGgithub.com/google/compile-testing

Annotation Processor testing library developped by Googleto help developping of the and projects.Auto Dagger

POSITIVE TESTSassert_().about(javaSource()) .that(forResource("PojoTest.java")) .processedWith(new AutoUiProcessor()) .compilesWithoutError() .and() .generatesSources(forResource("PojoTestAutoUi.java"));

AND NEGATIVE ONES

AND NEGATIVE ONESJavaFileObject fileObject = forResource("PojoErrorTest.java");

assert_().about(javaSource()) .that(fileObject) .processedWith(new AutoUiProcessor()) .failsToCompile() .withErrorContaining("No getter found") .in(fileObject) .onLine(23) .atColumn(5);

MISCELLANOUS

LIMITATIONS

LIMITATIONSNot a full access to the code's AST (instructions).

Processors cannot depend one on the other.

Incremental compilation is difficult when havingdependencies to more than one element or to external files.

on Eclipse : Alt+F5, on maven : have to disable incrementalcompilation.

Most of the time those limitations are not embarassing.

HACKING : based on JSR 269 and hacking both javac and jdt

in order to acces to internal implementations and mutatethe class AST.Technical explanations in

Lombok

The Hacker's guide to JavaC

NOTE ON USING TEMPLATES

NOTE ON USING TEMPLATESTry to generate the minimal amount of code, and base it ongeneric implementations. This will ease debugging.

Tools :

Velocity, ...Java Poet, ...String.replaceAll()

LIBRARIES KNOWN USING JSR-269

LIBRARIES KNOWN USING JSR-269JPA meta-model generation (JSR-317),Dagger,Google Auto,Immutables,Lombok,GWT (RequestFactory),Hexa Binding...

LINKS

THAT'S ALL, THANKS !

SEE YOU !

SEE YOU !Slides : lteconsulting.fr/annotation-processing

Demo project : github.com/ltearno/gwtcon-jsr269

Twitter : @ltearno

LTE Consulting : lteconsulting.fr

LinkedIn : fr.linkedin.com/in/lteconsulting