JAX-WS Tutorial for Beginners
Java API for XML Web Services (JAX-WS) is a Java programming language for creating web services, particularly SOAP services. In this tutorial, we will build a multi-module Maven project to demonstrate how to create, deploy, consume, and test JAX-WS web services.
Table Of Contents
1. Introduction
Java API for XML Web Services (JAX-WS) is a Java programming language for creating web services, particularly SOAP services. JAX-WS 2.0 specification was introduced in 2005 and has been part of JDK since JDK6. JAX-WS uses annotations to simplify the development of web services.
JAX-WS provides the javax.jws.WebService
annotation to define a class as a web service. A service endpoint interface (SEI) is an interface which defines the web methods that clients can invoke. An SEI is not required when building a JAX-WS endpoint. There are a few rules when developing JAX-WS web services:
- The implementation class of SEI must be annotated with either
javax.jws.WebService
orjavax.jws.WebServiceProvider
- The implementation class of SEI must have a default public constructor
- The methods of the implementation class of SEI must be public and must not be declared with
static
,abstract
, orfinal
- The methods must be annotated with
javax.jws.WebMethod
if clients need to invoke it
In this example, I will demonstrate how to:
- Create a web service using a Bottom-up approach
- Create a web service using a Top-down approach
- Deploy a web service
- Consume a web service
2. A Brief History of Web Services
Web services evolved from the Remote Procedure Call (RPC) mechanism in the Distributed Computing Environment (DCE) in the early 1990s. Common Object Request Broker Architecture (CORBA) and Microsoft’s Distributed COM are first-generation frameworks for DCE. Java Remote Method Invocation and Microsoft’s DotNet are second-generation frameworks for DCE. In 1998, Dave Winer of the UserLand Software developed XML-RPC, a technology innovation that marked the birth of web service. XML-RPC has two key features:
- Use of XML marshalling/unmarshalling to achieve language neutrality
- Reliance on HTTP for a transport layer
3. Technologies Used
The example code in this article was built and run using:
- Java 1.8.101
- Maven 3.3.9
- Eclipse Oxygen
- Tomcat 8.0.36
- SoapUI 5.2.1
- Cucumber 2.3.1
4. Maven Project
Apache Maven is a software project management and comprehension tool. It manages a project’s build, reporting, and documentation from a project object model (POM) file. In this step, I will create a jax-ws-tutorial
Maven project with five modules:
Module | Description | Note |
---|---|---|
bottom-up | Create and publish a web service with the Bottom-up approach. | |
employeeWsdlToJava | Use wsimport to generate Java stubs from employee.wsdl. | |
top-down | Create and publish a web service with the Top-down approach. | depends on employeeWsdlToJava |
tomcat-server | Create and publish a web service in tomcat container. | depends on employeeWsdlToJava |
soapcClient | Use wsimport to generate Java stubs from web services. |
- Launch
Eclipse
and clickFile
->New
->Maven Project
. - At the pop-up window, select the
POM
package option, then enter the group Id:jcg.zheng.demo
and artifact Id:jax-ws-tutorial
, then click.Finish
. - You will see the Maven project:
jax-ws-tutorial
is created. - Click
File
->New
->Maven Module
. - At the pop-up windows, enter the module name:
employeeWsdlToJava
, then clickFinish
. - You will see the Maven module:
employeeWsdlToJava
is created as a child ofjax-ws-tutorial
. - Repeat steps 4 and 5 for
bottom-up
,top-down
,tomcat-server
, andsoapClient
.
4.1 Parent Project
The generated parent POM.xml
includes five modules.
Pom.xml
<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>jcg.zheng.demo</groupId> <artifactId>jax-ws-tutorial</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>employeeWsdlToJava</module> <module>top-down</module> <module>bottom-up</module> <module>tomcat-server</module> <module>soapClient</module> </modules> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.5.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
5. Generated Stubs from Employee WSDL
JAX-WS provides WSDL-to-Java and Java-to-WSDL mapping tools. In this step, I will use wsimport
to generate Java stubs from the employee.wsdl
file. The generated source codes are saved into the src/main/java
folder. Here is a quick guide on the mapping:
WSDL | Java |
---|---|
namespace | package |
portType | SEI |
operation | method |
input | parameter |
output | return |
xsd:complextType | Java beans |
pom.xml
<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> <parent> <groupId>jcg.zheng.demo</groupId> <artifactId>jax-ws-tutorial</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>employeeWsdlToJava</artifactId> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>jaxws-maven-plugin</artifactId> <version>2.5</version> <executions> <execution> <id>employee_wsdl</id> <configuration> <wsdlDirectory>${basedir}/src/main/resources/wsdl</wsdlDirectory> <wsdlUrls> <wsdlUrl>${basedir}/src/main/resources/wsdl/employeeService.wsdl</wsdlUrl> </wsdlUrls> <packageName>jcg.zheng.demo.service.employee.generated</packageName> <keep>true</keep> <sourceDestDir>${basedir}/src/main/java</sourceDestDir> </configuration> <goals> <goal>wsimport</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
JAX-WS uses JAXB internally to convert Java objects to and from XMLs. Click here to learn more.
Generated Java Files
C:\gitworkspace\jax-ws-tutorial\employeeWsdlToJava\src\main\java\jcg\zheng\demo\service\employee\generated>dir Volume in drive C is OSDisk Volume Serial Number is 3A10-C6D4 Directory of C:\gitworkspace\jax-ws-tutorial\employeeWsdlToJava\src\main\java\jcg\zheng\demo\service\employee\generated 08/31/2018 03:57 PM <DIR> . 08/31/2018 03:57 PM <DIR> .. 08/31/2018 03:57 PM 1,822 EmployeeIdWrapper.java 08/31/2018 03:57 PM 4,116 EmployeeInfo.java 08/31/2018 03:57 PM 1,946 EmployeeInfoWrapper.java 08/31/2018 03:57 PM 1,287 EmployeeLookupService.java 08/31/2018 03:57 PM 3,492 EmployeeLookupService_Service.java 08/31/2018 03:57 PM 1,206 EmployeeType.java 08/31/2018 03:57 PM 3,524 ObjectFactory.java 08/31/2018 03:57 PM 205 package-info.java 08/31/2018 03:57 PM 1,207 UserDefinedException.java 08/31/2018 03:57 PM 1,438 UserDefinedFault.java 10 File(s) 20,243 bytes 2 Dir(s) 21,524,041,728 bytes free C:\gitworkspace\jax-ws-tutorial\employeeWsdlToJava\src\main\java\jcg\zheng\demo\service\employee\generated>
6. Bottom-Up
With a Bottom-up approach, developers start programming the classes and business logic as Java codes and then generate the WSDL from it. It is easy to develop but hard to maintain as every change at the Java classes will alter the WSDL file. Developers can use a wsgen
command to generate WSDL without publishing the web services.
In this step, I will create a HelloworldService
class, annotate it with @WebService
, and use wsgen
to generate WSDL.
6.1 Pom.xml
No additional dependency is needed in the pom.xml
.
pom.xml
<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> <parent> <groupId>jcg.zheng.demo</groupId> <artifactId>jax-ws-tutorial</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>bottom-up</artifactId> <packaging>jar</packaging> </project>
6.2 HelloWorldService
I will annotate the HelloWorldServiceImpl
with @javax.jws.WebService
and @javax.jws.WebMethod
.
HelloWorldServiceImpl.java
package jcg.zheng.demo.service.impl; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; import jcg.zheng.demo.model.TopNews; @WebService public class HelloWorldServiceImpl { @WebMethod public String hello(@WebParam(name = "name") String name) { return "Hello " + name + "!"; } @WebMethod public TopNews getTopNews() { TopNews tn = new TopNews(); tn.setHighlights("Mary Zheng published an example for Jax-ws tutorial."); tn.setTitle("Jax-WS Tutorial is Available"); return tn; } }
The TopNews
domain model is returned from getTopNews
, so annotate it with @XmlRootElement
.
TopNews.java
package jcg.zheng.demo.model; import java.io.Serializable; import java.util.Date; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class TopNews implements Serializable { private static final long serialVersionUID = -7846083786214239318L; private String title; private String highlights; private Date newsDate; public TopNews() { super(); } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getHighlights() { return highlights; } public void setHighlights(String highlights) { this.highlights = highlights; } public Date getNewsDate() { return newsDate; } public void setNewsDate(Date newsDate) { this.newsDate = newsDate; } }
Note: Execute wsgen -verbose -keep -cp . jcg.zheng.demo.service.impl.HelloWorldServiceImpl -wsdl
to generate WSDL.
6.3 Publish Web Service
I will create a ServerApp
to invoke javax.xml.ws.Endpopint
for publishing the HelloWorldServiceImpl
service to http://localhost:9980/helloworld
. Start it as a Java application.
ServerApp.java
package jcg.zheng.demo.app; import javax.xml.ws.Endpoint; import jcg.zheng.demo.service.impl.HelloWorldServiceImpl; public class ServerApp { public static void main(String[] args) { Endpoint.publish("http://localhost:9980/helloworld", new HelloWorldServiceImpl()); System.out.println("HelloworldServices Started!"); } }
6.4 Demo with SoapUI
SoapUI is a great tool for testing web services. Click here to download it. We started the web services in step 6.3. In this step, I will create a new SOAP project:
- Click
File
->New SOAP Project
- Enter the Initial WSDL:
http://localhost:9980/helloworld?wsdl
and ClickOK
- Expand the newly created project, then click
employeeLookup
, andRequest 1
- A SOAP message is populated, replace
?
with the test data - Submit the request
- A response XML is returned.
SoapUI Input
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:impl="http://impl.service.demo.zheng.jcg/"> <soapenv:Header/> <soapenv:Body> <impl:hello> <!--Optional:--> <name>Mary</name> </impl:hello> </soapenv:Body> </soapenv:Envelope>
SoapUI Output
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Body> <ns2:helloResponse xmlns:ns2="http://impl.service.demo.zheng.jcg/"> <return>Hello Mary!</return> </ns2:helloResponse> </S:Body> </S:Envelope>
7. Top-Down
With a Top-down approach, developers start with the WSDL file and generate the Java Stubs with wsimport
. In this step, I will create EmployeeLookupServiceImpl_WSDL
from the generated stubs in step 5.
7.1 Pom.xml
I will include employeeWsdlToJava
as a dependency.
pom.xml
<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> <parent> <groupId>jcg.zheng.demo</groupId> <artifactId>jax-ws-tutorial</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>top-down</artifactId> <dependencies> <dependency> <groupId>jcg.zheng.demo</groupId> <artifactId>employeeWsdlToJava</artifactId> <version>${project.version}</version> </dependency> </dependencies> </project>
7.2 EmployeeLookupService
I will create an implementation class: EmployeeLookupServiceImpl_WSDL
which implements EmployeeLookupService
and annotates it with @WebService
and sets the endpointInterface
value as jcg.zheng.demo.service.employee.generated.EmployeeLookupService
.
EmployeeLookupServiceImpl_WSDL .java
package jcg.zheng.demo.service.employee.impl; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.jws.WebService; import jcg.zheng.demo.service.employee.generated.EmployeeIdWrapper; import jcg.zheng.demo.service.employee.generated.EmployeeInfo; import jcg.zheng.demo.service.employee.generated.EmployeeInfoWrapper; import jcg.zheng.demo.service.employee.generated.EmployeeLookupService; import jcg.zheng.demo.service.employee.generated.EmployeeType; import jcg.zheng.demo.service.employee.generated.ObjectFactory; import jcg.zheng.demo.service.employee.generated.UserDefinedException; import jcg.zheng.demo.service.employee.generated.UserDefinedFault; @WebService(endpointInterface = "jcg.zheng.demo.service.employee.generated.EmployeeLookupService") public class EmployeeLookupServiceImpl_WSDL implements EmployeeLookupService { ObjectFactory of = new ObjectFactory(); @Override public EmployeeInfoWrapper employeeLookup(EmployeeIdWrapper employeeIdList) throws UserDefinedException { if (employeeIdList.getEid().isEmpty()) { UserDefinedFault e = of.createUserDefinedFault(); e.setMessage("Empty Employee ID"); throw new UserDefinedException("Please enter at lease one employee Id", e); } EmployeeInfoWrapper eWrapper = of.createEmployeeInfoWrapper(); List allEmps = getEmployees(employeeIdList.getEid()); eWrapper.getEmployeeInfo().addAll(allEmps); return eWrapper; } private List getEmployees(List ids) { List emps = new ArrayList<>(); for (String id : ids) { emps.add(buildDummyEmployee(id, EmployeeType.HOURLY)); } return emps; } private EmployeeInfo buildDummyEmployee(String id, EmployeeType type) { EmployeeInfo emp = of.createEmployeeInfo(); emp.setEid(id); Random rand = new Random(); emp.setFirstName("FName_" + id); emp.setLastName("LName_" + id); emp.setType(type); emp.setHourlyRate(new BigDecimal(rand.nextInt(40))); return emp; } }
7.3 Publish Web Service
JAX-WS Endpoint
class’s publish
method is used to deploy the services to the address. In this step, we will publish EmployeeLookupServiceImpl_WSDL
at http://localhost:9990/EmployeeLookupService
.
ServerApp.java
package jcg.zheng.demo.service.employee.impl; import javax.xml.ws.Endpoint; public class ServerApp { public static void main(String[] args) { Endpoint.publish("http://localhost:9990/EmployeeLookupService", new EmployeeLookupServiceImpl_WSDL()); System.out.println("EmployeeLookupService Started!"); } }
7.4 Demo with SoapUI
Repeat step 6.4 for http://localhost:9990/EmployeeLookupService?wsdl
and capture the SoapUI input and output:
Soap Request
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:emp="http://bestpay.payroll/employee"> <soapenv:Header/> <soapenv:Body> <emp:EmployeeIdList> <!--Zero or more repetitions:--> <emp:eid>12</emp:eid> </emp:EmployeeIdList> </soapenv:Body> </soapenv:Envelope>
Soap Response
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Body> <EmployeeInfoList xmlns="http://bestpay.payroll/employee"> <employeeInfo> <eid>12</eid> <firstName>FName_12</firstName> <lastName>LName_12</lastName> <hourlyRate>33</hourlyRate> <type>Hourly</type> </employeeInfo> </EmployeeInfoList> </S:Body> </S:Envelope>
8. Tomcat Server
In this step, I will deploy the JAX-WS web service into a Tomcat container.
8.1 Pom.xml
There are several dependencies must be included in order to deploy the web services into a tomcat container.
Pom.xml
<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> <parent> <groupId>jcg.zheng.demo</groupId> <artifactId>jax-ws-tutorial</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>tomcat-server</artifactId> <dependencies> <dependency> <groupId>jcg.zheng.demo</groupId> <artifactId>employeeWsdlToJava</artifactId> <version>${project.version}</version> </dependency> <!-- jax-ws maven dependency --> <dependency> <groupId>com.sun.xml.ws</groupId> <artifactId>jaxws-rt</artifactId> <version>2.2.8</version> </dependency> <!-- servlet provided by tomcat --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/com.sun.xml.bind/jaxb-core --> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-core</artifactId> <version>2.2.7</version> </dependency> <dependency> <groupId>com.sun.xml.stream.buffer</groupId> <artifactId>streambuffer</artifactId> <version>1.5.3</version> </dependency> <!-- https://mvnrepository.com/artifact/com.sun.xml.bind/jaxb-impl --> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.2.7</version> </dependency> <!-- https://mvnrepository.com/artifact/com.sun.xml.ws/policy --> <dependency> <groupId>com.sun.xml.ws</groupId> <artifactId>policy</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>org.glassfish.gmbal</groupId> <artifactId>gmbal-api-only</artifactId> <version>3.2.0-b003</version> </dependency> <!-- https://mvnrepository.com/artifact/org.glassfish.ha/ha-api --> <dependency> <groupId>org.glassfish.ha</groupId> <artifactId>ha-api</artifactId> <version>3.1.9</version> </dependency> </dependencies> <packaging>war</packaging> </project>
8.2 EmployeeService
I will create an EmployeeServiceImpl
and annotate it with @WebService
. This step is the same as step 7.2.
EmployeeServiceImpl.java
package jcg.zheng.demo.service; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.jws.WebService; import jcg.zheng.demo.service.employee.generated.EmployeeIdWrapper; import jcg.zheng.demo.service.employee.generated.EmployeeInfo; import jcg.zheng.demo.service.employee.generated.EmployeeInfoWrapper; import jcg.zheng.demo.service.employee.generated.EmployeeLookupService; import jcg.zheng.demo.service.employee.generated.EmployeeType; import jcg.zheng.demo.service.employee.generated.ObjectFactory; import jcg.zheng.demo.service.employee.generated.UserDefinedException; import jcg.zheng.demo.service.employee.generated.UserDefinedFault; @WebService(endpointInterface = "jcg.zheng.demo.service.employee.generated.EmployeeLookupService") public class EmployeeServiceImpl implements EmployeeLookupService { ObjectFactory of = new ObjectFactory(); @Override public EmployeeInfoWrapper employeeLookup(EmployeeIdWrapper employeeIdList) throws UserDefinedException { if (employeeIdList.getEid().isEmpty()) { UserDefinedFault e = of.createUserDefinedFault(); e.setMessage("Empty Employee ID"); throw new UserDefinedException("Please enter at lease one employee Id", e); } EmployeeInfoWrapper eWrapper = of.createEmployeeInfoWrapper(); List allEmps = getEmployees(employeeIdList.getEid()); eWrapper.getEmployeeInfo().addAll(allEmps); return eWrapper; } private List getEmployees(List ids) { List emps = new ArrayList(); for (String id : ids) { emps.add(buildDummyEmployee(id, EmployeeType.HOURLY)); } return emps; } private EmployeeInfo buildDummyEmployee(String id, EmployeeType type) { EmployeeInfo emp = of.createEmployeeInfo(); emp.setEid(id); Random rand = new Random(); emp.setFirstName("FName_" + id); emp.setLastName("LName_" + id); emp.setType(type); emp.setHourlyRate(new BigDecimal(rand.nextInt(40))); return emp; } }
8.3 Web Configuration
In this step, I will configure both sun-jaxws.xml
and web.xml
.
sun-jaxws.xml
<?xml version="1.0" encoding="UTF-8"?> <endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0"> <endpoint name="EmployeeServiceImpl" implementation="jcg.zheng.demo.service.EmployeeServiceImpl" url-pattern="/employeeWS" /> </endpoints>
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <listener> <listener-class> com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class> </listener> <servlet> <servlet-name>JAXWSServlet</servlet-name> <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>JAXWSServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> <session-config> <session-timeout>30</session-timeout> </session-config> </web-app>
8.4 Start the Web Services
Deploy tomcat-server-0.0.1-SNAPSHOT.war
to a tomcat server. Start the tomcat instance and verify EmployeeService
is up by browsing to http://localhost:8080/tomcat-server/employeeWS?wsdl
.
Tomcat Server Log
Aug 31, 2018 8:44:53 PM org.apache.jasper.servlet.TldScanner scanJars INFO: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time. Aug 31, 2018 8:44:55 PM com.sun.xml.ws.transport.http.servlet.WSServletDelegate INFO: WSSERVLET14: JAX-WS servlet initializing Aug 31, 2018 8:44:55 PM com.sun.xml.ws.transport.http.servlet.WSServletContextListener contextInitialized INFO: WSSERVLET12: JAX-WS context listener initializing Aug 31, 2018 8:44:55 PM com.sun.xml.ws.transport.http.servlet.WSServletContextListener contextInitialized INFO: WSSERVLET12: JAX-WS context listener initializing Aug 31, 2018 8:44:55 PM org.apache.coyote.AbstractProtocol start INFO: Starting ProtocolHandler [http-nio-8080] Aug 31, 2018 8:44:55 PM org.apache.coyote.AbstractProtocol start INFO: Starting ProtocolHandler [ajp-nio-8009] Aug 31, 2018 8:44:55 PM org.apache.catalina.startup.Catalina start INFO: Server startup in 17520 ms
9. SOAP Client
Consuming a web service is very easy with the JAX-WS. We will generate the java stubs and use the generated class which is annotated with @WebServiceClient
to consume the services.
pom.xml
<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> <parent> <groupId>jcg.zheng.demo</groupId> <artifactId>jax-ws-tutorial</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>soapClient</artifactId> <dependencies> <dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-java</artifactId> <version>2.3.1</version> <scope>test</scope> </dependency> <dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-junit</artifactId> <version>2.3.1</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>jaxws-maven-plugin</artifactId> <version>2.5</version> <executions> <execution> <id>helloworld_wsdl</id> <goals> <goal>wsimport</goal> </goals> <configuration> <wsdlUrls> <wsdlUrl>http://localhost:9980/helloworld?wsdl</wsdlUrl> </wsdlUrls> <keep>true</keep> <packageName>jcg.demo.jaxws.client.helloworld</packageName> <sourceDestDir>${basedir}/src/main/generated</sourceDestDir> </configuration> </execution> <execution> <id>employee_wsdl</id> <goals> <goal>wsimport</goal> </goals> <configuration> <wsdlUrls> <wsdlUrl>http://localhost:9990/EmployeeLookupService?wsdl</wsdlUrl> </wsdlUrls> <keep>true</keep> <packageName>jcg.demo.jaxws.client.employee</packageName> <sourceDestDir>${basedir}/src/main/generated</sourceDestDir> </configuration> </execution> </executions> </plugin> </plugins> </build> <profiles> <profile> <id>cucumber</id> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.9</version> <configuration> <includes> <include>**/RunCukesTest.java</include> </includes> <skipAfterFailureCount>1</skipAfterFailureCount> </configuration> </plugin> </plugins> </build> </profile> </profiles> </project>
9.1 Generate Stubs
Here are the generated Java files for HelloWorldService
.
Generated Stub for HelloworldService
C:\gitworkspace\jax-ws-tutorial\soapClient\src\main\generated\jcg\demo\jaxws\client\helloworld>dir Volume in drive C is OSDisk Volume Serial Number is 3A10-C6D4 Directory of C:\gitworkspace\jax-ws-tutorial\soapClient\src\main\generated\jcg\demo\jaxws\client\helloworld 08/31/2018 04:59 PM <DIR> . 08/31/2018 04:59 PM <DIR> .. 08/31/2018 04:59 PM 756 GetTopNews.java 08/31/2018 04:59 PM 1,521 GetTopNewsResponse.java 08/31/2018 04:59 PM 1,358 Hello.java 08/31/2018 04:59 PM 1,492 HelloResponse.java 08/31/2018 04:59 PM 2,114 HelloWorldServiceImpl.java 08/31/2018 04:59 PM 3,504 HelloWorldServiceImplService.java 08/31/2018 04:59 PM 4,445 ObjectFactory.java 08/31/2018 04:59 PM 132 package-info.java 08/31/2018 04:59 PM 2,884 TopNews.java 9 File(s) 18,206 bytes 2 Dir(s) 21,555,847,168 bytes free C:\gitworkspace\jax-ws-tutorial\soapClient\src\main\generated\jcg\demo\jaxws\client\helloworld>
Here are the generated Java files for EmployeeService
.
Generated Stub for EmployeeService
C:\gitworkspace\jax-ws-tutorial\soapClient\src\main\generated\jcg\demo\jaxws\client\employee>dir Volume in drive C is OSDisk Volume Serial Number is 3A10-C6D4 Directory of C:\gitworkspace\jax-ws-tutorial\soapClient\src\main\generated\jcg\demo\jaxws\client\employee 08/31/2018 04:59 PM <DIR> . 08/31/2018 04:59 PM <DIR> .. 08/31/2018 04:59 PM 1,888 EmployeeIdWrapper.java 08/31/2018 04:59 PM 3,981 EmployeeInfo.java 08/31/2018 04:59 PM 2,015 EmployeeInfoWrapper.java 08/31/2018 04:59 PM 1,675 EmployeeLookupService.java 08/31/2018 04:59 PM 3,782 EmployeeLookupServiceImplWSDLService.java 08/31/2018 04:59 PM 1,195 EmployeeType.java 08/31/2018 04:59 PM 3,491 ObjectFactory.java 08/31/2018 04:59 PM 194 package-info.java 08/31/2018 04:59 PM 1,185 UserDefinedException.java 08/31/2018 04:59 PM 1,427 UserDefinedFault.java 10 File(s) 20,833 bytes 2 Dir(s) 21,555,965,952 bytes free C:\gitworkspace\jax-ws-tutorial\soapClient\src\main\generated\jcg\demo\jaxws\client\employee>
9.2 Create Client Service
In this step, I will create a client service with the desired WSDL endpoint URL
EmployeeServiceclient.java
package jcg.zheng.demo.service.client; import java.net.MalformedURLException; import java.net.URL; import jcg.demo.jaxws.client.employee.*; public class EmployeeServiceClient { private String serviceUrl; public EmployeeServiceClient(String serviceUrl) { super(); this.serviceUrl = serviceUrl; } public EmployeeInfoWrapper employeeLookup(EmployeeIdWrapper employeeIdList) throws UserDefinedException { try { URL url = new URL(serviceUrl); EmployeeLookupServiceImplWSDLService empService = new EmployeeLookupServiceImplWSDLService(url); EmployeeLookupService eSrc = empService.getEmployeeLookupServiceImplWSDLPort(); return eSrc.employeeLookup(employeeIdList); } catch (MalformedURLException e) { UserDefinedFault fault = new UserDefinedFault(); fault.setMessage(e.getMessage()); throw new UserDefinedException("caught MalformedURLException. ", fault); } } }
HelloServiceClient .java
package jcg.zheng.demo.service.client; import java.net.MalformedURLException; import java.net.URL; import jcg.demo.jaxws.client.helloworld.HelloWorldServiceImpl; import jcg.demo.jaxws.client.helloworld.HelloWorldServiceImplService; import jcg.demo.jaxws.client.helloworld.TopNews; public class HelloServiceClient { public TopNews getTopNews() throws MalformedURLException { URL url = new URL("http://localhost:9980/helloworld?wsdl"); HelloWorldServiceImplService service = new HelloWorldServiceImplService(url); HelloWorldServiceImpl eSrc = service.getHelloWorldServiceImplPort(); return eSrc.getTopNews(); } }
Note: When the web service changes its WSDL, then clients must regenerate the Java stubs. I wrote another article for a dynamic web service client. Web services may take a long time to complete. Clients can use a callback to perform other tasks while waiting for the response. Click here for more details.
9.3 JUnit Tests
In this step, I will create a JUnit test for EmployeeServiceClient
.
EmployeeServiceClientTest.java
package jcg.zheng.demo.service.client; import static org.junit.Assert.assertEquals; import org.junit.Test; import jcg.demo.jaxws.client.employee.*; public class EmployeeServiceClientTest { private EmployeeServiceClient client = new EmployeeServiceClient( "http://localhost:9990/EmployeeLookupService?wsdl"); @Test public void employeeLookup_found() throws UserDefinedException { EmployeeIdWrapper employeeIdList = new EmployeeIdWrapper(); employeeIdList.getEid().add("100"); EmployeeInfoWrapper ret = client.employeeLookup(employeeIdList); assertEquals(1, ret.getEmployeeInfo().size()); assertEquals("100", ret.getEmployeeInfo().get(0).getEid()); } @Test(expected=UserDefinedException.class) public void employeeLookup_not_found() throws UserDefinedException { EmployeeIdWrapper employeeIdList = new EmployeeIdWrapper(); client.employeeLookup(employeeIdList); } }
9.4 Acceptance Tests
I will create two cucumber tests which generates a HTML
report.
9.4.1 Feature Files
Cucumber feature files are easy to read and communicates better with business than JUnit tests.
Employee Feature
Feature: Employee Look Up Service @Employee @SMOKE Scenario Outline: When invoking employeeLookupService Then should find the employee from the response Examples: | empId | | 12 |
Hello Feature
Feature: HelloWorld Service @Helloworld @SMOKE Scenario: When invoking getTopNew Then the response code should be ok
9.4.2 Step Definitions
I will create a Java class to handle the step defintions.
Hello Feature
package jcg.demo.servicetest.step; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import cucumber.api.java.en.Then; import cucumber.api.java.en.When; import jcg.demo.jaxws.client.employee.EmployeeIdWrapper; import jcg.demo.jaxws.client.employee.EmployeeInfoWrapper; import jcg.zheng.demo.service.client.EmployeeServiceClient; public class EmployeeServiceStep { EmployeeServiceClient emClient = new EmployeeServiceClient("http://localhost:9990/EmployeeLookupService?wsdl"); private EmployeeInfoWrapper serviceResponse; @When("^invoking employeeLookupService (.+)$") public void invoking_employeeLookupService(String empId) throws Exception { EmployeeIdWrapper employeeIdList = new EmployeeIdWrapper(); employeeIdList.getEid().add(empId); serviceResponse = emClient.employeeLookup(employeeIdList); } @Then("^should find the employee from the response$") public void the_response_code_should_find_the_employee() throws Exception { assertNotNull(serviceResponse); assertNotNull(serviceResponse.getEmployeeInfo()); assertEquals(1, serviceResponse.getEmployeeInfo().size()); } }
9.4.3 Run the Cucumber Tests
I will create RunCukesTest
and annotate it with @RunWith(Cucumber.class)
. Execute it with C:\gitworkspace\jax-ws-tutorial\soapClient>mvn install -P cucumber
.
RunCukesTest.java
package jcg.demo.servicetest; import org.junit.runner.RunWith; import cucumber.api.CucumberOptions; import cucumber.api.junit.Cucumber; @RunWith(Cucumber.class) @CucumberOptions(tags = { "@SMOKE" }, features = "src/test/resources/features", plugin = { "html:target/cucumber" }) public class RunCukesTest { }
9.4.4 Test Reports
Verify the cucumber reports.
10. JAX-WS Tutorial – Summary
In this article, I demonstrated how to create and consume web services using JAX-WS in a Maven project with both Top-down and Bottom-up approaches. Top-down approach is the preferred way, but developers usually use a Bottom-up approach to generate WSDL and revise it. Once the WSDL is finalized, developers complete it with a Top-down approach.
I also demonstrated how to deploy web services with a JAX-WS Endpoint as a standard Java application and deploy it in a Tomcat container. I also showed how to test the web services via SoapUI, Junit, and cucumber framework.
You may secure the web service. Click here for how to secure the web service with JAX-WS.
11. Download the Source Code
This tutorial consists of a Maven project to create, consume, and test the JAX-WS web services.
You can download the full source code of this example here: JAX-WS Tutorial for Beginners
Hello Mary, thanks for the awesome tutorial. I have a question related to the war and it would be nice to have feedback. How did you produce tomcat-server-0.0.1-SNAPSHOT.war ? Personally I navigated in the parent-pom’s directory by commandline and I’ve executed ‘mvn clean; mvn install’. The war is produced. I copy it into ‘/tomcatServer/webapps’ and apache automatically expands it. The URL I would need to use to test though, is not http://localhost:8080/tomcat-server/employeeWS?wsdl but http://localhost:8080/tomcat-server-0.0.1-SNAPSHOT/employeeWS?wsdl Is there some additional instructions we should put in the tomcat-server module’s pom or in the parent’s pom? I’ve downloaded this complete tutorial sources and I’ve seen, in… Read more »