Fork me on GitHub

Programming Design Notes

Java 使用 Bottom Up 和 JAX-WS 來部署 Web Service Provider

| Comments

網路上關於 Web Service 的資訊不是很多,中文的 Web Service 資源更是少之有少,可能是因為部署 Web Service 是一件麻煩的事,而且性能又不是太好,但 Web Service 也有很多好處。不用同一種程式語言也可以互相交換數據,執行程序,有標準的格式,大多數語言都提供了 Library 去操作。Web Service 大多數用於對外的對像,例如: 銀行和銀行之間的數據交換亦可以通過 Web Service 做到。

有 2 種主要的方式去設計一個 Web Service,就是 Top DownBottom Up,究竟有什麼分別呢?

Top Down 是由定義 Object (物件) 和 Interface (介面) 做起的,Object (物件) 你可以想像為傳遞數據的一個容器,而這個物件是由 XML Schema (XSD) 去描述的。Interface (介面) 則是描述這個 Web Service 提供什麼服務,服務需要傳入一個什麼 Object (物件) 和 返回一個什麼 Object (物件)等等描述,這些都是由 Web Services Description Language (WSDL) 去描述的。做好這 2 項工作後就需要製作和 XML Schema 描述一樣的 Object Class,亦要製作和 Web Services Description Language 描述一樣的 Functional Class。最後當然要寫程式式碼和放上 Application Server

Bottom Up 就比較簡單了,是由 Object ClassFunctional Class 先做起,做起後放上 Application ServerApplication Server 自動產生出 XML Schema (XSD)Web Services Description Language (WSDL)。不懂得 XML SchemaWeb Services Description Language 也一樣可以製作出一個 Web Service

最好當然是對 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

首先當然講解一下最簡單的 Bottom Up 方法,首先要在 IDE 建立一個 WAR project,加入一個 Java Class:
package sample.ws;

import javax.jws.WebMethod;
import javax.jws.WebService;

@WebService(
name = "CalculaterService",
serviceName = "CalculaterService",
portName = "CalculaterServicePort",
targetNamespace = "http://pro.ctlok.com/CalculaterService/")
public class CalculaterService {

@WebMethod
public int add(int i, int k) {
return i + k;
}

@WebMethod
public int subtract(int i, int k) {
return i - k;
}

}

就這樣完成了一個最簡單的 Web Service

@WebService(
name = "CalculaterService",
serviceName = "CalculaterService",
portName = "CalculaterServicePort",
targetNamespace = "http://pro.ctlok.com/CalculaterService/")

用於通知 Java EE 容器識別這個 Class 是一個 Web Service

  • name: 是在 Application Server 上的名稱
  • serviceName: 是在 URLWSDL 上顯示的名稱
  • portName: 是在 WSDL 上顯示的名稱
  • targetNamespace : 是在 WSDL 上顯示的命名空間,令 XML 內的名稱不會相同

@WebMethod

是指定這個 MethodWeb Service 的其中一個 Function

CalculaterService 亦可以是一個 EJB Session Bean:

package sample.ws;

@Local
public interface CalculaterService{

public int add(int i, int k);
public int subtract(int i, int k);

}

package sample.ws;

import javax.jws.WebMethod;
import javax.jws.WebService;

@Stateless
@WebService(
name = "CalculaterService",
serviceName = "CalculaterService",
portName = "CalculaterServicePort",
targetNamespace = "http://pro.ctlok.com/CalculaterService/")
public class CalculaterServiceBean implements CalculaterService{

@WebMethod
public int add(int i, int k) {
return i + k;
}

@WebMethod
public int subtract(int i, int k) {
return i - k;
}

}

放上 Application Server 後使用瀏覽器開啟: http://localhost/CalculaterService?wsdl 會顯示出以下的 WSDL
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="CalculaterService" targetNamespace="http://pro.ctlok.com/CalculaterService/"
xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://pro.ctlok.com/CalculaterService/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">

<types>
<xsd:schema>
<xsd:import namespace="http://pro.ctlok.com/CalculaterService/"
schemaLocation="CalculaterService_schema1.xsd" />
</xsd:schema>
</types>

<message name="addResponse">
<part name="parameters" element="tns:addResponse">
</part>
</message>

<message name="add">
<part name="parameters" element="tns:add">
</part>
</message>

<message name="subtractResponse">
<part name="parameters" element="tns:subtractResponse">
</part>
</message>

<message name="subtract">
<part name="parameters" element="tns:subtract">
</part>
</message>

<portType name="CalculaterService">
<operation name="add">
<input message="tns:add">
</input>
<output message="tns:addResponse">
</output>
</operation>

<operation name="subtract">
<input message="tns:subtract">
</input>
<output message="tns:subtractResponse">
</output>
</operation>
</portType>

<binding name="CalculaterServicePortBinding" type="tns:CalculaterService">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<operation name="add">
<soap:operation soapAction="" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
<operation name="subtract">
<soap:operation soapAction="" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
</binding>

<service name="CalculaterService">
<port name="CalculaterServicePort" binding="tns:CalculaterServicePortBinding">
<soap:address
location="http://localhost/CalculaterService" />
</port>
</service>
</definitions>

Application Server 除了自動產生出 WSDL 外,亦會產生出 XML Schema

使用瀏覽器開啟: http://localhost/CalculaterService/CalculaterService_schema1.xsd 會顯示出以下的 XML Schema:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://pro.ctlok.com/CalculaterService/" version="1.0"
targetNamespace="http://pro.ctlok.com/CalculaterService/">

<xs:element name="add" type="tns:add"></xs:element>
<xs:element name="addResponse" type="tns:addResponse"></xs:element>
<xs:element name="subtract" type="tns:subtract"></xs:element>
<xs:element name="subtractResponse" type="tns:subtractResponse"></xs:element>

<xs:complexType name="add">
<xs:sequence>
<xs:element name="arg0" type="xs:int"></xs:element>
<xs:element name="arg1" type="xs:int"></xs:element>
</xs:sequence>
</xs:complexType>

<xs:complexType name="addResponse">
<xs:sequence>
<xs:element name="return" type="xs:int"></xs:element>
</xs:sequence>
</xs:complexType>

<xs:complexType name="subtract">
<xs:sequence>
<xs:element name="arg0" type="xs:int"></xs:element>
<xs:element name="arg1" type="xs:int"></xs:element>
</xs:sequence>
</xs:complexType>

<xs:complexType name="subtractResponse">
<xs:sequence>
<xs:element name="return" type="xs:int"></xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>

看到這是資料即是成功部署到 Web Service,接下來使用 soapUI 來模擬 Web Service Client

soapUI 下載地址: http://sourceforge.net/projects/soapui/files/

請選擇安裝程式去下載,完成安裝後打開 soapUI:
  1. File -> New soapUI Project
  2. Project name 輸入 localhost 後按 OK
  3. Project 上按滑鼠加鍵 -> Add WSDL
  4. WSDL Location 輸入 http://localhost/CalculaterService/CalculaterService.wsdl

現在應該看得到 2 個 Method 名稱,分別為 addsubtract
打開 addRequest 1 左邊顯示以下資料:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ws="http://pro.ctlok.com/CalculaterService/">
<soapenv:Header />
<soapenv:Body>
<ws:add>
<arg0>?</arg0>
<arg1>?</arg1>
</ws:add>
</soapenv:Body>
</soapenv:Envelope>

改為:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ws="http://pro.ctlok.com/CalculaterService/">
<soapenv:Header />
<soapenv:Body>
<ws:add>
<arg0>1</arg0>
<arg1>2</arg1>
</ws:add>
</soapenv:Body>
</soapenv:Envelope>

然後傳送出去,右邊會顯示出以下 XML 即代表成功:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<dlwmin:addResponse xmlns:dlwmin="http://pro.ctlok.com/CalculaterService/">
<return>3</return>
</dlwmin:addResponse>
</soapenv:Body>
</soapenv:Envelope>

其實在 Web ServiceFunction 不一定要傳入或返回基本型態 (short, int, long, float, double, char, byte, boolean),可以傳入或返回 Object (物件),例如:

Object Class:
package sample.ws;

public class Info{

private String firstName;
private String lastName;

public String getFirstName(){
return firstName;
}

public String getLastName(){
return lastName;
}

public void setFirstName(String firstName){
this.firstName = firstName;
}

public void setLastName(String lastName){
this.lastName = lastName;
}

}

Web Service:
@WebMethod
public String sayHello(Info info) {
return "Hello! " + info.getFirstName()
+ " " + info.getLastName();
}

@WebMethod
public Info getInfo(int id){
return findInfo(id);
}

有時間會再講解 Top Down 的做法。

相關書籍: Java Web Services: Up and RunningJava Soa CookbookBuilding Web Services with Java: Making Sense of XML, SOAP, WSDL, and UDDI (2nd Edition)