java - ventana - evitar que un jinternalframe se abra dos veces
¿Cómo permitir ejecutar solo una instancia de un programa Java a la vez? (9)
Necesito evitar que los usuarios inicien mi aplicación Java (aplicación WebStart Swing) varias veces. Por lo tanto, si la aplicación ya se está ejecutando, no debería ser posible volver a iniciarla o mostrar una advertencia / cerrarse nuevamente.
¿Hay alguna manera conveniente de lograr esto? Pensé en bloquear un puerto o escribir algo en un archivo. ¿Pero espero que pueda acceder a algunas propiedades del sistema o a la JVM?
por cierto plataforma objetivo es Windows XP con Java 1.5
Como la pregunta indica que se está utilizando WebStart, la solución obvia es usar javax.jnlp.SingleInstanceService
.
Este servicio está disponible en 1.5. Tenga en cuenta que 1.5 actualmente se encuentra en su mayor parte a lo largo de su período de finalización de la vida útil. ¡Consigue con Java SE 6!
Creo que la mejor idea sería utilizar el bloqueo de archivos (una idea bastante antigua :)). Desde Java 1.4 se introdujo una nueva biblioteca de E / S, que permite el bloqueo de archivos.
Una vez que la aplicación se inicia, intenta adquirir un bloqueo en un archivo (o crearlo si no existe), cuando la aplicación sale, el bloqueo se relasifica. Si la aplicación no puede adquirir un bloqueo, se cierra.
El ejemplo de cómo hacer el bloqueo de archivos es, por ejemplo, en Java Developers Almanac .
Si desea utilizar el bloqueo de archivos en la aplicación Java Web Start o un applet, debe cantar la aplicación o el applet.
Creo que su sugerencia de abrir un puerto para escuchar cuando inicia su aplicación es la mejor idea.
Es muy fácil de hacer y no tiene que preocuparse por limpiarlo cuando cierre su aplicación. Por ejemplo, si escribe en un archivo pero alguien luego mata los procesos utilizando el Administrador de tareas, el archivo no se eliminará.
Además, si recuerdo correctamente, no hay una manera fácil de obtener el PID de un proceso Java desde el interior de la JVM, así que no intente formular una solución utilizando PID.
Algo como esto debería hacer el truco:
private static final int PORT = 9999;
private static ServerSocket socket;
private static void checkIfRunning() {
try {
//Bind to localhost adapter with a zero connection queue
socket = new ServerSocket(PORT,0,InetAddress.getByAddress(new byte[] {127,0,0,1}));
}
catch (BindException e) {
System.err.println("Already running.");
System.exit(1);
}
catch (IOException e) {
System.err.println("Unexpected error.");
e.printStackTrace();
System.exit(2);
}
}
Este código de muestra se vincula explícitamente a 127.0.0.1
que debería evitar cualquier advertencia de firewall, ya que el tráfico en esta dirección debe provenir del sistema local.
Al elegir un puerto, intente evitar uno mencionado en la lista de puertos conocidos . Idealmente, debería hacer que el puerto utilizado sea configurable en un archivo o mediante un interruptor de línea de comandos en caso de conflictos.
Hacemos lo mismo en C ++ creando un objeto de exclusión mutua del kernal y buscándolo en el inicio. Las ventajas son las mismas que usar un socket, es decir, cuando el proceso muere / se bloquea / sale / se mata, el kernel limpia el objeto de exclusión mutua.
No soy un programador de Java, así que no estoy seguro de si puedes hacer lo mismo en Java.
He creado la clase AppLock multiplataforma.
http://rumatoest.blogspot.com/2015/01/howto-run-only-single-java-application.html
Está utilizando la técnica de bloqueo de archivos.
Actualizar. En 2016-10-14, he creado un paquete compatible con maven / gradle https://github.com/jneat/jneat y lo expliqué aquí http://rumatoest.blogspot.com/2015/06/synchronize-code-over-multiple-jvm.html
He visto muchas de estas preguntas y buscaba resolver el mismo problema de una manera independiente de la plataforma que no tiene la oportunidad de colisionar con firewalls o meterse en cosas de socket.
Entonces, esto es lo que hice:
import java.io.File;
import java.io.IOException;
/**
* This static class is in charge of file-locking the program
* so no more than one instance can be run at the same time.
* @author nirei
*/
public class SingleInstanceLock {
private static final String LOCK_FILEPATH = System.getProperty("java.io.tmpdir") + File.separator + "lector.lock";
private static final File lock = new File(LOCK_FILEPATH);
private static boolean locked = false;
private SingleInstanceLock() {}
/**
* Creates the lock file if it''s not present and requests its deletion on
* program termination or informs that the program is already running if
* that''s the case.
* @return true - if the operation was succesful or if the program already has the lock.<br>
* false - if the program is already running
* @throws IOException if the lock file cannot be created.
*/
public static boolean lock() throws IOException {
if(locked) return true;
if(lock.exists()) return false;
lock.createNewFile();
lock.deleteOnExit();
locked = true;
return true;
}
}
El uso de System.getProperty ("java.io.tmpdir") para la ruta de acceso del archivo de bloqueo garantiza que siempre se cree el bloqueo en el mismo lugar.
Entonces, desde tu programa solo llamas algo como:
blah blah main(blah blah blah) {
try() {
if(!SingleInstanceLock.lock()) {
System.out.println("The program is already running");
System.exit(0);
}
} catch (IOException e) {
System.err.println("Couldn''t create lock file or w/e");
System.exit(1);
}
}
Y eso lo hace por mí. Ahora, si elimina el programa, no eliminará el archivo de bloqueo, pero puede resolverlo escribiendo el PID del programa en el archivo de bloqueo y haciendo que el método lock () verifique si ese proceso ya se está ejecutando. Esto se deja como una evaluación para cualquier persona interesada. :)
Podría usar el registro, aunque esto no sirve para nada con el propósito de usar un lenguaje de alto nivel como java. Al menos tu plataforma objetivo es windows = D
Prueba JUnique:
String appId = "com.example.win.run.main";
boolean alreadyRunning;
try {
JUnique.acquireLock(appId);
alreadyRunning = false;
} catch (AlreadyLockedException e) {
alreadyRunning = true;
}
if (alreadyRunning) {
Sysout("An Instance of this app is already running");
System.exit(1);
}
Puedes usar la librería JUnique. Proporciona soporte para ejecutar aplicaciones java de instancia única y es de código abierto.
http://www.sauronsoftware.it/projects/junique/
Vea también mi respuesta completa en ¿Cómo implementar una aplicación Java de una sola instancia?