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 2020/03/01 19:26:22 UTC

[archiva] branch master updated (59a2152 -> 939d127)

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

martin_s pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/archiva.git.


    from 59a2152  Adding test
     new de22e84  Adding stream based asset utility
     new 24aba5a  Moving storage utility methods to api module
     new 7ddf026  Changing visibility of utility method
     new adeb46c  Moving filesystem storage classes in sub package
     new 939d127  Additional methods for repository content

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../core/repository/AbstractRepositoryPurge.java   |  16 +-
 .../archiva/policies/CachedFailuresPolicyTest.java |   2 +-
 .../archiva/policies/ChecksumPolicyTest.java       |   3 +-
 .../PropagateErrorsDownloadPolicyTest.java         |   2 +-
 .../PropagateErrorsOnUpdateDownloadPolicyTest.java |   2 +-
 .../archiva/policies/SnapshotsPolicyTest.java      |   3 +-
 .../proxy/DefaultRepositoryProxyHandler.java       |   8 +-
 .../admin/mock/ArchivaIndexManagerMock.java        |  11 +-
 .../archiva/admin/mock/MavenIndexContextMock.java  |   5 +-
 .../repository/ManagedRepositoryContent.java       |  26 +-
 .../indexer/merger/base/DefaultIndexMerger.java    |   3 +-
 .../repository/base/BasicManagedRepository.java    |   2 +-
 .../repository/base/BasicRemoteRepository.java     |   2 +-
 .../content/base/builder/ProjectOptBuilder.java    |   2 -
 .../repository/content/base/ContentItemTest.java   |   2 +-
 .../mock/ManagedRepositoryContentMock.java         |  16 +-
 .../repository/scanner/RepositoryScannerTest.java  |   4 +-
 .../scanner/mock/ManagedRepositoryContentMock.java |  18 +-
 .../archiva-base/archiva-storage-api/pom.xml       |  15 +
 .../archiva/repository/storage/StorageAsset.java   |   2 +-
 .../storage/{ => util}/AssetSpliterator.java       |  56 ++--
 .../repository/storage/util/StorageUtil.java       | 285 ++++++++++++++++++
 .../repository/storage/AssetSpliteratorTest.java   | 100 -------
 .../archiva/repository/storage/mock/MockAsset.java |  58 +++-
 .../repository/storage/mock/MockStorage.java       | 189 ++++++++++++
 .../storage/util/AssetSpliteratorTest.java         | 161 +++++++++++
 .../storage/util/ConsumeVisitStatus.java}          |  14 +-
 .../repository/storage/util/StopVisitStatus.java}  |  24 +-
 .../repository/storage/util/StorageUtilTest.java   | 175 ++++++++++++
 .../repository/storage/util/VisitStatus.java       |  82 ++++++
 .../src/test/resources/log4j2-test.xml             |   5 +-
 .../storage/{ => fs}/FilesystemAsset.java          |  27 +-
 .../storage/{ => fs}/FilesystemStorage.java        |   9 +-
 .../{StorageUtil.java => fs/FsStorageUtil.java}    | 189 +-----------
 .../storage/{ => fs}/FilesystemAssetTest.java      |   7 +-
 .../storage/{ => fs}/FilesystemStorageTest.java    |  11 +-
 .../archiva/indexer/maven/MavenIndexContext.java   |   4 +-
 .../archiva/indexer/maven/MavenIndexManager.java   |   9 +-
 .../apache/archiva/proxy/MetadataTransferTest.java |   5 +-
 .../mock/ManagedRepositoryContentMock.java         |  18 +-
 .../maven2/ManagedDefaultRepositoryContent.java    | 317 +++++++++++++++++++--
 .../repository/maven2/MavenManagedRepository.java  |   2 +-
 .../repository/maven2/MavenRemoteRepository.java   |   2 +-
 .../repository/maven2/MavenRepositoryGroup.java    |   2 +-
 .../repository/maven2/MavenRepositoryProvider.java |   2 +-
 .../merge/Maven2RepositoryMerger.java              |   4 +-
 .../index/mock/ArchivaIndexManagerMock.java        |   4 +-
 .../index/mock/MavenIndexContextMock.java          |   2 +-
 .../archiva/mock/ArchivaIndexManagerMock.java      |  11 +-
 .../apache/archiva/mock/MavenIndexContextMock.java |   5 +-
 .../rest/services/DefaultBrowseService.java        |   8 +-
 .../rest/services/DefaultRepositoriesService.java  |   4 +-
 .../rest/services/utils/ArtifactBuilder.java       |   4 +-
 .../rest/services/ArtifactContentEntriesTests.java |   4 +-
 .../rest/services/utils/ArtifactBuilderTest.java   |   4 +-
 .../NewVersionsOfArtifactRssFeedProcessorTest.java |   2 +-
 .../apache/archiva/web/rss/RssFeedServletTest.java |   2 +-
 .../archiva/webdav/ArchivaDavResourceFactory.java  |   2 +-
 .../apache/archiva/webdav/util/IndexWriter.java    |   2 +-
 .../org/apache/archiva/webdav/DavResourceTest.java |   2 +-
 60 files changed, 1471 insertions(+), 486 deletions(-)
 rename archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/{ => util}/AssetSpliterator.java (77%)
 create mode 100644 archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/StorageUtil.java
 delete mode 100644 archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/AssetSpliteratorTest.java
 create mode 100644 archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/mock/MockStorage.java
 create mode 100644 archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/AssetSpliteratorTest.java
 copy archiva-modules/archiva-base/{archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/builder/WithAssetBuilder.java => archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/ConsumeVisitStatus.java} (77%)
 copy archiva-modules/archiva-base/{archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/builder/WithAssetBuilder.java => archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/StopVisitStatus.java} (63%)
 create mode 100644 archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/StorageUtilTest.java
 create mode 100644 archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/VisitStatus.java
 copy archiva-modules/{plugins/stage-repository-merge => archiva-base/archiva-storage-api}/src/test/resources/log4j2-test.xml (92%)
 rename archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/{ => fs}/FilesystemAsset.java (95%)
 rename archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/{ => fs}/FilesystemStorage.java (97%)
 rename archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/{StorageUtil.java => fs/FsStorageUtil.java} (50%)
 rename archiva-modules/archiva-base/archiva-storage-fs/src/test/java/org/apache/archiva/repository/storage/{ => fs}/FilesystemAssetTest.java (97%)
 rename archiva-modules/archiva-base/archiva-storage-fs/src/test/java/org/apache/archiva/repository/storage/{ => fs}/FilesystemStorageTest.java (96%)


[archiva] 02/05: Moving storage utility methods to api module

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 24aba5ac73da0ad83caf43ce606f07cd57bc2891
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Sat Feb 29 18:52:24 2020 +0100

    Moving storage utility methods to api module
---
 .../core/repository/AbstractRepositoryPurge.java   |  17 +-
 .../proxy/DefaultRepositoryProxyHandler.java       |   6 +-
 .../admin/mock/ArchivaIndexManagerMock.java        |   7 +-
 .../indexer/merger/base/DefaultIndexMerger.java    |   3 +-
 .../archiva-base/archiva-storage-api/pom.xml       |  15 +
 .../archiva/repository/storage/StorageAsset.java   |   2 +-
 .../repository/storage/util/AssetSpliterator.java  |   8 +-
 .../repository/storage/util/StorageUtil.java       | 192 +++++++++++
 .../archiva/repository/storage/mock/MockAsset.java |  37 ++-
 .../repository/storage/mock/MockStorage.java       | 189 +++++++++++
 .../repository/storage/util/StorageUtilTest.java   |  45 ++-
 .../repository/storage/util/VisitStatus.java       |  19 +-
 .../src/test/resources/log4j2-test.xml             |  39 +++
 .../archiva/repository/storage/FsStorageUtil.java  | 189 +++++++++++
 .../archiva/repository/storage/StorageUtil.java    | 369 ---------------------
 .../archiva/indexer/maven/MavenIndexManager.java   |   7 +-
 .../maven2/ManagedDefaultRepositoryContent.java    |   4 +-
 .../archiva/mock/ArchivaIndexManagerMock.java      |   7 +-
 .../rest/services/DefaultBrowseService.java        |   8 +-
 .../rest/services/DefaultRepositoriesService.java  |   4 +-
 .../rest/services/utils/ArtifactBuilder.java       |   4 +-
 .../apache/archiva/webdav/util/IndexWriter.java    |   2 +-
 22 files changed, 741 insertions(+), 432 deletions(-)

diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/AbstractRepositoryPurge.java b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/AbstractRepositoryPurge.java
index a8daf16..a3979e6 100644
--- a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/AbstractRepositoryPurge.java
+++ b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/AbstractRepositoryPurge.java
@@ -28,8 +28,9 @@ import org.apache.archiva.model.ArtifactReference;
 import org.apache.archiva.repository.ContentNotFoundException;
 import org.apache.archiva.repository.ManagedRepositoryContent;
 import org.apache.archiva.metadata.audit.RepositoryListener;
+import org.apache.archiva.repository.storage.FsStorageUtil;
 import org.apache.archiva.repository.storage.StorageAsset;
-import org.apache.archiva.repository.storage.StorageUtil;
+import org.apache.archiva.repository.storage.util.StorageUtil;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -419,17 +420,9 @@ public abstract class AbstractRepositoryPurge
 
         final String artifactName = artifactFile.getName( );
 
-        try
-        {
-
-            StorageUtil.recurse(parentDir, a -> {
-                if (!a.isContainer() && a.getName().startsWith(artifactName)) deleteSilently(a);
-            }, true, 3 );
-        }
-        catch ( IOException e )
-        {
-            log.error( "Purge of support files failed {}: {}", artifactFile, e.getMessage( ), e );
-        }
+        StorageUtil.walk(parentDir, a -> {
+            if (!a.isContainer() && a.getName().startsWith(artifactName)) deleteSilently(a);
+        });
 
     }
 
diff --git a/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/archiva/proxy/DefaultRepositoryProxyHandler.java b/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/archiva/proxy/DefaultRepositoryProxyHandler.java
index 71ec9ab..ddeb499 100644
--- a/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/archiva/proxy/DefaultRepositoryProxyHandler.java
+++ b/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/archiva/proxy/DefaultRepositoryProxyHandler.java
@@ -50,8 +50,8 @@ import org.apache.archiva.repository.RepositoryType;
 import org.apache.archiva.repository.metadata.base.MetadataTools;
 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
 import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.FsStorageUtil;
 import org.apache.archiva.repository.storage.StorageAsset;
-import org.apache.archiva.repository.storage.StorageUtil;
 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
 import org.apache.archiva.scheduler.repository.model.RepositoryTask;
 import org.apache.commons.collections4.CollectionUtils;
@@ -725,14 +725,14 @@ public abstract class DefaultRepositoryProxyHandler implements RepositoryProxyHa
 
         try
         {
-            StorageUtil.moveAsset( temp, target, true , StandardCopyOption.REPLACE_EXISTING);
+            org.apache.archiva.repository.storage.util.StorageUtil.moveAsset( temp, target, true , StandardCopyOption.REPLACE_EXISTING);
         }
         catch ( IOException e )
         {
             log.error( "Move failed from {} to {}, trying copy.", temp, target );
             try
             {
-                StorageUtil.copyAsset( temp, target, true );
+                FsStorageUtil.copyAsset( temp, target, true );
                 if (temp.exists()) {
                     temp.getStorage( ).removeAsset( temp );
                 }
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/ArchivaIndexManagerMock.java b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/ArchivaIndexManagerMock.java
index f20b4ab..127c314 100644
--- a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/ArchivaIndexManagerMock.java
+++ b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/ArchivaIndexManagerMock.java
@@ -44,7 +44,6 @@ import org.apache.archiva.repository.storage.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.repository.features.IndexCreationFeature;
 import org.apache.archiva.repository.features.RemoteIndexFeature;
-import org.apache.archiva.repository.storage.StorageUtil;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.maven.index.ArtifactContext;
 import org.apache.maven.index.ArtifactContextProducer;
@@ -455,11 +454,7 @@ public class ArchivaIndexManagerMock implements ArchivaIndexManager {
             } catch (IOException e) {
                 log.warn("Index close failed");
             }
-            try {
-                StorageUtil.deleteRecursively(context.getPath());
-            } catch (IOException e) {
-                throw new IndexUpdateFailedException("Could not delete index files");
-            }
+            org.apache.archiva.repository.storage.util.StorageUtil.deleteRecursively(context.getPath());
         });
         try {
             Repository repo = context.getRepository();
diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/indexer/merger/base/DefaultIndexMerger.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/indexer/merger/base/DefaultIndexMerger.java
index b575a20..e476ea0 100644
--- a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/indexer/merger/base/DefaultIndexMerger.java
+++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/indexer/merger/base/DefaultIndexMerger.java
@@ -28,7 +28,6 @@ import org.apache.archiva.indexer.merger.TemporaryGroupIndex;
 import org.apache.archiva.repository.Repository;
 import org.apache.archiva.repository.RepositoryRegistry;
 import org.apache.archiva.repository.storage.StorageAsset;
-import org.apache.archiva.repository.storage.StorageUtil;
 import org.apache.commons.lang3.time.StopWatch;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -133,7 +132,7 @@ public class DefaultIndexMerger
                 StorageAsset directory = temporaryGroupIndex.getDirectory();
                 if ( directory != null && directory.exists() )
                 {
-                    StorageUtil.deleteRecursively( directory );
+                    org.apache.archiva.repository.storage.util.StorageUtil.deleteRecursively( directory );
                 }
             }
         }
diff --git a/archiva-modules/archiva-base/archiva-storage-api/pom.xml b/archiva-modules/archiva-base/archiva-storage-api/pom.xml
index 5114864..9085887 100644
--- a/archiva-modules/archiva-base/archiva-storage-api/pom.xml
+++ b/archiva-modules/archiva-base/archiva-storage-api/pom.xml
@@ -37,6 +37,21 @@
   </properties>
 
 
+  <dependencies>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-slf4j-impl</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+
   <build>
     <plugins>
       <plugin>
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/StorageAsset.java b/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/StorageAsset.java
index 8209fcd..eb7074c 100644
--- a/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/StorageAsset.java
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/StorageAsset.java
@@ -85,7 +85,7 @@ public interface StorageAsset
      *
      * @return The list of children. If there are no children and if the asset is not a container, a empty list will be returned.
      */
-    List<StorageAsset> list();
+    List<? extends StorageAsset> list();
 
     /**
      * The size in bytes of the asset. If the asset does not have a size, -1 should be returned.
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/AssetSpliterator.java b/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/AssetSpliterator.java
index 7b45e12..3d2a8ce 100644
--- a/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/AssetSpliterator.java
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/AssetSpliterator.java
@@ -167,16 +167,16 @@ public class AssetSpliterator implements Spliterator<StorageAsset>, Closeable
     }
 
     // Assets are returned in reverse order
-    List<StorageAsset> getChildContainers( StorageAsset parent) {
-        final List<StorageAsset> children = parent.list( );
+    List<? extends StorageAsset> getChildContainers( StorageAsset parent) {
+        final List<? extends StorageAsset> children = parent.list( );
         final int len = children.size( );
         return IntStream.range( 0, children.size( ) ).mapToObj( i ->
             children.get(len - i - 1)).filter( StorageAsset::isContainer ).collect( Collectors.toList( ) );
     }
 
     // Assets are returned in reverse order
-    List<StorageAsset> getChildFiles(StorageAsset parent) {
-        final List<StorageAsset> children = parent.list( );
+    List<? extends StorageAsset> getChildFiles(StorageAsset parent) {
+        final List<? extends StorageAsset> children = parent.list( );
         final int len = children.size( );
         return IntStream.range( 0, children.size( ) ).mapToObj( i ->
             children.get(len - i - 1)).filter( StorageAsset::isLeaf ).collect( Collectors.toList( ) );
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/StorageUtil.java b/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/StorageUtil.java
index b912ee3..f8c5745 100644
--- a/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/StorageUtil.java
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/StorageUtil.java
@@ -18,8 +18,19 @@ package org.apache.archiva.repository.storage.util;
  * under the License.
  */
 
+import org.apache.archiva.repository.storage.RepositoryStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.nio.file.CopyOption;
+import java.nio.file.Files;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.stream.Stream;
@@ -34,6 +45,11 @@ import java.util.stream.StreamSupport;
  */
 public class StorageUtil
 {
+
+    private static final Logger LOG = LoggerFactory.getLogger( StorageUtil.class );
+
+    private static final int DEFAULT_BUFFER_SIZE = 4096;
+
     /**
      * Walk the tree starting at the given asset. The consumer is called for each asset found.
      * It runs a depth-first search where children are consumed before their parents.
@@ -89,5 +105,181 @@ public class StorageUtil
         return newAssetStream( start, false );
     }
 
+    /**
+     * Deletes the given asset and all child assets recursively.
+     * IOExceptions during deletion are ignored.
+     *
+     * @param baseDir The base asset to remove.
+     *
+     */
+    public static final void deleteRecursively(StorageAsset baseDir) {
+        RepositoryStorage storage = baseDir.getStorage( );
+        walk( baseDir,  a -> {
+            try {
+                storage.removeAsset(a);
+            } catch (IOException e) {
+                LOG.error( "Could not delete asset {}: {}", a.getPath( ), e.getMessage( ), e );
+            }
+        });
+    }
+
+    /**
+     * Deletes the given asset and all child assets recursively.
+     * @param baseDir The base asset to remove.
+     * @param stopOnError if <code>true</code> the traversal stops, if an exception is encountered
+     * @return returns <code>true</code>, if every item was removed. If an IOException was encountered during
+     * traversal it returns <code>false</code>
+     */
+    public static final boolean deleteRecursively(final StorageAsset baseDir, final boolean stopOnError) {
+        final RepositoryStorage storage = baseDir.getStorage( );
+        try(Stream<StorageAsset> stream = newAssetStream( baseDir ))
+        {
+            if ( stopOnError )
+            {
+                // Return true, if no exception occurred
+                // anyMatch is short-circuiting, that means it stops if the condition matches
+                return !stream.map( a -> {
+                    try
+                    {
+                        storage.removeAsset( a );
+                        // Returning false, if OK
+                        return Boolean.FALSE;
+                    }
+                    catch ( IOException e )
+                    {
+                        LOG.error( "Could not delete asset {}: {}", a.getPath( ), e.getMessage( ), e );
+                        // Returning true, if exception
+                        return Boolean.TRUE;
+                    }
+                } ).anyMatch( r -> r );
+            } else {
+                // Return true, if all removals were OK
+                // We want to consume all, so we use allMatch
+                return stream.map( a -> {
+                    try
+                    {
+                        storage.removeAsset( a );
+                        // Returning true, if OK
+                        return Boolean.TRUE;
+                    }
+                    catch ( IOException e )
+                    {
+                        LOG.error( "Could not delete asset {}: {}", a.getPath( ), e.getMessage( ), e );
+                        // Returning false, if exception
+                        return Boolean.FALSE;
+                    }
+                } ).allMatch( r -> r );
+            }
+        }
+    }
+
+    /**
+     * Moves a asset between different storage instances.
+     * If you know that source and asset are from the same storage instance, the move method of the storage
+     * instance may be faster.
+     *
+     * @param source The source asset
+     * @param target The target asset
+     * @param locked If true, a lock is used for the move operation.
+     * @param copyOptions Options for copying
+     * @throws IOException If the move fails
+     */
+    public static final void moveAsset(StorageAsset source, StorageAsset target, boolean locked, CopyOption... copyOptions) throws IOException
+    {
+        if (source.isFileBased() && target.isFileBased()) {
+            // Short cut for FS operations
+            // Move is atomic operation
+            if (!Files.exists(target.getFilePath().getParent())) {
+                Files.createDirectories(target.getFilePath().getParent());
+            }
+            Files.move( source.getFilePath(), target.getFilePath(), copyOptions );
+        } else {
+            try {
+                final RepositoryStorage sourceStorage = source.getStorage();
+                final RepositoryStorage targetStorage = target.getStorage();
+                sourceStorage.consumeDataFromChannel( source, is -> wrapWriteFunction( is, targetStorage, target, locked ), locked);
+                sourceStorage.removeAsset( source );
+            }  catch (IOException e) {
+                throw e;
+            }  catch (Throwable e) {
+                Throwable cause = e.getCause();
+                if (cause instanceof IOException) {
+                    throw (IOException)cause;
+                } else
+                {
+                    throw new IOException( e );
+                }
+            }
+        }
+
+    }
+
+    public static final void wrapWriteFunction( ReadableByteChannel is, RepositoryStorage targetStorage, StorageAsset target, boolean locked) {
+        try {
+            targetStorage.writeDataToChannel( target, os -> copy(is, os), locked );
+        } catch (Exception e) {
+            throw new RuntimeException( e );
+        }
+    }
+
+    public static final void copy( final ReadableByteChannel is, final WritableByteChannel os ) {
+        if (is instanceof FileChannel ) {
+            copy( (FileChannel) is, os );
+        } else if (os instanceof FileChannel) {
+            copy(is, (FileChannel)os);
+        } else
+        {
+            try
+            {
+                ByteBuffer buffer = ByteBuffer.allocate( DEFAULT_BUFFER_SIZE );
+                while ( is.read( buffer ) != -1 )
+                {
+                    buffer.flip( );
+                    while ( buffer.hasRemaining( ) )
+                    {
+                        os.write( buffer );
+                    }
+                    buffer.clear( );
+                }
+            }
+            catch ( IOException e )
+            {
+                throw new RuntimeException( e );
+            }
+        }
+    }
+
+    public static final void copy( final FileChannel is, final WritableByteChannel os ) {
+        try
+        {
+            is.transferTo( 0, is.size( ), os );
+        }
+        catch ( IOException e )
+        {
+            throw new RuntimeException( e );
+        }
+    }
+
+    public static final void copy( final ReadableByteChannel is, final FileChannel os ) {
+        try
+        {
+            os.transferFrom( is, 0, Long.MAX_VALUE );
+        }
+        catch ( IOException e )
+        {
+            throw new RuntimeException( e );
+        }
+    }
+
 
+    /**
+     * Returns the extension of the name of a given asset. Extension is the substring after the last occurence of '.' in the
+     * string. If no '.' is found, the empty string is returned.
+     *
+     * @param asset The asset from which to return the extension string.
+     * @return The extension.
+     */
+    public static final String getExtension(StorageAsset asset) {
+        return StringUtils.substringAfterLast(asset.getName(),".");
+    }
 }
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/mock/MockAsset.java b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/mock/MockAsset.java
index 3baad4f..0e19459 100644
--- a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/mock/MockAsset.java
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/mock/MockAsset.java
@@ -37,11 +37,16 @@ import java.util.Map;
 
 public class MockAsset implements StorageAsset
 {
-    private StorageAsset parent;
+    private MockAsset parent;
     private String path;
     private String name;
-    private LinkedHashMap<String, StorageAsset> children = new LinkedHashMap<>( );
+    private LinkedHashMap<String, MockAsset> children = new LinkedHashMap<>( );
     private boolean container = false;
+    private RepositoryStorage storage;
+
+
+
+    private boolean throwException;
 
     public MockAsset( String name ) {
         this.name = name;
@@ -52,18 +57,38 @@ public class MockAsset implements StorageAsset
         this.parent = parent;
         this.path = (parent.hasParent()?parent.getPath( ):"") + "/" + name;
         this.name = name;
+        this.storage = parent.getStorage( );
         parent.registerChild( this );
     }
 
-    public void registerChild(StorageAsset child) {
+    public void registerChild(MockAsset child) {
         children.putIfAbsent( child.getName(), child );
         this.container = true;
     }
 
+    public void unregisterChild(MockAsset child) {
+        children.remove( child.getName( ) );
+    }
+
+
+    public void setStorage(RepositoryStorage storage) {
+        this.storage = storage;
+    }
+
+    public boolean isThrowException( )
+    {
+        return throwException;
+    }
+
+    public void setThrowException( boolean throwException )
+    {
+        this.throwException = throwException;
+    }
+
     @Override
     public RepositoryStorage getStorage( )
     {
-        return null;
+        return storage;
     }
 
     @Override
@@ -97,7 +122,7 @@ public class MockAsset implements StorageAsset
     }
 
     @Override
-    public List<StorageAsset> list( )
+    public List<MockAsset> list( )
     {
         return new ArrayList( children.values( ) );
     }
@@ -169,7 +194,7 @@ public class MockAsset implements StorageAsset
     }
 
     @Override
-    public StorageAsset getParent( )
+    public MockAsset getParent( )
     {
         return this.parent;
     }
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/mock/MockStorage.java b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/mock/MockStorage.java
new file mode 100644
index 0000000..2741c30
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/mock/MockStorage.java
@@ -0,0 +1,189 @@
+package org.apache.archiva.repository.storage.mock;
+
+/*
+ * 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.archiva.repository.storage.RepositoryStorage;
+import org.apache.archiva.repository.storage.StorageAsset;
+import org.apache.archiva.repository.storage.util.VisitStatus;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.nio.file.CopyOption;
+import java.util.LinkedHashMap;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+/**
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+public class MockStorage implements RepositoryStorage
+{
+    public static final String ADD = "ADD";
+    public static final String REMOVE = "REMOVE";
+    private MockAsset root;
+    private LinkedHashMap<String, MockAsset> assets = new LinkedHashMap<>( );
+
+    private VisitStatus status = new VisitStatus( );
+
+    public MockStorage( MockAsset root )
+    {
+        this.root = root;
+        root.setStorage( this );
+    }
+
+    public MockStorage() {
+        this.root = new MockAsset( "" );
+        this.root.setStorage( this );
+    }
+
+    public VisitStatus getStatus() {
+        return status;
+    }
+
+    @Override
+    public URI getLocation( )
+    {
+        return null;
+    }
+
+    @Override
+    public void updateLocation( URI newLocation ) throws IOException
+    {
+
+    }
+
+    private String[] splitPath(String path) {
+        if (path.equals("/")) {
+            return new String[0];
+        } else
+        {
+            if (path.startsWith( "/" )) {
+                return path.substring( 1, path.length( ) ).split( "/" );
+            }
+            return path.split( "/" );
+        }
+    }
+
+    @Override
+    public StorageAsset getAsset( String path )
+    {
+        if (assets.containsKey( path )) {
+            return assets.get( path );
+        }
+        String[] pathArr = splitPath( path );
+        StorageAsset parent = root;
+        for (String pathElement : pathArr) {
+            Optional<? extends StorageAsset> next = parent.list( ).stream( ).filter( a -> a.getName( ).equals( pathElement ) ).findFirst( );
+            if (next.isPresent()) {
+                parent = next.get( );
+            } else {
+                MockAsset asset = new MockAsset( (MockAsset)parent, pathElement );
+                assets.put( asset.getPath( ), asset );
+                parent = asset;
+            }
+        }
+        return parent;
+    }
+
+    @Override
+    public void consumeData( StorageAsset asset, Consumer<InputStream> consumerFunction, boolean readLock ) throws IOException
+    {
+
+    }
+
+    @Override
+    public void consumeDataFromChannel( StorageAsset asset, Consumer<ReadableByteChannel> consumerFunction, boolean readLock ) throws IOException
+    {
+
+    }
+
+    @Override
+    public void writeData( StorageAsset asset, Consumer<OutputStream> consumerFunction, boolean writeLock ) throws IOException
+    {
+
+    }
+
+    @Override
+    public void writeDataToChannel( StorageAsset asset, Consumer<WritableByteChannel> consumerFunction, boolean writeLock ) throws IOException
+    {
+
+    }
+
+    @Override
+    public StorageAsset addAsset( String path, boolean container )
+    {
+        String[] pathArr = splitPath( path );
+        StorageAsset parent = root;
+        for (String pathElement : pathArr) {
+            Optional<? extends StorageAsset> next = parent.list( ).stream( ).filter( a -> a.getName( ).equals( pathElement ) ).findFirst( );
+            if (next.isPresent()) {
+                parent = next.get( );
+            } else {
+                MockAsset asset = new MockAsset( (MockAsset)parent, pathElement );
+                assets.put( asset.getPath( ), asset );
+                parent = asset;
+            }
+        }
+        status.add( ADD, parent );
+        return parent;
+    }
+
+    @Override
+    public void removeAsset( StorageAsset assetArg ) throws IOException
+    {
+        MockAsset asset = (MockAsset) assetArg;
+        if (asset.hasParent())
+        {
+            asset.getParent( ).unregisterChild( asset );
+        }
+        assets.remove( asset.getPath( ) );
+        status.add( REMOVE, asset );
+        if (asset.isThrowException()) {
+            throw new IOException( "Mocked IOException for " + asset.getPath( ) );
+        }
+    }
+
+    @Override
+    public StorageAsset moveAsset( StorageAsset origin, String destination, CopyOption... copyOptions ) throws IOException
+    {
+        return null;
+    }
+
+    @Override
+    public void moveAsset( StorageAsset origin, StorageAsset destination, CopyOption... copyOptions ) throws IOException
+    {
+
+    }
+
+    @Override
+    public StorageAsset copyAsset( StorageAsset origin, String destination, CopyOption... copyOptions ) throws IOException
+    {
+        return null;
+    }
+
+    @Override
+    public void copyAsset( StorageAsset origin, StorageAsset destination, CopyOption... copyOptions ) throws IOException
+    {
+
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/StorageUtilTest.java b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/StorageUtilTest.java
index 7907754..c1c53c7 100644
--- a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/StorageUtilTest.java
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/StorageUtilTest.java
@@ -21,8 +21,10 @@ package org.apache.archiva.repository.storage.util;
 
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.repository.storage.mock.MockAsset;
+import org.apache.archiva.repository.storage.mock.MockStorage;
 import org.junit.jupiter.api.Test;
 
+import java.io.IOException;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -40,11 +42,11 @@ class StorageUtilTest
 
 
 
-    private StorageAsset createTree() {
+    private MockAsset createTree() {
         return createTree( LEVEL1, LEVEL2, LEVEL3 );
     }
 
-    private StorageAsset createTree(int... levelElements) {
+    private MockAsset createTree(int... levelElements) {
         MockAsset root = new MockAsset( "" );
         recurseSubTree( root, 0, levelElements );
         return root;
@@ -131,4 +133,43 @@ class StorageUtilTest
         int expected = LEVEL2 * LEVEL3 + LEVEL2 + 1;
         assertEquals( expected, result.size( ) );
     }
+
+
+    @Test
+    void testDelete() throws IOException
+    {
+        MockAsset root = createTree( );
+        MockStorage storage = new MockStorage( root );
+
+        StorageUtil.deleteRecursively( root );
+        int expected = LEVEL1 * LEVEL2 * LEVEL3 + LEVEL1 * LEVEL2 + LEVEL1 + 1;
+        assertEquals( expected, storage.getStatus( ).size( MockStorage.REMOVE ) );
+
+    }
+
+    @Test
+    void testDeleteWithException() throws IOException
+    {
+        MockAsset root = createTree( );
+        MockStorage storage = new MockStorage( root );
+        root.list( ).get( 1 ).list( ).get( 2 ).setThrowException( true );
+
+        StorageUtil.deleteRecursively( root );
+        int expected = LEVEL1 * LEVEL2 * LEVEL3 + LEVEL1 * LEVEL2 + LEVEL1 + 1;
+        assertEquals( expected, storage.getStatus( ).size( MockStorage.REMOVE ) );
+
+    }
+
+    @Test
+    void testDeleteWithExceptionFailFast() throws IOException
+    {
+        MockAsset root = createTree( );
+        MockStorage storage = new MockStorage( root );
+        root.list( ).get( 1 ).list( ).get( 2 ).setThrowException( true );
+
+        StorageUtil.deleteRecursively( root, true );
+        int expected = 113;
+        assertEquals( expected, storage.getStatus( ).size( MockStorage.REMOVE ) );
+
+    }
 }
\ No newline at end of file
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/VisitStatus.java b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/VisitStatus.java
index 4df53ed..e0657d8 100644
--- a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/VisitStatus.java
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/VisitStatus.java
@@ -20,6 +20,8 @@ package org.apache.archiva.repository.storage.util;
 
 import org.apache.archiva.repository.storage.StorageAsset;
 
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.function.Consumer;
@@ -29,11 +31,13 @@ import java.util.function.Predicate;
 /**
  * @author Martin Stockhammer <ma...@apache.org>
  */
-class VisitStatus
+public class VisitStatus
 {
+
+    LinkedHashMap<String, LinkedList<StorageAsset>> applied = new LinkedHashMap<>( );
     LinkedList<StorageAsset> visited = new LinkedList<>( );
 
-    VisitStatus( )
+    public VisitStatus( )
     {
 
     }
@@ -44,6 +48,13 @@ class VisitStatus
         visited.addLast( asset );
     }
 
+    public void add(String type, StorageAsset asset) {
+        if (!applied.containsKey( type )) {
+            applied.put( type, new LinkedList<>( ) );
+        }
+        applied.get( type ).add( asset );
+    }
+
     public StorageAsset getLast( )
     {
         return visited.getLast( );
@@ -63,5 +74,9 @@ class VisitStatus
         return visited.size( );
     }
 
+    public int size(String type) {
+        return applied.get( type ).size( );
+    }
+
 
 }
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/test/resources/log4j2-test.xml b/archiva-modules/archiva-base/archiva-storage-api/src/test/resources/log4j2-test.xml
new file mode 100644
index 0000000..afe6cf5
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/test/resources/log4j2-test.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ 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.
+  -->
+
+
+<configuration status="error">
+  <appenders>
+    <Console name="console" target="SYSTEM_OUT">
+      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
+    </Console>
+  </appenders>
+  <loggers>
+
+    <logger name="org.apache.archiva.repository" level="info"/>
+    <logger name="org.springframework" level="error"/>
+
+    <root level="info">
+      <appender-ref ref="console"/>
+    </root>
+  </loggers>
+</configuration>
+
+
diff --git a/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FsStorageUtil.java b/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FsStorageUtil.java
new file mode 100644
index 0000000..a009440
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FsStorageUtil.java
@@ -0,0 +1,189 @@
+package org.apache.archiva.repository.storage;
+
+/*
+ * 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.archiva.common.filelock.FileLockException;
+import org.apache.archiva.common.filelock.FileLockManager;
+import org.apache.archiva.common.filelock.Lock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.nio.file.*;
+import java.util.HashSet;
+
+/**
+ *
+ * Utility class for assets. Allows to copy, move between different storage instances and
+ * recursively consume the tree.
+ *
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+public class FsStorageUtil
+{
+    private static final Logger log = LoggerFactory.getLogger( FsStorageUtil.class);
+
+    /**
+     * Copies the source asset to the target. The assets may be from different RepositoryStorage instances.
+     * If you know that source and asset are from the same storage instance, the copy method of the storage
+     * instance may be faster.
+     *
+     * @param source The source asset
+     * @param target The target asset
+     * @param locked If true, a readlock is set on the source and a write lock is set on the target.
+     * @param copyOptions Copy options
+     * @throws IOException
+     */
+    public static final void copyAsset( final StorageAsset source,
+                                        final StorageAsset target,
+                                        boolean locked,
+                                        final CopyOption... copyOptions ) throws IOException
+    {
+        if (source.isFileBased() && target.isFileBased()) {
+            // Short cut for FS operations
+            final Path sourcePath = source.getFilePath();
+            final Path targetPath = target.getFilePath( );
+            if (locked) {
+                final FileLockManager lmSource = ((FilesystemStorage)source.getStorage()).getFileLockManager();
+                final FileLockManager lmTarget = ((FilesystemStorage)target.getStorage()).getFileLockManager();
+                Lock lockRead = null;
+                Lock lockWrite = null;
+                try {
+                    lockRead = lmSource.readFileLock(sourcePath);
+                } catch (Exception e) {
+                    log.error("Could not create read lock on {}", sourcePath);
+                    throw new IOException(e);
+                }
+                try {
+                    lockWrite = lmTarget.writeFileLock(targetPath);
+                } catch (Exception e) {
+                    log.error("Could not create write lock on {}", targetPath);
+                    throw new IOException(e);
+                }
+                try {
+                    Files.copy(sourcePath, targetPath, copyOptions);
+                } finally {
+                    if (lockRead!=null) {
+                        try {
+                            lmSource.release(lockRead);
+                        } catch (FileLockException e) {
+                            log.error("Error during lock release of read lock {}", lockRead.getFile());
+                        }
+                    }
+                    if (lockWrite!=null) {
+                        try {
+                            lmTarget.release(lockWrite);
+                        } catch (FileLockException e) {
+                            log.error("Error during lock release of write lock {}", lockWrite.getFile());
+                        }
+                    }
+                }
+            } else
+            {
+                Files.copy( sourcePath, targetPath, copyOptions );
+            }
+        } else {
+            try {
+                final RepositoryStorage sourceStorage = source.getStorage();
+                final RepositoryStorage targetStorage = target.getStorage();
+                sourceStorage.consumeDataFromChannel( source, is -> org.apache.archiva.repository.storage.util.StorageUtil.wrapWriteFunction( is, targetStorage, target, locked ), locked);
+            }  catch (IOException e) {
+                throw e;
+            }  catch (Throwable e) {
+                Throwable cause = e.getCause();
+                if (cause instanceof IOException) {
+                    throw (IOException)cause;
+                } else
+                {
+                    throw new IOException( e );
+                }
+            }
+        }
+    }
+
+
+    public static final void copyToLocalFile(StorageAsset asset, Path destination, CopyOption... copyOptions) throws IOException {
+        if (asset.isFileBased()) {
+            Files.copy(asset.getFilePath(), destination, copyOptions);
+        } else {
+            try {
+
+                HashSet<OpenOption> openOptions = new HashSet<>();
+                for (CopyOption option : copyOptions) {
+                    if (option == StandardCopyOption.REPLACE_EXISTING) {
+                        openOptions.add(StandardOpenOption.CREATE);
+                        openOptions.add(StandardOpenOption.TRUNCATE_EXISTING);
+                        openOptions.add(StandardOpenOption.WRITE);
+                    } else {
+                        openOptions.add(StandardOpenOption.WRITE);
+                        openOptions.add(StandardOpenOption.CREATE_NEW);
+                    }
+                }
+                asset.getStorage().consumeDataFromChannel(asset, channel -> {
+                    try {
+                        FileChannel.open(destination, openOptions).transferFrom(channel, 0, Long.MAX_VALUE);
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+                }, false);
+            } catch (Throwable e) {
+                if (e.getCause() instanceof IOException) {
+                    throw (IOException)e.getCause();
+                } else {
+                    throw new IOException(e);
+                }
+            }
+        }
+    }
+
+    public static class PathInformation {
+        final Path path ;
+        final boolean tmpFile;
+
+        PathInformation(Path path, boolean tmpFile) {
+            this.path = path;
+            this.tmpFile = tmpFile;
+        }
+
+        public Path getPath() {
+            return path;
+        }
+
+        public boolean isTmpFile() {
+            return tmpFile;
+        }
+
+    }
+
+    public static final PathInformation getAssetDataAsPath(StorageAsset asset) throws IOException {
+        if (!asset.exists()) {
+            throw new IOException("Asset does not exist");
+        }
+        if (asset.isFileBased()) {
+            return new PathInformation(asset.getFilePath(), false);
+        } else {
+            Path tmpFile = Files.createTempFile(asset.getName(), org.apache.archiva.repository.storage.util.StorageUtil.getExtension(asset));
+            copyToLocalFile(asset, tmpFile, StandardCopyOption.REPLACE_EXISTING);
+            return new PathInformation(tmpFile, true);
+        }
+    }
+
+}
diff --git a/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/StorageUtil.java b/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/StorageUtil.java
deleted file mode 100644
index 839aab9..0000000
--- a/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/StorageUtil.java
+++ /dev/null
@@ -1,369 +0,0 @@
-package org.apache.archiva.repository.storage;
-
-/*
- * 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.archiva.common.filelock.FileLockException;
-import org.apache.archiva.common.filelock.FileLockManager;
-import org.apache.archiva.common.filelock.FileLockTimeoutException;
-import org.apache.archiva.common.filelock.Lock;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.WritableByteChannel;
-import java.nio.file.*;
-import java.util.HashSet;
-import java.util.function.Consumer;
-
-/**
- *
- * Utility class for assets. Allows to copy, move between different storage instances and
- * recursively consume the tree.
- *
- * @author Martin Stockhammer <ma...@apache.org>
- */
-public class StorageUtil
-{
-    private static final int DEFAULT_BUFFER_SIZE = 4096;
-    private static final Logger log = LoggerFactory.getLogger(StorageUtil.class);
-
-    /**
-     * Copies the source asset to the target. The assets may be from different RepositoryStorage instances.
-     * If you know that source and asset are from the same storage instance, the copy method of the storage
-     * instance may be faster.
-     *
-     * @param source The source asset
-     * @param target The target asset
-     * @param locked If true, a readlock is set on the source and a write lock is set on the target.
-     * @param copyOptions Copy options
-     * @throws IOException
-     */
-    public static final void copyAsset( final StorageAsset source,
-                                        final StorageAsset target,
-                                        boolean locked,
-                                        final CopyOption... copyOptions ) throws IOException
-    {
-        if (source.isFileBased() && target.isFileBased()) {
-            // Short cut for FS operations
-            final Path sourcePath = source.getFilePath();
-            final Path targetPath = target.getFilePath( );
-            if (locked) {
-                final FileLockManager lmSource = ((FilesystemStorage)source.getStorage()).getFileLockManager();
-                final FileLockManager lmTarget = ((FilesystemStorage)target.getStorage()).getFileLockManager();
-                Lock lockRead = null;
-                Lock lockWrite = null;
-                try {
-                    lockRead = lmSource.readFileLock(sourcePath);
-                } catch (Exception e) {
-                    log.error("Could not create read lock on {}", sourcePath);
-                    throw new IOException(e);
-                }
-                try {
-                    lockWrite = lmTarget.writeFileLock(targetPath);
-                } catch (Exception e) {
-                    log.error("Could not create write lock on {}", targetPath);
-                    throw new IOException(e);
-                }
-                try {
-                    Files.copy(sourcePath, targetPath, copyOptions);
-                } finally {
-                    if (lockRead!=null) {
-                        try {
-                            lmSource.release(lockRead);
-                        } catch (FileLockException e) {
-                            log.error("Error during lock release of read lock {}", lockRead.getFile());
-                        }
-                    }
-                    if (lockWrite!=null) {
-                        try {
-                            lmTarget.release(lockWrite);
-                        } catch (FileLockException e) {
-                            log.error("Error during lock release of write lock {}", lockWrite.getFile());
-                        }
-                    }
-                }
-            } else
-            {
-                Files.copy( sourcePath, targetPath, copyOptions );
-            }
-        } else {
-            try {
-                final RepositoryStorage sourceStorage = source.getStorage();
-                final RepositoryStorage targetStorage = target.getStorage();
-                sourceStorage.consumeDataFromChannel( source, is -> wrapWriteFunction( is, targetStorage, target, locked ), locked);
-            }  catch (IOException e) {
-                throw e;
-            }  catch (Throwable e) {
-                Throwable cause = e.getCause();
-                if (cause instanceof IOException) {
-                    throw (IOException)cause;
-                } else
-                {
-                    throw new IOException( e );
-                }
-            }
-        }
-    }
-
-    /**
-     * Moves a asset between different storage instances.
-     * If you know that source and asset are from the same storage instance, the move method of the storage
-     * instance may be faster.
-     *
-     * @param source The source asset
-     * @param target The target asset
-     * @param locked If true, a lock is used for the move operation.
-     * @param copyOptions Options for copying
-     * @throws IOException If the move fails
-     */
-    public static final void moveAsset(StorageAsset source, StorageAsset target, boolean locked, CopyOption... copyOptions) throws IOException
-    {
-        if (source.isFileBased() && target.isFileBased()) {
-            // Short cut for FS operations
-            // Move is atomic operation
-            if (!Files.exists(target.getFilePath().getParent())) {
-                Files.createDirectories(target.getFilePath().getParent());
-            }
-            Files.move( source.getFilePath(), target.getFilePath(), copyOptions );
-        } else {
-            try {
-                final RepositoryStorage sourceStorage = source.getStorage();
-                final RepositoryStorage targetStorage = target.getStorage();
-                sourceStorage.consumeDataFromChannel( source, is -> wrapWriteFunction( is, targetStorage, target, locked ), locked);
-                sourceStorage.removeAsset( source );
-            }  catch (IOException e) {
-                throw e;
-            }  catch (Throwable e) {
-                Throwable cause = e.getCause();
-                if (cause instanceof IOException) {
-                    throw (IOException)cause;
-                } else
-                {
-                    throw new IOException( e );
-                }
-            }
-        }
-
-    }
-
-    private static final void wrapWriteFunction(ReadableByteChannel is, RepositoryStorage targetStorage, StorageAsset target, boolean locked) {
-        try {
-            targetStorage.writeDataToChannel( target, os -> copy(is, os), locked );
-        } catch (Exception e) {
-            throw new RuntimeException( e );
-        }
-    }
-
-
-    private static final void copy( final ReadableByteChannel is, final WritableByteChannel os ) {
-        if (is instanceof FileChannel) {
-            copy( (FileChannel) is, os );
-        } else if (os instanceof FileChannel) {
-            copy(is, (FileChannel)os);
-        } else
-        {
-            try
-            {
-                ByteBuffer buffer = ByteBuffer.allocate( DEFAULT_BUFFER_SIZE );
-                while ( is.read( buffer ) != -1 )
-                {
-                    buffer.flip( );
-                    while ( buffer.hasRemaining( ) )
-                    {
-                        os.write( buffer );
-                    }
-                    buffer.clear( );
-                }
-            }
-            catch ( IOException e )
-            {
-                throw new RuntimeException( e );
-            }
-        }
-    }
-
-    private static final void copy( final FileChannel is, final WritableByteChannel os ) {
-        try
-        {
-            is.transferTo( 0, is.size( ), os );
-        }
-        catch ( IOException e )
-        {
-            throw new RuntimeException( e );
-        }
-    }
-
-    private static final void copy( final ReadableByteChannel is, final FileChannel os ) {
-        try
-        {
-            os.transferFrom( is, 0, Long.MAX_VALUE );
-        }
-        catch ( IOException e )
-        {
-            throw new RuntimeException( e );
-        }
-    }
-
-    /**
-     * Runs the consumer function recursively on each asset found starting at the base path
-     * @param baseAsset The base path where to start search
-     * @param consumer The consumer function applied to each found asset
-     * @param depthFirst If true, the deepest elements are consumed first.
-     * @param maxDepth The maximum depth to recurse into. 0 means, only the baseAsset is consumed, 1 the base asset and its children and so forth.
-     */
-    public static final void recurse(final StorageAsset baseAsset, final Consumer<StorageAsset> consumer, final boolean depthFirst, final int maxDepth) throws IOException {
-        recurse(baseAsset, consumer, depthFirst, maxDepth, 0);
-    }
-
-    /**
-     * Runs the consumer function recursively on each asset found starting at the base path. The function descends into
-     * maximum depth.
-     *
-     * @param baseAsset The base path where to start search
-     * @param consumer The consumer function applied to each found asset
-     * @param depthFirst If true, the deepest elements are consumed first.
-     */
-    public static final void recurse(final StorageAsset baseAsset, final Consumer<StorageAsset> consumer, final boolean depthFirst) throws IOException {
-        recurse(baseAsset, consumer, depthFirst, Integer.MAX_VALUE, 0);
-    }
-
-    /**
-     * Runs the consumer function recursively on each asset found starting at the base path. It does not recurse with
-     * depth first and stops only if there are no more children available.
-     *
-     * @param baseAsset The base path where to start search
-     * @param consumer The consumer function applied to each found asset
-     */
-    public static final void recurse(final StorageAsset baseAsset, final Consumer<StorageAsset> consumer) throws IOException {
-        recurse(baseAsset, consumer, false, Integer.MAX_VALUE, 0);
-    }
-
-    private static final void recurse(final StorageAsset baseAsset, final Consumer<StorageAsset> consumer, final boolean depthFirst, final int maxDepth, final int currentDepth)
-    throws IOException {
-        if (!depthFirst) {
-            consumer.accept(baseAsset);
-        }
-        if (currentDepth<maxDepth && baseAsset.isContainer()) {
-            for(StorageAsset asset : baseAsset.list() ) {
-                recurse(asset, consumer, depthFirst, maxDepth, currentDepth+1);
-            }
-        }
-        if (depthFirst) {
-            consumer.accept(baseAsset);
-        }
-    }
-
-    /**
-     * Deletes the given asset and all child assets recursively.
-     * @param baseDir The base asset to remove.
-     * @throws IOException
-     */
-    public static final void deleteRecursively(StorageAsset baseDir) throws IOException {
-        recurse(baseDir, a -> {
-            try {
-                a.getStorage().removeAsset(a);
-            } catch (IOException e) {
-                log.error("Could not delete asset {}", a.getPath());
-            }
-        },true);
-    }
-
-    /**
-     * Returns the extension of the name of a given asset. Extension is the substring after the last occurence of '.' in the
-     * string. If no '.' is found, the empty string is returned.
-     *
-     * @param asset The asset from which to return the extension string.
-     * @return The extension.
-     */
-    public static final String getExtension(StorageAsset asset) {
-        return StringUtils.substringAfterLast(asset.getName(),".");
-    }
-
-    public static final void copyToLocalFile(StorageAsset asset, Path destination, CopyOption... copyOptions) throws IOException {
-        if (asset.isFileBased()) {
-            Files.copy(asset.getFilePath(), destination, copyOptions);
-        } else {
-            try {
-
-                HashSet<OpenOption> openOptions = new HashSet<>();
-                for (CopyOption option : copyOptions) {
-                    if (option == StandardCopyOption.REPLACE_EXISTING) {
-                        openOptions.add(StandardOpenOption.CREATE);
-                        openOptions.add(StandardOpenOption.TRUNCATE_EXISTING);
-                        openOptions.add(StandardOpenOption.WRITE);
-                    } else {
-                        openOptions.add(StandardOpenOption.WRITE);
-                        openOptions.add(StandardOpenOption.CREATE_NEW);
-                    }
-                }
-                asset.getStorage().consumeDataFromChannel(asset, channel -> {
-                    try {
-                        FileChannel.open(destination, openOptions).transferFrom(channel, 0, Long.MAX_VALUE);
-                    } catch (IOException e) {
-                        throw new RuntimeException(e);
-                    }
-                }, false);
-            } catch (Throwable e) {
-                if (e.getCause() instanceof IOException) {
-                    throw (IOException)e.getCause();
-                } else {
-                    throw new IOException(e);
-                }
-            }
-        }
-    }
-
-    public static class PathInformation {
-        final Path path ;
-        final boolean tmpFile;
-
-        PathInformation(Path path, boolean tmpFile) {
-            this.path = path;
-            this.tmpFile = tmpFile;
-        }
-
-        public Path getPath() {
-            return path;
-        }
-
-        public boolean isTmpFile() {
-            return tmpFile;
-        }
-
-    }
-
-    public static final PathInformation getAssetDataAsPath(StorageAsset asset) throws IOException {
-        if (!asset.exists()) {
-            throw new IOException("Asset does not exist");
-        }
-        if (asset.isFileBased()) {
-            return new PathInformation(asset.getFilePath(), false);
-        } else {
-            Path tmpFile = Files.createTempFile(asset.getName(), getExtension(asset));
-            copyToLocalFile(asset, tmpFile, StandardCopyOption.REPLACE_EXISTING);
-            return new PathInformation(tmpFile, true);
-        }
-    }
-
-}
diff --git a/archiva-modules/archiva-maven/archiva-maven-indexer/src/main/java/org/apache/archiva/indexer/maven/MavenIndexManager.java b/archiva-modules/archiva-maven/archiva-maven-indexer/src/main/java/org/apache/archiva/indexer/maven/MavenIndexManager.java
index 7e007f0..777e6eb 100644
--- a/archiva-modules/archiva-maven/archiva-maven-indexer/src/main/java/org/apache/archiva/indexer/maven/MavenIndexManager.java
+++ b/archiva-modules/archiva-maven/archiva-maven-indexer/src/main/java/org/apache/archiva/indexer/maven/MavenIndexManager.java
@@ -43,7 +43,6 @@ import org.apache.archiva.repository.storage.RepositoryStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.repository.features.IndexCreationFeature;
 import org.apache.archiva.repository.features.RemoteIndexFeature;
-import org.apache.archiva.repository.storage.StorageUtil;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.maven.index.ArtifactContext;
 import org.apache.maven.index.ArtifactContextProducer;
@@ -476,11 +475,7 @@ public class MavenIndexManager implements ArchivaIndexManager {
             } catch (IOException e) {
                 log.warn("Index close failed");
             }
-            try {
-                StorageUtil.deleteRecursively(context.getPath());
-            } catch (IOException e) {
-                throw new IndexUpdateFailedException("Could not delete index files");
-            }
+            org.apache.archiva.repository.storage.util.StorageUtil.deleteRecursively(context.getPath());
         });
         try {
             Repository repo = context.getRepository();
diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/content/maven2/ManagedDefaultRepositoryContent.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/content/maven2/ManagedDefaultRepositoryContent.java
index 95196ff..780cf35 100644
--- a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/content/maven2/ManagedDefaultRepositoryContent.java
+++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/content/maven2/ManagedDefaultRepositoryContent.java
@@ -638,7 +638,7 @@ public class ManagedDefaultRepositoryContent
 
         // First gather up the versions found as artifacts in the managed repository.
 
-        try (Stream<StorageAsset> stream = artifactDir.list().stream() ) {
+        try (Stream<? extends StorageAsset> stream = artifactDir.list().stream() ) {
             return stream.filter(asset -> !asset.isContainer()).map(path -> {
                 try {
                     ArtifactReference artifact = toArtifactReference(path.getPath());
@@ -744,7 +744,7 @@ public class ManagedDefaultRepositoryContent
 
         // First gather up the versions found as artifacts in the managed repository.
 
-        try (Stream<StorageAsset> stream = repoDir.list().stream() ) {
+        try (Stream<? extends StorageAsset> stream = repoDir.list().stream() ) {
             return stream.filter(
                 asset -> !asset.isContainer())
                 .map(path -> {
diff --git a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/java/org/apache/archiva/mock/ArchivaIndexManagerMock.java b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/java/org/apache/archiva/mock/ArchivaIndexManagerMock.java
index fb175c0..1776c8f 100644
--- a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/java/org/apache/archiva/mock/ArchivaIndexManagerMock.java
+++ b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/java/org/apache/archiva/mock/ArchivaIndexManagerMock.java
@@ -44,7 +44,6 @@ import org.apache.archiva.repository.storage.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.repository.features.IndexCreationFeature;
 import org.apache.archiva.repository.features.RemoteIndexFeature;
-import org.apache.archiva.repository.storage.StorageUtil;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.maven.index.ArtifactContext;
 import org.apache.maven.index.ArtifactContextProducer;
@@ -443,11 +442,7 @@ public class ArchivaIndexManagerMock implements ArchivaIndexManager {
             } catch (IOException e) {
                 log.warn("Index close failed");
             }
-            try {
-                StorageUtil.deleteRecursively(context.getPath());
-            } catch (IOException e) {
-                throw new IndexUpdateFailedException("Could not delete index files");
-            }
+            org.apache.archiva.repository.storage.util.StorageUtil.deleteRecursively(context.getPath());
         });
         try {
             Repository repo = context.getRepository();
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultBrowseService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultBrowseService.java
index 7899fdf..053e12c 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultBrowseService.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultBrowseService.java
@@ -22,7 +22,6 @@ import org.apache.archiva.admin.model.beans.ManagedRepository;
 import org.apache.archiva.common.utils.VersionComparator;
 import org.apache.archiva.common.utils.VersionUtil;
 import org.apache.archiva.dependency.tree.maven2.DependencyTreeBuilder;
-import org.apache.archiva.maven2.metadata.MavenMetadataReader;
 import org.apache.archiva.maven2.model.Artifact;
 import org.apache.archiva.maven2.model.TreeEntry;
 import org.apache.archiva.metadata.generic.GenericMetadataFacet;
@@ -45,14 +44,13 @@ import org.apache.archiva.repository.RepositoryNotFoundException;
 import org.apache.archiva.repository.RepositoryRegistry;
 import org.apache.archiva.repository.metadata.MetadataReader;
 import org.apache.archiva.repository.metadata.base.MetadataTools;
+import org.apache.archiva.repository.storage.FsStorageUtil;
 import org.apache.archiva.repository.storage.StorageAsset;
-import org.apache.archiva.repository.storage.StorageUtil;
 import org.apache.archiva.rest.api.model.*;
 import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
 import org.apache.archiva.rest.api.services.BrowseService;
 import org.apache.archiva.rest.services.utils.ArtifactContentEntryComparator;
 import org.apache.archiva.security.ArchivaSecurityException;
-import org.apache.archiva.xml.XMLException;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -849,7 +847,7 @@ public class DefaultBrowseService
                 if ( StringUtils.isNotBlank( path ) )
                 {
                     // zip entry of the path -> path must a real file entry of the archive
-                    StorageUtil.PathInformation pathInfo = StorageUtil.getAssetDataAsPath(file);
+                    FsStorageUtil.PathInformation pathInfo = FsStorageUtil.getAssetDataAsPath(file);
                     JarFile jarFile = new JarFile( pathInfo.getPath().toFile());
                     ZipEntry zipEntry = jarFile.getEntry( path );
                     try (InputStream inputStream = jarFile.getInputStream( zipEntry ))
@@ -1180,7 +1178,7 @@ public class DefaultBrowseService
             filterDepth++;
         }
 
-        StorageUtil.PathInformation pathInfo = StorageUtil.getAssetDataAsPath(file);
+        FsStorageUtil.PathInformation pathInfo = FsStorageUtil.getAssetDataAsPath(file);
         JarFile jarFile = new JarFile(pathInfo.getPath().toFile());
         try
         {
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultRepositoriesService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultRepositoriesService.java
index c313cd9..0dcaee6 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultRepositoriesService.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultRepositoriesService.java
@@ -51,9 +51,9 @@ import org.apache.archiva.repository.RepositoryException;
 import org.apache.archiva.repository.RepositoryNotFoundException;
 import org.apache.archiva.repository.RepositoryRegistry;
 import org.apache.archiva.repository.RepositoryType;
+import org.apache.archiva.repository.storage.FsStorageUtil;
 import org.apache.archiva.repository.storage.RepositoryStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
-import org.apache.archiva.repository.storage.StorageUtil;
 import org.apache.archiva.metadata.audit.RepositoryListener;
 import org.apache.archiva.repository.metadata.base.MetadataTools;
 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
@@ -528,7 +528,7 @@ public class DefaultRepositoriesService
         throws IOException
     {
 
-        StorageUtil.copyAsset( sourceFile, targetPath, true );
+        FsStorageUtil.copyAsset( sourceFile, targetPath, true );
         if ( fixChecksums )
         {
             fixChecksums( targetPath );
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/utils/ArtifactBuilder.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/utils/ArtifactBuilder.java
index 2c6db17..76d3292 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/utils/ArtifactBuilder.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/utils/ArtifactBuilder.java
@@ -24,10 +24,8 @@ import org.apache.archiva.metadata.model.maven2.MavenArtifactFacet;
 import org.apache.archiva.model.ArtifactReference;
 import org.apache.archiva.repository.ManagedRepositoryContent;
 import org.apache.archiva.repository.storage.StorageAsset;
-import org.apache.archiva.repository.storage.StorageUtil;
-import org.apache.commons.io.FilenameUtils;
+import org.apache.archiva.repository.storage.util.StorageUtil;
 
-import java.nio.file.Path;
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
 import java.util.Locale;
diff --git a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/util/IndexWriter.java b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/util/IndexWriter.java
index f224c49..415a01f 100644
--- a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/util/IndexWriter.java
+++ b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/util/IndexWriter.java
@@ -168,7 +168,7 @@ public class IndexWriter
             SortedMap<String, StorageAsset> uniqueChildFiles = new TreeMap<>();
             for ( StorageAsset resource : repositoryAssets )
             {
-                List<StorageAsset> files = resource.list();
+                List<? extends StorageAsset> files = resource.list();
                 for ( StorageAsset file : files )
                 {
                     // the first entry wins


[archiva] 01/05: Adding stream based asset utility

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit de22e846edafd2ba8b06ab135be43b4c596c3db0
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Sat Feb 29 16:18:48 2020 +0100

    Adding stream based asset utility
---
 .../storage/{ => util}/AssetSpliterator.java       |  48 ++++--
 .../repository/storage/util/StorageUtil.java       |  93 ++++++++++++
 .../repository/storage/AssetSpliteratorTest.java   | 100 -------------
 .../archiva/repository/storage/mock/MockAsset.java |  21 ++-
 .../storage/util/AssetSpliteratorTest.java         | 161 +++++++++++++++++++++
 .../storage/util/ConsumeVisitStatus.java           |  35 +++++
 .../repository/storage/util/StopVisitStatus.java   |  45 ++++++
 .../repository/storage/util/StorageUtilTest.java   | 134 +++++++++++++++++
 .../repository/storage/util/VisitStatus.java       |  67 +++++++++
 9 files changed, 586 insertions(+), 118 deletions(-)

diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/AssetSpliterator.java b/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/AssetSpliterator.java
similarity index 81%
rename from archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/AssetSpliterator.java
rename to archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/AssetSpliterator.java
index 8195de7..7b45e12 100644
--- a/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/AssetSpliterator.java
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/AssetSpliterator.java
@@ -1,3 +1,5 @@
+package org.apache.archiva.repository.storage.util;
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -16,7 +18,7 @@
  * under the License.
  */
 
-package org.apache.archiva.repository.storage;
+import org.apache.archiva.repository.storage.StorageAsset;
 
 import java.io.Closeable;
 import java.util.Collections;
@@ -29,8 +31,6 @@ import java.util.Spliterator;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
 
 /**
  *
@@ -39,7 +39,13 @@ import java.util.stream.StreamSupport;
  * parents. If the spliterator is used in a parallel stream, there is no guarantee for
  * the order of returned assets.
  *
- * The estimated size is not accurate, because the tree paths are scanned on demand.
+ * The estimated size is not accurate, because the tree paths are scanned on demand (lazy loaded)
+ *
+ * The spliterator returns the status of the assets at the time of retrieval. If modifications occur
+ * during traversal the returned assets may not represent the latest state.
+ * There is no check for modifications during traversal and no <code>{@link java.util.ConcurrentModificationException}</code> are thrown.
+ *
+ *
  *
  * @since 3.0
  * @author Martin Stockhammer <ma...@apache.org>
@@ -53,35 +59,45 @@ public class AssetSpliterator implements Spliterator<StorageAsset>, Closeable
     private LinkedHashSet<StorageAsset> visitedContainers = new LinkedHashSet<>( );
     private long visited = 0;
     private final int splitThreshold;
-    private static final int CHARACTERISTICS =  Spliterator.DISTINCT|Spliterator.NONNULL;
+    private static final int CHARACTERISTICS =  Spliterator.DISTINCT|Spliterator.NONNULL|Spliterator.CONCURRENT;
 
 
-    AssetSpliterator( int splitThreshold, StorageAsset... assets) {
+    public AssetSpliterator( int splitThreshold, StorageAsset... assets) {
         this.splitThreshold = splitThreshold;
+        init( assets );
+    }
+
+    private void init( StorageAsset[] assets )
+    {
+        if (assets.length==0 || assets[0] == null) {
+            throw new IllegalArgumentException( "There must be at least one non-null asset" );
+        }
         Collections.addAll( this.workList, assets );
+        retrieveNextPath( this.workList.get( 0 ) );
     }
 
-    AssetSpliterator( StorageAsset... assets) {
+    public AssetSpliterator( StorageAsset... assets) {
         this.splitThreshold = DEFAULT_SPLIT_THRESHOLD;
-        Collections.addAll( this.workList, assets );
+        init( assets );
     }
 
-    AssetSpliterator() {
+    protected AssetSpliterator() {
         this.splitThreshold = DEFAULT_SPLIT_THRESHOLD;
     }
 
-    AssetSpliterator( int splitThreshold) {
+    protected AssetSpliterator( int splitThreshold) {
         this.splitThreshold = splitThreshold;
     }
 
 
-    AssetSpliterator( int splitThreshold, Set<StorageAsset> visitedContainers) {
+    protected AssetSpliterator( int splitThreshold, Set<StorageAsset> visitedContainers) {
         this.visitedContainers.addAll( visitedContainers );
         this.splitThreshold = splitThreshold;
     }
 
-    AssetSpliterator( List<StorageAsset> baseList, Set<StorageAsset> visitedContainers) {
+    protected AssetSpliterator( List<StorageAsset> baseList, Set<StorageAsset> visitedContainers) {
         this.workList.addAll(baseList);
+        retrieveNextPath( this.workList.get( 0 ) );
         this.visitedContainers.addAll( visitedContainers );
         this.splitThreshold = DEFAULT_SPLIT_THRESHOLD;
     }
@@ -150,7 +166,7 @@ public class AssetSpliterator implements Spliterator<StorageAsset>, Closeable
         }
     }
 
-    // In reverse order
+    // Assets are returned in reverse order
     List<StorageAsset> getChildContainers( StorageAsset parent) {
         final List<StorageAsset> children = parent.list( );
         final int len = children.size( );
@@ -158,7 +174,7 @@ public class AssetSpliterator implements Spliterator<StorageAsset>, Closeable
             children.get(len - i - 1)).filter( StorageAsset::isContainer ).collect( Collectors.toList( ) );
     }
 
-    // In reverse order
+    // Assets are returned in reverse order
     List<StorageAsset> getChildFiles(StorageAsset parent) {
         final List<StorageAsset> children = parent.list( );
         final int len = children.size( );
@@ -187,8 +203,8 @@ public class AssetSpliterator implements Spliterator<StorageAsset>, Closeable
                 //noinspection InfiniteLoopStatement
                 while (true)
                 {
-                    newWorkList.add( workList.getFirst( ) );
-                    newSpliterator.add( workList.getFirst( ) );
+                    newWorkList.add( workList.removeFirst( ) );
+                    newSpliterator.add( workList.removeFirst( ) );
                 }
             } catch (NoSuchElementException e) {
                 //
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/StorageUtil.java b/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/StorageUtil.java
new file mode 100644
index 0000000..b912ee3
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/StorageUtil.java
@@ -0,0 +1,93 @@
+package org.apache.archiva.repository.storage.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 org.apache.archiva.repository.storage.StorageAsset;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+/**
+ *
+ * Utility class for traversing the asset tree recursively and stream based access to the assets.
+ *
+ * @since 3.0
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+public class StorageUtil
+{
+    /**
+     * Walk the tree starting at the given asset. The consumer is called for each asset found.
+     * It runs a depth-first search where children are consumed before their parents.
+     *
+     * @param start the starting asset
+     * @param consumer the consumer that is applied to each asset
+     */
+    public static void walk( StorageAsset start, Consumer<StorageAsset> consumer ) {
+        try(Stream<StorageAsset> assetStream = newAssetStream( start, false )) {
+            assetStream.forEach( consumer::accept );
+        }
+    }
+
+    /**
+     * Walk the tree starting at the given asset. The consumer function is called for each asset found
+     * as long as it returns <code>true</code> as result. If the function returns <code>false</code> the
+     * processing stops.
+     * It runs a depth-first search where children are consumed before their parents.
+     *
+     * @param start the starting asset
+     * @param consumer the consumer function that is applied to each asset and that has to return <code>true</code>,
+     *                 if the walk should continue.
+     */
+    public static void walk( StorageAsset start, Function<StorageAsset, Boolean> consumer ) {
+        try(Stream<StorageAsset> assetStream = newAssetStream( start, false )) {
+            assetStream.anyMatch( a -> !consumer.apply( a ) );
+        }
+    }
+
+
+    /**
+     * Returns a stream of assets starting at the given start node. The returned stream returns a closable
+     * stream and should always be used in a try-with-resources statement.
+     *
+     * @param start the starting asset
+     * @param parallel <code>true</code>, if a parallel stream should be created, otherwise <code>false</code>
+     * @return the newly created stream
+     */
+    public static Stream<StorageAsset> newAssetStream( StorageAsset start, boolean parallel )
+    {
+        return StreamSupport.stream( new AssetSpliterator( start ), parallel );
+    }
+
+
+    /**
+     * Returns a non-parallel stream.
+     * Calls {@link #newAssetStream(StorageAsset, boolean)} with <code>parallel=false</code>.
+     *
+     * @param start the starting asset
+     * @return the returned stream object
+     */
+    public static Stream<StorageAsset> newAssetStream( StorageAsset start) {
+        return newAssetStream( start, false );
+    }
+
+
+}
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/AssetSpliteratorTest.java b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/AssetSpliteratorTest.java
deleted file mode 100644
index 7dea353..0000000
--- a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/AssetSpliteratorTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package org.apache.archiva.repository.storage;
-
-/*
- * 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.archiva.repository.storage.mock.MockAsset;
-import org.junit.jupiter.api.Test;
-
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-/**
- * Test the AssetSpliterator class
- *
- * @author Martin Stockhammer <ma...@apache.org>
- */
-class AssetSpliteratorTest
-{
-
-    private StorageAsset createTree() {
-        MockAsset root = new MockAsset( "" );
-        for (int i=0; i<10; i++) {
-            String name1 = "a" + String.format("%03d",i);
-            MockAsset parent1 = new MockAsset( root, name1 );
-            for (int k=0; k<15; k++) {
-                String name2 = name1 + String.format("%03d", k);
-                MockAsset parent2 = new MockAsset( parent1, name2 );
-                for (int u=0; u<5; u++) {
-                    String name3 = name2 + String.format("%03d", u);
-                    MockAsset parent3 = new MockAsset( parent2, name3 );
-                }
-            }
-        }
-        return root;
-    }
-
-    private class Status {
-        LinkedList<StorageAsset> visited = new LinkedList<>( );
-
-        Status() {
-
-        }
-
-        public void add(StorageAsset asset) {
-            visited.addLast( asset );
-        }
-
-        public StorageAsset getLast() {
-            return visited.getLast( );
-        }
-
-        public List<StorageAsset> getVisited() {
-            return visited;
-        }
-
-        public int size() {
-            return visited.size( );
-        }
-    }
-
-    @Test
-    void tryAdvance( )
-    {
-        StorageAsset root = createTree( );
-        AssetSpliterator spliterator = new AssetSpliterator( root );
-        final StorageAsset expectedTarget = root.list( ).get( 0 ).list( ).get( 0 ).list( ).get( 0 );
-        final Status status = new Status( );
-        spliterator.tryAdvance( a -> status.add( a ) );
-        assertEquals( expectedTarget, status.getLast( ) );
-    }
-
-    @Test
-    void forEachRemaining( )
-    {
-    }
-
-    @Test
-    void trySplit( )
-    {
-    }
-}
\ No newline at end of file
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/mock/MockAsset.java b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/mock/MockAsset.java
index 0d1764f..3baad4f 100644
--- a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/mock/MockAsset.java
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/mock/MockAsset.java
@@ -45,12 +45,12 @@ public class MockAsset implements StorageAsset
 
     public MockAsset( String name ) {
         this.name = name;
-        this.path = "";
+        this.path = "/";
     }
 
     public MockAsset( MockAsset parent, String name ) {
         this.parent = parent;
-        this.path = parent.getPath( ) + "/" + name;
+        this.path = (parent.hasParent()?parent.getPath( ):"") + "/" + name;
         this.name = name;
         parent.registerChild( this );
     }
@@ -189,4 +189,21 @@ public class MockAsset implements StorageAsset
     {
         return getPath();
     }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o ) return true;
+        if ( o == null || getClass( ) != o.getClass( ) ) return false;
+
+        MockAsset mockAsset = (MockAsset) o;
+
+        return path.equals( mockAsset.path );
+    }
+
+    @Override
+    public int hashCode( )
+    {
+        return path.hashCode( );
+    }
 }
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/AssetSpliteratorTest.java b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/AssetSpliteratorTest.java
new file mode 100644
index 0000000..ee16fe8
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/AssetSpliteratorTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.archiva.repository.storage.util;
+
+import org.apache.archiva.repository.storage.StorageAsset;
+import org.apache.archiva.repository.storage.mock.MockAsset;
+import org.junit.jupiter.api.Test;
+
+import java.util.Spliterator;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * Test the AssetSpliterator class
+ *
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+class AssetSpliteratorTest
+{
+
+    private static int LEVEL1 = 10;
+    private static int LEVEL2 = 15;
+    private static int LEVEL3 = 5;
+
+
+
+    private StorageAsset createTree() {
+        return createTree( LEVEL1, LEVEL2, LEVEL3 );
+    }
+
+    private StorageAsset createTree(int... levelElements) {
+        MockAsset root = new MockAsset( "" );
+        recurseSubTree( root, 0, levelElements );
+        return root;
+    }
+
+    private void recurseSubTree(MockAsset parent, int level, int[] levelElements) {
+        if (level < levelElements.length)
+        {
+            for ( int k = 0; k < levelElements[level]; k++ )
+            {
+                String name = parent.getName( ) + String.format( "%03d", k );
+                MockAsset asset = new MockAsset( parent, name );
+                recurseSubTree( asset, level + 1, levelElements );
+            }
+        }
+    }
+
+    @Test
+    void tryAdvance( )
+    {
+        StorageAsset root = createTree( );
+        AssetSpliterator spliterator = new AssetSpliterator( root );
+        final ConsumeVisitStatus status = new ConsumeVisitStatus( );
+        StorageAsset expectedTarget = root.list( ).get( 0 ).list( ).get( 0 ).list( ).get( 0 );
+        spliterator.tryAdvance( status );
+        assertEquals( 1, status.size( ) );
+        assertEquals( expectedTarget, status.getLast( ) );
+
+        spliterator.tryAdvance( status );
+        assertEquals( 2, status.size( ) );
+        expectedTarget = root.list( ).get( 0 ).list( ).get( 0 ).list( ).get( 1 );
+        assertEquals( expectedTarget, status.getLast( ) );
+
+    }
+
+    @Test
+    void forEachRemaining( )
+    {
+        StorageAsset root = createTree( );
+        AssetSpliterator spliterator = new AssetSpliterator( root );
+        final ConsumeVisitStatus status = new ConsumeVisitStatus( );
+        spliterator.forEachRemaining( status );
+        // 10 * 15 * 5 + 10 * 15 + 10 + 1
+        assertEquals( LEVEL1*LEVEL2*LEVEL3+LEVEL1*LEVEL2+LEVEL1+1
+            , status.size( ) );
+        assertEquals( root, status.getLast( ) );
+    }
+
+    @Test
+    void forEachRemaining2( )
+    {
+        StorageAsset root = createTree( );
+        AssetSpliterator spliterator = new AssetSpliterator( root );
+        final ConsumeVisitStatus status = new ConsumeVisitStatus( );
+        spliterator.tryAdvance( a -> {} );
+        spliterator.tryAdvance( a -> {} );
+        spliterator.tryAdvance( a -> {} );
+        spliterator.tryAdvance( a -> {} );
+
+        spliterator.forEachRemaining( status );
+        int expected = LEVEL1 * LEVEL2 * LEVEL3 + LEVEL1 * LEVEL2 + LEVEL1 + 1;
+        expected = expected - 4;
+        assertEquals( expected
+            , status.size( ) );
+        assertEquals( root, status.getLast( ) );
+    }
+
+    @Test
+    void forEachRemaining3( )
+    {
+        StorageAsset root = createTree( );
+        StorageAsset testRoot = root.list( ).get( 1 );
+        AssetSpliterator spliterator = new AssetSpliterator( testRoot );
+        final ConsumeVisitStatus status = new ConsumeVisitStatus( );
+        spliterator.forEachRemaining( status );
+        int expected = LEVEL2 * LEVEL3 + LEVEL2 + 1;
+        assertEquals( expected
+            , status.size( ) );
+        assertEquals( testRoot, status.getLast( ) );
+    }
+
+
+    @Test
+    void trySplit( )
+    {
+        StorageAsset root = createTree( );
+        AssetSpliterator spliterator = new AssetSpliterator( root );
+        final ConsumeVisitStatus status1 = new ConsumeVisitStatus( );
+        final ConsumeVisitStatus status2 = new ConsumeVisitStatus( );
+        Spliterator<StorageAsset> newSpliterator = spliterator.trySplit( );
+        assertNotNull( newSpliterator );
+        newSpliterator.forEachRemaining( status1 );
+        spliterator.forEachRemaining( status2 );
+
+        int sum = LEVEL1 * LEVEL2 * LEVEL3 + LEVEL1 * LEVEL2 + LEVEL1 + 1;
+        int expected1 = sum / 2;
+        int expected2 = sum / 2 + 1 ;
+        assertEquals( expected1, status1.size( ) );
+        assertEquals( expected2, status2.size( ) );
+
+    }
+
+    @Test
+    void checkCharacteristics() {
+        StorageAsset root = createTree( );
+        AssetSpliterator spliterator = new AssetSpliterator( root );
+        assertEquals( Spliterator.NONNULL, spliterator.characteristics( ) & Spliterator.NONNULL );
+        assertEquals( Spliterator.CONCURRENT, spliterator.characteristics( ) & Spliterator.CONCURRENT );
+        assertEquals( Spliterator.DISTINCT, spliterator.characteristics( ) & Spliterator.DISTINCT );
+
+
+    }
+}
\ No newline at end of file
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/ConsumeVisitStatus.java b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/ConsumeVisitStatus.java
new file mode 100644
index 0000000..dc03860
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/ConsumeVisitStatus.java
@@ -0,0 +1,35 @@
+package org.apache.archiva.repository.storage.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 org.apache.archiva.repository.storage.StorageAsset;
+
+import java.util.function.Consumer;
+
+/**
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+public class ConsumeVisitStatus extends VisitStatus implements Consumer<StorageAsset>
+{
+    @Override
+    public void accept( StorageAsset asset )
+    {
+        add( asset );
+    }
+}
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/StopVisitStatus.java b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/StopVisitStatus.java
new file mode 100644
index 0000000..a0b17df
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/StopVisitStatus.java
@@ -0,0 +1,45 @@
+package org.apache.archiva.repository.storage.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 org.apache.archiva.repository.storage.StorageAsset;
+
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+public class StopVisitStatus extends VisitStatus implements Function<StorageAsset, Boolean>
+{
+    private Predicate<StorageAsset> stopCondition;
+
+    public void setStopCondition( Predicate<StorageAsset> predicate )
+    {
+        this.stopCondition = predicate;
+    }
+
+    @Override
+    public Boolean apply( StorageAsset asset )
+    {
+        add( asset );
+        return !stopCondition.test( asset );
+    }
+
+}
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/StorageUtilTest.java b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/StorageUtilTest.java
new file mode 100644
index 0000000..7907754
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/StorageUtilTest.java
@@ -0,0 +1,134 @@
+package org.apache.archiva.repository.storage.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 org.apache.archiva.repository.storage.StorageAsset;
+import org.apache.archiva.repository.storage.mock.MockAsset;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+class StorageUtilTest
+{
+    private static int LEVEL1 = 12;
+    private static int LEVEL2 = 13;
+    private static int LEVEL3 = 6;
+
+
+
+    private StorageAsset createTree() {
+        return createTree( LEVEL1, LEVEL2, LEVEL3 );
+    }
+
+    private StorageAsset createTree(int... levelElements) {
+        MockAsset root = new MockAsset( "" );
+        recurseSubTree( root, 0, levelElements );
+        return root;
+    }
+
+    private void recurseSubTree(MockAsset parent, int level, int[] levelElements) {
+        if (level < levelElements.length)
+        {
+            for ( int k = 0; k < levelElements[level]; k++ )
+            {
+                String name = parent.getName( ) + String.format( "%03d", k );
+                MockAsset asset = new MockAsset( parent, name );
+                recurseSubTree( asset, level + 1, levelElements );
+            }
+        }
+    }
+
+    @Test
+    void testWalkFromRoot() {
+        StorageAsset root = createTree( );
+        ConsumeVisitStatus status = new ConsumeVisitStatus( );
+
+        StorageUtil.walk( root, status );
+        int expected = LEVEL1 * LEVEL2 * LEVEL3 + LEVEL1 * LEVEL2 + LEVEL1 + 1;
+        assertEquals( expected, status.size() );
+        StorageAsset first = root.list( ).get( 0 ).list( ).get( 0 ).list().get(0);
+        assertEquals( first, status.getFirst( ) );
+        assertEquals( root, status.getLast( ) );
+    }
+
+    @Test
+    void testWalkFromChild() {
+        StorageAsset root = createTree( );
+        ConsumeVisitStatus status = new ConsumeVisitStatus( );
+        StorageAsset testRoot = root.list( ).get( 3 );
+
+        StorageUtil.walk( testRoot, status );
+        int expected = LEVEL2 * LEVEL3 + LEVEL2 + 1;
+        assertEquals( expected, status.size() );
+        StorageAsset first = root.list( ).get( 3 ).list( ).get( 0 ).list().get(0);
+        assertEquals( first, status.getFirst( ) );
+        assertEquals( testRoot, status.getLast( ) );
+    }
+
+
+    @Test
+    void testWalkFromRootWithCondition() {
+        StorageAsset root = createTree( );
+        StopVisitStatus status = new StopVisitStatus( );
+        status.setStopCondition( a -> a.getName().equals("001002003") );
+
+        StorageUtil.walk( root, status );
+        assertEquals( "001002003", status.getLast( ).getName() );
+        int expected = LEVEL2 * LEVEL3 + LEVEL2 + 2 * LEVEL3 + 1 + 1 + 1 + 4;
+        assertEquals( expected, status.size() );
+    }
+
+    @Test
+    void testStream() {
+        StorageAsset root = createTree( );
+        ConsumeVisitStatus status = new ConsumeVisitStatus( );
+
+        List<StorageAsset> result;
+        try ( Stream<StorageAsset> stream = StorageUtil.newAssetStream( root, false ) )
+        {
+            result = stream.filter( a -> a.getName( ).startsWith( "001" ) ).collect( Collectors.toList());
+        }
+        int expected = LEVEL2 * LEVEL3 + LEVEL2 + 1;
+        assertEquals( expected, result.size( ) );
+        assertEquals( "001", result.get( result.size( ) - 1 ).getName() );
+        assertEquals( "001012", result.get( result.size( ) - 2 ).getName() );
+    }
+
+    @Test
+    void testStreamParallel() {
+        StorageAsset root = createTree( );
+        ConsumeVisitStatus status = new ConsumeVisitStatus( );
+
+        List<StorageAsset> result;
+        try ( Stream<StorageAsset> stream = StorageUtil.newAssetStream( root, true ) )
+        {
+            result = stream.filter( a -> a.getName( ).startsWith( "001" ) ).collect( Collectors.toList());
+        }
+        int expected = LEVEL2 * LEVEL3 + LEVEL2 + 1;
+        assertEquals( expected, result.size( ) );
+    }
+}
\ No newline at end of file
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/VisitStatus.java b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/VisitStatus.java
new file mode 100644
index 0000000..4df53ed
--- /dev/null
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/VisitStatus.java
@@ -0,0 +1,67 @@
+package org.apache.archiva.repository.storage.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 org.apache.archiva.repository.storage.StorageAsset;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+class VisitStatus
+{
+    LinkedList<StorageAsset> visited = new LinkedList<>( );
+
+    VisitStatus( )
+    {
+
+    }
+
+    public void add( StorageAsset asset )
+    {
+        // System.out.println( "Adding " + asset.getPath( ) );
+        visited.addLast( asset );
+    }
+
+    public StorageAsset getLast( )
+    {
+        return visited.getLast( );
+    }
+
+    public StorageAsset getFirst()  {
+        return visited.getFirst( );
+    }
+
+    public List<StorageAsset> getVisited( )
+    {
+        return visited;
+    }
+
+    public int size( )
+    {
+        return visited.size( );
+    }
+
+
+}


[archiva] 04/05: Moving filesystem storage classes in sub package

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit adeb46cce9199fd1d89a2dca68f677ea101df7ab
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Sat Feb 29 19:04:01 2020 +0100

    Moving filesystem storage classes in sub package
---
 .../consumers/core/repository/AbstractRepositoryPurge.java    |  1 -
 .../org/apache/archiva/policies/CachedFailuresPolicyTest.java |  2 +-
 .../java/org/apache/archiva/policies/ChecksumPolicyTest.java  |  3 +--
 .../archiva/policies/PropagateErrorsDownloadPolicyTest.java   |  2 +-
 .../policies/PropagateErrorsOnUpdateDownloadPolicyTest.java   |  2 +-
 .../java/org/apache/archiva/policies/SnapshotsPolicyTest.java |  3 +--
 .../apache/archiva/proxy/DefaultRepositoryProxyHandler.java   |  4 ++--
 .../apache/archiva/admin/mock/ArchivaIndexManagerMock.java    |  4 ++--
 .../org/apache/archiva/admin/mock/MavenIndexContextMock.java  |  5 ++---
 .../archiva/repository/base/BasicManagedRepository.java       |  2 +-
 .../apache/archiva/repository/base/BasicRemoteRepository.java |  2 +-
 .../archiva/repository/content/base/ContentItemTest.java      |  2 +-
 .../archiva/repository/scanner/RepositoryScannerTest.java     |  4 +---
 .../repository/scanner/mock/ManagedRepositoryContentMock.java |  2 +-
 .../archiva/repository/storage/{ => fs}/FilesystemAsset.java  |  7 ++++---
 .../repository/storage/{ => fs}/FilesystemStorage.java        |  9 +++++----
 .../archiva/repository/storage/{ => fs}/FsStorageUtil.java    |  7 ++++---
 .../repository/storage/{ => fs}/FilesystemAssetTest.java      |  7 ++++---
 .../repository/storage/{ => fs}/FilesystemStorageTest.java    | 11 ++++-------
 .../org/apache/archiva/indexer/maven/MavenIndexContext.java   |  4 +---
 .../org/apache/archiva/indexer/maven/MavenIndexManager.java   |  2 +-
 .../java/org/apache/archiva/proxy/MetadataTransferTest.java   |  5 +----
 .../archiva/repository/mock/ManagedRepositoryContentMock.java |  2 +-
 .../archiva/repository/maven2/MavenManagedRepository.java     |  2 +-
 .../archiva/repository/maven2/MavenRemoteRepository.java      |  2 +-
 .../archiva/repository/maven2/MavenRepositoryGroup.java       |  2 +-
 .../archiva/repository/maven2/MavenRepositoryProvider.java    |  2 +-
 .../archiva/stagerepository/merge/Maven2RepositoryMerger.java |  4 ++--
 .../repository/index/mock/ArchivaIndexManagerMock.java        |  4 ++--
 .../archiva/repository/index/mock/MavenIndexContextMock.java  |  2 +-
 .../java/org/apache/archiva/mock/ArchivaIndexManagerMock.java |  4 ++--
 .../java/org/apache/archiva/mock/MavenIndexContextMock.java   |  5 ++---
 .../apache/archiva/rest/services/DefaultBrowseService.java    |  2 +-
 .../archiva/rest/services/DefaultRepositoriesService.java     |  2 +-
 .../archiva/rest/services/ArtifactContentEntriesTests.java    |  4 ++--
 .../archiva/rest/services/utils/ArtifactBuilderTest.java      |  4 ++--
 .../processor/NewVersionsOfArtifactRssFeedProcessorTest.java  |  2 +-
 .../java/org/apache/archiva/web/rss/RssFeedServletTest.java   |  2 +-
 .../org/apache/archiva/webdav/ArchivaDavResourceFactory.java  |  2 +-
 .../test/java/org/apache/archiva/webdav/DavResourceTest.java  |  2 +-
 40 files changed, 64 insertions(+), 75 deletions(-)

diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/AbstractRepositoryPurge.java b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/AbstractRepositoryPurge.java
index a3979e6..2e04e66 100644
--- a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/AbstractRepositoryPurge.java
+++ b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/AbstractRepositoryPurge.java
@@ -28,7 +28,6 @@ import org.apache.archiva.model.ArtifactReference;
 import org.apache.archiva.repository.ContentNotFoundException;
 import org.apache.archiva.repository.ManagedRepositoryContent;
 import org.apache.archiva.metadata.audit.RepositoryListener;
-import org.apache.archiva.repository.storage.FsStorageUtil;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.repository.storage.util.StorageUtil;
 import org.apache.commons.lang3.StringUtils;
diff --git a/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/CachedFailuresPolicyTest.java b/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/CachedFailuresPolicyTest.java
index 27cda97..d46cfd4 100644
--- a/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/CachedFailuresPolicyTest.java
+++ b/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/CachedFailuresPolicyTest.java
@@ -22,7 +22,7 @@ package org.apache.archiva.policies;
 import junit.framework.TestCase;
 import org.apache.archiva.common.filelock.DefaultFileLockManager;
 import org.apache.archiva.policies.urlcache.UrlFailureCache;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
 import org.junit.Test;
diff --git a/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/ChecksumPolicyTest.java b/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/ChecksumPolicyTest.java
index d4c131c..b24695f 100644
--- a/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/ChecksumPolicyTest.java
+++ b/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/ChecksumPolicyTest.java
@@ -19,9 +19,8 @@ package org.apache.archiva.policies;
  * under the License.
  */
 
-import org.apache.archiva.checksum.Checksum;
 import org.apache.archiva.common.filelock.DefaultFileLockManager;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
 import org.apache.commons.io.FileUtils;
diff --git a/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/PropagateErrorsDownloadPolicyTest.java b/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/PropagateErrorsDownloadPolicyTest.java
index 4aff8c7..1509a5d 100644
--- a/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/PropagateErrorsDownloadPolicyTest.java
+++ b/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/PropagateErrorsDownloadPolicyTest.java
@@ -22,7 +22,7 @@ package org.apache.archiva.policies;
 import junit.framework.TestCase;
 import org.apache.archiva.common.filelock.DefaultFileLockManager;
 import org.apache.archiva.policies.urlcache.UrlFailureCache;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
 import org.junit.Test;
diff --git a/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/PropagateErrorsOnUpdateDownloadPolicyTest.java b/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/PropagateErrorsOnUpdateDownloadPolicyTest.java
index 9c09bd9..a059aff 100644
--- a/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/PropagateErrorsOnUpdateDownloadPolicyTest.java
+++ b/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/PropagateErrorsOnUpdateDownloadPolicyTest.java
@@ -22,7 +22,7 @@ package org.apache.archiva.policies;
 import junit.framework.TestCase;
 import org.apache.archiva.common.filelock.DefaultFileLockManager;
 import org.apache.archiva.policies.urlcache.UrlFailureCache;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
 import org.junit.Test;
diff --git a/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/SnapshotsPolicyTest.java b/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/SnapshotsPolicyTest.java
index 635d216..813c97d 100644
--- a/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/SnapshotsPolicyTest.java
+++ b/archiva-modules/archiva-base/archiva-policies/src/test/java/org/apache/archiva/policies/SnapshotsPolicyTest.java
@@ -20,7 +20,7 @@ package org.apache.archiva.policies;
  */
 
 import junit.framework.TestCase;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
 import org.junit.Before;
@@ -32,7 +32,6 @@ import javax.inject.Inject;
 import javax.inject.Named;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
-import java.nio.file.Path;
 import java.nio.file.attribute.FileTime;
 import java.util.Locale;
 import java.util.MissingResourceException;
diff --git a/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/archiva/proxy/DefaultRepositoryProxyHandler.java b/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/archiva/proxy/DefaultRepositoryProxyHandler.java
index ddeb499..3406c02 100644
--- a/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/archiva/proxy/DefaultRepositoryProxyHandler.java
+++ b/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/archiva/proxy/DefaultRepositoryProxyHandler.java
@@ -49,8 +49,8 @@ import org.apache.archiva.repository.RemoteRepositoryContent;
 import org.apache.archiva.repository.RepositoryType;
 import org.apache.archiva.repository.metadata.base.MetadataTools;
 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
-import org.apache.archiva.repository.storage.FilesystemStorage;
-import org.apache.archiva.repository.storage.FsStorageUtil;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FsStorageUtil;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
 import org.apache.archiva.scheduler.repository.model.RepositoryTask;
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/ArchivaIndexManagerMock.java b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/ArchivaIndexManagerMock.java
index 127c314..31b115a 100644
--- a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/ArchivaIndexManagerMock.java
+++ b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/ArchivaIndexManagerMock.java
@@ -39,8 +39,8 @@ import org.apache.archiva.repository.RemoteRepository;
 import org.apache.archiva.repository.Repository;
 import org.apache.archiva.repository.RepositoryType;
 import org.apache.archiva.repository.UnsupportedRepositoryTypeException;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.repository.features.IndexCreationFeature;
 import org.apache.archiva.repository.features.RemoteIndexFeature;
diff --git a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/MavenIndexContextMock.java b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/MavenIndexContextMock.java
index 843df59..ebdd0c7 100644
--- a/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/MavenIndexContextMock.java
+++ b/archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/MavenIndexContextMock.java
@@ -22,13 +22,12 @@ package org.apache.archiva.admin.mock;
 import org.apache.archiva.common.filelock.DefaultFileLockManager;
 import org.apache.archiva.indexer.ArchivaIndexingContext;
 import org.apache.archiva.repository.Repository;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.maven.index.context.IndexingContext;
 
 import java.io.IOException;
-import java.net.URI;
 import java.nio.file.Files;
 import java.nio.file.NoSuchFileException;
 import java.sql.Date;
diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/BasicManagedRepository.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/BasicManagedRepository.java
index 56f8bf9..f3e1361 100644
--- a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/BasicManagedRepository.java
+++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/BasicManagedRepository.java
@@ -26,7 +26,7 @@ import org.apache.archiva.repository.RepositoryCapabilities;
 import org.apache.archiva.repository.RepositoryRequestInfo;
 import org.apache.archiva.repository.RepositoryType;
 import org.apache.archiva.repository.StandardCapabilities;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.RepositoryStorage;
 import org.apache.archiva.repository.features.ArtifactCleanupFeature;
 import org.apache.archiva.repository.features.IndexCreationFeature;
diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/BasicRemoteRepository.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/BasicRemoteRepository.java
index ae62582..afec070 100644
--- a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/BasicRemoteRepository.java
+++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/base/BasicRemoteRepository.java
@@ -25,7 +25,7 @@ import org.apache.archiva.repository.ReleaseScheme;
 import org.apache.archiva.repository.RepositoryCapabilities;
 import org.apache.archiva.repository.RepositoryType;
 import org.apache.archiva.repository.StandardCapabilities;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.RepositoryStorage;
 import org.apache.archiva.repository.features.IndexCreationFeature;
 import org.apache.archiva.repository.features.RemoteIndexFeature;
diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/content/base/ContentItemTest.java b/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/content/base/ContentItemTest.java
index a1c3fd6..8f266d2 100644
--- a/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/content/base/ContentItemTest.java
+++ b/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/content/base/ContentItemTest.java
@@ -22,7 +22,7 @@ import org.apache.archiva.common.filelock.DefaultFileLockManager;
 import org.apache.archiva.repository.content.ContentItem;
 import org.apache.archiva.repository.content.base.builder.OptBuilder;
 import org.apache.archiva.repository.mock.ManagedRepositoryContentMock;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git a/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/RepositoryScannerTest.java b/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/RepositoryScannerTest.java
index bdf10b1..45476ee 100644
--- a/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/RepositoryScannerTest.java
+++ b/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/RepositoryScannerTest.java
@@ -29,11 +29,10 @@ import org.apache.archiva.repository.base.BasicRemoteRepository;
 import org.apache.archiva.repository.EditableManagedRepository;
 import org.apache.archiva.repository.EditableRemoteRepository;
 import org.apache.archiva.repository.ManagedRepository;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.scanner.mock.ManagedRepositoryContentMock;
 import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
 import org.apache.commons.io.FileUtils;
-import org.hamcrest.CoreMatchers;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.springframework.context.ApplicationContext;
@@ -43,7 +42,6 @@ import javax.inject.Inject;
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.net.URL;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
diff --git a/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/mock/ManagedRepositoryContentMock.java b/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/mock/ManagedRepositoryContentMock.java
index 8c7ebab..b19dd5b 100644
--- a/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/mock/ManagedRepositoryContentMock.java
+++ b/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/mock/ManagedRepositoryContentMock.java
@@ -35,7 +35,7 @@ import org.apache.archiva.repository.content.ItemSelector;
 import org.apache.archiva.repository.content.Namespace;
 import org.apache.archiva.repository.content.Project;
 import org.apache.archiva.repository.content.Version;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.commons.lang3.StringUtils;
 
diff --git a/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FilesystemAsset.java b/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/fs/FilesystemAsset.java
similarity index 98%
rename from archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FilesystemAsset.java
rename to archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/fs/FilesystemAsset.java
index 2f501d6..551729a 100644
--- a/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FilesystemAsset.java
+++ b/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/fs/FilesystemAsset.java
@@ -1,4 +1,4 @@
-package org.apache.archiva.repository.storage;
+package org.apache.archiva.repository.storage.fs;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -9,8 +9,7 @@ package org.apache.archiva.repository.storage;
  * "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
- *
+ * 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
@@ -19,6 +18,8 @@ package org.apache.archiva.repository.storage;
  * under the License.
  */
 
+import org.apache.archiva.repository.storage.RepositoryStorage;
+import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FilesystemStorage.java b/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/fs/FilesystemStorage.java
similarity index 97%
rename from archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FilesystemStorage.java
rename to archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/fs/FilesystemStorage.java
index ced3bac..0ae56e7 100644
--- a/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FilesystemStorage.java
+++ b/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/fs/FilesystemStorage.java
@@ -1,4 +1,4 @@
-package org.apache.archiva.repository.storage;
+package org.apache.archiva.repository.storage.fs;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -9,8 +9,7 @@ package org.apache.archiva.repository.storage;
  * "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
- *
+ * 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
@@ -24,6 +23,8 @@ import org.apache.archiva.common.filelock.FileLockManager;
 import org.apache.archiva.common.filelock.FileLockTimeoutException;
 import org.apache.archiva.common.filelock.Lock;
 import org.apache.archiva.common.utils.PathUtil;
+import org.apache.archiva.repository.storage.RepositoryStorage;
+import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.commons.io.FileUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -78,7 +79,7 @@ public class FilesystemStorage implements RepositoryStorage {
     }
 
     @Override
-    public void consumeData(StorageAsset asset, Consumer<InputStream> consumerFunction, boolean readLock ) throws IOException
+    public void consumeData( StorageAsset asset, Consumer<InputStream> consumerFunction, boolean readLock ) throws IOException
     {
         final Path path = asset.getFilePath();
         try {
diff --git a/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FsStorageUtil.java b/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/fs/FsStorageUtil.java
similarity index 97%
rename from archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FsStorageUtil.java
rename to archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/fs/FsStorageUtil.java
index 50eacac..842d22a 100644
--- a/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FsStorageUtil.java
+++ b/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/fs/FsStorageUtil.java
@@ -1,4 +1,4 @@
-package org.apache.archiva.repository.storage;
+package org.apache.archiva.repository.storage.fs;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -9,8 +9,7 @@ package org.apache.archiva.repository.storage;
  * "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
- *
+ * 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
@@ -22,6 +21,8 @@ package org.apache.archiva.repository.storage;
 import org.apache.archiva.common.filelock.FileLockException;
 import org.apache.archiva.common.filelock.FileLockManager;
 import org.apache.archiva.common.filelock.Lock;
+import org.apache.archiva.repository.storage.RepositoryStorage;
+import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.repository.storage.util.StorageUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/archiva-modules/archiva-base/archiva-storage-fs/src/test/java/org/apache/archiva/repository/storage/FilesystemAssetTest.java b/archiva-modules/archiva-base/archiva-storage-fs/src/test/java/org/apache/archiva/repository/storage/fs/FilesystemAssetTest.java
similarity index 97%
rename from archiva-modules/archiva-base/archiva-storage-fs/src/test/java/org/apache/archiva/repository/storage/FilesystemAssetTest.java
rename to archiva-modules/archiva-base/archiva-storage-fs/src/test/java/org/apache/archiva/repository/storage/fs/FilesystemAssetTest.java
index 566c0cb..4eae7f3 100644
--- a/archiva-modules/archiva-base/archiva-storage-fs/src/test/java/org/apache/archiva/repository/storage/FilesystemAssetTest.java
+++ b/archiva-modules/archiva-base/archiva-storage-fs/src/test/java/org/apache/archiva/repository/storage/fs/FilesystemAssetTest.java
@@ -1,4 +1,4 @@
-package org.apache.archiva.repository.storage;
+package org.apache.archiva.repository.storage.fs;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -9,8 +9,7 @@ package org.apache.archiva.repository.storage;
  * "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
- *
+ * 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
@@ -20,6 +19,8 @@ package org.apache.archiva.repository.storage;
  */
 
 import org.apache.archiva.common.filelock.DefaultFileLockManager;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.junit.After;
diff --git a/archiva-modules/archiva-base/archiva-storage-fs/src/test/java/org/apache/archiva/repository/storage/FilesystemStorageTest.java b/archiva-modules/archiva-base/archiva-storage-fs/src/test/java/org/apache/archiva/repository/storage/fs/FilesystemStorageTest.java
similarity index 96%
rename from archiva-modules/archiva-base/archiva-storage-fs/src/test/java/org/apache/archiva/repository/storage/FilesystemStorageTest.java
rename to archiva-modules/archiva-base/archiva-storage-fs/src/test/java/org/apache/archiva/repository/storage/fs/FilesystemStorageTest.java
index ebbc6a5..ddee6ce 100644
--- a/archiva-modules/archiva-base/archiva-storage-fs/src/test/java/org/apache/archiva/repository/storage/FilesystemStorageTest.java
+++ b/archiva-modules/archiva-base/archiva-storage-fs/src/test/java/org/apache/archiva/repository/storage/fs/FilesystemStorageTest.java
@@ -1,4 +1,4 @@
-package org.apache.archiva.repository.storage;
+package org.apache.archiva.repository.storage.fs;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -9,8 +9,7 @@ package org.apache.archiva.repository.storage;
  * "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
- *
+ * 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
@@ -20,9 +19,9 @@ package org.apache.archiva.repository.storage;
  */
 
 import org.apache.archiva.common.filelock.DefaultFileLockManager;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.junit.After;
@@ -37,8 +36,6 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
 
-import static org.junit.Assert.*;
-
 public class FilesystemStorageTest {
 
     private FilesystemStorage fsStorage;
diff --git a/archiva-modules/archiva-maven/archiva-maven-indexer/src/main/java/org/apache/archiva/indexer/maven/MavenIndexContext.java b/archiva-modules/archiva-maven/archiva-maven-indexer/src/main/java/org/apache/archiva/indexer/maven/MavenIndexContext.java
index c4c5e3b..3ece4b0 100644
--- a/archiva-modules/archiva-maven/archiva-maven-indexer/src/main/java/org/apache/archiva/indexer/maven/MavenIndexContext.java
+++ b/archiva-modules/archiva-maven/archiva-maven-indexer/src/main/java/org/apache/archiva/indexer/maven/MavenIndexContext.java
@@ -20,17 +20,15 @@ package org.apache.archiva.indexer.maven;
  */
 
 import org.apache.archiva.common.filelock.DefaultFileLockManager;
-import org.apache.archiva.common.filelock.FileLockManager;
 import org.apache.archiva.indexer.ArchivaIndexingContext;
 import org.apache.archiva.repository.Repository;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.maven.index.context.IndexingContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
-import java.net.URI;
 import java.nio.file.Files;
 import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
diff --git a/archiva-modules/archiva-maven/archiva-maven-indexer/src/main/java/org/apache/archiva/indexer/maven/MavenIndexManager.java b/archiva-modules/archiva-maven/archiva-maven-indexer/src/main/java/org/apache/archiva/indexer/maven/MavenIndexManager.java
index 777e6eb..5f190fc 100644
--- a/archiva-modules/archiva-maven/archiva-maven-indexer/src/main/java/org/apache/archiva/indexer/maven/MavenIndexManager.java
+++ b/archiva-modules/archiva-maven/archiva-maven-indexer/src/main/java/org/apache/archiva/indexer/maven/MavenIndexManager.java
@@ -38,7 +38,7 @@ import org.apache.archiva.repository.RemoteRepository;
 import org.apache.archiva.repository.Repository;
 import org.apache.archiva.repository.RepositoryType;
 import org.apache.archiva.repository.UnsupportedRepositoryTypeException;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.RepositoryStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.repository.features.IndexCreationFeature;
diff --git a/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/proxy/MetadataTransferTest.java b/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/proxy/MetadataTransferTest.java
index 47c5bda..ff7eb83 100644
--- a/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/proxy/MetadataTransferTest.java
+++ b/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/proxy/MetadataTransferTest.java
@@ -22,7 +22,6 @@ package org.apache.archiva.proxy;
 import org.apache.archiva.common.filelock.DefaultFileLockManager;
 import org.apache.archiva.common.utils.VersionUtil;
 import org.apache.archiva.configuration.ProxyConnectorConfiguration;
-import org.apache.archiva.maven2.metadata.MavenMetadataReader;
 import org.apache.archiva.model.ArchivaRepositoryMetadata;
 import org.apache.archiva.model.Plugin;
 import org.apache.archiva.model.ProjectReference;
@@ -35,14 +34,12 @@ import org.apache.archiva.policies.SnapshotsPolicy;
 import org.apache.archiva.repository.metadata.base.MetadataTools;
 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
 import org.apache.archiva.repository.metadata.base.RepositoryMetadataWriter;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.maven.wagon.TransferFailedException;
 import org.easymock.EasyMock;
 import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.xmlunit.builder.DiffBuilder;
 import org.xmlunit.diff.Diff;
 import org.xmlunit.diff.Difference;
diff --git a/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java b/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java
index 9b8b82a..b532d83 100644
--- a/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java
+++ b/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java
@@ -35,7 +35,7 @@ import org.apache.archiva.repository.content.ItemSelector;
 import org.apache.archiva.repository.content.Namespace;
 import org.apache.archiva.repository.content.Project;
 import org.apache.archiva.repository.content.Version;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.RepositoryStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.commons.lang3.StringUtils;
diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenManagedRepository.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenManagedRepository.java
index 977bc30..51467e6 100644
--- a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenManagedRepository.java
+++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenManagedRepository.java
@@ -24,7 +24,7 @@ import org.apache.archiva.common.filelock.FileLockManager;
 import org.apache.archiva.indexer.ArchivaIndexingContext;
 import org.apache.archiva.repository.*;
 import org.apache.archiva.repository.base.AbstractManagedRepository;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.content.maven2.MavenRepositoryRequestInfo;
 import org.apache.archiva.repository.features.ArtifactCleanupFeature;
 import org.apache.archiva.repository.features.IndexCreationFeature;
diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenRemoteRepository.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenRemoteRepository.java
index 72621c8..74d82ae 100644
--- a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenRemoteRepository.java
+++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenRemoteRepository.java
@@ -9,7 +9,7 @@ import org.apache.archiva.repository.RepositoryCapabilities;
 import org.apache.archiva.repository.RepositoryType;
 import org.apache.archiva.repository.StandardCapabilities;
 import org.apache.archiva.repository.UnsupportedFeatureException;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.features.IndexCreationFeature;
 import org.apache.archiva.repository.features.RemoteIndexFeature;
 import org.apache.archiva.repository.features.RepositoryFeature;
diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenRepositoryGroup.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenRepositoryGroup.java
index 9bd570d..ba88a83 100644
--- a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenRepositoryGroup.java
+++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenRepositoryGroup.java
@@ -23,7 +23,7 @@ import org.apache.archiva.common.filelock.DefaultFileLockManager;
 import org.apache.archiva.common.filelock.FileLockManager;
 import org.apache.archiva.repository.*;
 import org.apache.archiva.repository.base.AbstractRepositoryGroup;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.features.IndexCreationFeature;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenRepositoryProvider.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenRepositoryProvider.java
index aeb5477..7ca8e06 100644
--- a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenRepositoryProvider.java
+++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven2/MavenRepositoryProvider.java
@@ -29,7 +29,7 @@ import org.apache.archiva.repository.features.RemoteIndexFeature;
 import org.apache.archiva.repository.features.StagingRepositoryFeature;
 import org.apache.archiva.repository.base.BasicManagedRepository;
 import org.apache.archiva.repository.base.PasswordCredentials;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/stagerepository/merge/Maven2RepositoryMerger.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/stagerepository/merge/Maven2RepositoryMerger.java
index a37ced0..77541ee 100644
--- a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/stagerepository/merge/Maven2RepositoryMerger.java
+++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/stagerepository/merge/Maven2RepositoryMerger.java
@@ -38,8 +38,8 @@ import org.apache.archiva.repository.RepositoryException;
 import org.apache.archiva.repository.RepositoryType;
 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
 import org.apache.archiva.repository.metadata.base.RepositoryMetadataWriter;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.commons.io.FileUtils;
 import org.slf4j.Logger;
diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/index/mock/ArchivaIndexManagerMock.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/index/mock/ArchivaIndexManagerMock.java
index cf5a507..744a0b2 100644
--- a/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/index/mock/ArchivaIndexManagerMock.java
+++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/index/mock/ArchivaIndexManagerMock.java
@@ -40,8 +40,8 @@ import org.apache.archiva.repository.RemoteRepository;
 import org.apache.archiva.repository.Repository;
 import org.apache.archiva.repository.RepositoryType;
 import org.apache.archiva.repository.UnsupportedRepositoryTypeException;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.repository.features.IndexCreationFeature;
 import org.apache.archiva.repository.features.RemoteIndexFeature;
diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/index/mock/MavenIndexContextMock.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/index/mock/MavenIndexContextMock.java
index ded8ed3..e9e2f0f 100644
--- a/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/index/mock/MavenIndexContextMock.java
+++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/index/mock/MavenIndexContextMock.java
@@ -22,7 +22,7 @@ package org.apache.archiva.repository.index.mock;
 import org.apache.archiva.common.filelock.DefaultFileLockManager;
 import org.apache.archiva.indexer.ArchivaIndexingContext;
 import org.apache.archiva.repository.Repository;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.maven.index.context.IndexingContext;
 
diff --git a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/java/org/apache/archiva/mock/ArchivaIndexManagerMock.java b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/java/org/apache/archiva/mock/ArchivaIndexManagerMock.java
index 1776c8f..6182be5 100644
--- a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/java/org/apache/archiva/mock/ArchivaIndexManagerMock.java
+++ b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/java/org/apache/archiva/mock/ArchivaIndexManagerMock.java
@@ -39,8 +39,8 @@ import org.apache.archiva.repository.RemoteRepository;
 import org.apache.archiva.repository.Repository;
 import org.apache.archiva.repository.RepositoryType;
 import org.apache.archiva.repository.UnsupportedRepositoryTypeException;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.repository.features.IndexCreationFeature;
 import org.apache.archiva.repository.features.RemoteIndexFeature;
diff --git a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/java/org/apache/archiva/mock/MavenIndexContextMock.java b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/java/org/apache/archiva/mock/MavenIndexContextMock.java
index c8e2cd4..3d64f09 100644
--- a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/java/org/apache/archiva/mock/MavenIndexContextMock.java
+++ b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/java/org/apache/archiva/mock/MavenIndexContextMock.java
@@ -22,13 +22,12 @@ package org.apache.archiva.mock;
 import org.apache.archiva.common.filelock.DefaultFileLockManager;
 import org.apache.archiva.indexer.ArchivaIndexingContext;
 import org.apache.archiva.repository.Repository;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.maven.index.context.IndexingContext;
 
 import java.io.IOException;
-import java.net.URI;
 import java.nio.file.Files;
 import java.nio.file.NoSuchFileException;
 import java.sql.Date;
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultBrowseService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultBrowseService.java
index 053e12c..d7e2866 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultBrowseService.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultBrowseService.java
@@ -44,7 +44,7 @@ import org.apache.archiva.repository.RepositoryNotFoundException;
 import org.apache.archiva.repository.RepositoryRegistry;
 import org.apache.archiva.repository.metadata.MetadataReader;
 import org.apache.archiva.repository.metadata.base.MetadataTools;
-import org.apache.archiva.repository.storage.FsStorageUtil;
+import org.apache.archiva.repository.storage.fs.FsStorageUtil;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.rest.api.model.*;
 import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultRepositoriesService.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultRepositoriesService.java
index 0dcaee6..5c72d42 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultRepositoriesService.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultRepositoriesService.java
@@ -51,7 +51,7 @@ import org.apache.archiva.repository.RepositoryException;
 import org.apache.archiva.repository.RepositoryNotFoundException;
 import org.apache.archiva.repository.RepositoryRegistry;
 import org.apache.archiva.repository.RepositoryType;
-import org.apache.archiva.repository.storage.FsStorageUtil;
+import org.apache.archiva.repository.storage.fs.FsStorageUtil;
 import org.apache.archiva.repository.storage.RepositoryStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.metadata.audit.RepositoryListener;
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/ArtifactContentEntriesTests.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/ArtifactContentEntriesTests.java
index 4cd630e..03aba26 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/ArtifactContentEntriesTests.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/ArtifactContentEntriesTests.java
@@ -20,8 +20,8 @@ package org.apache.archiva.rest.services;
 
 import junit.framework.TestCase;
 import org.apache.archiva.common.filelock.DefaultFileLockManager;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.rest.api.model.ArtifactContentEntry;
 import org.apache.archiva.test.utils.ArchivaBlockJUnit4ClassRunner;
 import org.junit.Test;
diff --git a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/utils/ArtifactBuilderTest.java b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/utils/ArtifactBuilderTest.java
index f950992..5bc1d9b 100644
--- a/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/utils/ArtifactBuilderTest.java
+++ b/archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/utils/ArtifactBuilderTest.java
@@ -19,8 +19,8 @@ package org.apache.archiva.rest.services.utils;
  */
 
 import org.apache.archiva.common.filelock.DefaultFileLockManager;
-import org.apache.archiva.repository.storage.FilesystemAsset;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.easymock.TestSubject;
 import org.junit.Test;
diff --git a/archiva-modules/archiva-web/archiva-rss/src/test/java/org/apache/archiva/rss/processor/NewVersionsOfArtifactRssFeedProcessorTest.java b/archiva-modules/archiva-web/archiva-rss/src/test/java/org/apache/archiva/rss/processor/NewVersionsOfArtifactRssFeedProcessorTest.java
index 445b968..0a35b18 100644
--- a/archiva-modules/archiva-web/archiva-rss/src/test/java/org/apache/archiva/rss/processor/NewVersionsOfArtifactRssFeedProcessorTest.java
+++ b/archiva-modules/archiva-web/archiva-rss/src/test/java/org/apache/archiva/rss/processor/NewVersionsOfArtifactRssFeedProcessorTest.java
@@ -31,7 +31,7 @@ import org.apache.archiva.repository.base.ArchivaRepositoryRegistry;
 import org.apache.archiva.repository.base.BasicManagedRepository;
 import org.apache.archiva.repository.Repository;
 import org.apache.archiva.repository.RepositoryRegistry;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.rss.RssFeedGenerator;
 import org.apache.archiva.test.utils.ArchivaBlockJUnit4ClassRunner;
 import org.easymock.EasyMock;
diff --git a/archiva-modules/archiva-web/archiva-web-common/src/test/java/org/apache/archiva/web/rss/RssFeedServletTest.java b/archiva-modules/archiva-web/archiva-web-common/src/test/java/org/apache/archiva/web/rss/RssFeedServletTest.java
index 374d953..e0079bb 100644
--- a/archiva-modules/archiva-web/archiva-web-common/src/test/java/org/apache/archiva/web/rss/RssFeedServletTest.java
+++ b/archiva-modules/archiva-web/archiva-web-common/src/test/java/org/apache/archiva/web/rss/RssFeedServletTest.java
@@ -25,7 +25,7 @@ import org.apache.archiva.common.filelock.DefaultFileLockManager;
 import org.apache.archiva.configuration.ArchivaConfiguration;
 import org.apache.archiva.repository.base.BasicManagedRepository;
 import org.apache.archiva.repository.RepositoryRegistry;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
 import org.apache.commons.codec.Encoder;
 import org.apache.commons.codec.binary.Base64;
diff --git a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/ArchivaDavResourceFactory.java b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/ArchivaDavResourceFactory.java
index a2c90ec..2e6f833 100644
--- a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/ArchivaDavResourceFactory.java
+++ b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/ArchivaDavResourceFactory.java
@@ -65,7 +65,7 @@ import org.apache.archiva.repository.ReleaseScheme;
 import org.apache.archiva.repository.RepositoryGroup;
 import org.apache.archiva.repository.RepositoryRegistry;
 import org.apache.archiva.repository.RepositoryRequestInfo;
-import org.apache.archiva.repository.storage.FilesystemStorage;
+import org.apache.archiva.repository.storage.fs.FilesystemStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
 import org.apache.archiva.metadata.audit.AuditListener;
 import org.apache.archiva.repository.features.IndexCreationFeature;
diff --git a/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/archiva/webdav/DavResourceTest.java b/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/archiva/webdav/DavResourceTest.java
index e10e609..5329e09 100644
--- a/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/archiva/webdav/DavResourceTest.java
+++ b/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/archiva/webdav/DavResourceTest.java
@@ -24,7 +24,7 @@ import org.apache.archiva.common.filelock.FileLockManager;
 import org.apache.archiva.common.utils.FileUtils;
 import org.apache.archiva.repository.LayoutException;
 import org.apache.archiva.repository.RepositoryRegistry;
-import org.apache.archiva.repository.storage.FilesystemAsset;
+import org.apache.archiva.repository.storage.fs.FilesystemAsset;
 import org.apache.archiva.metadata.audit.AuditListener;
 import org.apache.archiva.repository.maven2.MavenManagedRepository;
 import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;


[archiva] 03/05: Changing visibility of utility method

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 7ddf0265ee0e6ba67b5fc34432250eb971bb9f5a
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Sat Feb 29 19:02:27 2020 +0100

    Changing visibility of utility method
---
 .../apache/archiva/repository/storage/util/StorageUtil.java  |  2 +-
 .../org/apache/archiva/repository/storage/FsStorageUtil.java | 12 +++++++++++-
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/StorageUtil.java b/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/StorageUtil.java
index f8c5745..fa637c6 100644
--- a/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/StorageUtil.java
+++ b/archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/StorageUtil.java
@@ -214,7 +214,7 @@ public class StorageUtil
 
     }
 
-    public static final void wrapWriteFunction( ReadableByteChannel is, RepositoryStorage targetStorage, StorageAsset target, boolean locked) {
+    private static final void wrapWriteFunction( ReadableByteChannel is, RepositoryStorage targetStorage, StorageAsset target, boolean locked) {
         try {
             targetStorage.writeDataToChannel( target, os -> copy(is, os), locked );
         } catch (Exception e) {
diff --git a/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FsStorageUtil.java b/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FsStorageUtil.java
index a009440..50eacac 100644
--- a/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FsStorageUtil.java
+++ b/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FsStorageUtil.java
@@ -22,11 +22,13 @@ package org.apache.archiva.repository.storage;
 import org.apache.archiva.common.filelock.FileLockException;
 import org.apache.archiva.common.filelock.FileLockManager;
 import org.apache.archiva.common.filelock.Lock;
+import org.apache.archiva.repository.storage.util.StorageUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
 import java.nio.file.*;
 import java.util.HashSet;
 
@@ -104,7 +106,7 @@ public class FsStorageUtil
             try {
                 final RepositoryStorage sourceStorage = source.getStorage();
                 final RepositoryStorage targetStorage = target.getStorage();
-                sourceStorage.consumeDataFromChannel( source, is -> org.apache.archiva.repository.storage.util.StorageUtil.wrapWriteFunction( is, targetStorage, target, locked ), locked);
+                sourceStorage.consumeDataFromChannel( source, is -> wrapWriteFunction( is, targetStorage, target, locked ), locked);
             }  catch (IOException e) {
                 throw e;
             }  catch (Throwable e) {
@@ -119,6 +121,14 @@ public class FsStorageUtil
         }
     }
 
+    private static final void wrapWriteFunction( ReadableByteChannel is, RepositoryStorage targetStorage, StorageAsset target, boolean locked) {
+        try {
+            targetStorage.writeDataToChannel( target, os -> StorageUtil.copy(is, os), locked );
+        } catch (Exception e) {
+            throw new RuntimeException( e );
+        }
+    }
+
 
     public static final void copyToLocalFile(StorageAsset asset, Path destination, CopyOption... copyOptions) throws IOException {
         if (asset.isFileBased()) {


[archiva] 05/05: Additional methods for repository content

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 939d127869e6eb3854adbd1cd9a0e54ec03e4f56
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Sun Mar 1 20:25:59 2020 +0100

    Additional methods for repository content
---
 .../repository/ManagedRepositoryContent.java       |  26 +-
 .../content/base/builder/ProjectOptBuilder.java    |   2 -
 .../mock/ManagedRepositoryContentMock.java         |  16 +-
 .../scanner/mock/ManagedRepositoryContentMock.java |  16 +-
 .../repository/storage/fs/FilesystemAsset.java     |  20 ++
 .../mock/ManagedRepositoryContentMock.java         |  16 +-
 .../maven2/ManagedDefaultRepositoryContent.java    | 313 ++++++++++++++++++++-
 7 files changed, 358 insertions(+), 51 deletions(-)

diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/ManagedRepositoryContent.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/ManagedRepositoryContent.java
index 3c0dc1d..7eb8da4 100644
--- a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/ManagedRepositoryContent.java
+++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/ManagedRepositoryContent.java
@@ -150,7 +150,7 @@ public interface ManagedRepositoryContent extends RepositoryContent
     Version getVersion(ItemSelector versionCoordinates) throws ContentAccessException, IllegalArgumentException;
 
     /**
-     * Returns the artifact for the given coordinates.
+     * Returns the artifact object for the given coordinates.
      *
      * Normally the following coordinates should be set at the given selector:
      * <ul>
@@ -170,8 +170,14 @@ public interface ManagedRepositoryContent extends RepositoryContent
      *     <li>extension</li>
      * </ul>
      *
+     * The method always returns a artifact object, if the coordinates are valid. It does not guarantee that the artifact
+     * exists. To check if there is really a physical representation of the artifact, use the <code>{@link Artifact#exists()}</code>
+     * method of the artifact.
+     * For upload and data retrieval use the methods of the {@link StorageAsset} reference returned in the artifact.
+     *
+     *
      * @param selector the selector with the artifact coordinates
-     * @return a artifact
+     * @return a artifact object
      * @throws ItemNotFoundException if the selector coordinates do not specify a artifact
      * @throws ContentAccessException if the access to the underlying storage failed
      */
@@ -187,7 +193,7 @@ public interface ManagedRepositoryContent extends RepositoryContent
      * @throws ItemNotFoundException if the specified coordinates cannot be found in the repository
      * @throws ContentAccessException if the access to the underlying storage failed
      */
-    List<Artifact> getAllArtifacts( ItemSelector selector) throws ContentAccessException;
+    List<? extends Artifact> getAllArtifacts( ItemSelector selector) throws ContentAccessException;
 
     /**
      * Returns the artifacts that match the given selector. It is up to the repository implementation
@@ -202,7 +208,7 @@ public interface ManagedRepositoryContent extends RepositoryContent
      * @throws ItemNotFoundException if the specified coordinates cannot be found in the repository
      * @throws ContentAccessException if the access to the underlying storage failed
      */
-    Stream<Artifact> getAllArtifactStream( ItemSelector selector) throws ContentAccessException;
+    Stream<? extends Artifact> getAllArtifactStream( ItemSelector selector) throws ContentAccessException;
 
 
     /**
@@ -211,7 +217,7 @@ public interface ManagedRepositoryContent extends RepositoryContent
      * @param namespace the namespace
      * @return the list of projects or a empty list, if there are no projects for the given namespace.
      */
-    List<Project> getProjects(Namespace namespace) throws ContentAccessException;
+    List<? extends Project> getProjects( Namespace namespace) throws ContentAccessException;
 
     /**
      * Return the existing versions of the given project.
@@ -219,7 +225,7 @@ public interface ManagedRepositoryContent extends RepositoryContent
      * @param project the project
      * @return a list of versions or a empty list, if not versions are available for the specified project
      */
-    List<Version> getVersions(Project project) throws ContentAccessException;
+    List<? extends Version> getVersions( Project project) throws ContentAccessException;
 
     /**
      * Return all the artifacts of a given content item (namespace, project, version)
@@ -227,7 +233,7 @@ public interface ManagedRepositoryContent extends RepositoryContent
      * @param item the item
      * @return a list of artifacts or a empty list, if no artifacts are available for the specified item
      */
-    List<Artifact> getArtifacts( ContentItem item) throws ContentAccessException;
+    List<? extends Artifact> getArtifacts( ContentItem item) throws ContentAccessException;
 
     /**
      * Return all the artifacts of a given namespace and all sub namespaces that are defined under the
@@ -236,7 +242,7 @@ public interface ManagedRepositoryContent extends RepositoryContent
      * @param namespace the namespace, which is the parent namespace
      * @return a list of artifacts or a empty list, if no artifacts are available for the specified namespace
      */
-    List<Artifact> getArtifactsStartingWith( Namespace namespace ) throws ContentAccessException;
+    List<? extends Artifact> getArtifactsStartingWith( Namespace namespace ) throws ContentAccessException;
 
 
     /**
@@ -250,7 +256,7 @@ public interface ManagedRepositoryContent extends RepositoryContent
      * @return a stream of artifacts. The stream is auto closable. You should always make sure, that the stream
      * is closed after use.
      */
-    Stream<Artifact> getArtifactStream( ContentItem item ) throws ContentAccessException;
+    Stream<? extends Artifact> getArtifactStream( ContentItem item ) throws ContentAccessException;
 
 
     /**
@@ -264,7 +270,7 @@ public interface ManagedRepositoryContent extends RepositoryContent
      * @return a stream of artifacts. The stream is auto closable. You should always make sure, that the stream
      * is closed after use.
      */
-    Stream<Artifact> getArtifactStreamStartingWith( Namespace namespace ) throws ContentAccessException;
+    Stream<? extends Artifact> getArtifactStreamStartingWith( Namespace namespace ) throws ContentAccessException;
 
 
     /**
diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/builder/ProjectOptBuilder.java b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/builder/ProjectOptBuilder.java
index 4964e13..f2ebebe 100644
--- a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/builder/ProjectOptBuilder.java
+++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/base/builder/ProjectOptBuilder.java
@@ -30,8 +30,6 @@ public interface ProjectOptBuilder
     extends OptBuilder<ArchivaProject, ProjectOptBuilder>
 {
 
-    ProjectOptBuilder withId( String id );
-
     ArchivaProject build( );
 
 }
diff --git a/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java b/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java
index 9e56da0..b5adf5c 100644
--- a/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java
+++ b/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java
@@ -121,49 +121,49 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent
     }
 
     @Override
-    public List<Artifact> getAllArtifacts( ItemSelector selector ) throws ContentAccessException
+    public List<? extends Artifact> getAllArtifacts( ItemSelector selector ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public Stream<Artifact> getAllArtifactStream( ItemSelector selector ) throws ContentAccessException
+    public Stream<? extends Artifact> getAllArtifactStream( ItemSelector selector ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public List<Project> getProjects( Namespace namespace ) throws ContentAccessException
+    public List<? extends Project> getProjects( Namespace namespace ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public List<Version> getVersions( Project project ) throws ContentAccessException
+    public List<? extends Version> getVersions( Project project ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public List<Artifact> getArtifacts( ContentItem item ) throws ContentAccessException
+    public List<? extends Artifact> getArtifacts( ContentItem item ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public List<Artifact> getArtifactsStartingWith( Namespace namespace ) throws ContentAccessException
+    public List<? extends Artifact> getArtifactsStartingWith( Namespace namespace ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public Stream<Artifact> getArtifactStream( ContentItem item ) throws ContentAccessException
+    public Stream<? extends Artifact> getArtifactStream( ContentItem item ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public Stream<Artifact> getArtifactStreamStartingWith( Namespace namespace ) throws ContentAccessException
+    public Stream<? extends Artifact> getArtifactStreamStartingWith( Namespace namespace ) throws ContentAccessException
     {
         return null;
     }
diff --git a/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/mock/ManagedRepositoryContentMock.java b/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/mock/ManagedRepositoryContentMock.java
index b19dd5b..5d2520f 100644
--- a/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/mock/ManagedRepositoryContentMock.java
+++ b/archiva-modules/archiva-base/archiva-repository-scanner/src/test/java/org/apache/archiva/repository/scanner/mock/ManagedRepositoryContentMock.java
@@ -122,49 +122,49 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent
     }
 
     @Override
-    public List<Artifact> getAllArtifacts( ItemSelector selector ) throws ContentAccessException
+    public List<? extends Artifact> getAllArtifacts( ItemSelector selector ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public Stream<Artifact> getAllArtifactStream( ItemSelector selector ) throws ContentAccessException
+    public Stream<? extends Artifact> getAllArtifactStream( ItemSelector selector ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public List<Project> getProjects( Namespace namespace ) throws ContentAccessException
+    public List<? extends Project> getProjects( Namespace namespace ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public List<Version> getVersions( Project project ) throws ContentAccessException
+    public List<? extends Version> getVersions( Project project ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public List<Artifact> getArtifacts( ContentItem item ) throws ContentAccessException
+    public List<? extends Artifact> getArtifacts( ContentItem item ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public List<Artifact> getArtifactsStartingWith( Namespace namespace ) throws ContentAccessException
+    public List<? extends Artifact> getArtifactsStartingWith( Namespace namespace ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public Stream<Artifact> getArtifactStream( ContentItem item ) throws ContentAccessException
+    public Stream<? extends Artifact> getArtifactStream( ContentItem item ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public Stream<Artifact> getArtifactStreamStartingWith( Namespace namespace ) throws ContentAccessException
+    public Stream<? extends Artifact> getArtifactStreamStartingWith( Namespace namespace ) throws ContentAccessException
     {
         return null;
     }
diff --git a/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/fs/FilesystemAsset.java b/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/fs/FilesystemAsset.java
index 551729a..bbcf38a 100644
--- a/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/fs/FilesystemAsset.java
+++ b/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/fs/FilesystemAsset.java
@@ -521,4 +521,24 @@ public class FilesystemAsset implements StorageAsset, Comparable {
         }
         return 0;
     }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o ) return true;
+        if ( o == null || getClass( ) != o.getClass( ) ) return false;
+
+        FilesystemAsset that = (FilesystemAsset) o;
+
+        if ( !assetPath.equals( that.assetPath ) ) return false;
+        return storage.equals( that.storage );
+    }
+
+    @Override
+    public int hashCode( )
+    {
+        int result = assetPath.hashCode( );
+        result = 31 * result + storage.hashCode( );
+        return result;
+    }
 }
diff --git a/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java b/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java
index b532d83..5314c18 100644
--- a/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java
+++ b/archiva-modules/archiva-maven/archiva-maven-proxy/src/test/java/org/apache/archiva/repository/mock/ManagedRepositoryContentMock.java
@@ -126,49 +126,49 @@ public class ManagedRepositoryContentMock implements ManagedRepositoryContent
     }
 
     @Override
-    public List<Artifact> getAllArtifacts( ItemSelector selector ) throws ContentAccessException
+    public List<? extends Artifact> getAllArtifacts( ItemSelector selector ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public Stream<Artifact> getAllArtifactStream( ItemSelector selector ) throws ContentAccessException
+    public Stream<? extends Artifact> getAllArtifactStream( ItemSelector selector ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public List<Project> getProjects( Namespace namespace ) throws ContentAccessException
+    public List<? extends Project> getProjects( Namespace namespace ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public List<Version> getVersions( Project project ) throws ContentAccessException
+    public List<? extends Version> getVersions( Project project ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public List<Artifact> getArtifacts( ContentItem item ) throws ContentAccessException
+    public List<? extends Artifact> getArtifacts( ContentItem item ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public List<Artifact> getArtifactsStartingWith( Namespace namespace ) throws ContentAccessException
+    public List<? extends Artifact> getArtifactsStartingWith( Namespace namespace ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public Stream<Artifact> getArtifactStream( ContentItem item ) throws ContentAccessException
+    public Stream<? extends Artifact> getArtifactStream( ContentItem item ) throws ContentAccessException
     {
         return null;
     }
 
     @Override
-    public Stream<Artifact> getArtifactStreamStartingWith( Namespace namespace ) throws ContentAccessException
+    public Stream<? extends Artifact> getArtifactStreamStartingWith( Namespace namespace ) throws ContentAccessException
     {
         return null;
     }
diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/content/maven2/ManagedDefaultRepositoryContent.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/content/maven2/ManagedDefaultRepositoryContent.java
index 780cf35..c1b2bd7 100644
--- a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/content/maven2/ManagedDefaultRepositoryContent.java
+++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/content/maven2/ManagedDefaultRepositoryContent.java
@@ -51,22 +51,29 @@ import org.apache.archiva.repository.content.base.ArchivaProject;
 import org.apache.archiva.repository.content.base.ArchivaVersion;
 import org.apache.archiva.repository.content.base.builder.ArtifactOptBuilder;
 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
+import org.apache.archiva.repository.storage.RepositoryStorage;
 import org.apache.archiva.repository.storage.StorageAsset;
+import org.apache.archiva.repository.storage.util.StorageUtil;
 import org.apache.commons.collections4.map.ReferenceMap;
 import org.apache.commons.lang3.StringUtils;
 
 import javax.inject.Inject;
 import javax.inject.Named;
 import java.io.IOException;
+import java.net.Socket;
 import java.net.URI;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Collections;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.function.Predicate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -87,7 +94,7 @@ public class ManagedDefaultRepositoryContent
 
     private ManagedRepository repository;
 
-    FileLockManager lockManager;
+    private FileLockManager lockManager;
 
     @Inject
     @Named("repositoryPathTranslator#maven2")
@@ -97,6 +104,15 @@ public class ManagedDefaultRepositoryContent
     @Named( "metadataReader#maven" )
     MavenMetadataReader metadataReader;
 
+    public static final String SNAPSHOT = "SNAPSHOT";
+
+    public static final Pattern UNIQUE_SNAPSHOT_PATTERN = Pattern.compile( "^(SNAPSHOT|[0-9]{8}\\.[0-9]{6}-[0-9]+)(.*)" );
+    public static final Pattern CLASSIFIER_PATTERN = Pattern.compile( "^-([^.]+)(\\..*)" );
+
+    public static final Pattern TIMESTAMP_PATTERN = Pattern.compile( "^([0-9]{8})\\.([0-9]{6})$" );
+
+    public static final Pattern GENERIC_SNAPSHOT_PATTERN = Pattern.compile( "^(.*)-" + SNAPSHOT );
+
     /**
      * We are caching content items in a weak reference map. To avoid always recreating the
      * the hierarchical structure.
@@ -255,7 +271,6 @@ public class ManagedDefaultRepositoryContent
 
 
     /*
-    TBD
      */
     private String getArtifactFileName(ItemSelector selector, String artifactVersion,
                                        String classifier, String extension) {
@@ -300,6 +315,26 @@ public class ManagedDefaultRepositoryContent
         }
     }
 
+    private String getTypeFromClassifierAndExtension(String classifierArg, String extensionArg) {
+        String extension = extensionArg.toLowerCase( ).trim( );
+        String classifier = classifierArg.toLowerCase( ).trim( );
+        if (StringUtils.isEmpty( extension )) {
+            return "";
+        } else if (StringUtils.isEmpty( classifier ) ) {
+            return extension;
+        } else if (classifier.equals("tests") && extension.equals("jar")) {
+            return "test-jar";
+        } else if (classifier.equals("client") && extension.equals( "jar" )) {
+            return "ejb-client";
+        } else if (classifier.equals("source") && extension.equals("jar")) {
+            return "java-source";
+        } else if (classifier.equals("javadoc") && extension.equals( "jar" )) {
+            return "javadoc";
+        } else {
+            return extension;
+        }
+    }
+
     private String getArtifactExtension(ItemSelector selector) {
         if (selector.hasExtension()) {
             return selector.getExtension( );
@@ -339,7 +374,7 @@ public class ManagedDefaultRepositoryContent
     }
 
 
-    public String getArtifactSnapshotVersion(StorageAsset artifactDir, String snapshotVersion) {
+    private String getArtifactSnapshotVersion(StorageAsset artifactDir, String snapshotVersion) {
         final StorageAsset metadataFile = artifactDir.resolve( METADATA_FILENAME );
         StringBuilder version = new StringBuilder( );
         try
@@ -369,16 +404,216 @@ public class ManagedDefaultRepositoryContent
         ArtifactOptBuilder builder = org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
             .withVersion( version )
             .withId( selector.getArtifactId( ) )
-            .withArtifactVersion( getArtifactVersion( artifactPath, selector ) );
-        if (selector.hasClassifier()) {
-            builder.withClassifier( selector.getClassifier( ) );
-        }
+            .withArtifactVersion( getArtifactVersion( artifactPath, selector ) )
+            .withClassifier( classifier );
         if (selector.hasType()) {
             builder.withType( selector.getType( ) );
         }
         return builder.build( );
     }
 
+    private String getNamespaceFromNamespacePath(final StorageAsset namespacePath) {
+        LinkedList<String> names = new LinkedList<>( );
+        StorageAsset current = namespacePath;
+        while (current.hasParent()) {
+            names.addFirst( current.getName() );
+        }
+        return String.join( ".", names );
+    }
+
+    private Namespace getNamespaceFromArtifactPath( final StorageAsset artifactPath) {
+        final StorageAsset namespacePath = artifactPath.getParent( ).getParent( ).getParent( );
+        final String namespace = getNamespaceFromNamespacePath( namespacePath );
+        return namespaceMap.computeIfAbsent( namespace,
+            myNamespace -> ArchivaNamespace.withRepository( this )
+                .withAsset( namespacePath )
+                .withNamespace( namespace )
+                .build( ) );
+    }
+
+    private Project getProjectFromArtifactPath( final StorageAsset artifactPath) {
+        final StorageAsset projectPath = artifactPath.getParent( ).getParent( );
+        return projectMap.computeIfAbsent( projectPath,
+            myProjectPath -> ArchivaProject.withAsset( projectPath )
+                .withNamespace( getNamespaceFromArtifactPath( artifactPath ) )
+                .withId( projectPath.getName( ) ).build( )
+        );
+    }
+
+    private Version getVersionFromArtifactPath( final StorageAsset artifactPath) {
+        final StorageAsset versionPath = artifactPath.getParent( );
+        return versionMap.computeIfAbsent( versionPath,
+            myVersionPath -> ArchivaVersion.withAsset( versionPath )
+                .withProject( getProjectFromArtifactPath( artifactPath ) )
+                .withVersion( versionPath.getName( ) ).build( ) );
+    }
+
+    private Artifact getArtifactFromPath(final StorageAsset artifactPath) {
+        final Version version = getVersionFromArtifactPath( artifactPath );
+        final ArtifactInfo info  = getArtifactInfoFromPath( version.getVersion(), artifactPath );
+        return artifactMap.computeIfAbsent( artifactPath, myArtifactPath ->
+            org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
+                .withVersion( version )
+                .withId( info.id )
+                .withClassifier( info.classifier )
+                .withRemainder( info.remainder )
+                .withType( info.type )
+                .withArtifactVersion( info.version )
+                .withContentType( info.contentType )
+                .build( )
+        );
+    }
+
+    private ContentItem getItemFromPath(final StorageAsset itemPath) {
+        if (itemPath.isLeaf()) {
+            return getArtifactFromPath( itemPath );
+        } else {
+            if (versionMap.containsKey( itemPath )) {
+                return versionMap.get( itemPath );
+            }
+            if (projectMap.containsKey( itemPath )) {
+                return projectMap.get( itemPath );
+            }
+            String ns = getNamespaceFromNamespacePath( itemPath );
+            if (namespaceMap.containsKey( ns )) {
+                return namespaceMap.get( ns );
+            }
+            // No cached item, so we have to gather more information:
+            // Check for version directory (contains at least a pom or metadata file)
+            if (itemPath.list( ).stream( ).map(a -> a.getName().toLowerCase()).anyMatch( n ->
+                n.endsWith( ".pom" )
+                || n.startsWith( "maven-metadata" )
+            )) {
+                return versionMap.computeIfAbsent( itemPath,
+                    myVersionPath -> ArchivaVersion.withAsset( itemPath )
+                        .withProject( (Project)getItemFromPath( itemPath.getParent() ) )
+                        .withVersion( itemPath.getName() ).build());
+            } else {
+                // We have to dig further and find the next directory with a pom
+                Optional<StorageAsset> foundFile = StorageUtil.newAssetStream( itemPath )
+                    .filter( a -> a.getName().toLowerCase().endsWith( ".pom" )
+                        || a.getName().toLowerCase().startsWith( "maven-metadata" ) )
+                    .findFirst( );
+                if (foundFile.isPresent())
+                {
+                    int level = 0;
+                    StorageAsset current = foundFile.get( );
+                    while (current.hasParent() && !current.equals(itemPath)) {
+                        level++;
+                        current = current.getParent( );
+                    }
+                    // Project path if it is one level up from the found file
+                    if (level==2) {
+                        return projectMap.computeIfAbsent( itemPath,
+                            myItemPath -> getProjectFromArtifactPath( foundFile.get( ) ) );
+                    } else {
+                        // All other paths are treated as namespace
+                        return namespaceMap.computeIfAbsent( ns,
+                            myNamespace -> ArchivaNamespace.withRepository( this )
+                                .withAsset( itemPath )
+                                .withNamespace( ns )
+                                .build( ) );
+                    }
+                } else {
+                    // Don't know what to do with it, so we treat it as namespace path
+                    return namespaceMap.computeIfAbsent( ns,
+                        myNamespace -> ArchivaNamespace.withRepository( this )
+                            .withAsset( itemPath )
+                            .withNamespace( ns )
+                            .build( ) );
+                }
+
+            }
+        }
+    }
+
+    // Simple object to hold artifact information
+    private class ArtifactInfo  {
+        private String id;
+        private String version;
+        private String extension;
+        private String remainder;
+        private String type;
+        private String classifier;
+        private String contentType;
+    }
+
+    private ArtifactInfo getArtifactInfoFromPath(String genericVersion, StorageAsset path) {
+        final ArtifactInfo info = new ArtifactInfo( );
+        info.id = path.getParent( ).getParent( ).getName( );
+        final String fileName = path.getName( );
+        if ( genericVersion.endsWith( "-" + SNAPSHOT ) )
+        {
+            String baseVersion = StringUtils.substringBeforeLast( genericVersion, "-" + SNAPSHOT );
+            String prefix = info.id+"-"+baseVersion+"-";
+            if (fileName.startsWith( prefix ))
+            {
+                String versionPostfix = StringUtils.removeStart( fileName, prefix );
+                Matcher matcher = UNIQUE_SNAPSHOT_PATTERN.matcher( versionPostfix );
+                if (matcher.matches()) {
+                    info.version = baseVersion + "-" + matcher.group( 1 );
+                    String newPrefix = prefix + info.version;
+                    if (fileName.startsWith( newPrefix ))
+                    {
+                        String classPostfix = StringUtils.removeStart( fileName, newPrefix );
+                        Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
+                        if (cMatch.matches()) {
+                            info.classifier = cMatch.group( 1 );
+                            info.remainder = cMatch.group( 2 );
+                        } else {
+                            info.classifier = "";
+                            info.remainder = classPostfix;
+                        }
+                    } else {
+                        log.error( "Artifact does not match the maven name pattern {}", path );
+                        info.classifier = "";
+                        info.remainder = StringUtils.substringAfter( fileName, prefix );
+                    }
+                } else {
+                    log.error( "Artifact does not match the snapshot version pattern {}", path );
+                    info.version = "";
+                    info.classifier = "";
+                    info.remainder = StringUtils.substringAfter( fileName, prefix );
+                }
+            } else {
+                log.error( "Artifact does not match the maven name pattern: {}", path );
+                info.version = "";
+                info.classifier = "";
+                info.remainder = StringUtils.substringAfterLast( fileName, "." );
+            }
+        } else {
+            String prefix = info.id+"-"+genericVersion;
+            if (fileName.startsWith( prefix ))
+            {
+                info.version=genericVersion;
+                String classPostfix = StringUtils.removeStart( fileName, prefix );
+                Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
+                if (cMatch.matches()) {
+                    info.classifier = cMatch.group( 1 );
+                    info.remainder = cMatch.group( 2 );
+                } else {
+                    info.classifier = "";
+                    info.remainder = classPostfix;
+                }
+            } else {
+                log.error( "Artifact does not match the version pattern {}", path );
+                info.version = "";
+                info.classifier = "";
+                info.remainder = StringUtils.substringAfterLast( fileName, "." );
+            }
+        }
+        info.extension = StringUtils.substringAfterLast( fileName, "." );
+        info.type = getTypeFromClassifierAndExtension( info.classifier, info.extension );
+        try {
+            info.contentType = Files.probeContentType( path.getFilePath( ) );
+        } catch (IOException e) {
+            info.contentType = "";
+            //
+        }
+        return info;
+
+    }
+
     @Override
     public Artifact getArtifact( final ItemSelector selector ) throws ContentAccessException
     {
@@ -402,11 +637,55 @@ public class ManagedDefaultRepositoryContent
         return artifactMap.computeIfAbsent( path, artifactPath -> createArtifact( path, selector, classifier, extension ) );
     }
 
+    private StorageAsset getBasePathFromSelector(ItemSelector selector) {
+        StringBuilder path = new StringBuilder( );
+        if (selector.hasNamespace()) {
+            path.append(String.join( "/", getNamespace( selector ).getNamespacePath( ) ));
+        }
+        if (selector.hasProjectId()) {
+            path.append( "/" ).append( selector.getProjectId( ) );
+        }
+        if (selector.hasVersion()) {
+            path.append( "/" ).append( selector.getVersion( ) );
+        }
+        return getStorage( ).getAsset( path.toString( ) );
+    }
+
+    /*
+     * File filter to select certain artifacts using the selector data.
+     */
+    private Predicate<StorageAsset> getFileFilterFromSelector(final ItemSelector selector) {
+        Predicate<StorageAsset> p = a -> a.isLeaf( );
+        if (selector.hasArtifactId()) {
+            final String pattern = selector.getArtifactId( );
+            p = p.and( a -> StringUtils.startsWithIgnoreCase( a.getName( ),  pattern ) );
+        }
+        if (selector.hasArtifactVersion()) {
+            final String pattern = selector.getArtifactVersion( );
+            p = p.and( a -> StringUtils.containsIgnoreCase( a.getName( ),  pattern ) );
+        }
+        if (selector.hasExtension()) {
+            final String pattern = "."+selector.getExtension( );
+            p = p.and( a -> StringUtils.endsWithIgnoreCase( a.getName( ), pattern ) );
+        } else if (selector.hasType()) {
+            final String pattern = "."+getArtifactExtension( selector );
+            p = p.and( a -> StringUtils.endsWithIgnoreCase( a.getName( ), pattern ) );
+        }
+        if (selector.hasClassifier()) {
+            final String pattern = "-" + selector.getClassifier( ) + ".";
+            p = p.and( a -> StringUtils.containsIgnoreCase( a.getName( ), pattern ) );
+        } else if (selector.hasType()) {
+            final String pattern = "-" + getClassifierFromType( selector.getType( ) ) + ".";
+            p = p.and( a -> StringUtils.containsIgnoreCase( a.getName( ).toLowerCase( ), pattern ) );
+        }
+        return p;
+    }
+
     /*
         TBD
      */
     @Override
-    public List<Artifact> getAllArtifacts( ItemSelector selector ) throws ContentAccessException
+    public List<? extends Artifact> getAllArtifacts( ItemSelector selector ) throws ContentAccessException
     {
         return null;
     }
@@ -415,7 +694,7 @@ public class ManagedDefaultRepositoryContent
         TBD
      */
     @Override
-    public Stream<Artifact> getAllArtifactStream( ItemSelector selector ) throws ContentAccessException
+    public Stream<? extends Artifact> getAllArtifactStream( ItemSelector selector ) throws ContentAccessException
     {
         return null;
     }
@@ -424,7 +703,7 @@ public class ManagedDefaultRepositoryContent
         TBD
      */
     @Override
-    public List<Project> getProjects( Namespace namespace )
+    public List<? extends Project> getProjects( Namespace namespace )
     {
         return null;
     }
@@ -433,7 +712,7 @@ public class ManagedDefaultRepositoryContent
         TBD
      */
     @Override
-    public List<Version> getVersions( Project project )
+    public List<? extends Version> getVersions( Project project )
     {
         return null;
     }
@@ -442,7 +721,7 @@ public class ManagedDefaultRepositoryContent
         TBD
      */
     @Override
-    public List<Artifact> getArtifacts( ContentItem item )
+    public List<? extends Artifact> getArtifacts( ContentItem item )
     {
         return null;
     }
@@ -451,7 +730,7 @@ public class ManagedDefaultRepositoryContent
         TBD
      */
     @Override
-    public List<Artifact> getArtifactsStartingWith( Namespace namespace )
+    public List<? extends Artifact> getArtifactsStartingWith( Namespace namespace )
     {
         return null;
     }
@@ -460,7 +739,7 @@ public class ManagedDefaultRepositoryContent
         TBD
      */
     @Override
-    public Stream<Artifact> getArtifactStream( ContentItem item )
+    public Stream<? extends Artifact> getArtifactStream( ContentItem item )
     {
         return null;
     }
@@ -469,7 +748,7 @@ public class ManagedDefaultRepositoryContent
         TBD
      */
     @Override
-    public Stream<Artifact> getArtifactStreamStartingWith( Namespace namespace )
+    public Stream<? extends Artifact> getArtifactStreamStartingWith( Namespace namespace )
     {
         return null;
     }
@@ -922,6 +1201,10 @@ public class ManagedDefaultRepositoryContent
         return repository.getAsset( "" ).getFilePath( );
     }
 
+    private RepositoryStorage getStorage() {
+        return repository.getAsset( "" ).getStorage( );
+    }
+
     /**
      * Convert a path to an artifact reference.
      *