扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
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 命令之一进行访问:
本文描述 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)
Web 服务设计元素
在 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 服务的代码具有下列元素:
下面是 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 服务
最终,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 加载后:
http://rperron300pl.notesdev.ibm.com/Webservices2.nsf/GetSubject?WSDL
,然后单击 Go。我们想要 ?WSDL(而非 ?OpenWebService),因为此处 Web Services Explorer 阅读 WSDL 文档。
响应返回在底部 (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 的样式属性设置如下:
(5) 在 wsdl:binding 下的输入和输出元素中,wsdlsoap:body 的使用属性设置如下:
(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 服务的所有者(编辑或签名设计元素的最后一个用户)。
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 服务使用与代理相同的框架。在后端,大多数但非全部代理上下文应用于 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; } } |
复杂数据类型
使用下列模型的操作不需要复杂数据类型:
返回多个标量输出值或接受多个标量输入值的操作需要复杂数据类型。使用复杂数据类型允许移动大的、各种不同的数据结构。
下面几节讨论复杂数据类型:
数组
数组映射为按数据类型加后缀的名为 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 服务代码放置在脚本库中。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者