Apenas uma instância da aplicação?

Olá PessoALL,

Se existe a resposta pra isso no Fórum, ta bem escondida… oiUHSoiUHSoIs :smiley:

É possivel em JAVA, deixar apenas uma instância da app rodar? caso o usuário tente rodar ela novamente, e já esteja aberta, ela seja chamada “à frente”? :wink:

Obrigadu!! :!:

Procure ler sobre Design Patterns “Sinlgetoon”, onde vc nunca criará instâncias da classe vc chama ela, e ela cria uma instância de memoria apenas 1 vez!

OK Luciano, vou dar um olhada nos singleton…

Ele criará apenas uma instância mesmo que tenha o main? :oops:

Vallew’s

Olá,

Você pode usar o singleton para criar instância única de objetos, como classes por exemplo. Isso não implica em proteção da aplicação rodando apenas em uma instância, pois isso já é de controle do Java, apesar que da para fazer dessa forma:

:seta: Configura a sua aplicação para uma única porta, dessa forma como toda e qualquer aplicação na máquina apenas poderá rodar em uma única porta, então caso uma nova instância seja lançada para a mesma porta, uma exception será lançada. Veja:

private static final int RUN_PORT = 8666; 

void main(String[] av) 
{ 
        try 
        { 
                java.net.ServerSocket ss = new java.net.ServerSocket(PORT); 
        } 
      catch (java.net.BindException ex) 
        { 
                System.out.println("Programa está rodando!"); 
                System.exit(1); 
        } 
      catch (java.io.IOException ex) 
        { 
                ex.printStackTrace(); 
                System.exit(1); 
        } 
}

Ainda por cima você pode setar um properties do qual poderá setar um flag quando a aplicação estiver rodando, se outra aplicação da mesma rodar, ele irá ler o arquivo e verificará que uma já está ativada, então sai do programa, o problema disso é quando um crashing na máquina é gerado, a variável ficará setada como aplicação rodando, então o usuário não poderá mais entrar, a não ser que altere o properties na mão.

sigleton garante UMA unica instancia por JVM. se vc mandar rodar o programa duas veze, vai disparar duas vezes a JVM, então isso nao vai funcionar.
quanto a usar arquivo para controlar isso, o problema eh se a app eh finalizada inesperadamente, deixando o arquivo lockado.
a melhor maneira que eu conheço(by Luca), eh vc colocar um server socket no ar quando iniciar a app. toda vez que a app for iniciar vai verificar se o socket ta no ar, se tiver nao inicia a segunda vez.

[]'s

Amigos jgbt e Grinvon

Vallew muito a idéia de vocês, eu sequer havia pensando nisso… :wink:

Muito obrigado!! :smiley:

[quote=jgbt]sigleton garante UMA unica instancia por JVM
[/quote]
Corrigindo.
Por ClassLoader.

Estou reupando esse tópico pois tenho uma dúvida sobre essa solução do serversocket…

Deixar essa porta aberta “no ar” não pode gerar nenhum problema de segurança na minha aplicação?
É possível alguém exploitar essa porta aberta?

Outra coisa… só esse código do Grinvon nao foi suficiente para reservar a porta para minha aplicação, tive que adicionar a linha ss.accept() para funcionar. Tem outro jeito de lockar a porta?

Fora essas soluções postadas aqui, eu pensei em salvar o número PID da minha aplicação em um arquivo quando ela inicializar, aí quando uma nova instancia da aplicação for chamada, ela verifica se o PID salvo em arquivo está rodando no momento. Se não estiver, ela deixa a aplicação abrir, caso contrário ela finaliza a aplicação, assim se a luz acabar ou alguém resetar a máquina no botão a aplicação não vai ficar lockada, já que o PID dificilmente será o mesmo.
Teria como eu pegar o PID da aplicação facilmente?

[]'s

alguem? :frowning:

não sei se esse tópico ainda está ativo, mas aí vaí…
javazilla…
essa última solução que você propôs (com relação ao PID) é a mais correta, porém não devemos salvar nada em arquivo, mas sim, fazermos a comparação em tempo de execução.
Dessa forma não precisamos reservar uma porta TCP sem necessidade e também não ficamos expostos à crash´s de aplicação.
segue o código e a explicação.

// imports necessários
import sun.management.ConnectorAddressLink;
import sun.jvmstat.monitor.HostIdentifier;
import sun.jvmstat.monitor.Monitor;
import sun.jvmstat.monitor.MonitoredHost;
import sun.jvmstat.monitor.MonitoredVm;
import sun.jvmstat.monitor.MonitoredVmUtil;
import sun.jvmstat.monitor.MonitorException;
import sun.jvmstat.monitor.VmIdentifier;

public static void main(String args[]) {
        /* O método ManagementFactory.getRuntimeMXBean() retorna um identificador com o PID da aplicação
            nas JVM´s da Sun, mas cada jvm pode ter sua implementação desse identificador.
            Portanto em outras jvm´s esse código pode não funcionar, :(
        */
        RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean();
        final int runtimePid = Integer.parseInt(rt.getName().substring(0,rt.getName().indexOf("@")));
                              
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                
                // se existe outra instância, mostra a mensagem e finaliza a instância atual.
                // Caso contrário inicia a aplicação.
                if (getMonitoredVMs(runtimePid))
                {
                    new MainFrame().setVisible(true);
                } else
                    JOptionPane.showMessageDialog(null,"There is another instance of this application running.");
                
            }
        });
}

O método getMonitoredVMs(int processPid) recebe o PID da aplicação atual, pega o nome da linha de comando,
por exemplo, a aplicação foi iniciada a partir do caminho c:\java\app\teste.jar, então a variável vai ficar atribuída com o valor “c:\java\app\teste.jar”. Feito isso iremos pegar apenas o nome da aplicação.
Depois varremos toda a JVM em busca de outro processo com o mesmo nome… caso já exista e o PID desse processo seja diferente, quer dizer que é a segunda instância da applicação.

private static boolean getMonitoredVMs(int processPid) {
        MonitoredHost host;
        Set vms;
        try {
            host = MonitoredHost.getMonitoredHost(new HostIdentifier((String)null));
            vms = host.activeVms();
        } catch (java.net.URISyntaxException sx) {
            throw new InternalError(sx.getMessage());
        } catch (MonitorException mx) {
            throw new InternalError(mx.getMessage());
        }
        MonitoredVm mvm = null;
        String processName = null;
        try{
            mvm = host.getMonitoredVm(new VmIdentifier(String.valueOf(processPid)));
            processName = MonitoredVmUtil.commandLine(mvm);
            processName = processName.substring(processName.lastIndexOf("\") + 1,processName.length());
            mvm.detach();
        } catch (Exception ex) {
            
        }
       // Essa linha é somente para verificar o nome do processo aberto. Pode ser retirada
        JOptionPane.showMessageDialog(null,processName);
        for (Object vmid: vms) {
            if (vmid instanceof Integer) {
                int pid = ((Integer) vmid).intValue();
                String name = vmid.toString(); // default to pid if name not available
                try {
                     mvm = host.getMonitoredVm(new VmIdentifier(name));
                     // use the command line as the display name
                     name =  MonitoredVmUtil.commandLine(mvm);
                     name = name.substring(name.lastIndexOf("\")+1,name.length());
                     mvm.detach();
                     if ((name.equalsIgnoreCase(processName)) && (processPid != pid))
                         return false;
                } catch (Exception x) {
                     // ignore
                }
            }
        }
        
        return true;
}

O problema de todo esse código são os imports que são classes definidas somente no arquivo tools.jar que têm um tamanho de 11MB. Porém, para contornar esse problema, eu descompactei esse arquivo e compactei um novo chamado VM.ZIP somente com as classes necessárias q ficou com um tamanho de 81kb.

Para utilizar esse arquivo é só adicioná-lo ao projeto como uma biblioteca.
Acho q é isso, qq dúvida é só colocar aqui no forum!

OBS: Quando vocês estiverem debugando o código o processName vai ficar atribuído à classe principal do projeto, por exemplo, o nome da sua classe é Principal e está no pacote com.main… a variável processName vai ficar atribuída como “com.main.Principal”. Para testar vc tem q abrir outra instância do Debug e vai dar certo.
No caso da versão Release vai aparecer o nome da aplicação conforme visualizado no commando JOptionPane.showMessageDialog(null,processName);

mto boa explicação, obrigado, ajudou mto!!!