The CXF-POJO application that you have developed results in a very tight coupling between the client and the server. Giving a direct access to the service interface can also pose severe security threats. Thus, decoupling between the client and the server is usually desired, which is achieved by using WSDL (Web Services Description Language).
We write the web service interface in a WSDL document which is XML-based. We will use a tool to map this WSDL to Apache CXF interfaces which are then implemented and used by our client and server applications. For providing decoupling, starting with a WSDL is a preferred way. For this, you need to first learn a new language - WSDL. Writing WSDL needs a careful approach and it would be better if you can gain some understanding on this before you start working on it.
In this lesson, we will start by defining a web service interface in a WSDL document. We will learn how to use CXF to create both server and client applications starting with WSDL. We will keep the application simple to maintain focus on the use of CXF. After the server application is created we will publish it to a desired URL using a built-in CXF class.
First, let us describe the WSDL that we are going to use.
The webservice that we are going to implement will have one single webmethod called greetings that accepts a string parameter holding the user name and returns a string message to the caller after appending a greetings message to the user name. The complete wsdl is shown below −
//Hello.wsdl <?xml version = "1.0" encoding = "UTF-8"?> <wsdl:definitions xmlns:soap = "http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns = "http://helloworld.howcodex.com/" xmlns:wsdl = "http://schemas.xmlsoap.org/wsdl/" xmlns:xsd = "http://www.w3.org/2001/XMLSchema" name = "HelloWorld" targetNamespace = "http://helloworld.howcodex.com/"> <wsdl:types> <xsd:schema attributeFormDefault = "unqualified" elementFormDefault = "qualified" targetNamespace = "http://helloworld.howcodex.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>
Note that writing a syntactically correct wsdl has always been a challenge to the developers; there are many tools and online editors are available for creating a wsdl. These editors ask for the names of messages that you want to implement along with the parameters that you wish to pass in a message and the type of return message that you want your client application to receive. If you know wsdl syntax, you may hand code the entire document or use one of the editors to create your own.
In the above wsdl, we have defined a single message called greetings. The message is delivered to the service called HelloWorldService that is running at http://localhost:9090/HelloServerPort.
With this, we will now proceed to server development. Before developing the server, we need to generate Apache CXF interface to our web service. This is to be done from the given wsdl. To do this, you use a tool called wsdl2java.
As we will be using maven to build the project, you will need to add the following plugin to the 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>
Note that we specify the location of the wsdl file as src/main/resources/Hello.wsdl. You will have to make sure that you create an appropriate directory structure for your project and add the earlier shown hello.wsdl file to the specified folder.
The wsdl2java plugin will compile this wsdl and create Apache CXF classes in a pre-defined folder. The full project structure is shown here for your ready reference.
Now, you are ready to create a server using the wsdl2java generated classes. The classes that wsdl2java has created is shown in the figure below −
In the list of generated classes, you must have noticed one of them is a Apache CXF interface - this is HelloWorldPortType.java. Examine this file in your code editor. The file contents are shown here for your ready reference −
//HelloWorldPortType.java package com.howcodex.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.howcodex.com/", name = "HelloWorldPortType") @XmlSeeAlso({ObjectFactory.class}) public interface HelloWorldPortType { @WebMethod @RequestWrapper(localName = "greetings", targetNamespace = "http://helloworld.howcodex.com/", className = "com.howcodex.helloworld.Greetings") @ResponseWrapper(localName = "greetingsResponse", targetNamespace = "http://helloworld.howcodex.com/", className = "com.howcodex.helloworld.GreetingsResponse") @WebResult(name = "return", targetNamespace = "http://helloworld.howcodex.com/") public java.lang.String greetings( @WebParam(name = "arg0", targetNamespace = "http://helloworld.howcodex.com/") java.lang.String arg0 ); }
Note that the interface contains a method called greetings. This was a message type in our wsdl. The wsdl2java tool has added this method to the generated interface. Now, you can understand that whatever messages you write in your wsdl, a corresponding method would be generated in the interface.
Now, your task would be to implement all these methods corresponding to the various messages that you have defined in your wsdl. Note that in the earlier example of Apache CXF-First, we started out with a Apache CXF interface for our web service. In this case, the Apache CXF interface is created from wsdl.
The implementation of service interface is trivial. The full implementation is shown in the listing below −
//HelloWorldImpl.java package com.howcodex.helloworld; public class HelloWorldImpl implements HelloWorldPortType { @Override public String greetings(String name) { return ("hi " + name); } }
The code implements the sole interface method called greetings. The method takes one parameter of string type, prepends a "hi" message to it and returns the resultant string to the caller.
Next, we will write the server application.
Developing server application is once again trivial. Here, we will use the CXF supplied Endpoint class to publish our service. This is done in the following two lines of code −
HelloWorldPortType implementor = new HelloWorldImpl(); Endpoint.publish("http://localhost:9090/HelloServerPort", implementor, new LoggingFeature());
First, we create an object of our service implementor class - HelloWorldImpl. Then, we pass this reference as a second parameter to the publish method. The first parameter is the address to which the service is published - the clients would use this URL to access the service. The entire source for the server application is given here −
//Server.java package com.howcodex.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); } }
To build this server class you will need to add a build profile in your pom.xml. This is shown below −
<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.howcodex.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>
Note that the fully qualified name of the Server class is specified in the configuration. Also, the dependency tag specifies that we will be using the embedded jetty web server to deploy our server application.
Finally, to deploy the server application, you will need to make one more modification in pom.xml to setup your application as a web application. The code that you need to add into your pom.xml is given below −
<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>
Before you deploy the application, you need to add two more files to your project. These are shown in the screenshot below −
These files are CXF standard files which define the mapping for CXFServlet. The code within the web.xml file is shown here for your quick reference −
//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>
In the cxf-servlet.xml you declare the properties for your service's endpoint. This is shown in the code snippet below −
<beans ...> <jaxws:endpoint xmlns:helloworld = "http://howcodex.com/" id="helloHTTP" address = "http://localhost:9090/HelloServerPort" serviceName = "helloworld:HelloServiceService" endpointName = "helloworld:HelloServicePort"> </jaxws:endpoint> </beans>
Here we define the id for our service endpoint, the address on which the service will be available, the service name and the endpoint name. Now, you understand how your service gets routed and processed by a CXF servlet.
The pom.xml includes a few more dependencies. Rather than describing all the dependencies, we have included the final version of pom.xml below −
<?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.howcodex</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.howcodex.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.howcodex.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>
Note that it also includes a profile for building client that we will be learning soon in the later sections.
Now, you are ready to run the web app. In the command window, run the build script using the following command.
mvn clean install
This will generate the appropriate Apache CXF classes from your wsdl, compile your Apache CXF classes, deploy the server on the embedded jetty server and run your application.
You will see the following message on the console −
INFO: Setting the server's publish address to be http://localhost:9090/HelloServerPort Server ready...
As before, you can test the server by opening the server URL in your browser.
As we did not specify any operation, only a fault message is returned to the browser by our application. Now, try adding the ?wsdl to your URL and you will see the following output −
So our server application is running as expected. You may use the SOAP Client such as Postman described earlier to further test your service.
The next part of this tutorial is to write a client that uses our service.
Writing the client in a CXF application is as important as writing a server. Here is the complete code for the client that essentially consists of only three lines, the rest of the lines just print the service information to the user.
//Client.java package com.howcodex.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"))); } }
Here, we simply create an instance of our service HelloWorldService, get its port by calling getHelloWorldPort method, and then pass our greetings message to it. Run the client and you will see the following output −
service: {http://helloworld.howcodex.com/}HelloWorldService wsdl location: file:/Users/drsarang/Desktop/tutorialpoint/cxf- wsdl/src/main/resources/Hello.wsdl hi drsarang
So far you have learned how to use CXF with Apache CXF-First and WSDL-First architectures. In the Apache CXF-First approach, you used a POJO with ServerFactoryBean class from CXF libraries to create a server. To create a client you used ClientProxyFactoryBean class from CXF library. In the WSDL-First approach, you used Endpoint class to publish the service at the desired URL and a specified implementor. You can now extend these techniques to integrate different protocols and transports.