Service Provider Interfaces

One obvious question resulting from the previous discussion is how do the ImageIO static methods know which ImageReader(s) can decode the image data? Theoretically, one way this can be done is to have each of the ImageReaders contain a set of methods that would return a list of image formats, file suffixes, and MIME types that it can decode. Another method could take an ImageInputStream and return true or false if the ImageReader can decode it. Using these techniques, it would be up to the plug-in developers to write these methods and therefore provide this information. Although this idea has its merits, there is one problem: In order to find out an ImageReader's functionality, it needs to be registered; and to register each of the ImageReaders, an object of each ImageReader class would need to be instantiated. This would be a waste of time and memory because not all of the ImageReaders will be needed. For this reason, service provider interfaces (spi) are used. Spis are small classes (such as ImageReaderSpi and ImageWriterSpi) that are used to describe the functionality of larger classes (such as ImageReader and ImageWriter). Thus, in practice the JVM can instantiate an object of each ImageReaderSpi, and these objects can be used to decide which ImageReader(s) can decode an image format. Similarly, the JVM can instantiate an object of each ImageWriterSpi, and these objects can be used to decide which ImageWriter(s) can encode an image format.

ImageReaderSpi

Consider Listing 5.3, which implements an ImageReaderSpi. The purpose of this listing is to illustrate how the ImageReaderSpi passes information about its corresponding ImageReader to the ImageIO's static methods. Note that the ImageReader that corresponds to this ImageReaderSpi will be developed in a later section entitled “ImageReadParam.”

Listing 5.3 ch5v1ImageReaderSpi.java
package ch5.imageio.plugins;

import java.io.*;
import java.util.*;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;

/**
 *  Simple, non-functional ImageReaderSpi used to understand how
 *  information regarding format name, suffices and mime types
 *  get passed to ImageIO static methods
 */
public class ch5v1ImageReaderSpi extends ImageReaderSpi {

    static final String[] suffixes = { "ch5", "CH5"};
    static final String[] names = {"ch5"};
    static final String[] MIMETypes = { "image/ch5" };

    static final String version = "0.50";

    static final String readerCN="ch5.imageio.plugins.ch5v1ImageReader";

    static final String vendorName = "Company Name";

    //writerSpiNames
    static final String[] wSN={"ch5.imageio.plugins.ch5v1ImageWriterSpi"};

    //StreamMetadataFormatNames and StreamMetadataFormatClassNames
    static final boolean supportedStandardStreamMetadataFormat = false;
    static final String nativeStreamMFN = null;
    static final String nativeStreamMFCN = null;
    static final String[] extraStreamMFN = null;
    static final String[] extraStreamMFCN = null;

    //ImageMetadataFormatNames and ImageMetadataFormatClassNames
    static final boolean supportedStandardImageMetadataFormat = false;
    static final String nativeImageMFN = null;
    static final String nativeImageMFCN = null;
    static final String[] extraImageMFN = {null};
    static final String[] extraImageMFCN = {null};

    public ch5v1ImageReaderSpi() {
    super(vendorName,
          version,
          names,
          suffixes,
          MIMETypes,
          readerCN, // reader class name
          STANDARD_INPUT_TYPE,
          wSN, // writer spi names
          supportedStandardStreamMetadataFormat,
          nativeStreamMFN,
          nativeStreamMFCN,
          extraStreamMFN,
          extraStreamMFCN,
          supportedStandardImageMetadataFormat,
          nativeImageMFN,
          nativeImageMFCN,
          extraImageMFN,
          extraImageMFCN);
    }

    public String getDescription(Locale locale) {
    return "Demo ch5 image reader, version " + version;
    }

    /**
     * We haven't created the corresponding ImageReader class yet,
     * so we'll just return null for now.
     */
    public ImageReader createReaderInstance(Object extension) {
    return new ch5v1ImageReader(this);
    }

    /**
     * This method gets called when an application wants to see if
     * the input image's format can be decoded by this ImageReader.
     * In this case, we'll simply check the first line of data to
     * see if it is a 5 which is the format type's magic number.
     * Note that we initially make sure the input object is of
     * type ImageInputStream so we know it is compatible with
     * mark and reset methods.
     */
    public boolean canDecodeInput(Object input) {
    boolean reply = false;

        if (!(input instanceof ImageInputStream))
        return reply;

    ImageInputStream iis = (ImageInputStream)input;
    iis.mark(); // mark where we are in ImageInputStream
    try {
        String magicNumber = iis.readLine().trim();
        iis.reset(); // reset stream back to marked location
        if (magicNumber.equals("5"))
        reply = true;
    }
    catch (IOException exception) {
    }
    return reply;
    }

    /**
     * This method gets called when the set of file suffices is
     * requested by the ImageIO's getImageReadersBySuffix method
     * It doesn't need to be redefined here, but is done for
     * illustrative purposes
     */
    public String[] getFileSuffixes() {
    return super.getFileSuffixes();
    }

    /**
     * This method gets called when the set of file mime types is
     * requested by the ImageIO's getImageReadersByMIMEType method
     * It doesn't need to be redefined here, but is done for
     * illustrative purposes
     */
    public String[] getMIMETypes() {
    return super.getMIMETypes();
    }

    /**
     * This method gets called when the set of format names is
     * requested by the ImageIO's getImageReadersByFormatName method
     * It doesn't need to be redefined here, but is done for
     * illustrative purposes
     */
    public String[] getFormatNames() {
    return super.getFormatNames();
    }
}

ImageWriterSpi

In general, the explanation of the ImageWriterSpi class is similar to that of the ImageReaderSpi class, except that instead of having a

public boolean canDecodeImage(Object source)

it has a

public boolean canEncodeImage(ImageTypeSpecifier its)

where, as previously mentioned, the ImageTypeSpecifier class is simply a container class for holding an image's ColorModel and SampleModel. Thus, while an ImageReader can be chosen using the input image's suffix, MIME type, format, or by examining the input stream, an ImageWriter can be chosen using the output image's suffix, MIME type, format, or by considering the image's ColorModel and SampleModel pair. Sample code for an ImageWriterSpi is shown in Listing 5.4. In this listing, all metadata will be given null values. In the final section, “Final Plug-in Code,” it will be redone using metadata.

Listing 5.4 ch5v1ImageWriterSpi.java
package ch5.imageio.plugins;

import java.io.*;
import java.util.*;
import java.awt.image.*;
import javax.imageio.ImageWriter;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageInputStream;

/**
 *  Simple, non-functional ImageWriterSpi used to understand how
 *  information regarding format name, suffices and mime types
 *  get passed to ImageIO static methods
 */
public class ch5v1ImageWriterSpi extends ImageWriterSpi {

    static final String[] suffixes = {"ch5", "CH5"};
    static final String[] names = {"ch5"};
    static final String[] MIMETypes = {"image/ch5" };

    static final String version = "0.50";

    static final String writerCN = "ch5.imageio.plugins.ch5v1ImageWriter";

    static final String vendorName = "Company Name";
    static final String[] rdrSpiNames={"ch5.imageio.ch5v1ImageReaderSpi"};

    static final boolean supportsStandardStreamMetadataFormat = false;
    static final String nativeStreamMetadataFormatName = null;
    static final String nativeStreamMetadataFormatClassName = null;
    static final String[] extraStreamMetadataFormatNames = null;
    static final String[] extraStreamMetadataFormatClassNames = null;


    static final boolean supportsStandardImageMetadataFormat = false;
    static final String nativeImageMetadataFormatName = null;
    static final String nativeImageMetadataFormatClassName = null;
    static final String[] extraImageMetadataFormatNames = null;
    static final String[] extraImageMetadataFormatClassNames = null;

    public ch5v1ImageWriterSpi() {
    super(vendorName,
          version,
          names,
          suffixes,
          MIMETypes,
          writerCN, //writer class name
          STANDARD_OUTPUT_TYPE,
          rdrSpiNames, //reader spi names
          supportsStandardStreamMetadataFormat,
          nativeStreamMetadataFormatName,
          nativeStreamMetadataFormatClassName,
          extraStreamMetadataFormatNames,
          extraStreamMetadataFormatClassNames,
          supportsStandardImageMetadataFormat,
          nativeImageMetadataFormatName,
          nativeImageMetadataFormatClassName,
          extraImageMetadataFormatNames,
          extraImageMetadataFormatClassNames);
    }

    public String getDescription(Locale locale) {
    return "Demo ch5 image writer, version " + version;
    }


    /**
     * We haven't created the corresponding ImageWriter class yet,
     * so we'll just return null for now.
     */
    public ImageWriter createWriterInstance(Object extension) {
    return new ch5v1ImageWriter(this);
    }

    /**
     * This method gets called when an application wants to see if
     * the corresponding ImageWriter can encode an image with
     * a ColorModel and SampleModel specified by the ImageTypeSpecifier.
     * For this example, we will only advertise that we can encode
     * gray scale images with 8 bit pixels.
     */
    public boolean canEncodeImage(ImageTypeSpecifier its) {
    if (its.getBufferedImageType() == BufferedImage.TYPE_BYTE_GRAY)
        return true;
    else
        return false;
    }

    /**
     * This method gets called when the set of file suffices is
     * requested by the ImageIO's getImageWritersBySuffix method
     * It doesn't need to be redefined here, but is done for
     * illustrative purposes
     */
    public String[] getFileSuffixes() {
    return super.getFileSuffixes();
    }

    /**
     * This method gets called when the set of file mime types is
     * requested by the ImageIO's getImageWritersByMIMEType method
     * It doesn't need to be redefined here, but is done for
     * illustrative purposes
     */
    public String[] getMIMETypes() {
    return super.getMIMETypes();
    }

    /**
     * This method gets called when the set of format names is
     * requested by the ImageIO's getImageWritersByFormatName method
     * It doesn't need to be redefined here, but is done for
     * illustrative purposes
     */
    public String[] getFormatNames() {
    return super.getFormatNames();
    }
}

Using JAR Files to Specify SPIs

In order for the JVM to discover the ImageReader and ImageWriter plug-ins, they must be contained in a properly formatted JAR file. Furthermore, the JAR file must contain a META-INF/services directory for listing the service providers contained in that JAR file. For each service provider interface that is implemented by a class stored in this JAR file, a file whose name is the fully qualified class name of the SPI should be placed in the services directory. Inside each of these files should be the fully qualified names of the implementation classes contained in the JAR file (one per line). For example, in Listing 5.3 the SPI is javax.imageio.spi.ImageReaderSpi, so that will be the name of a file in the META-INF/services directory. The name of the class implementing this interface is ch5.imageio.ch5v1ReaderSpi, so that name will go inside that file. Using Listings 5.15.4, the contents of their JAR files would show the following:

META-INF/
META-INF/MANIFEST.MF
META-INF/services/
META-INF/services/javax.imageio.spi.ImageReaderSpi
META-INF/services/javax.imageio.spi.ImageWriterSpi
ch5/
ch5/imageio/
ch5/imageio/RWtypes.class
ch5/imageio/plugins/
ch5/imageio/plugins/ch5v1ImageReaderSpi.class
ch5/imageio/plugins/ch5v1ImageWriterSpi.class
ch5/imageio/displayImage.class

If you examine the contents of the file META-INF/services/javax.imageio.spi.ImageReaderSpi, you will see the text ch5.imageio.plugins.ch5ImageReaderSpi.

Tip

One way to format your JAR file is to create an appropriate services directory, and then use the following commands:

(for UNIX)

jar cf ch5.jar ch5
jar xf ch5.jar META-INF
mv services META-INF/services
rm ch5.jar
jar cfM ch5.jar ch5 META-INF

(for DOS)

jar cf ch5.jar ch5
jar xf ch5.jar META-INF
move services META-INFservices
del ch5.jar
jar cfM ch5.jar ch5 META-INF


The last step in getting the application to acknowledge these SPI classes is to make sure that this JAR file is located somewhere on the application classpath. If we run Listing 5.1 with this JAR file located on the application classpath, the new output is as follows:

For Reading:
        By format:
                png
                jpeg
                JPEG
                gif
                jpg
                JPG
                ch5
        By MIME Types:
                image/jpeg
                image/ch5
                image/png
                image/x-png
                image/gif
For Writing:
        By format:
                PNG
                png
                jpeg
                JPEG
                jpg
                JPG
                ch5
        By MIME Types:
                image/jpeg
                image/ch5
                image/png
                image/x-png

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset