Job Quartz e FacesContex

6 respostas
W

Olá pessoal,

tenho uma aplicação Jsf que roda uma tarefa do quartz de 3 em 3 minutos beleza. O problema é que agora essa tarefa precisa receber um Facescontext via parametro do seu método execute implementado.
Mais ou menos assim:

public void execute(JobExecutionContext context) throws JobExecutionException {
FacesContext facesContext = (FacesContext) context.get(“facesContext”);

facesContext.addMessage("Já se passaram 3 minutos, ", null);
	}

Esse get(“FacesContext”) tinha que me devolver um objeto FacesContext porque ao fazer o schedule desse job eu fiz desse jeito:

jobTimer3Minuto.getJobDataMap().put(“facesContext”, FacesContext.getCurrentInstance());

Mas não recebe o FacesContext!

Pelo que li, tenho que implementar meu proprio or.quartz.scheduler.jobFactory, alguém já fez isso pra dar uma dica?

Brigadão, Deus abençoe.

6 Respostas

M

você está errando na hora de recuperar o objeto do dataMap.

tente assim:

public void execute(JobExecutionContext context) throws JobExecutionException {
    JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();

    // não consultei a API, mas deve existir o método get.
    FaceContext facesContext = (FacesContext) jobDataMap.get("facesContext"); 
    facesContext.addMessage("Já se passaram 3 minutos, ", null);
}

Só tem um problema, não acho legal você passar um FacesContext para o job. Tente pensar em outro forma de fazer isso. Se for apenas adicionar mensagens de erro, insira em uma lista por referência. ex:

public void execute(JobExecutionContext context) throws JobExecutionException {
    JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();

    // não consultei a API, mas deve existir o método get.
    List<String> mensagens = (List<String>) jobDataMap.get("mensagens"); 
    mensagens.add("Já se passaram 3 minutos");
}

foi apenas uma idéia.

t+

F

O FacesContext exite para uma requisição web.

Se teu job dispara a cada 3 minutos sem requisição, o FacesContext qe ele terá será inválido e sua mensagem não vai aparecer na tela do usuário.

O que você pode fazer é usar um ajax pull da vida e ficar checando no servidor se tem alguma mensagem a ser exibida na tela. Dai o job adicionaria a mensagem em algum lugar fora do faces que você pudesse ler.

W

Obrigado hein…

Segui primeiro sua orientação marcelo e consegui pegar o facesContext, mas acabei com outro problema: Se o job executar imediatamente o faces é recebido e tudo dá certo, mas se for executar daqui a 3 minutos… veja o que acontece:

12:56:50,108 ERROR JobRunShell:212 - Job DEFAULT.JobAvisaUsuarioPerguntaConicao threw an unhandled Exception:

java.lang.IllegalStateException: FacesContext already released

at org.apache.myfaces.context.servlet.ServletFacesContextImpl.addMessage(ServletFacesContextImpl.java:269)

at quartzJobs.JobAvisaUsuarioPerguntaConicao.execute(JobAvisaUsuarioPerguntaConicao.java:32)

at org.quartz.core.JobRunShell.run(JobRunShell.java:203)

at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:520)

12:56:50,108 ERROR ErrorLogger:2015 - Job (DEFAULT.JobAvisaUsuarioPerguntaConicao threw an exception.

org.quartz.SchedulerException: Job threw an unhandled exception. [See nested exception: java.lang.IllegalStateException: FacesContext already released]

at org.quartz.core.JobRunShell.run(JobRunShell.java:214)

at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:520)
  • Nested Exception (Underlying Cause) ---------------
    java.lang.IllegalStateException: FacesContext already released
    at org.apache.myfaces.context.servlet.ServletFacesContextImpl.addMessage(ServletFacesContextImpl.java:269)
    at quartzJobs.JobAvisaUsuarioPerguntaConicao.execute(JobAvisaUsuarioPerguntaConicao.java:32)
    at org.quartz.core.JobRunShell.run(JobRunShell.java:203)
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:520)

Confesso que fiquei um pouco confuso. Parece que o quartz instancia o job passando pra ele o facesContext numa boa, mas quando o job realmente executa o faces que ele tinha recebido nao serve mais!
E o interessante é que se o trigger disparasse o job de 3 em 3 minutos começando imediatamente, a primeira execução não dá essa exeption, o que mostra que o facesContex na primeira execução imediata é util, mas na próxima execuçao já não é mais.

Viu marcelo, sobre o List de mensagens: A idéia de passar o facesContext para o meu job foi a única alternativa que encontrei para enviar a mensagem para o usuário certo da sessão certa. Ou seja, como eu vou dar um aviso (mensagem) para o usuário se eu não colocar na sessão dele? Pelo que eu entendi da sua dica, qdo o job executa ele adiciona uma mensagem no seu DataMap. Bom, até aí até tudo bem, mas eu teria que mostrar pro usuário essa mensagem…como eu faria isso? (me descuple se eu entendi mal).

A sua idéia, Flin, pode ser uma alternativa viável, mas agora tenho que ponderar entre isso (que eu não sei como fazer) ou implementar meu proprio or.quartz.scheduler.jobFactory que é uma outra hipótese.

Qual vc acham que é mais adequada? Será que estou complicando algo que poderia ser simples?

de novo Brigadão!!!

S

wagner,

o FacesContext tem escopo de request, por isso nao faz sentido passa-lo pra esse job.

ao inves de passar o FacesContext, tenta passar direto a HttpSession… nao sei se funciona, mas faz mais sentido

M

como o sérgio já disse, tome cuidado com o uso do FacesContext.
Ele nada mais é do que uma threadLocal camuflada, rs. Quando a requisição acabar, ele não existirá mais.

Passar a sessão seria uma solução mais interessante. Mas ainda acho estranho.

Talvez se você manter uma lista com as mensagens de erro de cada usuário amarrada na sessão e passar esta lista para o job. Cairia na idéia que te falei, você iria inserir as mensagens nessa lista (que é a mesma que está na sessão).

W

Tudo bem pessoal?

Peguei a dica do sérgio e fiz algumas coisas que deram certo, embora não seja exatamente o que eu queria. Explico:

O meu Job recebe beleza o httpSession, beleza.
A questão é: Se fosse o facesContext era fácil fazer a mensagem aparecer ao término do request, porque aí eu fazia alguma coisa do tipo FacesContext.getCurrentInstance().addMessage(null, facesMessage);

Acontece que o que eu tenho é a sessao http e quando meu job roda (depois de 3 minutos) o que eu posso fazer é colocar na sessao a mensagem que eu quero que apareça e assim surge o seguinte problema: A mensagem entra na sessao após os 3 minutos, mas como fazer ela aparecer no MESMO MOMENTO em que ela entra na sessão?

Digo “mesmo momento” porque se eu ficar olhando para a tela 3 minutos, esperando a mensagem aparecer, a gente vai constatar que a tela já havia sido renderizada para o usuario antes da mensagem estar na sessão, de modo que qdo ela entra na sessão (decorridos os 3 minutos) o componente que renderiza a mensagem já havia sido renderizado antes, num momento que não tinha mensagem nenhuma, e por consequentemente não aparece a mensagem.

Então, o problema fica resolvido em partes, porque só depois de outro request é que a mensagem poderá ser vista, ou seja, a pagina é renderizada novamente após a mensagem estar disponível para renderização e aí sim ela aparece.

Não gostaria de dar por resolvido não. Porque apesar de o usuário poder ver a mensagem só depois de decorrido os 3 minutos, isso fica condicionado a pelo menos um request adicional, e eu não posso garantir que meu usuário vai dar mais algum click depois que a mensagem tenha sido adicionada na sessão.

Espero que eu tenha me feito entender!

Mas estou mostrando abaixo o que eu fiz, quem sabe dá uma luz…

public class MyFacesMsgPerguntaCognicao extends FacesMessage implements HttpSessionBindingListener{

public MyFacesMsgPerguntaCognicao(String msg) {
 super(msg);
}

public void valueBound(HttpSessionBindingEvent bindingEvent) {		
	System.out.println("syso=>SOU FacesMsgPerguntaCognicao, acabei de ser adicionada na sessao...");
	
	HttpSession session =  bindingEvent.getSession();
	System.out.println("session:" + session.getAttribute("avisoPergutaCognicao"));
	
}

public void valueUnbound(HttpSessionBindingEvent bindingEvent) {
	logger.debug("SOU FacesMsgPerguntaCognicao, acabei de ser retirada na sessao...");		
}

}

Essa minha mensagem, devido à interface HttpSessionBindingListener fica sabendo qdo o meu Job coloca ela na sessao, de modo que agora eu tenho um método pra fazer alguma coisa no momento que a mensagem é colocada na sesão. A idéia seria fazer ela aparecer utilizando esse método. Como fazer isso? Dá pra fazer um refresh na página via esse método? Acho que pegar o meu managedBean pra setar um atributo pra aparecer via EL é meio inviável e cheira a gambiarra, não sei…

Alguém poderia dizer, faz um
<f:verbatim>
avisoPergutaCognicao
</f:verbatim>
ou algo mais adequado com El expression…

Mas, estou fazendo isso, o que realmente dá pra mensagem aparecer, só que eu tenho que esperar outro request do usuario e só entao isso acontece. TALVES SE COM ESSE METODO EU PUDESSE DAR UM REFRESH…

Resumindo, a mensagem está na sessão mas eu não queria esperar outra requisição do usuario para a mensagem realmente aparecer.

PS1.:

Viu marcelo, tentei sua idéia também, veja se fiz o que vc estava sugerindo:
Quando o usuario entra na sessão, tem um listener meu que coloca um objeto List vazio.
O Job quando executa, pela esse objeto e adiciona a mensagem:

if (httpSession != null){

String mensagem = "Passaram-se os 3 minutos, ";

MyFacesMsgPerguntaCognicao facesMessage = new MyFacesMsgPerguntaCognicao(mensagem);

httpSession.setAttribute(avisoPergutaCognicao, facesMessage);

List listDeMsgDaSessao = (List) httpSession.getAttribute(listDeMsgDaSessao);

listDeMsgDaSessao.add(facesMessage.toString());//toString porque e so pra teste!!!

httpSession.setAttribute(listDeMsgDaSessao, listDeMsgDaSessao);//estou recolocando pois alterei a referencia a nao o objeto em si

System.out.println(JobAvisaUsuarioPerguntaConicao);

System.out.println(httpSession =! null);

} else {

System.out.println(JobAvisaUsuarioPerguntaConicao);

System.out.println(httpSession == null);

}

O resultado é extamente o mesmo que o de antes, ou seja, um List de string e uma string tem o mesmo comportamento, mesmo que o List já esteja na sessao antes de ser adicionada a mensagem. Isso ocorre, creio, que seja porque qdo pego o List da sessao coloco numa variavel de referencia, usando a variavel de referencia altero o objeto;

Pessoal, me condenem pelos scriplets mas me ajudem por favor!!! Talvez haja um jeito mas fácil, não quero complicar.

Até mais, brigado pela atenção e desculpem se não estou enchergando algo que seja óbvio, porque as vezes acontece!

Criado 6 de fevereiro de 2008
Ultima resposta 11 de fev. de 2008
Respostas 6
Participantes 4