pluggable annotation processing api

67
PLUGGABLE ANNOTATION PROCESSING API JUG TOULOUSE - 2015 - LTE CONSULTING

Upload: arnaud-tournier

Post on 15-Apr-2017

276 views

Category:

Software


2 download

TRANSCRIPT

Page 1: Pluggable annotation processing api

PLUGGABLE ANNOTATIONPROCESSING APIJUG TOULOUSE - 2015 - LTE CONSULTING

Page 2: Pluggable annotation processing api

ARNAUD TOURNIERArchiDév passionné chez LTE Consulting

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

@ltearno www.lteconsulting.fr

Développement - Formation - Conseil

Full stack (x86_64 to JavaScript) !

Page 3: Pluggable annotation processing api

AVANT TOUTCette présentation est disponible sur

github.com/ltearno/annotation-processing

Des hyper-liens sont présents

Page 4: Pluggable annotation processing api

JSR 269 ???

Page 5: Pluggable annotation processing api

PLUGGABLE ANNOTATIONPROCESSING API...

Page 6: Pluggable annotation processing api

La JSR 269 - Pluggable Annotation Processing API permetd'exploiter les annotations présentes dans le code en

s'insérant dans le processus de compilation, et de générer denouvelles sources.

Traitement des annotations à la compilation,Génération de code,Génération de fichiers de configuration,Documentation, Cartographie,Vérifications, Build breakers,etc...

On ne modifie pas les sources existants !

Page 7: Pluggable annotation processing api

AVANTAGES

Page 8: Pluggable annotation processing api

Le code généré est visible.

Pas de traitement au runtime donc pas d'impact sur lesperformances.

Pas d'instrumentation du byte-code, donc plus simple.

Page 9: Pluggable annotation processing api

BRÈVE HISTOIRE DUTRAITEMENT DES

ANNOTATIONS

Page 10: Pluggable annotation processing api

COMMENTAIRES JAVADOCXDoclet (2002)

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

Page 11: Pluggable annotation processing api

APT, retiré officiellement avec Java 7

car .Annotation Processing Tool

non extensible à Java > 5

Outil lancé en dehors de la compilation.

L'API Mirror utilise les packages com.sun.mirror.

Page 12: Pluggable annotation processing api

PLUGGABLE ANNOTATION PROCESSING APIDepuis 2006 (Java 6) la , créé par Joe Darcy.JSR-269

Intégré à la compilation javac.

Page 13: Pluggable annotation processing api

PRINCIPE

Page 14: Pluggable annotation processing api

On fournit un processeur d'annotation,Le compilateur gère des rounds de processing,A chaque round, les nouveaux sources sont traités (phasesParse et Enter),Les processeurs sont choisis et reçoivent l'AST des classestraitées,Les processeurs peuvent générer de nouveaux fichiers(sources, classes et resources) qui seront parsés et traités auround suivant.

Page 15: Pluggable annotation processing api

UTILISATION

Page 16: Pluggable annotation processing api

CRÉATION DE L'ANNOTATIONimport java.lang.annotation.*;

// package, class, method, ...@Target( value = { ElementType.METHOD } )@Retention( RetentionPolicy.SOURCE )public @interface MonAnnotation{ ...}

Page 17: Pluggable annotation processing api

LE PROCESSEUR@SupportedAnnotationTypes( value= { "fr.lteconsulting.MonAnnotation" } )@SupportedSourceVersion( SourceVersion.RELEASE_6 )public class MonAnnotationProcessor extends AbstractProcessor { @Override public boolean process( Set<?> extends TypeElement> annotations, RoundEnvironment roundEnv ) { Types typeUtils = processingEnv.getTypeUtils(); Elements elementUtils = processingEnv.getElementUtils(); Messager messager = processingEnv.getMessager();

for ( TypeElement element : roundEnv.getElementsAnnotatedWith(MonAnnotation processingEnv.getMessager().printMessage( Diagnostic.Kind.NOTE, element.getQualifiedName()); return true; }}

Page 18: Pluggable annotation processing api

LE FICHIER SPIPour packager et activer un processeur, le plus simple est

d'utiliser SPI :

Le fichierMETA-INF/services/javax.annotation.processing.Processor

contient la liste des processeurs :

fr.lteconsulting.MyAnnotationProcessor

Packager le tout dans un jar

Page 19: Pluggable annotation processing api

UN BOUT DE CODEDans un autre projet on peut utiliser l'annotation et le

processeur.

class UneClasse { @MonAnnotation void uneMethode() { ... }}

Page 20: Pluggable annotation processing api

PACKAGING ET COMPILATIONLe processeur et le fichier SPI sont dans un jar.

Ce jar est dans le class path au moment de la compilation deUneClasse.java.

C'est tout !

Page 21: Pluggable annotation processing api

SORTIE DE NOTRE EXEMPLEAt line 90 of UneClasse.java :fr.lteconsulting.UneClasse.uneMethode()

Page 22: Pluggable annotation processing api

FONCTIONNEMENT

Page 23: Pluggable annotation processing api

A chaque round, le processeur doit traiter les classesgénérées au round précédent.

S'il est appelé au premier round, il le sera pour les autres,jusqu'au dernier round (même si aucune annotation n'est

présente pour lui).

Page 24: Pluggable annotation processing api

DÉCOUVERTE DES PROCESSEURSLes proceseurs sont découverts par le compilateur.

JavaCompiler fournit des options pour contrôler l'ensembledes processeurs disponibles :

une liste prédéfinie,un chemin de recherche,utiliser SPI.

Page 25: Pluggable annotation processing api

CHOIX DU PROCESSEURAppel des processeurs en fonction :

des annotations présentes dans les classes traitées,les annotations supportées par les processeurs,le fait qu'un processeur ait claimé une annotation.

Page 26: Pluggable annotation processing api

CYCLE DE VIE DU PROCESSEURLe compilateur instancie le processeur,Appelle init avec un ProcessingEnvironment,Appelle getSupportedAnnotationTypes,getSupportedOptions etgetSupportedSourceVersion,Et appelle process à chaque round.

Page 27: Pluggable annotation processing api

A CHAQUE ROUNDjavac calcule l'ensemble des annotations sur les classes encours,si au moins une annotation est présente, au fur et à mesureque les processeurs les claime, elles sont retirées desannotations non matchées.quand l'ensemble est vide ou qu'il n'y a plus de processeurcandidat, le round est fini.si aucune annotation n'est présente, seuls les processeursuniversels ("*") sont appelés, et reçoivent un ensemble videde classes à traiter.

Page 28: Pluggable annotation processing api

PRÉCAUTIONS !Un processeur ne doit pas dépendre d'un autre,Idempotent,Commutatif.

Page 29: Pluggable annotation processing api

L'INTERFACE PROCESSORjavax.annotation.processing.Processor

interface Processor { void init( ProcessingEnvironment processingEnv );

Set<String> getSupportedAnnotationTypes();

boolean process( Set<? extends TypeElement> annotations, RoundEnvironment roundEnv );}

Page 30: Pluggable annotation processing api

PROCESSINGENVIRONMENTjavax.annotation.processing.ProcessingEnvironment

// UtilitairesElements getElementUtils();Types getTypeUtils();Locale getLocale();Map<String, String> getOptions();SourceVersion getSourceVersion();

// Création de fichiersFiler getFiler();

// Affichage utilisateurMessager getMessager();

// Autres classes :// ElementFilter, AbstractVisitors...

Page 31: Pluggable annotation processing api

GETSUPPORTEDANNOTATIONTYPES*fr.lteconsulting.annotations.*fr.lteconsulting.annotations.MonAnnotation

Page 32: Pluggable annotation processing api

LA MÉTHODE PROCESSboolean process( Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)

On reçoit l'ensemble des annotations à traiter,On retourne true pour claimer les annotations etempêcher les autres processeurs de les traiter.

Page 33: Pluggable annotation processing api

ROUNDENVIRONMENTjavax.annotation.processing.RoundEnvironment

Liste des classes dans le round :

Set<? extends Element> getRootElements()

Liste des éléments annotés :

Set<? extends Element> getElementsAnnotatedWith(TypeElement a)Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a)

Etat du round :

// si donne 'true', il ne faut rien générerboolean isProcessingOver()

Page 34: Pluggable annotation processing api

ELEMENTjavax.lang.model.element.Element

Représente un package, une classe, une méthode, ...

Pour parcourir les données d'un élément, il faut soit appelergetKind() soit utiliser un visiteur.

Ne pas utiliser instanceof !

Petit exercice : POURQUOI ?

Page 35: Pluggable annotation processing api

ELEMENT// Visiter l'élément :<R,P> R accept(ElementVisitor<R,P> v, P p);

// Obtenir le type :TypeMirror asType();

// Demander la sorte :ElementKind getKind();

// Demander les annotations présentes sur l'élément :List<? extends AnnotationMirror> getAnnotationMirrors();<A extends Annotation> A getAnnotation(Class<A> annotationType);

// Autres :// getModifiers(); getSimpleName(); getEnclosingElement(); getEnclosedElements();

Page 36: Pluggable annotation processing api

LES SORTES D'ELEMENTElementKind

annotation, class, constructeur, enum, une constante enum,parametre d'exception, champ, initializeur d'instance, interface,

variable locale, méthode, package, paramètre, variable deresource, initializeur statique, paramètre de type, autres (futur).

Page 38: Pluggable annotation processing api

CRÉER UN NOUVEAU SOURCE JAVAFiler filer = processingEnv.getFiler();JavaFileObject jfo = filer.createSourceFile( classElement.getQualifiedName() + "Info");PrintWriter pw = new PrintWriter( jfo );...

Page 39: Pluggable annotation processing api

CRÉER UNE NOUVELLE RESSOURCEFiler filer = processingEnv.getFiler();try { PrintWriter pw = new PrintWriter(filer.createResource( StandardLocation.SOURCE_OUTPUT, "fr.lteconsulting", "Todo.txt") .openOutputStream()); pw.println("Quelque chose"); pw.close();} catch (IOException ioe) { messager.printMessage(Kind.ERROR, ioe.getMessage());}

Page 40: Pluggable annotation processing api

CRÉER UN NOUVEAU FICHIER .CLASSFiler filer = processingEnv.getFiler();JavaFileObject jfo = filer.createClassFile( classElement.getQualifiedName() + "Info");PrintWriter pw = new PrintWriter( jfo );...

Page 41: Pluggable annotation processing api

UTILISATION DE TEMPLATES !Ne générer que le minimum de code !Velocity, ...Java Poet, ...

Page 42: Pluggable annotation processing api

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

// sortie :error: At Book.java, line 12 : Cannot find an ID field !1 error

Page 43: Pluggable annotation processing api

LE MESSAGER DANS ECLIPSE

La même erreur dans Eclipse

Page 44: Pluggable annotation processing api

LA COMPILATION JAVA

Page 45: Pluggable annotation processing api

3 PHASES

Plan d'exécution de javacParse et EnterAnnotation ProcessingAnalyse et Generate

openjdk.java.net/groups/compiler/doc/compilation-overview

Page 46: Pluggable annotation processing api

JAVACAction Paramètres

Générationdes sources

-s répertoire

Désigner unprocesseur

-processorfr.lteconsulting.MyAnnotationProcessor,autre...

Spécifier unchemin derecherche

-processorPath le_chemin

Passer desoptions

-Acle=valeur

Page 47: Pluggable annotation processing api

JAVAC (SUITE)Action Paramètres

Désactiver l'AP -proc:none

Seulement l'AP -proc:only

Chemin de recherche dessources

-sourcePath

Désactiver la génération des.class implicite

-implicit:none

Répertoire de sortie des.class

-d

Affichage debug -XprintRounds -XprintProcessorInfo

Page 48: Pluggable annotation processing api

ATTENTION : le warning si on ne met pas -implicit:none

docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html#searching

Page 49: Pluggable annotation processing api

INTÉGRATION AVECECLIPSE

Page 50: Pluggable annotation processing api

CONFIGURATIONEclipse utilise son propre compilateur, JDT.

Configuration possible du projet par m2e

Page 51: Pluggable annotation processing api
Page 52: Pluggable annotation processing api

ATTENTIONDans Eclipse, si le projet contenant le processeur est ouvert,

l'annotation processing est désactivé.

Page 53: Pluggable annotation processing api

TESTS UNITAIRES

Page 54: Pluggable annotation processing api

EXEMPLEJavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

// JDK Sun : task peut être casté en com.sun.source.util.JavacTaskCompilationTask task = compiler.getTask( null, // Default log (stderr) fileManager, // (InMemory)JavaFileManager diagnosticCollector, // DiagnosticCollector options, // Set<String> new HashSet<String>(), // Classes pour annotation processing sources); // Iterable<? extends JavaFileObject>

// Force les processeurstask.setProcessors(processors);

boolean successful = task.call(); // succès ou échecdiagnosticCollector.getDiagnostics(); // logs structurés de compilationfileManager.getOutputFiles(); // fichiers de sortie

Page 55: Pluggable annotation processing api

COMPILE-TESTINGBibliothèque de test développée par Google, pour aider le

développement de et Auto Dagger

Page 56: Pluggable annotation processing api

Test positif

assert_().about(javaSource()) .that(JavaFileObjects.forResource("HelloWorld.java")) .processedWith(new MyAnnotationProcessor()) .compilesWithoutError() .and() .generatesSources(JavaFileObjects.forResource("GeneratedHelloWorld.java"

Page 57: Pluggable annotation processing api

et négatif

JavaFileObject fileObject = JavaFileObjects.forResource("HelloWorld.java"assert_().about(javaSource()) .that(fileObject) .processedWith(new NoHelloWorld()) .failsToCompile() .withErrorContaining("No types named HelloWorld!") .in(fileObject) .onLine(23) .atColumn(5);

Page 58: Pluggable annotation processing api

DÉMO TIME !

Page 59: Pluggable annotation processing api

LIMITATIONS

Page 60: Pluggable annotation processing api

Pas d'accès à l'AST complet (corps des méthodes)Pas possible de modifier des classes existantesCertains bug ne permettent pas de traiter correctement lesgénériquesJava et les IDE ne surveillent que les éléments annotéspour redéclencher le processing (parfois problématiquelorsque l'on a des dépendances complexes -> Alt+F5 !)

Page 61: Pluggable annotation processing api

HACKING AU DELÀ : de bons hacks pour accéder à l'implémentation

(javac de sun et jdt) et modifier l'ASTImmutables : quelques workarounds captant lesimplementations JDK / JDT pour gérer les génériquesExplications techniques générales dans

Lombok

The Hacker's guideto JavaC

Page 62: Pluggable annotation processing api

EXEMPLES DEBIBLIOTHÈQUES

Page 63: Pluggable annotation processing api

JPA meta-model generation (JSR-317),Dagger,Google Auto,Immutables,Hexa Binding,Lombok,GWT (RequestFactory)

Page 64: Pluggable annotation processing api

LIENS EN VRAC

Page 65: Pluggable annotation processing api

, , , , , , ,

, , , ,

, , , ,

Hibernate Validation JM Doudoux Lombok How Lombokworks ? Lombok encore... Hacking JavaC HexaBinding

Coders Breakfast Prez d'Angelika Langer Dr. Macphail'strance Créez et utilisz vos annotations Histoire des

annotation processing 4 vidéos sur Parleys Utilisation de laJSR-269 chez Les Furets Save method parameter names Au

JUG Paris par Olivier Croisier...

Page 66: Pluggable annotation processing api

ET VOILÀ !

Page 67: Pluggable annotation processing api

MERCI !Rendez-vous sur

github.com/ltearno/annotation-processing

Twitter : @ltearno

LinkedIn : fr.linkedin.com/in/lteconsulting

LTE Consulting