
org.neo4j.io.fs.FileUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of neo4j-io Show documentation
Show all versions of neo4j-io Show documentation
Input/output abstraction layer for Neo4j.
/*
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.neo4j.io.fs;
import org.apache.commons.lang3.SystemUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import static java.nio.file.StandardOpenOption.APPEND;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.DSYNC;
import static java.nio.file.StandardOpenOption.READ;
import static java.nio.file.StandardOpenOption.SYNC;
import static java.nio.file.StandardOpenOption.WRITE;
public class FileUtils
{
private static final int WINDOWS_RETRY_COUNT = 5;
public static void deleteRecursively( File directory ) throws IOException
{
if ( ! directory.exists() )
{
return;
}
Path path = directory.toPath();
deletePathRecursively( path );
}
public static void deletePathRecursively( Path path ) throws IOException
{
Files.walkFileTree( path, new SimpleFileVisitor()
{
@Override
public FileVisitResult visitFile( Path file, BasicFileAttributes attrs ) throws IOException
{
deleteFileWithRetries( file, 0 );
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory( Path dir, IOException e ) throws IOException
{
if ( e != null )
{
throw e;
}
Files.delete( dir );
return FileVisitResult.CONTINUE;
}
} );
}
public static boolean deleteFile( File file )
{
if ( !file.exists() )
{
return true;
}
int count = 0;
boolean deleted;
do
{
deleted = file.delete();
if ( !deleted )
{
count++;
waitAndThenTriggerGC();
}
}
while ( !deleted && count <= WINDOWS_RETRY_COUNT );
return deleted;
}
/**
* Utility method that moves a file from its current location to the
* new target location. If rename fails (for example if the target is
* another disk) a copy/delete will be performed instead. This is not a rename,
* use {@link #renameFile(File, File)} instead.
*
* @param toMove The File object to move.
* @param target Target file to move to.
* @throws IOException
*/
public static void moveFile( File toMove, File target ) throws IOException
{
if ( !toMove.exists() )
{
throw new FileNotFoundException( "Source file[" + toMove.getAbsolutePath()
+ "] not found" );
}
if ( target.exists() )
{
throw new IOException( "Target file[" + target.getAbsolutePath()
+ "] already exists" );
}
if ( toMove.renameTo( target ) )
{
return;
}
if ( toMove.isDirectory() )
{
Files.createDirectories( target.toPath() );
copyRecursively( toMove, target );
deleteRecursively( toMove );
}
else
{
copyFile( toMove, target );
deleteFile( toMove );
}
}
/**
* Utility method that moves a file from its current location to the
* provided target directory. If rename fails (for example if the target is
* another disk) a copy/delete will be performed instead. This is not a rename,
* use {@link #renameFile(File, File)} instead.
*
* @param toMove The File object to move.
* @param targetDirectory the destination directory
* @return the new file, null iff the move was unsuccessful
* @throws IOException
*/
public static File moveFileToDirectory( File toMove, File targetDirectory ) throws IOException
{
if ( !targetDirectory.isDirectory() )
{
throw new IllegalArgumentException(
"Move target must be a directory, not " + targetDirectory );
}
File target = new File( targetDirectory, toMove.getName() );
moveFile( toMove, target );
return target;
}
public static boolean renameFile( File srcFile, File renameToFile ) throws IOException
{
if ( !srcFile.exists() )
{
throw new FileNotFoundException( "Source file[" + srcFile.getName() + "] not found" );
}
if ( renameToFile.exists() )
{
throw new FileNotFoundException( "Target file[" + renameToFile.getName() + "] already exists" );
}
if ( !renameToFile.getParentFile().isDirectory() )
{
throw new FileNotFoundException( "Target directory[" + renameToFile.getParent() + "] does not exists" );
}
int count = 0;
boolean renamed;
do
{
renamed = srcFile.renameTo( renameToFile );
if ( !renamed )
{
count++;
waitAndThenTriggerGC();
}
}
while ( !renamed && count <= WINDOWS_RETRY_COUNT );
return renamed;
}
public static void truncateFile( SeekableByteChannel fileChannel, long position )
throws IOException
{
int count = 0;
boolean success = false;
IOException cause = null;
do
{
count++;
try
{
fileChannel.truncate( position );
success = true;
}
catch ( IOException e )
{
cause = e;
}
}
while ( !success && count <= WINDOWS_RETRY_COUNT );
if ( !success )
{
throw cause;
}
}
public static void truncateFile( File file, long position ) throws IOException
{
try ( RandomAccessFile access = new RandomAccessFile( file, "rw" ) )
{
truncateFile( access.getChannel(), position );
}
}
/*
* See http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4715154.
*/
private static void waitAndThenTriggerGC()
{
try
{
Thread.sleep( 500 );
}
catch ( InterruptedException ee )
{
Thread.interrupted();
} // ok
System.gc();
}
public static String fixSeparatorsInPath( String path )
{
String fileSeparator = System.getProperty( "file.separator" );
if ( "\\".equals( fileSeparator ) )
{
path = path.replace( '/', '\\' );
}
else if ( "/".equals( fileSeparator ) )
{
path = path.replace( '\\', '/' );
}
return path;
}
public static void copyFile( File srcFile, File dstFile ) throws IOException
{
//noinspection ResultOfMethodCallIgnored
dstFile.getParentFile().mkdirs();
try ( FileInputStream input = new FileInputStream( srcFile );
FileOutputStream output = new FileOutputStream( dstFile ); )
{
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int bytesRead;
while ( (bytesRead = input.read( buffer )) != -1 )
{
output.write( buffer, 0, bytesRead );
}
}
catch ( IOException e )
{
// Because the message from this cause may not mention which file it's about
throw new IOException( "Could not copy '" + srcFile + "' to '" + dstFile + "'", e );
}
}
public static void copyRecursively( File fromDirectory, File toDirectory ) throws IOException
{
copyRecursively( fromDirectory, toDirectory, null );
}
public static void copyRecursively( File fromDirectory, File toDirectory, FileFilter filter) throws IOException
{
for ( File fromFile : fromDirectory.listFiles( filter ) )
{
File toFile = new File( toDirectory, fromFile.getName() );
if ( fromFile.isDirectory() )
{
Files.createDirectories( toFile.toPath() );
copyRecursively( fromFile, toFile, filter );
}
else
{
copyFile( fromFile, toFile );
}
}
}
public static void writeToFile( File target, String text, boolean append ) throws IOException
{
if ( !target.exists() )
{
Files.createDirectories( target.getParentFile().toPath() );
//noinspection ResultOfMethodCallIgnored
target.createNewFile();
}
try ( Writer out = new OutputStreamWriter( new FileOutputStream( target, append ), StandardCharsets.UTF_8 ) )
{
out.write( text );
}
}
public static BufferedReader newBufferedFileReader( File file, Charset charset ) throws FileNotFoundException
{
return new BufferedReader( new InputStreamReader( new FileInputStream( file ), charset) );
}
public static PrintWriter newFilePrintWriter( File file, Charset charset ) throws FileNotFoundException
{
return new PrintWriter( new OutputStreamWriter( new FileOutputStream( file, true ), charset) );
}
public static File path( String root, String... path )
{
return path( new File( root ), path );
}
public static File path( File root, String... path )
{
for ( String part : path )
{
root = new File( root, part );
}
return root;
}
public interface FileOperation
{
void perform() throws IOException;
}
public static void windowsSafeIOOperation( FileOperation operation ) throws IOException
{
IOException storedIoe = null;
for ( int i = 0; i < 10; i++ )
{
try
{
operation.perform();
return;
}
catch ( IOException e )
{
storedIoe = e;
System.gc();
}
}
throw storedIoe;
}
public interface LineListener
{
void line( String line );
}
public static LineListener echo( final PrintStream target )
{
return new LineListener()
{
@Override
public void line( String line )
{
target.println( line );
}
};
}
public static void readTextFile( File file, LineListener listener ) throws IOException
{
try(BufferedReader reader = new BufferedReader( new FileReader( file ) );)
{
String line;
while ( (line = reader.readLine()) != null )
{
listener.line( line );
}
}
}
public static String readTextFile( File file, Charset charset ) throws IOException
{
StringBuilder out = new StringBuilder();
for ( String s : Files.readAllLines( file.toPath(), charset ) )
{
out.append( s ).append( "\n" );
}
return out.toString();
}
private static void deleteFileWithRetries( Path file, int tries ) throws IOException
{
try
{
Files.delete( file );
}
catch ( IOException e )
{
if ( SystemUtils.IS_OS_WINDOWS && mayBeWindowsMemoryMappedFileReleaseProblem( e ) )
{
if ( tries >= WINDOWS_RETRY_COUNT )
{
throw new MaybeWindowsMemoryMappedFileReleaseProblem(e);
}
waitAndThenTriggerGC();
deleteFileWithRetries( file, tries + 1 );
}
else
{
throw e;
}
}
}
private static boolean mayBeWindowsMemoryMappedFileReleaseProblem( IOException e )
{
return e.getMessage().contains( "The process cannot access the file because it is being used by another process." );
}
public static class MaybeWindowsMemoryMappedFileReleaseProblem extends IOException
{
public MaybeWindowsMemoryMappedFileReleaseProblem( IOException e )
{
super(e);
}
}
/**
* Given a directory and a path under it, return filename of the path
* relative to the directory.
*
* @param baseDir The base directory, containing the storeFile
* @param storeFile The store file path, must be contained under
* baseDir
* @return The relative path of storeFile
to
* baseDir
* @throws IOException As per {@link File#getCanonicalPath()}
*/
public static String relativePath( File baseDir, File storeFile )
throws IOException
{
String prefix = baseDir.getCanonicalPath();
String path = storeFile.getCanonicalPath();
if ( !path.startsWith( prefix ) )
{
throw new FileNotFoundException();
}
path = path.substring( prefix.length() );
if ( path.startsWith( File.separator ) )
{
return path.substring( 1 );
}
return path;
}
// TODO javadoc what this one does. It comes from Serverutil initially.
public static File getMostCanonicalFile( File file )
{
try
{
return file.getCanonicalFile().getAbsoluteFile();
}
catch ( IOException e )
{
return file.getAbsoluteFile();
}
}
public static void writeAll( FileChannel channel, ByteBuffer src, long position ) throws IOException
{
long filePosition = position;
long expectedEndPosition = filePosition + src.limit() - src.position();
int bytesWritten;
while((filePosition += (bytesWritten = channel.write( src, filePosition ))) < expectedEndPosition)
{
if( bytesWritten <= 0 )
{
throw new IOException( "Unable to write to disk, reported bytes written was " + bytesWritten );
}
}
}
public static void writeAll( FileChannel channel, ByteBuffer src ) throws IOException
{
long bytesToWrite = src.limit() - src.position();
int bytesWritten;
while((bytesToWrite -= (bytesWritten = channel.write( src ))) > 0)
{
if( bytesWritten <= 0 )
{
throw new IOException( "Unable to write to disk, reported bytes written was " + bytesWritten );
}
}
}
public static OpenOption[] convertOpenMode( String mode )
{
OpenOption[] options;
switch ( mode )
{
case "r": options = new OpenOption[]{READ}; break;
case "rw": options = new OpenOption[] {CREATE, READ, WRITE}; break;
case "rws": options = new OpenOption[] {CREATE, READ, WRITE, SYNC}; break;
case "rwd": options = new OpenOption[] {CREATE, READ, WRITE, DSYNC}; break;
default: throw new IllegalArgumentException( "Unsupported mode: " + mode );
}
return options;
}
public static FileChannel open( Path path, String mode ) throws IOException
{
return FileChannel.open( path, convertOpenMode( mode ) );
}
public static InputStream openAsInputStream( Path path ) throws IOException
{
return Files.newInputStream( path, READ );
}
/**
* Check if directory is empty.
* @param directory - directory to check
* @return false if directory exists and empty, true otherwise.
* @throws IllegalArgumentException if specified directory represent a file
* @throws IOException if some problem encountered during reading directory content
*/
public static boolean isEmptyDirectory( File directory ) throws IOException
{
if ( directory.exists() )
{
if ( !directory.isDirectory() )
{
throw new IllegalArgumentException( "Expected directory, but was file: " + directory );
}
else
{
try ( DirectoryStream directoryStream = Files.newDirectoryStream( directory.toPath() ) )
{
return !directoryStream.iterator().hasNext();
}
}
}
return true;
}
public static OutputStream openAsOutputStream( Path path, boolean append ) throws IOException
{
OpenOption[] options;
if ( append )
{
options = new OpenOption[] {CREATE, WRITE, APPEND};
}
else
{
options = new OpenOption[] {CREATE, WRITE};
}
return Files.newOutputStream( path, options );
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy