Thu, 12 Oct 2017 19:44:07 +0800
merge
1 /*
2 * Copyright (c) 1997, 2010, 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 */
26 package com.sun.xml.internal.ws.policy.privateutil;
28 import java.io.BufferedReader;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.InputStreamReader;
32 import java.lang.reflect.Array;
33 import java.net.URL;
34 import java.util.ArrayList;
35 import java.util.Enumeration;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.NoSuchElementException;
39 import java.util.Set;
40 import java.util.TreeSet;
43 /**
44 *
45 * A simple service-provider lookup mechanism. A <i>service</i> is a
46 * well-known set of interfaces and (usually abstract) classes. A <i>service
47 * provider</i> is a specific implementation of a service. The classes in a
48 * provider typically implement the interfaces and subclass the classes defined
49 * in the service itself. Service providers may be installed in an
50 * implementation of the Java platform in the form of extensions, that is, jar
51 * files placed into any of the usual extension directories. Providers may
52 * also be made available by adding them to the applet or application class
53 * path or by some other platform-specific means.
54 * <p/>
55 * <p> In this lookup mechanism a service is represented by an interface or an
56 * abstract class. (A concrete class may be used, but this is not
57 * recommended.) A provider of a given service contains one or more concrete
58 * classes that extend this <i>service class</i> with data and code specific to
59 * the provider. This <i>provider class</i> will typically not be the entire
60 * provider itself but rather a proxy that contains enough information to
61 * decide whether the provider is able to satisfy a particular request together
62 * with code that can create the actual provider on demand. The details of
63 * provider classes tend to be highly service-specific; no single class or
64 * interface could possibly unify them, so no such class has been defined. The
65 * only requirement enforced here is that provider classes must have a
66 * zero-argument constructor so that they may be instantiated during lookup.
67 * <p/>
68 * <p> A service provider identifies itself by placing a provider-configuration
69 * file in the resource directory <tt>META-INF/services</tt>. The file's name
70 * should consist of the fully-qualified name of the abstract service class.
71 * The file should contain a list of fully-qualified concrete provider-class
72 * names, one per line. Space and tab characters surrounding each name, as
73 * well as blank lines, are ignored. The comment character is <tt>'#'</tt>
74 * (<tt>0x23</tt>); on each line all characters following the first comment
75 * character are ignored. The file must be encoded in UTF-8.
76 * <p/>
77 * <p> If a particular concrete provider class is named in more than one
78 * configuration file, or is named in the same configuration file more than
79 * once, then the duplicates will be ignored. The configuration file naming a
80 * particular provider need not be in the same jar file or other distribution
81 * unit as the provider itself. The provider must be accessible from the same
82 * class loader that was initially queried to locate the configuration file;
83 * note that this is not necessarily the class loader that found the file.
84 * <p/>
85 * <p> <b>Example:</b> Suppose we have a service class named
86 * <tt>java.io.spi.CharCodec</tt>. It has two abstract methods:
87 * <p/>
88 * <pre>
89 * public abstract CharEncoder getEncoder(String encodingName);
90 * public abstract CharDecoder getDecoder(String encodingName);
91 * </pre>
92 * <p/>
93 * Each method returns an appropriate object or <tt>null</tt> if it cannot
94 * translate the given encoding. Typical <tt>CharCodec</tt> providers will
95 * support more than one encoding.
96 * <p/>
97 * <p> If <tt>sun.io.StandardCodec</tt> is a provider of the <tt>CharCodec</tt>
98 * service then its jar file would contain the file
99 * <tt>META-INF/services/java.io.spi.CharCodec</tt>. This file would contain
100 * the single line:
101 * <p/>
102 * <pre>
103 * sun.io.StandardCodec # Standard codecs for the platform
104 * </pre>
105 * <p/>
106 * To locate an encoder for a given encoding name, the internal I/O code would
107 * do something like this:
108 * <p/>
109 * <pre>
110 * CharEncoder getEncoder(String encodingName) {
111 * for( CharCodec cc : ServiceFinder.find(CharCodec.class) ) {
112 * CharEncoder ce = cc.getEncoder(encodingName);
113 * if (ce != null)
114 * return ce;
115 * }
116 * return null;
117 * }
118 * </pre>
119 * <p/>
120 * The provider-lookup mechanism always executes in the security context of the
121 * caller. Trusted system code should typically invoke the methods in this
122 * class from within a privileged security context.
123 *
124 * @author Mark Reinhold
125 * @version 1.11, 03/12/19
126 * @since 1.3
127 */
128 final class ServiceFinder<T> implements Iterable<T> {
129 private static final PolicyLogger LOGGER = PolicyLogger.getLogger(ServiceFinder.class);
131 private static final String prefix = "META-INF/services/";
133 private final Class<T> serviceClass;
134 private final ClassLoader classLoader;
136 /**
137 * Locates and incrementally instantiates the available providers of a
138 * given service using the given class loader.
139 * <p/>
140 * <p> This method transforms the name of the given service class into a
141 * provider-configuration filename as described above and then uses the
142 * <tt>getResources</tt> method of the given class loader to find all
143 * available files with that name. These files are then read and parsed to
144 * produce a list of provider-class names. The iterator that is returned
145 * uses the given class loader to lookup and then instantiate each element
146 * of the list.
147 * <p/>
148 * <p> Because it is possible for extensions to be installed into a running
149 * Java virtual machine, this method may return different results each time
150 * it is invoked. <p>
151 *
152 * @param service The service's abstract service class
153 * @param loader The class loader to be used to load provider-configuration files
154 * and instantiate provider classes, or <tt>null</tt> if the system
155 * class loader (or, failing that the bootstrap class loader) is to
156 * be used
157 * @throws ServiceConfigurationError If a provider-configuration file violates the specified format
158 * or names a provider class that cannot be found and instantiated
159 * @see #find(Class)
160 */
161 static <T> ServiceFinder<T> find(final Class<T> service, final ClassLoader loader) {
162 if (null==service) {
163 throw LOGGER.logSevereException(new NullPointerException(LocalizationMessages.WSP_0032_SERVICE_CAN_NOT_BE_NULL()));
164 }
165 return new ServiceFinder<T>(service,loader);
166 }
168 /**
169 * Locates and incrementally instantiates the available providers of a
170 * given service using the context class loader. This convenience method
171 * is equivalent to
172 * <p/>
173 * <pre>
174 * ClassLoader cl = Thread.currentThread().getContextClassLoader();
175 * return Service.providers(service, cl);
176 * </pre>
177 *
178 * @param service The service's abstract service class
179 *
180 * @throws ServiceConfigurationError If a provider-configuration file violates the specified format
181 * or names a provider class that cannot be found and instantiated
182 * @see #find(Class, ClassLoader)
183 */
184 public static <T> ServiceFinder<T> find(final Class<T> service) {
185 return find(service,Thread.currentThread().getContextClassLoader());
186 }
188 private ServiceFinder(Class<T> service, ClassLoader loader) {
189 this.serviceClass = service;
190 this.classLoader = loader;
191 }
193 /**
194 * Returns discovered objects incrementally.
195 *
196 * @return An <tt>Iterator</tt> that yields provider objects for the given
197 * service, in some arbitrary order. The iterator will throw a
198 * <tt>ServiceConfigurationError</tt> if a provider-configuration
199 * file violates the specified format or if a provider class cannot
200 * be found and instantiated.
201 */
202 public Iterator<T> iterator() {
203 return new LazyIterator<T>(serviceClass,classLoader);
204 }
206 /**
207 * Returns discovered objects all at once.
208 *
209 * @return
210 * can be empty but never null.
211 *
212 * @throws ServiceConfigurationError
213 */
214 @SuppressWarnings({"unchecked"})
215 public T[] toArray() {
216 List<T> result = new ArrayList<T>();
217 for (T t : this) {
218 result.add(t);
219 }
220 return result.toArray((T[])Array.newInstance(serviceClass,result.size()));
221 }
223 private static void fail(final Class service, final String msg, final Throwable cause)
224 throws ServiceConfigurationError {
225 final ServiceConfigurationError sce
226 = new ServiceConfigurationError(LocalizationMessages.WSP_0025_SPI_FAIL_SERVICE_MSG(service.getName(), msg));
227 if (null != cause) {
228 sce.initCause(cause);
229 }
231 throw LOGGER.logSevereException(sce);
232 }
234 /* private static void fail(Class service, String msg)
235 throws ServiceConfigurationError {
236 throw new ServiceConfigurationError(LocalizationMessages.WSP_0025_SPI_FAIL_SERVICE_MSG(service.getName(), msg));
237 }*/
239 private static void fail(final Class service, final URL u, final int line, final String msg, final Throwable cause)
240 throws ServiceConfigurationError {
241 fail(service, LocalizationMessages.WSP_0024_SPI_FAIL_SERVICE_URL_LINE_MSG(u , line, msg), cause);
242 }
244 /**
245 * Parse a single line from the given configuration file, adding the name
246 * on the line to both the names list and the returned set iff the name is
247 * not already a member of the returned set.
248 */
249 private static int parseLine(final Class service, final URL u, final BufferedReader r, final int lc,
250 final List<String> names, final Set<String> returned)
251 throws IOException, ServiceConfigurationError {
252 String ln = r.readLine();
253 if (ln == null) {
254 return -1;
255 }
256 final int ci = ln.indexOf('#');
257 if (ci >= 0) ln = ln.substring(0, ci);
258 ln = ln.trim();
259 final int n = ln.length();
260 if (n != 0) {
261 if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
262 fail(service, u, lc, LocalizationMessages.WSP_0067_ILLEGAL_CFG_FILE_SYNTAX(), null);
263 int cp = ln.codePointAt(0);
264 if (!Character.isJavaIdentifierStart(cp))
265 fail(service, u, lc, LocalizationMessages.WSP_0066_ILLEGAL_PROVIDER_CLASSNAME(ln), null);
266 for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
267 cp = ln.codePointAt(i);
268 if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
269 fail(service, u, lc, LocalizationMessages.WSP_0066_ILLEGAL_PROVIDER_CLASSNAME(ln), null);
270 }
271 if (!returned.contains(ln)) {
272 names.add(ln);
273 returned.add(ln);
274 }
275 }
276 return lc + 1;
277 }
279 /**
280 * Parse the content of the given URL as a provider-configuration file.
281 *
282 * @param service The service class for which providers are being sought;
283 * used to construct error detail strings
284 * @param u The URL naming the configuration file to be parsed
285 * @param returned A Set containing the names of provider classes that have already
286 * been returned. This set will be updated to contain the names
287 * that will be yielded from the returned <tt>Iterator</tt>.
288 * @return A (possibly empty) <tt>Iterator</tt> that will yield the
289 * provider-class names in the given configuration file that are
290 * not yet members of the returned set
291 * @throws ServiceConfigurationError If an I/O error occurs while reading from the given URL, or
292 * if a configuration-file format error is detected
293 */
294 @SuppressWarnings({"StatementWithEmptyBody"})
295 private static Iterator<String> parse(Class service, URL u, Set<String> returned)
296 throws ServiceConfigurationError {
297 InputStream in = null;
298 BufferedReader r = null;
299 ArrayList<String> names = new ArrayList<String>();
300 try {
301 in = u.openStream();
302 r = new BufferedReader(new InputStreamReader(in, "utf-8"));
303 int lc = 1;
304 while ((lc = parseLine(service, u, r, lc, names, returned)) >= 0) ;
305 } catch (IOException x) {
306 fail(service, ": " + x, x);
307 } finally {
308 try {
309 if (r != null) r.close();
310 if (in != null) in.close();
311 } catch (IOException y) {
312 fail(service, ": " + y, y);
313 }
314 }
315 return names.iterator();
316 }
319 /**
320 * Private inner class implementing fully-lazy provider lookup
321 */
322 private static class LazyIterator<T> implements Iterator<T> {
323 Class<T> service;
324 ClassLoader loader;
325 Enumeration<URL> configs = null;
326 Iterator<String> pending = null;
327 Set<String> returned = new TreeSet<String>();
328 String nextName = null;
330 private LazyIterator(Class<T> service, ClassLoader loader) {
331 this.service = service;
332 this.loader = loader;
333 }
335 public boolean hasNext() throws ServiceConfigurationError {
336 if (nextName != null) {
337 return true;
338 }
339 if (configs == null) {
340 try {
341 final String fullName = prefix + service.getName();
342 if (loader == null)
343 configs = ClassLoader.getSystemResources(fullName);
344 else
345 configs = loader.getResources(fullName);
346 } catch (IOException x) {
347 fail(service, ": " + x, x);
348 }
349 }
350 while ((pending == null) || !pending.hasNext()) {
351 if (!configs.hasMoreElements()) {
352 return false;
353 }
354 pending = parse(service, configs.nextElement(), returned);
355 }
356 nextName = pending.next();
357 return true;
358 }
360 public T next() throws ServiceConfigurationError {
361 if (!hasNext()) {
362 throw new NoSuchElementException();
363 }
364 final String cn = nextName;
365 nextName = null;
366 try {
367 return service.cast(Class.forName(cn, true, loader).newInstance());
368 } catch (ClassNotFoundException x) {
369 fail(service, LocalizationMessages.WSP_0027_SERVICE_PROVIDER_NOT_FOUND(cn), x);
370 } catch (Exception x) {
371 fail(service, LocalizationMessages.WSP_0028_SERVICE_PROVIDER_COULD_NOT_BE_INSTANTIATED(cn), x);
372 }
373 return null; /* This cannot happen */
374 }
376 public void remove() {
377 throw new UnsupportedOperationException();
378 }
379 }
380 }