Logoff addShutdownHook

12 respostas
L

Bom dia Pessoal,

Estou com um problema! Espero que alguém me dê uma força.

Tenho uma aplicação que fica trabalhando ocultamente para os usuários,
ou seja fica nos processos do sistema operacional.

Esta aplicação fica enviando dados para o servidor de tempos em tempos.
Porém ao usuário desligar,reiniciar ou fazer logoff na máquina, minha aplicação deve pegar esta chamada de desligamento e alterar um campo na tabela (MYSQL) que é o servidor.

Pesquisei sobre este assunto e encontrei esta função:
addShutdownHook
Ela verifica se a JVM está finalizando e abre um treadh para eu fazer o que eu quiser com esse treadh, no caso inserir um valor no banco de dados MYSQL.

Ok… A questão é que quando minha aplicação dá um logoff, aí altera o campo no BD
Mas quando o usuário vai no iniciar e dá logoff… negativo!!! Isto pq mata os processos e não abre minha treadh.

Engraçado néh!
Alguém por favor se habilita a interagir e me auxiliar, por favor!
Obrigado!

12 Respostas

eberson_oliveira

ola lucas,

estive dando uma olhada no javadoc (desculpe mas aqui no trampo a gente usa java 4) e encontrei isso:

Já checou o que o seu sistema operacional faz quando você da logof.

Eu usei esse método na minha aplicação e funcionou normal… Qual sistema operacional você está usando? experimenta pesquisar o que ele faz pra ver se tem como contornar…

[]

L

Bom eberson…

Quanto ao o que o S.O faz… não tenho tanta experiência…
Mas se não estou me enganado ele busca um arquivo da pasta Windows32>logoff.exe

A partir daí ele roda o executavel. Que irá fechar a Virtual Machine e que DEVERIA chamar o meu método.
Eu até tentei criar um executavel em C++ que grave no banco de dados e depois chame o arquivo logoff2.exe que eu renomei, pois o executável do C++ coloquei com nome de logoff.exe. Mas o Windows atualiza a pasta windows32 e substitui o arquivo logoff.exe para o original… intão já era isso(a princípio).

Mas quanto a função addShutdownHook eu estou achando estranho… porque como vc disse deveria funcionar.
Em uma aplicação que você fez funcionou. Ainda que o usuário fizesse desligamento do sistema(logoff,shutdown,restart)???

Quem puder me esclarecer essa duvida, eu agradeço!
E muito obrigado Eberson!!! Valew pela força!

P

No caso do Windows, se vc. quer ter controle efetivo do login/logout vai ter que pesquisar um pouco sobre as APIs de Workstation e Desktop. Lembre-se de que, hoje em dia, é comum termos cenários em que um mesmo servidor é utilizado simultaneamente por mais de um usuário, via RDP ou Citrix. Para cada usuário conectado existe um objeto “Workstation”, que pode ou não ter um objeto “Desktop”.

Sinceramente, se eu fosse fazer algo assim não usaria Java. Implementar isto em C++, usando uma das “n” bibliotecas para implementação de serviço disponíveis por aí (procure no CodeGuru) vai ser bem mais simples e, como benefício extra, vai ficar bem mais leve em termos de consumo de memória.

Já se o seu caso for um ambiente multiplataforma (Windows/Linux/MacOS), eu faria um caminho semelhante, mas usando um ServerSocket no processo servidor (serviço no windows e daemon nos demais) e um miniaplicativo que ficaria no “System Tray” (btw: Java 6 possui uma API portável para acesso ao mesmo) e simplesmente estabeleceria uma conexão TCP/IP com keep-alive habilitado para o servidor local.

Se a sessão for finalizada de forma normal ou não, a conexão será finalizada, e seu processo servidor terá como identificar de forma garantida os logins/logouts. Ainda ficaria aberta a possibilidade de o usuário ou administrador do sistema matar o processo cliente, mas já é um começo…

L

Psevestre…

Há um tempo atrás tive dando uma olhada no sentid que você citou ServerSocket, enviando pacotes.
Bom como eu sou iniciante neste sentido… não quis me arriscar.

O sistema operacional é o Windows multiplataforma. O que praticamete seria mais viável o servidor fazer o papel de servidor.
Eu entendo o que você quis me passar. Mas não tenho esperiência neste ramo de enviar pacotes pela rede, já que é um laboratório.

Vou pesquisar no CodeGuru… espero achar uma solução!
Mas caso você ou outra pessoa tenha um material para estudo ou uma solução ajudem nos post.

Ps: Me cadastrei no dia 13/10/2008… incrível… o pessoal ajuda mesmo… CADASTREM!!!

L

Mas galera…

O que estou achando esquisito pe a função em questão no fórum addShutdownHook.
Porque minha aplicação está funcionando com excessão desta função…

Existe um .jar nas máquinas no laboratório que ficam enviando dados para o mysql.
E quando o usuário faz logoff por exemplo eu queria alterar um campo status na tabela.

É simples mas complicado…
Utilizei esta função dentro da main() está correto néh? porque foi assim que vi em outros exemplos.
Existe alguma maneira de captar quando o usuário está fazendo logoff?

Obrigado!

KWill

Se tu tiver coragem/paciência para tentar, pode ser que chamar a função atexit() da biblioteca padrão do C via JNI resolva seus problemas.
Um link de subsídio:
http://www.utas.edu.au/infosys/info/documentation/C/CStdLib.html

Inté.

KWill

Lembrei, lá na página do projeto JNA tem um exemplo que usa a função atexit(), fora que para mim é muito mais fácil usar código nativo via JNA do que via JNI. É lá na parte “Callbacks/Closures (Function Pointers)”.
Link do projeto do JNA:
https://jna.dev.java.net/

Inté.

L

Uma pergunta…

Esta função continua quando o Sistema Operacional entra em desligamento ou logoff???
atexit()

Quando por algum motivo a função falhar, no caso do S.O entrae em desligamento, esta função me retorna um valor que o java trate e perceba que deve chamar outra função, no caso, UPDATE no banco de dados.

Correto???
OU quando entra em modo de desligamento ou logoff, mata o processo e não dá tempo de nem tratar o retorno???

Obrigado!!!

L

PESSOAL MEU CÓDIGO ESTÁ ASSIM...

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package catraca_lpd;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.*;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;

/**
 *
 * @author lucas
 */

public class Main {
    private static String validacao;
    private static String status;
    private static String prioridade;
    private static String login;
    private static String so;
    private static String data;
    private static String time;
    
    private static int flag=0;//bandeira que diz para fazer o UPDATE
    private static String entrada_db=null;//horário da entrada do mysql
    private static String data_db=null;//data da entrada no mysql
    private static int online=0;
   
   
    
    
    
    
    
    /**
     * @param args the command line arguments
     */
    public static void verifica_user(String user) throws IOException{
        String update;
        String id;
        int id_insert = 0;
        try {
                  //Obtendo dados do MYSQL-----------------------------
                  Class.forName("com.mysql.jdbc.Driver");
                  Connection con = DriverManager.getConnection("jdbc:mysql://10.11.0.245/lpd?user=webmaster&password=");
                  
                  //Obtendo data 
                  Statement stm_data = con.createStatement();
                  ResultSet res_data = stm_data.executeQuery("SELECT CURRENT_DATE data"); 
                  if(res_data.next()){
                      data=res_data.getString("data");
                      if(data_db==null){//Somente na 1ª vez seta a variável
                        data_db=data;
                      }
                  }
                    
                  //Obtendo time 
                  Statement stm_time = con.createStatement();
                  ResultSet res_time = stm_time.executeQuery("SELECT CURRENT_TIME hora"); 
                  if(res_time.next()){
                        time=res_time.getString("hora");
                        if(entrada_db==null){//Somente na 1ª vez seta a variável
                            entrada_db=time;
                        }
                  }
                  
                  InetAddress endip = null;
                  try{
                       endip = InetAddress.getLocalHost();
                  }
                  catch (UnknownHostException e) {}
                  endip = InetAddress.getLocalHost();
                  String ip;
                  ip=endip.toString();
                  
                  so=(System.getProperty("os.name"));
                  
                  Statement stm = con.createStatement();
                  ResultSet res = stm.executeQuery("SELECT * FROM tb_catraca_usuarios WHERE ctr_usr_login='"+user+"'");                  
                  if(res.next()){
                    //Usuario está cadastrado e fará as verificações necessárias 
                    login=res.getString("ctr_usr_login");
                    prioridade=res.getString("ctr_usr_prioridade");
                    status=res.getString("ctr_usr_status");
                    
                    if(online==0){
                        if(status.equals("1") && prioridade.equals("1")){
                            //tentado fazer login, mas já tem alguém logado
                            Runtime.getRuntime().exec("logoff");
                        }
                    }
                    else{
                        if(status.equals("0")){
                           System.exit(1);
                        }
                    }
                    online=1;
                    
                    //Como passou pela verificação altera o campo de status, pq o user está online
                    Statement stm_status = con.createStatement();
                    update="UPDATE tb_catraca_usuarios SET ctr_usr_status='1' WHERE ctr_usr_login='"+user+"'";
                    stm_status.executeUpdate(update);
                     
                    //Obtendo o último ID  do mysql
                    Statement stm_id = con.createStatement();
                    ResultSet res_id = stm_id.executeQuery("SELECT ctr_acs_id FROM tb_catraca_acessos ORDER BY ctr_acs_id DESC LIMIT 1"); 
                    if(res_id.next()){
                        id=res_id.getString("ctr_acs_id");
                        id_insert=Integer.parseInt(id);//obtendo o último id
                        id_insert=id_insert+1;//id pronto para o insert
                    }
                    else{
                        //primeiro registro
                        id_insert='0';
                    }
                    
                    //verifica se deve fazer INSERT (1 vez) ou UPDATE (contínuo)
                    if(flag==1){
                         //Realiza os updates
                            Statement stm_insert = con.createStatement();
                            update="UPDATE tb_catraca_acessos SET ctr_acs_saida='"+time+"' WHERE ctr_acs_entrada='"+entrada_db+"' and ctr_acs_data='"+data_db+"'";
                            stm_insert.executeUpdate(update);
                            flag=1;
                    }
                    if(flag==0){
                            String inserir;
                            //Usuário não está logado(status==0), ou é um administrador
                            //Inserir as Informações de acesso
                            Statement stm_insert = con.createStatement();
                            inserir="INSERT INTO tb_catraca_acessos (`ctr_acs_id` ,`ctr_acs_usr_login` ,`ctr_acs_ip` ,`ctr_acs_so`,`ctr_acs_data` ,`ctr_acs_entrada` ,`ctr_acs_saida`) values ('"+id_insert+"','"+user+"','"+endip+"','"+so+"','"+data+"','"+time+"','"+time+"')";
                            stm_insert.executeUpdate(inserir);
                            flag=1;
                    }                    
                 }
                 //Este else é do if vindo do resultado do mysql 
                 else{
                    //Usuário não está cadastrado, logo fará o cadastro e mercações de acesso
                    Statement stm_insert = con.createStatement();
                    update="INSERT INTO tb_catraca_usuarios (`ctr_usr_login` ,`ctr_usr_prioridade` ,`ctr_usr_status`) VALUES ('"+user+"','1','1')";                  
                    stm_insert.executeUpdate(update);
                     
                    //Inserindo dados de marcações de acesso
                    update="INSERT INTO tb_catraca_acessos (`ctr_acs_id` ,`ctr_acs_usr_login` ,`ctr_acs_ip` ,`ctr_acs_so`,`ctr_acs_data` ,`ctr_acs_entrada` ,`ctr_acs_saida`) VALUES ('"+id_insert+"','"+user+"','"+endip+"','"+so+"','"+data+"','"+time+"','"+time+"')";
                    stm_insert.executeUpdate(update);
                    flag=1;
                    online=1;
                }
          }
          catch(ClassNotFoundException e) {
                System.out.println("Erro : "+e.getMessage()+" Erro");
          }
          catch(SQLException e) {
                System.out.println("Erro : "+e.getMessage()+" Erro");
          }        
    }
    //--------------------------------------------------------------------------
    public static void update(String user) throws IOException{
        String update;
        try {
                  //Obtendo dados do MYSQL-----------------------------
                  Class.forName("com.mysql.jdbc.Driver");
                  Connection con = DriverManager.getConnection("jdbc:mysql://10.11.0.245/lpd?user=webmaster&password=stpduas");
                  
                  System.out.println("Entrou no método UPDATE");
                  //Realiza os updates
                  Statement stm_insert = con.createStatement();
                  update="UPDATE tb_catraca_usuarios SET ctr_usr_status='0' WHERE ctr_usr_login='"+user+"'";
                  System.out.println("UPDATE = "+update);
                  stm_insert.executeUpdate(update);
                  online=0;
                  Runtime.getRuntime().exec("logoff");
          }
          catch(ClassNotFoundException e) {
                System.out.println("Erro : "+e.getMessage()+" Erro");
          }
          catch(SQLException e) {
                System.out.println("Erro : "+e.getMessage()+" Erro");
          }        
    }
    //--------------------------------------------------------------------------
    
    public static void main(String[] args) throws IOException {
            // TODO code application logic here
          int delay = 5000;   // delay for 40 sec.   
          int period = 5000;  // repeat every 40 sec.  
            
           Runtime.getRuntime().addShutdownHook(new Thread() { 
                //Esse método será chamado quando a aplicação for encerrada.  
                @Override
                public void run() { 
                try {
                    String user;
                    user = System.getProperty("user.name");
                    update(user);
                } catch (IOException ex) {
                    Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                }
                } 
            });
            
            Timer timer = new Timer();   
            timer.scheduleAtFixedRate(new TimerTask() {   
                public void run() { 
                    try {
                        String user;
                        user = System.getProperty("user.name");
                        verifica_user(user);
                    } catch (IOException ex) {
                        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                    }
            }   
            }, delay, period); 
          
          
    }
}
L

Pessoal estou falando desta atividade... que não está funcionando na minha aplicação...
Vejam um exemplo:

http://www.javawiki.com.br/example.action?id=125921

conteúdo do link abaixo:

/*Este exemplo mostra como executar uma última operação antes da aplicação java ser  
totalmente  
encerrada. Uma aplicação é encerrada quando a maquina virtual java é finalizada por  
System.exit(), uma  
control-c é fornecido pelo usuário ou quando o sistema operacional entra em desligamento.*/  
 
  
public class ShutdownHook { 
  public static void main(String[] args) throws Exception { 
    int a = 2; 
    int b = 2; 
    Runtime.getRuntime().addShutdownHook(new Thread() { 
    //Esse método será chamado quando a aplicação for encerrada.  
      public void run() { 
        System.out.println("Terminou"); 
      } 
    }); 
    System.out.println(a + b); // imprimirá 4  
  } 
}
KWill

Lucas Costa:
Uma pergunta…

Esta função continua quando o Sistema Operacional entra em desligamento ou logoff???
atexit()

Quando por algum motivo a função falhar, no caso do S.O entrae em desligamento, esta função me retorna um valor que o java trate e perceba que deve chamar outra função, no caso, UPDATE no banco de dados.

Correto???
OU quando entra em modo de desligamento ou logoff, mata o processo e não dá tempo de nem tratar o retorno???

Obrigado!!!

Se o atexit() da biblioteca padrão do C funciona quando o SO vai desligar ou em logoff eu não sei, mas para mim tu poderia testar aí e comentar para a gente se funciona, já que eu já te forneci a faca e o queijo.

Inté.

L

Alguém expert em Java pode analisar meu código por favor…

To acreditando que devo estar errando na maneira de colocar o método addShutdownHook
Alguém por favor comente-o.

Obrigado! :smiley:

Criado 13 de outubro de 2008
Ultima resposta 20 de out. de 2008
Respostas 12
Participantes 4