在 IBM Operational Decision Manager 中使用 Hosted Transparent Decision Service Interface (HTDS)

IBM® Operational Decision Manager (ODM),IBM 的业务规则管理系统 (BRMS) 产品,提供了一种通过 Hosted Transparent Decision Services (HTDS) 将规则集公开为 Web 服务的简单方法。本文将简要介绍 HTDS,讨论构建一个 HTDS 客户端和使用 IBM ODM V8.5 中新增的 RESTful 接口的方法。本文将重点介绍使用 HTDS 的潜在问题和难点,这些问题和难点主要与控制它的服务接口相关,本文还会介绍一些解决这些问题的实用方法。 本文来自于 IBM Business Process Management Journal 中文版

Raj Rao, 决策管理高级解决方案架构师, IBM

Rajesh (Raj) Rao在专家系统和业务规则管理系统方面有 20 多年的工作经验,在这期间,他应用业务规则技术跨越各个领域,比如制造、运输和金融业,来构建诊断、日程安排、资格证书和配置应用程序。他在 IBM 工作已经快 2 年了。他有 Artificial Intelligence 背景,感兴趣的领域包括 Natural Language Processing 和 Semantic Web。



Matt Voss, ODM 顾问, IBM

Matt Voss 的照片Matthew (Matt) Voss 研究专家系统和决策管理技术已有近 10 年,负责为医疗、制造、金融和抵押行业构建应用程序。在从事多年 ODM 服务活动咨询工作后,他于 2013 年加入 IBM。



2014 年 1 月 27 日

什么是 Hosted Transparent Decision (HTDS)?

Hosted Transparent Decision Service (HTDS) 是一个部署到应用服务器上的预定义的应用程序,它使得规则集可以自动公开为 Web 服务。此外,它还支持使用 JMX MBean 协议访问决策服务元数据和运行时统计数据。它是 IBM ODM Decision Server 中的一个标准执行组件,位于以下目录中的一个企业归档文件 (EAR) 中:<InstallDir>/executionserver/applicationservers/<app-server>/jrules-res-htds-<app_server>.ear。HTDS 集成了本地 ODM Rule Execution Server,必须与 eXecution Unit (XU) 位于相同的应用服务器中。

为什么使用 HTDS?

使用 HTDS,只要将规则集部署到 Rule Execution Server,就可以将它作为 Web 服务调用。无需额外的编码或部署,就可以将规则集公开为 Web 服务。这有助于将规则与远程规则客户端相集成,节省大量的开发时间和工作。

规则服务 WSDL

HTDS 将规则集公开为 Web 服务或规则服务,除了构建规则集之外,无需开发人员编码。这意味着开发人员无需为该 Web 服务构造一个 Web 服务描述语言 (WSDL) 文件。那么如何生成规则服务 WSDL?非常简单,WSDL 由 HTDS 生成。

在 ODM 中,无需输入和输出规则集参数来指定一个规则集的签名。不用奇怪,这些输入和输出参数是 HTDS 用于生成 WSDL 的元素。一个规则集的 WSDL 可以通过 Rule Execution Server 控制台来检索。WSDL 文件也可以通过以下网址进行访问:http://<HOST>:<PORT>/DecisionService/ws/<RULESETPATH>?wsdl,其中 <HOST>:<PORT> 表示应用服务器实例的位置,<RULESETPATH> 是通过串联 RuleApp 名称/版本和规则集名称/版本而形成的规则集标识符,例如:/miniloanruleapp/1.0/miniloanrules/1.0。

一个规则集的输入和输出参数是执行对象模型 (XOM) 的一部分。XOM 可以定义为 Java™ 类或 XML 模式定义 (XSD)。在 ODM V7.5 以前,只有 XSD XOM 和基本 Java 原语类型可用于 HTDS。但是,此限制已不再适用,Java XOM 可用于 HTDS。使用一个 Java XOM 会得到更好的性能,还支持将行为和方法附加到对象上,因此是首选的方法。必须将 Java XOM 部署到 Rule Execution Server 上,然后才能将它用于 HTDS。


使用一个 Java 客户端调用 HTDS

XOM 中定义的类由 HTDS 解析来生成 WSDL。使用 Java XOM(以及在更加受限制的情况下使用 XSD XOM),在某些情况下 HTDS 生成的默认 WSDL 要么不够,要么在从 Java 客户端调用时具有某些问题。本节将介绍一种简单的业务场景,以便重点介绍与从服务使用者调用 HTDS 相关的潜在难题和问题。具体来讲,我们的重点是 Java 客户端与一个使用 Java XOM 的 HTDS 规则集的集成。

场景描述

考虑一个简单的规则应用程序,它基于一系列业务策略确定哪些客户帐户可以升级。本文不会深入介绍业务策略,而是重点介绍客户端与规则服务的集成。规则服务的签名由规则集参数确定。在此场景中,我们使用请求/响应模式来识别传入规则服务和从规则服务返回的数据。这里使用的领域模型非常简单,只是为了强调与 HTDS 集成的潜在问题。简单地讲,如 图 1 中的 UML 类图中所示,请求包含一个具有一组帐户的客户,而响应包含一组可升级的帐户。

图 1. 该场景的领域模型
该场景的领域模型

此模型被实现为 Java 类,这些类构成规则集的 XOM。

HTDS Web 服务

分析 WSDL

之前已经提到过,在将规则集部署到 Rule Execution Server 之后,HTDS 就可以生成它的 WSDL。这个 WSDL 包含组成我们的 XOM 的领域类的所有 XSD 版本。此外,HTDS 使用规则集名称和规则集参数名称来生成一些特定于 HDTS 的元素:

  1. 元素由 HTDS 依据规则集参数名称而定义。
  2. 与规则集名称对应的 XML 包装器元素由 HTDS 创建。具体来讲,我们创建了 <rulesetName>Request<rulesetName>Response
  3. 默认的命名空间使用了 RuleApp 和规则集名称:http://www.ibm.com/rules/decisionservice/<RuleApp name>/<ruleset name>

使用一个规则服务客户端测试

现在已部署了规则服务,我们可以生成一个客户端来调用它。这可以使用 IDE 中的向导或使用调用 wsimport 的命令行脚本来完成。对于本文,我们将使用 Rational Software Architect V8.5。Rational Software Architect 提供了一个简单的 Web Service Client 向导 (图 2) 来生成一个 JAX-WS Web 服务客户端,可以通过提示指定 WSDL 的位置来启动该客户端。

图 2. Rational Software Architect 中的 Web Service Client 向导
Rational Software Architect 中的 Web Service Client 向导

点击查看大图

图 2. Rational Software Architect 中的 Web Service Client 向导

Rational Software Architect 中的 Web Service Client 向导

成功执行该向导后,将会生成两个项目:DetermineUpgradeableAccountsClient 和 DetermineUpgradeableAccountsClient EAR。EAR 项目可用于导出企业归档文件 (.ear),或直接将它部署到 Rational Software Architect 中配置的 WebSphere Application Server。在将企业归档文件部署到服务器之后,可以使用 Rational Software Architect 所提供的一种测试方法对其进行测试,比如 Test with Generic Service Client,如 图 3 中所示。使用此测试客户端,您首先要编辑一个请求,然后调用该服务,最后查看响应。

图 3. 使用生成的客户端测试 HTDS
使用生成的客户端测试 HTDS

点击查看大图

图 3. 使用生成的客户端测试 HTDS

使用生成的客户端测试 HTDS

HTDS REST 服务

从 IBM ODM 8.5 开始,也可以通过一个 RESTful 接口调用 HTDS。REST 服务的 Web 应用程序描述文件 (WADL) 可以通过在检索 HTDS 描述文件时选择 REST 来获取,如 图 4 中所示。

图 4. 检索 REST WADL
检索 REST WADL

也可以在以下网址直接获得它:http://<HOST>:<PORT>/DecisionService/rest/v1/<RULESETPATH>/WADL。例如,我们部署在本地的 RuleApp 的 URL 为:http://localhost:9080/DecisionService/rest/v1/determineupgradeableaccountsruleapp/1.0/DetermineUpgradeableAccountsRules/1.0/WADL。

备注:在通过 RES 控制台检索 WADL 后,可以使用 Test 轻松地执行和验证请求。

分析 WADL

WADL 包含一个具有 3 个方法的资源:

  • post 方法处理一条规则请求并返回一个响应
  • get 方法具有一个用来获取示例请求的资源路径 /xml。
  • post 方法具有一个在请求上执行验证的资源路径 /validate。

请注意,目前仅允许使用 XML 内容,不能使用 JSON。

构建 REST 客户端

构建一个 Java 客户端来调用 HTDS REST 服务并不像构建 Web 服务客户端那样简单,因为没有完成此操作的向导。相反,我们将从 HTDS 生成的 WADL 开始手动构建 REST 客户端。我们采用了一个叫做 wadl2java 的工具从该 WADL 生成 Java 存根。可以在 http://search.maven.org/remotecontent?filepath=org/jvnet/ws/wadl/wadl-dist/1.1.5/wadl-dist-1.1.5-bin.zip 上下载此工具。

要生成 Java 客户端,可以运行以下批处理脚本来生成这些存根:<path to wadl2java>\wadl2java -o .\src -p com.example.client http://localhost:9080/DecisionService/rest/v1/determineupgradeableaccountsruleapp/DetermineUpgradeableAccountsRules/WADL

这段脚本生成如 图 5 中所示的 Java 存根。除了映射到 XOM 中的领域类的存根,您还会看到以下类:

  • RequestResponse,它们是 HTDS 包装器类
  • Localhost_DecisionServiceRestV1DetermineupgradeableaccountsruleappDetermineUpgradeableAccountsRules,它是一个调用服务的代理
图 5. wadl2java 生成的存根
wadl2java 生成的存根

备注:由于方法参数重复,生成的类 Localhost_DecisionServiceRestV1DetermineupgradeableaccountsruleappDetermineUpgradeableAccountsRules 具有编译错误。这需要通过删除重复参数来手动修复。

要编译和执行客户端代码,必须向 lib 目录(和客户端的 Java 生成路径)添加多个 wadl2java 库依赖关系,如 图 6 中所示。wadl2java 没有生成调用该客户端的代码;您必须手动完成此操作。下面列出了一个示例客户端运行程序。请注意,它使用了 /xml 资源方法来构建请求,然后使用 index 和此请求载荷来执行 HTDS。

点击查看代码清单

import com.example.client.Localhost_
DecisionServiceRestV1DetermineupgradeableaccountsruleappDetermineUpgradeableAccountsRules;
import com.example.client.Localhost_
DecisionServiceRestV1DetermineupgradeableaccountsruleappDetermineUpgradeableAccountsRules.Index;
import com.example.client.Localhost_
DecisionServiceRestV1DetermineupgradeableaccountsruleappDetermineUpgradeableAccountsRules.Xml;
import com.example.client.Request;
import com.example.client.Response;


public class ClientRunner {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		XmlFormatter formatter = new XmlFormatter();
		com.sun.jersey.api.client.Client client = Localhost_
DecisionServiceRestV1DetermineupgradeableaccountsruleappDetermineUpgradeableAccountsRules.createClient();
		Xml xml = 
Localhost_
DecisionServiceRestV1DetermineupgradeableaccountsruleappDetermineUpgradeableAccountsRules.xml(client);
		Request request = xml.getAsRequest();
		String reqXml = formatter.marshalToString(request);
		System.out.println("Req=" + reqXml);

		Index index = Localhost_
DecisionServiceRestV1DetermineupgradeableaccountsruleappDetermineUpgradeableAccountsRules.index(client);
		Response response = index.postXmlAsResponse(request);
		System.out.println
(response.getDetermineUpgradeableAccountsResponse().getUpgradeableAccounts().iterator().next().getAccountId());
	}
}

生成存根后,您可以使用常规的 HTTP 客户端代码调用 REST 服务。例如,使用 Apache HTTP Components 的客户端运行程序代码如下所示。这段代码还使用 /xml GET 方法来构建基础请求,然后将修改的请求提交到 HTDS 来执行规则。这段代码使用了常规的 HTTP GET 和 HTTP POST 命令。

点击查看代码清单

import java.io.InputStream;
import java.net.URL;

import javax.xml.bind.Unmarshaller;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import com.example.client.Request;
import com.example.client.Response;

public class Main {

	private static String url = "http://localhost:9080/DecisionService/rest/v1/determineupgradeableaccountsruleapp/1.0/DetermineUpgradeableAccountsRules/1.0";

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		XmlFormatter formatter = new XmlFormatter();
		Unmarshaller u = XmlFormatter.unmarshaller;

		CloseableHttpClient httpclient = HttpClients.createDefault();
		String result = "";
		try {
			Request req = (Request) u.unmarshal(new URL(url + "/xml"));
			req.getDetermineUpgradeableAccountsRequest().getCustomer().setFirstName("James");
			req.getDetermineUpgradeableAccountsRequest().getCustomer().setLastName("Bond");
			String reqXml = formatter.marshalToString(req);
			System.out.println("Req=" + reqXml);
			
			StringEntity reqEntity = new StringEntity(reqXml, ContentType.create(
					"application/xml", "UTF-8"));

			HttpPost httppost = new HttpPost(url);
			httppost.setEntity(reqEntity);
			CloseableHttpResponse response = httpclient.execute(httppost);
			try {
				HttpEntity entity = response.getEntity();
				if (entity != null) {
					InputStream instream = entity.getContent();
					try {
						 Response resp = (Response) u.unmarshal(instream);
						 result = formatter.marshalToString(resp);
					} finally {
						instream.close();
					}
				}
			} finally {
				response.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
		System.out.println("Response = " + result);
	}
}

此方法的优势在于,它没有使用生成的 Localhost_DecisionServiceRestV1DetermineupgradeableaccountsruleappDetermineUpgradeableAccountsRules 类,前面已提到过,这个类具有一些编译错误。


架构问题和难题

通过 HTDS 公开的 Web 服务对规则集执行远程调用非常简单,会吸引人将它们推广为可以跨多个企业客户端使用的业务服务。但是,事实情况是,这些 Web 服务不应直接由企业客户端应用程序使用,而由一个用作网关的可重用的决策服务使用(参见 参考资料)。这么做有多种原因:

  • 依据最佳实践,规则集对象模型可能不同于企业集成模型,而且决策服务在调用规则引擎之前需要执行模型转换和请求扩充,或许通过访问外部数据源来完成。
  • 调用 HTDS 时,客户端知道一个规则集路径,严格来讲这违背了业务接口设计,因为客户端知道该服务是一种基于规则的实现。
  • 想要的服务操作粒度(这是 SOA 服务设计的一个重要方面)可能不同于 HTDS 所公开的服务签名。事实上,同一个规则集在不同的粒度级别上通常有多个服务操作。

出于这些原因,通常不应将 HTDS 视为由业务规则客户端直接调用的可重用的业务决策服务,而应将它视为由服务实现组件调用的规则服务(我们称为业务决策服务),或者由专门的一次性客户端调用的规则服务(比如 BPM 流程任务)。

设计此业务决策服务时,典型的用例是使用一个 POJORuleSession 从业务决策服务调用 Rule Execution Server,只要它们在同一个服务器上。但是,在某些情形下,由于许可限制或其他管理性约束,规则执行环境必须与业务决策服务分开和不同。在这些情形下,可以利用 HTDS 从 Business Decision Service 远程执行规则,如 图 6 中所示。为了完整起见,必须指出的是,远程调用也可以使用 ODM 所提供的规则执行 EJB 组件实现;但是,这会向 Business Decision Service 中注入一些额外的 ODM 库依赖关系。

图 6. 从一个决策服务调用 HTDS
从一个决策服务调用 HTDS

点击查看大图

图 6. 从一个决策服务调用 HTDS

从一个决策服务调用 HTDS

集成问题和难题

使用 Java 客户端(通过 SOAP 或 REST)调用 HTDS 规则集时,可能突然出现多个问题。本节介绍这些问题和解决它们的步骤。

名称冲突

问题:

在 Web Service Client 向导处理 可下载的基础项目 时,您会遇到错误,如 图 7 中所示:

图 7. Web 服务客户端生成错误
Web 服务客户端生成错误

同样的 HTDS 规则服务可以从 SoapUI 等客户端成功地调用,这表明生成的代码 (wsimport) 是罪魁祸首。

图 7 中显示的错误是名称冲突的结果。在领域模型中使用的类名称与 HTDS 为它的包装器类和元素生成的名称发生冲突时,就会出现这种情况。之前已经看到,HTDS 通过将 RequestResponse 作为后缀添加到规则集名称上来生成包装器类。例如,HTDS 创建的包装器请求类是:

点击查看代码清单

<xsd:element name="DetermineUpgradeableAccountsRequest">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element form="qualified" maxOccurs="1" minOccurs="0" name="DecisionID" type="xsd:string"/>
            <xsd:element ref="param:determineUpgradeableAccountsRequest"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>

在代码生成期间,这会与领域中定义的请求类发生冲突:

<xs:complexType name="determineUpgradeableAccountsRequest">
    <xs:sequence>
      <xs:element minOccurs="0" name="asOfDate" type="xs:dateTime"/>
      <xs:element minOccurs="0" name="customer" type="tns:customer"/>
    </xs:sequence>
</xs:complexType>

请注意,这些名称不同的情形,因此它们是 XSD 中的不同实体。但是,在运行 wsimport 时,这些名称会转换为 Java 类名称,其中第一个字母会是大写字母,这会导致冲突。

类似地,使用 REST 接口时,如果在领域模型中有一个名为 RequestResponse 的类,则会发生名称冲突。

解决方法:

可以通过两种方法解决这个问题。

  • 使用 JAXB 注释更改领域类名称。使用此方法,RequestResponse 领域类名称使用 @XmlType 注释更改。例如,以下代码会将请求类名称更改为 DetermineUpgradeableAccountsAnnotatedRequest
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name="DetermineUpgradeableAccountsAnnotatedRequest")
    public class DetermineUpgradeableAccountsRequest {
    …
    }

    Web Service Client 向导生成的 Java 类如 图 8 中所示。请注意,该领域请求类现在名为 DetermineUpgradeableAccountsAnnotatedRequest

    图 8. 生成的 Java 客户端源代码
    生成的 Java 客户端源代码

    此解决技术也适用于在处理 HTDS REST 接口冲突。

    请注意,重构项目来重命名领域类名称也是一种选择,但这需要重构规则项目,会花费更多的开发时间和精力。因此,使用 JAXB 注释更改生成的类的名称更简单。

  • 使用 Ruleset Archive 编辑器更改规则集名称,如 图 9 中所示。无需调用规则集 DetermineUpgradableAccounts,可以将它命名为 DetermineUpgradableAccountsRules
    图 9. Ruleset Archive 编辑器
    Ruleset Archive 编辑器

    现在 HTDS 生成的包装器类将被称为 DetermineUpgradableAccountsRulesRequestDetermineUpgradableAccountsRulesResponse,它们不会与任何领域类名称冲突。

领域类重复

问题:

WSDL 中使用的命名空间转换为为 Web 服务客户端生成的源代码的 Java 包。例如,WSDL 中默认的目标命名空间 http://www.ibm.com/rules/decisionservice/Determineupgradeableaccountsruleapp/DetermineUpgradeableAccounts 会导致在包 com.ibm.rules.decisionservice.determineupgradeableaccountsruleapp.determineupgradeableaccounts 中生成 Java 类,如 图 10 中所示。换句话说,默认的包基于 RuleApp 和规则集名称。请注意,只要您在生成 HTDS 时未选择兼容性模式 7.0 或 7.1(在这种情况下,命名空间硬编码为 http://www.ilog.com/rules/DecisionService),就会出现这种情况。如果未选择任何兼容性模式,WSDL 代码会使用以 http://www.ibm.com/ 开头的命名空间。

在多个 HTDS 规则集中使用相同的领域类时,就像在同一个领域模型上执行不同的决策点时常常出现的情况一样,默认的命名空间会导致生成的 Web 服务客户端中的类重复,因为会在不同的包中生成相同的类,每个对应一个 WSDL。这种类的重复可能导致维护和同步问题。

解决方法:

要避免重复,不同的规则集所对应的 WSDL 必须拥有相同的命名空间。您可以通过 Rule Execution Server Console 更改生成的规则服务的命名空间,如 图 10 中所示。

图 10. 通过 RES 控制台更改生成 WSDL 的命名空间
通过 RES 控制台更改生成 WSDL 的命名空间

隐藏的瞬态属性

问题:

通常,一个 Java 类有一些属性是您仅用于内部规则处理用途的 XOM 的一部分。例如,在 图 11 中所示的 Account 类中,eligible 属性应由规则引擎在内部用于将一个 Account 标记为合格或不合格。

图 11. 具有瞬态 eligibility 属性的 Account
具有瞬态 eligibility 属性的 Account 类

这些派生的属性与规则客户端毫无关系,不应在 WSDL 中公开。但是,WSDL 确实拥有此属性,如 图 12 中摘录的 WSDL 中所示。甚至在属性声明为瞬态属性时也是如此,比如 private transient boolean eligible;

图 12. Account 中摘录的 WSDL
Account 中摘录的 WSDL

在使用 REST 接口调用 HTDS 时也会发生此问题,本节中的剩余问题也是如此。

解决方法:

JAXB 注释可用在 Java 类上来将属性标为瞬态属性,并向 WSDL 隐藏它。具体来讲,可以使用 @XmlTransient 隐藏属性。例如:

@XmlAccessorType(XmlAccessType.FIELD)
public class Account {
	@XmlTransient
	private boolean eligible;
	…

备注:

  1. 如果属性具有瞬态字段修饰符,@XmlTransient 不能应用于属性本身。因此,不应使用 Java 关键字 transient。如果有必要使用 Java 关键字 transient,可以为 getter 添加 @XmlTransient 注释。在此示例和未来的示例中,该类使用了 @XmlAccessorType(XmlAccessType.FIELD) 注释,以表明该属性将被注释。默认情况下,JAXB 要求注释 getter。
  2. 另一方面,使用瞬态属性的能力是 Java XOM 优于 XSD XOM 的另一项优势。

将属性标记为强制性

问题:

常常,请求中有一些强制性的数据元素。可以通过编写数据验证规则来确保这些强制性字段没有空值,但由 WSDL 执行此规则更可取一些。例如,规则可能需要使用 Account Status,因此必须传入它。但是,在 图 13 中可以看到,accountStatus 字段有一个 minOccurs 0,因此请求中可能缺少它。

解决方法:

JAXB 注释可以用在 Java 类上,以便将属性标为强制性属性。具体来讲,可以使用 @XmlElement 将属性标记为强制性属性。例如:

@XmlAccessorType(XmlAccessType.FIELD)
public class Account {
	…
	@XmlElement(required=true, nillable=false)
	private AccountStatus status;
	…

在添加此注释后重新部署规则集时,生成的 WSDL (图 13) 没有 minOccurs(默认值为 1),因此是强制性属性。

图 13. 为 AccountStatus 添加了 @XmlElement(required=true) 标记后的部分 WSDL
为 AccountStatus 添加了 @XmlElement(required=true) 标记后的部分 WSDL

同一个注释也可以用于将关联标记为强制性的。例如,Customer 的 accounts 属性可按照以下形式进行注释:

@XmlElement(required=true,name="account")
	private Set<Account> accounts;

控制属性名称

有时,属性的名称需要一定的自定义。对于 Collections 尤其如此。Collections 的 Java 最佳实践是将该属性命名为复数形式。例如:

private Set<Account> accounts;

每个 <accounts> 是一个帐户。要让命名更加直观,可以为属性按照以下方式添加一个备用名称作为注释:

<accounts>
	<balanceAmount>?</balanceAmount>
	<id>?</id>
	<openDate>?</openDate>
	<status>?</status>
	<stateWhereOpened>?</stateWhereOpened>
	<type>?</type>
	<tier>?</tier>
</accounts>

控制数据类型

问题:

常常,生成的 WSDL 和生成的客户端代码中的数据类型并不理想。java.util.Date 类型的 XOM 属性在 WSDL 中被标记为 xs:dateTime。例如,在 图 14 中,openDate 被标记为 xs:dateTime。此外,在生成的客户端代码中,它被键入为 XmlGregorianCalendar

@XmlSchemaType(name = "dateTime")
protected XMLGregorianCalendar openDate;

XmlGregorianCalendar 比一个简单的 CalendarDate 对象更难处理。通常,XmlGregorianCalendar 提供的准确性是没有必要的,将属性生成为简单的 java.util.Date 更可取。

解决方法:

在 XOM 中,为属性添加 @XmlSchemaType 注释。例如:

@XmlSchemaType(name="date")	
private Date openDate;

生成的 openDate 的 WSDL 现在已正确地将 xs:date 作为类型。但是,生成的客户端 Java 类仍然使用 XmlGregorianCalendar。为了修复此问题,我们使用了一个 JAXB 适配器。首先,必须创建一个绑定文件来表明要将它用于一个 XSD 类型的 Java 类型,还要指定要在 JAXB 解组和编组期间分别使用的解析方法和打印方法。下面给出了这个 JAXB 绑定文件。

点击查看代码清单

<bindings xmlns="http://java.sun.com/xml/ns/jaxb" version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <globalBindings>
    <javaType name="java.util.Date" xmlType="xs:date"
      parseMethod="com.example.adapters.DateAdapter.parseDate"
      printMethod="com.example.adapters.DateAdapter.printDate"
    />
    <javaType name="java.util.Date" xmlType="xs:dateTime"
      parseMethod="com.example.adapters.DateAdapter.parseDate"
      printMethod="com.example.adapters.DateAdapter.printDate"
    />    
  </globalBindings>
</bindings>

此绑定文件在生成客户端期间使用,如 图 14 中所示。

图 14. 在 Web Service Client 向导中使用 JAXB 绑定文件
在 Web Service Client 向导中使用 JAXB 绑定文件

点击查看大图

图 14. 在 Web Service Client 向导中使用 JAXB 绑定文件

在 Web Service Client 向导中使用 JAXB 绑定文件

在使用 REST 客户端的情况下,JAXB 绑定文件必须使用 –c 选项传入:

wadl2java -o outputDir -p package [-a] [-s jaxrs20] [-c customization]* file.wadl

在生成的 Web 服务客户端项目中,必须使用绑定文件中表明的 parseMethodprintMethod 创建一个名为 com.example.adapters.DateAdapter 的类。下面给出了该文件的内容。

package com.example.adapters;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import javax.xml.bind.DatatypeConverter;

public class DateAdapter {
	public static Date parseDate(String s) {
		return DatatypeConverter.parseDate(s).getTime();
	}

	public static String printDate(Date dt) {
		Calendar cal = new GregorianCalendar();
		cal.setTime(dt);
		return DatatypeConverter.printDate(cal);
	}
}

问题:

在默认情况下,分配了包装器类类型的 XOM Java 类中用于某个原语的属性,将在客户端上生成为原语。例如:

@XmlElement(required=true)
private Boolean isLoyalCustomer;

在 Web 服务客户端中生成了以下代码:

@XmlElement(required = true)	
protected boolean isLoyalCustomer;

解决方法:

具有包装的原语类型的属性应该使用 nillable=true 注释。例如:

@XmlElement(required=true,nillable=true)
private Boolean isLoyalCustomer;

客户端将生成以下代码:

@XmlElement(required = true, type = Boolean.class, nillable = true)
protected Boolean isLoyalCustomer;

下表给出了针对多种常用数据类型,从 XOM 到 WSDL 再到 Web 服务的默认映射。

XOM 数据类型 WSDL 数据类型 Web 服务客户端生成的数据类型
String xs:string String
Double xs:double 默认数据类型为 double,或者在带 nillable=true 注释时为 Double
double xs:double double
int xs:int int
Integer xs:int int 或在带 nillable=true 注释时为 Integer
BigDecimal xs:decimal BigDecimal
Boolean xs:boolean 默认数据类型为 boolean,或者在带 nillable=true 注释时为 Boolean
boolean xs:boolean boolean
Set ex, tns:customerAccount List
Date xs:dateTime XmlGregorianCalendar

结束语

总体来讲,HTDS 为开发人员提供了一种将规则集公开为 Web 服务的简单方法,但随之而来的可能是滥用此权力的潜在风险。不应将 HTDS 视为可重用的业务决策服务,而应将它视为一个使决策服务能够轻松地远程调用规则的规则服务。在本文中,您看到了使用 HTDS 所生成的 WSDL 和 WADL 时的一些常见问题,了解了如何通过 HTDS 配置和利用 JAXB 注释来克服它们。


下载

描述名字大小
Base Rule Designer 工作区(包含问题)wsdl-demo-base-projects.zip39KB
Solution Rule Designer 工作区(包含修复程序)wsdl-demo-modified-projects.zip40KB

参考资料

学习

获得产品和技术

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=961385
ArticleTitle=在 IBM Operational Decision Manager 中使用 Hosted Transparent Decision Service Interface (HTDS)
publish-date=01272014