diff --git a/maven-resolver-provider/pom.xml b/maven-resolver-provider/pom.xml index 15a63bd68e21..8e3d4bf17d7f 100644 --- a/maven-resolver-provider/pom.xml +++ b/maven-resolver-provider/pom.xml @@ -78,6 +78,21 @@ under the License. javax.inject provided + + org.eclipse.sisu + org.eclipse.sisu.plexus + provided + + + org.eclipse.sisu + org.eclipse.sisu.inject + provided + + + com.google.inject + guice + provided + @@ -110,21 +125,6 @@ under the License. slf4j-simple test - - org.eclipse.sisu - org.eclipse.sisu.plexus - test - - - org.eclipse.sisu - org.eclipse.sisu.inject - test - - - com.google.inject - guice - test - 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 index 0d40d70d7736..d45ebeba1d10 100644 --- 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 @@ -28,9 +28,7 @@ import java.util.Properties; import java.util.Set; -import org.apache.maven.model.DistributionManagement; import org.apache.maven.model.Model; -import org.apache.maven.model.Relocation; import org.apache.maven.model.building.ArtifactModelSource; import org.apache.maven.model.building.DefaultModelBuildingRequest; import org.apache.maven.model.building.ModelBuilder; @@ -64,16 +62,13 @@ import org.eclipse.aether.resolution.VersionResolutionException; import org.eclipse.aether.resolution.VersionResult; import org.eclipse.aether.transfer.ArtifactNotFoundException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** + * Default artifact descriptor reader. */ @Named @Singleton public class DefaultArtifactDescriptorReader implements ArtifactDescriptorReader { - private static final Logger LOGGER = LoggerFactory.getLogger(DefaultArtifactDescriptorReader.class); - private final RemoteRepositoryManager remoteRepositoryManager; private final VersionResolver versionResolver; private final VersionRangeResolver versionRangeResolver; @@ -81,6 +76,8 @@ public class DefaultArtifactDescriptorReader implements ArtifactDescriptorReader private final RepositoryEventDispatcher repositoryEventDispatcher; private final ModelBuilder modelBuilder; private final ModelCacheFactory modelCacheFactory; + private final Map artifactRelocationSources; + private final ArtifactDescriptorReaderDelegate delegate; @Inject public DefaultArtifactDescriptorReader( @@ -90,7 +87,8 @@ public DefaultArtifactDescriptorReader( ArtifactResolver artifactResolver, ModelBuilder modelBuilder, RepositoryEventDispatcher repositoryEventDispatcher, - ModelCacheFactory modelCacheFactory) { + ModelCacheFactory modelCacheFactory, + Map artifactRelocationSources) { this.remoteRepositoryManager = Objects.requireNonNull(remoteRepositoryManager, "remoteRepositoryManager cannot be null"); this.versionResolver = Objects.requireNonNull(versionResolver, "versionResolver cannot be null"); @@ -100,6 +98,9 @@ public DefaultArtifactDescriptorReader( this.repositoryEventDispatcher = Objects.requireNonNull(repositoryEventDispatcher, "repositoryEventDispatcher cannot be null"); this.modelCacheFactory = Objects.requireNonNull(modelCacheFactory, "modelCacheFactory cannot be null"); + this.artifactRelocationSources = + Objects.requireNonNull(artifactRelocationSources, "artifactRelocationSources cannot be null"); + this.delegate = new ArtifactDescriptorReaderDelegate(); } @Override @@ -114,7 +115,7 @@ public ArtifactDescriptorResult readArtifactDescriptor( (ArtifactDescriptorReaderDelegate) config.get(ArtifactDescriptorReaderDelegate.class.getName()); if (delegate == null) { - delegate = new ArtifactDescriptorReaderDelegate(); + delegate = this.delegate; } delegate.populateResult(session, result, model); @@ -236,16 +237,10 @@ private Model loadPom( throw new ArtifactDescriptorException(result); } - Relocation relocation = getRelocation(model); - - if (relocation != null) { + Artifact relocatedArtifact = getRelocation(session, request, model); + if (relocatedArtifact != null) { result.addRelocation(a); - a = new RelocatedArtifact( - a, - relocation.getGroupId(), - relocation.getArtifactId(), - relocation.getVersion(), - relocation.getMessage()); + a = relocatedArtifact; result.setArtifact(a); } else { return model; @@ -264,13 +259,15 @@ private Properties toProperties(Map dominant, Map 0) ? groupId : null; - this.artifactId = (artifactId != null && artifactId.length() > 0) ? artifactId : null; - this.version = (version != null && version.length() > 0) ? version : null; - this.message = (message != null && message.length() > 0) ? message : null; + this.groupId = (groupId != null && !groupId.isEmpty()) ? groupId : null; + this.artifactId = (artifactId != null && !artifactId.isEmpty()) ? artifactId : null; + this.classifier = (classifier != null && !classifier.isEmpty()) ? classifier : null; + this.extension = (extension != null && !extension.isEmpty()) ? extension : null; + this.version = (version != null && !version.isEmpty()) ? version : null; + this.message = (message != null && !message.isEmpty()) ? message : null; } @Override @@ -65,6 +78,24 @@ public String getArtifactId() { } } + @Override + public String getClassifier() { + if (classifier != null) { + return classifier; + } else { + return artifact.getClassifier(); + } + } + + @Override + public String getExtension() { + if (extension != null) { + return extension; + } else { + return artifact.getExtension(); + } + } + @Override public String getVersion() { if (version != null) { @@ -81,7 +112,7 @@ public Artifact setVersion(String version) { if (current.equals(version) || (version == null && current.length() <= 0)) { return this; } - return new RelocatedArtifact(artifact, groupId, artifactId, version, message); + return new RelocatedArtifact(artifact, groupId, artifactId, classifier, extension, version, message); } @Override @@ -90,7 +121,8 @@ public Artifact setFile(File file) { if (Objects.equals(current, file)) { return this; } - return new RelocatedArtifact(artifact.setFile(file), groupId, artifactId, version, message); + return new RelocatedArtifact( + artifact.setFile(file), groupId, artifactId, classifier, extension, version, message); } @Override @@ -99,17 +131,8 @@ public Artifact setProperties(Map properties) { if (current.equals(properties) || (properties == null && current.isEmpty())) { return this; } - return new RelocatedArtifact(artifact.setProperties(properties), groupId, artifactId, version, message); - } - - @Override - public String getClassifier() { - return artifact.getClassifier(); - } - - @Override - public String getExtension() { - return artifact.getExtension(); + return new RelocatedArtifact( + artifact.setProperties(properties), groupId, artifactId, classifier, extension, version, message); } @Override diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/relocation/DistributionManagementArtifactRelocationSource.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/relocation/DistributionManagementArtifactRelocationSource.java new file mode 100644 index 000000000000..e4033c9dec7d --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/relocation/DistributionManagementArtifactRelocationSource.java @@ -0,0 +1,75 @@ +/* + * 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. + */ +package org.apache.maven.repository.internal.relocation; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.model.DistributionManagement; +import org.apache.maven.model.Model; +import org.apache.maven.model.Relocation; +import org.apache.maven.repository.internal.MavenArtifactRelocationSource; +import org.apache.maven.repository.internal.RelocatedArtifact; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.resolution.ArtifactDescriptorRequest; +import org.eclipse.sisu.Priority; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Relocation source from standard distribution management. This is the "one and only" relocation implementation that + * existed in Maven 3 land, uses POM distributionManagement/relocation. + *

+ * Note: this component should kick-in last regarding relocations. + * + * @since 4.0.0 + */ +@Singleton +@Named +@Priority(5) +@SuppressWarnings("checkstyle:MagicNumber") +public final class DistributionManagementArtifactRelocationSource implements MavenArtifactRelocationSource { + private static final Logger LOGGER = LoggerFactory.getLogger(DistributionManagementArtifactRelocationSource.class); + + @Override + public Artifact relocatedTarget(RepositorySystemSession session, ArtifactDescriptorRequest request, Model model) { + DistributionManagement distMgmt = model.getDistributionManagement(); + if (distMgmt != null) { + Relocation relocation = distMgmt.getRelocation(); + if (relocation != null) { + Artifact result = new RelocatedArtifact( + request.getArtifact(), + relocation.getGroupId(), + relocation.getArtifactId(), + null, + null, + relocation.getVersion(), + relocation.getMessage()); + LOGGER.debug( + "The artifact {} has been relocated to {}: {}", + request.getArtifact(), + result, + relocation.getMessage()); + return result; + } + } + return null; + } +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/relocation/UserPropertiesArtifactRelocationSource.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/relocation/UserPropertiesArtifactRelocationSource.java new file mode 100644 index 000000000000..fd3710b9e93b --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/relocation/UserPropertiesArtifactRelocationSource.java @@ -0,0 +1,201 @@ +/* + * 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. + */ +package org.apache.maven.repository.internal.relocation; + +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.maven.model.Model; +import org.apache.maven.repository.internal.MavenArtifactRelocationSource; +import org.apache.maven.repository.internal.RelocatedArtifact; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.resolution.ArtifactDescriptorRequest; +import org.eclipse.sisu.Priority; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Relocation source from user properties. + * + * @since 4.0.0 + */ +@Singleton +@Named +@Priority(50) +@SuppressWarnings("checkstyle:MagicNumber") +public final class UserPropertiesArtifactRelocationSource implements MavenArtifactRelocationSource { + private static final Logger LOGGER = LoggerFactory.getLogger(UserPropertiesArtifactRelocationSource.class); + + private static final String CONFIG_PROP_RELOCATIONS_ENTRIES = "maven.relocations.entries"; + + @Override + public Artifact relocatedTarget(RepositorySystemSession session, ArtifactDescriptorRequest request, Model model) { + Relocations relocations = (Relocations) session.getData() + .computeIfAbsent(getClass().getName() + ".relocations", () -> parseRelocations(session)); + if (relocations != null) { + Relocation relocation = relocations.getRelocation(request.getArtifact()); + if (relocation != null && (isProjectContext(request.getRequestContext()) || relocation.global)) { + Artifact result = new RelocatedArtifact( + request.getArtifact(), + isAny(relocation.target.getGroupId()) ? null : relocation.target.getGroupId(), + isAny(relocation.target.getArtifactId()) ? null : relocation.target.getArtifactId(), + isAny(relocation.target.getClassifier()) ? null : relocation.target.getClassifier(), + isAny(relocation.target.getExtension()) ? null : relocation.target.getExtension(), + isAny(relocation.target.getVersion()) ? null : relocation.target.getVersion(), + relocation.global ? "User global relocation" : "User project relocation"); + LOGGER.debug( + "The artifact {} has been relocated to {}: {}", + request.getArtifact(), + result, + relocation.global ? "User global relocation" : "User project relocation"); + return result; + } + } + return null; + } + + private boolean isProjectContext(String context) { + return context != null && context.startsWith("project"); + } + + private static boolean isAny(String str) { + return "*".equals(str); + } + + private static boolean matches(String pattern, String str) { + if (isAny(pattern)) { + return true; + } else if (pattern.endsWith("*")) { + return str.startsWith(pattern.substring(0, pattern.length() - 1)); + } else { + return Objects.equals(pattern, str); + } + } + + private static Predicate artifactPredicate(Artifact artifact) { + return a -> matches(artifact.getGroupId(), a.getGroupId()) + && matches(artifact.getArtifactId(), a.getArtifactId()) + && matches(artifact.getBaseVersion(), a.getBaseVersion()) + && matches(artifact.getExtension(), a.getExtension()) + && matches(artifact.getClassifier(), a.getClassifier()); + } + + private static class Relocation { + private final Predicate predicate; + private final boolean global; + private final Artifact source; + private final Artifact target; + + private Relocation(boolean global, Artifact source, Artifact target) { + this.predicate = artifactPredicate(source); + this.global = global; + this.source = source; + this.target = target; + } + + @Override + public String toString() { + return source + (global ? " >> " : " > ") + target; + } + } + + private static class Relocations { + private final List relocations; + + private Relocations(List relocations) { + this.relocations = relocations; + } + + private Relocation getRelocation(Artifact artifact) { + return relocations.stream() + .filter(r -> r.predicate.test(artifact)) + .findFirst() + .orElse(null); + } + } + + private Relocations parseRelocations(RepositorySystemSession session) { + String relocationsEntries = (String) session.getConfigProperties().get(CONFIG_PROP_RELOCATIONS_ENTRIES); + if (relocationsEntries == null) { + return null; + } + String[] entries = relocationsEntries.split(","); + try (Stream lines = Arrays.stream(entries)) { + List relocationList = lines.filter( + l -> l != null && !l.trim().isEmpty()) + .map(l -> { + boolean global; + String splitExpr; + if (l.contains(">>")) { + global = true; + splitExpr = ">>"; + } else if (l.contains(">")) { + global = false; + splitExpr = ">"; + } else { + throw new IllegalArgumentException("Unrecognized entry: " + l); + } + String[] parts = l.split(splitExpr); + if (parts.length < 1) { + throw new IllegalArgumentException("Unrecognized entry: " + l); + } + Artifact s = parseArtifact(parts[0]); + Artifact t; + if (parts.length > 1) { + t = parseArtifact(parts[1]); + } else { + t = new DefaultArtifact("org.apache.maven.banned:user-relocation:1.0"); + } + return new Relocation(global, s, t); + }) + .collect(Collectors.toList()); + LOGGER.info("Parsed {} user relocations", relocationList.size()); + return new Relocations(relocationList); + } + } + + private static Artifact parseArtifact(String coord) { + Artifact s; + String[] parts = coord.split(":"); + switch (parts.length) { + case 3: + s = new DefaultArtifact(parts[0], parts[1], "*", "*", parts[2]); + break; + case 4: + s = new DefaultArtifact(parts[0], parts[1], "*", parts[2], parts[3]); + break; + case 5: + s = new DefaultArtifact(parts[0], parts[1], parts[2], parts[3], parts[4]); + break; + default: + throw new IllegalArgumentException("Bad artifact coordinates " + coord + + ", expected format is :[:[:]]:"); + } + return s; + } +}