1. 概述
在前面我已经讲了一些关于SCA的基础知识,使用了本人实现的一个SCA容器做为讲解示例,9月中旬我把这个SCA容器做为开源项目在Sourceforge.net上立项了,并且正式给这个SCA容器取名为Balto。
这一次我将继续使用Balto作为示例SCA容器,并讲一下SCA程序设计中的外部服务(ExternalService)
ExternalService 在 SCA 中可以被看作是一个 Module 的应用出口,它定义了 Module 所要调用的非 module 内部服务的外部服务信息,在SCA程序设计中的地位举足轻重。关于ExternalService的一些基本信息介绍,大家可以看一下我的另一篇文章.
ExternalService 虽然描述了外部服务的信息,但是它需要通过 Binding 来对该外部服务的访问细节进行描述。关于 Binding 的更多信息,也可以查看上面所说的那篇文章。
我们接下来将要讲的 ExternalService 都是基于 WebService Binding 的外部服务。
2 .ExternalService的 XML 格式
ExternalService 的定义需要写在 sca.module 文件中,具体格式如下:
< externalService name ="xs:NCName" verride ="sca:OverrideOptions" ? > *
< binding .binding-type uri ="xs:anyURI" /> *
externalService >
1) 先看 externalService 元素,该元素具有两个属性,一个是 name, 一个是 override , name 是标识 externalService 的名称的,在 ModuleContext 中通过 localService 定位服务的时候,是通过 name 属性所写的名称进行查询外部服务的。
2) Interface 元素在之前的《本地服务》一文中有介绍,主要是指明该外部服务所对应的接口类型以及位置。一般情况下都使用 java 类型的接口:
3) Binding
3. 如何去构建一个可用的外部服务
既然是外部服务,有很大程度上都是属于异地节点上的服务,所以很多情况下我们使用外部服务都需要一些远程调用的手段对其进行调用,所以 ExternalService 也可以直接看作是一个 Remote Service 。
我们最常见的远程调用方式有以下几种: RMI 、 EJB 、 Web Service
外部服务远程调用的绑定协议是由 Binding 元素给出的,目前 SCA 规范中给除了两种 Binding ,一种是 SCABinding ,这种 Binding 方式没有确切的说明;还有一种就是 WebService Binding ,顾名思义,这种 Binding 是基于 WebService 的一种远程调用 Binding 。
一旦 ExternalService 指定了明确的 Binding 方式后,在调该 ExternalService 指定的接口的方法的时候, Balto SCA 容器就会通过 Java 的动态接口代理技术,生成一个动态的接口代理,然后通过 Binding 的类型以及 ExternalService 的一些信息细节,在动态接口代理方法中通过 Binding 对应的远程调用协议(比如 Web Service )进行对应的调用,如图所示: 
图 1
生成的不同的
InvokeHandler 会根据协议需要,解析出 Binding 类型中的信息细节,然后通过一些远程调用手段去调用该外部服务所在位置的服务实体。
说得不明不白的,举个
WebService Binding 的例子会清楚一些:
首先我们在 sca.module 文件中写入:
< externalService name ="WeatherProvider" >
< interface .java interface ="net.x.webservice.client.WeatherProvider" />
< binding .ws port ="http://www.webservicex.net/globalweather.asmx?WSDL
#wsdl.endpoint(GlobalWeather/GlobalWeatherSoap)" />
externalService >
这个
externalService 的含义是,我们讲通过一个 net.x.webservice.clinet.WeatherProvider 的接口类 ,去调用一个 web servicec ,而这个 web service 是通过 binding.ws 来描述的,这个 web service 的 wsdl 的地址是 http://www.webservicex.net/globalweather.asmx?WSDL ,而且该外部服务调用的是这个 wsdl 中描述的 GlobalWeather 服务,并且这个服务在 wsdl 中的 binding 名是 GlobalWeatherSoap 。
也就是说,Web Service binding的port属性所需值的格式规范是这样的:
WSDL 1.1 :
WSDL 2.0
不过这个接口是没有实现的,因为我们不可能在本地去实现这个接口类——我们根本就不知道这个服务的具体业务逻辑。我们只是通过这个接口类的方法调用,来确定所需要调用这个 Web Service 所要做的工作。
根据上面的图 1 可以清楚的知道,Balto SCA容器去定位一个外部服务的时候,当得到了该远程服务对应的Java接口后,会生成一个接口的动态代理,并且,通过这个远程服务的Binding信息以及一些调用方法信息(参数值,方法名),确定如何利用Axis2 Client去调用远程服务Binding到的那个Web Service。下面是动态代理处理用户调用方法的简要代码介绍:
public Object excute(ExternalService externalService, Binding binding,
Object proxy, Method method, Object[] parameters)
throws Throwable {
try {
if (binding instanceof WebServiceBinding) {
WebServiceBinding wsBinding = (WebServiceBinding) binding;
String wsdlURI = wsBinding.getWSDLNameSpaceURI();
WSDLFactory wsdlFactory = WSDLFactoryImpl.newInstance();
WSDLReader wsdlReader = wsdlFactory.newWSDLReader();
wsdlReader.setFeature( " javax.wsdl.verbose " , true );
Definition definition = wsdlReader.readWSDL( null ,
wsdlURI);
String webServiceName = wsBinding.getServiceName();
String portName = wsBinding.getPortName();
……….
// 这是Axis2 Client的调用代码
ServiceClient service = new ServiceClient();
Options ptions = new Options();
service.setOptions(options);
EndpointReference targetEPR = new
EndpointReference(serviceURI);
options.setTo(targetEPR);
targetEPR.setAddress(serviceURI);
service.setTargetEPR(targetEPR);
…….
// 根据返回值类型来确定调用方式
if (returnType == Void. class ) {
service.sendRobust(omElement);
return null ;
} else {
resultElement = service.sendReceive(omElement);
}
// 处理返回的SOAP体
………
}
这样做的目的是为了屏蔽掉开发人员在调用时候的一些细节处理,开发人员不关心整个 Web Service 的调用过程,只需要像调用简单 java 类一样调用即可。
4 .实战——天气预报
我们通过一个简单的例子来看看如何使用 Balto SCA 来进行做 ExternalService 。 示例工程下载
先考虑这么一个需求:
我们在登录一些网页的时候,网页上会显示出我们所在地当前的天气情况,这种比较个性化的功能常常能吸引不少网民的眼球。
问题是如何去实现呢?
首先,我们登录到某个网页上的时候,网站后台会得到我们的访问 IP 地址,通过这个地址是可以确定我们现在所在位置的。
然后,根据我们 IP 解析出来的物理位置,查询该地址最近的天气信息。
大概是这样去做。
但是我们怎么去解析 IP 地址获得物理位置呢?这种工作一般需要有一个存储了大量的 IP 地址到物理地址映射的数据库,一般情况下我们不可能拥有这样一个数据库。更何况,即使得到了物理地址,我们也不可能通过计算机去计算出当前的天气情况吧??
虽然我们不能做这些工作,但是在网络上存在这大量类似功能的 Web Service 。我们可以通过这些免费的 Web Service 来定制这么一个功能。
准备工作:安装 Eclipse WTP 1.0 ,下载 Balto_tomcat_0_0_2
我在网上找到了两个 Web Service:
1. 获得 IP 地址和物理地址映射的 WebService:
http://ws.fraudlabs.com/ip2locationwebservice.asmx?wsdl
2. 获得天气情况的 Web Service
http://www.webservicex.net/globalweather.asmx?WSDL
现在需要通过 Balto SCA 将这两个 Web Service 做成 ExternalService :
首先将 Balto_tomcat_test_0_9 添加到应用服务器中。这里需要说明一下, Balto_tomcat_test_0_9 是整合在 tomcat 中的,就是说Balto = Tomcat,Balto替换了tomcat的启动Host入口类,所以在 tomcat 启动的时候 Balto 就会去解析部署的web application,当发现该web应用是一个SCA模块的话,就会对这个web application进行解析,并注册解析出的相关SCA模块信息。当然,这些细节开发人员是不用关心的。 

完成操作后会生成一个新的EMF Model 文件,打开这个文件的编辑器,选中根节点,在弹出菜单中选择 Set SDO Defaults : 
完成上述操作后,再在弹出菜单中选择 Generate Model Code , Eclipse 就会自动生成一套 SDO 的模型代码,我们还要修改生成的SDO代码中的XXXPackageImpl的createExtendedMetaDataAnnotations方法,将代码中描述Element的name不正确的地方修改过来,并把创建EClass的地方所给出的ImplementClass的地方,将接口类替换成接口的实现类。上述步骤可以看一下《SCA程序设计——远程服务,以及实现远程服务的问题和想法》,其中有具体说明。
温馨小贴士: Eclipse EMF生成的SDO代码中,用于描述XML的Element名以及对应Java类的XXXPackageImpl类,其中含有一个createExtendedMetaDataAnnotations方法,这个方法中描述了对应Java类以及Java类具有的属性所对应的XML中的Element以及Attribute的名称。但是一般利用XSD或者WSDL直接生成的SDO代码中,EMF会默认给出一个DocumentRoot的类,也就是说这个类才是EMF真正序列化java对象的根节点,如果不利用DocumentRoot包装我们的创建的SDO Java对象,序列化出来的XML就会出现XML名“不正确”的情况,而这种所谓“不正确”情况下Java对象对应的XML名,是在ExtendedMetaDataAnnotations中给出的。当然,上述情况只限于EMF生成的SDO. 
将 SDO 代码生成好后,接下来就需要创建一个接口类。创建的这个接口类就是 ExternalService 所要指定的接口类。
这个接口类需要和ExternalService在Binding中给出的 WSDL 的 PortType 具有 相同的操作。使用过 Axis 或者 Axis2 的读者一定会联想到 Axis 以及 Axis2 提供给开发人员的 WSDL2Java 的工具,这个工具就是将 WSDL 生成一套 Axis 的客户端,包括复杂类型以及所要调用的 Web
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/12639375/viewspace-151122/ ,如需转载,请注明出处,否则将追究法律责任。