Path: csiph.com!usenet.pasdenom.info!news.albasani.net!eternal-september.org!feeder.eternal-september.org!mx04.eternal-september.org!.POSTED!not-for-mail From: Knute Johnson Newsgroups: comp.lang.java.programmer Subject: Re: single instance Date: Fri, 18 Jan 2013 20:50:37 -0800 Organization: A noiseless patient Spider Lines: 167 Message-ID: References: <50e8eb1f$0$284$14726298@news.sunsite.dk> Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Injection-Date: Sat, 19 Jan 2013 04:50:37 +0000 (UTC) Injection-Info: mx04.eternal-september.org; posting-host="aba33539224e5c782fe0c4053f7756fd"; logging-data="26236"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX186XoT4dgVfgez3D0YlPR8Q" User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:17.0) Gecko/20130107 Thunderbird/17.0.2 In-Reply-To: Cancel-Lock: sha1:PfViqsSKcEZfY6KzDO0xXeJOXn0= Xref: csiph.com comp.lang.java.programmer:21567 On 1/18/2013 1:47 AM, Roedy Green wrote: > I have been working on polishing Knute's code. Mostly I have been > adding informal comments and renaming to help myself understand how it > works. > > I have not run it yet, but it is getting close to a test. > > see > https://wush.net/svn/mindprod/com/mindprod/singleinstance/SingleInstance.java > > I have added UUIDs to break the tie for equal start times. > I have added app ids so different apps can share the same port. > > I have added the ability avoid several different apps. > I have added the ability to permit two apps to run, so long as they > run on different files. > Below is what I ended up with. I'm curious about the UUID. How do you create a time based UUID? And does it have less granularity that currentTimeMillis? package com.knutejohnson.classes; 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 static final int port = 35798; private static final String addr = "228.237.246.255"; private final long myTime; private final String name; private final InetAddress group; private final MulticastSocket socket; private final String token; private volatile boolean runFlag; private volatile Thread thread; public Exclusive(String name) throws IOException { myTime = System.currentTimeMillis(); this.name = name; if (name.indexOf(",") >= 0) throw new IllegalArgumentException( "Comma character not allowed in name"); group = InetAddress.getByName(addr); socket = new MulticastSocket(port); socket.joinGroup(group); token = String.format("%s,%d",name,myTime); } public void start() throws IOException { if (thread == null || !thread.isAlive()) { runFlag = true; thread = new Thread(this); thread.start(); sendToken(); } } public void sendToken() throws IOException { byte[] buf = token.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 recStr = new String(dp.getData(),dp.getOffset(), dp.getLength(),StandardCharsets.US_ASCII); String[] arr = recStr.split(","); // if names don't match there is nothing to do if (!name.equals(arr[0])) continue; long theirTime = Long.parseLong(arr[1]); // 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(); // 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(); } shutdown(); // if their time is after my time, send out my time } else if (theirTime > myTime) { sendToken(); } } 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 up to two seconds to see if any other copy responds // returns true if no other copy is running. public synchronized boolean waitFor() throws InterruptedException { wait(2000); return runFlag; } public void shutdown() { System.exit(0); } public static void main(String[] args) { try { Exclusive e = new Exclusive("Test"); 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