[FrontPage] [TitleIndex] [WordIndex

ServiceLoader

This page describes the writing and loading of interchangeable components for deegree 3 using the Java ServiceLoader.

1. Use cases

The ServiceLoader allows the use of different implementations for an interface. A good example is the support for various raster file formats. Internal all raster are stored in the deegree raster model, (AbstractRaster, etc). The loading of raster data into this model depends on the file format of the raster data. JPEG, PNG, GIF can be loaded with Java Advanced Imaging, but more sophisticated raster formats need specialized raster loader and writer. With the Java 6 ServiceLoader you can add new raster IO components without changing deegree, or even the configuration. The service loading is really simple. All you need is an interface that the components should offer (e.g. RasterReader), a class that needs these readers (e.g. RasterFactory) and one or more implementations (e.g. JAIRasterReader). The next paragraphs will describe these three parts.

1.1. The Interface

Well, you just create the interface -- also called provider -- you want all components to implement (like RasterReader). However, if you want some more flexibility on the implementation that the service loader chooses, you should add a getXXXX(String type) method to the interface. This method should return itself or null, depending if the implementation supports the requested type. So if a JAIRasterReader implementation is called with getRasterReader("png") it should return this, if it is called with type "ecw" it should return null. A provider can respond to more than one type. Also it is possible and useful to respond to different accuracy of the specified type. The JAIRasterReader may respond to org.deegree.dataaccess.jai.JAIRasterReader, jai, image, png, jpeg, etc. The ServiceLoader is not limited to a String as the type specifier, it's all up to your provider interface.

1.2. The ServiceLoader

The class that acts as the service loader contains a static ServiceLoader object. RasterFactory E.g.:1

private static ServiceLoader<RasterReader> rasterReaderLoader = ServiceLoader.load( RasterReader.class );

The ServiceLoader is iterable and returns all implementations of this type. If you just want one arbitrary implementation, you can use the first. Otherwise you have to call the getXXXX(type) method until you get a non-null response.

Here is a code snippet that shows how to get a raster reader for a specific format.

for ( RasterReader reader : rasterReaderLoader ) {
    RasterReader possibleReader = reader.getRasterReader( format );
    if ( possibleReader != null ) {
        return possibleReader;
    }
}

And that's it.

1.3. The Components

To allow the ServiceLoader to automatically find all implementations of an interface you have to name the class inside the META-INF/services directory. Put a file with the complete class name of the interface as the file name in this directory and list all implementing classes of this interface in this file. This META-INF should be included in your .jar file or in the classes directory (where your implementation is).

Back to our example: The RasterReader interface is called org.deegree.model.coverage.raster.RasterReader and is contained in the commons module. The JAI-based implementation of this interface is in the dataaccess module. To allow the RasterFactory in the commons module to access the JAIRasterReader, the dataaccess module has the file META-INF/services/org.deegree.model.coverage.raster.RasterReader with the following contents:

org.deegree.dataaccess.jai.JAIRasterReader

As soon as the .jar file from dataaccess (or the whole classes directory) is in the classpath of the commons module included, the RasterFactory can access the JAI implementation.

2. Creating new extensions

To create a new RasterReader you have to implement the interface and put that class in a .jar. The jar file also should contain the file META-INF/services/org.deegree.model.coverage.raster.RasterReader with the name of your implementing class (e.g. com.example.raster.MyRasterReader). Now put your jar in the classpath of your deegree service or deegree based application and enjoy your own RasterReader.

3. Loading Sequence

A last warning: The order of the service provider depends on the order in your classpath. So if you have two provider for 'tiff', only the first one in your classpath will be chosen. This can be tricky if you have no influence on the classpath (e.g. automaticaly generated in tomcat or via the new Java 6 wildcard option java -cp lib/*). If you depend on a specific implementation, you should state that in your call to the service provider (e.g. set type to jai instead of tiff if you need a JAI based reader).


CategoryDeegree3

  1. If you look in the code you won't see the RasterReader directly, because the provider for this interface is combined with the provider of the RasterWriter into the RasterIOProvider (1)


2018-04-20 12:05