You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by cs...@apache.org on 2022/09/13 18:24:41 UTC

[maven-resolver] branch remote-repository-filter updated: First sketch

This is an automated email from the ASF dual-hosted git repository.

cstamas pushed a commit to branch remote-repository-filter
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git


The following commit(s) were added to refs/heads/remote-repository-filter by this push:
     new 7d1cf6d6 First sketch
7d1cf6d6 is described below

commit 7d1cf6d66322cecdc8e1345907f6158df3a38a27
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Tue Sep 13 20:24:24 2022 +0200

    First sketch
---
 .../eclipse/aether/impl/DefaultServiceLocator.java |   3 +
 .../internal/impl/DefaultArtifactResolver.java     |  48 +++++++--
 .../internal/impl/DefaultMetadataResolver.java     |  24 ++++-
 .../DefaultRemoteRepositoryFilterSource.java       |  78 ++++++++++++++
 .../impl/filter/FilteringRepositoryConnector.java  | 113 +++++++++++++++++++++
 .../internal/impl/DefaultArtifactResolverTest.java |   2 +
 .../internal/impl/DefaultMetadataResolverTest.java |   2 +
 .../connector/filter/RemoteRepositoryFilter.java   |  51 ++++++++++
 .../filter/RemoteRepositoryFilterSource.java       |  35 +++++++
 9 files changed, 346 insertions(+), 10 deletions(-)

diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java
index 815c07cd..4a3e2b05 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java
@@ -56,12 +56,14 @@ import org.eclipse.aether.internal.impl.DefaultUpdatePolicyAnalyzer;
 import org.eclipse.aether.internal.impl.EnhancedLocalRepositoryManagerFactory;
 import org.eclipse.aether.internal.impl.Maven2RepositoryLayoutFactory;
 import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
+import org.eclipse.aether.internal.impl.filter.DefaultRemoteRepositoryFilterSource;
 import org.eclipse.aether.internal.impl.slf4j.Slf4jLoggerFactory;
 import org.eclipse.aether.internal.impl.synccontext.DefaultSyncContextFactory;
 import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactorySelector;
 import org.eclipse.aether.internal.impl.synccontext.named.SimpleNamedLockFactorySelector;
 import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource;
 import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory;
 import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider;
 import org.eclipse.aether.spi.connector.transport.TransporterProvider;
@@ -230,6 +232,7 @@ public final class DefaultServiceLocator
         addService( NamedLockFactorySelector.class, SimpleNamedLockFactorySelector.class );
         addService( ChecksumAlgorithmFactorySelector.class, DefaultChecksumAlgorithmFactorySelector.class );
         addService( LocalPathComposer.class, DefaultLocalPathComposer.class );
+        addService( RemoteRepositoryFilterSource.class, DefaultRemoteRepositoryFilterSource.class );
     }
 
     private <T> Entry<T> getEntry( Class<T> type, boolean create )
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultArtifactResolver.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultArtifactResolver.java
index 430ea381..52e948c3 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultArtifactResolver.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultArtifactResolver.java
@@ -45,6 +45,8 @@ import org.eclipse.aether.impl.OfflineController;
 import org.eclipse.aether.impl.RemoteRepositoryManager;
 import org.eclipse.aether.impl.RepositoryConnectorProvider;
 import org.eclipse.aether.impl.RepositoryEventDispatcher;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource;
 import org.eclipse.aether.spi.synccontext.SyncContextFactory;
 import org.eclipse.aether.impl.UpdateCheck;
 import org.eclipse.aether.impl.UpdateCheckManager;
@@ -106,6 +108,8 @@ public class DefaultArtifactResolver
 
     private OfflineController offlineController;
 
+    private RemoteRepositoryFilterSource remoteRepositoryFilterSource;
+
     public DefaultArtifactResolver()
     {
         // enables default constructor
@@ -117,7 +121,8 @@ public class DefaultArtifactResolver
                              VersionResolver versionResolver, UpdateCheckManager updateCheckManager,
                              RepositoryConnectorProvider repositoryConnectorProvider,
                              RemoteRepositoryManager remoteRepositoryManager, SyncContextFactory syncContextFactory,
-                             OfflineController offlineController )
+                             OfflineController offlineController,
+                             RemoteRepositoryFilterSource remoteRepositoryFilterSource )
     {
         setFileProcessor( fileProcessor );
         setRepositoryEventDispatcher( repositoryEventDispatcher );
@@ -127,6 +132,7 @@ public class DefaultArtifactResolver
         setRemoteRepositoryManager( remoteRepositoryManager );
         setSyncContextFactory( syncContextFactory );
         setOfflineController( offlineController );
+        setRemoteRepositoryFilterSource( remoteRepositoryFilterSource );
     }
 
     public void initService( ServiceLocator locator )
@@ -139,6 +145,7 @@ public class DefaultArtifactResolver
         setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) );
         setSyncContextFactory( locator.getService( SyncContextFactory.class ) );
         setOfflineController( locator.getService( OfflineController.class ) );
+        setRemoteRepositoryFilterSource( locator.getService( RemoteRepositoryFilterSource.class ) );
     }
 
     /**
@@ -203,6 +210,14 @@ public class DefaultArtifactResolver
         return this;
     }
 
+    public DefaultArtifactResolver setRemoteRepositoryFilterSource(
+            RemoteRepositoryFilterSource remoteRepositoryFilterSource )
+    {
+        this.remoteRepositoryFilterSource = requireNonNull( remoteRepositoryFilterSource,
+                "remote repository filter source cannot be null" );
+        return this;
+    }
+
     public ArtifactResult resolveArtifact( RepositorySystemSession session, ArtifactRequest request )
         throws ArtifactResolutionException
     {
@@ -257,7 +272,21 @@ public class DefaultArtifactResolver
             results.add( result );
 
             Artifact artifact = request.getArtifact();
-            List<RemoteRepository> repos = request.getRepositories();
+
+            RemoteRepositoryFilter filter = remoteRepositoryFilterSource.getRemoteRepositoryFilter( session );
+            List<RemoteRepository> filteredRepos = new ArrayList<>( request.getRepositories().size() );
+            for ( RemoteRepository repository : request.getRepositories() )
+            {
+                if ( !filter.acceptArtifact( repository, artifact ) )
+                {
+                    result.addException(  new ArtifactNotFoundException( artifact, repository,
+                            "Artifact " + artifact + " not allowed from " + repository ) );
+                }
+                else
+                {
+                    filteredRepos.add( repository );
+                }
+            }
 
             artifactResolving( session, trace, artifact );
 
@@ -283,7 +312,8 @@ public class DefaultArtifactResolver
             VersionResult versionResult;
             try
             {
-                VersionRequest versionRequest = new VersionRequest( artifact, repos, request.getRequestContext() );
+                VersionRequest versionRequest = new VersionRequest( artifact, filteredRepos,
+                        request.getRequestContext() );
                 versionRequest.setTrace( trace );
                 versionResult = versionResolver.resolveVersion( session, versionRequest );
             }
@@ -299,11 +329,11 @@ public class DefaultArtifactResolver
             {
                 if ( versionResult.getRepository() instanceof RemoteRepository )
                 {
-                    repos = Collections.singletonList( (RemoteRepository) versionResult.getRepository() );
+                    filteredRepos = Collections.singletonList( (RemoteRepository) versionResult.getRepository() );
                 }
                 else
                 {
-                    repos = Collections.emptyList();
+                    filteredRepos = Collections.emptyList();
                 }
             }
 
@@ -321,7 +351,7 @@ public class DefaultArtifactResolver
             }
 
             LocalArtifactResult local =
-                lrm.find( session, new LocalArtifactRequest( artifact, repos, request.getRequestContext() ) );
+                lrm.find( session, new LocalArtifactRequest( artifact, filteredRepos, request.getRequestContext() ) );
             if ( isLocallyInstalled( local, versionResult ) )
             {
                 if ( local.getRepository() != null )
@@ -356,13 +386,13 @@ public class DefaultArtifactResolver
             }
             else if ( local.getFile() != null )
             {
-                LOGGER.debug( "Verifying availability of {} from {}", local.getFile(), repos );
+                LOGGER.debug( "Verifying availability of {} from {}", local.getFile(), filteredRepos );
             }
 
-            LOGGER.debug( "Resolving artifact {} from {}", artifact, repos );
+            LOGGER.debug( "Resolving artifact {} from {}", artifact, filteredRepos );
             AtomicBoolean resolved = new AtomicBoolean( false );
             Iterator<ResolutionGroup> groupIt = groups.iterator();
-            for ( RemoteRepository repo : repos )
+            for ( RemoteRepository repo : filteredRepos )
             {
                 if ( !repo.getPolicy( artifact.isSnapshot() ).isEnabled() )
                 {
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultMetadataResolver.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultMetadataResolver.java
index f65f0c2f..9fc81320 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultMetadataResolver.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultMetadataResolver.java
@@ -47,6 +47,7 @@ import org.eclipse.aether.impl.OfflineController;
 import org.eclipse.aether.impl.RemoteRepositoryManager;
 import org.eclipse.aether.impl.RepositoryConnectorProvider;
 import org.eclipse.aether.impl.RepositoryEventDispatcher;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource;
 import org.eclipse.aether.spi.synccontext.SyncContextFactory;
 import org.eclipse.aether.impl.UpdateCheck;
 import org.eclipse.aether.impl.UpdateCheckManager;
@@ -95,6 +96,8 @@ public class DefaultMetadataResolver
 
     private OfflineController offlineController;
 
+    private RemoteRepositoryFilterSource remoteRepositoryFilterSource;
+
     public DefaultMetadataResolver()
     {
         // enables default constructor
@@ -105,7 +108,8 @@ public class DefaultMetadataResolver
                              UpdateCheckManager updateCheckManager,
                              RepositoryConnectorProvider repositoryConnectorProvider,
                              RemoteRepositoryManager remoteRepositoryManager, SyncContextFactory syncContextFactory,
-                             OfflineController offlineController )
+                             OfflineController offlineController,
+                             RemoteRepositoryFilterSource remoteRepositoryFilterSource )
     {
         setRepositoryEventDispatcher( repositoryEventDispatcher );
         setUpdateCheckManager( updateCheckManager );
@@ -113,6 +117,7 @@ public class DefaultMetadataResolver
         setRemoteRepositoryManager( remoteRepositoryManager );
         setSyncContextFactory( syncContextFactory );
         setOfflineController( offlineController );
+        setRemoteRepositoryFilterSource( remoteRepositoryFilterSource );
     }
 
     public void initService( ServiceLocator locator )
@@ -123,6 +128,7 @@ public class DefaultMetadataResolver
         setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) );
         setSyncContextFactory( locator.getService( SyncContextFactory.class ) );
         setOfflineController( locator.getService( OfflineController.class ) );
+        setRemoteRepositoryFilterSource( locator.getService( RemoteRepositoryFilterSource.class ) );
     }
 
     public DefaultMetadataResolver setRepositoryEventDispatcher( RepositoryEventDispatcher repositoryEventDispatcher )
@@ -165,6 +171,14 @@ public class DefaultMetadataResolver
         return this;
     }
 
+    public DefaultMetadataResolver setRemoteRepositoryFilterSource(
+            RemoteRepositoryFilterSource remoteRepositoryFilterSource )
+    {
+        this.remoteRepositoryFilterSource = requireNonNull( remoteRepositoryFilterSource,
+                "remote repository filter cannot be null" );
+        return this;
+    }
+
     public List<MetadataResult> resolveMetadata( RepositorySystemSession session,
                                                  Collection<? extends MetadataRequest> requests )
     {
@@ -226,6 +240,14 @@ public class DefaultMetadataResolver
                 continue;
             }
 
+            if ( !remoteRepositoryFilterSource.getRemoteRepositoryFilter( session )
+                    .acceptMetadata( repository, metadata ) )
+            {
+                result.setException( new MetadataNotFoundException( metadata, repository,
+                        "Metadata " + metadata + " not allowed from " + repository ) );
+                continue;
+            }
+
             List<RemoteRepository> repositories = getEnabledSourceRepositories( repository, metadata.getNature() );
 
             if ( repositories.isEmpty() )
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/DefaultRemoteRepositoryFilterSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/DefaultRemoteRepositoryFilterSource.java
new file mode 100644
index 00000000..2b18673c
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/DefaultRemoteRepositoryFilterSource.java
@@ -0,0 +1,78 @@
+package org.eclipse.aether.internal.impl.filter;
+
+/*
+ * 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 javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.metadata.Metadata;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource;
+import org.eclipse.aether.util.ConfigUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A filtering connector that filters transfers and delegates to another connector.
+ *
+ * @since TBD
+ */
+@Singleton
+@Named
+public final class DefaultRemoteRepositoryFilterSource
+        implements RemoteRepositoryFilterSource
+{
+    private static final String CONFIG_PROP_VALUE = "aether.artifactResolver.remoteRepositoryFilterSource.filter";
+
+    private static final Logger LOGGER = LoggerFactory.getLogger( DefaultRemoteRepositoryFilterSource.class );
+
+    private static final class StaticRemoteRepositoryFilter implements RemoteRepositoryFilter
+    {
+        private final boolean value;
+
+        private StaticRemoteRepositoryFilter( boolean value )
+        {
+            this.value = value;
+        }
+
+        @Override
+        public boolean acceptArtifact( RemoteRepository remoteRepository, Artifact artifact )
+        {
+            return value;
+        }
+
+        @Override
+        public boolean acceptMetadata( RemoteRepository remoteRepository, Metadata metadata )
+        {
+            return value;
+        }
+    }
+
+    @Override
+    public RemoteRepositoryFilter getRemoteRepositoryFilter( RepositorySystemSession session )
+    {
+        boolean value = ConfigUtils.getBoolean( session, true, CONFIG_PROP_VALUE );
+        LOGGER.debug( "Creating static filter with value={}", value );
+        return new StaticRemoteRepositoryFilter( value );
+    }
+}
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/FilteringRepositoryConnector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/FilteringRepositoryConnector.java
new file mode 100644
index 00000000..406e920a
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/FilteringRepositoryConnector.java
@@ -0,0 +1,113 @@
+package org.eclipse.aether.internal.impl.filter;
+
+/*
+ * 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.Collection;
+
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.spi.connector.ArtifactDownload;
+import org.eclipse.aether.spi.connector.ArtifactUpload;
+import org.eclipse.aether.spi.connector.MetadataDownload;
+import org.eclipse.aether.spi.connector.MetadataUpload;
+import org.eclipse.aether.spi.connector.RepositoryConnector;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * A filtering connector that filters transfers and delegates to another connector.
+ *
+ * @since TBD
+ */
+public final class FilteringRepositoryConnector
+        implements RepositoryConnector
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger( FilteringRepositoryConnector.class );
+
+    private final RemoteRepository remoteRepository;
+
+    private final RepositoryConnector repositoryConnector;
+
+    private final RemoteRepositoryFilter remoteRepositoryFilter;
+
+    public FilteringRepositoryConnector( RemoteRepository remoteRepository,
+                                  RepositoryConnector repositoryConnector,
+                                  RemoteRepositoryFilter remoteRepositoryFilter )
+    {
+        this.remoteRepository = requireNonNull( remoteRepository );
+        this.repositoryConnector = requireNonNull( repositoryConnector );
+        this.remoteRepositoryFilter = requireNonNull( remoteRepositoryFilter );
+    }
+
+    @Override
+    public void close()
+    {
+        repositoryConnector.close();
+    }
+
+    @Override
+    public void get( Collection<? extends ArtifactDownload> artifactDownloads,
+                     Collection<? extends MetadataDownload> metadataDownloads )
+    {
+        ArrayList<ArtifactDownload> filteredArtifactDownloads = new ArrayList<>( artifactDownloads.size() );
+        for ( ArtifactDownload artifactDownload : artifactDownloads )
+        {
+            if ( remoteRepositoryFilter.acceptArtifact( remoteRepository, artifactDownload.getArtifact() ) )
+            {
+                filteredArtifactDownloads.add( artifactDownload );
+            }
+            else
+            {
+                LOGGER.debug( "Artifact {} filtered out from remote repository {}",
+                        artifactDownload.getArtifact(), remoteRepository );
+            }
+        }
+        ArrayList<MetadataDownload> filteredMetadataDownloads = new ArrayList<>( metadataDownloads.size() );
+        for ( MetadataDownload metadataDownload : metadataDownloads )
+        {
+            if ( remoteRepositoryFilter.acceptMetadata( remoteRepository, metadataDownload.getMetadata() ) )
+            {
+                filteredMetadataDownloads.add( metadataDownload );
+            }
+            else
+            {
+                LOGGER.debug( "Metadata {} filtered out from remote repository {}",
+                        metadataDownload.getMetadata(), remoteRepository );
+            }
+        }
+        repositoryConnector.get( filteredArtifactDownloads, filteredMetadataDownloads );
+    }
+
+    @Override
+    public void put( Collection<? extends ArtifactUpload> artifactUploads,
+                     Collection<? extends MetadataUpload> metadataUploads )
+    {
+        repositoryConnector.put( artifactUploads, metadataUploads );
+    }
+
+    @Override
+    public String toString()
+    {
+        return "filtered(" + repositoryConnector.toString() + ")";
+    }
+}
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java
index 65ad4880..d5b3238b 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java
@@ -38,6 +38,7 @@ import org.eclipse.aether.artifact.ArtifactProperties;
 import org.eclipse.aether.artifact.DefaultArtifact;
 import org.eclipse.aether.impl.UpdateCheckManager;
 import org.eclipse.aether.impl.VersionResolver;
+import org.eclipse.aether.internal.impl.filter.DefaultRemoteRepositoryFilterSource;
 import org.eclipse.aether.internal.test.util.TestFileProcessor;
 import org.eclipse.aether.internal.test.util.TestFileUtils;
 import org.eclipse.aether.internal.test.util.TestLocalRepositoryManager;
@@ -103,6 +104,7 @@ public class DefaultArtifactResolverTest
         resolver.setRemoteRepositoryManager( new StubRemoteRepositoryManager() );
         resolver.setSyncContextFactory( new StubSyncContextFactory() );
         resolver.setOfflineController( new DefaultOfflineController() );
+        resolver.setRemoteRepositoryFilterSource( new DefaultRemoteRepositoryFilterSource() );
 
         artifact = new DefaultArtifact( "gid", "aid", "", "ext", "ver" );
 
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultMetadataResolverTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultMetadataResolverTest.java
index 27fec64c..e0c26aa9 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultMetadataResolverTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultMetadataResolverTest.java
@@ -31,6 +31,7 @@ import java.util.Set;
 
 import org.eclipse.aether.DefaultRepositorySystemSession;
 import org.eclipse.aether.internal.impl.DefaultMetadataResolver;
+import org.eclipse.aether.internal.impl.filter.DefaultRemoteRepositoryFilterSource;
 import org.eclipse.aether.internal.test.util.TestFileUtils;
 import org.eclipse.aether.internal.test.util.TestLocalRepositoryManager;
 import org.eclipse.aether.internal.test.util.TestUtils;
@@ -80,6 +81,7 @@ public class DefaultMetadataResolverTest
         resolver.setRemoteRepositoryManager( new StubRemoteRepositoryManager() );
         resolver.setSyncContextFactory( new StubSyncContextFactory() );
         resolver.setOfflineController( new DefaultOfflineController() );
+        resolver.setRemoteRepositoryFilterSource( new DefaultRemoteRepositoryFilterSource() );
         repository =
             new RemoteRepository.Builder( "test-DMRT", "default",
                                           TestFileUtils.createTempDir().toURI().toURL().toString() ).build();
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/filter/RemoteRepositoryFilter.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/filter/RemoteRepositoryFilter.java
new file mode 100644
index 00000000..e04bd13a
--- /dev/null
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/filter/RemoteRepositoryFilter.java
@@ -0,0 +1,51 @@
+package org.eclipse.aether.spi.connector.filter;
+
+/*
+ * 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.metadata.Metadata;
+import org.eclipse.aether.repository.RemoteRepository;
+
+/**
+ * Remote repository filter that decides should have given artifact or metadata be accepted (for further processing)
+ * from given remote repository.
+ *
+ * @since TBD
+ */
+public interface RemoteRepositoryFilter
+{
+    /**
+     * Decides should artifact be accepted from given remote repository.
+     *
+     * @param remoteRepository The remote repository, not {@code null}.
+     * @param artifact         The artifact, not {@code null}.
+     * @return {@code true} if the artifact should be accepted.
+     */
+    boolean acceptArtifact( RemoteRepository remoteRepository, Artifact artifact );
+
+    /**
+     * Decides should metadata be accepted from given remote repository.
+     *
+     * @param remoteRepository The remote repository, not {@code null}.
+     * @param metadata         The artifact, not {@code null}.
+     * @return {@code true} if the metadata should be accepted.
+     */
+    boolean acceptMetadata( RemoteRepository remoteRepository, Metadata metadata );
+}
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/filter/RemoteRepositoryFilterSource.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/filter/RemoteRepositoryFilterSource.java
new file mode 100644
index 00000000..377a8000
--- /dev/null
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/filter/RemoteRepositoryFilterSource.java
@@ -0,0 +1,35 @@
+package org.eclipse.aether.spi.connector.filter;
+
+/*
+ * 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.RepositorySystemSession;
+
+/**
+ * Remote repository filter source component.
+ *
+ * @since TBD
+ */
+public interface RemoteRepositoryFilterSource
+{
+    /**
+     * Provides the filter, never {@code null}.
+     */
+    RemoteRepositoryFilter getRemoteRepositoryFilter( RepositorySystemSession session );
+}