8. Versão 4 – Cliente/Servidor numa arquitetura de serviços Web
Nesta nova versão, a aplicação [Pam] funcionará em modo cliente/servidor no âmbito de uma arquitetura de serviços web. Vamos rever a arquitetura da aplicação anterior:
![]() |
Acima, uma camada de comunicação [C, RMI, S] permitia a comunicação transparente entre o cliente [ui] e a camada remota [business]. Iremos utilizar uma arquitetura semelhante, em que a camada de comunicação [C, RMI, S] será substituída por uma camada [C, HTTP / SOAP, S]:
![]() |
O protocolo HTTP/SOAP tem a vantagem, em relação ao protocolo RMI/EJB anterior, de ser multiplataforma. Assim, o serviço web pode ser escrito em Java e implementado no servidor Glassfish, enquanto o cliente pode ser um cliente .NET ou PHP.
Iremos desenvolver esta arquitetura em três modos diferentes:
- o serviço web será fornecido pelo EJB [Business]
- o serviço web será fornecido por uma aplicação web utilizando o EJB [Business]
- o serviço web será fornecido por uma aplicação web utilizando o Spring
Um serviço web pode ser implementado de várias formas num servidor Java EE:
- por uma classe anotada com @WebService que é executada num contentor web
![]() |
- por um EJB anotado com @WebService que é executado num contentor EJB
![]() |
Começaremos pela última arquitetura.
8.1. Serviço Web implementado por um EJB
8.1.1. O lado do servidor
8.1.1.1. O projeto NetBeans
Vamos começar por criar um novo projeto Maven que seja uma cópia do projeto EJB [mv-pam-ejb-metier-dao-jpa-eclipselink]:
![]() |
Na seguinte arquitetura:
![]() |
a camada [business] será o serviço web contactado pela camada [ui]. Esta classe não precisa de implementar uma interface. São as anotações que transformam um POJO (Plain Old Java Object) num serviço web. A classe [Business], que implementa a camada [business] acima, é transformada da seguinte forma:
<dependency>
<groupId>org.swinglabs</groupId>
<artifactId>swing-layout</artifactId>
<version>1.0.3</version>
</dependency>
- Linha 4: A anotação @WebService transforma a classe [Business] num serviço web. Um serviço web expõe métodos aos seus clientes. Estes métodos devem ser anotados com o atributo @WebMethod.
- Linhas 19 e 25: Os dois métodos da classe [Metier] tornam-se métodos do serviço web.
- Linha 29: é importante que os getters e setters sejam removidos; caso contrário, serão expostos no serviço web, causando erros de segurança.
A adição destas anotações é detetada pelo NetBeans, que, em seguida, altera a natureza do projeto:
![]() |
Em [1], surgiu uma árvore de [Serviços Web] no projeto. Esta contém o serviço Web Metier e os seus dois métodos. A aplicação de servidor pode ser implementada [2]. O servidor MySQL deve estar em execução, e a sua base de dados [dbpam_eclipselink] deve existir e estar preenchida. Pode ser necessário remover previamente [3] os EJBs do projeto EJB cliente/servidor estudado anteriormente para evitar conflitos de nomes. De facto, o nosso novo projeto inclui os mesmos EJBs que os do projeto anterior.
![]() |
Em [1], vemos a nossa aplicação de servidor implementada no servidor GlassFish. Assim que o serviço web estiver implementado, pode ser testado:
![]() |
- em [1], no projeto atual, testamos o serviço web [Metier]
- O serviço web é acessível através de diferentes URLs. A URL [2] permite-lhe testar o serviço web
- em [3], um link para o ficheiro XML que define o serviço web. Os clientes do serviço web precisam de saber o URL deste ficheiro. A camada cliente (stubs) do serviço web é gerada a partir deste ficheiro.
- Em [4,5], um formulário para testar os métodos expostos pelo serviço web. Estes são apresentados juntamente com os seus parâmetros, que o utilizador pode definir.
Por exemplo, vamos testar o método [findAllEmployees], que não requer parâmetros:
![]() |
Acima, testamos o método. Em seguida, recebemos a resposta abaixo (visualização parcial). Podemos, de facto, ver os dois funcionários com os seus benefícios. O leitor é convidado a testar o método [4] da mesma forma, passando-lhe os três parâmetros que este espera.

8.1.2. O lado do cliente
![]() |
8.1.2.1. O projeto NetBeans do cliente -console
Vamos agora criar um projeto Java do tipo [Aplicação Java] para o lado do cliente da aplicação. Em junho de 2012, não era possível criar um projeto Maven para este cliente. Ocorre um erro, que parece ser conhecido online, mas continua por resolver.
![]() | ![]() |
Assim que o projeto for criado, especificamos que será um cliente do serviço web que acabámos de implementar no servidor GlassFish:
![]() |
- em [2], selecionamos o novo projeto e clicamos no botão [Novo Ficheiro]
- em [3], especificamos que queremos criar um cliente de serviço web
![]() |
- em [4], selecionamos o projeto NetBeans para o serviço web
- na janela [5], todos os projetos com uma ramificação [Web Services] são listados; aqui, apenas o projeto [mv-pam-ws-metier-dao-eclipselink].
- Um projeto pode implementar vários serviços web. Em [6], especificamos o serviço web ao qual queremos ligar-nos.
![]() |
- Em [7], é apresentada a URL que define o serviço web. Esta URL é utilizada pelas ferramentas de software que geram a camada cliente que irá interagir com o serviço web.
![]() |
- A camada cliente [C] [1] que será gerada consiste num conjunto de classes Java que serão colocadas no mesmo pacote. O nome deste pacote é definido em [8].
- Assim que o assistente de criação do cliente do serviço web for concluído, clicando no botão [Concluir], a camada [C] descrita acima é criada.
Isto reflete-se numa série de alterações no projeto:
- Em [10] acima, surge uma árvore [Fontes Geradas], contendo as classes da camada [C] que permitem ao cliente [3] comunicar com o serviço web. Esta camada permite ao cliente [3] comunicar com a camada [negócio] [4] como se fosse local em vez de remota.
- Em [11], surge uma árvore [Referências de Serviços Web], listando os serviços web para os quais foi gerada uma camada de cliente.
Note-se que na camada [C] gerada [10], encontramos classes que foram implementadas no lado do servidor: Indemnite, Cotisation, Employe, FeuilleSalaire, ElementsSalaire, Metier. Metier é o serviço web, e as outras classes são necessárias para este serviço. Pode ser interessante examinar o seu código. Veremos que a definição das classes — que, quando instanciadas, representam objetos manipulados pelo serviço — consiste em definir os campos da classe e os seus acessores, bem como adicionar anotações que permitem a serialização da classe num fluxo XML. A classe Metier tornou-se uma interface que contém os dois métodos que foram anotados com @WebMethod. Cada um deles dá origem a duas classes, por exemplo [CalculatePayroll.java] e [CalculatePayrollResponse.java], onde uma encapsula a chamada do método e a outra o seu resultado. Por fim, a classe MetierService é a classe que permite ao cliente ter uma referência ao serviço web Metier remoto:
package metier;
...
@WebService
@Stateless()
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class Metier implements IMetierLocal,IMetierRemote {
// references on layers [DAO]
@EJB
private ICotisationDaoLocal cotisationDao = null;
@EJB
private IEmployeDaoLocal employeDao=null;
@EJB
private IIndemniteDaoLocal indemniteDao=null;
// get your payslip
@WebMethod
public FeuilleSalaire calculerFeuilleSalaire(String SS,
...
}
// list of employees
@WebMethod
public List<Employe> findAllEmployes() {
...
}
// important - no getters and setters for EJB
}
O método getMetierPort na linha 2 recupera uma referência ao serviço web Metier remoto.
8.1.2.2. O cliente de consola para o serviço web Metier
Resta apenas escrever o cliente para o serviço web Metier. Copiamos a classe [MainRemote] do projeto [mv-pam-client-metier-dao-jpa-eclipselink] — que era um cliente de um servidor EJB — para o novo projeto.
![]() |
- Em [1], a classe cliente do serviço web. A classe [MainRemote] contém erros. Para os corrigir, começaremos por remover todas as instruções [import] existentes na classe e regenerá-las utilizando a opção [Fix Imports]. Isto porque algumas das classes utilizadas pela classe [MainRemote] fazem agora parte do pacote [client] gerado.
- Em [3], o trecho de código onde a camada [business] é instanciada [3]. É instanciada utilizando código JNDI para obter uma referência a um EJB remoto.
Atualizamos o código da seguinte forma:
- O código JNDI foi removido
- Uma vez que a classe [PamException] não existe no lado do cliente, removemos o bloco catch associado para manter apenas o bloco catch da classe pai [Exception].
![]() |
- Em [4], ainda precisamos de obter uma referência ao serviço web remoto [Metier] para chamar o seu método [calculatePayroll].
- Em [5], utilizando o rato, arrastamos o método [calculerFeuilleSalaire] do serviço web [Metier] e soltamo-lo em [4]. O código é gerado [6]. Este código genérico pode então ser adaptado pelo programador.
![]() |
- Na linha 112, vemos que [calculatePayroll] é um método da classe [client.Metier] (linha 111). Agora que sabemos como obter a camada [metier], o código anterior pode ser reescrito da seguinte forma:
A linha 7 recupera uma referência ao serviço web Metier. Feito isto, o código da classe permanece inalterado, exceto que, na linha 10, não é o tipo [Exception] que é tratado, mas o tipo Throwable, mais geral, a classe pai da classe Exception. Se ocorrer uma exceção, exibimos todas as suas causas aninhadas até à causa raiz.
Estamos prontos para testar:
- Certifique-se de que o SGBD MySQL5 está em execução e que a base de dados dbpam_eclipselink foi criada e inicializada
- Certifique-se de que o serviço web está implementado no servidor GlassFish
- Compile o cliente (Limpar e Compilar)
- Configure o cliente para ser executado
![]() |
- Execute o cliente
Os resultados no console são os seguintes:
Com a seguinte configuração:

obtemos os seguintes resultados:
Note que, embora o serviço web [Metier] envie uma exceção do tipo [PamException], a exceção recebida pelo cliente é do tipo [SOAPFaultException]. Mesmo na cadeia de exceções, o tipo [PamException] não aparece.
8.1.3. O cliente Swing para o serviço web Metier
Tarefa: Portar o cliente Swing do projeto [mv-pam-client-ejb-metier-dao-jpa-eclipselink] para o novo projeto, de modo a que este também se torne um cliente do serviço web implementado no servidor GlassFish.
8.2. Serviço web implementado por uma aplicação web
Estamos agora a trabalhar dentro da seguinte estrutura arquitetónica:
![]() |
O serviço web é fornecido por uma aplicação web em execução no contentor web do servidor GlassFish. Este serviço web irá basear-se no EJB [Metier], que está implementado no contentor EJB3.
8.2.1. O lado do servidor
Criamos uma aplicação web:
![]() |
- Em [1], criamos um novo projeto
- em [2], este projeto é do tipo [Aplicação Web]
- em [3], nomeamo-lo [mv-pam-ws-ejb-metier-dao-eclipselink]
![]() |
- em [4], selecionamos Java EE 6
- em [6], o projeto é criado
No diagrama abaixo, a aplicação web criada será executada no contentor web. Utilizará o EJB [Metier], que será implementado no contentor EJB do servidor.
![]() |
Para garantir que a aplicação web criada tenha acesso às classes associadas ao EJB [Metier], adicionamos a dependência do servidor EJB [mv-pam-ejb-metier-dao-eclipselink] — que já abordámos — às bibliotecas da aplicação web [mv-pam-ws-ejb-metier-dao-eclipselink].
![]() |
- Em [1], adicionamos um projeto às dependências do projeto web;
- em [2], selecionamos o projeto [mv-pam-ejb-metier-dao-eclipselink],
- em [3], o tipo de dependência é ejb,
- em [4], o âmbito da dependência é fornecido, o que significa que será fornecido pelo ambiente de execução,
- em [5], a dependência foi adicionada.
Para criar o mesmo serviço web de antes, precisamos de:
- criar uma classe anotada com @WebService
- com dois métodos, calculerFeuilleSalaire e findAllEmployes, anotados com @WebMethod
Criamos uma classe [PamWsEjbMetier] num pacote [pam.ws]:
![]() |
![]() |
A classe [PamWsEjbMetier] é a seguinte:
- Linhas 7–10: A classe importa classes do módulo EJB [pam-serveurws-metier-dao-jpa-eclipselink], cujo projeto Maven foi adicionado às dependências do projeto.
- linha 12: a classe é um serviço web
- linha 13: implementa a interface IMetier definida no módulo EJB
- linhas 18-19: o método `calculerFeuilleSalaire` é exposto como um método de serviço web
- Linhas 23–24: O método `findAllEmployees` é exposto como um método de serviço web
- Linhas 15–16: a interface local do EJB [Metier] é injetada no campo na linha 16. Utilizamos a interface local porque a aplicação web e o módulo EJB são executados na mesma JVM.
- Linhas 20 e 25: Os métodos `calculerFeuilleSalaire` e `findAllEmployees` delegam o seu processamento aos métodos com o mesmo nome no EJB [Metier]. A classe serve, portanto, apenas para expor os métodos do EJB [Metier] a clientes remotos como métodos de serviço web.
No NetBeans, a aplicação web é reconhecida como expondo um serviço web:
![]() |
Para implementar o serviço web no servidor GlassFish, temos de implementar ambos:
- o módulo web no contentor web do servidor
- o módulo EJB no contentor EJB do servidor
Para tal, é necessário criar uma [Aplicação Empresarial] que implemente ambos os módulos em simultâneo. Para o efeito, ambos os projetos devem ser carregados no NetBeans [2].
Depois de feito isso, criamos um novo projeto [3].
![]() |
- Em [4], selecionamos o tipo de projeto [Aplicação Empresarial].
- Em [5], nomeamos o projeto
![]() |
- Em [6], configuramos o projeto. A versão Java EE será a Java EE 6. Um projeto empresarial pode ser criado com dois módulos: um módulo EJB e um módulo Web. Aqui, o projeto empresarial irá encapsular o módulo Web e o módulo EJB que já foram criados e carregados no NetBeans. Portanto, não solicitamos a criação de novos módulos.
- Em [7], o projeto empresarial [mv-pam-webapp-ear] é criado. Outro projeto Maven foi criado ao mesmo tempo [mv-pam-webapp]. Não nos preocuparemos com ele.
- Em [8], adicionamos dependências ao projeto empresarial
![]() |
- Em [9], adicionamos o projeto web do tipo WAR,
- Em [10], adicionamos o projeto EJB do tipo ejb,
![]() |
- Em [11], o projeto empresarial com as suas duas dependências.
Compilamos o projeto empresarial utilizando o Clean e o Build. Estamos quase prontos para o implementar no servidor GlassFish. Antes de o fazer, poderá ser necessário encerrar quaisquer aplicações já em execução no servidor para evitar potenciais conflitos de nomes EJB [11]:
![]() |
O servidor MySQL deve estar em execução e a base de dados [dbpam_eclipselink] deve estar disponível e preenchida. Assim que isto estiver feito, a aplicação empresarial pode ser implementada [12]. Em [13], podemos ver que foi implementada com sucesso no servidor GlassFish.
Podemos testar o serviço web que acabou de ser implementado:
![]() |
- Em [1], solicitamos o teste do serviço web [PamWsEjbMetier]
- em [2], a página de teste. Deixamos ao leitor a tarefa de realizar os testes.
8.2.2. O lado do cliente
Tarefa: Seguindo o procedimento descrito na Secção 8.1.2.1, crie um cliente de consola para o serviço web anterior.
8.3. Serviço web implementado com Spring e Tomcat
Estamos agora a trabalhar dentro da seguinte estrutura arquitetónica:
![]() |
O serviço web é fornecido por uma aplicação web em execução no contentor web do servidor Tomcat. A arquitetura da aplicação será a seguinte:
![]() |
Iremos basear-nos no projeto [mv-pam-spring-hibernate] criado na Secção 5.11:
![]() |
8.3.1. O lado do servidor
Criamos uma aplicação Maven do tipo web denominada [mv-pam-ws-spring-tomcat] [1]:
![]() |
Modificamos o ficheiro [pom.xml] para incluir as seguintes dependências [2]:
- linhas 3–7: a dependência do projeto [spring-pam-jpa-hibernate],
- linhas 8–17: dependências da estrutura Apache CXF [http://cxf.apache.org/]. Esta estrutura facilita a criação de serviços web.
Este ficheiro [pom.xml] apresenta inúmeras dependências [2].
Voltemos à arquitetura da aplicação:
![]() |
As chamadas ao serviço web que vamos construir são tratadas por um servlet da estrutura CXF. Isto reflete-se no ficheiro [WEB-INF/web.xml] da seguinte forma:
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mv-pam-spring-hibernate</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Apache CXF dependencies -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>2.2.12</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>2.2.12</version>
</dependency>
</dependencies>
- A estrutura CXF depende do Spring. Linhas 4–6: É declarado um ouvinte. A classe correspondente será carregada ao mesmo tempo que a aplicação web. Utilizará o ficheiro de configuração do Spring [WEB-INF/applicationContext.xml]:
![]() |
- Linhas 8–12: O servlet CXF que irá tratar as chamadas para o serviço web que vamos criar,
- Linhas 13–16: As URLs tratadas pelo servlet CXF terão o formato /ws/*. As demais não serão tratadas pelo CXF.
Para definir o serviço web, definimos uma interface e a sua implementação:
![]() |
A interface [IWsMetier] será a seguinte:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>mv-pam-ws-spring-tomcat</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- CXF configuration -->
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
- linha 7: a interface [IWsMetier] deriva da interface [IMetier] na camada [business] do projeto [mv-pam-spring-hibernate],
- linha 6: a interface [IWsMetier] é a de um serviço web.
A classe de implementação para esta interface é a seguinte:
package pam.ws;
import javax.jws.WebService;
import metier.IMetier;
@WebService
public interface IWsMetier extends IMetier{
}
- linha 11: a classe [PamWsMetier] implementa a interface definida anteriormente,
- linha 10: define a classe como um serviço web,
- linha 14: a camada [business] será injetada pelo Spring,
- linhas 21, 26: a anotação @WebMethod transforma um método num método exposto pelo serviço web,
- linhas 23, 28: os métodos são implementados utilizando a camada [business].
Ainda precisamos de definir o conteúdo do ficheiro de configuração do Spring [applicationContext.xml]:
![]() |
O seu conteúdo é o seguinte:
package pam.ws;
import java.util.List;
import javax.jws.WebMethod;
import javax.jws.WebService;
import jpa.Employe;
import metier.FeuilleSalaire;
import metier.IMetier;
@WebService
public class PamWsMetier implements IWsMetier {
// business layer
private IMetier metier;
// manufacturer
public PamWsMetier(){
}
@WebMethod
public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillees, int nbJoursTravailles) {
return metier.calculerFeuilleSalaire(SS, nbHeuresTravaillees, nbJoursTravailles);
}
@WebMethod
public List<Employe> findAllEmployes() {
return metier.findAllEmployes();
}
// getters and setters
public void setMetier(IMetier metier) {
this.metier = metier;
}
}
- Linhas 13–15: Os ficheiros de configuração do Apache CXF são importados. Estes são procurados no classpath do projeto (atributo classpath),
- Linhas 4, 9, 10: são declarados os namespaces específicos do Apache CXF,
- linha 18: o ficheiro de configuração do Spring para o projeto [mv-pam-spring-hibernate] é importado,
- Linhas 21–23: definem o bean do serviço web com a sua dependência da camada [business] (linha 22),
- Linhas 24–27: definem o próprio serviço web,
- linha 25: o bean Spring que implementa o serviço web é aquele definido na linha 21;
- linha 26: define a URL na qual o serviço web estará disponível, neste caso /business. Combinada com o formato exigido para URLs processadas pelo Apache CXF (ver ficheiro web.xml), esta URL torna-se /ws/business.
O nosso projeto está pronto para ser executado. Executamo-lo (Run) e solicitamos a URL [http://localhost:8080/mv-pam-ws-spring-tomcat/ws] num navegador:

A página lista todos os serviços web implementados. Aqui, existe apenas um. Seguimos a ligação WSDL:
![]() |
O texto apresentado [1] provém de um ficheiro XML que define a funcionalidade do serviço web, como o chamar e quais as respostas que este envia. Repare no URL [2] deste ficheiro WSDL. Todos os clientes do serviço web precisam de o conhecer.
8.3.2. O lado do cliente
Tarefa: Seguindo o procedimento descrito na Secção 8.1.2.1, crie um cliente de consola para o serviço web anterior.
Nota: Para especificar o URL do ficheiro WSDL do serviço web, proceda da seguinte forma:
![]() |
Introduza o URL indicado anteriormente em [2] em [3].












































