UniqueInstance.java Source code

Java tutorial

Introduction

Here is the source code for UniqueInstance.java

Source

/**
 *
DEScribe - A Discrete Experience Sampling cross platform application
Copyright (C) 2011
Sbastien Faure <sebastien.faure3@gmail.com>,
Bertrand Gros   <gros.bertrand@gmail.com>,
Yannick Prie    <yannick.prie@univ-lyon1.fr>.
    
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

//package api.utils;

/**
 * Class UniqueInstance.java
 * @description Limit the use of only one instance of DEScribe at a time
 * @author Sbastien Faure  <sebastien.faure3@gmail.com>
 * @author Bertrand Gros    <gros.bertrand@gmail.com>
 * @version 2011-01-28
 */
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.logging.Logger;

/**
Cette classe permet d'assurer l'unicit de l'instance de l'application. Deux applications ne peuvent pas tre lances
simultanment. Voici un exemple typique d'utilisation :
 *
 * <pre>
 * // Port  utiliser pour communiquer avec l'instance de l'application lance.
 * final int PORT = 32145;
 * // Message  envoyer  l'application lance lorsqu'une autre instance essaye de dmarrer.
 * final String MESSAGE = "nomDeMonApplication";
 * // Actions  effectuer lorsqu'une autre instance essaye de dmarrer.
 * final Runnable RUN_ON_RECEIVE = new Runnable() {
 * public void run() {
 * if(mainFrame != null) {
 * // Si la fentre n'est pas visible (uniquement dans le systray par exemple), on la rend visible.
 * if(!mainFrame.isVisible())
 * mainFrame.setVisible(true);
 * // On demande  la mettre au premier plan.
 * mainFrame.toFront();
 * }
 * }
 * });
 *
 * UniqueInstance uniqueInstance = new UniqueInstance(PORT, MESSAGE, RUN_ON_RECEIVE);
 * // Si aucune autre instance n'est lance...
 * if(uniqueInstance.launch()) {
 * // On dmarre l'application.
 * new MonApplication();
 * }
 * </pre>
 *
 * @author rom1v
 */
public class UniqueInstance {

    /** Port d'coute utilis pour l'unique instance de l'application. */
    private int port;
    /** Message  envoyer  l'ventuelle application dj lance. */
    private String message;
    /** Actions  effectuer lorsqu'une autre instance de l'application a indiqu qu'elle avait essay de dmarrer. */
    private Runnable runOnReceive;

    /**
     * Crer un gestionnaire d'instance unique de l'application.
     *
     * @param port
     * Port d'coute utilis pour l'unique instance de l'application.
     * @param message
     * Message  envoyer  l'ventuelle application dj lance, {@code null} si aucune action.
     * @param runOnReceive
     * Actions  effectuer lorsqu'une autre instance de l'application a indiqu qu'elle avait essay de
     * dmarrer, {@code null} pour aucune action.
     */
    public UniqueInstance(int port, String message, Runnable runOnReceive) {
        assert port > 0 && port < 1 << 16 : "Le port doit tre entre 1 et 65535";
        assert message != null
                || runOnReceive == null : "Il y a des actions  effectuer => le message ne doit pas tre null.";
        this.port = port;
        this.message = message;
        this.runOnReceive = runOnReceive;
    }

    /**
     * Crer un gestionnaire d'instance unique de l'application. Ce constructeur dsactive la communication entre
     * l'instance dj lance et l'instance qui essaye de dmarrer.
     *
     * @param port
     * Port d'coute utilis pour l'unique instance de l'application.
     */
    public UniqueInstance(int port) {
        this(port, null, null);
    }

    /**
     * Essaye de dmarrer le gestionnaire d'instance unique. Si l'initialisation a russi, c'est que l'instance est
     * unique. Sinon, c'est qu'une autre instance de l'application est dj lance. L'appel de cette mthode prvient
     * l'application dj lance qu'une autre vient d'essayer de se connecter.
     *
     * @return {@code true} si l'instance de l'application est unique.
     */
    public boolean launch() {
        /* Indique si l'instance du programme est unique. */
        boolean unique;

        try {
            /* On cre une socket sur le port dfini. */
            final ServerSocket server = new ServerSocket(port);

            /* Si la cration de la socket russit, c'est que l'instance du programme est unique, aucune autre n'existe. */
            unique = true;

            /* Si il y a des actions  faire lorsqu'une autre instance essaye de dmarrer... */
            if (runOnReceive != null) {

                /* On lance un Thread d'coute sur ce port. */
                Thread portListenerThread = new Thread() {

                    @Override
                    public void run() {
                        /* Tant que l'application est lance... */
                        while (true) {
                            try {
                                /* On attend qu'une socket se connecte sur le serveur. */
                                final Socket socket = server.accept();

                                /* Si une socket est connecte, on coute le message envoy dans un nouveau Thread. */
                                new Thread() {

                                    @Override
                                    public void run() {
                                        receive(socket);
                                    }
                                }.start();
                            } catch (IOException e) {
                                Logger.getLogger("UniqueInstance")
                                        .warning("Attente de connexion de socket choue.");
                            }
                        }
                    }
                };

                /* Le Thread d'coute de port est dmon. */
                portListenerThread.setDaemon(true);

                /* On dmarre le Thread. */
                portListenerThread.start();
            }
        } catch (IOException e) {
            /* Si la cration de la socket choue, c'est que l'instance de n'est pas unique, une autre n'existe. */
            unique = false;

            /* Si des actions sont prvues par l'instance dj lance... */
            if (runOnReceive != null) {
                /*
                 * Dans ce cas, on envoie un message  l'autre instance de l'application pour lui demander d'avoir le
                 * focus (par exemple).
                 */
                send();
            }
        }
        return unique;
    }

    /**
     * Envoie un message  l'instance de l'application dj ouverte.
     */
    private void send() {
        PrintWriter pw = null;
        try {
            /* On se connecte sur la machine locale. */
            Socket socket = new Socket("localhost", port);

            /* On dfinit un PrintWriter pour crire sur la sortie de la socket. */
            pw = new PrintWriter(socket.getOutputStream());

            /* On crit le message sur la socket. */
            pw.write(message);
        } catch (IOException e) {
            Logger.getLogger("UniqueInstance").warning("criture sur flux de sortie de la socket chou.");
        } finally {
            if (pw != null) {
                pw.close();
            }
        }
    }

    /**
     * Reoit un message d'une socket s'tant connecte au serveur d'coute. Si ce message est le message de l'instance
     * unique, l'application demande le focus.
     *
     * @param socket
     * Socket connect au serveur d'coute.
     */
    private void receive(Socket socket) {
        Scanner sc = null;

        try {
            /* On n'coute que 5 secondes, si aucun message n'est reu, tant pis... */
            socket.setSoTimeout(5000);

            /* On dfinit un Scanner pour lire sur l'entre de la socket. */
            sc = new Scanner(socket.getInputStream());

            /* On ne lit qu'une ligne. */
            String s = sc.nextLine();

            /* Si cette ligne est le message de l'instance unique... */
            if (message.equals(s)) {
                /* On excute le code demand. */
                runOnReceive.run();
            }
        } catch (IOException e) {
            Logger.getLogger("UniqueInstance").warning("Lecture du flux d'entre de la socket chou.");
        } finally {
            if (sc != null) {
                sc.close();
            }
        }

    }
}