jws

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.

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 or javax.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, or final
  • 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:

JAX-WS Tutorial - Web Service
Figure 1 Web Service

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:

ModuleDescriptionNote
bottom-upCreate and publish a web service with the Bottom-up approach.
employeeWsdlToJavaUse wsimport to generate Java stubs from employee.wsdl.
top-downCreate and publish a web service with the Top-down approach.depends on employeeWsdlToJava
tomcat-serverCreate and publish a web service in tomcat container.depends on employeeWsdlToJava
soapcClientUse wsimport to generate Java stubs from web services.

 

  1. Launch Eclipse and click File->New->Maven Project.
  2. 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.
  3. You will see the Maven project: jax-ws-tutorial is created.
  4. Click File->New->Maven Module.
  5. At the pop-up windows, enter the module name: employeeWsdlToJava, then click Finish.
  6. You will see the Maven module: employeeWsdlToJavais created as a child of jax-ws-tutorial.
  7. Repeat steps 4 and 5 for bottom-up, top-down, tomcat-server , and soapClient.

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:

WSDLJava
namespacepackage
portTypeSEI
operationmethod
inputparameter
outputreturn
xsd:complextTypeJava 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:

  1. Click File->New SOAP Project
  2. Enter the Initial WSDL: http://localhost:9980/helloworld?wsdl and Click OK
  3. Expand the newly created project, then click employeeLookup, and Request 1
  4. A SOAP message is populated, replace ? with the test data
  5. Submit the request
  6. 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.

JAX-WS Tutorial - Cucumber Report
Figure 2 Cucumber Report

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.

Download
You can download the full source code of this example here: JAX-WS Tutorial for Beginners

Mary Zheng

Mary has graduated from Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She works as a senior Software Engineer in the telecommunications sector where she acts as a leader and works with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Teresa
Teresa
3 years ago

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 »

Back to top button