From 8e4acdc82a875ca32e39eb216b879c002d228de3 Mon Sep 17 00:00:00 2001 From: Martin Stockhammer Date: Fri, 19 Jul 2019 19:29:01 +0200 Subject: [PATCH] Migrating to storage API --- .../apache/archiva/checksum/ChecksumUtil.java | 1 + .../archiva-configuration/pom.xml | 4 - .../apache/archiva/common/filelock/Lock.java | 4 +- .../policies/AbstractUpdatePolicy.java | 26 +-- .../policies/CachedFailuresPolicy.java | 4 +- .../archiva/policies/ChecksumPolicy.java | 21 +- .../archiva/policies/DownloadErrorPolicy.java | 5 +- .../archiva/policies/DownloadPolicy.java | 7 +- .../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 ++++++- .../repository/content/StorageAsset.java | 51 ++++- .../repository/AbstractRepository.java | 40 +++- .../repository/RepositoryRegistry.java | 3 + .../repository/content/FilesystemAsset.java | 65 ++++-- .../repository/content/FilesystemStorage.java | 213 +++++++++++++++++- .../repository/content/StorageUtil.java | 192 ++++++++++++++++ .../content/FilesystemAssetTest.java | 13 +- .../mock/ManagedRepositoryContentMock.java | 8 +- .../mock/ManagedRepositoryContentMock.java | 7 +- .../maven/MavenRepositoryProxyHandler.java | 28 +-- .../mock/ManagedRepositoryContentMock.java | 9 +- .../ManagedDefaultRepositoryContent.java | 44 ++-- .../repository/model/RepositoryTask.java | 7 +- ...ArchivaRepositoryScanningTaskExecutor.java | 5 +- .../services/DefaultRepositoriesService.java | 97 ++++---- .../archiva/webdav/ArchivaDavResource.java | 2 +- .../webdav/ArchivaDavResourceFactory.java | 10 +- 32 files changed, 834 insertions(+), 343 deletions(-) create mode 100644 archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/repository/content/StorageUtil.java 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 5a11b910f..bb6a710ae 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 newChecksums(List 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 75690291a..fd4565cce 100644 --- a/archiva-modules/archiva-base/archiva-configuration/pom.xml +++ b/archiva-modules/archiva-base/archiva-configuration/pom.xml @@ -134,10 +134,6 @@ org.apache.commons commons-collections4 - - org.apache.archiva - archiva-policies - org.apache.archiva archiva-test-utils 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 ab10d7404..f8e4e4603 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 1fbbfece5..cf1c0da4a 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 e92858fe8..ac20dbc8e 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 82d4d3da4..8f6885a6b 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 90261737e..28de5336b 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 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 8d149e619..f9a144331 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 1fab1209a..7a0baefe1 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 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 ec3289105..9b22a9b23 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 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 28e8cb9b1..700e4a560 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 eee2d86d2..f82d55514 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 5622fe5c4..fac557ed4 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 networkProxyMap = new ConcurrentHashMap<>(); + private List 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 policies, Map settings, - Properties request, Path localFile ) + Properties request, StorageAsset localFile ) throws PolicyViolationException { for ( Map.Entry entry : policies.entrySet() ) @@ -732,9 +744,9 @@ public abstract class DefaultRepositoryProxyHandler implements RepositoryProxyHa } } - private void validatePolicies(Map policies, Map settings, - Properties request, ArtifactReference artifact, RemoteRepositoryContent content, - Path localFile, Exception exception, Map previousExceptions ) + private void validatePolicies( Map policies, Map settings, + Properties request, ArtifactReference artifact, RemoteRepositoryContent content, + StorageAsset localFile, Exception exception, Map 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 1bab4a8ff..c50a89cf2 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 82b40b2ce..3dea0b294 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,13 +51,42 @@ 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 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 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 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 consumerFunction, boolean writeLock) throws IOException; + /** * Adds a new asset to the underlying storage. * @param path The path to the asset. @@ -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 16e70859d..38ef2a895 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 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,12 +151,21 @@ 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 de57977f2..87b5420b1 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 consumerFunction, boolean readLock ) throws IOException { storage.consumeData(asset, consumerFunction, readLock); } - protected void setStorage(RepositoryStorage storage) { + @Override + public void consumeDataFromChannel( StorageAsset asset, Consumer consumerFunction, boolean readLock ) throws IOException + { + storage.consumeDataFromChannel( asset, consumerFunction, readLock ); + } + + @Override + public void writeData( StorageAsset asset, Consumer consumerFunction, boolean writeLock ) throws IOException + { + storage.writeData( asset, consumerFunction, writeLock ); + } + + @Override + public void writeDataToChannel( StorageAsset asset, Consumer 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 2b0ac684a..e78130e33 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 41b45dfdc..e0d10f863 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(); } @@ -190,6 +201,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 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) { @@ -361,6 +390,12 @@ public class FilesystemAsset implements StorageAsset { return assetPath; } + @Override + public boolean isFileBased( ) + { + return true; + } + @Override public boolean hasParent( ) { @@ -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 344ebe633..92044fa86 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 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 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 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 consumerFunction) throws IOException { @@ -127,12 +213,93 @@ public class FilesystemStorage implements RepositoryStorage { } } + private void consumeDataFromChannelLocked( Path file, Consumer 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 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 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 + */ +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 b06546a1f..8e98e59bb 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 0eb574ed4..328f58a5a 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 @@ -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 e814a496c..ade81214a 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 b59d99835..9c383c37d 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 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 foundArtifacts; // First gather up the versions found as artifacts in the managed repository. - try (Stream stream = Files.list(repoDir)) { - foundArtifacts = stream.filter(Files::isRegularFile).map(path -> { + try (Stream 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 b93fe5a75..a858c1a84 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 c961a8df4..ea2cb2688 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 3c3cc7140..f3dd25569 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 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 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 eed07ea4f..b3843e513 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 46c920c9a..30e14433d 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 ) { -- 2.39.5