Reputation: 31
I found this code here which basically allow only one instance of the app at one moment. The code works fine when I try to launch two instances af the jar file from the command line but after I use jpackage to make an .exe file I can launch more than one instances of the app at a time. Does anyone know why it behaves like this? Is there anyway to lock a process in java to prevent multiple instance of the same app at the same time after making an .exe file with jpackage?
Upvotes: 1
Views: 79
Reputation: 44292
That code you found is an oversimplified version of what an application would need to do. It’s more like notes on the concept than an actual solution.
The proper way to do this is to create a file in a known, stable location, not just in whatever happens to be the current working directory of the program. There are a few different ways to let other attempts to open the program instead message the currently running instance. One of the simplest is a local socket:
import static java.lang.System.Logger.Level;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Files;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.BorderFactory;
public class SingletonApplication {
private static final System.Logger logger =
System.getLogger(SingletonApplication.class.getName());
private static final int MESSAGE_PORT = 25678;
private static final Path PID_FILE = Path.of(
System.getProperty("java.io.tmpdir"),
SingletonApplication.class.getName() + ".lock");
private final JFrame window;
private void monitorMessages() {
try (ServerSocket service = new ServerSocket(MESSAGE_PORT)) {
Files.writeString(PID_FILE,
String.valueOf(ProcessHandle.current().pid()));
while (true) {
try (Socket connection = service.accept();
BufferedReader reader = new BufferedReader(
new InputStreamReader(
connection.getInputStream(),
StandardCharsets.UTF_8))) {
reader.readLine();
EventQueue.invokeLater(() -> show());
}
}
} catch (IOException e) {
logger.log(Level.WARNING, "Message service failed.", e);
} finally {
try {
Files.deleteIfExists(PID_FILE);
} catch (IOException e) {
logger.log(Level.WARNING,
"Could not delete file \"" + PID_FILE + "\".", e);
}
}
}
public SingletonApplication() {
JLabel label = new JLabel(
String.valueOf(ProcessHandle.current().pid()));
label.setBorder(BorderFactory.createEmptyBorder(150, 150, 150, 150));
window = new JFrame("Singleton Application");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(label);
window.pack();
window.setLocationByPlatform(true);
}
private void show() {
window.setVisible(true);
window.toFront();
window.requestFocus();
}
private static boolean messageExistingInstance() {
if (Files.isReadable(PID_FILE)) {
try (Socket messageChannel =
new Socket(InetAddress.getLocalHost(), MESSAGE_PORT);
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(
messageChannel.getOutputStream(),
StandardCharsets.UTF_8))) {
writer.write("start");
} catch (IOException e) {
logger.log(Level.ERROR,
"Could not message existing instance.", e);
return false;
}
return true;
}
return false;
}
public static void main(String[] args) {
if (!messageExistingInstance()) {
EventQueue.invokeLater(() -> {
SingletonApplication application = new SingletonApplication();
application.show();
Thread monitorThread =
Thread.startVirtualThread(application::monitorMessages);
Runtime.getRuntime().addShutdownHook(
new Thread(monitorThread::interrupt));
});
}
}
}
Upvotes: 5