月蚀传说

浮躁让人失去理智
posts - 25, comments - 101, trackbacks - 0, articles - 0
  BlogJava :: 首页 ::  :: 联系 :: 聚合  :: 管理

1.  远程服务概念


顾名思义,远程服务是指可以通过远程通讯进行调用的服务。

SCA 中,在定义服务的时候可以将服务定义为远程服务,通过 @Remotable 标签加在服务接口的类名上,就将该服务接口定位为了远程服务。


SCA
规范中这样规定远程服务的

Remotable services are services that can be published through entry points. Published services can be 
accessed by clients outside of the module that contains the component that provides the service.

Whether a service is remotable is defined by the interface of the service. In the case of Java this is defined

by adding the @Remotable annotation to the Java interface. A service whose interface is defined by a Java class is not remotable. "


上面这段话主要意思是说,一个远程服务可以通过
Entry Points 进行发布,并且能够被该服务所在模块以外,但包含了该服务的组件所调用,并且,远程服务只能定义在 Java 接口类上,如果利用 Java 实现类(非 interface java 类)定义的服务进行 @Remotable 注释,这种远程服务是无效的。


可能上面的话有点太含糊其词,我们下面具体讲一下
SCA 远程服务的定义,以及 uxsca 容器实现该标准的简要实现手段。


2. 
定义远程服务

根据上面我们所讲的,在一个已经被定义为服务的接口的接口类上加上注释 @Remotable ,这样就表示该 sca 服务将会是一个远程服务。就这么简单!


3
UxSCA 容器中远程服务的实现概要


的确,光从
sca 规范上来看,一个远程服务的定义是如此简单,但是具体怎么去使用以及使用场合 ,SCA 规范中没有给出详细的示例,所以这里我也只能是根据远程服务定义去做实现了。


既然是远程服务,首先想到的实现手段就是
Web service ,当然,也可以用 Java 远程调用。


既然使用
Web service ,那在 Java 语言里, Axis 是一个既简单又使用的 Web service 组件了。这里简单说一下 Axis 是如何实现 web service 的:


Axis
其实也是通过 servlet 来实现 web service 的,通过一个 HTTP 的访问, Axis servelet 会去获得这段 HTTP Request 信息, HTTP 协议上架着 SOAP Axis 解析出 SOAP 后定位到具体的一个 Java 实现,然后在将该 Java 实现执行后的结果(或者没有返回结果)再构造成对应的 SOAP 返回回去。一般情况下,访问 Axis 的客户端是使用的 Axis 生成的客户端代码。 Axis web service 实现大致如上所述,如有错误请读者指出。


Axis
创建 Web service 的方法一般有 2 种:

1 . Axis 实现 Web service 可以通过 jws 文件来定位服务,也就是将一个写好的单独的 Java 类( .java 文件)改后缀名为 .jws ,然后放到 Axis webapp 下。访问的 URL 如下:


                Http://webserveraddress/axis/services/SomeJws.jws


当我们通过上面的
url 去访问的时候, Axis 会去查找编译好的 java 类,然后执行后返回结果。

这种方法很简单,但是很不实用。

2.
Axis 可以通过生成好的 wsdl ,利用 WSDL2Java 工具类产生 Axis 所需要的 wsdd 配置文件,并且生成对应客户端以及一些数据对象( DO )代码。然后通过 Axis 工具类生成一个 server-config.wsdd 文件,将原先生成好的 wsdd 文件中的 service 配置加入到其中,再生成编译好的 Java 类连同 server-config.wsdd 配置文件一同放到 Axis webapp 目录下对应的位置,这样可以通过 Axis 生成的客户端去调用这个 web service 了。

 

UxSCA 是利用第二种方式去构建远程服务的。步骤如下:


UxSCA
本身是一个 Web 应用,它的 web.xml 中加入了 Axis servlet ,这样就可以不去单独将 Axis 作为一个 Web 应用来获得 Web service 的请求了。而 UxSCA 在启动的时候(并不是 Web 服务容器启动的时候),会去解析在自己维护下的模块,从而获得各个模块的服务配置,然后:

1. 首先解析出远程服务接口类,以及其实现类。

2.   然后边历接口类中可以发布的接口方法(可发布的接口方法,必须是参数或者返回值都是简单类型或者是 SDO 类型),生成对应的 WSDL 文件。

3.   利用生成好的 WSDL 文件以及服务接口类再生成 server-config.wsdd 中的 service 元素片段,并加入到该文件中(如果 server-config.wsdd 不存在, UxSCA 会自动生成)

在作完以上工作后, UxSCA 会去自动启动 Web 服务容器,这样一来,远程调用就可以通过 uxsca 容器去访问远程服务了。

 

其中有几个问题存在:

1.    服务的查询是由 SCA 容器去管理的,外部具有远程服务的接口的模块组件怎么去获得非本地模块中的服务定义呢?

2.  
Axis 目前只支持 Castor XMLBean 或者 JavaBean 的复杂类型数据结构,如何让 Axis 支持 SDO 以及 JAXB 数据结构呢?

3.   如果外部模块获得了远程服务的接口, SCA 容器在返回接口实现的时候却不会有一个真正的接口类实现,就是说,外
部模块只有一个
interface 类,没有实现类(废话),如何去返回一个能让其调用的类呢?


对于第一个问题,我本人没有想好,目前也只有一个不成熟的解决办法:
SCA 在部署的时候能够知道各个节点的位置,然后再统一启动,每一个节点启动解析完成后再通过网络通讯将解析出来的服务模型反馈给每一个接点,这样每一个接点上都会有其他接点的服务描述,于是在使用 ModuleContext 定位服务时候也就可以作到一致了。当然这只是我个人的想法,行不行得通还需要再去熟悉 SCA 规范后加上实践才能证明其合理性。


第二个问题相对简单一点,请看我的另一篇文章:
Axis 支持 EMF 类型


第三个问题如下:

假设我们定义了一个TestInterface接口,并且有一个实现类,TestInterfaceImpl,现在我们的某一个客户端只拥有TestInterface这个接口类,而TestInterfaceImpl是在服务端的。所以在客户端中,是找不到有关 TestInterface 的实现的,所以这里使用了 Java 的动态接口代理。利用 Proxy ,在用户调用的时候,采用 Axis 的客户端代码访问 Web Service

 

4 .远程服务使用示例
先下载UxSCA包。
新的UxSCA是和Tomcat帮定在了一起,它作为一个Tomcat的webapp存在。在Tomcat启动的时候会去查询定义的Module以及定义的服务。当前UxSCA版本并不是完整的版本,只是为了测试远程服务而做的,在以后会完善改进。

下载地址(压缩包分成了三份):
Balto1

Balto2

Balto3
准备环境:Eclipse-WTP 1.0
先将下载好的带有UxSCA的Tomcat释放到某个目录下。
选择Eclipse的首选项(Preferences...),增加一个Server Runtime,路径指定到下栽好的Tomcat:

balto1.JPG 

然后在webapps中找到一个balto.war,并倒入到Eclipse工程中。

如果读者不是在WTP下调试,则不需要倒入。

然后新建立一个Java工程,命名为RemoteProject。

在该工程下建立一个XSD文件:Element.xsd,
这个Schema比较简单,只有一个元素:Element,Element有两个属性:name,age:

 

<? xml version="1.0" encoding="UTF-8" ?>
< schema  xmlns ="http://www.w3.org/2001/XMLSchema"
    targetNamespace
="http://www.example.org/Element"
    xmlns:tns
="http://www.example.org/Element" >
    
< element  name ="Element" >
        
< complexType >
            
< attribute  name ="name"  type ="string" ></ attribute >
            
< attribute  name ="age"  type ="int" ></ attribute >
        
</ complexType >
    
</ element >
</ schema >

选择这个Schema点击右键,选择新建一个EMF Model:

balto2.JPG

然后根据向导生成一个Element.genmodel文件,打开这个文件后会出现一个编辑界面,选中根元素后点右键,选择:Set SDO Defaults:

emfmodel.JPG

然后选择Generate Model Code,完成后我们发现在src下多出了一堆代码,这就是SDO模型代码:

modelcode.JPG

然后修改一下生成的代码:打开生成的ElementPackageImpl类,找到createExtendedMetaDataAnnotations方法,将

addAnnotation
          (elementTypeEClass, 
           source, 
           
new  String[] {
             
" name " " Element_._type " ,
             
" kind " " empty "
           });

该成:

addAnnotation
          (elementTypeEClass, 
           source, 
           
new  String[] {
             
" name " " Element " ,
             
" kind " " empty "
           });

改这段代码的目的是为了让EMF序列化模型的时候生成的XML片段准确一点,也就是Element_._type元素名要改成Element.

这里需要注意,如果我们生成SDO代码是由Ecore生成的,就不会有上述的问题,如果是以XSD生成的就会出现这种情况。

然后我们还需要改一些其他代码,找到initializePackageContents方法:

initEClass(documentRootEClass, DocumentRoot. class " DocumentRoot " ! IS_ABSTRACT,  ! IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS);
.....
initEClass(elementTypeEClass, ElementType.class, "ElementType", !IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS);


将上面代码修改为:

initEClass(documentRootEClass, DocumentRootImpl. class " DocumentRoot " ! IS_ABSTRACT,  ! IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS);
.....
initEClass(elementTypeEClass, ElementTypeImpl.class, "ElementType", !IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS);


改这段代码的目的是为了让UxSCA能认出我们的复杂类型是一个SDO模型。因为在UxSCA在遇到EMF模型时候,会去查看这个

EClass的instance类是否是一个SDO模型,但是在EMF生成的代码中,EClass的instance类是一个接口类,而且该接口类没有

实现EObject以及DataObject。当然,这是UxSCA的一个局限性,在以后的版本中我会改进的。


现在我们已经生成了我们的数据对象,在该工程下建立一个接口:MyRemotableService,该接口具有两个方法:getElement和

getName,getElement返回的是我们刚才生成的ElementTypeImpl对象,getName返回的是一个String。然后我们将这个类

利用SCA的Annotation定义为一个Remotable:

package  org.uxteam.sca.example;

import
 org.example.element.impl.ElementTypeImpl;
import
 org.osoa.sca.annotations.Remotable;

@Remotable
public   interface
 MyRemotableService {
    ElementTypeImpl getElement();
    String getName();
}



然后我们创建一个类,让这个类实现MyRemotableService接口,并且将它定义成一个Service:

@Service(MyRemotableService. class )
public   class  MyRemotableServiceImpl  implements
 MyRemotableService {

    
/*
 (non-Javadoc)
     * @see org.uxteam.sca.example.MyRemotableService#getElement()
     
*/

    
public  ElementTypeImpl getElement() {
        ElementTypeImpl element 
=
 (ElementTypeImpl) ElementFactory.eINSTANCE.createElementType();
        element.setAge(
10
);
        element.setName(
" Element Name "
);
        
return
 element;
    }
    
/*
 (non-Javadoc)
     * @see org.uxteam.sca.example.MyRemotableService#getName()
     
*/

    
public  String getName() {
        
return   " This is a remotable service "
;
    }
}



现在我们完成了远程服务的定义了。

然后我们将RemoteProject中代码进行打包。先选种src文件夹,然后点右键,选择Export,然后选JarFile,根据提示即可完成打包。我们将这个命名为Test.jar

现在打开Tomcat的webapps,会发现一个名为balto的web项目(UxSCA以后的代号就为balto),

打开文件夹后会发现一个名位sca-config.xml文件,打开文件,加入这么一段代码:

< property name  =   " entries " >
        
< values >
            
< value type="jar" name = "Test.jar"/ >
        </values>
</property>


这段代码的是让UxSCA找到我们需要加入到SCA容器管理的包的入口,value的名是包名。

现在我们通过外部的的应用程序去访问这个服务。

首先,我们需要将这个服务的接口类MyRemotableService以及SDO的数据模型打包,也就是说要给访问端工程数据以及服务的接口(这里我们不要把MyRemotableService的实现类打进去)。当然,可以直接通过访问web 服务的方法访问,这里这么做只是为了测试UxSCA容器定位服务的功能。

然后我们再新建一个web项目,TestProject,将刚打好的包放到其中。并在这个项目下创建一个Servlet

testservlet.JPG


在doGET中写如以下代码:

     protected   void  doGet(HttpServletRequest request,
            HttpServletResponse response) 
throws
 ServletException, IOException {
        ModuleContext context 
=
 CurrentModuleContext.getContext();
        MyRemotableService service 
=
 (MyRemotableService) context
                .locateService(
" MyRemotableService "
);

        ElementTypeImpl obj 
=
 service.getElement();
        String name 
=
 obj.getName();
        
        System.out.println(name);
        System.out.println(obj.getName());
        System.out.println(obj.getAge());
    }


然后我们在Eclipse的Servers视图下建立一个server:并把我们刚才新建的TestProject和倒入的balto加入进去:

servers.JPG



然后启动Tomcat,在Console上我们会发现这么一段话:

解析Java Annotation 得到一个Service - org.uxteam.sca.example.MyRemotableServiceImpl
解析Java Annotation得到一个Service的Java实现 - Service : MyRemotableService JavaImplemention : org.uxteam.sca.example.MyRemotableServiceImpl
为远程服务MyRemotableService创建WSDL文件.
远程服务MyRemotableServiceWSDL文件创建完成
注册远程服务到Axis wsdd文件中MyRemotableService


这就说明我们的服务已经被发现,并且UxSCA已经将该服务发布成了远程服务。

现在我们在浏览器中写访问这个服务:

http://localhost:8080/balto/services/MyRemotableService

axis.JPG

如果看到上面的这个界面,就说明MyRemotableService已经通过Axis被注册成为一个Web Service了。

现在在浏览器中输入:http://localhost:8080/TestProject/TestServlet

得到以下结果:

teset.JPG

 

5。总结

SCA的远程服务是衔接模块和模块的重要元素,它使得模块和模块之间有了交互。当远程服务配合上Scope以及回掉接口(异步时候的接口),将会发挥巨大的威力。

在该稿发布之时,我的UxSCA中远程服务部分在处理复杂类型数据的时候出现了一些问题,所以目前还只能是定义单个元素的复杂类型(不能使用数组),并且调用方法必须是无参数的。希望不是什么大问题,我会及时改正。


评论

# re: SCA程序设计——远程服务,以及远程服务实现的一些问题和想法  回复  更多评论   

2006-09-14 18:36 by 冰川
很好的东西
谢谢啊。

# re: SCA程序设计——远程服务,以及远程服务实现的一些问题和想法  回复  更多评论   

2007-01-18 19:48 by coolfish
SCA的远程服务,类似这样的服务应该是可以通过ESB这样的基础设施来达到目的的吧

只有注册用户登录后才能发表评论。


网站导航: