/*
* This file is a part of GrantMaster.
* Copyright (C) 2015 Gabor Feher <feherga@gmail.com>
*
* This program 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 com.github.gaborfeher.grantmaster.framework.utils;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.StandardOpenOption;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A mechanism for locking files while they are being "open"
* from the point of view of the user. (The program closes them after reading
* but the user still think they are open.)
*/
public class MyFileLock {
private static final Logger logger = LoggerFactory.getLogger(MyFileLock.class);
/**
* Unique identifier of this lock. We write it into the lock file so that it
* can be verified that noone else has changed it.
* Idea: use the name of the temp file used here. (TODO(gaborfeher))
*/
private String id;
/**
* The file that represents the lock. Normally this is originalFile_.lck
*/
private File lockFile;
private MyFileLock() {
}
private static File getLockFile(File original) {
String fname = original.getName();
fname = fname.replaceAll("\\.[^.]*$", "_.lck");
return new File(original.getParentFile(), fname);
}
public static void breakLock(File file) {
File lockFile = getLockFile(file);
lockFile.delete();
}
public static MyFileLock lockFile(File file) {
MyFileLock lock = new MyFileLock();
lock.lockFile = getLockFile(file);
if (lock.lockFile.exists()) {
return null;
}
try (FileChannel fileChannel = FileChannel.open(
lock.lockFile.toPath(),
StandardOpenOption.CREATE_NEW,
StandardOpenOption.WRITE)) {
// Note: checking the existence and creating the file is claimed to be
// atomic by JavaDoc. Therefore, we don't need system-level locking.
// TODO(gaborfeher): Investigate DropBox/Google Drive
lock.id = UUID.randomUUID().toString();
fileChannel.write(ByteBuffer.wrap(lock.id.getBytes()));
return lock;
} catch (IOException ex) {
logger.error(null, ex);
return null;
}
}
public boolean verify() {
try (FileChannel fileChannel = FileChannel.open(lockFile.toPath(), StandardOpenOption.READ)) {
ByteBuffer lockFileContent = ByteBuffer.allocate(100);
int numBytesRead = fileChannel.read(lockFileContent);
fileChannel.close();
String fileId = new String(lockFileContent.array(), 0, numBytesRead);
boolean result = fileId.equals(id);
return result;
} catch (IOException ex) {
logger.error(null, ex);
return false;
}
}
public boolean release() {
if (!verify()) {
// The lock is not ours.
return false;
}
lockFile.delete();
lockFile = null;
id = null;
return true;
}
}