Java tutorial
/** * 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(); } } } }