diff --git a/maven-resolver-provider/pom.xml b/maven-resolver-provider/pom.xml new file mode 100644 index 000000000..ece5e5132 --- /dev/null +++ b/maven-resolver-provider/pom.xml @@ -0,0 +1,183 @@ + + + + + + 4.0.0 + + + org.apache.maven.resolver + maven-resolver + 1.0.4-SNAPSHOT + .. + + + maven-resolver-provider + + Maven Artifact Resolver Provider + Extensions to Maven Resolver for utilizing Maven POM and repository metadata. + + + 7 + 1.7 + 1.7 + + + + org.apache.maven + maven-model + + + org.apache.maven + maven-model-builder + + + org.apache.maven + maven-repository-metadata + + + org.apache.maven.resolver + maven-resolver-api + + + org.apache.maven.resolver + maven-resolver-spi + + + org.apache.maven.resolver + maven-resolver-util + + + org.apache.maven.resolver + maven-resolver-impl + + + org.codehaus.plexus + plexus-component-annotations + 1.5.5 + + + org.codehaus.plexus + plexus-utils + 3.0.22 + + + org.eclipse.sisu + org.eclipse.sisu.inject + 0.3.2 + + + org.eclipse.sisu + org.eclipse.sisu.plexus + 0.3.2 + + + javax.enterprise + cdi-api + + + + + org.sonatype.sisu + sisu-guice + 3.2.2 + no_aop + + + aopalliance + aopalliance + + + com.google.code.findbugs + jsr305 + + + + + org.apache.commons + commons-lang3 + 3.4 + + + + org.apache.maven.resolver + maven-resolver-connector-basic + ${parent.version} + test + + + org.apache.maven.resolver + maven-resolver-transport-wagon + ${parent.version} + test + + + org.apache.maven.wagon + wagon-file + 2.10 + test + + + junit + junit + 4.11 + test + + + + org.mockito + mockito-core + 1.9.5 + test + + + + + + + org.codehaus.plexus + plexus-component-metadata + + + maven-enforcer-plugin + + + enforce-bytecode-version + + enforce + + + + + 1.8 + + + true + + + + + + + + + + + diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/ArtifactDescriptorReaderDelegate.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/ArtifactDescriptorReaderDelegate.java new file mode 100644 index 000000000..53c04752f --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/ArtifactDescriptorReaderDelegate.java @@ -0,0 +1,153 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.maven.model.DependencyManagement; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.License; +import org.apache.maven.model.Model; +import org.apache.maven.model.Prerequisites; +import org.apache.maven.model.Repository; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.ArtifactProperties; +import org.eclipse.aether.artifact.ArtifactType; +import org.eclipse.aether.artifact.ArtifactTypeRegistry; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.artifact.DefaultArtifactType; +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.graph.Exclusion; +import org.eclipse.aether.resolution.ArtifactDescriptorResult; + +/** + * Populates Aether {@link ArtifactDescriptorResult} from Maven project {@link Model}. + * + * @since 3.2.4 + * @provisional This class is part of work in progress and can be changed or removed without notice. + */ +public class ArtifactDescriptorReaderDelegate +{ + public void populateResult( RepositorySystemSession session, ArtifactDescriptorResult result, Model model ) + { + ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry(); + + for ( Repository r : model.getRepositories() ) + { + result.addRepository( ArtifactDescriptorUtils.toRemoteRepository( r ) ); + } + + for ( org.apache.maven.model.Dependency dependency : model.getDependencies() ) + { + result.addDependency( convert( dependency, stereotypes ) ); + } + + DependencyManagement mgmt = model.getDependencyManagement(); + if ( mgmt != null ) + { + for ( org.apache.maven.model.Dependency dependency : mgmt.getDependencies() ) + { + result.addManagedDependency( convert( dependency, stereotypes ) ); + } + } + + Map properties = new LinkedHashMap<>(); + + Prerequisites prerequisites = model.getPrerequisites(); + if ( prerequisites != null ) + { + properties.put( "prerequisites.maven", prerequisites.getMaven() ); + } + + List licenses = model.getLicenses(); + properties.put( "license.count", licenses.size() ); + for ( int i = 0; i < licenses.size(); i++ ) + { + License license = licenses.get( i ); + properties.put( "license." + i + ".name", license.getName() ); + properties.put( "license." + i + ".url", license.getUrl() ); + properties.put( "license." + i + ".comments", license.getComments() ); + properties.put( "license." + i + ".distribution", license.getDistribution() ); + } + + result.setProperties( properties ); + + setArtifactProperties( result, model ); + } + + private Dependency convert( org.apache.maven.model.Dependency dependency, ArtifactTypeRegistry stereotypes ) + { + ArtifactType stereotype = stereotypes.get( dependency.getType() ); + if ( stereotype == null ) + { + stereotype = new DefaultArtifactType( dependency.getType() ); + } + + boolean system = dependency.getSystemPath() != null && dependency.getSystemPath().length() > 0; + + Map props = null; + if ( system ) + { + props = Collections.singletonMap( ArtifactProperties.LOCAL_PATH, dependency.getSystemPath() ); + } + + Artifact artifact = + new DefaultArtifact( dependency.getGroupId(), dependency.getArtifactId(), dependency.getClassifier(), null, + dependency.getVersion(), props, stereotype ); + + List exclusions = new ArrayList<>( dependency.getExclusions().size() ); + for ( org.apache.maven.model.Exclusion exclusion : dependency.getExclusions() ) + { + exclusions.add( convert( exclusion ) ); + } + + Dependency result = new Dependency( artifact, dependency.getScope(), dependency.isOptional(), exclusions ); + + return result; + } + + private Exclusion convert( org.apache.maven.model.Exclusion exclusion ) + { + return new Exclusion( exclusion.getGroupId(), exclusion.getArtifactId(), "*", "*" ); + } + + private void setArtifactProperties( ArtifactDescriptorResult result, Model model ) + { + String downloadUrl = null; + DistributionManagement distMgmt = model.getDistributionManagement(); + if ( distMgmt != null ) + { + downloadUrl = distMgmt.getDownloadUrl(); + } + if ( downloadUrl != null && downloadUrl.length() > 0 ) + { + Artifact artifact = result.getArtifact(); + Map props = new HashMap<>( artifact.getProperties() ); + props.put( ArtifactProperties.DOWNLOAD_URL, downloadUrl ); + result.setArtifact( artifact.setProperties( props ) ); + } + } +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/ArtifactDescriptorUtils.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/ArtifactDescriptorUtils.java new file mode 100644 index 000000000..7d4ede872 --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/ArtifactDescriptorUtils.java @@ -0,0 +1,81 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.apache.maven.model.Repository; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.repository.RepositoryPolicy; + +/** + * Warning: This is an internal utility class that is only public for technical reasons, it is not part + * of the public API. In particular, this class can be changed or deleted without prior notice. + * + * @author Benjamin Bentmann + */ +public class ArtifactDescriptorUtils +{ + + public static Artifact toPomArtifact( Artifact artifact ) + { + Artifact pomArtifact = artifact; + + if ( pomArtifact.getClassifier().length() > 0 || !"pom".equals( pomArtifact.getExtension() ) ) + { + pomArtifact = + new DefaultArtifact( artifact.getGroupId(), artifact.getArtifactId(), "pom", artifact.getVersion() ); + } + + return pomArtifact; + } + + public static RemoteRepository toRemoteRepository( Repository repository ) + { + RemoteRepository.Builder builder = + new RemoteRepository.Builder( repository.getId(), repository.getLayout(), repository.getUrl() ); + builder.setSnapshotPolicy( toRepositoryPolicy( repository.getSnapshots() ) ); + builder.setReleasePolicy( toRepositoryPolicy( repository.getReleases() ) ); + return builder.build(); + } + + public static RepositoryPolicy toRepositoryPolicy( org.apache.maven.model.RepositoryPolicy policy ) + { + boolean enabled = true; + String checksums = RepositoryPolicy.CHECKSUM_POLICY_WARN; + String updates = RepositoryPolicy.UPDATE_POLICY_DAILY; + + if ( policy != null ) + { + enabled = policy.isEnabled(); + if ( policy.getUpdatePolicy() != null ) + { + updates = policy.getUpdatePolicy(); + } + if ( policy.getChecksumPolicy() != null ) + { + checksums = policy.getChecksumPolicy(); + } + } + + return new RepositoryPolicy( enabled, updates, checksums ); + } + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReader.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReader.java new file mode 100644 index 000000000..1df1c12a0 --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReader.java @@ -0,0 +1,415 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.apache.commons.lang3.Validate; +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Model; +import org.apache.maven.model.Relocation; +import org.apache.maven.model.building.DefaultModelBuilderFactory; +import org.apache.maven.model.building.DefaultModelBuildingRequest; +import org.apache.maven.model.building.FileModelSource; +import org.apache.maven.model.building.ModelBuilder; +import org.apache.maven.model.building.ModelBuildingException; +import org.apache.maven.model.building.ModelBuildingRequest; +import org.apache.maven.model.building.ModelProblem; +import org.apache.maven.model.resolution.UnresolvableModelException; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.eclipse.aether.RepositoryEvent; +import org.eclipse.aether.RepositoryEvent.EventType; +import org.eclipse.aether.RepositoryException; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.RequestTrace; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.impl.ArtifactDescriptorReader; +import org.eclipse.aether.impl.ArtifactResolver; +import org.eclipse.aether.impl.RemoteRepositoryManager; +import org.eclipse.aether.impl.RepositoryEventDispatcher; +import org.eclipse.aether.impl.VersionRangeResolver; +import org.eclipse.aether.impl.VersionResolver; +import org.eclipse.aether.repository.WorkspaceReader; +import org.eclipse.aether.repository.WorkspaceRepository; +import org.eclipse.aether.resolution.ArtifactDescriptorException; +import org.eclipse.aether.resolution.ArtifactDescriptorPolicy; +import org.eclipse.aether.resolution.ArtifactDescriptorPolicyRequest; +import org.eclipse.aether.resolution.ArtifactDescriptorRequest; +import org.eclipse.aether.resolution.ArtifactDescriptorResult; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResolutionException; +import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.resolution.VersionRequest; +import org.eclipse.aether.resolution.VersionResolutionException; +import org.eclipse.aether.resolution.VersionResult; +import org.eclipse.aether.spi.locator.Service; +import org.eclipse.aether.spi.locator.ServiceLocator; +import org.eclipse.aether.spi.log.Logger; +import org.eclipse.aether.spi.log.LoggerFactory; +import org.eclipse.aether.spi.log.NullLoggerFactory; +import org.eclipse.aether.transfer.ArtifactNotFoundException; + +/** + * @author Benjamin Bentmann + */ +@Named +@Component( role = ArtifactDescriptorReader.class ) +public class DefaultArtifactDescriptorReader + implements ArtifactDescriptorReader, Service +{ + + @SuppressWarnings( "unused" ) + @Requirement( role = LoggerFactory.class ) + private Logger logger = NullLoggerFactory.LOGGER; + + @Requirement + private RemoteRepositoryManager remoteRepositoryManager; + + @Requirement + private VersionResolver versionResolver; + + @Requirement + private VersionRangeResolver versionRangeResolver; + + @Requirement + private ArtifactResolver artifactResolver; + + @Requirement + private RepositoryEventDispatcher repositoryEventDispatcher; + + @Requirement + private ModelBuilder modelBuilder; + + public DefaultArtifactDescriptorReader() + { + // enable no-arg constructor + } + + @Inject + DefaultArtifactDescriptorReader( RemoteRepositoryManager remoteRepositoryManager, VersionResolver versionResolver, + ArtifactResolver artifactResolver, ModelBuilder modelBuilder, + RepositoryEventDispatcher repositoryEventDispatcher, LoggerFactory loggerFactory ) + { + setRemoteRepositoryManager( remoteRepositoryManager ); + setVersionResolver( versionResolver ); + setArtifactResolver( artifactResolver ); + setModelBuilder( modelBuilder ); + setLoggerFactory( loggerFactory ); + setRepositoryEventDispatcher( repositoryEventDispatcher ); + } + + public void initService( ServiceLocator locator ) + { + setLoggerFactory( locator.getService( LoggerFactory.class ) ); + setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) ); + setVersionResolver( locator.getService( VersionResolver.class ) ); + setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) ); + setArtifactResolver( locator.getService( ArtifactResolver.class ) ); + setRepositoryEventDispatcher( locator.getService( RepositoryEventDispatcher.class ) ); + modelBuilder = locator.getService( ModelBuilder.class ); + if ( modelBuilder == null ) + { + setModelBuilder( new DefaultModelBuilderFactory().newInstance() ); + } + } + + public DefaultArtifactDescriptorReader setLoggerFactory( LoggerFactory loggerFactory ) + { + this.logger = NullLoggerFactory.getSafeLogger( loggerFactory, getClass() ); + return this; + } + + void setLogger( LoggerFactory loggerFactory ) + { + // plexus support + setLoggerFactory( loggerFactory ); + } + + public DefaultArtifactDescriptorReader setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager ) + { + this.remoteRepositoryManager = Validate.notNull( remoteRepositoryManager, + "remoteRepositoryManager cannot be null" ); + return this; + } + + public DefaultArtifactDescriptorReader setVersionResolver( VersionResolver versionResolver ) + { + this.versionResolver = Validate.notNull( versionResolver, "versionResolver cannot be null" ); + return this; + } + + /** @since 3.2.2 */ + public DefaultArtifactDescriptorReader setVersionRangeResolver( VersionRangeResolver versionRangeResolver ) + { + this.versionRangeResolver = Validate.notNull( versionRangeResolver, "versionRangeResolver cannot be null" ); + return this; + } + + public DefaultArtifactDescriptorReader setArtifactResolver( ArtifactResolver artifactResolver ) + { + this.artifactResolver = Validate.notNull( artifactResolver, "artifactResolver cannot be null" ); + return this; + } + + public DefaultArtifactDescriptorReader setRepositoryEventDispatcher( + RepositoryEventDispatcher repositoryEventDispatcher ) + { + this.repositoryEventDispatcher = Validate.notNull( repositoryEventDispatcher, + "repositoryEventDispatcher cannot be null" ); + return this; + } + + public DefaultArtifactDescriptorReader setModelBuilder( ModelBuilder modelBuilder ) + { + this.modelBuilder = Validate.notNull( modelBuilder, "modelBuilder cannot be null" ); + return this; + } + + public ArtifactDescriptorResult readArtifactDescriptor( RepositorySystemSession session, + ArtifactDescriptorRequest request ) + throws ArtifactDescriptorException + { + ArtifactDescriptorResult result = new ArtifactDescriptorResult( request ); + + Model model = loadPom( session, request, result ); + if ( model != null ) + { + Map config = session.getConfigProperties(); + ArtifactDescriptorReaderDelegate delegate = + (ArtifactDescriptorReaderDelegate) config.get( ArtifactDescriptorReaderDelegate.class.getName() ); + + if ( delegate == null ) + { + delegate = new ArtifactDescriptorReaderDelegate(); + } + + delegate.populateResult( session, result, model ); + } + + return result; + } + + private Model loadPom( RepositorySystemSession session, ArtifactDescriptorRequest request, + ArtifactDescriptorResult result ) + throws ArtifactDescriptorException + { + RequestTrace trace = RequestTrace.newChild( request.getTrace(), request ); + + Set visited = new LinkedHashSet<>(); + for ( Artifact a = request.getArtifact();; ) + { + Artifact pomArtifact = ArtifactDescriptorUtils.toPomArtifact( a ); + try + { + VersionRequest versionRequest = + new VersionRequest( a, request.getRepositories(), request.getRequestContext() ); + versionRequest.setTrace( trace ); + VersionResult versionResult = versionResolver.resolveVersion( session, versionRequest ); + + a = a.setVersion( versionResult.getVersion() ); + + versionRequest = + new VersionRequest( pomArtifact, request.getRepositories(), request.getRequestContext() ); + versionRequest.setTrace( trace ); + versionResult = versionResolver.resolveVersion( session, versionRequest ); + + pomArtifact = pomArtifact.setVersion( versionResult.getVersion() ); + } + catch ( VersionResolutionException e ) + { + result.addException( e ); + throw new ArtifactDescriptorException( result ); + } + + if ( !visited.add( a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getBaseVersion() ) ) + { + RepositoryException exception = + new RepositoryException( "Artifact relocations form a cycle: " + visited ); + invalidDescriptor( session, trace, a, exception ); + if ( ( getPolicy( session, a, request ) & ArtifactDescriptorPolicy.IGNORE_INVALID ) != 0 ) + { + return null; + } + result.addException( exception ); + throw new ArtifactDescriptorException( result ); + } + + ArtifactResult resolveResult; + try + { + ArtifactRequest resolveRequest = + new ArtifactRequest( pomArtifact, request.getRepositories(), request.getRequestContext() ); + resolveRequest.setTrace( trace ); + resolveResult = artifactResolver.resolveArtifact( session, resolveRequest ); + pomArtifact = resolveResult.getArtifact(); + result.setRepository( resolveResult.getRepository() ); + } + catch ( ArtifactResolutionException e ) + { + if ( e.getCause() instanceof ArtifactNotFoundException ) + { + missingDescriptor( session, trace, a, (Exception) e.getCause() ); + if ( ( getPolicy( session, a, request ) & ArtifactDescriptorPolicy.IGNORE_MISSING ) != 0 ) + { + return null; + } + } + result.addException( e ); + throw new ArtifactDescriptorException( result ); + } + + Model model; + + // hack: don't rebuild model if it was already loaded during reactor resolution + final WorkspaceReader workspace = session.getWorkspaceReader(); + if ( workspace instanceof MavenWorkspaceReader ) + { + model = ( (MavenWorkspaceReader) workspace ).findModel( pomArtifact ); + if ( model != null ) + { + return model; + } + } + + try + { + ModelBuildingRequest modelRequest = new DefaultModelBuildingRequest(); + modelRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL ); + modelRequest.setProcessPlugins( false ); + modelRequest.setTwoPhaseBuilding( false ); + modelRequest.setSystemProperties( toProperties( session.getUserProperties(), + session.getSystemProperties() ) ); + modelRequest.setModelCache( DefaultModelCache.newInstance( session ) ); + modelRequest.setModelResolver( new DefaultModelResolver( session, trace.newChild( modelRequest ), + request.getRequestContext(), artifactResolver, + versionRangeResolver, remoteRepositoryManager, + request.getRepositories() ) ); + if ( resolveResult.getRepository() instanceof WorkspaceRepository ) + { + modelRequest.setPomFile( pomArtifact.getFile() ); + } + else + { + modelRequest.setModelSource( new FileModelSource( pomArtifact.getFile() ) ); + } + + model = modelBuilder.build( modelRequest ).getEffectiveModel(); + } + catch ( ModelBuildingException e ) + { + for ( ModelProblem problem : e.getProblems() ) + { + if ( problem.getException() instanceof UnresolvableModelException ) + { + result.addException( problem.getException() ); + throw new ArtifactDescriptorException( result ); + } + } + invalidDescriptor( session, trace, a, e ); + if ( ( getPolicy( session, a, request ) & ArtifactDescriptorPolicy.IGNORE_INVALID ) != 0 ) + { + return null; + } + result.addException( e ); + throw new ArtifactDescriptorException( result ); + } + + Relocation relocation = getRelocation( model ); + + if ( relocation != null ) + { + result.addRelocation( a ); + a = + new RelocatedArtifact( a, relocation.getGroupId(), relocation.getArtifactId(), + relocation.getVersion() ); + result.setArtifact( a ); + } + else + { + return model; + } + } + } + + private Properties toProperties( Map dominant, Map recessive ) + { + Properties props = new Properties(); + if ( recessive != null ) + { + props.putAll( recessive ); + } + if ( dominant != null ) + { + props.putAll( dominant ); + } + return props; + } + + private Relocation getRelocation( Model model ) + { + Relocation relocation = null; + DistributionManagement distMgmt = model.getDistributionManagement(); + if ( distMgmt != null ) + { + relocation = distMgmt.getRelocation(); + } + return relocation; + } + + private void missingDescriptor( RepositorySystemSession session, RequestTrace trace, Artifact artifact, + Exception exception ) + { + RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DESCRIPTOR_MISSING ); + event.setTrace( trace ); + event.setArtifact( artifact ); + event.setException( exception ); + + repositoryEventDispatcher.dispatch( event.build() ); + } + + private void invalidDescriptor( RepositorySystemSession session, RequestTrace trace, Artifact artifact, + Exception exception ) + { + RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DESCRIPTOR_INVALID ); + event.setTrace( trace ); + event.setArtifact( artifact ); + event.setException( exception ); + + repositoryEventDispatcher.dispatch( event.build() ); + } + + private int getPolicy( RepositorySystemSession session, Artifact a, ArtifactDescriptorRequest request ) + { + ArtifactDescriptorPolicy policy = session.getArtifactDescriptorPolicy(); + if ( policy == null ) + { + return ArtifactDescriptorPolicy.STRICT; + } + return policy.getPolicy( session, new ArtifactDescriptorPolicyRequest( a, request.getRequestContext() ) ); + } + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelCache.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelCache.java new file mode 100644 index 000000000..f13495dfa --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelCache.java @@ -0,0 +1,119 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.apache.maven.model.building.ModelCache; +import org.eclipse.aether.RepositoryCache; +import org.eclipse.aether.RepositorySystemSession; + +/** + * A model builder cache backed by the repository system cache. + * + * @author Benjamin Bentmann + */ +class DefaultModelCache + implements ModelCache +{ + + private final RepositorySystemSession session; + + private final RepositoryCache cache; + + public static ModelCache newInstance( RepositorySystemSession session ) + { + if ( session.getCache() == null ) + { + return null; + } + else + { + return new DefaultModelCache( session ); + } + } + + private DefaultModelCache( RepositorySystemSession session ) + { + this.session = session; + this.cache = session.getCache(); + } + + public Object get( String groupId, String artifactId, String version, String tag ) + { + return cache.get( session, new Key( groupId, artifactId, version, tag ) ); + } + + public void put( String groupId, String artifactId, String version, String tag, Object data ) + { + cache.put( session, new Key( groupId, artifactId, version, tag ), data ); + } + + static class Key + { + + private final String groupId; + + private final String artifactId; + + private final String version; + + private final String tag; + + private final int hash; + + public Key( String groupId, String artifactId, String version, String tag ) + { + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; + this.tag = tag; + + int h = 17; + h = h * 31 + this.groupId.hashCode(); + h = h * 31 + this.artifactId.hashCode(); + h = h * 31 + this.version.hashCode(); + h = h * 31 + this.tag.hashCode(); + hash = h; + } + + @Override + public boolean equals( Object obj ) + { + if ( this == obj ) + { + return true; + } + if ( null == obj || !getClass().equals( obj.getClass() ) ) + { + return false; + } + + Key that = (Key) obj; + return artifactId.equals( that.artifactId ) && groupId.equals( that.groupId ) + && version.equals( that.version ) && tag.equals( that.tag ); + } + + @Override + public int hashCode() + { + return hash; + } + + } +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelResolver.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelResolver.java new file mode 100644 index 000000000..f344959f1 --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelResolver.java @@ -0,0 +1,229 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import org.apache.maven.model.Parent; +import org.apache.maven.model.Repository; +import org.apache.maven.model.building.FileModelSource; +import org.apache.maven.model.building.ModelSource; +import org.apache.maven.model.resolution.InvalidRepositoryException; +import org.apache.maven.model.resolution.ModelResolver; +import org.apache.maven.model.resolution.UnresolvableModelException; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.RequestTrace; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.impl.ArtifactResolver; +import org.eclipse.aether.impl.RemoteRepositoryManager; +import org.eclipse.aether.impl.VersionRangeResolver; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResolutionException; +import org.eclipse.aether.resolution.VersionRangeRequest; +import org.eclipse.aether.resolution.VersionRangeResolutionException; +import org.eclipse.aether.resolution.VersionRangeResult; + +/** + * A model resolver to assist building of dependency POMs. This resolver gives priority to those repositories that have + * been initially specified and repositories discovered in dependency POMs are recessively merged into the search chain. + * + * @author Benjamin Bentmann + * @see DefaultArtifactDescriptorReader + */ +class DefaultModelResolver + implements ModelResolver +{ + + private final RepositorySystemSession session; + + private final RequestTrace trace; + + private final String context; + + private List repositories; + + private final List externalRepositories; + + private final ArtifactResolver resolver; + + private final VersionRangeResolver versionRangeResolver; + + private final RemoteRepositoryManager remoteRepositoryManager; + + private final Set repositoryIds; + + public DefaultModelResolver( RepositorySystemSession session, RequestTrace trace, String context, + ArtifactResolver resolver, VersionRangeResolver versionRangeResolver, + RemoteRepositoryManager remoteRepositoryManager, List repositories ) + { + this.session = session; + this.trace = trace; + this.context = context; + this.resolver = resolver; + this.versionRangeResolver = versionRangeResolver; + this.remoteRepositoryManager = remoteRepositoryManager; + this.repositories = repositories; + List externalRepositories = new ArrayList<>(); + externalRepositories.addAll( repositories ); + this.externalRepositories = Collections.unmodifiableList( externalRepositories ); + + this.repositoryIds = new HashSet<>(); + } + + private DefaultModelResolver( DefaultModelResolver original ) + { + this.session = original.session; + this.trace = original.trace; + this.context = original.context; + this.resolver = original.resolver; + this.versionRangeResolver = original.versionRangeResolver; + this.remoteRepositoryManager = original.remoteRepositoryManager; + this.repositories = new ArrayList<>( original.repositories ); + this.externalRepositories = original.externalRepositories; + this.repositoryIds = new HashSet<>( original.repositoryIds ); + } + + @Override + public void addRepository( Repository repository ) + throws InvalidRepositoryException + { + addRepository( repository, false ); + } + + @Override + public void addRepository( final Repository repository, boolean replace ) + throws InvalidRepositoryException + { + if ( session.isIgnoreArtifactDescriptorRepositories() ) + { + return; + } + + if ( !repositoryIds.add( repository.getId() ) ) + { + if ( !replace ) + { + return; + } + + removeMatchingRepository( repositories, repository.getId() ); + } + + List newRepositories = + Collections.singletonList( ArtifactDescriptorUtils.toRemoteRepository( repository ) ); + + this.repositories = + remoteRepositoryManager.aggregateRepositories( session, repositories, newRepositories, true ); + } + + private static void removeMatchingRepository( Iterable repositories, final String id ) + { + Iterables.removeIf( repositories, new Predicate() + { + @Override + public boolean apply( RemoteRepository remoteRepository ) + { + return remoteRepository.getId().equals( id ); + } + } ); + } + + @Override + public ModelResolver newCopy() + { + return new DefaultModelResolver( this ); + } + + @Override + public ModelSource resolveModel( String groupId, String artifactId, String version ) + throws UnresolvableModelException + { + Artifact pomArtifact = new DefaultArtifact( groupId, artifactId, "", "pom", version ); + + try + { + ArtifactRequest request = new ArtifactRequest( pomArtifact, repositories, context ); + request.setTrace( trace ); + pomArtifact = resolver.resolveArtifact( session, request ).getArtifact(); + } + catch ( ArtifactResolutionException e ) + { + throw new UnresolvableModelException( e.getMessage(), groupId, artifactId, version, e ); + } + + File pomFile = pomArtifact.getFile(); + + return new FileModelSource( pomFile ); + } + + public ModelSource resolveModel( Parent parent ) + throws UnresolvableModelException + { + Artifact artifact = new DefaultArtifact( parent.getGroupId(), parent.getArtifactId(), "", "pom", + parent.getVersion() ); + + VersionRangeRequest versionRangeRequest = new VersionRangeRequest( artifact, repositories, context ); + versionRangeRequest.setTrace( trace ); + + try + { + VersionRangeResult versionRangeResult = + versionRangeResolver.resolveVersionRange( session, versionRangeRequest ); + + if ( versionRangeResult.getHighestVersion() == null ) + { + throw new UnresolvableModelException( "No versions matched the requested range '" + parent.getVersion() + + "'", parent.getGroupId(), parent.getArtifactId(), + parent.getVersion() ); + + } + + if ( versionRangeResult.getVersionConstraint() != null + && versionRangeResult.getVersionConstraint().getRange() != null + && versionRangeResult.getVersionConstraint().getRange().getUpperBound() == null ) + { + throw new UnresolvableModelException( "The requested version range '" + parent.getVersion() + + "' does not specify an upper bound", parent.getGroupId(), + parent.getArtifactId(), parent.getVersion() ); + + } + + parent.setVersion( versionRangeResult.getHighestVersion().toString() ); + } + catch ( VersionRangeResolutionException e ) + { + throw new UnresolvableModelException( e.getMessage(), parent.getGroupId(), parent.getArtifactId(), + parent.getVersion(), e ); + + } + + return resolveModel( parent.getGroupId(), parent.getArtifactId(), parent.getVersion() ); + } + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResolver.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResolver.java new file mode 100644 index 000000000..ddd3e972f --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResolver.java @@ -0,0 +1,307 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.apache.commons.lang3.Validate; +import org.apache.maven.artifact.repository.metadata.Versioning; +import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.util.IOUtil; +import org.eclipse.aether.RepositoryEvent; +import org.eclipse.aether.RepositoryEvent.EventType; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.RequestTrace; +import org.eclipse.aether.SyncContext; +import org.eclipse.aether.impl.MetadataResolver; +import org.eclipse.aether.impl.RepositoryEventDispatcher; +import org.eclipse.aether.impl.SyncContextFactory; +import org.eclipse.aether.impl.VersionRangeResolver; +import org.eclipse.aether.metadata.DefaultMetadata; +import org.eclipse.aether.metadata.Metadata; +import org.eclipse.aether.repository.ArtifactRepository; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.repository.WorkspaceReader; +import org.eclipse.aether.resolution.MetadataRequest; +import org.eclipse.aether.resolution.MetadataResult; +import org.eclipse.aether.resolution.VersionRangeRequest; +import org.eclipse.aether.resolution.VersionRangeResolutionException; +import org.eclipse.aether.resolution.VersionRangeResult; +import org.eclipse.aether.spi.locator.Service; +import org.eclipse.aether.spi.locator.ServiceLocator; +import org.eclipse.aether.spi.log.Logger; +import org.eclipse.aether.spi.log.LoggerFactory; +import org.eclipse.aether.spi.log.NullLoggerFactory; +import org.eclipse.aether.util.version.GenericVersionScheme; +import org.eclipse.aether.version.InvalidVersionSpecificationException; +import org.eclipse.aether.version.Version; +import org.eclipse.aether.version.VersionConstraint; +import org.eclipse.aether.version.VersionScheme; + +import javax.inject.Inject; +import javax.inject.Named; +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Benjamin Bentmann + */ +@Named +@Component( role = VersionRangeResolver.class ) +public class DefaultVersionRangeResolver + implements VersionRangeResolver, Service +{ + + private static final String MAVEN_METADATA_XML = "maven-metadata.xml"; + + @SuppressWarnings( "unused" ) + @Requirement( role = LoggerFactory.class ) + private Logger logger = NullLoggerFactory.LOGGER; + + @Requirement + private MetadataResolver metadataResolver; + + @Requirement + private SyncContextFactory syncContextFactory; + + @Requirement + private RepositoryEventDispatcher repositoryEventDispatcher; + + public DefaultVersionRangeResolver() + { + // enable default constructor + } + + @Inject + DefaultVersionRangeResolver( MetadataResolver metadataResolver, SyncContextFactory syncContextFactory, + RepositoryEventDispatcher repositoryEventDispatcher, LoggerFactory loggerFactory ) + { + setMetadataResolver( metadataResolver ); + setSyncContextFactory( syncContextFactory ); + setLoggerFactory( loggerFactory ); + setRepositoryEventDispatcher( repositoryEventDispatcher ); + } + + public void initService( ServiceLocator locator ) + { + setLoggerFactory( locator.getService( LoggerFactory.class ) ); + setMetadataResolver( locator.getService( MetadataResolver.class ) ); + setSyncContextFactory( locator.getService( SyncContextFactory.class ) ); + setRepositoryEventDispatcher( locator.getService( RepositoryEventDispatcher.class ) ); + } + + public DefaultVersionRangeResolver setLoggerFactory( LoggerFactory loggerFactory ) + { + this.logger = NullLoggerFactory.getSafeLogger( loggerFactory, getClass() ); + return this; + } + + void setLogger( LoggerFactory loggerFactory ) + { + // plexus support + setLoggerFactory( loggerFactory ); + } + + public DefaultVersionRangeResolver setMetadataResolver( MetadataResolver metadataResolver ) + { + this.metadataResolver = Validate.notNull( metadataResolver, "metadataResolver cannot be null" ); + return this; + } + + public DefaultVersionRangeResolver setSyncContextFactory( SyncContextFactory syncContextFactory ) + { + this.syncContextFactory = Validate.notNull( syncContextFactory, "syncContextFactory cannot be null" ); + return this; + } + + public DefaultVersionRangeResolver setRepositoryEventDispatcher( + RepositoryEventDispatcher repositoryEventDispatcher ) + { + this.repositoryEventDispatcher = Validate.notNull( repositoryEventDispatcher, + "repositoryEventDispatcher cannot be null" ); + return this; + } + + public VersionRangeResult resolveVersionRange( RepositorySystemSession session, VersionRangeRequest request ) + throws VersionRangeResolutionException + { + VersionRangeResult result = new VersionRangeResult( request ); + + VersionScheme versionScheme = new GenericVersionScheme(); + + VersionConstraint versionConstraint; + try + { + versionConstraint = versionScheme.parseVersionConstraint( request.getArtifact().getVersion() ); + } + catch ( InvalidVersionSpecificationException e ) + { + result.addException( e ); + throw new VersionRangeResolutionException( result ); + } + + result.setVersionConstraint( versionConstraint ); + + if ( versionConstraint.getRange() == null ) + { + result.addVersion( versionConstraint.getVersion() ); + } + else + { + Map versionIndex = getVersions( session, result, request ); + + List versions = new ArrayList<>(); + for ( Map.Entry v : versionIndex.entrySet() ) + { + try + { + Version ver = versionScheme.parseVersion( v.getKey() ); + if ( versionConstraint.containsVersion( ver ) ) + { + versions.add( ver ); + result.setRepository( ver, v.getValue() ); + } + } + catch ( InvalidVersionSpecificationException e ) + { + result.addException( e ); + } + } + + Collections.sort( versions ); + result.setVersions( versions ); + } + + return result; + } + + private Map getVersions( RepositorySystemSession session, VersionRangeResult result, + VersionRangeRequest request ) + { + RequestTrace trace = RequestTrace.newChild( request.getTrace(), request ); + + Map versionIndex = new HashMap<>(); + + Metadata metadata = + new DefaultMetadata( request.getArtifact().getGroupId(), request.getArtifact().getArtifactId(), + MAVEN_METADATA_XML, Metadata.Nature.RELEASE_OR_SNAPSHOT ); + + List metadataRequests = new ArrayList<>( request.getRepositories().size() ); + + metadataRequests.add( new MetadataRequest( metadata, null, request.getRequestContext() ) ); + + for ( RemoteRepository repository : request.getRepositories() ) + { + MetadataRequest metadataRequest = new MetadataRequest( metadata, repository, request.getRequestContext() ); + metadataRequest.setDeleteLocalCopyIfMissing( true ); + metadataRequest.setTrace( trace ); + metadataRequests.add( metadataRequest ); + } + + List metadataResults = metadataResolver.resolveMetadata( session, metadataRequests ); + + WorkspaceReader workspace = session.getWorkspaceReader(); + if ( workspace != null ) + { + List versions = workspace.findVersions( request.getArtifact() ); + for ( String version : versions ) + { + versionIndex.put( version, workspace.getRepository() ); + } + } + + for ( MetadataResult metadataResult : metadataResults ) + { + result.addException( metadataResult.getException() ); + + ArtifactRepository repository = metadataResult.getRequest().getRepository(); + if ( repository == null ) + { + repository = session.getLocalRepository(); + } + + Versioning versioning = readVersions( session, trace, metadataResult.getMetadata(), repository, result ); + for ( String version : versioning.getVersions() ) + { + if ( !versionIndex.containsKey( version ) ) + { + versionIndex.put( version, repository ); + } + } + } + + return versionIndex; + } + + private Versioning readVersions( RepositorySystemSession session, RequestTrace trace, Metadata metadata, + ArtifactRepository repository, VersionRangeResult result ) + { + Versioning versioning = null; + + FileInputStream fis = null; + try + { + if ( metadata != null ) + { + + try ( SyncContext syncContext = syncContextFactory.newInstance( session, true ) ) + { + syncContext.acquire( null, Collections.singleton( metadata ) ); + + if ( metadata.getFile() != null && metadata.getFile().exists() ) + { + fis = new FileInputStream( metadata.getFile() ); + org.apache.maven.artifact.repository.metadata.Metadata m = + new MetadataXpp3Reader().read( fis, false ); + versioning = m.getVersioning(); + } + } + } + } + catch ( Exception e ) + { + invalidMetadata( session, trace, metadata, repository, e ); + result.addException( e ); + } + finally + { + IOUtil.close( fis ); + } + + return ( versioning != null ) ? versioning : new Versioning(); + } + + private void invalidMetadata( RepositorySystemSession session, RequestTrace trace, Metadata metadata, + ArtifactRepository repository, Exception exception ) + { + RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_INVALID ); + event.setTrace( trace ); + event.setMetadata( metadata ); + event.setException( exception ); + event.setRepository( repository ); + + repositoryEventDispatcher.dispatch( event.build() ); + } + +} \ No newline at end of file diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionResolver.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionResolver.java new file mode 100644 index 000000000..2b0890b15 --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionResolver.java @@ -0,0 +1,617 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.apache.commons.lang3.Validate; +import org.apache.maven.artifact.repository.metadata.Snapshot; +import org.apache.maven.artifact.repository.metadata.SnapshotVersion; +import org.apache.maven.artifact.repository.metadata.Versioning; +import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.StringUtils; +import org.eclipse.aether.RepositoryCache; +import org.eclipse.aether.RepositoryEvent; +import org.eclipse.aether.RepositoryEvent.EventType; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.RequestTrace; +import org.eclipse.aether.SyncContext; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.impl.MetadataResolver; +import org.eclipse.aether.impl.RepositoryEventDispatcher; +import org.eclipse.aether.impl.SyncContextFactory; +import org.eclipse.aether.impl.VersionResolver; +import org.eclipse.aether.internal.impl.CacheUtils; +import org.eclipse.aether.metadata.DefaultMetadata; +import org.eclipse.aether.metadata.Metadata; +import org.eclipse.aether.repository.ArtifactRepository; +import org.eclipse.aether.repository.LocalRepository; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.repository.WorkspaceReader; +import org.eclipse.aether.repository.WorkspaceRepository; +import org.eclipse.aether.resolution.MetadataRequest; +import org.eclipse.aether.resolution.MetadataResult; +import org.eclipse.aether.resolution.VersionRequest; +import org.eclipse.aether.resolution.VersionResolutionException; +import org.eclipse.aether.resolution.VersionResult; +import org.eclipse.aether.spi.locator.Service; +import org.eclipse.aether.spi.locator.ServiceLocator; +import org.eclipse.aether.spi.log.Logger; +import org.eclipse.aether.spi.log.LoggerFactory; +import org.eclipse.aether.spi.log.NullLoggerFactory; +import org.eclipse.aether.util.ConfigUtils; + +import javax.inject.Inject; +import javax.inject.Named; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Benjamin Bentmann + */ +@Named +@Component( role = VersionResolver.class ) +public class DefaultVersionResolver + implements VersionResolver, Service +{ + + private static final String MAVEN_METADATA_XML = "maven-metadata.xml"; + + private static final String RELEASE = "RELEASE"; + + private static final String LATEST = "LATEST"; + + private static final String SNAPSHOT = "SNAPSHOT"; + + @SuppressWarnings( "unused" ) + @Requirement( role = LoggerFactory.class ) + private Logger logger = NullLoggerFactory.LOGGER; + + @Requirement + private MetadataResolver metadataResolver; + + @Requirement + private SyncContextFactory syncContextFactory; + + @Requirement + private RepositoryEventDispatcher repositoryEventDispatcher; + + public DefaultVersionResolver() + { + // enable no-arg constructor + } + + @Inject + DefaultVersionResolver( MetadataResolver metadataResolver, SyncContextFactory syncContextFactory, + RepositoryEventDispatcher repositoryEventDispatcher, LoggerFactory loggerFactory ) + { + setMetadataResolver( metadataResolver ); + setSyncContextFactory( syncContextFactory ); + setLoggerFactory( loggerFactory ); + setRepositoryEventDispatcher( repositoryEventDispatcher ); + } + + public void initService( ServiceLocator locator ) + { + setLoggerFactory( locator.getService( LoggerFactory.class ) ); + setMetadataResolver( locator.getService( MetadataResolver.class ) ); + setSyncContextFactory( locator.getService( SyncContextFactory.class ) ); + setRepositoryEventDispatcher( locator.getService( RepositoryEventDispatcher.class ) ); + } + + public DefaultVersionResolver setLoggerFactory( LoggerFactory loggerFactory ) + { + this.logger = NullLoggerFactory.getSafeLogger( loggerFactory, getClass() ); + return this; + } + + void setLogger( LoggerFactory loggerFactory ) + { + // plexus support + setLoggerFactory( loggerFactory ); + } + + public DefaultVersionResolver setMetadataResolver( MetadataResolver metadataResolver ) + { + this.metadataResolver = Validate.notNull( metadataResolver, "metadataResolver cannot be null" ); + return this; + } + + public DefaultVersionResolver setSyncContextFactory( SyncContextFactory syncContextFactory ) + { + this.syncContextFactory = Validate.notNull( syncContextFactory, "syncContextFactory cannot be null" ); + return this; + } + + public DefaultVersionResolver setRepositoryEventDispatcher( RepositoryEventDispatcher repositoryEventDispatcher ) + { + this.repositoryEventDispatcher = Validate.notNull( repositoryEventDispatcher, + "repositoryEventDispatcher cannot be null" ); + return this; + } + + public VersionResult resolveVersion( RepositorySystemSession session, VersionRequest request ) + throws VersionResolutionException + { + RequestTrace trace = RequestTrace.newChild( request.getTrace(), request ); + + Artifact artifact = request.getArtifact(); + + String version = artifact.getVersion(); + + VersionResult result = new VersionResult( request ); + + Key cacheKey = null; + RepositoryCache cache = session.getCache(); + if ( cache != null && !ConfigUtils.getBoolean( session, false, "aether.versionResolver.noCache" ) ) + { + cacheKey = new Key( session, request ); + + Object obj = cache.get( session, cacheKey ); + if ( obj instanceof Record ) + { + Record record = (Record) obj; + result.setVersion( record.version ); + result.setRepository( + CacheUtils.getRepository( session, request.getRepositories(), record.repoClass, record.repoId ) ); + return result; + } + } + + Metadata metadata; + + if ( RELEASE.equals( version ) ) + { + metadata = new DefaultMetadata( artifact.getGroupId(), artifact.getArtifactId(), MAVEN_METADATA_XML, + Metadata.Nature.RELEASE ); + } + else if ( LATEST.equals( version ) ) + { + metadata = new DefaultMetadata( artifact.getGroupId(), artifact.getArtifactId(), MAVEN_METADATA_XML, + Metadata.Nature.RELEASE_OR_SNAPSHOT ); + } + else if ( version.endsWith( SNAPSHOT ) ) + { + WorkspaceReader workspace = session.getWorkspaceReader(); + if ( workspace != null && workspace.findVersions( artifact ).contains( version ) ) + { + metadata = null; + result.setRepository( workspace.getRepository() ); + } + else + { + metadata = + new DefaultMetadata( artifact.getGroupId(), artifact.getArtifactId(), version, MAVEN_METADATA_XML, + Metadata.Nature.SNAPSHOT ); + } + } + else + { + metadata = null; + } + + if ( metadata == null ) + { + result.setVersion( version ); + } + else + { + List metadataReqs = new ArrayList<>( request.getRepositories().size() ); + + metadataReqs.add( new MetadataRequest( metadata, null, request.getRequestContext() ) ); + + for ( RemoteRepository repository : request.getRepositories() ) + { + MetadataRequest metadataRequest = + new MetadataRequest( metadata, repository, request.getRequestContext() ); + metadataRequest.setDeleteLocalCopyIfMissing( true ); + metadataRequest.setFavorLocalRepository( true ); + metadataRequest.setTrace( trace ); + metadataReqs.add( metadataRequest ); + } + + List metadataResults = metadataResolver.resolveMetadata( session, metadataReqs ); + + Map infos = new HashMap<>(); + + for ( MetadataResult metadataResult : metadataResults ) + { + result.addException( metadataResult.getException() ); + + ArtifactRepository repository = metadataResult.getRequest().getRepository(); + if ( repository == null ) + { + repository = session.getLocalRepository(); + } + + Versioning v = readVersions( session, trace, metadataResult.getMetadata(), repository, result ); + merge( artifact, infos, v, repository ); + } + + if ( RELEASE.equals( version ) ) + { + resolve( result, infos, RELEASE ); + } + else if ( LATEST.equals( version ) ) + { + if ( !resolve( result, infos, LATEST ) ) + { + resolve( result, infos, RELEASE ); + } + + if ( result.getVersion() != null && result.getVersion().endsWith( SNAPSHOT ) ) + { + VersionRequest subRequest = new VersionRequest(); + subRequest.setArtifact( artifact.setVersion( result.getVersion() ) ); + if ( result.getRepository() instanceof RemoteRepository ) + { + RemoteRepository r = (RemoteRepository) result.getRepository(); + subRequest.setRepositories( Collections.singletonList( r ) ); + } + else + { + subRequest.setRepositories( request.getRepositories() ); + } + VersionResult subResult = resolveVersion( session, subRequest ); + result.setVersion( subResult.getVersion() ); + result.setRepository( subResult.getRepository() ); + for ( Exception exception : subResult.getExceptions() ) + { + result.addException( exception ); + } + } + } + else + { + String key = SNAPSHOT + getKey( artifact.getClassifier(), artifact.getExtension() ); + merge( infos, SNAPSHOT, key ); + if ( !resolve( result, infos, key ) ) + { + result.setVersion( version ); + } + } + + if ( StringUtils.isEmpty( result.getVersion() ) ) + { + throw new VersionResolutionException( result ); + } + } + + if ( cacheKey != null && metadata != null && isSafelyCacheable( session, artifact ) ) + { + cache.put( session, cacheKey, new Record( result.getVersion(), result.getRepository() ) ); + } + + return result; + } + + private boolean resolve( VersionResult result, Map infos, String key ) + { + VersionInfo info = infos.get( key ); + if ( info != null ) + { + result.setVersion( info.version ); + result.setRepository( info.repository ); + } + return info != null; + } + + private Versioning readVersions( RepositorySystemSession session, RequestTrace trace, Metadata metadata, + ArtifactRepository repository, VersionResult result ) + { + Versioning versioning = null; + + FileInputStream fis = null; + try + { + if ( metadata != null ) + { + + try ( SyncContext syncContext = syncContextFactory.newInstance( session, true ) ) + { + syncContext.acquire( null, Collections.singleton( metadata ) ); + + if ( metadata.getFile() != null && metadata.getFile().exists() ) + { + fis = new FileInputStream( metadata.getFile() ); + org.apache.maven.artifact.repository.metadata.Metadata m = + new MetadataXpp3Reader().read( fis, false ); + versioning = m.getVersioning(); + + /* + * NOTE: Users occasionally misuse the id "local" for remote repos which screws up the metadata + * of the local repository. This is especially troublesome during snapshot resolution so we try + * to handle that gracefully. + */ + if ( versioning != null && repository instanceof LocalRepository ) + { + if ( versioning.getSnapshot() != null && versioning.getSnapshot().getBuildNumber() > 0 ) + { + Versioning repaired = new Versioning(); + repaired.setLastUpdated( versioning.getLastUpdated() ); + Snapshot snapshot = new Snapshot(); + snapshot.setLocalCopy( true ); + repaired.setSnapshot( snapshot ); + versioning = repaired; + + throw new IOException( "Snapshot information corrupted with remote repository data" + + ", please verify that no remote repository uses the id '" + + repository.getId() + "'" ); + } + } + } + } + } + } + catch ( Exception e ) + { + invalidMetadata( session, trace, metadata, repository, e ); + result.addException( e ); + } + finally + { + IOUtil.close( fis ); + } + + return ( versioning != null ) ? versioning : new Versioning(); + } + + private void invalidMetadata( RepositorySystemSession session, RequestTrace trace, Metadata metadata, + ArtifactRepository repository, Exception exception ) + { + RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_INVALID ); + event.setTrace( trace ); + event.setMetadata( metadata ); + event.setException( exception ); + event.setRepository( repository ); + + repositoryEventDispatcher.dispatch( event.build() ); + } + + private void merge( Artifact artifact, Map infos, Versioning versioning, + ArtifactRepository repository ) + { + if ( StringUtils.isNotEmpty( versioning.getRelease() ) ) + { + merge( RELEASE, infos, versioning.getLastUpdated(), versioning.getRelease(), repository ); + } + + if ( StringUtils.isNotEmpty( versioning.getLatest() ) ) + { + merge( LATEST, infos, versioning.getLastUpdated(), versioning.getLatest(), repository ); + } + + for ( SnapshotVersion sv : versioning.getSnapshotVersions() ) + { + if ( StringUtils.isNotEmpty( sv.getVersion() ) ) + { + String key = getKey( sv.getClassifier(), sv.getExtension() ); + merge( SNAPSHOT + key, infos, sv.getUpdated(), sv.getVersion(), repository ); + } + } + + Snapshot snapshot = versioning.getSnapshot(); + if ( snapshot != null && versioning.getSnapshotVersions().isEmpty() ) + { + String version = artifact.getVersion(); + if ( snapshot.getTimestamp() != null && snapshot.getBuildNumber() > 0 ) + { + String qualifier = snapshot.getTimestamp() + '-' + snapshot.getBuildNumber(); + version = version.substring( 0, version.length() - SNAPSHOT.length() ) + qualifier; + } + merge( SNAPSHOT, infos, versioning.getLastUpdated(), version, repository ); + } + } + + private void merge( String key, Map infos, String timestamp, String version, + ArtifactRepository repository ) + { + VersionInfo info = infos.get( key ); + if ( info == null ) + { + info = new VersionInfo( timestamp, version, repository ); + infos.put( key, info ); + } + else if ( info.isOutdated( timestamp ) ) + { + info.version = version; + info.repository = repository; + info.timestamp = timestamp; + } + } + + private void merge( Map infos, String srcKey, String dstKey ) + { + VersionInfo srcInfo = infos.get( srcKey ); + VersionInfo dstInfo = infos.get( dstKey ); + + if ( dstInfo == null || ( srcInfo != null && dstInfo.isOutdated( srcInfo.timestamp ) + && srcInfo.repository != dstInfo.repository ) ) + { + infos.put( dstKey, srcInfo ); + } + } + + private String getKey( String classifier, String extension ) + { + return StringUtils.clean( classifier ) + ':' + StringUtils.clean( extension ); + } + + private boolean isSafelyCacheable( RepositorySystemSession session, Artifact artifact ) + { + /* + * The workspace/reactor is in flux so we better not assume definitive information for any of its + * artifacts/projects. + */ + + WorkspaceReader workspace = session.getWorkspaceReader(); + if ( workspace == null ) + { + return true; + } + + Artifact pomArtifact = ArtifactDescriptorUtils.toPomArtifact( artifact ); + + return workspace.findArtifact( pomArtifact ) == null; + } + + private static class VersionInfo + { + + String timestamp; + + String version; + + ArtifactRepository repository; + + public VersionInfo( String timestamp, String version, ArtifactRepository repository ) + { + this.timestamp = ( timestamp != null ) ? timestamp : ""; + this.version = version; + this.repository = repository; + } + + public boolean isOutdated( String timestamp ) + { + return timestamp != null && timestamp.compareTo( this.timestamp ) > 0; + } + + } + + private static class Key + { + + private final String groupId; + + private final String artifactId; + + private final String classifier; + + private final String extension; + + private final String version; + + private final String context; + + private final File localRepo; + + private final WorkspaceRepository workspace; + + private final List repositories; + + private final int hashCode; + + public Key( RepositorySystemSession session, VersionRequest request ) + { + Artifact artifact = request.getArtifact(); + groupId = artifact.getGroupId(); + artifactId = artifact.getArtifactId(); + classifier = artifact.getClassifier(); + extension = artifact.getExtension(); + version = artifact.getVersion(); + localRepo = session.getLocalRepository().getBasedir(); + workspace = CacheUtils.getWorkspace( session ); + repositories = new ArrayList<>( request.getRepositories().size() ); + boolean repoMan = false; + for ( RemoteRepository repository : request.getRepositories() ) + { + if ( repository.isRepositoryManager() ) + { + repoMan = true; + repositories.addAll( repository.getMirroredRepositories() ); + } + else + { + repositories.add( repository ); + } + } + context = repoMan ? request.getRequestContext() : ""; + + int hash = 17; + hash = hash * 31 + groupId.hashCode(); + hash = hash * 31 + artifactId.hashCode(); + hash = hash * 31 + classifier.hashCode(); + hash = hash * 31 + extension.hashCode(); + hash = hash * 31 + version.hashCode(); + hash = hash * 31 + localRepo.hashCode(); + hash = hash * 31 + CacheUtils.repositoriesHashCode( repositories ); + hashCode = hash; + } + + @Override + public boolean equals( Object obj ) + { + if ( obj == this ) + { + return true; + } + else if ( obj == null || !getClass().equals( obj.getClass() ) ) + { + return false; + } + + Key that = (Key) obj; + return artifactId.equals( that.artifactId ) && groupId.equals( that.groupId ) && classifier.equals( + that.classifier ) && extension.equals( that.extension ) && version.equals( that.version ) + && context.equals( that.context ) && localRepo.equals( that.localRepo ) + && CacheUtils.eq( workspace, that.workspace ) + && CacheUtils.repositoriesEquals( repositories, that.repositories ); + } + + @Override + public int hashCode() + { + return hashCode; + } + + } + + private static class Record + { + final String version; + + final String repoId; + + final Class repoClass; + + public Record( String version, ArtifactRepository repository ) + { + this.version = version; + if ( repository != null ) + { + repoId = repository.getId(); + repoClass = repository.getClass(); + } + else + { + repoId = null; + repoClass = null; + } + } + } + +} \ No newline at end of file diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/LocalSnapshotMetadata.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/LocalSnapshotMetadata.java new file mode 100644 index 000000000..a5720109b --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/LocalSnapshotMetadata.java @@ -0,0 +1,163 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.maven.artifact.repository.metadata.Metadata; +import org.apache.maven.artifact.repository.metadata.Snapshot; +import org.apache.maven.artifact.repository.metadata.SnapshotVersion; +import org.apache.maven.artifact.repository.metadata.Versioning; +import org.eclipse.aether.artifact.Artifact; + +/** + * @author Benjamin Bentmann + */ +final class LocalSnapshotMetadata + extends MavenMetadata +{ + + private final Collection artifacts = new ArrayList<>(); + + private final boolean legacyFormat; + + public LocalSnapshotMetadata( Artifact artifact, boolean legacyFormat ) + { + super( createMetadata( artifact, legacyFormat ), null ); + this.legacyFormat = legacyFormat; + } + + public LocalSnapshotMetadata( Metadata metadata, File file, boolean legacyFormat ) + { + super( metadata, file ); + this.legacyFormat = legacyFormat; + } + + private static Metadata createMetadata( Artifact artifact, boolean legacyFormat ) + { + Snapshot snapshot = new Snapshot(); + snapshot.setLocalCopy( true ); + Versioning versioning = new Versioning(); + versioning.setSnapshot( snapshot ); + + Metadata metadata = new Metadata(); + metadata.setVersioning( versioning ); + metadata.setGroupId( artifact.getGroupId() ); + metadata.setArtifactId( artifact.getArtifactId() ); + metadata.setVersion( artifact.getBaseVersion() ); + + if ( !legacyFormat ) + { + metadata.setModelVersion( "1.1.0" ); + } + + return metadata; + } + + public void bind( Artifact artifact ) + { + artifacts.add( artifact ); + } + + public MavenMetadata setFile( File file ) + { + return new LocalSnapshotMetadata( metadata, file, legacyFormat ); + } + + public Object getKey() + { + return getGroupId() + ':' + getArtifactId() + ':' + getVersion(); + } + + public static Object getKey( Artifact artifact ) + { + return artifact.getGroupId() + ':' + artifact.getArtifactId() + ':' + artifact.getBaseVersion(); + } + + @Override + protected void merge( Metadata recessive ) + { + metadata.getVersioning().updateTimestamp(); + + if ( !legacyFormat ) + { + String lastUpdated = metadata.getVersioning().getLastUpdated(); + + Map versions = new LinkedHashMap<>(); + + for ( Artifact artifact : artifacts ) + { + SnapshotVersion sv = new SnapshotVersion(); + sv.setClassifier( artifact.getClassifier() ); + sv.setExtension( artifact.getExtension() ); + sv.setVersion( getVersion() ); + sv.setUpdated( lastUpdated ); + versions.put( getKey( sv.getClassifier(), sv.getExtension() ), sv ); + } + + Versioning versioning = recessive.getVersioning(); + if ( versioning != null ) + { + for ( SnapshotVersion sv : versioning.getSnapshotVersions() ) + { + String key = getKey( sv.getClassifier(), sv.getExtension() ); + if ( !versions.containsKey( key ) ) + { + versions.put( key, sv ); + } + } + } + + metadata.getVersioning().setSnapshotVersions( new ArrayList<>( versions.values() ) ); + } + + artifacts.clear(); + } + + private String getKey( String classifier, String extension ) + { + return classifier + ':' + extension; + } + + public String getGroupId() + { + return metadata.getGroupId(); + } + + public String getArtifactId() + { + return metadata.getArtifactId(); + } + + public String getVersion() + { + return metadata.getVersion(); + } + + public Nature getNature() + { + return Nature.SNAPSHOT; + } + +} \ No newline at end of file diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/LocalSnapshotMetadataGenerator.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/LocalSnapshotMetadataGenerator.java new file mode 100644 index 000000000..072ba660e --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/LocalSnapshotMetadataGenerator.java @@ -0,0 +1,82 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.impl.MetadataGenerator; +import org.eclipse.aether.installation.InstallRequest; +import org.eclipse.aether.metadata.Metadata; +import org.eclipse.aether.util.ConfigUtils; + +/** + * @author Benjamin Bentmann + */ +class LocalSnapshotMetadataGenerator + implements MetadataGenerator +{ + + private Map snapshots; + + private final boolean legacyFormat; + + public LocalSnapshotMetadataGenerator( RepositorySystemSession session, InstallRequest request ) + { + legacyFormat = ConfigUtils.getBoolean( session.getConfigProperties(), false, "maven.metadata.legacy" ); + + snapshots = new LinkedHashMap<>(); + } + + public Collection prepare( Collection artifacts ) + { + for ( Artifact artifact : artifacts ) + { + if ( artifact.isSnapshot() ) + { + Object key = LocalSnapshotMetadata.getKey( artifact ); + LocalSnapshotMetadata snapshotMetadata = snapshots.get( key ); + if ( snapshotMetadata == null ) + { + snapshotMetadata = new LocalSnapshotMetadata( artifact, legacyFormat ); + snapshots.put( key, snapshotMetadata ); + } + snapshotMetadata.bind( artifact ); + } + } + + return Collections.emptyList(); + } + + public Artifact transformArtifact( Artifact artifact ) + { + return artifact; + } + + public Collection finish( Collection artifacts ) + { + return snapshots.values(); + } + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenAetherModule.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenAetherModule.java new file mode 100644 index 000000000..41e98aaea --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenAetherModule.java @@ -0,0 +1,80 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.model.building.DefaultModelBuilderFactory; +import org.apache.maven.model.building.ModelBuilder; +import org.eclipse.aether.impl.AetherModule; +import org.eclipse.aether.impl.ArtifactDescriptorReader; +import org.eclipse.aether.impl.MetadataGeneratorFactory; +import org.eclipse.aether.impl.VersionRangeResolver; +import org.eclipse.aether.impl.VersionResolver; + +import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import com.google.inject.name.Names; + +/** + * @deprecated As of Maven Resolver 1.0.3, please use class {@link MavenResolverModule}. + */ +@Deprecated +public final class MavenAetherModule + extends AbstractModule +{ + + @Override + protected void configure() + { + install( new AetherModule() ); + bind( ArtifactDescriptorReader.class ) // + .to( DefaultArtifactDescriptorReader.class ).in( Singleton.class ); + bind( VersionResolver.class ) // + .to( DefaultVersionResolver.class ).in( Singleton.class ); + bind( VersionRangeResolver.class ) // + .to( DefaultVersionRangeResolver.class ).in( Singleton.class ); + bind( MetadataGeneratorFactory.class ).annotatedWith( Names.named( "snapshot" ) ) // + .to( SnapshotMetadataGeneratorFactory.class ).in( Singleton.class ); + bind( MetadataGeneratorFactory.class ).annotatedWith( Names.named( "versions" ) ) // + .to( VersionsMetadataGeneratorFactory.class ).in( Singleton.class ); + bind( ModelBuilder.class ) // + .toInstance( new DefaultModelBuilderFactory().newInstance() ); + } + + @Provides + @Singleton + Set provideMetadataGeneratorFactories( @Named( "snapshot" ) + MetadataGeneratorFactory snapshot, + @Named( "versions" ) + MetadataGeneratorFactory versions ) + { + Set factories = new HashSet<>(); + factories.add( snapshot ); + factories.add( versions ); + return Collections.unmodifiableSet( factories ); + } + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenMetadata.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenMetadata.java new file mode 100644 index 000000000..aef44f6bb --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenMetadata.java @@ -0,0 +1,137 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.apache.maven.artifact.repository.metadata.Metadata; +import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; +import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.eclipse.aether.RepositoryException; +import org.eclipse.aether.metadata.AbstractMetadata; +import org.eclipse.aether.metadata.MergeableMetadata; + +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.util.Collections; +import java.util.Map; + +/** + * @author Benjamin Bentmann + */ +abstract class MavenMetadata + extends AbstractMetadata + implements MergeableMetadata +{ + + static final String MAVEN_METADATA_XML = "maven-metadata.xml"; + + private final File file; + + protected Metadata metadata; + + private boolean merged; + + protected MavenMetadata( Metadata metadata, File file ) + { + this.metadata = metadata; + this.file = file; + } + + public String getType() + { + return MAVEN_METADATA_XML; + } + + public File getFile() + { + return file; + } + + public void merge( File existing, File result ) + throws RepositoryException + { + Metadata recessive = read( existing ); + + merge( recessive ); + + write( result, metadata ); + + merged = true; + } + + public boolean isMerged() + { + return merged; + } + + protected abstract void merge( Metadata recessive ); + + static Metadata read( File metadataFile ) + throws RepositoryException + { + if ( metadataFile.length() <= 0 ) + { + return new Metadata(); + } + + try ( Reader reader = ReaderFactory.newXmlReader( metadataFile ) ) + { + return new MetadataXpp3Reader().read( reader, false ); + } + catch ( IOException e ) + { + throw new RepositoryException( "Could not read metadata " + metadataFile + ": " + e.getMessage(), e ); + } + catch ( XmlPullParserException e ) + { + throw new RepositoryException( "Could not parse metadata " + metadataFile + ": " + e.getMessage(), e ); + } + } + + private void write( File metadataFile, Metadata metadata ) + throws RepositoryException + { + metadataFile.getParentFile().mkdirs(); + try ( Writer writer = WriterFactory.newXmlWriter( metadataFile ) ) + { + new MetadataXpp3Writer().write( writer, metadata ); + } + catch ( IOException e ) + { + throw new RepositoryException( "Could not write metadata " + metadataFile + ": " + e.getMessage(), e ); + } + } + + public Map getProperties() + { + return Collections.emptyMap(); + } + + @Override + public org.eclipse.aether.metadata.Metadata setProperties( Map properties ) + { + return this; + } + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenRepositorySystemUtils.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenRepositorySystemUtils.java new file mode 100644 index 000000000..1b11cb34a --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenRepositorySystemUtils.java @@ -0,0 +1,145 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.util.Properties; + +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.artifact.DefaultArtifactType; +import org.eclipse.aether.collection.DependencyGraphTransformer; +import org.eclipse.aether.collection.DependencyManager; +import org.eclipse.aether.collection.DependencySelector; +import org.eclipse.aether.collection.DependencyTraverser; +import org.eclipse.aether.impl.ArtifactDescriptorReader; +import org.eclipse.aether.impl.DefaultServiceLocator; +import org.eclipse.aether.impl.MetadataGeneratorFactory; +import org.eclipse.aether.impl.VersionRangeResolver; +import org.eclipse.aether.impl.VersionResolver; +import org.eclipse.aether.util.artifact.DefaultArtifactTypeRegistry; +import org.eclipse.aether.util.graph.manager.ClassicDependencyManager; +import org.eclipse.aether.util.graph.selector.AndDependencySelector; +import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector; +import org.eclipse.aether.util.graph.selector.OptionalDependencySelector; +import org.eclipse.aether.util.graph.selector.ScopeDependencySelector; +import org.eclipse.aether.util.graph.transformer.ChainedDependencyGraphTransformer; +import org.eclipse.aether.util.graph.transformer.ConflictResolver; +import org.eclipse.aether.util.graph.transformer.JavaDependencyContextRefiner; +import org.eclipse.aether.util.graph.transformer.JavaScopeDeriver; +import org.eclipse.aether.util.graph.transformer.JavaScopeSelector; +import org.eclipse.aether.util.graph.transformer.NearestVersionSelector; +import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector; +import org.eclipse.aether.util.graph.traverser.FatArtifactTraverser; +import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy; + +/** + * A utility class to assist in setting up a Maven-like repository system. Note: This component is meant to + * assist those clients that employ the repository system outside of an IoC container, Maven plugins should instead + * always use regular dependency injection to acquire the repository system. + * + * @author Benjamin Bentmann + */ +public final class MavenRepositorySystemUtils +{ + + private MavenRepositorySystemUtils() + { + // hide constructor + } + + /** + * Creates a new service locator that already knows about all service implementations included in this library. To + * acquire a complete repository system, clients need to add some repository connectors for remote transfers. + * + * @return The new service locator, never {@code null}. + */ + public static DefaultServiceLocator newServiceLocator() + { + DefaultServiceLocator locator = new DefaultServiceLocator(); + locator.addService( ArtifactDescriptorReader.class, DefaultArtifactDescriptorReader.class ); + locator.addService( VersionResolver.class, DefaultVersionResolver.class ); + locator.addService( VersionRangeResolver.class, DefaultVersionRangeResolver.class ); + locator.addService( MetadataGeneratorFactory.class, SnapshotMetadataGeneratorFactory.class ); + locator.addService( MetadataGeneratorFactory.class, VersionsMetadataGeneratorFactory.class ); + return locator; + } + + /** + * Creates a new Maven-like repository system session by initializing the session with values typical for + * Maven-based resolution. In more detail, this method configures settings relevant for the processing of dependency + * graphs, most other settings remain at their generic default value. Use the various setters to further configure + * the session with authentication, mirror, proxy and other information required for your environment. + * + * @return The new repository system session, never {@code null}. + */ + public static DefaultRepositorySystemSession newSession() + { + DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(); + + DependencyTraverser depTraverser = new FatArtifactTraverser(); + session.setDependencyTraverser( depTraverser ); + + DependencyManager depManager = new ClassicDependencyManager(); + session.setDependencyManager( depManager ); + + DependencySelector depFilter = + new AndDependencySelector( new ScopeDependencySelector( "test", "provided" ), + new OptionalDependencySelector(), new ExclusionDependencySelector() ); + session.setDependencySelector( depFilter ); + + DependencyGraphTransformer transformer = + new ConflictResolver( new NearestVersionSelector(), new JavaScopeSelector(), + new SimpleOptionalitySelector(), new JavaScopeDeriver() ); + new ChainedDependencyGraphTransformer( transformer, new JavaDependencyContextRefiner() ); + session.setDependencyGraphTransformer( transformer ); + + DefaultArtifactTypeRegistry stereotypes = new DefaultArtifactTypeRegistry(); + stereotypes.add( new DefaultArtifactType( "pom" ) ); + stereotypes.add( new DefaultArtifactType( "maven-plugin", "jar", "", "java" ) ); + stereotypes.add( new DefaultArtifactType( "jar", "jar", "", "java" ) ); + stereotypes.add( new DefaultArtifactType( "ejb", "jar", "", "java" ) ); + stereotypes.add( new DefaultArtifactType( "ejb-client", "jar", "client", "java" ) ); + stereotypes.add( new DefaultArtifactType( "test-jar", "jar", "tests", "java" ) ); + stereotypes.add( new DefaultArtifactType( "javadoc", "jar", "javadoc", "java" ) ); + stereotypes.add( new DefaultArtifactType( "java-source", "jar", "sources", "java", false, false ) ); + stereotypes.add( new DefaultArtifactType( "war", "war", "", "java", false, true ) ); + stereotypes.add( new DefaultArtifactType( "ear", "ear", "", "java", false, true ) ); + stereotypes.add( new DefaultArtifactType( "rar", "rar", "", "java", false, true ) ); + stereotypes.add( new DefaultArtifactType( "par", "par", "", "java", false, true ) ); + session.setArtifactTypeRegistry( stereotypes ); + + session.setArtifactDescriptorPolicy( new SimpleArtifactDescriptorPolicy( true, true ) ); + + final Properties systemProperties = new Properties(); + + // MNG-5670 guard against ConcurrentModificationException + // MNG-6053 guard against key without value + Properties sysProp = System.getProperties(); + synchronized ( sysProp ) + { + systemProperties.putAll( sysProp ); + } + + session.setSystemProperties( systemProperties ); + session.setConfigProperties( systemProperties ); + + return session; + } + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenResolverModule.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenResolverModule.java new file mode 100644 index 000000000..bdfd21282 --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenResolverModule.java @@ -0,0 +1,74 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import com.google.inject.name.Names; +import org.apache.maven.model.building.DefaultModelBuilderFactory; +import org.apache.maven.model.building.ModelBuilder; +import org.eclipse.aether.impl.ArtifactDescriptorReader; +import org.eclipse.aether.impl.MetadataGeneratorFactory; +import org.eclipse.aether.impl.VersionRangeResolver; +import org.eclipse.aether.impl.VersionResolver; +import org.eclipse.aether.impl.guice.AetherModule; + +import javax.inject.Named; +import javax.inject.Singleton; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * A guice Module for injecting suitable providers for a the Maven Artifact Resolver + */ +public final class MavenResolverModule + extends AbstractModule +{ + + @Override + protected void configure() + { + install( new AetherModule() ); + bind( ArtifactDescriptorReader.class ).to( DefaultArtifactDescriptorReader.class ).in( Singleton.class ); + bind( VersionResolver.class ).to( DefaultVersionResolver.class ).in( Singleton.class ); + bind( VersionRangeResolver.class ).to( DefaultVersionRangeResolver.class ).in( Singleton.class ); + bind( MetadataGeneratorFactory.class ).annotatedWith( Names.named( "snapshot" ) ) + .to( SnapshotMetadataGeneratorFactory.class ).in( Singleton.class ); + + bind( MetadataGeneratorFactory.class ).annotatedWith( Names.named( "versions" ) ) + .to( VersionsMetadataGeneratorFactory.class ).in( Singleton.class ); + + bind( ModelBuilder.class ).toInstance( new DefaultModelBuilderFactory().newInstance() ); + } + + @Provides + @Singleton + Set provideMetadataGeneratorFactories( + @Named( "snapshot" ) MetadataGeneratorFactory snapshot, + @Named( "versions" ) MetadataGeneratorFactory versions ) + { + Set factories = new HashSet<>( 2 ); + factories.add( snapshot ); + factories.add( versions ); + return Collections.unmodifiableSet( factories ); + } + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenSnapshotMetadata.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenSnapshotMetadata.java new file mode 100644 index 000000000..e4c9a7e9e --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenSnapshotMetadata.java @@ -0,0 +1,100 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.maven.artifact.repository.metadata.Metadata; +import org.eclipse.aether.artifact.Artifact; + +/** + * @author Hervé Boutemy + */ +abstract class MavenSnapshotMetadata + extends MavenMetadata +{ + static final String SNAPSHOT = "SNAPSHOT"; + + protected final Collection artifacts = new ArrayList<>(); + + protected final boolean legacyFormat; + + protected MavenSnapshotMetadata( Metadata metadata, File file, boolean legacyFormat ) + { + super( metadata, file ); + this.legacyFormat = legacyFormat; + } + + protected static Metadata createRepositoryMetadata( Artifact artifact, boolean legacyFormat ) + { + Metadata metadata = new Metadata(); + if ( !legacyFormat ) + { + metadata.setModelVersion( "1.1.0" ); + } + metadata.setGroupId( artifact.getGroupId() ); + metadata.setArtifactId( artifact.getArtifactId() ); + metadata.setVersion( artifact.getBaseVersion() ); + + return metadata; + } + + public void bind( Artifact artifact ) + { + artifacts.add( artifact ); + } + + public Object getKey() + { + return getGroupId() + ':' + getArtifactId() + ':' + getVersion(); + } + + public static Object getKey( Artifact artifact ) + { + return artifact.getGroupId() + ':' + artifact.getArtifactId() + ':' + artifact.getBaseVersion(); + } + + protected String getKey( String classifier, String extension ) + { + return classifier + ':' + extension; + } + + public String getGroupId() + { + return metadata.getGroupId(); + } + + public String getArtifactId() + { + return metadata.getArtifactId(); + } + + public String getVersion() + { + return metadata.getVersion(); + } + + public Nature getNature() + { + return Nature.SNAPSHOT; + } +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenWorkspaceReader.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenWorkspaceReader.java new file mode 100644 index 000000000..74fc676af --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenWorkspaceReader.java @@ -0,0 +1,38 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.apache.maven.model.Model; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.repository.WorkspaceReader; + +/** + * Manages a repository backed by the IDE workspace, a build session or a similar ad-hoc collection of artifacts. + * + * @see org.eclipse.aether.RepositorySystemSession#getWorkspaceReader() + */ + +public interface MavenWorkspaceReader + extends WorkspaceReader +{ + + Model findModel( Artifact artifact ); + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/RelocatedArtifact.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/RelocatedArtifact.java new file mode 100644 index 000000000..c6ef3aa89 --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/RelocatedArtifact.java @@ -0,0 +1,114 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.File; +import java.util.Map; + +import org.apache.commons.lang3.Validate; +import org.eclipse.aether.artifact.AbstractArtifact; +import org.eclipse.aether.artifact.Artifact; + +/** + * @author Benjamin Bentmann + */ +final class RelocatedArtifact + extends AbstractArtifact +{ + + private final Artifact artifact; + + private final String groupId; + + private final String artifactId; + + private final String version; + + public RelocatedArtifact( Artifact artifact, String groupId, String artifactId, String version ) + { + this.artifact = Validate.notNull( artifact, "artifact cannot be null" ); + // TODO Use StringUtils here + this.groupId = ( groupId != null && groupId.length() > 0 ) ? groupId : null; + this.artifactId = ( artifactId != null && artifactId.length() > 0 ) ? artifactId : null; + this.version = ( version != null && version.length() > 0 ) ? version : null; + } + + public String getGroupId() + { + if ( groupId != null ) + { + return groupId; + } + else + { + return artifact.getGroupId(); + } + } + + public String getArtifactId() + { + if ( artifactId != null ) + { + return artifactId; + } + else + { + return artifact.getArtifactId(); + } + } + + public String getVersion() + { + if ( version != null ) + { + return version; + } + else + { + return artifact.getVersion(); + } + } + + public String getClassifier() + { + return artifact.getClassifier(); + } + + public String getExtension() + { + return artifact.getExtension(); + } + + public File getFile() + { + return artifact.getFile(); + } + + public String getProperty( String key, String defaultValue ) + { + return artifact.getProperty( key, defaultValue ); + } + + public Map getProperties() + { + return artifact.getProperties(); + } + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/RemoteSnapshotMetadata.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/RemoteSnapshotMetadata.java new file mode 100644 index 000000000..1e791d8a4 --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/RemoteSnapshotMetadata.java @@ -0,0 +1,157 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.File; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TimeZone; + +import org.apache.maven.artifact.repository.metadata.Metadata; +import org.apache.maven.artifact.repository.metadata.Snapshot; +import org.apache.maven.artifact.repository.metadata.SnapshotVersion; +import org.apache.maven.artifact.repository.metadata.Versioning; +import org.eclipse.aether.artifact.Artifact; + +/** + * @author Benjamin Bentmann + */ +final class RemoteSnapshotMetadata + extends MavenSnapshotMetadata +{ + public static final String DEFAULT_SNAPSHOT_TIMESTAMP_FORMAT = "yyyyMMdd.HHmmss"; + + public static final TimeZone DEFAULT_SNAPSHOT_TIME_ZONE = TimeZone.getTimeZone( "Etc/UTC" ); + + private final Map versions = new LinkedHashMap<>(); + + public RemoteSnapshotMetadata( Artifact artifact, boolean legacyFormat ) + { + super( createRepositoryMetadata( artifact, legacyFormat ), null, legacyFormat ); + } + + private RemoteSnapshotMetadata( Metadata metadata, File file, boolean legacyFormat ) + { + super( metadata, file, legacyFormat ); + } + + public MavenMetadata setFile( File file ) + { + return new RemoteSnapshotMetadata( metadata, file, legacyFormat ); + } + + public String getExpandedVersion( Artifact artifact ) + { + String key = getKey( artifact.getClassifier(), artifact.getExtension() ); + return versions.get( key ).getVersion(); + } + + @Override + protected void merge( Metadata recessive ) + { + Snapshot snapshot; + String lastUpdated; + + if ( metadata.getVersioning() == null ) + { + DateFormat utcDateFormatter = new SimpleDateFormat( DEFAULT_SNAPSHOT_TIMESTAMP_FORMAT ); + utcDateFormatter.setCalendar( new GregorianCalendar() ); + utcDateFormatter.setTimeZone( DEFAULT_SNAPSHOT_TIME_ZONE ); + + snapshot = new Snapshot(); + snapshot.setBuildNumber( getBuildNumber( recessive ) + 1 ); + snapshot.setTimestamp( utcDateFormatter.format( new Date() ) ); + + Versioning versioning = new Versioning(); + versioning.setSnapshot( snapshot ); + versioning.setLastUpdated( snapshot.getTimestamp().replace( ".", "" ) ); + lastUpdated = versioning.getLastUpdated(); + + metadata.setVersioning( versioning ); + } + else + { + snapshot = metadata.getVersioning().getSnapshot(); + lastUpdated = metadata.getVersioning().getLastUpdated(); + } + + for ( Artifact artifact : artifacts ) + { + String version = artifact.getVersion(); + + if ( version.endsWith( SNAPSHOT ) ) + { + String qualifier = snapshot.getTimestamp() + '-' + snapshot.getBuildNumber(); + version = version.substring( 0, version.length() - SNAPSHOT.length() ) + qualifier; + } + + SnapshotVersion sv = new SnapshotVersion(); + sv.setClassifier( artifact.getClassifier() ); + sv.setExtension( artifact.getExtension() ); + sv.setVersion( version ); + sv.setUpdated( lastUpdated ); + + versions.put( getKey( sv.getClassifier(), sv.getExtension() ), sv ); + } + + artifacts.clear(); + + Versioning versioning = recessive.getVersioning(); + if ( versioning != null ) + { + for ( SnapshotVersion sv : versioning.getSnapshotVersions() ) + { + String key = getKey( sv.getClassifier(), sv.getExtension() ); + if ( !versions.containsKey( key ) ) + { + versions.put( key, sv ); + } + } + } + + if ( !legacyFormat ) + { + metadata.getVersioning().setSnapshotVersions( new ArrayList<>( versions.values() ) ); + } + } + + private static int getBuildNumber( Metadata metadata ) + { + int number = 0; + + Versioning versioning = metadata.getVersioning(); + if ( versioning != null ) + { + Snapshot snapshot = versioning.getSnapshot(); + if ( snapshot != null && snapshot.getBuildNumber() > 0 ) + { + number = snapshot.getBuildNumber(); + } + } + + return number; + } + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/RemoteSnapshotMetadataGenerator.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/RemoteSnapshotMetadataGenerator.java new file mode 100644 index 000000000..825896620 --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/RemoteSnapshotMetadataGenerator.java @@ -0,0 +1,107 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.deployment.DeployRequest; +import org.eclipse.aether.impl.MetadataGenerator; +import org.eclipse.aether.metadata.Metadata; +import org.eclipse.aether.util.ConfigUtils; + +/** + * @author Benjamin Bentmann + */ +class RemoteSnapshotMetadataGenerator + implements MetadataGenerator +{ + + private final Map snapshots; + + private final boolean legacyFormat; + + public RemoteSnapshotMetadataGenerator( RepositorySystemSession session, DeployRequest request ) + { + legacyFormat = ConfigUtils.getBoolean( session.getConfigProperties(), false, "maven.metadata.legacy" ); + + snapshots = new LinkedHashMap<>(); + + /* + * NOTE: This should be considered a quirk to support interop with Maven's legacy ArtifactDeployer which + * processes one artifact at a time and hence cannot associate the artifacts from the same project to use the + * same timestamp+buildno for the snapshot versions. Allowing the caller to pass in metadata from a previous + * deployment allows to re-establish the association between the artifacts of the same project. + */ + for ( Metadata metadata : request.getMetadata() ) + { + if ( metadata instanceof RemoteSnapshotMetadata ) + { + RemoteSnapshotMetadata snapshotMetadata = (RemoteSnapshotMetadata) metadata; + snapshots.put( snapshotMetadata.getKey(), snapshotMetadata ); + } + } + } + + public Collection prepare( Collection artifacts ) + { + for ( Artifact artifact : artifacts ) + { + if ( artifact.isSnapshot() ) + { + Object key = RemoteSnapshotMetadata.getKey( artifact ); + RemoteSnapshotMetadata snapshotMetadata = snapshots.get( key ); + if ( snapshotMetadata == null ) + { + snapshotMetadata = new RemoteSnapshotMetadata( artifact, legacyFormat ); + snapshots.put( key, snapshotMetadata ); + } + snapshotMetadata.bind( artifact ); + } + } + + return snapshots.values(); + } + + public Artifact transformArtifact( Artifact artifact ) + { + if ( artifact.isSnapshot() && artifact.getVersion().equals( artifact.getBaseVersion() ) ) + { + Object key = RemoteSnapshotMetadata.getKey( artifact ); + RemoteSnapshotMetadata snapshotMetadata = snapshots.get( key ); + if ( snapshotMetadata != null ) + { + artifact = artifact.setVersion( snapshotMetadata.getExpandedVersion( artifact ) ); + } + } + + return artifact; + } + + public Collection finish( Collection artifacts ) + { + return Collections.emptyList(); + } + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/SnapshotMetadataGeneratorFactory.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/SnapshotMetadataGeneratorFactory.java new file mode 100644 index 000000000..79ffaade7 --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/SnapshotMetadataGeneratorFactory.java @@ -0,0 +1,52 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.codehaus.plexus.component.annotations.Component; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.deployment.DeployRequest; +import org.eclipse.aether.impl.MetadataGenerator; +import org.eclipse.aether.impl.MetadataGeneratorFactory; +import org.eclipse.aether.installation.InstallRequest; + +/** + * @author Benjamin Bentmann + */ +@Component( role = MetadataGeneratorFactory.class, hint = "snapshot" ) +public class SnapshotMetadataGeneratorFactory + implements MetadataGeneratorFactory +{ + + public MetadataGenerator newInstance( RepositorySystemSession session, InstallRequest request ) + { + return new LocalSnapshotMetadataGenerator( session, request ); + } + + public MetadataGenerator newInstance( RepositorySystemSession session, DeployRequest request ) + { + return new RemoteSnapshotMetadataGenerator( session, request ); + } + + public float getPriority() + { + return 10; + } + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/VersionsMetadata.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/VersionsMetadata.java new file mode 100644 index 000000000..f5e13be19 --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/VersionsMetadata.java @@ -0,0 +1,133 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashSet; + +import org.apache.maven.artifact.repository.metadata.Metadata; +import org.apache.maven.artifact.repository.metadata.Versioning; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.ArtifactProperties; + +/** + * @author Benjamin Bentmann + */ +final class VersionsMetadata + extends MavenMetadata +{ + + private final Artifact artifact; + + public VersionsMetadata( Artifact artifact ) + { + super( createRepositoryMetadata( artifact ), null ); + this.artifact = artifact; + } + + public VersionsMetadata( Artifact artifact, File file ) + { + super( createRepositoryMetadata( artifact ), file ); + this.artifact = artifact; + } + + private static Metadata createRepositoryMetadata( Artifact artifact ) + { + Metadata metadata = new Metadata(); + metadata.setGroupId( artifact.getGroupId() ); + metadata.setArtifactId( artifact.getArtifactId() ); + + Versioning versioning = new Versioning(); + versioning.addVersion( artifact.getBaseVersion() ); + if ( !artifact.isSnapshot() ) + { + versioning.setRelease( artifact.getBaseVersion() ); + } + if ( "maven-plugin".equals( artifact.getProperty( ArtifactProperties.TYPE, "" ) ) ) + { + versioning.setLatest( artifact.getBaseVersion() ); + } + + metadata.setVersioning( versioning ); + + return metadata; + } + + @Override + protected void merge( Metadata recessive ) + { + Versioning versioning = metadata.getVersioning(); + versioning.updateTimestamp(); + + if ( recessive.getVersioning() != null ) + { + if ( versioning.getLatest() == null ) + { + versioning.setLatest( recessive.getVersioning().getLatest() ); + } + if ( versioning.getRelease() == null ) + { + versioning.setRelease( recessive.getVersioning().getRelease() ); + } + + Collection versions = new LinkedHashSet<>( recessive.getVersioning().getVersions() ); + versions.addAll( versioning.getVersions() ); + versioning.setVersions( new ArrayList<>( versions ) ); + } + } + + public Object getKey() + { + return getGroupId() + ':' + getArtifactId(); + } + + public static Object getKey( Artifact artifact ) + { + return artifact.getGroupId() + ':' + artifact.getArtifactId(); + } + + public MavenMetadata setFile( File file ) + { + return new VersionsMetadata( artifact, file ); + } + + public String getGroupId() + { + return artifact.getGroupId(); + } + + public String getArtifactId() + { + return artifact.getArtifactId(); + } + + public String getVersion() + { + return ""; + } + + public Nature getNature() + { + return artifact.isSnapshot() ? Nature.RELEASE_OR_SNAPSHOT : Nature.RELEASE; + } + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/VersionsMetadataGenerator.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/VersionsMetadataGenerator.java new file mode 100644 index 000000000..5173001bb --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/VersionsMetadataGenerator.java @@ -0,0 +1,108 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.deployment.DeployRequest; +import org.eclipse.aether.impl.MetadataGenerator; +import org.eclipse.aether.installation.InstallRequest; +import org.eclipse.aether.metadata.Metadata; + +/** + * @author Benjamin Bentmann + */ +class VersionsMetadataGenerator + implements MetadataGenerator +{ + + private Map versions; + + private Map processedVersions; + + public VersionsMetadataGenerator( RepositorySystemSession session, InstallRequest request ) + { + this( session, request.getMetadata() ); + } + + public VersionsMetadataGenerator( RepositorySystemSession session, DeployRequest request ) + { + this( session, request.getMetadata() ); + } + + private VersionsMetadataGenerator( RepositorySystemSession session, Collection metadatas ) + { + versions = new LinkedHashMap<>(); + processedVersions = new LinkedHashMap<>(); + + /* + * NOTE: This should be considered a quirk to support interop with Maven's legacy ArtifactDeployer which + * processes one artifact at a time and hence cannot associate the artifacts from the same project to use the + * same version index. Allowing the caller to pass in metadata from a previous deployment allows to re-establish + * the association between the artifacts of the same project. + */ + for ( Iterator it = metadatas.iterator(); it.hasNext(); ) + { + Metadata metadata = it.next(); + if ( metadata instanceof VersionsMetadata ) + { + it.remove(); + VersionsMetadata versionsMetadata = (VersionsMetadata) metadata; + processedVersions.put( versionsMetadata.getKey(), versionsMetadata ); + } + } + } + + public Collection prepare( Collection artifacts ) + { + return Collections.emptyList(); + } + + public Artifact transformArtifact( Artifact artifact ) + { + return artifact; + } + + public Collection finish( Collection artifacts ) + { + for ( Artifact artifact : artifacts ) + { + Object key = VersionsMetadata.getKey( artifact ); + if ( processedVersions.get( key ) == null ) + { + VersionsMetadata versionsMetadata = versions.get( key ); + if ( versionsMetadata == null ) + { + versionsMetadata = new VersionsMetadata( artifact ); + versions.put( key, versionsMetadata ); + } + } + } + + return versions.values(); + } + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/VersionsMetadataGeneratorFactory.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/VersionsMetadataGeneratorFactory.java new file mode 100644 index 000000000..47ef36012 --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/VersionsMetadataGeneratorFactory.java @@ -0,0 +1,52 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.codehaus.plexus.component.annotations.Component; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.deployment.DeployRequest; +import org.eclipse.aether.impl.MetadataGenerator; +import org.eclipse.aether.impl.MetadataGeneratorFactory; +import org.eclipse.aether.installation.InstallRequest; + +/** + * @author Benjamin Bentmann + */ +@Component( role = MetadataGeneratorFactory.class, hint = "versions" ) +public class VersionsMetadataGeneratorFactory + implements MetadataGeneratorFactory +{ + + public MetadataGenerator newInstance( RepositorySystemSession session, InstallRequest request ) + { + return new VersionsMetadataGenerator( session, request ); + } + + public MetadataGenerator newInstance( RepositorySystemSession session, DeployRequest request ) + { + return new VersionsMetadataGenerator( session, request ); + } + + public float getPriority() + { + return 5; + } + +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/package-info.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/package-info.java new file mode 100644 index 000000000..7309c2b03 --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/package-info.java @@ -0,0 +1,25 @@ +// CHECKSTYLE_OFF: RegexpHeader +/** + * Maven Resolver extensions for utilizing the Maven POM and Maven + * repository metadata. + */ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ diff --git a/maven-resolver-provider/src/site/apt/index.apt b/maven-resolver-provider/src/site/apt/index.apt new file mode 100644 index 000000000..b50ea1dbf --- /dev/null +++ b/maven-resolver-provider/src/site/apt/index.apt @@ -0,0 +1,33 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you 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. + + ----- + Introduction + ----- + Hervé Boutemy + ----- + 2012-09-29 + ----- + +Maven Artifact Resolver Provider + + Maven Artifact Resolver Provider is a {{{/resolver/}Maven Artifact Resolver}} extension to support + Maven POMs and local+remote repositories. + + Main component is <<>> + ({{{./apidocs/org/apache/maven/repository/internal/MavenRepositorySystemUtils.html}javadoc}}, + {{{./xref/org/apache/maven/repository/internal/MavenRepositorySystemUtils.html}source}}). diff --git a/maven-resolver-provider/src/site/site.xml b/maven-resolver-provider/src/site/site.xml new file mode 100644 index 000000000..3a16bf98b --- /dev/null +++ b/maven-resolver-provider/src/site/site.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/AbstractRepositoryTestCase.java b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/AbstractRepositoryTestCase.java new file mode 100644 index 000000000..b81450cea --- /dev/null +++ b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/AbstractRepositoryTestCase.java @@ -0,0 +1,87 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.net.MalformedURLException; + +import org.apache.maven.repository.internal.util.ConsoleRepositoryListener; +import org.apache.maven.repository.internal.util.ConsoleTransferListener; +import org.codehaus.plexus.ContainerConfiguration; +import org.codehaus.plexus.PlexusConstants; +import org.codehaus.plexus.PlexusTestCase; +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.repository.LocalRepository; +import org.eclipse.aether.repository.RemoteRepository; + +public abstract class AbstractRepositoryTestCase + extends PlexusTestCase +{ + protected RepositorySystem system; + + protected RepositorySystemSession session; + + @Override + protected void customizeContainerConfiguration( ContainerConfiguration containerConfiguration ) + { + super.customizeContainerConfiguration( containerConfiguration ); + containerConfiguration.setAutoWiring( true ); + containerConfiguration.setClassPathScanning( PlexusConstants.SCANNING_INDEX ); + } + + @Override + protected void setUp() + throws Exception + { + super.setUp(); + system = lookup( RepositorySystem.class ); + session = newMavenRepositorySystemSession( system ); + } + + @Override + protected void tearDown() + throws Exception + { + session = null; + system = null; + super.tearDown(); + } + + public static RepositorySystemSession newMavenRepositorySystemSession( RepositorySystem system ) + { + DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); + + LocalRepository localRepo = new LocalRepository( "target/local-repo" ); + session.setLocalRepositoryManager( system.newLocalRepositoryManager( session, localRepo ) ); + + session.setTransferListener( new ConsoleTransferListener() ); + session.setRepositoryListener( new ConsoleRepositoryListener() ); + + return session; + } + + public static RemoteRepository newTestRepository() + throws MalformedURLException + { + return new RemoteRepository.Builder( "repo", "default", + getTestFile( "target/test-classes/repo" ).toURI().toURL().toString() ).build(); + } +} diff --git a/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReaderTest.java b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReaderTest.java new file mode 100644 index 000000000..e87313809 --- /dev/null +++ b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReaderTest.java @@ -0,0 +1,77 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.eclipse.aether.RepositoryEvent; +import org.eclipse.aether.RepositoryEvent.EventType; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.impl.ArtifactDescriptorReader; +import org.eclipse.aether.impl.RepositoryEventDispatcher; +import org.eclipse.aether.resolution.ArtifactDescriptorRequest; +import org.mockito.ArgumentCaptor; + +public class DefaultArtifactDescriptorReaderTest + extends AbstractRepositoryTestCase +{ + + public void testMng5459() + throws Exception + { + // prepare + DefaultArtifactDescriptorReader reader = (DefaultArtifactDescriptorReader) lookup( ArtifactDescriptorReader.class ); + + RepositoryEventDispatcher eventDispatcher = mock( RepositoryEventDispatcher.class ); + + ArgumentCaptor event = ArgumentCaptor.forClass( RepositoryEvent.class ); + + reader.setRepositoryEventDispatcher( eventDispatcher ); + + ArtifactDescriptorRequest request = new ArtifactDescriptorRequest(); + + request.addRepository( newTestRepository() ); + + request.setArtifact( new DefaultArtifact( "org.apache.maven.its", "dep-mng5459", "jar", "0.4.0-SNAPSHOT" ) ); + + // execute + reader.readArtifactDescriptor( session, request ); + + // verify + verify( eventDispatcher ).dispatch( event.capture() ); + + boolean missingArtifactDescriptor = false; + + for( RepositoryEvent evt : event.getAllValues() ) + { + if ( EventType.ARTIFACT_DESCRIPTOR_MISSING.equals( evt.getType() ) ) + { + assertEquals( "Could not find artifact org.apache.maven.its:dep-mng5459:pom:0.4.0-20130404.090532-2 in repo (" + newTestRepository().getUrl() + ")", evt.getException().getMessage() ); + missingArtifactDescriptor = true; + } + } + + if( !missingArtifactDescriptor ) + { + fail( "Expected missing artifact descriptor for org.apache.maven.its:dep-mng5459:pom:0.4.0-20130404.090532-2" ); + } + } +} diff --git a/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/DefaultVersionResolverTest.java b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/DefaultVersionResolverTest.java new file mode 100644 index 000000000..90dd6a4cf --- /dev/null +++ b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/DefaultVersionResolverTest.java @@ -0,0 +1,96 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.impl.VersionResolver; +import org.eclipse.aether.resolution.VersionRequest; +import org.eclipse.aether.resolution.VersionResult; +import org.eclipse.aether.artifact.DefaultArtifact; + +public class DefaultVersionResolverTest + extends AbstractRepositoryTestCase +{ + private DefaultVersionResolver versionResolver; + + @Override + protected void setUp() + throws Exception + { + super.setUp(); + // be sure we're testing the right class, i.e. DefaultVersionResolver.class + versionResolver = (DefaultVersionResolver) lookup( VersionResolver.class, "default" ); + } + + @Override + protected void tearDown() + throws Exception + { + versionResolver = null; + super.tearDown(); + } + + public void testResolveSeparateInstalledClassifiedNonUniqueVersionedArtifacts() + throws Exception + { + VersionRequest requestB = new VersionRequest(); + requestB.addRepository( newTestRepository() ); + Artifact artifactB = + new DefaultArtifact( "org.apache.maven.its", "dep-mng5324", "classifierB", "jar", "07.20.3-SNAPSHOT" ); + requestB.setArtifact( artifactB ); + + VersionResult resultB = versionResolver.resolveVersion( session, requestB ); + assertEquals( "07.20.3-20120809.112920-97", resultB.getVersion() ); + + VersionRequest requestA = new VersionRequest(); + requestA.addRepository( newTestRepository() ); + + Artifact artifactA = + new DefaultArtifact( "org.apache.maven.its", "dep-mng5324", "classifierA", "jar", "07.20.3-SNAPSHOT" ); + requestA.setArtifact( artifactA ); + + VersionResult resultA = versionResolver.resolveVersion( session, requestA ); + assertEquals( "07.20.3-20120809.112124-88", resultA.getVersion() ); + } + + public void testResolveSeparateInstalledClassifiedNonVersionedArtifacts() + throws Exception + { + VersionRequest requestA = new VersionRequest(); + requestA.addRepository( newTestRepository() ); + String versionA = "07.20.3-20120809.112124-88"; + Artifact artifactA = + new DefaultArtifact( "org.apache.maven.its", "dep-mng5324", "classifierA", "jar", versionA ); + requestA.setArtifact( artifactA ); + + VersionResult resultA = versionResolver.resolveVersion( session, requestA ); + assertEquals( versionA, resultA.getVersion() ); + + VersionRequest requestB = new VersionRequest(); + requestB.addRepository( newTestRepository() ); + String versionB = "07.20.3-20120809.112920-97"; + Artifact artifactB = + new DefaultArtifact( "org.apache.maven.its", "dep-mng5324", "classifierB", "jar", versionB ); + requestB.setArtifact( artifactB ); + + VersionResult resultB = versionResolver.resolveVersion( session, requestB ); + assertEquals( versionB, resultB.getVersion() ); + } +} diff --git a/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/MavenRepositorySystemUtilsTest.java b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/MavenRepositorySystemUtilsTest.java new file mode 100644 index 000000000..768835a55 --- /dev/null +++ b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/MavenRepositorySystemUtilsTest.java @@ -0,0 +1,45 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.impl.MetadataGeneratorFactory; +import org.eclipse.aether.spi.locator.ServiceLocator; + +import junit.framework.TestCase; + +public class MavenRepositorySystemUtilsTest + extends TestCase +{ + + public void testGetRepositorySystem() + { + ServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator(); + RepositorySystem repoSys = locator.getService( RepositorySystem.class ); + assertNotNull( repoSys ); + } + + public void testGetMetadataGeneratorFactories() + { + ServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator(); + assertEquals( 2, locator.getServices( MetadataGeneratorFactory.class ).size() ); + } + +} diff --git a/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/RemoteSnapshotMetadataTest.java b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/RemoteSnapshotMetadataTest.java new file mode 100644 index 000000000..278231f9f --- /dev/null +++ b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/RemoteSnapshotMetadataTest.java @@ -0,0 +1,82 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import static org.junit.Assert.assertTrue; + +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +import org.apache.maven.artifact.repository.metadata.Metadata; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class RemoteSnapshotMetadataTest +{ + private Locale defaultLocale; + + @Before + public void setLocaleToUseBuddhistCalendar() + { + defaultLocale = Locale.getDefault(); + Locale.setDefault( new Locale( "th", "TH" ) ); + } + + @After + public void restoreLocale() + { + Locale.setDefault( defaultLocale ); + } + + static String gregorianDate() + { + SimpleDateFormat df = new SimpleDateFormat( "yyyyMMdd" ); + df.setCalendar( new GregorianCalendar() ); + df.setTimeZone( RemoteSnapshotMetadata.DEFAULT_SNAPSHOT_TIME_ZONE ); + return df.format( new Date() ); + } + + @Test + public void gregorianCalendarIsUsed() + { + String dateBefore = gregorianDate(); + + RemoteSnapshotMetadata metadata = new RemoteSnapshotMetadata( + new DefaultArtifact( "a:b:1-SNAPSHOT" ), false); + metadata.merge( new Metadata() ); + + String dateAfter = gregorianDate(); + + String ts = metadata.metadata.getVersioning().getSnapshot().getTimestamp(); + String datePart = ts.replaceAll( "\\..*", "" ); + + /* Allow for this test running across midnight */ + Set expected = new HashSet( Arrays.asList( dateBefore, dateAfter ) ); + assertTrue( "Expected " + datePart + " to be in " + expected, + expected.contains( datePart ) ); + } +} diff --git a/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/RepositorySystemTest.java b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/RepositorySystemTest.java new file mode 100644 index 000000000..7943dc802 --- /dev/null +++ b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/RepositorySystemTest.java @@ -0,0 +1,220 @@ +package org.apache.maven.repository.internal; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.collection.CollectRequest; +import org.eclipse.aether.collection.CollectResult; +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.graph.DependencyNode; +import org.eclipse.aether.resolution.ArtifactDescriptorRequest; +import org.eclipse.aether.resolution.ArtifactDescriptorResult; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.artifact.DefaultArtifact; + +public class RepositorySystemTest + extends AbstractRepositoryTestCase +{ + public void testResolveVersionRange() + throws Exception + { + //VersionRangeResult resolveVersionRange( RepositorySystemSession session, VersionRangeRequest request ) + // throws VersionRangeResolutionException; + + } + + public void testResolveVersion() + throws Exception + { + //VersionResult resolveVersion( RepositorySystemSession session, VersionRequest request ) + // throws VersionResolutionException; + } + + public void testReadArtifactDescriptor() + throws Exception + { + Artifact artifact = new DefaultArtifact( "ut.simple:artifact:extension:classifier:1.0" ); + + ArtifactDescriptorRequest request = new ArtifactDescriptorRequest(); + request.setArtifact( artifact ); + request.addRepository( newTestRepository() ); + + ArtifactDescriptorResult result = system.readArtifactDescriptor( session, request ); + + List deps = result.getDependencies(); + assertEquals( 2, deps.size() ); + checkUtSimpleArtifactDependencies( deps.get( 0 ), deps.get( 1 ) ); + } + + /** + * check ut.simple:artifact:1.0 dependencies + */ + private void checkUtSimpleArtifactDependencies( Dependency dep1, Dependency dep2 ) + { + assertEquals( "compile", dep1.getScope() ); + assertFalse( dep1.isOptional() ); + assertEquals( 0, dep1.getExclusions().size() ); + Artifact depArtifact = dep1.getArtifact(); + assertEquals( "ut.simple", depArtifact.getGroupId() ); + assertEquals( "dependency", depArtifact.getArtifactId() ); + assertEquals( "1.0", depArtifact.getVersion() ); + assertEquals( "1.0", depArtifact.getBaseVersion() ); + assertNull( depArtifact.getFile() ); + assertFalse( depArtifact.isSnapshot() ); + assertEquals( "", depArtifact.getClassifier() ); + assertEquals( "jar", depArtifact.getExtension() ); + assertEquals( "java", depArtifact.getProperty( "language", null ) ); + assertEquals( "jar", depArtifact.getProperty( "type", null ) ); + assertEquals( "true", depArtifact.getProperty( "constitutesBuildPath", null ) ); + assertEquals( "false", depArtifact.getProperty( "includesDependencies", null ) ); + assertEquals( 4, depArtifact.getProperties().size() ); + + assertEquals( "compile", dep2.getScope() ); + assertFalse( dep2.isOptional() ); + assertEquals( 0, dep2.getExclusions().size() ); + depArtifact = dep2.getArtifact(); + assertEquals( "ut.simple", depArtifact.getGroupId() ); + assertEquals( "dependency", depArtifact.getArtifactId() ); + assertEquals( "1.0", depArtifact.getVersion() ); + assertEquals( "1.0", depArtifact.getBaseVersion() ); + assertNull( depArtifact.getFile() ); + assertFalse( depArtifact.isSnapshot() ); + assertEquals( "sources", depArtifact.getClassifier() ); + assertEquals( "jar", depArtifact.getExtension() ); + assertEquals( "java", depArtifact.getProperty( "language", null ) ); + assertEquals( "jar", depArtifact.getProperty( "type", null ) ); // shouldn't it be java-sources given the classifier? + assertEquals( "true", depArtifact.getProperty( "constitutesBuildPath", null ) ); // shouldn't it be false given the classifier? + assertEquals( "false", depArtifact.getProperty( "includesDependencies", null ) ); + assertEquals( 4, depArtifact.getProperties().size() ); + } + + public void testCollectDependencies() + throws Exception + { + Artifact artifact = new DefaultArtifact( "ut.simple:artifact:extension:classifier:1.0" ); + // notice: extension and classifier not really used in this test... + + CollectRequest collectRequest = new CollectRequest(); + collectRequest.setRoot( new Dependency( artifact, null ) ); + collectRequest.addRepository( newTestRepository() ); + + CollectResult collectResult = system.collectDependencies( session, collectRequest ); + + List nodes = collectResult.getRoot().getChildren(); + assertEquals( 2, nodes.size() ); + checkUtSimpleArtifactDependencies( nodes.get( 0 ).getDependency(), nodes.get( 1 ).getDependency() ); + } + + public void testResolveArtifact() + throws Exception + { + Artifact artifact = new DefaultArtifact( "ut.simple:artifact:1.0" ); + + ArtifactRequest artifactRequest = new ArtifactRequest(); + artifactRequest.setArtifact( artifact ); + artifactRequest.addRepository( newTestRepository() ); + + ArtifactResult artifactResult = system.resolveArtifact( session, artifactRequest ); + checkArtifactResult( artifactResult, "artifact-1.0.jar" ); + + artifact = new DefaultArtifact( "ut.simple:artifact:zip:1.0" ); + artifactRequest.setArtifact( artifact ); + artifactResult = system.resolveArtifact( session, artifactRequest ); + checkArtifactResult( artifactResult, "artifact-1.0.zip" ); + + artifact = new DefaultArtifact( "ut.simple:artifact:zip:classifier:1.0" ); + artifactRequest.setArtifact( artifact ); + artifactResult = system.resolveArtifact( session, artifactRequest ); + checkArtifactResult( artifactResult, "artifact-1.0-classifier.zip" ); + } + + private void checkArtifactResult( ArtifactResult result, String filename ) + { + assertFalse( result.isMissing() ); + assertTrue( result.isResolved() ); + Artifact artifact = result.getArtifact(); + assertNotNull( artifact.getFile() ); + assertEquals( filename, artifact.getFile().getName() ); + } + + public void testResolveArtifacts() + throws Exception + { + ArtifactRequest req1 = new ArtifactRequest(); + req1.setArtifact( new DefaultArtifact( "ut.simple:artifact:1.0" ) ); + req1.addRepository( newTestRepository() ); + + ArtifactRequest req2 = new ArtifactRequest(); + req2.setArtifact( new DefaultArtifact( "ut.simple:artifact:zip:1.0" ) ); + req2.addRepository( newTestRepository() ); + + ArtifactRequest req3 = new ArtifactRequest(); + req3.setArtifact( new DefaultArtifact( "ut.simple:artifact:zip:classifier:1.0" ) ); + req3.addRepository( newTestRepository() ); + + List requests = Arrays.asList( req1, req2, req3 ); + + List results = system.resolveArtifacts( session, requests ); + + assertEquals( 3, results.size() ); + checkArtifactResult( results.get( 0 ), "artifact-1.0.jar" ); + checkArtifactResult( results.get( 1 ), "artifact-1.0.zip" ); + checkArtifactResult( results.get( 2 ), "artifact-1.0-classifier.zip" ); + } + + public void testResolveMetadata() + throws Exception + { + //List resolveMetadata( RepositorySystemSession session, + // Collection requests ); + } + + public void testInstall() + throws Exception + { + //InstallResult install( RepositorySystemSession session, InstallRequest request ) + // throws InstallationException; + // release, snapshot unique ou non unique, attachment + } + + public void testDeploy() + throws Exception + { + //DeployResult deploy( RepositorySystemSession session, DeployRequest request ) + // throws DeploymentException; + } + + public void testNewLocalRepositoryManager() + throws Exception + { + //LocalRepositoryManager newLocalRepositoryManager( LocalRepository localRepository ); + } + + public void testNewSyncContext() + throws Exception + { + //SyncContext newSyncContext( RepositorySystemSession session, boolean shared ); + } + +} diff --git a/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/util/ConsoleRepositoryListener.java b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/util/ConsoleRepositoryListener.java new file mode 100644 index 000000000..2879a9fe4 --- /dev/null +++ b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/util/ConsoleRepositoryListener.java @@ -0,0 +1,132 @@ +package org.apache.maven.repository.internal.util; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.PrintStream; + +import org.eclipse.aether.AbstractRepositoryListener; +import org.eclipse.aether.RepositoryEvent; + +public class ConsoleRepositoryListener + extends AbstractRepositoryListener +{ + + private PrintStream out; + + public ConsoleRepositoryListener() + { + this( null ); + } + + public ConsoleRepositoryListener( PrintStream out ) + { + this.out = ( out != null ) ? out : System.out; + } + + public void artifactDeployed( RepositoryEvent event ) + { + println( "artifactDeployed", event.getArtifact() + " to " + event.getRepository() ); + } + + public void artifactDeploying( RepositoryEvent event ) + { + println( "artifactDeploying", event.getArtifact() + " to " + event.getRepository() ); + } + + public void artifactDescriptorInvalid( RepositoryEvent event ) + { + println( "artifactDescriptorInvalid", "for " + event.getArtifact() + ": " + event.getException().getMessage() ); + } + + public void artifactDescriptorMissing( RepositoryEvent event ) + { + println( "artifactDescriptorMissing", "for " + event.getArtifact() ); + } + + public void artifactInstalled( RepositoryEvent event ) + { + println( "artifactInstalled", event.getArtifact() + " to " + event.getFile() ); + } + + public void artifactInstalling( RepositoryEvent event ) + { + println( "artifactInstalling", event.getArtifact() + " to " + event.getFile() ); + } + + public void artifactResolved( RepositoryEvent event ) + { + println( "artifactResolved", event.getArtifact() + " from " + event.getRepository() ); + } + + public void artifactDownloading( RepositoryEvent event ) + { + println( "artifactDownloading", event.getArtifact() + " from " + event.getRepository() ); + } + + public void artifactDownloaded( RepositoryEvent event ) + { + println( "artifactDownloaded", event.getArtifact() + " from " + event.getRepository() ); + } + + public void artifactResolving( RepositoryEvent event ) + { + println( "artifactResolving", event.getArtifact().toString() ); + } + + public void metadataDeployed( RepositoryEvent event ) + { + println( "metadataDeployed", event.getMetadata() + " to " + event.getRepository() ); + } + + public void metadataDeploying( RepositoryEvent event ) + { + println( "metadataDeploying", event.getMetadata() + " to " + event.getRepository() ); + } + + public void metadataInstalled( RepositoryEvent event ) + { + println( "metadataInstalled", event.getMetadata() + " to " + event.getFile() ); + } + + public void metadataInstalling( RepositoryEvent event ) + { + println( "metadataInstalling", event.getMetadata() + " to " + event.getFile() ); + } + + public void metadataInvalid( RepositoryEvent event ) + { + println( "metadataInvalid", event.getMetadata().toString() ); + } + + public void metadataResolved( RepositoryEvent event ) + { + println( "metadataResolved", event.getMetadata() + " from " + event.getRepository() ); + } + + public void metadataResolving( RepositoryEvent event ) + { + println( "metadataResolving", event.getMetadata() + " from " + event.getRepository() ); + } + + private void println( String event, String message ) + { + out.println( "Aether Repository - " + event + ": " + message ); + } +} diff --git a/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/util/ConsoleTransferListener.java b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/util/ConsoleTransferListener.java new file mode 100644 index 000000000..cf587d89f --- /dev/null +++ b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/util/ConsoleTransferListener.java @@ -0,0 +1,186 @@ +package org.apache.maven.repository.internal.util; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.PrintStream; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.aether.transfer.AbstractTransferListener; +import org.eclipse.aether.transfer.TransferEvent; +import org.eclipse.aether.transfer.TransferResource; + +public class ConsoleTransferListener + extends AbstractTransferListener +{ + + private PrintStream out; + + private Map downloads = new ConcurrentHashMap<>(); + + private int lastLength; + + public ConsoleTransferListener() + { + this( null ); + } + + public ConsoleTransferListener( PrintStream out ) + { + this.out = ( out != null ) ? out : System.out; + } + + @Override + public void transferInitiated( TransferEvent event ) + { + String message = event.getRequestType() == TransferEvent.RequestType.PUT ? "Uploading" : "Downloading"; + + println( "transferInitiated", message + ": " + event.getResource().getRepositoryUrl() + event.getResource().getResourceName() ); + } + + @Override + public void transferProgressed( TransferEvent event ) + { + TransferResource resource = event.getResource(); + downloads.put( resource, event.getTransferredBytes() ); + + StringBuilder buffer = new StringBuilder( 64 ); + + for ( Map.Entry entry : downloads.entrySet() ) + { + long total = entry.getKey().getContentLength(); + long complete = entry.getValue(); + + buffer.append( getStatus( complete, total ) ).append( " " ); + } + + int pad = lastLength - buffer.length(); + lastLength = buffer.length(); + pad( buffer, pad ); + buffer.append( '\r' ); + + print( "transferProgressed", buffer.toString() ); + } + + private String getStatus( long complete, long total ) + { + if ( total >= 1024 ) + { + return toKB( complete ) + "/" + toKB( total ) + " KB "; + } + else if ( total >= 0 ) + { + return complete + "/" + total + " B "; + } + else if ( complete >= 1024 ) + { + return toKB( complete ) + " KB "; + } + else + { + return complete + " B "; + } + } + + private void pad( StringBuilder buffer, int spaces ) + { + String block = " "; + while ( spaces > 0 ) + { + int n = Math.min( spaces, block.length() ); + buffer.append( block, 0, n ); + spaces -= n; + } + } + + @Override + public void transferSucceeded( TransferEvent event ) + { + transferCompleted( event ); + + TransferResource resource = event.getResource(); + long contentLength = event.getTransferredBytes(); + if ( contentLength >= 0 ) + { + String type = ( event.getRequestType() == TransferEvent.RequestType.PUT ? "Uploaded" : "Downloaded" ); + String len = contentLength >= 1024 ? toKB( contentLength ) + " KB" : contentLength + " B"; + + String throughput = ""; + long duration = System.currentTimeMillis() - resource.getTransferStartTime(); + if ( duration > 0 ) + { + DecimalFormat format = new DecimalFormat( "0.0", new DecimalFormatSymbols( Locale.ENGLISH ) ); + double kbPerSec = ( contentLength / 1024.0 ) / ( duration / 1000.0 ); + throughput = " at " + format.format( kbPerSec ) + " KB/sec"; + } + + println( "transferSucceeded", type + ": " + resource.getRepositoryUrl() + resource.getResourceName() + " (" + + len + throughput + ")" ); + } + } + + @Override + public void transferFailed( TransferEvent event ) + { + transferCompleted( event ); + + println( "transferFailed", event.getException().getClass() + ": " + event.getException().getMessage() ); + } + + private void transferCompleted( TransferEvent event ) + { + downloads.remove( event.getResource() ); + + StringBuilder buffer = new StringBuilder( 64 ); + pad( buffer, lastLength ); + buffer.append( '\r' ); + out.println( buffer ); + } + + @Override + public void transferCorrupted( TransferEvent event ) + { + println( "transferCorrupted", event.getException().getClass() + ": " + event.getException().getMessage() ); + } + + protected long toKB( long bytes ) + { + return ( bytes + 1023 ) / 1024; + } + + private void println( String event, String message ) + { + print( event, message ); + out.println(); + } + + private void print( String event, String message ) + { + out.print( "Aether Transfer - " + event ); + if ( message != null ) + { + out.print( ": " ); + out.print( message ); + } + } +} diff --git a/maven-resolver-provider/src/test/resources/repo/org/apache/maven/its/dep-mng5324/07.20.3-SNAPSHOT/maven-metadata.xml b/maven-resolver-provider/src/test/resources/repo/org/apache/maven/its/dep-mng5324/07.20.3-SNAPSHOT/maven-metadata.xml new file mode 100644 index 000000000..9f0a7fedd --- /dev/null +++ b/maven-resolver-provider/src/test/resources/repo/org/apache/maven/its/dep-mng5324/07.20.3-SNAPSHOT/maven-metadata.xml @@ -0,0 +1,49 @@ + + + + + + org.apache.maven.its + dep-mng5324 + 07.20.3-SNAPSHOT + + + 20120809.112920 + 97 + + 20120809112920 + + + classifierA + jar + 07.20.3-20120809.112124-88 + 20120809112124 + + + classifierB + jar + 07.20.3-20120809.112920-97 + 20120809112920 + + + + \ No newline at end of file diff --git a/maven-resolver-provider/src/test/resources/repo/org/apache/maven/its/dep-mng5459/0.4.0-SNAPSHOT/maven-metadata.xml b/maven-resolver-provider/src/test/resources/repo/org/apache/maven/its/dep-mng5459/0.4.0-SNAPSHOT/maven-metadata.xml new file mode 100644 index 000000000..923f26feb --- /dev/null +++ b/maven-resolver-provider/src/test/resources/repo/org/apache/maven/its/dep-mng5459/0.4.0-SNAPSHOT/maven-metadata.xml @@ -0,0 +1,47 @@ + + + + + + org.apache.maven.its + dep-mng5459 + 0.4.0-SNAPSHOT + + + 20130404.090532 + 2 + + 20130404093657 + + + pom + 0.4.0-20130404.090532-2 + 20130404090532 + + + jar + 0.4.0-20130404.093655-3 + 20130404093655 + + + + \ No newline at end of file diff --git a/maven-resolver-provider/src/test/resources/repo/ut/simple/artifact/1.0/artifact-1.0-classifier.zip b/maven-resolver-provider/src/test/resources/repo/ut/simple/artifact/1.0/artifact-1.0-classifier.zip new file mode 100644 index 000000000..e69de29bb diff --git a/maven-resolver-provider/src/test/resources/repo/ut/simple/artifact/1.0/artifact-1.0.jar b/maven-resolver-provider/src/test/resources/repo/ut/simple/artifact/1.0/artifact-1.0.jar new file mode 100644 index 000000000..e69de29bb diff --git a/maven-resolver-provider/src/test/resources/repo/ut/simple/artifact/1.0/artifact-1.0.pom b/maven-resolver-provider/src/test/resources/repo/ut/simple/artifact/1.0/artifact-1.0.pom new file mode 100644 index 000000000..f8b72af51 --- /dev/null +++ b/maven-resolver-provider/src/test/resources/repo/ut/simple/artifact/1.0/artifact-1.0.pom @@ -0,0 +1,48 @@ + + + + + + 4.0.0 + + + ut.simple + parent + 1.0 + + + artifact + + Simple Unit Test Artifact + + + + ut.simple + dependency + + + ut.simple + dependency + 1.0 + sources + + + \ No newline at end of file diff --git a/maven-resolver-provider/src/test/resources/repo/ut/simple/artifact/1.0/artifact-1.0.zip b/maven-resolver-provider/src/test/resources/repo/ut/simple/artifact/1.0/artifact-1.0.zip new file mode 100644 index 000000000..e69de29bb diff --git a/maven-resolver-provider/src/test/resources/repo/ut/simple/artifact/maven-metadata.xml b/maven-resolver-provider/src/test/resources/repo/ut/simple/artifact/maven-metadata.xml new file mode 100644 index 000000000..2de7eccf4 --- /dev/null +++ b/maven-resolver-provider/src/test/resources/repo/ut/simple/artifact/maven-metadata.xml @@ -0,0 +1,34 @@ + + + + + + ut.simple + artifact + + 1.0 + 1.0 + + 1.0 + + 20111123122038 + + \ No newline at end of file diff --git a/maven-resolver-provider/src/test/resources/repo/ut/simple/dependency/1.0/dependency-1.0-sources.jar b/maven-resolver-provider/src/test/resources/repo/ut/simple/dependency/1.0/dependency-1.0-sources.jar new file mode 100644 index 000000000..e69de29bb diff --git a/maven-resolver-provider/src/test/resources/repo/ut/simple/dependency/1.0/dependency-1.0.jar b/maven-resolver-provider/src/test/resources/repo/ut/simple/dependency/1.0/dependency-1.0.jar new file mode 100644 index 000000000..e69de29bb diff --git a/maven-resolver-provider/src/test/resources/repo/ut/simple/dependency/1.0/dependency-1.0.pom b/maven-resolver-provider/src/test/resources/repo/ut/simple/dependency/1.0/dependency-1.0.pom new file mode 100644 index 000000000..c021dfac1 --- /dev/null +++ b/maven-resolver-provider/src/test/resources/repo/ut/simple/dependency/1.0/dependency-1.0.pom @@ -0,0 +1,31 @@ + + + + + + 4.0.0 + + ut.simple + dependency + 1.0 + + Simple Unit Test Dependency + \ No newline at end of file diff --git a/maven-resolver-provider/src/test/resources/repo/ut/simple/dependency/maven-metadata.xml b/maven-resolver-provider/src/test/resources/repo/ut/simple/dependency/maven-metadata.xml new file mode 100644 index 000000000..8a97c3411 --- /dev/null +++ b/maven-resolver-provider/src/test/resources/repo/ut/simple/dependency/maven-metadata.xml @@ -0,0 +1,34 @@ + + + + + + ut.simple + dependency + + 1.0 + 1.0 + + 1.0 + + 20111123122038 + + \ No newline at end of file diff --git a/maven-resolver-provider/src/test/resources/repo/ut/simple/parent/1.0/parent-1.0.pom b/maven-resolver-provider/src/test/resources/repo/ut/simple/parent/1.0/parent-1.0.pom new file mode 100644 index 000000000..ed64c9e69 --- /dev/null +++ b/maven-resolver-provider/src/test/resources/repo/ut/simple/parent/1.0/parent-1.0.pom @@ -0,0 +1,43 @@ + + + + + + 4.0.0 + + ut.simple + parent + 1.0 + pom + + Simple Unit Test Parent + + + + + ut.simple + dependency + 1.0 + + + + + \ No newline at end of file diff --git a/maven-resolver-provider/src/test/resources/repo/ut/simple/parent/maven-metadata.xml b/maven-resolver-provider/src/test/resources/repo/ut/simple/parent/maven-metadata.xml new file mode 100644 index 000000000..7199d52a3 --- /dev/null +++ b/maven-resolver-provider/src/test/resources/repo/ut/simple/parent/maven-metadata.xml @@ -0,0 +1,34 @@ + + + + + + ut.simple + parent + + 1.0 + 1.0 + + 1.0 + + 20111123122038 + + \ No newline at end of file diff --git a/maven-resolver-transport-wagon/pom.xml b/maven-resolver-transport-wagon/pom.xml index dae9129e0..6c850677c 100644 --- a/maven-resolver-transport-wagon/pom.xml +++ b/maven-resolver-transport-wagon/pom.xml @@ -36,7 +36,7 @@ - 1.0 + 2.10 @@ -72,13 +72,13 @@ org.codehaus.plexus plexus-classworlds - 2.4 + 2.5.2 true org.codehaus.plexus plexus-utils - 2.1 + 3.0.22 true diff --git a/pom.xml b/pom.xml index 824896a92..259e131da 100644 --- a/pom.xml +++ b/pom.xml @@ -62,6 +62,7 @@ + 3.3.9 true resolver-archives/resolver-LATEST UnusedImports,LineLength,InnerAssignment,MagicNumber,AvoidNestedBlocks,ParameterNumber,MethodLength,MemberName @@ -79,10 +80,28 @@ maven-resolver-transport-file maven-resolver-transport-http maven-resolver-transport-wagon + maven-resolver-provider + + + org.apache.maven + maven-model + ${version.of.maven} + + + org.apache.maven + maven-model-builder + ${version.of.maven} + + + org.apache.maven + maven-repository-metadata + ${version.of.maven} + + org.apache.maven.resolver maven-resolver-api @@ -150,12 +169,12 @@ org.eclipse.sisu org.eclipse.sisu.inject - 0.1.1 + 0.3.2 org.eclipse.sisu org.eclipse.sisu.plexus - 0.1.1 + 0.3.2 javax.enterprise @@ -166,7 +185,7 @@ org.sonatype.sisu sisu-guice - 3.1.6 + 3.2.2 no_aop