Check if the process works with PID in JAVA

I am currently creating an application in JAVA where there can be only one execution. Therefore, I am currently using a lock file in which I am writing the PID of the current execution.

So, whenever this application starts, it will open the file (if it exists) and try to determine if the PID written in the file is actually running.

This prevents the problem when my application crashes before unlocking the file.

I need this to work both on Windows (XP, 7 or 8) and Linux (all users are on debian-based distributions).

Here is some code to give you a better idea of ​​what I want to do:

//get the PID from the file int pidValue = new FileReader(file).read(); //get the OS type String os = System.getProperty("os.name").toLowerCase(); //Check PID depending of OS type if( os.contains("nux") || os.contains("nix") ){ /* * Check PID on Linux/Unix */ } else if ( os.contains("win") ) { /* * Check PID on Windows */ } 

I tried to find documentation on this, but so far I have not been able to find anything useful.

Many thanks.

+4
java linux windows
source share
4 answers

On posix systems, the typical way to request if pid works is to send it a null signal, for example. kill(pid, 0) . If the call succeeds, the process exists; if it returns an ESRCH , it is not. This, of course, is due to the inevitable conditions of the race, which in reality are much less than in theory. Less uniform ways to read the / proc file system (if it has an OS), which works more, is equal to the same thing and is still subject to the same race conditions.

Please note that the pid file locking method can be two-level. That is, the current process creates the file, blocks it, and writes its pid. It holds the lock for the entire duration of its launch, and thus greatly eliminates the aforementioned need to ask if the process is running, because if the process ends, the file lock will be released automatically, even if the file still exists. The logic is as follows:

 if file exists if can get lock prev instance died unnaturally continue with this new process else instance already running else good to go, continue with new process 

This technique also has race conditions.

I don’t remember enough Java to say whether it has shells for kill or file locking, such as flock , lockf and fcntl , necessary to implement this scheme.

+2
source share

The following code determines if the process is running with the specified pid. It has been tested on Windows 7 and Ubuntu 13. On Windows, it uses apache commons-exec to run the task list and determines if they found the specified pid based on the exit code. It overcomes the fact that tasklist always returns 0, associating the result with findstr. On linux, it uses ps to do the same. It also suppresses the stdout child process entry.

 public static boolean isProcessRunning(int pid, int timeout, TimeUnit timeunit) throws java.io.IOException { String line; if (OS.isFamilyWindows()) { //tasklist exit code is always 0. Parse output //findstr exit code 0 if found pid, 1 if it doesn't line = "cmd /c \"tasklist /FI \"PID eq " + pid + "\" | findstr " + pid + "\""; } else { //ps exit code 0 if process exists, 1 if it doesn't line = "ps -p " + pid; //`-p` is POSIX/BSD-compliant, `--pid` isn't<ref>https://github.com/apache/storm/pull/296#discussion_r20535744</ref> } CommandLine cmdLine = CommandLine.parse(line); DefaultExecutor executor = new DefaultExecutor(); // disable logging of stdout/strderr executor.setStreamHandler(new PumpStreamHandler(null, null, null)); // disable exception for valid exit values executor.setExitValues(new int[]{0, 1}); // set timer for zombie process ExecuteWatchdog timeoutWatchdog = new ExecuteWatchdog(timeunit.toMillis(timeout)); executor.setWatchdog(timeoutWatchdog); int exitValue = executor.execute(cmdLine); // 0 is the default exit code which means the process exists return exitValue == 0; } 
+4
source share

Please see below to copy / paste the example.

The boolean method isStillAllive (...) will receive the process identifier number (pid) as a parameter. The method call is quite general and designed to wrap device-specific logic to solve the same problem on Windows / Linux / Unix, for example, on operating systems.

 public boolean isStillAllive(String pidStr) { String OS = System.getProperty("os.name").toLowerCase(); String command = null; if (OS.indexOf("win") >= 0) { log.debug("Check alive Windows mode. Pid: [{}]", pidStr); command = "cmd /c tasklist /FI \"PID eq " + pidStr + "\""; } else if (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0) { log.debug("Check alive Linux/Unix mode. Pid: [{}]", pidStr); command = "ps -p " + pidStr; } else { log.warn("Unsuported OS: Check alive for Pid: [{}] return false", pidStr); return false; } return isProcessIdRunning(pidStr, command); // call generic implementation } 

The actual system call is passed to isProcessIdRunning (). This method will call a pre-programmed system-dependent command in general, and the result obtained from the system is consumed and analyzed line by line. If at least one of the response lines contains pid, we interpret this as success.

 private boolean isProcessIdRunning(String pid, String command) { log.debug("Command [{}]",command ); try { Runtime rt = Runtime.getRuntime(); Process pr = rt.exec(command); InputStreamReader isReader = new InputStreamReader(pr.getInputStream()); BufferedReader bReader = new BufferedReader(isReader); String strLine = null; while ((strLine= bReader.readLine()) != null) { if (strLine.contains(" " + pid + " ")) { return true; } } return false; } catch (Exception ex) { log.warn("Got exception using system command [{}].", command, ex); return true; } } 
+2
source share

For some time I struggled with the same problem ... none of the ideas presented here worked for me. In all cases, the lock (file, socket, or other) was not saved in the second instance of the process, so the second instance still worked.

So I decided to try the old school approach to just create a .pid file with the process id of the first process. Then any 2nd process will terminate if it finds a .pid file, and also confirms that the process number specified in the file still works. This approach worked for me.

There is quite a bit of code that I have provided here completely for your use ... a complete solution.

 package common.environment; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.*; import java.nio.charset.Charset; public class SingleAppInstance { private static final @Nonnull Logger log = LogManager.getLogger(SingleAppInstance.class.getName()); /** * Enforces that only a single instance of the given component is running. This * is resilient to crashes, unexpected reboots and other forceful termination * scenarios. * * @param componentName = Name of this component, for disambiguation with other * components that may run simultaneously with this one. * @return = true if the program is the only instance and is allowed to run. */ public static boolean isOnlyInstanceOf(@Nonnull String componentName) { boolean result = false; // Make sure the directory exists String dirPath = getHomePath(); try { FileUtil.createDirectories(dirPath); } catch (IOException e) { throw new RuntimeException(String.format("Unable to create directory: [%s]", dirPath)); } File pidFile = new File(dirPath, componentName + ".pid"); // Try to read a prior, existing pid from the pid file. Returns null if the file does not exist. String oldPid = FileUtil.readFile(pidFile); // See if such a process is running. if (oldPid != null && ProcessChecker.isStillAllive(oldPid)) { log.error(String.format("An instance of %s is already running", componentName)); } // If that process isn't running, create a new lock file for the current process. else { // Write current pid to the file. long thisPid = ProcessHandle.current().pid(); FileUtil.createFile(pidFile.getAbsolutePath(), String.valueOf(thisPid)); // Try to be tidy. Note: This won't happen on exit if forcibly terminated, so we don't depend on it. pidFile.deleteOnExit(); result = true; } return result; } public static @Nonnull String getHomePath() { // Returns a path like C:/Users/Person/ return System.getProperty("user.home") + "/"; } } class ProcessChecker { private static final @Nonnull Logger log = LogManager.getLogger(io.cpucoin.core.platform.ProcessChecker.class.getName()); static boolean isStillAllive(@Nonnull String pidStr) { String OS = System.getProperty("os.name").toLowerCase(); String command; if (OS.contains("win")) { log.debug("Check alive Windows mode. Pid: [{}]", pidStr); command = "cmd /c tasklist /FI \"PID eq " + pidStr + "\""; } else if (OS.contains("nix") || OS.contains("nux")) { log.debug("Check alive Linux/Unix mode. Pid: [{}]", pidStr); command = "ps -p " + pidStr; } else { log.warn("Unsupported OS: Check alive for Pid: [{}] return false", pidStr); return false; } return isProcessIdRunning(pidStr, command); // call generic implementation } private static boolean isProcessIdRunning(@Nonnull String pid, @Nonnull String command) { log.debug("Command [{}]", command); try { Runtime rt = Runtime.getRuntime(); Process pr = rt.exec(command); InputStreamReader isReader = new InputStreamReader(pr.getInputStream()); BufferedReader bReader = new BufferedReader(isReader); String strLine; while ((strLine = bReader.readLine()) != null) { if (strLine.contains(" " + pid + " ")) { return true; } } return false; } catch (Exception ex) { log.warn("Got exception using system command [{}].", command, ex); return true; } } } class FileUtil { static void createDirectories(@Nonnull String dirPath) throws IOException { File dir = new File(dirPath); if (dir.mkdirs()) /* If false, directories already exist so nothing to do. */ { if (!dir.exists()) { throw new IOException(String.format("Failed to create directory (access permissions problem?): [%s]", dirPath)); } } } static void createFile(@Nonnull String fullPathToFile, @Nonnull String contents) { try (PrintWriter writer = new PrintWriter(fullPathToFile, Charset.defaultCharset())) { writer.print(contents); } catch (IOException e) { throw new RuntimeException(String.format("Unable to create file at %s! %s", fullPathToFile, e.getMessage()), e); } } static @Nullable String readFile(@Nonnull File file) { try { try (BufferedReader fileReader = new BufferedReader(new FileReader(file))) { StringBuilder result = new StringBuilder(); String line; while ((line = fileReader.readLine()) != null) { result.append(line); if (fileReader.ready()) result.append("\n"); } return result.toString(); } } catch (IOException e) { return null; } } } 

To use it, just call it like this:

 if (!SingleAppInstance.isOnlyInstanceOf("my-component")) { // quit } 

I hope you find this helpful.

0
source share

All Articles