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

io.helidon.build.util.Maven Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2019, 2020 Oracle and/or its affiliates.
 *
 * 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 io.helidon.build.util;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;

import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.apache.maven.settings.Profile;
import org.apache.maven.settings.Repository;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.building.DefaultSettingsBuilderFactory;
import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
import org.apache.maven.settings.building.SettingsBuilder;
import org.apache.maven.settings.building.SettingsBuildingException;
import org.eclipse.aether.DefaultRepositoryCache;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.VersionRangeRequest;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
import org.eclipse.aether.version.Version;

import static io.helidon.build.util.Constants.DIR_SEP;
import static io.helidon.build.util.FileUtils.assertDir;
import static io.helidon.build.util.FileUtils.assertFile;
import static java.util.Objects.requireNonNull;

/**
 * Maven utilities.
 */
public class Maven {
    private static final String LATEST_VERSION = "[0,)";
    private static final String SNAPSHOT_SUFFIX = "-SNAPSHOT";
    private final RepositorySystem system;
    private final DefaultRepositorySystemSession session;
    private final List repositories;

    private Maven(Builder builder) {
        this.system = builder.system;
        this.session = builder.session;
        this.repositories = builder.repositories;
    }

    /**
     * Returns a new builder.
     *
     * @return The builder.
     */
    public static Builder builder() {
        return new Builder();
    }

    /**
     * Returns the latest version for the given artifact.
     *
     * @param groupId The artifact group id.
     * @param artifactId The artifact id.
     * @param allowSnapshot {@code true} if snapshot versions are allowed.
     * @return The latest version.
     */
    public Version latestVersion(String groupId, String artifactId, boolean allowSnapshot) {
        return latestVersion(toCoordinates(groupId, artifactId, LATEST_VERSION), allowSnapshot);
    }

    /**
     * Returns the latest version for the given artifact.
     *
     * @param coordinates The artifact coordinates.
     * @param allowSnapshot {@code true} if snapshot versions are allowed.
     * @return The latest version.
     */
    public Version latestVersion(String coordinates, boolean allowSnapshot) {
        final List versions = versions(coordinates);
        final int lastIndex = versions.size() - 1;
        return IntStream.rangeClosed(0, lastIndex)
                        .mapToObj(index -> versions.get(lastIndex - index))
                        .filter(version -> !version.toString().endsWith(SNAPSHOT_SUFFIX))
                        .findFirst()
                        .orElseThrow(() -> new IllegalStateException("no non-snapshot version found!"));
    }

    /**
     * Returns the versions for the given artifact.
     *
     * @param groupId The artifact group id.
     * @param artifactId The artifact id.
     * @return The versions.
     */
    public List versions(String groupId, String artifactId) {
        return versions(toCoordinates(groupId, artifactId, LATEST_VERSION));
    }

    /**
     * Returns the versions for the given artifact.
     *
     * @param coordinates The artifact coordinates.
     * @return The versions.
     */
    public List versions(String coordinates) {
        final Artifact artifact = new DefaultArtifact(coordinates);
        final VersionRangeRequest request = new VersionRangeRequest(artifact, repositories, null);
        try {
            return system.resolveVersionRange(session, request).getVersions();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Returns the path to the given artifact.
     *
     * @param groupId The artifact group id.
     * @param artifactId The artifact id.
     * @param version The artifact version.
     * @return The path.
     */
    public Path artifact(String groupId, String artifactId, String version) {
        return artifact(toCoordinates(groupId, artifactId, version));
    }

    /**
     * Returns the path to the given artifact.
     *
     * @param coordinates The artifact coordinates.
     * @return The path.
     */
    public Path artifact(String coordinates) {
        final Artifact artifact = new DefaultArtifact(coordinates);
        final ArtifactRequest request = new ArtifactRequest(artifact, repositories, null);
        try {
            ArtifactResult result = system.resolveArtifact(session, request);
            return result.getArtifact().getFile().toPath();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Returns the given artifact coordinate components as a GAV coordinate.
     *
     * @param groupId The artifact group id.
     * @param artifactId The artifact id.
     * @param version The artifact version.
     * @return The coordinates.
     */
    public static String toCoordinates(String groupId, String artifactId, String version) {
        return groupId + ":" + artifactId + ":" + version;
    }

    /**
     * Maven builder.
     */
    public static class Builder {
        private static final String PATH_VAR = "PATH";
        private static final String USER_HOME_PROPERTY = "user.home";
        private static final String MVN_BINARY_NAME = "mvn";
        private static final String GLOBAL_SETTINGS_PATH = "conf" + DIR_SEP + "settings.xml";
        private static final String USER_SETTINGS_PATH = ".m2" + DIR_SEP + "settings.xml";
        private static final String LOCAL_REPOSITORY_PATH = ".m2" + DIR_SEP + "repository";
        private static final String CENTRAL_ID = "central";
        private static final String REPOSITORY_TYPE = "default";
        private static final String CENTRAL_URL = "https://repo.maven.apache.org/maven2/";
        private static final String LOCAL_REPOSITORY_KEY = "localRepository";
        private static final String MAVEN_HOME_VAR = "MAVEN_HOME";
        private static final String MVN_HOME_VAR = "MVN_HOME";

        private final Path userHome;
        private Path mavenHome;
        private Path localRepositoryDir;
        private Path globalSettings;
        private Path userSettings;
        private boolean offline;
        private RepositorySystem system;
        private DefaultRepositorySystemSession session;
        private Settings settings;
        private List repositories;

        private Builder() {
            this.userHome = Paths.get(System.getProperty(USER_HOME_PROPERTY));
        }

        /**
         * Sets the path to the maven install directory.
         *
         * @param mavenHome The path.
         * @return This instance.
         */
        public Builder mavenHome(Path mavenHome) {
            this.mavenHome = assertDir(mavenHome);
            return this;
        }

        /**
         * Sets the path to the global {@code settings.xml} file.
         *
         * @param globalSettingsFile The path.
         * @return This instance.
         */
        public Builder globalSettingsFile(Path globalSettingsFile) {
            this.globalSettings = assertFile(globalSettingsFile);
            return this;
        }

        /**
         * Sets the path to the user {@code settings.xml} file.
         *
         * @param userSettingsFile The path.
         * @return This instance.
         */
        public Builder userSettingsFile(Path userSettingsFile) {
            this.userSettings = assertFile(userSettingsFile);
            return this;
        }

        /**
         * Sets the path to the local repository.
         *
         * @param localRepositoryDir The path.
         * @return This instance.
         */
        public Builder localRepositoryDir(Path localRepositoryDir) {
            this.localRepositoryDir = assertDir(localRepositoryDir);
            return this;
        }

        /**
         * Sets offline mode.
         *
         * @param offline {@code true} if offline.
         * @return This instance.
         */
        public Builder offline(boolean offline) {
            this.offline = offline;
            return this;
        }

        /**
         * Builds the {@link Maven} instance.
         *
         * @return The instance.
         */
        public Maven build() {
            system = newRepositorySystem();
            session = MavenRepositorySystemUtils.newSession()
                                                .setOffline(offline)
                                                .setCache(new DefaultRepositoryCache());
            final LocalRepository localRepo = localRepository();
            session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));

            //session.setTransferListener( new ConsoleTransferListener() );
            //session.setRepositoryListener( new ConsoleRepositoryListener() );

            settings = settings();
            repositories = repositories();
            return new Maven(this);
        }

        private Path mavenHome() {
            if (mavenHome == null) {
                mavenHome = envVarPath(MAVEN_HOME_VAR);
                if (mavenHome == null) {
                    mavenHome = envVarPath(MVN_HOME_VAR);
                    if (mavenHome == null) {
                        mavenHome = findExecutable(MVN_BINARY_NAME);
                    }
                }
            }
            return mavenHome;
        }

        private LocalRepository localRepository() {
            if (localRepositoryDir == null) {
                final String localRepo = System.getProperty(LOCAL_REPOSITORY_KEY);
                if (localRepo != null) {
                    final Path dir = Paths.get(localRepo);
                    if (Files.isDirectory(dir)) {
                        localRepositoryDir = dir;
                    }
                }
                if (localRepositoryDir == null) {
                    localRepositoryDir = assertDir(userHome.resolve(LOCAL_REPOSITORY_PATH));
                }
            }
            return new LocalRepository(localRepositoryDir.toFile());
        }

        private Settings settings() {
            if (globalSettings == null) {
                final Path home = mavenHome();
                this.globalSettings = home.resolve(GLOBAL_SETTINGS_PATH);
            }
            if (userSettings == null) {
                this.userSettings = userHome.resolve(USER_SETTINGS_PATH);
            }
            final SettingsBuilder settingsBuilder = new DefaultSettingsBuilderFactory().newInstance();
            final DefaultSettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
            if (Files.isRegularFile(globalSettings)) {
                request.setGlobalSettingsFile(globalSettings.toFile());
            }
            if (Files.isRegularFile(userSettings)) {
                request.setUserSettingsFile(userSettings.toFile());
            }
            request.setSystemProperties(System.getProperties());
            try {
                return settingsBuilder.build(request).getEffectiveSettings();
            } catch (SettingsBuildingException e) {
                throw new IllegalArgumentException("Could not process settings.xml", e);
            }
        }

        private List repositories() {
            final Map profiles = settings.getProfilesAsMap();
            final List result = new ArrayList<>();
            for (String profileName : settings.getActiveProfiles()) {
                for (Repository repo : profiles.get(profileName).getRepositories()) {
                    result.add(new RemoteRepository.Builder(repo.getId(), REPOSITORY_TYPE, repo.getUrl()).build());
                }
            }
            if (result.isEmpty()) {
                result.add(new RemoteRepository.Builder(CENTRAL_ID, REPOSITORY_TYPE, CENTRAL_URL).build());
            }
            return result;
        }

        private RepositorySystem newRepositorySystem() {
            final DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
            locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
            locator.addService(TransporterFactory.class, FileTransporterFactory.class);
            locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
            locator.setErrorHandler(new DefaultServiceLocator.ErrorHandler() {
                @Override
                public void serviceCreationFailed(Class type, Class impl, Throwable exception) {
                    Log.error("Service creation failed for %s implementation %s: %s",
                              type, impl, exception.getMessage(), exception);
                }
            });
            return locator.getService(RepositorySystem.class);
        }

        private static Path envVarPath(String var) {
            final String path = System.getenv(var);
            return path == null ? null : Paths.get(path);
        }

        private static Path findExecutable(String executableName) {
            return Arrays.stream(requireNonNull(System.getenv(PATH_VAR)).split(File.pathSeparator))
                         .map(dir -> Paths.get(dir))
                         .filter(path -> Files.isExecutable(path.resolve(executableName)))
                         .findFirst()
                         .orElseThrow(() -> new IllegalStateException(executableName + " not found in " + PATH_VAR));
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy