All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.polaris.core.service.ServiceLoader Maven / Gradle / Ivy
package io.polaris.core.service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import io.polaris.core.log.ILogger;
import io.polaris.core.log.ILoggers;
/**
* @author Qt
* @since 1.8
*/
public class ServiceLoader implements Iterable> {
private static final ILogger log = ILoggers.of(ServiceLoader.class);
public static final String[] PREFIX = {"META-INF/services/"};
private final Class type;
private final ClassLoader loader;
private final AtomicBoolean loaded = new AtomicBoolean(false);
private List> providers;
private List> wrappers;
private Map> namings;
public ServiceLoader(final Class type, final ClassLoader loader) {
this.type = type;
this.loader = loader == null ? defaultClassLoader() : loader;
}
public ServiceLoader(final Class type) {
this(type, defaultClassLoader());
}
public static ServiceLoader of(Class type) {
return new ServiceLoader<>(type);
}
public static ServiceLoader of(Class type, ClassLoader loader) {
return new ServiceLoader<>(type, loader);
}
public static ClassLoader defaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
} catch (Throwable ignored) {
}
if (cl == null) {
cl = ServiceLoader.class.getClassLoader();
if (cl == null) {
try {
cl = ClassLoader.getSystemClassLoader();
} catch (Throwable ignored) {
}
}
}
return cl;
}
public List> getProviders() {
load();
return providers;
}
public Map> getNamings() {
load();
return namings;
}
@Override
public Iterator> iterator() {
load();
return providers.iterator();
}
private void load() {
if (!loaded.get()) {
synchronized (this) {
if (!loaded.get()) {
loadClasses();
loaded.set(true);
}
}
}
}
private void loadClasses() {
final List> classes = new ArrayList<>();
loadDirectory(classes);
List> services = new ArrayList<>();
List> wrappers = new ArrayList<>();
Map> namings = new HashMap<>();
if (!classes.isEmpty()) {
List> ordered = new ArrayList<>();
List> others = new ArrayList<>();
List> defaults = new ArrayList<>();
Function wrapperBuilder = instance -> {
if (this.wrappers != null && !this.wrappers.isEmpty()) {
int count = this.wrappers.size();
for (int i = count - 1; i >= 0; i--) {
Service wrapper = this.wrappers.get(i);
instance = wrapper.newPureInstance(new Class[]{type}, new Object[]{instance});
}
}
return instance;
};
for (Class o : classes) {
Class clazz = (Class) o;
Map properties = new HashMap<>();
String serviceName = null;
if (clazz.isAnnotationPresent(ServiceName.class)) {
serviceName = clazz.getAnnotation(ServiceName.class).value();
properties.put("name", serviceName);
}
if (clazz.isAnnotationPresent(ServiceProperties.class)) {
ServiceProperties annos = clazz.getAnnotation(ServiceProperties.class);
for (ServiceProperty anno : annos.value()) {
properties.put(anno.name(), anno.value());
}
}
if (clazz.isAnnotationPresent(ServiceProperty.class)) {
ServiceProperty anno = clazz.getAnnotation(ServiceProperty.class);
properties.put(anno.name(), anno.value());
}
properties = properties.isEmpty() ? null : Collections.unmodifiableMap(properties);
if (clazz.isAnnotationPresent(ServiceOrder.class)) {
int order = clazz.getAnnotation(ServiceOrder.class).value();
ordered.add(new Service(clazz, properties, serviceName, order, wrapperBuilder));
} else if (clazz.isAnnotationPresent(ServiceDefault.class)) {
int order = clazz.getAnnotation(ServiceDefault.class).value();
defaults.add(new Service(clazz, properties, serviceName, order, wrapperBuilder));
} else {
others.add(new Service(clazz, properties, serviceName, 0, wrapperBuilder));
}
}
Collections.sort(ordered, Service.defaultComparator);
Collections.sort(others, Service.defaultComparator);
Collections.sort(defaults, Service.defaultComparator);
for (List> list : new List[]{ordered, others, defaults}) {
for (Service service : list) {
if (isWrapper(service.getServiceClass())) {
wrappers.add(service);
} else {
services.add(service);
if (service.getServiceName() != null) {
namings.putIfAbsent(service.getServiceName(), service);
}
}
}
}
}
this.providers = Collections.unmodifiableList(services);
this.wrappers = Collections.unmodifiableList(wrappers);
this.namings = Collections.unmodifiableMap(namings);
}
private void loadDirectory(List> classes) {
String typeName = type.getName();
for (String dir : PREFIX) {
String filename = dir + typeName;
try {
Enumeration urls = loader.getResources(filename);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
loadResources(classes, url);
}
} catch (Throwable ignored) {
}
}
}
private boolean isWrapper(Class> clazz) {
Constructor>[] constructors = clazz.getConstructors();
for (Constructor> constructor : constructors) {
if (constructor.getParameterTypes().length == 1 && constructor.getParameterTypes()[0] == type) {
return true;
}
}
return false;
}
private void loadResources(final List> classes, final URL url) throws IOException {
try (InputStream in = url.openStream();) {
BufferedReader br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
for (String line = br.readLine(); line != null; line = br.readLine()) {
line = line.trim().replaceAll("#.*", "");
if (line.length() > 0 && !line.startsWith("#")) {
try {
Class> clazz = Class.forName(line, true, loader);
if (type.isAssignableFrom(clazz)) {
classes.add((Class) clazz);
} else {
log.warn("加载服务类失败,类型不匹配:" + line);
}
} catch (Throwable e) {
log.warn("加载服务类失败:" + line, e);
}
}
}
} catch (Throwable e) {
log.warn("加载服务类失败,资源读取错误:" + url, e);
}
}
@Nullable
private S getSingleton(@Nullable Service s) {
return Optional.ofNullable(s).map(Service::getSingleton).orElse(null);
}
@Nullable
private S getPureSingleton(@Nullable Service s) {
return Optional.ofNullable(s).map(Service::getPureSingleton).orElse(null);
}
@Nullable
public Service get() {
load();
return providers.isEmpty() ? null : providers.get(0);
}
@Nullable
public S getSingleton() {
return getSingleton(get());
}
@Nullable
public S getPureSingleton() {
return getPureSingleton(get());
}
@Nullable
public Service get(String name) {
load();
return this.namings.get(name);
}
@Nullable
public S getSingleton(String name) {
return getSingleton(get(name));
}
@Nullable
public S getPureSingleton(String name) {
return getPureSingleton(get(name));
}
@Nullable
public Service get(String propertyName, String propertyValue) {
load();
for (Service service : providers) {
String value = service.getProperty(propertyName);
if (Objects.equals(propertyValue, value)) {
return service;
}
}
return null;
}
@Nullable
public S getSingleton(String propertyName, String propertyValue) {
return getSingleton(get(propertyName, propertyValue));
}
@Nullable
public S getPureSingleton(String propertyName, String propertyValue) {
return getPureSingleton(get(propertyName, propertyValue));
}
@Nullable
public Service get(@Nonnull Function, Boolean> matcher) {
load();
for (Service service : providers) {
if (matcher.apply(service)) {
return service;
}
}
return null;
}
@Nullable
public S getSingleton(@Nonnull Function, Boolean> matcher) {
return getSingleton(get(matcher));
}
@Nullable
public S getPureSingleton(@Nonnull Function, Boolean> matcher) {
return getPureSingleton(get(matcher));
}
public void reload() {
loaded.set(false);
load();
}
}