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

com.tngtech.archunit.core.PluginLoader Maven / Gradle / Ivy

Go to download

A Java architecture test library, to specify and assert architecture rules in plain Java - Module 'archunit'

There is a newer version: 1.3.0
Show newest version
/*
 * Copyright 2014-2022 TNG Technology Consulting GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.tngtech.archunit.core;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.collect.Ordering;
import com.tngtech.archunit.Internal;
import com.tngtech.archunit.base.MayResolveTypesViaReflection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.tngtech.archunit.base.Suppliers.memoize;

@Internal
public class PluginLoader {
    private static final Logger LOG = LoggerFactory.getLogger(PluginLoader.class);

    private final Supplier supplier;

    private PluginLoader(Class pluginType, Map versionToPlugins, T fallback) {
        supplier = memoize(new PluginSupplier<>(pluginType, versionToPlugins, fallback));
    }

    public static  Creator forType(Class pluginType) {
        return new Creator<>(pluginType);
    }

    public T load() {
        return supplier.get();
    }

    private static class PluginSupplier implements Supplier {
        private final Class pluginType;
        private final Map versionToPlugins;
        private final T fallback;

        private PluginSupplier(Class pluginType, Map versionToPlugins, T fallback) {
            this.pluginType = pluginType;
            this.versionToPlugins = versionToPlugins;
            this.fallback = fallback;
        }

        @Override
        public T get() {
            String currentVersion = System.getProperty("java.version");
            LOG.info("Detected Java version {}", currentVersion);

            for (JavaVersion version : JavaVersion.sortFromNewestToOldest(versionToPlugins.keySet())) {
                if (version.isLessOrEqualThan(currentVersion)) {
                    String className = versionToPlugins.get(version);
                    LOG.debug("Current Java version is compatible to {} => Loading Plugin {}", version, className);
                    return create(className);
                }
            }
            LOG.debug("Using legacy import plugin");
            return fallback;
        }

        @SuppressWarnings("unchecked") // We explicitly check that the loaded class is assignable to T (i.e. pluginType)
        @MayResolveTypesViaReflection(reason = "This only resolves ArchUnit's own classes, it's not dependent on any project classpath")
        private T create(String className) {
            try {
                Class clazz = Class.forName(className);
                checkCompatibility(className, clazz);
                return (T) clazz.getConstructor().newInstance();
            } catch (Exception e) {
                throw new PluginLoadingFailedException(e, "Couldn't load plugin of type %s", className);
            }
        }

        private void checkCompatibility(String className, Class clazz) {
            if (!pluginType.isAssignableFrom(clazz)) {
                throw new PluginLoadingFailedException("Class %s must implement %s", className, pluginType.getName());
            }
        }
    }

    @Internal
    public static class Creator {
        private final Map versionToPlugins = new HashMap<>();
        private final Class pluginType;

        private Creator(Class pluginType) {
            this.pluginType = pluginType;
        }

        private Creator addPlugin(JavaVersion version, String pluginClassName) {
            versionToPlugins.put(checkNotNull(version), checkNotNull(pluginClassName));
            return this;
        }

        public PluginLoader fallback(T fallback) {
            return new PluginLoader<>(pluginType, versionToPlugins, fallback);
        }

        public PluginEntry ifVersionGreaterOrEqualTo(JavaVersion version) {
            return new PluginEntry(version);
        }

        @Internal
        public class PluginEntry {
            private final PluginLoader.JavaVersion version;

            private PluginEntry(JavaVersion version) {
                this.version = version;
            }

            public Creator load(String pluginClassName) {
                return Creator.this.addPlugin(version, pluginClassName);
            }
        }
    }

    @Internal
    public enum JavaVersion {

        JAVA_9(9),
        JAVA_14(14);

        private final int releaseVersion;

        JavaVersion(int releaseVersion) {
            this.releaseVersion = releaseVersion;
        }

        private static final Ordering FROM_NEWEST_TO_OLDEST_ORDERING = Ordering.natural().reverse();
        private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+).*");

        public boolean isLessOrEqualThan(String version) {
            return this.releaseVersion <= parseJavaReleaseVersion(version);
        }

        static List sortFromNewestToOldest(Set javaVersions) {
            return FROM_NEWEST_TO_OLDEST_ORDERING.sortedCopy(javaVersions);
        }

        private static int parseJavaReleaseVersion(String version) {
            String versionToParse;
            if (version.startsWith("1.")) {
                // Up to JDK 8 the versioning scheme was sth. like 1.8.0_122
                versionToParse = version.substring(2);
            } else {
                // The new versioning scheme starting with JDK 9 is
                // - for major releases: 9
                // - for patches: 9.x
                // - for early access builds: 9-ea
                versionToParse = version;
            }

            Matcher matcher = VERSION_PATTERN.matcher(versionToParse);
            if (!matcher.matches()) {
                throw new IllegalArgumentException("Can't parse Java version " + version);
            }
            return Integer.parseInt(matcher.group(1));
        }
    }

    static class PluginLoadingFailedException extends RuntimeException {
        PluginLoadingFailedException(String message, Object... args) {
            super(String.format(message, args));
        }

        PluginLoadingFailedException(Exception e, String message, Object... args) {
            super(String.format(message, args), e);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy