Honesty Rocks! truth rules.

How To Implement Single Instance Application On Java

HOME      >>       Programming

xico

See the next few lines containing Java Code:

1. // imports 2. import sun.management.ConnectorAddressLink; 3. import sun.jvmstat.monitor.HostIdentifier; 4. import sun.jvmstat.monitor.Monitor; 5. import sun.jvmstat.monitor.MonitoredHost; 6. import sun.jvmstat.monitor.MonitoredVm; 7. import sun.jvmstat.monitor.MonitoredVmUtil; 8. import sun.jvmstat.monitor.MonitorException; 9. import sun.jvmstat.monitor.VmIdentifier; 10. 11. public static void main(String args[]) { 12. /* The method ManagementFactory.getRuntimeMXBean() returns an identifier with applcation PID 13. in the Sun JVM, but each jvm may have you own implementation. 14. So in anothers jvm, other than Sun, this code may not work., :( 15. */ 16. RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean(); 17. final int runtimePid = Integer.parseInt(rt.getName().substring(0,rt.getName().indexOf("@"))); 18. 19. java.awt.EventQueue.invokeLater(new Runnable() { 20. public void run() { 21. 22. // If exists another instance, show message and terminates the current instance. 23. // Otherwise starts application. 24. if (getMonitoredVMs(runtimePid)) 25. { 26. new MainFrame().setVisible(true); 27. } else 28. JOptionPane.showMessageDialog(null,"There is another instance of this application running."); 29. 30. } 31. }); 32. }


This code above shows us how to implement a single instance application. But let me explain it.

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.

1. private static boolean getMonitoredVMs(int processPid) { 2. MonitoredHost host; 3. Set vms; 4. try { 5. host = MonitoredHost.getMonitoredHost(new HostIdentifier((String)null)); 6. vms = host.activeVms(); 7. } catch (java.net.URISyntaxException sx) { 8. throw new InternalError(sx.getMessage()); 9. } catch (MonitorException mx) { 10. throw new InternalError(mx.getMessage()); 11. } 12. MonitoredVm mvm = null; 13. String processName = null; 14. try{ 15. mvm = host.getMonitoredVm(new VmIdentifier(String.valueOf(processPid))); 16. processName = MonitoredVmUtil.commandLine(mvm); 17. processName = processName.substring(processName.lastIndexOf("\\") + 1,processName.length()); 18. mvm.detach(); 19. } catch (Exception ex) { 20. 21. } 22. // This line is just to verify the process name. It can be removed. 23. JOptionPane.showMessageDialog(null,processName); 24. for (Object vmid: vms) { 25. if (vmid instanceof Integer) { 26. int pid = ((Integer) vmid).intValue(); 27. String name = vmid.toString(); // default to pid if name not available 28. try { 29. mvm = host.getMonitoredVm(new VmIdentifier(name)); 30. // use the command line as the display name 31. name = MonitoredVmUtil.commandLine(mvm); 32. name = name.substring(name.lastIndexOf("\\")+1,name.length()); 33. mvm.detach(); 34. if ((name.equalsIgnoreCase(processName)) && (processPid != pid)) 35. return false; 36. } catch (Exception x) { 37. // ignore 38. } 39. } 40. } 41. 42. return true; 43. }
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.

PS: When you're debbuging the code, the processName variable will be assigned to Class Main project, for example, the name of its class is Principal and is in the package com.main, then the variable processName will be assigned as "com.main.Principal." To test you have to open another instance of Debug and it will work.
In the release case, it will be showed the application name as we can see in the line code JOptionPane.showMessageDialog(null,processName).


Below the file attached VM.zip.
VM.zip

prashant144

Hi,

Its very nice yar.
I also want the same thing but i use some different technique.

1. I make a ServerSocket object and capture a port say 1111
If again on another instance you are trying to make a same object then it
is giving you error because your 1111 port is captured by 1st instance.

So easily you can track and throw a error to user.
Its very simple and effective too.

Just follow the link for all codes.

Single instance aaplicaion in JAVA

2. Another method is make a txt file with one instance and delete while exiting.
On next time just check if file is existing then any instance is running.
And you can track the error.
For being effective do not create file in C partition.


zakaluka

I've used the lock method described by Prashant. The only problem is that the file has to be deleted manually if the application crashes without removing the lock.The benefit is that this technique works on every operating system and platform, with any java toolkit. I had a problem with capturing certain ports on unix, especially with certain firewalls running in the background.Regards,z.


prashant144

Hi Zakuluka,

I am not tested in unix.
If you have problem in capturing port then just make unique port like 99999 something like that
which nobody use.


thanks

I've used the lock method described by Prashant. The only problem is that the file has to be deleted manually if the application crashes without removing the lock.
The benefit is that this technique works on every operating system and platform, with any java toolkit. I had a problem with capturing certain ports on unix, especially with certain firewalls running in the background.

Regards,

z.



nanaji

See the next few lines containing Java Code:

1. // imports 2. import sun.management.ConnectorAddressLink; 3. import sun.jvmstat.monitor.HostIdentifier; 4. import sun.jvmstat.monitor.Monitor; 5. import sun.jvmstat.monitor.MonitoredHost; 6. import sun.jvmstat.monitor.MonitoredVm; 7. import sun.jvmstat.monitor.MonitoredVmUtil; 8. import sun.jvmstat.monitor.MonitorException; 9. import sun.jvmstat.monitor.VmIdentifier; 10. 11. public static void main(String args[]) { 12. /* The method ManagementFactory.getRuntimeMXBean() returns an identifier with applcation PID 13. in the Sun JVM, but each jvm may have you own implementation. 14. So in anothers jvm, other than Sun, this code may not work., :( 15. */ 16. RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean(); 17. final int runtimePid = Integer.parseInt(rt.getName().substring(0,rt.getName().indexOf("@"))); 18. 19. java.awt.EventQueue.invokeLater(new Runnable() { 20. public void run() { 21. 22. // If exists another instance, show message and terminates the current instance. 23. // Otherwise starts application. 24. if (getMonitoredVMs(runtimePid)) 25. { 26. new MainFrame().setVisible(true); 27. } else 28. JOptionPane.showMessageDialog(null,"There is another instance of this application running."); 29. 30. } 31. }); 32. }


This code above shows us how to implement a single instance application. But let me explain it.

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.

1. private static boolean getMonitoredVMs(int processPid) { 2. MonitoredHost host; 3. Set vms; 4. try { 5. host = MonitoredHost.getMonitoredHost(new HostIdentifier((String)null)); 6. vms = host.activeVms(); 7. } catch (java.net.URISyntaxException sx) { 8. throw new InternalError(sx.getMessage()); 9. } catch (MonitorException mx) { 10. throw new InternalError(mx.getMessage()); 11. } 12. MonitoredVm mvm = null; 13. String processName = null; 14. try{ 15. mvm = host.getMonitoredVm(new VmIdentifier(String.valueOf(processPid))); 16. processName = MonitoredVmUtil.commandLine(mvm); 17. processName = processName.substring(processName.lastIndexOf("\\") + 1,processName.length()); 18. mvm.detach(); 19. } catch (Exception ex) { 20. 21. } 22. // This line is just to verify the process name. It can be removed. 23. JOptionPane.showMessageDialog(null,processName); 24. for (Object vmid: vms) { 25. if (vmid instanceof Integer) { 26. int pid = ((Integer) vmid).intValue(); 27. String name = vmid.toString(); // default to pid if name not available 28. try { 29. mvm = host.getMonitoredVm(new VmIdentifier(name)); 30. // use the command line as the display name 31. name = MonitoredVmUtil.commandLine(mvm); 32. name = name.substring(name.lastIndexOf("\\")+1,name.length()); 33. mvm.detach(); 34. if ((name.equalsIgnoreCase(processName)) && (processPid != pid)) 35. return false; 36. } catch (Exception x) { 37. // ignore 38. } 39. } 40. } 41. 42. return true; 43. }
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.

PS: When you're debbuging the code, the processName variable will be assigned to Class Main project, for example, the name of its class is Principal and is in the package com.main, then the variable processName will be assigned as "com.main.Principal." To test you have to open another instance of Debug and it will work.
In the release case, it will be showed the application name as we can see in the line code JOptionPane.showMessageDialog(null,processName).


Below the file attached VM.zip.
VM.zip



Great work

Thanks
Nanaji

nooc9

For web applications there is a jnlp method of doing it. Check out javax.jnlp.SingleInstanceService found in jnlp.jar, included in the sdk.


iGuest

here is full working code for achieving single instance of application in java :

How it works

New instance of application tries to connect to a specific ServerSocket (localhost, port#) to detect running applications. And a running application must have a ServerThread to detect possible run of new instance of the same application

The main Logic in steps

 

Find existing server socket running on localhost

If found(another instance was already running) --> exit current instance of application

else --

 

start a new Server thread to detect run of future applications

and start the application


iGuest

Hey Xico,

 

First of all thanks for the code. I have implemented the same in my app. It's perfectly working for all windows os except windows 7. Can you please help me out in the same.


iGuest

I am facing issues on windows 7. This isn't working. However after installing netbeans it works. Also if I uninstall netbeans it still works. What's this mystery?