Web Services 是 Lotus Notes/Domino 7 的新特性。本文介紹新 Web Services 設計元素,展示如何在 Domino Designer 中創建這種設計元素,並描述瞭如何用 LotusScript 和 Java 代碼示例來實現 Web Services。
[編者注:本文描述 Web Services 的 Lotus Notes/Domino 7 Beta 2 實現。它可能並不準確反映 Lotus Notes/Domino 7 的 Gold 版本的特性或功能。]
Web 服務是可以通過在 Internet 上發送消息來調用的遠程操作的檔案。Web 服務供應商發佈用於查詢和使用的 Web 服務,而 Web 服務消費者調用來自這些服務的操作。Web 服務供應商提供了定義服務接口的 WSDL(Web Services Description Language,Web 服務描述語言)文檔。WSDL 文檔是 XML 格式的。接口的底層由供應商實現,但大多數供應商將接口映射爲支持的編程語言的過程調用。來自消費者的入站請求傳遞給底層代碼,然後結果返回給消費者。
Lotus Domino 將 WSDL 接口映射爲可以用 LotusScript 或 Java 編碼的類似代理的 Web 服務設計元素。要被使用,Web 服務必須位於啓用 HTTP 的 Domino 服務器上。(我們可以通過 Notes 客戶機預覽中的 HTTP 會話測試 Web 服務。)通過下列 Domino URL 命令之一進行訪問:
- ?OpenWebService 調用 Web 服務以響應通過 HTTP POST 發送的 SOAP 編碼消息。HTTP GET(例如,瀏覽器查詢)返回服務的名稱及其操作。
- ?WSDL 返回 WSDL 文檔以響應 HTTP GET。
本文描述 Lotus Notes/Domino 7 中的 Web 服務器設計元素,並提供了設計元素的 LotusScript 和 Java 示例。本文假設您是瞭解 LotusScript 或 Java 的經驗豐富的 Notes 應用程序開發人員。
讓我們看一個簡單的例子。給定數據庫名稱、視圖名稱和文檔號,我們的操作返回 Subject 項的內容。我們將調用操作 getNthSubject。
圖 1. getNthSubject 圖表
要讓該操作可用於外部世界,將其發佈在名爲 GetSubject 的 Web 服務中。GetSubject 可以包含任意數目的操作。例如,可能發現 getFirstSubject 和 getLastSubject 都有用。但現在只處理示例操作 getNthSubject。下列代碼段摘自描述包含這樣一個操作的 Web 服務的 WSDL 文檔。下面看一下代碼段及其註釋。
<wsdl:message name="getNthSubjectRequest"> (4) <wsdl:part name="dbname" type="xsd:string"/> (5) <wsdl:part name="viewname" type="xsd:string"/> (5) <wsdl:part name="n" type="xsd:int"/> (5) </wsdl:message> <wsdl:message name="getNthSubjectResponse"> (4) <wsdl:part name="getNthSubjectReturn" type="xsd:string"/> (6) </wsdl:message> <wsdl:portType name="GetSubjectPortType"> (1) <wsdl:operation name="getNthSubject" parameterOrder="dbname viewname n"> (2) <wsdl:input message="impl:getnthSubjectRequest" name="GetNthSubjectRequest"/> (3) <wsdl:output message="impl:getNthSubjectResponse" name="GetNthSubjectResponse"/> (3) </wsdl:operation> </wsdl:portType> |
首先看 portType 元素 (1),它定義了服務的操作集合。我們的服務只有一個 portType,它只有一個操作 getNthSubject (2)。該操作有兩個“消息” (3):一個用於輸入,一個用於輸出。消息在消息元素 (4) 中定義。我們看到,輸入消息有三個部分 (5):兩個名爲 dbname 和 viewname 的字符串,一個名爲 n 的 int。輸出消息只有一個部分 (6),即名爲 getNthSubjectReturn 的字符串。
所以,我們的操作有三個輸入部分和一個輸出部分,這非常巧妙地映射爲具有三個只讀參數和一個返回值的過程。在 LotusScript 中,這樣的過程將通過下列函數來定義:
Public Function getNthSubject(dbname As String, viewname As String, n As Long) As String
在 Java 中,通過下列方法來定義:
public String getNthSubject(String dbname, String viewname, int n)
在 Domino Designer 中創建 Web 服務設計元素有多種可行方法。可以完全用 LotusScript 或 Java 來編碼。在這種情況下,保存設計元素會生成反映 LotusScript 或 Java 代碼的 WSDL 文檔。或者可以導入現有的 WSDL 文檔。在這種情況下,在導入的 WSDL 中會生成反映操作的 LotusScript 或 Java 代碼。Web 服務設計元素保存 WSDL 文檔以及代碼。如果公共接口未更改,則 WSDL 文檔保持不變。如果在編碼中更改了影響公共接口的部分,則會生成新的 WSDL。
在 Domino Designer 中,Web 服務設計元素駐留在 Shared 代碼中的 Agents 之下。Web 服務設計窗口的外觀與代理設計窗口非常相似。單擊“New Web Service”按鈕以創建新 Web 服務。雙擊現有 Web 服務以對其進行編輯。
圖 2. New Web Service
Web Services Property 框有三個類似代理的選項卡。下面是 Basics 選項卡:
圖 3. Web Services Property 框
名稱是必需的。別名和備註可以提供也可以不提供。如果編碼更改引起新 WSDL 的生成,則會收到警告消息。
PortType 類是定義映射爲 WSDL 操作的過程的類名。這些過程在 LotusScript 中必須是公共函數或子程序,在 Java 中必須是公共方法。私有函數、子程序和方法不通過 Web 服務接口暴露。不能在屬性框中輸入 PortType 類,除非已經通過編碼或導入 WSDL 創建了該類。稍後將詳細介紹代碼。
Security 選項卡幾乎與代理 Security 選項卡完全一樣。稍後將詳細介紹 Security。
Advanced 選項卡具有用於定義 Web 服務和生成 WSDL 的其他信息。稍後將詳細介紹。
編輯器窗格與代理的編輯器窗格相似。在右下拉框中,可以選擇 LotusScript 或 Java。下面顯示的是選擇 Java。左邊是 Objects 和 Reference 窗格。
圖 4. Web 服務 (Java)
使用“Import WSDL”按鈕以基於現有 WSDL 創建新 Web 服務。“Show WSDL”按鈕編譯對 Web 服務的任何更改,並顯示定義公共接口的 WSDL 文檔。“Export WSDL”按鈕編譯對 Web 服務的任何更改,並導出定義公共接口的 WSDL 文檔。還可以通過保存或關閉 Web 服務來編譯。僅當公共接口更改時,纔會重新生成 WSDL。
Web 服務的代碼具有下列元素:
- 實現代碼的類定義。該類必須成爲屬性框的 Basics 選項卡中命名的 PortType 類,且必須是公共的。
- 在類中,Web 服務中每個操作的過程(函數、子程序或方法)定義。這些過程必須是公共的。不想放在接口中的支持過程必須是私有的。
- LotusScript 中 lsxsd.lss 的包含。Java 中 lotus.domino.types.* 的導入。
- 訪問 Domino Objects 對象時 NotesSession (LotusScript) 或 Session (Java) 的初始化。對於 LotusScriptobject,此操作最好是在新塊中完成,對於 Java,最好是在無參構造函數中完成。對於 Java,使用 WebServiceBase.getCurrentSession() 來獲得 Session 對象。我們可能還想使用 Session.getAgentContext() 來獲得 AgentContext 對象。WebServiceBase 等同於 JavaAgent,但 Web 服務不能訪問對象。惟一有用的方法是靜態 getCurrentSession()。
下面是 LotusScript 代碼的模板,其中 Web 服務包含一個操作。該操作是上述具有三個輸入參數和一個返回值的示例操作。
Option Public %INCLUDE "lsxsd.lss" Dim s As NotesSession Class GetSubject Sub NEW Set s = New NotesSession End Sub Function getNthSubject(dbname As String, viewname As String, n As Long) As String ! Code for doing the operation goes here End Function End Class |
下面是 Java 代碼的模板,其中 Web 服務包含一個操作。構造函數必須是默認構造函數(無參數)。其他構造函數被忽略。
import lotus.domino.*; import lotus.domino.types.*; public class GetSubject { Session s; public GetSubject() { s = WebServiceBase.getCurrentSession(); } public String getNthSubject(String dbname, String viewname, int n) { // Code for doing operation goes here } } |
現在我們將擴展示例以包括工作代碼。下面是 LotusScript 代碼:
Option Public %INCLUDE "lsxsd.lss" Dim s As NotesSession Class GetSubject Sub NEW Set s = New NotesSession End Sub Function getNthSubject(dbname As String, viewname As String, n As Long) As String Dim db As NotesDatabase Dim view As NotesView Dim doc As NotesDocument Set db = s.GetDatabase("", dbname) If Not(db.IsOpen) Then getNthSubject = "Cannot open database " & dbname Exit Function End If Set view = db.GetView(viewname) If view Is Nothing Then getNthSubject = "Cannot open view " & viewname Exit Function End If Set doc = view.GetNthDocument(n) If doc Is Nothing Then getNthSubject = "Cannot get document " & n Exit Function End If If doc.HasItem("Subject") Then getNthSubject = doc.GetItemValue("Subject")(0) Else getNthSubject = "Document does not have Subject" End If End Function End Class |
下面是 Java 代碼:
import lotus.domino.*; import lotus.domino.types.*; public class GetSubject { Session s; public GetSubject() { s = WebServiceBase.getCurrentSession(); } public String getNthSubject(String dbname, String viewname, int n) { String subject = null; try { Database db = s.getDatabase(null, dbname); if (!db.isOpen()) subject = "Cannot open database " + dbname; else { View view = db.getView(viewname); if (view == null) subject = "Cannot open view " + viewname; else { Document doc = view.getNthDocument(n); if (doc == null) subject = "Cannot get document " + n; else { if (doc.hasItem("Subject")) subject = doc.getItemValueString("Subject"); else subject = "Document does not have Subject"; } } } } catch(Exception e) { subject = e.toString(); e.printStackTrace(); } return subject; } } |
最終,Web 服務設計元素必須駐留在運行 HTTP 的 Domino 7 服務器上。我們可以測試駐留於 Domino Designer 上的 Web 服務設計元素。首先必須在啓動 HTTP 的任何位置(比如,窗體)選擇 Web Browser 中的 Design - Preview。如果消費者與 Notes 客戶機位於同一機器上,則使用 127.0.0.1 作爲計算機地址。要扮演 Web 服務的消費者,必須在 HTTP 請求中向 Domino Web 服務的 URL 發送一條 SOAP 消息。URL 如下:
http://rperron300pl.notesdev.ibm.com/Webservices2.nsf/GetSubject?OpenWebService
SOAP 消息如下:
<SOAP-ENV:Envelope ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema" <SOAP-ENV:Body> <ns0:getNthSubject (1) SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns0="urn:DefaultNamespace"> <dbname xsi:type="xsd:string">Webservices2</dbname> (2) <viewname xsi:type="xsd:string">Main View</viewname> (2) <n xsi:type="xsd:int">2</n> (2) </ns0:getNthSubject> </SOAP-ENV:Body> </SOAP-ENV:Envelope> |
在本例中,SOAP 消息 (1) 標識操作, (2) 並提供輸入部分的值。顯示在 SOAP-ENV:body 中的特定元素由 WSDL 綁定特徵確定,尤其是 SOAP 消息格式。(詳細信息請參閱下文的“高級屬性”)。
Web 服務的 WebSphere SDK 提供了調用 Web 服務和查看結果的工具。此 SDK 運行於 Eclipse 中。必須安裝:
WebSpere SDK(截止撰寫本文時)不與 Eclipse 3.0 一起使用。還可以使用 WebSphere Studio Application Developer 來調用 Web 服務。最新版本是 v5.1.2。
要支持 WebSphere SDK 工具,打開 Eclipse 並選擇 Run - Launch the Web Services Explorer。Web Services Explorer 加載後:
- 單擊 WSDL Page 圖標。它是右上角右箭頭之後的第三個圖標。WSDL Main 鏈接顯示在左邊的 Navigator 窗格中。
- 單擊 WSDL Main 鏈接。Open WSDL 框顯示在右窗格中。
- 用命令 ?WSDL 輸入 Web 服務的 URL,例如,
http://rperron300pl.notesdev.ibm.com/Webservices2.nsf/GetSubject?WSDL
,然後單擊 Go。我們想要 ?WSDL(而非 ?OpenWebService),因爲此處 Web Services Explorer 閱讀 WSDL 文檔。 - WSDL Binding Details 框顯示在右窗格中,它包含到 Web 服務定義的操作的鏈接。
- 單擊操作名稱,例如 getNthSubject。顯示 Invoke a WSDL Operation 框。
- 輸入輸入部分(參數)的值,然後單擊 Go。
響應返回在底部 (Status) 窗格中。調用示例 Web 服務之後,Web Services Explorer 的外觀如下。
圖 5. Web Services Explorer
Actions 框的右上角有 Source 鏈接。單擊 Source 顯示實際 SOAP 消息。可以修改 SOAP 消息,然後單擊 Go 將其發送。單擊右上角的 Form 以返回到原始顯示。Status 框也有 Source 鏈接,該鏈接允許查看 SOAP 響應,如果狀態指示沒有要顯示的內容(且期望響應),則代碼可能失敗了。
運行 Web 服務之後,檢查服務器控制檯或 log.nsf 以獲得錯誤消息。可以通過插入 MessageBox 語句來登錄或調試,從而打印到服務器控制檯或 log.nsf。(不要使用 Beta 2 中的 Print 打印語句。這些語句轉到代理的 HTTP 流並破壞 SOAP 響應。)
屬性框的 Advanced 選項卡影響 WSDL 文檔反映的 Web 服務定義。
圖 6. Web Services Property 框的 Advanced 選項卡
可以提供想要爲端口類型、服務元素和服務端口命名的名稱。例如,可以將 GetSubject 用作所有名稱。爲了清晰,使用反映元素類型的後綴。在屬性框中提供名稱時,這些名稱插入到生成的 WSDL 文檔中。如果導入 WSDL 文檔,則 WSDL 文檔中的名稱自動插入到屬性框中。
下面是 GetSubject 示例完整的 WSDL 文檔。註釋部分是 Web Services Property 框的 Advanced 選項卡反映的內容。
<?xml version="1.0" encoding="UTF-8"?> <wsdl:definitions targetNamespace="urn:DefaultNamespace" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="urn:DefaultNamespace" xmlns:intf="urn:DefaultNamespace" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <wsdl:message name="getNthSubjectResponse"> <wsdl:part name="getNthSubjectReturn" type="xsd:string"/> (6) </wsdl:message> <wsdl:message name="getNthSubjectRequest"> <wsdl:part name="dbname" type="xsd:string"/> (6) <wsdl:part name="viewname" type="xsd:string"/> (6) <wsdl:part name="n" type="xsd:int"/> (6) </wsdl:message> <wsdl:portType name="GetSubjectPortType"> (1) <wsdl:operation name="getNthSubject" parameterOrder="dbname viewname n"> <wsdl:input message="impl:getNthSubjectRequest" name="getNthSubjectRequest"/> <wsdl:output message="impl:getNthSubjectResponse" name="getNthSubjectResponse"/> </wsdl:operation> </wsdl:portType> <wsdl:binding name="GetSubjectPortSoapBinding" type="impl:GetSubjectPortType"> <wsdlsoap:binding style="rpc" (4) transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="getNthSubject"> <wsdlsoap:operation soapAction=""/> (7) <wsdl:input name="getNthSubjectRequest"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" (5) namespace="urn:DefaultNamespace" use="encoded"/> (5) </wsdl:input> <wsdl:output name="getNthSubjectResponse"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" (5) namespace="urn:DefaultNamespace" use="encoded"/> (5) </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="GetSubjectElement"> (2) <wsdl:port binding="impl:GetSubjectPortSoapBinding" name="GetSubjectPort"> (3) <wsdlsoap:address location="http://localhost"/> </wsdl:port> </wsdl:service> </wsdl:definitions> |
(1) 端口類型定義一組操作。WSDL 文檔包含 wsdl:portType 的名稱屬性,對應於 Port 類型名稱高級屬性。 (2) 服務標識支持的端口。WSDL 文檔包含 wsdl:service 的名稱屬性,對應於 Service 元素名稱高級屬性。如果 WSDL 是通過 Export WSDL、Show WSDL 或 Domino Designer 中的瀏覽器預覽獲得的,則 wsdlsoap:address 的位置屬性不正確;如果 WSDL 是通過 ?WSDL URL 命令從服務器中獲得的,則位置屬性是正確的。 (3) 端口標識綁定,而綁定又反過來標識端口類型並提供了其他信息。WSDL 文檔包含 wsdl:service 中 wsdl:port 的名稱屬性。Domino 允許一個服務,並且每個服務允許一個端口。 (4) 兩種編程模型和四種 SOAP 消息格式可用。RPC 編程模型允許四種 SOAP 消息格式:RPC/encoded、RPC/literal、Doc/literal 和 Wrapped(Doc/encoded 的實用程序是第五種可用格式,但因爲不好理解,所以此處不支持)。Message 編程模型強制 Doc/literal 消息格式,但只作爲提示;在基於 Message 的 Web 服務中傳遞的實際 SOAP 消息格式沒有發佈,但通過消費者和供應商之間的私有合同來規定。 wsdlsoap:binding 的樣式屬性設置如下:
- 對於 RPC/encoded 和 RPC/literal,wsdlsoap:binding style="rpc"
- 對於 Doc/literal 和 Wrapped,wsdlsoap:binding style="document"
(5) 在 wsdl:binding 下的輸入和輸出元素中,wsdlsoap:body 的使用屬性設置如下:
- 對於 RPC/encoded,wsdlsoap:body use="encoded"。在這一情況下,有一個 encodingStyle 屬性。
- 對於 RPC/literal、Doc/literal 和 Wrapped,wsdlsoap:body use="literal"。在這些情況下,沒有 encodingStyle 屬性。
(6) 對於 RPC/encoded 和 RPC/literal,每個消息部分通過直接引用 XMLSchema 名稱空間(例如,type="xsd:string" 或 type="xsd:int")或 WSDL "types" 部分(本例未顯示)中定義的複雜類型來定義數據類型。
對於 Doc/literal,每個消息部分引用先前定義的數據元素。下面是帶有指定 Doc/literal 的示例 WSDL 中的代碼段。在 wsdl:types 下,每個輸出部分在按照過程代碼中相應參數命名的元素中定義,輸出部分在按照過程加“Return”命名的元素中定義。
<wsdl:types> <schema targetNamespace="urn:DefaultNamespace" xmlns="http://www.w3.org/2001/XMLSchema"> <element name="dbname" type="xsd:string"/> <element name="viewname" type="xsd:string"/> <element name="n" type="xsd:int"/> <element name="getNthSubjectReturn" type="xsd:string"/> </schema> </wsdl:types> <wsdl:message name="getNthSubjectResponse"> <wsdl:part element="impl:getNthSubjectReturn" name="getNthSubjectReturn"/> </wsdl:message> <wsdl:message name="getNthSubjectRequest"> <wsdl:part element="impl:dbname" name="dbname"/> <wsdl:part element="impl:viewname" name="viewname"/> <wsdl:part element="impl:n" name="n"/> </wsdl:message> |
對於 Wrapped,每個消息有一個部分,該部分引用先前定義的 complexType 類型的元素,元素按照使用它且無屬性的操作命名。下面是具有指定 Wrapped 的 WSDL 代碼段。
<wsdl:types> <schema targetNamespace="urn:DefaultNamespace" xmlns="http://www.w3.org/2001/XMLSchema"> <element name="getNthSubject"> <complexType> <sequence> <element name="dbname" type="xsd:string"/> <element name="viewname" type="xsd:string"/> <element name="n" type="xsd:int"/> </sequence> </complexType> </element> <element name="getNthSubjectResponse"> <complexType> <sequence> <element name="getNthSubjectReturn" type="xsd:string"/> </sequence> </complexType> </element> </schema> </wsdl:types> <wsdl:message name="getNthSubjectResponse"> <wsdl:part element="impl:getNthSubjectResponse" name="parameters"/> </wsdl:message> <wsdl:message name="getNthSubjectRequest"> <wsdl:part element="impl:getNthSubject" name="parameters"/> </wsdl:message> |
有關 SOAP 格式的完美討論,參閱由 Russell Butek 撰寫的 developerWorks 文章“ Which style of WSDL should I use?”。
(7) soapAction="" if "Include operation name in SOAP action" 保留不選中。如果選中該選項,則 soapAction 指定操作的名稱,例如:
<wsdlsoap:operation soapAction="getNthSubject"/>
Web 服務安全性類似於從 Web 中調用的服務器代理的安全性。下面是 Web Services Property 框中 Security 選項卡的示例。
圖 7. Web Services Property 框中的 Security 選項卡
前兩行確定誰在運行 Web 服務,即誰是有效用戶:如果兩行都不使用,則有效用戶是 Web 服務的所有者(編輯或簽名設計元素的最後一個用戶)。
- 如果選中“Run as Web user”選項,則有效用戶是協商網絡訪問包含 Web 服務的數據庫的用戶:如果數據庫允許匿名訪問則爲 Anonymous,否則爲提供給認證過程的名稱。
- 如果填充了“Run on behalf of”字段,則有效用戶是該用戶。
Web 服務的消費者必須能夠協商訪問服務器。如果 HTTP 端口允許匿名訪問,則訪問自動進行。否則,用戶必須提供有效的名稱和 Internet 密碼進行認證。數據庫 ACL 必須給予有效用戶至少一個選中 Read 公共文檔的 Depositor 訪問權。
“Compile Java code with debugging information”允許從 Java 調試器(比如支持 JPDA (Java Platform Debugger Architecture,Java 平臺調試器架構)的 Eclipse)連接到運行中的 Web 服務。Java 調試是版本 7 的新特性,且僅工作在 Notes 客戶機上。爲了調試,Web 服務必須駐留在 Notes 客戶機上。通過選擇任何位置的 Web Browser 中的 Design - Preview,啓動客戶機上的 HTTP 任務。調用 Web 服務。Web 服務應包含只調試代碼,以暫停片刻。然後將調試器連接到運行中的 Web 服務。
對於 LotusScript Web 服務,“Allow remote debugging”代替 Java 調試行。Web 服務的遠程調試與代理的遠程調試相同。在這種情況下,Web 服務必須駐留在服務器上。
“Profile this Web service”允許收集 Domino Objects 用去的時間。要報告所選 Web 服務的結果,選擇 Design - View Profile Results。概要分析是版本 7 的新特性,用於 LotusScript 和 Java 編碼的代理,以及 Web 服務。
“Set runtime security level”框允許三個級別的安全性。編號越高的安全性級別允許潛在損壞操作,比如寫入文件系統,操作環境變量,等等。
對於“Default access to this Web service”,可以允許所有讀者及以上用戶,或者可以列舉具有訪問權的用戶。
Web 服務使用與代理相同的框架。在後端,大多數但非全部代理上下文應用於 Web 服務。我們已經看到如何在 Java 中獲得 Session 和 AgentContext 對象,如何在 LotusScript 中獲得 NotesSession 對象。下面是與 Web 服務關聯的其他主要上下文元素。
上下文元素 | Java | LotusScript |
當前 Web 服務 | AgentContext.getCurrentAgent(); | NotesSession.CurrentAgent |
當前數據庫 | AgentContext.getCurrentDatabase(); | NotesSession.CurrentDatabase |
打印到服務器控制檯和 log.nsf | System.out | Messagebox |
Web 服務名稱和別名 | Agent.getName(); | NotesAgent.Name |
Web 服務所有者(完整名稱) | Agent.getOwner(); | NotesAgent.Owner |
Web 服務所有者(共用名稱) | Agent.getCommonOwner(); | NotesAgent.CommonOwner |
Web 服務運行時所有者 | Agent.getOnBehalfOf(); | NotesAgent.OnBehalfOf |
Web 服務備註 | Agent.getComment(); | NotesAgent.Comment |
Web 服務 HTTP URL | Agent.getHttpURL(); | NotesAgent.HttpURL |
Web 服務 Notes URL | Agent.getNotesURL(); | NotesAgent.NotesURL |
Web 服務父數據庫 | Agent.getParent(); | NotesAgent.Parent |
Web 服務鎖持有者 | Agent.getLockHolders(); | NotesAgent.LockHolders |
Web 服務設計元素與代理一樣,可以加鎖和解鎖。
下面是 LotusScript 示例,展示了獲得與 Web 服務上下文關聯的屬性。Web 服務具有三個操作。
Option Public %INCLUDE "lsxsd.lss" Dim s As NotesSession Dim agent As NotesAgent Class GetAgentContext Sub NEW Set s = New NotesSession Set agent = s.CurrentAgent End Sub Function getAgentName() As String getAgentName = agent.Name End Function Function getEffectiveUserName() As String getEffectiveUserName = s.EffectiveUserName End Function Function getDatabaseFileName() As String Dim db As NotesDatabase Set db = s.CurrentDatabase getDatabaseFileName = db.FileName End Function End Class |
下面是 Java 代碼。
import lotus.domino.*; import lotus.domino.types.*; public class GetAgentContext { Session s; AgentContext ac; public GetAgentContext() { s = WebServiceBase.getCurrentSession(); try { ac = s.getAgentContext(); } catch(Exception e) { e.printStackTrace(); } } public java.lang.String getAgentName() { String agentName = null; try { Agent agent = ac.getCurrentAgent(); agentName = agent.getName(); } catch(Exception e) { e.printStackTrace(); } return agentName; } public java.lang.String getEffectiveUserName() { String userName = null; try { userName = ac.getEffectiveUserName(); } catch(Exception e) { e.printStackTrace(); } return userName; } public java.lang.String getCurrentDatabase() { String dbFileName = null; try { Database db = ac.getCurrentDatabase(); dbFileName = db.getFileName(); } catch(Exception e) { e.printStackTrace(); } return dbFileName; } } |
使用下列模型的操作不需要複雜數據類型:
- 單個標量輸出值(或無輸出值)
- 標量輸入值(或無輸入值)
返回多個標量輸出值或接受多個標量輸入值的操作需要複雜數據類型。使用複雜數據類型允許移動大的、各種不同的數據結構。
下面幾節討論複雜數據類型:
- 數組
- 類
- Inout 和輸出參數
數組
數組映射爲按數據類型加後綴的名爲 ArrayOf 的 complexType WSDL 元素。WSDL 將 complexType 元素定義爲數組。
例如,按 Java 方法實現的下列操作返回 String 數組。
public java.lang.String[] getAll() { String[] info = new String[3]; try { info[0] = ac.getEffectiveUserName(); info[1] = s.getPlatform(); info[2] = s.getNotesVersion(); } catch(Exception e) { e.printStackTrace(); } return info; } |
Java String 數組映射爲名爲 ArrayOf_xsd_string 的 WSDL complexType 元素,該元素被定義爲 string 類型的數組。定義 getAll 操作 (getAllResponse) 返回值的消息有一個部分,其類型爲 ArrayOf_xsd_string。
- <wsdl:types> - <schema targetNamespace="urn:DefaultNamespace" xmlns="http://www.w3.org/2001/XMLSchema"> <import namespace="http://schemas.xmlsoap.org/soap/encoding/" /> - <complexType name="ArrayOf_xsd_string"> - <complexContent> - <restriction base="soapenc:Array"> <attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:string[]" /> </restriction> </complexContent> </complexType> </schema> </wsdl:types> - <wsdl:message name="getAllResponse"> <wsdl:part name="getAllReturn" type="impl:ArrayOf_xsd_string" /> </wsdl:message> |
在 LotusScript 中,不能爲 Web 服務消費者返回數組。語言規則要求數據返回值必須定義爲不提供足夠信息的 Variant,以便在生成 WSDL 時解釋類型。解決方案是將數組放在類中,如下所示。
Option Public %INCLUDE "lsxsd.lss" Dim s As NotesSession Class infoArray Public info() As String End Class Class GetSessionInfo Sub NEW Set s = New NotesSession End Sub Function getItAll() As infoArray Set getItAll = New infoArray Redim getItAll.info(1 To 3) getItAll.info(1) = s.EffectiveUserName getItAll.info(2) = s.Platform getItAll.info(3) = s.NotesVersion End Function End Class |
類
類映射爲按類命名的 complexType WSDL 元素。下面的 Java 示例提供與 Arrays 示例相同的數據,但不是返回數組,而是返回對象。
public InfoClass getAll2() { InfoClass info = new InfoClass(); try { info.effectiveUserName = ac.getEffectiveUserName(); info.platform = s.getPlatform(); info.notesVersion = s.getNotesVersion(); } catch(Exception e) { e.printStackTrace(); } return info; } public class InfoClass { public String effectiveUserName; public String platform; public String notesVersion; } |
Java 類 InfoClass 映射爲同名的 complexType。complexType 有三個元素,每個元素的類型都爲 xsd:string,按 Java InfoClass 類中的公共數據元素命名。
- <wsdl:types> - <schema targetNamespace="urn:DefaultNamespace" xmlns="http://www.w3.org/2001/XMLSchema"> <import namespace="http://schemas.xmlsoap.org/soap/encoding/" /> - <complexType name="InfoClass"> - <sequence> <element name="notesVersion" nillable="true" type="xsd:string" /> <element name="platform" nillable="true" type="xsd:string" /> <element name="effectiveUserName" nillable="true" type="xsd:string" /> </sequence> </complexType> </schema> </wsdl:types> - <wsdl:message name="getAll2Response"> <wsdl:part name="getAll2Return" type="impl:InfoClass" /> </wsdl:message> |
下面是等價的 LotusScript 示例。
Option Public %INCLUDE "lsxsd.lss" Dim s As NotesSession Class InfoClass Public EffectiveUserName As String Public Platform As String Public NotesVersion As String End Class Class GetSessionInfo Sub NEW Set s = New NotesSession End Sub Function getItAll2() As InfoClass Set getItAll2 = New InfoClass getItAll2.EffectiveUserName = s.EffectiveUserName getItAll2.Platform = s.Platform getItAll2.NotesVersion = s.NotesVersion End Function End Class |
Inout 和輸出參數
當輸出消息有一個部分時,不管它是簡單類型還是複雜類型,輸出都映射爲函數或方法的返回值。如果輸出消息有多個部分,則輸出映射爲參數,而非返回值。完整的映射取決於輸入部分以及輸入部分與輸出部分的組合。如果第一個輸出部分與任何輸入都不匹配,且其餘輸出部分與輸入部分相匹配,則第一個輸出部分映射爲函數或方法的返回值,而其餘部分映射爲 inout 參數。
否則,匹配的輸入和輸出部分映射爲 inout 參數,不匹配的輸入部分映射爲輸入參數,不匹配的輸出部分映射爲輸出參數。在這種情況下,沒有返回值,而且對於 LotusScript,使用子程序而非函數。
下列 WSDL 代碼段是第一個示例的變種,它返回輸入值作爲響應:
- <wsdl:message name="getNthSubjectResponse"> <wsdl:part name="getNthSubjectReturn" type="xsd:string" /> (1) <wsdl:part name="dbname" type="xsd:string" /> (2) <wsdl:part name="viewname" type="xsd:string" /> (3) <wsdl:part name="n" type="xsd:int" /> (4) </wsdl:message> - <wsdl:message name="getNthSubjectRequest"> <wsdl:part name="dbname" type="xsd:string" /> (2) <wsdl:part name="viewname" type="xsd:string" /> (3) <wsdl:part name="n" type="xsd:int" /> (4) </wsdl:message> |
(1) 一個輸出部分與輸入部分不匹配 —— getNthSubjectReturn。該部分映射爲函數或方法返回值。 (2) (3) (4) 其餘三個輸出部分 —— dbname、viewname 和 n —— 與三個輸入部分相同。這幾個部分映射爲 inout 參數。
Inout 和輸出參數不能是基本數據類型。標準 Java 提供包 javax.xml.rpc.holders,其中具有保存各種類型的 inout 和輸出參數的方法。Lotus Domino 將 inout 和輸出參數映射爲下列類,如下所示:
BigDecimalHolder | CalendarHolder | LongHolder |
BigIntegerHolder | DoubleHolder | LongWrapperHolder |
BooleanHolder | DoubleWrapperHolder | ObjectHolder |
BooleanWrapperHolder | FloatHolder | QNameHolder |
ByteArrayHolder | FloatWrapperHolder | ShortHolder |
ByteHolder | IntegerWrapperHolder | ShortWrapperHolder |
ByteWrapperHolder | IntHolder | StringHolder |
這些類有一個應用程序可以獲得和設置的公共變量“value”。下例是 getNthSubject 示例的變種,它像以前一樣返回一個 String,但通過使用 StringHolder 和 IntHolder 類生成三個 inout 參數。這些參數的值作爲 SOAP 響應傳回給消費者。
import lotus.domino.*; import lotus.domino.types.*; public class GetSubject { Session s; public GetSubject() { s = WebServiceBase.getCurrentSession(); } public String getNthSubject(javax.xml.rpc.holders.StringHolder dbname, javax.xml.rpc.holders.StringHolder viewname, javax.xml.rpc.holders.IntHolder n) { String subject = null; try { Database db = s.getDatabase(null, dbname.value); if (!db.isOpen()) subject = "Cannot open database " + dbname.value; else { View view = db.getView(viewname.value); if (view == null) subject = "Cannot open view " + viewname.value; else { Document doc = view.getNthDocument(n.value); if (doc == null) subject = "Cannot get document " + n.value; else { if (doc.hasItem("Subject")) subject = doc.getItemValueString("Subject"); else subject = "Document does not have Subject"; } } } } catch(Exception e) { e.printStackTrace(); } return subject; } } |
對於 LotusScript,include 文件 lsxsd.lss 爲 inout 和輸出參數定義了下列持有者類。
BOOLEAN_HOLDER | LONG_HOLDER |
BOOLEANARRAY_HOLDER | LONGARRAY_HOLDER |
BYTE_HOLDER | SINGLE_HOLDER |
BYTEARRAY_HOLDER | SINGLEARRAY_HOLDER |
DOUBLE_HOLDER | STRING_HOLDER |
DOUBLEARRAY_HOLDER | STRINGARRAY_HOLDER |
INTEGER_HOLDER | VARIANT_HOLDER |
INTEGERARRAY_HOLDER | VARIANTARRAY_HOLDER |
這些類有一個應用程序可以獲得和設置的公共變量“Value”。下一個 LotusScript 示例與前面的 Java 示例相同。持有者類用於三個 inout 參數。
Option Public %INCLUDE "lsxsd.lss" Dim s As NotesSession Class GetSubject Sub NEW Set s = New NotesSession End Sub Function getNthSubject(dbname As String_Holder, _ viewname As String_Holder, _ n As Long_Holder) As String Dim db As NotesDatabase Dim view As NotesView Dim doc As NotesDocument Set db = s.GetDatabase("", dbname.Value) If Not(db.IsOpen) Then getNthSubject = "Cannot open database " & _ dbname.Value Exit Function End If Set view = db.GetView(viewname.Value) If view Is Nothing Then getNthSubject = "Cannot open view " & _ viewname.Value Exit Function End If Set doc = view.GetNthDocument(n.Value) If doc Is Nothing Then getNthSubject = "Cannot get document " & _ n.Value Exit Function End If If doc.HasItem("Subject") Then getNthSubject = doc.GetItemValue("Subject")(0) Else getNthSubject = "Document does not have Subject" End If End Function End Class |
基本數據類型及其 XSD 對應物一般來回映射。例外情況是導入的 SOAPENC 數據類型映射爲對象。但是,對象在輸出到生成的 WSDL 時,映射爲 XSD 數據類型。
導入的 WSDL | Java 數據類型 LotusScript 數據類型 |
生成的 WSDL |
xsd:boolean | boolean Boolean |
xsd:boolean |
soapenc:boolean | java.lang.Boolean XSD_BOOLEAN (1) |
xsd:boolean |
xsd:byte | byte XSD_BYTE (2) |
xsd:byte |
soapenc:byte | java.lang.Byte XSD_BYTE |
xsd:byte |
xsd:double | double Double |
xsd:double |
soapenc:double | java.lang.Double XSD_DOUBLE |
xsd:double |
xsd:float | float Single |
xsd:float |
soapenc:float | java.lang.Float XSD_FLOAT |
xsd:float |
xsd:int | int Long |
xsd:int |
soapenc:int | java.lang.Integer XSD_INT |
xsd:int |
xsd:long | long XSD_LONG (3) |
xsd:long |
soapenc:long | java.lang.Long XSD_LONG |
xsd:long |
xsd:short | short Integer |
xsd:short |
soapenc:short | java.lang.Short XSD_SHORT |
xsd:short |
xsd:string | java.lang.String (4) String |
xsd:string |
soapenc:string | java.lang.String XSD_STRING |
xsd:string |
(1) Java 使用 java.lang 中定義的包裝器類:java.lang.Boolean、java.lang.Byte,等等。LotusScript 使用 lsxsd.lss 中定義的 XSD_ 類:XSD_BOOLEAN、XSD_BYTE,等等。LotusScript 類繼承下列方法:
Function GetValueAsString() As String
Sub SetValueAsString(value As String)
注意: 在未來的 Beta 版本中,名稱 SetValueAsString 將更改爲 SetValueFromString。
下面是返回 java.lang.Boolean 類型的操作的 Java 示例:
import lotus.domino.*; import lotus.domino.types.*; public class GetDatabaseInfo { Session s; AgentContext ac; Database db; public GetDatabaseInfo() { s = WebServiceBase.getCurrentSession(); try { ac = s.getAgentContext(); db = ac.getCurrentDatabase(); } catch(Exception e) { e.printStackTrace(); } } public Boolean doesViewExist(String viewName) { Boolean b = null; try { if (db.getView(viewName) == null) b = new Boolean(false); else b = new Boolean(true); } catch(Exception e) { e.printStackTrace(); } return b; } } |
LotusScript 中的對應操作返回 XSD_BOOLEAN 類型:
Option Public %INCLUDE "lsxsd.lss" Dim s As NotesSession Dim db As NotesDatabase Class GetDatabaseInfo Sub NEW Set s = New NotesSession Set db = s.CurrentDatabase End Sub Function DoesViewExist(viewName As String) As XSD_BOOLEAN Set b = New XSD_BOOLEAN If db.GetView(viewName) Is Nothing Then Call b.SetValueAsString("False") Else Call b.SetValueAsString("True") End If Set DoesViewExist = b End Function End Class |
(2) LotusScript 不爲 xsd:byte 使用基本數據類型。它通常映射爲 XSD_BYTE(LotusScript 基本數據類型映射爲 xsd:unsignedByte)。 (3) LotusScript 不爲 xsd:long 使用基本數據類型。它通常映射爲 XSD_LONG(LotusScript 基本數據類型映射爲 xsd:int)。 (4) Java 沒有 xsd:string 的基本數據類型。它通常映射爲 java.lang.String。
其他 XSD 數據類型在 Java 中映射爲 java.lang、java.math、java.util 和 lotus.domino.types(Lotus Notes/Domino 7 的新特性)對象,在 LotusScript 中映射爲 XSD_ objects。
WSDL | Java 數據類型 LotusScript 數據類型 |
xsd:anyType | java.lang.Object XSD_ANYTYPE Variant (1) |
xsd:anyURI | lotus.domino.types.URI XSD_ANYURI |
xsd:base64Binary soapenc:base64 (2) |
byte[] |
xsd:date | java.util.Date XSD_DATE |
xsd:dateTime | java.util.Calendar XSD_DATETIME |
xsd:decimal soapenc:decimal (3) |
java.math.BigDecimal XSD_DECIMAL |
xsd:duration | lotus.domino.types.Duration XSD_DURATION |
xsd:ENTITY | lotus.domino.types.Entity XSD_ENTITY |
xsd:ENTITES | lotus.domino.types.Entities XSD_ENTITIES |
xsd:gDay | lotus.domino.types.GDay XSD_GDAY |
xsd:gMonth | lotus.domino.types.GMonth XSD_GMONTH |
xsd:gMonthDay | lotus.domino.types.GMonthDay XSD_GMONTHDAY |
xsd:gYear | lotus.domino.types.GYear XSD_GYEAR |
xsd:gYearMonth | lotus.domino.types.GYearMonth XSD_GYEARMONTH |
xsd:hexBinary | lotus.domino.types.HexBinary XSD_HEXBINARY |
xsd:ID | lotus.domino.types.Id XSD_ID |
xsd:IDREF | lotus.domino.types.IDRef XSD_IDREF |
xsd:IDREFS | lotus.domino.types.IDRefs XSD_IDREFS |
xsd:integer soapenc:integer (3) |
java.math.BigInteger XSD_INTEGER |
xsd:language | lotus.domino.types.Language XSD_LANGUAGE |
xsd:Name | lotus.domino.types.Name XSD_NAME |
xsd:NCName | lotus.domino.types.NCName XSD_NCNAME |
xsd:negativeInteger | lotus.domino.types.NegativeInteger XSD_NEGATIVEINTEGER |
xsd:NMTOKEN | lotus.domino.types.NMToken XSD_NMTOKEN |
xsd:NMTOKENS | lotus.domino.types.NMTokens XSD_NMTOKENS |
xsd:nonNegativeInteger | lotus.domino.types.NonNegativeInteger XSD_NONNEGATIVEINTEGER |
xsd:nonPositiveInteger | lotus.domino.types.NonPositiveInteger XSD_NONPOSITIVEINTEGER |
xsd:NOTATION | lotus.domino.types.Notation XSD_NOTATION |
xsd:normalizedString | lotus.domino.types.NormalizedString XSD_NORMALIZEDSTRING |
xsd:positiveInteger | lotus.domino.types.PositiveInteger XSD_NONPOSITIVEINTEGER |
xsd:QName | javax.xml.namespace.QName XSD_QNAME |
xsd:time | lotus.domino.types.Time XSD_TIME |
xsd:token | lotus.domino.types.Token XSD_TOKEN |
xsd:unsignedByte | lotus.domino.types.UnsignedByte Byte XSD_UNSIGNEDBYTE (4) |
xsd:unsignedInt | lotus.domino.types.UnsignedInt XSD_UNSIGNEDINT |
xsd:unsignedLong | lotus.domino.types.UnsignedLong XSD_UNSIGNEDLONG |
xsd:unsignedShort | lotus.domino.types.UnsignedShort XSD_UNSIGNEDSHORT |
(1) Variant 在輸出到生成的 WSDL 時映射爲 xsd:anyType。 (2) soapenc:base64 在從 WSDL 導入時映射爲 byte[] 和 Byte。生成的 WSDL 通常映射爲 xsd:base64Binary。 (3) soapenc:decimal 和 soapenc:integer 在從 WSDL 導入時映射爲 XSD:DECIMAL 和 XSD:INTEGER。生成的 WSDL 通常映射爲 xsd:decimal 和 xsd:integer。 (4) xsd:unsignedByte 在從 WSDL 導入時映射爲 Byte。Byte 和 XSD_UNSIGNEDBYTE 在生成的 WSDL 中都映射爲 xsd:unsignedByte。
Domino Web 服務在實現類中暴露公共函數、子程序和方法。私有過程被隱藏了。下面是 GetSubject 示例的變種,它使用公共過程來暴露操作 getFirstSubject、getLastSubject 和 getNthSubject。公用代碼通過私有過程 openDatabase、openView 和 getSubject 提供。
Dim s As NotesSession Dim db As NotesDatabase Dim view As NotesView Dim doc As NotesDocument Dim msg As String Class GetSubject Sub NEW Set s = New NotesSession End Sub Function getFirstSubject(dbname As String, viewname As String) As String If openDatabase(dbname) Then If openView(viewname) Then Set doc = view.GetFirstDocument If doc Is Nothing Then msg = "Cannot get first document " Else Call getSubject End If End If End If getFirstSubject = msg End Function Function getLastSubject(dbname As String, viewname As String) As String If openDatabase(dbname) Then If openView(viewname) Then Set doc = view.GetLastDocument If doc Is Nothing Then msg = "Cannot get last document " Else Call getSubject End If End If End If getLastSubject = msg End Function Function getNthSubject(dbname As String, viewname As String, n As Integer) As String If openDatabase(dbname) Then If openView(viewname) Then Set doc = view.GetNthDocument(n) If doc Is Nothing Then msg = "Cannot get document " & n Else Call getSubject End If End If End If getNthSubject = msg End Function Private Function openDatabase(dbname As String) As Boolean Set db = s.GetDatabase("", dbname) If db.IsOpen Then openDatabase = True Else openDatabase = False msg = "Cannot open database " & dbname End If End Function Private Function openView(viewname As String) As Boolean Set view = db.GetView(viewname) If view Is Nothing Then openView = False msg = "Cannot open view " & viewname Else openView = True End If End Function Private Sub getSubject If doc.HasItem("Subject") Then msg = doc.GetItemValue("Subject")(0) Else msg = "Document does not have Subject" End If End Sub End Class |
下面是 Java 示例。
import lotus.domino.*; import lotus.domino.types.*; public class GetSubject { Session s; Database db; View view; Document doc; String msg; public GetSubject() { s = WebServiceBase.getCurrentSession(); } public String getFirstSubject(String dbname, String viewname) { try { if (openDatabase(dbname)) { if (openView(viewname)) { doc = view.getFirstDocument(); if (doc == null) msg = "Cannot get first document "; else getSubject(); } } } catch(Exception e) { e.printStackTrace(); } return msg; } public String getLastSubject(String dbname, String viewname) { try { if (openDatabase(dbname)) { if (openView(viewname)) { doc = view.getLastDocument(); if (doc == null) msg = "Cannot get last document "; else getSubject(); } } } catch(Exception e) { e.printStackTrace(); } return msg; } public String getNthSubject(String dbname, String viewname, int n) { try { if (openDatabase(dbname)) { if (openView(viewname)) { doc = view.getNthDocument(n); if (doc == null) msg = "Cannot get document " + n; else getSubject(); } } } catch(Exception e) { e.printStackTrace(); } return msg; } private boolean openDatabase(String dbname) { boolean b = false; try { db = s.getDatabase(null, dbname); if (db.isOpen()) b = true; else msg = "Cannot open database " + dbname; } catch(Exception e) { e.printStackTrace(); } return b; } private boolean openView(String viewname) { boolean b = false; try { view = db.getView(viewname); if (view != null) b = true; else msg = "Cannot open view " + viewname; } catch(Exception e) { e.printStackTrace(); } return b; } private void getSubject() { try { if (doc.hasItem("Subject")) msg = doc.getItemValueString("Subject"); else msg = "Document does not have Subject"; } catch(Exception e) { e.printStackTrace(); } } } |
|
Lotus Notes/Domino 7 通過用 Java 或 LotusScript 編碼的類似代理的設計元素來支持 Web 服務的供應商端。Web 服務必須駐留在啓用 HTTP 的 Domino 7 服務器上,此外,Web 服務可以通過 Notes 客戶機上的 Web 預覽來測試和調試。消費者通過 SOAP 編碼的 HTTP POST 請求來訪問 Domino Web 服務。
Web 服務操作映射爲公共 Java 方法和公共 LotusScript 函數和子程序。Web 服務數據部分映射爲參數和返回值。可能的話,XSD 數據類型映射爲 Java 和 LotusScript 基本類型。否則,complexType 元素映射爲對象。
本文基於 Lotus Notes/Domino 7 的 Beta 2 版本。在開發過程中可能進行增強。例如,未來版本預期支持將 Web 服務代碼放置在腳本庫中。