Class SingleInstanceManager

java.lang.Object
ca.corbett.extras.SingleInstanceManager

public class SingleInstanceManager extends Object
Allows an application to enforce a single running instance. If a second instance is started, the new instance will send its arguments to the primary instance and then immediately exit. This is great for adding right-click support in your OS's file manager, because then the user can quickly add new files to the already-running instance instead of launching multiple instances of the application. If this option is enabled, it will also bring the main window of the primary instance to the front if you select your application's icon from the system menu, instead of starting a new instance.

USAGE: Start by invoking the tryAcquireLock(...) method. This will return true if the lock was acquired - that means that this instance will be considered the "primary" instance. You can optionally specify a port number to use. (Locks are acquired by binding a server socket to a specific TCP port on localhost). You should supply an implementation of the ArgsListener interface to receive any command-line arguments sent from new instances and process them. If tryAcquireLock(...) returns false, then another instance is already running, and you should send your arguments to it using the sendArgsToRunningInstance(...) method, and then immediately exit. For example:

 // In your application startup:
 SingleInstanceManager instanceManager = SingleInstanceManager.getInstance();
 if (instanceManager.tryAcquireLock(args -> handleStartArgs(args))) {
     // This is the primary instance - continue your usual startup
 }
 else {
     // Another instance is already running - send args and exit
     instanceManager.sendArgsToRunningInstance(args);
     // If sendArgsToRunningInstance fails, an error dialog will be shown.
     // Otherwise, it might not be obvious to the user what just happened.
     System.exit(0);
 }
 

In your primary instance, implement the handleStartArgs(...) method to process any arguments received from new instances. Note that this method will be invoked on the Swing Event Dispatch Thread (EDT), so you can safely update your UI from there.

Cleanup: This class registers a JVM shutdown hook to invoke release() automatically when the JVM shuts down. Callers also have the option of invoking release() manually to explicitly release the lock and close the server socket. The release() method is idempotent and thread safe, so it is safe to call it multiple times.

Since:
swing-extras 2.6
Author:
scorbo2 (with copilot/claude)
  • Field Details

    • DEFAULT_PORT

      public static final int DEFAULT_PORT
      The port number should be something that is unlikely to conflict with any other application. Just pick a random high number. Callers can override this default by calling tryAcquireLock with a different port.
      See Also:
  • Method Details

    • getInstance

      public static SingleInstanceManager getInstance()
      Returns the singleton instance of SingleInstanceManager.
    • tryAcquireLock

      public boolean tryAcquireLock(SingleInstanceManager.ArgsListener listener)
      Attempts to start as primary instance. Returns true if this is the primary instance, false if another instance is already running.
    • tryAcquireLock

      public boolean tryAcquireLock(SingleInstanceManager.ArgsListener listener, int port)
      Attempts to start as primary instance using the specified port. Returns true if this is the primary instance, false if another instance is already running.
      Parameters:
      listener - the listener to be notified when arguments are received from new instances
      port - the port number to use (must be between 1024 and 65535, non-privileged ports only)
      Returns:
      true if this is the primary instance, false otherwise
      Throws:
      IllegalArgumentException - if the port is outside the valid range or is a privileged port
    • sendArgsToRunningInstance

      public void sendArgsToRunningInstance(String[] args)
      Sends the given arguments to the running instance. Will log an exception if unable to connect.
    • isListening

      public boolean isListening()
      Reports whether this instance is currently the primary instance and is currently listening for new instance connections.
    • getListeningPort

      public int getListeningPort()
      Returns the port number this instance is listening on. Useful for logging or diagnostics.
    • isShowErrorDialogOnArgSendFailure

      public boolean isShowErrorDialogOnArgSendFailure()
      Reports whether an error dialog should be shown if sending arguments to the running instance fails.
    • setShowErrorDialogOnArgSendFailure

      public void setShowErrorDialogOnArgSendFailure(boolean showErrorDialogOnArgSendFailure)
      Controls whether or not an error dialog is shown in the case where sending arguments to the running instance fails. The default value is true, because otherwise it may not be obvious to the user what went wrong if the second instance fails to send its args. This is here largely for unit testing purposes, so tests can disable the dialog.
    • release

      public void release()
      Release the lock on shutdown. This should be invoked by the application's shutdown hook to clean up the server socket. Note that for swing-extras applications that use the UpdateManager, the cleanup code should be registered as a shutdownHook with the UpdateManager so that it runs when the UpdateManager initiates an application restart.