Академический Документы
Профессиональный Документы
Культура Документы
Version 1.13.6
6.Usodeexceções ......................................................................................................................
6.1.Introdução ....................................................................................................................39
6.2.Usodeexceções ............................................................................................................39
6.2.1. O uso de BusinessException ................................................................................40
6.2.2. O uso de SystemException ..................................................................................41
6.3. Tratamento de exceções em serviços .............................................................................. 41
6.3.1. Controle transacional e exceções .........................................................................42
6.4. Boas práticas no uso de exceções ................................................................................... 42
6.4.1.Tratandoexceções. .............................................................................................43
6.4.2.Lançandoexceções. ............................................................................................43
6.4.3. Definindo classes de exceção ..............................................................................43
7.Persistência ............................................................................................................................
7.1.Introdução ....................................................................................................................45
7.2. DAO - Data Access Object ............................................................................................ 45
7.2.1.JPA ...................................................................................................................46
7.2.2.JDBC ................................................................................................................50
7.2.3.CriandoumDAO ...............................................................................................52
7.3.Transações....................................................................................................................53
7.4. Configuração de banco de dados .................................................................................... 53
7.4.1.Configuraçõesdodatasource ...............................................................................53
7.4.2.ConfiguraçãodoJPA ..........................................................................................54
7.4.3. Como adicionar um perisistence unit no arquivo data_access.xml .......................... 55
7.4.4. Como adicionar um segundo ou mais persistence units no arquivo data_access.xml.
....................................................................................................................................
56
7.4.5.UtilizandováriosDataSources/PersistenceUnits ...................................................57
8.Mensageria.............................................................................................................................
8.1.Introdução ....................................................................................................................59
8.2.JMS .............................................................................................................................59
8.2.1.ConfiguraçãodoJMS .........................................................................................59
8.2.2. Configuração do JMS (Fora do Websphere) ......................................................... 60
8.2.3.AbstractMessagingGateway ................................................................................61
8.2.4.Modosíncrono ...................................................................................................64
8.2.5.Modoassíncrono ................................................................................................66
8.3.MDB............................................................................................................................69
8.4.ArquiteturaEstendidaAltair ..........................................................................................70
8.4.1. Mapeamento para o formato de Request (Envio) ................................................... 71
8.4.2. Mapeamento para o formato de Response (Retorno) ............................................. 73
8.4.3. Mapeamento de tipos de retorno .......................................................................... 73
8.4.4. Enviando mensagens para a Arquitetura Estendida Altair ...................................... 73
9.WebServices ..........................................................................................................................
9.1.Introdução ....................................................................................................................75
9.2. Exportando um serviço de negócio como web service ...................................................... 75
9.3. Gerando um cliente web service a partir do WSDL .......................................................... 80
9.4.Consumindowebservices ..............................................................................................81
9.5. Expondo serviços para serem consumidos pelo WebCon ................................................. 83
10.Segurança.............................................................................................................................
10.1.Introdução ..................................................................................................................84
10.2.Autenticação ...............................................................................................................84
10.2.1. Configuração da autenticação para diferentes runningModes. .............................. 84
10.2.2.AuthFilter ........................................................................................................85
10.2.3.SmartAuthFilter................................................................................................86
10.3.Autorização ................................................................................................................87
14.2.4.InteroperabilidadecomJDK ..............................................................................140
14.3.Miscelânea..................................................................................................................140
14.3.1. A biblioteca Commons Lang .............................................................................140
14.3.2. A biblioteca Commons Collections ....................................................................143
14.4.Validadores.................................................................................................................145
14.4.1.Criandoumvalidador .......................................................................................147
14.5.Conversores ................................................................................................................147
14.5.1.Criandoumconversor .......................................................................................149
14.6.Manipulaçãodearquivos .............................................................................................149
14.6.1.CommonsIO ....................................................................................................150
14.6.2.Commonscodec ...............................................................................................151
14.6.3. Manipulação de arquivos formatados .................................................................153
14.7.ManipulaçãodeXML ..................................................................................................153
14.7.1.DOM ...............................................................................................................153
14.7.2.SAX ................................................................................................................154
14.8.Enviodee-mail ...........................................................................................................155
15.Gerenciamentoemonitoração ..............................................................................................
15.1.Introdução ..................................................................................................................157
15.1.1.JMX ................................................................................................................157
15.2.Gerenciamentopadrão .................................................................................................157
15.2.1.Monitoraçãodeserviços....................................................................................157
15.3.Monitoraçãodeversão .................................................................................................159
15.4. Exportando informações para gerenciamento ................................................................160
15.5. Uso do wsadmin com MBeans ..................................................................................... 161
16.TestesUnitários ....................................................................................................................
16.1.Introdução ..................................................................................................................162
16.2.Objetivo......................................................................................................................162
16.3.Benefícios...................................................................................................................162
16.4. Como criar testes unitários ...........................................................................................162
III.AplicaçãodeReferência .................................................................................................................
17. A aplicação de referência .....................................................................................................
17.1.Introdução ..................................................................................................................168
17.2.ABC-CDOnline..........................................................................................................168
17.2.1.ABC-Online.....................................................................................................168
17.2.2.ABC-Admin.....................................................................................................170
18.Arquiteturaeimplementação ...............................................................................................
18.1.Introdução ..................................................................................................................172
18.2.ABCCDOnline ..........................................................................................................172
18.2.1.CamadadePersistência .....................................................................................172
18.2.2.CamadadeServiço ...........................................................................................173
18.2.3.CamadaWeb ....................................................................................................175
18.3.ABC-Admin ...............................................................................................................177
18.3.1.CamadadePersistência .....................................................................................177
18.3.2.CamadadeServiço ...........................................................................................178
18.3.3.CamadaWeb ....................................................................................................180
18.4. Aplicações Batch ABC CD Online ............................................................................... 181
18.4.1.TratamentodePedido .......................................................................................181
18.4.2. Processamento Batch para Cds ..........................................................................183
A.Referências.....................................................................................................................................
A.1.Referências ..........................................................................................................................189
(1.13.6)
Parte I. Introdução
O Framework Java Arquitetura Brasil tem como objetivo padronizar o desenvolvimento de aplicações Java em
plataforma distribuída, garantindo um nível de performance, segurança, conectividade, produtividade e
reusabilidade de componentes das aplicações desenvolvidas pela Isban. Definindo uma arquitetura padrão,
podemos obter maior qualidade do software.
Esta seção apresenta os conceitos usados pelo Framework e introduz a arquitetura adotada para a construção de
aplicações.
• Capítulo 1. Pré-requisitos
• Capítulo 2. Arquitetura
• JDBC - http://java.sun.com/j2se/1.5.0/docs/guide/jdbc/
• JPA/Hibernate - http://java.sun.com/javaee/technologies/persistence.jsp
• JasperReports - http://jasperforge.org/plugins/project/project_home.php?group_id=102
2.1. Introdução
O Framework foi construído sobre os padrões Java Standard Edition 5 e Java 2 Enterprise Edition 1.4,
além de uma série de produtos de mercado que implementam funcionalidades adicionais.
Ele inclui um conjunto de bibliotecas e de diretrizes para a construção de aplicações baseadas nele.
O cenário planejado de uso do framework é dentro de um servidor de aplicações. Neste cenário o sistema que
faz uso do framework é um EAR instalado em um servidor de aplicações, e pode utilizar todas as APIs da
especificação J2EE, e as facilidades oferecidas pelo servidor de aplicações.
O ambiente padrão, que serve de base para o Framework é o WebSphere 6.1 rodando em servidor Intel com
sistema operacional Windows.
No entanto, o Framework não tem dependências internas com o WebSphere, podendo ser usado em servidores
de aplicação de outros fabricantes. Mas como o WebSphere 6.1 é o padrão atual, muitas funcionalidades já
estão pré-configuradas para esse ambiente. A figura abaixo mostra esse cenário:
Também é possível usar o Framework em outos cenários, de acordo com as necessidades da aplicação. Sendo o
Framework modular, o princípio básico de construção da aplicação será o mesmo, sendo seus módulos
substituídos ou desativados de acordo com a situação. Apenas o conjunto de funcionalidades e integrações
disponíveis em cada cenário será diferente.
Atenção
Vale lembrar que qualquer solução diferente do cenário com o WebSphere, não é homologada e
não existe infra-estrutura para sua execução, precisando ser analisada sob as óticas de arquitetura,
segurança e produção.
• Camada de Apresentação
• Camada de Negócio
Abaixo da camada de negócios, podem existir várias camadas, nomeadas genericamente de “camadas de
integração“. As camadas de persistência, mensagens e web services, são exemplos concretos de integração, que
serão providas pelo Framework.
A camada de aplicações web pode ser opcional, quando o sistema tiver apenas serviços de negócio, sem
nenhuma interface visual integrada. Neste caso, os clientes acessarão diretamente a camada de negócios.
O Framework foi implementado usando o Spring para integrar todos esses componentes em torno da camada de
serviços, simplificando o modelo de desenvolvimento.
Package Descrição
Package Descrição
Nota
Note que nem sempre todos esses pacotes serão necessários, podendo ser omitidos se for o caso. As
classes referentes a serviços e DAOs estarão presentes em praticamente todas aplicações.
A figura abaixo mostra um diagrama conceitual dos principais componentes presentes numa aplicação.
(1.13.6)
Arquitetura
A camada de apresentação usa o modelo MVC, sendo a página JSF o "view", o backing bean o "controller" e as
entidades ou DTOs o "model".
(1.13.6)
Arquitetura
Arquivo Descrição
Arquivo Descrição
Nota
Note que nem sempre todos esses arquivos de configuração serão necessários, podendo ser
omitidos se for o caso. O arquivo application.properties é essencial, pois ele é usado pelo
processo de build.
Para resolver tais problemas, foi usado o conceito de running mode, que permite determinar qual ambiente a
aplicação está sendo executada, e usar configurações distintas para cada um desses modos. Um running mode é
definido como uma chave de texto única, usada para identificar uma configuração possível.
Podemos definir tantos running modes quanto necessários, por exemplo: dev, pi, hml, prod, meu_ambiente, etc.
• um indicando qual o tipo de servidor em uso (WebSphere, Geronimo) que é definido automaticamente pelo
Framework.
• um indicando qual ambiente em uso (prod, dev, etc), definido externamente pelo administrador.
Dica
Esses running modes são usados para guardar configurações específicas de cada ambiente. As
aplicações são encorajadas a usar a mesma estratégia para separar qualquer tipo de configuração
que dependa de ambiente. Exemplos: o data source é obtido de uma maneira rodando no
WebSphere e de outra no Geronimo; a URL de um web service pode ser distinta nos ambientes de
desenvolvimento, homologação e produção.
1. Determina automaticamente o tipo de servidor em uso. Ativa um running mode com o valor websphere,
geronimo, batch ou unknown.
2. Se houver uma system property chamada runningMode, esse running mode é ativado.
Por exemplo, o servidor WebSphere do ambiente de produção deve ser configurado com o parâmetro da JVM:
-DrunningMode=prod
(1.13.6)
Capítulo 3. O ambiente de desenvolvimento
fw-1.13.6-dist\lib:
bundle:
fw-1.13.6.jar
compile:
apache-commons\commons-beanutils-1.8.2.jar
apache-commons\commons-codec-1.3.jar
apache-commons\commons-collections-3.2.1.jar
apache-commons\commons-digester-1.8.jar
apache-commons\commons-httpclient-3.1.jar
apache-commons\commons-io-1.4.jar
apache-commons\commons-lang-2.4.jar
apache-commons\commons-pool-1.3.jar
ehcache\backport-util-concurrent.jar
ehcache\ehcache-1.5.0.jar
ehcache\jsr107cache-1.0.jar
hibernate\antlr-2.7.6.jar
hibernate\dom4j-1.6.1.jar
hibernate\hibernate-annotations-3.4.0.GA.jar
hibernate\hibernate-commons-annotations-3.1.0.GA.jar
hibernate\hibernate-entitymanager-3.4.0.GA.jar
hibernate\hibernate-validator-3.1.0.CR2.jar
hibernate\hibernate3-3.3.1.GA.jar
hibernate\javassist-3.4.GA.jar
hsqldb\hsqldb.jar
jamon\fdsapi-1.2.jar
jamon\jamon-2.7.jar
jasper\iText-2.1.3.jar
jasper\jasperreports-3.0.1.jar
jasper\jcommon-1.0.13.jar
jasper\jfreechart-1.0.10.jar
jasper\poi-3.0.1-FINAL-20070705.jar
jasypt\icu4j-3.8.jar
jasypt\jasypt-1.5.jar
joda-time\joda-time-1.6.jar
jsf\commons-fileupload-1.3.jar
jsf\el-api.jar
jsf\el-ri.jar
jsf\icefaces-1.7.2-SP2.jar
jsf\icefaces-comps-1.7.2-SP2.jar
jsf\icefaces-facelets-1.7.2-SP2.jar
jsf\krysalis-jCharts-1.0.0-alpha-1.jar
logging\jcl-over-slf4j-1.5.5.jar
logging\logback-classic-0.9.15.jar
logging\logback-core-0.9.15.jar
logging\slf4j-api-1.5.6.jar
mbs\MBS_Client.jar
mbs\axis.jar
mbs\commons-discovery-0.2.jar
mbs\jaxrpc.jar
mbs\saaj.jar
spring\asm-2.2.3.jar
spring\asm-commons-2.2.3.jar
spring\asm-util-2.2.3.jar
spring\aspectjrt.jar
spring\aspectjweaver.jar
spring\cglib-nodep-2.1_3.jar
spring\jakarta-oro-2.0.8.jar
spring\spring-2.5.6.SEC01.jar
spring\spring-ldap-1.2.1.jar
spring\spring-ldap-tiger-1.2.1.jar
spring\spring-modules-cache-0.9.jar
spring\spring-webmvc-2.5.6.SEC01.jar
sun-ldap\ldapbp-1.0.jar
tibco\ssoRMI.jar
provided:
apache-commons\commons-dbcp-1.2.2.jar
j2ee\activation.jar
j2ee\ejb-api-3.0.jar
j2ee\jms.jar
j2ee\jta.jar
j2ee\mail.jar
j2ee\servlet-api.jar
jaxws\FastInfoset.jar
jaxws\http.jar
jaxws\jaxb-api.jar
jaxws\jaxb-impl.jar
jaxws\jaxb-xjc.jar
jaxws\jaxws-api.jar
jaxws\jaxws-rt.jar
jaxws\jaxws-tools.jar
jaxws\jsr173_api.jar
jaxws\jsr181-api.jar
jaxws\jsr250-api.jar
jaxws\mimepull.jar
jaxws\resolver.jar
jaxws\saaj-api.jar
jaxws\saaj-impl.jar
jaxws\sjsxp.jar
jaxws\stax-ex.jar
jaxws\streambuffer.jar
jpa\ejb3-persistence.jar
jsf\jsf-api-1.1_02.jar
jsf\jsf-impl-1.1_02.jar
jsf\jstl.jar
jsf\standard-1.1.2.jar
junit\junit-4.4.jar
mbs\wsdl4j-1.5.1.jar
mq\com.ibm.mq.jar
mq\com.ibm.mqjms.jar
mq\connector.jar
mq\dhbcore.jar
oracle\ojdbc14.jar
tests:
dbunit\dbunit-2.3.0.jar
mockrunner-j2ee1.4\dependencies.txt
mockrunner-j2ee1.4\mockrunner-ejb.jar
mockrunner-j2ee1.4\mockrunner-jca.jar
mockrunner-j2ee1.4\mockrunner-jdbc.jar
mockrunner-j2ee1.4\mockrunner-jms.jar
mockrunner-j2ee1.4\mockrunner-servlet.jar
mockrunner-j2ee1.4\mockrunner-tag.jar
mockrunner-j2ee1.4\mockrunner.jar
mockrunner-jee5\dependencies.txt
mockrunner-jee5\mockrunner-ejb.jar
mockrunner-jee5\mockrunner.jar
spring-test\spring-test.jar
checkstyle:
Altec_CheckStyle.xml
Altec_CheckStyle_ImportControl.xml
Altec_CheckStyle_critical.xml
Isban_CheckStyle_ImportControl.xml
Isban_Checkstyle_Critical.xml
Isban_Checkstyle_ImportControl_Critical.xml
altec-checkstyle.xml
checkstyle-all-5.0.jar
checkstyle-altec.xsl
eclipse\AltecStyle.importorder
eclipse\AltecStyle_CleanUp.xml
eclipse\AltecStyle_Formatter.xml
As bibliotecas contidas no diretório lib/compile devem ser usadas para a compilação do projeto, e serão
incluidas no pacote final da aplicação como dependências. As que estão no diretório lib/provided, devem ser
usadas apenas para a compilação, mas não farão parte do pacote por já estarem presentes no servidor de
aplicações.
dist - onde são colocados os pacotes jar, war e ear gerados pelo build
ear - onde ficam os arquivos application.xml, usados para gerar o ear
lib/bundle - bibliotecas que devem ser integradas a aplicação
lib/compile - bibliotecas que vão ser incluídas como dependência da aplicação
lib/provided - bibliotecas que não vão ser incluídas como dependência da aplicação
src - fontes da aplicação
src-published - fontes da aplicação, que serão colocadas num jar separado, para ser reutilizado
web/resources - classes e recursos a serem incluidos no arquivo war
O diretório src-published deve ser usado quando existirem algumas classes da aplicação que devem ficar
disponíveis para a reutilização. Assim, as classes presentes neste diretório serão empacotadas num arquivo jar
independente (com o sufixo-api) que poderá ser distribuído para ser usado em outras aplicações. Isso pode ser
preciso, por exemplo, quando uma interface de negócios for exportada, seus objetos de transferência (DTO) ou
ainda entidades JPA. Caso esta necessidade não esteja presente, o diretório pode ficar vazio.
No diretório lib da aplicação só devem ser incluídas dependências exclusivas da aplicação, e que ainda não são
atendidas pelo Framework. Na maioria dos casos esses diretórios podem ficar vazios. Caso a aplicação
necessite de alguma biblioteca adicional (como de leitura de código de barras ou uma API de serviços de outra
aplicação, por exemplo) ela pode ser colocada dentro do diretório lib/bundle, lib/compile.
Para obter e instalar esses produtos, consulte as informações apresentadas nos respectivos web sites.
Uma aplicação construída seguindo as normas do Framework pode ser gerada simplesmente com a linha de
comando, através da ferramenta Ant.
Um arquivo build.xml é provido, e ele pode ser usado sem nenhuma alteração. A única configuração
necessária é no arquivo build.properties presente na raíz do projeto. Ele precisa conter apenas a definição da
propriedade shared-lib.dir que aponta para o diretório onde estão todas as libs de dependências do
Framework.
• clean - limpa todos os artefatos gerados pelo build, retornando ao estado inicial.
Portanto, para gerar uma aplicação recem obtida do repositório, basta executar
ant dist
no diretorio raíz do projeto, que todos os artefatos necessários para o deploy serão gerados.
O procedimento de configuração do Eclipse 3.4 para o desenvolvimento de uma aplicação com o Framework é
descrito a seguir.
Ao iniciar um novo projeto é recomendado cria-lo a partir de uma aplicação vazia já existente. São fornecidos
os projetos WEB_blank, EJB_blank e Batch_blank, que podem ser usados como ponto de partida. O nome da
aplicação deve então ser alterado de blank para o nome correto do sistema a ser desenvolvido, e os arquivos que
não forem usados podem ser apagados.
Para criar o projeto clique em File -> New -> Java Project. Na tela New Java Project preencha da seguinte
forma:
5. Clique em Next.
(1.13.6)
O ambiente de desenvolvimento
Figura 3.1.
1. Adicione src e src-published como fontes do projeto, selecionando os dois diretórios e clicando no ícone
2. Clique em Next.
(1.13.6)
O ambiente de desenvolvimento
Figura 3.2.
Figura 3.3.
Figura 3.4.
Existem 2 bibliotecas principais que devem ser configuradas. Uma é a biblioteca do Framework que vem
dentro do arquivo zip do Framework. E as bibliotecas da aplicação que devem ser adicionadas de acordo com
as necessidades de cada aplicação.
(1.13.6)
O ambiente de desenvolvimento
(1.13.6)
O ambiente de desenvolvimento
Para cada novo projeto que utilizará a biblioteca do Framework apenas repita os passos a partir do passo 6 em
diante.
• Capítulo 5. Logging
• Capítulo 7. Persistência
• Capítulo 8. Mensageria
4.1. Introdução
O que é um serviço?
Nós definimos um serviço como sendo a unidade básica da camada de negócio, que executa regras de negócio,
e agrega uma série de métodos relacionados. Os serviços presentes num sistema definem a interface externa da
camada de negócio, que poderá ser usada pelas camadas superiores.
A camada de negócios é composta por vários serviços, que vão prover funcionalidades de negócio em alto
nível. A camada de negócio pode acessar as camadas inferiores, mas nunca as superiores.
Ao desenhar uma solução, a separação do negócio em serviços e métodos é a parte essencial. Deve ser tomada
atenção especial em quais objetos de domínio serão expostos pelos serviços e a granularidade dos métodos, ou
seja, o grau de detalhamamento e profundidade das informações que vão constituir a interface de negócios do
serviço.
1. Interface - uma interface Java com os métodos de negócio do serviço. É ela que define o "contrato"
oferecido pelo serviço. A interface deve fazer referência apenas a outras classes de negócio ou tipos
básicos de Java, nunca a classes de frameworks, protocolos, infra-estrutura, etc.
2. Implementação - uma classe Java que implementa a interface do serviço. Ela deve conter apenas lógica de
negócio. Lógica de persistência ou de integração deve ser delegada a outras camadas.
A definição do serviço não deve depender da classe que a implementa. E um usuário do serviço vai acessá-lo
através da interface apenas. Assim, ao chamador do serviço basta a interface de negócio.
O Framework provê uma série de funcionalidades adicionais aos serviços da camada de negócios durante
runtime. Assim, podemos facilmente controlar e monitorar os serviços, sem ter um esforço adicional. Essa
características serão detalhadas na seção de interceptadores de serviços.
Um serviço deve:
1 Ter uma interface com o sufixo *Service no nome. Essa interface define o "contrato" do serviço
e será usada pelos clientes.
3 Ter uma implementação com o sufixo *ServiceImpl no nome, que implemente a interface. Essa
Um serviço deve:
classe define a implementação do serviço e não deve ser instanciada ou acessada diretamente
pelo cliente.
6 Ter como pacote base com.altec.bsbr.app.*. Pode estar em qualquer outro subpacote, dadas as
restrições de nome declaradas acima.
Além dessas regras, é importante que a interface de um serviço só deve conter referências a objetos de tipos
básicos e de negócio. Não podem constar classes como ResultSet, Connection, HttpSession, HttpRequest,
ActionEvent, etc. A implementação só deve conter a lógica de negócios, sendo as lógicas de persistência ou de
integração delegadas a outras camadas.
Como exemplo, vamos definir um serviço para Pedidos, numa aplicação que tenha abc como nome ou sigla.
A interface do serviço:
package ❶com.altec.bsbr.app.abc.service;❷
import java.util.List;
import com.altec.bsbr.app.abc.entity.*;
interface PedidoService❸ {
A implementação do serviço:
package com.altec.bsbr.app.abc.service.impl;❶
import org.springframework.stereotype.Service;
@Service❷
public class PedidoServiceImpl❸ implements PedidoService❹ {
...
public Pedido criarPedido(Cliente cliente, FormaPagamento formaPagamento,
List<EstoqueQuantidade> estoques)throws PedidoException {
...
}
...
}
❶ A implementação está no sub package impl, tendo como base o mesmo package da interface.
❷ Possui a anotação @Service.
❸ O nome da implementação termina com ServiceImpl.
❹ Implementa a interface PedidoService.
Depois de criada a interface e a implementação do serviço de acordo com essas regras, não é necessário
nehuma configuração especial. O Framework fará automaticamente uma busca pelos serviços e os colocará a
Quando um serviço precisar acessar algum componente gerenciado pelo Spring, basta usar a anotação
org.springframework.beans.factory.annotation.Autowired no campo (@Autowired), que ele será injetado
durante a construção do serviço. No exemplo a seguir, o serviço FooServiceImpl vai acessar um outro serviço
BarService
@Service
public class FooServiceImpl implements FooService {
@Autowired
private BarService barService;
...
public void exemplo() {
barService.fazAlgo();
}
}
A injeção de dependência pela anotação @Autowired é feita "por tipo", ou seja, será injetado um bean que seja
do tipo BarService. Caso ocorra a situação de existir mais de um bean do mesmo tipo, ocorrerá um erro
durante a inicialização, pois o Spring não pode determinar qual deles é a dependência desejada. Neste caso
deve ser usada a anotação org.springframework.beans.factory.annotation.Qualifier (@Qualifier), que
vai dizer qual o nome do bean a ser injetado. Exemplo:
@Autowired
@Qualifier("barServiceImpl")
private BarService barService;
Para obter maiores informações sobre os mecanismos de injeção de dependências, consulte a documentação
oficial do Spring
Quando métodos precisarem fazer parte de uma transação ela sempre será delimitada nesta camada. Portanto,
uma transação sempre começará na entrada de um método de serviço e terminará quando a execução sair fora
desta camada.
Em uma operação que irá rodar dentro de um contexto transacional é importante que se defina quando a
transação será comitada e quando deverá ser feito o rollback. Normalmente, caso não ocorram erros a transação
corrente deve ser comitada ao sair da camada de serviço, e quando ele terminar com uma exceção deve ser feito
o rollback. Além disso, é importante checar o tipo de propagação, ou seja, se é necessário que outros
componentes estejam ou não dentro de uma transação. Portanto, após definir estes pontos vamos para a
implementação.
O Framework define o escopo de uma transação por métodos executados na camada de serviços. São usadas
Exemplo:
@Service
public class AnimalServiceImpl implements AnimalService {
@Transactional(❶
readOnly = false,❷
propagation = Propagation.Required,❸
rollbackFor = RuntimException.class)❹
public Animal salvar(Animal animal) {
...
}
Dica
Na maioria dos casos os parâmetros padrão de @Transactional são suficientes.
Os tipos de propagação suportados estão resumidos na tabela abaixo. Para maiores informações, consulte
manual do Spring.
Tabela 4.2.
Tipo Descrição
Mandatory É obrigatório que exista uma transação ativa, lança uma exceção caso não exista
nenhuma.
Never Não executa dentro de uma transação, lança uma exceção caso exista uma.
Not Supported Não executa dentro de uma transação, ele suspende a transação caso uma já
exista.
Required Executa dentro de uma transação, caso não exista ele cria uma nova.
Requires New Executa dentro de uma transação, caso já exista uma cria-se uma nova.
Supports Executa dentro de uma transação, caso já exista uma, caso contrário executa fora
de uma transação.
(1.13.6)
Serviços de negócio
4.5. Interceptadores
Todos os serviços do Framework ganharão uma série de funcionalidades adicionais de forma transparente.
Através de recursos de AOP do Spring, são acrescentados interceptadores aos serviços, que serão invocados
durante sua chamada.
• Auditoria - faz a auditoria da chamada do serviço. Para tanto, o método precisa ter a anotação @Audit. Veja
o seção de auditoria.
• Autorização - verifica autorização do usuário para executar o serviço. Para tanto, o método precisa ter a
anotação @Authorize. Veja o seção de autorização.
(1.13.6)
Serviços de negócio
• Tratamento de exceções - faz um tratamento padrão das exceções que ocorrerem no método. Veja a seção
de tratamento de exceções.
• Log - registra a chamada do método, parâmetros de entrada e saída. Veja a seção de logging.
• Monitoração - monitora a execução do método e mede o tempo gasto. Veja a seção de gerenciamento.
O Framework utiliza EJB's apenas para situações onde é necessário expor serviços remotamente para outras
aplicações utilizando o protocolo RMI/IIOP.
Stateless Session Beans são EJB's que não possuem estado. Cada chamada requisita uma instância do Pool de
SLSB's. Desta forma, ele é a forma de Session Beans mais escalável que existe.
1. Crie o serviço o qual irá ser exportado. Nos exemplos abaixo exportaremos o serviço ProdutoService.
...
<bean class="com.altec.bsbr.app.projeto.service.impl.ProdutoServiceImpl"/>
...
3. Crie a interface de negócio contendo os métodos de negócio do EJB anotando com a annotation @Remote.
Neste caso é permitido que se estenda a interface de Servico, se houver a necessidade de exportar todos os
métodos do serviço.
...
import javax.ejb.Remote;
...
@Remote
public interface ProdutoRemote {
public void criarProduto(Produto produto);
}
4. Crie a classe do EJB a qual irá expor os métodos do serviço anotando-a com a annotation @Stateless e
adicionando o Interceptor com.altec.bsbr.fw.ejb.EjbAutowiringInterceptor.
...
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
...
import com.altec.bsbr.fw.ejb.EjbAutowiringInterceptor;
...
@Stateless
@Interceptors(EjbAutowiringInterceptor.class)
public class ProdutoBean implements ProdutoRemote {
@Autowired
private ProdutoService produtoService;
Nota
Por questões de escalabilidade, recomendamos não usar Stateful Session Beans.
Stateful Session Bean são EJB's que possuem estado. Cada chamada requisita a mesma instância que foi
utilizada anteriormente, até que o Bean receba um status de removido ou ocorra um timeout.
1. Crie o serviço o qual irá ser exportado. Nos exemplos abaixo exportaremos o serviço ProdutoService.
...
<bean class="com.altec.bsbr.app.projeto.service.impl.ProdutoServiceImpl"/>
...
3. Crie a interface de negócio contendo os métodos de negócio do EJB anotado com a annotation @Remote.
Neste caso é permitido que se estenda a interface de Servico, se houver a necessidade de exportar todos os
métodos do serviço.
...
import javax.ejb.Remote;
...
@Remote
public interface ProdutoRemote {
public void criarProduto(Produto produto);
}
4. Crie a classe do EJB a qual irá expor os métodos do serviço anotando-a com a annotation @Stateful e
adicionando o Interceptor com.altec.bsbr.fw.ejb.EjbAutowiringInterceptor.
...
import javax.ejb.Stateful;
import javax.interceptor.Interceptors;
...
import com.altec.bsbr.fw.ejb.EjbAutowiringInterceptor;
...
@Stateful
@Interceptors(EjbAutowiringInterceptor.class)
Este componente representa a integração do Java Enterprise Edition com a API JMS, ou seja, adiciona o
suporte a mensagens a especificação de EJB's.
(1.13.6)
Capítulo 5. Logging
5.1. Introdução
O fluxo de uma aplicação deve gerar diversos eventos de Log que permitem acompanhar sua execução. O
mecanismo de Log do Framework captura estes eventos e os reporta em um destino configurado.
Para a implementação desta funcionalidade foram usadas as bibliotecas open source slf4j e Logback.
5.2. SLF4J
O Framework usa a API open source slf4j como interface de log. O slf4j provê uma simples fachada para vários
sistemas de logging. Todos os eventos de log são consumidos pelo slf4j e direcionados para o Logback que por
sua vez, fará o tratamento dos eventos baseado nos seus níveis e loggers.
O slf4j também intercepta chamadas ao Apache commons-logging usado por bibliotecas de terceiros (como o
Spring ou Hibernate) e permite que eles também recebam o mesmo tratamento dos eventos de log da aplicação.
O framework tem uma configuração do Logback para gravar os logs de cada aplicação em arquivos
independentes além de repassá-los ao log padrão java.util.logging (JUL) do JDK para ser tratado pelo
WebSphere de uma maneira global, consolidando os logs de todas aplicações hospedadas no servidor.
Neste guia é feita apenas uma breve introdução ao slf4j. Para maiores informações consulte o site oficial do
slf4j.
Os níveis de log suportados pelo slf4j são listados abaixo. Uma orientação de quando usar cada um desses
níveis está na seção de boas práticas
1. ERROR - erro
2. WARN - aviso
3. INFO - informação
4. DEBUG - debug
5. TRACE - trace
Para gerar algum evento de log é necessário ter um logger. O primeiro passo é obter a instancia do logger
desejado. A recomendação do Framework é usar o nome completamente qualificado da classe que está sendo
executada como o nome do logger. Dentro de uma classe Teste, podemos obter o logger da seguinte maneira:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
...
class Teste {
private static final Logger logger = LoggerFactory.getLogger(Teste.class);
}
Com o logger disponível, podemos chamar os métodos com os níveis desejados: trace(), debug(), info(),
warn(), error(). Os principais métodos da classe Logger são
• void trace(String format, Object arg) - loga uma mensagem no nível TRACE, usando o formato
especificado com um argumento.
• void trace(String format, Object arg1, Object arg2) - loga uma mensagem no nível TRACE,
usando o formato especificado com dois argumentos.
• void trace(String format, Object[] argArray) - loga uma mensagem no nível TRACE, usando o
formato especificado com um array de argumentos. O formato da mensagem é bem simples, usando {}
como marcadores que serão substituídos pelos parâmetros, na mesma ordem em que eles aparecem.
• void trace(String msg, Throwable t) - loga uma exceção no nível TRACE, com uma mensagem.
• Também existe um conjunto de métodos análogos para debug, info, warn e error.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Integer t;
Integer oldT;
5.3. Logback
O Framework usa a biblioteca open source Logback para fazer a geração de logs.
O Framework tem uma configuração interna do Logback que gera automaticamente as seguintes saídas de log:
• Arquivo de log da aplicação. O Logback gera um arquivo exclusivo para cada aplicação, com o nome
${application.name}.log onde application.name é valor da propriedada definida no arquivo
META-INF/config/application.properties. O arquivo é gerado no diretório definido pela propriedade
Java BASE_LOG_PATH, ou caso ela não esteja definida, no diretório corrente do processo Java. A propriedade
no formato abaixo pode ser colocada na definição do processo Java do servidor de aplicações ou
equivalente:
-DBASE_LOG_PATH=c:/temp
• Log do WebSphere. O Logback direciona todas mensagens de erro para o java.util.logging do JDK, que
vai ser tratado pelo WebSphere. Por default, todos logs de nível INFO e superior são enviados para o
arquivo SystemOut.log
A aplicação pode adicionar configurações do Logback caso seja preciso. O Framework vai carregar
automaticamente o arquivo META-INF/log/logback-runningMode.xml, caso ele exista.
<configuration>
<rollingPolicy class=ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<FileNamePattern;${BASE_LOG_PATH}/${application.name}_%.log.zip</FileNamePattern>
<MinIndex>1;/MinIndex>
<MaxIndex>10;/MaxIndex>
</rollingPolicy>
<triggeringPolicy class=”ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy”>
<MaxFileSize>5MB;/MaxFileSize>
</triggeringPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} {%X{user}} %msg%n</Pattern><co
id="co-log-3" />
</layout>
</appender>
<root>
<level value="DEBUG"/>
<appender-ref ref="DEBUG_APPLICATION_LOG_FILE"/><co id="co-log-4" />
</root>
<logger name="org.hibernate">
<level value="INFO"/><co id="co-log-5" />
</logger>
<logger name="org.hibernate.type"><co id="co-log-6" />
<level value="DEBUG"/>
</logger>
<logger name="com.altec.bsbr.fw">
<level value="DEBUG"/><co id="co-log-7" />
</logger>
<logger name="com.altec.bsbr.app.abc">
<level value="TRACE"/><co id="co-log-8" />
</logger>
</configuration>
co-log-1:
Declaramos
??? um appender chamado DEBUG_APPLICATION_LOG_FILE, que vai gerar a saída em arquivo.
co-log-2:
O arquivo
??? vai ser gerado no diretório ${BASE_LOG_PATH} com o nome ${application.name}-debug.log.
co-log-3:
Foi usado
??? um formato de mensagem de log customizado.
co-log-4:
O appender
??? deve ser atribuído ao logger raíz.
co-log-5:
Definimos
??? o nível INFO para org.hibernate.
co-log-6:
Definimos
??? o nível DEBUG para org.hibernate.type.
co-log-7:
Definimos
??? o nível DEBUG para com.altec.bsbr.fw, as classes do Framework.
co-log-8:
Definimos
??? o nível TRACE para com.altec.bsbr.app.abc, as classes da aplicação abc.
No framework, o tamanho suportado para log é de até de 10M, ao atingir este tamanho o arquivo é zipado e um
novo arquivo de log será ativado.
Fica a critério do usuário configurar a quantidade de logs zipados. Por exemplo, o usuário pode decidir por
excluir um zip (mais antigo) a cada 30 zips.
(1.13.6)
Logging
O uso direto de System.out para a geração de logs tem uma série de desvantagens:
5.4.2. O logger de uma classe deve ser um atributo privado, estático e final
Veja o exemplo:
class Exemplo {
...
private static final Logger logger = ...;
...
}
Usando o nome completamente qualificado da classe que está sendo executada como o nome do logger,
podemos agrupar os logger usando a mesma hierarquia de pacotes da aplicação. Essa estratégia tem a vantagem
de podermos configurar o nível de log desejado para cada pacote.
No trecho de código abaixo, usamos o método getLogger(Class) do slf4j para obter um logger com o nome da
classe passada como parâmetro:
class Exemplo {
...
private static final Logger logger = LoggerFactory.getLogger(Exemplo.class);
...
}
Importante
Cuidado ao fazer um copy and paste desse trecho de código! Lembre sempre de colocar o nome da
classe para onde você estiver copiando.
Cada nível de log, deve ser usado para fins específicos. Os níveis existentes no log4j são:
Nível Descrição
(1.13.6)
Logging
Nível Descrição
WARNING Indica a ocorrência de alguma situação anormal, mas que não impede a
continuidade do processamento, e pode gerar algum resultado inesperado.
Também pode indicar a recuperação de uma situação de erro.
INFO Indica alguma informação de interesse. É uma situação normal e continua com o
processamento normalmente. Geralmente mostra eventos de interesse durante a
operação em produção.
TRACE Similar a DEBUG, mas num nível de detalhe e profundidade maior. Pode
potencialmente mostrar um grande volume de informações.
É essencial o entendimento dessas definições por todos integrantes do time de desenvolvimento, para que os
níveis de log sejam usados de maneira consistente em todo o sistema.
Cada nível de log tem um público alvo, portanto escreva as mensagens de log de cada nível tendo em mente
quem vai ve-las. A informação deve ser mais ou menos descritiva dependendo do nível.
Por exemplo, os níveis INFO para cima serão vistos pelo operador no ambiente de produção, enquanto os
níveis de DEBUG e TRACE pelo desenvolvedor.
No entanto, num ambiente com muitos threads em execução simultaneamente, as mensagens de log podem
ficar intercaladas com outras, dificultando achar um correlacionamento entre elas. Neste caso, seria mais
apropriado o seguinte:
Se a configuração do log mostra até o nível DEBUG, não há problema. Mas se o log estiver configurado para
mostrar apenas os níveis de WARNING e ERROR, o warning vai ser inútil, pois não vai mostrar todos dados
relevantes. Portanto, toda informação necessária para entender a situação deve estar contida num mesmo nível
de log.
Ocasionalmente, o custo de geração da mensagem de log pode ser muito alto. Por exemplo, no trecho abaixo,
existe o custo de converter o inteiro i e entry[i] em String e concatenar as strings intermediárias. Isso vai
ocorrer sendo a mensagem logada ou não.
Uma maneira de evitar esse custo quando a mensagem não for logada, é fazendo um teste antes:
if (logger.isDebugEnabled()) {
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}
Uma boa alternativa é usar mensagens parametrizadas no log, pois os parâmetros só vão ser convertidos para
String, caso a mensagem de log seja mesmo gerada.
Atenção
O uso de mensagens parametrizadas apenas elimina o custo de conversão para String. Caso exista a
chamada de algum método que tenha um considerável custo de execução, a única solução é fazer o
teste antes de chamar o log:
if (logger.isDebugEnabled()) {
logger.debug("Taxa: {}", calculaTaxaComplicada());
}
Implemente o método toString(), para expor o estado de seus objetos de uma maneira legível. O método
• Ele não deve falhar nunca. (Cuidado para não ocorrer NullPointerException!)
Outra possibilidade para criar o método toString() é usar a biblioteca commons-lang que fornece a classe
ToStringBuilder que usa reflexão para criar um toString() com todos os atributos do objeto de uma forma
dinâmica (note que isso pode não ser desejável, por conter dados que podem ser irrelevantes e só dificultam a
leitura dos valores). Exemplo de uso:
A classe ToStringBuilder também pode ser usada diretamente no log para mostrar dados de classes de
terceiros, e que não tenham um toString() amigável:
Quando usamos o método toString() de um array em Java, obtemos uma representação que pode não ser
muito útil:
[Ljava.lang.Object;@e0b6f5
Para obter uma saída mais informativa podemos usar os métodos Arrays.asList(Object[]) ou
Arrays.deepToString(Object[]) presentes no pacote java.util do Java 5:
import java.util.Arrays;
[bar, baz]
[bar, baz]
Portanto, quando for preciso logar algum objeto array, é recomendado usar um dos métodos acima, para
facilitar a leitura dos resultados.
(1.13.6)
Logging
O Framework faz automaticamente um log da entrada e saída de todas as chamada a métodos da camada de
serviços.
Mensagens com nível de DEBUG são geradas contendo informações dos parâmetros de entrada do método e os
valores retornados. A entrada do método é marcada com >> e a saída com <<, como mostrado no exemplo de
saída abaixo:
...
>> findAll(class com.altec.bsbr.app.abc.entity.Categoria,10)
<< findAll -> ArrayList[size=5]
...
>> get(class com.altec.bsbr.app.abc.entity.Estoque,2872,false)
<< get -> com.altec.bsbr.app.abc.entity.Estoque@2b942b94[codigo=2872,quantidade=16,preco=10,
produto=com.altec.bsbr.app.abc.entity.Produto@29aa29aa[codigo=2862,nomeTitulo=Ritchie Blackmore's
Rainbow,nomeArtista=Rainbow,anoLancamento=1975,
categoria=com.altec.bsbr.app.abc.entity.Categoria@2ac62ac6[codigo=2635,nome=Rock]]]
...
>> findByCategoria(<null>)
<< findByCategoria -> ArrayList[size=0]
...
Nota
Para a impressão dos parâmetros é usado o método toString() do objeto, ou se não houver, o
ToStringBuilder do commons-lang.
Ao término da execução de cada método de serviço, é gerada uma mensagem com nível de DEBUG, contendo
algumas estatísticas das chamadas àquele método:
Uma descrição dos parâmetros mostrados está na seção sobre gerenciamento de serviços
(1.13.6)
Capítulo 6. Uso de exceções
6.1. Introdução
Tipos de exceções
Em Java, os erros em tempo de execução (também conhecidos como exceções) são classificados como checked
e unchecked.
Exceções checked: são aquelas que obrigatoriamente devem ser tratadas pelo chamador, o que é verificado em
tempo de compilação. Assim, quando um método de negócio é construído, na sua assinatura são definidas as
exceções checked que podem ocorrer mediante circunstâncias diversas quando da sua chamada. Todos
elementos do sistemas que utilizarem esse método devem tratar a possível ocorrência dessas exceções. Uma das
vantagens de exceções checked é impedir que um programador desavisado esqueça de fazer o tratamento de
uma exceção gerada na camada de negócio e isso venha ocasionar um fluxo não esperado ou não tratado no
sistema, culminando muitas vezes na parada da aplicação ou em mensagens de erros sistêmicas para o
operador.
Exceções unchecked: são aquelas onde sua ocorrência é não determinística. Exemplos desse tipo de exceção
são a falta de memória, manipulação indevida variáveis, ponteiros nulos, etc. Esses erros podem ser tratados
mas necessitam que o programador se atente na possibilidade e circunstâncias pelas quais eles podem
acontecer. No entanto, sempre alguns casos escapam da análise e acabam ocasionando o mal funcionamento da
aplicação ou em mensagens de erros não tratadas para o operador do sistema.
A forma como um sistema lança e trata exceções pode ter um forte impacto na sua robustez e como ele vai se
comportar em operações do dia a dia.
Quando devemos lançar uma exceção? Quando o método encontrar uma situação anormal que ele não pode
tratar, uma exceção deve ser lançada.
E quando devemos tratar uma exceção? Quando o chamador do método souber e quiser tomar alguma medida
corretiva do erro ocorrido.
Pra resolver esses problemas, o Framework fornece algumas exceções base, e possui uma estratégia padrão já
implementada de tratamento de exeções na camada de serviços.
• BusinessException: exceção checked para ser usada em erros de negócio, e que vão ser tratados pela
aplicação. A ocorrência de uma BusinessException deve ser considerada como parte do fluxo normal da
aplicação, não como um bug nem como uma condição de erro que venha a precisar de alguma intervenção
em produção.
• SystemException: exceção unchecked usada para sinalizar situações fora do curso esperado da aplicação, e
que não são tratadas explicitamente. A ocorrência de uma SystemException deve ser considerada anormal,
e para sua correção normalmente e necessário uma intervenção externa. Situações onde onde é
recomendado o uso da SystemException:
• Bugs do sistema.
• FaultException: exceção unchecked de uso interno do Framework, usada para encapsular informações de
outras exceções capturadas na camada de serviços. É a classe base de SystemException.
Desta forma, a camada cliente irá tratar sempre esses tipos básicos de exceções. Nesses moldes, o framework
irá facilitar o rastreio de erros a partir do registro formatado da exceção em log, identificador para o elemento
causador da exceção e informações sobre a fonte causadora do erro.
O desenvolvedor, pode estender as classes BusinessException e SystemException, para criar as suas próprias
exceções, de acordo com as necessidades específicas da aplicação.
O uso típico de BusinessException é como uma classe base para a criação de exceções de negócio específicas
da aplicação.
Então, algum serviço da camada de negócios poderá usar essa BusinessException da declaração de um método:
@Service
public ContaCorrenteServiceImpl {
try {
contaCorrenteService.efetuaSaque(numeroConta, valor);
}
catch (SaldoNegativoException e) {
String mensagem = "Operação não pode ser realizada pois o saldo está negativo: " + e.getSaldoAtual();
// Mostra a mensagem de erro para o usuário explicando o motivo da não transferência
}
Para instanciar uma SystemException é necessário passar um objeto do tipo ErrorMessage. Assim, todas as
possíveis exceções devem ter uma mensagem mapeada numa subclasse de com.altec.bsbr.fw.ErrorMessage
específica para cada aplicação.
A classe de mensagens de erro deve ter todos as instâncias de erros como atributos estáticos como no exemplo
abaixo:
Quando uma exceção resulta da chamada de um serviço, o Framework procede com o seguinte tratamento:
• Caso contrário, encapsula a exceção numa FaultException, faz um log de erro, e lança a FaultException
no lugar da original. O log contem os dados abaixo:
• Timestamp.
• Serviço.
• Método.
• A exceção.
Conforme descrito na seção sobre transações e serviços, os métodos da camada de negócios tem o controle
transacional feito automaticamente pelo Framework.
Quando um método do serviço tem a anotação @Transactional esse código será executado dentro de uma
transacão. Caso o método termine por causa de alguma exceção, o comportamente padrão é finalizar a
transação com um rollback. Se ele terminar normalmente, será feito um commit da transação.
Portanto, uma maneira simples de causar um rollback da transação é simplesmente lançar uma exceção
(BusinessException ou SystemException) dentro de um serviço, quando for desejado que não seja feito commit
da transação em uso.
(1.13.6)
Uso de exceções
Nota
Um erro muito comum é colocar apenas um log da exceção dentro do catch e continuar ou retornar
como se nada tivesse acontecido... Obviamente isso pode acarretar erros ainda mais graves
posteriormente.
No entanto, se voce não souber como tratar a exceção que aparecer, deixe-a passar, que em algum
momento ela será tratada por alguem. (O Framework possui um tratamento padrão na camada de
serviços, deixe que ele cuide disso)
Apenas faça o catch de uma superclasse quando tiver certeza que todas as subclasse tem o mesmo significado e
podem ser tratadas da mesma maneira.
Não é necessário fazer o log de uma exceção se ela está sendo apenas relançada ou sendo traduzida para uma
outra exceção, senão, essa exceção será logada repetidas vezes, atrapalhando a leitura do arquivo de logs.
Use uma exceção apropriada para a situação ocorrida, ou crie uma nova classe de exceção.
Se o método descobrir que o cliente não obedeceu ao contrato (passando dados inválidos, por exemplo) uma
exceção deve ser lançada.
Se o método não puder cumprir seu contrato, ele pode lançar uma exceção, de preferência unchecked, pois não
é razoável esperar que o cliente trate sempre essa situação fora do normal.
• A implementação está sendo exposta como parte da interface do método, dificultando modificações futuras.
(1.13.6)
Uso de exceções
Se você não quiser propagar uma exceção, pode traduzi-la para uma outra exceção dentro do domínio de
negócio que faça sentido para o cliente do método.
Voce pode criar uma classe de exceção para indicar erros que fazem parte do domínio de negócio. Lembre que
exceções só devem ser usadas para situações anormais, e que não fazem parte do fluxo natural de operação.
Dica
Lembre-se de acrescentar informações importantes sobre o erro na exceção. Assim o cliente terá
condições de tomar as medidas adequadas.
Numa situação onde podem ocorrer muitos tipos de exceções diferentes, elas podem ser agrupadas em apenas
uma exceção específica para a situação. Isso facilita o cliente do método, que precisa tratar apenas uma
exceção. Além disso a interface do método se torna mais estável, não sendo afetada por mudanças internas do
método que poderiam acarretar em colocar ou retirar as exceções da assinatura do método.
Use a classe com.altec.bsbr.fw.SystemException como base para exceções de implementação, que não são
de negócio.
Importante
É importante que numa situação de tradução de uma exceção por outra, a exceção original seja
encapsulada, para preservar o problema original, o que pode ser vital para resolver o problema. A
exceção original pode ser obtida com o método getCause() da classe Exception.
7.1. Introdução
A camada de persistência é responsável por fazer o acesso a bancos de dados. O mecanismo de persistência do
Framework é baseado em:
• JPA (Java Persistence API) usando o provider Hibernate 3.3. Com essa tecnologia é possível criar um
modelo de persistência independente de banco de dDados, pois os comandos SQL são gerados
automaticamente pelo Provider JPA.
A escolha adequada para cada situação depende da característica de acesso ao banco e a requisitos não
funcionais do projeto em questão.
O encapsulamento do mecanismo de persistência é feito usando o padrão DAO (Data Acess Object), por ser
bem conhecido e eficiente.
A principal vantagem desse padrão é expor uma API independente da tecnologia de persistência que será
utilizada pela camada de negócio. Caso seja necessário uma alteração no mecanismo de persistência em
decorrência de uma customização, evolução ou busca por melhor performance, a camada de negócio não é
impactada pois o padrão DAO proporciona desacoplamento entre a infra-estrutura de persistência e lógica de
negócio.
Sobre JPA
JPA ou Java Persistence API: Simplifica o modelo de persistência de entidades em Java
acrescentando um novo modo de mapeamento de entidades, usando Annotations. Além disso,
centraliza o acesso as entidades em um componente único chamado
javax.persistence.EntityManager.
JPA baseou-se nos principais frameworks de persistência existentes no mercado, como: Hibernate,
Oracle TopLink e Java Data Objects (JDO), para criar uma API que possua as melhores
características de cada um deles. Desta forma, é possível que existam várias implementações de
diferentes fornecedores.
Para mais informações veja o JEE 5 Tutorial - Part V Persistence e o site do Hibernate.
• GenericJpaDao
• GenericHibernateJpaDao
• CrudJpaDao
• GenericJdbcDao
7.2.1. JPA
Para auxiliar na construção de DAOs que usam JPA, o Framework oferece algumas classes base.
7.2.1.1. GenericJpaDao
@Repository
pubic class FooDaoImpl extends GenericJpaDao <FooEntity❶, Long❷> implements FooDao {
...
}
❶ Tipo da entidade.
❷ Tipo do ID da classe de entidade, a classe que representa a chave primária.
• persist(T entity): Persiste uma entidade T, caso a entidade já exista no Banco ela atualiza seu estado, ou
seja, executa um UPDATE, caso a entidade não exista ela é criada, ou seja, executa um INSERT.
• remove(T entity): Remove uma entidade T do banco de dados, ou seja, executa um DELETE. O objeto em
memória continua a existir mas não é mais persistente.
• findById(ID id, boolean lock): Retorna uma instância do objeto com o ID e faz o lock da entidade caso
seja passado true.
• findAll(int maxResults): Retorna uma List <T> do tipo genérico definido, com um limite máximo de
resultados.
• findByQuery(String queryString, Object... params): Retorna uma List <T> utilizando uma JPA-QL.
Exemplo:
@Repository
public class AnimalDaoImpl extends GenericJpaDao implements AnimalDao {
public List<Animal> findAnimais(String nome) {
String jpaQuery = "select a from Animal a where a.nome=?1";
return findByQuery(jpaQuery, nome);
}
}
• findOneByQuery(String queryString, Object... params): Retorna uma única instância utilizando uma
JPA-QL.
Exemplo:
@Repository
public class AnimalDaoImpl extends GenericJpaDao implements AnimalDao {
public Animal findAnimalById(Long id) {
interface QueryWrapper {
public String getQueryString();❶
public void setParam(int index, Query query, Object value);❷
}
Exemplo:
@Repository
public class AnimalDaoImpl extends GenericJpaDao<Animal, Long> implements AnimalDao {
public List<Animal> findAnimais(String nome, boolean val) {
QueryWrapper queryWrapper = new QueryWrapper() {
public String getQueryString() {
return "select a from Animal a where a.nome=?1 and a.ativo=?2";
}
public void setParam(int index, Query query, Object value) {
switch (index) {
case 1: {
query.setString(index, (String)value);
break;
}
case 2: {
Boolean booleanVal = value;
String simNaoVal = (booleanVal ? "T" : "F");
query.setString(index, simNaoVal);
break;
}
}
}
};
return findByQuery(jpaQuery, nome);
}
}
Nota
7.2.1.2. GenericHibernateJpaDao
Esta classe possui 2 parâmetros genéricos T e ID, onde em geral, T é o tipo de uma entidade mapeada, e o
parâmetro ID é o tipo do identificador (chave) da entidade. Exemplo:
@Repository
public class FooDaoImpl extends GenericHibernateJpaDao <FooEntity❶, Long❷> implements FooDao {
...
}
❶ Tipo da entidade.
❷ Tipo do id da classe de entidade.
Exemplo:
@Repository
public class AnimalDaoImpl extends GenericHibernateJpaDao<Animal, Long> implements AnimalDao {
public List<Animal> findByNome(String nome) {
Animal example = new Animal();
example.setNome(nome);
return findByExample(example, "id", "ativo");
}
public List<Animal> findByMesmaEspecie(Animal example) {
return findByExample(example, "id", "ativo");
}
}
Exemplo:
@Repository
public class AnimalDaoImpl extends GenericHibernateJpaDao<Animal, Long> implements AnimalDao {
public Animal findById(Long id) {
Animal example = new Animal();
example.setId(id);
return findOneByExample(example, "nome", "ativo");
}
}
Exemplo:
@Repository
public class AnimalDaoImpl extends GenericHibernateJpaDao<Animal, Long> implements AnimalDao {
(1.13.6)
Persistência
Exemplo:
@Repository
public class AnimalDaoImpl extends GenericHibernateJpaDao<Animal, Long> implements AnimalDao {
public Animal findById(Long id) {
return findOneByCriteria(org.hibernate.criterion.Restrictions.add(
org.hibernate.criterion.Restrictions.eq("id", id));
}
}
Exemplo:
@Repository
public class AnimalDaoImpl extends GenericHibernateJpaDao<Animal, Long> implements AnimalDao {
public Animal findByNome(String nome) {
HibernateCallback callback = new HibernateCallback() {
public Object doInHibernate(Session session) {
return session.createCriteria(Animal.class).add(
Restrictions.add(Restrictions.eq("nome", nome)))
.setCacheable(true).setCacheMode(CacheMode.NORMAL)
.setCacheRegion("default").list();
}
};
return findUsingCallback(callback);
}
}
Exemplo:
@Repository
public class AnimalDaoImpl extends GenericHibernateJpaDao<Animal, Long> implements AnimalDao {
public Animal findById(Long id) {
HibernateCallback callback = new HibernateCallback() {
public Object doInHibernate(Session session) {
return session.createCriteria(Animal.class).add(
Restrictions.add(Restrictions.eq("id", id)))
.setCacheable(true).setCacheMode(CacheMode.NORMAL)
.setCacheRegion("default").list();
}
};
return findUsingCallback(callback);
}
...
}
(1.13.6)
Persistência
Nota
Para usar com vários PersistenceUnits consulte a seção 4.4.
7.2.1.3. CrudJpaDaoImpl
Em muitas situações é necessário apenas um acesso comum de criar, listar, atualizar e apagar. Para essas
situações existe a classe com.altec.bsbr.fw.dao.jpa.CrudJpaDaoImpl.
Importante
Esta classe não pode ser estendida. Ela deve apenas ser usada nos casos específicos exemplificados
acima.
• persist(T entity): Persiste uma entidade T, caso a entidade já exista no Banco ela atualiza seu estado, ou
seja, executa um UPDATE, caso a entidade não exista ela é criada, ou seja, executa um INSERT.
• findById(Class<?> entityType, Serializable id, boolean lock): Retorna uma instância da entidade
entityType utilizando o id e efetuando o lock caso seja true.
• findAll(Class<?> entityType, int maxResults): Retorna uma List <T> que tenha até no máximo a
quantidade maxResults. Este método executa um SELECT sem clausula WHERE, portanto é importante
definir um número máximo de resultados.
7.2.2. JDBC
O Framework também suporta o uso direto de comandos SQL através da API JDBC usada da forma descrita a
seguir.
7.2.2.1. GenericJdbcDao
Esta classe possibilita que se faça o acesso direto ao Banco de Dados usando a API JDBC. Este modelo de
acesso deve ser usado com cautela, pois aumenta o acoplamento da aplicação com o banco de dados,
dificultando a manutenção da aplicação e tornando difícil adicionar recursos como cache.
Esta é uma classe abstrata a qual estende de SimpleJdbcDaoSupport oferencendo suporte a acesso ao banco
utilizando JDBC. Através desse DAO é possível obter uma instância da classe SimpleJdbcTemplate
utilizando-se o método getSimpleJdbcTemplate(). Essa classe disponibiliza uma série de alternativas para se
executar comandos SQL de tal forma que não é necessário utilizar a API JDBC de baixo nível, como
PreparedStatement, ResultSet, etc. Esta abstração trás vantagens em produtividade.
Nota
Para usar com vários DataSources consulte a seção 4.4.
Para executar comandos SQL utiliza-se a classe SimpleJdbcTemplate por uma série de métodos de acesso ao
banco de dados. Ela facilita o desenvolvimento, não sendo necessário preocupar-se com fechamento de
conexões e tratar ResultSets, além disso ela possui métodos que quando usados com Java 5 facilitam ainda
mais.
Exemplo de um GenericJdbcDao:
@Repository
public class EstoqueJdbcDaoImpl extends GenericJdbcDao implements EstoqueDao {
A chamada de stored procedures é feita utilizando-se a classe SimpleJdbcCall que facilita a chamada a stored
procedures conforme mostra o exemplo abaixo:
@Repository
public class ActorJdbcDaoImpl extends GenericJdbcDao implements ActorDao {
private SimpleJdbcCall procReadActor;
@Autowired
public void initDataSource(DataSource dataSource) {
super.setDataSource(dataSource);
this.procReadActor = new SimpleJdbcCall(dataSource).withProcedureName("read_actor");
}
...
}
É possível que um DAO JDBC utilize um Datasource específico, caso a aplicação possua vários Datasources.
(1.13.6)
Persistência
@Repository
public class AnimalDaoImpl extends GenericJpaDao<Animal, Long> implements AnimalDao {
...
}
E desta forma concluimos a criação do DAO. Para acessá-lo dentro de um serviço, basta criar um atributo com
modificador de acesso private, com o tipo da interface do DAO e anotada com
org.springframework.beans.factory.annotation.Autowired.
@Service
public class AnimalServiceImpl implements AnimalService {
@Autowired
private AnimalDao animalDao;
...
}
7.3. Transações
O controle transacional é feito pelo Framework na camada de serviços, não sendo necessário colocar lógica de
transações nos DAOs.
[Raiz_Projeto]/
src/
• Criar o arquivo data_access.xml. Neste arquivo é possível que sejam configurados vários datasources.
Abaixo estão 2 exemplos de arquivos de configuração para dois running modes diferentes:
(1.13.6)
Persistência
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.5.xsd">
<jee:jndi-lookup id="dataSource1"❶
jndi-name="jdbc/jab"❷
/>
</beans>
</beans>
jdbc.driverClassName=oracle.jdbc.OracleDriver
jdbc.url=jdbc:oracle:thin:@servidor:1521:DESENV
jdbc.username=usuario
jdbc.password=senha
JPA exige a criação do arquivo de configuração de persistência. No Framework esse arquivo é apontado
indiretamente pela propriedade jpa.persistence.config.file.
Essa propriedade é definida dentro do arquivo persistence.properties. Para usar configurações diferentes
para servidores de aplicação diferentes, podemos usar os running modes definidos automaticamente pelo
Framework: websphere. Para maiores informações veja a seção de running modes.
jpa.persistence.config.file=classpath*:META-INF/persistence-<runningMode>.xml
• META-INF/persistence-<runningMode>.xml
Exemplo:
</persistence>
❶ Este datasource deve ser o mesmo que deve estar mapeado no arquivo
META-INF/config/<runningMode>/data_access.xml. Veja a seção 4.3.
[Raiz_Projeto]/
src/
META-INF/
config/
dev/
persistence.properties
data_access.xml
jdbc.properties
prod/
persistence.properties
data_access.xml
persistence-websphere.xml
Para adicionar um novo persistence unit ao arquivo data_access.xml, é necessário seguir os passos abaixo:
1. Configure o persistence unit padrão a ser usado pela aplicação. Isso significa que quando for feita a
configuração do @PersistenceContext sem definir o parâmetro unitName,o framework irá assumir que
deseja-se usar o persistence unit padrão. Adicione a configuração a seguir ao arquivo data_access.xml.
❶ Nome do persistence unit padrão. Este nome deve ser o mesmo nome, que é encontrado nos arquivos
persistence-<runningMode>.xml.
Adicione o namespace:
xmlns:util="http://www.springframework.org/schema/util"
<util:list id="persistentXmlPaths">
<value>${jpa.persistence.config.file}</value>❶
</util:list>
❶ Local o qual deve-se procurar o xml. <util:list/> elemento pode conter vários <value/>.
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
autowire-candidate="default">
<property name="persistenceUnitManager" ref="persistenceUnitManager"/>❶
<property name="persistenceUnitName" value="manager1"/>❷
</bean>
❶ Referencia ao PersistenceUnitManager. Este é um valor fixo, pois o próprio framework irá fornecer,
basta configurá-lo de acordo com o valor mostrado no exemplo acima.
❷ Nome do persistence unit que está definido nos arquivos persistence-<runningMode>.xml.
<util:map id="dataSources">
<entry key="datasource1">❶
<ref local="dataSource1"/>❷
</entry>
</util:map>
❶ Este é o nome do DataSource o qual está configurado no persistence unit que está definido nos
arquivos persistence-<runningMode>.xml.
❷ Este é o nome do DataSource que foi definido no passo anterior.
Nota
Caso seja necessário adicionar um novo persistence unit repita os passos a partir do passo 2.
(1.13.6)
Persistência
1. Verifique o local onde encontra-se o persistence unit da aplicação. Caso esteja no mesmo arquivo
persistence-<runningMode>.xml vá para o próximo passo. Caso não esteja adicione uma nova tag
<value/>. Como no exemplo abaixo:
<util:list id="persistentXmlPaths">
<value>${jpa.persistence.config.file}</value>
<value>classpath*:META-INF/persistence-2.xml</value>❶
</util:list>
2. Criar um EntityManagerFactory para o novo persistence unit. Adicione a configuração a seguir ao arquivo
data_access.xml.
<bean id="entityManagerFactory2"❶
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
autowire-candidate="default">
<property name="persistenceUnitManager" ref="persistenceUnitManager"/>
<property name="persistenceUnitName" value="manager2"/>❷
</bean>
❶ Nome do Entity Manager Factory. Para configurar um novo, acrescente um número ao lado do nome.
❷ Nome do persistence unit que será usado por este Bean. Este nome pode ser encontrado no arquivo
XML definido no passo anterior.
<util:map id="dataSources">
<entry key="datasource2">❶
<ref local="dataSource2"/>❷
</entry>
</util:map>
❶ Este é o nome do DataSource o qual está configurado no persistence unit que está definido nos
arquivos XML definido no primeiro passo.
❷ Este é o nome do DataSource que foi definido no passo anterior.
É possível definir o DataSource ou PersistenceUnit que será usado em um Dao específico. Para isso siga as
instruções definidas a seguir:
@Repository
public class FooDaoImpl extends GenericJpaDao<Foo, Long> implements FooDao {
@PersistenceContext(unitName="manager1")❶
public void setEntityManager(EntityManager em) {
this.em = em;
}
...
}
(1.13.6)
Persistência
@Repository
public class FooDaoImpl extends GenericJdbcDao implements FooDao {
@Autowired
public void initDataSource(@Qualifier("datasource1")❶
DataSource dataSource) {
setDataSource(dataSource);
}
...
}
8.1. Introdução
Esta camada é responsável por fazer o acesso ao servidor de mensagens MQ, encapsulando toda a
implementação de baixo nível para postar e consumir mensagens. O Framework pode fazer a interação com o
MQ de duas formas: síncrona e assíncrona.
O formato das mensagens pode ser binário ou texto (com qualquer codificação) ficando o tratamento do
formato de entrada ou saída de responsabilidade da própria aplicação. O Framework provê classes utilitárias,
para tratar alguns dos formatos mais comuns, como descrito no capítulo de classes utilitárias.
8.2. JMS
Java Message Service (JMS) é um padrão de mensagens que permite componentes de aplicações baseados em
Java Enterprise Edition a criar, enviar, receber e ler mensagens. Ela permite comunicações distribuídas que são
pouco acopladas, confiáveis e asíncronas.
Para usar JMS ou AEA é preciso configurar as filas a serem usadas. É necessário adicionar a referência aos
recursos no web.xml ou ejb-jar.xml conforme definido abaixo:
<resource-ref>
<description>Default ConnectionFactory</description>
<res-ref-name>jms/AEAConnectionFactory</res-ref-name>❶
<res-type>javax.jms.ConnectionFactory</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<resource-ref>
<description>Default AEA Request Queue</description>
<res-ref-name>jms/RequestAEA</res-ref-name>❷
<res-type>javax.jms.Queue</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<resource-ref>
<description>Default AEA Response Queue</description>
<res-ref-name>jms/ResponseAEA</res-ref-name>❸
<res-type>javax.jms.Queue</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms-2.5.xsd">
<jee:jndi-lookup id="jmsConnectionFactory"
jndi-name="java:comp/env/jms/AEAConnectionFactory"❶
expected-type="javax.jms.ConnectionFactory"
proxy-interface="javax.jms.ConnectionFactory"
lookup-on-startup="true"/>
<jee:jndi-lookup id="requestQueueAEA"
jndi-name="java:comp/env/jms/RequestAEA"❷
expected-type="javax.jms.Queue"
proxy-interface="javax.jms.Queue"
lookup-on-startup="true"/>
<jee:jndi-lookup id="responseQueueAEA"
jndi-name="java:comp/env/jms/ResponseAEA"❸
expected-type="javax.jms.Queue"
proxy-interface="javax.jms.Queue"
lookup-on-startup="true"/>
</beans>
Para configurar aplicações que irão executar fora do ambiente do Websphere é necessário adicionar as
bibliotecas do MQ disponíveis no Framework e configurar o arquivo
META-INF/config/<runningMode>/jms.xml de acordo com o exemplo abaixo:
<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"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.5.xsd">
<util:constant id="queueTargetClient"
static-field="com.ibm.mq.jms.JMSC.MQJMS_CLIENT_NONJMS_MQ"/>
<util:constant id="transpType"
static-field="com.ibm.mq.jms.JMSC.MQJMS_TP_CLIENT_MQ_TCPIP"/>
<bean id="connectionFactoryTarget"
class="com.ibm.mq.jms.MQConnectionFactory">❸
<property name="hostName" value="10.9.8.250"></property>❹
<property name="port" value="1414"></property>❺
<property name="transportType" ref="transpType"></property>
<property name="queueManager" value="QM1"></property>❻
<property name="channel" value="SYSTEM.DEF.SVRCONN"/>❼
</bean>
<bean id="connectionFactory"
class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
<property name="targetConnectionFactory" ref="connectionFactoryTarget"/>
<property name="username" value=" "/>❽
</beans>
8.2.3. AbstractMessagingGateway
A api JMS possui uma grande variedade de classes e operações que podem ser usadas para enviar mensagens.
Com o objetivo de formalizar e padronizar algumas operações, o Framework provê a classe
com.altec.bsbr.fw.jms.AbstractMessagingGateway que deve ser estendida quando houver a necessidade de
comunicar-se com filas de maneira estendida e confiável. Com ela é possível:
A classe que estender AbstractMessagingGatewayImpl deverá definir as filas e a connection factory a serem
usadas, implementando os métodos abstratos getConnectionFactory(), getRequestQueue() e
getResponseQueue().
Para obter os recursos definidos no arquivo de configuração, basta usar autowiring nos atributos e passa-los
para os métodos que serão implementados. A classe precisa da anotação
org.springframework.stereotype.Component para ser carregada pelo Spring e ter suas dependências
injetadas. Exemplo abaixo:
@Component
public class FooMessaginGatewayImpl extends AbstractMessagingGateway implements FooMessagingGateway {
@Autowired
@Qualifier("requestQueue")❶
private Queue requestQueue;
@Autowired
@Qualifier("responseQueue")❷
private Queue responseQueue;
@Autowired
@Qualifier("jmsConnectionFactory")❸
private ConnectionFactory connectionFactory;
@Override
public Queue getRequestQueue() {❹
return requestQueue;
}
@Override
public Queue getResponseQueue() {❺
return responseQueue;
}
@Override
public ConnectionFactory getConnectionFactory() {❻
return connectionFactory;
}
}
Nota
É permitido que se retorne null para uma fila, caso caso ela não seja usada.
Nota
É permitido que se utilize a mesma fila tanto para request quanto para response
• setTimeOut(long timeOut): define o timeout (em milisegundos) das operações que farão receive de uma
mensagem da fila.
Este método não aguarda o retorno do processamento da mensagem no destino. Ele apenas posta a
mensagem na fila de request.
Nota
Para o uso deste método é necessário certificar-se que a mensagem sendo recebida é do tipo
javax.jms.TextMessage.
(1.13.6)
Mensageria
Nota
Para o uso deste método é necessário certificar-se que as mensagens sendo recebidas são do
tipo javax.jms.TextMessage.
• String sendAndReceiveTextMessage(String msg): Envia uma mensagem de texto para a fila de Request
e recebe a mensagem de resposta da fila de Response utilizando o JMSCorrelationID.
Abaixo estão descritas algumas formas de uso desta classe nos diferentes contextos: síncrono e assíncrono.
8.2.3.1. ExtendedMessageCreator
Exemplo:
@Component
public class FooMessagingGatewayImpl extends AbstractMessagingGateway implements FooMessagingGateway {
8.2.3.2. SelectorCreator
A classe com.altec.bsbr.fw.jms.aea.SelectorCreator é usada para fornecer o selector que será usado para
filtrar as mensagens de retorno.
Exemplo:
(1.13.6)
Mensageria
@Component
public class FooMessagingGatewayImpl extends AbstractMessagingGateway implements FooMessagingGateway {
O modelo síncrono de troca de mensagens é do tipo request/response, onde o cliente envia a mensagem numa
fila e fica esperando a resposta em outra fila. O Framework possui um componente que provê essa
funcionalidade para ser reutilizada em qualquer aplicação.
Para o usuário do componente, a chamada será apenas um método síncrono, simples de ser usado. O
Framework se encarregará de postar a mensagem e registrar um listener para esperar a resposta, fazendo a
sincronização. O diagrama de seqüencia a seguir mostra esse mecanismo.
O cliente pode especificar um tempo máximo de espera para a resposta chegar. Caso esse tempo seja
ultrapassado, não retornará nenhuma mensagem.
Exemplo:
@Component
public class EnviarReceberMessageGatewayImpl extends AbstractMessagingGateway
implements EnviarReceberMessageGateway {
@Autowired
@Qualifier("requestQueue")❶
private Queue requestQueue;
@Autowired
@Qualifier("responseQueue")❷
private Queue responseQueue;
@Autowired
@Qualifier("jmsConnectionFactory")❸
private ConnectionFactory connectionFactory;
@Override
public Queue getRequestQueue() {❹
return requestQueue;
}
@Override
public Queue getResponseQueue() {❺
return responseQueue;
}
@Override
public ConnectionFactory getConnectionFactory() {❻
return connectionFactory;
}
ExtendedMessageCreator<TextMessage> messageCreator = ❼
new ExtendedMessageCreator<TextMessage>() {
public void setParams(TextMessage textMessage) {
textMessage.setText(message);
}
};
SelectorCreator selector = ❽
new SelectorCreator() {
public String createSelector(Message reqMessage) {
return "JMSCorrelationID = '+reqMessage.getJMSMessageID()+'";
}
}
❾ sendAndReceiveTextMessage método usado para enviar mensagens síncronas e receber apenas uma
mensagem de resposta, há também o método sendAndReceiveMessage. Caso seja necessário receber
várias mensagens particionadas usar o método:
sendAndReceiveMessages(ExtendedMessageCreator<?> messageCreator, SelectorCreator
selectorCreator, int maxMsgCount, long timeOut): java.util.List<javax.jms.Message>.
No modelo assíncrono, o cliente posta uma mensagem na fila e não precisa esperar por uma resposta. O
framework possui uma API para postar mensagens em uma fila e abstrair os detalhes de baixo nível do JMS.
A recepção de mensagens no listener, poderá fazer parte de um contexto transacional compartilhado com outros
recursos (como banco de dados) se todos os drivers envolvidos forem XA compliant.
No envio assíncrono não há resposta, desta maneira a mensagem pode ser processada em um processo
separado. De acordo com o diagrama de sequência a seguir:
Abaixo estão descritos os passos para criar um gateway e enviar uma mensagem assincrona:
(1.13.6)
Mensageria
@Component
public class EnviarMessagingGatewayImpl extends AbstractMessagingGateway implements EnviarMessagingGateway {
@Autowire
@Qualifier("filaRequest")
private Queue requestQueue;
@Service
public class FooServiceImpl implements FooService {
@Autowired
private EnviarMessagingGateway enviarMessagingGateway;
Para receber mensagens assincronamente basta registrar um objeto que implemente a interface
javax.jms.MessageListener, que ele será invocado para tratar a mensagem assim que ela chegar (de modo
semelhante a MDB's).
@Component
public class ReceberMensagemAssincListener implements javax.jms.Listener {
public void onMessage(Message message) {
//processar mensagem
}
}
(1.13.6)
Mensageria
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms-2.5.xsd">
<bean id="requestQueueAEA.queueName"
class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>❶
<bean id="jmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsConnectionFactory"/>
<property name="destinationName" ref="requestQueueAEA.queueName"></property>❷
<property name="messageListener" ref="receberMensagemAssincListener"/>❸
</bean>
</beans>
❶ Local onde definimos o nome que será mapeado para a fila. Através deste recurso obtemos o nome
da Fila a qual utilizaremos para configurar a fila de acesso.
❷ Local onde é mapeada a fila a ser usada.
❸ Nome do listener criado anteriormente que irá processar as mensagens assíncronamente.
Para mais informações sobre como registrar novos listeners: Asynchronous Message - Spring JMS.
Para utilizar listeners que utilizem JTA para controle de transação é necessário configurar o arquivo
META-INF/config/prod/jms.xml da seguinte maneira:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms-2.5.xsd">
<bean id="requestQueue"
class="org.springframework.jndi.JndiObjectFactoryBean">❶
<property name="expectedType" value="javax.jms.Queue"/>
<property name="proxyInterfaces" value="javax.jms.Queue"/>
<property name="jndiName" value="java:comp/env/jms/RequestQueue"/>❷
<property name="lookupOnStartup" value="true"/>
</bean>
<bean id="responseQueue"
class="org.springframework.jndi.JndiObjectFactoryBean">❸
<property name="expectedType" value="javax.jms.Queue"/>
<property name="proxyInterfaces" value="javax.jms.Queue"/>
<property name="jndiName" value="java:comp/env/jms/ResponseQueue"/>❹
<property name="lookupOnStartup" value="true"/>
</bean>
<bean id="requestQueue.queueName"
class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>❺
<bean id="jmsConnectionFactory"
class="org.springframework.jndi.JndiObjectFactoryBean">❻
<property name="expectedType" value="javax.jms.ConnectionFactory"/>
<property name="proxyInterfaces" value="javax.jms.ConnectionFactory"/>
<property name="jndiName" value="java:comp/env/jms/ConnectionFactory"/>❼
<property name="lookupOnStartup" value="true"/>
</bean>
<bean id="taskExecutor"
class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">❽
<property name="workManagerName" value="wm/default"/>
</bean>
<bean id="jmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">❾
<property name="connectionFactory" ref="jmsConnectionFactory"/>
<property name="destinationName" ref="requestQueue.queueName"></property>
<property name="messageListener" ref="sampleListener"/>
<property name="taskExecutor" ref="taskExecutor"></property> ❿
<property name="transactionManager" ref="transactionManager"></property> 11
<property name="recoveryInterval" value="5000"/> 12
<property name="concurrentConsumers" value="1"/> 13
<property name="maxConcurrentConsumers" value="1"/> 14
</bean>
</beans>
Nota
Para utilizar vários listeners é necessário
cadastrar um
org.springframework.jms.listener.DefaultMessageListenerContainer para cada fila de
request.
Para maiores detalhes de parâmetros que podem ser passados ao message listener acesse: Spring Doc -
DefaultMessageListenerContainer
8.3. MDB
Importante
A utilização Framework Jab com EJB's é um pouco diferente de utilizar o Framework como um
projeto WEB. Dentro de um projeto EJB o Framework perde algumas funcionalidades que dão
produtividade aos desenvolvedores, como: Registro automático de serviços, componentes e
recursos de Banco de dados e injeção automática de dependências via Annotations. Isso se deve
pelo fato do ClassLoader de uma aplicação EJB ser diferente de uma aplicação WEB comum.
"Manualmente", se entende por declarar cada classe concreta, ou seja, apenas classes, não
interfaces Java, em um arquivo xml que somente está presente em aplicações EJB. Este arquivo
está localizado em [Raiz-projeto]/src/META-INF/config/app-beans.xml.
Abaixo mostramos um exemplo de como deverá ser feita a declaração manual de um serviço:
Para receber mensagens assincronamente você também pode criar um MDB de acordo com o exemplo abaixo:
Nota
Para MDB's é necessário criar as classes do MDB dentro do diretório [Raiz-Projeto]/src-ejb.
@Override
protected void onEjbCreate() {
delegate = (MessageListener) getBeanFactory().getBean("delegate");
}
Nota
Note que é necessário utilizar o método getBeanFactory() da classe para obter as dependências.
Para mais informações sobre a criação de MDB's no WebSphere consulte o IBM Info Center.
(1.13.6)
Mensageria
cliente envia a mensagem numa fila e fica esperando a resposta em outra fila.
Para enviar mensagens utilizando a Arquitetura Estendida Altair (AEA) deve-se primeiramente criar a classe de
request (que será mapeada para o formato de envio ao Altair) e as classes de response (que serão mapeadas para
os formatos de resposta do Altair).
Para criar a classe mapeada de envio basta seguir os passos abaixo, suponhamos uma determinada transação
PE47:
<?xml version="1.0"?>
<requestMsg>
<dse_operationName>PE47</dse_operationName>
<dse_formattedData>
<kColl id="entryData">
<field id="Usuario" value="NXCARDP"/>
<field id="PENOMPE" value="FABRICIO"/>
<field id="PETIPPE" value="F"/>
<field id="PECODEN" value="0033"/>
</kColl>
</dse_formattedData>
<dse_processParameters>LIST</dse_processParameters>
</requestMsg>
Para o formato acima criaremos uma classe que mapeie todos os fields (campos) para um POJO, como
demonstramos abaixo:
@AeaRequestMessage(operationName = "PE47")
public class Pe47AeaRequest {
@AeaField
private String PENOMPE;
@AeaField
private String PETIPPE;
@AeaField(validator = PECODENValidator.class)
private String PECODEN;
// getters e setters
...
}
Nota
É possível customizar o nome do atributos para um nome diferente do que foi definido no
FORMATO da AEA, basta utilizar o atributo id da annotation
com.altec.bsbr.fw.record.adapters.aea.annotation.AeaField.
Nota
Observe que para alguns campos onde é necessário que haja uma conversão, e validação do tipo
que vem do Altair para as classes Java, basta indicar na seguinte anotação
com.altec.bsbr.fw.record.adapters.aea.annotation.AeaField, os atributos converter e
validator, assim como definido abaixo.
(1.13.6)
Mensageria
com.altec.bsbr.fw.converter.BigDecimalConverter java.math.BigDecimal
com.altec.bsbr.fw.converter.YYYYMMDDDateConverter
Converter do formato yyyy-MM-dd para
um java.util.Date.
Todas as classes de
validação deverão estender a classe
com.altec.bsbr.fw.validator.BaseValidator.
Importante
Para os tipos não existentes deve-se criar um novo converter estendendo de
com.altec.bsbr.fw.converter.BaseConverter
Nota
O campo dse_operationName (<dse_operationName>PE47</dse_operationName>) pode ser
informado implementando o método getOperation() definido na interface AeaMessagingGateway
Nota
O campo Usuario (<field id="Usuario" value="NXCARDP"/>) deve ser informado por um bean
chamado aeaUserName, definido no arquivo META-INF/config/aea.xml como o exemplo que
segue:
E para cada formato de retorno criaremos uma classe, conforme a demonstração a seguir:
@AeaReplyMessage
public class PEM2650 {
@AeaField
private String PENUMPE; // NRO.PERSONANRO.PERSONA - A8
@AeaField
private String PECALPA; // CAL.PARCAL.PAR - A2
@AeaField(converter = com.altec.bsbr.fw.converter.TimestampDateConverter.class)
private Date PEHSTAM; // TIMESTAMPTIMESTAMP - A26
//getters e setters
...
}
...
private Map<String, Class<?>> aeaReplyFormats;
{
HashMap<String, Class<?>> map = new HashMap<String, Class<?>>();
map.put("PEM2650"❶, PEM2650.class❷);
map.put("PEM2690", PEM2690.class);
aeaReplyFormats = Collections.unmodifiableMap(map);
}
...
<kColl>
<field id="DC_FORMATO" value="PEM2650"/>
@Service
public class ExemploServiceImpl implements ExemploService {
@Autowired
private Pe47MessagingGateway<Pe47AeaRequest> pe47Messaging;
if (pe47Messaging.getWarnings() != null) {
for (AEAWarning warning : pe47Messaging.getWarnings())
// Processando warnings
}
// processa resultados
}
catch (AeaException ex) {
// tratamento de exceções
}
}
...
}
Note que para obtermos os resultados do envio existem dois principais métodos:
• getList(Class): Este método retorna os formatos encontrados que equivalem ao tipo fornecido.
Tabela 8.2. Exceções que podem ser lançadas durante o processamento das respostas
ParseException
AEA respondeu com um Não há como continuar a leitura da resposta, o
XML inválido. Problema processamento é interrompido.
com o formato da resposta.
MessageErrorException
AEA respondeu com a existe um método getErrors() que retorna uma
indicação de um erro (status lista de AeaError, com o identificador do erro e
NOK). sua descrição.
ValidateException
Problema com o valor de Um validador foi definido para o campo, e o
um determinado campo valor indicado não é válido, portanto o
processamento é interrompido, pois a mensagem
não atende o esperado.
Caso aconteça algum erro (tratável) durante o parsing, a descrição desses erros pode ser recuperada chamando
o método getParsingErrors().
(1.13.6)
Capítulo 9. Web Services
9.1. Introdução
Para o uso de web services, o Framework adotou o "WebSphere Application Server Version 6.1 Feature Pack
for Web Services" que adiciona suporte ao padrão de nova geração JAX-WS 2.0 (Java API for XML-Based
Web Services) ao servidor de aplicações. Usando o modelo de programação do JAX-WS, o desenvolvimento
de serviços e clientes web é simplificado através do uso de anotações padronizadas.
Nota
O padrão JAX-WS veio para substituir o antigo JAX-RPC. Os dois modelos podem ser usados,
mas é recomendado o JAX-WS, pela sua facilidade de desenvolvimento através de anotações.
O JAX-WS usa o padrão JAXB (Java Architecture for XML Binding), uma especificação Java que provê uma
maneira fácil e conveniente de mapear classes Java e XML para facilitar o desenvolvimento de web services.
Para uma descrição detalhada consulte o documento Using JAXB for XML data binding.
O termo service endpoint interface (SEI) é usado no âmbito do Java Enterprise Edition quando um JavaBean é
exposto como um web service. Através dele pode ser gerado o WSDL que define os métodos de um web
service em particular.
Para desenvolver web services baseados no modelo de programação JAX-WS, pode ser usada uma técnica
"bottom-up", onde o desenvolvimento é iniciado a partir de um JavaBean, que pode ser abilitado a ser um
serviço web, adicionando a anotação @WebService a classe. Não é necessário criar o WSDL do web service
pois isso será feito automaticamente pelo servidor no momento de deploy da aplicação.
Vejamos a situação onde será exportado um serviço da camada de negócios do Framework, chamado
HelloService. Precisamos criar um endpoint que servirá como uma fachada para esse serviço, delegando a
execução dos métodos para a implementação do serviço.
Descrição
2 O endpoint deve ter o mesmo nome do serviço com o sufixo Endpoint. Para um servico
HelloService, o endpoint será HelloEndpoint.
Descrição
org.springframework.web.context.support.SpringBeanAutowiringSupport.
8 A implementação do serviço deve ser obtida no atributo da classe com a anotação @Autowired. Se
houver mais de um bean implementando a mesma interface, usar também a anotação @Qualifier.
Nota
Uma classe com a anotação @WebService é instanciada pelo servidor de aplicações e não pelo
Spring. Por isso é necessário o endpoint estender SpringBeanAutowiringSupport, para que ele
possa ter acesso ao serviços e outros beans disponíveis no contexto do Spring através da anotação
@Autowired.
package com.altec.bsbr.app.demo.webservice;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import com.altec.bsbr.app.demo.service.HelloService;
@WebService
public class HelloEndpoint extends SpringBeanAutowiringSupport implements HelloService {
@Autowired
private HelloService service;
@WebMethod
public String sayHello(@WebParam(name = "name")String name) {
return service.sayHello(name);
}
Quando a interface desejada do web service não for exatamente a do serviço, deve ser criada uma interface
adicional, para uso exclusivo do web service.
Importante
A criação de uma interface diferenciada para o web service, distinta da interface do serviço de
negócio, pode ser necessária. Neste caso, o web service funciona como uma fachada para o serviço
propriamente dito, fazendo a adaptação de uma interface para a outra.
Dessa maneira, desacoplamos o serviço de negócio do cliente, através da fachada de web service.
As vantagens do descoplamento são:
1. Evitamos expor tipos ou estruturas de dados internos do serviço aos clientes. Assim
conseguimos maior abstração do serviço.
3. Podem existir diferentes versões do mesmo web service, usando o mesmo serviço de negócio.
Isso permite uma evolução segura do contrato do serviço com os clientes.
4. Pode ser necessário adaptar a interface do web service para clientes específicos. Podemos
modificar apenas a fachada, através de ajustes do WSDL e XSD, deixando o serviço de
negócio intacto.
A construção do endpoint é similar ao descrito anteriormente. Os itens abaixo marcados com (*) são as
diferenças:
Tabela 9.2. Construção de um Service Endpoint Interface para um web service exclusivo
Descrição
0 (*) A interface deve ter o mesmo nome do serviço com o sufixo WebService. Para um serviço
HelloService, a interface será HelloWebService. O pacote deve ser o mesmo da interface original.
2 O endpoint deve ter o mesmo nome do serviço com o sufixo Endpoint. Para um serviço
HelloService, o endpoint será HelloEndpoint.
8 A implementação do serviço deve ser obtida no atributo da classe com a anotação @Autowired. Se
houver mais de um bean implementando a mesma interface, usar também a anotação @Qualifier.
9 (*) Os métodos do endpoint devem transformar os dados de entrada e saída do formato da interface
web para o formato usado na implementação do serviço da camada de negócios.
Quando for necessário passar uma exceção de negócio do servidor para o cliente, de modo que essa informação
esteja presente no WSDL, basta criar uma exceção que contenha a anotação @WebFault. O Framework já possui
uma exeção desse tipo, chamada com.altec.bsbr.fw.webservice.WebServiceException, e que pode ser
usada por qualquer aplicação na implementação de um endpoint:
WSDL:
<definitions name="TestEndpointService"
targetNamespace="http://webservice.abc.app.bsbr.altec.com/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://webservice.abc.app.bsbr.altec.com/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
...
<message name="WebServiceException">
<part name="fault" element="tns:WebServiceException"/>
</message>
...
<portType name="TestEndpoint">
...
<operation name="getCd">
<input message="tns:getCd"/>
<output message="tns:getCdResponse"/>
<fault name="WebServiceException" message="tns:WebServiceException"/>
</operation>
</portType>
<binding name="TestEndpointPortBinding" type="tns:TestEndpoint">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="getCd">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
<fault name="WebServiceException">
<soap:fault name="WebServiceException" use="literal"/>
</fault>
</operation>
</binding>
...
</definitions>
XSD:
<xs:schema version="1.0">
...
<xs:complexType name="faultInfo">
<xs:sequence>
<xs:element name="code" type="xs:int" minOccurs="0"/>
<xs:element name="message" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
...
</xs:schema>
No exemplo a seguir vamos criar uma nova interface web para o servico ProductService.
(1.13.6)
Web Services
package com.altec.bsbr.app.demo.service;
package com.altec.bsbr.app.demo.service;
package com.altec.bsbr.app.demo.webservice;❶
import javax.jws.WebMethod;
import javax.jws.WebService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
@WebService
public class ProductEndpoint❷ extends SpringBeanAutowiringSupport implements ProductWebService❸ {
@Autowired
ProductService service;❹
@WebMethod❺
void createProduct(Long code, String name, BigDecimal price, String description)
throws WebServiceException {
Para obter maiores informações de como criar um web service consulte a documentação do WebSphere para
JAX-WS, onde é possível encontrar todas as opções para a definição do WSDL desejado.
(1.13.6)
Web Services
Para essa situação, o padrão JAX-WS oferece uma ferramenta chamada wsimport que pode apenas a partir do
WSDL gerar todas as classes necessárias para consumir o web service.
O padrão JAX-WS possui uma implementação de referência que pode ser instalada separadamente de um
servidor de aplicações. Assim podemos usar mais facilmente ferramentas como o wsimport. O pacote de
instalação do JAX-WS Reference Implementation pode ser baixado do site da Sun, e instalado na máquina de
desenvolvimento.
Outra opção é usar o wsimport que vem no Web Services Feature Pack para o WebSphere 6.1.
O wsimport é uma ferramenta de linha de comando muito simples. Basta apontar para o WSDL (que pode ser
um arquivo ou uma URL) que será gerado um cliente Java que que poderá ser usado para acessar o web
service. Normalmente, serão gerados as seguintes classes:
• Uma classe principal que é o ponto de acesso ao web service, que estende javax.xml.ws.Service.
• Uma interface com os métodos do serviço web. Um objeto que implemente esta interface é fornecido pelo
objeto do tipo Service, citado acima.
• Um objeto do tipo request, que representa os dados de entrada de um dos métodos do serviço web.
• Um objeto do tipo response, que representa os dados de saída de um dos métodos do serviço web.
• Quaisquer outros objetos que representem a estrutrua interna dos objetos de request e response.
• Uma classe ObjectFactory, de uso interno, para criar os objetos do tipo request e response citados acima.
Todas essas classes devem ser geradas num package Java específico para cada web service, de modo a
mante-los separados e não haver conflitos. O package padrão do Framework é:
com.altec.bsbr.app.[sistema].webclient.[nome_servico]
A sintaxe do comando wsimport é wsimport [opções] [wsdl], e os parâmetros mínimos que devem ser
passados são:
Opção Descrição
-target 2.0 Especifica que o código gerado deve ser compatível com o JAX-WS
versão 2.0, que é a usada pelo WebSphere.
Existem mais opções, que podem ser consultadas na documentação oficial do JAX-WS.
Como exemplo, vamos criar o cliente de um web service disponível na Internet, que faz uma validação de
endereço de e-mail, desenvolvido com tecnologia .Net, chamado XWebEmailValidation.
parsing WSDL...
generating code...
compiling code...
EmailValidation.java
ObjectFactory.java
package-info.java
ValidateEmailRequest.java
ValidateEmailResponse.java
XWebEmailValidationInterface.java
Para maiores informações veja Developing a JAX-WS client from a WSDL file.
Nota
Um proxy é um objeto que representa uma referência ao serviço remoto, tornando o acesso
indireto, mas de maneira transparente para o usuário.
Propriedade Descrição
serviceInterface Interface Java que o proxy deve implementar. É como os clientes verão o objeto.
lookupServiceOnStartup Indica se o serviço JAX-WS será verificado no início da aplicação. Caso seja
false, ele será do tipo "lazy", consultado apenas no primeiro acesso.
Para um cliente acessar um web service, basta declarar um bean que será o proxy do web service. No exemplo a
seguir fazemos a configuração de um proxy para o serviço Hello, e para o serviço EmailValidation, definido
anteriormente neste capítulo.
Arquivo META-INF/config/ws-clients.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
❶ Usamos a interface criada na própria a aplicação servidora. É a mesma que foi usada na declaração do
endpoint.
❷ Aqui, devemos usar a interface que foi criada pelo wsadmin.
As propriedades usadas no xml podem ficar definidas em arquivos separados para cada <runningMode> como
mostrado no exemplo a seguir:
Arquivo META-INF/config/desenv/ws-clients.properties:
hello.host=localhost
hello.port=8080
(1.13.6)
Web Services
Arquivo META-INF/config/prod/ws-clients.properties:
hello.host=server
hello.port=9080
Quando o web service precisar ser acessado pelo cliente, basta obter uma referência como se ele fosse um
serviço normal:
@Service
public class ExemploServiceImpl implements ExemploService {
@Autowired
private HelloService service;
@Autowired
private XWebEmailValidationInterface emailService;
return res.getStatus();
}
Dica
Se houver algum outro serviço local que também implemente a interface HelloService, será
necessário acrescentar a anotação @Qualifier("helloClient") para obter uma referência ao web
service cliente.
(1.13.6)
Capítulo 10. Segurança
10.1. Introdução
Este capítulo mostra como usar os recursos de segurança oferecidos pelo Framework: autenticação,
autorização, criptografia e auditoria.
10.2. Autenticação
A autenticação na rede corporativa acontence de forma transparente para os usuários, desde que tenha sido feito
login na estação com usuário/senha válidos para a rede Windows.
O Framework implementa autenticação que permite as aplicações Java fazerem parte da rede single sign on,
desde que acessadas a partir do Portal Corporativo.
O Portal autentica o usuário e envia para a aplicação Java um ticket se segurança assinado, contendo o nome do
usuário e o código do sistema sendo acessado. Com esses dados um servlet filter verifica a validade dos dados e
a autenticidade do usuário, criando uma HttpSession e permitindo que ele acesse o sistema.
• Para configurar a autenticação para utilizar o Header Http enviado pelo IIS utilize a classe
com.altec.bsbr.fw.security.authentication.filter.SmartAuthFilter. Exemplo:
10.2.2. AuthFilter
<filter>❶
<filter-name>authFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>authFilter</param-value>
</init-param>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>authFilter</filter-name>
<url-pattern>/pages/*</url-pattern>❷
</filter-mapping>
❶ Definição do AuthFilter.
❷ Configuração do mapeamento do filtro. Determina qual o inicio da árvore de diretórios que devem ser
protegidos
Nota
Note que existem vários recursos que não devem ser protegidos, como arquivos CSSs, por
exemplo.
10.2.3. SmartAuthFilter
Importante
O Filtro abaixo somete poderá ser usado caso o ambiente seja:
<filter>❶
<filter-name>authFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>authFilter</param-value>
</init-param>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
<!--
Parametros usados por com.altec.bsbr.fw.security.authentication.filter.AuthFilter -->
<init-param>
<param-name>com.altec.bsbr.fw.authentication.modo</param-name>❷
<param-value>LOCAL</param-value>
</init-param>
<init-param>
<param-name>com.altec.bsbr.fw.authentication.sigla</param-name>❸
<param-value>SIGLA</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>authFilter</filter-name>
<url-pattern>/pages/*</url-pattern>❹
</filter-mapping>
❶ Definição do SmartAuthFilter.
❷ Mode de autenticação: LOCAL - valida usuario logado no windows diretamente no framework
❸ Sigla do sistema.
❹ Configuração do mapeamento do filtro. Determina qual o inicio da árvore de diretórios que devem ser
protegidos
Nota
Note que existem vários recursos que não devem ser protegidos, como arquivos CSSs, por
exemplo.
Com o uso destes filtros, a aplicação pode ser toda construída com a suposição de que o usuário já está
autenticado.
10.3. Autorização
A autorização foi desenvolvida para facilitar e aproveitar os recursos do MBS, sistema utilizado atualmente
para a autorização nas aplicações.
Com os recursos fornecidos pelo framework é possível criar menus diferenciados, baseados no perfil de acesso
do usuário, simplemente usando um componente JSF, ou restringir o acesso a métodos específicos da camada
de serviços com o uso de uma anotação.
Caso a aplicação necessite de um menu hierárquico personalizado para cada usuário, deve ser usado o MBS.
Para tanto, é necessário configurar o MBS com as opções desejadas.
Existe uma planilha padrão para ser cadastrada no MBS, que deve ser preenchida com os dados de perfis,
hierarquia para o menu e funções (métodos de serviço) da aplicação.
Cada usuário do sistema deve ser associado a um perfil, e o menu e funções devem ser associados aos perfis,
assim, dependendo do perfil do usuário o menu apresentado é diferenciado, contendo somente as operações
permitidas.
A hierarquia do menu deve ter como "raíz" o nome da aplicação, e todos os itens do menu são "filhos" dela.
Todos os itens devem ter o nome do item pai, um nome único, uma descrição e uma URL. Caso não exista
URL associada a um item, ele simplesmente aparecerá no menu como um agrupamento de funções. As URLs
indicam qual página será chamada, no caso uma chamada para a servlet do ICEfaces, como relatorio.iface.
Exemplo de planilha:
JAB - Aplicação de - A B C
referência
(1.13.6)
Segurança
- JAB-RELATORIO Relatórios - A B -
Para usar o menu obtido do MBS na aplicação web, veja a seção Menu de acesso configurado pelo MBS.
Com o uso do Framework é possível fazer também autorização nos métodos da camada de serviços. Desse
modo podemos ter um controle mais granular da segurança de cada serviço, principalmente quando eles forem
chamados por outros sistemas e não pela camada web.
A autorização dos métodos de serviço será feita automaticamente se eles forem anotados com
com.altec.bsbr.fw.annotation.Authorize. A autorização será feita em um objeto do tipo SecurityInfo,
que deve ser passado como parâmetro do método do serviço. Abaixo segue um exemplo do uso de
SecurityInfo para indicar que o método de serviço deve ser autorizado:
@Service
public class TestServiceImpl implements TestService {
@Authorize
public String sayHello(SecurityInfo secInfo, String name) {
return "Hello " + name + "!";
}
}
Uma verificação de autorização será sempre feita, para saber se o usuário passado no parâmetro secInfo tem a
permissão de chamar o método. Caso ele não tenha permissão, será lançada a exceção
com.altec.bsbr.fw.security.authorization.AuthorizationException.
De modo análogo ao menu, os métodos devem ser listados na planilha, mas não devem ter URL nem item
"pai":
JAB JAB-TestServiceImpl.sayHello
Método para testes de - - B -
autorização de serviços
Importante
O valor do campo Funções da planílha não devem ter mais do que 64 caracteres. Ou seja, a soma
do Nome da Classe do Serviço + Nome do método + 1 NÃO deve ser maior do que 64 caracteres.
(1.13.6)
Segurança
MBS:
Durante o desenvolvimento, pode ser inconveniente acessar sempre o MBS. Para isso, pode ser usada a classe
DummyAuthorization, que retorna sempre true para a autorização e possui um menu de exemplo, podendo ser
estendida de acordo com a conveniência do programador. Para usar este autorizador, basta colocar um arquivo
authorization.xml que usa o DummyAuthorization no diretório do running mode de desenvolvimento, e outro
authorization.xml com o MBSAuthorization no diretório dos running modes onde ele deve ser usado.
Arquivo META-INF/conf/prod/authorization.xml:
<!--
=======================================================================================
--> <!-- MBS --> <!--
=======================================================================================
-->
<bean class="com.altec.bsbr.fw.security.authorization.impl.MBSAuthorization">
<property name="url" value="http://wsdotnetdes.sb:81/mbs-ext/mbscomi.asmx"/>
<property name="cacheSize" value="1000"/>
<property name="timeout" value="1200000"/>
</bean>
Arquivo META-INF/conf/dev/authorization.xml:
<bean
class="com.altec.bsbr.fw.security.authorization.impl.DummyAuthorization">
</bean>
Nota
Se o programador precisar de algum uso avançado do MBS, o método
Authorization.getAuthorizationProvider() retorna uma instância de MBSCaller que pode ser
usada diretamente para chamar o web service do MBS.
10.4. Criptografia
Para as funcionalidades de criptografia, o Framework Java Altec Brasil se baseia no padrão Java Cryptography
Architecture suportado pelo Java 5 Standard Edition.
No entanto, para essa tarefa foi incluída a biblioteca open source Jasypt, que torna mais simples o uso de
capacidades de encriptação/decripatção, sem a necessidade de conhecimentos profundos de criptologia.
Está fora do escopo deste documento explicar conceitos de criptografia ou mesmo documentar a biblioteca
Jasypt. O intuito é apresentar algumas das suas funcionalidades básicas. Maiores informações podem ser
obtidas abaixo:
10.4.1. Digesting
...
Digester digester = new Digester();
digester.setAlgorithm("SHA-1");
...
byte[] digest = digester.digest(message);
...
...
BasicPasswordEncryptor passwordEncryptor = new BasicPasswordEncryptor();
String encryptedPassword = passwordEncryptor.encryptPassword(userPassword);
...
if (passwordEncryptor.checkPassword(inputPassword, encryptedPassword)) {
// correto!
} else {
// senha incorreta!
}
...
...
StrongPasswordEncryptor passwordEncryptor = new StrongPasswordEncryptor();
String encryptedPassword = passwordEncryptor.encryptPassword(userPassword);
...
if (passwordEncryptor.checkPassword(inputPassword, encryptedPassword)) {
// correto!
} else {
// senha incorreta!
}
...
...
ConfigurablePasswordEncryptor passwordEncryptor = new ConfigurablePasswordEncryptor();
passwordEncryptor.setAlgorithm("SHA-1");
passwordEncryptor.setPlainDigest(true); String encryptedPassword =
passwordEncryptor.encryptPassword(userPassword);
...
if (passwordEncryptor.checkPassword(inputPassword, encryptedPassword)) {
// correto!
} else {
// senha incorreta!
}
...
Dependendo do algoritmo escolhido, pode ser necessária a instalação do Java Cryptography Extension
(JCE) Unlimited Strength Jurisdiction Policy Files.
(1.13.6)
Segurança
BigInteger. Antes de ser usado, ele precisa receber a senha que sera usada.
...
BasicIntegerNumberEncryptor integerEncryptor = new BasicIntegerNumberEncryptor();
integerEncryptor.setPassword(myEncryptionPassword);
...
BigInteger myEncryptedNumber = textEncryptor.encrypt(myNumber);
...
BigInteger plainNumber = textEncryptor.decrypt(myEncryptedNumber);
...
Dependendo do algoritmo escolhido, pode ser necessário a instalação do Java Cryptography Extension
(JCE) Unlimited Strength Jurisdiction Policy Files.
Dependendo do algoritmo escolhido, pode ser necessário a instalação do Java Cryptography Extension
(JCE) Unlimited Strength Jurisdiction Policy Files.
...
StrongDecimalNumberEncryptor decimalEncryptor = new StrongDecimalNumberEncryptor();
decimalEncryptor.setPassword(myEncryptionPassword);
...
BigDecimal myEncryptedNumber = decimalEncryptor.encrypt(myNumber);
...
BigDecimal plainNumber = decimalEncryptor.decrypt(myEncryptedNumber);
...
(1.13.6)
Segurança
...
BasicBinaryEncryptor binaryEncryptor = new BasicBinaryEncryptor();
binaryEncryptor.setPassword(myEncryptionPassword);
...
byte[] myEncryptedBinary = binaryEncryptor.encrypt(myBinary);
...
String plainBinary = binaryEncryptor.decrypt(myEncryptedBinary);
...
Dependendo do algoritmo escolhido, pode ser necessária a instalação do Java Cryptography Extension
(JCE) Unlimited Strength Jurisdiction Policy Files .
...
StrongBinaryEncryptor binaryEncryptor = new StrongBinaryEncryptor();
binaryEncryptor.setPassword(myEncryptionPassword);
...
String myEncryptedBinary = binaryEncryptor.encrypt(myBinary);
...
String plainBinary = binaryEncryptor.decrypt(myEncryptedBinary);
...
Importante
O gerenciamento de chaves e senhas está fora do escopo do Framework. Para sua utilização correta
a área de Segurança de Informação deve ser consultada.
Atenção
Não usar outros algoritmos, como o DES ou MD5.
10.5. Auditoria
É importante observar que existe uma diferença entre Log de aplicação e Auditoria. Enquanto o log faz o
registro de informações sobre o ciclo de vida da aplicação e dados internos do sistema, a Auditoria tem o
objetivo de rastrear as operações de negócio realizadas por motivos de segurança, e contém informações sobre
acesso e uso por clientes de um determinado recurso do sistema.
Além disso, o conjunto de informações auditadas costuma ser armazenado em estruturas indexadas para
consultas e geração de relatórios.
Portanto, o destino dos dados de auditoria geralmente é diferente dos dados de Log. Ao passo que informações
de Log são importantes para os técnicos e desenvolvedores detectarem problemas, registros de auditoria
auxiliam gerentes e analistas de negócio e segurança determinarem os padrões de acesso a um determinado
recurso.
Assim como o Log manual, a auditoria manual é a oportunidade que o desenvolvedor tem de determinar os
pontos e conjunto de dados que deverão ser auditados fora do escopo do método de serviço. Para tanto, o
desenvolvedor deve utilizar a API de auditoria para fazer as chamadas adequadas e registrar as informações
pertinentes ao acesso e utilização de um determinado recurso.
As informações a serem
persistidas no evento de auditoria, ficam contidas na classe
com.altec.bsbr.fw.audit.AuditInfo. Esta classe é responsável por armazenar pares de [parâmetro, valor]
A classe AuditInfo já possui algumas propriedades padrão, que devem ser seguidas por todas as aplicações:
A aplicação deve dizer como os eventos de auditoria devem ser gravados, implementando a interface
com.altec.bsbr.fw.audit.AuditHandler, que deve pegar as propriedades presentes no objeto AuditInfo e
efetuarem a persistência dos valores como, por exemplo, colocando-os numa fila ou num banco de dados.
O exemplo a seguir simplesmente insere as informações numa tabela do banco de dados. Para tanto ela
simplesmente estende a classe GenericJdbcDao, e usa o objeto SimpleJdbcTemplate disponibilizado para
executar o insert.
<bean
class="com.altec.bsbr.app.demo.audit.DbAuditHandler"> <property
name="dataSource" ref="dataSource"/> </bean>
Dica
Durante o processo de desenvolvimento, pode ser conveniente usar um AuditHandler simplificado,
que apenas envia a saída para o arquivo de log comum. Nesse caso pode ser usado um arquivo para
produção (META-INF/config/prod/audit.xml) com o DbAuditHandler e um outro para
desenvolvimento (META-INF/config/dev/audit.xml) com o DefaultAuditHander, que apenas
coloca as informações de auditoria no log de arquivos normal.
<bean
class="com.altec.bsbr.fw.audit.DefaultAuditHandler"/>
(1.13.6)
Segurança
Internamente, a classe AuditTrail coloca os eventos de log numa fila para serem persistidos de maneira
assíncrona. Assim, a aplicação não precisa incorrer no custo de uma gravação no banco de dados para continuar
o processamento.
Abaixo segue um exemplo do uso de @Audit para indicar que o método de serviço deve ser auditado:
@Service
public class TestServiceImpl implements TestService {
@Audit
public String sayHello(AuditInfo info, String name) {
return "Hello " + name + "!";
}
}
O chamador do serviço tem a obrigação de passar um objeto AuditInfo contendo os dados necessários. Caso o
método anotado com @Audit não possua nenhum parâmetro do tipo AuditInfo ou ele seja nulo, ocorrerá uma
exceção, e o método não será executado.
(1.13.6)
Capítulo 11. Interface Web
11.1. Introdução
A interface web está baseada nos seguintes frameworks JSF, ICEfaces com Facelets para componentes visuais
e templates, JasperReports para relatórios e Spring para integrar com a camada de serviços. Deste modo, o
Framework Santander Brasil oferece uma infra-estrutura básica para o desenvolvimento rápido e fácil
utilizando os Frameworks citados anteriormente.
O Framework tem como característica principal o desenvolvimento baseado nos padrões de interfaces gráficas
e em camadas. Portanto, o bom entendimento da interação de camadas torna-se imprescindível. Na figura a
seguir detalhamos num nível alto de abstração como a interface web interage com a camada de serviços.
framework de interface gráfica Java Swing, os componentes da interface fazem chamada a classes chamadas
Backing Beans, que atuam como fornecedores de dados aos componentes e respondem a eventos realizados nos
mesmos.
JSF é um padrão que está sendo amplamente utilizado em projetos de grande porte, além disso os principais
Servidores de Aplicação existentes possuem total suporte.
A figura acima descreve a iteração entre o Navegador (Browser) e uma aplicação JSF qualquer. Os
componentes da página jsp refletem o estado dos componentes UI que estão no servidor.
Para facilitar a criação e o desenho de novas telas JSF utiliza o padrão de tag libraries em arquivos jsp ou jspx.
Veja abaixo um exemplo de uso de tag libraries:
• Declaração em uma página jsp ou jspx é feita utilizando-se XML Namespaces. Com ela é possível incluir
componentes JSF e de terceiros nas páginas:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:jsp="http://java.sun.com/JSP/Page"❶
xmlns:ui="http://java.sun.com/jsf/facelets"❷
xmlns:h="http://java.sun.com/jsf/html"❸
xmlns:f="http://java.sun.com/jsf/core"❹
xmlns:ice="http://www.icesoft.com/icefaces/component"❺
version="2.0">
...
</html>
...
<ice:outputText value="..."/>
<ice:commandButton action="..." actionListener="..."/>
...
<h:outputText value="..."/>
<h:outputLink value="..."/>
...
Com JSF é possível criar componentes ricos de uma maneira simples e confiável, porém é necessário um
esforço inicial que muitas vezes não temos em um projeto. Desta forma iremos utilizar componentes já prontos
do projeto Icefaces.org. Este projeto tem uma grande variedade de componentes ricos e com suporte a AJAX,
ou seja, são componentes que agregam funcionalidades de alta tecnologia as aplicações web.
• Componente de tabela:
• Componente de árvore:
• Componente de menu:
(1.13.6)
Interface Web
• Componente de Gráficos:
• Componente de Mapa:
(1.13.6)
Interface Web
Estes componentes podem ser facilmente customizados bastando criar uma nova folha de estilo.
Projeto/
web/
resources/
index.jsp -> Página inicial (faz um redirocionamento para a URL do ICEfaces)
css/ -> Estilo
images/ -> Imagens
pages/ -> Páginas JSPX
WEB-INF/
faces-config.xml
faces-navigation.xml
web.xml
Cada nova aplicação deve configurar alguns parâmetros padrão no arquivo web.xml. Abaixo segue um template
de um arquivo web.xml configurado para um projeto web padrão.
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
Depois, são definidos os parâmetros de configuração de uma aplicação JSF. O exemplo abaixo contem os
valores default, que devem ser alterados apenas se necessário.
<context-param>
<description>
The ServletContext init parameter consulted by the
StateManager to tell where the state should be saved. Valid
values are given as the values of the constants: client or
server.
<context-param>
<description>
Context initialization parameter name for a comma delimited
list of context-relative resource paths (in addition to
/WEB-INF/faces-config.xml which is loaded automatically if
it exists) containing JavaServer Faces configuration
information.
</description>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>
/WEB-INF/faces-config.xml, /WEB-INF/faces-navigation.xml
</param-value>
</context-param>
<context-param>
<description>
The value to use for the default extension if the webapp is
using url extension mapping.
</description>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.jspx</param-value>
</context-param>
<context-param>
<description>Special Debug Output for Development</description>
<param-name>facelets.DEVELOPMENT</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<description>
Set this flag to true, if you want the JavaServer Faces
Reference Implementation to validate the XML in your
faces-config.xml resources against the DTD. Default value is
false.
</description>
<param-name>com.sun.faces.validateXml</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<description>
Set this flag to true, if you want the JavaServer Faces
Reference Implementation to verify that all of the
application objects you have configured (components,
converters, renderers, and validators) can be successfully
created. Default value is false.
</description>
<param-name>com.sun.faces.verifyObjects</param-name>
<param-value>true</param-value>
</context-param>
A seguir, os parâmetros de configuração de uma aplicação JSF com ICEfaces. O exemplo abaixo contem os
valores default, que devem ser alterados apenas se necessário.
<context-param>
<description>
By default, ICEfaces runs in asynchronous update mode, which
provides support for ICEfaces unique server-initiated
rendering (server-push) capabilities. However, many
applications do not require the full capabilities provided
by asynchronous update mode. In these cases, it is
recommended that synchronous update mode be configured.
<context-param>
<description>
To allow multiple windows for a single application,
concurrent DOM views must be enabled. This is set through
the ICEfaces context parameter,
com.icesoft.faces.concurrentDOMViews.
</description>
<param-name>com.icesoft.faces.concurrentDOMViews</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<description>
To cause request scope to last only for the duration of a
single user event, "standard request scope" must be enabled.
This is set through the ICEfaces context parameter,
com.icesoft.faces.standardRequestScope.
</description>
<param-name>com.icesoft.faces.standardRequestScope</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<description>
Resources such as JavaScript and CSS files can be compressed
when sent to the browser. This can improve application load
time in certain deployments. This configuration works
independently from the web-server configuration.
<context-param>
<description>
The maximum file upload size can be specified in the web.xml
file of your web application
</description>
<param-name>com.icesoft.faces.uploadMaxFileSize</param-name>
<param-value>1048576</param-value>
</context-param>
(1.13.6)
Interface Web
<context-param>
<description>
When a connection is lost, ICEFaces can be configured to
redirect the browser to a custom error page. This feature
can be turned on application-wide using the ICEfaces context
parameter, com.icesoft.faces.connectionLostRedirectURI.
</description>
<param-name>com.icesoft.faces.connectionLostRedirectURI</param-name>
<param-value>/</param-value>
</context-param>
<context-param>
<description>
The connectionTimeout parameter defines how long, in
milliseconds, that the bridge will wait for a response from
the server for a user-initiated request before declaring the
connection lost. The default value is 60000 (60 seconds).
</description>
<param-name>com.icesoft.faces.connectionTimeout</param-name>
<param-value>60000</param-value>
</context-param>
Definir os listeners de configuração do Spring. O exemplo abaixo contem os valores default, que devem ser
alterados apenas se necessário.
<listener>
<description>
Servlet 2.4+ listener that exposes the request to the
current thread, through both LocaleContextHolder and
RequestContextHolder. To be registered as listener in
web.xml.
<listener>
<listener-class>
com.altec.bsbr.fw.config.ContextLoaderListener
</listener-class>
</listener>
(1.13.6)
Interface Web
<listener>
<listener-class>
com.icesoft.faces.util.event.servlet.ContextEventRepeater
</listener-class>
</listener>
Os servlets do ICEfaces. O exemplo abaixo contem os valores default, que devem ser alterados apenas se
necessário.
<servlet>
<description>
Don't remove this servlet. Websphere requires it.
</description>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<description>
FacesServlet is a servlet that manages the request
processing lifecycle for web applications that are utilizing
JavaServer Faces to construct the user interface.
</description>
<servlet-name>Persistent Faces Servlet</servlet-name>
<servlet-class>
com.icesoft.faces.webapp.xmlhttp.PersistentFacesServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>Blocking Servlet</servlet-name>
<servlet-class>
com.icesoft.faces.webapp.xmlhttp.BlockingServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
Servlet de imagem é um Servlet que ajuda a rederizar imagens em uma página web, se necessário. Para mais
informações consulte Servlet de Imagem. Para resabilitar o cache de imagens utilize "0" no parâmetro
com.altec.bsbr.fw.web.image.MaxCachedImages.
<servlet>
<servlet-name>Image Render</servlet-name>
<servlet-class>
com.altec.bsbr.fw.web.image.ImageServlet
</servlet-class>
<init-param>
<description>
Configures the cached images to the provided value.
Default value is 5. Use "0" (zero) to disable the cache.
</description>
<param-name>
com.altec.bsbr.fw.web.image.MaxCachedImages
</param-name>
<param-value>20</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet>
<servlet-name>Pdf Report Servlet</servlet-name>
<servlet-class>
net.sf.jasperreports.j2ee.servlets.PdfServlet
</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet>
<servlet-name>Rtf Report Servlet</servlet-name>
<servlet-class>
net.sf.jasperreports.j2ee.servlets.RtfServlet
</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet>
<servlet-name>Odt Report Servlet</servlet-name>
<servlet-class>
net.sf.jasperreports.j2ee.servlets.OdtServlet
</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet>
<servlet-name>Xls Report Servlet</servlet-name>
<servlet-class>
net.sf.jasperreports.j2ee.servlets.XlsServlet
</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet>
<servlet-name>Xml Report Servlet</servlet-name>
<servlet-class>
net.sf.jasperreports.j2ee.servlets.XmlServlet
</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Pdf Report Servlet</servlet-name>
<url-pattern>*.pdf</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Odt Report Servlet</servlet-name>
<url-pattern>*.odt</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Xsl Report Servlet</servlet-name>
<url-pattern>*.xls</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Xml Report Servlet</servlet-name>
<url-pattern>*.xml</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Rtf Report Servlet</servlet-name>
<url-pattern>*.rtf</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Persistent Faces Servlet</servlet-name>
<url-pattern>*.iface</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Persistent Faces Servlet</servlet-name>
<url-pattern>/xmlhttp/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Blocking Servlet</servlet-name>
<url-pattern>/block/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Image Render</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<session-config>
<!--
The session-timeout element defines the default
session timeout interval for all sessions created
in this web application. The specified timeout
must be expressed in a whole number of minutes.
If the timeout is 0 or less, the container ensures
the default behaviour of sessions is never to time
out. If this element is not specified, the container
must set its default timeout period.
-->
<session-timeout>15</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page>
<error-code>500</error-code>
<location>/pages/error.iface</location>
</error-page>
</web-app>
• faces-config.xml: É o principal arquivo de configuração de uma implementação da API Java Server Faces
(JSF). Neste arquivo devem ser registrados os Converters, Backing Beans e Validators. Para um melhor
entendimento separamos a navegação do site em um outro arquivo.
• Converters: São usados para converter valores entre um formulário e o tipo definido em uma classe
Java.
• Backing Beans: São classes que irão interagir com a página .jspx, fornecendo dados, processando
formulários, processando cliques, mundaças na página, etc.
Nota
(1.13.6)
Interface Web
• Validators: São classes que irão fazer a validação dos dados que foram inseridos em um formulário.
Estes arquivos devem estar localizados no diretório WEB-INF da sua aplicação. Conforme mostrado abaixo:
[Raiz_Projeto]/
/src
/web/
WEB-INF/
faces-config.xml
faces-navigation.xml
Importante
Na inicialização da aplicação o Framework já adiciona as configurações básicas do JSF, como:
VariableResolver para resolver os Backing Beans via Spring, Converter para ExportOption.
Portanto, não há a necessidade de adicioná-los novamente.
Facelets agregam suporte a templates ao JSF e uma série de funcionalidades que facilitam a criação de UIs,
componentes e templates.
Para criar um novo template basta criar um arquivo utilizando a tag <ui:insert name="nome_da_secao"/>.
O seguinte exemplo mostra um arquivo template.jspx com o title e body a serem definidos (opicionalmente)
pelas UI's que estenderem este template.
<body>
<h1>
<ui:insert name="title">Default Title</ui:insert>
</h1>
<p>
<ui:insert name="body">Default Body</ui:insert>
</p>
</body>
</html>
(1.13.6)
Interface Web
UI ou User Interface
Interface com o usuário é o conjunto de características com as qual os utilizadores interagem com
as máquinas, dispositivos, programas de computador ou alguma outra ferramenta complexa.
O projeto de uma interface com o usuário afeta a quantidade de esforço que o usuário precisará
para prover as entradas ao sistema e para interpretar sua respectiva saída, além de quanto esforço
ele precisará para aprender o procedimento. A usabilidade é uma área do design que leva em
consideração a psicologia e a fisiologia dos usuários para tornar os sistemas mais efetivos,
eficientes e satisfatórios. Ela está principalmente associada as características da interface com o
usuário, mas também pode estar associada com a funcionalidade do produto.
Primeiro deve ser criado o arquivo .jspx na estrutura de diretórios desejada dentro do diretório pages.
<ui:define name="body">
<h1>#{messageBBean.message}</h1>
</ui:define>
</ui:composition>
Para uma lista completa de componentes que podem ser utilizados veja:
• Facelets - agrega suporte a templates ao JSF e uma série de funcionalidades que facilitam a criação de UIs.
Escopos existentes:
• Request: Registrar um backing bean no escopo request fará com que o bean exista apenas durante um
request. Este escopo deve ser usado para todo caso onde o usuário não necessitará acessar as informações
do estado do Backing Bean em outros Beans ou páginas durante a navegação no site.
• Session: Registrar um backing bean no escopo session fará com que o bean exista apenas durante uma
session. Além disso, cada usuário terá uma instância diferente do objeto. Este escopo deve ser usado para os
casos onde a informação do estado de um Backing Bean deverá ser guardada para futuros acessos.
Importante
Alguns aplication servers necessitam serializar a sessão quando estão entrando em estado de
shutdown (parando) e com ela todos os objetos que nela estão, portanto é importante checar se
o Backing Bean pode ser serializado e se depois ele pode restaurar um estado válido
novamente. Atributos que não precisam ser serializados podem ser declarados como
transient.
@Component
@Scope("session")
public class MessageBBean extends BasicBBean {
@Autowired
private transient MessageService messageService;
...
}
Nota
Observe que no exemplo anterior o Backing Bean utiliza a seguinte nomenclatura [Nome]BBean e
esta será adotada como padrão usado no Framework.
O Backing Bean define e utiliza o serviço MessageService bastando anotar o atributo como @Autowired. Deste
modo o Framework se encarrega de injetar o serviço solicitado ao Bean.
Importante
Observe que no exemplo anterior o Backing Bean declara como transient o serviço declarado
(MessageService). É importante seguir esta prática para todos os beans que não são serializáveis e
que foram criados utilizando Spring, como neste exemplo o serviço. No Framework Spring todos
os beans que são proxy de alguma classe não podem ser serializados.
O Backing Bean não precisa ser adicionado ao faces-config.xml, pois ele é instanciado automaticamente pelo
Framework. Ele será criado como um bean no contexto do Spring, com o nome definido pela regra abaixo:
Se o nome default não for satisfatório, é possível que se personalize o nome do seu Bean utilizando a seguinte
anotação: @Component("outroNomeBBean").
Desta maneira, os backing beans estarão disponíveis tanto para acesso através de outros beans quanto dentro
das páginas jspx.
Seguindo a mesma regra abaixo mostramos como acessar um Backing Bean em uma página .jspx.
...
<input type="text" jsfc="h:inputText" value="#{messageBBean.text}" required="true"/>
<input type="button" jsfc="h:commandButton" value="Pesquisar" action="Pesquisar"
actionListener="#{messageBBean.pesquisar}"/>
...
<faces-config>
<navigation-rule>
<description>Message Rules</description>
<from-view-id>/pages/message.jspx</from-view-id>
<navigation-case>
<from-outcome>Success</from-outcome>
<to-view-id>/pages/success.jspx</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
11.5. BasicBBean
A classe básica para a criação de um Backing Bean chamada com.altec.bsbr.fw.web.jsf.BasicBBean
oferece vários métodos de infra-estrutura os quais facilitam a implementação de um Backing Bean. Abaixo
segue a lista de métodos/atributos definidos desta classe.
• getBind(String managedBeanName): Cria uma instância do Backing Bean de acordo com a EL passada
(1.13.6)
Interface Web
como parâmetro.
• String SUCCESS: Atributo que já configura o bean para retornar as mensagens padrão "Success" para o
navigation controller.
• String ERROR: Atributo que já configura o bean para retornar as mensagens padrão "Error" para o
navigation controller.
Importante
O Framework JSF é bastante flexível e permite várias formas de comunicação entre a página e o
Backing Bean, as quais, devem permanecer da forma como foram idealizadas. Portanto, utilize os
métodos getHttpRequest(), getHttpResponse(), getHttpSession(boolean create),
getRequestAttribute(String attributeName), getSessionAttribute(String
attributeName), getRequestParameter(String paramName) com cuidado, apenas quando for
necessário comunicar com componentes de fora do contexto JSF, como um servlet.
11.6. CRUD
Em muitos projetos, grande parte das UI's tem o conceito de incluir, ler, editar, e excluir (CRUD,
Create-Read-Update-Delete). Para isso o Framework oferece algumas classes orientadas a estas características.
• save(T entity): Método que permite acessar o Serviço CRUD e salvar uma entidade qualquer.
• get(ID id, boolean lock): Retorna uma instância da entidade do tipo especificado via Generics.
• remove(ID primaryKey): Remove uma entidade a partir do tipo especificado via Generics.
(1.13.6)
Interface Web
@Component
@Scope("request")
public class CrudPessoaBBean extends CrudServiceBBean<Pessoa, Long>
implements Serializable{
private Pessoa pessoa;
...
...
}
• Map<String, Object> getReportParameters(): Este método provê os parâmetros para o relatório a ser
gerado.
Após isso basta chamar o método String render() dentro seu Backing Bean que o próprio framework irá
gerar e devolver o relatório gerado.
Esta classe também configura uma lista de opções de exportação. A qual pode ser acessada utilizando-se o
seguinte método: List<SelectItem> getListExportOptions().
Arquivo .jspx:
...
<form jsfc="h:form">
...
<td><label jsfc="h:outputLabel" for="exportOption">
#{msg['label.export.option']} (*):</label></td>
<td>
<select id="exportOption" jsfc="h:selectOneMenu"
value="#{testReportBBean.exportOption}">
<f:selectItems value="#{testReportBBean.listExportOptions}"/>
</select>
<br/>
<h:message errorClass="error" for="exportOption"/>
</td>
...
<td></td>
<td><input type="submit" jsfc="h:commandButton"
action="#{testReportBBean.render}"
value="#{msg['button.render']}" />
<input type="submit" jsfc="h:commandButton" immediate="true"
action="Cancel"
value="#{msg['button.cancel']}"/>
</td>
...
</form>
...
Backing Bean:
@Component
@Scope("request")
public class TestReportBBean extends ReportBBean {
@Override
protected JRDataSource getJRDataSource() {
List<Product> products = new ArrayList<Product>();
for (int i = 0; i < 10; i++) {
Product product = new Product();
product.setId(i);
product.setName("test-" + i);
products.add(product);
}
return new JRBeanCollectionDataSource(products);
}
@Override
protected Map<String, Object> getReportParameters() {
return null;
}
@Override
protected InputStream getReportStream() {
return FindReport.getReport(reportPath);
}
}
No faces-navigation.xml:
...
<navigation-rule>
<display-name>Test Report Products</display-name>
<from-view-id>/pages/TestReport.jspx</from-view-id>
<navigation-case>
<from-outcome>PDF</from-outcome>
<to-view-id>/report.pdf</to-view-id>
<redirect/>
</navigation-case>
<navigation-case>
<from-outcome>EXCEL</from-outcome>
<to-view-id>/report.xls</to-view-id>
<redirect/>
</navigation-case>
<navigation-case>
<from-outcome>Cancel</from-outcome>
<to-view-id>/pages/Welcome.jspx</to-view-id>
</navigation-case>
</navigation-rule>
...
Para construção do menu utilizamos o componente ice:tree o qual irá construir um árvode de links para as
páginas configuradas no MBS.
Para maiores informações sobre o componente visual que renderiza a árvore do menu, veja ice:tree .
Para configurar o menu de acesso devemos seguir alguns passos descritos abaixo:
• Criar uma interface que ofereça o método para acessar o TreeModel passando o nome do usuário e o nome
do sistema.
Exemplo:
...
public interface MenuService {
public javax.swing.tree.TreeModel buildUserMenu(String user, String system);
}
• Criar uma classe de serviço que implemente a interface que foi criada. Fazendo @Autowire na interface
com.altec.bsbr.fw.security.authorization.Authorization.
...
@Service
public class MenuServiceImpl implements MenuService {
@Autowired
private Authorization authorization;
public javax.swing.tree.TreeModel buildUserMenu(String user, String system) {
return authorization.getMenuTree(user, system)
}
}
• Criar uma classe Backing Bean que acesse o serviço de menu adicionando a anotação @Autowired no
atributo do tipo da interface que foi criada.
Exemplo:
...
@Component
public class MenuBBean extends BasicBBean implements InitializingBean {
@Autowired
private MenuService menuService;❶
(1.13.6)
Interface Web
Exemplo:
• ICEfaces.org.
• ICEfaces Documentation.
...
@Component
public class ProductImage implements ImageSupport {
@Autowired
private transient ProdutoService produtoService;
(1.13.6)
Interface Web
<ui:composition xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:fn="http://www.santander.com.br/functions"❶
template="template.jspx">
...
</ui:composition>
<img src="#{fn:getImageUrl('productImage',❶
val.estoque.produto.codigo❷
)}"/>
12.1. Introdução
O Framework prove algumas funcionalidades para ajudar o desenvolvimento de aplicações batch em Java. No
entanto elas deverão usar a infra-estrutura existente provida pelo Control-M para iniciar e controlar os
processos.
O framework tem um programa principal para iniciar processos batch, fornecendo uma infra-estrutura para que
a aplicação possa informar seu status de execução ao Control-M, através do código de saída do programa.
Elas poderão acessar todos os recursos oferecidos pelo framework, como controle dos serviços, mensageria,
persistência, acesso a web services, etc.
Existem controladores de fluxo de processamento preperados para rodar tarefas de modo sequencial e paralelo,
de forma muito simples. São configuráveis, flexíveis, tem bom desempenho, minimizam os erros comuns de
programação sequencial e concorrente.
12.2. Processamento
Um produtor pode ser qualquer objeto que implemente a interface
com.altec.bsbr.fw.batch.RecordProducer e tem a função de produzir os registros do processo batch. Cada
vez que o método next() for chamado, ele retornará mais um registro a ser processado.
Uma tarefa pode ser qualquer objeto que estenda a classe abstrata com.altec.bsbr.fw.batch.Task que tenha a
função de transformar a saída gerada pelo produtor (do tipo PARAM) em um objeto (do tipo RET) para ser
processado pelo consumidor. Nesta classe também é onde deve ser implementada a lógica de negócios do
processo batch.
A infra estrutura do Framework fará a chamada do método execute() passando como parâmetro o registro
fornecido pelo RecordProducer . Depois do processamento do método, o Framework passará o resultado para o
RecordConsumer .
Uma característica importante é o a capacidade de executar o processamento das Tasks em paralelo, podendo
aproveitar os recursos de múltiplas CPUs e múltiplos Cores.
• Produtor: responsável por transformar os dados de origem (seja de arquivos, banco de dados ou outra fonte
qualquer) em um tipo de objeto que represente algum sentido para as futuras tranformações. Um Produtor
deve implementar a interface RecordProducer , como mostrado no exemplo a seguir:
• Tarefa: responsável por transformar os dados de entrada em objetos prontos para serem consumidos. As
tarefas podem ser executadas em paralelo. Uma Tarefa deve implementar a interface Task, como mostrado
no exemplo a seguir:
DummyTask
Caso queira que o objeto produzido no RecordProducer seja consumido sem nenhuma
transformação para o RecordConsumer pode ser usada a task pertencente ao Framework
com.altec.bsbr.fw.batch.DummyTask que apenas repassa o conteúdo recebido para o
consumidor.
• Parâmetros : a classe Parameter define um meio para que o Spring injete os parametros nas
implementações de RecordProducer e RecordConsumer. Existem dois atributos na classe: parameter e
parameterType, sendo String e Integer respectivamente. O atributo parameter é o valor do parâmetro
que deseja passar, e o parameterType define se ele é de entrada (0) ou saída (1). Caso seja um parâmetro de
entrada será usado durante a execução do RecordProducer, caso seja de saída na execução do
RecordConsumer.
<!--
=======================================================================================
RecordProducer que gera os dados para o processo de batch
=======================================================================================
-->
<bean id="producer" class="com.altec.fw.bsbr.batch.teste.carga.poker.PokerProducer">
<property name="parameters"><ref bean="parameters"/></property>
</bean>
<bean id="parameters" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list>
<ref bean="parameter1"/>
</list>
</property>
</bean>
<bean id="parameter1" class="com.altec.bsbr.fw.batch.Parameter">
<property name="parameter" value="Poker/test/data/poker-hand-testing.data.txt"/>
<property name="parameterType" value="0"/>
</bean>
(1.13.6)
Batch
Durante o processamento do batch, é possível indicar a ocorrência de alguma situação anormal através do
lançamento de exceções. Caso algum erro ocorra existem duas exceções para indicar a gravidade do erro:
BatchWarningException e BatchErrorException as duas são RuntimeException e podem receber um código
que será passado para o Control-M indicando qual erro ou aviso.
Caso uma BatchWarningException seja lançada o processamento continua e o aviso será logado.
Caso uma BatchErrorException seja lançada todo o processamento será interrompido e terminado com o
código de erro indicado ou com o código de erro padrão (que é 1).
12.2.2. Configuração
Esse arquivo indica qual RecordProducer e RecordConsumer serão usados para esse processamento. Assim
como os parâmetros que as classes receberão. Os parâmetros podem ser passados no construtor da classe que
implementa as interfaces, ou usando o método setParameters(Parameters). Indica também quais os Handlers
serão usados, existem duas opções: startHandler e endHandler.
Dica
O arquivo batch.xml deve ficar no diretório: src
<!--
=======================================================================================
RecordProducer que gera os dados para o processo de batch
=======================================================================================
-->
<bean id="producer" class="com.altec.bsbr.fw.batch.PokerProducer"/>
<!--
=======================================================================================
RecordConsumer que consome os dados para o processo de batch
=======================================================================================
-->
<bean id="consumer" class="com.altec.bsbr.fw.batch.PokerConsumer"/>
<!--
=======================================================================================
Handlers
=======================================================================================
-->
<bean id="startHandler" class="com.altec.bsbr.fw.batch.DummyHandler"/>
<bean id="endHandler" class="com.altec.bsbr.fw.batch.DummyHandler"/>
Dica
A classe FromDatabaseProducer pode ser usada para gerar registros a partir de uma query no
banco de dados. A query é definida no batch.xml, sendo atribuída ao parâmetro query. O exemplo
a seguir mostra como:
<!--
=======================================================================================
RecordProducer que gera os dados para o processo de batch
=======================================================================================
-->
<bean id="producer" class="com.altec.bsbr.fw.batch.FromDatabaseProducer">
<property name="query" value="select * from jab.tb_pedi"/>
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.SingleConnectionDataSource">
<property name="driverClassName" value="oracle.jdbc.OracleDriver"/>
(1.13.6)
Batch
Dica
A classe ToStdoutConsumer é útil durante o desenvolvimento, todos os valores dos atributos do
objeto de entrada no método consume(Object object) são listados na console.
<!--
=======================================================================================
RecordConsumer que consome os dados para o processo de batch
=======================================================================================
-->
<bean id="consumer" class="com.altec.bsbr.fw.batch.ToStdoutConsumer"/>
Arquivo de configuração do TaskManager. Indica qual Task deve ser utilizada ( batchRunner.taskType ), qual
o tamanho do pool de threads deve ser usado ( batchRunner.poolSize ), de quantos em quantos registros os
controladores devem executar um flush (flush para arquivos, e commit para transações com banco, ou qualquer
outro controle a ser implementado no flush() da interface RecordConsumer ( batchRunner.flushFrequency )
e se a saída deve ou não ser compartilhada (batchRunner.sharedOutput ).
TaskManager
A classe Taskmanager controla todo o fluxo de execução para uma determinada tarefa. Pode executar as tasks
em paralelo (configuração no batch.properties ) ou em série. O processamento em série acontece a partir das
tasks os produtos gerados pelo produtor entram na fila de execução e os resultados das tasks, são enviados
para os consumidores. A configuração batchRunner.sharedOutput define se as saídas vão para um ponto
único, ou se elas podem ser tratadas em paralelo (por exemplo: arquivo -> saídas compartilhadas; banco de
dados -> saídas não compartilhadas). A configuração batchRunner.maxTasksRunning define qual a quantidade
máxima de tasks que podem ser inicializadas pelo producer, é importante que esta propriedade seja utilizada de
uma forma na qual o consumer consiga dar uma vazão maior do que o producer consegue produzir, para uma
quantidade ilimitada de tasks atribua -1 a esta variável. A configuraçção batchRunner.sleepPeriodInMilis
define o tempo em milisengundos na qual o task manager deve esperar para que continue produzir novas tasks,
ou seja, ele deve ser configurado de acordo com o tempo necessário que o consumer consiga executar sem
enfileirar as threads.
Dica
O arquivo batch.properties deve ficar no diretório: META-INF/config ou dentro do diretório do
runningMode: META-INF/config/[runningMode]/
Para executar uma aplicação batch é necessário usar scripts de inicialização para carregar os jars necessários no
classpath e chamar o método main() da classe com.altec.bsbr.fw.batch.BatchRunner. Os scripts de
inicialização mudam de um sistema operacional para outro, a seguir um exemplo de script para o Windows:
set JAVA_HOME=C:\Java\java❶
set FW_LIBS=C:\Java\workspace\Framework\fw\lib❷
set CLASSPATH=C:\Java\workspace\Framework\abcBatch\dist\abcBatch.jar❸
rem ===========================================================================
set JAVA="%JAVA_HOME%\bin\java.exe"
exit /b %ERRORLEVEL%❼
endlocal
O código de retorno da aplicação batch em Java pode ser usada para definir o comportamento do Control-M. O
modo de obter o valor numérico do código de retorno da aplicação depende do shell que estiver sendo usado:
Shell Variável
bash (Unix) $?
Nota
Este parâmetro sobre-escreve o batch.xml já existente no arquivo jar da aplicação.
12.3.1. DefaultDataInput
Implementação padrão da interface DataInput que baseada em um Mapper (classe que deternima como os
registros serão lidos dado um formato) retorna objetos populados com os dados no arquivo.
No exemplo a seguir o DefaultDataInput é preparado para ler um arquivo com campos separados por
delimitador, o tipo de registro é representado pela classe Cd.
...
DefaultDataInput titlesDataInput = new DefaultDataInput();
titlesDataInput.setMapper(new DefaultDelimiterMapper(Cd.class));
titlesDataInput.setReader(new BufferedReader(new FileReader(fileName), BUFFER_SIZE));
...
12.3.2. DefaultFileDataOutput
Implementação padrão da interface DataOutput para arquivos. Baseada em um Marshaller (classe que
deternima como os registros serão gerados dado um formato) retorna registros (ou registro) a partir de objetos
populados com os dados.
No exemplo a seguir o DefaultFileDataOutput é preparado para gravar um arquivo com campos separados
por delimitador, o tipo de registro é representado pela classe Cd.
...
DefaultFileDataOutput<Cd> dataOutput = new DefaultFileDataOutput<Cd>("test_csv.txt",
new DelimitedMarshaller<Cd>());
//save collection
dataOutput.save(getCd());
O método save() pode receber uma lista de objetos ou um simples objeto. Caso uma lista seja passada, o
retorno será um bloco de registros. Caso receba um unico objeto, um registro será retornado. Para a
FixedLenghtMarshaller o retorno do método save(), quando recebe uma lista é o conteúdo total do arquivo,
com header e trailler, caso existam.
12.3.3. FlatfileContextDataOutput
Implementação Flatfile da interface ContextDataOutput para arquivos posicionais. Esta implementação possui
o suporte a customização e parametrização de header e trail.
...
//Instanciando a implementação Flatfile.
ContextDataOutput<Collection<Person>> contextDataOutput =
new FlatfileContextDataOutput<Collection<Person>>("C:/Temp/Test.txt");
(1.13.6)
Batch
contextDataOutput.save(personList);
...
Abaixo mostramos um exemplo de geração de um arquivo com os valores de uma collection com um contexto
personalizado:
... //Da mesma forma que o exemplo anterior cria-se uma instancia do ContextDataOutput.
... //Definição da classe CustomMarshallContext
public class CustomMarshallContext implements MarshallContext {
public String param;
12.3.3.1. @Close
Para encerrar com segurança os recursos utilizados na geração dos arquivos, o framework disponibiliza uma
anotação (com.altec.bsbr.fw.batch.annotation.Close), que deve ser aplicada em um método na classe
Consumer.
/*
* Implementação da interface RecordConsumer. O objeto T está demonstrado
* em uma estrutura genérica, substitua pelo seu objeto concreto em sua implementação.
*/
public void consume(T t) {
try {
contextDataOutput.save( t );
}
catch (DataException e) {
//Tratar a exececao
}
}
(1.13.6)
Batch
contextDataOutput.close();
}
catch (DataException e) {
//Tratar a exececao
}
}
...
12.4.1. CSV
Existe um conjunto de anotações, um Mapper e um Marshaller para facilitar o trabalho com registros de
campos separados por delimitadores.
12.4.1.1. Anotações
12.4.1.1.1. DelimitedFieldFile
Anotação de classe, que indica q a classe representa um registro que tem os campos separados por
delimitadores. O delimitador deve ser informado, conforme o exemplo:
...
@DelimitedFieldFile(delimiter = ",")
public class Cd {
...
12.4.1.1.2. DelimitedField
Anotação de atributo, que indica que o atributo é um dos campos do registro. Os seguintes atributos podem ser
indicados na anotação:
• converter() - opcional: implementação da Converter para converter o valor do campo para um tipo
especifico e vice-versa.
...
@DelimitedField(position = 1)
private Integer code;
12.4.1.2. Mapper
A classe DelimitedMapper é uma implementação da interface Mapper, preparada para ler arquivos separados
por delimitadores, sendo o POJO passado para o construtor a representação de um registro.
12.4.1.3. Marshaller
A classe DelimitedMarshaller gera um arquivo baseado num POJO anotado com as anotações descritas
acima. Ao chamar o método marshaller, passando uma colecção de POJOs anotados, uma string com os
registros será retornada, caso um único objeto seja passado será retornada ums string com um único registro.
12.4.2.1. Anotações
12.4.2.1.1. FixedLenghtFieldFile
Identifica que o arquivo conterá registros com campos de tamanho fixo.
...
@FixedLenghtFieldFile()
public class Person {
...
• header() - opcional: implementação da interface Header que gera o cabeçalho para esse arquivo. Exemplo:
...
public class PersonHeader implements Header {
private String pattern = null;
• header() - opcional: implementação da interface ContextSupportHeader que gera o cabeçalho para esse
arquivo com suporte a parametrização. Exemplo:
...
public class PersonHeader implements ContextSupportHeader {
public String processHeader(MarshallContext marshallContext) throws MarshallException {
return "ProcessedSize = "+
((DataHolderMarshallContext<Collection<Person>>)marshallContext).getData().size();
}
}
...
Nota
Somente pode ser usado com a classe FixedLengthFlatfileDataMarshaller. Veja Marshaller.
• trail() - opcional: implementação da interface Trail que gera o rodapé para esse arquivo.
...
public class PersonTrail implements Trail {
private String pattern = null;
• trail() - opcional: implementação da interface ContextSupportTrail que gera o rodapé para esse arquivo
com suporte a parametrização. Exemplo:
...
public class PersonTrail implements ContextSupportTrail {
public String processTrail(MarshallContext marshallContext) throws MarshallException {
return "ProcessedSize = "+
((DataHolderMarshallContext<Collection<Person>>)marshallContext).getData().size();
}
}
...
Nota
Somente pode ser usado com a classe FixedLengthFlatfileDataMarshaller. Veja Marshaller.
Dica
Header e Trailler só funcionam gerando arquivo, ou seja a classe FixedLenghtMarshaller gera o
Header e o Trailler. E FixedLengthFlatfileDataMarshaller gera o Header e o Trailler com
suporte a parametrização.
12.4.2.1.2. FixedLenghtField
Anotação de atributo que identifica os atributos que estarão no registro.
• paddingAlign() - Para qual lado devem estar os caracteres de preenchimento. O padrão é alinhar a
esquerda, e os valores aceitáveis são:
• Align.LEFT
• Align.RIGHT
• converter() - opcional: implementação da Converter para converter o valor do campo para um tipo
especifico e vice-versa.
...
(1.13.6)
Batch
12.4.2.1.3. FixedLenghtFieldDetailId
Anotação de classe que permite criar um identificador para o registro de detalhe, muito comum em arquivos
com headers e traillers.
...
@FixedLenghtFieldDetailId(id = "2")
public class Person {
...
12.4.2.2. Mapper
A classe FixedLenghtFieldMapper é uma implementação da interface Mapper, preparada para ler registros com
campos de tamanho fixo, sendo o POJO passado para o construtor a representação de um registro.
12.4.2.3. Marshaller
• marshall(T obj):este método invoca o método anterior passando um MarshallContext padrão chamado
DataHolderMarshallContext o qual encapsula o dado que foi passado para o método marshall, para
posteriomente ser acesado pelo ContextSupportHeader.processHeader() e
ContextSupportTrail.processTrail() detalhados anteriormente.
(1.13.6)
Batch
...
<bean id="dataSource1" class="com.altec.bsbr.fw.jdbc.datasource.MbsUserCredentialDataSourceAdapter"
autowire-candidate="default">
<constructor-arg value="http://mbs_host:81/web_service"/>❶
<constructor-arg value="sistema"/>❷
<constructor-arg value="frase"/>❸
<property name="targetDataSource" ref="targetDataSource"/>❹
</bean>
<bean id="targetDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@host_db:1521:db" />❺
</bean>
...
...
<bean id="dataSource1" class="com.altec.bsbr.fw.jdbc.datasource.PoolingBatchDataSource">
<property name="driverClassName" value="oracle.jdbc.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@host_db:1521:db"/>❶
<property name="webService" value="http://mbs_host:81/web_service"/>❷
<property name="sistema" value="sistema"/>❸
<property name="frase" value="frase"/>❹
<property name="mbsAutentication" value="true"/>❺
<property name="paramMap"/>
<map>
<entry key="initialSize" value="initialSize"/>❻
<entry key="maxActive" value="maxActive"/>❼
</map/>
</property/>
</bean>
...
• Crie o DTO que será usado para trafegar os dados entre as camadas. Exemplo:
package com.altec.bsbr.app.abcLdap.dto;
...
public class UserInfo {
public String nome;
...
// gets e sets
...
}
package com.altec.bsbr.app.abcLdap.ldap.mapper;
...
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.simple.ParameterizedContextMapper;
import com.altec.bsbr.test.dto.UserInfo;
• Crie uma interface para o DAO que fara o acesso ao servidor LDAP. Exemplo:
package com.altec.bsbr.app.abcLdap.dao;
....
public interface UserInfoDao {
public List<UserInfo> findAll();
}
package com.altec.bsbr.app.abcLdap.dao.impl;
import com.altec.bsbr.fw.dao.ldap.GenericLdapDao;
...
@Repository
public class UserInfoDaoImpl extends GenericLdapDao implements UserInfoDao {
private final int PAGE_SIZE = 500;
• Anote a classe utilizando @Autowired de serviço para utilizar fazer o acesso ao DAO. Exemplo:
package com.altec.bsbr.app.abcLdap.service.impl;
...
@Service
public class UserInfoServiceImpl implements UserInfoService {
@Autowired
private UserInfoDao userInfoDao;
...
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/bea
http://www.springframework.org/schema/util http://www.springfr
<bean id="contextSource"
class="org.springframework.ldap.pool.factory.PoolingContextSource">
<property name="contextSource" ref="contextSourceTarget" />
</bean>
</beans>
pageSize): List<T>: Executa uma busca páginada para casos em que o resultado esperado excede a
quantidade máxima de resultados retornados pelo LDAP server.
...
@Repository
public class FooDaoImpl extends GenericLdapDao implements FooDao {
@Autowired
@Qualifier("mySimpleLdapTemplate")
@Override
public void setSimpleLdapTemplate(SimplaLdapTemplate simpleLdapTemplate) {
super.setSimpleLdapTemplate(simpleLdapTemplate);
}
}
• Spring Ldap
14.1. Introdução
Este capítulo descreve o uso das várias classes utilitárias do Framework, que podem ser usadas no dia-a-dia de
programação em Java.
Está fora do escopo deste manual, documentar bibliotecas de terceiros, que são usadas pelo Framework. O
intuito é apenas apresentar algumas das suas funcionalidades básicas. Maiores informações podem ser obtidas
na documentação oficial de cada produto.
Esta é uma rápida introdução da biblioteca Joda-Time, e alguns dos recursos oferecidos. A documentação
completa está no guia do usuário .
• DateMidnight - classe imutável que representa a data em que o tempo é forçado a meia-noite.
• LocalDate - classe imutável representando uma data local sem hora (e sem fuso horário).
• Local - classe imutável representando uma hora sem data (sem fuso horário).
• LocalDateTime - classe imutável representando uma data e hora locais (sem fuso horário).
Cada classe datetime fornece uma variedade de construtores. Estas incluem construtores com objetos, o que
permite construir, por exemplo, um DateTime a partir dos seguintes objetos:
• Long - em milisegundos
Esta lista é extensível. Em outras palavras o Joda-Time sacrifica um pouco de segurança de tipos pela
extensibilidade. Significa, porém, que a conversão a partir de um Date ou Calendar do JDK para uma classe
Joda-Time é fácil - basta passar a classe JDK no construtor.
Cada classe datetime fornece métodos de acesso simples para campos datetime. Por exemplo, para acessar o
mês você pode usar:
...
DateTime dt = new DateTime();
int month = dt.getMonthOfYear();
...
Todas as principais classes datetime são imutáveis (como String) e não podem ser alteradas após a criação. No
entanto, métodos simples são fornecidos para alterar valores de campos em objetos recém criados. Por
exemplo, para definir o ano, ou adicionar 2 horas você pode usar:
...
DateTime dt = new DateTime();
DateTime year2000 = dt.withYear(2000);
DateTime twoHoursLater = dt.plusHours(2);
...
Além de métodos get básicos, cada classe datetime provee métodos que retornam propriedades (
DateTime.Property ) para cada campo. Estas proporcionam acesso a todo o manancial de funcionalidade do
Joda-Time. Por exemplo, para obter informações acerca de um mês ou ano:
...
DateTime dt = new DateTime();
String monthName = dt.monthOfYear().getAsText();
String frenchShortName = dt.monthOfYear().getAsShortText(Locale.FRENCH);
boolean isLeapYear = dt.year().isLeap();
DateTime rounded = dt.dayOfMonth().roundFloorCopy();
...
Um intervalo é representado pela classe Interval . Ela possui um início e fim, e permite operações baseadas em
torno desse intervalo de tempo.
Um período de tempo é representada pela classe Period . Ela contém um período como 6 meses, 3 dias e 7
horas. Você pode criar uma Período diretamente, ou derivá-lo de um intervalo.
Uma duração de tempo é representada pela classe Duration . Esta detém uma duração exata em milisegundos.
Você pode criar uma duração diretamente, ou derivá-la de um intervalo.
Apesar de um período e uma duração parecerem semelhantes, elas funcionam de forma diferente. Considere,
por exemplo, adicionar um dia a um DateTime na mudança de horário de verão.
...
DateTime dt = new DateTime(2005, 3, 26, 12, 0, 0, 0);
DateTime plusPeriod = dt.plus(Period.days(1));
DateTime plusDuration = dt.plus(new Duration(24L*60L*60L*1000L));
...
Adicionar um período vai acrescentar 23 horas neste caso, e não 24 por causa da mudança do horário de verão,
assim o horário do resultado ainda será meio-dia. Adicionar uma duração irá acrescentar 24 horas não importa o
que, portanto, o horário do resultado será 13:00.
A leitura da informação de data ou hora de fontes externas que têm o seu próprio formato personalizado é uma
exigência frequente para aplicações que tenham computações com datas. Escrever com um formato
personalizado é também um requisito comum.
Muitos formatos personalizados podem ser representados por formatos que especificam uma seqüência de
campos calendário junto com a representação (numérico, nome string, etc) e comprimento do campo. Por
exemplo o padrão "yyyy" representaria um ano com 4 dígitos. Outros formatos não são tão facilmente
representados. Por exemplo, o padrão "yy" para um dígito com dois anos não identifica unicamente o século a
que pertence. Na saída, isto não irá causar problemas, mas há um problema de interpretação na entrada.
Além disso, existem várias normas para serialização de data/hora de uso comum, em particular o ISO8601.
Estas também devem ser suportadas pela maior parte das aplicações.
Joda-time suporta estes diferentes requisitos através de uma arquitetura flexível. Vamos agora descrever os
vários elementos desta arquitetura.
14.2.3.1. Formatadores
Todo parse e impressão é realizado utilizando um objeto DateTimeFormatter . Dado um objeto fmt do tipo
DateTimeFormatter , o parsing é realizado da seguinte forma
...
String strInputDateTime;
// string is populated with a date time string in some fashion
...
DateTime dt = fmt.parseDateTime(strInputDateTime);
...
Assim, um objeto DateTime é retornado a partir do método de parse do formatter. Do mesmo modo, a saída é
realizada como
...
String strOutputDateTime = fmt.print(dt);
...
O suporte para formatos padrão baseado no ISO8601 é fornecido pela ISODateTimeFormat classe. Isto
proporciona uma série de métodos de construção.
Por exemplo, se você quisesse usar o formato padrão ISO para datetime, que é yyyy-MM-dd'T'HH:mm:ss.SSSZ ,
você deve inicializar fmt como
...
DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
...
Depois, você poderia usar fmt , como descrito acima, para ler ou escrever objetos datetime neste formato.
Se você precisar de um formatter customizado que pode ser descrito em termos de um pattern, você pode usar o
método de fabricação fornecido pela classe DateTimeFormat . Assim, para obter um formatter para um ano de
com 4 dígitos, mês com 2 dígitos, e dia do mês com 2 dígitos, ou seja, um formato yyyyMMdd você faria
...
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyyMMdd");
...
Pode haver a necessidade de fazer parse ou imprimir no formato de um determinado país (locale). Isto é
conseguido chamando o método withLocale de um formatter, que retorna outro formatter baseada no original.
...
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyyMMdd");
DateTimeFormatter frenchFmt = fmt.withLocale(Locale.FRENCH);
DateTimeFormatter germanFmt = fmt.withLocale(Locale.GERMAN);
...
Formatadores são imutáveis, de modo que o original não é alterada pelo método withLocale .
Por último, se você tem um formato que não é facilmente representado por um pattern, o Joda-Time expõe uma
classe builder que pode ser usada para construir um formatter customizado que é definido através do programa.
Assim, se você quiser um formatter para imprimir e analisar datas na forma "22-Jan-65", você poderia fazer o
seguinte:
...
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
.appendDayOfMonth(2)
.appendLiteral('-')
.appendMonthOfYearShortText()
.appendLiteral('-')
.appendTwoDigitYear(1956) // pivot = 1956
.toFormatter();
...
Cada método append adiciona um novo campo para ser analisado / impresso para o builder chamador e retorna
um novo builder. O último método toFormatter cria o formatter real que será usado.
O que é particularmente interessante sobre este formato é o "dois dígitos pro ano" ( appendTwoDigitYear() ).
Uma vez que a interpretação de um ano com 2 dígitos é ambígua, o método appendTwoDigitYear toma um
parâmetro extra que define o intervalo de 100 anos dos dois dígitos, indicando o ponto médio do intervalo.
Neste exemplo, a faixa será (1956 - 50) = 1906, a (1956 + 49) = 2005. Assim, 04 será 2004, mas 07 será 1907.
Este tipo de conversão não é possível com formato normal em patterns, destacando o poder da arquitetura de
formatação do Joda-time.
Para simplificar o acesso ao formatter, são fornecidos métodos de acesso nas classes datetime.
...
DateTime dt = new DateTime();
String a = dt.toString();
String b = dt.toString("dd:MM:yy");
String c = dt.toString("EEE", Locale.FRENCH);
DateTimeFormatter fmt = ...;
String d = dt.toString(fmt);
...
(1.13.6)
Classes utilitárias
Cada um dos quatro resultados demonstram uma forma diferente de usar o formatadores. O resultado a é uma
string no padrão ISO8601 para o DateTime. O resultado b irá utilizar o padrão de saída 'dd:MM:yy' (note que
os padrões são guardados em cache internamente). O resultado c será a saída usando o padrão "EEE", em
francês. O resultado d usará o formatter especificado, e assim é o mesmo que fmt.print(dt) .
A classe DateTime tem um construtor que leva um Object como entrada. Em especial, a este construtor pode
ser passado um Date do JDK, Calendar do JDK ou GregorianCalendar do JDK (Ele também aceita uma
String formatada em ISO8601, ou um objeto Long representando milisegundos). Esta é uma metade da
interoperabilidade com o JDK. A outra metade da interoperabilidade com o JDK é fornecida por métodos
DateTime que retornam objetos do JDK.
Assim conversão entre DateTime do Joda-time e Date do JDK, e pode ser realizado da seguinte forma
...
// from Joda to JDK
DateTime dt = new DateTime();
Date jdkDate = dt.toDate();
...
// from Joda to JDK
DateTime dt = new DateTime();
Calendar jdkCal = dt.toCalendar(Locale.CHINESE);
e GregorianCalendar do JDK:
...
// from Joda to JDK
DateTime dt = new DateTime();
GregorianCalendar jdkGCal = dt.toGregorianCalendar();
14.3. Miscelânea
O projeto Apache fornece as bibliotecas open-source commons-lang e commons-collections para ajudar a
resolver problemas de programação do dia-a-dia, como manipulação de caracteres, Strings, arrays e coleções.
14.3.1.1. Descrição
A biblioteca Commons Lang fornece adições um tanto quanto necessárias ao padrão java.lang da JDK. Ela
(1.13.6)
Classes utilitárias
O pacote principal contem várias classes utilitárias, enquanto há vários sub-pacotes incluindo enums, exceção e
construtor. Usar as classes utilitárias é geralmente muito simples. São o equivalente a funções globais em uma
outra língua, uma coleção de métodos stand-alone, thread-safe e estáticos. Em contraste, sub-pacotes podem
conter interfaces que necessitem de implementações ou classes que podem precisar serem estendidas para obter
a funcionalidade total do código. Podem, no entanto, conter mais funções do tipo global.
Commons Lang procura suportar o Java 1.2 em diante - então, embora você possa ter visto características em
umas versões mais novas do Java, tais como métodos de separação e exceções aninhadas, o framework ainda
mantem versões não java.lang para usuários das versões anteriores de Java.
Você encontrará métodos depreciados ("deprecated") enquanto percorre a documentação do framework. Estes
métodos serão removidos na próxima grande versão.
Antes de começar, é uma boa hora para falar das classes Utils. Todas elas contêm construtores públicos vazios
com avisos para não utilizar. Isto pode parecer uma coisa estranha, mas isso permite que as ferramentas como o
Velocity acessem as classes como se fossem "beans". Em outras palavras, sim, nós conhecemos construtores
privados.
Lang tem uma série de utilitários para String. O primeiro é StringUtils, com várias e várias funções que
ajustam, transformam, melhoram e tratam os java.lang.Strings. Além de StringUtils, tem uma série de outras
classes para manipulação de String: RandomStringUtils, StringEscapeUtils e Tokenizer. O nome da classe
RandomStringUtils fala por si só Ela oferece formas de gerar textos aleatórios, que podem ser utilizados como
senhas iniciais. StringEscapeUtils contém métodos para transformar textos Java, JavaScript, HTML, XML e
SQL, retirando carateres reservados. Tokenizer é uma melhor alternativa para java.util.StringTokenizer.
Essas classes são ideais para começar a utilizar esse framework. Métodos capitalize,
substringBetween/Before/After, split e join de StringUtils são bons para começar a usar. Se você usa muito
java.sql.Statements, StringEscapeUtils.escapeSql pode ser interessante.
Além destas classes, WordUtils é um outro manipulador de String. Ele funciona com Strings a nível da palavra,
por exemplo WordUtils.capitalize vai transformar em caixa alta a primeira letra cada palavra em um texto.
WordUtils também contém métodos para quebrar texto.
Além de lidar com Strings, também é importante lidar com chars e Characters. CharUtils existe para esta
finalidade, enquanto CharSetUtils existe para ajustar e manipular as Strings como conjunto de caracteres.
Tenha cuidado, embora CharSetUtils receba um argumento do tipo String, este é somente um conjunto de
caracteres. Por exemplo, CharSetUtils.delete("testtest", "tr") removerá todos os t's e todos os r's da
String, e não apenas as String "tr".
CharRange e CharSet são ambos usados internamente por CharSetUtils e provavelmente serão usados
raramente.
SystemUtils é uma pequena classe simples que torna fácil descobrir informações sobre qual plataforma você
está. Para alguns, este é um mal necessário. Serve para eliminar alguns detalhes específicos da versão do JDK.
if (SystemUtils.isJavaVersionAtLeast(1.3f)) { ... }
A classe CharEncoding é também utilizada para interagir com o ambiente Java e pode ser utilizado para ver que
codificações ("encoding") de caracteres são suportadas em um ambiente particular.
Serialização não tem que ser tão difícil! Uma classe simples do util pode facilitar, alem disso fornece um
método para clonar um objeto deserializando e reserializando, um truque velho de Java.
ObjectUtils contém funções úteis para Objetos, principalmente, implementações que tratam 'null' de forma
segura nos métodos de java.lang.Object.
ClassUtils é em grande parte um conjunto de métodos auxiliares para "reflection". Uma nota especial para a
ocultação dos comparadores no ClassUtils, úteis para classificar classes de objetos e pacotes pelo nome; porém
eles simplesmente ordenam alfabeticamente e não compreendem o método simples de classificação de java e
javax .
Em seguida, ArrayUtils. Este é um assunto extenso com muitos métodos e muitas sobrecargas de métodos de
modo que provavelmente merece um olhar detalhado aqui. Antes de começarmos, assuma que cada método
mencionado está sobrecarregado para todos os primitivos e para Object. Além disso, o 'xxx' denota um tipo
primitivo genérico, mas geralmente inclui também a classe Object.
• ArrayUtils fornece arrays vazios, com instância única ("singletons"), para todos os tipos basicos. Estes
serão largamente utilizados na API Collections com métodos toArray, mas também devem ser utilizados
com métodos que retornam um array vazio em caso de erro.
• add(xxx[], xxx) irá adicionar um tipo primitivo para um array, redimensionando o array como você
espera. Também suporta a classe Object.
• indexOf(xxx[], xxx) e indexOf(xxx[], xxx, int) são cópias dos métodos clássicos da classe String,
mas desta vez para arrays de primitivos e de Objects. Além disso, existe um conjunto de métodos do tipo
lastIndexOf .
• isEmpty(xxx[]) permite que você saiba se um array possui tamanho zero ou é nulo.
• Além dos métodos de adição, há também dois tipos de métodos de remoção. O primeiro tipo remove o valor
em um índice, remove(xxx[], int) , enquanto o segundo tipo remove o primeiro valor do array,
remove(xxx[], xxx) .
...
Map colorMap = MapUtils.toMap(new String[][] {{
{"RED", "#FF0000"},
{"GREEN", "#00FF00"},
{"BLUE", "#0000FF"}
});
...
Nossa última classe útil é BooleanUtils. Ele contém diversos métodos que tratam Booleanos, provavelmente o
de maior interesse é o método BooleanUtils.toBoolean(String) que transforma várias Strings
positivas/negativas em um objeto booleano, e não apenas true/false como Boolean.valueOf.
O framework Lang também tem uma série de exceções que nós achamos úteis. Todas estas exceções são
descendentes de RuntimeException, elas são apenas um pouco mais significativas do que
java.lang.IllegalArgumentException.
14.3.1.8. Texto
O pacote de texto fornece, entre outras classes, um substituto para StringBuffer nomeado StrBuilder , uma
classe para substituir variáveis dentro de uma String nomeada StrSubstitutor e um substituto para
StringTokenizer nomeado StrTokenizer . Mesmo não tendo ganhos, o prefixo Str tem sido utilizado para
garantir que não tenha conflito com qualquer padrão de classes Java atual ou futura.
Chegando ao final do nosso pacote, nós ficamos com um par de classes que não se encaixam em nenhum dos
tópicos até agora.
A classe de BitField fornece uma classe wrapper ao redor do clássico bitmask integer, enquanto que a classe
Validate pode ser utilizada para asserções (lembre-se, Java 1.2 também é suportado).
Commons-Collections fornece um grande número de classes para ajudar o dia a dia de programação. Este
documento destaca algumas características chaves para você começar.
• Maps
• Iteração em Maps
• Maps ordenados
(1.13.6)
Classes utilitárias
• Maps bidirecionais
• Queues e Buffers
• Bags
Commons-collections utiliza uma estratégia de sincronização semelhante ao Java collections padrão. A maioria
das diversas implementações de collections, maps e bags não são thread safe sem sincronização adicional. O
método synchronizeXXX adequado em Collections é uma forma que essas implementações podem ser
sincronizadas para uso em uma aplicação multithread.
Os javadocs da classe deverão indicar se uma determinada implementação é segura para acesso multithread
sem sincronização adicional. Onde não há nenhuma indicação explícita que a implementação é thread safe
então deve-se presumir que a sincronização é necessária.
14.3.2.1. Utilitários
Uma classe utilitária é fornecida para cada interface principal de collection. Assim, as interfaces Set e
SortedSet são fornecidas pela SetUtils . Estas classes fornecem métodos úteis para trabalhar com esse tipo de
collection.
A maioria dos métodos são encontrados em duas classes “raiz” de collection utility - CollectionUtils e
MapUtils . Como todas as outras interfaces collection estendem Collection ou Map estas utilities podem ser
usadas amplamente. Elas incluem intersecção, contagem, iteração, funções e operações de typecasting, entre
outras. As classes utility também fornecem o acesso a classes decorator em um modo similar à classe
Collections do JDK.
14.3.2.2. Mapas
...
IterableMap map = new HashedMap();
MapIterator it = map.mapIterator();
while (it.hasNext()) {
Object key = it.next();
Object value = it.getValue();
it.setValue(newValue);
}
...
...
OrderedMap map = new LinkedMap();
map.put("FIVE", "5");
map.put("SIX", "6");
map.put("SEVEN", "7");
(1.13.6)
Classes utilitárias
...
BidiMap bidi = new TreeBidiMap();
bidi.put("SIX", "6");
bidi.get("SIX"); // returns "6"
bidi.getKey("6"); // returns "SIX"
bidi.removeValue("6"); // removes the mapping
BidiMap inverse = bidi.inverseBidiMap(); // returns a map with keys and values swapped
...
Interfaces adicionais são fornecidas para mapas bidirecionais ordenados. Implementações são fornecidas para
cada tipo de mapa bidirecional.
14.3.2.3. Filas
Uma nova hierarquia de interface foi adicionada para suportar queues (filas) e buffers - Buffer . Estas
representam collections que tenham uma ordem de remoção bem definida.
...
Buffer buffer = new UnboundedFifoBuffer();
buffer.add("ONE");
buffer.add("TWO");
buffer.add("THREE");
buffer.remove(); // removes and returns the next in order, "ONE" as this is a FIFO
buffer.remove(); // removes and returns the next in order, "TWO" as this is a FIFO
...
Implementações são fornecidas para FIFO (queue), LIFO (stack) e Priority (remoção na ordem do comparador).
14.3.2.4. Bags
Uma nova hierarquia de interface foi adicionado para suportar bags - Bag . Estas representam collections onde
um certo número de cópias de cada elemento é armazenada.
...
Bag bag = new HashBag();
bag.add("ONE", 6); // add 6 copies of "ONE"
bag.remove("ONE", 2); // removes 2 copies of "ONE"
bag.getCount("ONE"); // returns 4, the number of copies in the bag (6 - 2)
...
14.4. Validadores
O Framework fornece um conjunto de classes que implementam validação, que podem ser muito úteis. Esses
validadores ficam no package com.altec.bsbr.fw.validator e estão listados na tabela a seguir:
Validator Descrição
Algumas classes utilitárias do Framework, como as Mapper e Marshaller usam os validadores indicados nas
anotações automaticamente, como visto nas seções de arquitetura estendida e processamento de registros .
Mas os validadores podem ser usados a qualquer momento, basta usar uma instância do validador. Os exemplos
a seguir mostram como utilizar os validadores:
ou...
...
private Validator emailValidator = new EmailValidator();
package com.altec.bsbr.fw.validator;
import com.altec.bsbr.fw.record.exception.ValidateException;
Os validadores estendem a classe abstrata BaseValidator . Assim, para criar outros validadores basta
estendê-la e implementar o método validate() .
Um recurso muito útil é o método reject() que recebe uma mensagem e um objeto e lança uma exceção
ValidateException com a mensagem e valor indicados. A seguir um exemplo de validador:
public StringPatternValidator(String s) {
pattern = Pattern.compile(s);
}
if (!matcher.matches()) {
reject("stringpattern.match.error", string);
}
}
}
14.5. Conversores
O Framework fornece um conjunto de classes para conversão de String em Objetos e vice-versa, que podem ser
muito úteis. Esses conversores ficam no package com.altec.bsbr.fw.converter e estão listados na tabela a
seguir:
Converter Descrição
(1.13.6)
Classes utilitárias
Converter Descrição
(1.13.6)
Classes utilitárias
Algumas classes utilitárias do Framework, como as Mapper e Marshaller usam os conversores indicados nas
anotações automaticamente, como visto nas seções de arquitetura estendida e processamento de registros .
package com.altec.bsbr.fw.converter;
import com.altec.bsbr.fw.record.exception.ConverterException;
Os conversores estendem a classe abstrata BaseConverter . Assim, para criar outros conversores basta
estendê-la e implementar os métodos parseValue() e formatValue ().
Um recurso muito útil é o método reject() que recebe uma mensagem e um objeto e lança uma exceção
ConverterException com a mensagem e valor indicados. A seguir um exemplo de conversor:
14.6.1. Commons IO
14.6.1.1. IOUtils
IOUtils contem os métodos utilitários que tratam a leitura, a escrita e a cópia. Os métodos trabalham em
InputStream , OutputStream , Reader e Writer .
Como um exemplo, considere a tarefa de ler bytes de um URL, e sua impressão. Isto é feito tipicamente da
seguinte forma:
...
InputStream in = new URL("http://jakarta.apache.org").openStream();
try {
InputStreamReader inR = new InputStreamReader( in );
BufferedReader buf = new BufferedReader(inR);
String line;
while ((line = buf.readLine()) != null) {
System.out.println(line);
}
} finally {
in.close();
}
...
...
InputStream in = new URL( "http://jakarta.apache.org" ).openStream();
try {
System.out.println(IOUtils.toString(in));
} finally {
IOUtils.closeQuietly(in);
}
...
Em determinados domínios da aplicação, tais operações do IO são comuns, e esta classe pode economizar
muito tempo. E você pode confiar num código que está bem testado.
Para classes utilitárias como esta, flexibilidade e velocidade são de primordial importância. Entretanto você
deve igualmente compreender as limitações deste enfoque. Usar a técnica acima para ler um arquivo de 1GB
resultaria na tentativa de criar um objeto String de 1GB!
14.6.1.2. FileUtils
A classe FileUtils contem métodos utilitários para trabalhar com objetos de arquivo. Estes incluem a leitura,
escrita, cópia e comparação de arquivos.
Por exemplo para ler todas as linhas de um arquivo, linha a linha, você poderia usar:
...
File file = new File("/commons/io/project.properties");
List lines = FileUtils.readLines(file, "UTF-8");
...
14.6.1.3. FilenameUtils
A classe FilenameUtils contem métodos utilitários para trabalhar com nomes de arquivo sem usar objetos da
classe File. A classe tem o objetivo de ser consistente entre Unix e Windows, de ajudar na transições entre estes
ambientes (tal como mover do ambiente de desenvolvimento para a produção).
Por exemplo, normalizar um nome de arquivo que remove segmentos de ponto duplos:
...
String filename = "C:/commons/io/../lang/project.xml";
String normalized = FilenameUtils.normalize(filename);
// result is "C:/commons/lang/project.xml"
...
14.6.1.4. FileSystemUtils
A classe FileSystemUtils contem métodos utilitários para trabalhar com o sistema de arquivo cuja
funcionalidade não é suportada pelo JDK. Atualmente, o único método é para obter o espaço livre em disco.
Observe que é utilizado um comando de linha e não código nativo.
...
long freeSpace = FileSystemUtils.freeSpace("C:/");
...
14.6.1.5. Streams
• Byte array output stream - é uma versão mais rápida da classe JDK
Codec é composta por um modesto conjunto de utilitários e um framework simples para codificação e
decodificação de dados tanto para textos quanto para dados binários.
(1.13.6)
Classes utilitárias
(1.13.6)
Classes utilitárias
O Framework possui uma série de classes para facilitar a manipulação de arquivos no formato CSV e com
registro de tamanho fixo. Elas estão descritas no capítulo de manipulação de arquivos batch .
DOM SAX
Deve ser usado em documentos pequenos, onde Deve ser usado em documentos grandes, e o
grande parte dele precisa ser processada. documento pode ser processado em blocos.
14.7.1. DOM
A API DOM se baseia na manipulação de elementos e atributos de um documento que representa o XML.
import java.io.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
visit(doc, 0);
try {
// Get the matching elements
NodeList nodelist = org.apache.xpath.XPathAPI.selectNodeList(doc, xpath);
14.7.2. SAX
A API SAX se baseia no tratamento de eventos que são disparados durante a leitura de um documento XML.
import java.io.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
Quando a aplicação for utilizar um Mail Provider definido no WebSphere, um objeto JavaMailSenderImpl
deve ser definido num arquivo de configuração, apontando para uma mail session tal como registrado no
servidor de aplicações. Exemplo:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
(1.13.6)
Classes utilitárias
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">
</beans>
Para obter a instância do JavaMailSender basta usar a injeção de dependências do Spring. Exemplo de uso:
@Service
public class MailServiceImpl implements MailService {
@Autowired
private JavaMailSender sender;
// criamos a mensagem
MimeMessage message = sender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message);
helper.setFrom(fromEmail);
helper.setTo("test@host.com");
helper.setText(conteudo);
// colocamos um anexo
FileSystemResource file = new FileSystemResource(new File("logo.jpg"));
helper.addAttachment("ABC.jpg", file);
// enviamos a mensagem
sender.send(message);
}
Para obter maiores informações, inclusive de como enviar arquivos anexos ou usar templates, consulte a
documentação do Spring para e-mail.
(1.13.6)
Capítulo 15. Gerenciamento e monitoração
15.1. Introdução
Este capítulo mostra algumas características do Framework que facilitam o gerenciamento e monitoração de
aplicações construídas com ele.
15.1.1. JMX
JMX é um padrão Java que permite instrumentar aplicações para que elas sejam gerenciadas remotamente.
Com ela é possível monitorar um sistema e verificar ou alterar o valor de parâmetros de configuração.
A entidade básica que expõe informação de gerência é o MBean (Managed Bean). Portanto o MBean é usado
para instrumentar um recurso que se quer gerenciar, expondo uma interface para um recurso, que pode ser
manipulado usando um agente JMX.
Ferramentas de gerenciamento compatíveis com o padrão JMX (como o JConsole presente no JDK ou o Tivoli
Monitoring da IBM) podem ser usadas para monitorar e configurar MBeans.
A camada de negócio é o núcleo da lógica da aplicação. Como tanto, esta camada contém os pontos críticos de
performance e disponibilidade nos quais as requisições se concentram. Desta forma, as interfaces de serviço da
camada de negócio são as fortes candidatas a monitoração.
Todos os serviços implementados com o Framework são automaticamente monitorados e algumas informações
básicas sobre seu uso são coletadas. Para cada método definido na interface de negócio do serviço estão
disponíveis as propriedades da tabela a seguir.
Propriedade Descrição
Propriedade Descrição
Quando o Framework detecta um serviço, é criado um MBean relativo a cada um de seus métodos. Esse
MBean é registrado automaticamente no MBeanServer disponível (do servidor de aplicações ou JDK).
• As propriedades cell, node e process são acrescentadas pelo WebSphere de acordo com o servidor onde o
serviço está instalado.
Um exemplo de tal MBean numa instância do WebSphere Application Server, teria o ObjectName:
[demo:type=ServiceStats,name=com.altec.bsbr.app.demo.service.TestServiceImpl.findAll,
cell=bsbrsp311Node01Cell,node=bsbrsp311Node01,process=server1]
Todas as informações coletadas durante a execução da aplicação, são armazendas em memória, e serão
perdidas após a aplicação ser parada.
• Versão da aplicação.
Esses MBeans podem ser consultados na árvore JMX via uma interface de monitoração como JConsole. O
MBean com nome ApplicationVersionInfo guarda as seguintes informações referentes a aplicação:
• Título da aplicação
• Nome da aplicação
• Pacote de distribuição
• Fornecedor da aplicação
Importante
Esses dados são obtidos a partir do arquivo de propriedades
META-INF/config/application.properties empacotado na aplicação. Portanto, é essencial que
ele seja preenchido corretamente.
Para exportar um objeto como MBean, basta criar uma classe Java qualquer no pacote mbean, e anotá-la com
org.springframework.jmx.export.annotation.ManagedResource. Os métodos que devem ser expostos
devem ter as anotações org.springframework.jmx.export.annotation.ManagedOperation ou
org.springframework.jmx.export.annotation.ManagedAttribute.
Criando a classe exemplo abaixo, ela será automaticamente registrada no MBeanServer do servidor de
aplicações, e exportará a operação sayHello.
package com.altec.bsbr.app.demo.mbean;
@ManagedResource
public class HelloMBean implements HelloService {
@Autowired
private HelloService service;
@ManagedOperation
public String sayHello(String name) {
return service.sayHello(name);
}
(1.13.6)
Gerenciamento e monitoração
Para maiores informações sobre as anotações de managed beans, consulte o manual do Spring.
Abaixo podemos ver alguns comando para manipular MBeans gerados pelo Framework:
Para maiores informações de como usar o wsadmin, consulte o WebSphere Application Server V6 System
Management & Configuration Handbook.
(1.13.6)
Capítulo 16. Testes Unitários
16.1. Introdução
Em programação de computadores, o uso de testes unitários é um método de testes que verifica se unidades
individuais de código fonte estão funcionando apropriadamente. Uma unidade é a menor parte testável de uma
aplicação. Em programação orientada a objetos, a menor parte testável é um método, o qual pode pertencer a
uma classe base, super classe, classe abstrata e uma classe derivada.
É importante que cada caso de teste seja independente dos outros. Para isso, quando o objeto testado depende
de outros, é necessário o uso de objetos falsos (mock objects) os quais tem a responsabilidade de fazer o
isolamento entre os testes. Testes unitários são tipicamente feitos pelos desenvolvedores para asegurar que o
código escrito atenderá os requisitos da aplicação desenvolvida.
16.2. Objetivo
O principal objetivo dos testes unitários é isolar cada parte da aplicação afim de certificar que estão todas
corretas. Um teste unitário deve ser categorico ao testar uma unidade de código, ou seja, deve testar todos os
contextos possíveis onde tal unidade de código poderá estar sujeita.
16.3. Benefícios
• Facilidade de manutenção
Testes unitários permitem que um programador refatore um código em um momento posterior e ainda
asegurar que o módulo ainda funcionará corretamente (Testes de Regressão). O procedimento é escrever
casos de testes para todas as funções e métodos, de tal forma que sempre que uma mudança causar uma
falha, está será rapidamente identificada.
• Simplifica a integração
• Acrescenta a documentação
Testes unitários provêem uma documentação viva do sistema. Para os desenvolvedores afim de encontrar
facilmente uma documentação de como usar determinados componentes, classes utilitárias, regras de
negócio, iteração com classes, etc. podem achá-las nos testes unitários, os quais fornecerão uma ótima
descrição de como usar tal API e em quais contextos um determinado método ou classe foram desenhados.
foram desenhados utilizando-se interfaces para que sejam fácilmente substituidos afim de permitir que os testes
tenham o máximo de isolamento possível.
O Framework JUnit versão 4.4 é o que será usado para rodar os testes unitários.
Para criar uma classe de teste precisamos seguir os passos descritos abaixo:
[Raiz_Projeto]/
src/
src-test/❶
lib/
ear/
[Raiz-Projeto]/
src/
com.altec.bsbr.app.projetox.util.format/
FormataData.java❶
src-test/
com.altec.bsbr.app.projetox.util.format.test/
FormataDataTest.java❷
lib/
ear/
...
public class FormataData {
public String formatDate(java.util.Date date) {
...
}
}
...
public class FormataDataTest {
...
@org.junit.Test❶
public void testFormatDate() throws Exception {
❶ Anotação @org.junit.Test usada para assinar um método e informar o Framework que tal método
deve ser executado como test unitário.
JUnit.org
JUnit 4 in 60 Seconds
3. Agora, para executar o teste, utilizamos a ferramenta de build Ant o qual irá compilar os testes e
executa-los criando um relatório Html dos resultados dos testes. Veja abaixo os passos para executar o
teste unitário utilizando o Ant no Eclipse.
(1.13.6)
Testes Unitários
(1.13.6)
Parte III. Aplicação de Referência
Esta seção detalha a construção de uma aplicação de referência usando o Framework JAB.
17.1. Introdução
O Framework possui uma aplicação de referência para exemplificar seu uso. Essa aplicação de referência
contempla vários recursos do Framework, possibilitando por exemplo, a visibilidade na forma de
desenvolvimento de futuras aplicações. Este capítulo faz uma descrição funcional da aplicação de referência.
17.2.1. ABC-Online
• Home - página organizada por categorias musicais, e para cada categoria estão disponibilizados os produtos
com seus respectivos detalhes;
• Login - página que permite ao usuário inserir seu dados de login para o acesso do cliente na loja. Após
logado, o cliente poderá visualizar o pedido, concluir a compra e selecionar a forma de pagamento desejada;
• Carrinho - página onde são listados os produtos selecionados pelo usuário. É nesta página que o usuário
também conclui a compra de produtos;
• Cadastro de Cliente: A loja disponibiliza uma tela para cadastro do cliente, informando alguns dados
pessoais, como nome, endereço (incluindo cidade e estado), e-mail e o cadastro de senha de acesso.
• Login de Acesso: Para toda compra, o usuário deverá efetuar o login informando seu endereço de e-mail e a
sua senha cadastrada.
• Busca de Produtos: Para facilitar a busca de produtos, o usuário poderá dispor do uso da Busca disponível
na página principal da loja.
• Adicionar Produtos no Carrinho: Ao identificar um produto de interessse, o usuário deve escolher a opção
de adicionar os produtos no carrinho.
• Finalizar Pedido: Uma vez que os produtos estejam no carrinho, para concluir a compra dos mesmos o
usuário deverá optar por finalizar o pedido.
• Pagamento: O usuário terá a opção de escolhar a forma de pagamento de seu pedido, as opções são: cartão
de crédito ou débito e boleto bancário.
• Solicitar Suporte: O usuário poderá soliciar os serviços de suporte da loja. Basta o cliente informar o e-mail,
telefone e a descrição do serviço.
17.2.2. ABC-Admin
O ABC-Admin é o módulo destinado ao trabalho dos administradores do site, organizado na seguinte estrutura:
• Controle de Estoque
• Login de Acesso: O processo de login é de acordo com o perfil administrativo do usuário, sendo um
administrador geral, administrador do estoque ou acompanhamento de pedidos.
• Acompanhamento de Pedido: O usuário poderá acompanhar os pedidos fazendo consultas por status de
pedido.
• Tratamento de Pedido: O tratamento de pedido é feito por um processamento em batch para a mudança
de status do pedido. Os status se resumem a dois tipos, Pedido Realizado e Pedido Entregue.
• Pedido Realizado: é o primeiro status o qual um pedido deve assumir, após o pagamento realizado
pelo clinete na Loja ABC-Online.
• Pedido Entregue: é o segundo status o qual um pedido deve deve assumir, após a entrega do pedido
ao cliente.
• Emissão de Relatórios: É um recurso da aplicação para emitir relatórios para o controle da quantidade de
produtos no estoque, de status dos pedidos e da quantidade de vendas por período. Os relatórios podem ser
exportados em formato PDF ou XLS.
• Consulta Estoque: permite ao usuário analisar a quantidade de produtos no estoque. Os produtos serão
listados em ordem decrescente de quantidade.
(1.13.6)
Capítulo 18. Arquitetura e implementação
18.1. Introdução
Este capítulo detalha a arquitetura e implementação da aplicação de referência do Framework.
• Camada de Negócio: a aplicação segue as regras de criação de serviços do Framework, utilizando anotações
que serão controladas pelo Spring.
Interfaces:
• EstoqueDao
• LoginDao
• PopulateTablesDao
• ClienteDao
• PedidoDao
• ProdutoDao
Classes de Implementação:
• populate(): Método que popula informações dos produtos, como código, nome do CD, artista, data de
lançamento e categoria.
Interfaces:
• EstoqueService
• LoginException
• LoginService
• MailService
• PedidoException
• PedidoService
• PopulateTablesService
• ProdutoService
• TestService
Classe de Implementação:
implementando os métodos:
• TestServiceImpl: classe utilizada para teste, que implementa o método test(String name) que retorna
uma string.
(1.13.6)
Arquitetura e implementação
• BuscaBBean: classe que faz a busca de produtos pelo site, informando uma palavra-chave.
• CarrinhoBBean: classe onde são adicionados os produtos selecionados pelo usuário. A classe implementa
os principais métodos:
• CategoriaBBean: a classe lista todas a categorias musicais (existentes no estoque) que serão exibidos na
página inicial da aplicação.
• ClienteBBean: a classe para fazer o cadastro do cliente e também utilizada para autorização de login.
(1.13.6)
Arquitetura e implementação
• DetalhesCDBBean: a classe exibe os detalhes do produto que além do título, autor e ano de lançamento do
CD, são exibidos: o preço e a faixa musical do CD.
• FirstPageBBean: a classe exibe na página principal a classificação dos produtos por categoria musical.
• LoginBBean: Página de login para acesso do cliente a loja. A classe implementa os principais métodos:
• isPagPendente(): método retorna os pedidos com pagamentos pendentes ao cliente (caso este tiver
pendência).
• SuporteBBean: classe para solicitação de suporte, informando e-mail, telefone para contato com o cliente e
a descrição do serviço.
• PedidosEfetuadosBBean: classe responsável por listar pedidos efetuados pelo cliente, utilizando os
métodos void search(ActionEvent event) para a busca dos pedidos por cliente.
18.3. ABC-Admin
A camada de persistência
de dados da aplicação ABC-Admin encontra-se no pacote
com.altec.bsbr.app.abcAdmin.dao. O diagrama a seguir mostra a camada com as respectivas
implementações dos DAOs da aplicação.
Interfaces:
• EstoqueDao
• PedidoDao
Classes de Implementação:
• List<Pedido> findByStatus(final StatusPedido status): método que retorna uma lista de pedidos
por status.
Interfaces:
• EstoqueService
• HelloService
• MenuItemHolder
• MenuService
• PedidoService
Classes de Implementação:
• MenuServiceImpl: o serviço lista todos os acessos permitidos ao usuário. A classe chama o método
buildUserMenu(String user, String system) que retorna uma instância de Authorization com a
construção do menu autorizado ao usuário logado.
• PedidoServiceImpl: serviço localiza pedidos por período (data início e data final) e pedidos por status
(pedidos realizados e pedidos entregues).
(1.13.6)
Arquitetura e implementação
• FirstPageBBean: a classe renderiza o menu de acordo com a tipo de perfil do usuário logado.
• IntegracaoAeaBBean: classe que faz a comunicação síncrona (requisição e resposta) com a Arquitetura
Estendida Altair, fazendo a transformação entre objetos Java e o formato da AEA.
(1.13.6)
Arquitetura e implementação
• TestEJBBBean: a classe é um exemplo para EJB, que permite calcular dois números resultado na soma dos
mesmo.
• WebServiceBBean: a classe utiliza web service para validar endereço de e-mail, teste de serviço e busca de
cd através do código do produto.
A aplicação batch realiza a mudança de status de pedidos no sistema ABC Admin. No processo são utilizados
as classes PedidoTask e ProcessaPedidoConsumer, ambos encontram-se no pacote
com.altec.bsbr.app.abcAdmin.batch.
O processo batch usa o padrão produtor-transformação-consumidor, onde tanto o produtor como o consumidor
usam o banco de dados.
O Produtor utilizado para gerar os dados é indicado no batch.xml (package abcBatch.src.batch.xml), onde a
classe FromDatabaseProducer é utilizada para gerar os registros a partir de uma consulta na tabela de Pedido
localizando os pedidos com status de Pedido Realizados.
• PedidoTask:a classe faz a chamada do método execute() recebendo um objeto contendo as colunas do
ResultSet (fornecido pelo RecordProducer) dos pedidos com status de Pedido Realizado, e devolve apenas
• PedidoTask:a classe faz a chamada do método execute() recebendo um objeto contendo as colunas do
ResultSet (fornecido pelo RecordProducer) dos pedidos com status de Pedido Realizado, e devolve apenas
o código dos pedidos.
• GenericJdbcDao: Utiliza recursos jdbc para fazer update dos Pedidos Realizados para Pedidos Entregues na
tabela Pedidos.
O processo é representado pelos pojos Cd e Track que serão lidos pelo Produtor (FromFileProducer) e
processados por uma Task (CdtoProdutoTask) e consumidos pelo Consumidor (DatabaseConsumer).
A aplicaçao batch realiza a atualização da informações do Cd no sistema ABC Admin. No processo são
utilizados as classes FromFilesProducer (Produtor), CdtoProdutoTask (Tarefa) e DatabaseConsumer
(Consumidor), as classes encontram-se no pacote com.altec.bsbr.app.abcProductsDataLoader.
(1.13.6)
Arquitetura e implementação
• Código
• Nome do Artista
• Título do Cd
• Imagem da Capa do Cd
• Gênero Músical
(1.13.6)
Arquitetura e implementação
• Ordem da música
• Descrição
• next(): o metodo retornará mais um registro a ser processado, cada vez que o método for chamado.
• CdtoProdutoTask:
a classe faz a chamada do método execute() recebendo o Cd e retornando as atualizações
das informações do Produto - getProduto (Cd cd), do produto no Estoque - getEstoque(Cd data) e da
Parte do Produto - getPartesProduto.
• getProdutoFrom(Cd cd): insere as informações do cd como o nome, título, ano de lançamento, autor,
etc, atualizando a tabela Produto e Categoria
• getPartesproduto(Cd cd): insere as partes do cd, como a descriçao e a ordem das músicas do Cd,
atualizando a tabela ParteProduto.
• Task<Cd, Object>: classe padrão do Framework, responsável por transformar os dados de entrada em
objetos prontos para serem consumidos. A tarefa CdtoProdutoTask deve implementar a classe Task.
(1.13.6)
Arquitetura e implementação
• RecordConsumer<Object>: classe padrão do Framework, responsável por consumir os registros após eles
serem processados (processados pelo CdtoProdutoTask) , que neste caso deverá gravar as informações do
Cd nas tabelas envolvidas. A classe ProcessaPedidosConsumer deve implementar a interface RecordConsumer.
• GenericJdbcDao: Utiliza recursos jdbc do Framework para fazer os updates nas tabelas.
(1.13.6)
Apêndice A. Referências
A.1. Referências
Referências bibliográficas e internéticas.
• Slf4j 1.5.3 – proporciona uma fachada genérica para vários outras bibliotecas de log. Proporciona uma
mecanismo plugável de integração com os logs mais comuns utilizados. [http://www.slf4j.org/]
• Logback 0.9.9 – implementação de Log mais madura e considerada uma evolução ao log4j.
[http://logback.qos.ch/]
• Spring 2.5.5 – framework vastamente difundido usado como um “container leve”. Proporciona mecanismo
produtivo para desacoplamento entre as camadas da aplicação, executando de forma automática injeção de
dependência e integração com diversos serviços de aplicações corporativas como JTA, JPA, EJB.
[http://www.springframework.org/]
• ICEFaces 1.7 – implementação de componentes Java Server Faces, com suporte a Ajax.
[http://www.icefaces.org/]
• Jamon 2.7 – biblioteca que disponibiliza uma API para registro de acesso e monitoramento a métodos da
camada de serviços da aplicação. [http://jamonapi.sourceforge.net/]
• Joda-time 1.5.2 – proporciona uma forma mais madura e avançada que a API Java para tratamento de
data/hora e manipulação de Calendários. [http://joda-time.sourceforge.net/]
• Apache Commons – bibliotecas com várias classes utilitárias que simplificam tarefas de programação
comuns:
• IO 1.4 [http://commons.apache.org/io/]