3

I'm trying to prevent the launching multiple instances of a java application by binding a ServerSocket.

Currently I'm executing it in my main as seen below:

public static void main(String[] args) {

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {

            try {
                ServerSocket serverSocket = new ServerSocket(65535, 10);

                showFrame();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    });
}

It isn't working as in Eclipse I can still open two instances of the application.

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
TokTok123
  • 753
  • 3
  • 11
  • 27
  • 1
    Remove the ServerSocket initialization from the invokeLater. You don't need to create the socket on the EDT. Try to initialize the socket first, if it fails, exit. If not, continue as usual. – Kayaman Jul 16 '14 at 10:34

2 Answers2

3

There are some crons of using network socket.

  • What if the socket is used by other apps?
  • What if there is warning from firewall, which comes with some anti-virus?

Using an exclusive locked file seems to be more reliable.

AppLock.java

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

// http://jimlife.wordpress.com/2008/07/21/java-application-make-sure-only-singleone-instance-running-with-file-lock-ampampampampamp-shutdownhook/
public class AppLock {
    private static File f;
    private static FileChannel channel;
    private static FileLock lock;

    public static boolean lock() {
        try {
            String directory = Utils.getUserDataDirectory();
            String fileName = "jstock.lock";
            Utils.createCompleteDirectoryHierarchyIfDoesNotExist(directory);
            f = new File(directory + fileName);
            // Do we need these code?
            //if (f.exists()) {
            //    f.delete();
            //}
            channel = new RandomAccessFile(f, "rw").getChannel();
            lock = channel.tryLock();
            if(lock == null) {
                channel.close();
                return false;
            }
        } catch (FileNotFoundException ex) {            
            log.error(null, ex);
        } catch (IOException ex) {
            log.error(null, ex);
        }
        return true;
    }

    public static void unlock() {
        // release and delete file lock
        try {
            if (lock != null) {
                lock.release();
                channel.close();
                f.delete();
            }
        } catch(IOException e) {
            log.error(null, e);
        }        
    }

    private static final Log log = LogFactory.getLog(AppLock.class);
}

An usage example

public static void main(String args[]) {        
    if (false == AppLock.lock()) {
        System.exit(0);
    }

    installShutdownHook();

    ...
}

private static void installShutdownHook() {

    Runnable runner = new Runnable() {
        @Override
        public void run() {
            AppLock.unlock();
        }
    };
    Runtime.getRuntime().addShutdownHook(new Thread(runner, "Window Prefs Hook"));
}

Note, I pick the code snippet from an open source project : AppLock.java

Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875
2

Enforce one instance of a program running with a ServerSocket Lock

Java Code. Put this into a file called Main.java:

import java.net.*;
import java.io.*;
public class Main{
  public static void main(String args[]){
    ServerSocket socket = null;
    try {
      socket = new ServerSocket(34567);
      System.out.println("Doing hard work for 100 seconds");
      try{ Thread.sleep(100000); } catch(Exception e){ }
      socket.close();
    }
    catch (IOException ex) {
      System.out.println("App already running, exiting...");
    }
    finally {
      if (socket != null)
          try{ socket.close(); } catch(Exception e){}
    }
  }
}

Compile and run it

javac Main.java
java Main

Test it in a normal case:

Run the program. You have 100 seconds to run the program again in another terminal, it will fall through saying its already running. Then wait 100 seconds, it should allow you to run it in the 2nd terminal.

Test it after force halting the program with a kill -9

  1. Start the program in terminal 1.
  2. kill -9 that process from another terminal within 100 seconds.
  3. Run the program again, it is allowed to run.

Conclusion:

The socket occupation is cleaned up by the operating system when your program is no longer operating. So you can be sure that the program will not run twice.

Drawbacks

If some sneaky person, or some naughty process were to bind all of the ports, or just your port, then your program will not run because it thinks its already running.

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335