2009-05-28 29 views
26

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 reiniciarla o mostrar una advertencia/cerrar de nuevo.¿Cómo se permite ejecutar solo una instancia de un programa Java a la vez?

¿Hay alguna manera conveniente de lograr esto? Pensé en bloquear un puerto o escribir algo en un archivo. ¿Pero con suerte puede acceder a algunas propiedades del sistema o a la JVM?

btw. la plataforma de destino es Windows XP con Java 1.5

+0

más solución más simple :-) java.nio.file bloquear completa ver ejemplo http: // stackoverflow. com/a/20015771/185022 –

Respuesta

35

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 necesita preocuparse por limpiarlo cuando cierre la aplicación. Por ejemplo, si escribe en un archivo pero alguien mata los procesos usando el Administrador de tareas, el archivo no se eliminará.

Además, si mal no recuerdo, no hay una manera fácil de obtener el PID de un proceso de Java desde la JVM, así que no intente formular una solución utilizando PID.

Algo como esto debe 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 ejemplo se une de forma explícita a 127.0.0.1 que debe evitar las advertencias de firewall, como cualquier tráfico en esta dirección debe ser desde el sistema local.

Al elegir un puerto, trate de evitar uno mencionado en el list of Well Known Ports. Lo ideal es que el puerto utilizado se pueda configurar en un archivo o mediante un interruptor de línea de comando en caso de conflicto.

+0

¡Buena idea! ¿Podría haber problemas de seguridad con un SecurityManager muy estricto? – guerda

+1

¿Esto causaría alguna advertencia de firewall? ¿Cómo elegir un puerto que sabemos que no está siendo utilizado por otros programas? – Erwin

+0

Como señala Erwin, creo que esto causaría advertencias de firewall. No puedo estar completamente de acuerdo con este enfoque. Debe haber una manera más elegante. ¿Dónde está Jon Skeet? –

0

Puede utilizar el registro, aunque esto infructuosamente el propósito de utilizar un lenguaje de alto nivel como Java. Al menos su plataforma de destino es windows = D

+0

También sufre el mismo problema que un archivo de bloqueo: si el programa se muere, ¿cómo sabe cuándo romper el bloqueo? – simpleuser

1

Hacemos lo mismo en C++ creando un objeto mutex kernal y buscándolo al inicio. Las ventajas son las mismas que cuando se usa un socket, es decir, cuando el proceso muere/falla/se cierra/muere, el objeto mutex es limpiado por el kernel.

No soy un programador de Java, por lo que no estoy seguro de si puede hacer el mismo tipo de cosas en Java?

26

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 es actualmente la mayor parte de su período de fin de vida útil del servicio. ¡Conviértete en Java SE 6!

+0

Solución perfecta, gracias. –

3

Creo que la mejor idea sería usar 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, trata de obtener el bloqueo de un archivo (o crearlo si no existe), cuando la aplicación sale, el bloqueo se vuelve a mostrar. Si la aplicación no puede adquirir un bloqueo, se cierra.

El ejemplo de cómo bloquear 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.

+0

Los bloqueos de archivos no funcionarán debido a que Taskmanager o los comandos kill de Linux no liberan el archivo de bloqueo – JPM

-1

He visto muchas de estas preguntas y estaba buscando resolver el mismo problema de una manera independiente de plataforma no aprovecha la oportunidad de colisionar con firewalls o meterse en cosas de socket.

lo tanto, aquí 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; 
    } 
} 

Usando System.getProperty ("java.io.tmpdir") para la ruta de archivo de bloqueo se asegura de que siempre va a crear su bloqueo en el mismo lugar.

Entonces, desde su programa que acaba de llamar algo así 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 matas el programa, no eliminará el archivo de bloqueo, pero puedes resolverlo escribiendo el PID del programa en el archivo de bloqueo y haciendo que el método lock() compruebe si ese proceso ya se está ejecutando. Esto se deja como una tarea para cualquier persona interesada. :)

0

JUnique Probar:

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); 
} 
Cuestiones relacionadas