Apache CXF con WSDL First

L'applicazione CXF-POJO che avete sviluppato risulta in un accoppiamento molto stretto tra il client e il server. Anche fornire un accesso diretto all'interfaccia del servizio può rappresentare gravi minacce alla sicurezza. Pertanto, di solito si desidera il disaccoppiamento tra il client e il server, che si ottiene utilizzando WSDL (Web Services Description Language).

Scriviamo l'interfaccia del servizio web in un documento WSDL che è basato su XML. Useremo uno strumento per mappare questo WSDL alle interfacce Apache CXF che vengono poi implementate e utilizzate dalle nostre applicazioni client e server. Per fornire il disaccoppiamento, è preferibile iniziare con un WSDL. Per questo, devi prima imparare una nuova lingua: WSDL. La scrittura di WSDL richiede un approccio attento e sarebbe meglio se tu potessi acquisire una certa comprensione su questo prima di iniziare a lavorarci.

In questa lezione, inizieremo definendo un'interfaccia del servizio Web in un documento WSDL. Impareremo come utilizzare CXF per creare applicazioni server e client a partire da WSDL. Manterremo l'applicazione semplice per mantenere l'attenzione sull'uso di CXF. Dopo che l'applicazione server è stata creata, la pubblicheremo sull'URL desiderato utilizzando una classe CXF incorporata.

Innanzitutto, descriviamo il WSDL che utilizzeremo.

WSDL per HelloWorld

Il servizio web che implementeremo avrà un unico metodo web chiamato greetings che accetta a stringparametro contenente il nome utente e restituisce un messaggio di stringa al chiamante dopo aver aggiunto un messaggio di saluto al nome utente. Il wsdl completo è mostrato di seguito:

//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>

Notare che scrivere un wsdl sintatticamente corretto è sempre stata una sfida per gli sviluppatori; ci sono molti strumenti e sono disponibili editor online per creare un wsdl. Questi editor richiedono i nomi dei messaggi che si desidera implementare insieme ai parametri che si desidera passare in un messaggio e il tipo di messaggio di ritorno che si desidera ricevere dall'applicazione client. Se conosci la sintassi wsdl, puoi codificare manualmente l'intero documento o utilizzare uno degli editor per crearne uno tuo.

Nel wsdl sopra, abbiamo definito un singolo messaggio chiamato greetings. Il messaggio viene consegnato al servizio chiamatoHelloWorldService che sta funzionando a http://localhost:9090/HelloServerPort.

Con questo, procederemo ora allo sviluppo del server. Prima di sviluppare il server, dobbiamo generare l'interfaccia Apache CXF per il nostro servizio web. Questo deve essere fatto dal dato wsdl. Per fare ciò, usi uno strumento chiamatowsdl2java.

Il plugin wsdl2java

Poiché utilizzeremo Maven per creare il progetto, sarà necessario aggiungere il seguente plug-in al file pom.xml file.

<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>

Nota che specifichiamo la posizione del file wsdl file come src/main/resources/Hello.wsdl. Dovrai assicurarti di creare una struttura di directory appropriata per il tuo progetto e aggiungere quella mostrata in precedenzahello.wsdl file nella cartella specificata.

Il wsdl2javaplugin compilerà questo wsdl e creerà classi CXF di Apache in una cartella predefinita. La struttura completa del progetto è mostrata qui come riferimento immediato.

Ora sei pronto per creare un server utilizzando il wsdl2javaclassi generate. Le classi create da wsdl2java sono mostrate nella figura seguente:

Interfaccia di servizio generata

Nell'elenco delle classi generate, devi aver notato che una di esse è un'interfaccia CXF di Apache: questa è HelloWorldPortType.java. Esamina questo file nel tuo editor di codice. Il contenuto del file viene mostrato qui come riferimento pronto -

//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
   );
}

Notare che l'interfaccia contiene un metodo chiamato greetings. Questo era un tipo di messaggio nel nostro wsdl. Ilwsdl2javalo strumento ha aggiunto questo metodo all'interfaccia generata. Ora puoi capire che qualunque messaggio tu scrivi nel tuo wsdl, un metodo corrispondente verrà generato nell'interfaccia.

Ora, il tuo compito sarebbe implementare tutti questi metodi corrispondenti ai vari messaggi che hai definito nel tuo wsdl. Si noti che nel precedente esempio di Apache CXF-First, abbiamo iniziato con un'interfaccia Apache CXF per il nostro servizio web. In questo caso, l'interfaccia di Apache CXF viene creata da wsdl.

Implementazione dell'interfaccia del servizio

L'implementazione dell'interfaccia del servizio è banale. L'implementazione completa è mostrata nell'elenco seguente:

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

Il codice implementa l'unico metodo di interfaccia chiamato greetings. Il metodo accetta un parametro distring type, antepone un messaggio "hi" e restituisce la stringa risultante al chiamante.

Successivamente, scriveremo l'applicazione server.

Server di sviluppo

Lo sviluppo di applicazioni server è ancora una volta banale. Qui useremo il CXF fornitoEndpointclasse per pubblicare il nostro servizio. Questo viene fatto nelle seguenti due righe di codice:

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

Per prima cosa, creiamo un oggetto della nostra classe di implementazione del servizio - HelloWorldImpl. Quindi, passiamo questo riferimento come secondo parametro al filepublishmetodo. Il primo parametro è l'indirizzo in cui è pubblicato il servizio: i client utilizzerebbero questo URL per accedere al servizio. L'intera fonte per l'applicazione server è fornita qui:

//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);
   }
}

Per costruire questa classe server dovrai aggiungere un profilo di build nel tuo file pom.xml. Questo è mostrato di seguito -

<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>

Notare che il nome completo di Serverclass è specificato nella configurazione. Inoltre, il tag di dipendenza specifica che utilizzeremo il server web jetty incorporato per distribuire la nostra applicazione server.

Distribuzione del server

Infine, per distribuire l'applicazione server, dovrai apportare un'altra modifica in pom.xml per configurare la tua applicazione come applicazione web. Il codice che devi aggiungere al tuo filepom.xml è dato di seguito -

<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>

Prima di distribuire l'applicazione, è necessario aggiungere altri due file al progetto. Questi sono mostrati nello screenshot qui sotto:

Questi file sono file standard CXF che definiscono la mappatura per CXFServlet. Il codice all'interno diweb.xml file è mostrato qui per una rapida consultazione -

//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>

Nel cxf-servlet.xmldichiari le proprietà per l'endpoint del tuo servizio. Questo è mostrato nello snippet di codice di seguito:

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

Qui definiamo l'id per il nostro endpoint di servizio, l'indirizzo su cui il servizio sarà disponibile, il nome del servizio e il nome dell'endpoint. Ora capisci come il tuo servizio viene instradato ed elaborato da un servlet CXF.

The Final pom.xml

Il pom.xmlinclude alcune dipendenze in più. Piuttosto che descrivere tutte le dipendenze, abbiamo incluso la versione finale di pom.xml di seguito -

<?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>

Si noti che include anche un profilo per la creazione del cliente che impareremo presto nelle sezioni successive.

Esecuzione del servizio HelloWorld

Ora sei pronto per eseguire l'app Web. Nella finestra dei comandi, esegui lo script di compilazione utilizzando il seguente comando.

mvn clean install

Questo genererà le classi Apache CXF appropriate dal tuo wsdl, compilerà le tue classi Apache CXF, distribuirà il server sul jetty server incorporato ed eseguirà la tua applicazione.

Vedrai il seguente messaggio sulla console:

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

Come prima, puoi testare il server aprendo l'URL del server nel tuo browser.

Poiché non abbiamo specificato alcuna operazione, viene restituito al browser solo un messaggio di errore dalla nostra applicazione. Ora prova ad aggiungere il file?wsdl al tuo URL e vedrai il seguente output:

Quindi la nostra applicazione server funziona come previsto. È possibile utilizzare il client SOAP comePostman descritto in precedenza per testare ulteriormente il servizio.

La parte successiva di questo tutorial consiste nello scrivere un client che utilizza il nostro servizio.

Cliente in via di sviluppo

La scrittura del client in un'applicazione CXF è importante quanto la scrittura di un server. Ecco il codice completo per il client che consiste essenzialmente di sole tre righe, il resto delle righe stampa solo le informazioni sul servizio all'utente.

//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")));
   }
}

Qui creiamo semplicemente un'istanza del nostro servizio HelloWorldService, ottieni il suo porto chiamando getHelloWorldPort metodo, quindi passa il nostro greetingsmessaggio ad esso. Esegui il client e vedrai il seguente output:

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

Finora hai imparato come utilizzare CXF con le architetture Apache CXF-First e WSDL-First. Nell'approccio Apache CXF-First, hai utilizzato un POJO conServerFactoryBeanclass dalle librerie CXF per creare un server. Per creare un cliente che hai usatoClientProxyFactoryBeanclasse dalla libreria CXF. Nell'approccio WSDL-First, hai utilizzatoEndpointclass per pubblicare il servizio all'URL desiderato e un implementatore specificato. È ora possibile estendere queste tecniche per integrare diversi protocolli e trasporti.