Professional Documents
Culture Documents
Produzido por Alberto Barbosa Projeto SPB SP02 Centro de Tecnologia da Informao Renato Archer
Contedo
1 Introduo. ...................................................................................................................................... 2 1.1 Necessidade e Desafios de uma Integrao ............................................................................ 2 1.2 As Solues............................................................................................................................. 3 1.2.1 Soluo Point-to-Point .................................................................................................... 3 1.2.2 Soluo Hub-and-Spoke ................................................................................................. 4 1.2.3 Enterprise Message Bus .................................................................................................. 4 1.2.4 Enterprise Service Bus .................................................................................................... 5 Enterprise Service Bus ESB......................................................................................................... 6 2.1 Caractersticas de um ESB ...................................................................................................... 6 2.1.1 Difuso ............................................................................................................................ 6 2.1.2 Integrao Baseada em Padres ...................................................................................... 6 2.1.3 Integrao Altamente Distribuda e Seletivamente Implantada ...................................... 7 2.1.4 Transformao de Dados ................................................................................................ 7 2.1.5 Segurana e Confiabilidade ............................................................................................ 7 2.1.6 Ambiente Autnomo e Federado .................................................................................... 7 2.1.7 Adoo Incremental ........................................................................................................ 7 2.2 Funcionalidades de um ESB ................................................................................................... 8 2.2.1 Transparncia de Localizao ......................................................................................... 8 2.2.2 Converso de Protocolo de Transporte ........................................................................... 8 2.2.3 Transformao de Mensagem ......................................................................................... 9 2.2.4 Roteamento de Mensagem .............................................................................................. 9 2.2.5 Enriquecimento de Mensagens ..................................................................................... 10 2.2.6 Segurana ...................................................................................................................... 10 2.2.7 Monitoramento e Gerenciamento .................................................................................. 11 ESB Mule ...................................................................................................................................... 11 3.1 Arquitetura do ESB Mule ..................................................................................................... 11 3.1.1 Servios ......................................................................................................................... 11 3.1.2 Transportes .................................................................................................................... 12 3.1.3 Roteadores..................................................................................................................... 15 3.2 Arquivo de Configurao do ESB Mule ............................................................................... 15 Estudo de Caso .............................................................................................................................. 17 4.1 Arquitetura da Soluo.......................................................................................................... 17 Estudo de Caso Fase I ................................................................................................................ 18 5.1 Implementao da Aplicao ................................................................................................ 19 5.1.1 A Aplicao Web .......................................................................................................... 19 5.1.2 O Servio de Cotao de Emprstimo .......................................................................... 19 5.1.3 O Servio de Gerao de Respostas .............................................................................. 20 5.1.4 O Transformer de Solicitaes de Cotao ................................................................... 21 5.1.5 O Transformer de Resposta de Cotao ........................................................................ 24 5.2 Implementao do Fluxo de Mensagens ............................................................................... 24 5.2.1 Declaraes de Namespaces e Esquemas. .................................................................... 25 5.2.2 Configurao dos Transformers .................................................................................... 25 5.2.3 Configurao dos Endpoints ......................................................................................... 25 5.2.4 Configurao dos Servios............................................................................................ 28 5.3 Organizao da Aplicao .................................................................................................... 30
4 5
5.4 Executando a Aplicao ........................................................................................................ 31 Estudo de Caso Fase II ............................................................................................................... 34 6.1 Implementao da Aplicao ................................................................................................ 37 6.1.1 A Aplicao Web .......................................................................................................... 37 6.1.2 Servios da Agncia de Emprstimo............................................................................. 38 6.1.3 Agncia de Crdito........................................................................................................ 40 6.1.4 O Servio de Seleo Bancria ..................................................................................... 43 6.1.5 Bancos ........................................................................................................................... 46 6.1.6 Tratamento de Erros ...................................................................................................... 48 6.1.7 Transformers para a Agncia de Crdito ...................................................................... 49 6.1.8 Transformer para Seleo Bancria .............................................................................. 51 6.2 Implementao do Fluxo de Mensagens ............................................................................... 53 6.2.1 Declaraes de Namespaces e Esquemas ..................................................................... 53 6.2.2 Configurao dos Transformers .................................................................................... 53 6.2.3 Configurao dos Endpoints ......................................................................................... 54 6.2.4 Configurao dos Servios............................................................................................ 56 6.3 Organizao da Aplicao .................................................................................................... 66 6.4 Executando a Aplicao ........................................................................................................ 67 7 Estudo de Caso Fase III ............................................................................................................. 69 7.1 Implementao da Aplicao ................................................................................................ 73 7.1.1 Bancos ........................................................................................................................... 73 7.1.2 Gerador de Relatrio ..................................................................................................... 79 7.1.3 Emisso Automtica de Relatrio ................................................................................. 83 7.1.4 Transformer para os Bancos A e B ............................................................................... 84 7.2 Implementao do Fluxo de Mensagens ............................................................................... 86 7.2.1 Declaraes de Namespaces e Esquemas ..................................................................... 86 7.2.2 Configurao dos Transformers .................................................................................... 87 7.2.3 Configurao dos Endpoints ......................................................................................... 87 7.2.4 Configurao dos Servios............................................................................................ 92 7.3 O Arquivo de Configurao Mule ........................................................................................ 96 7.4 Organizao da Aplicao .................................................................................................. 103 7.5 Executando a Aplicao ...................................................................................................... 104 8 Referncias.................................................................................................................................. 104 6
1 Introduo.
O objetivo deste tutorial apresentar o uso de ESB (Enterprise Service Bus) como infra-estrutura para prover interoperabilidade entre aplicaes. Como o objetivo apresentar uma soluo prtica que auxilie arquitetos e desenvolvedores a construrem solues integradas usando ESB, utilizaremos o ESB Mule como estudo de caso. Antes de entrarmos no estudo de caso propriamente dito, vamos fazer uma breve introduo tecnologia ESB e ao ESB Mule.
decises. O processo de integrao entre aplicaes enfrenta uma srie de desafios que esto alm dos problemas tcnicos e de negcios: Aplicaes de negcios geralmente focam em uma rea de negcio especfica, como conseqncia, os grupos de TI geralmente esto alinhados com reas funcionais especficas. Uma integrao bem sucedida no estabelece comunicao apenas entre sistemas computacionais, mas tambm entre unidades de negcios e grupos de TI. Em um ambiente integrado, os grupos no controlam mais uma aplicao especfica porque cada aplicao passa a ser parte integrante de um fluxo geral de aplicaes e servios integrados. Devido a sua larga abrangncia, os esforos de integrao tm implicaes sobre os negcios. Uma vez incorporada uma funcionalidade crtica de negcio soluo integrada, toda a soluo passa a ser crtica para a empresa. Uma falha na soluo integrada pode ter grande impacto nos negcios da empresa. Uma restrio importante ao processo de integrao o baixo controle sobre as aplicaes existentes. Muitas aplicaes so aplicaes legadas sobre as quais no se consegue fazer alteraes para conect-las soluo integrada. A despeito da reconhecida necessidade de integrao de solues, poucos padres estabeleceram-se neste domnio. O advento do XML, XSL e Web Services so os avanos mais significantes nesta rea. Mesmo utilizando XML para padronizar toda a troca de dados, ainda temos um grande desafio no que se refere semntica das informaes. Desenvolver solues integradas um grande desafio, mas a operao e manuteno de tal soluo podem ser ainda mais amedrontadoras. A mistura de tecnologias e a natureza distribuda da soluo fazem com que a distribuio, monitoramento e resoluo de problemas sejam tarefas complexas que requerem uma combinao de competncias que podem estar espalhadas por vrios profissionais dentro da corporao. No existem respostas simples para os problemas de integrao, mas a adoo de tecnologias que utilizam padres de problemas e suas solues pode facilitar enormemente esta tarefa.
1.2 As Solues
Integrar aplicaes uma tarefa difcil e complexa devido grande variedade de arquiteturas e tecnologias utilizadas. Vrias tcnicas, padres e tecnologias foram adotados com o objetivo de integrar aplicaes, vamos destacar algumas arquiteturas utilizadas para integrao: Soluo Point-to-Point. Soluo Hub-and-Spoke Enterprise Message Bus Enterprise Service Bus. As publicaes [1], [2] e [3] constituem um excelente material para se conhecer as tecnologias utilizadas em integrao de aplicaes. A publicao [1] descreve os padres de integrao abordando de maneira extensa os padres relacionados com messaging. O livro [2] aborda especificamente a tecnologia ESB e [3] aborda principalmente JBI, como base para implementao de ESBs.
Aplicao
Aplicao
Aplicao
Aplicao
Aplicao
Aplicao
HUB
Aplicao
Aplicao
Aplicao
Barramento de mensagens
Aplicao
Aplicao
Aplicao CI
Aplicao CI
ESB
CI Aplicao
CI Aplicao
2.1.1 Difuso
Um ESB tem a capacidade de abranger toda a corporao e ir alm, formando um grid de difuso de aplicaes e servios com alcance global entre departamentos da organizao, unidades de negcios e parceiros comerciais. Aplicaes se conectam ao barramento quando necessrio, possibilitando visibilidade e troca de dados entre as aplicaes e servios conectados ao barramento.
C/C++. Adicionalmente ESB pode integrar facilmente aplicaes que suportam SOAP e APIs Web Service. Para compartilhamento de dados, ESB pode usar padres XML como: XSLT, XPath e XQuery, provendo transformao de dados e roteamento inteligente. ESB pode tambm utilizar WSDL para descrever interfaces abstratas de servios e Business Process Execution Language (BPEL4WS) para Web Services.
ESB
ESB
ESB broker
ESB
Domnio I
Domnio II
Domnio III
Domnio IV
Aplicao cliente
de protocolos e outros, adquiridos de terceiros, podem ser adicionados ao ESB. A figura a seguir demonstra a funcionalidade de converso de protocolo.
ESB Aplicao cliente Aplicao legada adaptador JMS adaptador arquivo Arquivo
transformador de mensagem
Mensagem EDI
Aplicao provedora A ESB Aplicao cliente roteador Aplicao provedora B Figura 9 Funcionalidade Roteamento de Mensagem.
ESB Aplicao cliente Mensagem original Mensagem enriquecida Banco de dados Figura 10 Enriquecimento de mensagem Aplicao provedora enriquecedor de mensagem
2.2.6 Segurana
Como um ESB lida com lgica de integrao de negcios, ele deve prover mecanismos para autenticao, autorizao e criptografia das mensagens que chegam at ele. Quando o ESB oferece ponto de acesso de integrao para aplicaes fora dos domnios da empresa, essa questo de segurana passa a ser ainda mais importante e crtica. A figura a seguir d uma idia de como podemos configurar segurana em um ESB.
Aplicao provedora
Mensagem criptografada
3 ESB Mule
At aqui descrevemos as caractersticas gerais de um ESB, a partir deste ponto vamos apresentar um ESB especfico, o ESB Mule, e descrever suas principais caractersticas e componentes. Iniciaremos apresentando sua arquitetura e componentes, seus conceitos principais e suas funcionalidades. Aps esta parte conceitual iniciaremos um estudo de caso utilizando o ESB Mule. Neste estudo de caso, apresentaremos, em detalhes, as configuraes da soluo integrada, que esperamos seja til para aqueles que esto envolvidos com a difcil tarefa de promover integrao entre aplicaes. As publicaes [4], [5], [6] e [7] abordam o ESB Mule em detalhes. A publicao [4] tambm descreve outro ESB open source, o ServiceMix.
3.1.1 Servios
Mule um ambiente de execuo que hospeda servios. Servios so conjuntos discretos de funcionalidades que so completamente separados uns dos outros, mas que podem trabalhar juntos sobre os mesmos objetos. Um servio Mule, representado pela figura a seguir, composto por trs elementos: Um inbound router, que especifica quais mensagens o componente de servio processar. Um componente de servio, que implementa a lgica de integrao do servio e pode ser implementado utilizando vrias tecnologias, como: POJO, REST service e BPM, entre outros.
Um outbound router, que determina para onde a mensagem dever ser enviada aps ser processada pelo componente.
Figura 12 Elementos que compem um servio. Quando uma mensagem enviada por uma aplicao, Mule pega a mensagem, envia-a para o servio que a processa usando alguma lgica e encaminhe-a para a aplicao correta. Mule possui muitas partes individuais que tratam o processamento e roteamento da mensagem. A principal parte de um servio o componente do servio, que executa lgica de negcio sobre as mensagens. Uma caracterstica importante do componente do servio que ele no tem que ter nenhum cdigo especfico Mule; ele pode ser simplesmente um POJO, um Spring bean, um Java bean ou um web service. Mule gerencia o componente de servio, envolve-o com configuraes e o expe como um servio, assegurando que a informao correta passada para ele e a partir dele conforme sua configurao. Um componente de servio no contm qualquer informao de como receber e enviar mensagens. Para assegurar que um componente de servio receba corretamente mensagens e as encaminhe apropriadamente aps o processamento, precisamos especificar um inbound router e um outbound router. Um inbound router especifica quais mensagens o componente de servio ir processar. Ele pode filtrar, agregar e mudar a seqncia de mensagens antes de encaminh-la ao componente de servio. Aps o componente de servio ter processado a mensagem, o outbound router especifica para onde encaminhar a mensagem. Podem ser definidos mtiplos inbound e outbound routers e mesmo cadeias de routers.
3.1.2 Transportes
Mule pode tratar mensagens que so enviadas em uma variedade de protocolos como, HTTP, JMS, FTP, VM, SMTP, JDBC e outros. O componente de servio no se preocupa com o protocolo usado, na verdade um componente de servio no sabe como ler mensagens e no precisa se preocupar com os formatos das mensagens. Ao invs disso, um transporte se encarrega de transportar as mensagens e os transformers se encarregam de mudar as mensagens de acordo com o formato requerido pelo componente de servio. Todo o transporte, transformao e roteamento de mensagens so completamente transparentes para o componente de servio. A figura a seguir apresenta um caso onde as mensagens chegam via transporte HTTP, so transformadas de XML para objetos Java, processadas e enviadas utilizando transporte JMS.
Inbound router
outbound router
Mensagem
Transporte HTTP
Transporte JMS
Mensagem
Figura 13 Roteamento e transformao de mensagens. Um transporte Mule prov todos os elementos ESB requeridos para receber, enviar e transformar mensagens para um protocolo particular. Um transporte se manifesta em uma configurao pelos seguintes elementos: conectores, endpoints e transformers. A figura a seguir apresenta os elementos de um transporte Mule.
Message dispatcher
Endpoint
3.1.2.1 Conectores
Um conector permite a um componente enviar ou receber dados atravs de um protocolo especfico. O conector tem a responsabilidade de controlar o uso de um protocolo particular. Ele configurado com parmetros especficos para um protocolo e mantm qualquer estado que pode ser compartilhado com as entidades abaixo que fazem a comunicao real. Temos ento conector JMS, conector HTTP, etc. Na figura 13 ns utilizamos um conector HTTP e um conector JMS. Um conector uma referncia para uma coleo de classes que so responsveis pela comunicao, como as seguintes: Message Receiver. uma classe que sabe como receber dados usando um protocolo e convert-los em um formato que o Mule possa us-los.
Message Dispatcher. uma classe que sabe como converter dados Mule para transmisso usando um protocolo especfico. Transport-specific transformer. Classes opcionais que permitem transformar os dados quando esto sendo enviados ou recebidos.
3.1.2.2 Endpoints
Um endpoint representa o uso especfico de um protocolo, se ele para listening/polling, para leitura ou escrita. Endpoints controlam quais entidades sero usadas com um conector. Endpoints so elementos-chave de configurao para ligar todos os servios. Inbound e outbound endpoints existem no contexto de um servio e representam respectivamente a entrada esperada e pontos de sada para mensagens. Dentro de Mule, um endpoint pode conectar componentes de servios a recursos locais (como arquivos) ou a recursos de rede (por exemplo, conexes HTTP ou aplicaes de terceiros). Eles servem tambm como locais onde configurar vrias outras caractersticas do ESB Mule, como filtros, transformers e transaes. A figura a seguir mostra o uso de endpoints para ligao entre servios internos e externos ao ESB Mule.
Instancia Mule Aplicao ou servio e p Componente de servio ep e p e p Componente de servio e p Aplicao ou servio e p ep - endpoint Figura 15 Ligao entre servios e aplicaes. Uma aplicao Mule pode ser composta por uma ou mais instancias, possvel conectar componentes de servios em instancias distintas atravs de endpoints. Os endpoints podem ser configurados em vrios elementos de Mule: Na seo inbound de um componente de servio. Neste caso o endpoint um inbound endpoint, dados so lidos dele. Em um router dentro de uma seo outbound de um componente de servio. Neste caso o endpoint um outbound endpoint e dados so escritos nele. Em definies de estratgias de exceo, usadas para gerir excees. Em definies de estratgias catch-all, usadas para gerir possibilidades de roteamento. Os transportes definem endpoints customizados, o que permite a configurao de endereos e valores de propriedades especficas. O endpoint mais simples possui apenas a configurao de um endereo, mas os seguintes atributos so comuns a todos os endpoints: synchronous permite configurar se as mensagens lidas ou escritas em um endpoint deveriam ser lidas ou escritas de maneira sncrona ou assncrona. O valor default falso (assncrono). Componente de servio e p Data source
connector-ref permite especificar que conector usar com um dado transporte. transformer-ref permite especificar uma lista de transformers a serem aplicados sobre as mensagens lidas ou escritas no endpoint.
3.1.2.3 Transformers
Transformers so utilizados para possibilitar a comunicao entre componentes que utilizam formatos de mensagens diferentes. Mule no impe nenhum formato de mensagem, portanto as aplicaes podem utilizar os formatos mais convenientes. Podem-se usar 3 tipos diferentes de transformaes: Transformao de tipo de mensagem. Esta transformao no altera a mensagem propriamente dita, apenas seu tipo. Por exemplo, podemos converter um byte stream para uma string; uma mensagem JMS para um objeto Java. Este o tipo de transformao especfica de transporte. Transformao de mensagem. Esta transformao envolve a converso da mensagem propriamente dita. o tipo de transformao especfica de aplicao. Transformaes envolvendo propriedades de uma mensagem. Mensagens podem conter propriedades, por exemplo, uma mensagem enviada para um servidor SMTP deveria ter as propriedades To, From e CC. Transformers so declarados nas configuraes Mule antes de serem usadas. Eles so referenciados por nome, normalmente dentro de endpoints. Transformers podem ser encadeados, acumulando seus efeitos.
3.1.3 Roteadores
Os roteadores desempenham um papel fundamental no controle da trajetria das mensagens que transitam pelo ESB Mule. Eles ficam na entrada dos endpoint direcionando as mensagens para que alcancem seus destinos corretos. Alguns roteadores so muito simples, outros so mais complexos examinando inclusive algumas caractersticas das mensagens para decidir quais rotas as mensagens devem seguir. Alguns roteadores chegam a um nvel bastante sofisticado podendo quebrar, classificar ou reagrupar mensagens baseando-se em certas condies, normalmente executadas por entidades denominadas de filtros. Os filtros so complementos poderosos dos roteadores, provendo-os com a capacidade de deciso do que fazer com as mensagens que esto em transito. Alguns filtros analisam o contedo das mensagens, baseando-se em determinados valores para fazer o encaminhamento das mensagens. O local do roteador no servio determina sua natureza (inbound, outbound ou async-reply) e os possveis papis nos quais ele poderia atuar (pass-through, aggregator, etc). Os inbound routers so atravessados antes que a mensagem alcance o componente de servio, enquanto os outbound routers so alcanados aps a mensagem ser processada pelo componente. Um roteador async-reply cuida de enviar resposta assncrona para o endpoint que a espera. Quando nenhum roteamento necessrio, o roteador pode ser o mais simples possvel. Neste caso o roteador pode ser um pass-through, dando passagem a qualquer mensagem que decida transitar por ele.
explicaremos em detalhes cada parte da configurao utilizada. A seguir apresentamos uma configurao vlida Mule para termos idia de como ela se apresenta. <mule xmlns="http://www.mulesource.org/schema/mule/core/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:spring="http://www.springframework.org/schema/beans" xmlns:http="http://www.mulesource.org/schema/mule/http/2.0" xmlns:vm="http://www.mulesource.org/schema/mule/vm/2.0" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/springbeans-2.0.xsd http://www.mulesource.org/schema/mule/core/2.0 http://www.mulesource.org/schema/mule/core/2.0/mule.xsd http://www.mulesource.org/schema/mule/vm/2.0 http://www.mulesource.org/schema/mule/vm/2.0/mule-vm.xsd http://www.mulesource.org/schema/mule/http/2.0 http://www.mulesource.org/schema/mule/http/2.0/mulehttp.xsd"> <custom-transformer name="StringToNameString" class="org.mule.samples.hello.StringToNameString"/> <custom-transformer name="HttpRequestToNameString" class="org.mule.samples.hello.HttpRequestToNameString"/> <model name="helloSample"> <service name="Greeter"> <inbound> <inbound-endpoint address="http://localhost:8888" transformer-refs="HttpRequestToNameString" synchronous="true"> </inbound-endpoint> <vm:inbound-endpoint path="greeter" transformer-refs="StringToNameString" synchronous="true"/> </inbound> <component class="org.mule.samples.hello.Greeter"/> <outbound> <filtering-router> <vm:outbound-endpoint path="chitchatter"/> <payload-type-filter expectedType="org.mule.samples.hello.NameString"/> </filtering-router> <filtering-router> <vm:outbound-endpoint path="userErrorHandler"/> <payload-type-filter expectedType="java.lang.Exception"/> </filtering-router> </outbound> </service> </model> </mule> Figura 16 Exemplo de um arquivo de configurao Mule
O bloco marcado com o nmero 1 representa declaraes de namespace e esquema, necessrias em um arquivo de configurao. No segundo bloco temos a declarao de dois transformers que sero utilizados nos dois inbound endpoints. No terceiro bloco iniciamos a declarao do modelo e de um servio. O quarto bloco declara dois inbound endpoints. O primeiro utiliza o protocolo HTTP e um transformer e o segundo o protocolo VM e outro transformer. O quinto bloco declara o componente de servio, implementado por uma classe Java. O ltimo bloco, de nmero 6, declara dois outbound endpoints que utilizam o protocolo VM com roteadores equipados com filtros.
4 Estudo de Caso
Neste estudo de caso apresentaremos uma soluo de integrao utilizando o ESB Mule. A soluo ser construda em vrias etapas, iniciando com uma estrutura simples e agregando novos componentes at construir a soluo completa. Toda a soluo ser comentada e seus componentes e configuraes explicadas. Onde necessrio complementaremos com mais teoria e caractersticas do ESB Mule. Nosso estudo de caso baseado no exemplo apresentado no livro Enterprise Integration Patterns [1] e uma variao da implementao loanbroker distribuda como exemplo na instalao do Mule verso comunidade.
Mdulo de Servio
Banco-B
Agncia de Crdito
Banco-C
Figura 17 Agencia de Emprstimo e seu ambiente. O Web site prov o acesso aos servios oferecidos pela agncia. Neste exemplo utilizaremos uma aplicao Web muito simples cujo objetivo colher as informaes sobre o cliente e o emprstimo desejado e solicitar a cotao do emprstimo. Esta aplicao pode rodar em um container como o Apache Tomcat. O mdulo de servio recebe as informaes enviadas pelo cliente, processa a solicitao e envia a resposta, que deve conter o nome da instituio financeira que ofereceu a melhor taxa e a taxa oferecida. Para a realizao do servio solicitado este mdulo utiliza os servios de uma agncia de crdito e de instituies bancrias. Estes servios so oferecidos por aplicaes externas Agncia de Emprstimo. A Agncia de Crdito uma aplicao externa responsvel por analisar o perfil do cliente para assegurar que o emprstimo solicitado seja vivel. Os bancos so aplicaes externas que fornecem cotaes para a solicitao de emprstimo do cliente, eles representam as instituies financeiras. No captulo seguinte apresentaremos a primeira fase deste estudo de caso.
Cliente (browser)
e n d p
VM
e n d p
jms
jms
transformer
De acordo com a figura anterior, o cliente envia sua solicitao atravs da aplicao Web, que utiliza o protocolo HTTP/REST para envi-la ao componente responsvel pelo servio de cotao de emprstimo. A solicitao chega a este servio por um endpoint que possui um transformer associado. Este transformer transforma a solicitao recebida em um objeto que representa a solicitao. O componente Servio de Cotao de Emprstimo acionado para processar a solicitao. Nesta implementao inicial este componente simplesmente registra a solicitao (log) e repassa-a para seu endpoint de sada. Este endpoint um endpoint de entrada para o componente Servio de Gerao de Respostas e o protocolo utilizado para a comunicao entre os dois componentes o VM. O componente Servio de Gerao de Respostas provisrio neste tutorial, ele ser substitudo nas prximas etapas medida que o exemplo avanar em direo soluo completa. Este componente recebe a solicitao do cliente e gera uma resposta como se fosse uma agncia bancria. A resposta gerada encaminhada atravs de seu endpoint de sada. Este endpoint tem dois transformers associados e envia a resposta do servio a um roteador assncrono utilizando o protocolo JMS. O primeiro transformer associado ao endpoint transforma a cotao gerada pelo componente, que um objeto, em uma string para ser exibida no browser. O segundo transformer transforma a string gerada pelo primeiro em uma mensagem JMS para que ela possa trafegar pelo JMS Bus. O roteador assncrono envia a resposta para a aplicao Web que a apresentar ao cliente. Na verdade no precisaramos neste momento deste roteador, mas quando este exemplo evoluir teremos respostas enviadas por vrios bancos e o cliente dever receber apenas a cotao mais vantajosa, ou seja, com a menor taxa. Caber a este roteador o papel de selecionar a resposta a ser enviada.
package spb; import spb.mensagens.RequisicaoCotacao; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Este eo servico que inicia o processo de obtencao de cotacoes. */ public class ServicoCotacaoEmprestimo implements ServicoAgencia { /** * Logger usado por esta classe */ protected final Log logger = LogFactory.getLog(getClass()); public Object getServico(Object requisicao) throws AgenciaEmprestimoException { if (!(requisicao instanceof RequisicaoCotacao)) { throw new AgenciaEmprestimoException("A requisicao recebida nao " + "e do tipo esperado. Esperado: " + RequisicaoCotacao.class.getName() + " Obtido: " + requisicao); } RequisicaoCotacao req = (RequisicaoCotacao)requisicao; String[] params = new String[] { req.getCliente().getNome(), String.valueOf(req.getCliente().getCpf()), String.valueOf(req.getValor()), String.valueOf(req.getPrazo()) }; logger.info("\n***** " + MensagemLocal.requisicaoRecebida(params)); return req; } } Figura 19 Servio de Cotao de Emprstimo
package spb; import import import import spb.mensagens.RequisicaoCotacao; spb.mensagens.Cotacao; org.apache.commons.logging.Log; org.apache.commons.logging.LogFactory;
import java.lang.Math; /** * Este eum servico provisorio de geracao de respostas a pedidos * de cotacao de emprestimo. Este servico sera substituido pelos * bancos ateo final da implementacao deste exemplo de integracao. */ public class GeradorRespostas { /** * Logger usado por esta classe */ protected final Log logger = LogFactory.getLog(getClass()); public Object geraResposta(RequisicaoCotacao requisicao) throws AgenciaEmprestimoException { Cotacao cotacao = new Cotacao(); // Os bancos receberao os nomes: Banco-A, Banco-B e Banco-C. cotacao.setNomeBanco("Banco-" + (char)(((new Double(Math.random() * 100).intValue())%3)+65)); // Uma taxa egerada randomicamente para simular uma taxa // oferecida pelo banco. cotacao.setTaxa(Math.random() * 10); logger.info("\n***** " + MensagemLocal.taxaRecebida(cotacao)); return cotacao; } }
Todas as vezes que precisamos ler ou alterar propriedades de uma mensagem em um transformer, precisamos ter acesso ao objeto org.mule.api.MuleMessage. A maneira mais adequada de se escrever um transformer para esses casos estendendo o transformer org.mule.transformer.AbstractMessageAwareTransformer, que o transformer que tem a referncia para a mensagem corrente. Usamos esta referencia para termos acesso s propriedades da mensagem corrente, teis para o transformer. Utilizaremos esta classe para construir o transformer de Solicitao de Cotao. Uma solicitao de cotao de emprstimo de um cliente chega aplicao integrada, atravs da aplicao Web, como uma requisio REST. Este transformer retira as informaes enviadas pelo cliente da mensagem REST e cria um objeto RequisicaoCotacao. Este objeto que ser enviad o ao Servio de Cotao de Emprstimo para processamento. O cdigo deste transformer apresentado na figura a seguir. O transformer RequisicaoRestParaRequisicaoCotacao, sobrescreve o mtodo transform da classe AbstractMessageAwareTransformer, l as informaes enviadas pelo cliente (nome, CPF, valor e prazo) e constri um objeto RequisicaoCotacao, que o resultado da transformao. O mtodo getStringProperty de MuleMessage utilizado para obter os valores, dados seus nomes, como esto estabelecidos na mensagem. Este transformer tambm registra a classe de retorno em seu construtor invocando o mtodo setReturnClass de AbstractMessageAwareTransformer, isto permite ao Mule validar a sada antes de passar a mensagem adiante. Neste caso estamos dizendo que o retorno ser um objeto da classe RequisicaoCotacao.
package spb.transformers; import import import import import org.mule.api.MuleMessage; org.mule.api.transformer.TransformerException; spb.mensagens.Cliente; spb.mensagens.RequisicaoCotacao; org.mule.transformer.AbstractMessageAwareTransformer;
/** * Converte parametros da mensagem REST para o objeto * RequisicaoCotacaoCliente. */ public class RequisicaoRestParaRequisicaoCotacao extends AbstractMessageAwareTransformer { public RequisicaoRestParaRequisicaoCotacao() { setReturnClass(RequisicaoCotacao.class); } public Object transform(MuleMessage message, String outputEncoding) throws TransformerException { String nome; String cpf; double valor; int prazo; try { nome = getParam(message, "nomeCliente"); cpf = getParam(message, "cpf"); valor = Double.parseDouble(getParam(message, "valorEmprestimo")); prazo = Integer.parseInt(getParam(message, "prazoEmprestimo")); } catch (Exception e) { throw new TransformerException(this, e); } Cliente c = new Cliente(nome, cpf); RequisicaoCotacao requisicao = new RequisicaoCotacao(c, valor, prazo); return requisicao; } protected String getParam(MuleMessage message, String nomeParam) throws NullPointerException { String valorParam = message.getStringProperty(nomeParam,null); if (valorParam == null) { throw new IllegalArgumentException("Parametro '" + nomeParam + "' nao pode ser omitido."); } return valorParam; } }
um ou mais conectores. Mas muitos conectores so utilizados em sua configurao default, possibilitando assim que o prprio Mule os crie, evitando assim a necessidade de declar-los no arquivo de configurao. Isso acontece normalmente com os transportes VM e HTTP. Uma configurao de um endpoint faz referncia ao conector a ser utilizado, mas, por facilidade, se temos apenas um conector para um determinado transporte, no precisamos mencion-lo na declarao do endpoint; o Mule associar automaticamente o conector ao endpoint. Vamos iniciar a configurao dos endpoints de nossa aplicao pelo endpoint que recebe requisies REST do cliente. O transporte Jetty prov suporte para expor servios sobre HTTP atravs de um servidor Jetty light-weight. O conector Jetty declarado da seguinte forma: <jetty:connector name="httpConnector" useContinuations="false" /> A propriedade useContinuations utilizada para definir se deve ser usado o mecanismo de continuao, permitindo a liberao de conexes em situaes de muita carga. Normalmente esse conector no precisa ser declarado, Mule o instanciar automaticamente. A configurao do endpoint que recebe requisies REST fica da seguinte forma: <endpoint name="RequisicaoClienteREST" connector-ref="httpConnector" address="jetty:rest://localhost:8888/AgenciaEmprestimo" /> O endpoint de nome RequisicaoClienteREST declara a URL do servio Web e usa o conector Jetty httpConnector. Outro transporte utilizado pela aplicao o transporte VM, usado na comunicao entre os servios Cotao de Emprstimo e Gerao de Respostas. O conector para o transporte VM definido da seguinte forma: <vm:connector name="vmConnector" queueEvents="true"/> O nome escolhido para o conector foi vmConnector e a propriedade queueEvents configurada para true. Esta propriedade define se as mensagens sero transferidas de maneira assncrona (true) ou sncrona. Na forma sncrona as mensagens so transferidas na modalidade ponto-a-ponto, caso contrrio as mensagens so colocadas em uma fila. O transporte VM de grande utilidade na comunicao entre componentes internos, como estamos utilizando em nossa aplicao. Ele tambm til em fase de testes para simular o transporte JMS, por exemplo. Outra caracterstica interessante deste transporte o fato de podermos persistir as mensagens que esto na fila quando o Mule desativado. Para configurarmos a fila para ser persistente utilizamos a propriedade queue -profile no conector: <vm:connector name="vmConnector" queueEvents="true"> <queue-profile persistence=true maxOutstandingMessages=1000/> </vm:connector> A propriedade maxOutstandingMessages define o nmero mximo de itens que sero persistidos. Definimos ento o endpoint da seguinte forma: <endpoint name="Requisicoes" connector-ref="vmConnector" address="vm://spb.emprestimo.requisicoes" /> O nome do endpoint Requisies, estamos utilizando o conector vmConnector e a fila a ser utilizada definida como spb.emprestimo.requisicoes, que criada no file system na estrutura da aplicao.
O ltimo transporte utilizado por nossa aplicao nesta fase o transporte JMS. O transporte JMS uma opo atrativa para integrao de aplicaes. Se voc est trabalhando em um ambiente Java e tem controle sobre a rede entre as aplicaes, usar o JMS faz muito sentido ele assncrono, seguro, confivel e normalmente muito rpido. Ele oferece a possibilidade de se trabalhar com diferentes payloads e em ambiente puramente Java podemos mesmo passar objetos serializados. O transporte JMS pode ser utilizado para enviar e receber mensagens sobre filas ou tpicos. Mule no implementa um JMS Server, podemos usar o JMS transporte em conjuno com implementaes como ActiveMQ, OpenMQ ou Tibco EMS. A declarao de um conector obrigatria para o transporte JMS, ao contrrio dos transportes VM e HTTP, por exemplo. A seguir temos uma declarao de conector para JMS: <jms:activemq-connector name="jmsConnector" /> Aqui definimos um conector JMS, de nome jmsConnector, e que utiliza a implementao ActiveMQ. Outras propriedades podem ser configuradas no conector, como, por exemplo, nome de usurio e senha para conectar ao JMS Server. Definimos o endpoint para nossa aplicao da seguinte forma: <endpoint name="Cotacoes" connector-ref="jmsConnector" address="jms://spb.emprestimo.cotacoes" /> O endpoint tem como nome spb.emprestimo.cotacoes. Cotacoes, usa o conector jmsConector e a fila
A figura 25 rene todas as configuraes de endpoints que estabelecemos para nossa aplicao em duas verses, uma completa e uma simplificada, aproveitando a caracterstica de configurao automtica que o Mule nos oferece. As duas so equivalentes.
<jetty:connector name="httpConnector" useContinuations="false" /> <vm:connector name="vmConnector" queueEvents="true"/> <jms:activemq-connector name="jmsConnector" /> <endpoint name="RequisicaoClienteREST" connector-ref="httpConnector" address="jetty:rest://localhost:8888/AgenciaEmprestimo" /> <endpoint name="Requisicoes" connector-ref="vmConnector" address="vm://spb.emprestimo.requisicoes" /> <endpoint name="Cotacoes" connector-ref="jmsConnector" address="jms://spb.emprestimo.cotacoes" />
Verso Simplificada
<jms:activemq-connector name="jmsConnector" /> <endpoint name="RequisicaoClienteREST" address="jetty:rest://localhost:8888/AgenciaEmprestimo" /> <endpoint name="Requisicoes" address="vm://spb.emprestimo.requisicoes" /> <endpoint name="Cotacoes" address="jms://spb.emprestimo.cotacoes" />
O endpoint declarado, RequisicaoClienteREST, o responsvel por receber requisies REST. Este endpoint est associado ao transformer RequisicaoRestParaRequisicaoCotacao, que transforma a mensagem recebida do cliente em um objeto da classe RequisicaoCotacao. O objeto resultante desta transformao encaminhado ao componente implementado pela classe ServicoCotacaoEmprestimo. Aps o processamento implementado pelo componente, o fluxo continua pelo outbound Requisicoes, que possui um roteador do tipo pass-through-router. Este roteador simplesmente passa adiante a mensagem. <outbound> <pass-through-router> <outbound-endpoint ref="Requisicoes" /> </pass-through-router> </outbound> O ltimo elemento deste servio um roteador assncrono de respostas configurado da seguinte forma: <async-reply> <inbound-endpoint ref="Cotacoes" /> <single-async-reply-router /> </async-reply> Um roteador assncrono de respostas aplicvel em situaes sncronas, j que uma mensagem assncrona no gera respostas. No nosso caso o cliente enviou uma solicitao e espera uma resposta e esse roteador o responsvel por encaminhar a mensagem no final do processamento. Mltiplas respostas podem existir porque a mensagem original pode ter disparado vrios servios que respondero em diferentes momentos. Os roteadores de respostas so utilizados para agregar as respostas em uma nica resposta a ser enviada para o cliente. No estado atual de nosso exemplo temos apenas uma resposta gerada para cada solicitao, o que no justifica o uso deste roteador. Mas quando evoluirmos este exemplo, ns teremos respostas de vrios bancos e haver a necessidade de selecionar a melhor resposta, que ser encaminhada ao cliente. Podemos ter trs tipos de roteadores de respostas, single-async-reply-router, collection-async-replyrouter e custom-async-reply-router. Os trs tipos possuem um endpoint associado, que o ponto de entrada das respostas que ele ter que encaminhar. O single-async-reply-router envia a primeira resposta que receber no endpoint e descarta as outras. O collection-async-reply-router retorna todas as mensagens recebidas no endpoint e o custom-async-reply-router repassa todas as mensagens recebidas para uma classe Java responsvel pela agregao das mensagens. Na evoluo de nosso exemplo mudaremos este single-async-reply-router para um custom-async-reply-router. O segundo servio configurado o gerador de respostas. Este servio conta com dois endpoints (inbound e outbound), um componente e dois transformers. A mensagem chega a este servio pelo endpoint Requisicoes, processada pelo componente GeradorRespostas e vai para o endpoint Cotacoes. Neste endpoint de sada temos um roteador do tipo pass-through e dois transformers, que so aplicados antes da mensagem deixar o endpoint. O primeiro transformer aplicado o CotacaoParaString, que gera a string a ser apresentada ao cliente a partir dos dados mantidos pelo objeto Cotacao. Para que a string gerada possa continuar trafegando pelo JMS bus e alcanar o roteador assncrono de respostas, ela passa pelo transformer ObjectToJmsMessage. Este transformer, que um transformer do transporte JMS, encapsula a string em uma mensagem JMS. A mensagem ento encaminhada pelo roteador assncrono para o cliente.
O diretrio tutorial abriga a aplicao mule no diretrio agencia-emprestimo e a aplicao Web no diretrio ClietRest. Vamos visitar primeiro a estrutura definida por agencia-emprestimo. Neste diretrio encontramos o arquivo build.xml, utilizado por ANT para gerar a aplicao, e o diretrio conf que contm o arquivo mule-config.xml, que o arquivo de configurao de nossa aplicao. Os arquivos fonte se encontram abaixo do diretrio src. No diretrio spb encontramos as classes que implementam os servios e dois diretrios, mensagens e transformers:
No diretrio transformers encontramos as duas classes que implementam os transformers utilizados no exemplo:
No diretrio resources/mensagens temos um arquivo para definir strings utilizadas no logging. O diretrio ClientRest contm a aplicao Web utilizada para invocar o servio oferecido pela aplicao que estamos desenvolvido neste tutorial. Esta aplicao roda em um container Web como o Tomcat. O diretrio AgenciaEmprestimo.war contm arquivos que constituem a pgina do site, este tutorial no formato PDF e os diretrios META-INF e WEB-INF:
No diretrio WEB-INF encontramos o arquivo de configurao da aplicao web.xml e um diretrio lib com as bibliotecas necessrias para invocar um servio utilizando REST.
Selecionando a opo Agencia de Emprstimo obteremos a pgina seguinte, que possibilita a invocao do servio implementado pela aplicao Mule em execuo:
Fornea as informaes necessrias e pressione o boto Cotar Emprestimo. O servio ser invocado retornado uma pgina de resposta como:
mantido pela Agncia de Crdito (EJB externo). Como o servio de cotao de emprstimo tambm depende do perfil de crdito do cliente, nos dois tipos de servios a requisio encaminhada Agncia de Crdito. A resposta (perfil de crdito) fornecida pela Agncia de Crdito pode tomar dois caminhos distintos. Se a requisio de servio for uma solicitao de perfil de crdito a resposta ser encaminhada diretamente ao roteador de respostas para ser enviada ao cliente. Se a requisio for de cotao de emprstimo ela ser encaminhada ao Seletor Bancrio. O Seletor Bancrio montar uma lista com os bancos que oferecem emprstimos nas condies solicitadas para clientes com o perfil de crdito obtido. Novamente a mensagem produzida, neste caso pelo Seletor Bancrio, pode tomar dois caminhos distintos. Se nenhum banco oferece emprstimo nas condies solicitadas, a mensagem encaminhada ao servio de Tratamento de Erros para ser encaminhada ao roteador de respostas. No sendo este o caso, a mensagem encaminhada ao Gateway Bancrio. O Gateway Bancrio envia a mensagem a todos os bancos que oferecem emprstimos nas condies solicitadas. Os bancos que recebem a mensagem com a requisio calculam a sua taxa de emprstimo e encaminham a resposta ao roteador de respostas. No item que trata da implementao do fluxo de mensagens veremos como so configurados os endpoints, utilizando roteadores e filtros, para implementar o fluxo que acabamos de descrever. A figura 28 apresenta o projeto para esta fase, apresentando os componentes e seus endpoints. O componente Servios da Agncia substitui o antigo componente Servio de Cotao de Emprstimo, isso porque agora temos dois servios implementados na agncia de emprstimo: Servio perfil de crdito este servio fornece o perfil de crdito do cliente em termos de uma pontuao, que espelha a pontualidade em pagamentos de emprstimos anteriores, e um histrico, que representa o perodo de tempo observado para calcular a pontuao. Servio cotao de emprstimo este o mesmo servio da implementao da fase I, que fornece a menor taxa oferecida por um banco para o emprstimo desejado. O Componente Gateway Agencia de Crdito estabelece a ligao entre a Agncia de Emprstimo e a Agncia de Crdito, que est em execuo em outro ambiente, no caso um servidor JBoss. O componente Agncia de Crdito um componente externo que fornece o perfil de crdito dos clientes mapeado em dois nmeros inteiros, representando uma pontuao e um perodo de avaliao do histrico do cliente. O Seletor Bancrio o componente responsvel por selecionar os bancos que oferecem emprstimos nas condies requeridas pelo cliente. Este componente se baseia no perfil de crdito do cliente, no valor do emprstimo e no prazo de pagamento. A lista de bancos selecionados passada ao componente Gateway Bancrio. O Gateway Bancrio faz a ligao entre a Agncia de Emprstimo e os bancos, que nesta fase ainda so componentes internos. O gateway envia a requisio do cliente para os bancos que fazem parte da lista construda pelo Seletor Bancrio. Os bancos so as instituies financeiras parceiras da Agncia de Emprstimo. Eles fornecem taxas de emprstimo para as solicitaes dos clientes da agncia. Finalmente temos o componente de tratamento de erros. Este componente prepara mensagens de erro para enviar ao cliente em situaes anormais.
Cliente
Requisio de servio
Perfil de crdito servio Cotao de emprstimo Seletor Bancrio sim erro no Tratamento de Erros Gateway Bancrio
Banco A
Banco B
Banco C
Roteador de Respostas
R e n d p r o u t vm
e n d p
Banco A
jms
Banco B
Gateway Bancrio
e n d p
Banco C
/** * Esta classe possui os pontos de entrada para as * requisicoes dos clientes. Dois tipos de servicos * sao oferecidos: Cotacao de Emprestimo e Perfil * de Credito. */ public class ServicosAgencia implements Servicos { /* Logger */ protected final Log logger = LogFactory.getLog(getClass()); /** * Este metodo einvocado quando chega uma * solicitacao de cotacao de emprestimo. */ public Object getCotacao(Object requisicao) throws AgenciaEmprestimoException { // Verifica se o argumento e' uma requisicao RequisicaoServico req = verificaRequisicao(requisicao); // A requisicao uma solicitacao de cotacao de emprestimo req.setTipo(TipoRequisicao.COTACAO); // Verifica se os argumentos da requisicao sao validos. // Os argumentos foram validados pelo transformer colocado // no inbound endpoint deste componente. StatusRequisicao status = req.getStatus();
if (status == StatusRequisicao.OK) { // Log da requisicao. String[] params = new String[] { req.getCliente().getNome(), String.valueOf(req.getCliente().getCpf()), String.valueOf(req.getValor()), String.valueOf(req.getPrazo()) }; logger.info("\n[REQUISICAO COTACAO EMPRESTIMO] " + MensagemLocal.requisicaoRecebida(params)); // A mensagem propagada pelo outbound endpoint contera' // a requisicao do servico Cotacao de Emprestimo. return req; } else { // A requisicao possui argumentos invalidos. A mensagem // propagada pelo outbound endpoint nao contera' a // requisicao e sim uma mensagem de erro. MensagemErro msgErro = new MensagemErro(status); return msgErro; } } /** * Este metodo einvocado quando chega uma * solicitacao de perfil de credito. */ public Object getPerfil(Object requisicao) throws AgenciaEmprestimoException { // Verifica se o argumento e' uma requisicao RequisicaoServico req = verificaRequisicao(requisicao); req.setTipo(TipoRequisicao.PERFIL); StatusRequisicao status = req.getStatus(); // // // if Verifica se os argumentos da requisicao sao validos. Os argumentos foram validados pelo transformer colocado no inbound endpoint deste componente. (status == StatusRequisicao.OK) { // Log da requisicao. String[] params = new String[] { req.getCliente().getNome(), String.valueOf(req.getCliente().getCpf())}; logger.info("\n[REQUISICAO PERFIL CREDITO] " + MensagemLocal.requisicaoHistorico(params)); // A mensagem propagada pelo outbound endpoint contera' // a requisicao do servico Perfil de Credito. return req; } else { // A requisicao possui argumentos invalidos. A mensagem // propagada pelo outbound endpoint nao contera' a // requisicao e sim uma mensagem de erro. MensagemErro msgErro = new MensagemErro(status); return msgErro; } }
/** * Este verifica se o objeto recebido e' uma * requisicao de servico. */ private RequisicaoServico verificaRequisicao(Object requisicao) throws AgenciaEmprestimoException { if (!(requisicao instanceof RequisicaoServico)) { throw new AgenciaEmprestimoException("A requisicao recebida nao " + "e do tipo esperado. Esperado: " ] + RequisicaoServico.class.getName() + " Obtido: " + requisicao); } return (RequisicaoServico)requisicao; } }
package org.credit; import java.text.MessageFormat; import javax.ejb.EJBException; import javax.ejb.SessionBean; import javax.ejb.SessionContext; /** * <code>AgenciaCreditoBean</code> obtem o de perfil de credito do * cliente. */ public class AgenciaCreditoBean implements SessionBean { // Mensagem com o perfil de credito solicitado. private static final String MSG = "<perfil-credito><nome-cliente>{0}</nome-cliente>" + "<cpf-cliente>{1}</cpf-cliente><pontuacao-credito>" + "{2}</pontuacao-credito><historico-cliente>{3}" + "</historico-cliente></perfil-credito>"; public void ejbActivate() throws EJBException { /* nada a fazer */ } public void ejbPassivate() throws EJBException { /* nada a fazer */ } public void ejbRemove() throws EJBException { /* nada a fazer */ } public void ejbCreate() throws EJBException { /* nada a fazer */ } public void setSessionContext(SessionContext sessionContext) throws EJBException { /* SessionContext pode ser ignorado */ } protected int getPontuacaoCredito(String cpf) { // Calcula randomicamente a pontuao do cliente. int pontuacaoCredito = (int)(Math.random() * 600 + 300); return pontuacaoCredito; } protected int getTempoHistoricoCredito(String cpf) { // Calcula randomicamente o tempo de historico. int tempoHistoricoCredito = (int)(Math.random() * 19 + 1); return tempoHistoricoCredito; } /* Usado pela chamada Ejb */ public String getPerfilCredito(String nomeCliente, String cpfCliente) { // Prepara a resposta com o perfil de credito. String msg = MessageFormat.format(MSG, nomeCliente, cpfCliente, getPontuacaoCredito(cpfCliente), getTempoHistoricoCredito(cpfCliente)); return msg; } }
package spb.credit; import spb.mensagens.PerfilCredito; import spb.mensagens.Cliente; /** * <code>ServicoAgenciaCredito</code> o servico prove o * perfil de credito doo cliente. */ public interface ServicoAgenciaCredito { PerfilCredito getPerfilCredito(Cliente cliente); }
package spb; import import import import import import import import spb.mensagens.RequisicaoServico; spb.mensagens.TipoRequisicao; org.apache.commons.logging.Log; org.apache.commons.logging.LogFactory; spb.credit.ServicoAgenciaCredito; spb.mensagens.PerfilCredito; spb.mensagens.RespostaCliente; spb.mensagens.Cliente;
public class GatewayAgenciaCredito { /** Logger usado por esta classe */ protected final Log logger = LogFactory.getLog(getClass()); // Para acesso ao EJB. Injetado pelo container. private ServicoAgenciaCredito servicoAgenciaCredito; public Object processaRequisicao(RequisicaoServico requisicao) throws AgenciaEmprestimoException, Exception { // Erro se nao foi possivel obter o acesso ao EJB if (null == servicoAgenciaCredito) { logger.info("\n" + MensagemLocal.mensagemDebug( "Nao foi possivel obter a Agencia de Credito.")); } // Invoca o EJB para obter o perfil de credito do cliente. else { PerfilCredito perfil = servicoAgenciaCredito. getPerfilCredito(requisicao.getCliente()); requisicao.setPerfilCredito(perfil); // Log para depuracao. logger.info("\n" + MensagemLocal.mensagemDebug( " Perfil de Credito: [" + perfil.getPontuacaoCredito() + ", " + perfil.getHistoricoCredito() + "]")); }
// Se o cliente requisitou seu perfil de credito o servico // termina aqui e a resposta deve ser gerada. if (requisicao.getTipo() == TipoRequisicao.PERFIL) { RespostaCliente resposta = new RespostaCliente(false, null, requisicao.getPerfilCredito()); return resposta; } // Se a requisicao do cliente e de cotacao de emprestimo // a requisicao deve prosseguir, agora contendo o perfil. return requisicao; } public ServicoAgenciaCredito getServicoAgenciaCredito() { return servicoAgenciaCredito; } public void setServicoAgenciaCredito(ServicoAgenciaCredito servicoAgenciaCredito) { this.servicoAgenciaCredito = servicoAgenciaCredito; } }
package spb; import import import import import import import import import import import spb.banco.ConfigBanco; spb.banco.ListaBancosParser; spb.mensagens.PerfilCredito; spb.mensagens.RequisicaoServico; spb.mensagens.StatusRequisicao; spb.MensagemLocal; spb.mensagens.MensagemErro; org.apache.commons.logging.Log; org.apache.commons.logging.LogFactory; java.util.List; java.util.ArrayList;
public class SeletorBancario { /** logger usado por esta classe. */ private static final Log logger = LogFactory.getLog(SeletorBancario.class); /** Configuracoes dos bancos. */ private List<ConfigBanco> confBancos; public SeletorBancario() { try { // Parser para as configuracoes dos bancos. ListaBancosParser handler = new ListaBancosParser("bancos.xml"); // Obtem a configuracao dos bancos a partir // do arquivo bancos.xml. confBancos = handler.getConfigBancos(); // Log para debug. for (ConfigBanco banco: confBancos) { logger.info("\n" + MensagemLocal.mensagemDebug( "Banco: " + banco.getNomeBanco() + " Valor minimo: " + banco.getValorMinimo() + " Valor maximo: " + banco.getValorMaximo() + " Prazo minimo: " + banco.getPrazoMinimo() + " Prazo maximo: " + banco.getPrazoMaximo() + " Pontuacao minima: " + banco.getPontuacaoMinima()+ " Historico minimo: " + banco.getHistoricoMinimo())); } } catch(Exception e) { logger.info("\n" + MensagemLocal.mensagemDebug( "Nao foi possivel obter as configuracoes dos bancos.")); e.printStackTrace(); } }
/** * Este metodo e' invocado ao chegar uma requisicao * de cotacao de credito pelo inbound endpoint. */ public Object setListaBancaria(RequisicaoServico requisicao) { // Obtem a lista de inbound endpoints dos bancos // que oferecem emprestimos nas condicoes solicitadas. List<String> endpoints = getEndpoints( requisicao.getPerfilCredito(), requisicao.getValor(), requisicao.getPrazo()); // // // if } // Se nenhum banco aceita fazer emprestimo nas condicoes // requeridas, encaminhe uma mensagem de erro ao cliente. else { MensagemErro msgErro = new MensagemErro( StatusRequisicao.BANCO_NAO_SEL); return msgErro; } } /** * Este metodo usa as configuracoes dos bancos * para determinar quais os bancos fazem emprestimo * nas condicoes solicitadas. Retorna uma lista * com os endpoints dos bancos selecionados. */ private List<String> getEndpoints(PerfilCredito perfilCredito, double valor, int prazo) { List<String> endpoints = new ArrayList<String>(); for (ConfigBanco banco: confBancos) { if ((valor >= banco.getValorMinimo()) && (valor <= banco.getValorMaximo()) && (prazo >= banco.getPrazoMinimo()) && (prazo <= banco.getPrazoMaximo()) && (perfilCredito.getPontuacaoCredito() >= banco.getPontuacaoMinima()) && (perfilCredito.getHistoricoCredito() >= banco.getHistoricoMinimo()) ) { endpoints.add(banco.getNomeBanco() + "-edp"); logger.info("\n" + MensagemLocal.mensagemDebug( "Banco selecionado: " + banco.getNomeBanco())); } } return endpoints; } } Se algum banco aceita fazer emprestimo nas condicoes requeridas, encaminhe a requisicao com os endpoints dos bancos selecionados. (!endpoints.isEmpty()) { requisicao.setEndpointsBancos(endpoints); return requisicao;
<lista-bancos> <banco> <nome-banco>Banco-A</nome-banco> <valor-minimo>10000.00</valor-minimo> <valor-maximo>1000000.00</valor-maximo> <prazo-minimo>6</prazo-minimo> <prazo-maximo>360</prazo-maximo> <pontuacao-minima>400</pontuacao-minima> <historico-minimo>3</historico-minimo> </banco> <banco> <nome-banco>Banco-B</nome-banco> <valor-minimo>2000.00</valor-minimo> <valor-maximo>100000.00</valor-maximo> <prazo-minimo>3</prazo-minimo> <prazo-maximo>144</prazo-maximo> <pontuacao-minima>350</pontuacao-minima> <historico-minimo>1</historico-minimo> </banco> <banco> <nome-banco>Banco-C</nome-banco> <valor-minimo>10000.00</valor-minimo> <valor-maximo>200000.00</valor-maximo> <prazo-minimo>8</prazo-minimo> <prazo-maximo>260</prazo-maximo> <pontuacao-minima>300</pontuacao-minima> <historico-minimo>2</historico-minimo> </banco> </lista-bancos>
6.1.5 Bancos
Nesta fase de nosso estudo de caso os bancos sero representados por servios internos implementados pela classe Banco. Esta classe (figura 34) implementa ServiceAware com o mtodo setService que automaticamente chamado na iniciao do servio pelo Mule (veja [8]). Isso foi feito apenas para obter o nome do banco que est configurado em config-mule.xml, j que temos apenas uma classe genrica para representar os trs bancos. O mtodo getCotacaoEmprestimo invocado ao chegar uma requisio do cliente ao inbound endpoint do banco. Este mtodo gera a resposta a ser enviada ao cliente com a cotao para o emprstimo. A resposta ser encaminhada ao roteador assncrono configurado para a aplicao. A figura 28, que apresenta a composio de nossa aplicao, apresenta outro componente, o Gateway Bancrio. Este componente no tem implementao, ele serve para receber a requisio do cliente e, baseado na lista de bancos produzida pelo Seletor Bancrio, enviar uma cpia da requisio para cada banco que consta da lista. Veremos em detalhes como funciona o Gateway Bancrio quando descrevermos a implementao do fluxo de mensagens mais adiante.
package spb.banco; import org.mule.api.config.ConfigurationException; import org.mule.api.service.Service; import org.mule.api.service.ServiceAware; import import import import spb.MensagemLocal; spb.mensagens.RequisicaoServico; spb.mensagens.Cotacao; spb.mensagens.RespostaCliente;
import java.io.Serializable; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * <code>Banco</code> representa uma instituicao bancaria. */ public class Banco implements ServiceAware, Serializable, ServicoBancario { /** logger usado por esta classe. */ protected static final Log logger = LogFactory.getLog(Banco.class); /** Nome do banco */ private String nomeBanco; /* Taxa oferecida pelo banco */ private double taxa; public Banco() { // Calcula a taxa para emprestimo randomicamente. this.taxa = Math.random() * 10; } /** * Este metodo e' invocado automaticamente quando * na iniciacao do servico. Ele usa a configuracao * do servico para obter o nome do banco. */ public void setService(Service servico) throws ConfigurationException { // Obtem o nome do banco da configuracao do servico. this.nomeBanco = servico.getName(); // Log para debug. logger.info("\n" + MensagemLocal.mensagemDebug( "Banco setService: " + nomeBanco)); }
/** * Este metodo e' invocado quando uma mensagem * chega ao inbound endpoint do servico. * Constroi a resposta ao cliente contendo a * cotacao do emprestimo. */ public RespostaCliente getCotacaoEmprestimo( RequisicaoServico requisicao) { // O banco cota o emprestimo. Cotacao cotacao = new Cotacao(); cotacao.setNomeBanco(getNomeBanco()); cotacao.setTaxa(taxa); // Log para debug. logger.info(MensagemLocal.taxaRecebida(cotacao)); // Prepara a resposta ao cliente. RespostaCliente resposta = new RespostaCliente(false, null, cotacao); return resposta; } public String getNomeBanco() { return nomeBanco; } public void setNomeBanco(String nomeBanco) { this.nomeBanco = nomeBanco; } public double getTaxa() { return taxa; } public void setTaxa(double taxa) { this.taxa = taxa; } }
package spb.excecoes; import spb.mensagens.RespostaCliente; import spb.MensagemLocal; import spb.mensagens.MensagemErro; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class TrataErrosRequisicao { /** logger usado por esta classe. */ private static final Log logger = LogFactory.getLog(TrataErrosRequisicao.class); /** * Este metodo e' invocado quando chega uma mensagem de erro * no inbound endpoint deste servico de tratamento de erros. * Ele gera uma resposta ao cliente com a mensagem de erro. */ public RespostaCliente trataErrosRequisicao(MensagemErro erro) { // Obtem a mensagem de erro para a resposta. String mensagem = erro.getMensagem(); // Log para debug. logger.info("\n" + MensagemLocal.mensagemDebug(mensagem)); // Constroi a resposta a ser enviada ao cliente. RespostaCliente resp = new RespostaCliente(true, mensagem, null); return resp; } }
Figura 36 Transformer de argumentos para a Agncia de Crdito. Um expression-transform utiliza avaliadores (evaluators) para avaliar o payload da mensagem corrente, podendo ser configurado para avaliar uma ou mais expresses. Dependendo da configurao , o payload da mensagem resultante ser um objeto ou um array de objetos. No nosso caso o transformer usa o avaliador bean para construir uma mensagem onde o payload contm duas
strings, o nome e o CPF do cliente. O avaliador bean avalia expresses como objeto.propriedadeA. No transformer que definimos o avaliador bean chama os mtodos getNome() e getCpf() do objeto que o payload da mensagem, no caso o objeto da classe Cliente. Portanto a classe Cliente precisa ter obrigatoriamente os mtodos getNome e getCpf. Atrs da cena, o avaliador bean utiliza o avaliador jpath. Mule oferece um rico framework de avaliao de expresses permitindo a voc incorporar lgica avanada diretamente no arquivo de configurao de uma aplicao Mule, sem a necessidade de escrever cdigo. Consulte [5] e [7] para ter um bom conhecimento a respeito de expression transformers e avaliadores suportados por Mule. O segundo transformer utilizado pelo GatewayAgenciaCredito o que transforma a mensagem contendo o perfil de crdito retornado pelo EJB Agncia de Crdito em um objeto da classe PerfilCredito. Este transformer um custom-transformer, tipo de transformer que j vimos na fase anterior deste tutorial. A figura 37 apresenta a classe PerfilCreditoXmlParaPerfilCredito que implementa este transformer. A Agncia de Crdito retorna uma string XML contendo o perfil de crdito do cliente e este transformer extrai as informaes do XML e constri um objeto PerfilCredito.
package spb.transformers; import org.mule.api.transformer.TransformerException; import org.mule.transformer.AbstractTransformer; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import spb.mensagens.PerfilCredito; import spb.MensagemLocal; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Esta classe transforma a resposta enviada * pela Agencia de Credito em um objeto * da classe PerfilCredito. */ public class PerfilCreditoXmlParaPerfilCredito extends AbstractTransformer { /** Logger usado por esta classe */ private final Log logger = LogFactory.getLog(getClass()); public PerfilCreditoXmlParaPerfilCredito() { // Recebe o perfil de credito como uma String. registerSourceType(String.class); // Retorna o perfil de credito como // um objeto PerfilCredito. setReturnClass(PerfilCredito.class); }
/** * Metodo de transformacao. O perfil de credito * enviado pela Agencia de Credito esta' no formato XML: * <perfil-credito> * <nome-cliente> ... </nome-cliente> * <cpf-cliente> ... </cpf-cliente> * <pontuacao-credito> ... </pontuacao-credito> * <historico-cliente> ... </historico-cliente> * </perfil-credito> */ public Object doTransform(Object src, String encoding) throws TransformerException { Document doc = null; // Parsing do perfil de credito recebido. try { doc = DocumentHelper.parseText(src.toString()); } catch (DocumentException e) { throw new TransformerException(this, e); } // Obtem os valores do perfil de credito e // atribui ao objeto PerfilCredito. String historico = doc.valueOf( "/perfil-credito/historico-cliente"); String pontuacao = doc.valueOf( "/perfil-credito/pontuacao-credito"); // Log para debug. logger.info("\n" + MensagemLocal.mensagemDebug( "Historico: " + historico + " Pontuacao: " + pontuacao)); PerfilCredito perfil = new PerfilCredito(); perfil.setHistoricoCredito( Integer.valueOf(historico).intValue()); perfil.setPontuacaoCredito( Integer.valueOf(pontuacao).intValue()); return perfil; } }
encaminhar uma mesma mensagem para os vrios endpoints que compem a lista. Este roteador ser estudado mais adiante, quando apresentarmos a implementao do fluxo de mensagens. A mensagem que sai do Seletor Bancrio passa pelo transformer BancoComoRecipientes (figura 38) antes de ser encaminhada ao GatewayBancrio. O transformer obtm da mensagem a requisio (payload da mensagem), obtm da requisio a lista dos endpoints dos bancos selecionados e monta uma lista de recipientes. A lista produzida ento armazenada na mensagem como valor da propriedade recipients. O Gateway Bancrio utilizar esta lista para encaminhar a requisio para os bancos cujos endpoints esto na lista de recipientes.
package spb.transformers; import org.mule.api.MuleMessage; import org.mule.api.transformer.TransformerException; import org.mule.routing.outbound.StaticRecipientList; import org.mule.transformer.AbstractMessageAwareTransformer; import spb.mensagens.RequisicaoServico; import spb.MensagemLocal; import java.util.List; public class BancosComoRecipientes extends AbstractMessageAwareTransformer { public BancosComoRecipientes() { this.setReturnClass(MuleMessage.class); } public Object transform(MuleMessage message, String outputEncoding) throws TransformerException { // Obtem a requisicao, que e' o payload da mensagem. Object req = message.getPayload(); // Obtem da requisicao a lista dos endpoints // dos bancos selecionados. List<String> endpoints = ((RequisicaoServico) req).getEndpointsBancos(); // Monta uma string com os endpoints // separados por virgulas. String recipientes = null; for (String endpoint: endpoints) { if (null != recipientes) { recipientes += ","; } else { recipientes = ""; } recipientes += endpoint; } // Armazena a lista de endpoints dos bancos selecionados // como valor da propriedade RECIPIENTS_PROPERTY. message.setProperty(StaticRecipientList.RECIPIENTS_PROPERTY, recipientes); return message; } }
<custom-transformer name="RequisicaoRestParaRequisicaoServico" class="spb.transformers.RequisicaoRestParaRequisicaoServico" /> <jms:object-to-jmsmessage-transformer name="ObjectToJmsMessage" /> <expression-transformer name="ClienteParaArgsAgenciaCredito"> <return-argument evaluator="bean" expression="nome"/> <return-argument evaluator="bean" expression="cpf"/> </expression-transformer> <custom-transformer name="PerfilCreditoXmlParaPerfilCredito" class="spb.transformers.PerfilCreditoXmlParaPerfilCredito" /> <custom-transformer name="BancosComoRecipientes" class="spb.transformers.BancosComoRecipientes" /> Figura 40 Configurao dos transformers.
jndiProviderUrl (herdada de AbstractJndiConnector) determina o provedor JNDI, no nosso caso localhost. Estamos rodando JBoss e Mule na mesma mquina. A configurao do endpoint que usa o transporte EJB no nosso estudo de caso : <ejb:endpoint name="AgenciaCreditoEdp" host="localhost" port="1099" object="/AgenciaCredito" method="getPerfilCredito" methodArgumentTypes="java.lang.String,java.lang.String" connector-ref="ejbConnector"/> Definimos neste endpoint o host, a porta, o objeto, o mtodo a ser invocado e o conector para o transporte. Um mtodo pode receber um ou mais argumentos. Definimos os tipos dos argumentos atravs da propriedade methodArgumentTypes como uma lista separada por vrgulas. No nosso caso a declarao dos tipos dos argumentos no seria necessria, j que temos apenas um mtodo, o getPerfilCredito. Os outros endpoints utilizados no apresentam novidades para ns, so endpoints para os transportes jetty, vm e jms. O endpoint que utiliza o transporte jetty o mesmo da fase anterior, mas mudamos seu nome de RequisicaoClienteREST para RequisicaoServicoREST, um nome mais adequado. A tabela a seguir apresenta os endpoints utilizados nesta fase com os respectivos transportes e utilizao. A figura 41 apresenta a configurao dos conectores e endpoints. Nome RequisicaoServicoREST GWAgenciaCredito AgenciaCreditoEdp SeletorBancarioEdp GWBancario RespostasServicos Transporte Jetty Jms Ejb Vm Jms Jms Utilizao Recebe requisies do cliente Comunicao entre ServicosAgencia e GatewayAgenciaCredito Comunicao entre GatewayAgenciaCredito e AgenciaCredito Comunicao entre GatewayAgenciaCredito e SeletorBancario Comunicao entre SeletorBancario e GatewayBancario Comunicao entre Bancos (A, B e C) e roteador assncrono de respostas ao cliente. Comunicao entre TrataErrosRequisicao e roteador assncrono de respostas ao cliente. Comunicao entre GatewayBancario e Banco-A Comunicao entre GatewayBancario e Banco-B Comunicao entre GatewayBancario e Banco-C Comunicao entre ServicosAgencia e TrataErrosRequisicao. Comunicao entre SeletorBancario e TrataErrosRequisicao.
<jetty:connector name="httpConnector" useContinuations="false" /> <vm:connector name="vmConnector" queueEvents="true"/> <jms:activemq-connector name="jmsConnector" /> <ejb:connector name="ejbConnector" securityPolicy="security.policy" jndiInitialFactory="org.jnp.interfaces.NamingContextFactory" jndiProviderUrl="localhost"> </ejb:connector> <endpoint name="RequisicaoServicoREST" connector-ref="httpConnector" address="jetty:rest://localhost:8888/AgenciaEmprestimo" /> <endpoint name="GWAgenciaCredito" connector-ref="jmsConnector" address="jms://spb.gw.agenciacredito" /> <ejb:endpoint name="AgenciaCreditoEdp" host="localhost" port="1099" object="/AgenciaCredito" method="getPerfilCredito" methodArgumentTypes="java.lang.String,java.lang.String" connector-ref="ejbConnector"/> <endpoint name="SeletorBancarioEdp" connector-ref="vmConnector" address="vm://spb.seletorbancario.bancos" /> <endpoint name="GWBancario" connector-ref="jmsConnector" address="jms://spb.gw.bancario" /> <endpoint name="RespostasServicos" connector-ref="jmsConnector" address="jms://spb.emprestimo.respostas" /> <endpoint name="Banco-A-edp" connector-ref="jmsConnector" address="jms://spb.banco.banco-a" /> <endpoint name="Banco-B-edp" connector-ref="jmsConnector" address="jms://spb.banco.banco-b" /> <endpoint name="Banco-C-edp" connector-ref="jmsConnector" address="jms://spb.banco.banco-c" /> <endpoint name="TrataErrosRequisicaoEdp" connector-ref="jmsConnector" address="jms://spb.erro.requisicao" />
objeto da classe RequisicaoServico. O objeto resultante desta transformao encaminhado ao componente implementado pela classe ServicosAgencia. Aps o processamento implementado pelo componente, o fluxo continua pelo outbound endpoint que possui dois roteadores com filtros (filtering-router) para definir o destino da mensagem: <outbound> <filtering-router> <outbound-endpoint ref="GWAgenciaCredito" /> <payload-type-filter expectedType="spb.mensagens.RequisicaoServico"/> </filtering-router> <filtering-router> <outbound-endpoint ref="TrataErrosRequisicaoEdp" /> <payload-type-filter expectedType="spb.mensagens.MensagemErro"/> </filtering-router> </outbound> O primeiro roteador encaminha para o endpoint GWAgenciaCredito mensagens cujo payload contm um objeto da classe RequisicaoServico e o segundo encaminha para o endpoint TrataErrosRequisicaoEdp mensagens cujo payload contm um objeto da classe MensagemErro. Com estes filtros podemos separar o fluxo de sada deste servio em um fluxo de mensagens vlidas e um fluxo de mensagens que reportam erros encontrados nas requisies de servios. Os filtros so muito utilizados como elementos de uma aplicao Mule. Filtragem a habilidade de escolher quais mensagens encaminhar ou aceitar e pode ser aplicada tanto em inbound quanto em outbound endpoints. Um filtro deve retornar um valor booleano, que indica se a mensagem deve ser aceita ou no. O roteador no precisa saber de detalhes especficos do filtro, ele opera de acordo com o valor booleano que o filtro retorna. O filtro precisa verificar uma expresso especfica contra a mensagem corrente. Esta expresso pode operar sobre uma mensagem Mule, isto , ela pode olhar para qualquer valor no payload ou propriedades de uma mensagem Mule. Existem vrios filtros padronizados em Mule, mas voc pode construir um filtro que seja adequado sua aplicao. Correntemente Mule possui os seguintes filtros padronizados (consulte [7] para detalhes): Payload Type Filter. Verifica a classe do objeto contido no payload da mensagem. Este o filtro que estamos utilizando no outbound endpoint de ServicosAgencia. Expression Filter. Utiliza avaliadores para avaliar expresses sobre o payload da mensagem. Vrios avaliadores podem ser utilizados, como, por exemplo, xpath, jxpath e bean. Para este filtro definimos o avaliador e a expresso. RegEx Filter. Aplica uma expresso regular sobre o payload da mensagem. O filtro aplica toString() ao payload para aplicar a expresso regular. Wildcard Filter. Aplica um padro Wildcard ao payload da mensagem. Este filtro tambm aplica toString() ao payload. Exception Type Filter. Um filtro que verifica se o payload um tipo de exceo esperada. Message Property Filter. Este filtro permite adicionar lgica ao roteador com base em uma ou mais propriedades da mensagem. Logic Filters. Existem 3 Logic Filters que podem ser utilizados com outros filtros: AND, OR e NOT. O ltimo elemento deste servio um roteador assncrono de respostas configurado da seguinte forma: <async-reply timeout="10000"> <inbound-endpoint ref="RespostasServicos" /> <custom-async-reply-router class="spb.routers.AgregadorRespostas" />
</async-reply> Na fase I utilizamos um roteador assncrono to tipo single-async-reply-router, nesta fase ns mudamos o tipo para custom- async-reply-router e adotamos um timeout para o recebimento das mensagens com respostas a serem enviadas ao cliente. Neste tipo de roteador assncrono as mensagens so encaminhadas a uma classe que implementa a seleo/agregao das respostas para enviar apenas uma resposta ao cliente. Neste caso especfico, a classe implementa a seleo da melhor cotao oferecida pelos bancos, no caso do servio de cotao de emprstimo. Para o servio de perfil de crdito s temos uma resposta, dado que s temos uma agncia de crdito. As figuras de 42 e 43 apresentam a implementao da seleo de respostas. Uma classe que implementa um agregador de respostas deve estender uma das duas classes Mule (veja a referncia [8]): org.mule.routing.response.AbstractResponseRouter ou org.mule.routing.response.AbstractResponseAggregator. A classe AgregadorRespostas (figura 42) estende a classe Mule ResponseCorrelationAgregator (que estende AbstractResponseRouter) e usa a classe LogicaAgregacaoRespostas para realizar a seleo da melhor resposta a ser enviada ao cliente. As mensagens que chegam ao inbound endpoint RespostasServicos so encaminhadas para o AgregadorRespostas. Quando mensagens transitam em Mule, de fato o que circula so eventos. Um evento carrega no somente o contedo de uma mensagem, mas tambm o contexto no qual a mensagem processada. No nosso caso, a classe AgregadorRespostas implementa o mtodo getCorrelatorCallback, que, quando invocado por Mule retorna uma classe para tratamento dos eventos (mensagens) que chegam ao roteador assncrono. Esta classe possui o mtodo aggregateEvents que Mule invoca para processar as respostas. Este mtodo usa a classe LogicaAgregacaoRespostas para processar os eventos (mensagens).
package spb.routers; import import import import import import org.mule.api.MuleMessage; org.mule.routing.AggregationException; org.mule.routing.CollectionCorrelatorCallback; org.mule.routing.EventCorrelatorCallback; org.mule.routing.inbound.EventGroup; org.mule.routing.response.ResponseCorrelationAggregator;
public class AgregadorRespostas extends ResponseCorrelationAggregator { @Override protected EventCorrelatorCallback getCorrelatorCallback() { return new CollectionCorrelatorCallback() { public MuleMessage aggregateEvents(EventGroup events) throws AggregationException { try { return LogicaAgregacaoRespostas.aggregateEvents(events); } catch (Exception e) { throw new AggregationException(events, null, e); } } }; } }
A classe LogicaAgregacaoRespostas (figura 43) recebe os eventos (mensagens) atravs do mtodo aggregateEvents e prepara a resposta a ser enviada ao cliente. Se as mensagens correspondem a respostas de cotao enviadas pelos bancos, a resposta contendo a menor taxa selecionada para ser enviada ao cliente. Se for uma resposta a um pedido de perfil de crdito, s existe uma resposta, no h seleo a ser feita, apenas preparar a resposta a ser enviada ao cliente. Se algum erro foi detectado durante o processamento do servio, ento uma resposta contendo uma mensagem de erro preparada.
package spb.routers; import import import import import import import import import org.mule.DefaultMuleMessage; org.mule.api.MuleEvent; org.mule.api.MuleMessage; spb.MensagemLocal; spb.mensagens.Cotacao; spb.mensagens.RespostaCliente; spb.mensagens.TipoRequisicao; spb.mensagens.PerfilCredito; org.mule.routing.inbound.EventGroup;
import java.util.Iterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Esta classe implementa a logica de selecao de resposta * a ser enviada ao cliente, no caso de respostas do * servico de cotacao de emprestimo. A resposta enviada ao * cliente e' a que contem a menor taxa de emprestimo. */ public class LogicaAgregacaoRespostas { protected static final Log logger = LogFactory.getLog(LogicaAgregacaoRespostas.class); public static MuleMessage aggregateEvents(EventGroup events) throws Exception { RespostaCliente resposta; Cotacao menorTaxa = null; Cotacao cotacao = null; MuleEvent event = null; String strCot = "<ul>"; int pontuacao = -1; int historico = -1;
// Processa os eventos (mensagens). for (Iterator iterator = events.iterator(); iterator.hasNext();) { event = (MuleEvent)iterator.next(); Object o = event.transformMessage(); if(o instanceof RespostaCliente) { resposta = (RespostaCliente)o; } else { throw new IllegalArgumentException("O objeto recebido " + "nao e do tipo esperado. Esperado: " + Cotacao.class.getName() + " Recebido: " + o); } // Se for uma mensagem de erro, prepara a mensagem ao // cliente if (resposta.isErro()) { strCot += "<li>" + resposta.getMensagemErro() + "</li>"; } // Se for uma cotacao, estabeleca a menor taxa. else if (resposta.getObjResposta() instanceof Cotacao) { cotacao = (Cotacao)resposta.getObjResposta(); logger.info(MensagemLocal.processandoCotacao(cotacao)); if (menorTaxa == null) { menorTaxa = cotacao; } else { if (cotacao.getTaxa() < menorTaxa.getTaxa()) { menorTaxa = cotacao; } } } // Se for resposta do servico Perfil de Credito, prepare // a resposta ao cliente. else if (resposta.getObjResposta() instanceof PerfilCredito){ PerfilCredito perfil = (PerfilCredito)resposta.getObjResposta(); pontuacao = perfil.getPontuacaoCredito(); historico = perfil.getHistoricoCredito(); } } // Prepare a resposta com a menor taxa, se for o caso. if (null != menorTaxa) { logger.info(MensagemLocal.menorCotacao(menorTaxa)); strCot += "A melhor cotacao recebida: <br/><li>" + "Instituicao financeira: " + menorTaxa.getNomeBanco() + "</li><li>Taxa: " + menorTaxa.getTaxa() + "</li></lu>"; } // Prepare a resposta com o perfil de credito, se for o caso. else if (-1 != pontuacao) { strCot += "Perfil do Cliente: <br/><li>Historico: " + historico + "</li><li>Pontuacao: " + pontuacao + "</li></lu>"; }
// Prepare a resposta com a mensagem de erro, se for o caso. else { strCot += "</lu>"; } // Construa uma mensagem Mule e retorna. return new DefaultMuleMessage(strCot, event.getMessage()); } }
Figura 43 Classe LogicaAgregacaoResposta (continuao) A figura 44 a seguir apresenta a configurao completa do servio ServicosAgencia. <service name="ServicosAgencia"> <inbound> <inbound-endpoint ref="RequisicaoServicoREST" transformer-refs="RequisicaoRestParaRequisicaoServico" /> </inbound> <component class="spb.ServicosAgencia" /> <outbound> <filtering-router> <outbound-endpoint ref="GWAgenciaCredito" /> <payload-type-filter expectedType="spb.mensagens.RequisicaoServico"/> </filtering-router> <filtering-router> <outbound-endpoint ref="TrataErrosRequisicaoEdp" /> <payload-type-filter expectedType="spb.mensagens.MensagemErro"/> </filtering-router> </outbound> <async-reply timeout="10000"> <inbound-endpoint ref="RespostasServicos" /> <custom-async-reply-router class="spb.routers.AgregadorRespostas" /> </async-reply> </service> Figura 44 Configurao do servio ServicosAgencia.
O componente implementado pela classe GatewayAgenciaCrdito configurado com uma interface para comunicao com a Agncia de Crdito, que um EJB externo. Esta interface declarada com o elemento binding. <component class="spb.GatewayAgenciaCredito"> <binding interface="spb.credit.ServicoAgenciaCredito" method="getPerfilCredito"> <outbound-endpoint synchronous="true" transformer-refs="ClienteParaArgsAgenciaCredito" responseTransformer-refs="PerfilCreditoXmlParaPerfilCredito" ref="AgenciaCreditoEdp" /> </binding> </component> O elemento binding define a classe Java que representa a interface para acesso ao EJB (ServicoAgenciaCredito), o mtodo a ser invocado (getPerfilCredito) e um outbound endpoint sncrono (AgenciaCreditoEdp), para onde a mensagem ser enviada para chegar ao EJB. No outbound endpoint foram utilizados dois transformers, um para transformar os argumentos a serem passados ao mtodo do EJB (ClienteParaArgsAgenciaCredito) e outro para converter a resposta recebida do EJB (PerfilCreditoXmlParaPerfilCredito). Aps o processamento implementado pelo componente, o fluxo continua pelo outbound endpoint que possui dois roteadores com filtros (filtering-router) para definir o destino da mensagem: <outbound> <filtering-router> <outbound-endpoint ref="SeletorBancarioEdp" /> <payload-type-filter expectedType="spb.mensagens.RequisicaoServico"/> </filtering-router> <filtering-router> <outbound-endpoint ref="RespostasServicos" /> <payload-type-filter expectedType="spb.mensagens.RespostaCliente"/> </filtering-router> </outbound> O primeiro roteador encaminha para o endpoint SeletorBancarioEdp mensagens cujo payload contm um objeto da classe RequisicaoServico e o segundo encaminha para o endpoint RespostasServicos mensagens cujo payload contm um objeto da classe RespostaCliente. Com estes filtros podemos separar o fluxo de sada deste servio em um fluxo de mensagens com requisies de cotaes de emprstimos, a serem encaminhadas ao Seletor Bancrio, e um fluxo de mensagens que so respostas a pedidos de perfil de crdito do cliente, cujo processamento termina neste ponto.
<service name="GatewayAgenciaCredito"> <inbound> <inbound-endpoint ref="GWAgenciaCredito" /> </inbound> <component class="spb.GatewayAgenciaCredito"> <binding interface="spb.credit.ServicoAgenciaCredito" method="getPerfilCredito"> <outbound-endpoint synchronous="true" transformer-refs="ClienteParaArgsAgenciaCredito" responseTransformer-refs="PerfilCreditoXmlParaPerfilCredito" ref="AgenciaCreditoEdp" /> </binding> </component> <outbound> <filtering-router> <outbound-endpoint ref="SeletorBancarioEdp" /> <payload-type-filter expectedType="spb.mensagens.RequisicaoServico"/> </filtering-router> <filtering-router> <outbound-endpoint ref="RespostasServicos" /> <payload-type-filter expectedType="spb.mensagens.RespostaCliente"/> </filtering-router> </outbound> </service> Figura 45 Configurao do servio GatewayAgenciaCredito
<outbound> <filtering-router> <outbound-endpoint ref="GWBancario" transformer-refs="BancosComoRecipientes ObjectToJmsMessage" /> <payload-type-filter expectedType="spb.mensagens.RequisicaoServico"/> </filtering-router> <filtering-router> <outbound-endpoint ref="TrataErrosRequisicaoEdp" /> <payload-type-filter expectedType="spb.mensagens.MensagemErro"/> </filtering-router> </outbound>
<service name="SeletorBancario"> <inbound> <inbound-endpoint ref="SeletorBancarioEdp" /> </inbound> <component class="spb.SeletorBancario" /> <outbound> <filtering-router> <outbound-endpoint ref="GWBancario" transformer-refs="BancosComoRecipientes ObjectToJmsMessage" /> <payload-type-filter expectedType="spb.mensagens.RequisicaoServico"/> </filtering-router> <filtering-router> <outbound-endpoint ref="TrataErrosRequisicaoEdp" /> <payload-type-filter expectedType="spb.mensagens.MensagemErro"/> </filtering-router> </outbound> </service> Figura 46 Configurao do servio SeletorBancario
outbound endpoint, a resposta gerada pelos bancos sero encaminhadas diretamente para o endpoint RespostasServicos. <service name="GatewayBancario"> <inbound> <inbound-endpoint ref="GWBancario" /> </inbound> <outbound> <static-recipient-list-router> <reply-to address="RespostasServicos" /> </static-recipient-list-router> </outbound> </service> Figura 47 Configurao do servio GatewayBancario
Para invocar os servios oferecidos pela Agncia de Emprstimo continuamos utilizando a aplicao Web. Para colocar esta aplicao no ar basta copiar o diretrio AgenciaEmprestimo.war para o diretrio Server\default\deploy do JBoss. Com o JBoss no ar usamos um browser para ter acesso ao servio atravs da URL http://localhost:8080/AgenciaEmprestimo. A mesma pgina web inicial da fase I ser apresentada. Ao selecionarmos Agencia de Emprestimo obteremos agora uma pgina diferente da apresentada na fase I, j que agora temos dois servios
oferecidos pela agncia, cotao de emprstimo e avaliao de perfil de crdito. A seguinte pgina ser apresentada:
O servio de cotao de emprstimo funciona da mesma forma que funcionava na fase I. O servio de avaliao apresentar como resultado um histrico e uma pontuao, como apresentado na figura seguinte.
Na prxima etapa deste tutorial apresentaremos novos elementos e transportes suportados por Mule.
Mdulo de Servio
Banco-B
Agncia de Crdito
Banco-C
Figura 51 Agncia de emprstimo e seu ambiente. A figura 52 apresenta o fluxo de mensagens da aplicao. Com a adio dos novos servios o fluxo de mensagens sofreu pequenas modificaes. Foram includos no fluxo dois novos componentes, o gerador de relatrio e o emissor automtico de relatrios. Mensagens de requisio de cotao de emprstimo que saem da agncia de crdito fluem tanto para o Seletor Bancrio quanto para o Gerador de Relatrio. Um fluxo paralelo estabelecido por eventos disparados por um timer para acionar a emisso automtica de relatrios. O resto do fluxo de mensagens permanece inalterado. A figura 53 mostra o projeto para essa fase do estudo de caso, apresentando os componentes e seus endpoints. Temos dois novos componentes externos, Banco A TCP e Banco B WS. Os componentes internos Banco A e Banco B definidos na fase anterior passam a funcionar como proxy para os correspondentes componentes externos. Roteadores do tipo chaining-router foram adicionados aos bancos A e B, como veremos no item de configurao dos servios. Dois novos componentes foram adicionados aplicao para implementar a funcionalidade de gerao e emisso de relatrios. O Gerador de Relatrio armazena cada requisio de cotao de emprstimo em um banco de dados e o Emissor Automtico de Relatrios acessa o banco para emitir o relatrio. A emisso do relatrio consiste em gravar o relatrio em arquivo e envi-lo por email. O emissor automtico temporizado para emitir relatrios em intervalos regulares de tempo.
Perfil de crdito servio Cotao de emprstimo Gerador de Relatrio sim erro no Tratamento de Erros Gateway Bancrio Seletor Bancrio
Banco A
Banco B
Banco C
Roteador de Respostas
R e n d p r o u t vm
e n d p
t r a n
Servios da Agncia
e n d p
r o u t
jms
e n d p
r. assncrono R jms R R r o u t tcp R r o u t cfx R e n d p Banco C e n d p ep Banco B ep e n d p r o u t jms e n d p Gateway Bancrio e n d p ep Banco A ep e n d p Tratamento de Erros e n d p e n d p jms r o u t e n d p Seletor Bancrio
r o u t
e n d p
Banco A TCP
jms
Banco B WS
e n d p
Gerador de Relatrio
e n d p
Email relatrio
7.1.1 Bancos
Duas novas aplicaes foram desenvolvidas para substituir os bancos A e B que na fase anterior eram implementados como componentes internos aplicao Mule. Estas novas aplicaes so integradas aplicao Mule atravs dos transportes TCP e Web Service. Uma das aplicaes o Banco_A_TCP, integrada via transporte TCP. Esta aplicao composta por duas classes Java: BancoServer e Banco. O BancoServer abre um socket para ouvir requisies enviadas pela aplicao Mule. Ao receber uma requisio o BancoServer a repassa ao Banco, atravs do mtodo getCotacaoEmprestimo, que calcula a taxa de emprstimo. O BancoServer ento envia a resposta aplicao Mule via socket. Os cdigos relativos s duas classes so apresentados nas figuras 54 e 55. O cdigo do banco muito parecido com o cdigo da classe Banco utilizada na fase anterior e o cdigo do BancoServer um cdigo simples de um servidor socket que abre uma janela onde as requisies recebidas so apresentadas. A outra aplicao o Banco_B_WS, integrada com a aplicao Mule via transporte CXF. Esta aplicao possui apenas duas classes, Banco (interface) e BancoImpl (implementao). Novamente, a implementao do banco similar apresentada na fase anterior. A figura 56 apresenta estas classes. Este banco, implementado como Web Service, executado no JBoss. O componente Banco-C no foi modificado nesta verso do estudo de caso e continua sendo um componente interno da aplicao Mule.
import java.text.MessageFormat; /** * <code>Banco_A_TCP</code> representa uma instituicao bancaria. */ public class Banco_A_TCP { /** Nome do banco */ private static final String nomeBanco = "Banco-A"; /* Taxa oferecida pelo banco */ private double taxa; /* Formulario para resposta */ private static final String XML_FORM = "<cotacao><nome-banco>{0}</nome-banco>" + "<taxa>{1}</taxa></cotacao>"; public Banco_A_TCP() { // Calcula a taxa para emprestimo randomicamente. this.taxa = Math.random() * 10; } /** * Este metodo e' invocado quando uma mensagem * chega ao inbound endpoint do servico Banco-A. * Constroi a resposta ao cliente contendo a * cotacao do emprestimo. */ public String getCotacaoEmprestimo( String requisicao) { // Para debug. System.out.println("<<<DEBUG>>>: Requisicao ao Banco-A: " + requisicao); String reqXml = MessageFormat.format(XML_FORM, getNomeBanco(), Double.toString(getTaxa())); return reqXml; } public String getNomeBanco() { return nomeBanco; } public double getTaxa() { return taxa; } } Figura 54 Classe Banco_A_TCP.
import java.awt.Color; import java.awt.BorderLayout; import java.awt.event.*; import javax.swing.*; import java.io.*; import java.net.*; class ClientWorker implements Runnable { private Banco_A_TCP banco; private Socket client; private JTextArea textArea; ClientWorker(Socket client, JTextArea textArea) { this.client = client; this.textArea = textArea; } public void run(){ String line; StringBuffer lineBuf = new StringBuffer(); boolean fim = false; InputStream is = null; PrintWriter out = null; try { is = client.getInputStream(); out = new PrintWriter(client.getOutputStream(), true); } catch (IOException e) { System.out.println("ERRO: Input/Output Stream."); System.exit(-1); } while (!fim) { try{ int r = is.read(); if (r != -1) { lineBuf.append((char)r); if (is.available() <= 0) { fim = true; } } else { break; } } Figura 55 Classe BancoServer (continua).
catch (IOException e) { System.out.println("ERRO DE LEITURA."); textArea.append("ERRO DE LEITURA. Cliente desconectou-se.\r\n\r\n"); break; } } if (lineBuf.length() == 0) { System.out.println("***** NENHUMA ENTRADA RECEBIDA *****"); } else { line = lineBuf.toString(); System.out.println("RECEBIDO: " + line); banco = new Banco_A_TCP(); String resp = banco.getCotacaoEmprestimo(line); out.println(resp); textArea.append(line + "\r\n " + resp + "\r\n"); } } } class BancoServer extends JFrame { JLabel label = new JLabel("Requisicoes recebidas:"); JPanel panel; JTextArea textArea = new JTextArea(); ServerSocket server = null; BancoServer() { panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.setBackground(Color.white); getContentPane().add(panel); panel.add("North", label); panel.add("Center", textArea); } public void listenSocket() { try { server = new ServerSocket(4599); } catch (IOException e) { System.out.println("Erro na criacao do socket porta 4599"); System.exit(-1); } Figura 55 Classe BancoServer (continuao).
while(true){ ClientWorker w; try { w = new ClientWorker(server.accept(), textArea); Thread t = new Thread(w); t.start(); } catch (IOException e) { System.out.println("Falha em Accept."); System.exit(-1); } } } protected void finalize(){ try { server.close(); } catch (IOException e) { System.out.println("Could not close socket"); System.exit(-1); } } public static void main(String[] args){ BancoServer frame = new BancoServer(); frame.setTitle("Server Banco-A"); WindowListener l = new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }; frame.addWindowListener(l); frame.pack(); frame.setVisible(true); frame.listenSocket(); } } Figura 55 Classe BancoServer (continuao).
package org; import javax.jws.WebService; /** * <code>Banco-B</code> representa uma instituicao bancaria. */ @WebService public interface Banco { /** * Este metodo e' invocado quando uma mensagem chega ao inbound endpoint do servico * Banco-B. Constroi a resposta ao cliente contendo a cotacao do emprestimo. */ String getCotacaoEmprestimo(String requisicao); }
package org; import javax.jws.WebService; import java.text.MessageFormat; @WebService(endpointInterface = "org.Banco", serviceName = "Banco") /** * <code>Banco-B</code> representa uma instituicao bancaria. */ public class BancoImpl implements Banco { /** Nome do banco */ private static final String nomeBanco = "Banco-B"; /* Taxa oferecida pelo banco */ private double taxa; private static final String XML_FORM = "<cotacao><nome-banco>{0}</nome-banco>" + "<taxa>{1}</taxa></cotacao>"; public BancoImpl() { // Calcula a taxa para emprestimo randomicamente. this.taxa = Math.random() * 10; }
/** * Este metodo e' invocado quando uma mensagem chega ao inbound endpoint do servico * Banco-B. Constroi a resposta ao cliente contendo a cotacao do emprestimo. */ public String getCotacaoEmprestimo(String requisicao) { // Para debug. System.out.println("<<<DEBUG>>>: Requisicao ao Banco-B: " + requisicao); String reqXml = MessageFormat.format(XML_FORM, getNomeBanco(), Double.toString(getTaxa())); return reqXml; } private String getNomeBanco() { return nomeBanco; } private double getTaxa() { return taxa; } } Figura 56 Classes do Banco-B-WS (continuao).
package spb; import spb.mensagens.RequisicaoServico; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import spb.mensagens.PerfilCredito; import spb.mensagens.Cliente; import java.util.*; import java.sql.SQLException; public class GeradorRelatorio implements org.mule.api.lifecycle.Initialisable { /** Logger usado por esta classe */ protected final Log logger = LogFactory.getLog(getClass()); /** Indicador de banco iniciado. */ private boolean initialized = false; /** * Inicia o banco de dados. */ public void initialise() { if (!initialized) { try { MemoryHSQLDB.startup(); // Log para debug logger.info("\n" + MensagemLocal.mensagemDebug( "##### BANCO DE DADOS HSQLDB INICIADO #####")); initialized = true; } catch (SQLException e) { // Log para debug logger.info("\n" + MensagemLocal.mensagemDebug( "##### ERRO INICIANDO BANCO DE DADOS HSQLDB #####")); } } }
/** * Este metodo e' invocado quando uma requisicao de cotacao de * emprestimo chega ao inbound endpoint do servico. um Map * contendo os dados correspondentes a requisicao e' construido. * Este map e' utilizado para inserir a requisicao no banco de dados. */ Figura 57 Gerador de Relatrio (continua).
public Map preparaLinhaRelatorio(RequisicaoServico requisicao) { Map linhaRel = new HashMap(); String cliente = requisicao.getCliente().getNome(); String cpf = requisicao.getCliente().getCpf(); String pontuacao = Integer.toString(requisicao.getPerfilCredito().getPontuacaoCredito()); String historico = Integer.toString(requisicao.getPerfilCredito().getHistoricoCredito()); String valor = Double.toString(requisicao.getValor()); String prazo = Integer.toString(requisicao.getPrazo()); // Log para debug logger.info("\n" + MensagemLocal.mensagemDebug("Dados para relatorio: " + cliente + ", " + cpf + ", " + pontuacao + ", " + historico + ", " + valor + ", " + prazo)); linhaRel.put("CLIENTE", cliente); linhaRel.put("CPF", cpf); linhaRel.put("PONTUACAO", pontuacao); linhaRel.put("HISTORICO", historico); linhaRel.put("VALOR", valor); linhaRel.put("PRAZO", prazo); return linhaRel; } } Figura 57 Gerador de Relatrio (continuao).
/* Esta classe utilizada para fazer o setup do banco HSQLDB. */ package spb; import java.sql.Connection; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import org.hsqldb.jdbc.jdbcDataSource; import java.sql.DriverManager; public class MemoryHSQLDB { /** * Metodo para criacao da tabela Relatorio. */ public static void startup() throws SQLException { Connection connRel = getConnectionRelatorio(); System.out.println("*** CRIANDO TABELA RELATORIO ***"); update(connRel, "CREATE TABLE RELATORIO(ID INTEGER GENERATED BY" + " DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY," + "CLIENTE VARCHAR(32),CPF VARCHAR(32),PONTUACAO VARCHAR(32)," + "HISTORICO VARCHAR(32),VALOR VARCHAR(32),PRAZO VARCHAR(32))"); } /** * Conexao com o banco. */ private static Connection getConnectionRelatorio() { try { Class.forName("org.hsqldb.jdbcDriver").newInstance(); return DriverManager.getConnection("jdbc:hsqldb:mem:relatorio", "sa", ""); } catch (SQLException se) { System.out.println("Excecao ao obter conexao com HSQLDB[1]"); se.printStackTrace(); } catch (Exception e) { System.out.println("Excecao ao obter conexao com HSQLDB[2]"); e.printStackTrace(); } return null; }
/** * Comando Update. */ private static synchronized void update(Connection conn, String expression) throws SQLException { Statement st = null; st = conn.createStatement(); int i = st.executeUpdate(expression); if (i == -1) { System.out.println("ERRO DB : " + expression); } st.close(); } } Figura 58 Setup do Banco (continuao).
package spb; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.*; public class EmissorRelatorio implements org.mule.api.lifecycle.Initialisable { /** Logger usado por esta classe */ protected final Log logger = LogFactory.getLog(getClass()); private int idUltimoRegistro; public void initialise() { idUltimoRegistro = -1; } public String preparaLinha(Map linhaRel) { String linha = null; int id = ((Integer)linhaRel.get("ID")).intValue(); if (id > idUltimoRegistro) { StringBuffer linhaBuf = new StringBuffer(); linhaBuf.append("<requisicao>" + '\n'); linhaBuf.append(" <nome>" + linhaRel.get("CLIENTE") + "</nome>" + '\n'); linhaBuf.append(" <cpf>" + linhaRel.get("CPF") + "</cpf>" + '\n'); linhaBuf.append(" <pontuacao>" + linhaRel.get("PONTUACAO") + "</pontuacao>" + '\n'); linhaBuf.append(" <historico>" + linhaRel.get("HISTORICO") + "</historico>" + '\n'); linhaBuf.append(" <valor>" + linhaRel.get("VALOR") + "</valor>" + '\n'); linhaBuf.append(" <prazo>" + linhaRel.get("PRAZO") + "</prazo>" + '\n'); linhaBuf.append("</requisicao>" + '\n'); logger.info("\n" + MensagemLocal.mensagemDebug(linhaBuf.toString())); linha = linhaBuf.toString(); idUltimoRegistro = id; } return linha; } } Figura 59 O Emissor de Relatrios.
<taxa> taxa oferecida pelo banco </taxa> </cotacao> Este no o formato esperado pelo roteador assncrono de respostas, que espera um objeto da classe RespostaCliente. Para fazer esta converso foi desenvolvido o custom transform BancoXMLparaRespostaCliente apresentado na figura 60. Este transformer no apresenta novidades em relao aos que j vimos anteriormente. Outros transformers pr-definidos sero utilizaos pelos bancos, como veremos na parte de configuraes. package spb.transformers; import org.mule.api.transformer.TransformerException; import org.mule.transformer.AbstractTransformer; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import spb.mensagens.RespostaCliente; import spb.mensagens.Cotacao; import spb.MensagemLocal; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Esta classe transforma a resposta enviada pelo banco em um * objeto da classe RespostaCliente. */ public class BancoXMLparaRespostaCliente extends AbstractTransformer { /** Logger usado por esta classe */ private final Log logger = LogFactory.getLog(getClass()); public BancoXMLparaRespostaCliente() { // Recebe a cotacao de credito como uma String XML. registerSourceType(String.class); // Retorna resposta ao cliente setReturnClass(RespostaCliente.class); } Figura 60 Transformer para os bancos A e B (continua).
/** * Metodo de transformacao. A cotacao enviada pelo * banco esta' no formato XML: * <cotacao> * <nome-banco> ... </nome-banco> * <taxa> ... </taxa> * </cotacao> */ public Object doTransform(Object src, String encoding) throws TransformerException { Document doc = null; // Parsing da cotacao recebida. try { doc = DocumentHelper.parseText(src.toString()); } catch (DocumentException e) { throw new TransformerException(this, e); } // Obtem os valores da cotacao e atribui ao objeto RespostaCliente. String nomeBanco = doc.valueOf("/cotacao/nome-banco"); String taxa = doc.valueOf("/cotacao/taxa"); // Log para debug. logger.info("\n" + MensagemLocal.mensagemDebug( "Banco: " + nomeBanco + " Taxa: " + taxa)); Cotacao cotacao = new Cotacao(); cotacao.setNomeBanco(nomeBanco); cotacao.setTaxa(Double.parseDouble(taxa)); RespostaCliente resp = new RespostaCliente(false, null, cotacao); return resp; } } Figura 60 Transformer para os bancos A e B (continuao).
xmlns:spring="http://www.springframework.org/schema/beans" xmlns:tcp="http://www.mulesource.org/schema/mule/tcp/2.2" xmlns:cxf="http://www.mulesource.org/schema/mule/cxf/2.2" xmlns:jdbc="http://www.mulesource.org/schema/mule/jdbc/2.2" xmlns:file="http://www.mulesource.org/schema/mule/file/2.2" xmlns:smtp="http://www.mulesource.org/schema/mule/smtp/2.2" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.mulesource.org/schema/mule/tcp/2.2 http://www.mulesource.org/schema/mule/tcp/2.2/mule-tcp.xsd http://www.mulesource.org/schema/mule/cxf/2.2 http://www.mulesource.org/schema/mule/cxf/2.2/mule-cxf.xsd http://www.mulesource.org/schema/mule/jdbc/2.2 http://www.mulesource.org/schema/mule/jdbc/2.2/mule-jdbc.xsd http://www.mulesource.org/schema/mule/file/2.2 http://www.mulesource.org/schema/mule/file/2.2/mule-file.xsd http://www.mulesource.org/schema/mule/smtp/2.2 http://www.mulesource.org/schema/mule/smtp/2.2/mule-smtp.xsd">
No conector JDBC vamos fazer referncia ao datasource e configurar as propriedades pollingFrequency e queryKey. PoolingFrequency faz sentido apenas para o inbound endpoint. Teremos duas propriedades queryKey definidas, uma para realizar INSERT e outra para SELECT. A configurao do conector fica:
<jdbc:connector name="jdbcConnector" pollingFrequency="60000" dataSource-ref="dataSourceRel"> <jdbc:query key="outboundInsertStatement" value="INSERT INTO RELATORIO (CLIENTE, CPF, PONTUACAO, HISTORICO, VALOR, PRAZO) VALUES (#[map-payload:CLIENTE],#[map-payload:CPF], #[map-payload:PONTUACAO], #[map-payload:HISTORICO], #[map-payload:VALOR], #[map-payload:PRAZO])"/> <jdbc:query key="inboundSelectStatement" value="SELECT ID, CLIENTE, CPF, PONTUACAO, HISTORICO, VALOR, PRAZO FROM RELATORIO ORDER BY ID"/> </jdbc:connector>
Vejamos como os dados so alimentados no comando INSERT e como so recebidos do comando SELECT. Os dados para o comando INSERT so obtidos do payload da mensagem Mule corrente. Esse payload um Map (Java.util.Map) na forma <nome coluna> <valor>. Por exemplo, na configurao do conector temos a expresso: #[map-payload:CLIENTE] significando que o dado a ser inserido na coluna CLIENTE da tabela ser obtido do elemento do Map cuja chave CLIENTE. A classe GeradorRelatorio, descrita no item 7.1.2, mostra a criao do Map para insero no banco de dados. Da mesma forma o resultado obtido atravs de um comando SELECT chega ao endpoint em uma mensagem Mule cujo payload um Map. A classe EmissorRelatorio obtm deste Map o resultado do SELECT para a emisso do relatrio. Falta agora configurar os dois endpoints, um inbound endpoint para realizar o select na base de dados e o outbound endpoint para realizar insert. As configuraes dos endpoints so as seguintes: <jdbc:endpoint name="insertDataBaseEdp" queryKey="outboundInsertStatement" connector-ref="jdbcConnector" /> <jdbc:endpoint name="selectDataBaseEdp" queryKey="inboundSelectStatement" connector-ref="jdbcConnector" />
A configurao do endpoint requer um pouco mais de atributos, precisamos definir o usurio do mailbox, sua senha, o endereo do SMTP Server, sua porta, o email do remetente e do destinatrio. Configuramos o endpoint da seguinte forma: <smtp:endpoint name="emailEdp" connector-ref="smtpConnector" host="mailhost.agencia.com.br" port="25" user="relatorio" password="relpasswd" from="relatorio@agencia.com.br" to="gerencia@agencia.com.br" /> A figura 63 apresenta os conectores e endpoints adicionados ao arquivo de configurao Mule (muleconfig.xml) para esta fase do estudo de caso.
<!-- Datasource JDBC --> <spring:bean id="dataSourceRel" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <spring:property name="driverClassName" value="org.hsqldb.jdbcDriver"/> <spring:property name="url" value="jdbc:hsqldb:mem:relatorio"/> <spring:property name="username" value="sa"/> <spring:property name="password" value=""/> </spring:bean> <tcp:connector name="tcpConnector" keepSendSocketOpen="false"> <tcp:direct-protocol payloadOnly="true"/> </tcp:connector> <jdbc:connector name="jdbcConnector" pollingFrequency="60000" dataSource-ref="dataSourceRel"> <jdbc:query key="outboundInsertStatement" value="INSERT INTO RELATORIO (CLIENTE, CPF, PONTUACAO, HISTORICO, VALOR, PRAZO) VALUES (#[map-payload:CLIENTE],#[map-payload:CPF], #[map-payload:PONTUACAO], #[map-payload:HISTORICO], #[map-payload:VALOR], #[map-payload:PRAZO])"/> <jdbc:query key="inboundSelectStatement" value="SELECT ID, CLIENTE, CPF, PONTUACAO, HISTORICO, VALOR, PRAZO FROM RELATORIO ORDER BY ID"/> </jdbc:connector> <file:connector name="fileConnector" outputAppend="true" outputPattern="solicitacoes.txt" /> <smtp:connector name="smtpConnector" subject="Requisicao Recebida" /> <tcp:endpoint name="TcpBancoAEdp" host="localhost" port="4599" connector-ref="tcpConnector"/> <endpoint name="WSDLCxfBancoBEdp" address="wsdl-cxf:http://localhost:8080/BancoBWS/cotacao? WSDL&method=getCotacaoEmprestimo" /> <endpoint name="GeradorRelatorioEdp" connector-ref="vmConnector" address="vm://spb.relatorio" /> <jdbc:endpoint name="insertDataBaseEdp" queryKey="outboundInsertStatement" connector-ref="jdbcConnector" /> <jdbc:endpoint name="selectDataBaseEdp" queryKey="inboundSelectStatement" connector-ref="jdbcConnector" />
<file:endpoint name="relatorioEdp" connector-ref="fileConnector" path="c:/tutorial/agencia-emprestimo/relatorio" /> <smtp:endpoint name="emailEdp" connector-ref="smtpConnector" host="mailhost.agencia.com.br" port="25" user="relatorio" password="relpasswd" from="relatorio@agencia.com.br" to="gerencia@agencia.com.br" />
Figura 64 Configurao do servio SeletorBancario. Um inbound wire tap router permite rotear uma mensagem que chega ao inbound endpoint para outros endpoints bem como para o componente onde ele configurado. Para rotear para outros endpoints feita uma cpia da mensagem que chega ao endpoint. No nosso caso, mensagens que chegam ao inbound endpoint do SeletorBancario (SeletorBancarioEdp) so roteadas tanto para o componente SeletorBancario quanto para o endpoint GeradorRelatorioEdp. Um Wire Tap router suporta tambm a
configurao de filtros, permitindo enviar para o outro endpoint apenas mensagens com certas caractersticas.
<service name="GatewayBancario"> <inbound> <inbound-endpoint ref="GWBancario" /> </inbound> <outbound> <static-recipient-list-router /> </outbound> </service>
<service name="Banco-A"> <inbound> <inbound-endpoint ref="Banco-A-edp" /> </inbound> <outbound> <chaining-router> <outbound-endpoint ref="TcpBancoAEdp" transformer-refs="RequisicaoServicoParaRequisicaoXML"/> <outbound-endpoint ref="RespostasServicos" transformer-refs="ByteArrayToString BancoXMLparaRespostaCliente ObjectToJmsMessage" /> </chaining-router> </outbound> </service>
<?xml version="1.0" encoding="UTF-8"?> <mule xmlns="http://www.mulesource.org/schema/mule/core/2.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jetty="http://www.mulesource.org/schema/mule/jetty/2.2" xmlns:vm="http://www.mulesource.org/schema/mule/vm/2.2" xmlns:jms="http://www.mulesource.org/schema/mule/jms/2.2" xmlns:ejb="http://www.mulesource.org/schema/mule/ejb/2.2" xmlns:spring="http://www.springframework.org/schema/beans" xmlns:tcp="http://www.mulesource.org/schema/mule/tcp/2.2" xmlns:cxf="http://www.mulesource.org/schema/mule/cxf/2.2" xmlns:jdbc="http://www.mulesource.org/schema/mule/jdbc/2.2" xmlns:file="http://www.mulesource.org/schema/mule/file/2.2" xmlns:smtp="http://www.mulesource.org/schema/mule/smtp/2.2" xsi:schemaLocation=" http://www.mulesource.org/schema/mule/jetty/2.2 http://www.mulesource.org/schema/mule/jetty/2.2/mule-jetty.xsd http://www.mulesource.org/schema/mule/core/2.2 http://www.mulesource.org/schema/mule/core/2.2/mule.xsd http://www.mulesource.org/schema/mule/vm/2.2 http://www.mulesource.org/schema/mule/vm/2.2/mule-vm.xsd http://www.mulesource.org/schema/mule/jms/2.2 http://www.mulesource.org/schema/mule/jms/2.2/mule-jms.xsd http://www.mulesource.org/schema/mule/ejb/2.2 http://www.mulesource.org/schema/mule/ejb/2.2/mule-ejb.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.mulesource.org/schema/mule/tcp/2.2 http://www.mulesource.org/schema/mule/tcp/2.2/mule-tcp.xsd http://www.mulesource.org/schema/mule/cxf/2.2 http://www.mulesource.org/schema/mule/cxf/2.2/mule-cxf.xsd http://www.mulesource.org/schema/mule/jdbc/2.2 http://www.mulesource.org/schema/mule/jdbc/2.2/mule-jdbc.xsd http://www.mulesource.org/schema/mule/file/2.2 http://www.mulesource.org/schema/mule/file/2.2/mule-file.xsd http://www.mulesource.org/schema/mule/smtp/2.2 http://www.mulesource.org/schema/mule/smtp/2.2/mule-smtp.xsd"> <!-- ####### TRANSFORMERS ####### --> <custom-transformer name="RequisicaoRestParaRequisicaoServico" class="spb.transformers.RequisicaoRestParaRequisicaoServico" /> <jms:object-to-jmsmessage-transformer name="ObjectToJmsMessage" /> <jms:jmsmessage-to-object-transformer name="JmsMessageToObject" /> <byte-array-to-string-transformer name="ByteArrayToString" /> <expression-transformer name="ClienteParaArgsAgenciaCredito"> <return-argument evaluator="bean" expression="nome"/> <return-argument evaluator="bean" expression="cpf"/> </expression-transformer> <custom-transformer name="PerfilCreditoXmlParaPerfilCredito" class="spb.transformers.PerfilCreditoXmlParaPerfilCredito" /> <custom-transformer name="BancosComoRecipientes" class="spb.transformers.BancosComoRecipientes" />
<expression-transformer name="RequisicaoServicoParaRequisicaoXML"> <return-argument evaluator="bean" expression="requisicaoXML"/> </expression-transformer> <custom-transformer name="BancoXMLparaRespostaCliente" class="spb.transformers.BancoXMLparaRespostaCliente" /> <!-- Datasource JDBC --> <spring:bean id="dataSourceRel" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <spring:property name="driverClassName" value="org.hsqldb.jdbcDriver"/> <spring:property name="url" value="jdbc:hsqldb:mem:relatorio"/> <spring:property name="username" value="sa"/> <spring:property name="password" value=""/> </spring:bean> <!-- ######## CONECTORES ######## --> <jetty:connector name="httpConnector" useContinuations="false" /> <vm:connector name="vmConnector" queueEvents="true"/> <jms:activemq-connector name="jmsConnector" /> <ejb:connector name="ejbConnector" securityPolicy="security.policy" jndiInitialFactory="org.jnp.interfaces.NamingContextFactory" jndiProviderUrl="localhost"> </ejb:connector> <tcp:connector name="tcpConnector" keepSendSocketOpen="false"> <tcp:direct-protocol payloadOnly="true"/> </tcp:connector> <jdbc:connector name="jdbcConnector" pollingFrequency="60000" dataSource-ref="dataSourceRel"> <jdbc:query key="outboundInsertStatement" value="INSERT INTO RELATORIO (CLIENTE, CPF, PONTUACAO, HISTORICO, VALOR, PRAZO) VALUES (#[map-payload:CLIENTE],#[map-payload:CPF], #[map-payload:PONTUACAO], #[map-payload:HISTORICO], #[map-payload:VALOR], #[map-payload:PRAZO])"/> <jdbc:query key="inboundSelectStatement" value="SELECT ID, CLIENTE, CPF, PONTUACAO, HISTORICO, VALOR, PRAZO FROM RELATORIO ORDER BY ID"/> </jdbc:connector> <file:connector name="fileConnector" outputAppend="true" outputPattern="solicitacoes.txt" /> <smtp:connector name="smtpConnector" subject="Requisicao Recebida" /> <!-- ######## ENDPOINTS ######### --> <endpoint name="RequisicaoServicoREST" connector-ref="httpConnector" address="jetty:rest://localhost:8888/AgenciaEmprestimo" /> <endpoint name="GWAgenciaCredito" connector-ref="jmsConnector" address="jms://spb.gw.agenciacredito" />
<ejb:endpoint name="AgenciaCreditoEdp" host="localhost" port="1099" object="/AgenciaCredito" method="getPerfilCredito" methodArgumentTypes="java.lang.String,java.lang.String" connector-ref="ejbConnector"/> <endpoint name="SeletorBancarioEdp" connector-ref="vmConnector" address="vm://spb.seletorbancario.bancos" /> <endpoint name="GWBancario" connector-ref="jmsConnector" address="jms://spb.gw.bancario" /> <endpoint name="RespostasServicos" connector-ref="jmsConnector" address="jms://spb.emprestimo.respostas" /> <endpoint name="Banco-A-edp" connector-ref="jmsConnector" address="jms://spb.banco.banco-a" /> <endpoint name="Banco-B-edp" connector-ref="jmsConnector" address="jms://spb.banco.banco-b" /> <endpoint name="Banco-C-edp" connector-ref="jmsConnector" address="jms://spb.banco.banco-c" /> <endpoint name="TrataErrosRequisicaoEdp" connector-ref="jmsConnector" address="jms://spb.erro.requisicao" /> <tcp:endpoint name="TcpBancoAEdp" host="localhost" port="4599" connector-ref="tcpConnector"/> <endpoint name="WSDLCxfBancoBEdp" address="wsdl-cxf:http://localhost:8080/BancoBWS/cotacao? WSDL&method=getCotacaoEmprestimo" /> <endpoint name="GeradorRelatorioEdp" connector-ref="vmConnector" address="vm://spb.relatorio" /> <jdbc:endpoint name="insertDataBaseEdp" queryKey="outboundInsertStatement" connector-ref="jdbcConnector" /> <jdbc:endpoint name="selectDataBaseEdp" queryKey="inboundSelectStatement" connector-ref="jdbcConnector" /> <file:endpoint name="relatorioEdp" connector-ref="fileConnector" path="c:/Alberto/tutorial/agencia-emprestimo/relatorio" /> <smtp:endpoint name="emailEdp" connector-ref="smtpConnector" host="mailhost.agencia.com.br" port="25" user="relatorio" password="relpasswd" from="relatorio@agencia.com.br" to="gerencia@agencia.com.br" /> <!-- ########### MODEL ########### --> <model name="AgenciaEmprestimo"> <service name="ServicosAgencia"> <inbound> <inbound-endpoint ref="RequisicaoServicoREST" transformer-refs="RequisicaoRestParaRequisicaoServico" /> </inbound> <component class="spb.ServicosAgencia" /> <outbound> <filtering-router> <outbound-endpoint ref="GWAgenciaCredito" /> <payload-type-filter expectedType="spb.mensagens.RequisicaoServico"/> </filtering-router>
<filtering-router> <outbound-endpoint ref="TrataErrosRequisicaoEdp" /> <payload-type-filter expectedType="spb.mensagens.MensagemErro"/> </filtering-router> </outbound> <async-reply timeout="15000"> <inbound-endpoint ref="RespostasServicos" /> <custom-async-reply-router class="spb.routers.AgregadorRespostas" /> </async-reply> </service> <service name="GatewayAgenciaCredito"> <inbound> <inbound-endpoint ref="GWAgenciaCredito" /> </inbound> <component class="spb.GatewayAgenciaCredito"> <binding interface="spb.credit.ServicoAgenciaCredito" method="getPerfilCredito"> <outbound-endpoint synchronous="true" transformer-refs="ClienteParaArgsAgenciaCredito" responseTransformer refs="PerfilCreditoXmlParaPerfilCredito" ref="AgenciaCreditoEdp" /> </binding> </component> <outbound> <filtering-router> <outbound-endpoint ref="SeletorBancarioEdp" /> <payload-type-filter expectedType="spb.mensagens.RequisicaoServico"/> </filtering-router> <filtering-router> <outbound-endpoint ref="RespostasServicos" /> <payload-type-filter expectedType="spb.mensagens.RespostaCliente"/> </filtering-router> </outbound> </service> <service name="SeletorBancario"> <inbound> <inbound-endpoint ref="SeletorBancarioEdp" /> <wire-tap-router> <outbound-endpoint ref="GeradorRelatorioEdp" /> </wire-tap-router> </inbound> <component class="spb.SeletorBancario" />
<outbound> <filtering-router> <outbound-endpoint ref="GWBancario" transformer-refs="BancosComoRecipientes ObjectToJmsMessage" /> <payload-type-filter expectedType="spb.mensagens.RequisicaoServico"/> </filtering-router> <filtering-router> <outbound-endpoint ref="TrataErrosRequisicaoEdp" /> <payload-type-filter expectedType="spb.mensagens.MensagemErro"/> </filtering-router> </outbound> </service> <service name="GatewayBancario"> <inbound> <inbound-endpoint ref="GWBancario" /> </inbound> <outbound> <static-recipient-list-router /> </outbound> </service> <service name="Banco-A"> <inbound> <inbound-endpoint ref="Banco-A-edp" /> </inbound> <outbound> <chaining-router> <outbound-endpoint ref="TcpBancoAEdp" transformer-refs="RequisicaoServicoParaRequisicaoXML"/> <outbound-endpoint ref="RespostasServicos" transformer-refs="ByteArrayToString BancoXMLparaRespostaCliente ObjectToJmsMessage" /> </chaining-router> </outbound> </service> <service name="Banco-B"> <inbound> <inbound-endpoint ref="Banco-B-edp" /> </inbound> <outbound> <chaining-router> <outbound-endpoint ref="WSDLCxfBancoBEdp" transformer-refs="RequisicaoServicoParaRequisicaoXML"/> <outbound-endpoint ref="RespostasServicos" transformer-refs="BancoXMLparaRespostaCliente ObjectToJmsMessage" /> </chaining-router> </outbound> </service>
<service name="Banco-C"> <inbound> <inbound-endpoint ref="Banco-C-edp" /> </inbound> <component class="spb.banco.Banco" /> <outbound> <pass-through-router> <outbound-endpoint ref="RespostasServicos" /> </pass-through-router> </outbound> </service> <service name="TrataErrosRequisicao"> <inbound> <inbound-endpoint ref="TrataErrosRequisicaoEdp" /> </inbound> <component class="spb.excecoes.TrataErrosRequisicao" /> <outbound> <pass-through-router> <outbound-endpoint ref="RespostasServicos" /> </pass-through-router> </outbound> </service> <service name="GeradorRelatorio"> <inbound> <inbound-endpoint ref="GeradorRelatorioEdp" /> </inbound> <component> <singleton-object class="spb.GeradorRelatorio"/> </component> <outbound> <pass-through-router> <outbound-endpoint ref="insertDataBaseEdp"/> </pass-through-router> </outbound> </service> <service name="EmissorAutomaticoRelatorio"> <inbound> <inbound-endpoint ref="selectDataBaseEdp"/> </inbound> <component> <singleton-object class="spb.EmissorRelatorio"/> </component>
<outbound> <multicasting-router> <outbound-endpoint ref="relatorioEdp" transformer-refs="JmsMessageToObject" /> <outbound-endpoint ref="emailEdp" /> <payload-type-filter expectedType="java.lang.String"/> </multicasting-router> </outbound> </service> </model> </mule>
8 Referncias
[1] Enterprise Integration Patterns. Gregor Hohpe and Bob Woolf. Addison Wesley 2003. [2] Enterprise Service Bus. David Chapell. OReilly 2004. [3] Service Oriented Java Business Integration. Binildas C. A. PACKT Publishing 2008. [4] Open Source ESBs in Action. Tijs Rademakers and Jos Dirksen. Manning 2009. [5] Mule in Action.
David Dossot and John D Emic. Manning 2007. [6] Mule 2: A developers Guide. Peter Delia, Antonie Borg and Ricston. Apress. [7] Mule 2.2.1 Users Guide. MuleSource.Org 2008 http://www.mulesource.org/display/MULE2USER/Home [8] Mule ESB 2.2.1 API. http://www.mulesource.org/docs/site/2.2.1/apidocs