See: Description
Class | Description |
---|---|
LayerBuilder |
Convenience class for generating fragments of an XML layer.
|
LayerGeneratingProcessor |
Convenience base class for an annotation processor which creates XML layer entries.
|
Exception | Description |
---|---|
LayerGenerationException |
Exception thrown when a layer entry cannot be generated due to erroneous sources.
|
Whenever an SPI author defines a new way for objects to be registered in
the system filesystem, it is encouraged to also define a matching annotation
which can create such a registration. If the SPI is associated with a particular
Java interface (or abstract class), conventionally this annotation should be named
Registration
and be located as a nested type inside the interface.
For example, consider an interface FrobnitzFactory
which should be
registered for a particular data type such as text/html
. The SPI may
declare that a frobnitz factory should be located in the system filesystem
in the folder FrobnitzFactories/mime/type/
where mime/type
is
the associated data type, and should be declared as an instance file whose instance
is assignable to FrobnitzFactory
(basename of file irrelevant).
(The SPI could also have just declared a global service interface where each instance specifies its desired MIME type in the return value of a method. This would work but would be undesirable from a performance perspective: the first time the SPI ran, it would need to load every frobnitz factory in the system, which could mean a lot of class loading, even though only one was actually going to be used. Choosing a smart registration style can help avoid this kind of performance mistake, and friendly registration annotations mean that module developers can do the right thing without much effort.)
The SPI may also stipulate that for a given data type, it may matter which factory is found "first". SPI code to handle the factories might look like:
static Frobnitz findFrobnitz(FileObject f) {
for (FrobnitzFactory ff : Lookups.forPath("FrobnitzFactories/" + f.getMIMEType()).
lookup(FrobnitzFactory.class).allInstances()) {
Frobnitz fz = ff.newFrobnitz(f);
if (fz != null) {
return fz;
}
}
return null;
}
There should then be an interface with a corresponding annotation:
public interface FrobnitzFactory { Frobnitz newFrobnitz(FileObject file); // may return null @interface Registration { String mimeType(); int position() default Integer.MAX_VALUE; } }
Using the annotation is simple. The module author need create just one file containing both the factory and its registration:
@FrobnitzFactory.Registration(mimeType="text/html", position=300) public class HtmlFactory implements FrobnitzFactory { public Frobnitz newFrobnitz(FileObject file) {...} }
Now writing the annotation processor to create such a layer registration is easy.
Put the processor in some nonpublic package in the SPI module and register it
using ServiceProvider
.
You should extend LayerGeneratingProcessor
, which manages the physical
writing of the generated layer fragment(s) in cooperation with any other active processors.
The LayerBuilder
helper class is used to create file entries and attributes.
LayerGenerationException
can also be thrown if the source code is erroneous.
@ServiceProvider(service=Processor.class) @SupportedSourceVersion(SourceVersion.RELEASE_6) public class FrobnitzFactoryProcessor extends LayerGeneratingProcessor { public @Override Set<String> getSupportedAnnotationTypes() { return Collections.singleton(Registration.class.getCanonicalName()); } protected boolean handleProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) throws LayerGenerationException { if (roundEnv.processingOver()) { return false; } for (Element e : roundEnv.getElementsAnnotatedWith(Registration.class)) { Registration r = e.getAnnotation(Registration.class); if (!r.mimeType().matches("(text|application|image)/([^/]+)")) { throw new LayerGenerationException("Bad MIME type: " + r.mimeType(), e); } layer(e).instanceFile("FrobnitzFactories/" + r.mimeType(), null, FrobnitzFactory.class).position(r.position()).write(); } return true; } }
Now when the module is compiled, build/classes/META-INF/generated-layer.xml
should look something like this:
<filesystem> <folder name="FrobnitzFactories"> <folder name="text"> <folder name="html"> <file name="my-module-HtmlFactory.instance">> <attr name="position" intvalue="300"/> </file> </folder> </folder> </folder>> </filesystem>
and this layer should be loaded automatically by the module system (in addition to any explicit layer specified in source code).
There are two basic ways to test a layer-generating processor:
AnnotationProcessorTestUtils
is useful.ServiceProviderProcessorTest
demonstrates both styles.