themeless

04/11/2009

Usando um Map<String, Object> para armazenar os parâmetros de um Managed Bean

Arquivado em: java — tnaires @ 21:35

Antes de escrever a segunda parte do artigo, vou apresentar o código do managed bean mencionado na seção de comentários do post anterior.

Na ocasião, o leitor Alessandro questionou os motivos de passar para os métodos create() e update() dos services um Map<String, Object> contendo os dados a serem persistidos ao invés da própria entidade. Respondi então que um desses motivos era a existência de um managed bean cujos atributos mapeados para a página JSF correspondente estavam contidos em um Map<String, Object> – onde a key é o nome do atributo e o value, seu valor – e assim seria mais fácil passá-lo para os services.

Sem mais delongas, segue o código (comentários javadoc removidos para torná-lo mais sucinto).

public abstract class ControllerBean {
	private Map<String, Object> parametros = new HashMap<String, Object>();

	protected final void definirParametros(String... nomesParametros) {
		for (String nome: nomesParametros) {
			parametros.put(nome, null);
		}
	}

	protected final void limparParametros() {
		for (String parametro: parametros.keySet()) {
			parametros.put(parametro, null);
		}
	}

	public final Map<String, Object> getParametros() {
		return parametros;
	}

	public final void setParametros(Map<String, Object> parametros) {
		this.parametros = parametros;
	}

	// Outros métodos omitidos.
}

Como usá-lo? Suponha que estamos construindo um managed bean responsável pelas operações de login do usuário (que contém os campos nome e senha). Ele pode ser implementado escrevendo-se o código abaixo:

public class LoginBean extends ControllerBean {
	public LoginBean() {
		// Definindo dois campos para a página de login.
		super.definirParametros("login", "senha");
	}

	public String efetuarLogin() {
		// A classe ApplicationServiceFactory foi apresentada no post anterior.
		Usuario usuario = ApplicationServiceFactory
			// Obtendo o service de usuário, que realizará o login.
			.getUsuarioService()
			// Passando diretamente o Map contendo os campos.
			.efetuarLogin(super.getParametros());

		// Restante do código omitido.
	}
}

Na página JSF correspondente a esse managed bean teríamos controles referenciando os parâmetros da seguinte forma:

<h:inputText value="#{loginBean.parametros['login']}" />
<h:inputText value="#{loginBean.parametros['senha']}" />

O ganho com essa abordagem aparece principalmente quando uma página possui muitos campos. Assim não precisa ficar replicando os atributos da entidade no managed bean, e nem mesmo usar a própria entidade - e se ver obrigado a usar getters e setters que expõem aqueles atributos que não seriam interessante expor. Sem contar que essa forma abre espaço para usar reflection para automatizar a passagem dos valores dos atributos para a entidade correspondente.

Quaisquer sugestões ou dúvidas podem ser apresentadas na seção de comentários.

Até lá!

23/10/2009

Services, transações e proxies dinâmicos – parte I

Arquivado em: design patterns, java — tnaires @ 00:44

Introdução

No primeiro semestre deste ano cursei uma disciplina na faculdade onde o trabalho final consistia em escrever um software usando apenas Hibernate e JSF. Desenvolvi então um software de gestão de transporte de cargas, que poderia ser comercializado para empresas que fazem uso de uma frota de veículos para realizar transportes rodoviários.

Falando um pouco sobre sua arquitetura, ele dispõe de uma camada de aplicação composta de serviços que são requisitados pela camada de apresentação – nesse caso, pelos managed beans. O código cliente conhece apenas a interface desse serviço, expressa no código abaixo:

interface EntityService<T, ID extends Serializable> {
    void create(Map<String, Object> parameters);
    Collection<T> readAll();
    T read(ID id);
    Collection readByParameters(Map<String, Object> parameters);
    void update(Map<String, Object> parameters);
    void delete(T t);
}

Cada serviço da camada deve ser expresso através de uma interface que estenda EntityService. Por exemplo, o código abaixo corresponde ao serviço de cadastro de veículos.

public interface VeiculoService extends EntityService<Veiculo, String> {
    // Declaram-se aqui outras operações requeridas por esse serviço.
}

A implementação desse serviço deve conter o corpo para todos os métodos declarados nas duas interfaces. Note que a mesma não precisa ser pública.

class VeiculoServiceImpl implements VeiculoService {
    // Fornece implementações para todos os métodos.
}

Há desvantagens nessa abordagem. Nem sempre é conveniente disponibilizar todos os serviços declarados em EntityService para uma determinada entidade. Por exemplo, e se algum requisito do projeto exigisse que não fosse possível alterar veículos? O código abaixo ilustra uma solução possível.

class VeiculoServiceImpl implements VeiculoService {
    // Não é possível alterar veículos.
    public void update(Map<String, Object> parameters) {
        throw new UnsupportedOperationException("Operação não permitida.");
    }
}

Porém, a discussão do que seria melhor ou não não está no escopo desse artigo, embora fosse interessante continuá-la na seção de comentários.

Finalmente, a classe abaixo é usada pela camada de apresentação para requisitar os serviços.

public class ApplicationServiceFactory {
    public static VeiculoService getVeiculoService() {
        return new VeiculoServiceImpl();
    }
}

Contexto transacional

A abordagem acima é interessante, mas as operações de cada serviço não são executadas sob contexto transacional. Isto é, se tivermos uma operação que precise realizar duas ou mais instruções de persistência de dados, uma eventual falha ocorrida em uma dessas instruções não cancela as anteriores. Isso pode acarretar em estado inconsistente dos dados. Precisamos então aplicar uma solução que permita que as operações dos serviços executem sob contexto transacional.

Há várias soluções para o problema:

  • Usar Spring;
  • Escrever um aspecto;
  • Usar o padrão Proxy.

O objetivo deste artigo é explorar o padrão Proxy e a API de proxies dinâmicos do Java para aplicar um contexto transacional para todos os serviços da aplicação. Será o que desenvolveremos na parte 2.

Até lá!

06/10/2009

Passagem explícita de tipos para métodos genéricos em Java

Arquivado em: java — tnaires @ 17:03

Quando temos uma classe genérica em Java, precisamos sempre passar de forma explícita o tipo do parâmetro genérico. O uso da classe ArrayList<E> exemplifica isto.

List<Usuario> l = new ArrayList<Usuario>();

Aqui estamos indicando explicitamente que a nossa lista contém apenas instâncias da classe Usuario.

Problema

Quando criamos métodos genéricos, geralmente declaramos argumentos na assinatura do método que permitem ao compilador inferir o tipo genérico, sem que precisemos indicar de forma explícita. Como exemplo, temos o método Collections.copy() da API do Java que copia elementos de uma collection para outra. A declaração do método é:

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
    // Copia os elementos de src para dest,
}

Se usarmos o método da seguinte forma:

List<Usuario> lista1 = new ArrayList<Usuario>();
List<Usuario> lista2 = new ArrayList<Usuario>();
Collections.copy(lista1, lista2);

Damos condições para que o compilador deduza, a partir do tipo dos argumentos dest e src, que T representa instâncias da classe Usuario. Mas há situações em que isso não é possível, como no método abaixo:

public class FabricaDeListas {
    public static <T> List<T> criarNovaListaDeQualquerCoisa() {
        return new ArrayList<T>();
    }
}

Aqui o compilador não dispõe de argumentos no método para tentar inferir o tipo T. Precisamos passar de forma explícita, assim como fazemos com classes. Mas como fazer isso?

Solução

A página 86 do livro Java Precisely (2ª edição, 2005), na seção 21.8 – Generic Methods, indica a sintaxe da linguagem para resolver o problema:

Classe.<Tipo1, Tipo2, ..., TipoN>metodoEstatico(); // Para métodos estáticos
instancia.<Tipo1, Tipo2, ..., TipoN>metodo(); // Para métodos de instância.

Ou seja, tomando o exemplo anterior, podemos escrever:

List<Usuario> = FabicaDeListas.<Usuario>criarNovaListaDeQualquerCoisa();
List<Cachorro> = FabicaDeListas.<Cachorro>criarNovaListaDeQualquerCoisa();

E assim sucessivamente. Notem que é preciso indicar de forma explícita a entidade que invoca o método, seja uma classe ou um objeto.

Blog no WordPress.com.