本文内容包括:
本文是系列文章的第 2 部分,描述如何使用 Java 编程语言实现 SCA 组件以及组件实现可以如何充当其他服务的客户机。文中将对远程和本地服务的实现及其配置进行说明。将讨论获取服务的不同方法——引用注入和模块上下文。还将说明用于声明实现的可配置方面的各个
Java Annotation。
引言
SCA Java 实现模型提供了用于在 Java 中实现 SCA 组件的框架。组件实现可以提供服务,也可以充当其他服务的客户机。本文将说明
SCA Java 组件实现和非 SCA Java 组件实现可以如何获得对服务的访问和调用服务方法。在本文中,所有元数据都使用 Java
2 Standard Edition (J2SE) 5 提供的 Annotation 功能定义。不过,SCA 服务客户机和组件实现也可使用
J2SE 1.4。
回页首
基本组件实现模型
此模型描述本地或远程服务的实现以及特定于实现的配置属性。
实现服务
服务使用接口表示,而接口则是使用以下方法之一进行定义的:
- Java 接口(SCA 的首选方式)
- Java 类
- 从 Web 服务描述语言(Web Services Description Language,WSDL)端口类型生成的
Java 接口
Java 类组件实现会实现服务接口定义的所有操作,它使用 @Service Annotation 来指定服务通过使用 Java
实现而实现的服务接口。旨在用作服务实现的类并不必具有 @Service Annotation。@Service Annotation
具有两个特性:
- Interfaces——此值是应该由其组件作为服务公开的接口或类对象的值。
- Value——仅在提供单个服务接口时的快捷方式。没有特性的 @Service 没有意义。
以下代码片段显示了名为 PayrollDataService 的 Java 服务接口和名为 PayrollDataServiceImpl
的 Java 接口的 Java 实现类。
清单 1. 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);
} |
清单 2. Java 服务组件实现
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 实现类中,服务接口由类本身定义,则上述代码中的 @Service Annotation 将替换为 @Service(PayrollDataServiceImp.class)。下面将说明此组件实现的组件类型,如清单
3 中所示。
清单 3. 组件类型
<!--<?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> -->
|
使用了接口的子类来提供具有相同接口的两个服务。@Service Annotation 同时列出了这两个接口。
实现远程服务
通过入口点发布远程服务,以提供访问外部组件的方法。为了定义远程服务,将在服务的 Java 接口中添加 @Remotable Annotation。接口不由
Java 类定义的服务不是远程服务。从 WSDL 端口类型生成的 Java 接口是远程接口。
以下将对带 @Remotable Annotation 的远程服务的 Java 接口进行说明:
清单 4. 带 @Remotable Annotation 的远程服务的 Java 接口
package services.payroll;
import org.osoa.sca.annotations.Remotable;
@Remotable
public interface PayrollService{
public PayrollStatement getPayrollStatement(String employeeID);
} |
下一个代码片段显示了 PayrollStatement Java 接口。 清单 5. PayrollStatement
Java 接口
package services.payroll;
import java.util.List;
public interface PayrollStatement {
List getPayrollDetails();
float getNetPay();
} |
远程服务使用封送技术来交换复杂数据类型。在使用 Web 服务绑定时,会将服务数据对象 (Service Data Object) 或
JAXB 数据类型作为参数传递。远程服务使用按值数据交换语义。
远程服务的实现上的 @AllowPassByReference Annotation 用于声明它是否允许在对其进行调用时通过引用数据交换语义进行传递。
以下代码片段显示了实现远程服务并允许通过引用传递的 Java 组件实现。
清单 6. 具有远程服务和允许通过引用传递的 Java 组件实现
package services.payrolldata;
Import org.osoa.sca.annotations.*;
@Service(PayrollDataService.class)
@AllowsPassByReference
public class PayrollDataServiceImp implements PayrollDataService {
. . . .
} |
实现本地服务
本地服务仅能由属于相同模块的客户机调用。Java 本地服务接口是在不使用 @Remotable Annotation 的情况下定义的。
以下代码片段显示了本地服务的 Java 接口。
清单 7. 本地服务的 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);
} |
本地服务的调用的数据交换语义是以引用为基础的,其中,客户机或服务提供者会看到对参数的更改。 实现有状态资源模式
确定了范围的服务通过在服务的接口定义或服务类上使用 @Scope Annotation 加以表示。@Scope Annotation
具有一个参数,即 value,其值为范围的名称。
以下是当前支持的范围值:
- Stateless(缺省)——每个请求分开处理。Java 实例可以从服务实例池获得。
- request——对于在处理远程服务请求时出现的所有本地服务调用,服务请求都被委托给相同的 Java 接口。
- Session——对于相同“会话”中的所有请求,服务请求都被委托给相同的 Java 实例。
- module——对于相同“模块”中的所有请求,服务请求都被委托给相同的 Java 实例。
除了模块范围内的实现外,对于其他三个,SCA 运行时都将防止对该实现的实例并发执行方法。
以下代码片段显示了会话范围内的服务的接口。
清单 8. 会话范围服务的接口
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);
} |
范围服务可以使用 @Init Annotation 和 @Destroy Annotation 在其
Java 实现上实现生命周期方法。@Init——方法仅在其范围开始处且已注入了其属性和引用后调用一次。@Destroy——方法在其范围结束时调用。
清单 9 显示了具有生命周期方法的 Java 实现类。
清单 9. 具有生命周期方法的 Java 实现类
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() { . . . }
} |
实现配置属性
Java 类中字段或方法上的 @Property Annotation 用于定义 Java 组件实现的配置属性。以下是
@Property Annotation 的特性:
- name——属性的名称,缺省值为 Java 类的字段的名称。
- 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) { . . . . }
} |
回页首
基本客户机模型
客户机可以从 SCA 组件和非 SCA 组件获得对服务的访问。
从 SCA 组件实现访问服务
获得对服务的访问的不同方法如下:
使用引用注入 通过定义服务的类型接口的 Java 类数据成员指定和通过 @Reference
Annotation 加标注,可使用引用注入获得对服务的访问。@Reference Annotation 的特性有:
- name——引用的名称。
- required——是否需要服务的注入。
清单 11 显示了使用 @Reference Annotation 的 Java 实现。
清单 11. 使用 @Reference Annotation 的 Java 实现
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) { .... }
} |
以下是上述组件实现对应的组件类型。
清单 12. 组件类型
<?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> |
使用模块上下文
使用模块上下文访问服务的必要事项有:
- 字段必须定义为能接受注入的模块上下文。
- 必须对注入的模块上下文调用方法。
通过将字段定义为 ModuleContext 类型,或使用 @Context Annotation,可指定使用模块上下文进行服务访问。清单
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 组件实现访问服务
属于 SCA 模块一部分的非 SCA 组件使用模块上下文获取对服务的访问。它们在其实现中使用
ModuleContext 查找服务。它们通过 CurrentModuleContext 类获取对 current ModuleContext
的访问,如清单 15 中所示。
清单 15. CurrentModuleContext 类
package org.osoa.sca;
public final class CurrentModuleContext {
public static ModuleContext getContext() {... }
} |
非 SCA 组件实现将包括与以下所示类似的代码行,以获取对模块上下文的访问:ModuleContext
moduleContext = CurrentModuleContext.getContext();
回页首
错误处理
调用服务方法客户机时可能会遇到以下异常:
- 业务异常——由被调用的服务方法的实现引发。
- SCA 运行时异常——由 SCA 运行时引发,指示组件的异常管理及与远程服务的交互中存在的问题。当前受支持的 SCA 运行时异常有服务运行时异常和服务不可用异常。
回页首
异步编程
在服务的异步编程中,客户机调用服务,并在不等待服务执行的情况下继续进行自身的执行。
SCA 异步编程模型支持三种类型的异步调用。即:
- 非阻塞方法调用
- 对话服务
- 回调
非阻塞调用
通过使用非阻塞调用,客户机可调用服务并立即继续进行后续处理,而不必等待服务执行。非阻塞方法使用 @Oneway Annotation
进行调用。当前 SCA 支持对返回“void”且没有已声明异常的方法进行非阻塞调用。
对话服务
服务的客户机和服务提供者之间的对话经常在远程服务执行期间发生。传统编程模型要求编写大量的代码来支持此模式。SCA 简化了对话服务的设计,而将
ID 生成、状态管理和路由的细节留给 SCA 容器处理。
在 SCA 中,使用会话来维护有关客户机和远程服务间的单个对话的信息。SCA 使用 @Scope、@Session
和 @SessionID Annotation 来实现对话服务。
回调
回调服务提供从服务提供者回到其客户机的异步通信。双向服务使用的回调是具有两个接口的服务,一个供服务提供者使用,另一个供客户机使用。
SCA 通过在远程服务接口上使用 @Callback Annotation 来提供回调服务(接受接口的 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);
} |
回页首
开放源代码运行时和工具
有一个开放源代码项目提供服务组件体系结构的运行时实现,可用于运行 SCA 应用程序。此项目称为 Tuscany,目前在 Apache
处于孵化期。此项目的主要参与者有
IBM、BEA、Oracle、Sybase、SAP、IONA
和 Siebel。
回页首
结束语
本文分别通过 Java 接口和 Java 类组件实现描述了服务的表示形式和实现。文中说明了使用 Java Annotation
的服务实现的配置属性。您了解了从 SCA 和非 SCA 组件访问服务的各种方式,还了解了各种错误处理机制、异步编程模型和各种异步调用。
对于希望使用 Java 的 SCA 实现开发应用程序的读者和开发人员,本文是一篇必读的文章。
回页首
参考资料
|