1 /*
   2  * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.imageio.spi;
  27 
  28 import java.security.PrivilegedAction;
  29 import java.security.AccessController;
  30 import java.util.HashMap;
  31 import java.util.Iterator;
  32 import java.util.Map;
  33 import java.util.NoSuchElementException;
  34 import java.util.Set;
  35 import java.util.Vector;
  36 import com.sun.imageio.spi.FileImageInputStreamSpi;
  37 import com.sun.imageio.spi.FileImageOutputStreamSpi;
  38 import com.sun.imageio.spi.InputStreamImageInputStreamSpi;
  39 import com.sun.imageio.spi.OutputStreamImageOutputStreamSpi;
  40 import com.sun.imageio.spi.RAFImageInputStreamSpi;
  41 import com.sun.imageio.spi.RAFImageOutputStreamSpi;
  42 import com.sun.imageio.plugins.gif.GIFImageReaderSpi;
  43 import com.sun.imageio.plugins.gif.GIFImageWriterSpi;
  44 import com.sun.imageio.plugins.jpeg.JPEGImageReaderSpi;
  45 import com.sun.imageio.plugins.jpeg.JPEGImageWriterSpi;
  46 import com.sun.imageio.plugins.png.PNGImageReaderSpi;
  47 import com.sun.imageio.plugins.png.PNGImageWriterSpi;
  48 import com.sun.imageio.plugins.bmp.BMPImageReaderSpi;
  49 import com.sun.imageio.plugins.bmp.BMPImageWriterSpi;
  50 import com.sun.imageio.plugins.wbmp.WBMPImageReaderSpi;
  51 import com.sun.imageio.plugins.wbmp.WBMPImageWriterSpi;
  52 import sun.awt.AppContext;
  53 import java.util.ServiceLoader;
  54 import java.util.ServiceConfigurationError;
  55 
  56 /**
  57  * A registry for service provider instances.  Service provider
  58  * classes may be detected at run time by means of meta-information in
  59  * the JAR files containing them.  The intent is that it be relatively
  60  * inexpensive to load and inspect all available service provider
  61  * classes.  These classes may them be used to locate and instantiate
  62  * more heavyweight classes that will perform actual work, in this
  63  * case instances of <code>ImageReader</code>,
  64  * <code>ImageWriter</code>, <code>ImageTranscoder</code>,
  65  * <code>ImageInputStream</code>, and <code>ImageOutputStream</code>.
  66  *
  67  * <p> Service providers found on the system classpath (typically
  68  * the <code>lib/ext</code> directory in the Java
  69  * installation directory) are automatically loaded as soon as this class is
  70  * instantiated.
  71  *
  72  * <p> When the <code>registerApplicationClasspathSpis</code> method
  73  * is called, service provider instances declared in the
  74  * meta-information section of JAR files on the application class path
  75  * are loaded.  To declare a service provider, a <code>services</code>
  76  * subdirectory is placed within the <code>META-INF</code> directory
  77  * that is present in every JAR file.  This directory contains a file
  78  * for each service provider interface that has one or more
  79  * implementation classes present in the JAR file.  For example, if
  80  * the JAR file contained a class named
  81  * <code>com.mycompany.imageio.MyFormatReaderSpi</code> which
  82  * implements the <code>ImageReaderSpi</code> interface, the JAR file
  83  * would contain a file named:
  84  *
  85  * <pre>
  86  * META-INF/services/javax.imageio.spi.ImageReaderSpi
  87  * </pre>
  88  *
  89  * containing the line:
  90  *
  91  * <pre>
  92  * com.mycompany.imageio.MyFormatReaderSpi
  93  * </pre>
  94  *
  95  * <p> The service provider classes are intended to be lightweight
  96  * and quick to load.  Implementations of these interfaces
  97  * should avoid complex dependencies on other classes and on
  98  * native code.
  99  *
 100  * <p> It is also possible to manually add service providers not found
 101  * automatically, as well as to remove those that are using the
 102  * interfaces of the <code>ServiceRegistry</code> class.  Thus
 103  * the application may customize the contents of the registry as it
 104  * sees fit.
 105  *
 106  * <p> For more details on declaring service providers, and the JAR
 107  * format in general, see the <a
 108  * href="{@docRoot}/../technotes/guides/jar/jar.html">
 109  * JAR File Specification</a>.
 110  *
 111  */
 112 public final class IIORegistry extends ServiceRegistry {
 113 
 114     /**
 115      * A <code>Vector</code> containing the valid IIO registry
 116      * categories (superinterfaces) to be used in the constructor.
 117      */
 118     private static final Vector<Class<?>> initialCategories = new Vector<>(5);
 119 
 120     static {
 121         initialCategories.add(ImageReaderSpi.class);
 122         initialCategories.add(ImageWriterSpi.class);
 123         initialCategories.add(ImageTranscoderSpi.class);
 124         initialCategories.add(ImageInputStreamSpi.class);
 125         initialCategories.add(ImageOutputStreamSpi.class);
 126     }
 127 
 128     /**
 129      * Set up the valid service provider categories and automatically
 130      * register all available service providers.
 131      *
 132      * <p> The constructor is private in order to prevent creation of
 133      * additional instances.
 134      */
 135     private IIORegistry() {
 136         super(initialCategories.iterator());
 137         registerStandardSpis();
 138         registerApplicationClasspathSpis();
 139     }
 140 
 141     /**
 142      * Returns the default <code>IIORegistry</code> instance used by
 143      * the Image I/O API.  This instance should be used for all
 144      * registry functions.
 145      *
 146      * <p> Each <code>ThreadGroup</code> will receive its own
 147      * instance; this allows different <code>Applet</code>s in the
 148      * same browser (for example) to each have their own registry.
 149      *
 150      * @return the default registry for the current
 151      * <code>ThreadGroup</code>.
 152      */
 153     public static IIORegistry getDefaultInstance() {
 154         AppContext context = AppContext.getAppContext();
 155         IIORegistry registry =
 156             (IIORegistry)context.get(IIORegistry.class);
 157         if (registry == null) {
 158             // Create an instance for this AppContext
 159             registry = new IIORegistry();
 160             context.put(IIORegistry.class, registry);
 161         }
 162         return registry;
 163     }
 164 
 165     private void registerStandardSpis() {
 166         // Hardwire standard SPIs
 167         registerServiceProvider(new GIFImageReaderSpi());
 168         registerServiceProvider(new GIFImageWriterSpi());
 169         registerServiceProvider(new BMPImageReaderSpi());
 170         registerServiceProvider(new BMPImageWriterSpi());
 171         registerServiceProvider(new WBMPImageReaderSpi());
 172         registerServiceProvider(new WBMPImageWriterSpi());
 173         registerServiceProvider(new PNGImageReaderSpi());
 174         registerServiceProvider(new PNGImageWriterSpi());
 175         registerServiceProvider(new JPEGImageReaderSpi());
 176         registerServiceProvider(new JPEGImageWriterSpi());
 177         registerServiceProvider(new FileImageInputStreamSpi());
 178         registerServiceProvider(new FileImageOutputStreamSpi());
 179         registerServiceProvider(new InputStreamImageInputStreamSpi());
 180         registerServiceProvider(new OutputStreamImageOutputStreamSpi());
 181         registerServiceProvider(new RAFImageInputStreamSpi());
 182         registerServiceProvider(new RAFImageOutputStreamSpi());
 183 
 184         registerInstalledProviders();
 185     }
 186 
 187     /**
 188      * Registers all available service providers found on the
 189      * application class path, using the default
 190      * <code>ClassLoader</code>.  This method is typically invoked by
 191      * the <code>ImageIO.scanForPlugins</code> method.
 192      *
 193      * @see javax.imageio.ImageIO#scanForPlugins
 194      * @see ClassLoader#getResources
 195      */
 196     public void registerApplicationClasspathSpis() {
 197         // FIX: load only from application classpath
 198 
 199         ClassLoader loader = Thread.currentThread().getContextClassLoader();
 200 
 201         Iterator<Class<?>> categories = getCategories();
 202         while (categories.hasNext()) {
 203             @SuppressWarnings("unchecked")
 204             Class<IIOServiceProvider> c = (Class<IIOServiceProvider>)categories.next();
 205             Iterator<IIOServiceProvider> riter =
 206                     ServiceLoader.load(c, loader).iterator();
 207             while (riter.hasNext()) {
 208                 try {
 209                     // Note that the next() call is required to be inside
 210                     // the try/catch block; see 6342404.
 211                     IIOServiceProvider r = riter.next();
 212                     registerServiceProvider(r);
 213                 } catch (ServiceConfigurationError err) {
 214                     if (System.getSecurityManager() != null) {
 215                         // In the applet case, we will catch the  error so
 216                         // registration of other plugins can  proceed
 217                         err.printStackTrace();
 218                     } else {
 219                         // In the application case, we will  throw the
 220                         // error to indicate app/system  misconfiguration
 221                         throw err;
 222                     }
 223                 }
 224             }
 225         }
 226     }
 227 
 228     private void registerInstalledProviders() {
 229         /*
 230           We need to load installed providers from the
 231           system classpath (typically the <code>lib/ext</code>
 232           directory in in the Java installation directory)
 233           in the privileged mode in order to
 234           be able read corresponding jar files even if
 235           file read capability is restricted (like the
 236           applet context case).
 237          */
 238         PrivilegedAction<Object> doRegistration =
 239             new PrivilegedAction<Object>() {
 240                 public Object run() {
 241                     Iterator<Class<?>> categories = getCategories();
 242                     while (categories.hasNext()) {
 243                         @SuppressWarnings("unchecked")
 244                         Class<IIOServiceProvider> c = (Class<IIOServiceProvider>)categories.next();
 245                         for (IIOServiceProvider p : ServiceLoader.loadInstalled(c)) {
 246                             registerServiceProvider(p);
 247                         }
 248                     }
 249                     return this;
 250                 }
 251             };
 252 
 253         AccessController.doPrivileged(doRegistration);
 254     }
 255 }