15

It is requited that only one instance of a JAVA program can be executed at a certain time. I have observed a good number of solutions proposed in stack overflow in different earlier posts.

The solutions are based on:

  • By opening socket: To open a socket connection.
  • Based on the file locking: To create a temporary file and to hold a lock. And to add a shutdown hook to unlock that file when the JVM shuts down.

I do not want to use port locking as it can cause a possible conflict in ports usage.

So I was thinking to use file locking. After searching a bit, I have found the proponents of the port locking based mechanism has mentioned file locking can be unreliable if application crashes and for other IO errors.

What I need is to find a solution which will work consistently in cross platform and in multiple JDK. My intended platform is Windows and Linux and JDK is Sun and IBM JDK.

Can anyone shed some light on to this?

xlecoustillier
  • 16,183
  • 14
  • 60
  • 85
Exploring
  • 2,493
  • 11
  • 56
  • 97
  • 4
    One way of improving the file-locking method is to put the process' PID into the lock file. This way if a new instance starts, it can check if the process which created the lock file is still alive. If not, the lock file is just a leftover after the app's crash or similar event and can be ignored. This may require some non-portable code, though as the process ID-s are operating-system-specific. – Michał Kosmulski Sep 29 '13 at 19:27
  • 1
    getting a process id will result into a operating system specific code-so it will not be my desired solution. – Exploring Sep 29 '13 at 19:31
  • What about adding a [ShutdownHook](http://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html#addShutdownHook(java.lang.Thread)) that would release the file lock? – sp00m Sep 29 '14 at 12:18
  • 1
    Does this answer your question? [How to implement a single instance Java application?](https://stackoverflow.com/questions/177189/how-to-implement-a-single-instance-java-application) – Alexander Biryukov Nov 07 '20 at 00:24

5 Answers5

6

The way it is often done in unix systems is to create a file, which is an atomic operation and then check whether the file could be created or not. If it was possible to create the file then that process has the lock and is allowed to run. If the file could not be created, someone else must have the lock and the instance terminates promptly. The code for such a thing

private boolean lock()
 {
   try
    {
        final File file=new File("bpmdj.lock");
        if (file.createNewFile())
        {
            file.deleteOnExit();
            return true;
        }
        return false;
    }
    catch (IOException e)
    {
        return false;
    }
}

in the main of the app you then start with

    if (!lock())
    {
        System.out.println("Cannot lock database. Check that no other instance of BpmDj is running and if so, delete the file bpmdj.lock");
        return;
    }

Of course, there are two caveats to be mentioned. First of all: if the app crashes hard, then the file will very likely not be deleted, resulting in some inconvenience for the user (he will need to remove the lockfile himself).

Secondly: the java documentation states the following:

createNewFile atomically creates a new, empty file ... if and only if a file with this name does not yet exist. The check for the existence of the file and the creation of the file if it does not exist are a single operation that is atomic with respect to all other filesystem activities that might affect the file. Note: this method should not be used for file-locking, as the resulting protocol cannot be made to work reliably. The FileLock facility should be used instead.

Especially the last note is interesting because in this case we don't really use it for file-locking, merely to check that no other instances of the same application are present. Yet I'm a bit curious to understand why they write that the 'resulting protocol cannot be made to work reliable'

Matthieu
  • 2,736
  • 4
  • 57
  • 87
  • 1
    NB that `deleteOnExit()` should not be used in general (can create memory overflow when used repeatedly) but that is the perfect use case. – Matthieu Jan 05 '16 at 17:09
3

You may use ManagementFactory object. From here:-

import sun.management.ConnectorAddressLink;  
import sun.jvmstat.monitor.HostIdentifier;  

import sun.jvmstat.monitor.Monitor;  
import sun.jvmstat.monitor.MonitoredHost;  

import sun.jvmstat.monitor.MonitoredVm;  
import sun.jvmstat.monitor.MonitoredVmUtil;  
import sun.jvmstat.monitor.MonitorException;  
import sun.jvmstat.monitor.VmIdentifier;  

public static void main(String args[]) {  
/* The method ManagementFactory.getRuntimeMXBean() returns an identifier with applcation PID
   in the Sun JVM, but each jvm may have you own implementation. 
   So in anothers jvm, other than Sun, this code may not work., :( 
*/  
 RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean();  
         final int runtimePid = Integer.parseInt(rt.getName().substring(0,rt.getName().indexOf("@")));  

  java.awt.EventQueue.invokeLater(new Runnable() {  
  public void run() {  

  // If exists another instance, show message and terminates the current instance.  
  // Otherwise starts application.  
  if (getMonitoredVMs(runtimePid))  
  {  
     new MainFrame().setVisible(true);  
  } else  
  JOptionPane.showMessageDialog(null,"There is another instance of this application running.");  

  }  
  });  
  }

The getMonitoredVMs(int processPid) method receives as paramter 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\teste.jar". This way, we will catch just application name on the line 17 of the code below. After that, we search JVM for antoher process with the same name, if we found it and the application PID is different, it means that is the second application instance.

private static boolean getMonitoredVMs(int processPid) {  
         MonitoredHost host;  
         Set vms;  
try {  
     host = MonitoredHost.getMonitoredHost(new HostIdentifier((String)null));  
     vms = host.activeVms();  
    } catch (java.net.URISyntaxException sx) {  
 throw new InternalError(sx.getMessage());  
  } catch (MonitorException mx) {  
 throw new InternalError(mx.getMessage());  
 }  
 MonitoredVm mvm = null;  
 String processName = null;  
 try{  
     mvm = host.getMonitoredVm(new VmIdentifier(String.valueOf(processPid)));  
     processName = MonitoredVmUtil.commandLine(mvm);  
     processName = processName.substring(processName.lastIndexOf("\\") + 1,processName.length());  
             mvm.detach();  
     } catch (Exception ex) {  

     }  
 // This line is just to verify the process name. It can be removed. 
  JOptionPane.showMessageDialog(null,processName);  
  for (Object vmid: vms) {  
  if (vmid instanceof Integer) {  
  int pid = ((Integer) vmid).intValue();  
  String name = vmid.toString(); // default to pid if name not available  
  try {  
      mvm = host.getMonitoredVm(new VmIdentifier(name));  
      // use the command line as the display name  
    name =  MonitoredVmUtil.commandLine(mvm);  
    name = name.substring(name.lastIndexOf("\\")+1,name.length());  
    mvm.detach();  
    if ((name.equalsIgnoreCase(processName)) && (processPid != pid))  
    return false;  
   } catch (Exception x) {  
   // ignore  
   }  
   }  
   }  

   return true;  
   }

Also check the Using the SingleInstanceService Service

The javax.jnlp.SingleInstanceService provides a set of methods for applications to register themselves as singletons, and to register listener(s) for handling arguments passed in from different instances of applications.

Rahul Tripathi
  • 168,305
  • 31
  • 280
  • 331
  • 2
    I can see two issues with this solution: (a) This is dependent on SUN JDK and may not work for IBM J9 JDK. (b) It needs tools.jar which will increase the size of the application. – Exploring Sep 29 '13 at 19:47
  • For (b) `The problem of all this code are the imports, that are defined only in the file tools.jar file and has a size of 11MB. But to solve this problem, I have unpacked this file and a packed a new file called VM.ZIP only with the necessary classes, and it has a size of just 81kb.` – Rahul Tripathi Sep 29 '13 at 19:54
  • For (a):- In your question you mentioned that your intended platform is SUN JDK. I am not sure about IBM J9 JDK that whether it will work there also. The least I would say is Better give it a try!! :) – Rahul Tripathi Sep 29 '13 at 19:58
  • 1
    Thanks Rahul. Please note my intended platform is Windows and Linux and JDK is Sun and IBM JDK. JDK version > 1.4 – Exploring Sep 29 '13 at 20:00
1

If you are familiar with C, you could address this issue with named pipes in windows and local socket in unix. Both of them require a little of JNI. These communication channel are resources of your application, thus when your application crashes OS has the duty of freeing your resources. Furthermore they are identified by a textual name, so the chance of name clashing is the same for file locking.

You can pickup an example of local socket in this stackoverflow answer.

An example of named pipe in windows could be found here

Community
  • 1
  • 1
FabioDch
  • 516
  • 4
  • 8
1

hi there are many way to do it , just visit this page. i just copy paste. also view this thread [stackoverflow][1]

One of the most asking question in java world is how to make java application as a single instance.

I google it and found many of the techniques. I am posted here some of popular techniques. Just go ahead and contact if you have problem......

  1. By capturing port or through ServerSocket (short code) . On this method we are creating a object of java.net.ServerSocket class. And by passing a port number we are captured while first instance so that if another instance occurred it is throwing a bind Exception and you can tracked that any more instance is running on system.

Just see the link for code http://yuvadevelopers.dmon.com/java_examples/Single_Instance_small.htm

  1. By capturing port or through ServerSocket (Big code). It is same as the first method but while google i got this big code with different option just go through the code.

Just see the link for code See the original source here get from google http://www.rbgrn.net/blog/2008/05/java-single-application-instance.html

  1. By accessing file from local file system. This is also another method for doing the same thing. But it is not that much preferable because sometime when JVM crashes or due to some IO error occured then file is not deleted from hard disk. note:- Dont put your file (you can use any file) in C drive or where OS exist.
    Just see below for code
/*
* Program for setting single instance in JAVA
* Copyright 2009 @ yuvadeveloper
* Code By:- Prashant Chandrakar
*
*/
import java.net.ServerSocket;
import javax.swing.JOptionPane;
import javax.swing.JFrame;
import java.io.IOException;
import java.net.BindException;
class SingleInstance
{
  public static ServerSocket serverSocket;
  public static String errortype = "Access Error";
  public static String error = "Application already running.....";
  public static void main(String as[])
  {
    try
    {
        //creating object of server socket and bind to some port number serverSocket = new ServerSocket(15486);
        ////do not put common port number like 80 etc.
        ////Because they are already used by system
        JFrame jf = new JFrame();
        jf.setVisible(true);
        jf.setSize(200, 200);
     }
     catch (BindException exc)
     {
        JOptionPane.showMessageDialog(null, error, errortype, JOptionPane.ERROR_MESSAGE);
        System.exit(0);
     }
     catch (IOException exc)
     {
        JOptionPane.showMessageDialog(null, error, errortype, JOptionPane.ERROR_MESSAGE);
        System.exit(0);
     }
   }
}
  1. By using java sun.jvmstat package from tools.jar.

Just see the link for code

  1. By using Launch4j application. It is a third party tools for creating a EXE for your application. It is giving you a facility of creating single instance application. Just try it. It is perfect tool.

Just see the launch4j application doc http://launch4j.sourceforge.net/

Slavenko Miljic
  • 3,836
  • 2
  • 23
  • 36
jdev
  • 5,304
  • 1
  • 17
  • 20
  • Hi jdev, I have already research the links that you have mentioned and still have not found a cross platform and cross JVM solution that will handle all the cases. – Exploring Sep 29 '13 at 20:02
  • by using file lock or socket you acheive a cross platform solution .what is your problem with file lock .if your program have another share resource like DB you can do it by inserting some value for launching application in DB and then check it.you need some thing to log you application launching.it could be db or file or socket. – jdev Sep 29 '13 at 20:07
  • Socket based solution is not a good idea as it can possibly cause a port conflict. For my specific case I do not have any DB. So looks like the only option is to use File lock. However have seen proponents of the port locking based mechanism has mentioned file locking can be unreliable if application crashes and for other IO errors and hence the question was raised. – Exploring Sep 29 '13 at 20:11
  • when program crash the will be automatically removed.then there is no problem . – jdev Sep 29 '13 at 20:18
0

You can use JUnique library. It provides support for running single-instance java application and is open-source. It's based on file locks, but use also random port to send/receive messages from other running java instances.

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

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

Community
  • 1
  • 1
kolobok
  • 3,835
  • 3
  • 38
  • 54