All Downloads are FREE. Search and download functionalities are using the official Maven repository.

name.remal.Services Maven / Gradle / Ivy

package name.remal;

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;

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.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceConfigurationError;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

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  List<@NotNull Class> loadImplementationClassesList(@NotNull Class serviceType, @Nullable String serviceSubtypeName, @Nullable ClassLoader classLoader) {
        return iterableToList(loadImplementationClasses(serviceType, serviceSubtypeName, classLoader));
    }

    @NotNull
    public static  List<@NotNull Class> loadImplementationClassesList(@NotNull Class serviceType, @Nullable String serviceSubtypeName) {
        return iterableToList(loadImplementationClasses(serviceType, serviceSubtypeName));
    }

    @NotNull
    public static  List<@NotNull Class> loadImplementationClassesList(@NotNull Class serviceType, @Nullable ClassLoader classLoader) {
        return iterableToList(loadImplementationClasses(serviceType, classLoader));
    }

    @NotNull
    public static  List<@NotNull Class> loadImplementationClassesList(@NotNull Class serviceType) {
        return iterableToList(loadImplementationClasses(serviceType));
    }

    @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);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy