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