Namek Dev
a developer's log
NamekDev

Java: enforce a single instance of an app

May 16, 2016

The Console is an app which typical usecase fits within running a single instance. That’s by design. However, we sometimes forget to check whether some the app is waiting for us in the background and try to launch them. That’s when disaster happens. Two consoles placed on top of window and reacting to same hotkey? Cannot be!

Here’s a short story about preventing from having multiple instances of same app made in Java. There’s a note about communication, too.

Why single instance?

Let’s recap. For this aplication it doesn’t make sense to open two or more instances. That’s because of two reasons:

  1. it is placed always on the top of the screen (it’s not movable)
  2. it listens to global hotkey (CTRL+~ at the moment) for toggling visibility

Application could bend those rules with support for multiple screens:

  1. place every instance on distinct screen
  2. on hotkey toggle the instance depending on mouse position, e.g. when mouse is on second screen then toggle visibility of instance on second screen

However, I don’t feel like such approach would benefit anyone’s usage.

Requirements

So I got two goals:

  1. prevent from running multiple instances
  2. when running a second instance make first one open it’s window

1. Solutions for single instance

I already have installed the JNativeHook library which throws an exception which I could just catch and close the app:

JNativeHook-2.0.2.dll (The process cannot access the file because it is being used by another process)

Between alternatives we have:

  • file locking
  • socket binding
  • JUnique library

There’s Runtime#addShutdownHook() which could help me with removing lockers. While file locking is pretty old and more a Linux-like solution it would probably be fine. Socket binding method is not bullet proof when socket is not properly closed every time an app exits (note crashes during development).

However, I’ve found that JUniqe library works fairly OK, so why not forgetting about implementing it on my own?

The JUnique library can perform cross-JVM lock operations. It is mainly intended to prevent a user to run simultaneously more instances of the same Java application, and it also offers a communication layer enabling message exchange between different JVMs.

License is GNU GPL which is suited only for open source projects. Fits this one.

2. Communicate with first instance

Goal: send a message so the first instance will open up it’s window.

Methods?

  • sockets, but error-prone
  • modify a file so first instance will watch on the file or folder and recognize a modification. Big leap even for a workaround.
  • OS-specific messaging (IPC)
  • Java Message Service
  • JUnique library again

Final choice

JUnique allows to perform both tasks at once: prevent from having multiple instances and open first instance when second is tried to be opened. Snippet talks for itself:

class Main {
	static val MSG_OPEN = "open"

	static ConsoleApp app

	def public static main(String[] args) {
		val uniqueAppId = ConsoleApp.typeName
		try {
			JUnique.acquireLock(uniqueAppId, [msg |
				if (msg.equals(MSG_OPEN)) {
					app.show()
					return null
				}
			])
		}
		catch (AlreadyLockedException exc) {
			// one instance is already running, inform it to open but don't continue!
			JUnique.sendMessage(uniqueAppId, MSG_OPEN)
			return
		}

		// TODO initialize your app
                app = new ConsoleApp()
	}
}

Small drawback here is a lack of Maven repository for this library. But there’s a workaround:

<dependency>
	<groupId>it.sauronsoftware</groupId>
	<artifactId>junique</artifactId>
	<version>1.0.4</version>
	<scope>system</scope>
	<systemPath>${project.basedir}/lib/junique-1.0.4.jar</systemPath>
</dependency>

The library works perfectly for my case.

References

Daj Się Poznać, the-console
comments powered by Disqus