1/*
2 * Copyright (c) 1999, 2017, 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
26package javax.imageio.spi;
27
28import java.security.PrivilegedAction;
29import java.security.AccessController;
30import java.util.HashMap;
31import java.util.Iterator;
32import java.util.Map;
33import java.util.NoSuchElementException;
34import java.util.Set;
35import java.util.Vector;
36import com.sun.imageio.spi.FileImageInputStreamSpi;
37import com.sun.imageio.spi.FileImageOutputStreamSpi;
38import com.sun.imageio.spi.InputStreamImageInputStreamSpi;
39import com.sun.imageio.spi.OutputStreamImageOutputStreamSpi;
40import com.sun.imageio.spi.RAFImageInputStreamSpi;
41import com.sun.imageio.spi.RAFImageOutputStreamSpi;
42import com.sun.imageio.plugins.gif.GIFImageReaderSpi;
43import com.sun.imageio.plugins.gif.GIFImageWriterSpi;
44import com.sun.imageio.plugins.jpeg.JPEGImageReaderSpi;
45import com.sun.imageio.plugins.jpeg.JPEGImageWriterSpi;
46import com.sun.imageio.plugins.png.PNGImageReaderSpi;
47import com.sun.imageio.plugins.png.PNGImageWriterSpi;
48import com.sun.imageio.plugins.bmp.BMPImageReaderSpi;
49import com.sun.imageio.plugins.bmp.BMPImageWriterSpi;
50import com.sun.imageio.plugins.wbmp.WBMPImageReaderSpi;
51import com.sun.imageio.plugins.wbmp.WBMPImageWriterSpi;
52import com.sun.imageio.plugins.tiff.TIFFImageReaderSpi;
53import com.sun.imageio.plugins.tiff.TIFFImageWriterSpi;
54import sun.awt.AppContext;
55import java.util.ServiceLoader;
56import java.util.ServiceConfigurationError;
57
58/**
59 * A registry for service provider instances.  Service provider
60 * classes may be detected at run time by means of meta-information in
61 * the JAR files containing them.  The intent is that it be relatively
62 * inexpensive to load and inspect all available service provider
63 * classes.  These classes may them be used to locate and instantiate
64 * more heavyweight classes that will perform actual work, in this
65 * case instances of {@code ImageReader},
66 * {@code ImageWriter}, {@code ImageTranscoder},
67 * {@code ImageInputStream}, and {@code ImageOutputStream}.
68 *
69 * Service providers found from the Java platform are automatically
70 * loaded as soon as this class is instantiated.
71 *
72 * <p> When the {@code registerApplicationClasspathSpis} 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}
76 * subdirectory is placed within the {@code META-INF} 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} which
82 * implements the {@code ImageReaderSpi} 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} 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
108 * <a href="{@docRoot}/../specs/jar/jar.html">JAR File Specification</a>.
109 */
110public final class IIORegistry extends ServiceRegistry {
111
112    /**
113     * A {@code Vector} containing the valid IIO registry
114     * categories (superinterfaces) to be used in the constructor.
115     */
116    private static final Vector<Class<?>> initialCategories = new Vector<>(5);
117
118    static {
119        initialCategories.add(ImageReaderSpi.class);
120        initialCategories.add(ImageWriterSpi.class);
121        initialCategories.add(ImageTranscoderSpi.class);
122        initialCategories.add(ImageInputStreamSpi.class);
123        initialCategories.add(ImageOutputStreamSpi.class);
124    }
125
126    /**
127     * Set up the valid service provider categories and automatically
128     * register all available service providers.
129     *
130     * <p> The constructor is private in order to prevent creation of
131     * additional instances.
132     */
133    private IIORegistry() {
134        super(initialCategories.iterator());
135        registerStandardSpis();
136        registerApplicationClasspathSpis();
137    }
138
139    /**
140     * Returns the default {@code IIORegistry} instance used by
141     * the Image I/O API.  This instance should be used for all
142     * registry functions.
143     *
144     * <p> Each {@code ThreadGroup} will receive its own
145     * instance; this allows different {@code Applet}s in the
146     * same browser (for example) to each have their own registry.
147     *
148     * @return the default registry for the current
149     * {@code ThreadGroup}.
150     */
151    public static IIORegistry getDefaultInstance() {
152        AppContext context = AppContext.getAppContext();
153        IIORegistry registry =
154            (IIORegistry)context.get(IIORegistry.class);
155        if (registry == null) {
156            // Create an instance for this AppContext
157            registry = new IIORegistry();
158            context.put(IIORegistry.class, registry);
159        }
160        return registry;
161    }
162
163    private void registerStandardSpis() {
164        // Hardwire standard SPIs
165        registerServiceProvider(new GIFImageReaderSpi());
166        registerServiceProvider(new GIFImageWriterSpi());
167        registerServiceProvider(new BMPImageReaderSpi());
168        registerServiceProvider(new BMPImageWriterSpi());
169        registerServiceProvider(new WBMPImageReaderSpi());
170        registerServiceProvider(new WBMPImageWriterSpi());
171        registerServiceProvider(new TIFFImageReaderSpi());
172        registerServiceProvider(new TIFFImageWriterSpi());
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}.  This method is typically invoked by
191     * the {@code ImageIO.scanForPlugins} 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
231          in the privileged mode in order to
232          be able read corresponding jar files even if
233          file read capability is restricted (like the
234          applet context case).
235         */
236        PrivilegedAction<Object> doRegistration =
237            new PrivilegedAction<Object>() {
238                public Object run() {
239                    Iterator<Class<?>> categories = getCategories();
240                    while (categories.hasNext()) {
241                        @SuppressWarnings("unchecked")
242                        Class<IIOServiceProvider> c = (Class<IIOServiceProvider>)categories.next();
243                        for (IIOServiceProvider p : ServiceLoader.loadInstalled(c)) {
244                            registerServiceProvider(p);
245                        }
246                    }
247                    return this;
248                }
249            };
250
251        AccessController.doPrivileged(doRegistration);
252    }
253}
254