8. Versão 4 – cliente/servidor em uma arquitetura de serviço web
Nesta nova versão, o aplicativo [Pam] será executado no modo cliente/servidor em uma arquitetura de serviço web. Voltemos à arquitetura do aplicativo anterior:
![]() |
Acima, uma camada de comunicação [C, RMI, S] permitia uma comunicação transparente entre o cliente [ui] e a camada remota [metier]. Vamos utilizar uma arquitetura semelhante, na qual 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 anterior RMI / EJB, de ser multiplataforma. Assim, o serviço web pode ser escrito em Java e implantado no servidor Glassfish, enquanto o cliente pode ser um cliente .NET ou PHP.
Vamos desenvolver essa arquitetura de acordo com três modos diferentes:
- o serviço web será fornecido pelo EJB [Metier]
- o serviço web será fornecido por um aplicativo web que utiliza o EJB e o [Metier]
- o serviço web será fornecido por uma aplicação web que utiliza o Spring
Um serviço web pode ser implementado de diversas maneiras em um servidor Java EE:
- por meio de uma classe anotada com @WebService que é executada em um contêiner web
![]() |
- por meio de um EJB anotado com @WebService que é executado em um contêiner EJB
![]() |
Começaremos por esta última arquitetura.
8.1. Serviço web implementado por um EJB
8.1.1. A parte do servidor
8.1.1.1. O projeto NetBeans
Vamos começar criando um novo projeto Maven, cópia do projeto EJB [mv-pam-ejb-metier-dao-jpa-eclipselink]:
![]() |
Com a seguinte arquitetura:
![]() |
a camada [metier] será o serviço web acionado pela camada [ui]. Essa classe não precisa implementar uma interface. São as anotações que transformam um POJO (Plain Ordinary Java Object) em um serviço web. A classe [Metier], que implementa a camada [metier] acima, é transformada da seguinte maneira:
package metier;
...
@WebService
@Stateless()
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class Metier implements IMetierLocal,IMetierRemote {
// referências sobre as camadas [DAO]
@EJB
private ICotisationDaoLocal cotisationDao = null;
@EJB
private IEmployeDaoLocal employeDao=null;
@EJB
private IIndemniteDaoLocal indemniteDao=null;
// obter a folha de pagamento
@WebMethod
public FeuilleSalaire calculerFeuilleSalaire(String SS,
...
}
// lista de funcionários
@WebMethod
public List<Employe> findAllEmployes() {
...
}
// importante — sem getters e setters para os EJB
}
- na linha 4, a anotação @WebService transforma a classe [Metier] em um serviço web. Um serviço web expõe métodos aos seus clientes. Esses 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, eles serão expostos no serviço web, o que causará erros de segurança.
A adição dessas anotações é detectada pelo NetBeans, que então altera a natureza do projeto:
![]() |
Em [1], surgiu uma árvore de diretórios [Web Services] no projeto. Nela, encontram-se o serviço web Metier e seus dois métodos. A aplicação de servidor pode ser implantada em [2]. O servidor MySQL deve estar em execução e seu banco de dados [dbpam_eclipselink] deve existir e estar preenchido. Pode ser necessário, antes disso, excluir os arquivos [3] e EJB do projeto cliente/servidor EJB analisado anteriormente, para evitar conflitos de nomes. De fato, nosso novo projeto traz consigo os mesmos EJB do projeto anterior.
![]() |
No [1], vemos nosso aplicativo serveur implantado no servidor Glassfish. Uma vez implantado o serviço web, ele pode ser testado:
![]() |
- no [1], no projeto atual, testamos o serviço web [Metier]
- o serviço web está acessível por meio de diferentes URL. O URL e o [2] permitem testar o serviço web
- em [3], um link no arquivo XML que define o serviço web. Os clientes do serviço web precisam conhecer o URL desse arquivo. É a partir dele que é gerada a camada cliente (stubs) do serviço web.
- no [4,5], um formulário que permite testar os métodos expostos pelo serviço web. Esses métodos são apresentados com seus parâmetros, que o usuário pode definir.
Por exemplo, vamos testar o método [findAllEmployes], que não requer nenhum parâmetro:
![]() |
Acima, testamos o método. Recebemos então a resposta abaixo (visualização parcial). Nela, encontramos os dois funcionários com suas indenizações. O leitor é convidado a testar da mesma forma o método [4], passando-lhe os três parâmetros que ele espera.

8.1.2. A parte do cliente
![]() |
8.1.2.1. O projeto NetBeans do cliente console
Agora criamos um projeto Java do tipo [Java Application] para a parte client do aplicativo. Não foi possível (junho de 2012) criar um projeto Maven para esse cliente. Ocorre um erro, que parece ser conhecido na internet, mas continua sem solução.
![]() | ![]() |
Após a criação do projeto, indicamos que ele será cliente do serviço web que acabamos de implantar no servidor Glassfish:
![]() |
- em [2], selecionamos o novo projeto e clicamos no botão [New File]
- em [3], indicamos que queremos criar um cliente de serviço web
![]() |
- com [4], vamos designar o projeto NetBeans do serviço web
- na janela [5], estão listados todos os projetos com um branch [Web Services], aqui apenas o projeto [mv-pam-ws-metier-dao-eclipselink].
- Um projeto pode implantar vários serviços web. Em [6], selecionamos o serviço web ao qual queremos nos conectar.
![]() |
- No [7], é exibida a definição do serviço web URL. Essa definição URL é utilizada pelas ferramentas de software que geram a camada cliente que fará a interface com o serviço web.
![]() |
- A camada cliente [C] [1] que será gerada é constituída por um conjunto de classes Java que serão colocadas em um mesmo pacote. O nome deste pacote é definido como [8].
- Assim que o assistente de criação do cliente do serviço web for concluído com o botão [Finish], a camada [C] acima é criada.
Isso se reflete em algumas alterações no projeto:
- No [10] acima, aparece uma árvore de classes [Generated Sources] que contém as classes da camada [C], as quais permitem que o cliente [3] se comunique com o serviço web. Essa camada permite que o cliente [3] se comunique com a camada [metier] e [4] como se ela fosse local e não remota.
- Em [11], aparece uma árvore [Web Service References] que lista os serviços web para os quais foi gerada uma camada de cliente.
Observe-se que, na camada [C] [10] gerada, encontramos classes que foram implantadas no lado do servidor: Indemnite, Cotisation, Employe, FeuilleSalaire, ElementsSalaire, Metier. Metier é o serviço web e as demais classes são necessárias para esse serviço. Podemos ter a curiosidade de consultar o código delas. Veremos que a definição das classes que, quando instanciadas, representam objetos manipulados pelo serviço, consiste na definição dos campos da classe e de seus acessadores, bem como na adição de anotações que permitem a serialização da classe no fluxo XML. A classe Metier tornou-se uma interface contendo os dois métodos que foram anotados com @WebMethod. Cada um desses métodos gera duas classes, por exemplo, [CalculerFeuilleSalaire.java] e [CalculerFeuilleSalaireResponse.java], em que uma encapsula a chamada ao método e a outra, seu resultado. Por fim, a classe MetierService é a classe que permite ao cliente obter uma referência ao serviço web de negócio remoto:
O método getMetierPort da linha 2 permite obter uma referência ao serviço web Metier remoto.
8.1.2.2. O cliente de console do serviço web Metier
Resta-nos apenas escrever o cliente do 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 do cliente do serviço web. A classe [MainRemote] apresenta erros. Para corrigi-los, começaremos removendo todas as instruções [import] existentes na classe e as regeneraremos usando a opção [Fix Imports]. De fato, algumas das classes utilizadas pela classe [MainRemote] agora fazem parte do pacote [client] gerado.
- No [3], o trecho de código onde a camada [metier] é instanciada é o [3]. Isso é feito com o código JNDI para obter uma referência a um EJB remoto.
Fazemos a evolução do código da seguinte maneira:
- o código JNDI é removido
- como a classe [PamException] não existe no lado do cliente, excluímos o catch associado para manter apenas o catch na classe pai [Exception].
![]() |
- no [4], resta-nos obter uma referência ao serviço web remoto [Metier] para podermos chamar seu método [calculerFeuilleSalaire].
- em [5], com o mouse, arrastamos (drag) o método [calculerFeuilleSalaire] do serviço web [Metier] para soltá-lo (drop) em [4]. É gerado o código [6]. Esse código genérico pode ser posteriormente adaptado pelo desenvolvedor.
![]() |
- na linha 112, vemos que [calculerFeuilleSalaire] é 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 obtém uma referência ao serviço web Metier. Feito isso, o código da classe não se altera, exceto que, na linha 10, não é a exceção do tipo [Exception] que é tratada, mas sim o tipo mais geral Throwable, a classe pai da classe Exception. Se ocorrer uma exceção, exibimos todas as causas aninhadas até a causa original.
Estamos prontos para os testes:
- verificar se o SGBD MySQL5 foi iniciado, se a base de dados dbpam_eclipselink foi criada e inicializada
- verificar se o serviço web está implantado no servidor Glassfish
- compilar o cliente (Clean and Build)
- configurar a execução do cliente
![]() |
- Executar o cliente
Os resultados no console são os seguintes:
Com a seguinte configuração:

obtêm-se os seguintes resultados:
Observe-se 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 do serviço web Metier
Tarefa a ser realizada: portar o cliente Swing do projeto [mv-pam-client-ejb-metier-dao-jpa-eclipselink] para o novo projeto, de modo que ele também seja um cliente do serviço web implantado no servidor Glassfish.
8.2. Serviço web implementado por uma aplicação web
Agora, situamo-nos no contexto da seguinte arquitetura:
![]() |
O serviço web é fornecido por um aplicativo web executado no contêiner web do servidor Glassfish. Esse serviço web utilizará o EJB e o [Metier], que, por sua vez, estão implantados no contêiner EJB3.
8.2.1. A parte do servidor
Criamos uma aplicação web:
![]() |
- no [1], criamos um novo projeto
- em [2]; esse projeto é do tipo [Web Application]
- em [3], nomeamos o projeto como [mv-pam-ws-ejb-metier-dao-eclipselink]
![]() |
- em [4], escolhemos a versão Java EE 6
- em [6], o projeto criado
No esquema abaixo, o aplicativo web criado será executado no contêiner web. Ele utilizará o EJB [Metier], que, por sua vez, será implantado no contêiner EJB do servidor.
![]() |
Para que a aplicação web criada tenha acesso às classes associadas ao EJB [Metier], adicionamos às bibliotecas da aplicação web [mv-pam-ws-ejb-metier-dao-eclipselink], a dependência do servidor EJB [mv-pam-ejb-metier-dao-eclipselink], já analisado.
![]() |
- no [1], adicionamos um projeto às dependências do projeto web,
- em [2], seleciona-se o projeto [mv-pam-ejb-metier-dao-eclipselink],
- em [3], o tipo da dependência é ejb,
- em [4], o escopo da dependência é provided, ou seja, ela será fornecida pelo ambiente de execução,
- em [5], a dependência foi adicionada.
Para criar o mesmo serviço web de antes, precisamos:
- criar uma classe com a tag @Webservice
- com dois métodos calculerFeuilleSalaire e findAllEmployes marcados com @WebMethod
Criamos uma classe [PamWsEjbMetier] no pacote [pam.ws]:
![]() |
![]() |
A classe [PamWsEjbMetier] é a seguinte:
- linhas 7-10: a classe importa classes dos módulos EJB e [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: ela implementa a interface IMetier definida no módulo EJB
- linhas 18-19: o método calculerFeuilleSalaire é exposto como método do serviço web
- linhas 23-24: o método findAllEmployes é exposto como método do serviço web
- linhas 15-16: a interface local do EJB [Metier] é inserida no campo da linha 16. Utilizamos a interface local porque a aplicação web e o módulo EJB são executados no mesmo JVM.
- linhas 20 e 25: os métodos calculerFeuilleSalaire e findAllEmployes delegam seu processamento aos métodos de mesmo nome do EJB [Metier]. Portanto, a classe serve apenas para expor aos clientes remotos os métodos de EJB e [Metier] como métodos de um serviço web.
No NetBeans, a aplicação web é reconhecida como expondo um serviço web:
![]() |
Para implantar o serviço web no servidor GlassFish, precisamos implantar:
- o módulo web no contêiner web do servidor
- o módulo EJB no contêiner EJB do servidor
Para isso, precisamos criar uma aplicação do tipo [Enterprise Application] que irá implantar os dois módulos ao mesmo tempo. Para tal, é necessário que os dois projetos estejam carregados no NetBeans [2].
Feito isso, criamos um novo projeto [3].
![]() |
- no [4], escolhemos um projeto do tipo [Enterprise Application].
- em [5], nomeamos o projeto
![]() |
- em [6], configuramos o projeto. A versão do Java EE será 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 corporativo irá encapsular o módulo Web e o módulo EJB já criados e carregados no NetBeans. Portanto, não solicitamos a criação de novos módulos.
- No [7], o projeto empresarial [mv-pam-webapp-ear] assim criado. Outro projeto Maven foi criado ao mesmo tempo: [mv-pam-webapp]. Não vamos nos preocupar com ele.
- No [8], adicionamos dependências ao projeto corporativo
![]() |
- no [9], adicionamos o projeto web do tipo WAR,
- no [10], adicionamos o projeto EJB do tipo EJB,
![]() |
- em [11], o projeto corporativo com suas duas dependências.
Compilamos o projeto corporativo com um “Clean and Build”. Estamos quase prontos para implantá-lo no servidor GlassFish. Antes disso, pode ser necessário descarregar as aplicações já carregadas no servidor para evitar possíveis conflitos de nomes entre EJB e [11]:
![]() |
O servidor MySQL deve estar em execução e o banco de dados [dbpam_eclipselink] disponível e preenchido. Feito isso, a aplicação corporativa [12] pode ser implantada. No [13], é possível verificar que ela foi efetivamente implantada no servidor Glassfish.
Podemos testar o serviço web que acaba de ser implantado:
![]() |
- em [1], solicitamos o teste do serviço web [PamWsEjbMetier]
- em [2], a página de teste. Deixamos a cargo do leitor a realização dos testes.
8.2.2. A parte do cliente
Tarefa a ser realizada: seguindo a abordagem descrita no parágrafo 8.1.2.1, construir um cliente de console para o serviço web anterior.
8.3. Serviço web implementado com Spring e Tomcat
Passamos agora a considerar a seguinte arquitetura:
![]() |
O serviço web é fornecido por uma aplicação web executada no contêiner web do servidor Tomcat. A arquitetura da aplicação será a seguinte:
![]() |
Vamos nos basear no projeto [mv-pam-spring-hibernate] desenvolvido no parágrafo 5.11:
![]() |
8.3.1. A parte do servidor
Criamos um aplicativo Maven do tipo web chamado [mv-pam-ws-spring-tomcat] [1]:
![]() |
Modificamos o arquivo [pom.xml] para incluir as seguintes dependências [2]:
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mv-pam-spring-hibernate</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Dependências do Apache CXF -->
<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>
- linhas 3-7: a dependência do projeto [spring-pam-jpa-hibernate],
- linhas 8-17: as dependências do framework Apache CXF e [http://cxf.apache.org/]. Esse framework facilita a criação de serviços web.
Este arquivo [pom.xml] traz várias dependências [2].
Voltemos à arquitetura do aplicativo:
![]() |
As chamadas ao serviço web que vamos construir são gerenciadas por um servlet do framework CXF. Isso se traduz no arquivo [WEB-INF / web.xml] da seguinte maneira:
<?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>
<!-- Configuração do CXF -->
<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>
- O framework CXF depende do Spring. Linhas 4-6: é declarado um listener. A classe correspondente será carregada junto com a aplicação web. Ela utilizará o arquivo de configuração do Spring [WEB-INF / applicationContext.xml]:
![]() |
- linhas 8-12: o servlet CXF, que irá gerenciar as chamadas ao serviço web que iremos criar,
- linhas 13-16: as chamadas URL processadas pelo servlet CXF serão do tipo /ws/*. As demais não serão processadas pelo CXF.
Para definir o serviço web, definimos uma interface e sua implementação:
![]() |
A interface [IWsMetier] será a seguinte:
package pam.ws;
import javax.jws.WebService;
import metier.IMetier;
@WebService
public interface IWsMetier extends IMetier{
}
- linha 7: a interface [IWsMetier] deriva da interface [IMetier] da camada [métier] do projeto [mv-pam-spring-hibernate],
- linha 6: a interface [IWsMetier] é a de um serviço web.
A classe de implementação dessa interface é a 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 {
// camada de negócios
private IMetier metier;
// Construtor
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 e setters
public void setMetier(IMetier metier) {
this.metier = metier;
}
}
- linha 11: a classe [PamWsMetier] implementa a interface definida anteriormente,
- linha 10: define a classe como um serviço web,
- linha 14: a camada [métier] será injetada pelo Spring,
- linhas 21 e 26: a anotação @WebMethod transforma um método em um método exposto pelo serviço web,
- linhas 23 e 28: os métodos são implementados por meio da camada [métier].
Resta-nos definir o conteúdo do arquivo de configuração do Spring [applicationContext.xml]:
![]() |
Seu conteúdo é o seguinte:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
<!-- Apache CXF -->
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<!-- camadas inferiores -->
<import resource="classpath:spring-config-metier-dao.xml" />
<!-- serviço web -->
<bean id="wsMetier" class="pam.ws.PamWsMetier">
<property name="metier" ref="metier"/>
</bean>
<jaxws:endpoint id="wsmetier"
implementor="#wsMetier"
address="/metier">
</jaxws:endpoint>
</beans>
- linhas 13-15: importam-se arquivos de configuração do Apache CXF. Estes são buscados no Classpath do projeto (atributo classpath:),
- linhas 4, 9, 10: são declarados namespaces específicos do Apache CXF,
- linha 18: importa-se o arquivo de configuração do Spring do projeto [mv-pam-spring-hibernate],
- linhas 21-23: definem o bean do serviço web com sua dependência da camada [métier] (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 o URL no qual o serviço web estará disponível, neste caso /metier. Combinada com o formato que as URL processadas pelo Apache CXF devem ter (consulte o arquivo web.xml), essa URL passa a ser /ws/metier.
Nosso projeto está pronto para ser executado. Nós o executamos (Run) e acessamos o URL [http://localhost:8080/mv-pam-ws-spring-tomcat/ws] em um navegador:

A página lista todos os serviços web implantados. Aqui, há apenas um. Seguimos o link WSDL:
![]() |
O texto exibido [1] corresponde a um arquivo XML que define as funcionalidades do serviço web, como chamá-lo e quais respostas ele envia. Observe que o URL e o [2] estão contidos neste arquivo WSDL. Todos os clientes do serviço web precisam conhecê-lo.
8.3.2. A parte do cliente
Tarefa a ser realizada: seguindo a abordagem descrita no parágrafo 8.1.2.1, criar um cliente de console para o serviço web anterior.
Observação: para indicar o URL do arquivo WSDL do serviço web, proceda da seguinte forma:
![]() |
Colocaremos em [3] o URL anotado anteriormente em [2].












































