Browse Source

Adding recursive delete with cumulated result status to FileUtils

pull/58/head
Martin Stockhammer 4 years ago
parent
commit
dfe9604884

+ 6
- 0
archiva-modules/archiva-base/archiva-common/pom.xml View File

@@ -52,6 +52,12 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>

</dependencies>
<build>
<pluginManagement>

+ 72
- 0
archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/common/utils/FileStatus.java View File

@@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.archiva.common.utils;

import java.io.IOException;
import java.nio.file.Path;

/**
* File operation status for a given file.
*
* @author Martin Stockhammer <martin_s@apache.org>
*/
public class FileStatus
{
final private Path path;
final private StatusResult result;
final private IOException exception;

/**
* Success status
*
* @param path the file path
* @param statusResult the status of the file operation
*/
FileStatus( Path path, StatusResult statusResult) {
this.path = path;
this.result = statusResult;
this.exception = null;
}

/**
* Error status
* @param path the file path
* @param e the exception, that occured during the file operation
*/
FileStatus( Path path, IOException e) {
this.path = path;
this.result = StatusResult.ERROR;
this.exception = e;
}

public IOException getException( )
{
return exception;
}

public Path getPath( )
{
return path;
}

public StatusResult getResult( )
{
return result;
}
}

+ 45
- 9
archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/common/utils/FileUtils.java View File

@@ -29,6 +29,7 @@ import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

@@ -46,14 +47,17 @@ public class FileUtils {
* @param dir
*/
public static void deleteQuietly(Path dir) {
try {
Files.walk(dir)
.sorted(Comparator.reverseOrder())
.forEach(file -> {
try(Stream<Path> stream = Files.walk(dir)) {
stream
.sorted( Comparator.reverseOrder() )
.forEach(file -> {
try {
Files.delete(file);
} catch (IOException e) {
// Ignore this
if (log.isDebugEnabled()) {
log.debug( "Exception during file delete: {}", e.getMessage( ), e );
}
}

});
@@ -64,6 +68,38 @@ public class FileUtils {

}

public static IOStatus deleteDirectoryWithStatus(Path dir) throws IOException {
if (!Files.exists(dir)) {
IOStatus status = new IOStatus( );
status.addError( dir, new FileNotFoundException( "Directory not found " + dir ) );
return status;
}
if (!Files.isDirectory(dir)) {
IOStatus status = new IOStatus( );
status.addError(dir, new IOException("Given path is not a directory " + dir));
}
try( Stream<Path> stream = Files.walk(dir)) {
return stream
.sorted( Comparator.reverseOrder() )
.map(file ->
{
try {
Files.delete(file);
return new FileStatus( file, StatusResult.DELETED );
} catch (UncheckedIOException e) {
log.warn("File could not be deleted {}", file);
return new FileStatus( file, e.getCause( ) );
}
catch ( IOException e )
{
return new FileStatus( file, e );
}
}).collect( IOStatus::new, IOStatus::accumulate, IOStatus::combine );
} catch (UncheckedIOException e) {
throw new IOException("File deletion failed ", e);
}
}

public static void deleteDirectory(Path dir) throws IOException {
if (!Files.exists(dir)) {
return;
@@ -72,16 +108,16 @@ public class FileUtils {
throw new IOException("Given path is not a directory " + dir);
}
boolean result = true;
try {
result = Files.walk(dir)
.sorted(Comparator.reverseOrder())
.map(file ->
try(Stream<Path> stream = Files.walk(dir)) {
result = stream
.sorted( Comparator.reverseOrder() )
.map(file ->
{
try {
Files.delete(file);
return Optional.of(Boolean.TRUE);
} catch (UncheckedIOException | IOException e) {
log.warn("File could not be deleted {}", file);
log.warn("File could not be deleted {}: {}", file, e.getMessage());
return Optional.empty();
}


+ 168
- 0
archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/common/utils/IOStatus.java View File

@@ -0,0 +1,168 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.archiva.common.utils;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;

/**
*
* Collects information about file system operational status, e.g. if a file could be deleted,
* or IOException was thrown.
*
* @author Martin Stockhammer <martin_s@apache.org>
*/
public class IOStatus
{

Map<Path,IOException> errorList;
Map<Path, StatusResult> okList = new TreeMap<>( );

/**
* Returns <code>true</code>, if no error was recorded.
* @return
*/
boolean isOk() {
return !hasErrors( );
}

/**
* Returns <code>true</code>, if at least one error was recorded
* @return
*/
boolean hasErrors() {
if (errorList==null || errorList.size()==0) {
return false;
} else {
return true;
}
}

/**
* Accumulator method used for stream collecting
*
* @param ioStatus
* @param fileStatus
* @return
*/
public static IOStatus accumulate(IOStatus ioStatus, FileStatus fileStatus) {
ioStatus.addStatus( fileStatus );
return ioStatus;
}

/**
* Combiner used for stream collecting
* @param ioStatus1
* @param ioStatus2
* @return
*/
public static IOStatus combine(IOStatus ioStatus1, IOStatus ioStatus2) {
IOStatus status = new IOStatus( );
status.addAllSuccess( ioStatus1.getSuccessFiles() );
status.addAllSuccess( ioStatus2.getSuccessFiles( ) );
status.addAllErrors( ioStatus1.getErrorFiles( ) );
status.addAllErrors( ioStatus2.getErrorFiles( ) );
return status;
}

/**
* Add the status for a specific file to this status collection.
*
* @param status the status for a given file
* @return the status object itself
*/
public IOStatus addStatus(FileStatus status) {
if (status.getResult()== StatusResult.ERROR) {
addError( status.getPath( ), status.getException( ) );
} else {
addSuccess( status.getPath( ), status.getResult( ) );
}
return this;
}

/**
* Adds an error to the status collection.
*
* @param path the file path
* @param e the exception thrown during the file operation
*/
public void addError( Path path, IOException e) {
if (errorList==null) {
errorList = new TreeMap<>( );
}
errorList.put( path, e );
}

/**
* Adds multiple errors to the collection.
*
* @param errors the map of file, error pairs
*/
public void addAllErrors(Map<Path, IOException> errors) {
if (errorList == null) {
errorList = new TreeMap<>( );
}
errorList.putAll( errors );
}

/**
* Adds all successful states to the collection.
*
* @param success a map of file, StatusResult pairs
*/
public void addAllSuccess( Map<Path, StatusResult> success) {
okList.putAll( success );
}

/**
* Add success status for a given file to the collection.
*
* @param path the file path
* @param status the status of the file operation, e.g. DELETED
*/
public void addSuccess( Path path, StatusResult status) {
okList.put( path, status );
}

/**
* Returns all the recorded errors as map of path, exception pairs.
* @return the map of path, exception pairs.
*/
public Map<Path, IOException> getErrorFiles() {
if (errorList==null) {
return Collections.emptyMap( );
}
return errorList;
}

/**
* Returns all the recorded successful operations.
*
* @return the map of path, StatusResult pairs
*/
public Map<Path, StatusResult> getSuccessFiles() {
if (okList==null) {
return Collections.emptyMap( );
}
return okList;
}
}

+ 29
- 0
archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/common/utils/StatusResult.java View File

@@ -0,0 +1,29 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.archiva.common.utils;

/**
* Result status for file operations.
*
* @author Martin Stockhammer <martin_s@apache.org>
*/
public enum StatusResult
{
DELETED, EXIST, ERROR
}

+ 38
- 2
archiva-modules/archiva-base/archiva-common/src/test/java/org/apache/archiva/common/utils/FileUtilsTest.java View File

@@ -34,8 +34,7 @@ import java.nio.file.attribute.PosixFilePermission;
import java.util.HashSet;
import java.util.Set;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;

/**
* @author Martin Stockhammer <martin_s@apache.org>
@@ -110,6 +109,43 @@ public class FileUtilsTest

}


@Test
public void testDeleteWithStatus() throws IOException
{
Path td = Files.createTempDirectory( "FileUtilsTest" );
Path f1 = td.resolve("file1.txt");
Path f2 = td.resolve("file2.txt");
Path d1 = td.resolve("dir1");
Files.createDirectory( d1 );
Path d11 = d1.resolve("dir11");
Files.createDirectory( d11 );
Path f111 = d11.resolve("file111.txt");
Path f112 = d11.resolve("file112.txt");
Files.write(f1,"file1".getBytes());
Files.write(f2, "file2".getBytes());
Files.write(f111, "file111".getBytes());
Files.write(f112, "file112".getBytes());
assertTrue(Files.exists(d1));
assertTrue(Files.exists(f1));
assertTrue(Files.exists(f2));
assertTrue(Files.exists(f111));
assertTrue(Files.exists(f112));

IOStatus status = FileUtils.deleteDirectoryWithStatus( td );
assertFalse(Files.exists(f1));
assertFalse(Files.exists(f2));
assertFalse(Files.exists(f111));
assertFalse(Files.exists(f112));
assertFalse(Files.exists(d1));

assertTrue( status.isOk( ) );
assertFalse( status.hasErrors( ) );
assertEquals( 7, status.getSuccessFiles( ).size( ) );
assertEquals( 0, status.getErrorFiles( ).size() );
}


@Test
public void testDeleteNonExist() throws IOException
{

+ 1
- 1
archiva-modules/archiva-base/archiva-common/src/test/resources/log4j2-test.xml View File

@@ -33,7 +33,7 @@
</appenders>
<loggers>
<logger name="org.apache.archiva" level="info"/>
<logger name="org.apache.archiva.repository.scanner" level="info"/>
<logger name="org.apache.archiva.common.utils" level="info"
<root level="error" includeLocation="true">
<appender-ref ref="console"/>
</root>

Loading…
Cancel
Save