blob: 70b129440eceebe942bfe3073d9ef4488203d3a1 [file] [log] [blame]
/*
* 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.tika.config;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
/**
* Internal utility class that Tika uses to look up service providers.
*
* @since Apache Tika 0.9
*/
public class ServiceLoader {
/**
* The default context class loader to use for all threads, or
* <code>null</code> to automatically select the context class loader.
*/
private static volatile ClassLoader contextClassLoader = null;
/**
* Returns the context class loader of the current thread. If such
* a class loader is not available, then the loader of this class or
* finally the system class loader is returned.
*
* @see <a href="https://issues.apache.org/jira/browse/TIKA-441">TIKA-441</a>
* @return context class loader, or <code>null</code> if no loader
* is available
*/
private static ClassLoader getContextClassLoader() {
ClassLoader loader = contextClassLoader;
if (loader == null) {
loader = Thread.currentThread().getContextClassLoader();
}
if (loader == null) {
loader = ServiceLoader.class.getClassLoader();
}
if (loader == null) {
loader = ClassLoader.getSystemClassLoader();
}
return loader;
}
/**
* Sets the context class loader to use for all threads that access
* this class. Used for example in an OSGi environment to avoid problems
* with the default context class loader.
*
* @param loader default context class loader,
* or <code>null</code> to automatically pick the loader
*/
public static void setContextClassLoader(ClassLoader loader) {
contextClassLoader = loader;
}
private final ClassLoader loader;
private final LoadErrorHandler handler;
public ServiceLoader(ClassLoader loader, LoadErrorHandler handler) {
this.loader = loader;
this.handler = handler;
}
public ServiceLoader(ClassLoader loader) {
this(loader, LoadErrorHandler.IGNORE);
}
public ServiceLoader() {
this(getContextClassLoader());
}
/**
* Returns all the available service providers of the given type.
*
* @param service service provider interface
* @return available service providers
*/
@SuppressWarnings("unchecked")
public <T> List<T> loadServiceProviders(Class<T> service) {
List<T> providers = new ArrayList<T>();
if (loader != null) {
Set<String> names = new HashSet<String>();
try {
String name = service.getName();
Enumeration<URL> resources = loader.getResources("META-INF/services/" + name);
for (URL resource : Collections.list(resources)) {
try {
names.addAll(getServiceClassNames(resource));
} catch (IOException e) {
handler.handleLoadError(name, e);
}
}
} catch (IOException ignore) {
// We couldn't get the list of service resource files
}
for (String name : names) {
try {
Class<?> klass = loader.loadClass(name);
if (service.isAssignableFrom(klass)) {
providers.add((T) klass.newInstance());
}
} catch (Throwable t) {
handler.handleLoadError(name, t);
}
}
}
return providers;
}
private static final Pattern COMMENT = Pattern.compile("#.*");
private static final Pattern WHITESPACE = Pattern.compile("\\s+");
private Set<String> getServiceClassNames(URL resource)
throws IOException {
Set<String> names = new HashSet<String>();
InputStream stream = resource.openStream();
try {
BufferedReader reader =
new BufferedReader(new InputStreamReader(stream, "UTF-8"));
String line = reader.readLine();
while (line != null) {
line = COMMENT.matcher(line).replaceFirst("");
line = WHITESPACE.matcher(line).replaceAll("");
if (line.length() > 0) {
names.add(line);
}
line = reader.readLine();
}
} finally {
stream.close();
}
return names;
}
}