Suprindo a falta do @KeepAlive no JSF 2

Para quem programa em JSF 1.2 com RichFaces + A4j, um recurso muito popular é o @KeepAlive. Anotando um ManagedBean dessa forma ele fica com um escopo maior que o de Request e menor que o de Sessão, sobrevivendo na memória enquando o usuário navegar entre páginas que façam uso de um mesmo ManagedBean. Exemplos clássicos: 1. Páginas de listagem e edição; 2. Formulários preênchidos em etapas.

Ao pisar no terreno do JSF 2.0 essa tag deixa de existir. Ainda que existisse, você pode não querer usar o RichFaces e escolher outra biblioteca, como por exemplo a PrimeFaces. A ausência do @KeepAlive no JSF 2.0 parece não ser vista como algo grave já que existem novas possibilidades de controle de escopo tais como as anotações próprio JSF e a as anotações do CDI. O JSF 2.0 fornece por exemplo a @ViewScoped, usada para páginas recheadas de Ajax, mantendo o ManagedBean vivo enquanto existem requests para a mesma página, ou seja, quando não há navegação (métodos acionados devem ser void ou retornar String null). Já o CDI fornece o @ConversationScoped que permite dizer quando a “conversação” com o ManagedBean inicia e termina de forma programática.

Para quem ainda sente falta do simples e preciso @KeepAlive, uma solução é recorrer ao pacote CDI Extensions (CODI) do projeto Apache MyFaces, que fornece a notação @ViewAcessScoped que nos dá exatamente o comportamente desejado. Para instalá-lo via Maven, adicione as seguintes dependências ao projeto:

<dependency>
	<groupId>org.apache.myfaces.extensions.cdi.modules</groupId>
	<artifactId>myfaces-extcdi-jsf20-module-api</artifactId>
	<version>1.0.3</version>
	<scope>compile</scope>
</dependency>

<dependency>
	<groupId>org.apache.myfaces.extensions.cdi.modules</groupId>
	<artifactId>myfaces-extcdi-jsf20-module-impl</artifactId>
	<version>1.0.3</version>
	<scope>compile</scope>
</dependency>

<dependency>
	<groupId>org.apache.myfaces.extensions.cdi.core</groupId>
	<artifactId>myfaces-extcdi-core-api</artifactId>
	<version>1.0.3</version>
	<scope>compile</scope>
</dependency>

<dependency>
	<groupId>org.apache.myfaces.extensions.cdi.core</groupId>
	<artifactId>myfaces-extcdi-core-impl</artifactId>
	<version>1.0.3</version>
	<scope>compile</scope>
</dependency>

Para usar a anotação no seu ManagedBean, faça como segue:

import br.gov.frameworkdemoiselle.stereotype.ViewController;

@ViewAccessScoped
public class MeuMB {
    ...
}

JSF 1.2: Forçando a recriação de um Managed Bean

A configuração do escopo de ManagedBeans no JSF 1.2 é um tópico que gera bastante confusão. Além de poder definir se o ManagedBean vai ter um ciclo de vida atrelado do Request, Sessão ou Aplicação, existem outros modificadores como o @KeepAlive do RichFaces.

O fato é que essa farofa de escopos misturados com recursos de outras bibliotecas trazem algumas situações indesejadas. Seja em sistemas legados ou seja quando estamos em equipes muito heterogêneas, onde não dá pra garantir um bom planejamento do escopo correto de um MB, pode acontecer de você desenvolver uma solução considerando que o ManagedBean vai ser criado e inicializado ao acessar uma determianda tela, mas ao executar a aplicação, descobre erros que advém do fato do ManagedBean estar sobrevivendo na memória, sendo difícil perceber seu real ciclo de vida. Nesse instante, pode surgir a necessidade da recriação de um Managed Bean, evitando vários problemas.

A dica é implementar um método que será acionado por um commandButton ou commandLink da sua aplicação e que, ao invés de navegar diretamente para uma tela, obterá o ManagedBean via EL (no exemplo abaixo a classe DestinoMB), criará ele novamente, e em seguida chamará a tela desejada:

public class OrigemMB(){
	...
	public String onClickMeuBotao(){				
		FacesContext context = FacesContext.getCurrentInstance();
		ELContext elContext = context.getELContext();
		String elMB = "#{destinoMB}"; // Você deve conhecer a EL do seu MB
		
		ValueExpression expression = context.getApplication().getExpressionFactory()
			.createValueExpression(elContext, elMB, DestinoMB.class);
		expression.setValue(elContext, null);

		//Se necessário é possível ainda obter o MB para incializações
		DestinoMB destinoMB = (DestinoMB) context.getApplication().getExpressionFactory()
			.createValueExpression(elContext, elMB, DestinoMB.class).getValue(elContext);
                destinoMB.inicializar();
			
		return "outcomeDaTelaDesejada";
	}
	...
}