UDDI
Para a publicação e pesquisa de Web Services
usa-se um servidor de nomes que implementa a norma
UDDI (Universal Description, Discovery and Integration).
Mais informação:
UDDI
JAX-R
Para comunicar com servidores UDDI,
existe a biblioteca JAX-R (Java API for XML Registries),
que permite publicar, pesquisar e eliminar registos de Web Services.
O esquema de dados do UDDI é consideravelmente mais rico do que o habitual num servidor de nomes,
com diversas entidades e relações,
que permitem guardar informação de negócio sobre a organização e os seus serviços.
Como consequência, o JAX-R é uma biblioteca verbosa,
que obriga a escrever muitas linhas de código para realizar as tarefas mais habituais de registo e de pesquisa.
Mais informação:
JAX-R
UDDI Naming
Para tornar a utilização do UDDI mais simples
foi criada a biblioteca UDDINaming que reduziu o esquema de dados para suportar registos simplificados com apenas:
1 organização, 1 serviço e 1 implementação.
Esta biblioteca torna o registo e pesquisa de serviços mais sucinto.
O código fonte está disponível para consulta e modificação.
Mais informação:
Exemplo
O exemplo seguinte é um Web Service que se regista no UDDI:
Seguem-se instruções detalhadas para construir e
executar o exemplo.
jUDDI (Java UDDI)
-
O servidor de nomes a utilizar é o jUDDI
alojado na RNL.
-
Para usar basta configurar a propriedade
uddi.url com o seguinte valor:
http://user:pass@uddi.sd.rnl.tecnico.ulisboa.pt:9090/
-
Os valores de user e pass para acesso ao UDDI devem ser pedidos ao professor no laboratório.
-
Para que a configuração seja feita para todos os exemplos,
pode-se criar um perfil Maven que sobrepõe o valor da propriedade para todos os projetos do utilizador.
Para isso, criar ou editar o ficheiro settings.xml na pasta .m2 da home do utilizador.
-
Como alternativa ao jUDDI da RNL, pode-se usar um jUDDI local:
-
O pacote de instalação pode ser encontrado na lista de software que se pediu para instalar.
-
Para lançar o servidor,
basta executar o seguinte comando na pasta juddi-.../bin:
-
startup.sh (Linux e Mac)
-
startup.bat (Windows)
-
juddi-startup / juddi-shutdown (laboratórios das aulas)
UDDINaming
-
Obter o código da biblioteca UDDINaming:
-
Instalar o módulo no repositório Maven local:
-
cd uddi-naming
-
mvn install
-
A biblioteca inclui testes de integração
(IT - Integration Tests)
que verificam o funcionamento correto do jUDDI antes de instalar o módulo.
-
Uma vez instalado o módulo no repositório Maven local,
a biblioteca pode ser usada como dependência em qualquer pom.xml.
Servidor JAX-WS
-
Construir e executar o servidor:
-
cd hello-ws_juddi
-
mvn compile exec:java
O nome da classe a executar e
os argumentos estão definidos no pom.xml
O servidor deve executar sem erros,
disponibilizando o endpoint address e
registando-se no UDDI.
-
Para confirmar que o servidor está a funcionar e à espera de pedidos,
consultar o contrato que é gerado automaticamente, através dos seguintes endereços:
-
Este exemplo usa a abordagem de desenvolvimento implementation-first, que gera o WSDL e XSD necessários.
Cliente JAX-WS
-
Construir o cliente:
-
cd hello-ws-cli_juddi
-
mvn compile
Executa previamente generate-sources onde
o cliente obtém o contrato WSDL a partir do servidor e
usa a ferramenta wsimport para gerar as classes de invocação do serviço
(em target/)
-
Executar o cliente:
-
mvn exec:java
O cliente deve executar sem erros,
consultando o UDDI para descobrir o endereço do servidor,
e fazendo uma invocação remota.
Resumindo:
primeiro foi configurado o servidor de nomes jUDDI da RNL.
Depois foi instalada a biblioteca UDDINaming no repositório Maven local,
que testa também o funcionamento do servidor jUDDI.
Em seguida, foi construído e iniciado o servidor, que se regista no jUDDI e
fica à espera de pedidos no endpoint address.
Finalmente, o cliente obtém o WSDL a partir do servidor e
gera o código de invocação que permite depois fazer invocações remotas.
Visão global do exemplo Hello World
Exercício
Desenvolvimento do Projeto ForkExec
O ponto de partida é o projeto base.
Caso já tenha o repositório de GitHub criado (confirme junto do professor), poderá também obter o código base a partir do GitHub:
git clone https://github.com/tecnico-distsys/CXX-ForkExec
(substituir CXX pelo identificador do grupo)
A primeira tarefa a realizar, para prevenir conflitos de módulos Maven,
é alterar todos os pom.xml para substituir as referências ao grupo CXX pelo identificador do grupo.
Preencher também todos os ficheiros README.md com a identificação do grupo e dos autores.
O servidor de restaurante tem um esqueleto funcional com a operação ctrlPing e tem um teste de integração.
Agora, pretende-se ter múltiplos restaurantes e desenvolver também o serviço hub-ws que vai descobrir e comunicar com todos eles.
Assim sendo, o UDDI torna-se útil e necessário.
Para começar, fazer a instalação do módulo principal.
Pretende-se agora confirmar que rst-ws
se regista no UDDI.
-
Confirmar a dependência para a biblioteca UDDINaming:
...
<!-- UDDI Naming -->
<dependency>
<groupId>pt.ulisboa.tecnico.sdis</groupId>
<artifactId>uddi-naming</artifactId>
<version>1.2.0</version>
</dependency>
...
-
A RestaurantApp deverá receber o endereço do UDDI e o nome do serviço como argumento.
-
O RestaurantEndpointManager deverá ter um construtor que recebe o endereço do UDDI e o nome do serviço, e que efetua o registo
(e apaga quando o serviço termina).
-
Feitas as alterações, o servidor deverá iniciar-se, registar-se no UDDI e depois ficar à espera de pedidos.
Lançar mais do que uma instância do rst-ws.
-
A forma mais simples de permitir múltiplas instâncias da estação é
parametrizar as propriedades de configuração com um número de instância.
-
No pom.xml é possível ver as seguintes definições
(substituir CXX pelo identificador do grupo):
...
<ws.i>1</ws.i>
<ws.host>localhost</ws.host>
<ws.port>808${ws.i}</ws.port>
<ws.url>http://${ws.host}:${ws.port}/rst-ws/endpoint</ws.url>
<ws.name>CXX_Restaurant${ws.i}</ws.name>
...
-
Para lançar um restaurante:
-
mvn compile
-
mvn exec:java
Por omissão, será a instância 1,
que fica à escuta de pedidos no porto 8081 e
que se regista com o nome CXX_Restaurant1.
-
Para lançar outro restaurante:
-
mvn exec:java -Dws.i=2
A instância 2 fica à escuta de pedidos no porto 8082 e
regista-se com o nome CXX_Restaurant2.
Pretende-se agora confirmar como rst-ws-cli
pesquisa no UDDI.
-
Confirmar a dependência para a biblioteca UDDINaming.
-
O RestaurantClient deverá ter um construtor que recebe o endereço do UDDI e
o nome do serviço a contactar, e
que efetua a pesquisa para localizar o servidor.
-
A RestaurantClientApp deverá receber o endereço do UDDI e
o nome do serviço como argumento.
Hub
O ponto de partida inclui o servidor Hub incompleto
(hub-ws) e
o cliente incompleto
(hub-ws-cli).
Vamos começar pelo servidor hub-ws:
-
A estrutura do servidor está representada neste
diagrama de classes
.
- O pacote à esquerda contém as classes que são geradas a partir do contrato WSDL.
- O pacote à direita contém as classes do servidor:
- a App onde a aplicação arranca,
- o EndpointManager que disponibiliza o endereço do serviço,
- o PortImpl que concretiza as operações definidas no WSDL, e
- as classes de domínio que representam a informação armazenada pelo serviço.
-
As classes de domínio da aplicação devem depois ser implementadas.
Apenas existe o objeto inicial, o Domain root.
-
De seguida,
consultar o contrato WSDL do serviço a implementar:
- Hub WSDL
-
Analise o contrato para ver que operações define e
quais os respectivos parâmetros e exceções.
-
Vamos gerar código Java a partir do WSDL.
O WSDL está na pasta hub-contract.
O Maven está configurado para chamar a ferramenta wsimport.
-
cd hub-ws
-
mvn generate-sources
Caso o WSDL esteja bem formado e válido,
a ferramenta wsimport gera vários ficheiros que suportam o web service.
Entre eles, estarão as classes para os tipos complexos usados como parâmetros
e a interface Java que define o Web Service.
-
Faça refresh
(right-click, Maven, Update Project..., Force Update of Snapshots/Releases, OK)
no Eclipse e
consulte as classes geradas na pasta:
target/generated-sources/wsimport.
Em especial,
consulte a classe ...Service, e
descubra a interface Java ...PortType que foi gerada a partir do WSDL.
-
Vamos agora concretizar o serviço.
-
Confirme que a classe de implementação do serviço ...PortImpl implementa a interface Java gerada.
-
Confirmar a associação da classe PortImpl ao WSDL através da
anotação @WebService com os seguintes atributos:
endpoint interface (nome do tipo Java do PortType),
wsdlLocation (nome do ficheiro WSDL),
name (definido no WSDL),
portName (WSDL),
targetNamespace (WSDL) e
serviceName (WSDL).
-
Para já vamos compilar e executar o servidor sem estas operações estarem concluídas.
Coloque apenas um corpo vazio ou um retorno simples (e.g. return null).
-
Executar o servidor:
-
mvn compile exec:java
O nome da classe a executar e
os argumentos estão definidos no pom.xml
O servidor deve executar sem erros,
disponibilizando o endpoint address.
-
Confirmar que o servidor está à espera de pedidos no endereço:
Vamos agora usar o cliente hub-ws-cli para testar o servidor.
-
A estrutura do cliente está representada neste
diagrama de classes
.
- O pacote à direita contém as classes que são geradas a partir do contrato WSDL.
- O pacote à esquerda contém as classes do servidor:
- a ClientApp onde a aplicação arranca, e
- o Client que usa o stub gerado para fazer invocações remotas.
Existem também os testes de integração, em que cada classe de testes se destina a testar uma operação remota.
-
Vamos gerar o código Java para invocação do serviço.
-
Consultar o pom.xml do cliente para:
- confirmar que o WSDL está a ser corretamente referenciado
- confirmar que a fase de wsimport está configurada.
-
cd hub-ws-cli
-
mvn generate-sources
As classes são geradas na pasta:
target/generated-sources/wsimport.
-
Vamos fazer uma chamada simples, correndo a aplicação cliente.
-
mvn compile exec:java
A operação auxiliar ctrlPing deverá ser invocada.
-
Depois do teste pontual, vamos correr os testes de integração já existentes.
-
mvn verify
O Maven executa todos os testes contidos em classes ...IT e
um resumo é apresentado.
Vamos agora implementar uma operação simples do servidor hub-ws que contacta as estações através de um ctrlPing.
-
O hub-ws vai ser cliente de rst-ws.
Em vez de repetir código,
vamos usar o objeto RestaurantClient,
já desenvolvido e testado.
-
Em primeiro lugar, instalar o módulo do cliente do restaurante:
cd rst-ws-cli
mvn install
-
Depois,
acrescentar a dependência Maven no hub-ws/pom.xml
(substituir CXX pelo identificador do grupo):
...
<dependency>
<groupId>com.forkexec.CXX</groupId>
<artifactId>rst-ws-cli</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
...
O sistema em funcionamento terá três processos neste exercício:
-
Concretizar a operação ctrlPing do Hub.
-
Consultar o UDDI para pesquisar restaurantes.
-
Criar um RestaurantClient para cada estação encontrada.
-
Chamar a operação ctrlPing de cada um.
-
Juntar as respostas e
devolver como resultado.
Finalmente,
usar o hub-ws-cli para chamar o ctrlPing que vai fazer ping ao próprio Hub e a todos os restaurantes.
-
Abrir consola para o Restaurant 1:
-
cd rst-ws
-
mvn compile exec:java
-
Abrir consola para o Restaurant 2:
-
cd rst-ws
-
mvn compile exec:java -Dws.i=2
-
Restaurant 3
-
mvn compile exec:java -Dws.i=3
-
Abrir consola para o Hub:
-
cd hub-ws
-
mvn compile exec:java
-
Finalmente, na consola para o cliente do Hub:
-
cd hub-ws-cli
-
mvn compile exec:java
O resultado final do ctrlPing deverá ser impresso nesta consola.
O que falta fazer?
-
No rst-ws:
-
Implementar todas as operações de acordo com o enunciado
-
Fazer um bom tratamento de exceções
-
Não esquecer também que o servidor é multi-tarefa (thread) e
que os acessos a dados partilhados devem ser devidamente sincronizados
-
No rst-ws-cli:
-
Fazer testes de integração das operações principais
-
Testar casos mais importantes
-
Não esquecer os casos com entradas incorretas: null, "", valores inesperados, etc
-
Os testes de integração correm com mvn verify
-
Continuar para os restantes serviços... pts-ws ...
Continuação de bom trabalho!