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.ArrayList;
import java.util.Arrays;
import java.util.List;

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

    private Settings settings;

    private Settings localSettings;

    private Settings projectSettings;

    private List<RemoteRepository> remoteRepositories;

    private DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();

    private RepositorySystem repoSys;

    private TransfertListener transfertListener = new TransfertListener();

    private File projectSettingsFile;

    public MvnRepository(File projectSettings){
        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 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 File getProjectSettingsFile() {
        return projectSettingsFile;
    }

    private Settings getLocalSettings() throws DocAsCodeException {
        if ( localSettings == null )
        {
            File userSettingsFile = newFile( 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 = newFile( System.getProperty( "user.home" ), ".m2", "repository" ).getAbsolutePath();
                localSettings.setLocalRepository( location );
            }
        }
        return localSettings;
    }

    public List<RemoteRepository> getRemoteRepositories(String id) throws DocAsCodeException {
        if (remoteRepositories == null){
            remoteRepositories = new ArrayList<>();
            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);
                    }
                }
            }
            for (Profile profile : profiles){
                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 authentification parameters found for server '%s'.",
                                    r.getId()), Event.Level.WARN);
                    }
                    Authentication a = authBuilder.build();
                    builder.setAuthentication(a);
                    remoteRepositories.add(builder.build());
                }
            }
        }
        return remoteRepositories;
    }

    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;
    }

    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<>();
        }
    }

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

    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, List<RemoteRepository> repos) throws DocAsCodeException {
        ArtifactResult result = null;
        for (RemoteRepository r : repos){
            ArtifactResult ret = resolve(artifact,r);
            if (ret.isResolved()){
                result = ret;
            }
        }
        return result;
    }

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

    public Void deploy(List<Artifact> artifacts, List<RemoteRepository> repos) throws DocAsCodeException {
        for (RemoteRepository r : repos){
            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;
    }

    private File newFile( String parent, String subdir, String filename )
    {
        return new File( new File( parent, subdir ), filename );
    }
}