I have been talk about Bottom up method. In this post, I will talk about Top Down method to deploy Web Service.
Using Top Down method, you must have a knowledge in XML Schema and Web Services Description Language. If you do not have those knowledge, please follow the URL to learn about that.
XML Schema: http://www.w3schools.com/schema/default.asp
Web Services Description Language: http://www.w3schools.com/WSDL/default.asp
In this tutorial, I will simulate a employee lookup service. Client send a list of employee to service provider. Service provider lookup an employee info and return a list of info to client.
When creating a Web service using a top-down approach, first you design the implementation of the Web service by creating a WSDL file. You can do this using the WSDL Editor. You can then use the Web services wizard to create the Web service and skeleton Java™ classes to which you can add the required code.
First, you need create a WAR project in Eclipse, NetBeans or other IDE. Add 2 file in web root – company.xsd and company.wsdl.
company.xsd:
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.memorylack.com/company" xmlns:tns="http://www.memorylack.com/company" 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="EmployeeTitle"> <xs:restriction base="xs:string"> <xs:enumeration value="CEO" /> <xs:enumeration value="Manger" /> <xs:enumeration value="Supervisor" /> <xs:enumeration value="Clerk" /> </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="age" type="xs:unsignedShort" minOccurs="0" nillable="false" /> <xs:element name="title" type="tns:EmployeeTitle" 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>
company.wsdl:
<?xml version="1.0" encoding="UTF-8"?> <wsdl:definitions name="Company" targetNamespace="http://www.memorylack.com/company" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.memorylack.com/company" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <wsdl:types> <xsd:schema> <xsd:import namespace="http://www.memorylack.com/company" schemaLocation="company.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://www.memorylack.com/company/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>
you have complete web service interface, but you need create a corresponding Java class. We use JAXB compiler to transfer XML Schema complex type to Java object. Open the CMD.exe to enter below command:
xjc -wsdl company.wsdl -p com.memorylack.www.ws.model
**If Windows alert you xjc not found, Please add %JAVA_HOME%/bin to system path. default %JAVA_HOME% is: C:Program FilesJavajdk1.6.0_21bin**
- -wsdl option is set your WSDL location
- -p is set generated Java class package
More option can be found in: JAXB Using
After enter command. You can see following .java file.
- EmployeeIdWrapper
- EmployeeInfo
- EmployeeInfoWrapper
- EmployeeTitle
- ObjectFactory
- package-info
EmployeeIdWrapper:
// // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.3 in JDK 1.6 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> // Any modifications to this file will be lost upon recompilation of the source schema. // Generated on: 2011.06.23 at 04:28:35 PM CST // package com.memorylack.www.ws.model; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlType; /** * <p>Java class for EmployeeIdWrapper complex type. * * <p>The following schema fragment specifies the expected content contained within this class. * * <pre> * <complexType name="EmployeeIdWrapper"> * <complexContent> * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> * <sequence> * <element name="eid" type="{http://www.memorylack.com/company}EmployeeId" maxOccurs="unbounded" minOccurs="0"/> * </sequence> * </restriction> * </complexContent> * </complexType> * </pre> * * */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "EmployeeIdWrapper", propOrder = { "eid" }) public class EmployeeIdWrapper { protected List<String> eid; /** * Gets the value of the eid property. * * <p> * This accessor method returns a reference to the live list, * not a snapshot. Therefore any modification you make to the * returned list will be present inside the JAXB object. * This is why there is not a <CODE>set</CODE> method for the eid property. * * <p> * For example, to add a new item, do as follows: * <pre> * getEid().add(newItem); * </pre> * * * <p> * Objects of the following type(s) are allowed in the list * {@link String } * * */ public List<String> getEid() { if (eid == null) { eid = new ArrayList<String>(); } return this.eid; } }
EmployeeInfo:
// // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.3 in JDK 1.6 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> // Any modifications to this file will be lost upon recompilation of the source schema. // Generated on: 2011.06.23 at 04:28:35 PM CST // package com.memorylack.www.ws.model; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlSchemaType; import javax.xml.bind.annotation.XmlType; /** * <p>Java class for EmployeeInfo complex type. * * <p>The following schema fragment specifies the expected content contained within this class. * * <pre> * <complexType name="EmployeeInfo"> * <complexContent> * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> * <sequence> * <element name="eid" type="{http://www.memorylack.com/company}EmployeeId" minOccurs="0"/> * <element name="firstName" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/> * <element name="lastName" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/> * <element name="age" type="{http://www.w3.org/2001/XMLSchema}unsignedShort" minOccurs="0"/> * <element name="title" type="{http://www.memorylack.com/company}EmployeeTitle" minOccurs="0"/> * </sequence> * </restriction> * </complexContent> * </complexType> * </pre> * * */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "EmployeeInfo", propOrder = { "eid", "firstName", "lastName", "age", "title" }) public class EmployeeInfo { protected String eid; protected String firstName; protected String lastName; @XmlSchemaType(name = "unsignedShort") protected Integer age; protected EmployeeTitle title; /** * Gets the value of the eid property. * * @return * possible object is * {@link String } * */ public String getEid() { return eid; } /** * Sets the value of the eid property. * * @param value * allowed object is * {@link String } * */ public void setEid(String value) { this.eid = value; } /** * Gets the value of the firstName property. * * @return * possible object is * {@link String } * */ public String getFirstName() { return firstName; } /** * Sets the value of the firstName property. * * @param value * allowed object is * {@link String } * */ public void setFirstName(String value) { this.firstName = value; } /** * Gets the value of the lastName property. * * @return * possible object is * {@link String } * */ public String getLastName() { return lastName; } /** * Sets the value of the lastName property. * * @param value * allowed object is * {@link String } * */ public void setLastName(String value) { this.lastName = value; } /** * Gets the value of the age property. * * @return * possible object is * {@link Integer } * */ public Integer getAge() { return age; } /** * Sets the value of the age property. * * @param value * allowed object is * {@link Integer } * */ public void setAge(Integer value) { this.age = value; } /** * Gets the value of the title property. * * @return * possible object is * {@link EmployeeTitle } * */ public EmployeeTitle getTitle() { return title; } /** * Sets the value of the title property. * * @param value * allowed object is * {@link EmployeeTitle } * */ public void setTitle(EmployeeTitle value) { this.title = value; } }
EmployeeInfoWrapper:
// // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.3 in JDK 1.6 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> // Any modifications to this file will be lost upon recompilation of the source schema. // Generated on: 2011.06.23 at 04:28:35 PM CST // package com.memorylack.www.ws.model; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlType; /** * <p>Java class for EmployeeInfoWrapper complex type. * * <p>The following schema fragment specifies the expected content contained within this class. * * <pre> * <complexType name="EmployeeInfoWrapper"> * <complexContent> * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType"> * <sequence> * <element name="employeeInfo" type="{http://www.memorylack.com/company}EmployeeInfo" maxOccurs="unbounded" minOccurs="0"/> * </sequence> * </restriction> * </complexContent> * </complexType> * </pre> * * */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "EmployeeInfoWrapper", propOrder = { "employeeInfo" }) public class EmployeeInfoWrapper { protected List<EmployeeInfo> employeeInfo; /** * Gets the value of the employeeInfo property. * * <p> * This accessor method returns a reference to the live list, * not a snapshot. Therefore any modification you make to the * returned list will be present inside the JAXB object. * This is why there is not a <CODE>set</CODE> method for the employeeInfo property. * * <p> * For example, to add a new item, do as follows: * <pre> * getEmployeeInfo().add(newItem); * </pre> * * * <p> * Objects of the following type(s) are allowed in the list * {@link EmployeeInfo } * * */ public List<EmployeeInfo> getEmployeeInfo() { if (employeeInfo == null) { employeeInfo = new ArrayList<EmployeeInfo>(); } return this.employeeInfo; } }
EmployeeTitle:
// // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.3 in JDK 1.6 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> // Any modifications to this file will be lost upon recompilation of the source schema. // Generated on: 2011.06.23 at 04:28:35 PM CST // package com.memorylack.www.ws.model; import javax.xml.bind.annotation.XmlEnum; import javax.xml.bind.annotation.XmlEnumValue; import javax.xml.bind.annotation.XmlType; /** * <p>Java class for EmployeeTitle. * * <p>The following schema fragment specifies the expected content contained within this class. * <p> * <pre> * <simpleType name="EmployeeTitle"> * <restriction base="{http://www.w3.org/2001/XMLSchema}string"> * <enumeration value="CEO"/> * <enumeration value="Manger"/> * <enumeration value="Supervisor"/> * <enumeration value="Clerk"/> * </restriction> * </simpleType> * </pre> * */ @XmlType(name = "EmployeeTitle") @XmlEnum public enum EmployeeTitle { CEO("CEO"), @XmlEnumValue("Manger") MANGER("Manger"), @XmlEnumValue("Supervisor") SUPERVISOR("Supervisor"), @XmlEnumValue("Clerk") CLERK("Clerk"); private final String value; EmployeeTitle(String v) { value = v; } public String value() { return value; } public static EmployeeTitle fromValue(String v) { for (EmployeeTitle c: EmployeeTitle.values()) { if (c.value.equals(v)) { return c; } } throw new IllegalArgumentException(v); } }
ObjectFactory:
// // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.3 in JDK 1.6 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> // Any modifications to this file will be lost upon recompilation of the source schema. // Generated on: 2011.06.23 at 04:28:35 PM CST // package com.memorylack.www.ws.model; import javax.xml.bind.JAXBElement; import javax.xml.bind.annotation.XmlElementDecl; import javax.xml.bind.annotation.XmlRegistry; import javax.xml.namespace.QName; /** * This object contains factory methods for each * Java content interface and Java element interface * generated in the com.memorylack.www.ws.model package. * <p>An ObjectFactory allows you to programatically * construct new instances of the Java representation * for XML content. The Java representation of XML * content can consist of schema derived interfaces * and classes representing the binding of schema * type definitions, element declarations and model * groups. Factory methods for each of these are * provided in this class. * */ @XmlRegistry public class ObjectFactory { private final static QName _EmployeeIdList_QNAME = new QName("http://www.memorylack.com/company", "EmployeeIdList"); private final static QName _EmployeeInfoList_QNAME = new QName("http://www.memorylack.com/company", "EmployeeInfoList"); /** * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.memorylack.www.ws.model * */ public ObjectFactory() { } /** * Create an instance of {@link EmployeeIdWrapper } * */ public EmployeeIdWrapper createEmployeeIdWrapper() { return new EmployeeIdWrapper(); } /** * Create an instance of {@link EmployeeInfoWrapper } * */ public EmployeeInfoWrapper createEmployeeInfoWrapper() { return new EmployeeInfoWrapper(); } /** * Create an instance of {@link EmployeeInfo } * */ public EmployeeInfo createEmployeeInfo() { return new EmployeeInfo(); } /** * Create an instance of {@link JAXBElement }{@code <}{@link EmployeeIdWrapper }{@code >}} * */ @XmlElementDecl(namespace = "http://www.memorylack.com/company", name = "EmployeeIdList") public JAXBElement<EmployeeIdWrapper> createEmployeeIdList(EmployeeIdWrapper value) { return new JAXBElement<EmployeeIdWrapper>(_EmployeeIdList_QNAME, EmployeeIdWrapper.class, null, value); } /** * Create an instance of {@link JAXBElement }{@code <}{@link EmployeeInfoWrapper }{@code >}} * */ @XmlElementDecl(namespace = "http://www.memorylack.com/company", name = "EmployeeInfoList") public JAXBElement<EmployeeInfoWrapper> createEmployeeInfoList(EmployeeInfoWrapper value) { return new JAXBElement<EmployeeInfoWrapper>(_EmployeeInfoList_QNAME, EmployeeInfoWrapper.class, null, value); } }
package-info:
// // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.3 in JDK 1.6 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> // Any modifications to this file will be lost upon recompilation of the source schema. // Generated on: 2011.06.23 at 04:28:35 PM CST // @javax.xml.bind.annotation.XmlSchema(namespace = "http://www.memorylack.com/company", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) package com.memorylack.www.ws.model;
Please copy .java file yo your WAR project and create a class named EmployeeLookupService:
package com.ctlok.pro.ws; import java.util.HashMap; import java.util.Map; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; import javax.jws.soap.SOAPBinding; import javax.xml.bind.annotation.XmlSeeAlso; import com.memorylack.www.ws.model.EmployeeIdWrapper; import com.memorylack.www.ws.model.EmployeeInfo; import com.memorylack.www.ws.model.EmployeeInfoWrapper; import com.memorylack.www.ws.model.EmployeeTitle; import com.memorylack.www.ws.model.ObjectFactory; @WebService( name = "employeeLookupService", serviceName = "employeeLookupService", portName = "employeeLookupPort", targetNamespace= "http://www.memorylack.com/company", wsdlLocation = "company.wsdl") @SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE) @XmlSeeAlso({ObjectFactory.class}) public class EmployeeLookupService { private Map<String, EmployeeInfo> infoMap; public EmployeeLookupService(){ infoMap = new HashMap<String, EmployeeInfo>(); EmployeeInfo info1 = new EmployeeInfo(); info1.setEid("E1000000"); info1.setFirstName("Lawrence"); info1.setLastName("Cheung"); info1.setAge(24); info1.setTitle(EmployeeTitle.CEO); EmployeeInfo info2 = new EmployeeInfo(); info2.setEid("E1524125"); info2.setFirstName("Tom"); info2.setLastName("Wong"); info2.setAge(22); info2.setTitle(EmployeeTitle.CLERK); EmployeeInfo info3 = new EmployeeInfo(); info3.setEid("E7452145"); info3.setFirstName("John"); info3.setLastName("Lee"); info3.setAge(29); info3.setTitle(EmployeeTitle.MANGER); EmployeeInfo info4 = new EmployeeInfo(); info4.setEid("E6523547"); info4.setFirstName("Katty"); info4.setLastName("Choi"); info4.setAge(24); info4.setTitle(EmployeeTitle.SUPERVISOR); infoMap.put(info1.getEid(), info1); infoMap.put(info2.getEid(), info2); infoMap.put(info3.getEid(), info3); infoMap.put(info4.getEid(), info4); } @WebMethod(operationName="employeeLookup") @WebResult(name = "EmployeeInfoList", partName = "employeeInfoList") public EmployeeInfoWrapper employeeLookup( @WebParam(name = "EmployeeIdList", partName = "employeeIdList") EmployeeIdWrapper employeeIdWrapper){ EmployeeInfoWrapper employeeInfoWrapper = new EmployeeInfoWrapper(); for (String eid: employeeIdWrapper.getEid()){ EmployeeInfo info = infoMap.get(eid); if (info == null) info = new EmployeeInfo(); employeeInfoWrapper.getEmployeeInfo().add(info); } return employeeInfoWrapper; } } Now, you have complete the web service provider.
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
This annotation default value is WRAPPED. JAX-WS auto create a wrapper to wrap the object. In company.xsd, I have a wrapper so do not need this option. Set value to BARE to avoid JAX-WS auto create a wrapper.
——————- separate ——————-
@XmlSeeAlso({ObjectFactory.class})
This annotation will be allowed on any JAXB-bound type, and when JAXB binds that type, classes listed in @XmlSeeAlso will be also bound.
——————- separate ——————-
@WebResult( name = "EmployeeInfoList", partName = "employeeInfoList")
This annotation is mapped with WSDL:
<wsdl:message name="employeeLookupRequest"> <wsdl:part element="tns:EmployeeIdList" name="employeeIdList" /> </wsdl:message>
——————- separate ——————-
@WebResult( name = "EmployeeInfoList", partName = "employeeInfoList")
This annotation is mapped with WSDL:
<wsdl:message name="employeeLookupResponse"> <wsdl:part element="tns:EmployeeInfoList" name="employeeInfoList" /> </wsdl:message>
Now you can deploy this project to application server and run this URL in browser: http://localhost/employeeLookupService?wsdl.
If this URL not equal your project URL, please modify WSDL address:
<wsdl:service name="employeeLookupService"> <wsdl:port binding="tns:employeeLookupBinding" name="employeeLookupPort"> <soap:address location="http://localhost" /> </wsdl:port> </wsdl:service>
Complete this part you can use soapUI to test your Web Service Provider. soapUI sent below SOAP XML to server:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:com="http://www.memorylack.com/company"> <soapenv:Header/> <soapenv:Body> <com:EmployeeIdList> <!--Zero or more repetitions:--> <com:eid>E1000000</com:eid> <com:eid>E1524125</com:eid> </com:EmployeeIdList> </soapenv:Body> </soapenv:Envelope>
Server response:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Body> <EmployeeInfoList xmlns="http://www.memorylack.com/company"> <employeeInfo> <eid>E1000000</eid> <firstName>Lawrence</firstName> <lastName>Cheung</lastName> <age>24</age> <title>CEO</title> </employeeInfo> <employeeInfo> <eid>E1524125</eid> <firstName>Tom</firstName> <lastName>Wong</lastName> <age>22</age> <title>Clerk</title> </employeeInfo> </EmployeeInfoList> </soapenv:Body> </soapenv:Envelope>