Como permitir executando apenas uma instância de um programa em Java em um momento?

StackOverflow https://stackoverflow.com/questions/920386

  •  06-09-2019
  •  | 
  •  

Pergunta

Eu preciso impedir os utilizadores de iniciar a minha aplicação Java (WebStart balanço app) várias vezes. Então, se o aplicativo já está em execução, não deve ser possível para iniciá-lo novamente ou mostrar um aviso / ser fechada novamente.

Existe alguma maneira conveniente para conseguir isso? Eu pensei sobre o bloqueio de uma porta ou escrever sth para um arquivo. Mas espero que você pode acessar algumas propriedades do sistema ou a JVM?

btw. plataforma de destino é Windows XP com Java 1.5

Foi útil?

Solução

Eu acho que a sua sugestão de abrir uma porta para ouvir quando você iniciar seu aplicativo é a melhor idéia.

É muito fácil de fazer e você não precisa se preocupar em limpá-lo quando você fecha o aplicativo. Por exemplo, se você escrever para um arquivo mas alguém, em seguida, mata os processos usando o Gerenciador de Tarefas do arquivo não vai ficar excluído.

Além disso, se bem me lembro há nenhuma maneira fácil de obter o PID de um processo Java de dentro da JVM então não tente e formular uma solução utilizando PIDs.

Algo como isso deve fazer o truque:

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 exemplo explicitamente liga a 127.0.0.1 que deve evitar quaisquer avisos de firewall, como qualquer tráfego neste endereço deve ser a partir do sistema local.

Ao escolher uma tentativa porta para evitar mencionado na href="http://www.iana.org/assignments/port-numbers" lista do conhecido Portos . Idealmente, você deve fazer a porta usada configurável em um arquivo ou através de uma opção de linha de comando em caso de conflitos.

Outras dicas

Como os questão afirma que WebStart está sendo usado, a solução óbvia é usar javax.jnlp.SingleInstanceService .

Este serviço está disponível em 1,5. Note-se que 1.5 é atualmente a maior parte do caminho através de seu final do período de vida útil. Obter com o Java SE 6!

Eu acho que a melhor idéia seria usar bloqueio de arquivo (bastante uma ideia antiga :)). Desde Java 1.4 uma nova biblioteca de I / O foi introduzido, que permite o bloqueio de arquivos.

Uma vez que o aplicativo é iniciado tenta trava adquirida em um arquivo (ou criá-lo se não existe), quando o aplicativo é encerrado o bloqueio é relased. Se a aplicação não pode adquirir um bloqueio, ele sai.

O exemplo de como fazer o bloqueio de arquivos é, por exemplo, em Java Developers Almanac .

Se você quiser usar o bloqueio de arquivos no aplicativo Java Web Start ou um applet que você precisa para cantar o aplicativo ou o applet.

Nós fazemos o mesmo em C ++, criando um objeto mutex kernal e olhando para ele no arranque. As vantagens são o mesmo que usar um encaixe, ou seja, quando o processo de fieiras / falhas / saídas / é morto, o objecto de exclusão mútua é limpo pelo kernel.

Eu não sou um programador Java, então eu não tenho certeza se você pode fazer o mesmo tipo de coisa em Java?

Você pode usar a biblioteca JUnique. Ele fornece suporte para a execução de aplicativos Java de instância única e é open-source.

http://www.sauronsoftware.it/projects/junique/

Veja também a minha resposta completa em Como implementar um aplicativo Java única instância?

Eu tenho criar a classe plataforma AppLock cruz.

http: //rumatoest.blogspot .com / 2015/01 / howto-run-only-single-java-application.html

É utilizando a técnica de bloqueio de arquivo.

Update. No 2016/10/14 Eu criei pacote compatível com maven / Gradle https://github.com/jneat/ jneat e explicou-lo aqui http: / /rumatoest.blogspot.com/2015/06/synchronize-code-over-multiple-jvm.html

Você pode usar o registro, embora esta derrota halfheartedly o propósito de usar uma linguagem de alto nível como Java. Pelo menos sua plataforma de destino é janelas = D

Tente 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);
}

Eu vi tantos de esta pergunta e eu estava olhando para resolver o mesmo problema de uma forma independente de plataforma que não tem a chance de colidir com firewalls ou entrar em coisas socket.

Então, aqui está o que eu fiz:

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 o caminho lockfile garante que você sempre irá criar o seu bloqueio no mesmo lugar.

Em seguida, a partir do programa que você acabou de chamar 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);
    }
}

E que faz isso por mim. Agora, se você matar o programa não irá apagar o arquivo de bloqueio, mas você pode resolver isso por escrito PID do programa na lockfile e fazer a verificação método lock () se esse processo já está em execução. Este é deixado como um assingment para qualquer pessoa interessada. :)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top