1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/jaxws_classes/com/sun/xml/internal/ws/policy/privateutil/ServiceFinder.java Wed Apr 27 01:27:09 2016 +0800 1.3 @@ -0,0 +1,380 @@ 1.4 +/* 1.5 + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +package com.sun.xml.internal.ws.policy.privateutil; 1.30 + 1.31 +import java.io.BufferedReader; 1.32 +import java.io.IOException; 1.33 +import java.io.InputStream; 1.34 +import java.io.InputStreamReader; 1.35 +import java.lang.reflect.Array; 1.36 +import java.net.URL; 1.37 +import java.util.ArrayList; 1.38 +import java.util.Enumeration; 1.39 +import java.util.Iterator; 1.40 +import java.util.List; 1.41 +import java.util.NoSuchElementException; 1.42 +import java.util.Set; 1.43 +import java.util.TreeSet; 1.44 + 1.45 + 1.46 +/** 1.47 + * 1.48 + * A simple service-provider lookup mechanism. A <i>service</i> is a 1.49 + * well-known set of interfaces and (usually abstract) classes. A <i>service 1.50 + * provider</i> is a specific implementation of a service. The classes in a 1.51 + * provider typically implement the interfaces and subclass the classes defined 1.52 + * in the service itself. Service providers may be installed in an 1.53 + * implementation of the Java platform in the form of extensions, that is, jar 1.54 + * files placed into any of the usual extension directories. Providers may 1.55 + * also be made available by adding them to the applet or application class 1.56 + * path or by some other platform-specific means. 1.57 + * <p/> 1.58 + * <p> In this lookup mechanism a service is represented by an interface or an 1.59 + * abstract class. (A concrete class may be used, but this is not 1.60 + * recommended.) A provider of a given service contains one or more concrete 1.61 + * classes that extend this <i>service class</i> with data and code specific to 1.62 + * the provider. This <i>provider class</i> will typically not be the entire 1.63 + * provider itself but rather a proxy that contains enough information to 1.64 + * decide whether the provider is able to satisfy a particular request together 1.65 + * with code that can create the actual provider on demand. The details of 1.66 + * provider classes tend to be highly service-specific; no single class or 1.67 + * interface could possibly unify them, so no such class has been defined. The 1.68 + * only requirement enforced here is that provider classes must have a 1.69 + * zero-argument constructor so that they may be instantiated during lookup. 1.70 + * <p/> 1.71 + * <p> A service provider identifies itself by placing a provider-configuration 1.72 + * file in the resource directory <tt>META-INF/services</tt>. The file's name 1.73 + * should consist of the fully-qualified name of the abstract service class. 1.74 + * The file should contain a list of fully-qualified concrete provider-class 1.75 + * names, one per line. Space and tab characters surrounding each name, as 1.76 + * well as blank lines, are ignored. The comment character is <tt>'#'</tt> 1.77 + * (<tt>0x23</tt>); on each line all characters following the first comment 1.78 + * character are ignored. The file must be encoded in UTF-8. 1.79 + * <p/> 1.80 + * <p> If a particular concrete provider class is named in more than one 1.81 + * configuration file, or is named in the same configuration file more than 1.82 + * once, then the duplicates will be ignored. The configuration file naming a 1.83 + * particular provider need not be in the same jar file or other distribution 1.84 + * unit as the provider itself. The provider must be accessible from the same 1.85 + * class loader that was initially queried to locate the configuration file; 1.86 + * note that this is not necessarily the class loader that found the file. 1.87 + * <p/> 1.88 + * <p> <b>Example:</b> Suppose we have a service class named 1.89 + * <tt>java.io.spi.CharCodec</tt>. It has two abstract methods: 1.90 + * <p/> 1.91 + * <pre> 1.92 + * public abstract CharEncoder getEncoder(String encodingName); 1.93 + * public abstract CharDecoder getDecoder(String encodingName); 1.94 + * </pre> 1.95 + * <p/> 1.96 + * Each method returns an appropriate object or <tt>null</tt> if it cannot 1.97 + * translate the given encoding. Typical <tt>CharCodec</tt> providers will 1.98 + * support more than one encoding. 1.99 + * <p/> 1.100 + * <p> If <tt>sun.io.StandardCodec</tt> is a provider of the <tt>CharCodec</tt> 1.101 + * service then its jar file would contain the file 1.102 + * <tt>META-INF/services/java.io.spi.CharCodec</tt>. This file would contain 1.103 + * the single line: 1.104 + * <p/> 1.105 + * <pre> 1.106 + * sun.io.StandardCodec # Standard codecs for the platform 1.107 + * </pre> 1.108 + * <p/> 1.109 + * To locate an encoder for a given encoding name, the internal I/O code would 1.110 + * do something like this: 1.111 + * <p/> 1.112 + * <pre> 1.113 + * CharEncoder getEncoder(String encodingName) { 1.114 + * for( CharCodec cc : ServiceFinder.find(CharCodec.class) ) { 1.115 + * CharEncoder ce = cc.getEncoder(encodingName); 1.116 + * if (ce != null) 1.117 + * return ce; 1.118 + * } 1.119 + * return null; 1.120 + * } 1.121 + * </pre> 1.122 + * <p/> 1.123 + * The provider-lookup mechanism always executes in the security context of the 1.124 + * caller. Trusted system code should typically invoke the methods in this 1.125 + * class from within a privileged security context. 1.126 + * 1.127 + * @author Mark Reinhold 1.128 + * @version 1.11, 03/12/19 1.129 + * @since 1.3 1.130 + */ 1.131 +final class ServiceFinder<T> implements Iterable<T> { 1.132 + private static final PolicyLogger LOGGER = PolicyLogger.getLogger(ServiceFinder.class); 1.133 + 1.134 + private static final String prefix = "META-INF/services/"; 1.135 + 1.136 + private final Class<T> serviceClass; 1.137 + private final ClassLoader classLoader; 1.138 + 1.139 + /** 1.140 + * Locates and incrementally instantiates the available providers of a 1.141 + * given service using the given class loader. 1.142 + * <p/> 1.143 + * <p> This method transforms the name of the given service class into a 1.144 + * provider-configuration filename as described above and then uses the 1.145 + * <tt>getResources</tt> method of the given class loader to find all 1.146 + * available files with that name. These files are then read and parsed to 1.147 + * produce a list of provider-class names. The iterator that is returned 1.148 + * uses the given class loader to lookup and then instantiate each element 1.149 + * of the list. 1.150 + * <p/> 1.151 + * <p> Because it is possible for extensions to be installed into a running 1.152 + * Java virtual machine, this method may return different results each time 1.153 + * it is invoked. <p> 1.154 + * 1.155 + * @param service The service's abstract service class 1.156 + * @param loader The class loader to be used to load provider-configuration files 1.157 + * and instantiate provider classes, or <tt>null</tt> if the system 1.158 + * class loader (or, failing that the bootstrap class loader) is to 1.159 + * be used 1.160 + * @throws ServiceConfigurationError If a provider-configuration file violates the specified format 1.161 + * or names a provider class that cannot be found and instantiated 1.162 + * @see #find(Class) 1.163 + */ 1.164 + static <T> ServiceFinder<T> find(final Class<T> service, final ClassLoader loader) { 1.165 + if (null==service) { 1.166 + throw LOGGER.logSevereException(new NullPointerException(LocalizationMessages.WSP_0032_SERVICE_CAN_NOT_BE_NULL())); 1.167 + } 1.168 + return new ServiceFinder<T>(service,loader); 1.169 + } 1.170 + 1.171 + /** 1.172 + * Locates and incrementally instantiates the available providers of a 1.173 + * given service using the context class loader. This convenience method 1.174 + * is equivalent to 1.175 + * <p/> 1.176 + * <pre> 1.177 + * ClassLoader cl = Thread.currentThread().getContextClassLoader(); 1.178 + * return Service.providers(service, cl); 1.179 + * </pre> 1.180 + * 1.181 + * @param service The service's abstract service class 1.182 + * 1.183 + * @throws ServiceConfigurationError If a provider-configuration file violates the specified format 1.184 + * or names a provider class that cannot be found and instantiated 1.185 + * @see #find(Class, ClassLoader) 1.186 + */ 1.187 + public static <T> ServiceFinder<T> find(final Class<T> service) { 1.188 + return find(service,Thread.currentThread().getContextClassLoader()); 1.189 + } 1.190 + 1.191 + private ServiceFinder(Class<T> service, ClassLoader loader) { 1.192 + this.serviceClass = service; 1.193 + this.classLoader = loader; 1.194 + } 1.195 + 1.196 + /** 1.197 + * Returns discovered objects incrementally. 1.198 + * 1.199 + * @return An <tt>Iterator</tt> that yields provider objects for the given 1.200 + * service, in some arbitrary order. The iterator will throw a 1.201 + * <tt>ServiceConfigurationError</tt> if a provider-configuration 1.202 + * file violates the specified format or if a provider class cannot 1.203 + * be found and instantiated. 1.204 + */ 1.205 + public Iterator<T> iterator() { 1.206 + return new LazyIterator<T>(serviceClass,classLoader); 1.207 + } 1.208 + 1.209 + /** 1.210 + * Returns discovered objects all at once. 1.211 + * 1.212 + * @return 1.213 + * can be empty but never null. 1.214 + * 1.215 + * @throws ServiceConfigurationError 1.216 + */ 1.217 + @SuppressWarnings({"unchecked"}) 1.218 + public T[] toArray() { 1.219 + List<T> result = new ArrayList<T>(); 1.220 + for (T t : this) { 1.221 + result.add(t); 1.222 + } 1.223 + return result.toArray((T[])Array.newInstance(serviceClass,result.size())); 1.224 + } 1.225 + 1.226 + private static void fail(final Class service, final String msg, final Throwable cause) 1.227 + throws ServiceConfigurationError { 1.228 + final ServiceConfigurationError sce 1.229 + = new ServiceConfigurationError(LocalizationMessages.WSP_0025_SPI_FAIL_SERVICE_MSG(service.getName(), msg)); 1.230 + if (null != cause) { 1.231 + sce.initCause(cause); 1.232 + } 1.233 + 1.234 + throw LOGGER.logSevereException(sce); 1.235 + } 1.236 + 1.237 +/* private static void fail(Class service, String msg) 1.238 + throws ServiceConfigurationError { 1.239 + throw new ServiceConfigurationError(LocalizationMessages.WSP_0025_SPI_FAIL_SERVICE_MSG(service.getName(), msg)); 1.240 + }*/ 1.241 + 1.242 + private static void fail(final Class service, final URL u, final int line, final String msg, final Throwable cause) 1.243 + throws ServiceConfigurationError { 1.244 + fail(service, LocalizationMessages.WSP_0024_SPI_FAIL_SERVICE_URL_LINE_MSG(u , line, msg), cause); 1.245 + } 1.246 + 1.247 + /** 1.248 + * Parse a single line from the given configuration file, adding the name 1.249 + * on the line to both the names list and the returned set iff the name is 1.250 + * not already a member of the returned set. 1.251 + */ 1.252 + private static int parseLine(final Class service, final URL u, final BufferedReader r, final int lc, 1.253 + final List<String> names, final Set<String> returned) 1.254 + throws IOException, ServiceConfigurationError { 1.255 + String ln = r.readLine(); 1.256 + if (ln == null) { 1.257 + return -1; 1.258 + } 1.259 + final int ci = ln.indexOf('#'); 1.260 + if (ci >= 0) ln = ln.substring(0, ci); 1.261 + ln = ln.trim(); 1.262 + final int n = ln.length(); 1.263 + if (n != 0) { 1.264 + if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0)) 1.265 + fail(service, u, lc, LocalizationMessages.WSP_0067_ILLEGAL_CFG_FILE_SYNTAX(), null); 1.266 + int cp = ln.codePointAt(0); 1.267 + if (!Character.isJavaIdentifierStart(cp)) 1.268 + fail(service, u, lc, LocalizationMessages.WSP_0066_ILLEGAL_PROVIDER_CLASSNAME(ln), null); 1.269 + for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) { 1.270 + cp = ln.codePointAt(i); 1.271 + if (!Character.isJavaIdentifierPart(cp) && (cp != '.')) 1.272 + fail(service, u, lc, LocalizationMessages.WSP_0066_ILLEGAL_PROVIDER_CLASSNAME(ln), null); 1.273 + } 1.274 + if (!returned.contains(ln)) { 1.275 + names.add(ln); 1.276 + returned.add(ln); 1.277 + } 1.278 + } 1.279 + return lc + 1; 1.280 + } 1.281 + 1.282 + /** 1.283 + * Parse the content of the given URL as a provider-configuration file. 1.284 + * 1.285 + * @param service The service class for which providers are being sought; 1.286 + * used to construct error detail strings 1.287 + * @param u The URL naming the configuration file to be parsed 1.288 + * @param returned A Set containing the names of provider classes that have already 1.289 + * been returned. This set will be updated to contain the names 1.290 + * that will be yielded from the returned <tt>Iterator</tt>. 1.291 + * @return A (possibly empty) <tt>Iterator</tt> that will yield the 1.292 + * provider-class names in the given configuration file that are 1.293 + * not yet members of the returned set 1.294 + * @throws ServiceConfigurationError If an I/O error occurs while reading from the given URL, or 1.295 + * if a configuration-file format error is detected 1.296 + */ 1.297 + @SuppressWarnings({"StatementWithEmptyBody"}) 1.298 + private static Iterator<String> parse(Class service, URL u, Set<String> returned) 1.299 + throws ServiceConfigurationError { 1.300 + InputStream in = null; 1.301 + BufferedReader r = null; 1.302 + ArrayList<String> names = new ArrayList<String>(); 1.303 + try { 1.304 + in = u.openStream(); 1.305 + r = new BufferedReader(new InputStreamReader(in, "utf-8")); 1.306 + int lc = 1; 1.307 + while ((lc = parseLine(service, u, r, lc, names, returned)) >= 0) ; 1.308 + } catch (IOException x) { 1.309 + fail(service, ": " + x, x); 1.310 + } finally { 1.311 + try { 1.312 + if (r != null) r.close(); 1.313 + if (in != null) in.close(); 1.314 + } catch (IOException y) { 1.315 + fail(service, ": " + y, y); 1.316 + } 1.317 + } 1.318 + return names.iterator(); 1.319 + } 1.320 + 1.321 + 1.322 + /** 1.323 + * Private inner class implementing fully-lazy provider lookup 1.324 + */ 1.325 + private static class LazyIterator<T> implements Iterator<T> { 1.326 + Class<T> service; 1.327 + ClassLoader loader; 1.328 + Enumeration<URL> configs = null; 1.329 + Iterator<String> pending = null; 1.330 + Set<String> returned = new TreeSet<String>(); 1.331 + String nextName = null; 1.332 + 1.333 + private LazyIterator(Class<T> service, ClassLoader loader) { 1.334 + this.service = service; 1.335 + this.loader = loader; 1.336 + } 1.337 + 1.338 + public boolean hasNext() throws ServiceConfigurationError { 1.339 + if (nextName != null) { 1.340 + return true; 1.341 + } 1.342 + if (configs == null) { 1.343 + try { 1.344 + final String fullName = prefix + service.getName(); 1.345 + if (loader == null) 1.346 + configs = ClassLoader.getSystemResources(fullName); 1.347 + else 1.348 + configs = loader.getResources(fullName); 1.349 + } catch (IOException x) { 1.350 + fail(service, ": " + x, x); 1.351 + } 1.352 + } 1.353 + while ((pending == null) || !pending.hasNext()) { 1.354 + if (!configs.hasMoreElements()) { 1.355 + return false; 1.356 + } 1.357 + pending = parse(service, configs.nextElement(), returned); 1.358 + } 1.359 + nextName = pending.next(); 1.360 + return true; 1.361 + } 1.362 + 1.363 + public T next() throws ServiceConfigurationError { 1.364 + if (!hasNext()) { 1.365 + throw new NoSuchElementException(); 1.366 + } 1.367 + final String cn = nextName; 1.368 + nextName = null; 1.369 + try { 1.370 + return service.cast(Class.forName(cn, true, loader).newInstance()); 1.371 + } catch (ClassNotFoundException x) { 1.372 + fail(service, LocalizationMessages.WSP_0027_SERVICE_PROVIDER_NOT_FOUND(cn), x); 1.373 + } catch (Exception x) { 1.374 + fail(service, LocalizationMessages.WSP_0028_SERVICE_PROVIDER_COULD_NOT_BE_INSTANTIATED(cn), x); 1.375 + } 1.376 + return null; /* This cannot happen */ 1.377 + } 1.378 + 1.379 + public void remove() { 1.380 + throw new UnsupportedOperationException(); 1.381 + } 1.382 + } 1.383 +}