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

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

commit 8e4acdc82a875ca32e39eb216b879c002d228de3
Author: Martin Stockhammer <[email protected]>
AuthorDate: Fri Jul 19 19:29:01 2019 +0200

    Migrating to storage API
---
 .../org/apache/archiva/checksum/ChecksumUtil.java  |   1 +
 .../archiva-base/archiva-configuration/pom.xml     |   4 -
 .../org/apache/archiva/common/filelock/Lock.java   |   4 +-
 .../archiva/policies/AbstractUpdatePolicy.java     |  26 +--
 .../archiva/policies/CachedFailuresPolicy.java     |   4 +-
 .../apache/archiva/policies/ChecksumPolicy.java    |  21 +-
 .../archiva/policies/DownloadErrorPolicy.java      |   5 +-
 .../apache/archiva/policies/DownloadPolicy.java    |   7 +-
 .../policies/PropagateErrorsDownloadPolicy.java    |   4 +-
 .../PropagateErrorsOnUpdateDownloadPolicy.java     |   6 +-
 .../archiva/proxy/model/ProxyFetchResult.java      |   8 +-
 .../proxy/model/RepositoryProxyHandler.java        |   6 +-
 .../proxy/DefaultRepositoryProxyHandler.java       | 193 +++++++++----------
 .../repository/ManagedRepositoryContent.java       |  13 +-
 .../repository/content/RepositoryStorage.java      |  81 ++++++--
 .../archiva/repository/content/StorageAsset.java   |  51 ++++-
 .../archiva/repository/AbstractRepository.java     |  40 +++-
 .../archiva/repository/RepositoryRegistry.java     |   3 +
 .../repository/content/FilesystemAsset.java        |  65 +++++--
 .../repository/content/FilesystemStorage.java      | 213 +++++++++++++++++++--
 .../archiva/repository/content/StorageUtil.java    | 192 +++++++++++++++++++
 .../repository/content/FilesystemAssetTest.java    |  13 +-
 .../mock/ManagedRepositoryContentMock.java         |   8 +-
 .../scanner/mock/ManagedRepositoryContentMock.java |   7 +-
 .../proxy/maven/MavenRepositoryProxyHandler.java   |  28 +--
 .../mock/ManagedRepositoryContentMock.java         |   9 +-
 .../maven2/ManagedDefaultRepositoryContent.java    |  44 ++---
 .../scheduler/repository/model/RepositoryTask.java |   7 +-
 .../ArchivaRepositoryScanningTaskExecutor.java     |   5 +-
 .../rest/services/DefaultRepositoriesService.java  |  97 +++++-----
 .../apache/archiva/webdav/ArchivaDavResource.java  |   2 +-
 .../archiva/webdav/ArchivaDavResourceFactory.java  |  10 +-
 32 files changed, 834 insertions(+), 343 deletions(-)

diff --git 
a/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/ChecksumUtil.java
 
b/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/ChecksumUtil.java
index 5a11b91..bb6a710 100644
--- 
a/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/ChecksumUtil.java
+++ 
b/archiva-modules/archiva-base/archiva-checksum/src/main/java/org/apache/archiva/checksum/ChecksumUtil.java
@@ -97,4 +97,5 @@ public class ChecksumUtil {
     public static List<Checksum> newChecksums(List<ChecksumAlgorithm> 
checksumAlgorithms) {
         return checksumAlgorithms.stream().map( a -> new 
Checksum(a)).collect(Collectors.toList());
     }
+
 }
diff --git a/archiva-modules/archiva-base/archiva-configuration/pom.xml 
b/archiva-modules/archiva-base/archiva-configuration/pom.xml
index 7569029..fd4565c 100644
--- a/archiva-modules/archiva-base/archiva-configuration/pom.xml
+++ b/archiva-modules/archiva-base/archiva-configuration/pom.xml
@@ -136,10 +136,6 @@
     </dependency>
     <dependency>
       <groupId>org.apache.archiva</groupId>
-      <artifactId>archiva-policies</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.archiva</groupId>
       <artifactId>archiva-test-utils</artifactId>
       <version>${project.version}</version>
       <scope>test</scope>
diff --git 
a/archiva-modules/archiva-base/archiva-filelock/src/main/java/org/apache/archiva/common/filelock/Lock.java
 
b/archiva-modules/archiva-base/archiva-filelock/src/main/java/org/apache/archiva/common/filelock/Lock.java
index ab10d74..f8e4e46 100644
--- 
a/archiva-modules/archiva-base/archiva-filelock/src/main/java/org/apache/archiva/common/filelock/Lock.java
+++ 
b/archiva-modules/archiva-base/archiva-filelock/src/main/java/org/apache/archiva/common/filelock/Lock.java
@@ -34,7 +34,7 @@ import java.util.concurrent.atomic.AtomicInteger;
  * @author Olivier Lamy
  * @since 2.0.0
  */
-public class Lock
+public class Lock implements Closeable
 {
     private Path file;
 
@@ -104,7 +104,7 @@ public class Lock
         return this.fileClients.remove( thread ) != null;
     }
 
-    protected void close()
+    public void close()
         throws IOException
     {
         IOException ioException = null;
diff --git 
a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/AbstractUpdatePolicy.java
 
b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/AbstractUpdatePolicy.java
index 1fbbfec..cf1c0da 100644
--- 
a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/AbstractUpdatePolicy.java
+++ 
b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/AbstractUpdatePolicy.java
@@ -20,13 +20,13 @@ package org.apache.archiva.policies;
  */
 
 import org.apache.archiva.common.utils.VersionUtil;
+import org.apache.archiva.repository.content.StorageAsset;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.nio.file.Files;
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
@@ -106,7 +106,7 @@ public abstract class AbstractUpdatePolicy
     }
 
     @Override
-    public void applyPolicy( String policySetting, Properties request, Path 
localFile )
+    public void applyPolicy( String policySetting, Properties request, 
StorageAsset localFile )
         throws PolicyViolationException, PolicyConfigurationException
     {
         if ( !StringUtils.equals( request.getProperty( "filetype" ), 
"artifact" ) )
@@ -157,7 +157,7 @@ public abstract class AbstractUpdatePolicy
             throw new PolicyViolationException( "NO to update, " + 
getUpdateMode() + " policy set to NEVER." );
         }
 
-        if ( !Files.exists(localFile) )
+        if ( !localFile.exists() )
         {
             // No file means it's ok.
             log.debug( "OK to update {}, local file does not exist.", 
getUpdateMode() );
@@ -176,15 +176,7 @@ public abstract class AbstractUpdatePolicy
             Calendar cal = Calendar.getInstance();
             cal.add( Calendar.DAY_OF_MONTH, -1 );
             Calendar fileCal = Calendar.getInstance();
-            try
-            {
-                fileCal.setTimeInMillis( 
Files.getLastModifiedTime(localFile).toMillis() );
-            }
-            catch ( IOException e )
-            {
-                fileCal.setTimeInMillis( new Date().getTime() );
-                log.error("Could not read modification time of {}", localFile);
-            }
+            fileCal.setTimeInMillis( 
localFile.getModificationTime().toEpochMilli() );
 
             if ( cal.after( fileCal ) )
             {
@@ -203,15 +195,7 @@ public abstract class AbstractUpdatePolicy
             Calendar cal = Calendar.getInstance();
             cal.add( Calendar.HOUR, -1 );
             Calendar fileCal = Calendar.getInstance();
-            try
-            {
-                fileCal.setTimeInMillis( 
Files.getLastModifiedTime(localFile).toMillis() );
-            }
-            catch ( IOException e )
-            {
-                fileCal.setTimeInMillis( new Date().getTime() );
-                log.error("Could not read modification time of {}", localFile);
-            }
+            fileCal.setTimeInMillis( 
localFile.getModificationTime().toEpochMilli());
 
             if ( cal.after( fileCal ) )
             {
diff --git 
a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/CachedFailuresPolicy.java
 
b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/CachedFailuresPolicy.java
index e92858f..ac20dbc 100644
--- 
a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/CachedFailuresPolicy.java
+++ 
b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/CachedFailuresPolicy.java
@@ -20,13 +20,13 @@ package org.apache.archiva.policies;
  */
 
 import org.apache.archiva.policies.urlcache.UrlFailureCache;
+import org.apache.archiva.repository.content.StorageAsset;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 
 import javax.inject.Inject;
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
@@ -64,7 +64,7 @@ public class CachedFailuresPolicy
     }
 
     @Override
-    public void applyPolicy( String policySetting, Properties request, Path 
localFile )
+    public void applyPolicy( String policySetting, Properties request, 
StorageAsset localFile )
         throws PolicyViolationException, PolicyConfigurationException
     {
         if ( !options.contains( policySetting ) )
diff --git 
a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/ChecksumPolicy.java
 
b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/ChecksumPolicy.java
index 82d4d3d..8f6885a 100644
--- 
a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/ChecksumPolicy.java
+++ 
b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/ChecksumPolicy.java
@@ -22,6 +22,7 @@ package org.apache.archiva.policies;
 import org.apache.archiva.checksum.ChecksumAlgorithm;
 import org.apache.archiva.checksum.ChecksummedFile;
 import org.apache.archiva.checksum.UpdateStatus;
+import org.apache.archiva.repository.content.StorageAsset;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -80,7 +81,7 @@ public class ChecksumPolicy
     }
 
     @Override
-    public void applyPolicy( String policySetting, Properties request, Path 
localFile )
+    public void applyPolicy( String policySetting, Properties request, 
StorageAsset localFile )
         throws PolicyViolationException, PolicyConfigurationException
     {
         if ( "resource".equals( request.getProperty( "filetype" ) ) )
@@ -103,16 +104,16 @@ public class ChecksumPolicy
             return;
         }
 
-        if ( !Files.exists(localFile) )
+        if ( !localFile.exists() )
         {
             // Local File does not exist.
             throw new PolicyViolationException(
-                "Checksum policy failure, local file " + 
localFile.toAbsolutePath() + " does not exist to check." );
+                "Checksum policy failure, local file " + localFile.getPath() + 
" does not exist to check." );
         }
 
-        if ( FAIL.equals( policySetting ) )
+        if ( FAIL.equals( policySetting ) && localFile.isFileBased() )
         {
-            ChecksummedFile checksum = new ChecksummedFile( localFile );
+            ChecksummedFile checksum = new ChecksummedFile( 
localFile.getFilePath() );
             if ( checksum.isValidChecksums( algorithms ) )
             {
                 return;
@@ -133,7 +134,7 @@ public class ChecksumPolicy
 
             try
             {
-                Files.deleteIfExists( localFile );
+                localFile.getStorage().removeAsset( localFile );
             }
             catch ( IOException e )
             {
@@ -141,12 +142,12 @@ public class ChecksumPolicy
             }
             throw new PolicyViolationException(
                 "Checksums do not match, policy set to FAIL, " + "deleting 
checksum files and local file "
-                    + localFile.toAbsolutePath() + "." );
+                    + localFile.getPath() + "." );
         }
 
-        if ( FIX.equals( policySetting ) )
+        if ( FIX.equals( policySetting ) && localFile.isFileBased())
         {
-            ChecksummedFile checksum = new ChecksummedFile( localFile );
+            ChecksummedFile checksum = new ChecksummedFile( 
localFile.getFilePath() );
             if ( checksum.fixChecksums( algorithms ).getTotalStatus() != 
UpdateStatus.ERROR )
             {
                 log.debug( "Checksum policy set to FIX, checksum files have 
been updated." );
@@ -156,7 +157,7 @@ public class ChecksumPolicy
             {
                 throw new PolicyViolationException(
                     "Checksum policy set to FIX, " + "yet unable to update 
checksums for local file "
-                        + localFile.toAbsolutePath() + "." );
+                        + localFile.getPath() + "." );
             }
         }
 
diff --git 
a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/DownloadErrorPolicy.java
 
b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/DownloadErrorPolicy.java
index 9026173..28de533 100644
--- 
a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/DownloadErrorPolicy.java
+++ 
b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/DownloadErrorPolicy.java
@@ -19,7 +19,8 @@ package org.apache.archiva.policies;
  * under the License.
  */
 
-import java.nio.file.Path;
+import org.apache.archiva.repository.content.StorageAsset;
+
 import java.util.Map;
 import java.util.Properties;
 
@@ -43,7 +44,7 @@ public interface DownloadErrorPolicy
      * @return whether to process the exception or not
      * @throws PolicyConfigurationException if the policy is improperly 
configured
      */
-    boolean applyPolicy( String policySetting, Properties request, Path 
localFile, Exception exception,
+    boolean applyPolicy( String policySetting, Properties request, 
StorageAsset localFile, Exception exception,
                          Map<String, Exception> previousExceptions )
         throws PolicyConfigurationException;
 }
diff --git 
a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/DownloadPolicy.java
 
b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/DownloadPolicy.java
index 8d149e6..f9a1443 100644
--- 
a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/DownloadPolicy.java
+++ 
b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/DownloadPolicy.java
@@ -19,7 +19,8 @@ package org.apache.archiva.policies;
  * under the License.
  */
 
-import java.nio.file.Path;
+import org.apache.archiva.repository.content.StorageAsset;
+
 import java.util.Properties;
 
 /**
@@ -37,9 +38,9 @@ public interface DownloadPolicy
      * @param policySetting the policy setting.
      * @param request the list of request properties that the policy might use.
      * @param localFile
-     * 
+     *
      * @throws PolicyViolationException if the policy has been violated.
      */
-    void applyPolicy( String policySetting, Properties request, Path localFile 
)
+    void applyPolicy( String policySetting, Properties request, StorageAsset 
localFile )
         throws PolicyViolationException, PolicyConfigurationException;
 }
diff --git 
a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/PropagateErrorsDownloadPolicy.java
 
b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/PropagateErrorsDownloadPolicy.java
index 1fab120..7a0baef 100644
--- 
a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/PropagateErrorsDownloadPolicy.java
+++ 
b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/PropagateErrorsDownloadPolicy.java
@@ -19,12 +19,12 @@ package org.apache.archiva.policies;
  * under the License.
  */
 
+import org.apache.archiva.repository.content.StorageAsset;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -64,7 +64,7 @@ public class PropagateErrorsDownloadPolicy
     }
 
     @Override
-    public boolean applyPolicy( String policySetting, Properties request, Path 
localFile, Exception exception,
+    public boolean applyPolicy( String policySetting, Properties request, 
StorageAsset localFile, Exception exception,
                                 Map<String, Exception> previousExceptions )
         throws PolicyConfigurationException
     {
diff --git 
a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/PropagateErrorsOnUpdateDownloadPolicy.java
 
b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/PropagateErrorsOnUpdateDownloadPolicy.java
index ec32891..9b22a9b 100644
--- 
a/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/PropagateErrorsOnUpdateDownloadPolicy.java
+++ 
b/archiva-modules/archiva-base/archiva-policies/src/main/java/org/apache/archiva/policies/PropagateErrorsOnUpdateDownloadPolicy.java
@@ -19,11 +19,11 @@ package org.apache.archiva.policies;
  * under the License.
  */
 
+import org.apache.archiva.repository.content.StorageAsset;
 import org.apache.commons.lang.StringUtils;
 import org.springframework.stereotype.Service;
 
 import java.nio.file.Files;
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -55,7 +55,7 @@ public class PropagateErrorsOnUpdateDownloadPolicy
     }
 
     @Override
-    public boolean applyPolicy( String policySetting, Properties request, Path 
localFile, Exception exception,
+    public boolean applyPolicy( String policySetting, Properties request, 
StorageAsset localFile, Exception exception,
                                 Map<String, Exception> previousExceptions )
         throws PolicyConfigurationException
     {
@@ -76,7 +76,7 @@ public class PropagateErrorsOnUpdateDownloadPolicy
         if ( NOT_PRESENT.equals( policySetting ) )
         {
             // cancel the exception if the file exists
-            return !Files.exists(localFile);
+            return !localFile.exists();
         }
 
         throw new PolicyConfigurationException(
diff --git 
a/archiva-modules/archiva-base/archiva-proxy-api/src/main/java/org/apache/archiva/proxy/model/ProxyFetchResult.java
 
b/archiva-modules/archiva-base/archiva-proxy-api/src/main/java/org/apache/archiva/proxy/model/ProxyFetchResult.java
index 28e8cb9..700e4a5 100644
--- 
a/archiva-modules/archiva-base/archiva-proxy-api/src/main/java/org/apache/archiva/proxy/model/ProxyFetchResult.java
+++ 
b/archiva-modules/archiva-base/archiva-proxy-api/src/main/java/org/apache/archiva/proxy/model/ProxyFetchResult.java
@@ -20,6 +20,8 @@ package org.apache.archiva.proxy.model;
  */
 
 
+import org.apache.archiva.repository.content.StorageAsset;
+
 import java.nio.file.Path;
 
 /**
@@ -31,18 +33,18 @@ public class ProxyFetchResult
 {
 
     //The file returned
-    private Path file;
+    private StorageAsset file;
 
     //Was the local file modified by the fetch?
     private boolean modified;
 
-    public ProxyFetchResult( Path file, boolean modified )
+    public ProxyFetchResult( StorageAsset file, boolean modified )
     {
         this.file = file;
         this.modified = modified;
     }
 
-    public Path getFile()
+    public StorageAsset getFile()
     {
         return file;
     }
diff --git 
a/archiva-modules/archiva-base/archiva-proxy-api/src/main/java/org/apache/archiva/proxy/model/RepositoryProxyHandler.java
 
b/archiva-modules/archiva-base/archiva-proxy-api/src/main/java/org/apache/archiva/proxy/model/RepositoryProxyHandler.java
index eee2d86..f82d555 100644
--- 
a/archiva-modules/archiva-base/archiva-proxy-api/src/main/java/org/apache/archiva/proxy/model/RepositoryProxyHandler.java
+++ 
b/archiva-modules/archiva-base/archiva-proxy-api/src/main/java/org/apache/archiva/proxy/model/RepositoryProxyHandler.java
@@ -23,8 +23,8 @@ import org.apache.archiva.model.ArtifactReference;
 import org.apache.archiva.policies.ProxyDownloadException;
 import org.apache.archiva.repository.ManagedRepositoryContent;
 import org.apache.archiva.repository.RepositoryType;
+import org.apache.archiva.repository.content.StorageAsset;
 
-import java.nio.file.Path;
 import java.util.List;
 import java.util.Map;
 
@@ -50,7 +50,7 @@ public interface RepositoryProxyHandler
      * @return the file that was obtained, or null if no content was obtained
      * @throws ProxyDownloadException if there was a problem fetching the 
content from the target repositories.
      */
-    Path fetchFromProxies( ManagedRepositoryContent repository, 
ArtifactReference artifact )
+    StorageAsset fetchFromProxies( ManagedRepositoryContent repository, 
ArtifactReference artifact )
         throws ProxyDownloadException;
     
     /**
@@ -74,7 +74,7 @@ public interface RepositoryProxyHandler
      * @param path the path of the resource to fetch
      * @return the file that was obtained, or null if no content was obtained
      */
-    Path fetchFromProxies( ManagedRepositoryContent managedRepository, String 
path );
+    StorageAsset fetchFromProxies( ManagedRepositoryContent managedRepository, 
String path );
 
     /**
      * Get the List of {@link ProxyConnector} objects of the source repository.
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 5622fe5..fac557e 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
@@ -19,6 +19,8 @@ package org.apache.archiva.proxy;
  * under the License.
  */
 
+import org.apache.archiva.checksum.ChecksumAlgorithm;
+import org.apache.archiva.checksum.ChecksumUtil;
 import org.apache.archiva.proxy.model.ProxyConnectorRuleType;
 import org.apache.archiva.common.filelock.FileLockException;
 import org.apache.archiva.common.filelock.FileLockManager;
@@ -37,6 +39,9 @@ import 
org.apache.archiva.redback.components.registry.Registry;
 import org.apache.archiva.redback.components.registry.RegistryListener;
 import org.apache.archiva.redback.components.taskqueue.TaskQueueException;
 import org.apache.archiva.repository.*;
+import org.apache.archiva.repository.content.FilesystemStorage;
+import org.apache.archiva.repository.content.StorageAsset;
+import org.apache.archiva.repository.content.StorageUtil;
 import org.apache.archiva.repository.metadata.MetadataTools;
 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
@@ -94,12 +99,14 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
     private FileLockManager fileLockManager;
 
     private Map<String, NetworkProxy> networkProxyMap = new 
ConcurrentHashMap<>();
+    private List<ChecksumAlgorithm> checksumAlgorithms;
 
     @PostConstruct
     public void initialize()
     {
         initConnectors();
         archivaConfiguration.addChangeListener( this );
+        checksumAlgorithms = 
ChecksumUtil.getAlgorithms(archivaConfiguration.getConfiguration().getArchivaRuntimeConfiguration().getChecksumTypes());
 
     }
 
@@ -243,10 +250,10 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
     }
 
     @Override
-    public Path fetchFromProxies(ManagedRepositoryContent repository, 
ArtifactReference artifact )
+    public StorageAsset fetchFromProxies( ManagedRepositoryContent repository, 
ArtifactReference artifact )
         throws ProxyDownloadException
     {
-        Path localFile = toLocalFile( repository, artifact );
+        StorageAsset localFile = toLocalFile( repository, artifact );
 
         Properties requestProperties = new Properties();
         requestProperties.setProperty( "filetype", "artifact" );
@@ -275,13 +282,13 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
 
             try
             {
-                Path downloadedFile =
+                StorageAsset downloadedFile =
                     transferFile( connector, targetRepository, targetPath, 
repository, localFile, requestProperties,
                                   true );
 
-                if ( fileExists( downloadedFile ) )
+                if ( downloadedFile.exists() )
                 {
-                    log.debug( "Successfully transferred: {}", 
downloadedFile.toAbsolutePath() );
+                    log.debug( "Successfully transferred: {}", 
downloadedFile.getPath() );
                     return downloadedFile;
                 }
             }
@@ -314,12 +321,12 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
     }
 
     @Override
-    public Path fetchFromProxies( ManagedRepositoryContent repository, String 
path )
+    public StorageAsset fetchFromProxies( ManagedRepositoryContent repository, 
String path )
     {
-        Path localFile = Paths.get( repository.getRepoRoot(), path );
+        StorageAsset localFile = repository.getRepository().getAsset( path );
 
         // no update policies for these paths
-        if ( Files.exists(localFile) )
+        if ( localFile.exists() )
         {
             return null;
         }
@@ -343,13 +350,13 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
 
             try
             {
-                Path downloadedFile =
+                StorageAsset downloadedFile =
                     transferFile( connector, targetRepository, targetPath, 
repository, localFile, requestProperties,
                                   false );
 
                 if ( fileExists( downloadedFile ) )
                 {
-                    log.debug( "Successfully transferred: {}", 
downloadedFile.toAbsolutePath() );
+                    log.debug( "Successfully transferred: {}", 
downloadedFile.getPath() );
                     return downloadedFile;
                 }
             }
@@ -384,7 +391,7 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
     @Override
     public ProxyFetchResult fetchMetadataFromProxies(ManagedRepositoryContent 
repository, String logicalPath )
     {
-        Path localFile = Paths.get( repository.getRepoRoot(), logicalPath );
+        StorageAsset localFile = repository.getRepository().getAsset( 
logicalPath );
 
         Properties requestProperties = new Properties();
         requestProperties.setProperty( "filetype", "metadata" );
@@ -401,7 +408,7 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
 
             RemoteRepositoryContent targetRepository = 
connector.getTargetRepository();
 
-            Path localRepoFile = toLocalRepoFile( repository, 
targetRepository, logicalPath );
+            StorageAsset localRepoFile = toLocalRepoFile( repository, 
targetRepository, logicalPath );
             long originalMetadataTimestamp = getLastModified( localRepoFile );
 
             try
@@ -442,7 +449,7 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
             metadataNeedsUpdating = true;
         }
 
-        if ( metadataNeedsUpdating || !Files.exists(localFile))
+        if ( metadataNeedsUpdating || !localFile.exists())
         {
             try
             {
@@ -450,7 +457,7 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
             }
             catch ( RepositoryMetadataException e )
             {
-                log.warn( "Unable to update metadata {}:{}", 
localFile.toAbsolutePath(), e.getMessage(), e );
+                log.warn( "Unable to update metadata {}:{}", 
localFile.getPath(), e.getMessage(), e );
             }
 
         }
@@ -463,27 +470,19 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
         return new ProxyFetchResult( null, false );
     }
 
-    private long getLastModified(Path file )
+    private long getLastModified(StorageAsset file )
     {
-        if ( !Files.exists(file) || !Files.isRegularFile(file) )
+        if ( !file.exists() || file.isContainer() )
         {
             return 0;
         }
 
-        try
-        {
-            return Files.getLastModifiedTime(file).toMillis();
-        }
-        catch ( IOException e )
-        {
-            log.error("Could get the modified time of file {}", 
file.toAbsolutePath());
-            return 0;
-        }
+        return file.getModificationTime().toEpochMilli();
     }
 
-    private boolean hasBeenUpdated(Path file, long originalLastModified )
+    private boolean hasBeenUpdated(StorageAsset file, long 
originalLastModified )
     {
-        if ( !Files.exists(file) || !Files.isRegularFile(file) )
+        if ( !file.exists() || file.isContainer() )
         {
             return false;
         }
@@ -492,11 +491,11 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
         return ( currentLastModified > originalLastModified );
     }
 
-    private Path toLocalRepoFile(ManagedRepositoryContent repository, 
RemoteRepositoryContent targetRepository,
-                                 String targetPath )
+    private StorageAsset toLocalRepoFile( ManagedRepositoryContent repository, 
RemoteRepositoryContent targetRepository,
+                                          String targetPath )
     {
         String repoPath = metadataTools.getRepositorySpecificName( 
targetRepository, targetPath );
-        return Paths.get( repository.getRepoRoot(), repoPath );
+        return repository.getRepository().getAsset( repoPath );
     }
 
     /**
@@ -511,7 +510,7 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
         }
     }
 
-    private Path toLocalFile(ManagedRepositoryContent repository, 
ArtifactReference artifact )
+    private StorageAsset toLocalFile(ManagedRepositoryContent repository, 
ArtifactReference artifact )
     {
         return repository.toFile( artifact );
     }
@@ -522,19 +521,19 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
      * @param file the file to test. (may be null)
      * @return true if file exists. false if the file param is null, doesn't 
exist, or is not of type File.
      */
-    private boolean fileExists( Path file )
+    private boolean fileExists( StorageAsset file )
     {
         if ( file == null )
         {
             return false;
         }
 
-        if ( !Files.exists(file))
+        if ( !file.exists())
         {
             return false;
         }
 
-        return Files.isRegularFile(file);
+        return !file.isContainer();
     }
 
     /**
@@ -544,7 +543,7 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
      * @param remoteRepository  the remote repository get the resource from.
      * @param remotePath        the path in the remote repository to the 
resource to get.
      * @param repository        the managed repository that will hold the file
-     * @param resource          the local file to place the downloaded 
resource into
+     * @param resource          the path relative to the repository storage 
where the file should be downloaded to
      * @param requestProperties the request properties to utilize for policy 
handling.
      * @param executeConsumers  whether to execute the consumers after proxying
      * @return the local file that was downloaded, or null if not downloaded.
@@ -553,9 +552,9 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
      *                              the remote resource is not newer than the 
local File.
      * @throws ProxyException       if transfer was unsuccessful.
      */
-    protected Path transferFile( ProxyConnector connector, 
RemoteRepositoryContent remoteRepository, String remotePath,
-                               ManagedRepositoryContent repository, Path 
resource, Properties requestProperties,
-                               boolean executeConsumers )
+    protected StorageAsset transferFile( ProxyConnector connector, 
RemoteRepositoryContent remoteRepository, String remotePath,
+                                         ManagedRepositoryContent repository, 
StorageAsset resource, Properties requestProperties,
+                                         boolean executeConsumers )
         throws ProxyException, NotModifiedException
     {
         String url = remoteRepository.getURL().getUrl();
@@ -594,7 +593,7 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
         catch ( PolicyViolationException e )
         {
             String emsg = "Transfer not attempted on " + url + " : " + 
e.getMessage();
-            if ( fileExists( resource ) )
+            if ( resource.exists() )
             {
                 log.debug( "{} : using already present local file.", emsg );
                 return resource;
@@ -605,15 +604,27 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
         }
 
         Path workingDirectory = createWorkingDirectory( repository );
-        Path tmpResource = workingDirectory.resolve(resource.getFileName());
-        Path tmpMd5 = 
workingDirectory.resolve(resource.getFileName().toString() + ".md5" );
-        Path tmpSha1 = workingDirectory.resolve( 
resource.getFileName().toString() + ".sha1" );
+        FilesystemStorage tmpStorage = null;
+        try
+        {
+            tmpStorage = new FilesystemStorage( workingDirectory, 
fileLockManager );
+        }
+        catch ( IOException e )
+        {
+            throw new ProxyException( "Could not create tmp storage" );
+        }
+        StorageAsset tmpResource = tmpStorage.getAsset( resource.getName( ) );
+        StorageAsset[] tmpChecksumFiles = new 
StorageAsset[checksumAlgorithms.size()];
+        for(int i=0; i<checksumAlgorithms.size(); i++) {
+            ChecksumAlgorithm alg = checksumAlgorithms.get( i );
+            tmpChecksumFiles[i] = tmpStorage.getAsset( resource.getName() + 
"." + alg.getDefaultExtension() );
+        }
 
         try
         {
 
-            transferResources( connector, remoteRepository, tmpMd5, tmpSha1, 
tmpResource, url, remotePath, resource,
-                               workingDirectory, repository );
+            transferResources( connector, remoteRepository, 
tmpResource,tmpChecksumFiles , url, remotePath,
+                resource, workingDirectory, repository );
 
             // Handle post-download policies.
             try
@@ -632,11 +643,12 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
 
             if ( resource != null )
             {
-                synchronized ( resource.toAbsolutePath().toString().intern() )
+                synchronized ( resource.getPath().intern() )
                 {
-                    Path directory = resource.getParent();
-                    moveFileIfExists( tmpMd5, directory );
-                    moveFileIfExists( tmpSha1, directory );
+                    StorageAsset directory = resource.getParent();
+                    for (int i=0; i<tmpChecksumFiles.length; i++) {
+                        moveFileIfExists( tmpChecksumFiles[i], directory );
+                    }
                     moveFileIfExists( tmpResource, directory );
                 }
             }
@@ -656,11 +668,11 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
         return resource;
     }
 
-    protected abstract void transferResources(ProxyConnector connector, 
RemoteRepositoryContent remoteRepository, Path tmpMd5, Path tmpSha1,
-                                              Path tmpResource, String url, 
String remotePath, Path resource, Path workingDirectory,
-                                              ManagedRepositoryContent 
repository) throws ProxyException, NotModifiedException;
+    protected abstract void transferResources( ProxyConnector connector, 
RemoteRepositoryContent remoteRepository,
+                                               StorageAsset tmpResource, 
StorageAsset[] checksumFiles, String url, String remotePath, StorageAsset 
resource, Path workingDirectory,
+                                               ManagedRepositoryContent 
repository ) throws ProxyException, NotModifiedException;
 
-    private void queueRepositoryTask(String repositoryId, Path localFile )
+    private void queueRepositoryTask(String repositoryId, StorageAsset 
localFile )
     {
         RepositoryTask task = new RepositoryTask();
         task.setRepositoryId( repositoryId );
@@ -675,7 +687,7 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
         catch ( TaskQueueException e )
         {
             log.error( "Unable to queue repository task to execute consumers 
on resource file ['{}"
-                           + "'].", localFile.getFileName() );
+                           + "'].", localFile.getName() );
         }
     }
 
@@ -685,12 +697,12 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
      * @param fileToMove this could be either the main artifact, sha1 or md5 
checksum file.
      * @param directory  directory to write files to
      */
-    private void moveFileIfExists( Path fileToMove, Path directory )
+    private void moveFileIfExists( StorageAsset fileToMove, StorageAsset 
directory )
         throws ProxyException
     {
-        if ( fileToMove != null && Files.exists(fileToMove) )
+        if ( fileToMove != null && fileToMove.exists() )
         {
-            Path newLocation = directory.resolve(fileToMove.getFileName());
+            StorageAsset newLocation = directory.getStorage().getAsset( 
directory.getPath()+ "/" + fileToMove.getName());
             moveTempToTarget( fileToMove, newLocation );
         }
     }
@@ -701,13 +713,13 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
      * @param policies  the map of policies to execute. (Map of String policy 
keys, to {@link DownloadPolicy} objects)
      * @param settings  the map of settings for the policies to execute. (Map 
of String policy keys, to String policy
      *                  setting)
-     * @param request   the request properties (utilized by the {@link 
DownloadPolicy#applyPolicy(String, Properties, Path)}
+     * @param request   the request properties (utilized by the {@link 
DownloadPolicy#applyPolicy(String, Properties, StorageAsset)}
      *                  )
-     * @param localFile the local file (utilized by the {@link 
DownloadPolicy#applyPolicy(String, Properties, Path)})
+     * @param localFile the local file (utilized by the {@link 
DownloadPolicy#applyPolicy(String, Properties, StorageAsset)})
      * @throws PolicyViolationException
      */
     private void validatePolicies( Map<String, ? extends DownloadPolicy> 
policies, Map<String, String> settings,
-                                   Properties request, Path localFile )
+                                   Properties request, StorageAsset localFile )
         throws PolicyViolationException
     {
         for ( Map.Entry<String, ? extends DownloadPolicy> entry : 
policies.entrySet() )
@@ -732,9 +744,9 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
         }
     }
 
-    private void validatePolicies(Map<String, DownloadErrorPolicy> policies, 
Map<String, String> settings,
-                                  Properties request, ArtifactReference 
artifact, RemoteRepositoryContent content,
-                                  Path localFile, Exception exception, 
Map<String, Exception> previousExceptions )
+    private void validatePolicies( Map<String, DownloadErrorPolicy> policies, 
Map<String, String> settings,
+                                   Properties request, ArtifactReference 
artifact, RemoteRepositoryContent content,
+                                   StorageAsset localFile, Exception 
exception, Map<String, Exception> previousExceptions )
         throws ProxyDownloadException
     {
         boolean process = true;
@@ -813,61 +825,28 @@ public abstract class DefaultRepositoryProxyHandler 
implements RepositoryProxyHa
      * @param target The final location of the downloaded file
      * @throws ProxyException when the temp file cannot replace the target file
      */
-    private void moveTempToTarget( Path temp, Path target )
+    private void moveTempToTarget( StorageAsset temp, StorageAsset target )
         throws ProxyException
     {
 
-        Lock lock;
         try
         {
-            lock = fileLockManager.writeFileLock( target );
-            try {
-                Files.deleteIfExists(lock.getFile());
-            } catch (IOException e) {
-                throw new ProxyException( "Unable to overwrite existing target 
file: " + target.toAbsolutePath() );
-            }
-
-            try {
-                Files.createDirectories(lock.getFile().getParent());
-            } catch (IOException e) {
-                throw new ProxyException("Unable to create parent directory 
"+lock.getFile().getParent());
-            }
-
+            StorageUtil.moveAsset( temp, target, true );
+        }
+        catch ( IOException e )
+        {
+            log.error( "Move failed from {} to {}, trying copy.", temp, target 
);
             try
             {
-                Files.move(temp, lock.getFile() );
+                StorageUtil.copyAsset( temp, target, true );
+                if (temp.exists()) {
+                    temp.getStorage( ).removeAsset( temp );
+                }
             }
-            catch ( IOException e )
+            catch ( IOException ex )
             {
-                log.warn( "Unable to rename tmp file to its final name... 
resorting to copy command." );
-
-                try
-                {
-                    Files.copy( temp, lock.getFile());
-                }
-                catch ( IOException e2 )
-                {
-                    if ( Files.exists(lock.getFile()) )
-                    {
-                        log.debug( "Tried to copy file {} to {} but file with 
this name already exists.",
-                            temp.getFileName(), 
lock.getFile().toAbsolutePath() );
-                    }
-                    else
-                    {
-                        throw new ProxyException(
-                            "Cannot copy tmp file " + temp.toAbsolutePath() + 
" to its final location", e2 );
-                    }
-                }
-                finally
-                {
-                    org.apache.archiva.common.utils.FileUtils.deleteQuietly( 
temp );
-                }
+                throw new ProxyException("Could not move temp file 
"+temp.getPath()+" to target "+target.getPath(), e);
             }
-
-        }
-        catch ( FileLockException | FileLockTimeoutException e )
-        {
-            throw new ProxyException( e.getMessage(), e );
         }
     }
 
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 1bab4a8..c50a89c 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
@@ -23,18 +23,9 @@ import org.apache.archiva.model.ArchivaArtifact;
 import org.apache.archiva.model.ArtifactReference;
 import org.apache.archiva.model.ProjectReference;
 import org.apache.archiva.model.VersionedReference;
-import org.apache.archiva.repository.content.RepositoryStorage;
 import org.apache.archiva.repository.content.StorageAsset;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Path;
-import java.time.Instant;
-import java.util.List;
 import java.util.Set;
-import java.util.function.Consumer;
-import java.util.function.Function;
 
 /**
  * ManagedRepositoryContent interface for interacting with a managed 
repository in an abstract way,
@@ -198,7 +189,7 @@ public interface ManagedRepositoryContent extends 
RepositoryContent
      * @param reference the artifact reference to use.
      * @return the relative path to the artifact.
      */
-    Path toFile( ArtifactReference reference );
+    StorageAsset toFile( ArtifactReference reference );
 
     /**
      * Given an {@link ArchivaArtifact}, return the file reference to the 
artifact.
@@ -206,7 +197,7 @@ public interface ManagedRepositoryContent extends 
RepositoryContent
      * @param reference the archiva artifact to use.
      * @return the relative path to the artifact.
      */
-    Path toFile( ArchivaArtifact reference );
+    StorageAsset toFile( ArchivaArtifact reference );
 
     /**
      * Given a {@link ProjectReference}, return the path to the metadata for
diff --git 
a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/RepositoryStorage.java
 
b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/RepositoryStorage.java
index 82b40b2..3dea0b2 100644
--- 
a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/RepositoryStorage.java
+++ 
b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/RepositoryStorage.java
@@ -21,6 +21,10 @@ package org.apache.archiva.repository.content;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.nio.file.CopyOption;
 import java.util.function.Consumer;
 
 /**
@@ -47,14 +51,43 @@ public interface RepositoryStorage {
     /**
      * Consumes the data and sets a lock for the file during the operation.
      *
-     * @param asset
-     * @param consumerFunction
-     * @param readLock
+     * @param asset The asset from which the data is consumed.
+     * @param consumerFunction The consumer that reads the data
+     * @param readLock If true, a read lock is acquired on the asset.
      * @throws IOException
      */
     void consumeData(StorageAsset asset, Consumer<InputStream> 
consumerFunction, boolean readLock) throws IOException;
 
     /**
+     * Consumes the data and sets a lock for the file during the operation.
+     *
+     * @param asset The asset from which the data is consumed.
+     * @param consumerFunction The consumer that reads the data
+     * @param readLock If true, a read lock is acquired on the asset.
+     * @throws IOException
+     */
+    void consumeDataFromChannel( StorageAsset asset, 
Consumer<ReadableByteChannel> consumerFunction, boolean readLock) throws 
IOException;
+
+    /**
+     * Writes data to the asset using a write lock.
+     *
+     * @param asset The asset to which the data is written.
+     * @param consumerFunction The function that provides the data.
+     * @param writeLock If true, a write lock is acquired on the destination.
+     */
+    void writeData( StorageAsset asset, Consumer<OutputStream> 
consumerFunction, boolean writeLock) throws IOException;;
+
+    /**
+     * Writes data and sets a lock during the operation.
+     *
+     * @param asset The asset to which the data is written.
+     * @param consumerFunction The function that provides the data.
+     * @param writeLock If true, a write lock is acquired on the destination.
+     * @throws IOException
+     */
+    void writeDataToChannel( StorageAsset asset, Consumer<WritableByteChannel> 
consumerFunction, boolean writeLock) throws IOException;
+
+    /**
      * Adds a new asset to the underlying storage.
      * @param path The path to the asset.
      * @param container True, if the asset should be a container, false, if it 
is a file.
@@ -73,19 +106,43 @@ public interface RepositoryStorage {
     /**
      * Moves the asset to the given location and returns the asset object for 
the destination.
      *
-     * @param origin
-     * @param destination
-     * @return
+     * @param origin The original asset
+     * @param destination The destination path pointing to the new asset.
+     * @param copyOptions The copy options.
+     * @return The asset representation of the moved object.
+     */
+    StorageAsset moveAsset(StorageAsset origin, String destination, 
CopyOption... copyOptions) throws IOException;
+
+    /**
+     * Moves the asset to the new path.
+     *
+     * @param origin The original asset
+     * @param destination The destination asset.
+     * @param copyOptions The copy options (e.g. {@link 
java.nio.file.StandardCopyOption#REPLACE_EXISTING}
+     * @throws IOException If it was not possible to copy the asset.
      */
-    StorageAsset moveAsset(StorageAsset origin, String destination) throws 
IOException;
+    void moveAsset(StorageAsset origin, StorageAsset destination, 
CopyOption... copyOptions) throws IOException;
 
     /**
      * Copies the given asset to the new destination.
      *
-     * @param origin
-     * @param destination
-     * @return
-     * @throws IOException
+     * @param origin The original asset
+     * @param destination The path to the new asset
+     * @param copyOptions The copy options, e.g. (e.g. {@link 
java.nio.file.StandardCopyOption#REPLACE_EXISTING}
+     * @return The asset representation of the copied object
+     * @throws IOException If it was not possible to copy the asset
      */
-    StorageAsset copyAsset(StorageAsset origin, String destination) throws 
IOException;
+    StorageAsset copyAsset(StorageAsset origin, String destination, 
CopyOption... copyOptions) throws IOException;
+
+    /**
+     * Copies the given asset to the new destination.
+     *
+     * @param origin The original asset
+     * @param destination The path to the new asset
+     * @param copyOptions The copy options, e.g. (e.g. {@link 
java.nio.file.StandardCopyOption#REPLACE_EXISTING}
+     * @throws IOException If it was not possible to copy the asset
+     */
+    void copyAsset( StorageAsset origin, StorageAsset destination, 
CopyOption... copyOptions) throws IOException;
+
+
 }
diff --git 
a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/StorageAsset.java
 
b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/StorageAsset.java
index 16e7085..38ef2a8 100644
--- 
a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/StorageAsset.java
+++ 
b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/content/StorageAsset.java
@@ -22,6 +22,8 @@ package org.apache.archiva.repository.content;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
 import java.nio.file.Path;
 import java.time.Instant;
 import java.util.List;
@@ -39,6 +41,13 @@ import java.util.function.Consumer;
  */
 public interface StorageAsset
 {
+
+    /**
+     * Returns the storage this asset belongs to.
+     * @return
+     */
+    RepositoryStorage getStorage();
+
     /**
      * Returns the complete path relative to the repository to the given asset.
      *
@@ -68,7 +77,7 @@ public interface StorageAsset
     /**
      * List the child assets.
      *
-     * @return The list of children. If there are no children, a empty list 
will be returned.
+     * @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();
 
@@ -82,22 +91,41 @@ public interface StorageAsset
     /**
      * Returns the input stream of the artifact content.
      * It will throw a IOException, if the stream could not be created.
-     * Implementations should create a new stream instance for each invocation.
+     * Implementations should create a new stream instance for each invocation 
and make sure that the
+     * stream is proper closed after usage.
      *
      * @return The InputStream representing the content of the artifact.
      * @throws IOException
      */
-    InputStream getData() throws IOException;
+    InputStream getReadStream() throws IOException;
+
+    /**
+     * Returns a NIO representation of the data.
+     *
+     * @return A channel to the asset data.
+     * @throws IOException
+     */
+    ReadableByteChannel getReadChannel() throws IOException;
 
     /**
      *
-     * Returns an output stream where you can write data to the asset.
+     * Returns an output stream where you can write data to the asset. The 
operation is not locked or synchronized.
+     * User of this method have to make sure, that the stream is proper closed 
after usage.
      *
      * @param replace If true, the original data will be replaced, otherwise 
the data will be appended.
      * @return The OutputStream where the data can be written.
      * @throws IOException
      */
-    OutputStream writeData( boolean replace) throws IOException;
+    OutputStream getWriteStream( boolean replace) throws IOException;
+
+    /**
+     * Returns a NIO representation of the asset where you can write the data.
+     *
+     * @param replace True, if the content should be replaced by the data 
written to the stream.
+     * @return The Channel for writing the data.
+     * @throws IOException
+     */
+    WritableByteChannel getWriteChannel( boolean replace) throws IOException;
 
     /**
      * Replaces the content. The implementation may do an atomic move 
operation, or keep a backup. If
@@ -107,7 +135,7 @@ public interface StorageAsset
      *
      * @param newData Replaces the data by the content of the given file.
      */
-    boolean storeDataFile( Path newData) throws IOException;
+    boolean replaceDataFromFile( Path newData) throws IOException;
 
     /**
      * Returns true, if the asset exists.
@@ -123,13 +151,22 @@ public interface StorageAsset
 
     /**
      * Returns the real path to the asset, if it exist. Not all 
implementations may implement this method.
+     * The method throws {@link UnsupportedOperationException}, if and only if 
{@link #isFileBased()} returns false.
      *
      * @return The filesystem path to the asset.
-     * @throws UnsupportedOperationException
+     * @throws UnsupportedOperationException If the underlying storage is not 
file based.
      */
     Path getFilePath() throws UnsupportedOperationException;
 
     /**
+     * Returns true, if the asset can return a file path for the given asset. 
If this is true, the  {@link #getFilePath()}
+     * will not throw a {@link UnsupportedOperationException}
+     *
+     * @return
+     */
+    boolean isFileBased();
+
+    /**
      * Returns true, if there is a parent to this asset.
      * @return
      */
diff --git 
a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/AbstractRepository.java
 
b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/AbstractRepository.java
index de57977..87b5420 100644
--- 
a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/AbstractRepository.java
+++ 
b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/AbstractRepository.java
@@ -35,7 +35,11 @@ import org.slf4j.LoggerFactory;
 
 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.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -348,24 +352,54 @@ public abstract class AbstractRepository implements 
EditableRepository, Reposito
     }
 
     @Override
-    public StorageAsset moveAsset( StorageAsset origin, String destination ) 
throws IOException
+    public StorageAsset moveAsset( StorageAsset origin, String destination, 
CopyOption... copyOptions ) throws IOException
     {
         return storage.moveAsset(origin, destination);
     }
 
     @Override
-    public StorageAsset copyAsset( StorageAsset origin, String destination ) 
throws IOException
+    public void moveAsset( StorageAsset origin, StorageAsset destination, 
CopyOption... copyOptions ) throws IOException
+    {
+        storage.moveAsset( origin, destination, copyOptions );
+    }
+
+    @Override
+    public StorageAsset copyAsset( StorageAsset origin, String destination, 
CopyOption... copyOptions ) throws IOException
     {
         return storage.copyAsset(origin, destination);
     }
 
     @Override
+    public void copyAsset( StorageAsset origin, StorageAsset destination, 
CopyOption... copyOptions ) throws IOException
+    {
+        storage.copyAsset( origin, destination, copyOptions);
+    }
+
+    @Override
     public void consumeData(StorageAsset asset, Consumer<InputStream> 
consumerFunction, boolean readLock ) throws IOException
     {
         storage.consumeData(asset, consumerFunction, readLock);
     }
 
-    protected void setStorage(RepositoryStorage storage) {
+    @Override
+    public void consumeDataFromChannel( StorageAsset asset, 
Consumer<ReadableByteChannel> consumerFunction, boolean readLock ) throws 
IOException
+    {
+        storage.consumeDataFromChannel( asset, consumerFunction, readLock );
+    }
+
+    @Override
+    public void writeData( StorageAsset asset, Consumer<OutputStream> 
consumerFunction, boolean writeLock ) throws IOException
+    {
+        storage.writeData( asset, consumerFunction, writeLock );
+    }
+
+    @Override
+    public void writeDataToChannel( StorageAsset asset, 
Consumer<WritableByteChannel> consumerFunction, boolean writeLock ) throws 
IOException
+    {
+        storage.writeDataToChannel( asset, consumerFunction, writeLock );
+    }
+
+    protected void setStorage( RepositoryStorage storage) {
         this.storage = storage;
     }
 
diff --git 
a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/RepositoryRegistry.java
 
b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/RepositoryRegistry.java
index 2b0ac68..e78130e 100644
--- 
a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/RepositoryRegistry.java
+++ 
b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/RepositoryRegistry.java
@@ -26,6 +26,8 @@ import 
org.apache.archiva.indexer.IndexCreationFailedException;
 import org.apache.archiva.indexer.IndexManagerFactory;
 import org.apache.archiva.indexer.IndexUpdateFailedException;
 import org.apache.archiva.redback.components.registry.RegistryException;
+import org.apache.archiva.repository.content.RepositoryStorage;
+import org.apache.archiva.repository.content.StorageAsset;
 import org.apache.archiva.repository.features.IndexCreationEvent;
 import org.apache.archiva.repository.features.IndexCreationFeature;
 import org.apache.archiva.repository.features.StagingRepositoryFeature;
@@ -1378,4 +1380,5 @@ public class RepositoryRegistry implements 
ConfigurationListener, RepositoryEven
             listener.raise(event);
         }
     }
+
 }
diff --git 
a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/FilesystemAsset.java
 
b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/FilesystemAsset.java
index 41b45df..e0d10f8 100644
--- 
a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/FilesystemAsset.java
+++ 
b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/FilesystemAsset.java
@@ -26,6 +26,9 @@ import org.slf4j.LoggerFactory;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
 import java.nio.file.*;
 import java.nio.file.attribute.*;
 import java.time.Instant;
@@ -85,15 +88,20 @@ public class FilesystemAsset implements StorageAsset {
     boolean supportsAcl = false;
     boolean supportsPosix = false;
     final boolean setPermissionsForNew;
+    final RepositoryStorage storage;
 
     boolean directoryHint = false;
 
+    private static final OpenOption[] REPLACE_OPTIONS = new 
OpenOption[]{StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE};
+    private static final OpenOption[] APPEND_OPTIONS = new 
OpenOption[]{StandardOpenOption.APPEND};
 
-    FilesystemAsset(String path, Path assetPath, Path basePath) {
+
+    FilesystemAsset(RepositoryStorage storage, String path, Path assetPath, 
Path basePath) {
         this.assetPath = assetPath;
         this.relativePath = path;
         this.setPermissionsForNew=false;
         this.basePath = basePath;
+        this.storage = storage;
         init();
     }
 
@@ -104,11 +112,12 @@ public class FilesystemAsset implements StorageAsset {
      * @param path The logical path for the asset relative to the repository.
      * @param assetPath The asset path.
      */
-    public FilesystemAsset(String path, Path assetPath) {
+    public FilesystemAsset(RepositoryStorage storage, String path, Path 
assetPath) {
         this.assetPath = assetPath;
         this.relativePath = path;
         this.setPermissionsForNew = false;
         this.basePath = null;
+        this.storage = storage;
         init();
     }
 
@@ -121,12 +130,13 @@ public class FilesystemAsset implements StorageAsset {
      * @param directory This is only relevant, if the represented file or 
directory does not exist yet and
      *                  is a hint.
      */
-    public FilesystemAsset(String path, Path assetPath, Path basePath, boolean 
directory) {
+    public FilesystemAsset(RepositoryStorage storage, String path, Path 
assetPath, Path basePath, boolean directory) {
         this.assetPath = assetPath;
         this.relativePath = path;
         this.directoryHint = directory;
         this.setPermissionsForNew = false;
         this.basePath = basePath;
+        this.storage = storage;
         init();
     }
 
@@ -139,12 +149,13 @@ public class FilesystemAsset implements StorageAsset {
      * @param directory This is only relevant, if the represented file or 
directory does not exist yet and
      *                  is a hint.
      */
-    public FilesystemAsset(String path, Path assetPath, Path basePath, boolean 
directory, boolean setPermissionsForNew) {
+    public FilesystemAsset(RepositoryStorage storage, String path, Path 
assetPath, Path basePath, boolean directory, boolean setPermissionsForNew) {
         this.assetPath = assetPath;
         this.relativePath = path;
         this.directoryHint = directory;
         this.setPermissionsForNew = setPermissionsForNew;
         this.basePath = basePath;
+        this.storage = storage;
         init();
     }
 
@@ -191,6 +202,12 @@ public class FilesystemAsset implements StorageAsset {
 
 
     @Override
+    public RepositoryStorage getStorage( )
+    {
+        return storage;
+    }
+
+    @Override
     public String getPath() {
         return relativePath;
     }
@@ -233,7 +250,7 @@ public class FilesystemAsset implements StorageAsset {
     @Override
     public List<StorageAsset> list() {
         try {
-            return Files.list(assetPath).map(p -> new 
FilesystemAsset(relativePath + "/" + p.getFileName().toString(), 
assetPath.resolve(p)))
+            return Files.list(assetPath).map(p -> new FilesystemAsset(storage, 
relativePath + "/" + p.getFileName().toString(), assetPath.resolve(p)))
                     .collect(Collectors.toList());
         } catch (IOException e) {
             return Collections.EMPTY_LIST;
@@ -262,7 +279,7 @@ public class FilesystemAsset implements StorageAsset {
      * @throws IOException
      */
     @Override
-    public InputStream getData() throws IOException {
+    public InputStream getReadStream() throws IOException {
         if (isContainer()) {
             throw new IOException("Can not create input stream for container");
         }
@@ -270,13 +287,18 @@ public class FilesystemAsset implements StorageAsset {
     }
 
     @Override
-    public OutputStream writeData(boolean replace) throws IOException {
-        OpenOption[] options;
-        if (replace) {
-            options = new OpenOption[]{StandardOpenOption.TRUNCATE_EXISTING, 
StandardOpenOption.CREATE};
-        } else {
-            options = new OpenOption[]{StandardOpenOption.APPEND};
-        }
+    public ReadableByteChannel getReadChannel( ) throws IOException
+    {
+        return FileChannel.open( assetPath, StandardOpenOption.READ );
+    }
+
+    private OpenOption[] getOpenOptions(boolean replace) {
+        return replace ? REPLACE_OPTIONS : APPEND_OPTIONS;
+    }
+
+    @Override
+    public OutputStream getWriteStream( boolean replace) throws IOException {
+        OpenOption[] options = getOpenOptions( replace );
         if (!Files.exists( assetPath )) {
             create();
         }
@@ -284,7 +306,14 @@ public class FilesystemAsset implements StorageAsset {
     }
 
     @Override
-    public boolean storeDataFile(Path newData) throws IOException {
+    public WritableByteChannel getWriteChannel( boolean replace ) throws 
IOException
+    {
+        OpenOption[] options = getOpenOptions( replace );
+        return FileChannel.open( assetPath, options );
+    }
+
+    @Override
+    public boolean replaceDataFromFile( Path newData) throws IOException {
         final boolean createNew = !Files.exists(assetPath);
         Path backup = null;
         if (!createNew) {
@@ -362,6 +391,12 @@ public class FilesystemAsset implements StorageAsset {
     }
 
     @Override
+    public boolean isFileBased( )
+    {
+        return true;
+    }
+
+    @Override
     public boolean hasParent( )
     {
         if (basePath!=null && assetPath.equals(basePath)) {
@@ -382,7 +417,7 @@ public class FilesystemAsset implements StorageAsset {
         }
         String relativeParent = StringUtils.substringBeforeLast( 
relativePath,"/");
         if (parentPath!=null) {
-            return new FilesystemAsset( relativeParent, parentPath, basePath, 
true, setPermissionsForNew );
+            return new FilesystemAsset(storage, relativeParent, parentPath, 
basePath, true, setPermissionsForNew );
         } else {
             return null;
         }
diff --git 
a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/FilesystemStorage.java
 
b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/FilesystemStorage.java
index 344ebe6..92044fa 100644
--- 
a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/FilesystemStorage.java
+++ 
b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/FilesystemStorage.java
@@ -30,9 +30,16 @@ import org.slf4j.LoggerFactory;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
+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.nio.file.Path;
 import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
 import java.util.function.Consumer;
 
 /**
@@ -100,6 +107,85 @@ public class FilesystemStorage implements 
RepositoryStorage {
 
     }
 
+    @Override
+    public void consumeDataFromChannel( StorageAsset asset, 
Consumer<ReadableByteChannel> consumerFunction, boolean readLock ) throws 
IOException
+    {
+        final Path path = asset.getFilePath();
+        try {
+            if (readLock) {
+                consumeDataFromChannelLocked( path, consumerFunction );
+            } else
+            {
+                try ( FileChannel is = FileChannel.open( path, 
StandardOpenOption.READ ) )
+                {
+                    consumerFunction.accept( is );
+                }
+                catch ( IOException e )
+                {
+                    log.error("Could not read the input stream from file {}", 
path);
+                    throw e;
+                }
+            }
+        } catch (RuntimeException e)
+        {
+            log.error( "Runtime exception during data consume from artifact 
{}. Error: {}", path, e.getMessage() );
+            throw new IOException( e );
+        }
+    }
+
+    @Override
+    public void writeData( StorageAsset asset, Consumer<OutputStream> 
consumerFunction, boolean writeLock ) throws IOException
+    {
+        final Path path = asset.getFilePath();
+        try {
+            if (writeLock) {
+                writeDataLocked( path, consumerFunction );
+            } else
+            {
+                try ( OutputStream is = Files.newOutputStream( path ) )
+                {
+                    consumerFunction.accept( is );
+                }
+                catch ( IOException e )
+                {
+                    log.error("Could not write the output stream to file {}", 
path);
+                    throw e;
+                }
+            }
+        } catch (RuntimeException e)
+        {
+            log.error( "Runtime exception during data consume from artifact 
{}. Error: {}", path, e.getMessage() );
+            throw new IOException( e );
+        }
+
+    }
+
+    @Override
+    public void writeDataToChannel( StorageAsset asset, 
Consumer<WritableByteChannel> consumerFunction, boolean writeLock ) throws 
IOException
+    {
+        final Path path = asset.getFilePath();
+        try {
+            if (writeLock) {
+                writeDataToChannelLocked( path, consumerFunction );
+            } else
+            {
+                try ( FileChannel os = FileChannel.open( path, 
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, 
StandardOpenOption.CREATE ))
+                {
+                    consumerFunction.accept( os );
+                }
+                catch ( IOException e )
+                {
+                    log.error("Could not write the data to file {}", path);
+                    throw e;
+                }
+            }
+        } catch (RuntimeException e)
+        {
+            log.error( "Runtime exception during data consume from artifact 
{}. Error: {}", path, e.getMessage() );
+            throw new IOException( e );
+        }
+    }
+
     private void consumeDataLocked( Path file, Consumer<InputStream> 
consumerFunction) throws IOException
     {
 
@@ -127,12 +213,93 @@ public class FilesystemStorage implements 
RepositoryStorage {
         }
     }
 
+    private void consumeDataFromChannelLocked( Path file, 
Consumer<ReadableByteChannel> consumerFunction) throws IOException
+    {
+
+        final Lock lock;
+        try
+        {
+            lock = fileLockManager.readFileLock( file );
+            try ( FileChannel is = FileChannel.open( lock.getFile( ), 
StandardOpenOption.READ ))
+            {
+                consumerFunction.accept( is );
+            }
+            catch ( IOException e )
+            {
+                log.error("Could not read the input stream from file {}", 
file);
+                throw e;
+            } finally
+            {
+                fileLockManager.release( lock );
+            }
+        }
+        catch ( FileLockException | FileNotFoundException | 
FileLockTimeoutException e)
+        {
+            log.error("Locking error on file {}", file);
+            throw new IOException(e);
+        }
+    }
+
+
+    private void writeDataLocked( Path file, Consumer<OutputStream> 
consumerFunction) throws IOException
+    {
+
+        final Lock lock;
+        try
+        {
+            lock = fileLockManager.writeFileLock( file );
+            try ( OutputStream is = Files.newOutputStream( lock.getFile()))
+            {
+                consumerFunction.accept( is );
+            }
+            catch ( IOException e )
+            {
+                log.error("Could not write the output stream to file {}", 
file);
+                throw e;
+            } finally
+            {
+                fileLockManager.release( lock );
+            }
+        }
+        catch ( FileLockException | FileNotFoundException | 
FileLockTimeoutException e)
+        {
+            log.error("Locking error on file {}", file);
+            throw new IOException(e);
+        }
+    }
+
+    private void writeDataToChannelLocked( Path file, 
Consumer<WritableByteChannel> consumerFunction) throws IOException
+    {
+
+        final Lock lock;
+        try
+        {
+            lock = fileLockManager.writeFileLock( file );
+            try ( FileChannel is = FileChannel.open( lock.getFile( ), 
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, 
StandardOpenOption.CREATE ))
+            {
+                consumerFunction.accept( is );
+            }
+            catch ( IOException e )
+            {
+                log.error("Could not write to file {}", file);
+                throw e;
+            } finally
+            {
+                fileLockManager.release( lock );
+            }
+        }
+        catch ( FileLockException | FileNotFoundException | 
FileLockTimeoutException e)
+        {
+            log.error("Locking error on file {}", file);
+            throw new IOException(e);
+        }
+    }
 
     @Override
     public StorageAsset getAsset( String path )
     {
         try {
-            return new FilesystemAsset( path, getAssetPath(path));
+            return new FilesystemAsset(this, path, getAssetPath(path));
         } catch (IOException e) {
             throw new IllegalArgumentException("Path navigates outside of base 
directory "+path);
         }
@@ -142,7 +309,7 @@ public class FilesystemStorage implements RepositoryStorage 
{
     public StorageAsset addAsset( String path, boolean container )
     {
         try {
-            return new FilesystemAsset( path, getAssetPath(path), basePath, 
container);
+            return new FilesystemAsset(this, path, getAssetPath(path), 
basePath, container);
         } catch (IOException e) {
             throw new IllegalArgumentException("Path navigates outside of base 
directory "+path);
         }
@@ -155,29 +322,51 @@ public class FilesystemStorage implements 
RepositoryStorage {
     }
 
     @Override
-    public StorageAsset moveAsset( StorageAsset origin, String destination ) 
throws IOException
+    public StorageAsset moveAsset( StorageAsset origin, String destination, 
CopyOption... copyOptions ) throws IOException
     {
         boolean container = origin.isContainer();
-        FilesystemAsset newAsset = new FilesystemAsset( destination, 
getAssetPath(destination), basePath, container );
-        Files.move(origin.getFilePath(), newAsset.getFilePath());
+        FilesystemAsset newAsset = new FilesystemAsset(this, destination, 
getAssetPath(destination), basePath, container );
+        moveAsset( origin, newAsset, copyOptions );
         return newAsset;
     }
 
     @Override
-    public StorageAsset copyAsset( StorageAsset origin, String destination ) 
throws IOException
+    public void moveAsset( StorageAsset origin, StorageAsset destination, 
CopyOption... copyOptions ) throws IOException
+    {
+        Files.move(origin.getFilePath(), destination.getFilePath(), 
copyOptions);
+    }
+
+    @Override
+    public StorageAsset copyAsset( StorageAsset origin, String destination, 
CopyOption... copyOptions ) throws IOException
     {
         boolean container = origin.isContainer();
-        FilesystemAsset newAsset = new FilesystemAsset( destination, 
getAssetPath(destination), basePath, container );
-        if (Files.exists(newAsset.getFilePath())) {
-            throw new IOException("Destination file exists already "+ 
newAsset.getFilePath());
+        FilesystemAsset newAsset = new FilesystemAsset(this, destination, 
getAssetPath(destination), basePath, container );
+        copyAsset( origin, newAsset, copyOptions );
+        return newAsset;
+    }
+
+    @Override
+    public void copyAsset( StorageAsset origin, StorageAsset destination, 
CopyOption... copyOptions ) throws IOException
+    {
+        Path destinationPath = destination.getFilePath();
+        boolean overwrite = false;
+        for (int i=0; i<copyOptions.length; i++) {
+            if (copyOptions[i].equals( StandardCopyOption.REPLACE_EXISTING )) {
+                overwrite=true;
+            }
+        }
+        if (Files.exists(destinationPath) && !overwrite) {
+            throw new IOException("Destination file exists already "+ 
destinationPath);
         }
         if (Files.isDirectory( origin.getFilePath() ))
         {
-            FileUtils.copyDirectory(origin.getFilePath( ).toFile(), 
newAsset.getFilePath( ).toFile() );
+            FileUtils.copyDirectory(origin.getFilePath( ).toFile(), 
destinationPath.toFile() );
         } else if (Files.isRegularFile( origin.getFilePath() )) {
-            FileUtils.copyFile(origin.getFilePath( ).toFile(), 
newAsset.getFilePath( ).toFile() );
+            if (!Files.exists( destinationPath )) {
+                Files.createDirectories( destinationPath );
+            }
+            Files.copy( origin.getFilePath( ), destinationPath, copyOptions );
         }
-        return newAsset;
     }
 
     public FileLockManager getFileLockManager() {
diff --git 
a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/StorageUtil.java
 
b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/StorageUtil.java
new file mode 100644
index 0000000..4b8d11b
--- /dev/null
+++ 
b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/StorageUtil.java
@@ -0,0 +1,192 @@
+package org.apache.archiva.repository.content;
+
+/*
+ * 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 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.nio.file.Path;
+
+/**
+ * @author Martin Stockhammer <[email protected]>
+ */
+public class StorageUtil
+{
+    private static final int DEFAULT_BUFFER_SIZE = 4096;
+
+    /**
+     * Copies the source asset to the target. The assets may be from different 
RepositoryStorage instances.
+     *
+     * @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();
+                try (Lock lockRead = lmSource.readFileLock( sourcePath ); Lock 
lockWrite = lmTarget.writeFileLock( targetPath ) )
+                {
+                    Files.copy( sourcePath, targetPath, copyOptions );
+                }
+                catch ( FileLockException e )
+                {
+                    throw new IOException( e );
+                }
+                catch ( FileLockTimeoutException e )
+                {
+                    throw new IOException( e );
+                }
+            } 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 );
+                }
+            }
+        }
+    }
+
+    /**
+     *
+     * @param source
+     * @param target
+     * @param locked
+     * @param copyOptions
+     * @throws IOException
+     */
+    public static 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
+            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 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 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 void copy( final FileChannel is, final WritableByteChannel 
os ) {
+        try
+        {
+            is.transferTo( 0, is.size( ), os );
+        }
+        catch ( IOException e )
+        {
+            throw new RuntimeException( e );
+        }
+    }
+
+    private static void copy( final ReadableByteChannel is, final FileChannel 
os ) {
+        try
+        {
+            os.transferFrom( is, 0, Long.MAX_VALUE );
+        }
+        catch ( IOException e )
+        {
+            throw new RuntimeException( e );
+        }
+    }
+
+}
diff --git 
a/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/content/FilesystemAssetTest.java
 
b/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/content/FilesystemAssetTest.java
index b06546a..8e98e59 100644
--- 
a/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/content/FilesystemAssetTest.java
+++ 
b/archiva-modules/archiva-base/archiva-repository-layer/src/test/java/org/apache/archiva/repository/content/FilesystemAssetTest.java
@@ -25,7 +25,6 @@ import org.junit.Before;
 import org.junit.Test;
 
 import java.io.*;
-import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -124,7 +123,7 @@ public class FilesystemAssetTest {
     public void getData() throws IOException {
         FilesystemAsset asset = new FilesystemAsset("/test1234", 
assetPathFile);
         Files.write(assetPathFile, "abcdef".getBytes("ASCII"));
-        try(InputStream is = asset.getData()) {
+        try(InputStream is = asset.getReadStream()) {
             assertEquals("abcdef", IOUtils.toString(is, "ASCII"));
         }
 
@@ -135,7 +134,7 @@ public class FilesystemAssetTest {
         FilesystemAsset asset = new FilesystemAsset("/test1234", assetPathDir);
         Files.write(assetPathFile, "abcdef".getBytes("ASCII"));
         try {
-            InputStream is = asset.getData();
+            InputStream is = asset.getReadStream();
             assertFalse("Exception expected for data on dir", true);
         } catch (IOException e) {
             // fine
@@ -147,7 +146,7 @@ public class FilesystemAssetTest {
     public void writeData() throws IOException {
         FilesystemAsset asset = new FilesystemAsset("/test1234", 
assetPathFile);
         Files.write(assetPathFile, "abcdef".getBytes("ASCII"));
-        try(OutputStream os  = asset.writeData(true)) {
+        try(OutputStream os  = asset.getWriteStream(true)) {
             IOUtils.write("test12345", os, "ASCII");
         }
         assertEquals("test12345", 
IOUtils.toString(assetPathFile.toUri().toURL(), "ASCII"));
@@ -157,7 +156,7 @@ public class FilesystemAssetTest {
     public void writeDataAppend() throws IOException {
         FilesystemAsset asset = new FilesystemAsset("/test1234", 
assetPathFile);
         Files.write(assetPathFile, "abcdef".getBytes("ASCII"));
-        try(OutputStream os  = asset.writeData(false)) {
+        try(OutputStream os  = asset.getWriteStream(false)) {
             IOUtils.write("test12345", os, "ASCII");
         }
         assertEquals("abcdeftest12345", 
IOUtils.toString(assetPathFile.toUri().toURL(), "ASCII"));
@@ -168,7 +167,7 @@ public class FilesystemAssetTest {
         FilesystemAsset asset = new FilesystemAsset("/test1234", assetPathDir);
         try {
 
-            OutputStream os = asset.writeData(true);
+            OutputStream os = asset.getWriteStream(true);
             assertTrue("Writing to a directory should throw a IOException", 
false);
         } catch (IOException e) {
             // Fine
@@ -182,7 +181,7 @@ public class FilesystemAssetTest {
         try(OutputStream os = Files.newOutputStream(dataFile)) {
             IOUtils.write("testkdkdkd", os, "ASCII");
         }
-        asset.storeDataFile(dataFile);
+        asset.replaceDataFromFile(dataFile);
         assertEquals("testkdkdkd", 
IOUtils.toString(assetPathFile.toUri().toURL(), "ASCII"));
     }
 
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 0eb574e..328f58a 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
@@ -31,11 +31,7 @@ import org.apache.archiva.repository.RepositoryException;
 import org.apache.archiva.repository.content.StorageAsset;
 import org.springframework.stereotype.Service;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Path;
 import java.util.Set;
-import java.util.function.Consumer;
 
 /**
  * @author Martin Stockhammer <[email protected]>
@@ -136,13 +132,13 @@ public class ManagedRepositoryContentMock implements 
ManagedRepositoryContent
     }
 
     @Override
-    public Path toFile( ArtifactReference reference )
+    public StorageAsset toFile( ArtifactReference reference )
     {
         return null;
     }
 
     @Override
-    public Path toFile( ArchivaArtifact reference )
+    public StorageAsset toFile( ArchivaArtifact reference )
     {
         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 e814a49..ade8121 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
@@ -27,10 +27,9 @@ import org.apache.archiva.model.ArtifactReference;
 import org.apache.archiva.model.ProjectReference;
 import org.apache.archiva.model.VersionedReference;
 import org.apache.archiva.repository.*;
-import org.apache.archiva.repository.content.FilesystemStorage;
+import org.apache.archiva.repository.content.StorageAsset;
 import org.apache.commons.lang.StringUtils;
 
-import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.Map;
@@ -328,13 +327,13 @@ public class ManagedRepositoryContentMock implements 
ManagedRepositoryContent
 
 
     @Override
-    public Path toFile( ArtifactReference reference )
+    public StorageAsset toFile( ArtifactReference reference )
     {
         return Paths.get(getRepoRoot(), refs.get(reference));
     }
 
     @Override
-    public Path toFile( ArchivaArtifact reference )
+    public StorageAsset toFile( ArchivaArtifact reference )
     {
         return null;
     }
diff --git 
a/archiva-modules/archiva-maven/archiva-maven-proxy/src/main/java/org/apache/archiva/proxy/maven/MavenRepositoryProxyHandler.java
 
b/archiva-modules/archiva-maven/archiva-maven-proxy/src/main/java/org/apache/archiva/proxy/maven/MavenRepositoryProxyHandler.java
index b59d998..9c383c3 100644
--- 
a/archiva-modules/archiva-maven/archiva-maven-proxy/src/main/java/org/apache/archiva/proxy/maven/MavenRepositoryProxyHandler.java
+++ 
b/archiva-modules/archiva-maven/archiva-maven-proxy/src/main/java/org/apache/archiva/proxy/maven/MavenRepositoryProxyHandler.java
@@ -28,6 +28,7 @@ import org.apache.archiva.proxy.ProxyException;
 import org.apache.archiva.proxy.model.NetworkProxy;
 import org.apache.archiva.proxy.model.ProxyConnector;
 import org.apache.archiva.repository.*;
+import org.apache.archiva.repository.content.StorageAsset;
 import org.apache.commons.lang.StringUtils;
 import org.apache.maven.wagon.ConnectionException;
 import org.apache.maven.wagon.ResourceDoesNotExistException;
@@ -101,9 +102,8 @@ public class MavenRepositoryProxyHandler extends 
DefaultRepositoryProxyHandler {
     /**
      * @param connector
      * @param remoteRepository
-     * @param tmpMd5
-     * @param tmpSha1
      * @param tmpResource
+     * @param checksumFiles
      * @param url
      * @param remotePath
      * @param resource
@@ -112,9 +112,9 @@ public class MavenRepositoryProxyHandler extends 
DefaultRepositoryProxyHandler {
      * @throws ProxyException
      * @throws NotModifiedException
      */
-    protected void transferResources(ProxyConnector connector, 
RemoteRepositoryContent remoteRepository, Path tmpMd5,
-                                     Path tmpSha1, Path tmpResource, String 
url, String remotePath, Path resource,
-                                     Path workingDirectory, 
ManagedRepositoryContent repository)
+    protected void transferResources( ProxyConnector connector, 
RemoteRepositoryContent remoteRepository,
+                                      Path tmpResource, Path[] checksumFiles, 
String url, String remotePath, StorageAsset resource,
+                                      Path workingDirectory, 
ManagedRepositoryContent repository )
             throws ProxyException, NotModifiedException {
         Wagon wagon = null;
         try {
@@ -146,16 +146,17 @@ public class MavenRepositoryProxyHandler extends 
DefaultRepositoryProxyHandler {
 
             boolean connected = connectToRepository(connector, wagon, 
remoteRepository);
             if (connected) {
-                transferArtifact(wagon, remoteRepository, remotePath, 
repository, resource, workingDirectory,
+                transferArtifact(wagon, remoteRepository, remotePath, 
repository, resource.getFilePath(), workingDirectory,
                         tmpResource);
 
                 // TODO: these should be used to validate the download based 
on the policies, not always downloaded
                 // to
                 // save on connections since md5 is rarely used
-                transferChecksum(wagon, remoteRepository, remotePath, 
repository, resource, workingDirectory, ".sha1",
-                        tmpSha1);
-                transferChecksum(wagon, remoteRepository, remotePath, 
repository, resource, workingDirectory, ".md5",
-                        tmpMd5);
+                for (int i=0; i<checksumFiles.length; i++) {
+                    String ext = "."+StringUtils.substringAfterLast( 
checksumFiles[i].getFileName( ).toString( ), "." );
+                    transferChecksum(wagon, remoteRepository, remotePath, 
repository, resource.getFilePath(), ext,
+                        checksumFiles[i]);
+                }
             }
         } catch (NotFoundException e) {
             urlFailureCache.cacheFailure(url);
@@ -196,13 +197,12 @@ public class MavenRepositoryProxyHandler extends 
DefaultRepositoryProxyHandler {
      * @param remotePath       the remote path to the resource to get.
      * @param repository       the managed repository that will hold the file
      * @param resource         the local file that should contain the 
downloaded contents
-     * @param tmpDirectory     the temporary directory to download to
      * @param ext              the type of checksum to transfer (example: 
".md5" or ".sha1")
      * @throws ProxyException if copying the downloaded file into place did 
not succeed.
      */
-    protected void transferChecksum(Wagon wagon, RemoteRepositoryContent 
remoteRepository, String remotePath,
-                                    ManagedRepositoryContent repository, Path 
resource, Path tmpDirectory, String ext,
-                                    Path destFile)
+    protected void transferChecksum( Wagon wagon, RemoteRepositoryContent 
remoteRepository, String remotePath,
+                                     ManagedRepositoryContent repository, Path 
resource, String ext,
+                                     Path destFile )
             throws ProxyException {
         String url = remoteRepository.getURL().getUrl() + remotePath + ext;
 
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 2f10b16..61ad3d3 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
@@ -27,19 +27,14 @@ import org.apache.archiva.model.ArtifactReference;
 import org.apache.archiva.model.ProjectReference;
 import org.apache.archiva.model.VersionedReference;
 import org.apache.archiva.repository.*;
-import org.apache.archiva.repository.content.PathParser;
 import org.apache.archiva.repository.content.StorageAsset;
 import org.apache.commons.lang.StringUtils;
 import org.springframework.stereotype.Service;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Consumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -334,13 +329,13 @@ public class ManagedRepositoryContentMock implements 
ManagedRepositoryContent
 
 
     @Override
-    public Path toFile( ArtifactReference reference )
+    public StorageAsset toFile( ArtifactReference reference )
     {
         return Paths.get(getRepoRoot(), refs.get(reference));
     }
 
     @Override
-    public Path toFile( ArchivaArtifact reference )
+    public StorageAsset toFile( ArchivaArtifact reference )
     {
         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 01e429e..9c36647 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
@@ -19,10 +19,7 @@ package org.apache.archiva.repository.content.maven2;
  * 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.archiva.common.utils.PathUtil;
 import org.apache.archiva.configuration.FileTypes;
 import 
org.apache.archiva.metadata.repository.storage.maven2.ArtifactMappingProvider;
@@ -37,15 +34,10 @@ import org.apache.archiva.repository.LayoutException;
 import org.apache.archiva.repository.ManagedRepository;
 import org.apache.archiva.repository.ManagedRepositoryContent;
 import org.apache.archiva.repository.RepositoryException;
-import org.apache.archiva.repository.content.FilesystemAsset;
-import org.apache.archiva.repository.content.FilesystemStorage;
 import org.apache.archiva.repository.content.StorageAsset;
-import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang.StringUtils;
 
-import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStream;
 import java.net.URI;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -55,7 +47,6 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
-import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -202,30 +193,30 @@ public class ManagedDefaultRepositoryContent
     public Set<ArtifactReference> getRelatedArtifacts( ArtifactReference 
reference )
         throws ContentNotFoundException
     {
-        Path artifactFile = toFile( reference );
-        Path repoBase = 
PathUtil.getPathFromUri(repository.getLocation()).toAbsolutePath();
-        Path repoDir = artifactFile.getParent().toAbsolutePath();
+        StorageAsset artifactFile = toFile( reference );
+        StorageAsset repoBase = repository.getAsset( "" );
+        StorageAsset repoDir = artifactFile.getParent();
 
-        if ( !Files.exists(repoDir))
+        if ( !repoDir.exists())
         {
             throw new ContentNotFoundException(
-                "Unable to get related artifacts using a non-existant 
directory: " + repoDir.toAbsolutePath() );
+                "Unable to get related artifacts using a non-existant 
directory: " + repoDir.getPath() );
         }
 
-        if ( !Files.isDirectory( repoDir ) )
+        if ( !repoDir.isContainer() )
         {
             throw new ContentNotFoundException(
-                "Unable to get related artifacts using a non-directory: " + 
repoDir.toAbsolutePath() );
+                "Unable to get related artifacts using a non-directory: " + 
repoDir.getPath() );
         }
 
         Set<ArtifactReference> foundArtifacts;
 
         // First gather up the versions found as artifacts in the managed 
repository.
 
-        try (Stream<Path> stream = Files.list(repoDir)) {
-            foundArtifacts = stream.filter(Files::isRegularFile).map(path -> {
+        try (Stream<StorageAsset> stream = repoDir.list().stream() ) {
+            foundArtifacts = stream.filter(asset -> 
!asset.isContainer()).map(path -> {
                 try {
-                    ArtifactReference artifact = 
toArtifactReference(repoBase.relativize(path).toString());
+                    ArtifactReference artifact = 
toArtifactReference(path.getPath());
                     if( artifact.getGroupId().equals( reference.getGroupId() ) 
&& artifact.getArtifactId().equals(
                             reference.getArtifactId() ) && 
artifact.getVersion().equals( reference.getVersion() )) {
                         return artifact;
@@ -237,9 +228,6 @@ public class ManagedDefaultRepositoryContent
                     return null;
                 }
             }).filter(Objects::nonNull).collect(Collectors.toSet());
-        } catch (IOException e) {
-            log.error("Could not read directory {}: {}", repoDir, 
e.getMessage(), e);
-            return Collections.emptySet();
         }
         return foundArtifacts;
     }
@@ -379,8 +367,8 @@ public class ManagedDefaultRepositoryContent
     @Override
     public boolean hasContent( ArtifactReference reference )
     {
-        Path artifactFile = toFile( reference );
-        return Files.exists(artifactFile) && Files.isRegularFile( artifactFile 
);
+        StorageAsset artifactFile = toFile( reference );
+        return artifactFile.exists() && !artifactFile.isContainer();
     }
 
     @Override
@@ -454,15 +442,15 @@ public class ManagedDefaultRepositoryContent
 
 
     @Override
-    public Path toFile( ArtifactReference reference )
+    public StorageAsset toFile( ArtifactReference reference )
     {
-        return PathUtil.getPathFromUri( repository.getLocation()).resolve( 
toPath( reference ) );
+        return repository.getAsset(toPath(reference));
     }
 
     @Override
-    public Path toFile( ArchivaArtifact reference )
+    public StorageAsset toFile( ArchivaArtifact reference )
     {
-        return PathUtil.getPathFromUri( repository.getLocation()).resolve( 
toPath( reference ) );
+        return repository.getAsset( toPath( reference ) );
     }
 
     /**
diff --git 
a/archiva-modules/archiva-scheduler/archiva-scheduler-repository-api/src/main/java/org/apache/archiva/scheduler/repository/model/RepositoryTask.java
 
b/archiva-modules/archiva-scheduler/archiva-scheduler-repository-api/src/main/java/org/apache/archiva/scheduler/repository/model/RepositoryTask.java
index b93fe5a..a858c1a 100644
--- 
a/archiva-modules/archiva-scheduler/archiva-scheduler-repository-api/src/main/java/org/apache/archiva/scheduler/repository/model/RepositoryTask.java
+++ 
b/archiva-modules/archiva-scheduler/archiva-scheduler-repository-api/src/main/java/org/apache/archiva/scheduler/repository/model/RepositoryTask.java
@@ -1,6 +1,7 @@
 package org.apache.archiva.scheduler.repository.model;
 
 import org.apache.archiva.redback.components.taskqueue.Task;
+import org.apache.archiva.repository.content.StorageAsset;
 
 import java.nio.file.Path;
 
@@ -33,7 +34,7 @@ public class RepositoryTask
 {
     private String repositoryId;
 
-    private Path resourceFile;
+    private StorageAsset resourceFile;
 
     private boolean updateRelatedArtifacts;
 
@@ -81,12 +82,12 @@ public class RepositoryTask
         return 0;
     }
 
-    public Path getResourceFile()
+    public StorageAsset getResourceFile()
     {
         return resourceFile;
     }
 
-    public void setResourceFile( Path resourceFile )
+    public void setResourceFile( StorageAsset resourceFile )
     {
         this.resourceFile = resourceFile;
     }
diff --git 
a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/main/java/org/apache/archiva/scheduler/repository/ArchivaRepositoryScanningTaskExecutor.java
 
b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/main/java/org/apache/archiva/scheduler/repository/ArchivaRepositoryScanningTaskExecutor.java
index c961a8d..ea2cb26 100644
--- 
a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/main/java/org/apache/archiva/scheduler/repository/ArchivaRepositoryScanningTaskExecutor.java
+++ 
b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/main/java/org/apache/archiva/scheduler/repository/ArchivaRepositoryScanningTaskExecutor.java
@@ -115,7 +115,10 @@ public class ArchivaRepositoryScanningTaskExecutor
             if ( task.getResourceFile() != null )
             {
                 log.debug( "Executing task from queue with job name: {}", task 
);
-                consumers.executeConsumers( arepo, task.getResourceFile(), 
task.isUpdateRelatedArtifacts() );
+                if (task.getResourceFile().isFileBased())
+                {
+                    consumers.executeConsumers( arepo, task.getResourceFile( 
).getFilePath(), task.isUpdateRelatedArtifacts( ) );
+                }
             }
             else
             {
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 3c3cc71..f3dd255 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
@@ -54,6 +54,9 @@ import org.apache.archiva.repository.ManagedRepositoryContent;
 import org.apache.archiva.repository.RepositoryException;
 import org.apache.archiva.repository.RepositoryNotFoundException;
 import org.apache.archiva.repository.RepositoryRegistry;
+import org.apache.archiva.repository.content.RepositoryStorage;
+import org.apache.archiva.repository.content.StorageAsset;
+import org.apache.archiva.repository.content.StorageUtil;
 import org.apache.archiva.repository.events.RepositoryListener;
 import org.apache.archiva.repository.metadata.MetadataTools;
 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
@@ -384,9 +387,9 @@ public class DefaultRepositoriesService
                                                        null );
             }
 
-            Path artifactFile = source.getLocalPath().resolve( 
artifactSourcePath );
+            StorageAsset artifactFile = source.getAsset( artifactSourcePath );
 
-            if ( !Files.exists(artifactFile) )
+            if ( !artifactFile.exists() )
             {
                 log.error( "cannot find artifact {}", artifactTransferRequest 
);
                 throw new ArchivaRestServiceException( "cannot find artifact " 
+ artifactTransferRequest.toString(),
@@ -401,18 +404,19 @@ public class DefaultRepositoriesService
             int lastIndex = artifactPath.lastIndexOf( '/' );
 
             String path = artifactPath.substring( 0, lastIndex );
-            Path targetPath = target.getLocalPath().resolve( path );
+            StorageAsset targetPath = target.getAsset( path );
 
             Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
             int newBuildNumber = 1;
             String timestamp = null;
 
-            Path versionMetadataFile = targetPath.resolve( 
MetadataTools.MAVEN_METADATA );
+            StorageAsset versionMetadataFile = target.getAsset(path + "/" + 
MetadataTools.MAVEN_METADATA );
             /* unused */ getMetadata( versionMetadataFile );
 
-            if ( !Files.exists(targetPath) )
+            if ( !targetPath.exists() )
             {
-                Files.createDirectories( targetPath );
+                targetPath = target.addAsset(targetPath.getPath(), true);
+                targetPath.create();
             }
 
             String filename = artifactPath.substring( lastIndex + 1 );
@@ -420,8 +424,8 @@ public class DefaultRepositoriesService
             boolean fixChecksums =
                 !( archivaAdministration.getKnownContentConsumers().contains( 
"create-missing-checksums" ) );
 
-            Path targetFile = targetPath.resolve( filename );
-            if ( Files.exists(targetFile) && target.blocksRedeployments())
+            StorageAsset targetFile = target.getAsset(targetPath + "/" + 
filename );
+            if ( targetFile.exists() && target.blocksRedeployments())
             {
                 throw new ArchivaRestServiceException(
                     "artifact already exists in target repo: " + 
artifactTransferRequest.getTargetRepositoryId()
@@ -430,7 +434,7 @@ public class DefaultRepositoriesService
             }
             else
             {
-                copyFile( artifactFile, targetPath, filename, fixChecksums );
+                copyFile( source, artifactFile, target, targetFile, 
fixChecksums );
                 queueRepositoryTask( target.getId(), targetFile );
             }
 
@@ -442,13 +446,14 @@ public class DefaultRepositoriesService
             }
             pomFilename = FilenameUtils.removeExtension( pomFilename ) + 
".pom";
 
-            Path pomFile = source.getLocalPath().resolve(
-                artifactSourcePath.substring( 0, artifactPath.lastIndexOf( '/' 
) )).resolve( pomFilename );
+            StorageAsset pomFile = source.getAsset(
+                artifactSourcePath.substring( 0, artifactPath.lastIndexOf( '/' 
) )+"/"+ pomFilename );
 
-            if ( pomFile != null && Files.size( pomFile ) > 0 )
+            if ( pomFile != null && pomFile.exists() )
             {
-                copyFile( pomFile, targetPath, pomFilename, fixChecksums );
-                queueRepositoryTask( target.getId(), targetPath.resolve( 
pomFilename ) );
+                StorageAsset targetPomFile = target.getAsset( 
targetPath.getPath() + "/" + pomFilename );
+                copyFile( source, pomFile, target, targetPomFile, fixChecksums 
);
+                queueRepositoryTask( target.getId(), targetPomFile );
 
 
             }
@@ -456,7 +461,7 @@ public class DefaultRepositoriesService
             // explicitly update only if metadata-updater consumer is not 
enabled!
             if ( !archivaAdministration.getKnownContentConsumers().contains( 
"metadata-updater" ) )
             {
-                updateProjectMetadata( targetPath.toAbsolutePath().toString(), 
lastUpdatedTimestamp, timestamp, newBuildNumber,
+                updateProjectMetadata( target, targetPath, 
lastUpdatedTimestamp, timestamp, newBuildNumber,
                                        fixChecksums, artifactTransferRequest );
 
 
@@ -487,8 +492,9 @@ public class DefaultRepositoriesService
         return true;
     }
 
-    private void queueRepositoryTask( String repositoryId, Path localFile )
+    private void queueRepositoryTask( String repositoryId, StorageAsset 
localFile )
     {
+
         RepositoryTask task = new RepositoryTask();
         task.setRepositoryId( repositoryId );
         task.setResourceFile( localFile );
@@ -502,19 +508,19 @@ public class DefaultRepositoriesService
         catch ( TaskQueueException e )
         {
             log.error( "Unable to queue repository task to execute consumers 
on resource file ['{}"
-                           + "'].", localFile.getFileName());
+                           + "'].", localFile.getName());
         }
     }
 
-    private ArchivaRepositoryMetadata getMetadata( Path metadataFile )
+    private ArchivaRepositoryMetadata getMetadata( StorageAsset metadataFile )
         throws RepositoryMetadataException
     {
         ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
-        if ( Files.exists(metadataFile) )
+        if ( metadataFile.exists() )
         {
             try
             {
-                metadata = MavenMetadataReader.read( metadataFile );
+                metadata = MavenMetadataReader.read( 
metadataFile.getFilePath() );
             }
             catch ( XMLException e )
             {
@@ -524,44 +530,49 @@ public class DefaultRepositoriesService
         return metadata;
     }
 
-    private Path getMetadata( String targetPath )
+    private StorageAsset getMetadata( RepositoryStorage storage, String 
targetPath )
     {
-        String artifactPath = targetPath.substring( 0, targetPath.lastIndexOf( 
FileSystems.getDefault().getSeparator() ));
+        return storage.getAsset( targetPath + "/" + 
MetadataTools.MAVEN_METADATA );
 
-        return Paths.get( artifactPath, MetadataTools.MAVEN_METADATA );
     }
 
-    private void copyFile( Path sourceFile, Path targetPath, String 
targetFilename, boolean fixChecksums )
+    /*
+     * Copies the asset to the new target.
+     */
+    private void copyFile( RepositoryStorage sourceStorage,  StorageAsset 
sourceFile, RepositoryStorage targetStorage, StorageAsset targetPath, boolean 
fixChecksums )
         throws IOException
     {
-        Files.copy( sourceFile, targetPath.resolve( targetFilename ), 
StandardCopyOption.REPLACE_EXISTING,
-                    StandardCopyOption.COPY_ATTRIBUTES );
 
+        StorageUtil.copyAsset( sourceStorage, sourceFile, targetStorage, 
targetPath, true );
         if ( fixChecksums )
         {
-            fixChecksums( targetPath.resolve( targetFilename ) );
+            fixChecksums( targetPath );
         }
     }
 
-    private void fixChecksums( Path file )
+    private void fixChecksums( StorageAsset file )
     {
-        ChecksummedFile checksum = new ChecksummedFile( file );
-        checksum.fixChecksums( algorithms );
+        Path destinationFile = file.getFilePath();
+        if (destinationFile!=null)
+        {
+            ChecksummedFile checksum = new ChecksummedFile( destinationFile );
+            checksum.fixChecksums( algorithms );
+        }
     }
 
-    private void updateProjectMetadata( String targetPath, Date 
lastUpdatedTimestamp, String timestamp, int buildNumber,
+    private void updateProjectMetadata( RepositoryStorage storage, 
StorageAsset targetPath, Date lastUpdatedTimestamp, String timestamp, int 
buildNumber,
                                         boolean fixChecksums, 
ArtifactTransferRequest artifactTransferRequest )
         throws RepositoryMetadataException
     {
         List<String> availableVersions = new ArrayList<>();
         String latestVersion = artifactTransferRequest.getVersion();
 
-        Path projectDir = Paths.get( targetPath ).getParent();
-        Path projectMetadataFile = projectDir.resolve( 
MetadataTools.MAVEN_METADATA );
+        StorageAsset projectDir = targetPath.getParent();
+        StorageAsset projectMetadataFile = storage.getAsset( 
projectDir.getPath()+"/"+MetadataTools.MAVEN_METADATA );
 
         ArchivaRepositoryMetadata projectMetadata = getMetadata( 
projectMetadataFile );
 
-        if ( Files.exists(projectMetadataFile) )
+        if ( projectMetadataFile.exists() )
         {
             availableVersions = projectMetadata.getAvailableVersions();
 
@@ -601,7 +612,7 @@ public class DefaultRepositoriesService
             projectMetadata.setReleasedVersion( latestVersion );
         }
 
-        RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile);
+        RepositoryMetadataWriter.write( projectMetadata, 
projectMetadataFile.getFilePath());
 
         if ( fixChecksums )
         {
@@ -765,7 +776,7 @@ public class DefaultRepositoriesService
             TimeZone timezone = TimeZone.getTimeZone( "UTC" );
             DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
             fmt.setTimeZone( timezone );
-            ManagedRepository repoConfig = 
repositoryRegistry.getManagedRepository( repositoryId );
+            ManagedRepository repo = repositoryRegistry.getManagedRepository( 
repositoryId );
 
             VersionedReference ref = new VersionedReference();
             ref.setArtifactId( artifact.getArtifactId() );
@@ -801,9 +812,9 @@ public class DefaultRepositoriesService
 
                 int index = path.lastIndexOf( '/' );
                 path = path.substring( 0, index );
-                Path targetPath = repoConfig.getLocalPath().resolve( path );
+                StorageAsset targetPath = repo.getAsset( path );
 
-                if ( !Files.exists(targetPath) )
+                if ( targetPath.exists() )
                 {
                     //throw new ContentNotFoundException(
                     //    artifact.getGroupId() + ":" + 
artifact.getArtifactId() + ":" + artifact.getVersion() );
@@ -825,7 +836,7 @@ public class DefaultRepositoriesService
                         repository.deleteArtifact( artifactRef );
                     }
                 }
-                Path metadataFile = getMetadata( 
targetPath.toAbsolutePath().toString() );
+                StorageAsset metadataFile = getMetadata( repo, 
targetPath.getPath() );
                 ArchivaRepositoryMetadata metadata = getMetadata( metadataFile 
);
 
                 updateMetadata( metadata, metadataFile, lastUpdatedTimestamp, 
artifact );
@@ -1115,14 +1126,14 @@ public class DefaultRepositoriesService
      *
      * @param metadata
      */
-    private void updateMetadata( ArchivaRepositoryMetadata metadata, Path 
metadataFile, Date lastUpdatedTimestamp,
+    private void updateMetadata( ArchivaRepositoryMetadata metadata, 
StorageAsset metadataFile, Date lastUpdatedTimestamp,
                                  Artifact artifact )
         throws RepositoryMetadataException
     {
         List<String> availableVersions = new ArrayList<>();
         String latestVersion = "";
 
-        if ( Files.exists(metadataFile) )
+        if ( metadataFile.exists() )
         {
             if ( metadata.getAvailableVersions() != null )
             {
@@ -1166,8 +1177,8 @@ public class DefaultRepositoriesService
         metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
         metadata.setAvailableVersions( availableVersions );
 
-        RepositoryMetadataWriter.write( metadata, metadataFile);
-        ChecksummedFile checksum = new ChecksummedFile( metadataFile );
+        RepositoryMetadataWriter.write( metadata, metadataFile.getFilePath());
+        ChecksummedFile checksum = new ChecksummedFile( 
metadataFile.getFilePath() );
         checksum.fixChecksums( algorithms );
     }
 
diff --git 
a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/ArchivaDavResource.java
 
b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/ArchivaDavResource.java
index eed07ea..b3843e5 100644
--- 
a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/ArchivaDavResource.java
+++ 
b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/ArchivaDavResource.java
@@ -336,7 +336,7 @@ public class ArchivaDavResource
                 }
                 StorageAsset member = repositoryStorage.addAsset( newPath, 
false );
                 member.create();
-                member.storeDataFile( tempFile );
+                member.replaceDataFromFile( tempFile );
             }
             catch ( IOException e )
             {
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 46c920c..30e1443 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
@@ -24,10 +24,8 @@ import 
org.apache.archiva.admin.model.remote.RemoteRepositoryAdmin;
 import org.apache.archiva.audit.Auditable;
 import org.apache.archiva.checksum.ChecksumAlgorithm;
 import org.apache.archiva.checksum.ChecksumUtil;
-import org.apache.archiva.checksum.ChecksummedFile;
 import org.apache.archiva.checksum.StreamingChecksum;
 import org.apache.archiva.common.filelock.FileLockManager;
-import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
 import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
 import org.apache.archiva.common.utils.PathUtil;
 import org.apache.archiva.common.utils.VersionUtil;
@@ -68,7 +66,6 @@ import org.apache.archiva.repository.RepositoryGroup;
 import org.apache.archiva.repository.RepositoryRegistry;
 import org.apache.archiva.repository.RepositoryRequestInfo;
 import org.apache.archiva.repository.content.FilesystemAsset;
-import org.apache.archiva.repository.content.FilesystemStorage;
 import org.apache.archiva.repository.content.StorageAsset;
 import org.apache.archiva.repository.events.AuditListener;
 import org.apache.archiva.repository.features.IndexCreationFeature;
@@ -94,7 +91,6 @@ import org.apache.jackrabbit.webdav.DavServletResponse;
 import org.apache.jackrabbit.webdav.DavSession;
 import org.apache.jackrabbit.webdav.lock.LockManager;
 import org.apache.jackrabbit.webdav.lock.SimpleLockManager;
-import org.codehaus.plexus.digest.Digester;
 import org.codehaus.plexus.digest.DigesterException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -1254,7 +1250,7 @@ public class ArchivaDavResourceFactory
         throws RepositoryMetadataException, DigesterException, IOException
     {
         StorageAsset asset = repoGroup.addAsset( outputFilename, false );
-        OutputStream stream = asset.writeData( true );
+        OutputStream stream = asset.getWriteStream( true );
         OutputStreamWriter sw = new OutputStreamWriter( stream, "UTF-8" );
         RepositoryMetadataWriter.write( mergedMetadata, sw );
 
@@ -1269,7 +1265,7 @@ public class ArchivaDavResourceFactory
             String ext = algo.getDefaultExtension( );
             try
             {
-                return repo.getAsset( path + "." + ext ).writeData( true );
+                return repo.getAsset( path + "." + ext ).getWriteStream( true 
);
             }
             catch ( IOException e )
             {
@@ -1279,7 +1275,7 @@ public class ArchivaDavResourceFactory
         } ).filter( Objects::nonNull ).collect( Collectors.toList( ) );
         try
         {
-            StreamingChecksum.updateChecksums( repo.getAsset(path).getData(), 
algorithms, outStreams );
+            StreamingChecksum.updateChecksums( 
repo.getAsset(path).getReadStream(), algorithms, outStreams );
         }
         catch ( IOException e )
         {

Reply via email to