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