| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.openjpa.lib.util; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.security.AccessController; |
| import java.security.PrivilegedActionException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Enumeration; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| import java.util.TreeSet; |
| |
| /** |
| * Utility classes to locate services, as defined in the <a |
| * href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html |
| * #Service%20Provider">Jar File Specification</a>. Most of the methods in this |
| * class can also be found in the <em>sun.misc.Service</em> class, but since |
| * it is undocumented, we cannot rely on its API. |
| * Service location for a specified interface is done by searching for the |
| * resource <em>/META-INF/services/</em><i>service.class.name</i>, and |
| * loading the resource. |
| * Methods in this class that do not declare exceptions will never throw |
| * Runtime exceptions: exceptions are silently swallowed and empty array values |
| * are returned. |
| * |
| * @author Marc Prud'hommeaux |
| */ |
| public class Services { |
| |
| private static final String PREFIX = "META-INF/services/"; |
| |
| /** |
| * Return an array of Strings of class names of all known service |
| * implementors of the specified interface or class. |
| */ |
| public static String[] getImplementors(Class serviceClass) { |
| return getImplementors(serviceClass, null); |
| } |
| |
| /** |
| * Return an array of Strings of class names of all known service |
| * implementors of the specified interface or class. |
| */ |
| public static String[] getImplementors(Class serviceClass, |
| ClassLoader loader) { |
| return getImplementors(serviceClass.getName(), loader); |
| } |
| |
| /** |
| * Return an array of Strings of class names of all known service |
| * implementors of the specified class name(as resolved by the current |
| * thread's context class loader). |
| */ |
| public static String[] getImplementors(String serviceName) { |
| return getImplementors(serviceName, null); |
| } |
| |
| /** |
| * Return an array of Strings of class names of all known service |
| * implementors of the specified class name, as resolved by the specified |
| * {@link ClassLoader}. |
| */ |
| public static String[] getImplementors(String serviceName, |
| ClassLoader loader) { |
| if (loader == null) |
| loader = AccessController.doPrivileged( |
| J2DoPrivHelper.getContextClassLoaderAction()); |
| |
| try { |
| Set resourceList = new TreeSet(); |
| Enumeration resources = AccessController.doPrivileged( |
| J2DoPrivHelper.getResourcesAction(loader, |
| PREFIX + serviceName)); |
| while (resources.hasMoreElements()) |
| addResources((URL) resources.nextElement(), resourceList); |
| |
| return (String[]) resourceList.toArray(new String[resourceList |
| .size()]); |
| } catch (PrivilegedActionException | IOException pae) { |
| // silently swallow all exceptions. |
| } |
| return new String[0]; |
| } |
| |
| /** |
| * Parse the URL resource and add the listed class names to the specified |
| * Set. Class names are separated by lines. Lines starting with '#' are |
| * ignored. |
| */ |
| private static void addResources(URL url, Set set) throws IOException { |
| InputStream in = null; |
| BufferedReader reader = null; |
| URLConnection urlCon = null; |
| |
| try { |
| urlCon = url.openConnection(); |
| urlCon.setUseCaches(false); |
| in = urlCon.getInputStream(); |
| reader = new BufferedReader(new InputStreamReader(in)); |
| |
| String line; |
| while ((line = reader.readLine()) != null) { |
| if (line.trim().startsWith("#") |
| || line.trim().length() == 0) |
| continue; |
| |
| StringTokenizer tok = new StringTokenizer(line, "# \t"); |
| if (tok.hasMoreTokens()) { |
| String next = tok.nextToken(); |
| if (next != null) { |
| next = next.trim(); |
| if (next.length() > 0 && !next.startsWith("#")) |
| set.add(next); |
| } |
| } |
| } |
| } finally { |
| try { |
| reader.close(); |
| } catch (IOException ioe) { |
| // silently consume exception |
| } |
| try { |
| in.close(); |
| } catch (IOException ioe) { |
| // silently consume exception |
| } |
| } |
| } |
| |
| public static Class[] getImplementorClasses(Class serviceClass) { |
| return getImplementorClasses(serviceClass.getName(), null); |
| } |
| |
| public static Class[] getImplementorClasses(Class serviceClass, |
| ClassLoader loader) { |
| Set invalid = new HashSet(); |
| Class[] classes = getImplementorClasses(serviceClass.getName(), loader); |
| |
| // filter out any classes that have any classloader issues wrt. |
| // the specified service class. |
| for (Class aClass : classes) |
| if (!serviceClass.isAssignableFrom(aClass)) |
| invalid.add(aClass); |
| if (invalid.size() != 0) { |
| List list = new ArrayList(Arrays.asList(classes)); |
| list.removeAll(invalid); |
| return (Class[]) list.toArray(new Class[list.size()]); |
| } else { |
| return classes; |
| } |
| } |
| |
| /** |
| * Return an array of Class objects of all known service implementors of the |
| * specified class name(as resolved by the current thread's context class |
| * loader). |
| */ |
| public static Class[] getImplementorClasses(String serviceName) { |
| return getImplementorClasses(serviceName, null); |
| } |
| |
| public static Class[] getImplementorClasses(String serviceName, |
| ClassLoader loader) { |
| try { |
| return getImplementorClasses(serviceName, loader, true); |
| } catch (Exception cnfe) { |
| // this will never happen with skipmissing |
| return new Class[0]; |
| } |
| } |
| |
| /** |
| * Return an array of Class objects of all known service implementors of the |
| * specified class name, as resolved by the specified {@link ClassLoader}. |
| * |
| * @param skipMissing if true, then ignore classes that cannot be loaded by |
| * the classloader; otherwise, resolution failures will throw a |
| * {@link ClassNotFoundException}. |
| */ |
| public static Class[] getImplementorClasses(String serviceName, |
| ClassLoader loader, boolean skipMissing) throws ClassNotFoundException { |
| if (loader == null) |
| loader = AccessController.doPrivileged( |
| J2DoPrivHelper.getContextClassLoaderAction()); |
| |
| String[] names = getImplementors(serviceName, loader); |
| if (names == null) |
| return new Class[0]; |
| |
| List classes = new ArrayList(names.length); |
| for (String name : names) { |
| try { |
| classes.add(Class.forName(name, false, loader)); |
| } |
| catch (UnsupportedClassVersionError ecve) { |
| if (!skipMissing) |
| throw ecve; |
| } |
| catch (ClassNotFoundException | LinkageError e) { |
| if (!skipMissing) |
| throw e; |
| } |
| } |
| return (Class[]) classes.toArray(new Class[classes.size()]); |
| } |
| } |