Entity Converters pra dar e vender

Uma coisa que sempre aconselho aos desenvolvedores é que tentem sempre que possível trabalhar em JSF diretamente com os objetos como se estivessem em um ambiente stateful, pois um dos objetivos da tecnologia é tentar abstrair a natureza stateless do HTTP. Não que seja algo tão simples de se fazer algumas vezes, mas não é tão complexo ao ponto de abrir mão desta abstração.

Pensando no que eu disse acima, um dos problemas comuns e chatinhos quando se trabalha com SelectOneMenu (ou SelectManyMenu) e entidades em JSF ocorre quando queremos que o value do nosso SelectItem seja a própria entidade, e não o “id” da mesma. Bem, o que estou querendo dizer é exatamente isso:

public List<SelectItem> getEmpresas() {
	List<SelectItem> items = new ArrayList<SelectItem>();
	for (Empresa e : this.empresas) {
		// observem que o value do meu SelectItem é a própria entidade
		items.add(new SelectItem(e, e.getNome()));
	}
	return items;
}

Isso acaba se tornando trabalhoso pois somos obrigados a implementar um converter para cada entidade, o que particularmente eu não gosto. Levando em consideração que não queremos criar um converter para cada entidade, quais outras soluções temos?

Cenário

Antes de explicar cada solução, vou demonstrar um cenário qualquer para facilitar o entendimento, ou seja, vou demonstrar os artefatos necessários, eles seguem abaixo:

1) Nosso managed bean

public class EmpresaBean {

	private Empresa selectedEmpresa;
	private List<Empresa> empresas = new ArrayList<Empresa>();

	public EmpresaBean() {
		empresas.add(new Empresa(7, "Triadworks"));
		empresas.add(new Empresa(88, "Ivia"));
		empresas.add(new Empresa(921, "Thoughtworks"));
		empresas.add(new Empresa(15, "Caelum"));
		empresas.add(new Empresa(2, "ImproveIT"));
	}

	public List<Empresa> getEmpresas() {
		return empresas;
	}
	public Empresa getSelectedEmpresa() {
		return selectedEmpresa;
	}
	public void setSelectedEmpresa(Empresa selectedEmpresa) {
		this.selectedEmpresa = selectedEmpresa;
		System.out.println("Empresa selecionada: " + selectedEmpresa.getNome());
	}
}

2) Nossa entidade (já implementando a interface BaseEntity para a solução do SimpleEntityConverter)

public class Empresa implements BaseEntity, Serializable {

	private static final long serialVersionUID = 1L;

	private Integer codigo;
	private String nome;

	public Empresa(Integer codigo, String nome) {
		this.codigo = codigo;
		this.nome = nome;
	}

	public Long getId() {
		return new Long(codigo);
	}

	// Métodos getters e setters
	// Não esquecer os métodos equals e hashCode
}

Por favor, não esqueçam de implementar os métodos equals e hashCode, evitemos cair naquele velho probleminha, ok?

3) Nosso formulário

<h:form id="form">
	<h:panelGrid columns="2" border="1">
		<h:outputLabel value="Empresa" for="empresa"/>
		<h:column>
			<h:selectOneMenu id="empresa"
				value="#{empresaBean.selectedEmpresa}"
				converter="simpleEntityConverter" required="true"
				requiredMessage="Valor é obrigatório">
				<f:selectItem itemValue="" itemLabel="Selecione uma empresa"/>
				<t:selectItems value="#{empresaBean.empresas}" var="o" itemLabel="#{o.nome}" itemValue="#{o}"/>
			</h:selectOneMenu>
			<br/>
			<h:message for="empresa" errorStyle="color:darkred;font-size:11px;"></h:message>
		</h:column>
		<f:facet name="footer">
			<h:commandButton value="Submit"/>
		</f:facet>
	</h:panelGrid>
</h:form>

Reparem que nosso componente h:selectOneMenu possui um converter declarado, ou seja, será necessário alterar o converter de acordo com a solução escolhida.

Nada de complexo, certo? É um básico cenário comum.
Então vamos as possíveis soluções :)

EntityConverter

O componente/feature EntityConverter foi desenvolvido pelo Rogério Araújo, que é um dos coordenadores do JavaServer Faces International Group. O componente em si foi pensado inicialmente para o cenário em que é necessário carregar a entidade de um banco de dados, mas isso não impede que a entidade seja obtida de qualquer outro recurso externo, como um web service ou EJB, por exemplo.

O componente funciona perfeitamente bem e é muito simples de configurar, porém ainda assim eu o acho interessante somente em alguns casos específicos, como no caso em que a entidade precisa ser carregada do banco de dados com todos seus atributos e/ou relacionamentos ao submeter o formulário, evitando-se assim onerar o servidor com grandes entidades em memória.

Alguns desenvolvedores reclamam porque o componente -idealmente- efetua uma requisição ao bando de dados para obter a entidade, mas venhamos e convenhamos, isso não deveria ser uma preocupação, principalmente para quem vem de algum framework “action-like” como o Struts. Além do mais, se você possui algum recurso de cache na camada de persistência -como o fornecido pelo Hibernate– não deveria se preocupar tanto com isso.

Eu não pretendo explicar como configurar o componente, pois na wiki do Myfaces você tem tudo que precisa saber, além do mais o criador do componente é coordenador da Javasf e é brasileiro :)

SimpleEntityConverter

Esta solução foi baseada no caso mais comum de um converter para cada entidade, a única diferença é que tentei torna-la “genérica” para qualquer entidade, evitando-se escrever um converter para cada entidade (é esta nossa intenção, certo?).

Sendo, segue abaixo o código do nosso converter:

public class SimpleEntityConverter implements Converter {

	public Object getAsObject(FacesContext ctx, UIComponent component, String value) {
		if (value != null) {
			return this.getAttributesFrom(component).get(value);
		}
		return null;
	}

	public String getAsString(FacesContext ctx, UIComponent component, Object value) {

		if (value != null
				&& !"".equals(value)) {

			BaseEntity entity = (BaseEntity) value;

			// adiciona item como atributo do componente
			this.addAttribute(component, entity);

			Long codigo = entity.getId();
			if (codigo != null) {
				return String.valueOf(codigo);
			}
		}

		return (String) value;
	}

	protected void addAttribute(UIComponent component, BaseEntity o) {
		String key = o.getId().toString(); // codigo da empresa como chave neste caso
		this.getAttributesFrom(component).put(key, o);
	}

	protected Map<String, Object> getAttributesFrom(UIComponent component) {
		return component.getAttributes();
	}

}

Como podem ver, o que basicamente ocorre é que armazenamos as entidades como atributos do nosso componente no método getAsString(), e recuperamos a entidade correta através da chave (neste caso o Id) associada a ela, no método getAsObject(). Mas quem é este BaseEntity?

public interface BaseEntity {

	public Long getId();

}

BaseEntity é uma interface com um método em comum entre as entidades, ou seja, nossas entidades precisarão implementar esta interface para que nosso converter funcione de acordo.

A solução é funcional, porém há algumas ressalvas que gostaria de explicitar. 1) Eu particularmente não gosto da idéia de “sujar” minhas entidades estendendo alguma interface ou classe para poupar código ou resolver problemas não-funcionais, mas isso é uma opinião minha. 2) Outro problema que vejo é que este converter acaba consumindo um pouco mais de memória no servidor por você está alterando o estado do componente, principalmente se sua lista de itens for muito grande, mas não é nada que você deva se preocupar inicialmente, ou seja, somente esquente a cabeça com isso se for realmente necessário.

SimpleIndexConverter

Esta solução eu tomei emprestada dos componentes do Myfaces Trinidad, que por sinal é excelente. A diferença é que eu a simplifiquei um pouco (o código para ser mais exato), logo ela não está tão robusta quanto a original (que se preocupa com casos mais específicos), mas funciona muito bem na maioria dos casos.

Sua idéia principal é utilizar o index da lista de items como chave para cada entidade, assim o que submetemos no formulário é o index da lista, e não um valor (Id?) da entidade como de costume.

O código do converter ficou grandinho, mas é possível enxuga-lo movendo alguns métodos para alguma classe utils. Segue abaixo o código do nosso SimpleIndexConverter:

public class SimpleIndexConverter implements Converter {

	private int index = -1;

	/* (non-Javadoc)
	 * @see javax.faces.convert.Converter#getAsObject(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.String)
	 */
	public Object getAsObject(FacesContext ctx, UIComponent component, String value) {

		SelectItem selectedItem = this.getSelectedItemByIndex(component, Integer.parseInt(value));
		if (selectedItem != null)
			return selectedItem.getValue();

		return null;
	}

	/* (non-Javadoc)
	 * @see javax.faces.convert.Converter#getAsString(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.Object)
	 */
	public String getAsString(FacesContext ctx, UIComponent component, Object value) {
		index++;
		return String.valueOf(index);
	}

	/**
	 * Obtem o SelecItem de acordo com a opção selecionada pelo usuário
	 */
	protected SelectItem getSelectedItemByIndex(UIComponent component, int index) {

		List<SelectItem> items = this.getSelectItems(component);
		int size = items.size();

		if (index > -1
				&& size > index) {
			return items.get(index);
		}

		return null;
	}

	protected List<SelectItem> getSelectItems(UIComponent component) {

		List<SelectItem> items = new ArrayList<SelectItem>();

		int childCount = component.getChildCount();
	    if (childCount == 0)
	      return items;

	    List<UIComponent> children = component.getChildren();
		for (UIComponent child : children) {
			if (child instanceof UISelectItem) {
				this.addSelectItem((UISelectItem) child, items);
			} else if (child instanceof UISelectItems) {
				this.addSelectItems((UISelectItems) child, items);
			}
		}

		return items;
	}

	protected void addSelectItem(UISelectItem uiItem, List<SelectItem> items) {

		boolean isRendered = uiItem.isRendered();
		if (!isRendered) {
			items.add(null);
			return;
		}

		Object value = uiItem.getValue();
		SelectItem item;

		if (value instanceof SelectItem) {
			item = (SelectItem) value;
		} else {
			Object itemValue = uiItem.getItemValue();
			String itemLabel = uiItem.getItemLabel();
			// JSF throws a null pointer exception for null values and labels,
			// which is a serious problem at design-time.
			item = new SelectItem(itemValue == null ? "" : itemValue,
					itemLabel == null ? "" : itemLabel, uiItem
							.getItemDescription(), uiItem.isItemDisabled());
		}

		items.add(item);
	}

	@SuppressWarnings("unchecked")
	protected void addSelectItems(UISelectItems uiItems, List<SelectItem> items) {

		boolean isRendered = uiItems.isRendered();
		if (!isRendered) {
			items.add(null);
			return;
		}

		Object value = uiItems.getValue();
		if (value instanceof SelectItem) {
			items.add((SelectItem) value);
		} else if (value instanceof Object[]) {
			Object[] array = (Object[]) value;
			for (int i = 0; i < array.length; i++) {
				// TODO test - this section is untested
				if (array[i] instanceof SelectItemGroup) {
					resolveAndAddItems((SelectItemGroup) array[i], items);
				} else {
					items.add((SelectItem) array[i]);
				}
			}
		} else if (value instanceof Collection) {
			Iterator<SelectItem> iter = ((Collection<SelectItem>) value)
					.iterator();
			SelectItem item;
			while (iter.hasNext()) {
				item = iter.next();
				if (item instanceof SelectItemGroup) {
					resolveAndAddItems((SelectItemGroup) item, items);
				} else {
					items.add(item);
				}
			}
		} else if (value instanceof Map) {
			for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) value).entrySet()) {
				Object label = entry.getKey();
				SelectItem item = new SelectItem(entry.getValue(),
						label == null ? (String) null : label.toString());
				// TODO test - this section is untested
				if (item instanceof SelectItemGroup) {
					resolveAndAddItems((SelectItemGroup) item, items);
				} else {
					items.add(item);
				}
			}
		}
	}

	protected void resolveAndAddItems(SelectItemGroup group, List<SelectItem> items) {
		for (SelectItem item : group.getSelectItems()) {
			if (item instanceof SelectItemGroup) {
				resolveAndAddItems((SelectItemGroup) item, items);
			} else {
				items.add(item);
			}
		}
	}

}

Não há muito a se explicar sobre o converter acima, a “mágica” toda está nos métodos auxiliares (que por sinal maior parte do código foi retirado do Trinidad, porém enxugado para nossas necessidades).

Este converter diferentemente do SimpleEntityConverter não altera o estado do componente, evitando assim a utilização de mais memória, ele busca as entidades -que já existem- dentro do componente (SelectOneMenu?) como estado do mesmo.

Assim como os outros converters, este também possui algumas ressalvas. O que podemos citar é 1) Por ele se utilizar do index da lista de items talvez não seja possível trabalhar com o componente no lado cliente (javascript) se você depender dos valores (Id?) de cada item.

Concluindo

Dizer que não é possível simplificar a vida com JSF é mentir, a tecnologia te fornece recursos para abstrair a natureza stateless do HTTP, e é interessante que se aproveite destes recursos.

As soluções explanadas acima não são únicas, existem outras com toda certeza, eu tentei demonstrar algumas delas quando não se tem algum framework (Seam?) ou conjunto de componentes (Trinidad?) para nos auxiliar, ou seja, quando contamos apenas com a implementação do JSF, o que -acredito eu- na maioria dos casos é o que ocorre.

É possível estende-las e até melhora-las, você é livre para isso, e dependendo da tua necessidade provavelmente será o melhor caminho, só não deixe de contribuir com o código para a comunidade.

Enfim, todas as soluções são plausíveis e funcionam para a maioria dos cenários, porém em determinados cenários cada uma se adequa melhor, resta a você analisar e ver qual se encaixa nas tuas necessidades. Além do mais, elas não são mutuamente excludentes.

Não era mais uma receita de bolo JSF!

É incrível como depois do post Utilizando AJAX com JSF de maneira eficiente muitos desenvolvedores tem utilizado o exemplo básico do post -que nada mais era do que uma prova de conceito- como receita de bolo para criar aplicações sérias. Talvez eu não tenha sido claro, mas o intuito do post não era demonstrar um passo-a-passo de como desenvolver uma aplicação com alguns conjuntos de componentes, mas sim passar o conceito de como aproveitar melhor os recursos oferecidos pela tecnologia para criar aplicações mais ricas e leves.

Eu tentei ao máximo deixar claro que não eram os componentes X, Y ou Z que me forneciam recursos para implementar o conceito, como está bem claro nos trechos do post abaixo:

Utilizar um conjunto de componentes ricos e um framework AJAX. (Neste caso eu optei pelo Richfaces/Ajax4jsf por ser bastante simples de usar e integrar, por ser estável e por ser bastante utilizado na comunidade, contudo, qualquer outro conjunto de componentes AJAX poderia ser utilizado, como Trinidad ou mesmo o IceFaces);

E este,

Eu não me prendi a utilização dos frameworks ou conjuntos de componentes utilizados pois este não era o intuito do post, isso é algo simples e acredito que a maioria dos leitores não tenham dificuldades em relação a isso. Além do mais é possível implementar isso não somente com o Richfaces/Ajax4jsf, mas com qualquer outro framework ou conjuntos de componentes AJAX, por exemplo, […]

Não é necessário utilizar Richfaces/Ajax4jsf, Tomahawk e Facelets para aproveitar os recursos da tecnologia de forma eficiente, você poderá conseguir isso com qualquer outro framework ou conjunto de componentes que te permitam unir AJAX e JSF de maneira prática, sejam eles Trinidad, JBoss Seam, IceFaces, Ajax4jsf, ADF Faces e/ou JSFTemplating.

Mais claro que isso é impossível.. porém ainda não acabou, um fato engraçado é que alguns desenvolvedores chegaram a se queixar de que eu fui muito radical ao dizer para desenvolverem toda uma aplicação em uma única página. Eu em nenhum momento disse ou mesmo insinuei isto, o que eu disse foi:

A idéia principal tem por objetivo evitar escrever muitas páginas para representar cada view (tela), diminuindo assim o número de páginas no projeto, o número de regras de navegação no faces-config.xml, diminuindo o overhead no servidor e no cliente a cada refresh de página, […]

Vocês não precisam e provavelmente não deveriam implementar suas aplicações seguindo o exemplo do post, ele é básico, ele serve como referência, possui uma implementação simples e não se preocupa com alguns requisitos não funcionais como a paginação sob demanda ou quais os objetos que deveriam permanecer entre as requisições. Além do mais o exemplo é um CRUD, e não uma interface com o usuário mais complexa no sistema.

Entendam uma vez por todas que não existe uma arquitetura de referência ou receita de bolo para desenvolver sistemas, cada sistema possui suas necessidades e peculiaridades, e estas devem ser estudadas e analisadas para se chegar a uma solução plausível.

O importante é que vocês compreendam o conceito, seus prós e contras e principalmente onde e quando utiliza-lo. Não se prendam ao framework ou conjunto de componentes, a maioria deles hoje em dia te fornecem os recursos necessários para implementar a idéia sem muitas dificuldades.

Enfim, estudem, entendam, aprimorem, façam testes e implementem o conceito onde for necessário, com os componentes e frameworks cabíveis e sempre com bom senso.

Utilizando AJAX com JSF de maneira eficiente

Graças a natureza orientada à componentes e eventos do JavaServer Faces podemos implementar aplicações com AJAX de maneira simples e prática. Existem vários frameworks e conjuntos de componentes AJAX disponíveis para JSF, uns mais simples e super fáceis de integrar, outros mais chatinhos, porém bastante eficientes em determinados cenários. Entre eles o mais utilizado e prático sem dúvida nenhuma é o Ajax4jsf que é trazido juntamente com o conjunto de componentes Richfaces da JBoss, com ele é possível inserir recursos AJAX em aplicações JSF de forma transparente e bastante produtiva.

Mesmo com a toda a simplicidade e gama enorme de componentes/frameworks AJAX ainda assim estes recursos não são aproveitados de forma eficiente ao se desenvolver com JSF, muitos desenvolvedores mesmo utilizando-se destes recursos continuam pensando de forma “click’n’wait”, o que no final das contas acaba meio que “anulando” os benefícios oferecidos por tais recursos.

Na onda de aplicações ricas e WEB2.0 não podemos deixar de aproveitar certos recursos essenciais para desenvolver tais aplicações. Utilizar estes recursos em JSF de forma eficiente trará ganhos consideráveis em vários aspectos para o projeto, à equipe de desenvolvimento e principalmente ao cliente, entre eles podemos destacar aspectos como performance, escalabilidade, produtividade e principalmente usabilidade.

Mas fica a pergunta: Como conseguir isso?

Abandonando a velha escola

O que precisamos fazer é deixar de lado o pensamento em interfaces “click’n’wait”, deixar de lado o desenvolvimento baseado em “action-like”, abandonar a navegação dirigida à páginas, precisamos aproveitar melhor os recursos AJAX oferecidos por frameworks e componentes ricos, inserir maior usabilidade nas páginas, aprender um pouco sobre AJAX Patterns e obter mais produtividade ao desenvolver páginas com JSF.

Com estes pensamentos em mente já temos uma melhor direção para um desenvolvimento mais eficiente. Para obter isso eu pretendo me utilizar de certas premissas básicas, como:

  • Utilizar um conjunto de componentes ricos e um framework AJAX. (Neste caso eu optei pelo Richfaces/Ajax4jsf por ser bastante simples de usar e integrar, por ser estável e por ser bastante utilizado na comunidade, contudo, qualquer outro conjunto de componentes AJAX poderia ser utilizado, como Trinidad ou mesmo o IceFaces);
  • Desenvolver as regras de navegação entre views orientada à estados e não à páginas;
  • Inserir um maior controle de fluxo de navegação e inteligência nos managed beans;
  • Utilizar algum framework de templating. (Optei pelo Facelets por ser simples, prático, produtivo, estável, ter uma excelente performance e principalmente por ter sido desenvolvido para JSF);
  • Diminuir o número de páginas (xhtml, jspx, jsp etc) no projeto;
  • Evitar reloads/refresh de páginas ao máximo, ou sempre que possível;

Observando a lista acima fica fácil ver que a maioria dos itens se complementam e colaboram entre si, porém o mais importante sem dúvida é o 2º item, “Desenvolver as regras de navegação entre views orientada à estados e não à páginas”, acho que este item resume 90% do que pretendo falar. Baseado nestas premissas básicas vou implementar uma aplicação bem simples (CRUD) e comentar certos pontos nela que facilitarão o entendimento.

Desenvolvendo as regras de navegação orientada à estados

Primeiramente devemos tirar da cabeça a maneira convencional de criar as regras de navegação orientada à páginas, ou seja, ter que desenvolver 3 ou 4 páginas para implementar um CRUD por exemplo, páginas como searchlist.jsp, add.jsp, update.jsp e show.jsp (algumas vezes se resume em até 2 páginas) devem ser descartadas. Ter várias páginas significa neste caso ter várias árvores de componentes, significa que o framework precisa gerar novas árvores de componentes a cada mudança de página, e isso custa memória e processamento no servidor, sem falar consumo de banda e overhead no cliente (browser).

Sendo, substituiremos essa abordagem da velha escola por outra mais eficiente, orientada à estados, em que utilizaremos somente uma única página que terá seu fluxo de navegação gerenciado por um managed bean mais inteligente.

A idéia principal tem por objetivo evitar escrever muitas páginas para representar cada view (tela), diminuindo assim o número de páginas no projeto, o número de regras de navegação no faces-config.xml, diminuindo o overhead no servidor e no cliente a cada refresh de página, obtendo uma melhor performance utilizando sempre a mesma árvore de componentes, inserir maior usabilidade na aplicação para o usuário, e claro, aproveitar eficientemente os recursos AJAX em cada página.

Logo, eu criei um template bem simples com Facelets para nossas páginas de CRUDs, com este template dá para ter uma idéia de como poderíamos escrever páginas bem melhores para nossas aplicações, o template (parte dele) seria algo do tipo:

[myCrudTemplate.xhtml]

<body>
	<a4j:outputPanel ajaxRendered="true" id="mainOutputPanel">
		<a4j:form>
			<!-- bloco de pesquisa -->
			<rich:panel id="searchlistBlock"
						rendered="#{defaultBean.pesquisarState}">
				<f:facet name="header">
					Pesquisar
		        </f:facet>
				<ui:insert name="searchlistBlock">
				</ui:insert>
			</rich:panel>
			<!-- bloco de edição -->
			<rich:panel id="addUpdateBlock"
						rendered="#{defaultBean.adicionarState or defaultBean.editarState}">
				<f:facet name="header">
					Dados do Usuário
		        </f:facet>
				<ui:insert name="addUpdateBlock">
				</ui:insert>
			</rich:panel>
		</a4j:form>
	</a4j:outputPanel>
</body>

Nosso template se resume em dois blocos, um bloco para pesquisa e exibição dos resultados (searchlistBlock):
SearchlistBlock

e outro bloco para o formulário de inclusão e edição de cada registro (addUpdateBlock):
AddUpdateBlock

Estes blocos substituem as páginas que a velha escola sempre pregou, assim ao se pensar em navegar entre views (telas) nós não mais navegaremos entre páginas (recursos físicos), mas sim entre blocos (recursos lógicos) dentro daquela mesma página. Estes blocos serão gerenciados por estado(s) no managed bean e não mais regras de navegação do faces-config.xml, assim sempre que quisermos “mudar de tela” nós alteramos o estado atual no managed bean e atualizamos parte da página com AJAX escondendo os blocos desnecessários e exibindo os blocos correspondentes à nova view.

Reparem no template que os blocos somente serão renderizados (através do atributo rendered do componente rich:panel) de acordo com o estado atual da view.

Meu managed bean será como qualquer outro, porém eu terei um atributo para representar o estado atual da view (tela) e alguns métodos para expor este estado de maneira legível, logo meu managed bean será algo como:

[UsuarioCrudBean.java]

public class UsuarioCrudBean {

	public static final String PESQUISAR_STATE = "pesquisar";
	public static final String ADICIONAR_STATE = "adicionar";
	public static final String EDITAR_STATE = "editar";

	private String currentState = PESQUISAR_STATE;
	private String nome;
	private Usuario usuario;
	private List<Usuario> usuarios = new ArrayList<Usuario>();
	private UIPanel panelForm;
	// métodos gets e sets

	/**
	 * Pesquisa usuários por nome
	 */
	public void pesquisar() {
		this.setCurrentState(PESQUISAR_STATE);
		this.usuarios = UsuarioRepositorio.findUsuarioByNome(this.nome);
	}

	/**
	 * Prepara view adicionar
	 */
	public void prepareAdicionar() {
		this.clear();
		this.setCurrentState(ADICIONAR_STATE);
	}

	/**
	 * Adiciona usuário
	 */
	public void adicionar() {
		UsuarioRepositorio.add(this.usuario);
		this.pesquisar();
	}

	/**
	 * Prepara view editar
	 */
	public void prepareEditar() {
		this.setCurrentState(EDITAR_STATE);
	}

	/**
	 * Edita usuário
	 */
	public void editar() {
		// já edita diretamente na lista (a lista está como static)
		this.pesquisar();
	}

	/**
	 * Exclui usuário
	 */
	public void excluir() {
		UsuarioRepositorio.delete(this.usuario);
		this.pesquisar();
	}

	/**
	 * Referente ao botão voltar
	 */
	public void voltar() {
		this.clear();
		this.pesquisar();
	}

	/**
	 * Limpa atributos
	 */
	private void clear() {
		this.nome = "";
		this.usuario = new Usuario();
		this.cleanSubmittedValues(this.panelForm);
	}

	/*
	 * Métodos que expõem o estado à página
	 */

	public boolean isPesquisarState() {
		String state = this.getCurrentState();
		return (state == null || PESQUISAR_STATE.equals(state));
	}
	public boolean isAdicionarState() {
		return ADICIONAR_STATE.equals(this.getCurrentState());
	}
	public boolean isEditarState() {
		return EDITAR_STATE.equals(this.getCurrentState());
	}

	/**
	 * Limpa os componentes filhos para que depois eles possam ser recriados
	 * @param component
	 */
	protected void cleanSubmittedValues(UIComponent component) {
		component.getChildren().clear();
	}
}

As regras de navegação serão gerenciadas internamente pelo managed bean, a página (jsp, xhtml etc) deve trabalhar em cima dos métodos expostos pelo managed bean, e não do atributo currentState em si, ou seja, para a página não importa como está implementada as regras de navegação, a página somente precisa conhecer qual o estado atual para que assim ela possa renderizar ou não seus componentes.

Nossa página de CRUD utilizará o template myCrudTemplate.xhtml como base, segue abaixo o código fonte da página somente com o bloco de edição:

[usuarioCrud.xhtml]

<ui:composition template="/pages/myCrudTemplate.xhtml">

	<!-- passa parâmetro para o template -->
	<ui:param name="defaultBean" value="#{usuarioCrudBean}" />

	<ui:define name="title">
		Cadastro de Usuários
		<t:saveState id="_bean" value="#{usuarioCrudBean}" />
	</ui:define>

	<ui:define name="searchlistBlock">
		<!-- bloco de pesquisa -->
	</ui:define>

	<ui:define name="addUpdateBlock">
		<h:panelGrid columns="2" id="panel2" binding="#{usuarioCrudBean.panelForm}" columnClasses="odd-row,even-row">
			<h:outputLabel value="Login: "/>
			<h:column>
				<h:inputText value="#{usuarioCrudBean.usuario.login}" size="30" required="true" id="login"
							requiredMessage="Campo Login é obrigatório."
							readonly="#{usuarioCrudBean.editarState}"
							style="#{usuarioCrudBean.editarState ? 'color: gray;' : ''}"/>
				<br/>
				<h:message for="login" errorStyle="color: darkred;"/>
			</h:column>
			<h:outputLabel value="Senha: "/>
			<h:column>
				<h:inputSecret value="#{usuarioCrudBean.usuario.senha}" size="30" redisplay="true"/>
				<br/>
			</h:column>
			<h:outputLabel value="Nome: "/>
			<h:column>
				<h:inputText value="#{usuarioCrudBean.usuario.nome}" size="60" required="true" id="nome"
							requiredMessage="Campo Nome é obrigatório." />
				<br/>
				<h:message for="nome" errorStyle="color: darkred;"/>
			</h:column>
			<h:outputLabel value="Blog: "/>
			<h:column>
				<h:inputText value="#{usuarioCrudBean.usuario.blog}" size="90" id="blog"/>
			</h:column>
			<h:outputLabel value="Ativo?"/>
			<h:column>
				<h:selectOneMenu value="#{usuarioCrudBean.usuario.ativo}" id="ativo"
						requiredMessage="Campo Ativo é obrigatório."
						required="true">
					<f:selectItem itemValue="" itemLabel="[Selecione uma opção]" />
					<f:selectItem itemValue="#{true}" itemLabel="Sim" />
					<f:selectItem itemValue="#{false}" itemLabel="Não" />
				</h:selectOneMenu>
				<br/>
				<h:message for="ativo" errorStyle="color: darkred;"/>
			</h:column>
		</h:panelGrid>
		<a4j:commandButton value="Adicionar" action="#{usuarioCrudBean.adicionar}" rendered="#{usuarioCrudBean.adicionarState}"/>
		<a4j:commandButton value="Editar" action="#{usuarioCrudBean.editar}" rendered="#{usuarioCrudBean.editarState}"/>
		<a4j:commandButton value="Voltar" action="#{usuarioCrudBean.voltar}" immediate="true"/>
	</ui:define>
</ui:composition>

Assim como nossos blocos principais (searchlistBlock e addUpdateBlock) do template, muitos componenentes também são renderizados ou mudam de estado dependendo do fluxo de navegação, um deles por exemplo é o botão “Editar” que somente será renderizado quando nossa view estiver no estado de edição:

<a4j:commandButton value="Editar" action="#{usuarioCrudBean.editar}" rendered="#{usuarioCrudBean.editarState}"/>

Na minha página usuarioCrud.xhtml eu estou passando a referência do managed bean atual como parâmetro para o template para que ele consiga gerenciar os blocos corretamente, isso me possibilita ter um template mais genérico para minhas páginas de CRUDs:

<ui:param name="defaultBean" value="#{usuarioCrudBean}" />

Atentem que eu estou utilizando o componente t:saveState do Myfaces Tomahawk para ampliar o escopo de conversação, com ele eu posso manter meus managed beans como escopo de request enquanto eu navego entre os blocos lógicos, depois de mudar literalmente de página (recurso físico) eu terei aqueles dados removidos da memória do servidor.

<t:saveState id="_bean" value="#{usuarioCrudBean}" />

Outra coisa interessante é que por estarmos sempre trabalhando sob a mesma árvore de componentes (viewRoot) é necessário resetar o estado de alguns componentes ou bloco de componentes quando necessário, pois mesmo que estes componentes não sejam renderizados eles ainda assim mantém o estado anterior.

No nosso caso eu estou sempre limpando o estado do formulário de edição ao clicar no botão “Voltar” da aplicação, que por sua vez faz uma chamada ao método cleanSubmittedValues(), é este método que se encarrega de limpar o estado anterior do formulário:

protected void cleanSubmittedValues(UIComponent component) {
	component.getChildren().clear();
}

Existem outras abordagens para resetar o estado dos componentes de um formulário, eu busquei utilizar a maneira mais simples, porém creio que não seja a mais performática -neste caso-. Mesmo não possuindo a melhor performance ainda assim é -várias vezes- mais rápida que gerar todo uma nova árvore de componentes (utilizado pela velha escola), com toda certeza.

E quais seriam as vantagens dessa abordagem?

Existem várias vantagens em relação a abordagem da velha escola, entre elas podemos destacar:

  • Menor consumo de memória e processamento no servidor, pois estaremos sempre trabalhando sob a mesma árvore de componentes (viewRoot);
  • Menor overhead no cliente (browser), pois estaremos evitando reload completo de páginas;
  • Menor tráfego de dados na rede, muito menor, pois estaremos apenas transportando pequenos blocos de página;
  • Maior escalabilidade e performance na aplicação;
  • Diminui drasticamente o número de pontos de manutenção na aplicação (páginas, arquivos de configuração etc);
  • Renderização dos componentes muito mais rápida, sejam parciais ou grandes blocos lógicos;
  • Maior usabilidade e controle de renderização parcial nas páginas;
  • Evita o problema do botão voltar (back button);
  • Evita resubmissão do formulário após um refresh (F5);
  • Managed beans realmente mais inteligentes;
  • Exibição das páginas com um aspecto mais leve, possibilitando ainda a utilização de efeitos através de frameworks javascripts;
  • Facilidade em criar classes de managed beans mais genéricas (um CrudBean por exemplo);
  • Melhor controle/tratamento de respostas entre cliente e o servidor (barra de progresso, timed out etc);

Bem, vantagens é o que não falta em relação a velha escola. Provavelmente possamos discutir mais algumas vantagens e até quem sabe desvantagens na lista de discussão do JavaSF.

Concluindo

Mesmo esta abordagem sendo superior a abordagem da velha escola ela ainda assim não é a “bala de prata”, como toda nova abordagem ela deve ser estudada e utilizada com bom senso, haverá momentos em que será mais prático e até melhor efetuar um reload completo de página, ou dividir os blocos lógicos em pequenas páginas para evitar grandes arquivos físicos, enfim, bom senso é algo que sempre deve ser levado em consideração.

Eu não me prendi a utilização dos frameworks ou conjuntos de componentes utilizados pois este não era o intuito do post, isso é algo simples e acredito que a maioria dos leitores não tenham dificuldades em relação a isso. Além do mais é possível implementar isso não somente com o Richfaces/Ajax4jsf, mas com qualquer outro framework ou conjuntos de componentes AJAX, por exemplo, no último projeto que participei implementamos nossas páginas utilizando-se desse conceito com Myfaces Trinidad, que por sinal está funcionando muito bem.

É bastante notório a velocidade na renderização das páginas, sendo aconselho vocês a fazerem alguns testes com esta abordagem e observarem o quanto mais rápido se torna o desenvolvimento e a renderização das páginas. Eu fiz a aplicação o mais simples possível para demonstrar essa abordagem, com certeza ainda é possível otimizar as requisições AJAX através de configurações do Ajax4jsf e do Richfaces, você pode ver mais sobre isso na documentação do mesmo.

Eu estou disponibilizando esta aplicação exemplo como prova de conceito, é um projeto do Eclipse (MyEclipse para ser mais exacto), sendo basta importa-lo e efetuar o deploy da aplicação.