Package it.sauronsoftware.junique

The JUnique library can perform cross-JVM lock operations.

See:
          Description

Interface Summary
MessageHandler This interface describes how to handle messages received through an ID lock channel.
 

Class Summary
JUnique Point-of-entry of the JUnique library.
 

Exception Summary
AlreadyLockedException This exception is thrown when an ID cannot be locked since it is currently already locked by another JUnique caller.
 

Package it.sauronsoftware.junique Description

The JUnique library can perform cross-JVM lock operations. It is mainly intended to prevent a user to run simultaneously more instances of the same Java application, and it also offers a communication layer enabling message exchange between different JVMs.

JUnique is based on a user related cross-JVM exclusive lock concept. Suppose you want to avoid your Java desktop application to be started more than once by the same user. When the main() method of your application is called, you can require to JUnique a lock on a certain ID, i.e. myapplicationid. At the first application launch that ID will be surely available, and so the lock will be taken. The obtained lock can be explicitly released with a JUnique.releaseLock() call, otherwise it will be automatically released when JVM halts. If another JVM instance (or even the same one) will try to impose a lock on the same ID, JUnique will return with an AlreadyLockedException, notifying the instance that another one is currently running. Then the second instance can choose to abort its start sequence, and optionally it can send one or more messages to the active one before exiting.

Please note that a JUnique lock is user-related: two different users accessing the system at the same time can still run two separate instances of the same application, since JUnique IDs reside in a user scope.

Quickstart

A sample main() method preventing multiple running instances of the same application for the same user:

public static void main(String[] args) {
        String appId = "myapplicationid";
        boolean alreadyRunning;
        try {
                JUnique.acquireLock(appId);
                alreadyRunning = false;
        } catch (AlreadyLockedException e) {
                alreadyRunning = true;
        }
        if (!alreadyRunning) {
                // Start sequence here
        }
}

A message handling example, transfering arguments from the aborting instance to the active one:

public static void main(String[] args) {
        String appId = "myapplicationid";
        boolean alreadyRunning;
        try {
                JUnique.acquireLock(appId, new MessageHandler() {
                        public String handle(String message) {
                                // A brand new argument received! Handle it!
                                return null;
                        }
                });
                alreadyRunning = false;
        } catch (AlreadyLockedException e) {
                alreadyRunning = true;
        }
        if (!alreadyRunning) {
                // Start sequence here
        } else {
                for (int i = 0; i < args.length; i++) {
                        JUnique.sendMessage(appId, args[0]));
                }
        }
}

Real-world examples can be found inside the JUnique distribution archive.

Lock ID strategies

To avoid potential conflicts, it is advisable to choose a fully qualifying lock ID for each application using JUnique. Using a generic ID, such "notepad", "chat" or "myapp", is not a good idea. Better to use something like "myName.myAppName", and even better it's to pick your application main class full name as the JUnique lock ID.