在 Doug Tidwell 的“UDDI4J: Matchmaking for Web services”之后,本文接下来将讨论 UDDI 客户机 API 的下一个版本,UDDI4J v2。我总结了版本 2 的 UDDI 规范中的种种变化,并更详细地讨论了新版本的 UDDI4J 中的 API 变化。新的 API 已经被修改并扩展为支持 UDDI 版本 2,并增加了一些与新的 UDDI 规范无关的一些变化。我提供了几个代码片段,并对它们进行了讨论,以说明如何使用新的 API。
UDDI4J 是什么?
Web 服务为分布式计算提供了一种新的范例。根据存储在 WSDL 中的描述而动态地调用 Web 服务,这种做法很有前景。由于 Web 服务用户越来越成熟,我们面临的下一步挑战将是如何寻找或发现他们可能感兴趣的那些服务的存在。因为 Web 服务调用自动化的程度越来越高,所以对我们来说重要的是有一种能够按计划实现,而且可以用一种可预知的、可重复的方式来发现 Web 服务的方法。
统一描述、发现和集成(Universal Description,Discovery and Integration,UDDI)就提供了这样一种机制。UDDI 本身就是作为 Web 服务而定义的,它允许企业以及它们的服务在 UDDI 注册中心进行注册。一个已注册的服务可以包括象 WSDL 这样的描述性信息,还可以按照任意多种方式进行分类。对 Web 服务进行分类使您能够使用复杂的搜索,以便查找最希望得到的服务。
UDDI 被定义为一组能够从 UDDI 注册中心发送和接收的 XML 消息。UDDI4J 是用来与 UDDI 注册中心进行交互的客户机 Java API。UDDI4J 对象和方法用来构建请求消息并将其发送到注册中心。同样地,UDDI4J 解释来自 UDDI 注册中心的响应消息,并提供一组用来访问响应消息的对象和方法。
发行 UDDI4J 版本 1 是为了配合最初发行的公用 UDDI 企业注册中心(UDDI Business Registry,UBR)。UDDI 一直在不断发展,UDDI4J 也随之不断发展。UDDI 版本 2 规范几个月前就可以使用了,随之可用的还有一些测试版的注册中心。公用 UDDI 企业注册中心现在支持 UDDI 规范版本 2。2001 年 11 月 发行了 UDDI4J 版本 2 的测试版,然后 2002 年 7 月发行了非测试版。这个新的 UDDI4J 发行版增加了对规范中新特性的支持,还另外改进了可用性和配置。
UDDI4J 最初是由 IBM 于 2001 年 1 月作为一个开放源代码项目在 developerWorks 上发行的,这鼓励了其它公司和个人的加入。在版本 2 中,HP 作为 UDDI4J 的发展中一个主要的参加者加入了这一阵营,UDDI 版本 2 中的很多改进都是由它贡献的。在 IBM、HP 和 SAP 的积极支持和认可之下,UDDI4J 已经成为进行 UDDI 交互的缺省 Java API。
随着 UDDI 为了满足日益增长的 Web 服务社区的需求而继续发展,UDDI4J 将继续顺应这些变化。
UDDI4J 基础知识
UDDI4J 可以分为各种包。org.uddi4j.client.UDDIProxy 是用来与 UDDI 注册中心进行交互的主要的类。它代表将与之进行通信的那个 UDDI 注册中心的代理。UDDIProxy 提供了很紧密地与 UDDI 规范中定义的 API 进行映射的一些方法。其中的参数通常代表 UDDI 中各种元素的数据对象。
很多 API 调用都返回 org.uddi4j.response.DispositionReport 来表明操作的成功或失败。一些调用将抛出 org.uddi4j.UDDIException 来表明错误情况。根据错误的不同性质,UDDIException 可能包含 DispositionReport,其中有关于错误的更多信息。
UDDI API 可以分为查询 API 和发布 API。这些 API 使用不同的 URL。发布 URL 通常使用 SSL 来指定 https 地址。在使用 Java 编程语言中的 SSL 时,必须定义 JSSE 提供者,并将其添加到安全性管理器组中。下面的代码片段使用 Sun 的 Java 技术 JSSE 包启用了 SSL。您还可以通过修改 jdk/jre/lib/security/Java.security 配置文件来添加安全性提供者。
System.setProperty("Java.protocol.handler.pkgs",
"com.sun.Net.ssl.internal.www.protocol");
Java.security.Security.addProvider(new com.sun.Net.ssl.internal.ssl.Provider());
UDDIProxy 要求同时指定查询 URL 和发布 URL。一旦设置之后,在修改这些值前所有的方法调用都使用这些 URL。在使用发布 API 时,需要输入用户名和密码。通常情况下是通过用与所选的注册中心有关的 Web 站点创建一个帐户来获得用户名和密码。
下面的代码片段是使用发布 API 的代码所需的典型的设置步骤。
// Construct a UDDIProxy object
UDDIProxy proxy = new UDDIProxy();
try {
// Select the desired UDDI server node
proxy.setInquiryURL("inquiryURL");
proxy.setPublishURL("publishURL");
// Pass in userid and password registered at the UDDI site
AuthToken token = proxy.get_authToken("userid", "password");
...
} catch (UDDIException e) {
...
UDDI4J 的变化
UDDI 版本 2 中有很多重要的变化,包括对版本 2 规范中内容的支持,还改进了 UDDI4J 的行为,但这些改进与规范无关。
对 UDDI 版本 2 特性的支持,包括:
企业关系
服务投影
增强的查询
国际化功能
与 UDDI 版本 2 无关的改进:
包名称的变化
可插的传输
调试日志记录
集合类的改进
兼容性
UDDI 版本 2 修改了一些已有的消息 API,并改变了所有进入和发出的消息上的版本标识符。结果,UDDI 版本 2 和版本 1 之间出现了不兼容性。UDDI 版本 2 注册中心能够接受版本 1 和版本 2 的消息。然而,版本 1 注册中心却不能处理版本 2 的消息。
UDDI4Jv2 只生成和处理版本 2 的消息,不支持版本 1 的消息。UDDI4Jv1 可以用来发送和接收版本 1 的消息。您可以在同一个 JVM 中同时使用 UDDI4Jv1 和 UDDI4Jv2 来与版本 1 或版本 2 的注册中心进行通信。我们并不期望有很多版本 1 的注册中心将继续运行下去,因为大部分注册中心都将升级以支持版本 2。
包名称的变化
作为对开放源代码的进一步承诺,UDDI4J 的包名称已被从 com.ibm.uddi 改为 org.uddi4j。同样地,还定义了一个新的 Web 站点,http://www.uddi4j.org,以容纳发行版和关于该项目信息。通过邮件列表,您不仅可以得到对常见问题的解答,而且还可以参与 UDDI4J 的开发。
样本
UDDI4J 版本 2 提供了一组扩展样本。它们使用配置器(configurator)类根据属性文件中提供的信息建立 SSL 提供者、设置 URL 等等,从而简化了一些较为常规的设置工作。由于提供注册中心的是扩展的供应商,有关当前公共注册中心 URL 的信息已经从样本中删除了。您可以在 http://www.uddi4j.org 站点的“links”主题下找到一个较为常用的注册中心 URL 的列表。
配置改进
UDDI4J 版本 2 增加了使用属性来设置的配置选项。这些属性可以作为系统属性来指定,如果需要,还可以在属性对象中设置它们,并传送到新构造器的 UDDIProxy。如果同一个 JVM 中需要 UDDI4J 的多个副本,这个新的构造器能够使用一种机制,根据属性文件配置环境,并允许 UDDI4J 的每个副本按不同的配置运行。使用系统属性是一种更简单和更直观的配置 UDDI4J 的方法。
您可以在代码中或在命令行设置系统属性,如下所示:
Java -DpropertyName=propertyValue Pluggable transports
UDDI4Jv2 增加了对可插传输层的支持。UDDI4Jv1 需要使用 Apache SOAP。自从第一次发行之后,出现了更多的 SOAP 实现。创建抽象和工厂层是为了支持多个 SOAP 传输提供者。Apache SOAP 项目已经被接下来的 Apache Axis 项目取代了。UDDI4Jv2 的主要贡献者之一 HP 也提供 SOAP 传输。UDDI4Jv2 支持这三种 SOAP 传输。Apache SOAP 还是缺省的传输。您可以通过设置下面的属性来指定使用其它的传输。
org.uddi4j.TransportClassName=transport_class_name
被指定的传输类名称可以是下面包括的传输之一,也可以是第三方提供的 SOAP 传输。
org.uddi4j.transport.ApacheSOAPTransport(缺省)
org.uddi4j.transport.ApacheAxisTransport
org.uddi4j.transport.HPSOAPTransport
源自传输的通信错误将导致 org.uddi4j.transport.TransportException 被抛出。这种异常将包装可能包含其它详细信息的特定于传输的异常。
调试支持
UDDI4J 增强了记录进入和发出消息的日志的能力。这在诊断 UDDI 注册中心的通信问题时非常有用。输出是通过设置下面的系统属性实现的:
org.uddi4j.logEnabled=true
所有 SOAP 传输都支持日志记录。HP SOAP 传输还有能用于决定日志目的地的属性。
代理
代理通常用于穿过防火墙进行通信,使得我们能够从公司内部网与因特网进行通信。代理可以通过 HTTP 或 socks 实现。UDDI4J 现在支持常用的 Java 属性,以定义 HTTP 和 socks 代理,从而实现穿越防火墙的通信。所需的值依赖于公司的网络配置,可以从网络管理员处获得。UDDI4J 处理将一组基本的 http 代理属性传送到底层传输的任务。它支持的属性包括:
http.proxyHost ? 代理服务器的主机名。
http.proxyPort ? 代理服务器使用的端口,缺省值为 80。
https.proxyHost ? 代理服务器的主机名(如果不同于 http 代理主机)。
https.proxyPort ? 代理服务器使用的 SSL 端口,缺省值为 80。
http.proxyUserName ? 如果代理服务器需要认证,请指定用户名。
http.proxyPassword ? 如果给定了代理用户名,请指定与其匹配的密码。
JDK 直接支持下面与代理有关的属性,为了完整起见,我们将连它们一起讨论。
socksProxyHost ? socks 代理服务器的主机名。
socksProxyPort ? socks 代理服务器的端口名。
改进的可用性
很多 UDDI4J 类都与 UDDI 模式中定义的元素直接一一对应。在 UDDI4J v1 中,这将产生若干听起来象集合类,但实际上只是真正集合的容器的类。UDDI4J v2 通过向这些类添加一组集合类方法改进了这些类的可用性。这些方法并没有增强包含属性以及集合的更复杂的类的可用性。
被添加的方法是:
public void add(KeyedReference)
public boolean remove(KeyedReference)
public KeyedReference get(int index)
public int size()
我们修改了下面的类,以包括这些新的方法:
util.IdentifierBag collection of KeyedReferences
util.CategoryBag collection of KeyedReferences
util.DiscoveryURLs collection of DiscoveryURL objects
util.FindQualifiers collection of FindQualifier objects
util.TmodelBag collection of Tmodel objects
response.BusinessInfos collection of BusinessInfo objects
response.PublisherAssertions collection of PublisherAssertion objects
response.RelatedBusinessInfos collection of RelatedBusinessInfo objects
response.ServiceInfos collection of ServiceInfo objects
response.TmodelInfos collection of TmodelInfo objects
datatype.binding.BindingTemplates collection of BindingTemplate objects
datatype.binding.TModelInstanceDetails collection of TmodelInstanceInfo objects
datatype.business.Contacts collection of Contact objects
datatype.service.BusinessServices collection of BusinessService objects
我们还为 KeyedReference 类提供了一个新的构造器,允许提供 KeyedReference 类的全部属性来构造这个类。
public KeyedReference(String keyName, String keyValue, String tModelKey)
下面的示例说明了如何在使用和不使用新方法的情况下向标识符包添加一个用键值标识的引用。
使用老进程需要下面的代码:
Vector ibVector = new Vector();
KeyedReference kr1 = new KeyedReference("name", "value");
kr1.setTModelKey("tmodelKey");
ibVector.add(kr1);
IdentifierBag ib = new IdentifierBag();
// The identifierbag is not really a collection, simply a container for a vector
ib.setKeyedReferenceVector(ibVector);
如果使用新的集合类方法,上面的代码就被简化为:
KeyedReference kr1 = new KeyedReference("name", "value", "tmodelKey");
IdentifierBag ib = new IdentifierBag();
// New identifierBag method, it now acts like a simple collection class
ib.add(kr1);
UDDI 版本 2 的改变:企业关系
UDDI 版本 2 为复杂的组织增加了建模支持。企业关系这个概念是 UDDI 版本 2 中影响到 UDDI4J 的最大的一个改变。大型组织一直在使用 UDDI,它们碰到过如何表示企业的部门或企业的一部分的问题。通常,每个部门都将创建它们自己的 UDDI 企业实体,然而我们很难建立这样一种关系,用来表明这些不同企业实际上都是同一个整体企业的一部分。UDDI 版本 2 加入了添加这种关系的能力。UDDI4J 中所反映出的大部分变化的都与这种表达企业之间相互关系的能力有关。
为了处理这些关系,还增加了两种新的 UDDI 构件:发布者声明和服务投影。发布者声明是由企业的发布者作出的表明两个企业之间存在关系的声明。当每个发布者都声明了同样的关系之后,就建立了两个企业之间的关系。当 UDDI 注册中心收到一个声明时,它将关系置于未决状态。当收到来自另一个企业的发布者的匹配的声明之后,它们就建立了企业关系。共定义了三种类型的关系。父子关系描述分级关联,如子公司或部门。对等关系代表两个企业都不被认定为父企业的关联。等同关系表明两个企业实体都代表相同的企业,这类似于创建企业的一个别名。
一旦两个企业之间建立了关系,其中一个企业中定义的服务就可以被另一个企业引用。当其中一个企业中出现另一个企业的服务时,这就是所谓的服务投影。公用 UDDI v2 企业注册中心目前还不支持服务投影。不久以后将增加这种支持。
使用发布者声明
UDDI4J 在实现 API 时严格遵循 UDDI 规范。理解 UDDI4J 所需的最好的、唯一的参考就是 UDDI 规范本身,您可以从 UDDI 组织站点(请参阅参考资料)获得它。UDDI 版本 2 规范的附录 J 中有关于如何使用企业关系的参考资料。
下面几个与企业关系有关的新的 API 已经在 UDDI 版本 2 中得到定义。这些 API 在 UDDI4J 中是作为 UDDIProxy 类上的新方法表现的。
RelatedBusinessesList find_relatedBusinesses (FindQualifiers findQualifiers,
String businessKey
KeyedReference keyedReference);
DispositionReport add_publisherAssertions (String authInfo,
PublisherAssertion publisherAssertion);
DispositionReport add_publisherAssertions (String authInfo,Vector publisherAssertion);
DispositionReport delete_publisherAssertions (String authInfo,
PublisherAssertion publisherAssertion);
DispositionReport delete_publisherAssertions (String authInfo,Vector publisherAssertion);
AssertionStatusReport get_assertionStatusReport (String authInfo,
CompletionStatus completionStatus);
AssertionStatusReport get_assertionStatusReport (String authInfo,
String completionStatus);
PublisherAssertions get_publisherAssertions (String authInfo);
PublisherAssertions set_publisherAssertions (String authInfo,Vector publisherAssertion);
PublisherAssertions set_publisherAssertions (String authInfo,PublisherAssertion pub);
下面的示例将说明第一个发布者如何创建发布者声明。首先要创建一个用键值标识的引用来描述要建立的关系。键值表明要声明的关系的类型。定义的值类型为对等、父子或等同类型,它们在 UDDI 数据结构规范中定义。在使用父子类型时,from key 代表父企业,to key 代表子企业。要创建发布者声明,您需要两个发布者,每一个发布者都有自己的注册中心用户标识。
// Follows example in v2 spec appendix J
KeyedReference kr = new KeyedReference("Holding Company", // Key name
"parent-child", // Key value
TModel.RELATIONSHIPS_TMODEL_KEY);// Tmodelkey
PublisherAssertion pa = new PublisherAssertion(bk1, // from key
bk2, // to key
kr); // keyed reference
// Add publisher assertion linking two businesses
DispositionReport dr = proxy.add_publisherAssertions(token1.getAuthInfoString(),pa);
当一个发布者添加声明时,注册中心将关系置为未决状态,等待受到影响的企业响应,作出相同的声明,表明双方都同意该关系声明。第二个发布者的回应是添加相同的声明。一旦双方都声明了相同的关系,UDDI 注册中心将创建它们的企业关系。下面的代码片段中执行了一个查询,寻找影响该发布者的未决声明。来自声明报告的企业键值用来重新创建最初与之匹配的声明。
// List available assertions (relationships)
AssertionStatusReport asr = proxy.get_assertionStatusReport(token2.getAuthInfoString(),
CompletionStatus.TOKEY_INCOMPLETE);
Vector v = asr.getAssertionStatusItemVector();
for (int i = 0; i < v.size(); i++) {
AssertionStatusItem asi = (AssertionStatusItem)v.elementAt(i);
PublisherAssertion pa = new PublisherAssertion(asi.getFromKeyString(),
asi.getToKeyString(),
asi.getKeyedReference());
DispositionReport dr = proxy.add_publisherAssertions(pa);
}
使用服务投影
请注意,在撰写本文之时,公用 UDDI 企业注册中心还不支持服务投影。下面的信息供您将来参考。
在下面的代码片段中,第二个发布者创建一个服务投影。我们在相关的(第一个)企业上执行查询,这个企业中包含要投影的服务。代码将存储 BusinessService 对象,在之后的 save_business() 调用中被用来向第二个企业添加该服务作为投影。
BusinessDetail bd1 = proxy.get_businessDetail(bk1);
BusinessEntity be1 = (BusinessEntity)bd1.getBusinessEntityVector().elementAt(0);
BusinessServices bss1 = be1.getBusinessServices();
BusinessService serviceProjection = bss1.get(0); // Desired service
// Update a business and include the service projection
BusinessDetail bd2 = proxy.get_businessDetail(bk2);
BusinessEntity be2 = (BusinessEntity)bd2.getBusinessEntityVector().elementAt(0);
BusinessServices bs = new BusinessServices();
bs.add(serviceProjection);
be2.setBusinessServices(bs);
Vector entities = new Vector();
entities.addElement(be2);
proxy.save_business(token2.getAuthInfoString(),entities);
您可以检查服务,以确定它们是否为使用下面代码的投影。在本示例中,您将检索关于企业实体的信息,包括其服务的列表。您可以检查服务,以确定它们是否属于企业本身还是属于相关的企业。投影产生的服务会指定被投影的企业(而不是被查询的企业)的 businesskey(企业键值)。
// How to look at services and see if one is a service projection
BusinessDetail bd = proxy.get_businessDetail("businessKey");
BusinessEntity be = (BusinessEntity)bd.getBusinessEntityVector().elementAt(0);
BusinessServices bss = be.getBusinessServices();
for (int i = 0; i < bss.size(); i++) {
BusinessService bs = bss.get(i);
String businessKey = bs.getBusinessKey();
if (!businessServiceKey.equals(businessKey)) {
System.out.println("Service " + bs.getName() + " is a projection");
}
}
国际化
我们还实现了若干描述性字符串,作为允许使用特定于语言的标识符的对象。UDDI 通过允许用户指定这些对象的集合来处理国际化,每个对象中有一个值和语言标识符。如果提供的是一个单值,那么语言标识符缺省为创建发布者帐户时注册中心决定的语言,通常为英语。语言值是那些允许在 xml:lang 属性中使用的值,在 RFC 1766 中指定。
造成混淆的一个常见原因是错误地认为多个值将被连接起来,从而能够使用更长的字符串。事实并非如此。提供的每个对象都只有唯一的语言。将一个以上的对象指定为相同(或没有指定)语言标识符是错误的做法。UDDI4J 不会验证这些值。UDDI 注册中心将产生错误。
加入了新的 NLS 功能,那就是向 Name 类增加了语言标识符。
Name(string, language)
UDDIProxy 上的 find_business 和 find_service 方法现在接受“Name”对象的集合,而不是只接受一个单值。
自 UDDI 版本 1 出现就可以使用的 org.uddi4j.datatype.Description 类也有同样的行为和使用情况。
增强的查询
查询操作得到了巩固,这样我们就能够使用单独的 find_business() 调用来根据 DiscoveryURLs、IdentifierBag、CategoryBag 和 TmodelBag 进行搜索,而不必循环进行多个搜索。
UDDIProxy 上的 find_business() 方法的新签名是:
BusinessList find_business (FindQualifiers findQualifiers,
Vector names,
DiscoveryURLs discoveryBag,
IdentifierBag identifierBag,
CategoryBag categoryBag,
TmodelBag tModelBag
Int maxRows);
如 UDDI v2 规范的文档所示,还增加了另外的 findQualifier。
本文来自: 第七城市(www.th7.cn) 详细出处参考:http://www.th7.cn/Article/bc/ja/200801/98416_5.html