Quarkus Extension - Index Build Item

Quarkus dispose d’un système d’extension très puissant basé sur le concept de BuildItem. Chaque BuildItem permettant de configurer une extension de manière bien particulière. Cependant, la documentation de ces BuildItem est très incomplète. Parmi les centaines de BuildItems existant, je vous propose d’en étudier deux dans ce post. Focus sur les ApplicationIndexBuildItem et CombinedIndexBuildItem

Une histoire de Jandex.

Quarkus a besoin de récolter les informations de l’application au moment du build. Jandex permet d’analyser et de stocker les métadonnées des classes de notre application dans un fichier jandex.idx. Ce fichier sera ensuite exploité par Quarkus pour récupérer des informations sur les classes. Cela vient remplacer avantageusement l’utilisation de l’introspection au runtime.

Création d’un index Jandex.

Pour créer un index Jandex, le plus simple est d’utiliser le plugin maven : jandex-maven-plugin.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<build>
    <plugins>
        <plugin>
            <!-- https://github.com/wildfly/jandex-maven-plugin -->
            <groupId>org.jboss.jandex</groupId>
            <artifactId>jandex-maven-plugin</artifactId>
            <version>1.2.3</version>
            <executions>
                <execution>
                    <id>make-index</id>
                    <goals>
                        <!-- phase is 'process-classes by default' -->
                        <goal>jandex</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

La création du fichier META-INF/jandex.idx interviendra juste après la phase de compilation. Cela permettra d’éviter que l’index ne soit recalculé à chaque fois par l’ensemble des applications utilisant cette dépendance.

ApplicationIndexBuildItem et CombinedIndexBuildItem

Fonctionnellement, ces deux builds items font exactement la même chose, scanner l’application. La seule différence étant que l’ApplicationIndexBuildItem ne scanne que le classpath de l’application alors que le CombinedIndexBuildItem, scannera les classpaths de l’application et de ses dépendances. Dans la pratique le CombinedIndexBuildItem sera souvent préféré.

Exemple

Voyons maintenant un exemple d’utilisation :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@BuildStep()
void indexClassPath(ApplicationIndexBuildItem appIndex, CombinedIndexBuildItem fullIndex) {
    // Application classpath scanning
    appIndex.getIndex().getAnnotations(Foo.class)
            .forEach(annotationInstance -> {
                LOG.info(annotationInstance.target.asClass().name());
            });
    // Application + dependencies classpath scanning
    fullIndex.getIndex().getAnnotations(Foo.class)
            .forEach(annotationInstance -> {
                LOG.info(annotationInstance.target.asClass().name());
            });
}

Dans cet exemple, l’ensemble des classes annotées avec @Foo, seront découvertes et affichées sur la sortie console. D’autres méthodes sont aussi accessibles comme getKnownClasses(), getAllKnownImplementors(), getAllKnownSubClasses() permettant respectivement de lister l’ensemble des classes, celles implémentant une interface spécifique et celles étendant d’une classe particulière.

Il devient ensuite possible de fournir les classes trouvées à d’autres BuildItem pour par exemple ajouter ces classes en tant que Beans injectable par Quarkus.

1
2
3
4
5
6
7
8
@BuildStep()
void indexClassPath(CombinedIndexBuildItem fullIndex, BuildProducer<AdditionalBeanBuildItem> producer) {
    // Application + dependencies classpath scanning
    fullIndex.getIndex().getAnnotations(Foo.class)
            .forEach(annotationInstance -> producer.produce(
                    new AdditionalBeanBuildItem(annotationInstance.target().asClass().name().toString()))
            );
}

Conclusion

ApplicationIndexBuildItem et CombinedIndexBuildItem permettent de facilement récupérer des informations sur les classes présentes dans le classPath pour ensuite les utiliser dans notre extension. Ce scan s’effectue lors du Build de l’application via Jandex.

0%