| 級別: 中級 Sreedevi Penugonda (sreedevip@in.ibm.com), 系統軟件工程師, IBM Software Group Rakesh Kumar Dash (rakesdas@in.ibm.com), 系統軟件工程師, IBM Software Group
2007 年 1 月 04 日 本 文是系列文章的第 2 部分,描述如何使用 Java 編程語(yǔ)言實(shí)現 SCA 組件以及組件實(shí)現可以如何充當其他服務(wù)的客戶(hù)機。文中將對遠程和本地服務(wù)的實(shí)現及其配置進(jìn)行說(shuō)明。將討論獲取服務(wù)的不同方法——引用注入和模塊上下文。還 將說(shuō)明用于聲明實(shí)現的可配置方面的各個(gè) Java Annotation。 引言 SCA Java 實(shí)現模型提供了用于在 Java 中實(shí)現 SCA 組件的框架。組件實(shí)現可以提供服務(wù),也可以充當其他服務(wù)的客戶(hù)機。本文將說(shuō)明 SCA Java 組件實(shí)現和非 SCA Java 組件實(shí)現可以如何獲得對服務(wù)的訪(fǎng)問(wèn)和調用服務(wù)方法。在本文中,所有元數據都使用 Java 2 Standard Edition (J2SE) 5 提供的 Annotation 功能定義。不過(guò),SCA 服務(wù)客戶(hù)機和組件實(shí)現也可使用 J2SE 1.4。
基本組件實(shí)現模型 此模型描述本地或遠程服務(wù)的實(shí)現以及特定于實(shí)現的配置屬性。 實(shí)現服務(wù) 服務(wù)使用接口表示,而接口則是使用以下方法之一進(jìn)行定義的: - Java 接口(SCA 的首選方式)
- Java 類(lèi)
- 從 Web 服務(wù)描述語(yǔ)言(Web Services Description Language,WSDL)端口類(lèi)型生成的 Java 接口
Java 類(lèi)組件實(shí)現會(huì )實(shí)現服務(wù)接口定義的所有操作,它使用 @Service Annotation 來(lái)指定服務(wù)通過(guò)使用 Java 實(shí)現而實(shí)現的服務(wù)接口。旨在用作服務(wù)實(shí)現的類(lèi)并不必具有 @Service Annotation。@Service Annotation 具有兩個(gè)特性: - Interfaces——此值是應該由其組件作為服務(wù)公開(kāi)的接口或類(lèi)對象的值。
- Value——僅在提供單個(gè)服務(wù)接口時(shí)的快捷方式。沒(méi)有特性的 @Service 沒(méi)有意義。
以下代碼片段顯示了名為 PayrollDataService 的 Java 服務(wù)接口和名為 PayrollDataServiceImpl 的 Java 接口的 Java 實(shí)現類(lèi)。 清單 1. Java 服務(wù)接口 package services.payrolldata; public interface PayrollDataService { float getBasic(String employeeID); float getvariablePay(String employeeID); float getHRA(String employeeID); float getProfessionalTax(String employeeID); float getNetPay(String employeeID); } | 清單 2. Java 服務(wù)組件實(shí)現 package services.payrolldata; Import org.osoa.sca.annotations.*; @Service(PayrollDataService.class) public class PayrollDataServiceImp implements PayrollDataService { public float getBasic(String employeeID){ . . . } public float getVariablePay(String employeeID){ . . . } public float getHRA(String employeeID){ . . . } float getProfessionalTax(String employeeID){ . . . } public float getNetPay(String employeeID){ . . . } }
| 如果在 Java 實(shí)現類(lèi)中,服務(wù)接口由類(lèi)本身定義,則上述代碼中的 @Service Annotation 將替換為 @Service(PayrollDataServiceImp.class)。下面將說(shuō)明此組件實(shí)現的組件類(lèi)型,如清單 3 中所示。 清單 3. 組件類(lèi)型 <!--<?xml version="1.0" encoding="ASCII"?> <componentType xmlns=http://www.osoa.org/xmlns/sca/0.9> <service name="PayrollDataService"> <interface.java interface="services.payrolldata. PayrollDataService"/> </service> </componentType> -->
| 使用了接口的子類(lèi)來(lái)提供具有相同接口的兩個(gè)服務(wù)。@Service Annotation 同時(shí)列出了這兩個(gè)接口。 實(shí)現遠程服務(wù) 通過(guò)入口點(diǎn)發(fā)布遠程服務(wù),以提供訪(fǎng)問(wèn)外部組件的方法。為了定義遠程服務(wù),將在服務(wù)的 Java 接口中添加 @Remotable Annotation。接口不由 Java 類(lèi)定義的服務(wù)不是遠程服務(wù)。從 WSDL 端口類(lèi)型生成的 Java 接口是遠程接口。 以下將對帶 @Remotable Annotation 的遠程服務(wù)的 Java 接口進(jìn)行說(shuō)明: 清單 4. 帶 @Remotable Annotation 的遠程服務(wù)的 Java 接口 package services.payroll; import org.osoa.sca.annotations.Remotable; @Remotable public interface PayrollService{ public PayrollStatement getPayrollStatement(String employeeID); } | 下一個(gè)代碼片段顯示了 PayrollStatement Java 接口。 清單 5. PayrollStatement Java 接口 package services.payroll; import java.util.List; public interface PayrollStatement { List getPayrollDetails(); float getNetPay(); } | 遠程服務(wù)使用封送技術(shù)來(lái)交換復雜數據類(lèi)型。在使用 Web 服務(wù)綁定時(shí),會(huì )將服務(wù)數據對象 (Service Data Object) 或 JAXB 數據類(lèi)型作為參數傳遞。遠程服務(wù)使用按值數據交換語(yǔ)義。 遠程服務(wù)的實(shí)現上的 @AllowPassByReference Annotation 用于聲明它是否允許在對其進(jìn)行調用時(shí)通過(guò)引用數據交換語(yǔ)義進(jìn)行傳遞。 以下代碼片段顯示了實(shí)現遠程服務(wù)并允許通過(guò)引用傳遞的 Java 組件實(shí)現。 清單 6. 具有遠程服務(wù)和允許通過(guò)引用傳遞的 Java 組件實(shí)現 package services.payrolldata; Import org.osoa.sca.annotations.*; @Service(PayrollDataService.class) @AllowsPassByReference public class PayrollDataServiceImp implements PayrollDataService { . . . . } | 實(shí)現本地服務(wù) 本地服務(wù)僅能由屬于相同模塊的客戶(hù)機調用。Java 本地服務(wù)接口是在不使用 @Remotable Annotation 的情況下定義的。 以下代碼片段顯示了本地服務(wù)的 Java 接口。 清單 7. 本地服務(wù)的 Java 接口 package services.payrolldata; public interface PayrollDataService { float getBasic(String employeeID); float getvariablePay(String employeeID); float getHRA(String employeeID); float getProfessionalTax(String employeeID); float getNetPay(String employeeID); } | 本地服務(wù)的調用的數據交換語(yǔ)義是以引用為基礎的,其中,客戶(hù)機或服務(wù)提供者會(huì )看到對參數的更改。 實(shí)現有狀態(tài)資源模式 確定了范圍的服務(wù)通過(guò)在服務(wù)的接口定義或服務(wù)類(lèi)上使用 @Scope Annotation 加以表示。@Scope Annotation 具有一個(gè)參數,即 value,其值為范圍的名稱(chēng)。 以下是當前支持的范圍值: - Stateless(缺?。?/em>——每個(gè)請求分開(kāi)處理。Java 實(shí)例可以從服務(wù)實(shí)例池獲得。
- request——對于在處理遠程服務(wù)請求時(shí)出現的所有本地服務(wù)調用,服務(wù)請求都被委托給相同的 Java 接口。
- Session——對于相同“會(huì )話(huà)”中的所有請求,服務(wù)請求都被委托給相同的 Java 實(shí)例。
- module——對于相同“模塊”中的所有請求,服務(wù)請求都被委托給相同的 Java 實(shí)例。
除了模塊范圍內的實(shí)現外,對于其他三個(gè),SCA 運行時(shí)都將防止對該實(shí)現的實(shí)例并發(fā)執行方法。 以下代碼片段顯示了會(huì )話(huà)范圍內的服務(wù)的接口。 清單 8. 會(huì )話(huà)范圍服務(wù)的接口 package services.profile; import org.osoa.sca.annotations.Scope; @Scope("session") public interface ProfileService{ public String getFirstName(); public void setFirstName(String pName); public String getLastName(); public void setLastName(String pName); public boolean isLoggedIn(); public void setLoggedIn(boolean pStatus); public String getId(); public void setId(String pId); } | 范圍服務(wù)可以使用 @Init Annotation 和 @Destroy Annotation 在其 Java 實(shí)現上實(shí)現生命周期方法。@Init——方法僅在其范圍開(kāi)始處且已注入了其屬性和引用后調用一次。@Destroy——方法在其范圍結束時(shí)調用。 清單 9 顯示了具有生命周期方法的 Java 實(shí)現類(lèi)。 清單 9. 具有生命周期方法的 Java 實(shí)現類(lèi) package services.profile; import org.osoa.sca.annotations.*; @Service(ProfileService.class) public class ProfileServiceImpl implements ProfileService { public String getFirstName( ) { ....} @Init public void start() { . . . } @Destroy public void stop() { . . . }
} | 實(shí)現配置屬性 Java 類(lèi)中字段或方法上的 @Property Annotation 用于定義 Java 組件實(shí)現的配置屬性。以下是 @Property Annotation 的特性: - name——屬性的名稱(chēng),缺省值為 Java 類(lèi)的字段的名稱(chēng)。
- require——指定是否需要注入,缺省值為 false。
清單 10 顯示了使用 @Property Annotation 的配置屬性的定義。 清單 10. 使用 @Property Annotation 的配置屬性的定義 package services.payroll; import org.osoa.sca.annotations.Property; import org.osoa.sca.annotations.Reference; import services.payrolldata.PayrollDataService; public class PayrollServiceImpl implements PayrollService { @Property private String currency = "INR"; @Reference private PayrollDataService payrollDataService; public PayrollStatement getPayrollStatement (String employeeID) { . . . . } } |
基本客戶(hù)機模型 客戶(hù)機可以從 SCA 組件和非 SCA 組件獲得對服務(wù)的訪(fǎng)問(wèn)。 從 SCA 組件實(shí)現訪(fǎng)問(wèn)服務(wù) 獲得對服務(wù)的訪(fǎng)問(wèn)的不同方法如下: 使用引用注入 通過(guò)定義服務(wù)的類(lèi)型接口的 Java 類(lèi)數據成員指定和通過(guò) @Reference Annotation 加標注,可使用引用注入獲得對服務(wù)的訪(fǎng)問(wèn)。@Reference Annotation 的特性有: - name——引用的名稱(chēng)。
- required——是否需要服務(wù)的注入。
清單 11 顯示了使用 @Reference Annotation 的 Java 實(shí)現。 清單 11. 使用 @Reference Annotation 的 Java 實(shí)現 package services.profile; import org.osoa.sca.annotations.Service; import org.osoa.sca.annotations.Reference; @Service(LoginService.class) public class LoginServiceImpl implements LoginService{ @Reference(name="profileService", required=true) private ProfileService profileService; public int login(String employeeID, String password) { .... } } | 以下是上述組件實(shí)現對應的組件類(lèi)型。 清單 12. 組件類(lèi)型 <?xml version="1.0" encoding="ASCII"?> <componentType xmlns="http://www.osoa.org/xmlns/sca/0.9"> <service name="ProfileService "> <interface.java interface="services.profile.LoginService"/> </service> <reference name="profileService"> <interface.java interface="services.profile.ProfileService"/> </reference> </componentType>
| 使用模塊上下文 使用模塊上下文訪(fǎng)問(wèn)服務(wù)的必要事項有: - 字段必須定義為能接受注入的模塊上下文。
- 必須對注入的模塊上下文調用方法。
通過(guò)將字段定義為 ModuleContext 類(lèi)型,或使用 @Context Annotation,可指定使用模塊上下文進(jìn)行服務(wù)訪(fǎng)問(wèn)。清單 13 顯示了 ModuleContext 接口及其 locateService() 方法。 清單 13. ModuleContext 接口 package org.osoa.sca; public interface ModuleContext { ... Object locateService(String serviceName); } | 清單 14 顯示了使用 @Context Annotation 的模塊上下文定義示例: 清單 14. 使用 @Context Annotation 的模塊上下文定義 package innovasolutions.web.ui; import java.io.IOException; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.osoa.sca.CurrentModuleContext; import org.osoa.sca.ModuleContext; import services.profile.LoginService; public class LoginServlet extends HttpServlet{ private ServletContext mContext; public void init(ServletConfig pCfg) throws ServletException{ mContext = pCfg.getServletContext(); } public void doPost(HttpServletRequest pReq, HttpServletResponse pResp) throws ServletException{ LoginService loginMgr = (LoginService)CurrentModuleContext.getContext(). locateService("LoginServiceComponent"); if (loginMgr == null){ throw new ServletException("LoginManager not found"); } . . . . } | 從非 SCA 組件實(shí)現訪(fǎng)問(wèn)服務(wù) 屬于 SCA 模塊一部分的非 SCA 組件使用模塊上下文獲取對服務(wù)的訪(fǎng)問(wèn)。它們在其實(shí)現中使用 ModuleContext 查找服務(wù)。它們通過(guò) CurrentModuleContext 類(lèi)獲取對 current ModuleContext 的訪(fǎng)問(wèn),如清單 15 中所示。 清單 15. CurrentModuleContext 類(lèi) package org.osoa.sca; public final class CurrentModuleContext { public static ModuleContext getContext() {... } } | 非 SCA 組件實(shí)現將包括與以下所示類(lèi)似的代碼行,以獲取對模塊上下文的訪(fǎng)問(wèn):ModuleContext moduleContext = CurrentModuleContext.getContext();
錯誤處理 調用服務(wù)方法客戶(hù)機時(shí)可能會(huì )遇到以下異常: - 業(yè)務(wù)異?!杀徽{用的服務(wù)方法的實(shí)現引發(fā)。
- SCA 運行時(shí)異?!?SCA 運行時(shí)引發(fā),指示組件的異常管理及與遠程服務(wù)的交互中存在的問(wèn)題。當前受支持的 SCA 運行時(shí)異常有服務(wù)運行時(shí)異常和服務(wù)不可用異常。
異步編程 在服務(wù)的異步編程中,客戶(hù)機調用服務(wù),并在不等待服務(wù)執行的情況下繼續進(jìn)行自身的執行。 SCA 異步編程模型支持三種類(lèi)型的異步調用。即: - 非阻塞方法調用
- 對話(huà)服務(wù)
- 回調
非阻塞調用 通過(guò)使用非阻塞調用,客戶(hù)機可調用服務(wù)并立即繼續進(jìn)行后續處理,而不必等待服務(wù)執行。非阻塞方法使用 @Oneway Annotation 進(jìn)行調用。當前 SCA 支持對返回“void”且沒(méi)有已聲明異常的方法進(jìn)行非阻塞調用。 對話(huà)服務(wù) 服務(wù)的客戶(hù)機和服務(wù)提供者之間的對話(huà)經(jīng)常在遠程服務(wù)執行期間發(fā)生。傳統編程模型要求編寫(xiě)大量的代碼來(lái)支持此模式。SCA 簡(jiǎn)化了對話(huà)服務(wù)的設計,而將 ID 生成、狀態(tài)管理和路由的細節留給 SCA 容器處理。 在 SCA 中,使用會(huì )話(huà)來(lái)維護有關(guān)客戶(hù)機和遠程服務(wù)間的單個(gè)對話(huà)的信息。SCA 使用 @Scope、@Session 和 @SessionID Annotation 來(lái)實(shí)現對話(huà)服務(wù)。 回調 回調服務(wù)提供從服務(wù)提供者回到其客戶(hù)機的異步通信。雙向服務(wù)使用的回調是具有兩個(gè)接口的服務(wù),一個(gè)供服務(wù)提供者使用,另一個(gè)供客戶(hù)機使用。 SCA 通過(guò)在遠程服務(wù)接口上使用 @Callback Annotation 來(lái)提供回調服務(wù)(接受接口的 Java Class 對象作為參數)。 清單 16 顯示了 @Callback Annotation 的使用。 清單 16. @Callback Annotation package somepackage; import org.osoa.sca.annotations.Callback; import org.osoa.sca.annotations.Remotable; @Remotable @Callback(MyServiceCallback.class) public interface MyService { public void someMethod(String arg); } @Remotable public interface MyServiceCallback { public void receiveResult(String result); } |
開(kāi)放源代碼運行時(shí)和工具 有一個(gè)開(kāi)放源代碼項目提供服務(wù)組件體系結構的運行時(shí)實(shí)現,可用于運行 SCA 應用程序。此項目稱(chēng)為 Tuscany,目前在 Apache 處于孵化期。此項目的主要參與者有 IBM、BEA、Oracle、Sybase、SAP、IONA 和 Siebel。
結束語(yǔ) 本文分別通過(guò) Java 接口和 Java 類(lèi)組件實(shí)現描述了服務(wù)的表示形式和實(shí)現。文中說(shuō)明了使用 Java Annotation 的服務(wù)實(shí)現的配置屬性。您了解了從 SCA 和非 SCA 組件訪(fǎng)問(wèn)服務(wù)的各種方式,還了解了各種錯誤處理機制、異步編程模型和各種異步調用。 對于希望使用 Java 的 SCA 實(shí)現開(kāi)發(fā)應用程序的讀者和開(kāi)發(fā)人員,本文是一篇必讀的文章。
參考資料
作者簡(jiǎn)介 |
| | Sreedevi Penugonda 是 IBM Java 技術(shù)中心的一位系統軟件工程師。她專(zhuān)長(cháng)于分布式對象計算系統、數據結構及算法和操作系統 | |
| | Rakesh Kumar Dash 是 IBM Java 技術(shù)中心的一位系統軟件工程師。他對 RMI 和 ORB 開(kāi)發(fā)進(jìn)行了廣泛的研究。他所感興趣的領(lǐng)域包括 SOA、Web 服務(wù)、CORBA 和操作系統。 | |