Documenter Groovy avec Groovydoc

Groovydoc a été introduit en 2007 pour fournir à Groovy ce que Javadoc fournit pour Java. Groovydoc est utilisé pour générer la documentation API pour les classes Groovy et Java qui composent le langage Groovy. Dans cet article, je regarde appeler Groovydoc via la ligne de commande et via la tâche Ant personnalisée fournie par Groovy.

Code source Groovy et Java avec commentaires Groovydoc / Javadoc

J'utiliserai des versions adaptées du script Groovy et des classes introduites pour la première fois dans mon article de blog Easy Groovy Logger Injection and Log Guarding pour démontrer Groovydoc. Le script Groovy principal et les classes Groovy de cet article ont été modifiés pour inclure plus de commentaires de style Javadoc afin de mieux démontrer Groovydoc en action. Le script révisé et les classes associées sont présentés dans les listes de codes suivantes.

demoGroovyLogTransformation.groovy

#!/usr/bin/env groovy /** * demoGroovyLogTransformation.groovy * * Grab SLF4J, Log4j, and Apache Commons Logging dependencies using @Grab and * demonstrate Groovy 1.8's injected logging handles. * * //marxsoftware.blogspot.com/2011/05/easy-groovy-logger-injection-an... */ // No need to "grab" java.util.logging: it's part of the JDK! /* * Specifying 'slf4j-simple' rather than 'slf4j-api' to avoid the error * "Failed to load class "org.slf4j.impl.StaticLoggerBinder" that is caused by * specifying no or more than one of the actual logging binding libraries to * be used (see //www.slf4j.org/codes.html#StaticLoggerBinder). One should * be selected from 'slf4j-nop', 'slf4j-simple', 'slf4j-log4j12.jar', * 'slf4j-jdk14', or 'logback-classic'. An example of specifying the SLF4J * dependency via @Grab is available at * //mvnrepository.com/artifact/org.slf4j/slf4j-api/1.6.1. */ @Grab(group='org.slf4j', module="slf4j-simple", version="1.6.1") /* * An example of specifying the Log4j dependency via @Grab is at * //mvnrepository.com/artifact/log4j/log4j/1.2.16. */ @Grab(group='log4j', module="log4j", version="1.2.16") /* * An example of specifying the Apache Commons Logging dependency via @Grab is at * //mvnrepository.com/artifact/commons-logging/commons-logging-api/1..... */ @Grab(group='commons-logging', module="commons-logging-api", version="1.1") /* * Run the tests... */ int headerSize = 79 printHeader("java.util.logger", headerSize) def javaUtilLogger = new JavaUtilLoggerClass() printHeader("Log4j", headerSize) def log4jLogger = new Log4jLoggerClass() printHeader("SLF4j", headerSize) def slf4jLogger = new Slf4jLoggerClass() printHeader("Apache Commons", headerSize) def commonsLogger = new ApacheCommonsLoggerClass() /** * Print header with provided text. * * @param textForHeader Text to be included in the header. * @param sizeOfHeader Number of characters in each row of header. */ def printHeader(final String textForHeader, final int sizeOfHeader) { println "=".multiply(sizeOfHeader) println "= ${textForHeader}${' '.multiply(sizeOfHeader-textForHeader.size()-4)}=".multiply(sizeOfHeader) } 

JavaUtilLoggerClass.groovy

import groovy.util.logging.Log /** * Sample Groovy class using {@code @Log} to inject java.util.logging logger * into the class. */ @Log class JavaUtilLoggerClass { /** * Constructor. */ public JavaUtilLoggerClass() { println "\njava.util.logging (${log.name}: ${log.class}):" log.info "${this.printAndReturnValue(1)}" log.finer "${this.printAndReturnValue(2)}" } /** * Print provided value and then return it as part of String indicating part * of JDK's java.util.logging. * * @param newValue Value to be printed and included in return String. * @return String indicating newValue and JDK for java.util.logging. */ public String printAndReturnValue(int newValue) { println "JDK: Print method invoked for ${newValue}" return "JDK: ${newValue}" } } 

Log4jLoggerClass.groovy

import groovy.util.logging.Log4j import org.apache.log4j.Level /** * Sample Groovy class using {@code @Log4j} to inject Log4j logger * into the class. */ @Log4j class Log4jLoggerClass { /** * Constructor. */ Log4jLoggerClass() { // It is necessary to set logging level here because default is FATAL and // we are not using a Log4j external configuration file in this example log.setLevel(Level.INFO) println "\nLog4j Logging (${log.name}: ${log.class}):" log.info "${this.printAndReturnValue(1)}" log.debug "${this.printAndReturnValue(2)}" } /** * Print provided value and then return it as part of String indicating part * of Log4j. * * @param newValue Value to be printed and included in return String. * @return String indicating newValue and Log4j. */ public String printAndReturnValue(int newValue) { println "Log4j: Print method invoked for ${newValue}" return "Log4j: ${newValue}" } } 

Slf4jLoggerClass.groovy

import groovy.util.logging.Slf4j /** * Sample Groovy class using {@code @Slf4j} to inject Simple Logging Facade for * Java (SLF4J) logger into the class. */ @Slf4j class Slf4jLoggerClass { /** * Constructor. */ public Slf4jLoggerClass() { println "\nSLF4J Logging (${log.name}: ${log.class}):" log.info "${this.printAndReturnValue(1)}" log.debug "${this.printAndReturnValue(2)}" } /** * Print provided value and then return it as part of String indicating part * of SLF4J logging. * * @param newValue Value to be printed and included in return String. * @return String indicating newValue and SLF4J. */ public String printAndReturnValue(int newValue) { println "SLF4J: Print method invoked for ${newValue}" return "SLF4J: ${newValue}" } } 

ApacheCommonsLoggerClass.groovy

import groovy.util.logging.Commons /** * Sample Groovy class using {@code @Commons} to inject Apache Commons logger * into the class. */ @Commons class ApacheCommonsLoggerClass { /** * Constructor. */ public ApacheCommonsLoggerClass() { println "\nApache Commons Logging (${log.name}: ${log.class}):" log.info "${this.printAndReturnValue(1)}" log.debug "${this.printAndReturnValue(2)}" } /** * Print provided value and then return it as part of String indicating part * of Apache Commons Logging. * * @param newValue Value to be printed and included in return String. * @return String indicating newValue and Apache Commons Logging. */ public String printAndReturnValue(int newValue) { println "Commons: Print method invoked for ${newValue}" return "Commons: ${newValue}" } } 

En plus du script et des classes Groovy ci-dessus, j'utilise également une nouvelle classe Java ici pour illustrer que Groovydoc fonctionne sur les classes Java ainsi que sur les classes Groovy. La classe Java ne fait pas grand-chose en plus de fournir les commentaires Javadoc à traiter par Groovydoc.

DoNothingClass.java

/** * Class that does not do anything, but is here to be a Java class run through * groovydoc. */ public class DoNothingClass { /** * Simple method that returns literal "Hello _addressee_!" string where * _addressee_ is the name provided to this method. * * @param addressee Name for returned salutation to be addressed to. * @return "Hello!" */ public String sayHello(final String addressee) { return "Hello, " + addressee; } /** * Main executable function. */ public static void main(final String[] arguments) { final DoNothingClass me = new DoNothingClass(); me.sayHello("Dustin"); } /** * Provide String representation of this object. * * @return String representation of me. */ @Override public String toString() { return "Hello!"; } } 

Exécution de Groovydoc sur la ligne de commande

Avec le script Groovy, les classes Groovy et la classe Java montrés ci-dessus prêts à l'emploi, il est temps de se tourner vers l'exécution de Groovydoc contre ces classes et ces scripts. Comme c'est le cas avec Javadoc, Groovydoc peut être exécuté à partir de la ligne de commande. La commande pour exécuter Groovydoc sur les classes et scripts ci-dessus (en supposant qu'ils se trouvent tous dans le même répertoire dans lequel la commande est exécutée) ressemble à ceci:

groovydoc -classpath C:\groovy-1.8.0\lib\ -d output -windowtitle "Groovy 1.8 Logging Example" -header "Groovy 1.8 Logging (Inspired by Actual Events)" -footer "Inspired by Actual Events: Logging in Groovy 1.8" -doctitle "Logging in Groovy 1.8 Demonstrated" *.groovy *.java 

La commande ci-dessus est exécutée sur une seule ligne. Cependant, pour une meilleure lisibilité, j'ai ajouté des sauts de ligne pour décomposer la commande.

groovydoc -classpath C:\groovy-1.8.0\lib\ -d output -windowtitle "Groovy 1.8 Logging Example" -header "Groovy 1.8 Logging (Inspired by Actual Events)" -footer "Inspired by Actual Events: Logging in Groovy 1.8" -doctitle "Logging in Groovy 1.8 Demonstrated" *.groovy *.java 

Les paramètres de la commande groovydoc semblent familiers à quiconque a utilisé javadoc depuis la ligne de commande. La dernière partie de la commande spécifie que groovydoc doit être exécuté sur du code Groovy et Java.

Lancer Groovydoc depuis Ant

Groovydoc est également facilement accessible via une tâche Ant personnalisée, comme décrit dans le Guide de l'utilisateur Groovy. Il est assez facile d'appliquer la tâche groovydoc Ant en configurant d'abord le taskdef approprié, puis en utilisant cette balise définie. Cela est illustré dans l'extrait de code XML suivant d'un build.xmlfichier pertinent .

Portions d'un fichier Ant build.xml démontrant la tâche groovydoc


    
    

La partie de la fourmi build.xmlci-dessus est à peu près équivalente à celle utilisée sur la ligne de commande. Avoir Groovydoc disponible via Ant est important car cela facilite l'intégration de la construction de la documentation Groovy à partir de systèmes de construction basés sur Ant.

Documentation générée par Groovydoc

Étant donné que chaque approche pour générer de la documentation Groovy via Groovydoc (ligne de commande ou basée sur Ant) fonctionne à peu près de la même manière que l'autre, je vais maintenant me concentrer sur la sortie HTML qui pourrait provenir de l'une ou l'autre approche. La prochaine série d'instantanés d'écran montre la documentation générée en commençant par la page principale, suivie de la page DefaultPackage (j'ai laissé paresseusement le script, les classes Groovy et la classe Java dans le répertoire courant et sans aucune déclaration de package), suivi respectivement de la sortie pour le script Groovy, pour un exemple de classe Groovy et pour la classe Java artificielle. Les trois dernières images permettent de différencier la sortie d'un Groovy Script d'une classe Groovy par rapport à une classe Java.

Exemple de page principale Groovydoc

Sortie Groovydoc pour l'exemple de package (DefaultPackage)

Sortie Groovydoc pour l'exemple de script Groovy

Sortie Groovydoc pour l'exemple de classe Groovy

Sortie Groovydoc pour l'exemple de classe Java

Plusieurs observations peuvent être faites à partir de la sortie Groovydoc ci-dessus. Tout d'abord, la documentation générée pour le script Groovy n'a documenté que les méthodes définies dans le script (y compris la mainméthode implicite ). Ce qui n'est pas si évident à partir des images statiques ci-dessus, c'est qu'en fait, aucune sortie Groovydoc n'est créée pour un script à moins qu'au moins une méthode ne soit explicitement définie dans le script. Si une méthode est définie dans le script, la sortie Groovydoc est générée pour toutes les méthodes définies et pour la méthode principale implicite. L'option -nomainforscriptspeut être passée à Groovydoc pour n'avoir aucun Groovydoc généré pour la mainméthode implicite . La sortie de l'ajout de cette option est affichée ci-dessous (notez que la maindocumentation de n'est plus affichée).

L' -nommainforscriptsoption est intéressante car nous ne voulons souvent pas que la mainfonction soit implicitement documentée pour nos scripts. En effet, la mainfonction est généralement «cachée» pour nous en tant que scénaristes et mainteneurs.

Une deuxième observation en regardant la sortie générée par Groovydoc est que la sortie générée fait la distinction entre le code source Groovy et Java. Les scripts et les classes Groovy sont étiquetés avec «[Groovy]» et les classes Java sont étiquetées avec «[Java]». Cela est également évident dans la documentation de l'API Groovy générée par Groovydoc, où ces fonctionnalités permettent d'identifier facilement que groovy.sql.Sql et AntBuilder sont des classes Java tandis que JmxBuilder et SwingBuilder sont des classes Groovy.