📄 fileutil.java
字号:
"Backup file '" + backup_file
+ "' has been used for recovery purposes"));
// rewrite the good data, don't use backups here as we want to
// leave the original backup in place for the moment
writeResilientFile( parent_dir, file_name, res, false );
}else{
// neither main nor backup file ok, retry main file with recovery
res = readResilientFileSupport( parent_dir, file_name, true );
}
}
if ( res == null ){
res = new HashMap();
}
return( res );
}
// synchronised against writes to make sure we get a consistent view
private static Map
readResilientFileSupport(
File parent_dir,
String file_name,
boolean attempt_recovery )
{
try{
class_mon.enter();
try{
getReservedFileHandles();
Map res = null;
try{
res = readResilientFile( parent_dir, file_name, 0, false );
}catch( Throwable e ){
// ignore, it'll be rethrown if we can't recover below
}
if ( res == null && attempt_recovery ){
res = readResilientFile( parent_dir, file_name, 0, true );
if ( res != null ){
Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_WARNING,
"File '" + file_name + "' has been partially recovered, "
+ "information may have been lost!"));
}
}
return( res );
}catch( Throwable e ){
Debug.printStackTrace( e );
return( null );
}finally{
releaseReservedFileHandles();
}
}finally{
class_mon.exit();
}
}
private static Map
readResilientFile(
File parent_dir,
String file_name,
int fail_count,
boolean recovery_mode )
{
// logging in here is only done during "non-recovery" mode to prevent subsequent recovery
// attempts logging everything a second time.
// recovery-mode allows the decoding process to "succeed" with a partially recovered file
boolean using_backup = file_name.endsWith(".saving");
File file = new File( parent_dir, file_name );
//make sure the file exists and isn't zero-length
if ( (!file.exists()) || file.length() <= 1L ){
if ( using_backup ){
if ( !recovery_mode ){
if ( fail_count == 1 ){
// we only alert the user if at least one file was found and failed
// otherwise it could be start of day when neither file exists yet
Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_ERROR,
"Load of '" + file_name + "' fails, no usable file or backup"));
}else{
// drop this log, it doesn't really help to inform about the failure to
// find a .saving file
//if (Logger.isEnabled())
// Logger.log(new LogEvent(LOGID, LogEvent.LT_ERROR, "Load of '"
// + file_name + "' fails, file not found"));
}
}
return( null );
}
if ( !recovery_mode ){
// kinda confusing log this as we get it under "normal" circumstances (loading a config
// file that doesn't exist legitimately, e.g. shares or bad-ips
// if (Logger.isEnabled())
// Logger.log(new LogEvent(LOGID, LogEvent.LT_ERROR, "Load of '"
// + file_name + "' failed, " + "file not found or 0-sized."));
}
return( readResilientFile( parent_dir, file_name + ".saving", 0, recovery_mode ));
}
BufferedInputStream bin = null;
try{
int retry_limit = 5;
while(true){
try{
bin = new BufferedInputStream( new FileInputStream(file), 16384 );
break;
}catch( IOException e ){
if ( --retry_limit == 0 ){
throw( e );
}
if (Logger.isEnabled())
Logger.log(new LogEvent(LOGID, "Failed to open '" + file.toString()
+ "', retrying", e));
Thread.sleep(500);
}
}
BDecoder decoder = new BDecoder();
if ( recovery_mode ){
decoder.setRecoveryMode( true );
}
Map res = decoder.decodeStream(bin);
if ( using_backup && !recovery_mode ){
Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_WARNING,
"Load of '" + file_name.substring(0, file_name.length() - 7)
+ "' had to revert to backup file"));
}
return( res );
}catch( Throwable e ){
Debug.printStackTrace( e );
try {
if (bin != null){
bin.close();
bin = null;
}
} catch (Exception x) {
Debug.printStackTrace( x );
}
// if we're not recovering then backup the file
if ( !recovery_mode ){
// Occurs when file is there but b0rked
// copy it in case it actually contains useful data, so it won't be overwritten next save
File bad;
int bad_id = 0;
while(true){
File test = new File( parent_dir, file.getName() + ".bad" + (bad_id==0?"":(""+bad_id)));
if ( !test.exists()){
bad = test;
break;
}
bad_id++;
}
if (Logger.isEnabled())
Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING, "Read of '"
+ file_name + "' failed, decoding error. " + "Renaming to "
+ bad.getName()));
// copy it so its left in place for possible recovery
copyFile( file, bad );
}
if ( using_backup ){
if ( !recovery_mode ){
Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_ERROR,
"Load of '" + file_name + "' fails, no usable file or backup"));
}
return( null );
}
return( readResilientFile( parent_dir, file_name + ".saving", 1, recovery_mode ));
}finally{
try {
if (bin != null){
bin.close();
}
}catch (Exception e) {
Debug.printStackTrace( e );
}
}
}
private static void
getReservedFileHandles()
{
try{
class_mon.enter();
while( reserved_file_handles.size() > 0 ){
// System.out.println( "releasing reserved file handle");
InputStream is = (InputStream)reserved_file_handles.remove(0);
try{
is.close();
}catch( Throwable e ){
Debug.printStackTrace( e );
}
}
}finally{
class_mon.exit();
}
}
private static void
releaseReservedFileHandles()
{
try{
class_mon.enter();
File lock_file = new File(SystemProperties.getUserPath() + ".lock");
lock_file.createNewFile();
while( reserved_file_handles.size() < RESERVED_FILE_HANDLE_COUNT ){
// System.out.println( "getting reserved file handle");
InputStream is = new FileInputStream( lock_file );
reserved_file_handles.add(is);
}
}catch( Throwable e ){
Debug.printStackTrace( e );
}finally{
class_mon.exit();
}
}
/**
* Backup the given file to filename.bak, removing the old .bak file if necessary.
* If _make_copy is true, the original file will copied to backup, rather than moved.
* @param _filename name of file to backup
* @param _make_copy copy instead of move
*/
public static void backupFile( final String _filename, final boolean _make_copy ) {
backupFile( new File( _filename ), _make_copy );
}
/**
* Backup the given file to filename.bak, removing the old .bak file if necessary.
* If _make_copy is true, the original file will copied to backup, rather than moved.
* @param _file file to backup
* @param _make_copy copy instead of move
*/
public static void backupFile( final File _file, final boolean _make_copy ) {
if ( _file.length() > 0L ) {
File bakfile = new File( _file.getAbsolutePath() + ".bak" );
if ( bakfile.exists() ) bakfile.delete();
if ( _make_copy ) {
copyFile( _file, bakfile );
}
else {
_file.renameTo( bakfile );
}
}
}
/**
* Copy the given source file to the given destination file.
* Returns file copy success or not.
* @param _source_name source file name
* @param _dest_name destination file name
* @return true if file copy successful, false if copy failed
*/
public static boolean copyFile( final String _source_name, final String _dest_name ) {
return copyFile( new File(_source_name), new File(_dest_name));
}
/**
* Copy the given source file to the given destination file.
* Returns file copy success or not.
* @param _source source file
* @param _dest destination file
* @return true if file copy successful, false if copy failed
*/
/*
// FileChannel.transferTo() seems to fail under certain linux configurations.
public static boolean copyFile( final File _source, final File _dest ) {
FileChannel source = null;
FileChannel dest = null;
try {
if( _source.length() < 1L ) {
throw new IOException( _source.getAbsolutePath() + " does not exist or is 0-sized" );
}
source = new FileInputStream( _source ).getChannel();
dest = new FileOutputStream( _dest ).getChannel();
source.transferTo(0, source.size(), dest);
return true;
}
catch (Exception e) {
Debug.out( e );
return false;
}
finally {
try {
if (source != null) source.close();
if (dest != null) dest.close();
}
catch (Exception ignore) {}
}
}
*/
public static boolean copyFile( final File _source, final File _dest ) {
try {
copyFile( new FileInputStream( _source ), new FileOutputStream( _dest ) );
return true;
}
catch( Throwable e ) {
Debug.printStackTrace( e );
return false;
}
}
public static boolean copyFile( final File _source, final OutputStream _dest, boolean closeInputStream ) {
try {
copyFile( new FileInputStream( _source ), _dest, closeInputStream );
return true;
}
catch( Throwable e ) {
Debug.printStackTrace( e );
return false;
}
}
public static void
copyFile(
final InputStream _source,
final File _dest )
throws IOException
{
FileOutputStream dest = null;
try{
dest = new FileOutputStream(_dest);
copyFile( _source, dest, true );
}finally{
if ( dest != null ){
dest.close();
}
}
}
public static void
copyFile(
InputStream is,
OutputStream os )
throws IOException {
copyFile(is,os,true);
}
public static void
copyFile(
InputStream is,
OutputStream os,
boolean closeInputStream )
throws IOException
{
try{
if ( !(is instanceof BufferedInputStream )){
is = new BufferedInputStream(is);
}
byte[] buffer = new byte[65536*2];
while(true){
int len = is.read(buffer);
if ( len == -1 ){
break;
}
os.write( buffer, 0, len );
}
}finally{
try{
if(closeInputStream){
is.close();
}
}catch( IOException e ){
}
os.close();
}
}
public static void
copyFileOrDirectory(
File from_file_or_dir,
File to_parent_dir )
throws IOException
{
if ( !from_file_or_dir.exists()){
throw( new IOException( "File '" + from_file_or_dir.toString() + "' doesn't exist" ));
}
if ( !to_parent_dir.exists()){
throw( new IOException( "File '" + to_parent_dir.toString() + "' doesn't exist" ));
}
if ( !to_parent_dir.isDirectory()){
throw( new IOException( "File '" + to_parent_dir.toString() + "' is not a directory" ));
}
if ( from_file_or_dir.isDirectory()){
File[] files = from_file_or_dir.listFiles();
File new_parent = new File( to_parent_dir, from_file_or_dir.getName());
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -