Prevent launching multiple instances of a java application

前端 未结 10 2052
你的背包
你的背包 2020-12-03 03:19

I want to prevent the user from running my java application multiple times in parallel.

To prevent this, I have created a lock file when am opening the application,

相关标签:
10条回答
  • 2020-12-03 03:47

    There are already available java methods in File class to achieve the same. The method is deleteOnExit() which ensure the file is automatically deleted when the JVM exits. However, it does not cater to forcible terminations. One should use FileLock in case of forcible termination.

    For more details check, https://docs.oracle.com/javase/7/docs/api/java/io/File.html

    Thus code snippet which could be used in the main method can be like :

    public static void main(String args[]) throws Exception {
    
        File f = new File("checkFile");
    
        if (!f.exists()) {
            f.createNewFile();
        } else {
            System.out.println("App already running" );
            return;
        }
    
        f.deleteOnExit();
    
        // whatever your app is supposed to do
        System.out.println("Blah Blah")
    }
    
    0 讨论(0)
  • 2020-12-03 03:48

    You can write something like this.

    If file exists try to delete it. if it is not able to delete. We can say that application is already running.

    Now create the same file again and redirect the sysout and syserr.

    This works for me

    0 讨论(0)
  • 2020-12-03 03:51

    I struggled with this same problem for a while... none of the ideas presented here worked for me. In all cases, the lock (file, socket or otherwise) did not persist into the 2nd process instance, so the 2nd instance still ran.

    So I decided to try an old school approach to simply crate a .pid file with the process id of the first process. Then any 2nd process would quit if it finds the .pid file, and also the process number specified in the file is confirmed to be still running. This approach worked for me.

    There is a fair bit of code, which I provide here in full 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 doesn't 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, simply invoke it like this:

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

    I hope you find this helpful.

    0 讨论(0)
  • 2020-12-03 03:53

    ..what other mechanism could I use?

    If the app. has a GUI it can be launched using Java Web Start. The JNLP API provided to web-start offers the SingleInstanceService. Here is my demo. of the SingleInstanceService.

    0 讨论(0)
提交回复
热议问题