How to Implement a Single Instance Java Application

How to implement a single instance Java application?

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

Run one instance of java application

A simple way to have one instance is to use a service port.

ServerSocket ss = new ServerSocket(MY_PORT);

The benefit of using this approach instead of a locking a file is that you communicate to the instance already running and even check it is working. e.g. if you can't start the server socket use a plain Socket to send it a message like "open a file for me"

Java Application with Single Instance per User

Sockets will be a bit problematic if you want the application to run concurrently under different users.

The option of using an NIO FileLock is possible. You create the file under the user's directory so that another user can have his own lock file. The key thing to do here is to still try to acquire the file lock if the file exists already, by attempting to delete it before recreating it. This way if the application crashes and the file is still there, you will still be able to acquire a lock on it. Remember that the OS should release all locks, open file handles and system resources when a process terminates.

Something like this:

 public ExclusiveApplicationLock
throws Exception {

private final File file;
private final FileChannel channel;
private final FileLock lock;

private ExclusiveApplicationLock() {

String homeDir = System.getProperty("user.home");

file = new File(homeDir + "/.myapp", app.lock");
if (file.exists()) {
file.delete();
}

channel = new RandomAccessFile(file, "rw").getChannel();
lock = channel.tryLock();
if (lock == null) {
channel.close();
throw new RuntimeException("Application already running.");
}

Runtime.getRuntime().addShutdownHook(new Thread(() -> releaseLock());
}

private void releaseLock() {
try {
if (lock != null) {
lock.release();
channel.close();
file.delete();
}
}
catch (Exception ex) {
throw new RuntimeException("Unable to release application process lock", ex);
}
}
}

Another alternative is to use a library that does this for you like Junique. I haven't tried it myself but you could have a go. It seems very old but I guess there isn't much that needs to change in something like this, nothing much changed in NIO since Java 1.4.

http://www.sauronsoftware.it/projects/junique/

It is on Maven Central though so you can import it easily.
https://mvnrepository.com/artifact/it.sauronsoftware/junique/1.0.4

If you look at the code you will see that it does the same thing with file locks:
https://github.com/poolborges/it.sauronsoftware.junique/blob/master/src/main/java/it/sauronsoftware/junique/JUnique.java

How to make sure that only a single instance of a Java application is running?

What you are looking for can probably best be accomplished with a lock file. By lock file I simply mean a file that will have a predefined location and whose existence is your mutex.

Test if that file exists when your program starts, if it does, exit immediately. Create a file in a known location. If your program exits normally, delete the lock file.

Probably best is if you can also populate the file with a pid (process id) so that you can detect abnormal exits that didn't delete the file but this get OS specific.

How to allow running only one instance of a Java program at a time?

I think your suggestion of opening a port to listen when you start your application is the best idea.

It's very easy to do and you don't need to worry about cleaning it up when you close your application. For example, if you write to a file but someone then kills the processes using Task Manager the file won't get deleted.

Also, if I remember correctly there is no easy way of getting the PID of a Java process from inside the JVM so don't try and formulate a solution using PIDs.

Something like this should do the trick:

private static final int PORT = 9999;
private static ServerSocket socket;

private static void checkIfRunning() {
try {
//Bind to localhost adapter with a zero connection queue
socket = new ServerSocket(PORT,0,InetAddress.getByAddress(new byte[] {127,0,0,1}));
}
catch (BindException e) {
System.err.println("Already running.");
System.exit(1);
}
catch (IOException e) {
System.err.println("Unexpected error.");
e.printStackTrace();
System.exit(2);
}
}

This sample code explicitly binds to 127.0.0.1 which should avoid any firewall warnings, as any traffic on this address must be from the local system.

When picking a port try to avoid one mentioned in the list of Well Known Ports. You should ideally make the port used configurable in a file or via a command line switch in case of conflicts.

How to let run only one instance of application at a time?

You could try JUnique. It's an open source library doing exactly what you ask for. Import junique-1.0.4.jar to your project as a library. It's just 10kb file.

It's manual neatly describes how to implement it on a project. For a JavaFX application, implementation would look something like this:

Make sure to import these classes to your main

import it.sauronsoftware.junique.AlreadyLockedException;
import it.sauronsoftware.junique.JUnique;
    public static void main(String[] args) {

String appId = "myapplicationid";
boolean alreadyRunning;
try {
JUnique.acquireLock(appId);
alreadyRunning = false;
} catch (AlreadyLockedException e) {
alreadyRunning = true;
}
if (!alreadyRunning) {
launch(args); // <-- This the your default JavaFX start sequence
}else{ //This else is optional. Just to free up memory if you're calling the program from a terminal.
System.exit(1);
}
}

check for single instance java program

Launch the application using Java Web Start and implement the SingleInstanceService of the JNLP API. Here is a demo. of the SingleInstanceService.

If it attempted to open another instance of the program I would like the current instance to be brought to the foreground.

Hook that up in the newActivation(String[]) method of the SingleInstanceListener. It will be passed any arguments that were provided for the new launch. The existing instance gets to decide what to do with the new args (e.g. change file, add new tab, ignore..)



Related Topics



Leave a reply



Submit