I want my application to check if another version of itself is already running.
For example, demo.jar
started, user clicks to run it again, but the sec
Simple yet powerful tested solution.
static File file;
static FileChannel fileChannel;
static FileLock lock;
static boolean running = false;
@SuppressWarnings("resource")
public static boolean checkIfAlreadyRunning() throws IOException {
file = new File(FilePath.FILEPATH + "az-client.lock");
if (!file.exists()) {
file.createNewFile();
running = true;
} else {
file.delete();
}
fileChannel = new RandomAccessFile(file, "rw").getChannel();
lock = fileChannel.tryLock();
if (lock == null) {
fileChannel.close();
return true;
}
ShutdownHook shutdownHook = new ShutdownHook();
Runtime.getRuntime().addShutdownHook(shutdownHook);
return running;
}
public static void unlockFile() {
try {
if (lock != null)
lock.release();
fileChannel.close();
file.delete();
running = false;
} catch (IOException e) {
e.printStackTrace();
}
}
static class ShutdownHook extends Thread {
public void run() {
unlockFile();
}
}
Put these methods in some Util class and before launching your main class just check that if already exists then show some dialog to user otherwise launch an application. It works even if you abnormally shutdown java process or what ever you do. It is robust and efficient, no need to set up DataGram listeners or whatever...
If your application is running on Windows, you can call CreateMutex through JNI.
jboolean ret = FALSE;
HANDLE hMutex = CreateMutex(NULL, FALSE, mutexName);
ret = TRUE;
if(WAIT_TIMEOUT == WaitForSingleObject(hMutex, 10))
{
ret = FALSE;
}
else if(GetLastError() != 0)
{
ret = FALSE;
}
This returns true if nobody else is using this mutex, false otherwise. You could provide "myApplication" as a mutex name or "Global\MyApplication" if you want your mutex to be shared by all Windows sessions.
Edit: It's not as complicated as it looks :) and I find it clean.
Following solution work in two deadly scenerio too. 1> Even your launched exe scheduled as javaw.exe in task manager. 2> You can install your application at two location and from launching both location it also works.
String tempDir = System.getProperty("java.io.tmpdir");// dependent to OS find any tem dir.
String filePath = tempDir + "lockReserverd.txt";
try {
final File file = new File(filePath);
if(file.exists())
return false;
final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
final FileLock fileLock = randomAccessFile.getChannel().tryLock();
if (fileLock != null) {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
try {
fileLock.release();
randomAccessFile.close();
file.delete();
} catch (Exception e) {
//log.error("Unable to remove lock file: " + lockFile, e);
}
}
});
return true;
}
} catch (Exception e) {
//log.Error("Unable to create and/or lock file");
}
return false
or
This will work if your application.exe is listed in task manager
"tasklist /FI \"IMAGENAME eq "+MyApplication+".exe
Check PID and file lock technique
We can write the process id of the process that created the lock file into the file. When we encounter an existing lock file, we do not just quit, but we check if the process with that id is still alive. If not, then create a new application instance. I think MongoDB use this technique.
static File file;
static FileChannel fileChannel;
static FileLock lock;
static boolean running = false;
static String currentPID = null;
static String lockFilePID = null;
public static final String USER_DIR = System.getProperty("user.dir");
public static final String LOCK_FILE = "az-client.lock";
public static boolean checkInstance() {
try {
file = new File(USER_DIR + File.separator + LOCK_FILE);
currentPID = Integer.toString(getCurrentPID());
if (!file.exists()) {
file.createNewFile();
writePID(currentPID);
lockFile();
addShudDownHook();
running = true;
return running;
} else {
if (isFileLocked()) {
syso("App already running");
System.exit(0);
} else {
lockFilePID = getPIDFromLockFile();
if (isProcessIdRunningOnWindows(Integer.parseInt(lockFilePID))) {
lockFile();
addShudDownHook();
running = true;
return running;
} else {
file.delete();
file.createNewFile();
writePID(currentPID);
lockFile();
addShudDownHook();
running = true;
return running;
}
}
}
} catch (Exception e) {
syso(e + "App already running");
System.exit(0);
}
return running;
}
/**
*
* @return
* @throws IOException
*/
@SuppressWarnings("resource")
private static boolean isFileLocked() throws IOException {
fileChannel = new RandomAccessFile(file, "rw").getChannel();
lock = fileChannel.tryLock();
if (lock == null) {
fileChannel.close();
fileChannel = null;
return true;
} else {
lock.release();
fileChannel.close();
fileChannel = null;
}
return false;
}
public static int getCurrentPID() {
// This function should work with Windows, Linux and Mac but you'll have
// to
// test to make sure. If not then get a suitable getCurrentPID function
// replacement.
try {
java.lang.management.RuntimeMXBean runtime = java.lang.management.ManagementFactory.getRuntimeMXBean();
java.lang.reflect.Field jvm = runtime.getClass().getDeclaredField("jvm");
jvm.setAccessible(true);
sun.management.VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime);
java.lang.reflect.Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId");
pid_method.setAccessible(true);
return (int) pid_method.invoke(mgmt);
} catch (Exception e) {
throw new RuntimeException("Cannot get the current PID");
}
}
public static boolean isProcessIdRunningOnWindows(int pid) {
// This Function only works for windows, if you want it to work on linux
// or mac, you will have to go find a replacement method that
// takes the processID as a parameter and spits out a true/false
// if it is running on the operating system.
try {
Runtime runtime = Runtime.getRuntime();
String cmds[] = { "cmd", "/c", "tasklist /FI \"PID eq " + pid + "\"" };
Process proc = runtime.exec(cmds);
InputStream inputstream = proc.getInputStream();
InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
String line;
while ((line = bufferedreader.readLine()) != null) {
if (line.contains(" " + pid + " ")) {
return true;
}
}
return false;
} catch (Exception ex) {
throw new RuntimeException("Cannot run the tasklist command to query if a pid is running or not");
}
}
/**
* This method write PID to Lock file
*
* @param pid
* @throws Exception
*/
private static void writePID(String pid) throws Exception {
try {
// To Do write PID to LockFile
} catch (Exception e) {
syso(e);
throw e;
}
}
/**
* This method return PID from Lock File
*
* @return
* @throws Exception
*/
private static String getPIDFromLockFile() throws Exception {
try {
return //To Do getPID from File
} catch (Exception e) {
syso(e);
throw e;
}
}
private static void addShudDownHook() {
try {
ShutdownHook shutdownHook = new ShutdownHook();
Runtime.getRuntime().addShutdownHook(shutdownHook);
} catch (Exception e) {
LogWriter.logger.error(e);
}
}
private static void unlockFile() {
try {
if (lock != null) {
lock.release();
}
fileChannel.close();
file.delete();
running = false;
} catch (IOException e) {
syso(e);
}
}
private static void lockFile() {
try {
fileChannel = new RandomAccessFile(file, "rw").getChannel();
lock = fileChannel.tryLock();
if (lock == null) {
fileChannel.close();
fileChannel = null;
}
} catch (IOException e) {
syso(e);
}
}
static class ShutdownHook extends Thread {
public void run() {
unlockFile();
}
}
What you are looking for can probably best be accomplished with a lock file. By lock file I simply mean a file that will have a predefined location and whose existence is your mutex.
Test if that file exists when your program starts, if it does, exit immediately. Create a file in a known location. If your program exits normally, delete the lock file.
Probably best is if you can also populate the file with a pid (process id) so that you can detect abnormal exits that didn't delete the file but this get OS specific.