/*
* Copyright 2016 Red Hat, Inc. 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 org.guvnor.common.services.project.backend.server;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.maven.project.MavenProject;
import org.apache.maven.settings.Server;
import org.apache.maven.settings.Settings;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.repository.ArtifactRepository;
import org.eclipse.aether.repository.Authentication;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;
import org.guvnor.common.services.project.backend.server.utils.POMContentHandler;
import org.guvnor.common.services.project.model.GAV;
import org.guvnor.common.services.project.model.MavenRepositoryMetadata;
import org.guvnor.common.services.project.model.MavenRepositorySource;
import org.guvnor.common.services.project.model.POM;
import org.guvnor.common.services.project.model.Project;
import org.guvnor.common.services.project.preferences.GAVPreferences;
import org.guvnor.common.services.project.service.ProjectRepositoryResolver;
import org.guvnor.common.services.shared.preferences.GuvnorPreferenceScopes;
import org.guvnor.common.services.shared.preferences.WorkbenchPreferenceScopeResolutionStrategies;
import org.jboss.errai.bus.server.annotations.Service;
import org.kie.scanner.Aether;
import org.kie.scanner.embedder.MavenEmbedder;
import org.kie.scanner.embedder.MavenProjectLoader;
import org.kie.scanner.embedder.MavenSettings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.uberfire.annotations.Customizable;
import org.uberfire.backend.server.util.Paths;
import org.uberfire.backend.vfs.Path;
import org.uberfire.preferences.shared.impl.PreferenceScopeResolutionStrategyInfo;
import org.uberfire.io.IOService;
import org.uberfire.java.nio.file.NoSuchFileException;
import static org.guvnor.common.services.project.backend.server.MavenLocalRepositoryUtils.*;
@Service
@ApplicationScoped
public class ProjectRepositoryResolverImpl
implements ProjectRepositoryResolver {
private static final Logger log = LoggerFactory.getLogger( ProjectRepositoryResolverImpl.class );
private IOService ioService;
private POMContentHandler pomContentHandler = new POMContentHandler();
private Instance<GAVPreferences> gavPreferencesProvider;
private WorkbenchPreferenceScopeResolutionStrategies scopeResolutionStrategies;
public ProjectRepositoryResolverImpl() {
}
@Inject
public ProjectRepositoryResolverImpl( final @Named("ioStrategy") IOService ioService,
final Instance<GAVPreferences> gavPreferencesProvider,
@Customizable final WorkbenchPreferenceScopeResolutionStrategies scopeResolutionStrategies ) {
this.ioService = ioService;
this.gavPreferencesProvider = gavPreferencesProvider;
this.scopeResolutionStrategies = scopeResolutionStrategies;
}
@Override
public Set<MavenRepositoryMetadata> getRemoteRepositoriesMetaData() {
final Set<MavenRepositoryMetadata> repositories = new HashSet<MavenRepositoryMetadata>();
final Aether aether = Aether.getAether();
final Map<MavenRepositorySource, Collection<RemoteRepository>> remoteRepositories = getRemoteRepositories();
//Local Repository
repositories.add( makeRepositoryMetaData( aether.getSession().getLocalRepository(),
MavenRepositorySource.LOCAL ) );
if ( remoteRepositories.isEmpty() ) {
return repositories;
}
for ( Map.Entry<MavenRepositorySource, Collection<RemoteRepository>> e : remoteRepositories.entrySet() ) {
repositories.addAll( makeRepositoriesMetaData( e.getValue(),
e.getKey() ) );
}
return repositories;
}
@Override
public Set<MavenRepositoryMetadata> getRemoteRepositoriesMetaData( final Project project ) {
if ( project == null ) {
return Collections.emptySet();
}
final Set<MavenRepositoryMetadata> repositories = new HashSet<MavenRepositoryMetadata>();
try {
//Load Project's pom.xml
final Path pomXMLPath = project.getPomXMLPath();
final org.uberfire.java.nio.file.Path nioPomXMLPath = Paths.convert( pomXMLPath );
final String pomXML = ioService.readAllString( nioPomXMLPath );
final InputStream pomStream = new ByteArrayInputStream( pomXML.getBytes( StandardCharsets.UTF_8 ) );
final MavenProject mavenProject = MavenProjectLoader.parseMavenPom( pomStream );
final Aether aether = new Aether( mavenProject );
final Map<MavenRepositorySource, Collection<RemoteRepository>> remoteRepositories = getRemoteRepositories( mavenProject );
//Local Repository
repositories.add( makeRepositoryMetaData( aether.getSession().getLocalRepository(),
MavenRepositorySource.LOCAL ) );
if ( remoteRepositories.isEmpty() ) {
return repositories;
}
for ( Map.Entry<MavenRepositorySource, Collection<RemoteRepository>> e : remoteRepositories.entrySet() ) {
repositories.addAll( makeRepositoriesMetaData( e.getValue(),
e.getKey() ) );
}
} catch ( IllegalArgumentException iae ) {
log.error( "Unable to get Remote Repositories for Project '%s'. Returning empty Collection. ",
project.getProjectName(),
iae );
} catch ( NoSuchFileException nsfe ) {
log.error( "Unable to get Remote Repositories for Project '%s'. Returning empty Collection. ",
project.getProjectName(),
nsfe );
} catch ( org.uberfire.java.nio.IOException ioe ) {
log.error( "Unable to get Remote Repositories for Project '%s'. Returning empty Collection. ",
project.getProjectName(),
ioe );
}
return repositories;
}
private Set<MavenRepositoryMetadata> makeRepositoriesMetaData( final Collection<? extends ArtifactRepository> repositories,
final MavenRepositorySource source ) {
final Set<MavenRepositoryMetadata> metadata = new HashSet<MavenRepositoryMetadata>();
for ( ArtifactRepository repository : repositories ) {
final MavenRepositoryMetadata md = makeRepositoryMetaData( repository,
source );
if ( md != null ) {
metadata.add( md );
}
}
return metadata;
}
private MavenRepositoryMetadata makeRepositoryMetaData( final ArtifactRepository repository,
final MavenRepositorySource source ) {
if ( repository instanceof LocalRepository ) {
final LocalRepository localRepository = (LocalRepository) repository;
return new MavenRepositoryMetadata( localRepository.getId(),
MavenSettings.getSettings().getLocalRepository(),
source );
} else if ( repository instanceof RemoteRepository ) {
final RemoteRepository remoteRepository = (RemoteRepository) repository;
return new MavenRepositoryMetadata( remoteRepository.getId(),
remoteRepository.getUrl(),
source );
}
return null;
}
@Override
public Set<MavenRepositoryMetadata> getRepositoriesResolvingArtifact( final GAV gav,
final MavenRepositoryMetadata... filter ) {
GAVPreferences gavPreferences = gavPreferencesProvider.get();
gavPreferences.load();
if ( gavPreferences.isConflictingGAVCheckDisabled() ) {
return Collections.EMPTY_SET;
}
final Set<MavenRepositoryMetadata> repositoriesResolvingArtifact = new HashSet<MavenRepositoryMetadata>();
try {
//Construct the Project's pom.xml
final String pomXML = pomContentHandler.toString( new POM( gav ) );
final InputStream pomStream = new ByteArrayInputStream( pomXML.getBytes( StandardCharsets.UTF_8 ) );
final MavenProject mavenProject = MavenProjectLoader.parseMavenPom( pomStream );
repositoriesResolvingArtifact.addAll( getRepositoriesResolvingArtifact( gav,
mavenProject ) );
//Filter results if necessary
if ( filter != null && filter.length > 0 ) {
repositoriesResolvingArtifact.retainAll( Arrays.asList( filter ) );
}
} catch ( IOException ioe ) {
log.error( "Unable to get Remote Repositories for Project '" + gav.toString() + "'. Returning empty Collection. ",
ioe );
}
return repositoriesResolvingArtifact;
}
@Override
public Set<MavenRepositoryMetadata> getRepositoriesResolvingArtifact( final GAV gav,
final Project project,
final MavenRepositoryMetadata... filter ) {
GAVPreferences gavPreferences = gavPreferencesProvider.get();
final PreferenceScopeResolutionStrategyInfo scopeResolutionStrategyInfo = scopeResolutionStrategies.getUserInfoFor( GuvnorPreferenceScopes.PROJECT, project.getEncodedIdentifier() );
gavPreferences.load( scopeResolutionStrategyInfo );
if ( gavPreferences.isConflictingGAVCheckDisabled() ) {
return Collections.EMPTY_SET;
}
final Set<MavenRepositoryMetadata> repositoriesResolvingArtifact = new HashSet<MavenRepositoryMetadata>();
try {
//Load Project's pom.xml
final Path pomXMLPath = project.getPomXMLPath();
final org.uberfire.java.nio.file.Path nioPomXMLPath = Paths.convert( pomXMLPath );
final String pomXML = ioService.readAllString( nioPomXMLPath );
final InputStream pomStream = new ByteArrayInputStream( pomXML.getBytes( StandardCharsets.UTF_8 ) );
final MavenProject mavenProject = MavenProjectLoader.parseMavenPom( pomStream );
repositoriesResolvingArtifact.addAll( getRepositoriesResolvingArtifact( gav,
mavenProject ) );
//Filter results if necessary
if ( filter != null && filter.length > 0 ) {
repositoriesResolvingArtifact.retainAll( Arrays.asList( filter ) );
}
} catch ( IllegalArgumentException iae ) {
log.error( "Unable to get Remote Repositories for Project '" + project.getProjectName() + "'. Returning empty Collection. ",
iae );
} catch ( NoSuchFileException nsfe ) {
log.error( "Unable to get Remote Repositories for Project '" + project.getProjectName() + "'. Returning empty Collection. ",
nsfe );
} catch ( org.uberfire.java.nio.IOException ioe ) {
log.error( "Unable to get Remote Repositories for Project '" + project.getProjectName() + "'. Returning empty Collection. ",
ioe );
}
return repositoriesResolvingArtifact;
}
@Override
public Set<MavenRepositoryMetadata> getRepositoriesResolvingArtifact( final String pomXML,
final MavenRepositoryMetadata... filter ) {
GAVPreferences gavPreferences = gavPreferencesProvider.get();
gavPreferences.load();
if ( gavPreferences.isConflictingGAVCheckDisabled() ) {
return Collections.EMPTY_SET;
}
final InputStream pomStream = new ByteArrayInputStream( pomXML.getBytes( StandardCharsets.UTF_8 ) );
final MavenProject mavenProject = MavenProjectLoader.parseMavenPom( pomStream );
final GAV gav = new GAV( mavenProject.getGroupId(),
mavenProject.getArtifactId(),
mavenProject.getVersion() );
final Set<MavenRepositoryMetadata> repositoriesResolvingArtifact = new HashSet<MavenRepositoryMetadata>();
repositoriesResolvingArtifact.addAll( getRepositoriesResolvingArtifact( gav,
mavenProject ) );
//Filter results if necessary
if ( filter != null && filter.length > 0 ) {
repositoriesResolvingArtifact.retainAll( Arrays.asList( filter ) );
}
return repositoriesResolvingArtifact;
}
private Set<MavenRepositoryMetadata> getRepositoriesResolvingArtifact( final GAV gav,
final MavenProject mavenProject ) {
ArtifactResult result = null;
ArtifactRequest artifactRequest = null;
final String artifactName = gav.toString();
final Artifact artifact = new DefaultArtifact( artifactName );
final Aether aether = new Aether( mavenProject );
final Set<MavenRepositoryMetadata> repositoriesResolvingArtifact = new HashSet<MavenRepositoryMetadata>();
final Map<MavenRepositorySource, Collection<RemoteRepository>> repositories = getRemoteRepositories( mavenProject );
//Local Repository
artifactRequest = new ArtifactRequest();
artifactRequest.setArtifact( artifact );
try {
result = aether.getSystem().resolveArtifact( aether.getSession(),
artifactRequest );
if ( result != null && result.isResolved() ) {
final MavenRepositoryMetadata artifactRepositoryMetaData = makeRepositoryMetaData( result.getRepository(),
MavenRepositorySource.LOCAL );
if ( artifactRepositoryMetaData != null ) {
repositoriesResolvingArtifact.add( artifactRepositoryMetaData );
}
}
} catch ( ArtifactResolutionException are ) {
//Ignore - this means the Artifact could not be resolved against the given RemoteRepository
}
//Remote Repositories
try {
for ( Map.Entry<MavenRepositorySource, Collection<RemoteRepository>> e : repositories.entrySet() ) {
for ( ArtifactRepository repository : e.getValue() ) {
artifactRequest = new ArtifactRequest();
artifactRequest.setArtifact( artifact );
java.nio.file.Path tempLocalRepositoryBasePath = null;
try {
// Maven always tries to resolve against LocalRepository first, which is not much use when we want to check
// if the Artifact is available on a RemoteRepository. Therefore substitute the default RepositorySystemSession
// with one that provides a LocalRepositoryManager that always uses an empty transient LocalRepository to ensure
// Maven does not resolve Artifacts locally.
artifactRequest.addRepository( (RemoteRepository) repository );
tempLocalRepositoryBasePath = getRepositoryPath( gav );
result = aether.getSystem().resolveArtifact( new MavenRepositorySystemSessionWrapper( tempLocalRepositoryBasePath.toString(),
aether.getSession() ),
artifactRequest );
if ( result != null && result.isResolved() ) {
final MavenRepositoryMetadata artifactRepositoryMetaData = makeRepositoryMetaData( result.getRepository(),
e.getKey() );
if ( artifactRepositoryMetaData != null ) {
repositoriesResolvingArtifact.add( artifactRepositoryMetaData );
}
}
} catch ( ArtifactResolutionException are ) {
//Ignore - this means the Artifact could not be resolved against the given RemoteRepository
} finally {
tearDownMavenRepository( tempLocalRepositoryBasePath );
}
}
}
} catch ( IOException ioe ) {
log.error( "Error resolving '" + gav.toString() + "' against Repositories. Returning empty Collection. ",
ioe );
}
return repositoriesResolvingArtifact;
}
private Map<MavenRepositorySource, Collection<RemoteRepository>> getRemoteRepositories() {
final Map<MavenRepositorySource, Collection<RemoteRepository>> repositories = new HashMap<MavenRepositorySource, Collection<RemoteRepository>>();
//Settings.xml Repositories
final Collection<RemoteRepository> settingsRepositories = new HashSet<RemoteRepository>( MavenSettings.getMavenRepositoryConfiguration().getRemoteRepositoriesForRequest() );
if ( settingsRepositories != null ) {
repositories.put( MavenRepositorySource.SETTINGS,
settingsRepositories );
}
return repositories;
}
private Map<MavenRepositorySource, Collection<RemoteRepository>> getRemoteRepositories( final MavenProject mavenProject ) {
//Get Local and Settings.xml Repositories
final Map<MavenRepositorySource, Collection<RemoteRepository>> repositories = new HashMap<MavenRepositorySource, Collection<RemoteRepository>>();
repositories.putAll( getRemoteRepositories() );
//Project's Repositories, includes those in setting.xml
final Collection<RemoteRepository> projectRepositories = new HashSet<RemoteRepository>( mavenProject.getRemoteProjectRepositories() );
if ( projectRepositories != null ) {
//Remove Project Repositories that are in settings.xml
final Collection<RemoteRepository> settingsRepositories = repositories.get( MavenRepositorySource.SETTINGS );
removeProjectRepositoriesThatAreInSettings( projectRepositories,
settingsRepositories );
repositories.put( MavenRepositorySource.PROJECT,
projectRepositories );
}
//Project's <distributionManagement> Repositories
final org.apache.maven.artifact.repository.ArtifactRepository distributionManagementRepository = mavenProject.getDistributionManagementArtifactRepository();
if ( distributionManagementRepository != null ) {
repositories.put( MavenRepositorySource.DISTRIBUTION_MANAGEMENT,
new HashSet<RemoteRepository>() {{
add( convertToArtifactRepository( distributionManagementRepository ) );
}} );
}
return repositories;
}
private void removeProjectRepositoriesThatAreInSettings( final Collection<RemoteRepository> projectRepositories,
final Collection<RemoteRepository> settingsRepositories ) {
final Collection<RemoteRepository> projectRepositoriesToRemove = new HashSet<RemoteRepository>();
final Iterator<RemoteRepository> projectRepositoryItr = projectRepositories.iterator();
while ( projectRepositoryItr.hasNext() ) {
final RemoteRepository projectRepository = projectRepositoryItr.next();
for ( RemoteRepository settingsRepository : settingsRepositories ) {
if ( projectRepository.getId().equals( settingsRepository.getId() ) ) {
if ( projectRepository.getUrl().equals( settingsRepository.getUrl() ) ) {
projectRepositoriesToRemove.add( projectRepository );
}
}
}
}
projectRepositories.removeAll( projectRepositoriesToRemove );
}
private RemoteRepository convertToArtifactRepository( final org.apache.maven.artifact.repository.ArtifactRepository artifactRepository ) {
final MavenEmbedder mavenEmbedder = MavenProjectLoader.newMavenEmbedder( MavenSettings.getSettings().isOffline() );
final RemoteRepository.Builder remoteRepoBuilder = new RemoteRepository.Builder( artifactRepository.getId(),
artifactRepository.getLayout().getId(),
artifactRepository.getUrl() )
.setSnapshotPolicy( new RepositoryPolicy( true,
RepositoryPolicy.UPDATE_POLICY_DAILY,
RepositoryPolicy.CHECKSUM_POLICY_WARN ) )
.setReleasePolicy( new RepositoryPolicy( true,
RepositoryPolicy.UPDATE_POLICY_ALWAYS,
RepositoryPolicy.CHECKSUM_POLICY_WARN ) );
final Settings settings = MavenSettings.getSettings();
final Server server = settings.getServer( artifactRepository.getId() );
if ( server != null ) {
final Authentication authentication = mavenEmbedder
.getMavenSession()
.getRepositorySession()
.getAuthenticationSelector()
.getAuthentication( remoteRepoBuilder.build() );
remoteRepoBuilder.setAuthentication( authentication );
}
return remoteRepoBuilder.build();
}
}