Java-Programm nur einmal starten

Aus Byte-Welt Wiki
Zur Navigation springenZur Suche springen
Baustelle.png Dieser Beitrag wird derzeit noch bearbeitet. Der Text ist deshalb unvollständig und kann Fehler oder ungeprüfte Aussagen enthalten.

Einleitung

Unter bestimmten Umständen kann es gewünscht sein, dass nur eine aktive Instanz eines Programms auf einem Rechner ausführbar ist. Sollte das Programm ein weiteres Mal aufgerufen werden, soll die gerade aktive, unsichtbare Instanz des Programms in den Fokus des Bedieners geholt werden. Es stellt sich also die Frage: Wie kann überprüft werden, ob ein Programm schon läuft und dann das zweite Starten verhindert werden?

Jedes Java-Programm wird in einer eigenen virtuellen Maschine (VM) gestartet, somit sind sie räumlich voneinander getrennt.

Im Web sind einige interessante Vorschläge zu finden, die beschreiben, wie man ein Java-Programm so programmiert, dass nur eine aktive Instanz im Arbeitsspeicher zugelassen wird, man also ein Java-Programm nur "einmal" ausführen kann.

Möglichkeiten zur Lösung gibt es. Hier einige Ansätze:

  • eine Lock-Datei
  • einen Port sperren
  • Client/Server-Anwendung
  • eine Kombination aus alle dem.

Jede der Lösungen bietet Vorteile, wie auch Nachteile in der Praxis. In unserem Artikel Java-Anwendung nur einmal ausführen - Java-Blog-Buch wurden die wichtigsten beschrieben.

Demnach ist einer der größten Nachteile beim Einsatz von Lockdateien, dass man mit einer bereits gestarteten Anwendung nicht kommunizieren kann. Man kann die laufende Instanz bspw. nicht sichtbar machen, wenn sie gerade verdeckt ist. Das Gleiche trifft auf die Port-Sperrung zu. Client-/Server-Anwendungen sind meist etwas komplexer, können aber derartige Probleme lösen.

Eine bisher nicht besprochene Lösung wird im Folgenden beschrieben. Eine Lösung mit Hilfe der seit dem JDK 1.0 mitgelieferten Java-Bibliothek für verteilte Anwendungen - RMI. Sie besticht durch ihre Einfachheit gegenüber einer Client-/Server-Anwendung und einem Maximum an Vorteilen.

RMI - eine Einführung

Um die in diesem Artikel besprochene Lösung zu programmieren, werden keine RMI-Kenntnisse vorausgesetzt, sie werden aber beim Verstehen des Codes und der Vorgehensweise helfen, so dass später die Implementierung in eigenen Programmen leichter fällt.

Daher empfehlen wir, bevor wir weiter machen, den kurz und knapp gehaltenen Einführungsartikel (RMI minimal) zu studieren.

Schritt für Schritt zum Start-Limit

Beginnen wir also nun, eine kleine Demo-Anwendung zu programmieren, mit dem Ziel, dass diese nur einmal gestartet werden kann. Weitere Starts werden nur die bereits aktive Anwendung in den Fokus des Benutzers holen.
Wenn man es genau nimmt, wird zwar eine weitere Instanz der Anwendung gestartet werden können, jedoch nach der Kommunikation mit der bereits laufenden Instanz sofort wieder beendet.

Die Ausgangslage - ein einfaches Fenster

Zunächst möchten wir eine einfache Anwendung erzeugen, die wir dann im weiteren Schritt mit den gewünschten Funktionen ausbauen. <code=java> import java.awt.*; import java.awt.event.*; import javax.swing.*;

/**

* DemoFrame ist der Einstiegspunkt in die Anwendung.
* Hier werden wichtige Prüfungen angestoßen und die Benutzeroberfläche zusammengebaut.
* @author Gernot Segieth
*/

public class DemoFrame {

  public DemoFrame() {
     JFrame frame = new JFrame("Demo-Frame");
     frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
     frame.addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
           frame.dispose();
        }
     });
     frame.add(createMainPanel());
     frame.pack();
     frame.setLocationByPlatform(true);
     frame.setVisible(true);
  }
  private JPanel createMainPanel() {
     JPanel panel = new JPanel(new BorderLayout());
     panel.setPreferredSize(new Dimension(600, 400));
     return panel;
  }
  public static void main(String[] args) {
     try {

UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

     }
     catch(Exception ex) {

System.out.println(ex);

     }
     SwingUtilities.invokeLater(new Runnable() {
        public void run() {
           new DemoFrame();
        }
     });
  }

} </code=java>

Der Code der Anwendung ist schon für den nächsten Schritt vorbereitet, so dass in den folgenden Schritten weniger verwirrende Code-Änderungen durchgeführt werden müssen.

Die Ausgabe: ein leerer JFrame

Wir erzeugen also einen leeren JFrame.

In Zeile 8 schalten wir das Standardverhalten des JFrames beim Beenden (Klick auf den Schließen-Button des Fensters) aus. Damit der JFrame geschlossen werden kann, implementieren wir anschließend (Zeile 9 bis Zeile 13) einen WindowListener, der für die Beendigung des Programms sorgt. Man könnte das auch auch einfacher lösen, in dem man in Zeile 8 frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); schreiben würde. Aber wie erwähnt wird bereits ein später benötigtes Verhalten hier implementiert.

In Zeile 15 fügen wir das in Zeile 21 bis Zeile 25 definierte JPanel in dem JFrame ein. Das JPanel sorgt hier erst mal nur für die Größe des JFrames bei der Ausgabe auf dem Bildschirm. Später werden wir noch Code für das JPanel hinzufügen, der die Demo anschaulicher machen wird.

In Zeile 17 legen wir fest, dass die JVM unser Fenster dort auf dem Bildschirm positionieren wird, wie es den Regeln des Host-Systems zur Ausgabe von neuen Fenstern auf dem Bildschirm entspricht.

Vorüberlegung

Gut, eine einfache GUI wurde aufgebaut. Später werden wir noch einige Objekte darin ablegen/einfügen, um eine sinnvollere Anwendung zu erhalten.

Folgende Frage müssen wir uns nun beantworten: Wie muss sich ein Programm verhalten, um zu ermitteln, ob ein gleichartiges Java-Programm bereits ausgeführt wird?

1. Wird das Programm das "erste" Mal gestartet, muss es zunächst nach einer weiteren aktiven gleichartigen Anwendung "suchen". 2. Sollte es keine aktive gleichartige Anwendung geben, ist die soeben gestartete Instanz die erste und wird dem Benutzer auf dem Bildschirm angezeigt. 3. Ist allerdings bereits eine aktive gleichartige Anwendung auf dem Bildschirm, muss sich die zuletzt gestartete Instanz wieder beenden und die möglicherweise gerade nicht sichtbare erste Instanz in den Vordergrund des Bildschirms gebracht werden.

Und genau dieses Verhalten werden wir nun schrittweise implementieren.

Definition des Remote-Interfaces

Hier nun also die Definition unseres Remote-Interfaces, welches alle Methoden enthält, die unser Programm für das gewünschte Verhalten benötigt.

<code=java> import java.rmi.Remote; import java.rmi.RemoteException;

/**

* Das Interface beschreibt die benötigten Methoden, die von einem RemoteTask implementiert werden müssen.
* @author Gernot Segieth
*/

public interface RemoteTask extends Remote {

   /**
    * Wird prüfen, ob bereits ein RMI-Server online ist.
    * Der RMI-Server ist online, wenn bspw. der RMI-Port (1099) belegt ist, ein Remote-Objekt 
    * in der RMI-Registry gespeichert wurde und eine Methode dieses Objektes aufgerufen werden kann.
    * @return true, wenn festgestellt werden konnte, dass der RMI-Server bereits online ist,
    * sonst false.
    * @throws java.rmi.RemoteException
    */
   boolean isRunningAnotherInstance() throws RemoteException;
   
   /**
    * Wird die Benutzeroberfläche der bereits laufenden Programminstanz anzeigen.
    * @throws java.rmi.RemoteException
    */
   void showRunningInstance() throws RemoteException;
   

} </code=java>

Implementieren des Servers

Implementieren des Clients

Zusammenspiel Server und Client

Ausbau der Anwendung

Testen der fertigen Anwendung

Fragen

Das Thema wurde nicht ausreichend behandelt? Du hast Fragen dazu und brauchst weitere Informationen? Lass Dir von uns helfen!

Wir helfen dir gerne!


Dir hat dieser Artikel gefallen? Oder Du hast Fehler entdeckt und möchtest zur Berichtigung beitragen? Prima! Schreibe einen Kommentar!

Du musst angemeldet sein, um einen Kommentar abzugeben.