Package org.jboss.jandex
It supports the following capabilities:
- Indexing all runtime visible Java annotations for a set of classes into a memory efficient representation.
- Indexing the class hierarchy and interface implementation of a set classes.
- Browsing and searching of declared methods on an indexed class.
- Browsing and searching of declared fields on an indexed class.
- Browsing of all generic type information on methods, fields, and classes.
- Browsing and searching annotations, including Java 8 type annotations
- Persisting an index into a custom storage efficient format.
- Quick-loading of the storage efficient format
- Compatibility with previous API and storage format versions
- Execution via an API, a command line tool, and ant
The starting point for most usages is to use Indexer
on a set of class file streams
to ultimately produce an Index
. Alternatively, if a persisted Jandex index file is
available, IndexReader
can be used to load the Index
directly
from disk. The index files can be produced by using an IndexWriter
, or by using
the command line tool, which is available when using "-jar" on the Jandex jar, or by using the
JandexAntTask
with ant.
Browsing a Class
The following example demonstrates indexing a class and browsing its methods:
// Index java.util.Map Indexer indexer = new Indexer(); // Normally a direct file is opened, but class-loader backed streams work as well. InputStream stream = getClass().getClassLoader().getResourceAsStream("java/util/Map.class"); indexer.index(stream); Index index = indexer.complete(); // Retrieve Map from the index and print its declared methods ClassInfo clazz = index.getClassByName(DotName.createSimple("java.util.Map")); for (MethodInfo method : clazz.methods()) { System.out.println(method); }
Creating a Persisted Index
The following example demonstrates indexing a class and persisting the index to disk. The resulting file can later be loaded scanning Java classes:
// Index java.util.Map Indexer indexer = new Indexer(); // Normally a direct file is opened, but class-loader backed streams work as well. InputStream stream = getClass().getClassLoader().getResourceAsStream("java/util/Map.class"); indexer.index(stream); Index index = indexer.complete(); FileOutputStream out = new FileOutputStream("/tmp/index.idx"); IndexWriter writer = new IndexWriter(out); try { writer.write(index); } finally { safeClose(out); }
Creating a Persisted Index Using the CLI
The following example demonstrates indexing hibernate core, followed by the entire Java JDK using Jandex on the CLI:
$ java -jar target/jandex-2.0.0.Alpha1.jar hibernate-core-4.0.0.Final.jar Wrote /Users/jason/devel/jandex/hibernate-core-4.0.0.Final-jar.idx in 0.9020 seconds (2722 classes, 20 annotations, 1696 instances, 621565 bytes) $ java -jar target/jandex-2.0.0.Alpha1.jar rt.jar Wrote /Users/jason/devel/jandex/rt-jar.idx in 4.2870 seconds (19831 classes, 41 annotations, 1699 instances, 4413790 bytes)
The above summary output tells us that this version of hibernate has 2,722 classes, and those classes contained 1,696 annotation declarations, using 20 different annotation types. The resulting index is 606KB uncompressed, which is only 14% of the 4.1MB compressed jar size, or 4% of the uncompressed class file data. If the index is stored in the jar (using the -m option) it can be compressed an additional 47%, leading to a jar growth of only 8%
Loading a Persisted Index
The following example demonstrates loading the index from the previous example and using that
index to print all methods on java.util.Map
:
FileInputStream input = new FileInputStream("/tmp/index.idx"); IndexReader reader = new IndexReader(input); try { index = reader.read(); } finally { safeClose(input); } // Retrieve Map from the index and print its declared methods ClassInfo clazz = index.getClassByName(DotName.createSimple("java.util.Map")); for (MethodInfo method : clazz.methods()) { System.out.println(method); }
Searching for an Annotation
The following example demonstrates indexing the Thread and String classes, and searching for methods that have been marked with @Deprecated:
Indexer indexer = new Indexer(); InputStream stream = getClass().getClassLoader().getResourceAsStream("java/lang/Thread.class"); InputStream stream = getClass().getClassLoader().getResourceAsStream("java/lang/String.class"); indexer.index(stream); Index index = indexer.complete(); DotName deprecated = DotName.createSimple("java.lang.Deprecated"); List<AnnotationInstance> annotations = index.getAnnotations(deprecated); for (AnnotationInstance annotation : annotations) { switch (annotation.target().kind()) { case METHOD: System.out.println(annotation.target()); break; } }
Analyzing Generics
The following example demonstrates indexing the Collections class and printing the resolved bound
on the List<T>
method parameter, which resolves to Comparable from the method type parameter.
The sort() method analyzed by the example is defined in source as:
public static <T extends Comparable<? super T>> void sort(List<T> list)
The example code, which prints "Comparable<? super T>"
, followed by "T"
is:
Indexer indexer = new Indexer(); InputStream stream = getClass().getClassLoader().getResourceAsStream("java/util/Collections.class"); indexer.index(stream); Index index = indexer.complete(); // Find method ClassInfo clazz = index.getClassByName(DotName.createSimple("java.util.Collections")); Type listType = Type.create(DotName.createSimple("java.util.List"), Type.Kind.CLASS); MethodInfo sort = clazz.method("sort", listType); Type t = sort.parameters().get(0).asParameterizedType() // List<T extends Comparable<? super T>> .arguments().get(0) // T extends Comparable<? super T> .asTypeVariable().bounds().get(0); // Comparable<? super T> System.out.println(t); System.out.println(t.asWildcardType().superBound()); // T
Browsing Type Annotations
Consider a complex nested generic structure which contains a @Label
annotation
Map<Integer, List<@Label("Name") String>> names
The following code will print "Name"
, the annotation value associated with the type:
Indexer indexer = new Indexer(); InputStream stream = new FileInputStream("/tmp/Test.class"); indexer.index(stream); stream = new FileInputStream("/tmp/Test$Label.class"); indexer.index(stream); Index index = indexer.complete(); DotName test = DotName.createSimple("Test"); FieldInfo field = index.getClassByName(test).field("names"); System.out.println( field.type().asParameterizedType().arguments().get(1) .asParameterizedType().arguments().get(0) .annotations().get(0).value().asString() );
Searching for Type Annotations
A type annotation can also be located by searching for the annotation. The target for a found type annotation
is represented as a TypeTarget
. The TypeTarget
provides a reference to the annotated
type, as well as the enclosing target that contains the type. The target itself can be a method, a class, or a field.
The usage on that target can be a number of places, including parameters, return types, type parameters,
type arguments, class extends values, type bounds and receiver types. Subclasses of TypeTarget
provide
the necessary information to locate the starting point of the usage.
Since the particular type use can occur at any depth, the relevant branch of the type tree constrained by the above starting point must be traversed to understand the context of the use.
Consider a complex nested generic structure which contains a @Label
annotation
Map<Integer, List<@Label("Name") String>> names
The following code locates a type annotation using a hardcoded path:
Indexer indexer = new Indexer(); InputStream stream = new FileInputStream("/tmp/Test.class"); indexer.index(stream); stream = new FileInputStream("/tmp/Test$Label.class"); indexer.index(stream); Index index = indexer.complete(); List<AnnotationInstance> annotations = index.getAnnotations(DotName.createSimple("Test$Label")); for (AnnotationInstance annotation : annotations) { if (annotation.target().kind() == AnnotationTarget.Kind.TYPE) { TypeTarget typeTarget = annotation.target().asType(); System.out.println("Type usage is located within: " + typeTarget.enclosingTarget()); System.out.println("Usage type: " + typeTarget.usage()); System.out.println("Target type:" + typeTarget.target()); System.out.println("Equivalent? " + (typeTarget.enclosingTarget().asField().type() .asParameterizedType().arguments().get(1) .asParameterizedType().arguments().get(0) == typeTarget.target())); }
The above code prints the following output:
Type usage is located within: java.util.Map<java.lang.Integer, java.util.List<@Label(value = "Name") java.lang.String>> Test.names Usage type: EMPTY Target type:@Label(value = "Name") java.lang.String Equivalent? true
-
ClassDescriptionAn annotation instance represents a specific usage of an annotation on a target.Represents an object that can be a target of an annotation.Specifies the kind of object a target represents.AnnotationTargetFilterCollection<T extends AnnotationTarget>Iteratively filters a map of multiple annotation targets to the instances with a specific target type.An annotation value represents a specific name and value combination in the parameter list of an annotation instance.Specifies the kind of annotation value, which can be used to determine the underlying Java type.Represents a Java array type declaration.Represents a type annotation target which occurs in the extends or implements clause of an enclosing class.Represents a class entry in an index.Provides information on the enclosing method or constructor for a local or anonymous class, if available.Describes the form of nesting used by a classRepresents a standard raw class name.Composite annotation index.A DotName represents a dot separated name, typically a Java package or a Java class.Represents a type annotation target which occurs directly on a field type, a method return type, or a method receiver type.Represents a field.A list which wraps FieldInternal objects with a FieldInfo, so that the declaring class' reference can be set.The shared internal representation for FieldInfo objects.A simple recursive decent generic signature parser.An index useful for quickly processing annotations.Analyzes and indexes the annotation and key structural information of a set of classes.Reads a Jandex index file and returns the saved index.A common base class for index reader implementations.Reads a Jandex index file and returns the saved index.Reads a Jandex index file and returns the saved index.The basic contract for accessing Jandex indexed information.Writes a Jandex index file to a stream.Writes a Jandex index file to a stream.Writes a Jandex index file to a stream.Ant Task that indexes jars, and saves the resulting indexClass which contains utility methods to create an index for a jar fileResponsible for launching the indexing tool on a java command line.Represents a Java method, constructor, or static initializer.A list which wraps MethodInternal objects with a MethodInfo, so that the declaring class' reference can be set.The shared internal representation for MethodInfo objects.Represents an individual Java method parameter that was annotated.Represents a type annotation target which occurs within a method parameter type.Represents a module descriptor entry in an index.A collection of intern pools.An input stream that reads integers that were packed by
PackedDataOutputStream
An output stream that can pack integers into as few bytes as possible.Represents a generic parameterized type.A common parent for type targets which provide a position.Represents a primitive Java type.Specifies the underlying Java primitive type for aPrimitiveType
Represents an individual Java record component that was annotated.A list which wraps RecordComponentInternal objects with a RecordComponentInfo, so that the declaring class' reference can be set.The shared internal representation for RecordComponentInfo objects.The result from a jar indexing operation.A strong intern pool.Represents a type annotation target which occurs within a throwable type on a method.Represents a Java type declaration usage that is specified on methods, fields, classes, annotations, or other types.Represents a "kind" of Type.Represents a type annotation target which occurs within a bound of type parameter type.Represents a type annotation target which occurs within a type parameter type.Represents a type that is the target of a type annotation.Specifies a form of usage of a type annotationRepresents a resolved type parameter or type argument.Represents a type variable that could not be resolved during indexing.The version encountered is not supported.Common utilitiesSpecifies "void" in a method signature.Represents a generic wildcard.