SCA程序設(shè)計——遠程服務(wù),以及遠程服務(wù)實現(xiàn)的一些問題和想法
Posted on 2006-09-11 17:02 Dart 閱讀(2670) 評論(2) 編輯 收藏 所屬分類: SCA1.? 遠程服務(wù)概念
顧名思義,遠程服務(wù)是指可以通過遠程通訊進行調(diào)用的服務(wù)。
在
SCA
中,在定義服務(wù)的時候可以將服務(wù)定義為遠程服務(wù),通過
@Remotable
標簽加在服務(wù)接口的類名上,就將該服務(wù)接口定位為了遠程服務(wù)。
SCA
規(guī)范中這樣規(guī)定遠程服務(wù)的:
“
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
上面這段話主要意思是說,一個遠程服務(wù)可以通過
Entry Points
進行發(fā)布,并且能夠被該服務(wù)所在模塊以外,但包含了該服務(wù)的組件所調(diào)用,并且,遠程服務(wù)只能定義在
Java
接口類上,如果利用
Java
實現(xiàn)類(非
interface java
類)定義的服務(wù)進行
@Remotable
注釋,這種遠程服務(wù)是無效的。
可能上面的話有點太含糊其詞,我們下面具體講一下
SCA
遠程服務(wù)的定義,以及
uxsca
容器實現(xiàn)該標準的簡要實現(xiàn)手段。
2.?
定義遠程服務(wù)
根據(jù)上面我們所講的,在一個已經(jīng)被定義為服務(wù)的接口的接口類上加上注釋
@Remotable
,這樣就表示該
sca
服務(wù)將會是一個遠程服務(wù)。就這么簡單!
3
.
UxSCA
容器中遠程服務(wù)的實現(xiàn)概要
的確,光從
sca
規(guī)范上來看,一個遠程服務(wù)的定義是如此簡單,但是具體怎么去使用以及使用場合
,SCA
規(guī)范中沒有給出詳細的示例,所以這里我也只能是根據(jù)遠程服務(wù)定義去做實現(xiàn)了。
既然是遠程服務(wù),首先想到的實現(xiàn)手段就是
Web service
,當然,也可以用
Java
遠程調(diào)用。
既然使用
Web service
,那在
Java
語言里,
Axis
是一個既簡單又使用的
Web service
組件了。這里簡單說一下
Axis
是如何實現(xiàn)
web service
的:
Axis
其實也是通過
servlet
來實現(xiàn)
web service
的,通過一個
HTTP
的訪問,
Axis
的
servelet
會去獲得這段
HTTP
的
Request
信息,
HTTP
協(xié)議上架著
SOAP
,
Axis
解析出
SOAP
后定位到具體的一個
Java
實現(xiàn),然后在將該
Java
實現(xiàn)執(zhí)行后的結(jié)果(或者沒有返回結(jié)果)再構(gòu)造成對應(yīng)的
SOAP
返回回去。一般情況下,訪問
Axis
的客戶端是使用的
Axis
生成的客戶端代碼。
Axis
的
web service
實現(xiàn)大致如上所述,如有錯誤請讀者指出。
Axis
創(chuàng)建
Web service
的方法一般有
2
種:
1?.
Axis
實現(xiàn)
Web service
可以通過
jws
文件來定位服務(wù),也就是將一個寫好的單獨的
Java
類(
.java
文件)改后綴名為
.jws
,然后放到
Axis
的
webapp
下。訪問的
URL
如下:
??????????????? Http://webserveraddress/axis/services/SomeJws.jws
當我們通過上面的
url
去訪問的時候,
Axis
會去查找編譯好的
java
類,然后執(zhí)行后返回結(jié)果。
這種方法很簡單,但是很不實用。
2.
Axis
可以通過生成好的
wsdl
,利用
WSDL2Java
工具類產(chǎn)生
Axis
所需要的
wsdd
配置文件,并且生成對應(yīng)客戶端以及一些數(shù)據(jù)對象(
DO
)代碼。然后通過
Axis
工具類生成一個
server-config.wsdd
文件,將原先生成好的
wsdd
文件中的
service
配置加入到其中,再生成編譯好的
Java
類連同
server-config.wsdd
配置文件一同放到
Axis
的
webapp
目錄下對應(yīng)的位置,這樣可以通過
Axis
生成的客戶端去調(diào)用這個
web service
了。
UxSCA 是利用第二種方式去構(gòu)建遠程服務(wù)的。步驟如下:
UxSCA
本身是一個
Web
應(yīng)用,它的
web.xml
中加入了
Axis
的
servlet
,這樣就可以不去單獨將
Axis
作為一個
Web
應(yīng)用來獲得
Web service
的請求了。而
UxSCA
在啟動的時候(并不是
Web
服務(wù)容器啟動的時候),會去解析在自己維護下的模塊,從而獲得各個模塊的服務(wù)配置,然后:
1.
首先解析出遠程服務(wù)接口類,以及其實現(xiàn)類。
2.??
然后邊歷接口類中可以發(fā)布的接口方法(可發(fā)布的接口方法,必須是參數(shù)或者返回值都是簡單類型或者是
SDO
類型),生成對應(yīng)的
WSDL
文件。
3.??
利用生成好的
WSDL
文件以及服務(wù)接口類再生成
server-config.wsdd
中的
service
元素片段,并加入到該文件中(如果
server-config.wsdd
不存在,
UxSCA
會自動生成)
在作完以上工作后, UxSCA 會去自動啟動 Web 服務(wù)容器,這樣一來,遠程調(diào)用就可以通過 uxsca 容器去訪問遠程服務(wù)了。
其中有幾個問題存在:
1.???
服務(wù)的查詢是由
SCA
容器去管理的,外部具有遠程服務(wù)的接口的模塊組件怎么去獲得非本地模塊中的服務(wù)定義呢?
2.??
Axis
目前只支持
Castor
和
XMLBean
或者
JavaBean
的復雜類型數(shù)據(jù)結(jié)構(gòu),如何讓
Axis
支持
SDO
以及
JAXB
數(shù)據(jù)結(jié)構(gòu)呢?
3.??
如果外部模塊獲得了遠程服務(wù)的接口,
SCA
容器在返回接口實現(xiàn)的時候卻不會有一個真正的接口類實現(xiàn),就是說,外
部模塊只有一個
interface
類,沒有實現(xiàn)類(廢話),如何去返回一個能讓其調(diào)用的類呢?
對于第一個問題,我本人沒有想好,目前也只有一個不成熟的解決辦法:
SCA
在部署的時候能夠知道各個節(jié)點的位置,然后再統(tǒng)一啟動,每一個節(jié)點啟動解析完成后再通過網(wǎng)絡(luò)通訊將解析出來的服務(wù)模型反饋給每一個接點,這樣每一個接點上都會有其他接點的服務(wù)描述,于是在使用
ModuleContext
定位服務(wù)時候也就可以作到一致了。當然這只是我個人的想法,行不行得通還需要再去熟悉
SCA
規(guī)范后加上實踐才能證明其合理性。
第二個問題相對簡單一點,請看我的另一篇文章:讓
Axis
支持
EMF
類型
第三個問題如下:
假設(shè)我們定義了一個TestInterface接口,并且有一個實現(xiàn)類,TestInterfaceImpl,現(xiàn)在我們的某一個客戶端只擁有TestInterface這個接口類,而TestInterfaceImpl是在服務(wù)端的。所以在客戶端中,是找不到有關(guān)
TestInterface
的實現(xiàn)的,所以這里使用了
Java
的動態(tài)接口代理。利用
Proxy
,在用戶調(diào)用的時候,采用
Axis
的客戶端代碼訪問
Web Service
。
4
.遠程服務(wù)使用示例
新的UxSCA是和Tomcat幫定在了一起,它作為一個Tomcat的webapp存在。在Tomcat啟動的時候會去查詢定義的Module以及定義的服務(wù)。當前UxSCA版本并不是完整的版本,只是為了測試遠程服務(wù)而做的,在以后會完善改進。
下載地址(壓縮包分成了三份):
Balto1
Balto2
Balto3
準備環(huán)境:Eclipse-WTP 1.0
先將下載好的帶有UxSCA的Tomcat釋放到某個目錄下。
選擇Eclipse的首選項(Preferences...),增加一個Server Runtime,路徑指定到下栽好的Tomcat:
?
然后在webapps中找到一個balto.war,并倒入到Eclipse工程中。
如果讀者不是在WTP下調(diào)試,則不需要倒入。
然后新建立一個Java工程,命名為RemoteProject。
在該工程下建立一個XSD文件:Element.xsd,
這個Schema比較簡單,只有一個元素:Element,Element有兩個屬性:name,age:
?
< 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:
然后根據(jù)向?qū)梢粋€Element.genmodel文件,打開這個文件后會出現(xiàn)一個編輯界面,選中根元素后點右鍵,選擇:Set SDO Defaults:
然后選擇Generate Model Code,完成后我們發(fā)現(xiàn)在src下多出了一堆代碼,這就是SDO模型代碼:
然后修改一下生成的代碼:打開生成的ElementPackageImpl類,找到createExtendedMetaDataAnnotations方法,將
??????????(elementTypeEClass,?
???????????source,?
??????????? new ?String[]?{
????????????? " name " ,? " Element_._type " ,
????????????? " kind " ,? " empty "
???????????});
該成:
??????????(elementTypeEClass,?
???????????source,?
??????????? new ?String[]?{
????????????? " name " ,? " Element " ,
????????????? " kind " ,? " empty "
???????????});
改這段代碼的目的是為了讓EMF序列化模型的時候生成的XML片段準確一點,也就是Element_._type元素名要改成Element.
這里需要注意,如果我們生成SDO代碼是由Ecore生成的,就不會有上述的問題,如果是以XSD生成的就會出現(xiàn)這種情況。
然后我們還需要改一些其他代碼,找到initializePackageContents方法:
.....
initEClass(elementTypeEClass, ElementType.class, "ElementType", !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類是一個接口類,而且該接口類沒有
實現(xiàn)EObject以及DataObject。當然,這是UxSCA的一個局限性,在以后的版本中我會改進的。
現(xiàn)在我們已經(jīng)生成了我們的數(shù)據(jù)對象,在該工程下建立一個接口:MyRemotableService,該接口具有兩個方法:getElement和
getName,getElement返回的是我們剛才生成的ElementTypeImpl對象,getName返回的是一個String。然后我們將這個類
利用SCA的Annotation定義為一個Remotable:
import ?org.example.element.impl.ElementTypeImpl;
import ?org.osoa.sca.annotations.Remotable;
@Remotable
public ? interface ?MyRemotableService?{
????ElementTypeImpl?getElement();
????String?getName();
}
然后我們創(chuàng)建一個類,讓這個類實現(xiàn)MyRemotableService接口,并且將它定義成一個Service:
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 " ;
????}
}
現(xiàn)在我們完成了遠程服務(wù)的定義了。
然后我們將RemoteProject中代碼進行打包。先選種src文件夾,然后點右鍵,選擇Export,然后選JarFile,根據(jù)提示即可完成打包。我們將這個命名為Test.jar
現(xiàn)在打開Tomcat的webapps,會發(fā)現(xiàn)一個名為balto的web項目(UxSCA以后的代號就為balto),
打開文件夾后會發(fā)現(xiàn)一個名位sca-config.xml文件,打開文件,加入這么一段代碼:
???????? < values >
???????????? < value?type="jar" name = "Test.jar"/?>
????????</values>
</property>
這段代碼的是讓UxSCA找到我們需要加入到SCA容器管理的包的入口,value的名是包名。
現(xiàn)在我們通過外部的的應(yīng)用程序去訪問這個服務(wù)。
首先,我們需要將這個服務(wù)的接口類MyRemotableService以及SDO的數(shù)據(jù)模型打包,也就是說要給訪問端工程數(shù)據(jù)以及服務(wù)的接口(這里我們不要把MyRemotableService的實現(xiàn)類打進去)。當然,可以直接通過訪問web 服務(wù)的方法訪問,這里這么做只是為了測試UxSCA容器定位服務(wù)的功能。
然后我們再新建一個web項目,TestProject,將剛打好的包放到其中。并在這個項目下創(chuàng)建一個Servlet
在doGET中寫如以下代碼:
????????????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加入進去:
然后啟動Tomcat,在Console上我們會發(fā)現(xiàn)這么一段話:
解析Java?Annotation得到一個Service的Java實現(xiàn)?-?Service?:?MyRemotableService?JavaImplemention?:?org.uxteam.sca.example.MyRemotableServiceImpl
為遠程服務(wù)MyRemotableService創(chuàng)建WSDL文件

遠程服務(wù)MyRemotableServiceWSDL文件創(chuàng)建完成
注冊遠程服務(wù)到Axis?wsdd文件中MyRemotableService
這就說明我們的服務(wù)已經(jīng)被發(fā)現(xiàn),并且UxSCA已經(jīng)將該服務(wù)發(fā)布成了遠程服務(wù)。
現(xiàn)在我們在瀏覽器中寫訪問這個服務(wù):
http://localhost:8080/balto/services/MyRemotableService
如果看到上面的這個界面,就說明MyRemotableService已經(jīng)通過Axis被注冊成為一個Web Service了。
現(xiàn)在在瀏覽器中輸入:http://localhost:8080/TestProject/TestServlet
得到以下結(jié)果:
?
5??偨Y(jié)
SCA的遠程服務(wù)是銜接模塊和模塊的重要元素,它使得模塊和模塊之間有了交互。當遠程服務(wù)配合上Scope以及回掉接口(異步時候的接口),將會發(fā)揮巨大的威力。
在該稿發(fā)布之時,我的UxSCA中遠程服務(wù)部分在處理復雜類型數(shù)據(jù)的時候出現(xiàn)了一些問題,所以目前還只能是定義單個元素的復雜類型(不能使用數(shù)組),并且調(diào)用方法必須是無參數(shù)的。希望不是什么大問題,我會及時改正。