Finally switching the file lock to java.nio

This commit is contained in:
Martin Stockhammer 2017-09-20 22:34:10 +02:00
parent 5806dc2980
commit bed24eac44
7 changed files with 75 additions and 104 deletions

View File

@ -24,11 +24,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -43,7 +44,7 @@ public class DefaultFileLockManager
// TODO currently we create lock for read and write!! // TODO currently we create lock for read and write!!
// the idea could be to store lock here with various clients read/write // the idea could be to store lock here with various clients read/write
// only read could be a more simple lock and acquire a write lock means waiting the end of all reading threads // only read could be a more simple lock and acquire a write lock means waiting the end of all reading threads
private static final ConcurrentMap<File, Lock> lockFiles = new ConcurrentHashMap<File, Lock>( 64 ); private static final ConcurrentMap<Path, Lock> lockFiles = new ConcurrentHashMap<Path, Lock>( 64 );
private boolean skipLocking = true; private boolean skipLocking = true;
@ -53,7 +54,7 @@ public class DefaultFileLockManager
@Override @Override
public Lock readFileLock( File file ) public Lock readFileLock( Path file )
throws FileLockException, FileLockTimeoutException throws FileLockException, FileLockTimeoutException
{ {
if ( skipLocking ) if ( skipLocking )
@ -63,7 +64,11 @@ public class DefaultFileLockManager
} }
StopWatch stopWatch = new StopWatch(); StopWatch stopWatch = new StopWatch();
boolean acquired = false; boolean acquired = false;
mkdirs( file.getParentFile() ); try {
mkdirs(file.getParent());
} catch (IOException e) {
throw new FileLockException("Could not create directories "+file.getParent(), e);
}
Lock lock = null; Lock lock = null;
@ -116,14 +121,9 @@ public class DefaultFileLockManager
lock=null; lock=null;
} }
} }
catch ( FileNotFoundException e ) catch ( FileNotFoundException | NoSuchFileException e )
{ {
// can happen if an other thread has deleted the file
// close RandomAccessFile!!!
if ( lock != null )
{
closeQuietly( lock.getRandomAccessFile() );
}
log.debug( "read Lock skip: {} try to create file", e.getMessage() ); log.debug( "read Lock skip: {} try to create file", e.getMessage() );
createNewFileQuietly( file ); createNewFileQuietly( file );
} }
@ -143,7 +143,7 @@ public class DefaultFileLockManager
@Override @Override
public Lock writeFileLock( File file ) public Lock writeFileLock( Path file )
throws FileLockException, FileLockTimeoutException throws FileLockException, FileLockTimeoutException
{ {
if ( skipLocking ) if ( skipLocking )
@ -151,7 +151,11 @@ public class DefaultFileLockManager
return new Lock( file ); return new Lock( file );
} }
mkdirs( file.getParentFile() ); try {
mkdirs( file.getParent() );
} catch (IOException e) {
throw new FileLockException("Could not create directory "+file.getParent(), e);
}
StopWatch stopWatch = new StopWatch(); StopWatch stopWatch = new StopWatch();
boolean acquired = false; boolean acquired = false;
@ -207,14 +211,9 @@ public class DefaultFileLockManager
lock=null; lock=null;
} }
} }
catch ( FileNotFoundException e ) catch ( FileNotFoundException | NoSuchFileException e )
{ {
// can happen if an other thread has deleted the file
// close RandomAccessFile!!!
if ( lock != null )
{
closeQuietly( lock.getRandomAccessFile() );
}
log.debug( "write Lock skip: {} try to create file", e.getMessage() ); log.debug( "write Lock skip: {} try to create file", e.getMessage() );
createNewFileQuietly( file ); createNewFileQuietly( file );
} }
@ -233,28 +232,11 @@ public class DefaultFileLockManager
} }
private void closeQuietly( RandomAccessFile randomAccessFile ) private void createNewFileQuietly( Path file )
{
if ( randomAccessFile == null )
{
return;
}
try
{
randomAccessFile.close();
}
catch ( IOException e )
{
// ignore
}
}
private void createNewFileQuietly( File file )
{ {
try try
{ {
file.createNewFile(); Files.createFile(file);
} }
catch ( IOException e ) catch ( IOException e )
{ {
@ -297,9 +279,8 @@ public class DefaultFileLockManager
lockFiles.clear(); lockFiles.clear();
} }
private boolean mkdirs( File directory ) private Path mkdirs( Path directory ) throws IOException {
{ return Files.createDirectories(directory);
return directory.mkdirs();
} }
@Override @Override

View File

@ -19,8 +19,8 @@ package org.apache.archiva.common.filelock;
* under the License. * under the License.
*/ */
import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.nio.file.Path;
/** /**
* @author Olivier Lamy * @author Olivier Lamy
@ -35,7 +35,7 @@ public interface FileLockManager
* @throws FileLockException * @throws FileLockException
* @throws FileLockTimeoutException * @throws FileLockTimeoutException
*/ */
Lock writeFileLock( File file ) Lock writeFileLock( Path file )
throws FileLockException, FileLockTimeoutException; throws FileLockException, FileLockTimeoutException;
/** /**
@ -45,7 +45,7 @@ public interface FileLockManager
* @throws FileLockException * @throws FileLockException
* @throws FileLockTimeoutException * @throws FileLockTimeoutException
*/ */
Lock readFileLock( File file ) Lock readFileLock( Path file )
throws FileLockException, FileLockTimeoutException; throws FileLockException, FileLockTimeoutException;
void release( Lock lock ) void release( Lock lock )

View File

@ -20,12 +20,11 @@ package org.apache.archiva.common.filelock;
*/ */
import java.io.Closeable; import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.nio.channels.FileLock; import java.nio.channels.FileLock;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@ -37,7 +36,7 @@ import java.util.concurrent.atomic.AtomicInteger;
*/ */
public class Lock public class Lock
{ {
private File file; private Path file;
private AtomicBoolean write; private AtomicBoolean write;
@ -45,25 +44,22 @@ public class Lock
private FileLock fileLock; private FileLock fileLock;
private RandomAccessFile randomAccessFile;
private FileChannel fileChannel; private FileChannel fileChannel;
public Lock( File file ) public Lock( Path file )
{ {
this.file = file; this.file = file;
} }
public Lock( File file, boolean write ) public Lock( Path file, boolean write )
throws FileNotFoundException throws IOException
{ {
this.file = file; this.file = file;
this.write = new AtomicBoolean( write ); this.write = new AtomicBoolean( write );
randomAccessFile = new RandomAccessFile( file, write ? "rw" : "r" ); fileChannel = write ? FileChannel.open(file, StandardOpenOption.WRITE, StandardOpenOption.READ) : FileChannel.open(file, StandardOpenOption.READ);
fileChannel = randomAccessFile.getChannel();
} }
public File getFile() public Path getFile()
{ {
return file; return file;
} }
@ -73,7 +69,7 @@ public class Lock
return write; return write;
} }
public void setFile( File file ) public void setFile( Path file )
{ {
this.file = file; this.file = file;
} }
@ -122,7 +118,6 @@ public class Lock
} }
closeQuietly( fileChannel ); closeQuietly( fileChannel );
closeQuietly( randomAccessFile );
fileClients.remove( Thread.currentThread() ); fileClients.remove( Thread.currentThread() );
@ -144,10 +139,7 @@ public class Lock
} }
protected RandomAccessFile getRandomAccessFile()
{
return randomAccessFile;
}
private void closeQuietly( Closeable closeable ) private void closeQuietly( Closeable closeable )
{ {

View File

@ -32,11 +32,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
@ -66,9 +62,9 @@ public class DefaultFileLockManagerTest {
FileLockManager fileLockManager; FileLockManager fileLockManager;
File file = new File(System.getProperty("buildDirectory"), "foo.txt"); Path file = Paths.get(System.getProperty("buildDirectory"), "foo.txt");
File largeJar = new File(System.getProperty("basedir"), "src/test/cassandra-all-2.0.3.jar"); Path largeJar = Paths.get(System.getProperty("basedir"), "src/test/cassandra-all-2.0.3.jar");
ConcurrentFileWrite(FileLockManager fileLockManager) ConcurrentFileWrite(FileLockManager fileLockManager)
throws IOException { throws IOException {
@ -104,8 +100,8 @@ public class DefaultFileLockManagerTest {
logger.info("thread1"); logger.info("thread1");
Lock lock = fileLockManager.writeFileLock(this.file); Lock lock = fileLockManager.writeFileLock(this.file);
try { try {
lock.getFile().delete(); Files.deleteIfExists(lock.getFile());
copyFile(largeJar.toPath(), lock.getFile().toPath()); copyFile(largeJar, lock.getFile());
} finally { } finally {
fileLockManager.release(lock); fileLockManager.release(lock);
} }
@ -124,8 +120,8 @@ public class DefaultFileLockManagerTest {
logger.info("thread2"); logger.info("thread2");
Lock lock = fileLockManager.writeFileLock(this.file); Lock lock = fileLockManager.writeFileLock(this.file);
try { try {
lock.getFile().delete(); Files.deleteIfExists(lock.getFile());
copyFile(largeJar.toPath(), lock.getFile().toPath()); copyFile(largeJar, lock.getFile());
} finally { } finally {
fileLockManager.release(lock); fileLockManager.release(lock);
} }
@ -147,7 +143,7 @@ public class DefaultFileLockManagerTest {
Path outFile = null; Path outFile = null;
try { try {
outFile = Files.createTempFile("foo", ".jar"); outFile = Files.createTempFile("foo", ".jar");
Files.copy(Paths.get(lock.getFile().getPath()), Files.copy(lock.getFile(),
Files.newOutputStream(outFile)); Files.newOutputStream(outFile));
} finally { } finally {
fileLockManager.release(lock); fileLockManager.release(lock);
@ -169,8 +165,8 @@ public class DefaultFileLockManagerTest {
logger.info("thread4"); logger.info("thread4");
Lock lock = fileLockManager.writeFileLock(this.file); Lock lock = fileLockManager.writeFileLock(this.file);
try { try {
lock.getFile().delete(); Files.deleteIfExists(lock.getFile());
copyFile(largeJar.toPath(), lock.getFile().toPath()); copyFile(largeJar, lock.getFile());
} finally { } finally {
fileLockManager.release(lock); fileLockManager.release(lock);
} }
@ -190,8 +186,8 @@ public class DefaultFileLockManagerTest {
logger.info("thread5"); logger.info("thread5");
Lock lock = fileLockManager.writeFileLock(this.file); Lock lock = fileLockManager.writeFileLock(this.file);
try { try {
lock.getFile().delete(); Files.deleteIfExists(lock.getFile());
copyFile(largeJar.toPath(), lock.getFile().toPath()); copyFile(largeJar, lock.getFile());
} finally { } finally {
fileLockManager.release(lock); fileLockManager.release(lock);
} }
@ -213,7 +209,7 @@ public class DefaultFileLockManagerTest {
Path outFile = null; Path outFile = null;
try { try {
outFile = Files.createTempFile("foo", ".jar"); outFile = Files.createTempFile("foo", ".jar");
Files.copy(lock.getFile().toPath(), Files.newOutputStream( outFile )); Files.copy(lock.getFile(), Files.newOutputStream( outFile ));
} finally { } finally {
fileLockManager.release(lock); fileLockManager.release(lock);
if (outFile!=null) Files.delete( outFile ); if (outFile!=null) Files.delete( outFile );
@ -234,8 +230,8 @@ public class DefaultFileLockManagerTest {
logger.info("thread7"); logger.info("thread7");
Lock lock = fileLockManager.writeFileLock(this.file); Lock lock = fileLockManager.writeFileLock(this.file);
try { try {
lock.getFile().delete(); Files.deleteIfExists(lock.getFile());
copyFile(largeJar.toPath(), lock.getFile().toPath()); copyFile(largeJar, lock.getFile());
} finally { } finally {
fileLockManager.release(lock); fileLockManager.release(lock);
} }
@ -257,7 +253,7 @@ public class DefaultFileLockManagerTest {
Path outFile = null; Path outFile = null;
try { try {
outFile = Files.createTempFile("foo", ".jar"); outFile = Files.createTempFile("foo", ".jar");
Files.copy(lock.getFile().toPath(), Files.newOutputStream( outFile )); Files.copy(lock.getFile(), Files.newOutputStream( outFile ));
} finally { } finally {
fileLockManager.release(lock); fileLockManager.release(lock);
if (outFile!=null) Files.delete( outFile ); if (outFile!=null) Files.delete( outFile );
@ -278,8 +274,8 @@ public class DefaultFileLockManagerTest {
logger.info("thread9"); logger.info("thread9");
Lock lock = fileLockManager.writeFileLock(this.file); Lock lock = fileLockManager.writeFileLock(this.file);
try { try {
lock.getFile().delete(); Files.deleteIfExists(lock.getFile());
copyFile(largeJar.toPath(), lock.getFile().toPath()); copyFile(largeJar, lock.getFile());
} finally { } finally {
fileLockManager.release(lock); fileLockManager.release(lock);
} }
@ -300,7 +296,7 @@ public class DefaultFileLockManagerTest {
Path outFile = null; Path outFile = null;
try { try {
outFile = Files.createTempFile("foo", ".jar"); outFile = Files.createTempFile("foo", ".jar");
Files.copy(lock.getFile().toPath(), Files.newOutputStream( outFile )); Files.copy(lock.getFile(), Files.newOutputStream( outFile ));
} finally { } finally {
fileLockManager.release(lock); fileLockManager.release(lock);
if (outFile!=null) Files.delete(outFile); if (outFile!=null) Files.delete(outFile);

View File

@ -29,11 +29,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.FileSystemException; import java.nio.file.*;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
/** /**
* @author Olivier Lamy * @author Olivier Lamy
@ -65,16 +62,16 @@ public class DefaultFileLockManagerTimeoutTest
{ {
try { try {
File file = new File(System.getProperty("buildDirectory"), "foo.txt"); Path file = Paths.get(System.getProperty("buildDirectory"), "foo.txt");
Files.deleteIfExists(file.toPath()); Files.deleteIfExists(file);
File largeJar = new File(System.getProperty("basedir"), "src/test/cassandra-all-2.0.3.jar"); Path largeJar = Paths.get(System.getProperty("basedir"), "src/test/cassandra-all-2.0.3.jar");
Lock lock = fileLockManager.writeFileLock(file); Lock lock = fileLockManager.writeFileLock(file);
try { try {
Files.copy(largeJar.toPath(), lock.getFile().toPath(), StandardCopyOption.REPLACE_EXISTING); Files.copy(largeJar, lock.getFile(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) { } catch (IOException e) {
logger.warn("Copy failed {}", e.getMessage()); logger.warn("Copy failed {}", e.getMessage());
// On windows a FileSystemException is thrown // On windows a FileSystemException is thrown

View File

@ -1089,17 +1089,22 @@ public class DefaultRepositoryProxyConnectors
Lock lock; Lock lock;
try try
{ {
lock = fileLockManager.writeFileLock( target.toFile() ); lock = fileLockManager.writeFileLock( target );
if ( lock.getFile().exists() && !lock.getFile().delete() ) try {
{ Files.deleteIfExists(lock.getFile());
} catch (IOException e) {
throw new ProxyException( "Unable to overwrite existing target file: " + target.toAbsolutePath() ); throw new ProxyException( "Unable to overwrite existing target file: " + target.toAbsolutePath() );
} }
lock.getFile().getParentFile().mkdirs(); try {
Files.createDirectories(lock.getFile().getParent());
} catch (IOException e) {
throw new ProxyException("Unable to create parent directory "+lock.getFile().getParent());
}
try try
{ {
Files.move(temp, lock.getFile().toPath() ); Files.move(temp, lock.getFile() );
} }
catch ( IOException e ) catch ( IOException e )
{ {
@ -1107,14 +1112,14 @@ public class DefaultRepositoryProxyConnectors
try try
{ {
Files.copy( temp, lock.getFile().toPath() ); Files.copy( temp, lock.getFile());
} }
catch ( IOException e2 ) catch ( IOException e2 )
{ {
if ( lock.getFile().exists() ) if ( Files.exists(lock.getFile()) )
{ {
log.debug( "Tried to copy file {} to {} but file with this name already exists.", log.debug( "Tried to copy file {} to {} but file with this name already exists.",
temp.getFileName(), lock.getFile().getAbsolutePath() ); temp.getFileName(), lock.getFile().toAbsolutePath() );
} }
else else
{ {

View File

@ -212,8 +212,8 @@ public class ArchivaDavResource
{ {
if ( !isCollection() && outputContext.hasStream() ) if ( !isCollection() && outputContext.hasStream() )
{ {
Lock lock = fileLockManager.readFileLock( localResource.toFile() ); Lock lock = fileLockManager.readFileLock( localResource );
try (InputStream is = Files.newInputStream( lock.getFile().toPath() )) try (InputStream is = Files.newInputStream( lock.getFile()))
{ {
IOUtils.copy( is, outputContext.getOutputStream() ); IOUtils.copy( is, outputContext.getOutputStream() );
} }