Ensure that there is only one instance
/**
*
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();
}
}
}
}
Related examples in the same category