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:
?
然后在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:

然后根據向導生成一個Element.genmodel文件,打開這個文件后會出現一個編輯界面,選中根元素后點右鍵,選擇:Set SDO Defaults:

然后選擇Generate Model Code,完成后我們發現在src下多出了一堆代碼,這就是SDO模型代碼:

然后修改一下生成的代碼:打開生成的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

在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加入進去:

然后啟動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

如果看到上面的這個界面,就說明MyRemotableService已經通過Axis被注冊成為一個Web Service了。
現在在瀏覽器中輸入:http://localhost:8080/TestProject/TestServlet
得到以下結果:

?
5。總結
SCA的遠程服務是銜接模塊和模塊的重要元素,它使得模塊和模塊之間有了交互。當遠程服務配合上Scope以及回掉接口(異步時候的接口),將會發揮巨大的威力。
在該稿發布之時,我的UxSCA中遠程服務部分在處理復雜類型數據的時候出現了一些問題,所以目前還只能是定義單個元素的復雜類型(不能使用數組),并且調用方法必須是無參數的。希望不是什么大問題,我會及時改正。