1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
|
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 lancées
* simultanément. Voici un exemple typique d'utilisation :
*
* <pre>
* // Port à utiliser pour communiquer avec l'instance de l'application lancée.
* final int PORT = 32145;
* // Message à envoyer à l'application lancée lorsqu'une autre instance essaye de démarrer.
* final String MESSAGE = "nomDeMonApplication";
* // Actions à effectuer lorsqu'une autre instance essaye de démarrer.
* final Runnable RUN_ON_RECEIVE = new Runnable() {
* public void run() {
* if(mainFrame != null) {
* // Si la fenêtre 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 lancée...
* if(uniqueInstance.launch()) {
* // On démarre l'application.
* new MonApplication();
* }
* </pre>
*
* @author rom1v
*/
public class UniqueInstance
{
private int port; // Port d'écoute utilisé pour l'unique instance de l'application.
private String message; // Message à envoyer à l'éventuelle application déjà lancée.
private Runnable runOnReceive; // Actions à effectuer lorsqu'une autre instance de l'application a indiqué qu'elle avait essayé de démarrer.
/**
* Créer 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 déjà lancée, {@code null} si aucune action.
* @param runOnReceive
* Actions à effectuer lorsqu'une autre instance de l'application a indiqué qu'elle avait essayé de
* démarrer, {@code null} pour aucune action.
* @param runOnReceive
* Actions à effectuer lorsqu'une autre instance de l'application a indiqué qu'elle
* avait essayé de démarrer, {@code null} pour aucune action.
* @throws IllegalArgumentException
* si le port n'est pas compris entre 1 et 65535, ou si
* {@code runOnReceive != null && message == null} (s'il y a des actions à
* effectuer, le message ne doit pas être {@code null}.
*/
public UniqueInstance(int port, String message, Runnable runOnReceive)
{
if (port == 0 || (port & 0xffff0000) != 0)
throw new IllegalArgumentException("Le port doit être compris entre 1 et 65535 : " + port + ".");
if (runOnReceive != null && message == null)
throw new IllegalArgumentException("runOnReceive != null ==> message == null.");
this.port = port;
this.message = message;
this.runOnReceive = runOnReceive;
}
/**
* Créer un gestionnaire d'instance unique de l'application. Ce constructeur désactive la communication entre
* l'instance déjà lancée et l'instance qui essaye de démarrer.
*
* @param port
* Port d'écoute utilisé pour l'unique instance de l'application.
*/
public UniqueInstance(int port)
{
this(port, null, null);
}
/**
* Essaye de démarrer le gestionnaire d'instance unique. Si l'initialisation a réussi, c'est que l'instance est
* unique. Sinon, c'est qu'une autre instance de l'application est déjà lancée. L'appel de cette méthode prévient
* l'application déjà lancée qu'une autre vient d'essayer de se connecter.
*
* @return {@code true} si l'instance de l'application est unique.
*/
public boolean launch()
{
boolean unique; // Indique si l'instance du programme est unique.
try {
final ServerSocket server = new ServerSocket(port); // On crée une socket sur le port défini.
unique = true; // Si la création de la socket réussit, c'est que l'instance du programme est unique, aucune autre n'existe. */
if(runOnReceive != null) // S'il y a des actions à faire lorsqu'une autre instance essaye de démarrer...
{
Thread portListenerThread = new Thread("UniqueInstance-PortListenerThread")// On lance un Thread d'écoute sur ce port.
{
{
setDaemon(true);
}
@Override public void run()
{
while(true) // Tant que l'application est lancée...
{
try {
final Socket socket = server.accept(); // On attend qu'une socket se connecte sur le serveur.
new Thread("UniqueInstance-SocketReceiver") // Si une socket est connectée, on écoute le message envoyé dans un nouveau Thread.
{
{
setDaemon(true);
}
@Override public void run() {
receive(socket);
}
}.start();
} catch(IOException e) {
Logger.getLogger("UniqueInstance").warning("Attente de connexion de socket échouée.");
}
}
}
};
portListenerThread.start(); // On démarre le Thread.
}
} catch(IOException e) {
unique = false; //Si la création de la socket échoue, c'est que l'instance n'est pas unique, une autre n'existe.
if(runOnReceive != null) // Si des actions sont prévues par l'instance déjà lancée...
{
/*
* 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 déjà ouverte.
*/
private void send()
{
PrintWriter pw = null;
try
{
Socket socket = new Socket("localhost", port); // On se connecte sur la machine locale.
pw = new PrintWriter(socket.getOutputStream()); // On définit un PrintWriter pour écrire sur la sortie de la socket.
pw.write(message); // On écrit le message sur la socket.
} catch(IOException e)
{
Logger.getLogger("UniqueInstance").warning("Écriture sur flux de sortie de la socket échouée.");
} finally
{
if(pw != null)
pw.close();
}
}
/**
* Reçoit un message d'une socket s'étant connectée au serveur d'écoute. Si ce message est le message de l'instance
* unique, l'application demande le focus.
*
* @param socket
* Socket connectée au serveur d'écoute.
*/
private synchronized void receive(Socket socket)
{
Scanner sc = null;
try
{
socket.setSoTimeout(5000); // On n'écoute que 5 secondes, si aucun message n'est reçu, tant pis...
sc = new Scanner(socket.getInputStream()); // On définit un Scanner pour lire sur l'entrée de la socket. */
String s = sc.nextLine(); // On ne lit qu'une ligne.
if(message.equals(s)) // Si cette ligne est le message de l'instance unique...
{
runOnReceive.run(); // On exécute le code demandé.
}
} catch(IOException e) {
Logger.getLogger("UniqueInstance").warning("Lecture du flux d'entrée de la socket échoué.");
} finally {
if(sc != null)
sc.close();
}
}
} |
Partager