Fork me on GitHub

Programming Design Notes

Java 使用 Top Down 和 JAX-WS 來部署 Web Service Provider

| Comments

上一篇已經介紹過使用 Bottom Up 的方法來開發 Web Service: Java 使用 Bottom Up 和 JAX-WS 來部署 Web Service。今次當然是使用 Top Down 的方法,個人認為 Top Down 其實比 Bottom Up 方法更好,原因是自動產生出來的 XML SchemaWSDL 不太好。雖然是方便,但有很多細節地方不能自己操作到,所以我是比較喜歡 Top Down 的。


使用 Top Down 一定要懂 XML SchemaWeb Services Description Language

XML Schema: http://www.w3schools.com/schema/default.asp
Web Services Description Language: http://www.w3schools.com/WSDL/default.asp

這次的 Web Service 功能是客戶端 (Web service client) 送出一個 Employee Id list 到 服務器端 (Web service provider)。

首先建立一個 WAR project,然後在 web root 目錄 加入 company.xsdcompany.wsdl

以下是 company.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://pro.ctlok.com/company" xmlns:tns="http://pro.ctlok.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://pro.ctlok.com/company"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://pro.ctlok.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://pro.ctlok.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://pro.ctlok.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:9080/WebService" />
</wsdl:port>
</wsdl:service>

</wsdl:definitions>

現在完成了 Web Service 的介面了,但要將 Web ServiceObject 轉為 Java Class 才可以。幸好有工具可以替我們完成這項工作,不用一個一個手動建立。使用 JAXB compiler 幫我們將 WSDL 內用到的 Object 轉為 Java Class。在 Windows 打開 CMD 輸入以下指令:
xjc -wsdl company.wsdl -p com.ctlok.pro.ws.model

*** 如果你不能執行 xjc,請查看 System Path 有沒有加入 %JAVA_HOME%/bin 。 ***

  • -wsdlwsdl 檔案存放的位置
  • -p 是產生出來的 Java class 所在 package

想知更多指令可參考: JAXB Using

執行指令後會看到有 6 個 .java 檔案,分別是:
  1. EmployeeIdWrapper
  2. EmployeeInfo
  3. EmployeeInfoWrapper
  4. EmployeeTitle
  5. ObjectFactory
  6. 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.ctlok.pro.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>
* &lt;complexType name="EmployeeIdWrapper">
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="eid" type="{http://pro.ctlok.com/company}EmployeeId" maxOccurs="unbounded" minOccurs="0"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/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.ctlok.pro.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>
* &lt;complexType name="EmployeeInfo">
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="eid" type="{http://pro.ctlok.com/company}EmployeeId" minOccurs="0"/>
* &lt;element name="firstName" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* &lt;element name="lastName" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* &lt;element name="age" type="{http://www.w3.org/2001/XMLSchema}unsignedShort" minOccurs="0"/>
* &lt;element name="title" type="{http://pro.ctlok.com/company}EmployeeTitle" minOccurs="0"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/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.ctlok.pro.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>
* &lt;complexType name="EmployeeInfoWrapper">
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="employeeInfo" type="{http://pro.ctlok.com/company}EmployeeInfo" maxOccurs="unbounded" minOccurs="0"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/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.ctlok.pro.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>
* &lt;simpleType name="EmployeeTitle">
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}string">
* &lt;enumeration value="CEO"/>
* &lt;enumeration value="Manger"/>
* &lt;enumeration value="Supervisor"/>
* &lt;enumeration value="Clerk"/>
* &lt;/restriction>
* &lt;/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.ctlok.pro.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.ctlok.pro.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://pro.ctlok.com/company", "EmployeeIdList");
private final static QName _EmployeeInfoList_QNAME = new QName("http://pro.ctlok.com/company", "EmployeeInfoList");

/**
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.ctlok.pro.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://pro.ctlok.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://pro.ctlok.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://pro.ctlok.com/company", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.ctlok.pro.ws.model;

將這些 Java Class 複製到 WAR project 內。

再建立一個 Java Class - 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.ctlok.pro.ws.model.EmployeeIdWrapper;
import com.ctlok.pro.ws.model.EmployeeInfo;
import com.ctlok.pro.ws.model.EmployeeInfoWrapper;
import com.ctlok.pro.ws.model.EmployeeTitle;
import com.ctlok.pro.ws.model.ObjectFactory;

@WebService(
name = "employeeLookupService",
serviceName = "employeeLookupService",
portName = "employeeLookupPort",
targetNamespace= "http://pro.ctlok.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;

}

}

完成了 Web Service Provider

@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)

以上這句注解很重要,預設是 WRAPPED,即是 JAX-WS 會替你自動包裝好 List Object,但我們在 XML Schema 已經設定了 Wrapper,所以不用設定為 WRAPPED

————– 分隔線 ————–

@XmlSeeAlso({ObjectFactory.class})

以上的注解則是確保 JAXB 正常運作,由 XML 轉為 ObjectObject 轉為 XML

————– 分隔線 ————–

@WebResult(name = "EmployeeInfoList", partName = "employeeInfoList")

以上的注解對應 WSDL 內的:

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

————– 分隔線 ————–

@WebResult(name = "EmployeeInfoList", partName = "employeeInfoList")

以上的注解則對應 WSDL 內的:

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


現在將 WAR project 放上 Application Server 並運行 http://localhost/employeeLookupService?wsdl。如果發現找不到 wsdl 檔案即是 wsdl 放的位置不正確。

成功找到後可以使用 soapUI 去測試一下,傳送以下 SOAP XMLServer:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:com="http://pro.ctlok.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 回傳以下 XML 即代表成功設定:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<EmployeeInfoList xmlns="http://pro.ctlok.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>

有時間再講解一下 Java Web Service Client 如何設定。

相閞書籍: Java Web Services: Up and RunningJava Soa CookbookJava Web Services: Up and Running