Apache CXF com WSDL First

O aplicativo CXF-POJO que você desenvolveu resulta em um acoplamento muito forte entre o cliente e o servidor. Fornecer acesso direto à interface de serviço também pode representar ameaças graves à segurança. Assim, normalmente é desejável o desacoplamento entre o cliente e o servidor, o que é obtido por meio de WSDL (Web Services Description Language).

Escrevemos a interface de serviço da web em um documento WSDL que é baseado em XML. Usaremos uma ferramenta para mapear este WSDL para interfaces Apache CXF que são então implementadas e usadas por nossos aplicativos cliente e servidor. Para fornecer desacoplamento, começar com um WSDL é a forma preferida. Para isso, você precisa primeiro aprender um novo idioma - WSDL. Escrever WSDL requer uma abordagem cuidadosa e seria melhor se você pudesse obter algum entendimento sobre isso antes de começar a trabalhar nele.

Nesta lição, começaremos definindo uma interface de serviço da web em um documento WSDL. Aprenderemos como usar o CXF para criar aplicativos de servidor e cliente começando com WSDL. Manteremos o aplicativo simples para manter o foco no uso do CXF. Depois que o aplicativo do servidor for criado, iremos publicá-lo em um URL desejado usando uma classe CXF integrada.

Primeiro, vamos descrever o WSDL que vamos usar.

WSDL para HelloWorld

O serviço da web que vamos implementar terá um único método da web chamado greetings que aceita um stringparâmetro que contém o nome do usuário e retorna uma mensagem de string para o chamador após anexar uma mensagem de saudação ao nome do usuário. O wsdl completo é mostrado abaixo -

//Hello.wsdl
<?xml version = "1.0" encoding = "UTF-8"?>
<wsdl:definitions xmlns:soap = "http://schemas.xmlsoap.org/wsdl/soap/"
   xmlns:tns = "http://helloworld.tutorialspoint.com/"
   xmlns:wsdl = "http://schemas.xmlsoap.org/wsdl/"
   xmlns:xsd = "http://www.w3.org/2001/XMLSchema"
   name = "HelloWorld"
   targetNamespace = "http://helloworld.tutorialspoint.com/">
   <wsdl:types>
      <xsd:schema attributeFormDefault = "unqualified"
         elementFormDefault = "qualified"
         targetNamespace = "http://helloworld.tutorialspoint.com/">
         <xsd:element name = "greetings" type = "tns:greetings"/>
         <xsd:complexType name = "greetings">
            <xsd:sequence>
               <xsd:element minOccurs = "0" name = "arg0" type = "xsd:string"/>
            </xsd:sequence>
         </xsd:complexType>
         <xsd:element name = "greetingsResponse"
         type = "tns:greetingsResponse"/>
         <xsd:complexType name = "greetingsResponse">
            <xsd:sequence>
               <xsd:element minOccurs = "0" name = "return" type = "xsd:string"/>
            </xsd:sequence>
         </xsd:complexType>
      </xsd:schema>
   </wsdl:types>
   <wsdl:message name = "greetings">
      <wsdl:part element = "tns:greetings" name = "parameters"> </wsdl:part>
   </wsdl:message>
   <wsdl:message name = "greetingsResponse">
      <wsdl:part element = "tns:greetingsResponse" name = "parameters"> </wsdl:part>
   </wsdl:message>
   <wsdl:portType name = "HelloWorldPortType">
      <wsdl:operation name = "greetings">
         <wsdl:input message = "tns:greetings" name = "greetings">  </wsdl:input>
         <wsdl:output message = "tns:greetingsResponse" name = "greetingsResponse">
         </wsdl:output>
      </wsdl:operation>
   </wsdl:portType>
   <wsdl:binding name = "HelloWorldSoapBinding" type = "tns:HelloWorldPortType">
      <soap:binding style = "document"
      transport = "http://schemas.xmlsoap.org/soap/http"/>
      <wsdl:operation name = "greetings">
         <soap:operation soapAction = "" style = "document"/>
         <wsdl:input name = "greetings"></wsdl:input>
         <wsdl:output name = "greetingsResponse">
            <soap:body use = "literal"/>
         </wsdl:output>
         </wsdl:operation>
   </wsdl:binding>
   <wsdl:service name = "HelloWorldService">
      <wsdl:port binding = "tns:HelloWorldSoapBinding" name = "HelloWorldPort">
         <soap:address location = "http://localhost:9090/HelloServerPort"/>
      </wsdl:port>
   </wsdl:service>
</wsdl:definitions>

Observe que escrever um wsdl sintaticamente correto sempre foi um desafio para os desenvolvedores; existem muitas ferramentas e editores online disponíveis para criar um wsdl. Esses editores pedem os nomes das mensagens que você deseja implementar, juntamente com os parâmetros que deseja passar em uma mensagem e o tipo de mensagem de retorno que deseja que seu aplicativo cliente receba. Se você conhece a sintaxe wsdl, pode codificar manualmente o documento inteiro ou usar um dos editores para criar o seu próprio.

No wsdl acima, definimos uma única mensagem chamada greetings. A mensagem é entregue ao serviço chamadoHelloWorldService que está funcionando em http://localhost:9090/HelloServerPort.

Com isso, passaremos agora ao desenvolvimento do servidor. Antes de desenvolver o servidor, precisamos gerar a interface Apache CXF para nosso serviço web. Isso deve ser feito a partir do wsdl fornecido. Para fazer isso, você usa uma ferramenta chamadawsdl2java.

O plugin wsdl2java

Como usaremos o maven para construir o projeto, você precisará adicionar o seguinte plugin ao pom.xml Arquivo.

<plugins>
   <plugin>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-codegen-plugin</artifactId>
      <version>3.3.0</version>
      <executions>
         <execution>
            <id>generate-sources</id>
            <phase>generate-sources</phase>
            <configuration>
               <wsdlOptions>
                  <wsdlOption>
                     <wsdl>src/main/resources/hello.wsdl</wsdl>
                     <faultSerialVersionUID> 1 </faultSerialVersionUID>
                  </wsdlOption>
               </wsdlOptions>
            </configuration>
            <goals>
               <goal>wsdl2java</goal>
            </goals>
         </execution>
      </executions>
   </plugin>
</plugins>

Observe que especificamos a localização do wsdl arquivo como src/main/resources/Hello.wsdl. Você terá que se certificar de criar uma estrutura de diretório apropriada para o seu projeto e adicionar o mostrado anteriormentehello.wsdl arquivo para a pasta especificada.

o wsdl2javaplugin irá compilar este wsdl e criar classes Apache CXF em uma pasta pré-definida. A estrutura completa do projeto é mostrada aqui para sua referência imediata.

Agora, você está pronto para criar um servidor usando o wsdl2javaclasses geradas. As classes que wsdl2java criou são mostradas na figura abaixo -

Interface de serviço gerada

Na lista de classes geradas, você deve ter notado que uma delas é uma interface Apache CXF - esta é HelloWorldPortType.java. Examine este arquivo em seu editor de código. O conteúdo do arquivo é mostrado aqui para sua referência -

//HelloWorldPortType.java
package com.tutorialspoint.helloworld;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
/**
* This class was generated by Apache CXF 3.3.0
* 2019-02-11T12:05:55.220+05:30
* Generated source version: 3.3.0
*
*/

@WebService(targetNamespace = "http://helloworld.tutorialspoint.com/",
   name = "HelloWorldPortType")
@XmlSeeAlso({ObjectFactory.class})
public interface HelloWorldPortType {
   @WebMethod
   @RequestWrapper(localName = "greetings", targetNamespace =
      "http://helloworld.tutorialspoint.com/", className =
      "com.tutorialspoint.helloworld.Greetings")
      @ResponseWrapper(localName = "greetingsResponse", targetNamespace =
         "http://helloworld.tutorialspoint.com/", className =
         "com.tutorialspoint.helloworld.GreetingsResponse")
   @WebResult(name = "return", targetNamespace =
      "http://helloworld.tutorialspoint.com/")
   public java.lang.String greetings(
      @WebParam(name = "arg0", targetNamespace =
      "http://helloworld.tutorialspoint.com/")
      java.lang.String arg0
   );
}

Observe que a interface contém um método chamado greetings. Este era um tipo de mensagem em nosso wsdl. owsdl2javaferramenta adicionou este método à interface gerada. Agora, você pode entender que quaisquer mensagens que você escrever em seu wsdl, um método correspondente seria gerado na interface.

Agora, sua tarefa seria implementar todos esses métodos correspondentes às várias mensagens que você definiu em seu wsdl. Observe que no exemplo anterior do Apache CXF-First, começamos com uma interface Apache CXF para nosso serviço da web. Nesse caso, a interface Apache CXF é criada a partir do wsdl.

Implementando a Interface de Serviço

A implementação da interface de serviço é trivial. A implementação completa é mostrada na lista abaixo -

//HelloWorldImpl.java
package com.tutorialspoint.helloworld;
public class HelloWorldImpl implements HelloWorldPortType {
   @Override
   public String greetings(String name) {
      return ("hi " + name);
   }
}

O código implementa o único método de interface chamado greetings. O método usa um parâmetro destring tipo, acrescenta uma mensagem "oi" e retorna a string resultante para o chamador.

A seguir, escreveremos o aplicativo do servidor.

Servidor de Desenvolvimento

O desenvolvimento de aplicativos de servidor é mais uma vez trivial. Aqui, usaremos o CXF fornecidoEndpointclasse para publicar nosso serviço. Isso é feito nas duas linhas de código a seguir -

HelloWorldPortType implementor = new HelloWorldImpl();
   Endpoint.publish("http://localhost:9090/HelloServerPort",
      implementor,
      new LoggingFeature());

Primeiro, criamos um objeto de nossa classe de implementador de serviço - HelloWorldImpl. Então, passamos esta referência como um segundo parâmetro para opublishmétodo. O primeiro parâmetro é o endereço no qual o serviço é publicado - os clientes usariam essa URL para acessar o serviço. Toda a fonte do aplicativo de servidor é fornecida aqui -

//Server.java
package com.tutorialspoint.helloworld;
import javax.xml.ws.Endpoint;
import org.apache.cxf.ext.logging.LoggingFeature;
public class Server {
   public static void main(String[] args) throws Exception {
      HelloWorldPortType implementor = new HelloWorldImpl();
      Endpoint.publish("http://localhost:9090/HelloServerPort",
         implementor,
         new LoggingFeature());
      System.out.println("Server ready...");
      Thread.sleep(5 * 60 * 1000);
      System.out.println("Server exiting");
      System.exit(0);
   }
}

Para construir esta classe de servidor, você precisará adicionar um perfil de construção em seu pom.xml. Isso é mostrado abaixo -

<profile>
   <id>server</id>
   <build>
      <defaultGoal>test</defaultGoal>
      <plugins>
         <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.6.0</version>
            <executions>
               <execution>
                  <phase>test</phase>
                  <goals>
                     <goal>java</goal>
                  </goals>
                  <configuration>
                     <mainClass>
                        com.tutorialspoint.helloworld.Server
                     </mainClass>
                  </configuration>
               </execution>
            </executions>
         </plugin>
      </plugins>
   </build>
   <dependencies>
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-transports-http-jetty</artifactId>
         <version>3.3.0</version>
      </dependency>
   </dependencies>
</profile>

Observe que o nome totalmente qualificado do Serverclasse é especificada na configuração. Além disso, a tag de dependência especifica que usaremos o servidor web jetty integrado para implantar nosso aplicativo de servidor.

Implantando Servidor

Finalmente, para implantar o aplicativo do servidor, você precisará fazer mais uma modificação em pom.xml para configurar seu aplicativo como um aplicativo da web. O código que você precisa adicionar ao seupom.xml é dado abaixo -

<defaultGoal>install</defaultGoal>
<pluginManagement>
   <plugins>
      <plugin>
         <artifactId>maven-war-plugin</artifactId>
         <version>3.2.2</version>
         <configuration>
            <webXml>src/main/webapp/WEB-INF/web.xml</webXml>
            <webResources>
               <resource>
                  <directory>src/main/resources</directory>
                  <targetPath>WEB-INF</targetPath>
                  <includes>
                     <include>*.wsdl</include>
                  </includes>
               </resource>
            </webResources>
         </configuration>
      </plugin>
   </plugins>
</pluginManagement>

Antes de implantar o aplicativo, você precisa adicionar mais dois arquivos ao seu projeto. Eles são mostrados na imagem abaixo -

Esses arquivos são arquivos padrão CXF que definem o mapeamento para CXFServlet. O código dentro doweb.xml arquivo é mostrado aqui para sua referência rápida -

//cxf-servlet.xml
<web-app xmlns = "http://java.sun.com/xml/ns/javaee"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" version="2.5"
   xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee
   http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
   <display-name>cxf</display-name>
   <servlet>
      <description>Apache CXF Endpoint</description>
      <display-name>cxf</display-name>
      <servlet-name>cxf</servlet-name>
      <servlet-class>
         org.apache.cxf.transport.servlet.CXFServlet
      </servlet-class>
      <load-on-startup>1</load-on-startup>
   </servlet>
   <servlet-mapping>
      <servlet-name>cxf</servlet-name>
      <url-pattern>/services/*</url-pattern>
   </servlet-mapping>
   <session-config>
      <session-timeout>60</session-timeout>
   </session-config>
</web-app>

No cxf-servlet.xmlvocê declara as propriedades do ponto de extremidade do seu serviço. Isso é mostrado no snippet de código abaixo -

<beans ...>
   <jaxws:endpoint xmlns:helloworld = "http://tutorialspoint.com/"
      id="helloHTTP"
      address = "http://localhost:9090/HelloServerPort"
      serviceName = "helloworld:HelloServiceService"
      endpointName = "helloworld:HelloServicePort">
   </jaxws:endpoint>
</beans>

Aqui, definimos a id do nosso endpoint de serviço, o endereço no qual o serviço estará disponível, o nome do serviço e o nome do endpoint. Agora, você entende como seu serviço é roteado e processado por um servlet CXF.

O pom.xml final

o pom.xmlinclui mais algumas dependências. Em vez de descrever todas as dependências, incluímos a versão final de pom.xml abaixo -

<?xml version="1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.tutorialspoint</groupId>
   <artifactId>cxf-wsdl</artifactId>
   <version>1.0</version>
   <packaging>jar</packaging>
   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <maven.compiler.source>1.8</maven.compiler.source>
      <maven.compiler.target>1.8</maven.compiler.target>
   </properties>
   <build>
      <defaultGoal>install</defaultGoal>
      <pluginManagement>
         <plugins>
            <plugin>
               <artifactId>maven-war-plugin</artifactId>
               <version>3.2.2</version>
               <configuration>
                  <webXml>src/main/webapp/WEB-INF/web.xml</webXml>
                  <webResources>
                     <resource>
                        <directory>src/main/resources</directory>
                        <targetPath>WEB-INF</targetPath>
                        <includes>
                           <include>*.wsdl</include>
                        </includes>
                     </resource>
                  </webResources>
               </configuration>
            </plugin>
         </plugins>
      </pluginManagement>
      <plugins>
         <plugin>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-codegen-plugin</artifactId>
            <version>3.3.0</version>
            <executions>
               <execution>
                  <id>generate-sources</id>
                  <phase>generate-sources</phase>
                  <configuration>
                     <wsdlOptions>
                        <wsdlOption>
                           <wsdl>src/main/resources/Hello.wsdl</wsdl>
                           <faultSerialVersionUID>1</faultSerialVersionUID>
                        </wsdlOption>
                     </wsdlOptions>
                  </configuration>
                  <goals>
                     <goal>wsdl2java</goal>
                  </goals>
               </execution>
            </executions>
         </plugin>
      </plugins>
   </build>
   <profiles>
      <profile>
         <id>server</id>
         <build>
            <defaultGoal>test</defaultGoal>
            <plugins>
               <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>exec-maven-plugin</artifactId>
                  <version>1.6.0</version>
                  <executions>
                     <execution>
                        <phase>test</phase>
                        <goals>
                           <goal>java</goal>
                        </goals>
                        <configuration>
                           <mainClass>
                              com.tutorialspoint.helloworld.Server
                           </mainClass>
                        </configuration>
                     </execution>
                  </executions>
               </plugin>
            </plugins>
         </build>
         <dependencies>
            <dependency>
               <groupId>org.apache.cxf</groupId>
               <artifactId>cxf-rt-transports-http-jetty</artifactId>
               <version>3.3.0</version>
            </dependency>
         </dependencies>
      </profile>
      <profile>
         <id>client</id>
         <build>
            <defaultGoal>test</defaultGoal>
            <plugins>
               <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>exec-maven-plugin</artifactId>
                  <executions>
                     <execution>
                        <phase>test</phase>
                        <goals>
                           <goal>java</goal>
                        </goals>
                        <configuration>
                           <mainClass>
                              com.tutorialspoint.helloworld.Client
                           </mainClass>
                        </configuration>
                     </execution>
                  </executions>
               </plugin>
            </plugins>
         </build>
      </profile>
   </profiles>
   <dependencies>
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-frontend-jaxws</artifactId>
         <version>3.3.0</version>
      </dependency>
     
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-transports-http</artifactId>
         <version>3.3.0</version>
      </dependency>
      
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-management</artifactId>
         <version>3.3.0</version>
      </dependency>
      
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-features-metrics</artifactId>
         <version>3.3.0</version>
      </dependency>
      
      <dependency>
         <groupId>org.apache.cxf.xjc-utils</groupId>
         <artifactId>cxf-xjc-runtime</artifactId>
         <version>3.3.0</version>
      </dependency>
      
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-features-logging</artifactId>
         <version>3.3.0</version>
      </dependency>
     
     <dependency>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>exec-maven-plugin</artifactId>
         <version>1.6.0</version>
      </dependency>
      
      <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>slf4j-api</artifactId>
         <version>1.8.0-beta2</version>
      </dependency>
      
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-transports-http-jetty</artifactId>
         <version>3.3.0</version>
      </dependency>
   </dependencies>
</project>

Observe que também inclui um perfil para construir o cliente que aprenderemos em breve nas próximas seções.

Executando o serviço HelloWorld

Agora você está pronto para executar o aplicativo da web. Na janela de comando, execute o script de construção usando o seguinte comando.

mvn clean install

Isso irá gerar as classes Apache CXF apropriadas de seu wsdl, compilar suas classes Apache CXF, implementar o servidor no servidor jetty integrado e executar seu aplicativo.

Você verá a seguinte mensagem no console -

INFO: Setting the server's publish address to be 
http://localhost:9090/HelloServerPort
Server ready...

Como antes, você pode testar o servidor abrindo a URL do servidor em seu navegador.

Como não especificamos nenhuma operação, apenas uma mensagem de falha é retornada ao navegador por nosso aplicativo. Agora, tente adicionar o?wsdl ao seu URL e você verá a seguinte saída -

Portanto, nosso aplicativo de servidor está funcionando conforme o esperado. Você pode usar o cliente SOAP, comoPostman descrito anteriormente para testar ainda mais seu serviço.

A próxima parte deste tutorial é escrever um cliente que usa nosso serviço.

Cliente em desenvolvimento

Escrever o cliente em um aplicativo CXF é tão importante quanto escrever em um servidor. Aqui está o código completo para o cliente que consiste essencialmente em apenas três linhas, o resto das linhas apenas imprime as informações do serviço para o usuário.

//Client.java
package com.tutorialspoint.helloworld;
public class Client {
   public static void main(String[] args) throws Exception {
      //Create the service client with its default wsdlurl
      HelloWorldService helloServiceService = new HelloWorldService();
      System.out.println("service: " +
         helloServiceService.getServiceName());
      System.out.println("wsdl location: " +
         helloServiceService.getWSDLDocumentLocation());
      HelloWorldPortType helloService =
         helloServiceService.getHelloWorldPort();
      System.out.println(helloService.greetings
      (System.getProperty("user.name")));
   }
}

Aqui, simplesmente criamos uma instância do nosso serviço HelloWorldService, obtenha sua porta chamando getHelloWorldPort método, e então passar nosso greetingsmensagem para ele. Execute o cliente e você verá a seguinte saída -

service: {http://helloworld.tutorialspoint.com/}HelloWorldService
wsdl location: file:/Users/drsarang/Desktop/tutorialpoint/cxf-
wsdl/src/main/resources/Hello.wsdl
hi drsarang

Até agora, você aprendeu como usar o CXF com as arquiteturas Apache CXF-First e WSDL-First. Na abordagem Apache CXF-First, você usou um POJO comServerFactoryBeanclasse de bibliotecas CXF para criar um servidor. Para criar um cliente que você usouClientProxyFactoryBeanclasse da biblioteca CXF. Na abordagem WSDL-First, você usouEndpointclasse para publicar o serviço na URL desejada e um implementador especificado. Agora você pode estender essas técnicas para integrar diferentes protocolos e transportes.