Projeto Cliente Desktop Northwind (Criando os Projetos) - Parte 01 [Java]

12. abril 2012 00:15 by BBranquinho in Java, Maven, Spring  //  Tags: , ,   //   Comentários (0)

Boa noite pessoal.

Hoje começamos a tratar do exemplo de um cliente que consome os serviços disponibilizadas pelo servidor. Será estabelecida uma conexão RMI com o servidor e serviços de CRUD serão invocados.

1. Interface Gráfica

Interface gráfica do cliente Desktop criada com o Window Builder.

Usamos o plugin Window Builder para a construção de uma interface gráfica bem simples. Temos um JTable com os dados de Categoria, os campos com dados selecionados e os botões com os eventos de CRUD.

2. Estrutura do Projeto

Estrutura do projeto do cliente Desktop.

A estrutura do projeto segue a mesma forma do servidor.

Também é usado o Maven para o gerenciamento das dependências e o Spring na injeção de dependência.

São criados os módulos:

wpattern-northwind-application: Parte visual da aplicação baseado na API Swing.

wpattern-northwind-application-all: Agrupa todos os módulos.

wpattern-northwind-application-factory: Responsável por realizar a integração entre os módulos.

wpattern-northwind-application-service-client: Estabelece a comunicação RMI com o servidor.

wpattern-northwind-application-utils: Classes, interfaces e enumerations, de transporte de dados e integração dos módulos.

Um fato importante é que neste cliente existe a dependência do módulo Utils do servidor. Isso é necessário pois a interface de serviço do servidor está neste módulo.

3. Considerações Finais

O desenvolvimento desta estrutura segue os mesmos passos que foram demonstrados no servidor. No vídeo abaixo são mostrados passo a passo todo este processo.

Até mais, abraços.

Projeto Northwind (Desenvolvimento dos Serviços RMI e do Módulo Factory) - Parte 10 [Java]

7. abril 2012 03:25 by BBranquinho in Hibernate, Java, Northwind, Tutoriais  //  Tags: , ,   //   Comentários (5)

Aew pessoal.

Neste post irei tratar da criação dos serviços remotos utilizando RMI (Remote Method Invocation) e também do desenvolvimento do módulo de wpattern-northwind-factory.

O nosso serviço é composto pela interface IServices que está disponível no módulo wpattern-northwind-utils para que seja acessada por todos os módulos e por outros sistemas. A implementação do serviço é realizada no módulo wpattern-northwind-service na classe Services. Inicialmente temos apenas alguns serviços básicos. Com o desenvolvimento dos clientes serão criados novos serviços. Abaixo são apresentadas essas implementações.

package br.com.wpattern.northwind.utils.service.interfaces;

import java.util.List;

import br.com.wpattern.northwind.utils.database.beans.CategoriesAndSuppliersBean;
import br.com.wpattern.northwind.utils.database.beans.CategoryBean;

public interface IServices {

	public CategoriesAndSuppliersBean findCategoriesAndSuppliersByProductName(String productName);

	public List<CategoryBean> findAllCategories();

}
package br.com.wpattern.northwind.service;

import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;

import org.apache.log4j.Logger;

import br.com.wpattern.northwind.utils.database.beans.CategoriesAndSuppliersBean;
import br.com.wpattern.northwind.utils.database.beans.CategoryBean;
import br.com.wpattern.northwind.utils.database.interfaces.ICategoryService;
import br.com.wpattern.northwind.utils.database.interfaces.IProductService;
import br.com.wpattern.northwind.utils.service.interfaces.IServices;

@Named
public class Services implements IServices
{
	private final Logger logger = Logger.getLogger(getClass());

	@Inject
	private ICategoryService categoryService;

	@Inject
	private IProductService productService;

	@Override
	public List<CategoryBean> findAllCategories() {
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Service invoked - findAllCategories");
		}

		return this.categoryService.findAll();
	}

	@Override
	public CategoriesAndSuppliersBean findCategoriesAndSuppliersByProductName(String productName) {
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Service invoked - findCategoriesAndSuppliersByProductName");
		}

		return this.productService.findCategoriesAndSuppliersByProductName(productName);
	}

}

Para tornarem disponíveis os serviços é realizada a configuração do contexto do Spring Framework. Nesta configuração especificamos um arquivo de configurações e os parâmetros do serviços. Essa configuração é mostrada a seguir.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.1.xsd">

	<!-- Define o pacote inicial que é considerado por este contexto. -->
	<context:component-scan base-package="br.com.wpattern.northwind.service" />
	
	<bean id="remoteService" class="br.com.wpattern.northwind.service.Services" />
	
	<!-- Define o arquivo que é usado para buscar as informações de configuração. -->
	<bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
		<property name="location" value="file:./conf/northwind-conf.properties" />
	</bean>
	
	<bean name="remoteServiceBean" class="org.springframework.remoting.rmi.RmiServiceExporter">
		<property name="serviceName" value="remote-service" />
		<property name="service" ref="remoteService" />
		<property name="serviceInterface" value="br.com.wpattern.northwind.utils.service.interfaces.IServices" />
		<property name="registryPort" value="${remote.service.rmi.port}" />
	</bean>
	
</beans>

Para subir o nosso serviço é preciso criar o módulo wpattern-northwind-factory que será responsável por integrar todos os módulos e disponibilizar os serviços. Nós poderemos executar o servidor pelo Eclipse usando um teste unitário. Também será gerado um artefato (.jar) que ficará disponível para subir nossa aplicação externa ao Eclipse. Ambas as abordagens trabalham com uma classe FactoryDistribution que determina o processo de inciar nossa aplicação. Por sua vez, a classe principal usada pelo artefato .jar é chamada NorthwindMain. A implementação dessas classes é demostrada abaixo.

package br.com.wpattern.northwind.factory.interfaces;

public interface IFactoryDistribution {

	public void startApplication();

}
package br.com.wpattern.northwind.factory;

import javax.inject.Named;

import org.apache.log4j.Logger;

import br.com.wpattern.northwind.factory.interfaces.IFactoryDistribution;

@Named
public class FactoryDistribution implements IFactoryDistribution {

	private final Logger logger = Logger.getLogger(getClass());

	@Override
	public void startApplication() {
		if (this.logger.isInfoEnabled()) {
			this.logger.info("Northwind Application Started!");
		}
	}

}
package br.com.wpattern.northwind.factory;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import br.com.wpattern.northwind.factory.interfaces.IFactoryDistribution;

public class NorthwindMain {

	private static final String BUSINESS_CONTEXT = "/ctx-wpattern-northwind-business.xml";

	private static final String DATABASE_CONTEXT = "/ctx-wpattern-northwind-database.xml";

	private static final String FACTORY_CONTEXT = "/ctx-wpattern-northwind-factory.xml";

	private static final String SERVICE_CONTEXT = "/ctx-wpattern-northwind-service.xml";

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext(BUSINESS_CONTEXT, DATABASE_CONTEXT,
				FACTORY_CONTEXT, SERVICE_CONTEXT);

		IFactoryDistribution factoryDistribution = context.getBean(IFactoryDistribution.class);

		factoryDistribution.startApplication();
	}

}

Também temos o arquivo de configuração da aplicação northwind-conf.properties, como é exibido abaixo.

# Define a porta usada para abrir o serviço RMI.
remote.service.rmi.port=6681

O teste unitário trabalha com arquivos diferentes de configuração para os contextos do spring. Sendo assim, é preciso determinar estes arquivos durante a chamada do teste unitário. O AbstractFactoryTestBase especifica os contextos. Já a classe ServerDistributionTest possui o teste unitário que coloca em execução o servidor. Isso pode ser visto abaixo.

package br.com.wpattern.northwind.test.factory.base;

import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
		AbstractFactoryTestBase.BUSINESS_TEST_CONTEXT,
		AbstractFactoryTestBase.DATABASE_TEST_CONTEXT,
		AbstractFactoryTestBase.FACTORY_TEST_CONTEXT,
		AbstractFactoryTestBase.SERVICE_TEST_CONTEXT })
public class AbstractFactoryTestBase {

	public static final String BUSINESS_TEST_CONTEXT = "/ctx-wpattern-northwind-business.xml";

	public static final String DATABASE_TEST_CONTEXT = "/ctx-wpattern-northwind-database.xml";

	public static final String FACTORY_TEST_CONTEXT = "/ctx-wpattern-northwind-test-factory.xml";

	public static final String SERVICE_TEST_CONTEXT = "/ctx-wpattern-northwind-service-integration.xml";

}
package br.com.wpattern.northwind.test.factory;

import javax.inject.Inject;

import org.apache.log4j.Logger;
import org.junit.Test;

import br.com.wpattern.northwind.factory.interfaces.IFactoryDistribution;
import br.com.wpattern.northwind.test.factory.base.AbstractFactoryTestBase;

public class ServerDistributionTest extends AbstractFactoryTestBase {

	private static final Logger logger = Logger.getLogger(ServerDistributionTest.class);

	@Inject
	private IFactoryDistribution factoryDistribution;

	@Test
	public void startApplication() {
		this.factoryDistribution.startApplication();

		Object waitObject = new Object();

		synchronized (waitObject) {
			while (true) {
				try {
					waitObject.wait();
				} catch (InterruptedException e) {
					logger.error(e.getMessage(), e);
				} catch (Exception e) {
					logger.error(waitObject);
				}
			}
		}
	}

}

O único contexto que temos diferente é o do serviço em que colocada a busca do arquivo de configuração no classpath em vez de um arquivo local pelo file. Este arquivo é o ctx-wpattern-northwind-service-integration.xml e é mostrado a seguir.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.1.xsd">

	<!-- Define o pacote inicial que é considerado por este contexto. -->
	<context:component-scan base-package="br.com.wpattern.northwind.service" />
	
	<bean id="remoteService" class="br.com.wpattern.northwind.service.Services" />
	
	<!-- Define o arquivo que é usado para buscar as informações de configuração. -->
	<bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
		<property name="location" value="classpath:./conf/northwind-conf.properties" />
	</bean>
	
	<bean name="remoteServiceBean" class="org.springframework.remoting.rmi.RmiServiceExporter">
		<property name="serviceName" value="remote-service" />
		<property name="service" ref="remoteService" />
		<property name="serviceInterface" value="br.com.wpattern.northwind.utils.service.interfaces.IServices" />
		<property name="registryPort" value="${remote.service.rmi.port}" />
	</bean>
	
</beans>

Todo o trabalho é mostrado no vídeo.

Com isso finalizamos este post.

Até mais a todos.

Projeto Northwind (Desenvolvimento dos Serviços usando Spring) - Parte 08 [Java]

27. março 2012 02:45 by BBranquinho in Hibernate, Java, Northwind, Spring, Tutoriais  //  Tags: , , ,   //   Comentários (1)

Boa noite.

Neste post finalizamos o desenvolvimento do módulo Database. Foi realizado o desenvolvimento dos serviços com o auxílio dos abstracts GenericService e GenericServiceWithKey. Um serviço inicialmente trabalha com o CRUD, assim como temos nos DAOs. Contudo, os serviços são públicos para todo o sistema e tratam apenas de beans.

Para o desenvolvimento de um serviço é necessário apenas herdar um dos abstracts e implementar o método abstrato getConcreteDao() que é usado nos abstracts para acesso ao DAO básico que está relacionado ao serviço. Cada serviço está associado com uma interface, novamente, essa interface é nossa porta de comunicação com o sistema e ela permite a injeção de dependência usando o Spring. Abaixo é mostrado um exemplo de serviço, primeiro da interface e posteriormente a classe concreta.

package br.com.wpattern.northwind.utils.database.interfaces;

import br.com.wpattern.northwind.utils.database.beans.CustomerBean;

public interface ICustomerService extends IServiceBase<CustomerBean, Long> {

}
package br.com.wpattern.northwind.database.services;

import javax.inject.Inject;
import javax.inject.Named;

import br.com.wpattern.northwind.database.entities.CustomerEntity;
import br.com.wpattern.northwind.database.interfaces.ICustomerDao;
import br.com.wpattern.northwind.database.utils.GenericService;
import br.com.wpattern.northwind.database.utils.interfaces.IGenericDao;
import br.com.wpattern.northwind.utils.database.beans.CustomerBean;
import br.com.wpattern.northwind.utils.database.interfaces.ICustomerService;

@Named
public class CustomerService extends GenericService<CustomerBean, CustomerEntity, Long> implements ICustomerService {

	@Inject
	private ICustomerDao customerDao;

	@Override
	protected IGenericDao<CustomerEntity, Long> getConcreteDao() {
		return this.customerDao;
	}

}

Para os testes dos serviços foram desenvolvidos testes unitários apenas para determinar se os dados eram recuperados. Estes testes estão disponíveis na classe DatabaseServiceTest, exibida a seguir.

package br.com.wpattern.northwind.test.database;

import java.util.List;

import javax.inject.Inject;

import org.apache.log4j.Logger;
import org.junit.Test;

import br.com.wpattern.northwind.test.database.base.AbstractDatabaseTestBase;
import br.com.wpattern.northwind.utils.database.beans.CategoryBean;
import br.com.wpattern.northwind.utils.database.beans.CustomerBean;
import br.com.wpattern.northwind.utils.database.beans.CustomerCustomerDemoBean;
import br.com.wpattern.northwind.utils.database.beans.CustomerDemographicBean;
import br.com.wpattern.northwind.utils.database.beans.EmployeeBean;
import br.com.wpattern.northwind.utils.database.beans.EmployeeTerritoryBean;
import br.com.wpattern.northwind.utils.database.beans.OrderBean;
import br.com.wpattern.northwind.utils.database.beans.OrderDetailBean;
import br.com.wpattern.northwind.utils.database.beans.ProductBean;
import br.com.wpattern.northwind.utils.database.beans.RegionBean;
import br.com.wpattern.northwind.utils.database.beans.ShipperBean;
import br.com.wpattern.northwind.utils.database.beans.SupplierBean;
import br.com.wpattern.northwind.utils.database.beans.TerritoryBean;
import br.com.wpattern.northwind.utils.database.interfaces.ICategoryService;
import br.com.wpattern.northwind.utils.database.interfaces.ICustomerCustomerDemoService;
import br.com.wpattern.northwind.utils.database.interfaces.ICustomerDemographicService;
import br.com.wpattern.northwind.utils.database.interfaces.ICustomerService;
import br.com.wpattern.northwind.utils.database.interfaces.IEmployeeService;
import br.com.wpattern.northwind.utils.database.interfaces.IEmployeeTerritoryService;
import br.com.wpattern.northwind.utils.database.interfaces.IOrderDetailService;
import br.com.wpattern.northwind.utils.database.interfaces.IOrderService;
import br.com.wpattern.northwind.utils.database.interfaces.IProductService;
import br.com.wpattern.northwind.utils.database.interfaces.IRegionService;
import br.com.wpattern.northwind.utils.database.interfaces.IShipperService;
import br.com.wpattern.northwind.utils.database.interfaces.ISupplierService;
import br.com.wpattern.northwind.utils.database.interfaces.ITerritoryService;

public class DatabaseServiceTest extends AbstractDatabaseTestBase {

	private final Logger logger = Logger.getLogger(getClass());

	@Inject
	private ICategoryService categoryService;

	@Inject
	private ICustomerCustomerDemoService customerCustomerDemoService;

	@Inject
	private ICustomerDemographicService customerDemographicService;

	@Inject
	private ICustomerService customerService;

	@Inject
	private IEmployeeService employeeService;

	@Inject
	private IEmployeeTerritoryService employeeTerritoryService;

	@Inject
	private IOrderService orderService;

	@Inject
	private IOrderDetailService orderDetailService;

	@Inject
	private IProductService productService;

	@Inject
	private IRegionService regionService;

	@Inject
	private IShipperService shipperService;

	@Inject
	private ISupplierService supplierService;

	@Inject
	private ITerritoryService territoryService;

	@Test
	public void testCategoryService() {
		List<CategoryBean> categories = this.categoryService.findAll();

		this.logger.debug(categories);
	}

	@Test
	public void testCustomerCustomerDemoService() {
		List<CustomerCustomerDemoBean> customerCustomerDemos = this.customerCustomerDemoService.findAll();

		this.logger.debug(customerCustomerDemos);
	}

	@Test
	public void testCustomerDemographicService() {
		List<CustomerDemographicBean> customerDemographics = this.customerDemographicService.findAll();

		this.logger.debug(customerDemographics);
	}

	@Test
	public void testCustomerService() {
		List<CustomerBean> customers = this.customerService.findAll();

		this.logger.debug(customers);
	}

	@Test
	public void testEmployeeService() {
		List<EmployeeBean> employees = this.employeeService.findAll();

		this.logger.debug(employees);
	}

	@Test
	public void testEmployeeTerritoryService() {
		List<EmployeeTerritoryBean> employeeTerritories = this.employeeTerritoryService.findAll();

		this.logger.debug(employeeTerritories);
	}

	@Test
	public void testOrderService() {
		List<OrderBean> orders = this.orderService.findAll();

		this.logger.debug(orders);
	}

	@Test
	public void testOrderDetailService() {
		List<OrderDetailBean> orderDetails = this.orderDetailService.findAll();

		this.logger.debug(orderDetails);
	}

	@Test
	public void testProductService() {
		List<ProductBean> products = this.productService.findAll();

		this.logger.debug(products);
	}

	@Test
	public void testRegionService() {
		List<RegionBean> regions = this.regionService.findAll();

		this.logger.debug(regions);
	}

	@Test
	public void testShipperService() {
		List<ShipperBean> shippers = this.shipperService.findAll();

		this.logger.debug(shippers);
	}

	@Test
	public void testSupplierService() {
		List<SupplierBean> suppliers = this.supplierService.findAll();

		this.logger.debug(suppliers);
	}

	@Test
	public void testTerritoryService() {
		List<TerritoryBean> territories = this.territoryService.findAll();

		this.logger.debug(territories);
	}

}

Além disso, criamos uma classe com o padrão Factory para recuperamos os DAOs. Este Factory é interessante em um outro módulo que o processo de injeção seja diferente. Este Factory está presente na classe ServiceFactory implementando a interface IServiceFactory. Tais implementações estão disponíveis abaixo.

package br.com.wpattern.northwind.utils.database.interfaces;

public interface IDatabaseService {

	public ICategoryService getCategoryService();

	public ICustomerService getCustomerService();

	public ICustomerCustomerDemoService getCustomerCustomerDemoService();

	public ICustomerDemographicService getCustomerDemographicService();

	public IEmployeeService getEmployeeService();

	public IEmployeeTerritoryService getEmployeeTerritoryService();

	public IOrderService getOrderService();

	public IOrderDetailService getOrderDetailService();

	public IProductService getProductService();

	public IRegionService getRegionService();

	public IShipperService getShipperService();

	public ISupplierService getSupplierService();

	public ITerritoryService getTerritoryService();

}
package br.com.wpattern.northwind.database;

import javax.inject.Inject;
import javax.inject.Named;

import br.com.wpattern.northwind.utils.database.interfaces.ICategoryService;
import br.com.wpattern.northwind.utils.database.interfaces.ICustomerCustomerDemoService;
import br.com.wpattern.northwind.utils.database.interfaces.ICustomerDemographicService;
import br.com.wpattern.northwind.utils.database.interfaces.ICustomerService;
import br.com.wpattern.northwind.utils.database.interfaces.IDatabaseService;
import br.com.wpattern.northwind.utils.database.interfaces.IEmployeeService;
import br.com.wpattern.northwind.utils.database.interfaces.IEmployeeTerritoryService;
import br.com.wpattern.northwind.utils.database.interfaces.IOrderDetailService;
import br.com.wpattern.northwind.utils.database.interfaces.IOrderService;
import br.com.wpattern.northwind.utils.database.interfaces.IProductService;
import br.com.wpattern.northwind.utils.database.interfaces.IRegionService;
import br.com.wpattern.northwind.utils.database.interfaces.IShipperService;
import br.com.wpattern.northwind.utils.database.interfaces.ISupplierService;
import br.com.wpattern.northwind.utils.database.interfaces.ITerritoryService;

@Named
public class ServiceFactory implements IDatabaseService {

	@Inject
	private ICategoryService categoryService;

	@Inject
	private ICustomerCustomerDemoService customerCustomerDemoService;

	@Inject
	private ICustomerService customerService;

	@Inject
	private ICustomerDemographicService customerDemographicService;

	@Inject
	private IEmployeeService employeeService;

	@Inject
	private IEmployeeTerritoryService employeeTerritoryService;

	@Inject
	private IOrderService orderService;

	@Inject
	private IOrderDetailService orderDetailService;

	@Inject
	private IProductService productService;

	@Inject
	private IRegionService regionService;

	@Inject
	private IShipperService shipperService;

	@Inject
	private ISupplierService supplierService;

	@Inject
	private ITerritoryService territoryService;

	@Override
	public ICategoryService getCategoryService() {
		return this.categoryService;
	}

	@Override
	public ICustomerService getCustomerService() {
		return this.customerService;
	}

	@Override
	public ICustomerCustomerDemoService getCustomerCustomerDemoService() {
		return this.customerCustomerDemoService;
	}

	@Override
	public ICustomerDemographicService getCustomerDemographicService() {
		return this.customerDemographicService;
	}

	@Override
	public IEmployeeService getEmployeeService() {
		return this.employeeService;
	}

	@Override
	public IEmployeeTerritoryService getEmployeeTerritoryService() {
		return this.employeeTerritoryService;
	}

	@Override
	public IOrderService getOrderService() {
		return this.orderService;
	}

	@Override
	public IOrderDetailService getOrderDetailService() {
		return this.orderDetailService;
	}

	@Override
	public IProductService getProductService() {
		return this.productService;
	}

	@Override
	public IRegionService getRegionService() {
		return this.regionService;
	}

	@Override
	public IShipperService getShipperService() {
		return this.shipperService;
	}

	@Override
	public ISupplierService getSupplierService() {
		return this.supplierService;
	}

	@Override
	public ITerritoryService getTerritoryService() {
		return this.territoryService;
	}

}

Com este desenvolvimento finalizamos o módulo Database. O vídeo a seguir mostram estes passos que foram desenvolvidos.

Nos próximos posts irei começar o desenvolvimento dos outros módulos.

Até mais e boa noite :D

Criando um Serviço Genérico de Banco de Dados e Mapeamento Automático entre Entidades e Bean (Java)

24. fevereiro 2012 22:19 by BBranquinho in Java, Reflection  //  Tags: , , ,   //   Comentários (0)


Olá pessoal.

Apesar de já ter muito tempo que não público artigos, passei este período trabalhando em uma aplicação em Java e outra em .NET para postar no blog e ajudar meus alunos. Hoje irei escrever um pouco de uma parte do sistema em Java que estou trabalhando. Futuramente irei postar mais informações no blog, incluindo vídeos, diagramas e os códigos.

O código que irei mostrar é uma melhoria do tutorial de Introdução ao Hibernate. O objetivo é tornar os serviços mais simples de serem implementados e realizar o parser entre Entidades e Beans e vice-versa. Para realizar o parser é utilizada a biblioteca Dozer. Essa biblioteca é muito poderosa, permitindo a configuração de como é realizado o parser. Além disso, ela trata de beans complexos e é fácil de ser utilizada. Para adicionar essa biblioteca utilizando o maven temos o dependency:

<dependency>
	<groupId>net.sf.dozer</groupId>
	<artifactId>dozer</artifactId>
	<version>5.3.2</version>
</dependency>

Porém, com a adição dessa dependência ocorreram conflitos com a slf4j-log4j12. Sendo assim, para o meu cenário foi necessário remover as dependências da SLF4J. Sendo assim, temos

<dependency>
	<groupId>net.sf.dozer</groupId>
	<artifactId>dozer</artifactId>
	<version>5.3.2</version>
	<exclusions>
		<exclusion>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
		</exclusion>
		<exclusion>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-jdk14</artifactId>
		</exclusion>
	</exclusions>
</dependency>

Utilizando a Dozer o parser de objetos mais simples é realizado especificando o objeto que possui os valores e o tipo de destino que será feita a conversão. Uma vez que foi realizado o parser é retornado o objeto do tipo de destino. O código a seguir mostra um exemplo do método de parser de beans para entidades.

private static final Mapper dozerMapper = new DozerBeanMapper();

public static <S extends BaseBean, D extends BaseEntity> D parserBeanToEntity(S sourceObject, Class<D> destinationType) {
	return dozerMapper.map(sourceObject, destinationType);
}

Para realizar o parser em geral de entidades, beans e keys, temos:

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.dozer.DozerBeanMapper;
import org.dozer.Mapper;

import br.com.wpattern.northwind.utils.beans.BaseBean;

public class ParserDatabase {

	private static final Mapper dozerMapper = new DozerBeanMapper();

	public static <S extends BaseBean, D extends BaseEntity> D parserBeanToEntity(S sourceObject, Class<D> destinationType) {
		return dozerMapper.map(sourceObject, destinationType);
	}

	public static <S extends BaseBean, D extends BaseEntity> List<D> parserListBeansToEntities(List<S> sourceObjects, Class<D> destinationType) {
		List<D> destinationObjects = new ArrayList<D>();

		for (Object obj : sourceObjects) {
			destinationObjects.add(dozerMapper.map(obj, destinationType));
		}

		return destinationObjects;
	}

	public static <S extends BaseEntity, D extends BaseBean> D parserEntityToBean(S sourceObject, Class<D> destinationType) {
		return dozerMapper.map(sourceObject, destinationType);
	}

	public static <S extends BaseEntity, D extends BaseBean> List<D> parserListEntitiesToBeans(List<S> sourceObjects, Class<D> destinationType) {
		List<D> destinationObjects = new ArrayList<D>();

		for (Object obj : sourceObjects) {
			destinationObjects.add(dozerMapper.map(obj, destinationType));
		}

		return destinationObjects;
	}

	public static <IDB extends Serializable, IDE extends Serializable> IDE parserKeyBeanToKeyEntity(IDB sourceObject, Class<IDE> destinationType) {
		return dozerMapper.map(sourceObject, destinationType);
	}

	public static <IDE extends Serializable, IDB extends Serializable> IDB parserKeyEntityToKeyBean(IDE sourceObject, Class<IDB> destinationType) {
		return dozerMapper.map(sourceObject, destinationType);
	}

}

Com a classe ParserDatabase conseguimos realizar o parser em geral dos objetos usados em um módulo de acesso ao banco de dados e serviços.

Além do ParserDatabase, implementei uma classe abstrata para tornar o desenvolvimento de serviços básicos mais simples. Nem sempre um serviço utilizara esses métodos de "CRUD", porém, essa classe deixa o trabalho de criação destes serviços muito simples. Inicialmente, temos a classe GenericService que possui alguns métodos otimizados quando o identificador de uma entidade é um tipo de dado básico (em geral é um Long). Está classe é mostrada a seguir.

import java.io.Serializable;
import java.util.List;

import br.com.wpattern.northwind.utils.beans.BaseBean;
import br.com.wpattern.northwind.utils.database.interfaces.IServiceBase;

public abstract class GenericService<T extends BaseBean, K extends BaseEntity, ID extends Serializable>
extends GenericServiceWithKey<T, K, ID, ID> implements IServiceBase<T, ID> {

	/////////////////////////////////////////////////////////////////////////////////
	// PUBLIC METHODS (IServiceBase)
	/////////////////////////////////////////////////////////////////////////////////

	@Override
	public T findById(ID id) {
		return parserEntity(this.getConcreteDao().findById(id));
	}

	@Override
	public List<T> findAll() {
		return parserEntity(this.getConcreteDao().findAll());
	}

	@Override
	public ID insert(T bean) {
		return getConcreteDao().save(parserBean(bean));
	}

	@Override
	public void update(T bean) {
		this.getConcreteDao().update(parserBean(bean));
	}

	@Override
	public void delete(T bean) {
		this.getConcreteDao().delete(parserBean(bean));
	}

}

Na GenericService especificamos o tipo da entidade e o bean correspondente. Além disso, definimos o tipo da chave usada pela entidade. É interessante observar que T estende BaseBean e K estende BaseEntity. Com isso evitamos possíveis erros quando as classes de serviço herdarem este abstract. Abaixo apresento a classe GenericServiceWithKey que é herdada pela GenericService. A classe GenericServiceWithKey trabalha da mesma forma que GenericService. Contudo, GenericServiceWithKey trata de chaves compostas de entidades. A seguir mostro a implementação da classe GenericServiceWithKey

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import org.apache.log4j.Logger;

import br.com.wpattern.northwind.database.utils.interfaces.IGenericDao;
import br.com.wpattern.northwind.utils.beans.BaseBean;
import br.com.wpattern.northwind.utils.database.interfaces.IServiceBase;

public abstract class GenericServiceWithKey<T extends BaseBean, K extends BaseEntity, IDE extends Serializable, IDB extends Serializable> implements IServiceBase<T, IDB> {

	private final Logger logger = Logger.getLogger(this.getClass());

	private final Class<T> typeOfBean;

	private final Class<K> typeOfEntity;

	private final Class<IDE> typeOfKeyEntity;

	private final Class<IDB> typeOfKeyBean;

	/////////////////////////////////////////////////////////////////////////////////
	// CONSTRUCTORS
	/////////////////////////////////////////////////////////////////////////////////

	@SuppressWarnings("unchecked")
	public GenericServiceWithKey() {
		try {
			ParameterizedType parameterizedType = (ParameterizedType)getClass().getGenericSuperclass();
			Type[] genericTypes = parameterizedType.getActualTypeArguments();

			this.typeOfBean = (Class<T>)genericTypes[0];
			this.typeOfEntity = (Class<K>)genericTypes[1];
			this.typeOfKeyEntity = (Class<IDE>)genericTypes[2];
			this.typeOfKeyBean = (Class<IDB>)genericTypes[(genericTypes.length == 4) ? 3 : 2];
		} catch (RuntimeException e) {
			this.logger.error(e.getMessage(), e);
			// Re-throw the exception.
			throw e;
		}

		if (this.logger.isInfoEnabled()) {
			this.logger.info(String.format("Creating a service with the bean [%s], entity [%s], key bean [%s] and key entity [%s].",
					getTypeOfBean(), getTypeOfEntity(), getTypeOfKeyBean(), getTypeOfKeyEntity()));
		}
	}

	/////////////////////////////////////////////////////////////////////////////////
	// ABSTRACT METHODS
	/////////////////////////////////////////////////////////////////////////////////

	protected abstract IGenericDao<K, IDE> getConcreteDao();

	/////////////////////////////////////////////////////////////////////////////////
	// PUBLIC METHODS (IServiceBase)
	/////////////////////////////////////////////////////////////////////////////////

	@Override
	public T findById(IDB id) {
		return parserEntity(this.getConcreteDao().findById(parserKeyBean(id)));
	}

	@Override
	public List<T> findAll() {
		return parserEntity(this.getConcreteDao().findAll());
	}

	@Override
	public IDB insert(T bean) {
		return parserKeyEntity(getConcreteDao().save(parserBean(bean)));
	}

	@Override
	public void update(T bean) {
		this.getConcreteDao().update(parserBean(bean));
	}

	@Override
	public void delete(T bean) {
		this.getConcreteDao().delete(parserBean(bean));
	}

	/////////////////////////////////////////////////////////////////////////////////
	// PUBLIC METHODS (GET)
	/////////////////////////////////////////////////////////////////////////////////

	public final Class<T> getTypeOfBean() {
		return this.typeOfBean;
	}

	public final Class<K> getTypeOfEntity() {
		return this.typeOfEntity;
	}

	public final Class<IDE> getTypeOfKeyEntity() {
		return this.typeOfKeyEntity;
	}

	public final Class<IDB> getTypeOfKeyBean() {
		return this.typeOfKeyBean;
	}

	/////////////////////////////////////////////////////////////////////////////////
	// PUBLIC METHODS
	/////////////////////////////////////////////////////////////////////////////////

	public final T parserEntity(K entity) {
		return ParserDatabase.parserEntityToBean(entity, getTypeOfBean());
	}

	public final K parserBean(T bean) {
		return ParserDatabase.parserBeanToEntity(bean, getTypeOfEntity());
	}

	public final IDB parserKeyEntity(IDE idEntity) {
		return ParserDatabase.parserKeyEntityToKeyBean(idEntity, getTypeOfKeyBean());
	}

	public final IDE parserKeyBean(IDB idBean) {
		return ParserDatabase.parserKeyBeanToKeyEntity(idBean, getTypeOfKeyEntity());
	}

	public final List<T> parserEntity(List<K> entities) {
		return ParserDatabase.parserListEntitiesToBeans(entities, getTypeOfBean());
	}

	public final List<K> parserBean(List<T> beans) {
		return ParserDatabase.parserListBeansToEntities(beans, getTypeOfEntity());
	}

}

Entretanto, existe um problema na implementação da GenericServiceWithKey. Na chamada por reflexão da super classe não necessariamente teremos um classe GenericService ou GenericServiceWithKey. Sendo assim, é preciso que recursivamente seja(m) recuperada(s) a(s) super(s) classe(s), e assim sejam extraídos os tipos genéricos. Em um próximo post irei falar sobre a solução deste problema. 

A partir dessas classes podemos criar os serviços. A seguir apresento um exemplo de serviço desenvolvida seguindo estes conceitos.

import javax.inject.Inject;
import javax.inject.Named;

import br.com.wpattern.northwind.database.entities.CustomerCustomerDemoEntity;
import br.com.wpattern.northwind.database.entities.keys.CustomerCustomerDemoKeyEntity;
import br.com.wpattern.northwind.database.interfaces.ICustomerCustomerDemoDao;
import br.com.wpattern.northwind.database.utils.GenericServiceWithKey;
import br.com.wpattern.northwind.database.utils.interfaces.IGenericDao;
import br.com.wpattern.northwind.utils.database.beans.CustomerCustomerDemoBean;
import br.com.wpattern.northwind.utils.database.interfaces.ICustomerCustomerDemoService;
import br.com.wpattern.northwind.utils.database.keys.CustomerCustomerDemoKeyBean;

@Named
public class CustomerCustomerDemoService extends GenericServiceWithKey<CustomerCustomerDemoBean, CustomerCustomerDemoEntity, CustomerCustomerDemoKeyBean, CustomerCustomerDemoKeyEntity>
implements ICustomerCustomerDemoService {

	@Inject
	private ICustomerCustomerDemoDao customerCustomerDemoDao;

	@Override
	protected IGenericDao<CustomerCustomerDemoEntity, CustomerCustomerDemoKeyEntity> getConcreteDao() {
		return this.customerCustomerDemoDao;
	}

}

Um melhoria que estou planejando de fazer é de recuperar no próprio abstract a instância do DAO. O código completo do projeto que estou trabalhando pode ser encontrada em wpattern.codeplex.com (sample projects => java => wpattern-northwind-sample => wpattern-northwind-all).

Nos próximos posts irei explicar em detalhes este sistema.

Até mais a todos.

WPattern

O objetivo deste blog é de disponibilizar os resultados dos meus estudos. Não será tratada apenas uma tecnologia ou área, mas qualquer tópico que seja interessante e possa contribuir com a comunidade de desenvolvedores, comunidade científica e meus alunos. Entretanto, meus trabalhos sempre estão voltado principalmente para as tecnologias .NET, Java e C/C++.

Posts Recentes

Google Translator

JetBrains

JetBrains: Ferramentas de desenvolvimento com .NET.

ReSharper: Excelente ferramenta para .NET.