Groovy mit Groovydoc dokumentieren

Groovydoc wurde 2007 eingeführt, um für Groovy das bereitzustellen, was Javadoc für Java bereitstellt. Groovydoc wird verwendet, um die API-Dokumentation für die Groovy- und Java-Klassen zu generieren, aus denen die Groovy-Sprache besteht. In diesem Beitrag beschäftige ich mich mit dem Aufrufen von Groovydoc über die Befehlszeile und über die von Groovy bereitgestellte benutzerdefinierte Ant-Task.

Groovy- und Java-Quellcode mit Groovydoc / Javadoc-Kommentaren

Ich werde angepasste Versionen des Groovy-Skripts und der Klassen verwenden, die zuerst in meinem Blogbeitrag Easy Groovy Logger Injection und Log Guarding vorgestellt wurden, um Groovydoc zu demonstrieren. Das Groovy-Hauptskript und die Groovy-Klassen aus diesem Beitrag wurden so geändert, dass sie mehr Kommentare im Javadoc-Stil enthalten, um Groovydoc in Aktion besser zu demonstrieren. Das überarbeitete Skript und die zugehörigen Klassen werden in den nächsten Codelisten angezeigt.

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

Zusätzlich zu den oben genannten Groovy-Skripten und -Klassen verwende ich hier auch eine neue Java-Klasse, um zu veranschaulichen, dass Groovydoc sowohl für Java-Klassen als auch für Groovy-Klassen funktioniert. Die Java-Klasse bietet nicht viel außer der Bereitstellung der von Groovydoc zu verarbeitenden Javadoc-Kommentare.

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

Ausführen von Groovydoc in der Befehlszeile

Wenn das oben gezeigte Groovy-Skript, die Groovy-Klassen und die Java-Klasse einsatzbereit sind, ist es an der Zeit, die Aufmerksamkeit darauf zu richten, Groovydoc für diese Klassen und dieses Skript auszuführen. Wie bei Javadoc kann Groovydoc über die Befehlszeile ausgeführt werden. Der Befehl zum Ausführen von Groovydoc für die oben genannten Klassen und Skripte (vorausgesetzt, sie befinden sich alle in demselben Verzeichnis, in dem der Befehl ausgeführt wird) sieht ungefähr so ​​aus:

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 

Der obige Befehl wird alle in einer Zeile ausgeführt. Zur Verbesserung der Lesbarkeit habe ich jedoch Zeilenumbrüche hinzugefügt, um den Befehl aufzuschlüsseln.

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 

Die Parameter des Befehls groovydoc sind jedem bekannt, der Javadoc über die Befehlszeile verwendet hat. Der letzte Teil des Befehls gibt an, dass groovydoc für Groovy- und Java-Code ausgeführt werden soll.

Ausführen von Groovydoc von Ant

Auf Groovydoc kann auch einfach über eine benutzerdefinierte Ant-Task zugegriffen werden, wie im Groovy-Benutzerhandbuch beschrieben. Es ist ziemlich einfach, die Aufgabe groovydoc Ant anzuwenden, indem zuerst das entsprechende taskdef eingerichtet und dann das definierte Tag verwendet wird. Dies wird im folgenden XML-Snippet aus einer relevanten build.xmlDatei demonstriert .

Teile einer Ant build.xml-Datei, die die groovydoc-Aufgabe demonstriert


    
    

Der build.xmloben gezeigte Teil der Ameise entspricht in etwa dem in der Befehlszeile verwendeten. Die Verfügbarkeit von Groovydoc über Ant ist wichtig, da es einfacher ist, die Erstellung von Groovy-Dokumentation aus Ant-basierten Build-Systemen zu integrieren.

Von Groovydoc erstellte Dokumentation

Da jeder Ansatz zum Generieren von Groovy-Dokumentation über Groovydoc (Befehlszeile oder Ant-basiert) ungefähr genauso funktioniert wie der andere, werde ich mich jetzt auf die HTML-Ausgabe konzentrieren, die von beiden Ansätzen stammen kann. Die nächste Serie von Bildschirmschnappschüssen zeigt die generierte Dokumentation beginnend mit der Hauptseite, gefolgt von der DefaultPackage-Seite (ich habe das Skript, die Groovy-Klassen und die Java-Klasse träge im aktuellen Verzeichnis und ohne Paketdeklaration belassen), gefolgt von der Ausgabe für das Groovy-Skript, für ein Beispiel für eine Groovy-Klasse und für die erfundene Java-Klasse. Die letzten drei Bilder helfen bei der Unterscheidung zwischen der Ausgabe eines Groovy-Skripts und einer Groovy-Klasse und einer Java-Klasse.

Beispiel für eine Groovydoc-Hauptseite

Groovydoc-Ausgabe für Beispielpaket (DefaultPackage)

Groovydoc-Ausgabe für Beispiel Groovy Script

Groovydoc-Ausgabe zum Beispiel Groovy-Klasse

Groovydoc-Ausgabe für Beispiel-Java-Klasse

Aus der oben gezeigten Groovydoc-Ausgabe können mehrere Beobachtungen gemacht werden. Erstens dokumentierte die generierte Dokumentation für das Groovy-Skript nur die im Skript definierten Methoden (einschließlich der impliziten mainMethode). Was aus den obigen statischen Bildern nicht so offensichtlich ist, ist, dass tatsächlich keine Groovydoc-Ausgabe für ein Skript erstellt wird, es sei denn, mindestens eine Methode ist explizit im Skript definiert. Wenn im Skript eine Methode definiert ist, wird die Groovydoc-Ausgabe für alle definierten Methoden und für die implizite Hauptmethode generiert. Die Option -nomainforscriptskann an Groovydoc übergeben werden, damit für die implizite mainMethode kein Groovydoc generiert wird. Die Ausgabe des Hinzufügens dieser Option wird als Nächstes angezeigt (beachten Sie, dass die mainDokumentation der Option nicht mehr angezeigt wird).

Die -nommainforscriptsOption ist nett, weil wir oft nicht möchten, dass die mainFunktion implizit für unsere Skripte dokumentiert wird. In der Tat ist die mainFunktion uns als Drehbuchautoren und -betreuer normalerweise "verborgen".

Eine zweite Beobachtung bei der Betrachtung der von Groovydoc generierten Ausgabe ist, dass die generierte Ausgabe zwischen Groovy- und Java-Quellcode unterscheidet. Groovy-Skripte und -Klassen sind mit "[Groovy]" und Java-Klassen mit "[Java]" gekennzeichnet. Dies wird auch in der von Groovydoc generierten Groovy-API-Dokumentation deutlich, in der anhand dieser Funktionen leicht festgestellt werden kann, dass groovy.sql.Sql und AntBuilder Java-Klassen sind, während JmxBuilder und SwingBuilder Groovy-Klassen sind.