Gabi und Sascha
Kategorien : Alle | Berlin | Bücher | Fotografie | Java | Linkhalde | Weichware | Verfassung

Ich habe mich in den letzten Tagen mit der Entwicklung eines Java Annotation Processors befasst. So ein Prozessor hängt sich seit Java 6 transparent in den Compiler ein und kann Java Annotationen verarbeiten. Um dies zu erreichen muss die Implementierung des Processors für das Service API vorbereitet werden. Und damit bekam ich das erste Problem, welches zu lösen war.

Ich verwende Maven für die Umsetzung des Projektes. Wenn in einem Maven Projekt die entsprechenden Ressourcen angelegt werden, dann sind diese bereits beim Compilerlauf erreichbar. Da die Implementierung aber noch nicht vorhanden ist, kommt es zu einem Fehler und Abbruch des Compilerlaufs.


    MAVEN_PROJECT
         ├─ pom.xml
         └─┬─ src
           └─┬─ main
             └─┬─ resources
               └─┬─ META-INF
                 └─┬─ services
                   └── javax.annotation.processing.Processor

Um dies zu vermeiden, musste ich die META-INF/services/ javax.annotation.processing.Processor nachträglich in das erzeugte JAR einbringen. Dies wird zur Zeit mit dem Maven TrueZIP Plugin erledigt. Dabei sieht das Project Layout wie folgt aus:


    MAVEN_PROJECT
         ├─ pom.xml
         └─┬─ src
           └─┬─ main
             ├─── resources
             └─┬─ service-data
               └─┬─ META-INF
                 └─┬─ services
                   └── javax.annotation.processing.Processor

Der Nachteil dieser Lösung: der Processor kann nicht im gleichen Projekt getestet werden. Deswegen habe ich für de Tests auf das Maven Invoker Plugin zurückgegriffen. Dabei wird innerhalb des Maven Projektes in der src-Hierarchie ein it-Hierarchie eingerichtet. it steht für Integration Test. Innerhalb dieser it-Hierarchie werden dann weiter Integrationstest Projekte angelegt. Diese Projekte sind ganz normale Maven Projekte. Innerhalb ihres <dependencies> Elements wird auf das Basisprojekt des Annotation Processors referenziert. Als >scope> wird dabei compile verwendet, da der Processor nicht für die Runtimeumgebung benötigt wird, sondern nur für die Erstellung von Artefakten.


    MAVEN_PROJECT
         ├─ pom.xml
         └─┬─ src
           ├─┬─ main
           │ ├─── resources
           │ └─┬─ service-data
           │   └─┬─ META-INF
           │     └─┬─ services
           │       └── javax.annotation.processing.Processor
           └─┬─ it
             ├── test-project-1
             ├── test-project-2
             └── …

Die POM Datei des Basisprojektes sieht dann ungefähr wie folgt aus (unwichtige Teile wurden der besseren Lesbarkeit weggelassen):


    <project>

        <modelVersion>4.0.0</modelVersion>

        <groupId>processor-project</groupId>
        <artifactId>simple-annotation-processor</artifactId>
        <version>0.1.0-SNAPSHOT</version>

        <properties>
            <archive>${project.build.directory}/${project.build.finalName}.jar</archive>
        </properties>

        <build>
            <plugins>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>truezip-maven-plugin</artifactId>
                    <version>1.1</version>
                    <executions>
                        <execution>
                            <id>copy-into</id>
                            <goals>
                                <goal>copy</goal>
                            </goals>
                            <phase>package</phase>
                            <configuration>
                                <fileset>
                                    <directory>${basedir}/src/main/service-data/</directory>
                                    <includes>
                                        <include>**/*</include>
                                    </includes>
                                    <outputDirectory>${archive}</outputDirectory>
                                </fileset>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>

        <profiles>
            <profile>
                <id>run-its</id>
                <build>
                    <plugins>
                        <plugin>
                            <groupId>org.apache.maven.plugins</groupId>
                            <artifactId>maven-invoker-plugin</artifactId>
                            <version>1.8</version>
                            <configuration>
                                <projectsDirectory>src/it</projectsDirectory>
                                <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
                                <pomIncludes>
                                    <pomInclude>test-project-1/pom.xml</pomInclude>
                                    <pomInclude>test-project-2/pom.xml</pomInclude>
                                </pomIncludes>
                                <localRepositoryPath>${project.build.directory}/local-repo</localRepositoryPath>
                                <goals>
    	                            <goal>clean</goal>
                                    <goal>test</goal>
                                </goals>
                            </configuration>
                            <executions>
	                        <execution>
                	            <id>integration-test</id>
	                            <goals>
                                        <goal>install</goal>
                                        <goal>integration-test</goal>
                                    </goals>
                                </execution>
                            </executions>
                        </plugin>
                    </plugins>
                </build>
            </profile>
        </profiles>
    </project>

Die Integrationstest Projekte POMs sehen ungefähr wie folgt aus:


    <project>

        <modelVersion>4.0.0</modelVersion>

        <groupId>processor-project</groupId>
        <artifactId>simple-annotation-processor-integration-test-1</artifactId>
        <version>0.1.0-SNAPSHOT</version>

        <dependencies>
            <dependency>
                <groupId>processor-project</groupId>
                <artifactId>simple-annotation-processor</artifactId>
                <version>0.1.0-SNAPSHOT</version>
                <scope>compile</scope>
            </dependency>
        </dependencies>
    </project>

Fazit

Die Maven Projekt Konfiguration für Java Annotation Processor Implementierung ist komplexer als für normale Projekte. Am Ende lohnt sich der Aufwand. Durch die Prozessorimplementierung für Annotationen muß weniger Code manuell implementiert werden. Der Prozessor übernimmt diese fehleranfällige Arbeit der Codeerzeugung.