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:
- Generate Java stubs from the WSDL file
- Create an implementation class for the generated interface and annotate it with
@WebService(endpointInterface="")
- 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 forInternalEmployee
- Line 19: Create
Marshaller
instance - Line 21: Use
setProperty
to set XML as formatted - Line 22: Invoke
marshal
to convert theInternalEmployee
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.
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.
You can download the full source code of this example here: JAX-WS JAXB Example
Thank you, Mary! It’s a great article! :)
Thanks A lot!!.. i was some confused about jax-ws and jaxb.. it let me clear..
Very useful, thank you !!
Not work to me… the target class folder is empty. Any Idea?