第
1 部分:服务组件体系结构概述
本文是介绍服务组件体系结构(Service Component Architecture,SCA)的系列文章的第
1 部分。此部分将概览 SCA,并说明它如何简化使用面向服务的体系结构(Service-Oriented
Architecture,SOA)所构建的业务应用程序的设计和集成。本系列的后续文章将讨论 SCA 体系结构及其主要概念,包括服务实现、服务客户机和各种组件的组装。
引言
面向服务的体系结构 (SOA) 是一个框架,用于组合各个业务功能和流程(称为服务),以便实现复杂的业务应用程序和流程。在
SOA 框架中,相对粗粒度的业务组件被作为服务公开。SOA 将 IT 资产构造为一系列可重用的服务,这些服务是松散耦合的,与平台和实现无关。SOA
将解决方案设计为服务的组装,通过定义良好的接口和契约进行连接。
服务组件体系结构 (SCA) 是一个规范,它描述用于使用 SOA 构建应用程序和系统的模型。它可简化使用
SOA 进行的应用程序开发和实现工作。
动机
SCA 可简化使用 SOA 构建的业务应用程序的创建和集成。SCA 提供了构建粗粒度组件的机制,这些粗粒度组件由细粒度组件组装而成。
SCA 将传统中间件编程从业务逻辑分离出来,从而使程序员免受其复杂性的困扰。它允许开发人员集中精力编写业务逻辑,而不必将大量的时间花费在更为底层的技术实现上。
SCA 方法的优势包括:
简化业务组件开发
简化作为服务网络构建的业务解决方案的组装和部署
提高可移植性、可重用性和灵活性
通过屏蔽底层技术变更来保护业务逻辑资产
提高可测试性
SCA 体系结构
SCA 将构建面向服务的应用程序的步骤划分为两个主要部分:
实现提供服务和使用其他服务的组件
组装组件,以通过服务引用其他服务的方式来构建业务应用程序
SCA 提供了一种机制,用于打包和部署那些紧密相关的组件,这些组件是作为一个整体开发和部署的。这种机制使服务的实现和组装避免了陷入基础设施功能的细节,也避免了调用外部系统。这样可支持不同基础设施间的服务可移植性。
服务实现和服务客户机
服务实现是业务逻辑的具体实现,提供服务和/或使用服务。这些实现会使用众多实现技术中的任意一种,如
Java?、BPEL 或 C++。实现是业务流程的“仆人”。
实现可以提供服务,而服务则是由一个接口定义的一组操作,此接口供其他组件调用。实现也可以调用其他服务,称之为服务引用,服务引用指示了此实现对外界服务的依赖关系。实现还可以具有一个或多个可配置属性。属性是可以从外部配置的数据值,会影响此实现的业务功能。
SCA 服务通常对参数和返回值使用文档样式的业务数据,最好使用服务数据对象(Service
Data Object,SDO)表示这些参数(有关更多信息,请参见参考资料部分)。
服务、引用和属性是实现的可配置方面——SCA 将其统称为组件类型。
配置引用是通过将引用绑定到目标服务来完成的,随后实现可以通过调用引用来使用目标服务。属性的配置将涉及到为属性设置具体的数据值。在
SCA 框架中,可以使用一个实现构建多个不同的组件,每个组件具有不同的引用和属性配置。组件及其服务可以被其他本地组件调用,或者用于远程访问。
组装
组装一个组合业务应用程序的过程,在此过程中配置并连接提供服务实现的组件。SCA
组装在两个层次进行:
系统内松散连接的组件的组装
模块内松散连接的组件的组装
SCA 组装模型包括一系列由 XML 元素定义的构件。
模块组装
SCA 模块 是一起开发和部署到 SCA 系统 的最大紧密耦合组件。它是
SCA 系统内的松散耦合组合的基本单元。SCA 模块包含一系列组件、外部服务、入口点,以及用于衔接这些部分的机制。模块向
SCA 系统提供服务实现。
入口点 定义模块提供的公共服务,此服务可以由同一模块内的其他组件使用,也可以在模块外使用。入口点用于使用特定的绑定
发布模块提供的服务。
模块内的外部服务 表示其他模块提供的远程服务。它们位于使用此服务的 SCA
模块之外。组件可以像访问 SCA 组件提供的任何服务一样访问这些外部服务。外部服务使用绑定来描述对外部服务的访问。
外部服务的接口必须为可远程访问的。
系统组装
在SCA 系统 中,SCA 系统 用于聚合那些提供了相关业务功能的模块。这是通过配置和管理模块组件、外部服务、入口点,以及连接机制来完成的。SCA
系统的配置由所有部署到其中的子系统的组合加以表示。图 1 是系统组装的一个示例;它说明了如何使用服务和引用连接各个子系统和模块。
图 1. 服务组件体系结构
让我们逐个分析一下图 1 中所看到的内容:
子系统配置中的模块组件表示 SCA 模块的一个已配置实例,模块组件可以在其中为模块的外部服务设置值,并能够为模块公开的属性设置值。
外部服务是位于使用服务的 SCA 系统外部的远程服务。模块级别的外部服务与系统级别的外部服务的不同之处包括:
名称必须在子系统中定义的所有外部服务中保持唯一性。
子系统中没有与模块组件具有相同名称的外部服务,因为它们都可能成为连接机制的目标。
入口点用于声明可外部访问的子系统服务。它们由其他组装或客户作为 Web 服务使用。模块级别的入口点和系统级别的入口点的不同之处包括:
名称必须在子系统内定义的所有入口点中保持唯一性。子系统内不能存在与模块组件同名的入口点。
引用子元素的指定是可选的,因为可以通过另一个子系统提供的连接机制进行连接。
开放源代码运行时和工具
有一个开放源代码项目提供服务组件体系结构的运行时实现,您可以使用该实现来运行 SCA 应用程序。此项目称为
Tuscany,目前在 Apache 中处于孵化期。此项目的主要参与者有 IBM、BEA、Oracle、Sybase、SAP、IONA
和 Siebel。
结束语
在本文中,我们介绍了 SCA 体系结构及其组装模型,并说明了 SCA 子系统及各种组件,包括组成此子系统的模块、连接机制和外部服务。这些细节应该能够帮助您理解使用
SCA 构建和集成不同业务应用程序所需的各个概念。
第 2 部分: 用于 Java 的 SCA 客户机和实现模型
本文是系列文章的第 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 实现开发应用程序的读者和开发人员,本文是一篇必读的文章。
第 3 部分: 开发示例 SCA 应用程序
引言
本文将说明开发和部署 SCA 应用程序的主要概念。我们将说明如何创建组件实现来提供远程服务、本地服务、配置属性和服务引用以及如何创建组件、入口点、外边服务和模块。
示例应用程序:Innova Solutions 开发
我们将详细说明一家名为 Innova solutions 的虚构软件公司所进行的活动,该公司向其员工提供不同的服务,以便查看自己的工资单信息、休假详细信息、能力详细信息、个人信息和绩效评级。
以下示例说明了构建用于查看员工的工资单信息的服务的过程,可以通过 Web
应用程序和 Web 服务客户机访问此服务。Innova solutions 将其应用程序实现为两个模块,从而提供了可重用性和灵活性。这两个模块分别为
Payroll 模块和 Web 前端模块:
图 1. Payroll 模块关系图
innovasolutions.payrollmodule 模块表示 payroll
服务,此服务用于使用 Web 服务协议访问遗留系统中的员工工资单信息。它包含以下内容:
Payroll service component:提供远程 payroll
服务,并提供有关员工基本工资、HRA、浮动工资、付薪方式、所得税和特惠税的综合说明。
Payroll data service component:通过接口与遗留系统连接,并向
payroll 服务提供有关基本工资、HRA、浮动工资、奖金和所得税的信息。
External Tax rate rules service:提供不同收入水平的当前税率。
Entry point payroll service:将 payroll
服务通过 Web 服务绑定发布,以供 Web 服务客户机模块及其远程 Web 服务客户机访问。
Assembly:配置并连接模块的所有元素。
下图显示了 Web front-end 模块的不同组件:
图 2. Payroll front-end
模块
innovasolutions.webclientmodule 模块可对用于将信息记录到系统中和访问员工工资单信息的基于浏览器的功能起到促进作用。它包含以下内容:
用于处理 Web 请求和显示员工工资单信息的 login HTML 文件、login
servlet 和 payroll statement JSP。
login service 和 profile service 组件,用于提供管理员工状态的本地服务。
External payroll service:用于访问 innovasolutions.payrollmodule
的远程 payroll 服务。
Assembly:配置和连接模块的所有元素。
应用程序开发
示例应用程序开发涉及到两个 SCA 模块的开发,下面我们将对此进行介绍。
创建 innovasolutions.payrollmodule
SCA 模块 innovasolutions.payrollmodule
是通过这样构建的:在文件系统中构建一个名为 innovasolutions.payrollmodule
的文件夹,并在该文件夹的根目录下放置一个 sca.module 文件。
清单 1 显示了 sca.module 的内容,该文件是一个 xml 文件。
清单 1. sca.module
<?xml version="1.0" encoding="ASCII"?> <module xmlns="http://www.osoa.org/xmlns/sca/0.9" name="innovasolutions.payrollmodule"> </module> |
Payroll Data Service Implementation:我们首先创建由
PayrollDataService 服务提供的 PayrollDataService 接口;此服务向其员工提供工资单信息。然后,我们将创建
PayrollDataServiceImpl,以向 Innova solutions 的员工提供 PayrollDataService
服务。
我们创建名为 services/payrolldata 的子目录,并将所有组成
PayrollDataServiceImpl 实现的文件放置到其中。
清单 2 显示了 PayrollDataService Java 接口。
清单 2. PayrollDataService Java 接口
package services.payrolldata; public interface PayrollDataService { float getBasic(String employeeID); float getvariablePay(String employeeID); float getHRA(String employeeID); float getProvidentFund(String employeeID); float getConveyance(String employeeID); float getIncomeTax(String employeeID); float getProfessionalTax(String employeeID); float getNetPay(String employeeID); } |
清单 3 说明了 PayrollDataServiceImpl Java
实现。
清单 3. PayrollDataServiceImpl Java 实现
package services.payrolldata; @Service(PayrollDataService.class) public class payrollDataServiceImpl implements payrollDataService { public float getBasic(String employeeID){ . . . } public float getVariablePay(String employeeID){ . . . } public float getHRA(String employeeID){ . . . } public float getProvidentFund(String employeeID){ . . . } float getConveyance(String employeeID){ . . .} float getProfessionalTax(String employeeID){ . . . } public float getNetPay(String employeeID){ . . . } } |
上面的代码片段实现了之前的 PayrollDataService 接口。在实现中使用了一个
@Service Annotation 来声明服务及其接口。
清单 4 显示了上述实现的组件类型、side 文件。
清单 4. 组件类型、side 文件
<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> |
Payroll Data Service Component:SCA 组件是通过配置实现的引用和属性创建的。配置引用需要将其连接到其他组件提供的服务或外部服务。此处,我们将通过配置
PayrollDataServiceImpl 创建 PayrollDataServiceComponent。
将使用 component 元素将所创建的 SCA 组件添加到 sca.module
文件,该元素具有一个 name 属性,指定组件的名称。
清单 5 显示了创建 payrollDataServiceComponent
后的 sca.module 文件。
清单 5. 创建了 payrollDataServiceComponent
后的 sca.module 文件
<?xml version="1.0" encoding="ASCII"?> <module xmlns="http://www.osoa.org/xmlns/sca/0.9" name="innovasolutions.payrollmodule"> <component name="payrollDataServiceComponent"> <implementation.java class="services.payrolldata.PayrollDataServiceImpl"> </component> </module> |
创建 TaxRateRules Web 服务外部服务:此处我们将创建一个外部服务
TaxRateRulesService,以提供向 innovasolutions.payrollmodule
的员工提供 TaxRateRulesService 接口的服务。
我们首先创建一个名为 services/taxraterules 的子目录,以将
TaxRateRulesService 外部服务所需的所有文件放入其中。业务合作伙伴实际实现此外部服务,将提供一个
TaxRateRulesService.wsdl。为了处理静态数据类型,我们为 wsdl portType
创建了对应的 Java 接口。
清单 6 显示了 wsdl portType 的 Java 接口的大致情况。
清单 6. wsdl portType 的 Java 接口
package services.taxrate; public interface TaxRateRulesService { public float getTaxRate (Float taxableIncome); } |
接下来,我们将以下代码添加到前面指定的 sca.module 文件,以创建名为
TaxrateRulesService 的外部服务。
清单 7 显示了添加 TaxrateRulesService 后的 sca.module
文件。
清单 7. 添加 TaxrateRulesService 后的 sca.module
文件
<externalService name="TaxRateRulesService"> <interface.java interface="services.taxraterules.TaxRateRulesService"/> <binding.ws port="http://www.taxrules.com/ TaxRateRulesService # wsdl.endpoint(TaxRateRulesService / TaxRateRulesService SOAP)"/> </externalService> |
Payroll Service Implementation:此处我们将讨论
PayrollServiceImpl 的实现,该实现提供一个服务,此服务在 innovasolutions.payrollmodule
中向其员工提供 PayrollService 接口。此实现使用两个其他服务提供的接口引用这两个服务,即
PayrollDataService 接口(本地服务)和 TaxRateRulesService 接口(外部服务)。
我们将首先创建名为 services/payroll 的子目录,并将所有组成
PayrollServiceImpl 实现的文件放入其中。
清单 8 显示了 PayrollService Java 接口。
清单 8. PayrollService Java 接口
package services.payroll; import org.osoa.sca.annotations.Remotable; @Remotable public interface PayrollService{ public PayrollStatement getPayrollStatement(String employeeID); |
上面的 PayrollService Java 接口包含一个 getPayrollStatement()
方法,其参数是 employeeID,将返回 PayrollStatement 对象。由于这需要为外部服务,因此使用
@Remotable Annotation 将接口声明为远程接口。在此实现中,通过远程服务交换的所有参数都是服务数据对象(Service
Data Objects,SDO)类型的数据。
清单 9 显示了 PayrollStatement Java 接口。
清单 9. PayrollStatement Java 接口
package services.payroll; import java.util.List; public interface PayrollStatement { List getPayrollDetails(); float getNetPay(); } |
清单 10 显示了 PaySplitUP Java 接口。
清单 10. PaySplitUP Java 接口
package services.payroll; public interface PaySplitUP { String getPaycategory(); void setPaycategory(String payCategory); void setEarnings(); void setDeductions(); float getEarnings(); float getDeductions(); } |
清单 11 说明了 PayrollServiceImpl Java 实现类。
清单 11. PayrollServiceImpl Java 实现类
package services.payroll; import java.util.List; import commonj.sdo.DataFactory; import org.osoa.sca.annotations.Property; import org.osoa.sca.annotations.Reference; import services.payrolldata.PayrollDataService; import services.taxrate.TaxRateRulesService; public class PayrollServiceImpl implements PayrollService { @Property private String currency = "INR"; @Reference private PayrollDataService payrollDataService; @Reference private TaxRateRulesService taxraterulesService; public PayrollStatement getPayrollStatement (String employeeID) { DataFactory dataFactory = DataFactory.INSTANCE; PayrollStatement payrollStatement = (PayrollStatement)dataFactory.create(PayrollStatement.class); List payslip = payrollStatement.getPayrollDetails(); float basic = payrollDataService.getBasic(employeeID); PlaySplitUP basicSalary = (PlaySplitUP)dataFactory.create(PlaySplitUP.class); basicSalary.setPaycategory("BASIC"); basicSalary.setEarnings(currencyConverter(basic)); payslip.add(basicSalary); . . . . . . . code for HRA . . . . . . . . . . . code for Variable pay . . . . . . . . . . . code for conveyance. . . . . . . . . . . code for Profident fund . . . . float IncomeTaxAmount = payrollDataService.getIncomeTax(employeeID); PlaySplitUP IncomeTax = (PlaySplitUP)dataFactory.create(PlaySplitUP.class); IncomeTax.setPaycategory("INCOMETAX"); IncomeTax.setDeductions(currencyConverter(IncomeTaxAmount)); payslip.add(IncomeTax); . . . . . . . code for Professional Tax . . . . . . return payrollStatement; } private float currencyConverter(float value){ if (currency.equals("INR")) return value; else if (currency.equals("USD")) return value * 45.5f; else return 0.0f; } } |
上面所示的 PayrollServiceImpl 类实现了 PayrollService
接口。PayrollServiceImpl 使用 @Reference Annotation 定义了两个成员变量,以引用另外两个服务,即
PayrollDataService 和 TaxRateRulesService。SCA 运行时通过注入解析这些引用。PayrollServiceImpl
使用 @Property Annotation 在运行时配置成员变量 currency。PayrollStatement
对象是使用 SDO 数据工厂 API 作为服务数据对象 (SDO) 创建的。
清单 12 显示了上述 PayrollServiceImpl 的组件类型文件。
清单 12. PayrollServiceImpl 的组件类型文件
<?xml version="1.0" encoding="ASCII"?> <componentType xmlns="http://www.osoa.org/xmlns/sca/0.9" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <service name="PayrollService"> <interface.java interface="services.payroll.PayrollService"/> </service> <reference name="payrollDataService"> <interface.java interface="services.payrolldata.PayrollDataService"/> </reference> <reference name="stockRatestockQuoteService"> <interface.java interface="services.taxRateRules.TaxRateRulesService"/> </reference> <property name="currency" type="xsd:string" default="INR"/> </componentType> |
Payroll Service Component:现在我们将通过配置上面实现的
PayrollServiceImpl 来创建 PayrollServiceComponent。我们将对上面指定的
sca.module 文件添加若干行代码来包括 PayrollServiceComponent,如清单
13 中所示。
清单 13. 添加了额外代码行的 sca.module 文件
<component name="PayrollServiceComponent"> <implementation.java class="services.payroll.payrollServiceImpl"/> <properties> <v:currency>INR</v:currency> </properties> <references> <v:payrollDataService>PayrollDataServiceComponent</v:payrollDataService> <v:taxRateRulesService>TaxRateRulesService</v:taxRateRulesService> </references> </component> |
创建 payroll 服务 Web 服务入口点:下一步我们将创建名为
PayrollService 的入口点来发布 PayrollService,以便 Web 服务客户机能够访问
PayrollService。需要首先创建 SCA 运行时用于绑定到入口点的 WSDL 定义文件,然后才能在
sca.module 文件中创建入口点。我们向 sca.module 文件添加数行代码来表示 PayrollService
入口点的创建。
清单 14 显示了添加到上面指定的 sca.module 文件中的代码行。
清单 14. 添加了额外代码行的指定 sca.module 文件
<entryPoint name="PayrollService"> <interface.java interface="services.payroll.PayrollService"/> <binding.ws port="http://www.innovasolutions.com/PayrollService# wsdl.endpoint(PayrollService/PayrollServiceSOAP)"/> <reference>PayrollServiceComponent</reference> </entryPoint> |
创建 innovasolutions.webclientmodule
在此部分,我们将创建 Innova Solutions Web 应用程序,以访问名为
payroll service 的外部服务,此服务使用了本地服务组件、SCA 范围管理、JSP 和 Servlet。Innova
Solutions Web 应用程序允许其员工登录并使用范围确定为 HTTP 会话的本地服务跟踪自己的概要,还向其提供了对工资单系统的访问和显示他们每月的工资单。它将首先把用户导航到登录页,然后前进到工资单系统,在其中显示每月的工资单。
以下各个部分将说明如何创建 InnovaSolutions Web 应用程序外部的各个
SCA 服务,即 login、user profile 和 payroll。然后我们将了解如何由 Servlet、JSP
和标记库访问这些服务,以提供用户界面。
我们将首先实现 LoginServiceImpl,此服务负责将用户登录到应用程序中。LoginServiceImpl
提供服务 LoginService,该服务在 innovasolutions.webclientmodule
中向员工提供 LoginService 接口。
首先,我们创建名为 services/profile 的子文件夹,并将属于
LoginServiceImpl 实现的所有文件放入其中。
清单 15 显示了 LoginService Java 接口。
清单 15. LoginService Java 接口
package services.profile; public interface LoginService{ public static final int SUCCESS = 1; public static final int INVALID_LOGIN = -1; public static final int INVALID_PASSWORD = -2; public int login(String employeeID, String password); } |
接下来,编写实现 LoningService 接口的 LoginServiceImpl
Java 实现,该实现使用其接口 profileservice 引用本地服务。
清单 16 给出了 LoginServiceImpl Java 实现。
清单 16. LoginServiceImpl 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 private ProfileService profileService; public int login(String employeeID, String password) { …. } } |
上面的 LoginServiceImpl 实现了 LoginService
接口,并使用其接口 profileservice 引用本地服务。
Login Component:此处我们将通过配置 LoginServiceImpl
实现来创建 LoginServiceComponent。让我们看看显示包含 LoginServiceComponent
的 innovasolutions.webclientmodule 的 sca.module 文件的代码。
清单 17 显示了 innovasolutions.webclientmodule
的 sca.module 文件。
清单 17. innovasolutions.webclientmodule
的 sca.module 文件
<?xml version="1.0" encoding="ASCII"?> <module xmlns="http://www.osoa.org/xmlns/sca/0.9" xmlns:v="http://www.osoa.org/xmlns/sca/values/0.9" name="innovasolutions.webclientmodule"> <component name="LoginServiceComponent"> <implementation.java class="services.profile.LoginServiceImpl"/> </component> </module> |
Profile Service Implementation:在此部分,我们将简要地给出
profileServiceImpl,此实现提供了由接口 ProfileService 访问的服务。通过使用
ProfileService,可以在用户的 Web 应用程序内导航时跟踪用户状态。我们使用前面创建的子文件夹
services/profile 来放置组成 profileServiceImpl 实现的所有文件。
profileService Java 接口使用 @Scope Annotation
来声明 ProfileServiceImpl 实例的范围由会话确定。
清单 18 显示了 ProfileService Java 接口。
清单 18. ProfileService Java 接口
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); } |
ProfileServiceImpl 实现了 ProfileService
Java 接口。清单 19 说明了 ProfileServiceImpl Java 实现。
清单 19. ProfileServiceImpl Java 实现
package services.profile; import org.osoa.sca.annotations.Property; import org.osoa.sca.annotations.Scope; import org.osoa.sca.annotations.Service; @Service(ProfileService.class") @Scope("session") public class ProfileServiceImpl implements ProfileService{ @Property private String firstName; . . . . . . } } |
Profile Service Component:现在我们将清单 20
中所示的代码片段添加到 innovasolutions.webclientmodule 的 sca.module
文件中,从而创建 ProfileServiceComponent。
清单 20. 添加了额外代码行的 sca.module 文件
<component name="ProfileServiceComponent"> <implementation.java class="services.profile.ProfileImpl"/> <properties> <v:firstName>Anonymous</v:firstName> </properties> </component> |
清单 21 显示了在 component 元素下添加的三行代码 LoginServiceComponent。
清单 21. LoginServiceComponent
<references> <v:profileService>ProfileServiceComponent</v:profileService> </references> |
Payroll Service Web Service External
Service:在此部分,我们将创建名为 PayrollService 的外部服务,该接口在 innovasolutions.webclientmodule
中向用户提供名为 PayrollService 的接口。
我们首先创建一个名为 services/payroll 的子目录,以将
PayrollService 外部服务所需的所有文件放入其中。此外部服务提供一个 PayrollService.wsdl
文件。我们为 wsdl portType 创建对应的 Java 接口,即 PayrollService、PayrollStatement
和 PaySplitUP,以便在使用 PayrollService 时处理静态 Java 类型。
最后,我们添加清单 22 中所示的代码,以在 innovasolutions.webclientmodule
的 sca.module 文件中创建名为 PayrollService 的外部服务。
清单 22. 经过修改以创建 PayrollService 的 sca.module
文件
<externalService name="PayrollService"> <interface.java interface="services.account.PayrollService"/> <binding.ws port="http://www.innovasolutions.com/PayrollService# wsdl.endpoint(PayrollService/PayrollServiceSOAP)"/> </externalService> |
Login HTML 页:login.html 页将当前用户的用户名和密码发送给
login Servlet。
Login Servlet:login Servlet 处理从 login.html
获得的用户名和密码,并使用 ModuleContext API 调用 login 本地服务。CurrentModuleContext.getContext()
返回当前的模块上下文,并随后调用 locateService("LoginServiceComponent")
查询本地服务。
Payroll Statement JSP: PayrollStatement.JSP
显示 payroll 服务返回的员工的所有工资单信息。为了让 Payroll Statement 页不受实现细节的限制,Payroll
Statement.jsp 使用了一系列 JSP 标记,即 login 标记、service 标记和 payroll
status 标记。
部署
以下部分对子系统构件进行说明。
创建 innovasolutions.payrollsubsystem
在此部分,我们将创建 innovasolutions.payrollsubsystem,此子系统对
innovasolutions.payroll 模块进行配置。首先,在文件系统中创建名为 innovasolutions.payrollsubsystem
的文件夹,并在根目录放置 sca.subsystem 文件。
清单 23 显示了 innovasolutions.payrollsubsystem
的 sca.subsystem 文件。
清单 23. innovasolutions.payrollsubsystem
的 sca.subsystem 文件
<?xml version="1.0" encoding="ASCII"?> <subsystem xmlns="http://www.osoa.org/xmlns/sca/0.9" name="innovasolutions.payrollsubsytem"> <moduleComponent name="PayrollModuleComponent"module="innovasolutions.payrollmodule"/> </subsystem> |
创建 innovasolutions.webclientsubsystem
在此部分,我们将创建 innovasolutions.webclientsubsystem,此子系统对
innovasolutions.webclient 模块进行配置。首先,在文件系统中创建名为 innovasolutions.webclientsubsystem
的文件夹,并在根目录放置一个 sca.subsystem 文件。
清单 24 显示了 innovasolutions.webclientsubsystem
的 sca.subsystem 文件。
清单 24. innovasolutions.webclientsubsystem
的 sca.subsystem 文件
<?xml version="1.0" encoding="ASCII"?> <subsystem xmlns="http://www.osoa.org/xmlns/sca/0.9" xmlns:v="http://www.osoa.org/xmlns/sca/values/0.9" name="innovasolutions.webclientsubsytem"> <moduleComponent name="WebClientModuleComponent" module="innovasolutions.webclientmodule"> <references> <v:PayrollService>innovasolutions.payrollsunbsystem/ PayrollModuleComponent/PayrollService</v:PayrollService> </references> </moduleComponent> </subsystem> |
结束语
我们已经演示了应用程序 Innova Solutions 开发过程,以帮助您使用
SCA 客户机和实现模型利用 Java 编程语言构建和集成应用程序。SCA 应用程序提供了服务的可移植性、可重用性和灵活性,从而将开发人员从中间件编程的复杂性中解放出来。
|