
name.remal.Services Maven / Gradle / Ivy
package name.remal;
import javax.annotation.Nonnull;
import javax.annotation.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.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 {
@Nonnull
public static Iterable loadServices(@Nonnull 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;
}
@Nonnull
public static Iterable loadServices(@Nonnull Class serviceType, @Nullable String serviceSubtypeName) {
return loadServices(serviceType, serviceSubtypeName, null);
}
@Nonnull
public static Iterable loadServices(@Nonnull Class serviceType, @Nullable ClassLoader classLoader) {
return loadServices(serviceType, null, classLoader);
}
@Nonnull
public static Iterable loadServices(@Nonnull Class serviceType) {
return loadServices(serviceType, null, null);
}
@Nonnull
public static Iterable> loadImplementationClasses(@Nonnull Class serviceType, @Nullable String serviceSubtypeName, @Nullable ClassLoader classLoader) {
String trueServiceSubtypeName = null != serviceSubtypeName ? serviceSubtypeName : serviceType.getName();
ClassLoader trueClassLoader = null != classLoader ? classLoader : serviceType.getClassLoader();
Stream> stream = readServiceLines(SERVICE_FILE_BASE_PATH + "/" + trueServiceSubtypeName, classLoader)
.map(name -> {
try {
return UncheckedCast.>uncheckedCast(trueClassLoader.loadClass(name));
} catch (ClassNotFoundException e) {
throw new ServiceConfigurationError(format(
"%s: Implementation class %s can't be found",
trueServiceSubtypeName,
name
), e);
}
})
.peek(clazz -> {
if (!serviceType.isAssignableFrom(clazz)) {
throw new ServiceConfigurationError(format(
"%s: Implementation class %s isn't a subtype",
trueServiceSubtypeName,
clazz.getName()
));
}
});
return stream::iterator;
}
@Nonnull
public static Iterable> loadImplementationClasses(@Nonnull Class serviceType, @Nullable String serviceSubtypeName) {
return loadImplementationClasses(serviceType, serviceSubtypeName, null);
}
@Nonnull
public static Iterable> loadImplementationClasses(@Nonnull Class serviceType, @Nullable ClassLoader classLoader) {
return loadImplementationClasses(serviceType, null, classLoader);
}
@Nonnull
public static Iterable> loadImplementationClasses(@Nonnull Class serviceType) {
return loadImplementationClasses(serviceType, null, null);
}
@Nonnull
public static Stream readServiceLines(@Nonnull String resourcePath, @Nullable ClassLoader classLoader) {
if (null == classLoader) 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
),
false
)
.flatMap(Services::readServiceLines)
.distinct();
}
@Nonnull
private static Stream readServiceLines(@Nonnull 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 (null == line) 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);
}
}
@Nonnull
public static Stream readServiceLines(@Nonnull String resourcePath) {
return readServiceLines(resourcePath, null);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy