views:

1511

answers:

5

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

A: 

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

codebliss
+14  A: 

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.

Dave Webb
Nice idea! Could there be any security problems with a very strict SecurityManager?
furtelwart
Would this cause any firewall warnings? How to choose a port which we know is not being used by other programs?
Erwin
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?
Peter Perháč
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.
Dave Webb
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!
räph
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.
Mr. Shiny and New
thanks Mr Shiny. if I store the ServerSocket in a classvariable it's working!!
räph
@Mr Shiny. Whoops. Thanks. Will fix that.
Dave Webb
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.
andrew
+1  A: 

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?

Colin Desmond
+17  A: 

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!

Tom Hawtin - tackline
+2  A: 

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.

Piotr Kochański