本文根据Apache Tuscany 1.x文档翻译编写
1. Build Calculator Using Command Line
1.1. 前言
这篇导航文章使用基于命令行的方式开始Apache Tuscany Java SCA。Tuscany提供了许多样例,其中最简单之一的就是计算器样例(The Calculator Sample)。我们将使用此样例,所以请确保已经正确下载Tuscany。正如此样例名称暗示,此样例是个典型的计算操作。给定二个数,然后将操作作用于它们。我们将处理加(add),减(subtract),乘(multiply),除(divide)等操作。看完这篇导航后你将知道如果开发,部署,运行计算器样例,并且知道怎么配置使之运行于不同环境。
1.2. 安装环境
首先我们将运行计算器样例以确保已正确安装了Tuscany,然后将介绍如何一步步地实现计算器样例,这样你就可以知道如何开发单独运行于SCA环境的Apache Tuscany Java SCA基本应用。接下来我们将更新计算器样例,以介绍Apache Tuscany Java SCA的其他JAVA拓展。最后我们将用计算器样例展示如何构建使用Apache Tuscany Java SCA的面向服务架构(Service Oriented Architecture,SOA)的分布式应用。
l 下载Apache Tuscany Java SCA
下载界面分别有Windows,Linux上的二进制,源码版本。这边下载二进制版本即可。
l 下载JDK
若没有安装JDK,必须先安装,版本需是1.5或之后的版本。
l 下载构建工具
构建工具可以使用Maven 2.0.7+,或Ant 1.7.0+。
1.3. 运行存在的计算器应用
Tuscany Java SCA的二进制版本中提供了一个计算器应用,你可以从samples/calculator目录找到。如图-1所示每个样例都有个README文件,里面说明了如何运行该样例,及一个PNG图片展示SCA应用样子。
图-1
我们先来运行此样例,以有个感性认识。运行此例很简单,只需从命令行中切换到samples/calculator目录执行如下命令
>ant run |
若没有安装ant,可以按如下命令运行
Windows
>java -cp../../lib/tuscany-sca-manifest.jar;target/sample-calculator.jar calculator.CalculatorClient |
Linux
>java -cp../../lib/tuscany-sca-manifest.jar:target/sample-calculator.jar calculator.CalculatorClient |
运行结果如下
3 + 2=5.0 3 - 2=1.0 3 * 2=6.0 3 / 2=1.5 |
1.4. 用JAVA构建计算器应用
这个样例展示如何定义你的应用,使之关注于业务逻辑。它将引导你一步步构建计算器应用,这个组合构件(Composite)里的所有构件(Component)都是本地且用JAVA接口描述的。
1.4.1. 定义构建应用所需的模块(Block)
想想如何将你的应用分解成更小的功能或服务。每个模块是一个能够在整个应用使用的逻辑单元。在这个样例中,计算器应用可以分解成5个模块:AddService模块,SubtractService模块,MultiplyService模块,DivideService模块及一个接收并路由请求到相应模块的主入口模块,我们把这个主模块称作CalculatorService。
图-2
1.4.2. 实现每个模块
现在你已经分解出你的应用中的每个功能模块,准备实现这些模块。在SCA中功能模块被抽象为构件(Component),所以看看我们是怎么实现这些构件的。我们使用AddService构件作为第一个例子。
AddService构件提供将二个实数相加并返回操作结果的功能,当请求是加操作时,CalculatorService构件使用AddService构件。假设我们使用普通JAVA对象(POJO)写AddService构件,我们将从JAVA接口描述开始。
package calculator;
/** * The Add service interface */ public interface AddService {
double add(double n1, double n2);
} |
接下来提供该接口的实现
package calculator;
import java.util.logging.Level; import java.util.logging.Logger;
/** * An implementation of the Add service */ public class AddServiceImpl implements AddService {
public double add(double n1, double n2) { Logger logger = Logger.getLogger("calculator"); logger.log(Level.FINEST, "Adding " + n1 + " and " + n2); return n1 + n2; }
} |
哇,这就写了个SCA构件么?它不应该比普通JAVA接口及实现复杂么?嗯,这就是SCA构件,使用普通JAVA接口及实现就可以写出个SCA构件,SCA构件就是这么简单。我们可以不改变AddService构件实现就可以将它暴露为SCA支持的绑定形式,如Web Service,JMS,或者RMI。
让我们来看下CalculatorService构件,它引用AddService构件。
首先还是定义构件接口,它定义了CalculatorService构件提供的服务。
package calculator;
/** * The Calculator service interface. */ public interface CalculatorService {
double add(double n1, double n2);
double subtract(double n1, double n2);
double multiply(double n1, double n2);
double divide(double n1, double n2);
} |
接着实现它
package calculator;
import org.osoa.sca.annotations.Reference;
/** * An implementation of the Calculator service. */ public class CalculatorServiceImpl implements CalculatorService {
private AddService addService; private SubtractService subtractService; private MultiplyService multiplyService; private DivideService divideService;
@Reference public void setAddService(AddService addService) { this.addService = addService; }
@Reference public void setSubtractService(SubtractService subtractService) { this.subtractService = subtractService; }
@Reference public void setDivideService(DivideService divideService) { this.divideService = divideService; }
@Reference public void setMultiplyService(MultiplyService multiplyService) { this.multiplyService = multiplyService; }
public double add(double n1, double n2) { return addService.add(n1, n2); }
public double subtract(double n1, double n2) { return subtractService.subtract(n1, n2); }
public double multiply(double n1, double n2) { return multiplyService.multiply(n1, n2); }
public double divide(double n1, double n2) { return divideService.divide(n1, n2); }
} |
我们现在有了些用JAVA实现的构件,每个构件都定义了接口,本例是使用JAVA接口。注意它们只是有业务逻辑的普通的JAVA服务。
图-3
1.5. 组装计算器应用
到目前为止,我们已经创建了几个构件。我们如何实际运行这个应用呢?JAVA程序员可能会想到用下面的方法
public class CalculatorClient { public static void main(String[] args) throws Exception {
CalculatorServiceImpl calculatorService = new CalculatorServiceImpl(); AddService addService = new AddServiceImpl(); calculatorService.setAddService(addService);
System.out.println("3 + 2=" + calculatorService.add(3, 2)); // calls to other methods go here if we have implemented SubtractService, MultiplyService, DivideService } } |
但是这样没有用于Tuscany运行环境,也没有使用Web Service的接口的方式。我们来看下使用Tuscany运行环境的代码
public class CalculatorClient { public static void main(String[] args) throws Exception {
SCADomain scaDomain = SCADomain.newInstance("Calculator.composite"); CalculatorService calculatorService = scaDomain.getService(CalculatorService.class, "CalculatorServiceComponent");
System.out.println("3 + 2=" + calculatorService.add(3, 2)); // calls to other methods go here if we have implemented SubtractService, MultiplyService, DivideService
scaDomain.close(); } } |
你可以看到我们使用SCADomain的静态方法创建了一个SCADomain实例,SCADomain代表SCA系统的边界。它可以分布到多个处理程序,这个例子只是运行在单一的JVM环境。
参数“Calculator.composite”是一个用于描述SCA如何组装构件的XML文件。
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="Calculator">
<component name="CalculatorServiceComponent"> <implementation.java class="calculator.CalculatorServiceImpl"/> <reference name="addService" target="AddServiceComponent" /> <!-- You can add references to SubtractComponent, MultiplyComponent and DivideComponent --> </component>
<component name="AddServiceComponent"> <implementation.java class="calculator.AddServiceImpl"/> </component>
<!-- definitions of SubtractComponent, MultiplyComponent and DivideComponent -->
</composite> |
在此文件中我们定义了二个构件,Tuscany SCA需要加载使它们在.composite文件中运行。
请注意CalculatorServiceComponent构件有一个名称为“addService”的引用,此引用名“addService”并不是巧合,而是跟CalculatorService接口的实现CalculatorServiceImpl的addService属性名称一致的。Tuscany SCA运行时解析XML composite文件中的信息,然后用这些信息创建对象及组织它们间的关系。在我们的计算器应用中,它首先会创建AddServiceImpl及CalculatorServiceImpl实例,然后将AddServiceImpl实例引用注入到CalculatorServiceImpl实例的addService属性中。仔细观察CalculatorServiceImpl的代码,可以注意到setAddService方法上的@Reference注释,这个注释告诉Tuscany SCA这个属性需要自动注入。它等价于下面的代码
CalculatorServiceImpl calculatorService = new CalculatorServiceImpl(); AddService addService = new AddServiceImpl(); calculatorService.setAddService(addService); |
一旦composite文件被加载到SCADomain,客户端就可以通过SCADomain获取CalculatorServiceComponent构件的引用
CalculatorService calculatorService = scaDomain.getService(CalculatorService.class, "CalculatorServiceComponent"); |
SCA规范经常使用图表来描述SCA应用。它帮助我们快速概述应用中的构件及构件的连接方式。如果我们为计算器应用画图,它应该像图-4所示
图-4
你可以发现Tuscany中的所有样例都有提供PNG图,它使我们快速熟悉样例中的构件。
1.6. 部署计算器应用
组合构件描述SCA构件如何被实现以及它们如何通过引用到目标间的连线被装配。组合构件文件由一些依赖组成,如计算器应用依赖用于实现SCA构件定义的Java类及接口文件。组合构件文件及运行SCA应用所需的其他元素的集合组成一个或几个SCA Contribution。一个SCA Contribution可以是一文件目录,或者是一打包好的文件,如Jar文件。SCA没有要求Contribution任何特别的打包模式。在我们的这个计算器应用中,你可以把Contribution看成持有Calculator组合构件及其所有依赖。实际上你可以查看CalcalatorService应用生成的Jar包,其文件内容如下
calculator/AddService.class calculator/AddServiceImpl.class calculator/CalculatorClient.class calculator/CalculatorService.class calculator/CalculatorServiceImpl.class calculator/DivideService.class calculator/DivideServiceImpl.class calculator/MultiplyService.class calculator/MultiplyServiceImpl.class calculator/SubtractService.class calculator/SubtractServiceImpl.class Calculator.composite |
都是运行计算器应用所需的元素。我们只需要将此Contribution添加到SCA运行环境,然后就可以调用其提供的服务。
Tuscany所有样例都提供了build.xml及pom.xml文件,以供修改后重新构建应用。
1.7. 重新配置计算器应用-改变绑定方式
往回看,我们刚开始写的使用SCA运行时环境的计算器应用的客户端代码不比一般Java应用客户端长。我们有描述应用如何装配的XML composite文件。
对于我们的应用来说装配思想是个很大的优势,因为更加灵活且我们可以使用SCA应用编程模型改变,重用,及与其他应用整合或者后继开发。不管用什么语言实现它们。
例如,假设我们的计算器应用功能很强大又流行,我们想在公司内部共享这个应用,其他同事只要通过基于Web2.0应用的浏览器就可以访问。在Tuscany SCA中我们很容易实现,只需更改XML配置文件即可。下面展示了所需更改
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="Calculator">
<service name="CalculatorService" promote="CalculatorServiceComponent/CalculatorService"> <interface.java interface="calculator.CalculatorService"/> <binding.jsonrpc/> </service>
<component name="CalculatorServiceComponent"> <implementation.java class="calculator.CalculatorServiceImpl"/> <reference name="addService" target="AddServiceComponent" /> <!-- references to SubtractComponent, MultiplyComponent and DivideComponent --> </component>
<component name="AddServiceComponent"> <implementation.java class="calculator.AddServiceImpl"/> </component>
<!-- definitions of SubtractComponent, MultiplyComponent and DivideComponent -->
</composite> |
我们所做的只是在配置文件中增加个service元素,它告诉Tuscany SCA将CalculatorServiceComponent构件暴露为JSONRPC服务。注意我们并没有修改构件的JAVA代码,只是配置的改变。Helloworld-jsonrpc样例展示了使用jsonrpc绑定的方法,可以参考它来修改计算器应用,使之暴露为JSONRPC服务。如果还想把计算器应用暴露为SOAP/HTTP web service服务,只需作如下更改
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="Calculator">
<service name="CalculatorService" promote="CalculatorServiceComponent/CalculatorService"> <interface.java interface="calculator.CalculatorService"/> <!-- ** Below we added info for json binding ** --> <binding.jsonrpc/> <binding.ws/> </service>
<component name="CalculatorServiceComponent"> <implementation.java class="calculator.CalculatorServiceImpl"/> <reference name="addService" target="AddServiceComponent" /> <!-- references to SubtractComponent, MultiplyComponent and DivideComponent --> </component>
<component name="AddServiceComponent"> <implementation.java class="calculator.AddServiceImpl"/> </component>
<!-- definitions of SubtractComponent, MultiplyComponent and DivideComponent -->
</composite> |
这边我们添加了个binding.ws,这样计算器应用就可以同时暴露为JSONRPC服务和web service服务。helloworld-ws-service及helloworld-ws-reference样例展示了如何使用web service绑定形式。
SCA允许其他灵活性。我们可以重新连接构件,如把其中之一作为远程绑定,就像RMI,我们可以使CalculatorServiceComponent构件运行于一台机器,它远程引用其他构件。calculator-rmi-service及calculator-rmi-reference样例展示了RMI绑定形式。
1.8. 使用其他实现方式
我们可以介绍用不同语言实现的构件,如用Ruby实现SubtractServiceComponent构件。
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="Calculator">
<component name="CalculatorServiceComponent"> <implementation.java class="calculator.CalculatorServiceImpl"/> <reference name="addService" target="AddServiceComponent" /> <reference name="subtractService" target="SubtractServiceComponent" /> <!-- references to MultiplyComponent and DivideComponent --> </component>
<component name="AddServiceComponent"> <implementation.java class="calculator.AddServiceImpl"/> </component>
<component name="SubtractServiceComponent"> <implementation.script script="calculator/SubtractServiceImpl.rb"/> </component>
<!-- definitions of MultiplyComponent and DivideComponent -->
</composite> |
SubtractServiceComponent Ruby实现如下
def subtract(n1, n2) return n1 - n2 end |
Tuscany SCA运行时环境处理Java构件与Ruby构件间的连接及必要的数据转换。calculator-script样例展示了使用不同脚本语言实现构件的方法。
所以用SCA装配的应用具有较强的可扩展性及可整合性。