Explicação sobre PhaseListener

Alguém pode me dar uma explicação sobre PhaseListener, como funciona?

Olá.

A API do JSF define o as fases como etapas do ciclo de vida do framework.
Existem 6 fases no JSF, começando nesta sequência:

  1. Restore View
  2. Apply Request Values
  3. Process Validators
  4. Update Model
  5. Invoke Application
  6. Render Response

Os PhaseListener são objetos que serão notificados no início e término de cada etapa.

Normalmente são usados como “interceptadores”, isto é, você quer interceptar uma requisição para realizar algo específico, como validar o login do usuário no sistema, por exemplo.

Quando você implementa o PhaseListener, você obrigatoriamente precisa obedecer o contrato no qual garante os métodos:

[code]
afterPhase(javax.faces.event.PhaseEvent event)
// Manipula uma notificação que está procesando uma fase do ciclo de vida JSF quando este já completou.

void beforePhase(javax.faces.event.PhaseEvent event);
// Manipula uma notificação que está procesando uma fase do ciclo de vida JSF quando este está prestes a iniciar.

javax.faces.event.PhaseId getPhaseId();
// Retorna o Tipo de Fase no JSF que o objeto será notificado. Pode ser qualquer uma das 6 fases, ou todas as fases.[/code]

Att.

Mais uma coisa, como faço para dispara uma action na fase Render Response quando uma determinada página for ativada “executada”?

Bom.

O que você quer então é executar um método toda vez que alguma página específica for chamada, certo?

Opções:

Você poderia fazer algo como:

String pagina = event.getFacesContext().getViewRoot().getViewId(); if ("algumaPagina.jsf".indexOf(pagina) > -1) { // Se seu metodo for algo como: gerenteSistema.executaAcao() event.getFacesContext().getApplication().createMethodBinding("#{gerenteSistema.executaAcao()}", new Class[] {}).invoke(event.getFacesContext(), new Object[] {}); }
Neste código acima, sua action não possui parâmetros, mas pode incluí-la.

  1. Em vez de usar MethodBinding, usar createValueBinding() para instanciar o seu managedBean ou retornar da memória e executar a sua action;

Tem outras opções com filtro (ServletFilter), mas acho mais complicado.

Este código funciona se sua página de origem for JSF, caso contrário, você deverá substituir o código para retornar a página atual por outro que identifque através do request (ServletRequest).

Att.

Ah,

Este código você deve colocar em um dos métodos (beforePhase ou afterPhase).
Acredito que seja beforePhase.

Att.

Acredito que deve resolver, vou testar com a tua dica e depois retorno o resultado.

A intenção de executar essa action na fase Render Response quando uma determinada página for ativada, é para atualizar componentes da página visualmente, exemplo:

mudar imagens das tabs (guias) do sistema, ou seja, quando clicar em um outputlink, mudar a imagem da tab ativa para a imagem da tab inativa e mudar a imagem da tab inativa para a imagem da tab ativa.

Valeu pela ajuda!

E se eu tentar fazer dessa forma?

lifecycle.addPhaseListener(
new PhaseListener()
{
public void beforePhase(PhaseEvent event)
{
priceQuote = QuoteService.getLatestQuote(currentQuoteId);
}
public void afterPhase(PhaseEvent event)
{
}
public PhaseId getPhaseId()
{
return PhaseId.RENDER_RESPONSE;
}
});

Olá.

Então…Você pode inserir phaseListeners em tempo de execução, no entanto você precisa fazer isso no início de sua aplicação, de forma que você possa garantir as funcionalidades do menu que vc quer.

Eu acharia melhor você fazer uma classe Listener e incluir no faces-config.xml, desta forma, fica mais fácil a manutenção da configuração.

Att.

Depois de muito pesquisar e de tentar muitas formas cheguei até esse código:

[quote]package br.com.intercom.sciweb.administracao.action.workspace;

import javax.faces.component.UIViewRoot;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

import com.icesoft.faces.component.ext.HtmlForm;
import com.icesoft.faces.component.ext.HtmlOutputText;

public class AppPhaseListener implements PhaseListener{

public AppPhaseListener() {
}

public void beforePhase(PhaseEvent e) {
}

public void afterPhase(PhaseEvent e) {

	// ativa o gerenciador de workspaces
	if (e.getFacesContext().getViewRoot().getViewId().equals("/administracao/main.xhtml"))
		startup(e.getFacesContext().getViewRoot());
	
}

protected void startup(UIViewRoot root) {
	
	HtmlForm form = (HtmlForm) root.findComponent("form");
	
	if (form == null){
		System.out.println("FORM NULO !!!!!!!!!!!!!!!!");
		return;
	}
	
	HtmlOutputText copyright = (HtmlOutputText) form.findComponent("copyright");
	copyright.setValue("ALTERADO ---------------------");
	
}

public PhaseId getPhaseId() {
	return PhaseId.RENDER_RESPONSE;
}

}[/quote]

O problema que estou tendo é que mesmo achando o componente e setando o novo valor, na view continua o valor antigo, agora eu pergunto será preciso chamar a fase do render response de novo para que os novos valores sejam renderizados ou falta alguma coisa pra fazer.

PS. estou usando o myeclipse

Olá.

Não seria mais fácil as guias do sistema estarem ligadas a um managedBean que informa qual página tab está ativa/inativa?
Daí você poderia usar aquele código do MethodBinding para alterar o valor do objeto.

A fase deve ser esta mesma (RENDER_RESPONSE), no entanto, deve estar no beforePhase e não afterPhase().

Se você trocar, provavelmente seu código vai funcionar.

Mas mesmo assim acharia melhor você usar um managedBean para as guias em escopo de sessão, um objeto mutável (POJO) que informasse qual guia está selecionada.
Então você teria um método no seu managedBean que alterasse este estado (guia selecionada) e seu listener chamaria este método.
Não gosto muito de criar dependência entre funcionalidades e página JSP. Desta forma, se algum designer mexer na sua página de guia, não precisará se preocupar com IDs, forms, etc. Questão de gosto. :slight_smile:

Att.

o problema é que não é só as tabs que iram ser trocadas outros componentes também receberiam novos valores.

E quanto a usar o beforPhase não da certo pois nesse momento a arvore dos componentes ainda não foi criada por tanto não há componente nesse momento para ser modificado o unico lugar possível é no afterPhase.

E essa forma é a que se encaixa na solução que preciso o problema é que esta faltando algum detalhe para que a mudança de valor seja vista na view.

Você precisará mudar o seu código de qualquer jeito.

A)
Em RENDER_RESPONSE:

  1. no BeforePhase() a página ainda não foi enviada, isto é, os componentes não foram criados para resposta, e por isso você não consegue achá-los.

  2. No AfterPhase() os componentes já foram criados, porém, não podem ser mais alterados, e aí você não pode mudar o estado visual deles a não ser que realize um refresh na página.

Desta forma então acredito que não existe possibilidade.
Você poderia fazer como disse, e todos os outros componentes (além do guia) que precisam ser atualizados você os linka ao managedBean, e assim no beforePhase() você realiza as mudanças necessárias.

B)
Uma outra solução seria usa ajaxForJava (A4J). Ao realizar a troca de guia, você ativaria uma ação usando a4j:commandButton/. O método executado por ele executaria as mudanças no managedBean. O segredo seria que ao terminar o processo, você poderia redenrizar as partes que foram alteradas, fonecedendo o ID de seus componentes. Desta forma, seu JSP alteraria a guia, de acordo com a ação A4J.
Você precisaria, no entanto, usar um evento javascript para detectar a mudança de guia, e então executar este processo.
Esta não é uma das melhores saídas (é um chuncho). Continuo preferindo a primeira.

Att.

nesse código esta tudo certo, o problema é na carga da página, a mudança é feita só não é renderizada na página, o que eu gostaria de saber é como faço para que o RENDER_RESPONSE seja chamadado após eu enviar os novos valores?

Você deveria chamar o seguinte método após realizar as mudanças:

FacesContext.getCurrentInstance().renderResponse();

Mas provavelmente lançará uma IllegalStateException já que o RENDER_RESPONSE já executou (Entre o beforePhase e AfterPhase).

Este não é o melhor caminho.

Att.

luissandro, conseguiu resolver te problema? eu tive um identico e consegui…qlqr coisa dá um grito

Olá jonataswingeter, td bem?
Cara, eu sou novato no java, e estou procurando explicações sobre o PhaseListener, para fazer a validação do usuário. Estou com problemas na implementação do código abaixo:

[code]package br.com.caelum.fj26.util;

import javax.faces.application.NavigationHandler;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.http.HttpSession;

public class AuthorizationListener implements PhaseListener {

/**
 * 
 */
private static final long serialVersionUID = 1L;

public void afterPhase(PhaseEvent event) {
	System.out.println("<-----------VALIDANDO REQUISIÇÃO------------>");

	FacesContext facesContext = event.getFacesContext();
	String currentPage = facesContext.getViewRoot().getViewId();

	boolean isLoginPage = (currentPage.lastIndexOf("olajsf.jsp") > -1);
	HttpSession session = (HttpSession) facesContext.getExternalContext()
			.getSession(true);
	Boolean currentUser = (Boolean) session.getAttribute("currentUser");

	if (!isLoginPage && currentUser == null) {
		NavigationHandler nh = facesContext.getApplication()
				.getNavigationHandler();
		System.out.println("<-------condicao lançada-------->");
		nh.handleNavigation(facesContext, null, "loginPage");
		System.out.println("<-------usuario nao logado-------->");
	}

}

public void beforePhase(PhaseEvent event) {

}

public PhaseId getPhaseId() {
	return PhaseId.RESTORE_VIEW;
}

}[/code]

Qual a situação: eu quero que o usuario logue no sistema pela página de login, e que quando ele digitar usuario e senha, ele tenha acesso a uma página principal com algumas opções de acesso e inclusao de dados num banco. O que está acontecendo é o seguinte: o usuario loga, entra na pagina principal, mas quando tenta acessar qualquer outra opção dentro dessa pagina, ele volta para a pagina de login. O que pode estar errado no meu código??
Agradeço a atençao.

createMethodBinding está depreciado, vcs tem alguma outra alternativa?