JobMonitor: mudanças entre as edições
(24 revisões intermediárias pelo mesmo usuário não estão sendo mostradas) | |||
Linha 1: | Linha 1: | ||
O JobMonitor é o serviço do BISFW que permite que tarefas sejam executar em uma Thread paralela para que a thread principal não fique bloqueada esperando. Esse recurso é extremamente útil para tarefas demoradas, uma vez que permite retornar status sobre a execução da tarefa enquanto ela acontece, mesmo que seja pela interface WEB. | O JobMonitor é o serviço do BISFW que permite que tarefas sejam executar em uma Thread paralela para que a thread principal não fique bloqueada esperando. Esse recurso é extremamente útil para tarefas demoradas, uma vez que permite retornar status sobre a execução da tarefa enquanto ela acontece, mesmo que seja pela interface WEB. | ||
= Criando o Job | = Criando o Job = | ||
Para colocar uma tarefa em background basta implementar a classe Job e iniciar a execução dentro do método '''runJob()'''. | Para colocar uma tarefa em background basta implementar a classe Job e iniciar a execução dentro do método '''runJob()'''. | ||
Linha 10: | Linha 10: | ||
public Object runJob(Job job, JobStatus jobStatus) throws Throwable { | public Object runJob(Job job, JobStatus jobStatus) throws Throwable { | ||
//Código para execução da tarefa... | //Código para execução da tarefa... | ||
//E atualizações do andamento da tarefa no objeto jobStatus | |||
// | |||
} | } | ||
}; | }; | ||
Linha 21: | Linha 17: | ||
Quando a tarefa iniciar, o método '''runJob()''' receberá dois parâmetros: | |||
* '''job''' - referência da própria instância do Job criado. | * '''job''' - referência da própria instância do Job criado. | ||
* '''jobStatus''' - Instância do objeto de status do job. Dentro desse objeto a tarefa deve atualizar as propriedades como "mensagens", percentual de tarefa realizada, etc.. | * '''jobStatus''' - Instância do objeto de status do job. Dentro desse objeto a tarefa deve atualizar as propriedades como "mensagens", percentual de tarefa realizada, etc.. | ||
Linha 35: | Linha 30: | ||
}} | }} | ||
== Session Context do Job == | |||
Quando o job é utilizado para executar uma tarefa no CRUD, é preciso se atentar que depois que o Job é disparado, a Thread principal do usuário é finalizada retornando o JobStatus para o usuário. Uma vez que a chamada é retornada, o contexto de sessão do EJB é finalizado, e o Job não conseguirá mais acessar o banco de dados ou outros recursos que dependam do contexto. | |||
Para resolver isso é preciso que o Job faça a chamada através da fachada para que um novo contexto seja criado para ele. | |||
Nestes casos a recomendação é que dentro da própria fachada ('''BISFacade''') seja criado o Job que chama o método correspondente da fachada privada ('''BISFacadePrivate'''). Para uma chamada simples não vale a pena tumultuar os arquivos do CRUD com um método só para isso. | |||
Siga o exemplo que foi utilizado para o método de geração do SPED: | |||
{{java|Exemplo Tarefa em Execução em Background|<syntaxhighlight lang="java"> | |||
@Override | |||
@Security(action = SecurityAction.HASKEY, key = BISSystem.SECKEY_COMPANY_FISCAL_SPED_GENERATE) | |||
public JobStatus generateSPED(String uuid, SPEDOptionBeanVO optBean) throws BISException { | |||
Job job = new Job("Generate SPED") { | |||
@Override | |||
public Object runJob(Job job, JobStatus jStatus) throws Throwable { | |||
return KernelCrud.getBISFacadePrivate().generateSPEDBackground(uuid, optBean, jStatus.getJobUUID()); | |||
} | |||
}; | |||
job.start(); | |||
return job.getJobStatus(); | |||
} | |||
</syntaxhighlight>}} | |||
=== Padrão de Nomenclatura === | |||
Os métodos que trabalham em background (orientados por um Job), devem ter o sufixo "Background" no nome. Facilitando assim a pesquisa e entendimento sobre as funções do método. Além de receberem o jobUUID como parâmetro. | |||
{{stop|Não Passar o JobMonitor pela Fachada|Ao passar o JobMonitor pela fachada o objeto é clonado e a referência perdida. Por isso passe sempre o jobUUID e recupere o JobStatus dentro do método.}} | |||
== Interrompendo o Job == | == Interrompendo o Job == | ||
O cancelamento do Job em segundo plano pode ser solicitado pelo usuário. Para que isso ocorra a interface deve chamar o método '''interrupt()''' | O cancelamento do Job em segundo plano pode ser solicitado pelo usuário. Para que isso ocorra a interface deve chamar o método '''JobStatus.interrupt()'''. Ao chamar esse método o JobStatus ficará marcado que a tarefa obteve a solicitação de cancelamento de sua execução. | ||
Linha 50: | Linha 76: | ||
= JobMonitor = | = JobMonitor = | ||
O JobMonitor é uma classe estática que controla e mantém todos os Jobs do Sistema. Ao criar uma instância de '''Job''' ele automaticamente se registra no JobMonitor. Mesmo antes de iniciar a tarefa o JobMonitor já tem a referência da classe e monitora seu status. | O JobMonitor é uma classe estática, como um Singleton, que controla e mantém todos os Jobs do Sistema. Ao criar uma instância de '''Job''' ele automaticamente se registra no '''JobMonitor'''. Mesmo antes de iniciar a tarefa o '''JobMonitor''' já tem a referência da classe e monitora seu status. | ||
Quando o '''Job''' é criado, ela cria também uma instância final da '''JobStatus''' que tem um identificador único (UUID). Este UUID é o identificador do '''Job''' e pode ser utilizado para realizar operações no '''JobMonitor'''. | |||
{{nota|JobMonitor por JVM|Uma vez que '''JobMonitor''' é uma classe estática, ela é única por ClassLoader e só será possível encontrar referência do '''Job''' se estivermos procurando dentro da mesma ClassLoader em que o '''Job''' foi criado. | |||
{{java|Exemplo | Em casos em que o Job é criado em outra ClassLoader, será preciso implementar métodos de acesso ao '''JobMonitor''' remoto para realizar as operações desejadas.}} | ||
Ao criar um Job, devemos nos preocupar em salvar o JobStatus, ou ao menos o UUID para que seja possível referenciar a tarefa em background a partir do '''JobMonitor'''. | |||
{{java|Exemplo de Criação do Job e Obtenção do Status a partir do '''JobMonitor'''|<syntaxhighlight lang="java"> | |||
//Implementação do Job | //Implementação do Job | ||
Job job = new Job("Título da Tarefa") { ... }; | Job job = new Job("Título da Tarefa") { ... }; | ||
Linha 68: | Linha 103: | ||
</syntaxhighlight>}} | </syntaxhighlight>}} | ||
{{ | Note que no exemplo acima o '''.start()''' do job pode ser dado a qualquer momento. O job não precisa estar em execução para que todo o restante funcione. | ||
{{stop|JobStatus Referência em Memória|Note que o JobStatus é um objeto final dentro do Job, e que não é criada uma nova instância para cada alteração. Isto quer dizer que o objeto pode ser manipulado dentro do Job e as alterações serem lidas concorrentemente em outra Thread. De certa forma o JobStatus funciona apenas como "ponteiros de memória" indicando onde as informações estão sendo escritas. | |||
Um ponto a se observar aqui é que, embora o JobStatus possa ser recuperado e ser único na memória quando estamos dentro da mesma JVM, é preciso observar que ao devolver o JobStatus através de uma fachada utilize o [[SessionManager#SMInterceptor]] ele será clonado justamente para prevenir esse tipo de apontamento e modificação "no objeto original". Assim, não mais representará o mesmo objeto na memória. Ao retornar o objeto pela fachada, o outro lado deve utilizar o jobUUID para recuperar o objeto correto do JobMonitor para receber atualizações diretamente.}} | |||
= Ciclo de Vida do Job = | |||
Abaixo os passos do ciclo de vida do Job para exemplificar como tudo funciona: | |||
# Criamos a tarefa instanciando a classe Job: new Job(...); | |||
## Já no construtor, '''Job''' se registra no '''JobMonitor''' e ganha seu '''jobUUID'''. | |||
## Com o '''jobUUID''' recebido, '''Job''' cria o objeto final '''JobStatus'''. | |||
# A partir da instância de '''Job''' que fora criado, podemos obter o '''JobStatus''' e o '''jobUUID''' através dos respectivos métodos: '''job.getJobStatus()''' e '''job.getJobStatus().getJobUUID()''' | |||
# Iniciamos a tarefa disparando a Thread do job com '''job.start()'''; | |||
## Neste momento a Thread do Job começa a ser executada. | |||
# O método do usuário deve retornar o '''JobStatus''' ou o '''jobUUID''' para que o Caller consiga acompanhar a tarefa e solicitar seu cancelamento. | |||
== Dentro do JobMonitor == | |||
Dentro do JobMonitor, quando a tarefa se registrou, o JobMonitor cria um Timer de segurança que verifica se o Job não foi esquecido dentro do JobMonitor e não teve seu .start() chamado. Passado o tempo do timer (no momento 1h) se o Job não estiver sendo executado, forçaremos a limpeza do Job como um vazamento de recursos. | |||
Caso esteja rodando, faremos um Log de que a tarefa está demorando demais, '''tarefas do JobMonitor não devem ser demoradas desta forma, tarefas de manutenção devem ser executadas pelo Scheduler. JobMonitor se destina mais para tarefas do usuário que possam demorar um pouco mais que o desejado para deixar a tela travada, não para execuções de longo prazo desse jeito'''. | |||
== Dentro do Job == | |||
* Quando instanciado o Job tem o seu atributo '''step''' definido como '''IDLE''', indicando que a tarefa está inerte por ainda não ter sido iniciada. Ao chamar o '''.start()'''; | |||
* Antes da chamada do método '''runJob(...)''' o '''step''' é atualizado para '''RUNNING''', e deve ficar neste passo enquanto a implementação do método é executada. | |||
* Caso este método retorno normalmente, o objeto retornado é salvo dentro do '''JobStatus''', no atributo '''jobReturn''', e o '''step''' é alterado para '''FINISHED''' indicando que a tarefa terminou sua execução com sucesso. | |||
* Caso o método lance qualquer Exception o '''step''' é alterado para '''EXCEPTION''' e a Exception lançada é salva no atributo '''exception''' do '''JobStatus'''. | |||
Ao finalizar o Job, independente do sucesso, o Job notifica o '''JobMonitor''' de que o Job terminou. Ao realizar essa tarefa, o JobMonitor cancela o Timer que ele criou no registro (explicado na sessão anterior). No entanto ele cria um novo timer (atualmente de 10min) que eliminará por completo qualquer outra referência do '''Job''' de dentro do '''JobMonitor''' para garantir que todos os recursos serão liberados e estarão disponíveis para o GC. | |||
O '''JobMonitor''' não excluí imediatamente os recursos porque essa notificação ocorre no momento em que a tarefa se extinguiu, e outras Thread ainda podem precisar encontrar o '''JobStatus''' tanto para recuperar o retorno do método (ou a Exception) para notificar o usuário. | |||
Uma vez que a Thread cliente recupera o "último" JobStatus, obtendo seu retorno, é possível (e recomendado) acelerar a liberação dos recursos chamando o método '''JobMonitor.cleanJob(jobUUID)'''. Assim, tanto os recursos utilizados pelo Job, quando o Timer criado serão liberados. E a Thread cliente fará o que for necessário, salvando ou não, o JobStatus e seus retornos. | |||
= JobChecker = | = JobChecker = | ||
JobChecker é a implementação de uma Thread que verifica periodicamente a tarefa | JobChecker é a implementação de uma Thread que verifica periodicamente a tarefa em segundo plano e dispara eventos de alteração no formato de listeners. | ||
Para criar o JobChecker basta passar o | Para criar o JobChecker basta passar o '''jobUUID''' do job na sua construção: | ||
{{java|JobChecker verificando o progresso da tarefa|<syntaxhighlight lang="java"> | {{java|JobChecker verificando o progresso da tarefa|<syntaxhighlight lang="java"> | ||
JobChecker jobChecker = new JobChecker(uuid | JobChecker jobChecker = new JobChecker(uuid); | ||
checker.addListener(new JobCheckerListener() { | |||
@Override | @Override | ||
public void updateStatus(JobStatus jobStatus) { | public void updateStatus(JobStatus jobStatus, boolean lastCall) { | ||
// Código para lêr o conteúdo do JobStatus e atualizar qualquer sistema | // Código para lêr o conteúdo do JobStatus e atualizar qualquer sistema | ||
// Abaixo um código exemplo para atualizar o conteúdo da | // Abaixo um código exemplo para atualizar o conteúdo da interface do Vaadin. | ||
// É necessário obter a UI do usuário e criar uma classe de acesso primeiro | |||
ui.access(new Runnable() { | ui.access(new Runnable() { | ||
@Override | @Override | ||
Linha 98: | Linha 169: | ||
</syntaxhighlight>}} | </syntaxhighlight>}} | ||
Isso | No método do listener são recebidos 2 parâmetros: | ||
* '''JobStatus''' - o objeto JobStatus da tarefa com as últimas informações disponíveis. | |||
* '''lastCall''' - um boleano indicando se esta é a última chamada do listener. Esse atributo só é true quando o step do Job já for '''FINISHED''' ou '''EXCEPTION'''. E permite que algumas ações sejam realizadas só quando a tarefa terminou. | |||
Dentro de um mesmo JobChecker é possível colocar vários listeners que manipulem e executem partes diferentes do sistema. Como listeners para atualizar a tela e o progresso da tarefa, bem como o listener específico para disparar novas tarefas em cadeia. | |||
{{nota|LastCall e Step do Job|Note que quando LastCall for true será de fato a última fez que o listener será chamado. No entanto, mesmo com lastCall for false a tarefa pode estar já nos passos '''FINISHED''' ou '''EXCEPTION'''. Isso ocorre porque, caso o JobStatus utilizado seja o mesmo objeto de memória em que a tarefa esteja atualizando, entre a definição do lastCall e a chamada do listener o step pode ter sido atualizado. Mesmo asim, o listener será chamado novamente passando o lastCall como true. | |||
Em resumo: | |||
* '''Quando lastCall for true:''' | |||
** O passo da tarefa será '''FINISHED''' ou '''EXCEPTION''' | |||
** Será a última chamada do listener | |||
** A tarefa já terminou sua execução. | |||
* '''Quando lastCall for false:''' | |||
** O passo da tarefa pode estar em qualquer estágio, inclusive terminado. | |||
** Teremos pelo menos uma chamada do listener. Mesmo que a tarefa já tenha terminado. | |||
}} | |||
{{nota|JobChecker é sempre chamado!|Note que dependendo da velocidade em que a tarefa é terminada, o JobChecker pode ser iniciado depois que a tarefa já esteja no passo '''FINISHED''' ou '''EXCEPTION'''. Ainda assim é garantido que o método '''updateStatus(...)''' será chamado ao menos uma única vez. | |||
Quando o '''updateStatus''' for chamado e o passo do '''JobStatus''' recebido for '''FINISHED''' ou '''EXCEPTION''' esta será a última vez (ou única) que será chamado. Quando detectado que chegamos em um desses dois passos o '''JobChecker''' se encerra.}} | |||
== JobStatusSupplier == | |||
Para suprir a necessidade de encontrar o Job em um JobMonitor de outro ClassLoader, é possível criar o '''JobChecker''' passando uma implementação do '''JobStatusSupplier''' que seja responsável por realizar a ponte até o JobMonitor correto. | |||
O FW fornece uma implementação padrão '''JobStatusSupplierDefault''' que faz a ponte para o '''JobMonitor''' dentro do mesmo ClassLoader. Essa implementação também é utilizada internamente quando o JobChecker é instanciando passando ''null'' no argumento do Supplier. | |||
Já no BISVaadin, para que o JobChecker possa acessar as tarefas criadas no Core, é oferecida a implementação '''JobStatusSupplierBISImpl''' que faz a chamada através da fachada e questiona o '''JobMonitor''' do Core. | |||
= JobMonitorComponent = | |||
O JobMonitorComponent é um componente do pacote BISFW.Vaadin, que permite que uma tarefa seja monitorada e exibida em um pequeno quadro com uma barra de progresso e alguns botões para manipulação. | |||
O componente encapsula dentro dele um JobChecker que faz o trabalho da verificação da mudança de estados do '''Job'''. Assim o JobMonitorComponente por ser instanciado basicamente de 2 maneiras: | |||
* Passando o jobUUID e um '''JobStatusSupplier''' (opcional) - neste modo o componente instancia e configura seu próprio '''JobChecker''' para verificar a tarefa. | |||
* Passando um '''JobChecker''' já criado - Neste modo o componente aproveita o '''JobChecker''' já criado e só coloca seu listener para obter os eventos e atualizar seus componentes. | |||
Este componente além de atualizar a barra de progresso e mensagem da tarefa, ainda é capaz de exibir 3 botões de forma automática: | |||
* '''Solicitação de Cancelamento''' - Enquanto o '''Job''' estiver em execução o botão é exibido e executa a chamada do '''JobStatus.interrupt()''', solicitando o cancelamento da tarefa; | |||
* '''Visualização do Relatório''' - Quando o JobStatus tiver um relatório (.hasReport() for true) o botão é exibido e ao clicar exibe o conteúdo do relatório da tarefa em um Popup que também se atualiza automaticamente. | |||
* '''Exception''' - Caso a tarefa tenha terminado em exception, o botão é exibido e quando clicado a exception é exibida para o usuário. | |||
Além desses, outros botões podem ser adicionados ao componente para personalizar sua aparência através do método | |||
<pre>.addButton()</pre> |
Edição atual tal como às 15h20min de 12 de fevereiro de 2022
O JobMonitor é o serviço do BISFW que permite que tarefas sejam executar em uma Thread paralela para que a thread principal não fique bloqueada esperando. Esse recurso é extremamente útil para tarefas demoradas, uma vez que permite retornar status sobre a execução da tarefa enquanto ela acontece, mesmo que seja pela interface WEB.
Criando o Job
Para colocar uma tarefa em background basta implementar a classe Job e iniciar a execução dentro do método runJob().
![]() |
Exemplo Tarefa em Execução em Background
Job job = new Job("Título da Tarefa") { //O título da tarefa é colocado no título da thread, o que ajuda no DEV.
@Override
public Object runJob(Job job, JobStatus jobStatus) throws Throwable {
//Código para execução da tarefa...
//E atualizações do andamento da tarefa no objeto jobStatus
}
};
job.start(); //inicia a tarefa
|
Quando a tarefa iniciar, o método runJob() receberá dois parâmetros:
- job - referência da própria instância do Job criado.
- jobStatus - Instância do objeto de status do job. Dentro desse objeto a tarefa deve atualizar as propriedades como "mensagens", percentual de tarefa realizada, etc..
![]() |
|
Session Context do Job
Quando o job é utilizado para executar uma tarefa no CRUD, é preciso se atentar que depois que o Job é disparado, a Thread principal do usuário é finalizada retornando o JobStatus para o usuário. Uma vez que a chamada é retornada, o contexto de sessão do EJB é finalizado, e o Job não conseguirá mais acessar o banco de dados ou outros recursos que dependam do contexto.
Para resolver isso é preciso que o Job faça a chamada através da fachada para que um novo contexto seja criado para ele.
Nestes casos a recomendação é que dentro da própria fachada (BISFacade) seja criado o Job que chama o método correspondente da fachada privada (BISFacadePrivate). Para uma chamada simples não vale a pena tumultuar os arquivos do CRUD com um método só para isso.
Siga o exemplo que foi utilizado para o método de geração do SPED:
![]() |
Exemplo Tarefa em Execução em Background
@Override
@Security(action = SecurityAction.HASKEY, key = BISSystem.SECKEY_COMPANY_FISCAL_SPED_GENERATE)
public JobStatus generateSPED(String uuid, SPEDOptionBeanVO optBean) throws BISException {
Job job = new Job("Generate SPED") {
@Override
public Object runJob(Job job, JobStatus jStatus) throws Throwable {
return KernelCrud.getBISFacadePrivate().generateSPEDBackground(uuid, optBean, jStatus.getJobUUID());
}
};
job.start();
return job.getJobStatus();
}
|
Padrão de Nomenclatura
Os métodos que trabalham em background (orientados por um Job), devem ter o sufixo "Background" no nome. Facilitando assim a pesquisa e entendimento sobre as funções do método. Além de receberem o jobUUID como parâmetro.
![]() |
|
Interrompendo o Job
O cancelamento do Job em segundo plano pode ser solicitado pelo usuário. Para que isso ocorra a interface deve chamar o método JobStatus.interrupt(). Ao chamar esse método o JobStatus ficará marcado que a tarefa obteve a solicitação de cancelamento de sua execução.
Note que depende do Job Implementado verificar se essa solicitação foi realizada e parar a execução da tarefa tomando as providências necessárias (como dar rollback em tarefas pela metade ou concluí-las e cancelar depois, etc.). Há duas maneiras do Job verificar esse cancelamento:
- Verificação Manual - Na verificação manual o desenvolvedor pode utilizar o método getInterruptResquested() e obter o valor do atributo. Caso true, indica que a solicitação de cancelamento foi realizada e deste ponto em diante direcionar o método para os procedimentos de cancelamento, e lançar a exception.
- Recomendado lançar a exception:
throw new BISValidationException("FW_ERROR_000004");
Este Código de exception é utilizado para identificar que a operação foi cancelada pelo usuário.
- Recomendado lançar a exception:
- Verificação Automática - Neste modo basta o desenvolvedor incluir a chamada do método checkInterrupt() do JonMonitor. Este método verifica o status do atributo e se tiver em true já lança a exception como citada acima (Validation com o código 'FW_ERROR_000004'). Só é preciso tomar cuidado para que o próprio código do Job não trate a exception e deixe que ela vaze para fora do método do Job.
- Note que neste método a exception é lançada diretamente, assim, o método que verifica o status deve ser bem posicionado. Ou realizar as tratativas no catch/finally.
JobMonitor
O JobMonitor é uma classe estática, como um Singleton, que controla e mantém todos os Jobs do Sistema. Ao criar uma instância de Job ele automaticamente se registra no JobMonitor. Mesmo antes de iniciar a tarefa o JobMonitor já tem a referência da classe e monitora seu status.
Quando o Job é criado, ela cria também uma instância final da JobStatus que tem um identificador único (UUID). Este UUID é o identificador do Job e pode ser utilizado para realizar operações no JobMonitor.
![]() |
|
Ao criar um Job, devemos nos preocupar em salvar o JobStatus, ou ao menos o UUID para que seja possível referenciar a tarefa em background a partir do JobMonitor.
![]() |
Exemplo de Criação do Job e Obtenção do Status a partir do JobMonitor
//Implementação do Job
Job job = new Job("Título da Tarefa") { ... };
//Recuepra o objeto que mantém o status de progresso diretamente da tarefa
JobStatus status = job.getStatus();
//Obtemos e salvamos para referência futura o UUID do Job
String uuid = status.getUuid();
//Obtendo o JobStatus a partir do JobMonitor com o UUID do job.
JobStatus status2 = JobMonitor.getJobStatus(uuid);
|
Note que no exemplo acima o .start() do job pode ser dado a qualquer momento. O job não precisa estar em execução para que todo o restante funcione.
![]() |
|
Ciclo de Vida do Job
Abaixo os passos do ciclo de vida do Job para exemplificar como tudo funciona:
- Criamos a tarefa instanciando a classe Job: new Job(...);
- Já no construtor, Job se registra no JobMonitor e ganha seu jobUUID.
- Com o jobUUID recebido, Job cria o objeto final JobStatus.
- A partir da instância de Job que fora criado, podemos obter o JobStatus e o jobUUID através dos respectivos métodos: job.getJobStatus() e job.getJobStatus().getJobUUID()
- Iniciamos a tarefa disparando a Thread do job com job.start();
- Neste momento a Thread do Job começa a ser executada.
- O método do usuário deve retornar o JobStatus ou o jobUUID para que o Caller consiga acompanhar a tarefa e solicitar seu cancelamento.
Dentro do JobMonitor
Dentro do JobMonitor, quando a tarefa se registrou, o JobMonitor cria um Timer de segurança que verifica se o Job não foi esquecido dentro do JobMonitor e não teve seu .start() chamado. Passado o tempo do timer (no momento 1h) se o Job não estiver sendo executado, forçaremos a limpeza do Job como um vazamento de recursos.
Caso esteja rodando, faremos um Log de que a tarefa está demorando demais, tarefas do JobMonitor não devem ser demoradas desta forma, tarefas de manutenção devem ser executadas pelo Scheduler. JobMonitor se destina mais para tarefas do usuário que possam demorar um pouco mais que o desejado para deixar a tela travada, não para execuções de longo prazo desse jeito.
Dentro do Job
- Quando instanciado o Job tem o seu atributo step definido como IDLE, indicando que a tarefa está inerte por ainda não ter sido iniciada. Ao chamar o .start();
- Antes da chamada do método runJob(...) o step é atualizado para RUNNING, e deve ficar neste passo enquanto a implementação do método é executada.
- Caso este método retorno normalmente, o objeto retornado é salvo dentro do JobStatus, no atributo jobReturn, e o step é alterado para FINISHED indicando que a tarefa terminou sua execução com sucesso.
- Caso o método lance qualquer Exception o step é alterado para EXCEPTION e a Exception lançada é salva no atributo exception do JobStatus.
Ao finalizar o Job, independente do sucesso, o Job notifica o JobMonitor de que o Job terminou. Ao realizar essa tarefa, o JobMonitor cancela o Timer que ele criou no registro (explicado na sessão anterior). No entanto ele cria um novo timer (atualmente de 10min) que eliminará por completo qualquer outra referência do Job de dentro do JobMonitor para garantir que todos os recursos serão liberados e estarão disponíveis para o GC.
O JobMonitor não excluí imediatamente os recursos porque essa notificação ocorre no momento em que a tarefa se extinguiu, e outras Thread ainda podem precisar encontrar o JobStatus tanto para recuperar o retorno do método (ou a Exception) para notificar o usuário.
Uma vez que a Thread cliente recupera o "último" JobStatus, obtendo seu retorno, é possível (e recomendado) acelerar a liberação dos recursos chamando o método JobMonitor.cleanJob(jobUUID). Assim, tanto os recursos utilizados pelo Job, quando o Timer criado serão liberados. E a Thread cliente fará o que for necessário, salvando ou não, o JobStatus e seus retornos.
JobChecker
JobChecker é a implementação de uma Thread que verifica periodicamente a tarefa em segundo plano e dispara eventos de alteração no formato de listeners.
Para criar o JobChecker basta passar o jobUUID do job na sua construção:
![]() |
JobChecker verificando o progresso da tarefa
JobChecker jobChecker = new JobChecker(uuid);
checker.addListener(new JobCheckerListener() {
@Override
public void updateStatus(JobStatus jobStatus, boolean lastCall) {
// Código para lêr o conteúdo do JobStatus e atualizar qualquer sistema
// Abaixo um código exemplo para atualizar o conteúdo da interface do Vaadin.
// É necessário obter a UI do usuário e criar uma classe de acesso primeiro
ui.access(new Runnable() {
@Override
public void run() {
// Código do Vaadin para atualizar a UI do usuário
}
});
}
};
|
No método do listener são recebidos 2 parâmetros:
- JobStatus - o objeto JobStatus da tarefa com as últimas informações disponíveis.
- lastCall - um boleano indicando se esta é a última chamada do listener. Esse atributo só é true quando o step do Job já for FINISHED ou EXCEPTION. E permite que algumas ações sejam realizadas só quando a tarefa terminou.
Dentro de um mesmo JobChecker é possível colocar vários listeners que manipulem e executem partes diferentes do sistema. Como listeners para atualizar a tela e o progresso da tarefa, bem como o listener específico para disparar novas tarefas em cadeia.
![]() |
|
![]() |
|
JobStatusSupplier
Para suprir a necessidade de encontrar o Job em um JobMonitor de outro ClassLoader, é possível criar o JobChecker passando uma implementação do JobStatusSupplier que seja responsável por realizar a ponte até o JobMonitor correto.
O FW fornece uma implementação padrão JobStatusSupplierDefault que faz a ponte para o JobMonitor dentro do mesmo ClassLoader. Essa implementação também é utilizada internamente quando o JobChecker é instanciando passando null no argumento do Supplier.
Já no BISVaadin, para que o JobChecker possa acessar as tarefas criadas no Core, é oferecida a implementação JobStatusSupplierBISImpl que faz a chamada através da fachada e questiona o JobMonitor do Core.
JobMonitorComponent
O JobMonitorComponent é um componente do pacote BISFW.Vaadin, que permite que uma tarefa seja monitorada e exibida em um pequeno quadro com uma barra de progresso e alguns botões para manipulação.
O componente encapsula dentro dele um JobChecker que faz o trabalho da verificação da mudança de estados do Job. Assim o JobMonitorComponente por ser instanciado basicamente de 2 maneiras:
- Passando o jobUUID e um JobStatusSupplier (opcional) - neste modo o componente instancia e configura seu próprio JobChecker para verificar a tarefa.
- Passando um JobChecker já criado - Neste modo o componente aproveita o JobChecker já criado e só coloca seu listener para obter os eventos e atualizar seus componentes.
Este componente além de atualizar a barra de progresso e mensagem da tarefa, ainda é capaz de exibir 3 botões de forma automática:
- Solicitação de Cancelamento - Enquanto o Job estiver em execução o botão é exibido e executa a chamada do JobStatus.interrupt(), solicitando o cancelamento da tarefa;
- Visualização do Relatório - Quando o JobStatus tiver um relatório (.hasReport() for true) o botão é exibido e ao clicar exibe o conteúdo do relatório da tarefa em um Popup que também se atualiza automaticamente.
- Exception - Caso a tarefa tenha terminado em exception, o botão é exibido e quando clicado a exception é exibida para o usuário.
Além desses, outros botões podem ser adicionados ao componente para personalizar sua aparência através do método
.addButton()