Re: single instance
On 1/3/2013 12:55 AM, Peter Duniho wrote:
As far as the sockets approach goes, you might be able to avoid the need of
dealing with a magic file or hard-coded socket port by using UDP multicast
with the SO_REUSEADDR option. This would allow your program to join to a
multicast port without interference from other programs, ensuring it's
ready to receive a UDP datagram sent on the channel from a new instance
searching for a prior existing instance.
Unfortunately, unlike the exclusive-create option available for a file,
there's no such built-in support for resolving race conditions in the
socket-based approach. Unless you are willing to go without a solution, or
are confident you can correctly design and implement a solution, you may
want to stick with the more user-driven approaches suggested.
But it's there in case you want to try. :)
Pete
I'm not sure how you could do this without establishing what group and
port your were going to use before hand. But maybe that isn't what you
were saying.
So what do you think of this implementation? I did try to start two
copies with a batch file and I could get them to fail on occasion. I
don't think it would be possible for a user to start two copies
simultaneously though.
Thanks,
knute...
import java.awt.*;
import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.nio.charset.*;
import java.util.*;
import javax.swing.*;
public class Exclusive implements Runnable {
private final long myTime;
private final MulticastSocket socket;
private final InetAddress group;
private final int port;
private volatile boolean runFlag;
private volatile Thread thread;
public Exclusive(String ipStr, String portStr) throws IOException {
myTime = System.currentTimeMillis();
group = InetAddress.getByName(ipStr);
port = Integer.parseInt(portStr);
socket = new MulticastSocket(port);
socket.joinGroup(group);
}
public void start() throws IOException {
if (thread == null || !thread.isAlive()) {
runFlag = true;
thread = new Thread(this);
thread.start();
// send out my time
String str = Long.toString(myTime);
byte[] buf = str.getBytes(StandardCharsets.US_ASCII);
DatagramPacket dp =
new DatagramPacket(buf,buf.length,group,port);
socket.send(dp);
}
}
@Override public void run() {
while (runFlag) {
try {
// receive their time
byte[] buf = new byte[64];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
socket.receive(dp);
String timeStr = new String(dp.getData(),dp.getOffset(),
dp.getLength(),StandardCharsets.US_ASCII);
long theirTime = Long.parseLong(timeStr);
// if we are seeing our own packet, do nothing
if (theirTime == myTime) {
// if their time is before my time, we need to shut down
} else if (theirTime < myTime) {
stop();
shutdownHook();
// if their time is after my time, send out my time
} else if (theirTime > myTime) {
String str = Long.toString(myTime);
buf = str.getBytes(StandardCharsets.US_ASCII);
dp = new DatagramPacket(buf,buf.length,group,port);
socket.send(dp);
}
} catch (IOException|NumberFormatException ex) {
ex.printStackTrace();
stop();
}
}
}
private void stop() {
if (thread != null && thread.isAlive()) {
runFlag = false;
if (socket != null)
socket.close();
}
// signal the waitFor() method to stop waiting
synchronized (this) {
notify();
}
}
// wait for two seconds to delay program startup until we have
// time to determine if there is another copy running.
// returns true if no other copy is running.
public synchronized boolean waitFor() throws InterruptedException {
wait(2000);
return runFlag;
}
public void shutdownHook() {
// can't use invokeLater()
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(null,
"Another Copy of this Program is Already Running",
"Start Inhibited",JOptionPane.ERROR_MESSAGE);
}
});
} catch (InterruptedException|InvocationTargetException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
try {
Exclusive e = new Exclusive("227.228.229.230","23222");
e.start();
if (e.waitFor())
System.out.println("no other copy running!");
else
System.out.println("another copy is running");
} catch (IOException|InterruptedException ex) {
// probably don't want to start if you get an exception either
ex.printStackTrace();
}
}
}
--
Knute Johnson