Frage

Manchmal sehe ich viele Anwendungen wie MSN, Windows Media Player usw., die einzelne Instanz Anwendungen sind (wenn der Benutzer ausführt, während der Anwendung eine neue Anwendungsinstanz ausgeführt wird nicht erstellt).

In C #, verwende ich Mutex Klasse für diese, aber ich weiß nicht, wie dies in Java zu tun.

War es hilfreich?

Lösung

Wenn ich glaube, dieses Artikel rel="nofollow durch:

  

mit dem ersten Instanz Versuch, einen Socket auf der lokalen Host-Schnittstelle zu öffnen. Wenn es in der Lage ist, den Sockel zu öffnen, wird angenommen, dass dies die erste Instanz der Anwendung gestartet werden soll. Wenn nicht, ist die Annahme, dass eine Instanz dieser Anwendung bereits ausgeführt wird. Die neue Instanz muss die vorhandene Instanz benachrichtigen, dass ein Start versucht wurde, dann beenden. Die vorhandene Instanz übernimmt nach Erhalt der Benachrichtigung und feuert ein Ereignis an den Hörer, der die Aktion behandelt.

Hinweis: Ahe erwähnt in der Bemerkung, dass InetAddress.getLocalHost() mit heikel sein kann:

  
      
  • es funktioniert nicht wie in DHCP-Umgebung zu erwarten, da Adresse ist abhängig von zurückgegeben, ob die Computer-Netzwerk-Zugang hat.
      Lösung war Verbindung zu öffnen mit InetAddress.getByAddress(new byte[] {127, 0, 0, 1}) ;
      Wahrscheinlich im Zusammenhang mit Fehler 4.435.662 rel="nofollow.
  •   
  • Ich fand auch Fehler 4.665.037 die Berichte als Erwartete Ergebnisse getLocalHost: Rückkehr IP-Adresse der Maschine, und tatsächliche Ergebnisse: 127.0.0.1 zurückzukehren.
  

ist es überraschend getLocalHost Rückkehr 127.0.0.1 auf Linux zu haben, aber nicht auf Fenster.


Oder Sie können eine href verwenden <= "http://web.archive.org/web/20090529082521/http://www.trap17.com/index.php/how-implement-single-instance-application- java_t59760.html“rel = "nofollow noreferrer"> ManagementFactory Objekt. Wie erklärt hier :

  

Die getMonitoredVMs(int processPid) Methode erhält als Parameter die aktuelle Anwendung PID und den Namen der Anwendung fangen, die von der Kommandozeile, zum Beispiel genannt wird, wurde die Anwendung von c:\java\app\test.jar Pfad gestartet wird, dann wird der Wert Variable „c:\\java\\app\\test.jar“. Auf diese Weise werden wir fangen gerade Anwendungsnamen auf der Linie 17 des Code unten.
  Danach werden wir JVM für einen anderen Prozess mit dem gleichen Namen suchen, wenn wir sie und die Anwendung gefunden PID anders ist, bedeutet dies, dass die zweite Anwendungsinstanz ist.

JNLP bietet auch eine SingleInstanceListener

Andere Tipps

Ich verwende die folgende Methode im Hauptverfahren. Dies ist die einfachste, robusteste und schonendste Methode, die ich gesehen habe, so dachte ich, dass ich es teilen würde.

private static boolean lockInstance(final String lockFile) {
    try {
        final File file = new File(lockFile);
        final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
        final FileLock fileLock = randomAccessFile.getChannel().tryLock();
        if (fileLock != null) {
            Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    try {
                        fileLock.release();
                        randomAccessFile.close();
                        file.delete();
                    } catch (Exception e) {
                        log.error("Unable to remove lock file: " + lockFile, e);
                    }
                }
            });
            return true;
        }
    } catch (Exception e) {
        log.error("Unable to create and/or lock file: " + lockFile, e);
    }
    return false;
}

Wenn die App. verfügt über eine GUI, starten Sie es mit JWS und die SingleInstanceService verwenden. Sehen Sie sich die Demo. die SingleInstanceService (Demo. und) Beispielcode.

Ja, das ist eine wirklich anständige Antwort für Eclipse RCP einzelne Instanz Anwendung Eclipse unter meinem Code

in application.java

if(!isFileshipAlreadyRunning()){
        MessageDialog.openError(display.getActiveShell(), "Fileship already running", "Another instance of this application is already running.  Exiting.");
        return IApplication.EXIT_OK;
    } 


private static boolean isFileshipAlreadyRunning() {
    // socket concept is shown at http://www.rbgrn.net/content/43-java-single-application-instance
    // but this one is really great
    try {
        final File file = new File("FileshipReserved.txt");
        final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
        final FileLock fileLock = randomAccessFile.getChannel().tryLock();
        if (fileLock != null) {
            Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    try {
                        fileLock.release();
                        randomAccessFile.close();
                        file.delete();
                    } catch (Exception e) {
                        //log.error("Unable to remove lock file: " + lockFile, e);
                    }
                }
            });
            return true;
        }
    } catch (Exception e) {
       // log.error("Unable to create and/or lock file: " + lockFile, e);
    }
    return false;
}

Wir verwenden Datei für diese Verriegelung (eine exklusive Sperre auf einer magischen Datei in der App-Data-Verzeichnis des Benutzers greifen), aber wir sind in erster Linie daran interessiert, mehrere Instanzen von je laufen zu verhindern.

Wenn Sie versuchen, die zweite Instanz Passbefehlszeile args zu haben, etc ... an der ersten Instanz, dann eine Socket-Verbindung auf localhost verwenden, wird zwei Vögel mit einem Stein töten. Allgemein Algorithmus:

  • Beim Start versuchen Zuhörer auf localhost auf Port XXXX zu öffnen
  • Wenn nicht, öffnen Sie ein Schriftsteller zu diesem Port auf localhost und die Befehlszeile args senden, dann Abschalten
  • sonst hört auf Port XXXXX auf localhost. Wenn das Empfangsbefehlszeile args, verarbeiten sie, als ob die App mit dieser Befehlszeile gestartet wurde.

Ich habe eine Lösung, ein wenig cartoonish Erklärung gefunden, funktioniert aber immer noch in den meisten Fällen. Es nutzt die einfache alte Lockdatei Sachen zu schaffen, aber in einer ganz anderen Sicht:

http://javalandscape.blogspot.com/ 2008/07 / Single-Instance-from-your-application.html

Ich denke, es wird eine Hilfe für diejenigen mit einer strengen Firewall-Einstellung sein.

Sie können Junique Bibliothek verwenden. Es bietet Unterstützung für Single-Instance-Java-Anwendung ausgeführt und ist Open-Source.

http://www.sauronsoftware.it/projects/junique/

  

Die Junique Bibliothek kann verwendet werden, um einen Benutzer zu verhindern, bei den gleichen lief   Zeit mehr Instanzen derselben Java-Anwendung.

     

Junique implementiert Schlösser und Kommunikationskanäle zwischen allen geteilt   die JVM-Instanzen durch den gleichen Benutzer gestartet.

public static void main(String[] args) {
    String appId = "myapplicationid";
    boolean alreadyRunning;
    try {
        JUnique.acquireLock(appId, new MessageHandler() {
            public String handle(String message) {
                // A brand new argument received! Handle it!
                return null;
            }
        });
        alreadyRunning = false;
    } catch (AlreadyLockedException e) {
        alreadyRunning = true;
    }
    if (!alreadyRunning) {
        // Start sequence here
    } else {
        for (int i = 0; i < args.length; i++) {
            JUnique.sendMessage(appId, args[0]));
        }
    }
}

Unter der Haube schafft es Dateisperren in% USER_DATA% /. Junique Ordnern und erstellt einen Server-Socket an zufällig Port für jede eindeutige appId, die Nachrichten zwischen Java-Anwendungen ermöglicht das Senden / Empfangen.

Unter Windows können Sie verwenden, launch4j .

ManagementFactory Klasse unterstützt in J2SE 5.0 oder höher Detail

aber jetzt benutze ich J2SE 1.4 und ich fand diese ein http://audiprimadhanty.wordpress.com/2008/06/30/ensuring-one-instance-of-application-running-at-one-time/ aber ich habe nie testen. Was denken Sie darüber?

Sie könnten versuchen, die Einstellungen API. Es ist plattformunabhängig.

Eine generische Art und Weise der Anzahl der Instanz der Begrenzung auf einer einzigen Maschine ist oder sogar ein ganzes Netzwerk, ist eine Multicast-Buchse zu verwenden.

einen Multicast-Buchse verwenden, können Sie eine Nachricht an jede Menge Instanzen Ihrer Anwendung übertragen, von denen einige auf physisch entfernten Rechnern über ein Firmennetzwerk sein kann.

Auf diese Weise können Sie viele Arten von Konfigurationen ermöglichen, zu kontrollieren Dinge wie

  • eine oder mehrere Instanzen pro Maschine
  • eine oder mehrere Instanzen pro Netzwerk
  • (zB Installationen auf Client-Seite zu steuern)

Java Multicast-Unterstützung ist über java.net Paket mit MulticastSocket & Datagram die wichtigsten Werkzeuge zu sein.

Hinweis : MulticastSocket ist keine Garantie für die Lieferung von Datenpaketen, so dass Sie ein Werkzeug auf den Multicast-Buchsen wie JGroups . JGroups funktioniert Garantie Lieferung aller Daten. Es ist eine einzige JAR-Datei, mit einem sehr einfachen API.

JGroups hat rund eine Weile gewesen und hat einige beeindruckende Verwendungen in der Industrie, zum Beispiel untermauert es JBoss Clustering der Mechanismus Daten tun Broadcast an alle Instanz eines Clusters.

JGroups verwenden, um die Anzahl der Instanzen einer Anwendung zu begrenzen (auf einer Maschine oder ein Netzwerk, kann sagen: die Anzahl der Lizenzen ein Kunde gekauft hat) ist vom Konzept her sehr einfach:

  • Beim Start der Anwendung, jede Instanz versucht, eine benannte Gruppe zB „My Great App Group“ beizutreten. Sie haben wird diese Gruppe konfiguriert 0, 1 oder N-Mitglieder ermöglichen
  • Wenn das Gruppenmitglied Zahl größer ist als das, was Sie dafür konfiguriert haben .. Ihre App sollte starten verweigern.

Sie können eine Memory Mapped-Datei öffnen und dann sehen, ob die Datei bereits geöffnet ist. wenn es bereits geöffnet ist, können Sie aus dem Haupt zurück.

Weitere Möglichkeiten, um Lock-Dateien (Standard-Unix-Praxis) zu verwenden. Eine weitere Möglichkeit ist, etwas in die Zwischenablage zu setzen, wenn der Haupt beginnt nach der Überprüfung, wenn etwas bereits in der Zwischenablage.

Else, können Sie eine Steckdose in einem Hörmodus (ServerSocket-) öffnen. Zuerst versuchen zu hte Steckdose zu verbinden; wenn Sie keine Verbindung herstellen können, dann einen Serversocket öffnen. wenn Sie eine Verbindung herstellen, dann wissen Sie, dass eine andere Instanz bereits ausgeführt wird.

So ziemlich jede Systemressource verwendet werden kann, um zu wissen, dass eine App ausgeführt wird.

BR, ~ A

verwenden I-Buchsen für die und je nachdem, ob die Anwendung auf der Client-Seite oder die Serverseite ist das Verhalten etwas anders ist:

  • Client-Seite. Wenn eine Instanz bereits vorhanden ist (ich kann nicht an einem bestimmten Port hören) Ich werde die Anwendungsparameter und Ausfahrt passieren (Sie können einige Aktionen in der vorherige Instanz auszuführen), wenn nicht ich die Anwendung starten
  • Serverseite:. Wenn eine Instanz bereits existiert, wird ich eine Nachricht und beendet sich, wenn nicht ich die Anwendung starten
public class SingleInstance {
    public static final String LOCK = System.getProperty("user.home") + File.separator + "test.lock";
    public static final String PIPE = System.getProperty("user.home") + File.separator + "test.pipe";
    private static JFrame frame = null;

    public static void main(String[] args) {
        try {
            FileChannel lockChannel = new RandomAccessFile(LOCK, "rw").getChannel();
            FileLock flk = null; 
            try {
                flk = lockChannel.tryLock();
            } catch(Throwable t) {
                t.printStackTrace();
            }
            if (flk == null || !flk.isValid()) {
                System.out.println("alread running, leaving a message to pipe and quitting...");
                FileChannel pipeChannel = null;
                try {
                    pipeChannel = new RandomAccessFile(PIPE, "rw").getChannel();
                    MappedByteBuffer bb = pipeChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1);
                    bb.put(0, (byte)1);
                    bb.force();
                } catch (Throwable t) {
                    t.printStackTrace();
                } finally {
                    if (pipeChannel != null) {
                        try {
                            pipeChannel.close();
                        } catch (Throwable t) {
                            t.printStackTrace();
                        }
                    } 
                }
                System.exit(0);
            }
            //We do not release the lock and close the channel here, 
            //  which will be done after the application crashes or closes normally. 
            SwingUtilities.invokeLater(
                new Runnable() {
                    public void run() {
                        createAndShowGUI();
                    }
                }
            );

            FileChannel pipeChannel = null;
            try {
                pipeChannel = new RandomAccessFile(PIPE, "rw").getChannel();
                MappedByteBuffer bb = pipeChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1);
                while (true) {
                    byte b = bb.get(0);
                    if (b > 0) {
                        bb.put(0, (byte)0);
                        bb.force();
                        SwingUtilities.invokeLater(
                            new Runnable() {
                                public void run() {
                                    frame.setExtendedState(JFrame.NORMAL);
                                    frame.setAlwaysOnTop(true);
                                    frame.toFront();
                                    frame.setAlwaysOnTop(false);
                                }
                            }
                        );
                    }
                    Thread.sleep(1000);
                }
            } catch (Throwable t) {
                t.printStackTrace();
            } finally {
                if (pipeChannel != null) {
                    try {
                        pipeChannel.close();
                    } catch (Throwable t) {
                        t.printStackTrace();
                    } 
                } 
            }
        } catch(Throwable t) {
            t.printStackTrace();
        } 
    }

    public static void createAndShowGUI() {

        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(800, 650);
        frame.getContentPane().add(new JLabel("MAIN WINDOW", 
                    SwingConstants.CENTER), BorderLayout.CENTER);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

Bearbeiten : Anstatt diesen WatchService Ansatzes der Verwendung, ein einfacher 1 Sekunde-Timer Thread verwendet werden könnte, wenn die indicatorFile.exists zu überprüfen (). So, und dann bringt die Anwendung toFront ().

Bearbeiten : Ich möchte wissen, warum diese Downvoted wurde. Es ist die beste Lösung, die ich bisher gesehen habe. Z.B. der Server-Socket-Ansatz schlägt fehl, wenn eine andere Anwendung bereits passiert mit dem Port zu lauschen.

Einfach herunterladen Microsoft Windows Sysinternals TCPView (oder verwenden Sie netstat) , starten sie es, sortiert nach „State“, suchen sie nach dem Zeilenblock, der „ZUHÖREN“ sagt einer, dessen Fern Pick-Adresse sagt den Namen Ihres Computers, setzen sie diesen Port in Ihrem neuen Socket () - Lösung. In meiner Implementierung davon, kann ich Ausfall jedes Mal produzieren. Und es ist logische , weil es die Grundlage des Ansatzes. Oder was soll ich nicht immer darüber, wie diese umzusetzen?

Bitte informieren Sie mich, ob und wie ich bin falsch darüber!

Meine Sicht - was ich Sie bitte, wenn möglich, zu widerlegen - ist, dass die Entwickler einen Ansatz in der Produktion Code zu verwenden empfohlen werden, die in mindestens 1 von etwa 60000 Fälle scheitern. Und wenn diese Ansicht richtig sein geschieht, dann kann es absolut nicht sein, dass eine Lösung vorgestellt, die dieses Problem nicht haben wird und Downvoted für seine Menge an Code kritisiert.

Nachteile des Sockels Ansatz im Vergleich:

  • schlägt fehl, wenn die falschen Lotterielos (Portnummer) gewählt werden.
  • schlägt in Multi-User-Umgebung: Nur ein Benutzer die Anwendung zur gleichen Zeit ausgeführt werden kann. (Mein Ansatz wäre leicht geändert werden muß, um die Datei (en) im Benutzerbaum zu erstellen, aber das ist trivial.)
  • schlägt fehl, wenn Firewall-Regeln zu streng sind.
  • Macht verdächtige Benutzer (die ich in der freien Natur entsprachen) fragen, was Spielereien sind Sie bis zu, wenn Sie Ihren Texteditor einen Server-Socket behauptet.

Ich hatte gerade eine nette Idee, wie man das neue Instanz-existing-Instanz Java Kommunikationsproblem in einer Art und Weise zu lösen, die auf jedem System funktionieren sollen. So peitschte ich in etwa zwei Stunden, um diese Klasse auf. Funktioniert wie ein Zauber: D

Es basiert auf Robert 's Dateisperre Ansatz (auch auf dieser Seite), die ich seitdem verwendet haben. Um die bereits laufende Instanz zu sagen, dass ein andere Instanz zu starten versucht (aber nicht) ... eine Datei erstellt und sofort gelöscht, und die erste Instanz verwendet die WatchService diese Ordner Inhaltsänderung zu erkennen. Ich kann nicht glauben, dass dies offenbar ist eine neue Idee, da, wie grundlegend das Problem ist.

Dies kann leicht geändert werden, um nur erstellen und die Datei nicht löschen und dann können Informationen in sie gesetzt werden, dass die richtige Instanz beurteilen kann, zum Beispiel die Befehlszeilenargumente - und die richtige Instanz können dann die Löschung durchzuführen. Ich persönlich brauchte nur zu wissen, wann mein Anwendungsfenster wieder herzustellen und es nach vorne senden.

Beispiel für die Verwendung:

public static void main(final String[] args) {

    // ENSURE SINGLE INSTANCE
    if (!SingleInstanceChecker.INSTANCE.isOnlyInstance(Main::otherInstanceTriedToLaunch, false)) {
        System.exit(0);
    }

    // launch rest of application here
    System.out.println("Application starts properly because it's the only instance.");
}

private static void otherInstanceTriedToLaunch() {
    // Restore your application window and bring it to front.
    // But make sure your situation is apt: This method could be called at *any* time.
    System.err.println("Deiconified because other instance tried to start.");
}

Hier ist die Klasse:

package yourpackagehere;

import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileLock;
import java.nio.file.*;




/**
 * SingleInstanceChecker v[(2), 2016-04-22 08:00 UTC] by dreamspace-president.com
 * <p>
 * (file lock single instance solution by Robert https://stackoverflow.com/a/2002948/3500521)
 */
public enum SingleInstanceChecker {

    INSTANCE; // HAHA! The CONFUSION!


    final public static int POLLINTERVAL = 1000;
    final public static File LOCKFILE = new File("SINGLE_INSTANCE_LOCKFILE");
    final public static File DETECTFILE = new File("EXTRA_INSTANCE_DETECTFILE");


    private boolean hasBeenUsedAlready = false;


    private WatchService watchService = null;
    private RandomAccessFile randomAccessFileForLock = null;
    private FileLock fileLock = null;


    /**
     * CAN ONLY BE CALLED ONCE.
     * <p>
     * Assumes that the program will close if FALSE is returned: The other-instance-tries-to-launch listener is not
     * installed in that case.
     * <p>
     * Checks if another instance is already running (temp file lock / shutdownhook). Depending on the accessibility of
     * the temp file the return value will be true or false. This approach even works even if the virtual machine
     * process gets killed. On the next run, the program can even detect if it has shut down irregularly, because then
     * the file will still exist. (Thanks to Robert https://stackoverflow.com/a/2002948/3500521 for that solution!)
     * <p>
     * Additionally, the method checks if another instance tries to start. In a crappy way, because as awesome as Java
     * is, it lacks some fundamental features. Don't worry, it has only been 25 years, it'll sure come eventually.
     *
     * @param codeToRunIfOtherInstanceTriesToStart Can be null. If not null and another instance tries to start (which
     *                                             changes the detect-file), the code will be executed. Could be used to
     *                                             bring the current (=old=only) instance to front. If null, then the
     *                                             watcher will not be installed at all, nor will the trigger file be
     *                                             created. (Null means that you just don't want to make use of this
     *                                             half of the class' purpose, but then you would be better advised to
     *                                             just use the 24 line method by Robert.)
     *                                             <p>
     *                                             BE CAREFUL with the code: It will potentially be called until the
     *                                             very last moment of the program's existence, so if you e.g. have a
     *                                             shutdown procedure or a window that would be brought to front, check
     *                                             if the procedure has not been triggered yet or if the window still
     *                                             exists / hasn't been disposed of yet. Or edit this class to be more
     *                                             comfortable. This would e.g. allow you to remove some crappy
     *                                             comments. Attribution would be nice, though.
     * @param executeOnAWTEventDispatchThread      Convenience function. If false, the code will just be executed. If
     *                                             true, it will be detected if we're currently on that thread. If so,
     *                                             the code will just be executed. If not so, the code will be run via
     *                                             SwingUtilities.invokeLater().
     * @return if this is the only instance
     */
    public boolean isOnlyInstance(final Runnable codeToRunIfOtherInstanceTriesToStart, final boolean executeOnAWTEventDispatchThread) {

        if (hasBeenUsedAlready) {
            throw new IllegalStateException("This class/method can only be used once, which kinda makes sense if you think about it.");
        }
        hasBeenUsedAlready = true;

        final boolean ret = canLockFileBeCreatedAndLocked();

        if (codeToRunIfOtherInstanceTriesToStart != null) {
            if (ret) {
                // Only if this is the only instance, it makes sense to install a watcher for additional instances.
                installOtherInstanceLaunchAttemptWatcher(codeToRunIfOtherInstanceTriesToStart, executeOnAWTEventDispatchThread);
            } else {
                // Only if this is NOT the only instance, it makes sense to create&delete the trigger file that will effect notification of the other instance.
                //
                // Regarding "codeToRunIfOtherInstanceTriesToStart != null":
                // While creation/deletion of the file concerns THE OTHER instance of the program,
                // making it dependent on the call made in THIS instance makes sense
                // because the code executed is probably the same.
                createAndDeleteOtherInstanceWatcherTriggerFile();
            }
        }

        optionallyInstallShutdownHookThatCleansEverythingUp();

        return ret;
    }


    private void createAndDeleteOtherInstanceWatcherTriggerFile() {

        try {
            final RandomAccessFile randomAccessFileForDetection = new RandomAccessFile(DETECTFILE, "rw");
            randomAccessFileForDetection.close();
            Files.deleteIfExists(DETECTFILE.toPath()); // File is created and then instantly deleted. Not a problem for the WatchService :)
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private boolean canLockFileBeCreatedAndLocked() {

        try {
            randomAccessFileForLock = new RandomAccessFile(LOCKFILE, "rw");
            fileLock = randomAccessFileForLock.getChannel().tryLock();
            return fileLock != null;
        } catch (Exception e) {
            return false;
        }
    }


    private void installOtherInstanceLaunchAttemptWatcher(final Runnable codeToRunIfOtherInstanceTriesToStart, final boolean executeOnAWTEventDispatchThread) {

        // PREPARE WATCHSERVICE AND STUFF
        try {
            watchService = FileSystems.getDefault().newWatchService();
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        final File appFolder = new File("").getAbsoluteFile(); // points to current folder
        final Path appFolderWatchable = appFolder.toPath();


        // REGISTER CURRENT FOLDER FOR WATCHING FOR FILE DELETIONS
        try {
            appFolderWatchable.register(watchService, StandardWatchEventKinds.ENTRY_DELETE);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }


        // INSTALL WATCHER THAT LOOKS IF OUR detectFile SHOWS UP IN THE DIRECTORY CHANGES. IF THERE'S A CHANGE, ANOTHER INSTANCE TRIED TO START, SO NOTIFY THE CURRENT ONE OF THAT.
        final Thread t = new Thread(() -> watchForDirectoryChangesOnExtraThread(codeToRunIfOtherInstanceTriesToStart, executeOnAWTEventDispatchThread));
        t.setDaemon(true);
        t.setName("directory content change watcher");
        t.start();
    }


    private void optionallyInstallShutdownHookThatCleansEverythingUp() {

        if (fileLock == null && randomAccessFileForLock == null && watchService == null) {
            return;
        }

        final Thread shutdownHookThread = new Thread(() -> {
            try {
                if (fileLock != null) {
                    fileLock.release();
                }
                if (randomAccessFileForLock != null) {
                    randomAccessFileForLock.close();
                }
                Files.deleteIfExists(LOCKFILE.toPath());
            } catch (Exception ignore) {
            }
            if (watchService != null) {
                try {
                    watchService.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        Runtime.getRuntime().addShutdownHook(shutdownHookThread);
    }


    private void watchForDirectoryChangesOnExtraThread(final Runnable codeToRunIfOtherInstanceTriesToStart, final boolean executeOnAWTEventDispatchThread) {

        while (true) { // To eternity and beyond! Until the universe shuts down. (Should be a volatile boolean, but this class only has absolutely required features.)

            try {
                Thread.sleep(POLLINTERVAL);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


            final WatchKey wk;
            try {
                wk = watchService.poll();
            } catch (ClosedWatchServiceException e) {
                // This situation would be normal if the watcher has been closed, but our application never does that.
                e.printStackTrace();
                return;
            }

            if (wk == null || !wk.isValid()) {
                continue;
            }


            for (WatchEvent<?> we : wk.pollEvents()) {

                final WatchEvent.Kind<?> kind = we.kind();
                if (kind == StandardWatchEventKinds.OVERFLOW) {
                    System.err.println("OVERFLOW of directory change events!");
                    continue;
                }


                final WatchEvent<Path> watchEvent = (WatchEvent<Path>) we;
                final File file = watchEvent.context().toFile();


                if (file.equals(DETECTFILE)) {

                    if (!executeOnAWTEventDispatchThread || SwingUtilities.isEventDispatchThread()) {
                        codeToRunIfOtherInstanceTriesToStart.run();
                    } else {
                        SwingUtilities.invokeLater(codeToRunIfOtherInstanceTriesToStart);
                    }

                    break;

                } else {
                    System.err.println("THIS IS THE FILE THAT WAS DELETED: " + file);
                }

            }

            wk.reset();
        }
    }

}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top