Generating signatures of the Java Runtime
Basic
To generate signatures of any API, you simple construct a project with the appropriate dependencies exposed by the API and then add an execution of the animal-sniffer:build
goal to your project. In the case of the Java Runtime, this is a project with no dependencies, e.g.
<project> <modelVersion>4.0.0</modelVersion> <groupId>____</groupId> <artifactId>____</artifactId> <version>____</version> <packaging>pom</packaging> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>animal-sniffer-maven-plugin</artifactId> <version>1.24</version> <executions> <execution> <id>___id of execution___</id> <phase>package</phase> <goals> <goal>build</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
Then you just build this project with the appropriate Java Runtime, and it will generate the signatures of that Java Runtime.
The observant reader may have spotted that it may not always be possible to run Maven with the Java Runtime that you want to generate the signatures of. In these cases you have a number of solutions:
- Use Maven Toolchains support and the
jdk
parameter to define what toolchain you require. This will use automatic boot classpath detection, which does not work for Java 1.1 or earlier and may not work on some exotic Java Runtimes.An example of using the
jdk
parameter is:<project> ... <build> ... <plugins> ... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>animal-sniffer-maven-plugin</artifactId> <version>1.24</version> ... <configuration> ... <jdk> <version>[1.6.0,1.6.1)</version> <vendor>sun</vendor> </jdk> ... </configuration> ... </plugin> ... </plugins> ... </build> ... </project>
- Use the
javaHome
parameter to specify the Java Home that you want to generate the signatures of. This will use automatic boot classpath detection, which does not work for Java 1.1 or earlier and may not work on some exotic Java Runtimes.An example of using the
javaHome
parameter is:<project> ... <build> ... <plugins> ... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>animal-sniffer-maven-plugin</artifactId> <version>1.24</version> ... <configuration> ... <javaHome>C:\Program Files\Java\jdk_1.6.0_14</javaHome> ... </configuration> ... </plugin> ... </plugins> ... </build> ... </project>
- Use the
javaHomeClassPath
parameter to specify the exact classpath to generate the signatures of. This is the only way to specify the Java Runtime when automatic boot classpath detection fails.An example of this technique is:
<project> ... <build> ... <plugins> ... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>animal-sniffer-maven-plugin</artifactId> <version>1.24</version> ... <configuration> ... <javaHomeClassPath> <javaHomeClassPath>C:\Program Files\Java\jdk_1.6.0_14\jre\lib\rt.jar</javaHomeClassPath> <javaHomeClassPath>C:\Program Files\Java\jdk_1.6.0_14\jre\lib\jsse.jar</javaHomeClassPath> </javaHomeClassPath> ... </configuration> ... </plugin> ... </plugins> ... </build> ... </project>
Generating "pure" signatures
The above examples generate signatures of the entire classpath of the JRE. That will include all the implementation classes which are not part of the public contract of the JRE. For example sun.misc.BASE64Encoder is part of Sun's JRE runtime libraries, but is not part of the JRE specification and if you run on a JRE produced by a different vendor, it is highly likely that that class will not be available to your program.
In order to ensure that the Java signatures you generate only include those classes which are officially published by the JRE, you will need to tune your signatures.
Inclusion based tuning
One technique is to only include those classes which you know are part of the JRE public specification. For example:
<project> ... <build> ... <plugins> ... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>animal-sniffer-maven-plugin</artifactId> <version>1.24</version> ... <configuration> ... <jdk> <version>[1.4.0,1.5.0)</version> </jdk> <includeClasses> <includeClass>java.*</includeClass> <includeClass>javax.*</includeClass> <includeClass>org.omg.*</includeClass> <includeClass>org.w3c.dom.*</includeClass> <!-- etc --> ... </includeClasses> ... </configuration> ... </plugin> ... </plugins> ... </build> ... </project>
This requires that you known exactly what classes are part of the JRE specification.
Exclusion based tuning
The other technique is to specify which classes are not to be included. Note that a combination of the two can also be used. For example:
<project> ... <build> ... <plugins> ... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>animal-sniffer-maven-plugin</artifactId> <version>1.24</version> ... <configuration> ... <jdk> <version>[1.4.0,1.5.0)</version> </jdk> <excludeClasses> <excludeClass>com.sun.*</excludeClass> <excludeClass>sun.*</excludeClass> <!-- etc --> ... </excludeClasses> ... </configuration> ... </plugin> ... </plugins> ... </build> ... </project>
When the version of Java you require is not available
By default, if the version of Java that you require is not available, the animal-sniffer plugin will fail the build. If you are generating multiple signatures, this may not be exactly what you want. For example you may be generating signatures for JREs on Windows, Linux and MacOS from the same project. This would require building the same project at least three times and setting the skipIfNoJavaHome
option to true
for example
<project> ... <build> ... <plugins> ... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>animal-sniffer-maven-plugin</artifactId> <version>1.24</version> ... <configuration> ... <skipIfNoJavaHome>true</skipIfNoJavaHome> ... </configuration> ... <executions> <execution> <id>windows</id> ... <configuration> ... <classifier>windows</classifier> <jdk> <version>[1.4.0,1.5.0)</version> <os>windows</os> </jdk> ... </configuration> ... </execution> <execution> <id>linux ... <configuration> ... <classifier>linux</classifier> <jdk> <version>[1.4.0,1.5.0)</version> <os>linux</os> </jdk> ... </configuration> ... </execution> </executions> </plugin> ... </plugins> ... </build> ... </project>
Automatic boot classpath detection
Animal sniffer uses a helper module to try and determine the boot classpath of the Java Home that has been specified. This can be done in one of two ways:
- Indirectly via a toolchain reference using the
jdk
configuration option - Directly using the
javaHome
option
The helper module should work for most common versions of Java providing they are at least Java 1.2.
If the default helper module does not work for the version of Java that you need to generate signatures of, you can replace the helper module with your own module. The replacement module must meet the following specification:
- Must be an executable JAR file
- If the Java boot classpath cannot be detected, it must return with exit code 1.
- If the Java boot classpath has been detected, it must print the boot class path to
stdout
as a single line with each element of the boot classpath separated from the next usingFile.pathSeparator
and return with exit code 0.
When invoking the helper module, animal-sniffer will:
- Clear the CLASSPATH environment variable in the forked execution environment.
- Clear the JAVA_HOME environment variable in the forked execution environment.
- Invoke the
java
command in thejavaHome
orjdk
that has been configured with the options-jar
and the full path to the Java boot classpath detector module specified in the configuration of the animal-sniffer plugin. Note: this module JAR file will be located in the Maven local repository.
A replacement module must be added to the dependencies
section of the Animal Sniffer Plugin and the groupId and artifactId must be configured using the jbcpdGroupId
and jbcpdArtifactId
configuration options. For example, if your custom boot classpath detector is com.mycompany:detect-boot-classpath:1.2.0:jar then you could configure animal-sniffer to use this with the following:
<project> ... <build> ... <plugins> ... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>animal-sniffer-maven-plugin</artifactId> <version>1.24</version> ... <configuration> ... <jbcpdGroupId>com.mycompany</jbcpdGroupId> <jbcpdArtifactId>detect-boot-classpath</jbcpdArtifactId> ... </configuration> ... <dependencies> ... <dependency> <groupId>com.mycompany</groupId> <artifactId>detect-boot-classpath</artifactId> <version>1.2.0</version> </dependency> ... </dependencies> ... </plugin> ... </plugins> ... </build> ... </project>