]> source.dussan.org Git - archiva.git/commitdiff
Moving storage utility methods to api module
authorMartin Stockhammer <martin_s@apache.org>
Sat, 29 Feb 2020 17:52:24 +0000 (18:52 +0100)
committerMartin Stockhammer <martin_s@apache.org>
Sat, 29 Feb 2020 17:52:24 +0000 (18:52 +0100)
22 files changed:
archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/AbstractRepositoryPurge.java
archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/archiva/proxy/DefaultRepositoryProxyHandler.java
archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/test/java/org/apache/archiva/admin/mock/ArchivaIndexManagerMock.java
archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/archiva/indexer/merger/base/DefaultIndexMerger.java
archiva-modules/archiva-base/archiva-storage-api/pom.xml
archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/StorageAsset.java
archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/AssetSpliterator.java
archiva-modules/archiva-base/archiva-storage-api/src/main/java/org/apache/archiva/repository/storage/util/StorageUtil.java
archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/mock/MockAsset.java
archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/mock/MockStorage.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/StorageUtilTest.java
archiva-modules/archiva-base/archiva-storage-api/src/test/java/org/apache/archiva/repository/storage/util/VisitStatus.java
archiva-modules/archiva-base/archiva-storage-api/src/test/resources/log4j2-test.xml [new file with mode: 0644]
archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/FsStorageUtil.java [new file with mode: 0644]
archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/StorageUtil.java [deleted file]
archiva-modules/archiva-maven/archiva-maven-indexer/src/main/java/org/apache/archiva/indexer/maven/MavenIndexManager.java
archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/content/maven2/ManagedDefaultRepositoryContent.java
archiva-modules/archiva-scheduler/archiva-scheduler-repository/src/test/java/org/apache/archiva/mock/ArchivaIndexManagerMock.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultBrowseService.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultRepositoriesService.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/utils/ArtifactBuilder.java
archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/util/IndexWriter.java

index a8daf16a2743e37c77a70d93a2cd2b996a2a3a4b..a3979e6a19f5299162de3ee13ce072b694be54af 100644 (file)
@@ -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);
+        });
 
     }
 
index 71ec9ab64d189a9ba03e19c732996cdb9b71be42..ddeb499f47256f127ef944ef81c22f9f53b8cfeb 100644 (file)
@@ -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 );
                 }
index f20b4ab1ce056b1d43210028db1504d6fa1551cb..127c314834e6a97e7b693305f133d62d9628a443 100644 (file)
@@ -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();
index b575a2016209dca1ece868a7cce55037f1cf4433..e476ea00826d5d3e281f1cbd63582f97b47f25b9 100644 (file)
@@ -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 );
                 }
             }
         }
index 5114864210d82972ab4b739544f45d5f695d3a7f..9085887fe64401df22a2a8d6a5b5f998ff5b6430 100644 (file)
   </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>
index 8209fcdaba72e2949aa55cebef49610488ced54d..eb7074c0ff3b3dd5b4805bea367489ad6d388edc 100644 (file)
@@ -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.
index 7b45e126d301025fb122f349815dbab936c57b08..3d2a8cef356c8169b5f58415f90b44ebf0d6fa89 100644 (file)
@@ -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( ) );
index b912ee3de8a25b880e6961a299ce4500b6c23cfb..f8c5745f1ee2794319d5824338a0c38ebc2b75fb 100644 (file)
@@ -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(),".");
+    }
 }
index 3baad4f16634bc75f598322cea6efc23703b6f77..0e19459f0a6aece4e2b717ad2f20b5b81cbd0392 100644 (file)
@@ -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 (file)
index 0000000..2741c30
--- /dev/null
@@ -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 <martin_s@apache.org>
+ */
+public class MockStorage implements RepositoryStorage
+{
+    public static final String ADD = "ADD";
+    public static final String REMOVE = "REMOVE";
+    private MockAsset root;
+    private LinkedHashMap<String, MockAsset> assets = new LinkedHashMap<>( );
+
+    private VisitStatus status = new VisitStatus( );
+
+    public MockStorage( MockAsset root )
+    {
+        this.root = root;
+        root.setStorage( this );
+    }
+
+    public MockStorage() {
+        this.root = new MockAsset( "" );
+        this.root.setStorage( this );
+    }
+
+    public VisitStatus getStatus() {
+        return status;
+    }
+
+    @Override
+    public URI getLocation( )
+    {
+        return null;
+    }
+
+    @Override
+    public void updateLocation( URI newLocation ) throws IOException
+    {
+
+    }
+
+    private String[] splitPath(String path) {
+        if (path.equals("/")) {
+            return new String[0];
+        } else
+        {
+            if (path.startsWith( "/" )) {
+                return path.substring( 1, path.length( ) ).split( "/" );
+            }
+            return path.split( "/" );
+        }
+    }
+
+    @Override
+    public StorageAsset getAsset( String path )
+    {
+        if (assets.containsKey( path )) {
+            return assets.get( path );
+        }
+        String[] pathArr = splitPath( path );
+        StorageAsset parent = root;
+        for (String pathElement : pathArr) {
+            Optional<? extends StorageAsset> next = parent.list( ).stream( ).filter( a -> a.getName( ).equals( pathElement ) ).findFirst( );
+            if (next.isPresent()) {
+                parent = next.get( );
+            } else {
+                MockAsset asset = new MockAsset( (MockAsset)parent, pathElement );
+                assets.put( asset.getPath( ), asset );
+                parent = asset;
+            }
+        }
+        return parent;
+    }
+
+    @Override
+    public void consumeData( StorageAsset asset, Consumer<InputStream> consumerFunction, boolean readLock ) throws IOException
+    {
+
+    }
+
+    @Override
+    public void consumeDataFromChannel( StorageAsset asset, Consumer<ReadableByteChannel> consumerFunction, boolean readLock ) throws IOException
+    {
+
+    }
+
+    @Override
+    public void writeData( StorageAsset asset, Consumer<OutputStream> consumerFunction, boolean writeLock ) throws IOException
+    {
+
+    }
+
+    @Override
+    public void writeDataToChannel( StorageAsset asset, Consumer<WritableByteChannel> consumerFunction, boolean writeLock ) throws IOException
+    {
+
+    }
+
+    @Override
+    public StorageAsset addAsset( String path, boolean container )
+    {
+        String[] pathArr = splitPath( path );
+        StorageAsset parent = root;
+        for (String pathElement : pathArr) {
+            Optional<? extends StorageAsset> next = parent.list( ).stream( ).filter( a -> a.getName( ).equals( pathElement ) ).findFirst( );
+            if (next.isPresent()) {
+                parent = next.get( );
+            } else {
+                MockAsset asset = new MockAsset( (MockAsset)parent, pathElement );
+                assets.put( asset.getPath( ), asset );
+                parent = asset;
+            }
+        }
+        status.add( ADD, parent );
+        return parent;
+    }
+
+    @Override
+    public void removeAsset( StorageAsset assetArg ) throws IOException
+    {
+        MockAsset asset = (MockAsset) assetArg;
+        if (asset.hasParent())
+        {
+            asset.getParent( ).unregisterChild( asset );
+        }
+        assets.remove( asset.getPath( ) );
+        status.add( REMOVE, asset );
+        if (asset.isThrowException()) {
+            throw new IOException( "Mocked IOException for " + asset.getPath( ) );
+        }
+    }
+
+    @Override
+    public StorageAsset moveAsset( StorageAsset origin, String destination, CopyOption... copyOptions ) throws IOException
+    {
+        return null;
+    }
+
+    @Override
+    public void moveAsset( StorageAsset origin, StorageAsset destination, CopyOption... copyOptions ) throws IOException
+    {
+
+    }
+
+    @Override
+    public StorageAsset copyAsset( StorageAsset origin, String destination, CopyOption... copyOptions ) throws IOException
+    {
+        return null;
+    }
+
+    @Override
+    public void copyAsset( StorageAsset origin, StorageAsset destination, CopyOption... copyOptions ) throws IOException
+    {
+
+    }
+}
index 7907754ded3441d6b904ccbe5a87db7483fcf27f..c1c53c7e231192af8035965b61e513e091806362 100644 (file)
@@ -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
index 4df53ed18e7b2a77f3df6487ec776db12a540848..e0657d8b916c839fee7af5c45d5c55131fe75070 100644 (file)
@@ -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 <martin_s@apache.org>
  */
-class VisitStatus
+public class VisitStatus
 {
+
+    LinkedHashMap<String, LinkedList<StorageAsset>> applied = new LinkedHashMap<>( );
     LinkedList<StorageAsset> visited = new LinkedList<>( );
 
-    VisitStatus( )
+    public VisitStatus( )
     {
 
     }
@@ -44,6 +48,13 @@ class VisitStatus
         visited.addLast( asset );
     }
 
+    public void add(String type, StorageAsset asset) {
+        if (!applied.containsKey( type )) {
+            applied.put( type, new LinkedList<>( ) );
+        }
+        applied.get( type ).add( asset );
+    }
+
     public StorageAsset getLast( )
     {
         return visited.getLast( );
@@ -63,5 +74,9 @@ class VisitStatus
         return visited.size( );
     }
 
+    public int size(String type) {
+        return applied.get( type ).size( );
+    }
+
 
 }
diff --git a/archiva-modules/archiva-base/archiva-storage-api/src/test/resources/log4j2-test.xml b/archiva-modules/archiva-base/archiva-storage-api/src/test/resources/log4j2-test.xml
new file mode 100644 (file)
index 0000000..afe6cf5
--- /dev/null
@@ -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 (file)
index 0000000..a009440
--- /dev/null
@@ -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 <martin_s@apache.org>
+ */
+public class FsStorageUtil
+{
+    private static final Logger log = LoggerFactory.getLogger( FsStorageUtil.class);
+
+    /**
+     * Copies the source asset to the target. The assets may be from different RepositoryStorage instances.
+     * If you know that source and asset are from the same storage instance, the copy method of the storage
+     * instance may be faster.
+     *
+     * @param source The source asset
+     * @param target The target asset
+     * @param locked If true, a readlock is set on the source and a write lock is set on the target.
+     * @param copyOptions Copy options
+     * @throws IOException
+     */
+    public static final void copyAsset( final StorageAsset source,
+                                        final StorageAsset target,
+                                        boolean locked,
+                                        final CopyOption... copyOptions ) throws IOException
+    {
+        if (source.isFileBased() && target.isFileBased()) {
+            // Short cut for FS operations
+            final Path sourcePath = source.getFilePath();
+            final Path targetPath = target.getFilePath( );
+            if (locked) {
+                final FileLockManager lmSource = ((FilesystemStorage)source.getStorage()).getFileLockManager();
+                final FileLockManager lmTarget = ((FilesystemStorage)target.getStorage()).getFileLockManager();
+                Lock lockRead = null;
+                Lock lockWrite = null;
+                try {
+                    lockRead = lmSource.readFileLock(sourcePath);
+                } catch (Exception e) {
+                    log.error("Could not create read lock on {}", sourcePath);
+                    throw new IOException(e);
+                }
+                try {
+                    lockWrite = lmTarget.writeFileLock(targetPath);
+                } catch (Exception e) {
+                    log.error("Could not create write lock on {}", targetPath);
+                    throw new IOException(e);
+                }
+                try {
+                    Files.copy(sourcePath, targetPath, copyOptions);
+                } finally {
+                    if (lockRead!=null) {
+                        try {
+                            lmSource.release(lockRead);
+                        } catch (FileLockException e) {
+                            log.error("Error during lock release of read lock {}", lockRead.getFile());
+                        }
+                    }
+                    if (lockWrite!=null) {
+                        try {
+                            lmTarget.release(lockWrite);
+                        } catch (FileLockException e) {
+                            log.error("Error during lock release of write lock {}", lockWrite.getFile());
+                        }
+                    }
+                }
+            } else
+            {
+                Files.copy( sourcePath, targetPath, copyOptions );
+            }
+        } else {
+            try {
+                final RepositoryStorage sourceStorage = source.getStorage();
+                final RepositoryStorage targetStorage = target.getStorage();
+                sourceStorage.consumeDataFromChannel( source, is -> org.apache.archiva.repository.storage.util.StorageUtil.wrapWriteFunction( is, targetStorage, target, locked ), locked);
+            }  catch (IOException e) {
+                throw e;
+            }  catch (Throwable e) {
+                Throwable cause = e.getCause();
+                if (cause instanceof IOException) {
+                    throw (IOException)cause;
+                } else
+                {
+                    throw new IOException( e );
+                }
+            }
+        }
+    }
+
+
+    public static final void copyToLocalFile(StorageAsset asset, Path destination, CopyOption... copyOptions) throws IOException {
+        if (asset.isFileBased()) {
+            Files.copy(asset.getFilePath(), destination, copyOptions);
+        } else {
+            try {
+
+                HashSet<OpenOption> openOptions = new HashSet<>();
+                for (CopyOption option : copyOptions) {
+                    if (option == StandardCopyOption.REPLACE_EXISTING) {
+                        openOptions.add(StandardOpenOption.CREATE);
+                        openOptions.add(StandardOpenOption.TRUNCATE_EXISTING);
+                        openOptions.add(StandardOpenOption.WRITE);
+                    } else {
+                        openOptions.add(StandardOpenOption.WRITE);
+                        openOptions.add(StandardOpenOption.CREATE_NEW);
+                    }
+                }
+                asset.getStorage().consumeDataFromChannel(asset, channel -> {
+                    try {
+                        FileChannel.open(destination, openOptions).transferFrom(channel, 0, Long.MAX_VALUE);
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+                }, false);
+            } catch (Throwable e) {
+                if (e.getCause() instanceof IOException) {
+                    throw (IOException)e.getCause();
+                } else {
+                    throw new IOException(e);
+                }
+            }
+        }
+    }
+
+    public static class PathInformation {
+        final Path path ;
+        final boolean tmpFile;
+
+        PathInformation(Path path, boolean tmpFile) {
+            this.path = path;
+            this.tmpFile = tmpFile;
+        }
+
+        public Path getPath() {
+            return path;
+        }
+
+        public boolean isTmpFile() {
+            return tmpFile;
+        }
+
+    }
+
+    public static final PathInformation getAssetDataAsPath(StorageAsset asset) throws IOException {
+        if (!asset.exists()) {
+            throw new IOException("Asset does not exist");
+        }
+        if (asset.isFileBased()) {
+            return new PathInformation(asset.getFilePath(), false);
+        } else {
+            Path tmpFile = Files.createTempFile(asset.getName(), org.apache.archiva.repository.storage.util.StorageUtil.getExtension(asset));
+            copyToLocalFile(asset, tmpFile, StandardCopyOption.REPLACE_EXISTING);
+            return new PathInformation(tmpFile, true);
+        }
+    }
+
+}
diff --git a/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/StorageUtil.java b/archiva-modules/archiva-base/archiva-storage-fs/src/main/java/org/apache/archiva/repository/storage/StorageUtil.java
deleted file mode 100644 (file)
index 839aab9..0000000
+++ /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 <martin_s@apache.org>
- */
-public class StorageUtil
-{
-    private static final int DEFAULT_BUFFER_SIZE = 4096;
-    private static final Logger log = LoggerFactory.getLogger(StorageUtil.class);
-
-    /**
-     * Copies the source asset to the target. The assets may be from different RepositoryStorage instances.
-     * If you know that source and asset are from the same storage instance, the copy method of the storage
-     * instance may be faster.
-     *
-     * @param source The source asset
-     * @param target The target asset
-     * @param locked If true, a readlock is set on the source and a write lock is set on the target.
-     * @param copyOptions Copy options
-     * @throws IOException
-     */
-    public static final void copyAsset( final StorageAsset source,
-                                        final StorageAsset target,
-                                        boolean locked,
-                                        final CopyOption... copyOptions ) throws IOException
-    {
-        if (source.isFileBased() && target.isFileBased()) {
-            // Short cut for FS operations
-            final Path sourcePath = source.getFilePath();
-            final Path targetPath = target.getFilePath( );
-            if (locked) {
-                final FileLockManager lmSource = ((FilesystemStorage)source.getStorage()).getFileLockManager();
-                final FileLockManager lmTarget = ((FilesystemStorage)target.getStorage()).getFileLockManager();
-                Lock lockRead = null;
-                Lock lockWrite = null;
-                try {
-                    lockRead = lmSource.readFileLock(sourcePath);
-                } catch (Exception e) {
-                    log.error("Could not create read lock on {}", sourcePath);
-                    throw new IOException(e);
-                }
-                try {
-                    lockWrite = lmTarget.writeFileLock(targetPath);
-                } catch (Exception e) {
-                    log.error("Could not create write lock on {}", targetPath);
-                    throw new IOException(e);
-                }
-                try {
-                    Files.copy(sourcePath, targetPath, copyOptions);
-                } finally {
-                    if (lockRead!=null) {
-                        try {
-                            lmSource.release(lockRead);
-                        } catch (FileLockException e) {
-                            log.error("Error during lock release of read lock {}", lockRead.getFile());
-                        }
-                    }
-                    if (lockWrite!=null) {
-                        try {
-                            lmTarget.release(lockWrite);
-                        } catch (FileLockException e) {
-                            log.error("Error during lock release of write lock {}", lockWrite.getFile());
-                        }
-                    }
-                }
-            } else
-            {
-                Files.copy( sourcePath, targetPath, copyOptions );
-            }
-        } else {
-            try {
-                final RepositoryStorage sourceStorage = source.getStorage();
-                final RepositoryStorage targetStorage = target.getStorage();
-                sourceStorage.consumeDataFromChannel( source, is -> wrapWriteFunction( is, targetStorage, target, locked ), locked);
-            }  catch (IOException e) {
-                throw e;
-            }  catch (Throwable e) {
-                Throwable cause = e.getCause();
-                if (cause instanceof IOException) {
-                    throw (IOException)cause;
-                } else
-                {
-                    throw new IOException( e );
-                }
-            }
-        }
-    }
-
-    /**
-     * Moves a asset between different storage instances.
-     * If you know that source and asset are from the same storage instance, the move method of the storage
-     * instance may be faster.
-     *
-     * @param source The source asset
-     * @param target The target asset
-     * @param locked If true, a lock is used for the move operation.
-     * @param copyOptions Options for copying
-     * @throws IOException If the move fails
-     */
-    public static final void moveAsset(StorageAsset source, StorageAsset target, boolean locked, CopyOption... copyOptions) throws IOException
-    {
-        if (source.isFileBased() && target.isFileBased()) {
-            // Short cut for FS operations
-            // Move is atomic operation
-            if (!Files.exists(target.getFilePath().getParent())) {
-                Files.createDirectories(target.getFilePath().getParent());
-            }
-            Files.move( source.getFilePath(), target.getFilePath(), copyOptions );
-        } else {
-            try {
-                final RepositoryStorage sourceStorage = source.getStorage();
-                final RepositoryStorage targetStorage = target.getStorage();
-                sourceStorage.consumeDataFromChannel( source, is -> wrapWriteFunction( is, targetStorage, target, locked ), locked);
-                sourceStorage.removeAsset( source );
-            }  catch (IOException e) {
-                throw e;
-            }  catch (Throwable e) {
-                Throwable cause = e.getCause();
-                if (cause instanceof IOException) {
-                    throw (IOException)cause;
-                } else
-                {
-                    throw new IOException( e );
-                }
-            }
-        }
-
-    }
-
-    private static final void wrapWriteFunction(ReadableByteChannel is, RepositoryStorage targetStorage, StorageAsset target, boolean locked) {
-        try {
-            targetStorage.writeDataToChannel( target, os -> copy(is, os), locked );
-        } catch (Exception e) {
-            throw new RuntimeException( e );
-        }
-    }
-
-
-    private static final void copy( final ReadableByteChannel is, final WritableByteChannel os ) {
-        if (is instanceof FileChannel) {
-            copy( (FileChannel) is, os );
-        } else if (os instanceof FileChannel) {
-            copy(is, (FileChannel)os);
-        } else
-        {
-            try
-            {
-                ByteBuffer buffer = ByteBuffer.allocate( DEFAULT_BUFFER_SIZE );
-                while ( is.read( buffer ) != -1 )
-                {
-                    buffer.flip( );
-                    while ( buffer.hasRemaining( ) )
-                    {
-                        os.write( buffer );
-                    }
-                    buffer.clear( );
-                }
-            }
-            catch ( IOException e )
-            {
-                throw new RuntimeException( e );
-            }
-        }
-    }
-
-    private static final void copy( final FileChannel is, final WritableByteChannel os ) {
-        try
-        {
-            is.transferTo( 0, is.size( ), os );
-        }
-        catch ( IOException e )
-        {
-            throw new RuntimeException( e );
-        }
-    }
-
-    private static final void copy( final ReadableByteChannel is, final FileChannel os ) {
-        try
-        {
-            os.transferFrom( is, 0, Long.MAX_VALUE );
-        }
-        catch ( IOException e )
-        {
-            throw new RuntimeException( e );
-        }
-    }
-
-    /**
-     * Runs the consumer function recursively on each asset found starting at the base path
-     * @param baseAsset The base path where to start search
-     * @param consumer The consumer function applied to each found asset
-     * @param depthFirst If true, the deepest elements are consumed first.
-     * @param maxDepth The maximum depth to recurse into. 0 means, only the baseAsset is consumed, 1 the base asset and its children and so forth.
-     */
-    public static final void recurse(final StorageAsset baseAsset, final Consumer<StorageAsset> consumer, final boolean depthFirst, final int maxDepth) throws IOException {
-        recurse(baseAsset, consumer, depthFirst, maxDepth, 0);
-    }
-
-    /**
-     * Runs the consumer function recursively on each asset found starting at the base path. The function descends into
-     * maximum depth.
-     *
-     * @param baseAsset The base path where to start search
-     * @param consumer The consumer function applied to each found asset
-     * @param depthFirst If true, the deepest elements are consumed first.
-     */
-    public static final void recurse(final StorageAsset baseAsset, final Consumer<StorageAsset> consumer, final boolean depthFirst) throws IOException {
-        recurse(baseAsset, consumer, depthFirst, Integer.MAX_VALUE, 0);
-    }
-
-    /**
-     * Runs the consumer function recursively on each asset found starting at the base path. It does not recurse with
-     * depth first and stops only if there are no more children available.
-     *
-     * @param baseAsset The base path where to start search
-     * @param consumer The consumer function applied to each found asset
-     */
-    public static final void recurse(final StorageAsset baseAsset, final Consumer<StorageAsset> consumer) throws IOException {
-        recurse(baseAsset, consumer, false, Integer.MAX_VALUE, 0);
-    }
-
-    private static final void recurse(final StorageAsset baseAsset, final Consumer<StorageAsset> consumer, final boolean depthFirst, final int maxDepth, final int currentDepth)
-    throws IOException {
-        if (!depthFirst) {
-            consumer.accept(baseAsset);
-        }
-        if (currentDepth<maxDepth && baseAsset.isContainer()) {
-            for(StorageAsset asset : baseAsset.list() ) {
-                recurse(asset, consumer, depthFirst, maxDepth, currentDepth+1);
-            }
-        }
-        if (depthFirst) {
-            consumer.accept(baseAsset);
-        }
-    }
-
-    /**
-     * Deletes the given asset and all child assets recursively.
-     * @param baseDir The base asset to remove.
-     * @throws IOException
-     */
-    public static final void deleteRecursively(StorageAsset baseDir) throws IOException {
-        recurse(baseDir, a -> {
-            try {
-                a.getStorage().removeAsset(a);
-            } catch (IOException e) {
-                log.error("Could not delete asset {}", a.getPath());
-            }
-        },true);
-    }
-
-    /**
-     * Returns the extension of the name of a given asset. Extension is the substring after the last occurence of '.' in the
-     * string. If no '.' is found, the empty string is returned.
-     *
-     * @param asset The asset from which to return the extension string.
-     * @return The extension.
-     */
-    public static final String getExtension(StorageAsset asset) {
-        return StringUtils.substringAfterLast(asset.getName(),".");
-    }
-
-    public static final void copyToLocalFile(StorageAsset asset, Path destination, CopyOption... copyOptions) throws IOException {
-        if (asset.isFileBased()) {
-            Files.copy(asset.getFilePath(), destination, copyOptions);
-        } else {
-            try {
-
-                HashSet<OpenOption> openOptions = new HashSet<>();
-                for (CopyOption option : copyOptions) {
-                    if (option == StandardCopyOption.REPLACE_EXISTING) {
-                        openOptions.add(StandardOpenOption.CREATE);
-                        openOptions.add(StandardOpenOption.TRUNCATE_EXISTING);
-                        openOptions.add(StandardOpenOption.WRITE);
-                    } else {
-                        openOptions.add(StandardOpenOption.WRITE);
-                        openOptions.add(StandardOpenOption.CREATE_NEW);
-                    }
-                }
-                asset.getStorage().consumeDataFromChannel(asset, channel -> {
-                    try {
-                        FileChannel.open(destination, openOptions).transferFrom(channel, 0, Long.MAX_VALUE);
-                    } catch (IOException e) {
-                        throw new RuntimeException(e);
-                    }
-                }, false);
-            } catch (Throwable e) {
-                if (e.getCause() instanceof IOException) {
-                    throw (IOException)e.getCause();
-                } else {
-                    throw new IOException(e);
-                }
-            }
-        }
-    }
-
-    public static class PathInformation {
-        final Path path ;
-        final boolean tmpFile;
-
-        PathInformation(Path path, boolean tmpFile) {
-            this.path = path;
-            this.tmpFile = tmpFile;
-        }
-
-        public Path getPath() {
-            return path;
-        }
-
-        public boolean isTmpFile() {
-            return tmpFile;
-        }
-
-    }
-
-    public static final PathInformation getAssetDataAsPath(StorageAsset asset) throws IOException {
-        if (!asset.exists()) {
-            throw new IOException("Asset does not exist");
-        }
-        if (asset.isFileBased()) {
-            return new PathInformation(asset.getFilePath(), false);
-        } else {
-            Path tmpFile = Files.createTempFile(asset.getName(), getExtension(asset));
-            copyToLocalFile(asset, tmpFile, StandardCopyOption.REPLACE_EXISTING);
-            return new PathInformation(tmpFile, true);
-        }
-    }
-
-}
index 7e007f04bcc89a453a0d4d27edba82934c55073f..777e6eb30a21fd0090b101c54a146397e6168ae1 100644 (file)
@@ -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();
index 95196ff58209f9892e19e63f234d945edf74c68a..780cf35a8737afd0a9dd4e19b64b924ba4530f19 100644 (file)
@@ -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 -> {
index fb175c058759b41222ae7814149d2404df2eb3fc..1776c8f9caf4de3db0853e3225771778f4f20a58 100644 (file)
@@ -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();
index 7899fdf223dd00a6bdb27476b5c4a17b360a71a5..053e12c3a4ce68057510536f37b6469c8a547e35 100644 (file)
@@ -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
         {
index c313cd9d7e15de4a74d5d936e3e94abbb86f07d6..0dcaee6810a7c651b3ecaf6e5729c08dfedb2cee 100644 (file)
@@ -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 );
index 2c6db17bf81d1e620ebfd0fdf3ecd496204d8e21..76d3292a869648dc165535d6fbb3196ade5c4e33 100644 (file)
@@ -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;
index f224c49da963279adf9df4ffc6413fab5c2bf6f3..415a01f1a5a954004634a01285f7a7753ec64703 100644 (file)
@@ -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