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

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