Sometimes in software development you need to run only one application instance. If you want to do this in Java you will have to think about realization, because this feature is not complete in standard Java language library. In the web you can find 2 basic approaches to make it work: port lock and file lock ideas. I've try to create really reusable piece of code :) which can handle cross application lock.
My code is based on the file lock idea. I think that port lock usage is not really reliable because of conflict possibility in ports usage.
Don't know why, but it always suggest to put lock file in the same directory with executable file. In this case you still be able to run another copy of application from another folder where your program was copied. Also there is possibility that your application will not have write access to the directory where it placed.
I'm try to solve this problems in this way. Lock file will be created in temporary system folder. File name will be generated as md5 hash, based on some application unique keyword. Another application instances will try to search for the appropriate lock file, and than decide what to do.
This is how my lock class usage sample
// Try to get LOCK //
if (!AppLock.setLock("MY_CUSTOM_LOCK_KEY")) {
throw new RuntimeException("Only one application instance may run at the same time!");
}
// YOUR CODE
}
finally{
AppLock.releaseLock(); // Release lock
}
AppLock class has only 2 static methods: setLock and ReleaseLock. Hope you will not have any problems in it usage :). This is cross platform code, it works on Windows and Linux..
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* AppLock
* @author http://fresh2L.com
* @version 1.0
*/
public class AppLock
{
/** hidden constructor */
private AppLock() { }
/** Lock file */
File lock_file = null;
FileLock lock = null;
FileChannel lock_channel = null;
FileOutputStream lock_stream = null;
/**
* Creates class lock instance
* @param key Unique application key
*/
private AppLock(String key) throws Exception
{
String tmp_dir = System.getProperty("java.io.tmpdir");
if (!tmp_dir.endsWith(System.getProperty("file.separator"))) {
tmp_dir += System.getProperty("file.separator");
}
// Acquire MD5
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
md.reset();
String hash_text = new java.math.BigInteger(1, md.digest(key.getBytes())).toString(16);
// Как правило нули в начале строки урезаются этот код добавляет нули обратно
while (hash_text.length() < 32) {
hash_text = "0" + hash_text;
}
lock_file = new File(tmp_dir + hash_text + ".app_lock");
}
catch (Exception ex) {
}
// MD5 acquire fail
if (lock_file == null) {
lock_file = new File(tmp_dir + key + ".app_lock");
}
lock_stream = new FileOutputStream(lock_file);
String f_content = "Java AppLock Object\r\nLocked by key: " + key + "\r\n";
lock_stream.write(f_content.getBytes());
lock_channel = lock_stream.getChannel();
lock = lock_channel.tryLock();
if (lock == null) {
throw new Exception("Can't create Lock");
}
}
/**
* Remove Lock. Now another application instance can gain lock.
* @throws Throwable
*/
private void release() throws Throwable
{
if (lock != null) {
lock.release();
}
if (lock_channel != null) {
lock_channel.close();
}
if (lock_stream != null) {
lock_stream.close();
}
if (lock_file != null) {
lock_file.delete();
}
}
@Override
protected void finalize() throws Throwable
{
this.release();
super.finalize();
}
private static AppLock instance;
/**
* Set Lock based on input key
* Method can be run only one time per application. Second calls will be ignored.
*
* @param key Lock key
*/
public static boolean setLock(String key)
{
if (instance != null) {
return true;
}
try {
instance = new AppLock(key);
}
catch (Exception ex) {
instance = null;
Logger.getLogger(Application.class.getName()).log(Level.SEVERE, "Обламался AppLock", ex);
return false;
}
Runtime.getRuntime().addShutdownHook(new Thread()
{
@Override
public void run()
{
AppLock.releaseLock();
}
});
return true;
}
/**
* Try to release Lock.
* After releasing you can not user AppLock again in application.
*/
public static void releaseLock()
{
if (instance == null) {
return;
}
try {
instance.release();
}
catch (Throwable ex) {
Logger.getLogger(AppLock.class.getName()).log(Level.SEVERE, null, ex);
}
}
}


