You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@archiva.apache.org by ma...@apache.org on 2019/08/25 11:15:29 UTC

[archiva] 01/02: Adding stream methods for artifact search

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

martin_s pushed a commit to branch feature/storage_refactoring
in repository https://gitbox.apache.org/repos/asf/archiva.git

commit 63cbe9b685a17483cdc88dbfe833fa1e14094843
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Sat Aug 24 22:11:44 2019 +0200

    Adding stream methods for artifact search
---
 archiva-modules/metadata/metadata-model/pom.xml    |   4 +
 .../archiva/metadata/model/ArtifactMetadata.java   |   7 +-
 .../repository/AbstractMetadataRepository.java     |  42 ++++-
 .../metadata/repository/MetadataRepository.java    |  73 ++++++++-
 .../repository/AbstractMetadataRepositoryTest.java |  56 ++++++-
 .../cassandra/CassandraMetadataRepository.java     |   7 +-
 .../repository/file/FileMetadataRepository.java    | 172 +++++++++++++++------
 .../file/FileMetadataRepositoryTest.java           |   2 +
 .../repository/jcr/JcrMetadataRepository.java      |   2 +-
 9 files changed, 302 insertions(+), 63 deletions(-)

diff --git a/archiva-modules/metadata/metadata-model/pom.xml b/archiva-modules/metadata/metadata-model/pom.xml
index 4d81ad9..b4894b3 100644
--- a/archiva-modules/metadata/metadata-model/pom.xml
+++ b/archiva-modules/metadata/metadata-model/pom.xml
@@ -40,6 +40,10 @@
       <groupId>org.apache.geronimo.specs</groupId>
       <artifactId>geronimo-jpa_2.0_spec</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-collections4</artifactId>
+    </dependency>
   </dependencies>
 
 
diff --git a/archiva-modules/metadata/metadata-model/src/main/java/org/apache/archiva/metadata/model/ArtifactMetadata.java b/archiva-modules/metadata/metadata-model/src/main/java/org/apache/archiva/metadata/model/ArtifactMetadata.java
index fd44feb..d8e9290 100644
--- a/archiva-modules/metadata/metadata-model/src/main/java/org/apache/archiva/metadata/model/ArtifactMetadata.java
+++ b/archiva-modules/metadata/metadata-model/src/main/java/org/apache/archiva/metadata/model/ArtifactMetadata.java
@@ -20,6 +20,7 @@ package org.apache.archiva.metadata.model;
  */
 
 import org.apache.archiva.checksum.ChecksumAlgorithm;
+import org.apache.commons.collections4.bidimap.DualHashBidiMap;
 import sun.reflect.generics.repository.MethodRepository;
 
 import javax.xml.bind.annotation.XmlRootElement;
@@ -99,7 +100,7 @@ public class ArtifactMetadata
     /**
      * The list of checksums.
      */
-    private Map<ChecksumAlgorithm, String> checksums = new HashMap<>();
+    private Map<ChecksumAlgorithm, String> checksums = new DualHashBidiMap<>( );
 
     private String toStringValue = "";
     private int lastHash = 0;
@@ -177,6 +178,10 @@ public class ArtifactMetadata
         return this.checksums;
     }
 
+    public boolean hasChecksum(String checksum) {
+        return this.checksums.containsValue( checksum );
+    }
+
     public void setChecksums(Map<ChecksumAlgorithm,String> checksums) {
         this.checksums = checksums;
     }
diff --git a/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/AbstractMetadataRepository.java b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/AbstractMetadataRepository.java
index c4e2710..bcb3e83 100644
--- a/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/AbstractMetadataRepository.java
+++ b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/AbstractMetadataRepository.java
@@ -28,6 +28,8 @@ import org.apache.archiva.metadata.model.ProjectVersionMetadata;
 import org.apache.archiva.metadata.model.ProjectVersionReference;
 import org.apache.commons.collections4.ComparatorUtils;
 
+import javax.annotation.Nonnull;
+import javax.management.Query;
 import java.time.ZonedDateTime;
 import java.util.*;
 import java.util.stream.Stream;
@@ -342,6 +344,10 @@ public abstract class AbstractMetadataRepository
         throw new UnsupportedOperationException();
     }
 
+    protected QueryParameter getParameterOrDefault(QueryParameter queryParameter) {
+        return queryParameter == null ? new QueryParameter( ) : queryParameter;
+    }
+
     @Override
     public <T extends MetadataFacet> Stream<T> getMetadataFacetStream( RepositorySession session, String repositoryId, Class<T> facetClazz ) throws MetadataRepositoryException
     {
@@ -349,9 +355,9 @@ public abstract class AbstractMetadataRepository
     }
 
     @Override
-    public Stream<ArtifactMetadata> getArtifactsByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime ) throws MetadataRepositoryException
+    public Stream<ArtifactMetadata> getArtifactByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime ) throws MetadataRepositoryException
     {
-        return getArtifactsByDateRangeStream( session, repositoryId, startTime, endTime, new QueryParameter());
+        return getArtifactByDateRangeStream( session, repositoryId, startTime, endTime, new QueryParameter());
     }
 
     @Override
@@ -376,7 +382,7 @@ public abstract class AbstractMetadataRepository
 
 
     @Override
-    public Stream<ArtifactMetadata> getArtifactsByDateRangeStream(RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException
+    public Stream<ArtifactMetadata> getArtifactByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException
     {
         throw new UnsupportedOperationException();
     }
@@ -403,7 +409,35 @@ public abstract class AbstractMetadataRepository
     @Override
     public List<ArtifactMetadata> getArtifactsByDateRange(RepositorySession session, String repoId, ZonedDateTime startTime, ZonedDateTime endTime)
             throws MetadataRepositoryException {
-        return getArtifactsByDateRange(session, repoId, startTime, endTime, new QueryParameter());
+        return getArtifactsByDateRange(session, repoId, startTime, endTime, getParameterOrDefault( null ));
+    }
+
+    @Override
+    public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repositoryId ) throws MetadataResolutionException
+    {
+        return getArtifactStream( session, repositoryId, getParameterOrDefault( null ) );
+    }
+
+    @Override
+    public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repoId,
+                                                       final String namespace, final String projectId,
+                                                       final String projectVersion) throws MetadataResolutionException
+    {
+        return getArtifactStream( session,repoId,namespace, projectId, projectVersion, getParameterOrDefault( null ));
+    }
+
+    @Override
+    public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repositoryId, QueryParameter queryParameter ) throws MetadataResolutionException
+    {
+        throw new UnsupportedOperationException( );
+    }
+
+    @Override
+    public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repoId,
+                                                       final String namespace, final String projectId,
+                                                       final String projectVersion, final QueryParameter queryParameter ) throws MetadataResolutionException
+    {
+        throw new UnsupportedOperationException( );
     }
 
 }
diff --git a/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataRepository.java b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataRepository.java
index e679704..da54715 100644
--- a/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataRepository.java
+++ b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataRepository.java
@@ -26,7 +26,8 @@ import org.apache.archiva.metadata.model.ProjectMetadata;
 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
 import org.apache.archiva.metadata.model.ProjectVersionReference;
 
-import java.time.ZoneId;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import java.time.ZonedDateTime;
 import java.util.Collection;
 import java.util.List;
@@ -319,7 +320,7 @@ public interface MetadataRepository
      * @throws MetadataRepositoryException
      * @since 3.0
      */
-    Stream<ArtifactMetadata> getArtifactsByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime )
+    Stream<ArtifactMetadata> getArtifactByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime )
         throws MetadataRepositoryException;
 
     /**
@@ -336,7 +337,7 @@ public interface MetadataRepository
      * @throws MetadataRepositoryException
      * @since 3.0
      */
-    Stream<ArtifactMetadata> getArtifactsByDateRangeStream(RepositorySession session, String repositoryId,
+    Stream<ArtifactMetadata> getArtifactByDateRangeStream( RepositorySession session, String repositoryId,
                                                            ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter)
         throws MetadataRepositoryException;
 
@@ -454,6 +455,72 @@ public interface MetadataRepository
         throws MetadataRepositoryException;
 
     /**
+     * Returns a stream of artifacts that are stored in the given repository. The number and order of elements in the stream
+     * is defined by the <code>queryParameter</code>.
+     * The efficiency of ordering of elements is dependent on the implementation.
+     * There may be some implementations that have to put a hard limit on the elements returned.
+     * If there are no <code>sortFields</code> defined in the query parameter, the order of elements in the stream is undefined and depends
+     * on the implementation.
+     *
+     * @param session
+     * @param repositoryId
+     * @return A stream of artifact metadata objects for each artifact found in the repository.
+     * @since 3.0
+     */
+    Stream<ArtifactMetadata> getArtifactStream( @Nonnull RepositorySession session, @Nonnull String repositoryId, @Nullable QueryParameter queryParameter )
+        throws MetadataResolutionException;
+
+    /**
+     * Returns a stream of all the artifacts in the given repository using default query parameter.
+     * The order of the artifacts returned in the stream depends on the implementation.
+     * The number of elements in the stream is unlimited, but there may be some implementations that have to put a hard
+     * limit on the elements returned.
+     * For further information see {@link #getArtifactStream(RepositorySession, String, QueryParameter)} 
+     *
+     * @param session The repository session
+     * @param repositoryId The repository id
+     * @return A (unlimited) stream of artifact metadata elements that are found in this repository
+     * @since 3.0
+     * @see #getArtifactStream(RepositorySession, String, QueryParameter)
+     */
+    Stream<ArtifactMetadata> getArtifactStream( @Nonnull RepositorySession session, @Nonnull String repositoryId)
+        throws MetadataResolutionException;
+
+    /**
+     * Returns a stream of artifacts found for the given artifact coordinates and using the <code>queryParameter</code>
+     *
+     * @param session The repository session. May not be <code>null</code>.
+     * @param repoId The repository id. May not be <code>null</code>.
+     * @param namespace The namespace. May not be <code>null</code>.
+     * @param projectId The project id. May not be <code>null</code>.
+     * @param projectVersion The project version. May not be <code>null</code>.
+     * @return A stream of artifact metadata object. Order and number of elements returned, depends on the <code>queryParameter</code>.
+     * @since 3.0
+     * @throws MetadataResolutionException if there are no elements for the given artifact coordinates.
+     */
+    Stream<ArtifactMetadata> getArtifactStream( @Nonnull RepositorySession session, @Nonnull String repoId,
+                                                @Nonnull String namespace, @Nonnull String projectId,
+                                                @Nonnull String projectVersion, @Nullable QueryParameter queryParameter )
+        throws MetadataResolutionException;
+
+    /**
+     * Returns a stream of artifacts found for the given artifact coordinates. The order of elements returned, depends on the
+     * implementation.
+     *
+     * @param session The repository session. May not be <code>null</code>.
+     * @param repoId The repository id. May not be <code>null</code>.
+     * @param namespace The namespace. May not be <code>null</code>.
+     * @param projectId The project id. May not be <code>null</code>.
+     * @param projectVersion The project version. May not be <code>null</code>.
+     * @return A stream of artifact metadata object. Order and number of elements returned, depends on the <code>queryParameter</code>.
+     * @since 3.0
+     * @throws MetadataResolutionException if there are no elements for the given artifact coordinates.
+     */
+    Stream<ArtifactMetadata> getArtifactStream( @Nonnull RepositorySession session, @Nonnull String repoId,
+                                                @Nonnull String namespace, @Nonnull String projectId,
+                                           @Nonnull String projectVersion)
+        throws MetadataResolutionException;
+    /**
      * basically just checking it exists not complete data returned
      *
      *
diff --git a/archiva-modules/metadata/metadata-repository-api/src/test/java/org/apache/archiva/metadata/repository/AbstractMetadataRepositoryTest.java b/archiva-modules/metadata/metadata-repository-api/src/test/java/org/apache/archiva/metadata/repository/AbstractMetadataRepositoryTest.java
index c777097..6fb1e75 100644
--- a/archiva-modules/metadata/metadata-repository-api/src/test/java/org/apache/archiva/metadata/repository/AbstractMetadataRepositoryTest.java
+++ b/archiva-modules/metadata/metadata-repository-api/src/test/java/org/apache/archiva/metadata/repository/AbstractMetadataRepositoryTest.java
@@ -20,6 +20,7 @@ package org.apache.archiva.metadata.repository;
  */
 
 import junit.framework.TestCase;
+import org.apache.archiva.checksum.ChecksumAlgorithm;
 import org.apache.archiva.metadata.QueryParameter;
 import org.apache.archiva.metadata.generic.GenericMetadataFacet;
 import org.apache.archiva.metadata.generic.GenericMetadataFacetFactory;
@@ -70,6 +71,8 @@ public abstract class AbstractMetadataRepositoryTest
 
     private static final String UNKNOWN = "unknown";
 
+    private static final String TEST_SHA256 = "e43857b4e75e04a09d167564ca9a4636e5d233035483ba4ecf1243e34325d565";
+
     private static final String TEST_MD5 = "bd4a9b642562547754086de2dab26b7d";
 
     private static final String TEST_SHA1 = "2e5daf0201ddeb068a62d5e08da18657ab2c6be9";
@@ -989,7 +992,7 @@ public abstract class AbstractMetadataRepositoryTest
             ZonedDateTime upper = artifact2.getWhenGathered().plusSeconds(10);
 
             tryAssert( ( ) -> {
-                Stream<ArtifactMetadata> stream = getRepository().getArtifactsByDateRangeStream(session, TEST_REPO_ID, lower, upper, new QueryParameter());
+                Stream<ArtifactMetadata> stream = getRepository().getArtifactByDateRangeStream(session, TEST_REPO_ID, lower, upper, new QueryParameter());
                 assertNotNull(stream);
 
                 List<ArtifactMetadata> artifacts = stream.collect(Collectors.toList());
@@ -1201,6 +1204,29 @@ public abstract class AbstractMetadataRepositoryTest
     }
 
     @Test
+    public void testGetArtifactStream( )
+        throws Exception
+    {
+        try ( RepositorySession session = getSessionFactory( ).createSession( ) )
+        {
+            ArtifactMetadata artifact1 = createArtifact( );
+            ArtifactMetadata artifact2 = createArtifact( "pom" );
+            getRepository( ).updateArtifact( session, TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact1 );
+            getRepository( ).updateArtifact( session, TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact2 );
+
+            tryAssert( ( ) -> {
+                Stream<ArtifactMetadata> artifacts =
+                    getRepository( ).getArtifactStream( session, TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION );
+                assertNotNull( artifacts );
+                List<ArtifactMetadata> actual = artifacts
+                    .sorted( ( o1, o2 ) -> o1.getId( ).compareTo( o2.getId( ) ) ).collect( Collectors.toList( ) );
+                assertEquals( Arrays.asList( artifact1, artifact2 ), actual );
+            } );
+
+        }
+    }
+
+    @Test
     public void testGetArtifactVersions( )
         throws Exception
     {
@@ -1592,6 +1618,23 @@ public abstract class AbstractMetadataRepositoryTest
     }
 
     @Test
+    public void testGetArtifactsByChecksumSingleResultSha256( )
+        throws Exception
+    {
+        try ( RepositorySession session = getSessionFactory( ).createSession( ) )
+        {
+            ArtifactMetadata artifact = createArtifact( );
+            artifact.setChecksum( ChecksumAlgorithm.SHA256,  TEST_SHA256);
+            getRepository( ).updateArtifact( session, TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact );
+            session.save( );
+
+            assertEquals( Collections.singletonList( artifact ),
+                new ArrayList<>( getRepository( ).getArtifactsByChecksum( session, TEST_REPO_ID, TEST_SHA256 ) ) );
+
+        }
+    }
+
+    @Test
     public void testGetArtifactsByChecksumSingleResultMd5( )
         throws Exception
     {
@@ -1635,10 +1678,13 @@ public abstract class AbstractMetadataRepositoryTest
             getRepository( ).updateArtifact( session, TEST_REPO_ID, namespace, TEST_PROJECT, TEST_PROJECT_VERSION, artifact );
             session.save( );
 
-            tryAssert( ( ) -> assertEquals( Collections.singletonList( artifact ),
-                new ArrayList<>( getRepository( ).getArtifactsByChecksum( session, TEST_REPO_ID, TEST_SHA1 ) ) ) );
-            tryAssert( ( ) -> assertEquals( Collections.singletonList( artifact ),
-                new ArrayList<>( getRepository( ).getArtifactsByChecksum( session, TEST_REPO_ID, TEST_MD5 ) ) ) );
+            tryAssert( ( ) ->
+            {
+                assertEquals( Collections.singletonList( artifact ),
+                    new ArrayList<>( getRepository( ).getArtifactsByChecksum( session, TEST_REPO_ID, TEST_SHA1 ) ) );
+                assertEquals( Collections.singletonList( artifact ),
+                    new ArrayList<>( getRepository( ).getArtifactsByChecksum( session, TEST_REPO_ID, TEST_MD5 ) ) );
+            });
 
         }
     }
diff --git a/archiva-modules/plugins/metadata-store-cassandra/src/main/java/org/apache/archiva/metadata/repository/cassandra/CassandraMetadataRepository.java b/archiva-modules/plugins/metadata-store-cassandra/src/main/java/org/apache/archiva/metadata/repository/cassandra/CassandraMetadataRepository.java
index 768a94b..844bba7 100644
--- a/archiva-modules/plugins/metadata-store-cassandra/src/main/java/org/apache/archiva/metadata/repository/cassandra/CassandraMetadataRepository.java
+++ b/archiva-modules/plugins/metadata-store-cassandra/src/main/java/org/apache/archiva/metadata/repository/cassandra/CassandraMetadataRepository.java
@@ -72,7 +72,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.time.Instant;
-import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.util.*;
 import java.util.function.BiFunction;
@@ -1918,7 +1917,7 @@ public class CassandraMetadataRepository
     }
 
     /**
-     * For documentation see {@link MetadataRepository#getArtifactsByDateRangeStream(RepositorySession, String, ZonedDateTime, ZonedDateTime, QueryParameter)}
+     * For documentation see {@link MetadataRepository#getArtifactByDateRangeStream(RepositorySession, String, ZonedDateTime, ZonedDateTime, QueryParameter)}
      *
      * This implementation orders the stream. It does not order the query in the backend.
      *
@@ -1929,10 +1928,10 @@ public class CassandraMetadataRepository
      * @param queryParameter Additional parameters for the query that affect ordering and number of returned results.
      * @return
      * @throws MetadataRepositoryException
-     * @see MetadataRepository#getArtifactsByDateRangeStream
+     * @see MetadataRepository#getArtifactByDateRangeStream
      */
     @Override
-    public Stream<ArtifactMetadata> getArtifactsByDateRangeStream(RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException
+    public Stream<ArtifactMetadata> getArtifactByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException
     {
         Comparator<ArtifactMetadata> comp = getArtifactMetadataComparator(queryParameter, "whenGathered");
         return getArtifactsByDateRange(session, repositoryId, startTime, endTime, queryParameter).stream().sorted(comp).skip(queryParameter.getOffset()).limit(queryParameter.getLimit());
diff --git a/archiva-modules/plugins/metadata-store-file/src/main/java/org/apache/archiva/metadata/repository/file/FileMetadataRepository.java b/archiva-modules/plugins/metadata-store-file/src/main/java/org/apache/archiva/metadata/repository/file/FileMetadataRepository.java
index 272f536..4191945 100644
--- a/archiva-modules/plugins/metadata-store-file/src/main/java/org/apache/archiva/metadata/repository/file/FileMetadataRepository.java
+++ b/archiva-modules/plugins/metadata-store-file/src/main/java/org/apache/archiva/metadata/repository/file/FileMetadataRepository.java
@@ -19,14 +19,18 @@ package org.apache.archiva.metadata.repository.file;
  * under the License.
  */
 
+import org.apache.archiva.checksum.ChecksumAlgorithm;
 import org.apache.archiva.configuration.ArchivaConfiguration;
 import org.apache.archiva.configuration.ManagedRepositoryConfiguration;
 import org.apache.archiva.metadata.QueryParameter;
 import org.apache.archiva.metadata.model.*;
 import org.apache.archiva.metadata.repository.*;
+import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -413,7 +417,7 @@ public class FileMetadataRepository
      * @throws MetadataRepositoryException
      */
     @Override
-    public Stream<ArtifactMetadata> getArtifactsByDateRangeStream(RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException {
+    public Stream<ArtifactMetadata> getArtifactByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException {
         try {
             List<ArtifactMetadata> artifacts = new ArrayList<>();
             for (String ns : getRootNamespaces(session, repositoryId)) {
@@ -452,6 +456,7 @@ public class FileMetadataRepository
         }
     }
 
+
     @Override
     public Collection<ArtifactMetadata> getArtifacts(RepositorySession session, String repoId, String namespace, String projectId,
                                                      String projectVersion)
@@ -491,10 +496,9 @@ public class FileMetadataRepository
                         artifact.setWhenGathered(ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(value)), ZoneId.of("GMT")));
                     } else if ("version".equals(field)) {
                         artifact.setVersion(value);
-                    } else if ("md5".equals(field)) {
-                        artifact.setMd5(value);
-                    } else if ("sha1".equals(field)) {
-                        artifact.setSha1(value);
+                    } else if (field.startsWith("checksum")) {
+                        String algorithmStr = StringUtils.removeStart( name, "artifact:checksum:"+id+":");
+                        artifact.setChecksum( ChecksumAlgorithm.valueOf( algorithmStr ), value );
                     } else if ("facetIds".equals(field)) {
                         if (value.length() > 0) {
                             String propertyPrefix = "artifact:facet:" + id + ":";
@@ -566,11 +570,9 @@ public class FileMetadataRepository
             // alternatively, we could build a referential tree in the content repository, however it would need some levels
             // of depth to avoid being too broad to be useful (eg. /repository/checksums/a/ab/abcdef1234567)
 
-            List<ArtifactMetadata> artifacts = new ArrayList<>();
-            for (String ns : getRootNamespaces(session, repositoryId)) {
-                getArtifactsByChecksum(session, artifacts, repositoryId, ns, checksum);
-            }
-            return artifacts;
+            return getArtifactStream( session, repositoryId ).filter(
+                a -> a.hasChecksum( checksum )
+            ).collect( Collectors.toList() );
         } catch (MetadataResolutionException e) {
             throw new MetadataRepositoryException(e.getMessage(), e);
         }
@@ -607,8 +609,8 @@ public class FileMetadataRepository
             properties.remove("artifact:updated:" + id);
             properties.remove("artifact:whenGathered:" + id);
             properties.remove("artifact:size:" + id);
-            properties.remove("artifact:md5:" + id);
-            properties.remove("artifact:sha1:" + id);
+            artifactMetadata.getChecksums().entrySet().stream().forEach( entry ->
+                properties.remove( "artifact:checksum:"+id+":"+entry.getKey().name() ));
             properties.remove("artifact:version:" + id);
             properties.remove("artifact:facetIds:" + id);
 
@@ -638,15 +640,15 @@ public class FileMetadataRepository
             properties.remove("artifact:updated:" + id);
             properties.remove("artifact:whenGathered:" + id);
             properties.remove("artifact:size:" + id);
-            properties.remove("artifact:md5:" + id);
-            properties.remove("artifact:sha1:" + id);
             properties.remove("artifact:version:" + id);
             properties.remove("artifact:facetIds:" + id);
 
-            String prefix = "artifact:facet:" + id + ":";
-            for (Object key : new ArrayList<>(properties.keySet())) {
-                String property = (String) key;
-                if (property.startsWith(prefix)) {
+            String facetPrefix = "artifact:facet:" + id + ":";
+            String checksumPrefix = "artifact:checksum:"+id+":";
+            for (String property  : properties.stringPropertyNames()) {
+                if (property.startsWith( checksumPrefix )) {
+                    properties.remove( property );
+                } else if (property.startsWith(facetPrefix)) {
                     properties.remove(property);
                 }
             }
@@ -687,27 +689,6 @@ public class FileMetadataRepository
         }
     }
 
-    private void getArtifactsByChecksum(RepositorySession session, List<ArtifactMetadata> artifacts, String repositoryId, String ns,
-                                        String checksum)
-            throws MetadataRepositoryException {
-        try {
-            for (String namespace : getNamespaces(session, repositoryId, ns)) {
-                getArtifactsByChecksum(session, artifacts, repositoryId, ns + "." + namespace, checksum);
-            }
-
-            for (String project : getProjects(session, repositoryId, ns)) {
-                for (String version : getProjectVersions(session, repositoryId, ns, project)) {
-                    for (ArtifactMetadata artifact : getArtifacts(session, repositoryId, ns, project, version)) {
-                        if (checksum.equals(artifact.getMd5()) || checksum.equals(artifact.getSha1())) {
-                            artifacts.add(artifact);
-                        }
-                    }
-                }
-            }
-        } catch (MetadataResolutionException e) {
-            throw new MetadataRepositoryException(e.getMessage(), e);
-        }
-    }
 
     @Override
     public List<ArtifactMetadata> getArtifactsByProjectVersionMetadata(RepositorySession session, String key, String value, String repositoryId)
@@ -771,12 +752,8 @@ public class FileMetadataRepository
             properties.setProperty("artifact:whenGathered:" + id,
                     Long.toString(artifact.getWhenGathered().toInstant().toEpochMilli()));
             properties.setProperty("artifact:size:" + id, Long.toString(artifact.getSize()));
-            if (artifact.getMd5() != null) {
-                properties.setProperty("artifact:md5:" + id, artifact.getMd5());
-            }
-            if (artifact.getSha1() != null) {
-                properties.setProperty("artifact:sha1:" + id, artifact.getSha1());
-            }
+            artifact.getChecksums().entrySet().stream().forEach( entry ->
+                properties.setProperty( "artifact:checksum:"+id+":"+entry.getKey().name(), entry.getValue() ));
             properties.setProperty("artifact:version:" + id, artifact.getVersion());
 
             Set<String> facetIds = new LinkedHashSet<>(artifact.getFacetIds());
@@ -1043,6 +1020,32 @@ public class FileMetadataRepository
         return getNamespaces(session, repoId, null);
     }
 
+    private Stream<String> getAllNamespacesStream(RepositorySession session, String repoId) {
+        Path directory = null;
+        try
+        {
+            directory = getDirectory(repoId);
+        }
+        catch ( IOException e )
+        {
+            return Stream.empty( );
+        }
+        if (!(Files.exists(directory) && Files.isDirectory(directory))) {
+            return Stream.empty( );
+        }
+        final String searchFile = NAMESPACE_METADATA_KEY + ".properties";
+        try
+        {
+            return  Files.list(directory).filter(Files::isDirectory).filter(path ->
+                    Files.exists(path.resolve(searchFile))
+                ).map(path -> path.getFileName().toString());
+        }
+        catch ( IOException e )
+        {
+            return Stream.empty( );
+        }
+    }
+
     @Override
     public Collection<String> getNamespaces(RepositorySession session, String repoId, String baseNamespace)
             throws MetadataResolutionException {
@@ -1182,6 +1185,85 @@ public class FileMetadataRepository
         }
     }
 
+    private class ArtifactCoordinates {
+        final String namespace;
+        final String project;
+        final String version;
+
+        public ArtifactCoordinates(String namespace, String project, String version) {
+            this.namespace = namespace;
+            this.project = project;
+            this.version = version;
+        }
+
+        public String getNamespace( )
+        {
+            return namespace;
+        }
+
+        public String getProject( )
+        {
+            return project;
+        }
+
+        public String getVersion( )
+        {
+            return version;
+        }
+    }
+
+    @Override
+    public Stream<ArtifactMetadata> getArtifactStream( @Nonnull final RepositorySession session, @Nonnull final String repositoryId,
+                                                       @Nullable QueryParameter queryParameter ) throws MetadataResolutionException
+    {
+
+        queryParameter = getParameterOrDefault( queryParameter );
+        return getAllNamespacesStream( session, repositoryId ).filter( Objects::nonNull ).flatMap( ns ->
+            {
+                try
+                {
+                    return getProjects( session, repositoryId, ns ).stream( ).map( proj ->
+                        new ArtifactCoordinates( ns, proj, null ) );
+                }
+                catch ( MetadataResolutionException e )
+                {
+                    return null;
+                }
+            }
+        ).filter( Objects::nonNull ).flatMap( artifactCoordinates ->
+            {
+                try
+                {
+                    return getProjectVersions( session, repositoryId, artifactCoordinates.getNamespace( ), artifactCoordinates.getProject( ) )
+                        .stream( ).map(version -> new ArtifactCoordinates( artifactCoordinates.getNamespace(), artifactCoordinates.getProject(), version ));
+                }
+                catch ( MetadataResolutionException e )
+                {
+                    return null;
+                }
+            }
+        ).filter( Objects::nonNull ).flatMap( ac ->
+            {
+                try
+                {
+                    return getArtifactStream( session, repositoryId, ac.getNamespace(), ac.getProject(), ac.getVersion() );
+                }
+                catch ( MetadataResolutionException e )
+                {
+                    return null;
+                }
+            }
+            ).filter( Objects::nonNull );
+    }
+
+    @Override
+    public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repoId,
+                                                       final String namespace, final String projectId,
+                                                       final String projectVersion ) throws MetadataResolutionException
+    {
+        return getArtifacts( session, repoId, namespace, projectId, projectVersion ).stream( );
+    }
+
     private void getArtifacts(RepositorySession session, List<ArtifactMetadata> artifacts, String repoId, String ns)
             throws MetadataResolutionException {
         for (String namespace : getNamespaces(session, repoId, ns)) {
diff --git a/archiva-modules/plugins/metadata-store-file/src/test/java/org/apache/archiva/metadata/repository/file/FileMetadataRepositoryTest.java b/archiva-modules/plugins/metadata-store-file/src/test/java/org/apache/archiva/metadata/repository/file/FileMetadataRepositoryTest.java
index 41497ac..053b283 100644
--- a/archiva-modules/plugins/metadata-store-file/src/test/java/org/apache/archiva/metadata/repository/file/FileMetadataRepositoryTest.java
+++ b/archiva-modules/plugins/metadata-store-file/src/test/java/org/apache/archiva/metadata/repository/file/FileMetadataRepositoryTest.java
@@ -64,6 +64,8 @@ public class FileMetadataRepositoryTest
         throws Exception
     {
         super.setUp();
+        assertMaxTries = 1;
+        assertRetrySleepMs = 10;
 
         Path directory = Paths.get( "target/test-repositories" );
         if (Files.exists(directory))
diff --git a/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepository.java b/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepository.java
index 101b633..25b11de 100644
--- a/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepository.java
+++ b/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepository.java
@@ -815,7 +815,7 @@ public class JcrMetadataRepository
     }
 
     @Override
-    public Stream<ArtifactMetadata> getArtifactsByDateRangeStream(RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException
+    public Stream<ArtifactMetadata> getArtifactByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException
     {
         final Session jcrSession = getSession( session );
         QueryResult result = queryArtifactByDateRange(jcrSession, repositoryId, startTime, endTime, queryParameter);