name.remal.Services Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of common Show documentation
Show all versions of common Show documentation
Java & Kotlin tools: common
package name.remal;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.sort;
import static java.util.Collections.unmodifiableList;
import static java.util.Spliterator.NONNULL;
import static java.util.Spliterator.ORDERED;
import static java.util.Spliterators.spliteratorUnknownSize;
import static name.remal.Constants.SERVICE_FILE_BASE_PATH;
import static name.remal.SneakyThrow.sneakyThrow;
public class Services {
@NotNull
private static List<@NotNull T> iterableToList(@NotNull Iterable iterable) {
List list = new ArrayList<>();
for (T element : iterable) list.add(element);
return unmodifiableList(list);
}
@NotNull
public static List<@NotNull T> loadServicesList(@NotNull Class serviceType, @Nullable String serviceSubtypeName, @Nullable ClassLoader classLoader) {
return iterableToList(loadServices(serviceType, serviceSubtypeName, classLoader));
}
@NotNull
public static List<@NotNull T> loadServicesList(@NotNull Class serviceType, @Nullable String serviceSubtypeName) {
return iterableToList(loadServices(serviceType, serviceSubtypeName));
}
@NotNull
public static List<@NotNull T> loadServicesList(@NotNull Class serviceType, @Nullable ClassLoader classLoader) {
return iterableToList(loadServices(serviceType, classLoader));
}
@NotNull
public static List<@NotNull T> loadServicesList(@NotNull Class serviceType) {
return iterableToList(loadServices(serviceType));
}
@NotNull
public static Iterable<@NotNull T> loadServices(@NotNull Class serviceType, @Nullable String serviceSubtypeName, @Nullable ClassLoader classLoader) {
Iterable result = () -> {
Iterator> implementationClasses = loadImplementationClasses(serviceType, serviceSubtypeName, classLoader).iterator();
return new Iterator() {
@Override
public boolean hasNext() {
return implementationClasses.hasNext();
}
@Override
public T next() {
Class implementationClass = implementationClasses.next();
try {
return implementationClass.newInstance();
} catch (Exception e) {
throw new ServiceConfigurationError(format(
"%s: Implementation class %s can't be instantiated",
serviceType.getName(),
implementationClass.getName()
), e);
}
}
};
};
if (Comparable.class.isAssignableFrom(serviceType)) {
List list = new ArrayList<>();
for (T impl : result) list.add(impl);
sort(UncheckedCast.>>uncheckedCast(list));
result = list;
}
return result;
}
@NotNull
public static Iterable<@NotNull T> loadServices(@NotNull Class serviceType, @Nullable String serviceSubtypeName) {
return loadServices(serviceType, serviceSubtypeName, null);
}
@NotNull
public static Iterable<@NotNull T> loadServices(@NotNull Class serviceType, @Nullable ClassLoader classLoader) {
return loadServices(serviceType, null, classLoader);
}
@NotNull
public static Iterable<@NotNull T> loadServices(@NotNull Class serviceType) {
return loadServices(serviceType, null, null);
}
@NotNull
public static Iterable<@NotNull Class> loadImplementationClasses(@NotNull Class serviceType, @Nullable String serviceSubtypeName, @Nullable ClassLoader classLoader) {
String trueServiceSubtypeName = serviceSubtypeName != null ? serviceSubtypeName : serviceType.getName();
ClassLoader trueClassLoader = classLoader != null ? classLoader : serviceType.getClassLoader();
Stream> stream = readServiceLines(SERVICE_FILE_BASE_PATH + "/" + trueServiceSubtypeName, classLoader)
.map(name -> {
try {
return trueClassLoader.loadClass(name);
} catch (@NotNull LinkageError | ClassNotFoundException e) {
throw new ServiceConfigurationError(format(
"%s: Implementation class %s can't be found",
trueServiceSubtypeName,
name
), e);
}
})
.map(clazz -> {
if (!serviceType.isAssignableFrom(clazz)) {
throw new ServiceConfigurationError(format(
"%s: Implementation class %s isn't a subtype",
trueServiceSubtypeName,
clazz.getName()
));
}
return UncheckedCast.>uncheckedCast(clazz);
});
return stream::iterator;
}
@NotNull
public static Iterable<@NotNull Class> loadImplementationClasses(@NotNull Class serviceType, @Nullable String serviceSubtypeName) {
return loadImplementationClasses(serviceType, serviceSubtypeName, null);
}
@NotNull
public static Iterable<@NotNull Class> loadImplementationClasses(@NotNull Class serviceType, @Nullable ClassLoader classLoader) {
return loadImplementationClasses(serviceType, null, classLoader);
}
@NotNull
public static Iterable<@NotNull Class> loadImplementationClasses(@NotNull Class serviceType) {
return loadImplementationClasses(serviceType, null, null);
}
@NotNull
public static Stream<@NotNull String> readServiceLines(@NotNull String resourcePath, @Nullable ClassLoader classLoader) {
if (classLoader == null) classLoader = Services.class.getClassLoader();
final Enumeration resourceURLs;
try {
resourceURLs = classLoader.getResources(resourcePath);
} catch (Throwable throwable) {
throw sneakyThrow(throwable);
}
return StreamSupport.stream(
spliteratorUnknownSize(
new Iterator() {
@Override
public boolean hasNext() {
return resourceURLs.hasMoreElements();
}
@Override
public URL next() {
return resourceURLs.nextElement();
}
},
ORDERED | NONNULL
),
false
)
.flatMap(Services::readServiceLines)
.distinct();
}
@NotNull
private static Stream<@NotNull String> readServiceLines(@NotNull URL resourceURL) {
try {
List lines = new ArrayList<>();
URLConnection urlConnection = resourceURL.openConnection();
urlConnection.setUseCaches(false);
try (InputStream inputStream = urlConnection.getInputStream()) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, UTF_8))) {
while (true) {
String line = reader.readLine();
if (line == null) break;
int commentPos = line.indexOf('#');
if (0 <= commentPos) line = line.substring(0, commentPos);
line = line.trim();
if (line.isEmpty()) continue;
lines.add(line);
}
}
} finally {
if (urlConnection instanceof HttpURLConnection) {
((HttpURLConnection) urlConnection).disconnect();
}
}
return lines.stream();
} catch (Throwable throwable) {
throw sneakyThrow(throwable);
}
}
@NotNull
public static Stream<@NotNull String> readServiceLines(@NotNull String resourcePath) {
return readServiceLines(resourcePath, null);
}
}