Desenvolvimento - Java

Detectando tempo ocioso

Este artigo se propõe a demonstrar uma maneira de detectar inatividade de um sistema feito em AWT ou Swing.

por Hamilton Lima



Imagine que uma aplicação Swing precisa em algum momento verificar se exitem novas versões de arquivos disponíveis na rede, realizar alguma verificação de segurança, ou ainda fazer alguma coisa que provavelmente irá consumir um bom percentual de processamento da máquina local, bom seria se esta tarefas fossem realizadas quando o sistema estivesse sem uso por um determinado tempo.

Imagine também que desejamos que se o usuário deixar o sistema aberto por mais de N minutos, queremos bloquear o usuário, ou sair do sistema, ou ainda registrar esta inatividade em algum tipo de log, seja no banco de dados ou em outra estrutura de armazenamento.

Este artigo se propõe a demonstrar uma maneira de detectar inatividade de um sistema feito em AWT ou Swing.

A idéia

Na verdade a idéia é bem simples, usar a arquitetura de delegação de tratamento de eventos, e criar uma super classe para todas as janelas, que ao ser exibida garanta que sejam atribuidos tratadores de evento adicionais para o controle do tempo, e a criação de uma classe que seja executada em um thread separado, para verificar os eventos relacionados ao tempo.

A implementação

o diagrama de classes abaixo descreve as classes envolvidas


app

principal para execução do teste

Timer

implementação básica do controle do tempo

InactivityMonitor

filha de Timer que realiza alguma tarefa após determinado tempo de inatividade

ShowInactivity

filha de Timer que exeibe em um label o número de segundos de inatividade

SuperFrame

Deve ser superclasse de todas as janelas do sistema, executa método do InactivityListener para adicionar os listeners adicionais

MainFrame

Tela principal do exemplo

FrameTest

Tela de exemplo com diversos componentes



Observe a classe SuperFrame abaixo

package com.athanazio.timeout;
import
javax.swing.JFrame;

public class
SuperFrame extends JFrame {
private boolean init = false;
public void show(){
super.show();
if (!init) {
InactivityListener.addListener(this);
}
}
}

Quando chama o método addListener( ) da classe InactivityListener, faz com que a janela e todos os componentes que estejam incluidos nela, recebam tratadores de eventos adicionais para os eventos de Mouse e teclado, e a janela recebe tratadores de eventos para eventos de janela.

O cõdigo do método addListener( )

public static void addListener(Window main) {
main.addWindowStateListener(InactivityListener.getInstance());
Stack stack = new Stack();
stack.push(main);

while (!stack.empty()) {

Container container = (Container) stack.pop();
container.addKeyListener(InactivityListener.getInstance());
container.addMouseListener(InactivityListener.getInstance());

Component[] components = container.getComponents();

for (int i = 0; i < components.length; i++) {
if (components[i] instanceof Container) {
stack.push(components[i]);
} else {
components[i].addKeyListener(InactivityListener.getInstance());
components[i].addMouseListener(InactivityListener.getInstance());
}
}
}
}


Este método adiciona uma instância da prória classe como tratador dos eventos:

components[i].addKeyListener(InactivityListener.getInstance());
components[i].addMouseListener(InactivityListener.getInstance());

de mudança de estado de janela (WindowStateListener), de mouse (MouseListener) e de teclado (KeyListener), pois a classe InactivityListener implementa os métodos necessários ao tratamento destes diversos métodos, e todos eles, na verdade marcam o momento em que houve algum destes eventos, mantendo assim o momento em que ocorreu algum evento.

private void setLastAction() {
lastAction = System.currentTimeMillis();
}

Para garantir que todos os subcomponentes sejam alcançados, o método percorre a lista de componentes a partir da janela verificando quais são da classe Container (que podem possuir subcomponentes) e adiciona os mesmos em uma pilha para que sejam verificados, e todo subcomponente encontrado recebe um instância da classe como tratador de eventos.

Desta forma armazenamos o ultimo instante quando algum evento ocorreu, o próximo passo é verificar quando o tempo decorrido desde o último evento chega a alguma condição pré estabelecida, para tal criamos filhos da classe Timer, que é uma classe abstrata que executa o conteúdo do método doIt( ) a cada N segundos.

As classes filhas de Timer devem implementar este método e chamar em seus contrutores o construtor da classe Timer indicando de quantos em quantos segundos a execução deve ocorrer, veja por exemplo a classe InactivityMonitor que verifica se o tempo inativo passou de 15 segundos

package com.athanazio.timeout;
import javax.swing.JOptionPane;

public class InactivityMonitor extends Timer {

public InactivityMonitor() {
super(2);
}

public void doIt() {
if (InactivityListener.getInstance().getInactivitySeconds() > 15) {
JOptionPane.showMessageDialog(
null,
"15 segundos fazendo nada ...",
"monitor de ociosidade",
JOptionPane.WARNING_MESSAGE);
}
}
}
}

Simplemente a cada 2 segundos verifica se o tempo já chegou a 15 segundos de inatividade, e caso tenha chegado, exibe mansagem, podendo é clarro realizar qualquer tarefa necessária.

A classe principal só faz exibir as duas janelas, uma para exibir o tempo inativo, e outra de teste, e disparar os processamentos paralelos para verificação de tempo. Para testar execute a aplicação e realize algum evento na segunda janela e o tempo de inatividade na primeira será zerado.

baixe aqui os fontes e o arquivo do diagrama (feito no jude)

Aproveite
Hamilton Lima (athanazio)
Dezembro 2003

Hamilton Lima

Hamilton Lima - Athanazio, além de professor de Tecnologias, é estudioso da Bíblia Sagrada, Teólogo formado desde 1996.