Java Single Application Instance

A single instance application is one that only allows for 1 of the application to run no matter how many times the user tries to launch.  Windows and linux native applications have an API to detect the instance of an application but when in Java, it is not quite as easy or reliable.  To complicate matters, often times the requirement will be for the original application instance to react in some way to the launch, as in bringing the window to the front, opening a file or displaying a message.

Fortunately it's not all that difficult to code this if you know the trick.  In this article I'll give you all the code you need to copy and paste into your application to make it single instance.

I've seen suggestions to use files as a cross-instance lock, but that is unreliable as application crashes and other IO errors may not allow for the file to be cleaned up.  The better solution in my opinion is to use a local server socket. 

This code example works 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.

For security, I added a shared key that is used between the sender the receiver.  In my implementation, the only constraint is that it must end with a newline character.  That was just for ease of coding.  The imports are all in java.io or java.net.

public class ApplicationInstanceManager {

    private static ApplicationInstanceListener subListener;

    /** Randomly chosen, but static, high socket number */
    public static final int SINGLE_INSTANCE_NETWORK_SOCKET = 44331;

    /** Must end with newline */
    public static final String SINGLE_INSTANCE_SHARED_KEY = "$$NewInstance$$\n";

    /**
     * Registers this instance of the application.
     *
     * @return true if first instance, false if not.
     */
    public static boolean registerInstance() {
        // returnValueOnError should be true if lenient (allows app to run on network error) or false if strict.
        boolean returnValueOnError = true;
        // try to open network socket
        // if success, listen to socket for new instance message, return true
        // if unable to open, connect to existing and send new instance message, return false
        try {
            final ServerSocket socket = new ServerSocket(SINGLE_INSTANCE_NETWORK_SOCKET, 10, InetAddress
                    .getLocalHost());
            log.debug("Listening for application instances on socket " + SINGLE_INSTANCE_NETWORK_SOCKET);
            Thread instanceListenerThread = new Thread(new Runnable() {
                public void run() {
                    boolean socketClosed = false;
                    while (!socketClosed) {
                        if (socket.isClosed()) {
                            socketClosed = true;
                        } else {
                            try {
                                Socket client = socket.accept();
                                BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
                                String message = in.readLine();
                                if (SINGLE_INSTANCE_SHARED_KEY.trim().equals(message.trim())) {
                                    log.debug("Shared key matched - new application instance found");
                                    fireNewInstance();
                                }
                                in.close();
                                client.close();
                            } catch (IOException e) {
                                socketClosed = true;
                            }
                        }
                    }
                }
            });
            instanceListenerThread.start();
            // listen
        } catch (UnknownHostException e) {
            log.error(e.getMessage(), e);
            return returnValueOnError;
        } catch (IOException e) {
            log.debug("Port is already taken.  Notifying first instance.");
            try {
                Socket clientSocket = new Socket(InetAddress.getLocalHost(), SINGLE_INSTANCE_NETWORK_SOCKET);
                OutputStream out = clientSocket.getOutputStream();
                out.write(SINGLE_INSTANCE_SHARED_KEY.getBytes());
                out.close();
                clientSocket.close();
                log.debug("Successfully notified first instance.");
                return false;
            } catch (UnknownHostException e1) {
                log.error(e.getMessage(), e);
                return returnValueOnError;
            } catch (IOException e1) {
                log.error("Error connecting to local port for single instance notification");
                log.error(e1.getMessage(), e1);
                return returnValueOnError;
            }

        }
        return true;
    }

    public static void setApplicationInstanceListener(ApplicationInstanceListener listener) {
        subListener = listener;
    }

    private static void fireNewInstance() {
      if (subListener != null) {
        subListener.newInstanceCreated();
      }
  }
}

public interface ApplicationInstanceListener {
    public void newInstanceCreated();
}

public class MyApplication {
    public static void main(String[] args) {
       if (!ApplicationInstanceManager.registerInstance()) {
                    // instance already running.
                    System.out.println("Another instance of this application is already running.  Exiting.");
                    System.exit(0);
       }
       ApplicationInstanceManager.setApplicationInstanceListener(new ApplicationInstanceListener() {
          public void newInstanceCreated() {
             System.out.println("New instance detected...");
             // this is where your handler code goes...
          }
       });
    }
}

5 Comments

Post a comment here or discuss this and other topics in the forums

Works great!

Thanks a lot for your work.

No Problem!

I'm just happy people are finding it and saving programming time.

thanks!

It works, excellent! A few remarks:

- I had to comment out the calls to the log function
- fireNewInstance is missing in the above code. I implemented it as:
private static void fireNewInstance() {
if (subListener != null) {
subListener.newInstanceCreated();
}
}

Many thanks, your code saved me hours of work.

Thanks!

I must have accidentally left that out when I originally wrote the article. I'll add your lines in for completion.

re: Java Single Application Instance

An alternative offers the (internal) sun.jvmstat package from tools.jar.
package com.tutego.jvmstat;
import java.util.Set;
import javax.swing.JOptionPane;
import sun.jvmstat.monitor.*;
public class JvmStat{
@SuppressWarnings("unchecked")
public static void main( String[] args ) throws Exception {
MonitoredHost monitoredhost = MonitoredHost.getMonitoredHost( "//localhost" );
for ( int id : (Set) monitoredhost.activeVms() ) {
VmIdentifier vmidentifier = new VmIdentifier( "" + id );
MonitoredVm monitoredvm = monitoredhost.getMonitoredVm( vmidentifier, 0 );
System.out.printf( "%d %s %s %s%n", id,MonitoredVmUtil.mainClass( monitoredvm, true ),MonitoredVmUtil.jvmArgs( monitoredvm ),MonitoredVmUtil.mainArgs( monitoredvm ) );
}
}
}

Are there two occurrences of the result of MonitoredVmUtil.mainClass(), the program is started twice.

Christian

Post new comment

The content of this field is kept private and will not be shown publicly.
Add image
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <table> <tr> <td>
  • Lines and paragraphs break automatically.
  • Images can be added to this post.
  • Image links with 'rel="lightbox"' in the <a> tag will appear in a Lightbox when clicked on.
  • Image links with 'rel="lightshow"' in the <a> tag will appear in a Lightbox slideshow when clicked on.
  • Links to HTML content with 'rel="lightframe"' in the <a> tag will appear in a Lightbox when clicked on.
  • Links to video content with 'rel="lightvideo"' in the <a> tag will appear in a Lightbox when clicked on.
  • Links to inline or modal content with 'rel="lightmodal"' in the <a> tag will appear in a Lightbox when clicked on.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.