MvnRepository.java

package org.docascode.api.core.mvn;

import org.apache.maven.model.building.DefaultModelBuilderFactory;
import org.apache.maven.model.building.ModelBuilder;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.apache.maven.settings.*;
import org.apache.maven.settings.io.DefaultSettingsReader;
import org.docascode.api.core.errors.DocAsCodeException;
import org.docascode.api.event.Event;
import org.docascode.api.listener.APIEventListener;
import org.docascode.api.listener.TransfertListener;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.deployment.DeployRequest;
import org.eclipse.aether.deployment.DeploymentException;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.repository.Authentication;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.*;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.transport.classpath.ClasspathTransporterFactory;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
import org.eclipse.aether.version.Version;

import java.io.File;
import java.io.IOException;
import java.util.*;

public class MvnRepository extends APIEventListener {
    private static final ModelBuilder MODEL_BUILDER = new DefaultModelBuilderFactory().newInstance();

    private Settings settings;

    private Settings localSettings;

    private Settings projectSettings;

    private File projectSettingsFile;

    private List<RemoteRepository> remoteRepositories = new ArrayList<>();

    private List<String> profilesId = new ArrayList<>();

    private DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();

    private RepositorySystem repoSys;

    private TransfertListener transfertListener = new TransfertListener();

    private File baseDir;

    public MvnRepository(File projectSettings, File baseDir){
        this.baseDir = baseDir;
        this.projectSettingsFile = projectSettings;
        locator.setServices( ModelBuilder.class, MODEL_BUILDER );
        locator.addService( RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class );
        locator.addService( TransporterFactory.class, FileTransporterFactory.class );
        locator.addService( TransporterFactory.class, HttpTransporterFactory.class );
        locator.addService( TransporterFactory.class, ClasspathTransporterFactory.class );
    }

    private Settings getLocalSettings() throws DocAsCodeException {
        if ( localSettings == null )
        {
            File userSettingsFile = new File( System.getProperty( "user.home" ),".m2/settings.xml" );
            if ( userSettingsFile.exists() )
            {
                DefaultSettingsReader reader = new DefaultSettingsReader();
                try {
                    localSettings = reader.read(userSettingsFile,null);
                } catch (IOException e) {
                    throw new DocAsCodeException(
                            String.format("Unable to load user settings file '%s'.",userSettingsFile),
                            e);
                }
            } else {
                localSettings = new Settings();
            }
            if ( localSettings.getLocalRepository()==null )
            {
                String location = new File( System.getProperty( "user.home" ), ".m2/repository" ).getAbsolutePath();
                localSettings.setLocalRepository( location );
            }
        }
        return localSettings;
    }

    private File getProjectSettingsFile() {
        return projectSettingsFile;
    }

    private Settings getProjectSettings() throws DocAsCodeException {
        if ( projectSettings == null )
        {
            if ( getProjectSettingsFile().exists() )
            {
                DefaultSettingsReader reader = new DefaultSettingsReader();
                try {
                    projectSettings = reader.read(getProjectSettingsFile(),null);
                } catch (IOException e) {
                    throw new DocAsCodeException(
                            String.format("Unable to load project settings file '%s'.",projectSettingsFile),
                            e);
                }
            } else {
                projectSettings = new Settings();
            }
        }
        return projectSettings;
    }

    private List<Profile> getProfiles(String id) throws DocAsCodeException {
        List<String> activeProfiles = getSettings().getActiveProfiles();
        List<Profile> profiles = new ArrayList<>();
        if (id != null){
            profiles.add(getSettings().getProfilesAsMap().get(id));
        } else {
            for (Profile profile : getSettings().getProfiles()) {
                if ((profile.getActivation() != null && profile.getActivation().isActiveByDefault())
                        || activeProfiles.contains(profile.getId())) {
                    profiles.add(profile);
                }
            }
        }
        return profiles;
    }

    public MvnRepository addRemoteRepositories(String id) throws DocAsCodeException {
        if (!this.profilesId.contains(id)) {
            this.profilesId.add(id);
            List<Profile> profiles = getProfiles(id);
            for (Profile profile : profiles) {
                if (profile.getRepositories().isEmpty()) {
                    Repository r = new Repository();
                    r.setUrl(String.format(
                            "file://%s/releases",
                            this.baseDir.getAbsolutePath()));
                    r.setId("default");
                    r.setLayout("default");
                    profile.addRepository(r);
                }
                for (Repository r : profile.getRepositories()) {
                    RemoteRepository.Builder builder =
                            new RemoteRepository.Builder(
                                    r.getId(),
                                    r.getLayout(),
                                    r.getUrl());
                    Server server = getSettings().getServer(r.getId());
                    AuthenticationBuilder authBuilder = new AuthenticationBuilder();
                    if (server != null) {
                        authBuilder
                                .addUsername(server.getUsername())
                                .addPassword(server.getPassword());
                    } else {
                        log(
                                String.format(
                                        "No authentication parameters found for server '%s'.",
                                        r.getId()), Event.Level.DEBUG);
                    }
                    Authentication a = authBuilder.build();
                    builder.setAuthentication(a);
                    remoteRepositories.add(builder.build());
                }
            }
        }
        return this;
    }

    private Settings getSettings() throws DocAsCodeException {
        if ( settings == null )
        {
            SettingsUtils.merge( getProjectSettings(), getLocalSettings(), TrackableBase.GLOBAL_LEVEL );
            this.settings = projectSettings;

        }
        return settings;
    }

    public synchronized RepositorySystem getSystem()
    {
        if ( repoSys == null )
        {
            repoSys = locator.getService( RepositorySystem.class );
        }
        return repoSys;
    }

    public List<Version> getVersionRange(Artifact artifact) throws DocAsCodeException {
        List<Version> result = new ArrayList<>();
        for (RemoteRepository r : this.remoteRepositories){
            result.addAll(getVersionRange(artifact,r));
        }
        return result;
    }


    private List<Version> getVersionRange(Artifact artifact, RemoteRepository repo) throws DocAsCodeException {
        VersionRangeRequest request = new VersionRangeRequest();
        request.setRepositories(Arrays.asList(repo));
        Artifact a =
                new DefaultArtifact(artifact.getGroupId(),
                        artifact.getArtifactId(),
                        artifact.getClassifier(),
                        artifact.getExtension(),
                        "[0,)");
        request.setArtifact(a);
        try {
            VersionRangeResult result = getSystem().resolveVersionRange(
                    getSession(),
                    request);
            return result.getVersions();
        } catch (DocAsCodeException e) {
            throw new DocAsCodeException(
                    String.format("Unable to get versions list for artifact '%s'.",a),
                    e);
        } catch (VersionRangeResolutionException e) {
            log(String.format("Unable to get versions list for artifact '%s'.",a), Event.Level.WARN);
            return new ArrayList<>();
        }
    }

    private ArtifactResult resolve(Artifact artifact, RemoteRepository repo) throws DocAsCodeException {
        RepositorySystemSession session = getSession( );
        ArtifactRequest request = new ArtifactRequest();
        request.setRepositories(Arrays.asList(repo));
        request.setArtifact(artifact);
        try {
            fireEvent(new Event(this)
                    .setMessage(String.format(
                            "Resolving artifact '%s'",artifact)
                    ));
            return getSystem().resolveArtifacts(session, Arrays.asList(request)).get(0);
        } catch (ArtifactResolutionException e) {
            throw new DocAsCodeException(
                    String.format("Unable to resolve artifact '%s' : %s",
                            artifact,
                            e.getMessage()),e);
        }
    }

    public ArtifactResult resolve(Artifact artifact) throws DocAsCodeException {
        ArtifactResult result = null;
        for (RemoteRepository r : this.remoteRepositories){
            ArtifactResult ret = resolve(artifact,r);
            if (ret.isResolved()){
                result = ret;
            }
        }
        return result;
    }

    public Void deploy(List<Artifacts> artifacts, RemoteRepository repo) throws DocAsCodeException {
        RepositorySystemSession session = getSession( );
        DeployRequest request = new DeployRequest();
        request.setRepository( repo );
        for (Artifacts a : artifacts){
            List<Version> listVersion = getVersionRange(a.getMainArtifact(),repo);
            List<String> listVersionString = new ArrayList<>();
            for (Version v : listVersion){
                listVersionString.add(v.toString());
            }
            if (!(listVersionString.contains(a.getMainArtifact().getVersion()))){
                request.setArtifacts(a.getArtifacts());
                log(String.format(
                        "Deploying artifact '%s'...",a.getMainArtifact()), Event.Level.INFO);
                try {
                    getSystem().deploy(session, request);
                } catch (DeploymentException ex) {
                    throw new DocAsCodeException(
                            String.format(
                                    "Failed to deploy artifact '%s': %s",
                                    a.getMainArtifact(),
                                    ex.getMessage())
                            , ex);
                }
            } else {
                log(String.format(
                        "Artifact '%s' is already deployed.",a.getMainArtifact()), Event.Level.INFO);
            }
        }
        return null;
    }

    public Void deploy(List<Artifacts> artifacts) throws DocAsCodeException {
        for (RemoteRepository r : this.remoteRepositories){
            deploy(artifacts,r);
        }
        return null;
    }

    private RepositorySystemSession getSession() throws DocAsCodeException {
        DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
        org.eclipse.aether.repository.LocalRepository localRepo =
                new org.eclipse.aether.repository.LocalRepository(new File(
                        getSettings().getLocalRepository()
                ));
        session.setLocalRepositoryManager(
                getSystem().newLocalRepositoryManager( session, localRepo));
        session.setTransferListener(transfertListener);
        return session;
    }

    @Override
    public MvnRepository addListener(org.docascode.api.listener.EventListener listener){
        super.addListener(listener);
        transfertListener.addListener(listener);
        return this;
    }
}