# Programm darf nicht mehrfach startbar sein



## programmierer123 (22. Nov 2017)

Hallo Leute,
wie schaffe ich es, dass ein Programm nicht mehrfach startbar ist?
Habe mir folgendes überlegt:

1. Gleich am Anfang der main Methode überprüfen, ob eine bestimmte Textdatei existiert.
    2a) Falls, ja, dann Programm mit einer Meldung beenden.
    2b) Falls, nein, dann diese Textdatei(z.B. auf dem Desktop) erstellen und Code "ganz normal" ausführen.
2. Beim Beenden des Programms wird die Datei gelöscht.

Problem: Beim Absturz des Programms wird die Datei nicht gelöscht und das Programm ist nicht mehr startbar.

Hättet ihr ein paar Verbesserungsvorschläge und Tipps?


----------



## Robat (22. Nov 2017)

Ich würde das Programm an einen ServerSocket binden... Man kann nur einen ServerSocket mit dem selben Port haben, daher kann man auch nur eine Instanz der Anwendung gleichzeitig offen haben. 

Nachteil: im worst Case nutzt eine andere Applikation diesen Port eventuell schon..


----------



## VfL_Freak (22. Nov 2017)

Moin,


Robat hat gesagt.:


> Ich würde das Programm an einen ServerSocket binden... Man kann nur einen ServerSocket mit dem selben Port haben, daher kann man auch nur eine Instanz der Anwendung gleichzeitig offen haben.


Hinter sowas suche ich auch schon länger hinterher.
Wir haben hier in einem Programm auch die Logik mit dem ServerSocketeingebaut, allerdings scheint das nicht auf jedem Rechner zu funktionieren !!
Es gibt Rechner (sind alles ganz normale Win7-Rechner), bei denen ich trotzdem zwei Instanzen starten kann 
Eine wirkliche Lösung habe ich dafür noch nicht gefunden!

Sieht bei mir so aus:

```
/**
     * @brief Soll verhindern, dass mehr als eine Worker-Instanz aktiv wird (funktioniert nicht auf jedem Rechner !?!?)
     */
     private void checkMultiWorkers()
     {
             try
             {
                 listenerSocket = new ServerSocket( nMutexServerport );  // ist ne fixe Nummer - 62500 !!
             }
             catch( Exception ex )
             {
                 ex.printStackTrace( );
                 JOptionPane.showMessageDialog( this,
                                    "Es ist bereits ein Worker auf diesem System aktiv.\n" +
                                    "Sollte dies nicht der Fall sein, bitte den Rechner neu starten !",
                                    "Fehler", JOptionPane.ERROR_MESSAGE, null );
                 System.exit( 0 );
             }
     } // checkMultiWorkers
```

VG Klaus


----------



## X5-599 (22. Nov 2017)

Kann es sein, dass der GC das Objekt in dem der "listenerSocket" liegt irgendwann abräumt? Bei StackOverflow gibt es Lösungen wo der Socket deshalb als static Attribut abgelegt wird. Aber auch dort diskutieren die Leute heiss über etwaige Probleme einer solchen Socket Lösung (Firewalls, Port schon belegt durch anderes Programm etc) Mir scheint, es gibt keine "Catch All" Lösung...


----------



## Enceladus271 (22. Nov 2017)

Ich würde einen Shutdown Hook zum Löschen der Datei verwenden. Der wird auch aufgerufen wenn das Programm abstüzt: https://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html#addShutdownHook(java.lang.Thread)


----------



## Thallius (22. Nov 2017)

du kannst auch einfach einen TimeStamp in die datei schreiben und diesen mit einem Timer z.B. jede Minute erneuern. Ist der TimeStamp beim Starten älter als 1min, wird das Programm gestartet sonst nicht.

Gruß

Claus


----------



## truesoul (22. Nov 2017)

Darf die Anwendung auch als Service laufen?
Wenn ja, dann kann man für den Service einen Namen vergeben und via batch z. B die Anwendung starten wenn der Service nicht mehr als Prozess läuft.
Sprich nach dem Prinzip:

Batch erstellen
In der Batch nach dem Prozess suchen
Wenn nicht gefunden dann starten
Dann via nssm z.B ein Service erstellen mit der eingebunden batch
Was ist das für eine Anwendung?

Java 9 bietet auch einen neue Process Api. Vielleicht lohnt es sich da mal rein zu schauen.
Mit der neuen Process Api kannst du dann von der Anwendung die pid in der Datei Speichern und dann beim start der Anwendung die Datei auslesen und die pid suchen. Wenn die gefunden wird, beendest du die ANwendung wieder.

Oder du blockierst mit deiner Anwendung die Datei solange sie läuft. Somit an keine zweite Anwendung die Datei beschreiben. Ist aber eher unschön. Damit kannst du dann checken ob die Datei "beschreibbar" ist.

Ansonsten finde ich @Thallius auch ganz schön gut. Nur das es nicht verhindert innerhalb der eine Minute mehrere Anwendungen zu starten. 

Grüße


----------



## programmierer123 (22. Nov 2017)

Ich würde gerne die Methode verwenden, wo man ein lock in einer Datei hat.
Könntet ihr mir dazu Tipps geben, wie ich das ambesten mache?


----------



## truesoul (22. Nov 2017)

Naja. Ich würde sagen du beschreibst die Datei und schließt nicht den Stream. Ich muss aber dazu sagen dass ich das noch nicht getestet habe. Also gebe keine Garantie dass es funktioniert.  

Grüße


----------



## Thallius (22. Nov 2017)

truesoul hat gesagt.:


> Ansonsten finde ich @Thallius auch ganz schön gut. Nur das es nicht verhindert innerhalb der eine Minute mehrere Anwendungen zu starten.
> 
> Grüße



Überleg nochmal genau wie meine Idee funktioniert 

Natürlich verhindert diese das.
Sie macht es nur nicht möglich nach einem Absturz die Software diese direkt wieder zu starten. Da muss man halt im schlimmsten Fall 1 Minute warten oder die Datei löschen.


----------



## truesoul (22. Nov 2017)

Thallius hat gesagt.:


> Überleg nochmal genau wie meine Idee funktioniert
> 
> Natürlich verhindert diese das.
> Sie macht es nur nicht möglich nach einem Absturz die Software diese direkt wieder zu starten. Da muss man halt im schlimmsten Fall 1 Minute warten oder die Datei löschen.



Oops. Nicht älter als... Ok. Habe nix gesagt/geschrieben.


----------



## truesoul (23. Nov 2017)

Ein Beispiel wie man eine Datei "locked"


```
RandomAccessFile file = null;
        FileLock fileLock = null;

        try {

            file = new RandomAccessFile("test.txt", "rw");
            FileChannel fileChannel = file.getChannel();
            fileLock = fileChannel.tryLock();

            if (fileLock != null) {

                System.out.println("File is locked");
                int sec = 0;
                while (sec < 20) {
                    sec++;
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }

        } finally {

            if (fileLock != null) {
                fileLock.release();
            }
        }
```

Grüße


----------



## JuKu (23. Nov 2017)

Robat hat gesagt.:


> Ich würde das Programm an einen ServerSocket binden... Man kann nur einen ServerSocket mit dem selben Port haben, daher kann man auch nur eine Instanz der Anwendung gleichzeitig offen haben.



Diese Lösung wird mit Java 9 nicht mehr garantiert funktionieren, da ab Java 9 mehrere JVMs auf dem selben Port listen können.
Ein Beispiel dazu gibt es hier:
https://gist.github.com/thomasdarimont/c5fcb95a7fbbc562cee5b0b3fccf74e8

Wie @Enceladus271 halte ich die Variante mit dem Shutdown Hook am sinnvollsten:
http://jukusoft.com/2016/12/05/java-shutdown-hook/

Oder alternativ die Datei locken. Wobei ich mir nicht sicher bin, ob die JVM die Datei unlocked, wenn sie hart beendet wird oder crashed.
Es gibt aber meines Wissens auch noch eine Methode File.onDeleteOnExit(), mit der man sicherstellen kann, dass die Datei wirklich beim Ende des Programmes gelöscht wird.


----------



## mrBrown (23. Nov 2017)

JuKu hat gesagt.:


> Wie @Enceladus271 halte ich die Variante mit dem Shutdown Hook am sinnvollsten:
> http://jukusoft.com/2016/12/05/java-shutdown-hook/
> 
> Oder alternativ die Datei locken. Wobei ich mir nicht sicher bin, ob die JVM die Datei unlocked, wenn sie hart beendet wird oder crashed.
> Es gibt aber meines Wissens auch noch eine Methode File.onDeleteOnExit(), mit der man sicherstellen kann, dass die Datei wirklich beim Ende des Programmes gelöscht wird.


Nö, wenn die JVM crashed oder gekillt wird, werden ShutdownHooks und afaik auch deleteOnExit nicht ausgeführt.


----------



## RalleYTN (24. Nov 2017)

Ist jetzt eine Windows-Only Lösung.
Wenn man eine Ausführbare JAR mit Launch4j in eine EXE verpackt kann man unter anderem ein Kreuz machen was dafür sorgt, dass das Programm nur einmal gestartet wird.


----------



## programmierer123 (25. Nov 2017)

truesoul hat gesagt.:


> Ein Beispiel wie man eine Datei "locked"
> 
> 
> ```
> ...




@truesoul 
Kannst du mir den Code erklären?
Zwar habe ich in der Java API geschaut aber so richtig klar ist mir der Code nicht.

1) Die RandomAccessFile Klasse kann in eine zufällige Datei lesen oder schreiben.
Wozu brauchen wir diese Klasse? Wir haben doch eine Klasse File, mit der wir eine Datei erstellt haben?
2) Warum brauchen wir einen Kanal(FileChannel)? In der API steht, dass diese Klasse
 zum Lesen, Schreiben, Mappen und Bearbeiten einer Datei da ist. Ist das nicht unnötig? Geht das bearbeiten einer Datei auch nur über die Klasse File?
3) Wozu ist die Methode getChannel() da?
4) Warum wird hochgezählt und der Thread schlafen gelegt?
5) Muss die Überprüfung im finally Block nicht so fileLock == null anstatt fileLock != null lauten?


----------



## truesoul (26. Nov 2017)

Hallo. 

1. Wie brauchen RandomAccessFile weil die uns ein FileChannel liefert. 

2. Mit FileChannel haben wir halt die Möglichkeit das beschreiben für andere zu blockieren.

3. getChannel() liefert uns das eindeutig der Datei zugeordnete FileChannel. 

4. Diente als Beispiel. Sprich ich habe für 20 Sekunden die Datei blockiert. Jede Schleifendurchlauf halt ich den Main Thread für 1 Sekunden an. Somit kommen wir auf 20 Sekunden. 

5. Nur wenn ich die Datei blockiere brauche ich auch nur 20 Sekunden diese Blockade halten. Deswegen  != null. 

Wenn es sich um eine Windows Anwendung handelt ist @RalleYTN Tipp auch gut.

Grüße


----------

