jws

JAX-WS JAXB Example

1. Introduction

Java Architecture for XML Web Services (JAX-WS) is a Java programming language for creating web services, particularly SOAP services.

Java Architecture for XML Binding (JAXB) is a Java-XML binding technology that simplifies the development of web services by enabling transformations between a schema and Java objects, and also between XML instance documents and Java object instances.

Both JAX-WS and JAXB have been a part of JDK since version 6. JAX-WS uses JAXB internally as the binding layer to convert Java objects to and from XML.

In this example, I will build a JAX-WS service with three steps:

  1. Generate Java stubs from the WSDL file
  2. Create an implementation class for the generated interface and annotate it with @WebService(endpointInterface="")
  3. Create an Endpoint to publish the service

2. Business Use Case

BestPay is a fictional company which provides employee paycheck services. BestPay defines WSDL. BestPay‘s customer implements the service. BestPay reads the client’s employee records via the service.

BestPay WSDL which defines the employeeLookupService and employee detail from employee.xsd.

employeeService.wsdl

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="Employee"
	targetNamespace="http://bestpay.payroll/employee" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	xmlns:tns="http://bestpay.payroll/employee" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema">

	<wsdl:types>
		<xsd:schema>
			<xsd:import namespace="http://bestpay.payroll/employee"
				schemaLocation="../xsd/employee.xsd" />
		</xsd:schema>
	</wsdl:types>

	<wsdl:message name="employeeLookupRequest">
		<wsdl:part element="tns:EmployeeIdList" name="employeeIdList" />
	</wsdl:message>

	<wsdl:message name="employeeLookupResponse">
		<wsdl:part element="tns:EmployeeInfoList" name="employeeInfoList" />
	</wsdl:message>

	<wsdl:portType name="employeeLookupService">
		<wsdl:operation name="employeeLookup">
			<wsdl:input message="tns:employeeLookupRequest" />
			<wsdl:output message="tns:employeeLookupResponse" />
		</wsdl:operation>
	</wsdl:portType>

	<wsdl:binding name="employeeLookupBinding" type="tns:employeeLookupService">
		<soap:binding style="document"
			transport="http://schemas.xmlsoap.org/soap/http" />
		<wsdl:operation name="employeeLookup">
			<soap:operation soapAction="http://bestpay.payroll/employee/employeeLookup" />
			<wsdl:input>
				<soap:body use="literal" />
			</wsdl:input>
			<wsdl:output>
				<soap:body use="literal" />
			</wsdl:output>
		</wsdl:operation>
	</wsdl:binding>

	<wsdl:service name="employeeLookupService">
		<wsdl:port binding="tns:employeeLookupBinding" name="employeeLookupPort">
			<soap:address location="http://localhost" />
		</wsdl:port>
	</wsdl:service>

</wsdl:definitions>

Employee schema file which defines EmployeeInfo data structure.

employee.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
	targetNamespace="http://bestpay.payroll/employee" xmlns:tns="http://bestpay.payroll/employee"
	elementFormDefault="qualified">

	<xs:simpleType name="EmployeeId">
		<xs:restriction base="xs:string">
			<xs:length value="7" />
			<xs:pattern value="E[0-9]{7}" />
		</xs:restriction>
	</xs:simpleType>

	<xs:simpleType name="EmployeeType">
		<xs:restriction base="xs:string">
			<xs:enumeration value="Hourly" />
			<xs:enumeration value="Salary" />
		</xs:restriction>
	</xs:simpleType>

	<xs:complexType name="EmployeeInfo">
		<xs:sequence>
			<xs:element name="eid" type="tns:EmployeeId" minOccurs="0"
				nillable="false" />
			<xs:element name="firstName" type="xs:string" minOccurs="0"
				nillable="false" />
			<xs:element name="lastName" type="xs:string" minOccurs="0"
				nillable="false" />
			<xs:element name="hourlyRate" type="xs:decimal" minOccurs="0"
				nillable="false" />
			<xs:element name="type" type="tns:EmployeeType"
				minOccurs="0" nillable="false" />
		</xs:sequence>
	</xs:complexType>

	<xs:complexType name="EmployeeInfoWrapper">
		<xs:sequence>
			<xs:element name="employeeInfo" type="tns:EmployeeInfo"
				minOccurs="0" maxOccurs="unbounded" nillable="false" />
		</xs:sequence>
	</xs:complexType>

	<xs:complexType name="EmployeeIdWrapper">
		<xs:sequence>
			<xs:element name="eid" type="tns:EmployeeId" minOccurs="0"
				maxOccurs="unbounded" nillable="false" />
		</xs:sequence>
	</xs:complexType>

	<xs:element name="EmployeeIdList" type="tns:EmployeeIdWrapper" />
	<xs:element name="EmployeeInfoList" type="tns:EmployeeInfoWrapper" />

</xs:schema>

Each customer of BestPay implements the employeeLookupService as defined in the WSDL. There is no additional development work at BestPay except configuring the customer’s service endpoint.

3. Technologies used

The example code in this article was built and run using:

  • Java 1.8.101 (1.8.x will do fine)
  • Maven 3.3.9 (3.3.x will do fine)
  • Eclipse Mars (Any Java IDE would work)

4. Generate Java Stub

4.1. Set up Pom.xml

Create a Maven project and configure it with the wsimport plug-in to automatically generate source codes from the employeeLookupService.wsdl.

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>Jax-ws-server</groupId>
	<artifactId>jax-ws-server-wsdl</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.5.1</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.6</version>
				<configuration>
					<warSourceDirectory>WebContent</warSourceDirectory>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
			<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.demo.service.employee</packageName>
							<keep>true</keep>
							<sourceDestDir>${basedir}/target/generated/src/main/java</sourceDestDir>
						</configuration>
						<goals>
							<goal>wsimport</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
</project>

4.2. Generate the Stubs

Execute mvn install to generate the Java stubs.

Output

[INFO] jaxws:wsimport args: [-keep, -s, 'C:\MZheng_Java_workspace\Java Code Geek Examples\JAX-WS-Demo\jax-ws-server-wsdl\target\generated\src\main\java', -d, 'C:\MZheng_Java_workspace\Java Code Geek Examples\JAX-WS-Demo\jax-ws-server-wsdl\target\classes', -Xnocompile, -p, jcg.demo.service.employee, "file:/C:/MZheng_Java_workspace/Java%20Code%20Geek%20Examples/JAX-WS-Demo/jax-ws-server-wsdl/src/main/resources/wsdl/employeeService.wsdl"]

4.2 Generated Codes

Check the generated codes which includes several JAXB Annotations:

  • @XmlRootElement
  • @XmlElement
  • @XmlSeeAlso
  • @XmlType
  • @XmlAccessorType
  • @XmlSchemaType
  • @XmlEnumValue
  • @XmlEnum
  • @XmlRegistry

Note: Never modify the generated codes.

5. Implement the Generated Interface

Implements the generated interface EmployeeLookupService by retrieving its employees records.

5.1. Create the Implementation Class

Create EmployeeLookupServiceImpl to implement the generated interface:EmployeeLookupService. It uses InternalEmployeeComponent to get employee detail.

EmployeeLookupServiceImpl.java

package jcg.demo.service.impl;

import java.util.List;

import javax.jws.WebService;

import jcg.demo.model.InternalEmployee;
import jcg.demo.service.employee.EmployeeIdWrapper;
import jcg.demo.service.employee.EmployeeInfoWrapper;
import jcg.demo.service.employee.EmployeeLookupService;

@WebService(endpointInterface = "jcg.demo.service.employee.EmployeeLookupService")
public class EmployeeLookupServiceImpl implements EmployeeLookupService {

	private InternalEmployeeComponent empService = new InternalEmployeeComponent();

	@Override
	public EmployeeInfoWrapper employeeLookup(EmployeeIdWrapper employeeIdList) {

		EmployeeInfoWrapper eWrapper = new EmployeeInfoWrapper();

		List allEmps = empService.getEmployees(employeeIdList.getEid());

		eWrapper.getEmployeeInfo().addAll(allEmps);

		return eWrapper;
	}

}
  • Line 12: Indicate it as JAX-WS Web Service

5.2. Create Internal Employee Service

Create InternalEmployeeComponent to retrieve the employee records and print it out as an xml String.

InternalEmployeeComponent.java

package jcg.demo.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import jcg.demo.model.InternalEmployee;
import jcg.demo.service.employee.EmployeeType;

public class InternalEmployeeComponent {

	public void printOutAsXml(InternalEmployee interEmp) throws JAXBException {
		JAXBContext jaxbContext = JAXBContext.newInstance(InternalEmployee.class);
		Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

		jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		jaxbMarshaller.marshal(interEmp, System.out);
	}

	public List getEmployees(List ids) {
		List emps = new ArrayList();
		for (String id : ids) {
			emps.add(buildDummyEmployee(id, "Internal data " + id, EmployeeType.HOURLY));
		}

		return emps;
	}

	private InternalEmployee buildDummyEmployee(String id, String internal, EmployeeType type) {
		InternalEmployee emp = new InternalEmployee();
		emp.setEid(id);
		emp.setInternal(internal);
		Random rand = new Random();

		emp.setFirstName("FName_" + id);
		emp.setLastName("LName_" + id);
		emp.setType(type);

		emp.setHourlyRate(new BigDecimal(rand.nextInt(40)));
		
		try {
			printOutAsXml(emp);
		} catch (JAXBException e) {
			e.printStackTrace();
		}
		return emp;
	}
}
  • Line 18: Create JAXBContext instance for InternalEmployee
  • Line 19: Create Marshaller instance
  • Line 21: Use setProperty to set XML as formatted
  • Line 22: Invoke marshal to convert the InternalEmployee to formatted XML String
  • Line 47: Call printOutAsXml to demonstrate the JAXB usage

5.3. Create Company Employee

Create InternalEmployee to represent the actual employee data for the company. In this example, it extends from the generated EmployeeInfo with additional data.

InternalEmployee.java

package jcg.demo.model;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

import jcg.demo.service.employee.EmployeeInfo;

@XmlRootElement
public class InternalEmployee extends EmployeeInfo {

	@XmlAttribute
	private String internal;
	
	@XmlTransient
	private String hiddenFromXml;

	public String getInternal() {
		return internal;
	}

	public void setInternal(String internal) {
		this.internal = internal;
	}

	public String getHiddenFromXml() {
		return hiddenFromXml;
	}

	public void setHiddenFromXml(String hiddenFromXml) {
		this.hiddenFromXml = hiddenFromXml;
	}

}
  • Line 9,12,15: JAXB annotation usage

5.4. Create JAX-WS Server

Create a simple Java application to publish the service at http://localhost:9990/EmployeeLookupService.

ServerApp .java

package jcg.demo.app;

import javax.xml.ws.Endpoint;

import jcg.demo.service.impl.EmployeeLookupServiceImpl;

public class ServerApp {
	public static void main(String[] args) {
		Endpoint.publish("http://localhost:9990/EmployeeLookupService", new EmployeeLookupServiceImpl());
	}
}

Image below shows the Java project detail.

Figure 1 Jax-ws project

6. Demo Time

Start ServerApp and navigate to http://localhost:9990/EmployeeLookupService. Test with SOAPUI project.

SOAPUI 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>emp001</emp:eid>
         <emp:eid>emp002</emp:eid>
         <emp:eid>emp003</emp:eid>
      </emp:EmployeeIdList>
   </soapenv:Body>
</soapenv:Envelope>

SOAPUI Response

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <EmployeeInfoList xmlns="http://bestpay.payroll/employee">
         <employeeInfo>
            <eid>emp001</eid>
            <firstName>FName_emp001</firstName>
            <lastName>LName_emp001</lastName>
            <hourlyRate>39</hourlyRate>
            <type>Hourly</type>
         </employeeInfo>
         <employeeInfo>
            <eid>emp002</eid>
            <firstName>FName_emp002</firstName>
            <lastName>LName_emp002</lastName>
            <hourlyRate>35</hourlyRate>
            <type>Hourly</type>
         </employeeInfo>
         <employeeInfo>
            <eid>emp003</eid>
            <firstName>FName_emp003</firstName>
            <lastName>LName_emp003</lastName>
            <hourlyRate>34</hourlyRate>
            <type>Hourly</type>
         </employeeInfo>
      </EmployeeInfoList>
   </S:Body>
</S:Envelope>

ServerApp also prints out the employee data.

ServerApp output

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<internalEmployee xmlns:ns2="http://bestpay.payroll/employee" internal="Internal data emp001">
    <ns2:eid>emp001</ns2:eid>
    <ns2:firstName>FName_emp001</ns2:firstName>
    <ns2:lastName>LName_emp001</ns2:lastName>
    <ns2:hourlyRate>39</ns2:hourlyRate>
    <ns2:type>Hourly</ns2:type>
</internalEmployee>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<internalEmployee xmlns:ns2="http://bestpay.payroll/employee" internal="Internal data emp002">
    <ns2:eid>emp002</ns2:eid>
    <ns2:firstName>FName_emp002</ns2:firstName>
    <ns2:lastName>LName_emp002</ns2:lastName>
    <ns2:hourlyRate>35</ns2:hourlyRate>
    <ns2:type>Hourly</ns2:type>
</internalEmployee>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<internalEmployee xmlns:ns2="http://bestpay.payroll/employee" internal="Internal data emp003">
    <ns2:eid>emp003</ns2:eid>
    <ns2:firstName>FName_emp003</ns2:firstName>
    <ns2:lastName>LName_emp003</ns2:lastName>
    <ns2:hourlyRate>34</ns2:hourlyRate>
    <ns2:type>Hourly</ns2:type>
</internalEmployee>

Note: internal is printed as an attribute and the hiddenFromXml is not printed out.

7. Summary

In this example, I built a JAX-WS server based on WSDL definition. I verified the generated source code with JAXB annotation and created new data model which extends from the generated code with additional JAXB annotation.

8. Download the Source Code

This example consists of a JAX-WS service with JAXB usage.

Download
You can download the full source code of this example here: JAX-WS JAXB Example

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.

4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Yatin
6 years ago

Thank you, Mary! It’s a great article! :)

Jonathan
Jonathan
5 years ago

Thanks A lot!!.. i was some confused about jax-ws and jaxb.. it let me clear..

pupuv
pupuv
4 years ago

Very useful, thank you !!

carlos
carlos
2 years ago

Not work to me… the target class folder is empty. Any Idea?

Back to top button