/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.impl.nioneo.store;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.OverlappingFileLockException;
import org.neo4j.kernel.Config;
public abstract class FileLock
{
private static FileLock wrapOrNull( final java.nio.channels.FileLock lock )
{
if ( lock == null )
{
return null;
}
return new FileLock()
{
@Override
public void release() throws IOException
{
lock.release();
}
};
}
public static FileLock getOsSpecificFileLock( String fileName, FileChannel channel )
throws IOException
{
if ( Config.osIsWindows() )
{
// Only grab one lock, say for the "neostore" file
if ( fileName.endsWith( NeoStore.DEFAULT_NAME ) )
{
return getLockFileBasedFileLock( new File( fileName ).getParentFile() );
}
// For the rest just return placebo locks
return new PlaceboFileLock();
}
else if ( fileName.endsWith( NeoStore.DEFAULT_NAME ) )
{
FileLock regular = wrapOrNull( channel.tryLock() );
if ( regular == null ) return null;
FileLock extra = getLockFileBasedFileLock( new File( fileName ).getParentFile() );
if ( extra == null )
{
regular.release();
return null;
}
return new DoubleFileLock( regular, extra );
}
else
{
return wrapOrNull( channel.tryLock() );
}
}
private static FileLock getLockFileBasedFileLock( File storeDir ) throws IOException
{
File lockFile = new File( storeDir, "lock" );
if ( !lockFile.exists() )
{
if ( !lockFile.createNewFile() )
{
throw new IOException( "Couldn't create lock file " + lockFile.getAbsolutePath() );
}
}
FileChannel fileChannel = new RandomAccessFile( lockFile, "rw" ).getChannel();
java.nio.channels.FileLock fileChannelLock = null;
try
{
fileChannelLock = fileChannel.tryLock();
}
catch ( OverlappingFileLockException e )
{
// OK, let fileChannelLock continue to be null and we'll deal with it below
}
if ( fileChannelLock == null )
{
fileChannel.close();
return null;
}
return new WindowsFileLock( lockFile, fileChannel, fileChannelLock );
}
public abstract void release() throws IOException;
private static class PlaceboFileLock extends FileLock
{
@Override
public void release() throws IOException
{
}
}
private static class DoubleFileLock extends FileLock
{
private final FileLock regular;
private final FileLock extra;
DoubleFileLock( FileLock regular, FileLock extra )
{
this.regular = regular;
this.extra = extra;
}
@Override
public void release() throws IOException
{
regular.release();
extra.release();
}
}
private static class WindowsFileLock extends FileLock
{
private final File lockFile;
private final FileChannel fileChannel;
private final java.nio.channels.FileLock fileChannelLock;
public WindowsFileLock( File lockFile, FileChannel fileChannel, java.nio.channels.FileLock lock )
throws IOException
{
this.lockFile = lockFile;
this.fileChannel = fileChannel;
this.fileChannelLock = lock;
}
@Override
public void release() throws IOException
{
try
{
fileChannelLock.release();
}
finally
{
try
{
fileChannel.close();
}
finally
{
if ( !lockFile.delete() )
{
throw new IOException( "Couldn't delete lock file " + lockFile.getAbsolutePath() );
}
}
}
}
}
}