Kirk Rader  1.0-SNAPSHOT
Packages | Classes
Package us.rader.logging

Annotation processing using reflection and instrumentation. More...

Packages

package  demo
 Manual integration tests for the us.rader.logging package.
 
package  statuscodes
 Status code extraction using reflection and instrumentation.
 
package  tracing
 Aspects that inject logging into any application.
 

Classes

class  AnnotationsAgent
 Instrumentation agent used to scan loaded classes. More...
 

Detailed Description

Annotation processing using reflection and instrumentation.

Java's reflection API supports:

I.e. given a set of instances of Class<?>, one can use reflection to inspect those meta-objects for information about the corresponding types, including information about annotations. This begs the question: how does one obtain such a set of meta-objects?

If one already knows that a given class has been loaded as an explicit dependency at compile time, one can simply reference the relevant meta-object directly using syntax like SomeType.class. Sometimes, however – and especially when writing tools to process custom annoations – one needs to dynamically load classes that are not explicit compile time dependencies and discover what classes were loaded at run time.

Baffingly enough, the reflection API is not sufficient to accomplish this. For reasons surpassing understanding, those responsible for Java's meta-object protocol chose to enable this most basic of functionality only through a separate API called instrumentation.

What this means is that only a class that has been configured to run as an instrumentation agent is supplied with an instance implementing the Instrumentation interface, and that is the only supported API that can answer the question, "what classes are currently loaded?" by way of its Instrumentation.getAllLoadedClasses() method.

That is the purpose of of the us.rader.logging.AnnotationsAgent class. In particular, it makes Instrumentation.getAllLoadedClasses() available as a static method when us.rader.logging.AnnotationsAgent is loaded via either the "premain" or "agent" mechanisms. This requires that the correct Premain-Class and Agent-Class entries appear in the JAR file's manifest from which us.rader.logging.AnnotationsAgent is loaded, as is accomplished by the maven-jar-plugin configuration of the status-codes project's POM:

<?xml version="1.0" encoding="UTF-8"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>us.rader.logging</groupId>
<artifactId>logging</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>status-codes</artifactId>
<name>status-codes</name>
<description>Custom annotations to support monitoring and reporting</description>
<distributionManagement>
<site>
<id>site</id>
<name>Site</name>
<!-- TODO: What should the site URL be? -->
<url>scp://rader.us/docs/${project.parent.artifactId}/${project.parent.version}/${project.artifactId}/</url>
</site>
</distributionManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.sonarsource.java</groupId>
<artifactId>sonar-jacoco-listeners</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Premain-Class>us.rader.logging.AnnotationsAgent</Premain-Class>
<Agent-Class>us.rader.logging.AnnotationsAgent</Agent-Class>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemProperties>
<property>
<name>java.util.logging.config.file</name>
<value>src/test/resources/logging.properties</value>
</property>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
</project>

It also means that this feature can only be exploited when the JAR is loaded in the appropriate way at run time:

Note that the status-codes module can be used without installing its JAR as an instrumentation agent at run time, but this facility is supported where appropriate. For example, the module described by the Status Codes Maven Plugin page provides its own custom class loader – us.rader.logging.maven.DynamicClassLoader – which is an approach to discovering classes at run time suitable for a build tool like a Maven plugin.

See also
us.rader.logging.AnnotationsAgent