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

9

29

I need to prevent users from starting my Java application (WebStart Swing app) multiple times. So if the application is already running it shouldn't be possible to start it again or show a warning / be closed again.

Is there some convenient way to achieve this? I thought about blocking a port or write sth to a file. But hopefully you can access some system properties or the JVM?

btw. target platform is Windows XP with Java 1.5

Rankins answered 28/5, 2009 at 11:28 Comment(1)
Most simplest solution :-) java.nio.file lock see complete example https://mcmap.net/q/197154/-how-to-make-sure-that-only-a-single-instance-of-a-java-application-is-runningRetinue
S
36

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.

Scarlett answered 28/5, 2009 at 11:32 Comment(9)
Nice idea! Could there be any security problems with a very strict SecurityManager?Burnejones
Would this cause any firewall warnings? How to choose a port which we know is not being used by other programs?Avelin
As Erwin points out, I believe this would cause firewall warnings. I can't quite agree with this approach. There must be a more elegant way. Where's Jon Skeet?Simitar
In the sample code I explicitly bind to 127.0.0.1 which should avoid any firewall warnings since any traffic on that address can only be internal to the local system.Scarlett
thanks. but actually for some reason its not working with the sample code. if i try a port which is already in use by another service its working as proposed by throwing a BindException. But if I try an unassigned port there won't be an exception and I can still start my app several times!Hairtail
It looks to me like this code leaks the socket, which will be GC'd and eventually closed. I think the code should store the ServerSocket object in a static variable. But in any case it looks like Tom Hawtin wrote a better solution, the javax.jnlp.SingleInstanceService.Miscalculate
thanks Mr Shiny. if I store the ServerSocket in a classvariable it's working!!Hairtail
@Mr Shiny. Whoops. Thanks. Will fix that.Scarlett
This is a bad idea because ports are allocated per-system, not per-user. On a Windows Terminal Server, Windows with Fast User Switching, Mac with Fast User Switching, or multiple people logged into a UNIX box, this becomes a denial-of-service attack.Marucci
M
26

As the question states that WebStart is being used, the obvious solution is to use javax.jnlp.SingleInstanceService.

This service is available in 1.5. Note that 1.5 is currently most of the way through its End Of Service Life period. Get with Java SE 6!

Middleton answered 28/5, 2009 at 12:33 Comment(0)
I
3

I think that the better idea would be to use file lock (quite an old idea :) ). Since Java 1.4 a new I/O library was introduced, that allows file locking.

Once the application starts it tries to acquire lock on a file (or create it if does not exist), when the application exits the lock is relased. If application cannot acquire a lock, it quits.

The example how to do file locking is for example in Java Developers Almanac.

If you want to use file locking in Java Web Start application or an applet you need to sing the application or the applet.

Insufferable answered 28/5, 2009 at 12:35 Comment(1)
File locks will not work due to Taskmanager or Linux kill commands not releasing lock fileBischoff
E
2

You can use JUnique library. It provides support for running single-instance java application and is open-source.

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

See also my full answer at How to implement a single instance Java application?

Epiphyte answered 23/11, 2016 at 11:32 Comment(0)
U
1

We do the same in C++ by creating a kernal mutex object and looking for it at start up. The advantages are the same as using a socket, ie when the process dies/crashes/exits/is killed, the mutex object is cleaned up by the kernel.

I'm not a Java programmer, so I am not sure whether you can do the same kind of thing in Java?

Undressed answered 28/5, 2009 at 12:6 Comment(0)
W
1

I've create the cross platform AppLock class.

http://mixeddev.info/articles/2015/02/01/run-single-jvm-app-instance.html

It is using file lock technique.

Update. At 2016-10-14 I've created package compatible with maven/gradle https://github.com/jneat/jneat and explained it here http://mixeddev.info/articles/2015/06/01/synchronize-different-jvm-instances.html

Weathersby answered 20/7, 2012 at 11:8 Comment(1)
Look like a neat offer. Does your solution recognize if previous instance of my application was killed, rather than gracefully stopped?Teter
D
0

You could use the registry, although this halfheartedly defeats the purpose of using a high-level language like java. At least your target platform is windows =D

Deary answered 28/5, 2009 at 11:31 Comment(1)
It also suffers from the same problem as a lock file: if the program dies, how do you know when to break the lock?Trump
S
0

Try JUnique:

String appId = "com.example.win.run.main";
boolean alreadyRunning;
try {
    JUnique.acquireLock(appId);
    alreadyRunning = false;
} catch (AlreadyLockedException e) {
    alreadyRunning = true;
}
if (alreadyRunning) {
    Sysout("An Instance of this app is already running");
    System.exit(1);
}
Shirty answered 12/9, 2017 at 14:21 Comment(0)
E
-1

I've seen so many of this questions and I was looking to solve the same problem in a platform independent way that doesn't take the chance to collide with firewalls or get into socket stuff.

So, here's what I did:

import java.io.File;
import java.io.IOException;

/**
 * This static class is in charge of file-locking the program
 * so no more than one instance can be run at the same time.
 * @author nirei
 */
public class SingleInstanceLock {

    private static final String LOCK_FILEPATH = System.getProperty("java.io.tmpdir") + File.separator + "lector.lock";
    private static final File lock = new File(LOCK_FILEPATH);
    private static boolean locked = false;

    private SingleInstanceLock() {}

    /**
     * Creates the lock file if it's not present and requests its deletion on
     * program termination or informs that the program is already running if
     * that's the case.
     * @return true - if the operation was succesful or if the program already has the lock.<br>
     * false - if the program is already running
     * @throws IOException if the lock file cannot be created.
     */
    public static boolean lock() throws IOException {
        if(locked) return true;

        if(lock.exists()) return false;

        lock.createNewFile();
        lock.deleteOnExit();
        locked = true;
        return true;
    }
}

Using System.getProperty("java.io.tmpdir") for the lockfile path makes sure that you will always create your lock on the same place.

Then, from your program you just call something like:

blah blah main(blah blah blah) {
    try() {
        if(!SingleInstanceLock.lock()) {
            System.out.println("The program is already running");
            System.exit(0);
        }
    } catch (IOException e) {
        System.err.println("Couldn't create lock file or w/e");
        System.exit(1);
    }
}

And that does it for me. Now, if you kill the program it won't delete the lock file but you can solve this by writing the program's PID into the lockfile and making the lock() method check if that process is already running. This is left as an assingment for anyone interested. :)

Eccrine answered 6/3, 2014 at 12:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.