com.spotify.helios.serviceregistration.ServiceRegistrarLoader Maven / Gradle / Ivy
/*-
* -\-\-
* Helios Service Registration
* --
* Copyright (C) 2016 Spotify AB
* --
* Licensed 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 com.spotify.helios.serviceregistration;
import static java.util.Arrays.asList;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import org.slf4j.Logger;
/**
* Helpers for loading {@link ServiceRegistrarFactory} instances.
*/
public class ServiceRegistrarLoader {
private static final List PROVIDED = asList(Logger.class.getPackage(),
ServiceRegistrarLoader.class.getPackage());
private static final ClassLoader CURRENT = ServiceRegistrarLoader.class.getClassLoader();
/**
* Load a {@link ServiceRegistrarFactory} using the current class loader.
*
* @return A {@link ServiceRegistrarFactory}, null if none could be found.
*
* @throws ServiceRegistrarLoadingException if loading failed.
*/
public static ServiceRegistrarFactory load() throws ServiceRegistrarLoadingException {
return load("classpath", CURRENT);
}
/**
* Load a {@link ServiceRegistrarFactory} from a plugin jar file with a parent class loader that
* will not load classes from the jvm classpath. Any dependencies of the plugin must be included
* in the plugin jar.
*
* @param plugin The plugin jar file to load.
*
* @return A {@link ServiceRegistrarFactory}, null if none could be found.
*
* @throws ServiceRegistrarLoadingException if loading failed.
*/
public static ServiceRegistrarFactory load(final Path plugin)
throws ServiceRegistrarLoadingException {
return load(plugin, CURRENT, extensionClassLoader(CURRENT));
}
/**
* Load a {@link ServiceRegistrarFactory} from a plugin jar file with a specified parent class
* loader and a list of exposed classes.
*
* @param plugin The plugin jar file to load.
* @param environment The class loader to use for providing plugin interface dependencies.
* @param parent The parent class loader to assign to the class loader of the jar.
*
* @return A {@link ServiceRegistrarFactory}, null if none could be found.
*
* @throws ServiceRegistrarLoadingException if loading failed.
*/
public static ServiceRegistrarFactory load(final Path plugin,
final ClassLoader environment,
final ClassLoader parent)
throws ServiceRegistrarLoadingException {
return load("plugin jar file: " + plugin, pluginClassLoader(plugin, environment, parent));
}
/**
* Load a {@link ServiceRegistrarFactory} using a class loader.
*
* @param source The source of the class loader.
* @param classLoader The class loader to load from.
*
* @return A {@link ServiceRegistrarFactory}, null if none could be found.
*
* @throws ServiceRegistrarLoadingException if loading failed.
*/
public static ServiceRegistrarFactory load(final String source, final ClassLoader classLoader)
throws ServiceRegistrarLoadingException {
final ServiceLoader loader;
try {
loader = ServiceLoader.load(ServiceRegistrarFactory.class, classLoader);
} catch (ServiceConfigurationError e) {
throw new ServiceRegistrarLoadingException(
"Failed to load service registrar from " + source, e);
}
final Iterator iterator = loader.iterator();
if (iterator.hasNext()) {
return iterator.next();
} else {
return null;
}
}
/**
* Attempt to get the extension class loader, which can only load jdk classes and classes from
* jvm
* extensions. Adapted from {@link ServiceLoader#loadInstalled(Class)}
*/
private static ClassLoader extensionClassLoader(final ClassLoader environment) {
ClassLoader cl = environment;
ClassLoader prev = null;
while (cl != null) {
prev = cl;
cl = cl.getParent();
}
return prev;
}
/**
* Create a class loader for a plugin jar.
*/
private static ClassLoader pluginClassLoader(final Path plugin,
final ClassLoader environment,
final ClassLoader parent) {
final URL url;
try {
url = plugin.toFile().toURI().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException("Failed to load plugin jar " + plugin, e);
}
final ClassLoader providedClassLoader = new FilteringClassLoader(PROVIDED, environment, parent);
return new URLClassLoader(new URL[]{ url }, providedClassLoader);
}
/**
* A class loader that exposes a specific list of packages from another class loader.
*/
private static class FilteringClassLoader extends ClassLoader {
private final List packages;
private final ClassLoader environment;
public FilteringClassLoader(final List packages,
final ClassLoader environment,
final ClassLoader parent) {
super(parent);
this.packages = packages;
this.environment = environment;
}
@Override
protected Class> findClass(final String name) throws ClassNotFoundException {
for (final Package pkg : packages) {
if (name.startsWith(pkg.getName())) {
return environment.loadClass(name);
}
}
return super.findClass(name);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy