How to implement a single instance Java application?

后端 未结 17 720
北荒
北荒 2020-11-22 04:32

Sometime I see many application such as msn, windows media player etc that are single instance applications (when user executes while application is running a new applicatio

相关标签:
17条回答
  • 2020-11-22 04:34
    
    public class SingleInstance {
        public static final String LOCK = System.getProperty("user.home") + File.separator + "test.lock";
        public static final String PIPE = System.getProperty("user.home") + File.separator + "test.pipe";
        private static JFrame frame = null;
    
        public static void main(String[] args) {
            try {
                FileChannel lockChannel = new RandomAccessFile(LOCK, "rw").getChannel();
                FileLock flk = null; 
                try {
                    flk = lockChannel.tryLock();
                } catch(Throwable t) {
                    t.printStackTrace();
                }
                if (flk == null || !flk.isValid()) {
                    System.out.println("alread running, leaving a message to pipe and quitting...");
                    FileChannel pipeChannel = null;
                    try {
                        pipeChannel = new RandomAccessFile(PIPE, "rw").getChannel();
                        MappedByteBuffer bb = pipeChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1);
                        bb.put(0, (byte)1);
                        bb.force();
                    } catch (Throwable t) {
                        t.printStackTrace();
                    } finally {
                        if (pipeChannel != null) {
                            try {
                                pipeChannel.close();
                            } catch (Throwable t) {
                                t.printStackTrace();
                            }
                        } 
                    }
                    System.exit(0);
                }
                //We do not release the lock and close the channel here, 
                //  which will be done after the application crashes or closes normally. 
                SwingUtilities.invokeLater(
                    new Runnable() {
                        public void run() {
                            createAndShowGUI();
                        }
                    }
                );
    
                FileChannel pipeChannel = null;
                try {
                    pipeChannel = new RandomAccessFile(PIPE, "rw").getChannel();
                    MappedByteBuffer bb = pipeChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1);
                    while (true) {
                        byte b = bb.get(0);
                        if (b > 0) {
                            bb.put(0, (byte)0);
                            bb.force();
                            SwingUtilities.invokeLater(
                                new Runnable() {
                                    public void run() {
                                        frame.setExtendedState(JFrame.NORMAL);
                                        frame.setAlwaysOnTop(true);
                                        frame.toFront();
                                        frame.setAlwaysOnTop(false);
                                    }
                                }
                            );
                        }
                        Thread.sleep(1000);
                    }
                } catch (Throwable t) {
                    t.printStackTrace();
                } finally {
                    if (pipeChannel != null) {
                        try {
                            pipeChannel.close();
                        } catch (Throwable t) {
                            t.printStackTrace();
                        } 
                    } 
                }
            } catch(Throwable t) {
                t.printStackTrace();
            } 
        }
    
        public static void createAndShowGUI() {
    
            frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(800, 650);
            frame.getContentPane().add(new JLabel("MAIN WINDOW", 
                        SwingConstants.CENTER), BorderLayout.CENTER);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    }
    
    
    0 讨论(0)
  • 2020-11-22 04:39

    If the app. has a GUI, launch it with JWS and use the SingleInstanceService.

    Update

    The Java Plug-In (required for both applets and JWS apps) was deprecated by Oracle and removed from the JDK. Browser manufacturers had already removed it from their browsers.

    So this answer is defunct. Only leaving it here to warn people looking at old documentation.

    0 讨论(0)
  • 2020-11-22 04:41

    If I believe this article, by :

    having the first instance attempt to open a listening socket on the localhost interface. If it's able to open the socket, it is assumed that this is the first instance of the application to be launched. If not, the assumption is that an instance of this application is already running. The new instance must notify the existing instance that a launch was attempted, then exit. The existing instance takes over after receiving the notification and fires an event to the listener that handles the action.

    Note: Ahe mentions in the comment that using InetAddress.getLocalHost() can be tricky:

    • it does not work as expected in DHCP-environment because address returned depends on whether the computer has network access.
      Solution was to open connection with InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
      Probably related to bug 4435662.
    • I also found bug 4665037 which reports than Expected results of getLocalHost: return IP address of machine, vs. Actual results : return 127.0.0.1.

    it is surprising to have getLocalHost return 127.0.0.1 on Linux but not on windows.


    Or you may use ManagementFactory object. As explained here:

    The getMonitoredVMs(int processPid) method receives as parameter the current application PID, and catch the application name that is called from command line, for example, the application was started from c:\java\app\test.jar path, then the value variable is "c:\\java\\app\\test.jar". This way, we will catch just application name on the line 17 of the code below.
    After that, we search JVM for another process with the same name, if we found it and the application PID is different, it means that is the second application instance.

    JNLP offers also a SingleInstanceListener

    0 讨论(0)
  • 2020-11-22 04:42

    The Unique4j library can be used for running a single instance of a Java application and pass messages. You can see it at https://github.com/prat-man/unique4j. It supports Java 1.6+.

    It uses a combination of file locks and dynamic port locks to detect and communicate between instances with the primary goal of allowing only one instance to run.

    Following is a simple example of the same:

    import tk.pratanumandal.unique4j.Unique4j;
    import tk.pratanumandal.unique4j.exception.Unique4jException;
    
    public class Unique4jDemo {
    
        // unique application ID
        public static String APP_ID = "tk.pratanumandal.unique4j-mlsdvo-20191511-#j.6";
    
        public static void main(String[] args) throws Unique4jException, InterruptedException {
    
            // create unique instance
            Unique4j unique = new Unique4j(APP_ID) {
                @Override
                public void receiveMessage(String message) {
                    // display received message from subsequent instance
                    System.out.println(message);
                }
    
                @Override
                public String sendMessage() {
                    // send message to first instance
                    return "Hello World!";
                }
            };
    
            // try to obtain lock
            boolean lockFlag = unique.acquireLock();
    
            // sleep the main thread for 30 seconds to simulate long running tasks
            Thread.sleep(30000);
    
            // try to free the lock before exiting program
            boolean lockFreeFlag = unique.freeLock();
    
        }
    
    }
    

    Disclaimer: I created and maintain Unique4j library.

    0 讨论(0)
  • 2020-11-22 04:42

    I wrote a dedicated library for that https://sanyarnd.github.io/applocker

    It is based on file-channel locking, so it will not block a port number, or deadlock application in case of power outage (channel is released once process is terminated).

    Library is lightweight itself and has a fluent API.

    It was inspired by http://www.sauronsoftware.it/projects/junique/, but it's based on file channels instead. And there are other extra new features.

    0 讨论(0)
  • 2020-11-22 04:45

    A more generic way of limiting the number of instance's on a single machine, or even an entire network, is to use a multicast socket.

    Using a multicast socket, enables you to broadcast a message to any amount of instances of your application, some of which can be on physically remote machines across a corporate network.

    In this way you can enable many types of configurations, to control things like

    • One or Many instances per machine
    • One or Many instances per network (eg controlling installs on a client site)

    Java's multicast support is via java.net package with MulticastSocket & DatagramSocket being the main tools.

    Note: MulticastSocket's do not guarantee delivery of data packets, so you should use a tool built on top of multicast sockets like JGroups. JGroups does guarantee delivery of all data. It is one single jar file, with a very simple API.

    JGroups has been around a while, and has some impressive usages in industry, for example it underpins JBoss's clustering mechanism do broadcast data to all instance of a cluster.

    To use JGroups, to limit the number of instances of an app (on a machine or a network, lets say: to the number of licences a customer has bought) is conceptually very simple :

    • Upon startup of your application, each instance tries to join a named group eg "My Great App Group". You will have configured this group to allow 0, 1 or N members
    • When the group member count is greater than what you have configured for it.. your app should refuse to start up.
    0 讨论(0)
提交回复
热议问题