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

eu.maveniverse.maven.mima.cli.CommandSupport Maven / Gradle / Ivy

package eu.maveniverse.maven.mima.cli;

import eu.maveniverse.maven.mima.context.Context;
import eu.maveniverse.maven.mima.context.ContextOverrides;
import eu.maveniverse.maven.mima.context.HTTPProxy;
import eu.maveniverse.maven.mima.context.MavenSystemHome;
import eu.maveniverse.maven.mima.context.MavenUserHome;
import eu.maveniverse.maven.mima.context.Runtime;
import eu.maveniverse.maven.mima.context.Runtimes;
import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import org.apache.maven.settings.Proxy;
import org.apache.maven.settings.Settings;
import org.eclipse.aether.repository.RemoteRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;

/**
 * Support.
 */
public abstract class CommandSupport implements Callable {
    @CommandLine.Option(
            names = {"-v", "--verbose"},
            description = "Be verbose about things happening")
    protected boolean verbose;

    @CommandLine.Option(
            names = {"-o", "--offline"},
            description = "Work offline")
    protected boolean offline;

    @CommandLine.Option(
            names = {"-s", "--settings"},
            description = "The Maven User Settings file to use")
    protected Path userSettingsXml;

    @CommandLine.Option(
            names = {"-gs", "--global-settings"},
            description = "The Maven Global Settings file to use")
    protected Path globalSettingsXml;

    @CommandLine.Option(
            names = {"-P", "--activate-profiles"},
            split = ",",
            description = "Comma delimited list of profile IDs to activate (may use '+', '-' and '!' prefix)")
    protected java.util.List profiles;

    @CommandLine.Option(
            names = {"-D", "--define"},
            description = "Define a user property")
    protected List userProperties;

    @CommandLine.Option(
            names = {"--proxy"},
            description = "Define a HTTP proxy (host:port)")
    protected String proxy;

    protected Logger logger = LoggerFactory.getLogger(getClass());

    private static final ConcurrentHashMap> EXECUTION_CONTEXT = new ConcurrentHashMap<>();

    private static final AtomicBoolean VWO = new AtomicBoolean(false);

    protected void writeVersionOnce(Runtime runtime) {
        if (VWO.compareAndSet(false, true)) {
            logger.info("MIMA (Runtime '{}' version {})", runtime.name(), runtime.version());
            logger.info("====");
        }
    }

    protected Object getOrCreate(String key, Supplier supplier) {
        ArrayDeque deque = EXECUTION_CONTEXT.computeIfAbsent(key, k -> new ArrayDeque<>());
        if (deque.isEmpty()) {
            deque.push(supplier.get());
        }
        return deque.peek();
    }

    protected void push(String key, Object object) {
        ArrayDeque deque = EXECUTION_CONTEXT.computeIfAbsent(key, k -> new ArrayDeque<>());
        deque.push(object);
    }

    protected Object pop(String key) {
        ArrayDeque deque = EXECUTION_CONTEXT.get(key);
        if (deque == null || deque.isEmpty()) {
            throw new IllegalStateException("No element to pop");
        }
        return deque.pop();
    }

    protected Object peek(String key) {
        ArrayDeque deque = EXECUTION_CONTEXT.get(key);
        if (deque == null || deque.isEmpty()) {
            throw new IllegalStateException("No element to peek");
        }
        return deque.peek();
    }

    protected void mayDumpEnv(Runtime runtime, Context context) {
        writeVersionOnce(runtime);
        logger.info("          Maven version {}", runtime.mavenVersion());
        logger.info("                Managed {}", runtime.managedRepositorySystem());
        logger.info("                Basedir {}", context.basedir());
        logger.info(
                "                Offline {}", context.repositorySystemSession().isOffline());

        MavenSystemHome mavenSystemHome = context.mavenSystemHome();
        logger.info("");
        logger.info("             MAVEN_HOME {}", mavenSystemHome == null ? "undefined" : mavenSystemHome.basedir());
        if (mavenSystemHome != null) {
            logger.info("           settings.xml {}", mavenSystemHome.settingsXml());
            logger.info("         toolchains.xml {}", mavenSystemHome.toolchainsXml());
        }

        MavenUserHome mavenUserHome = context.mavenUserHome();
        logger.info("");
        logger.info("              USER_HOME {}", mavenUserHome.basedir());
        logger.info("           settings.xml {}", mavenUserHome.settingsXml());
        logger.info("  settings-security.xml {}", mavenUserHome.settingsSecurityXml());
        logger.info("       local repository {}", mavenUserHome.localRepository());

        logger.info("");
        logger.info("               PROFILES");
        logger.info("                 Active {}", context.contextOverrides().getActiveProfileIds());
        logger.info("               Inactive {}", context.contextOverrides().getInactiveProfileIds());

        logger.info("");
        logger.info("    REMOTE REPOSITORIES");
        for (RemoteRepository repository : context.remoteRepositories()) {
            if (repository.getMirroredRepositories().isEmpty()) {
                logger.info("                        {}", repository);
            } else {
                logger.info("                        {}, mirror of", repository);
                for (RemoteRepository mirrored : repository.getMirroredRepositories()) {
                    logger.info("                          {}", mirrored);
                }
            }
        }

        if (context.httpProxy() != null) {
            HTTPProxy proxy = context.httpProxy();
            logger.info("");
            logger.info("             HTTP PROXY");
            logger.info("                    url {}://{}:{}", proxy.getProtocol(), proxy.getHost(), proxy.getPort());
            logger.info("          nonProxyHosts {}", proxy.getNonProxyHosts());
        }

        if (verbose) {
            logger.info("");
            logger.info("        USER PROPERTIES");
            context.contextOverrides().getUserProperties().entrySet().stream()
                    .sorted(Map.Entry.comparingByKey())
                    .forEach(e -> logger.info("                         {}={}", e.getKey(), e.getValue()));
            logger.info("      SYSTEM PROPERTIES");
            context.contextOverrides().getSystemProperties().entrySet().stream()
                    .sorted(Map.Entry.comparingByKey())
                    .forEach(e -> logger.info("                         {}={}", e.getKey(), e.getValue()));
            logger.info("      CONFIG PROPERTIES");
            context.contextOverrides().getConfigProperties().entrySet().stream()
                    .sorted(Map.Entry.comparingByKey())
                    .forEach(e -> logger.info("                         {}={}", e.getKey(), e.getValue()));
        }
        logger.info("");
    }

    protected Runtime getRuntime() {
        Runtime runtime = (Runtime) getOrCreate(Runtime.class.getName(), Runtimes.INSTANCE::getRuntime);
        writeVersionOnce(runtime);
        return runtime;
    }

    protected ContextOverrides getContextOverrides() {
        return (ContextOverrides) getOrCreate(ContextOverrides.class.getName(), () -> {
            // create builder with some sane defaults
            ContextOverrides.Builder builder = ContextOverrides.create().withUserSettings(true);
            if (offline) {
                builder.offline(true);
            }
            if (userSettingsXml != null) {
                builder.withUserSettingsXmlOverride(userSettingsXml);
            }
            if (globalSettingsXml != null) {
                builder.withGlobalSettingsXmlOverride(globalSettingsXml);
            }
            if (profiles != null && !profiles.isEmpty()) {
                ArrayList activeProfiles = new ArrayList<>();
                ArrayList inactiveProfiles = new ArrayList<>();
                for (String profile : profiles) {
                    if (profile.startsWith("+")) {
                        activeProfiles.add(profile.substring(1));
                    } else if (profile.startsWith("-") || profile.startsWith("!")) {
                        inactiveProfiles.add(profile.substring(1));
                    } else {
                        activeProfiles.add(profile);
                    }
                }
                builder.withActiveProfileIds(activeProfiles).withInactiveProfileIds(inactiveProfiles);
            }
            if (userProperties != null && !userProperties.isEmpty()) {
                HashMap defined = new HashMap<>(userProperties.size());
                String name;
                String value;
                for (String property : userProperties) {
                    int i = property.indexOf('=');
                    if (i <= 0) {
                        name = property.trim();
                        value = Boolean.TRUE.toString();
                    } else {
                        name = property.substring(0, i).trim();
                        value = property.substring(i + 1);
                    }
                    defined.put(name, value);
                }
                builder.userProperties(defined);
            }
            if (proxy != null) {
                String[] elems = proxy.split(":");
                if (elems.length != 2) {
                    throw new IllegalArgumentException("Proxy must be specified as 'host:port'");
                }
                Proxy proxySettings = new Proxy();
                proxySettings.setId("mima-mixin");
                proxySettings.setActive(true);
                proxySettings.setProtocol("http");
                proxySettings.setHost(elems[0]);
                proxySettings.setPort(Integer.parseInt(elems[1]));
                Settings proxyMixin = new Settings();
                proxyMixin.addProxy(proxySettings);
                builder.withEffectiveSettingsMixin(proxyMixin);
            }
            return builder.build();
        });
    }

    protected Context getContext() {
        return (Context) getOrCreate(Context.class.getName(), () -> getRuntime().create(getContextOverrides()));
    }
}