今天給大家講的是calculator的分布式版本,在這個案例中,我們先來看看其設計圖,如下:

從圖中可以明顯地看出有3個節點nodeA,nodeB,nodeC,而nodeA上有三個元件,nodeB和nodeC上分别有一個元件。這3個節點在同一個domain中。服務引用的關系在這裡就不多說了。
那麼我們下面進入開發過程了。
先建立一個普通的java project,項目名叫"MyDistributedCalculator",預設設定
在書寫主要的業務邏輯單元代碼之前,先象案例一一樣,加入MyTuscany使用者庫,好,現在集中精力書寫邏輯代碼,因為這裡對案例一有一個承接關系,是以建議看案例二之前先看明白案例一。我們這裡将案例一項目中的calculator包下的除CalculatorClient.java檔案外的java檔案都copy到myDistributedCalculator項目的calculator包下。如下圖:
這裡絕大部分代碼和案例一都一樣,隻是因為分布式的原因,是以就要修改幾個接口部分的代碼,因為AddServiceComponent和SubtractServiceComponent都在另外的機子上,是以在他們相應的服務接口上要聲明為@Remotable,以便讓nodeA上的CalculatorServiceComponent遠端引用,這和案例一是不一樣的地方。這裡給出他們修改後的代碼:
package calculator;
import org.osoa.sca.annotations.Remotable;
@Remotable
public interface AddService ... {
double add(double n1, double n2);
}
package calculator;
import org.osoa.sca.annotations.Remotable;
@Remotable
public interface SubtractService ... {
double subtract(double n1, double n2);
}
接下來要做的就是要寫一個domain manager類和node啟動類,我們先建立node包,然後建立相應的檔案,其中用于啟動domain manager類的檔案名為DomainNode.java,用于啟動node的類檔案名為CalculatorNode.java
他們分别的内容為:
package node;
import java.io.IOException;
import javax.xml.namespace.QName;
import org.apache.tuscany.sca.domain.SCADomain;
import org.apache.tuscany.sca.node.SCANode;
import org.apache.tuscany.sca.node.SCANodeFactory;
import calculator.CalculatorService;
public class CalculatorNode ... {
public static void main(String[] args) throws Exception ...{
// Check that the correct arguments have been provided
if (null == args || args.length < 2) ...{
System.err.println("Useage: java CalculatorNode domainname nodename");
System.exit(1);
}
try ...{
String domainName = args[0];
String nodeName = args[1];
ClassLoader cl = CalculatorNode.class.getClassLoader();
SCANodeFactory nodeFactory = SCANodeFactory.newInstance();
SCANode node = nodeFactory.createSCANode(nodeName, domainName);
node.addContribution(nodeName, cl.getResource(nodeName + "/"));
//System.out.println(cl.getResource(nodeName + "/").toString());
node.deployComposite(new QName("http://sample", "Calculator"));
node.start();
// nodeA is the head node and runs some tests while all other nodes
// simply listen for incoming messages
if ( nodeName.equals("nodeA") ) ...{
// do some application stuff
CalculatorService calculatorService =
node.getDomain().getService(CalculatorService.class, "CalculatorServiceComponentA");
// Calculate
System.out.println("3 + 2=" + calculatorService.add(3, 2));
System.out.println("3 - 2=" + calculatorService.subtract(3, 2));
System.out.println("3 * 2=" + calculatorService.multiply(3, 2));
System.out.println("3 / 2=" + calculatorService.divide(3, 2));
// a little hidden loop test to put some load on the nodes
if (args.length > 2)...{
for (int i=0; i < 1000; i++)...{
// Calculate
System.out.println("3 + 2=" + calculatorService.add(3, 2));
System.out.println("3 - 2=" + calculatorService.subtract(3, 2));
System.out.println("3 * 2=" + calculatorService.multiply(3, 2));
System.out.println("3 / 2=" + calculatorService.divide(3, 2));
}
}
} else ...{
// start up and wait for messages
try ...{
System.out.println("Node started (press enter to shutdown)");
System.in.read();
} catch (IOException e) ...{
e.printStackTrace();
}
}
// stop the node and all the domains in it
node.stop();
} catch(Exception ex) ...{
System.err.println("Exception in node - " + ex.getMessage());
ex.printStackTrace(System.err);
}
}
}
package node;
import org.apache.tuscany.sca.domain.SCADomain;
import org.apache.tuscany.sca.domain.SCADomainFactory;
public class DomainNode ... {
private static String DEFAULT_DOMAIN_URI = "http://localhost:8877";
public static void main(String[] args) ...{
try ...{
SCADomainFactory domainFactory = SCADomainFactory.newInstance();
SCADomain domain = domainFactory.createSCADomain(DEFAULT_DOMAIN_URI);
domain.start();
System.out.println("Domain started (press enter to shutdown)");
System.in.read();
domain.stop();
} catch (Exception e) ...{
e.printStackTrace();
}
System.out.println("Domain stopped");
}
}
這裡将我自己的了解和對代碼的分析寫下來:
先從DomainNode.java開始分析,裡面用到了兩個類, SCADomainFactory 和 SCADomain。代碼比較簡單,就是用 SCADomainFactory 本身的newInstance()生成自己的執行個體,然後通過 createSCADomain()函數生成SCADomain的執行個體,然後用 domain.start(); 啟動domain。這裡要特别注意的是,雖然代碼很簡單,但其實它完成的工作可不少,首先 createSCADomain()會去執行個體化一個實作了SCADomain接口的類,這裡是SCADomainImpl類,通過檢視分析tuscany的源代碼,發現該類初始化函數調用了init()函數
而在init()函數中完成了一些關鍵的工作,其中裡面有一個就是初始化一些domain manager的服務元件,那麼根據什麼來初始化這些元件呢,就是根據domain.composite檔案,該檔案的位置是在tuscany-sca-all-1.0.1-incubating.jar檔案内部,如圖:
該檔案的内容如下:
< composite xmlns ="http://www.osoa.org/xmlns/sca/1.0"
targetNamespace ="http://sample"
xmlns:sample ="http://sample"
xmlns:tuscany ="http://tuscany.apache.org/xmlns/sca/1.0"
name ="Domain" >
< component name ="domain" >
< tuscany:implementation .resource location ="webroot" />
< service name ="Resource" >
< tuscany:binding .http uri ="http://localhost:8877/domain" />
</ service >
</ component >
< component name ="DomainManagerComponent" >
< implementation .java class ="org.apache.tuscany.sca.domain.impl.DomainManagerServiceImpl" />
< service name ="DomainManagerInitService" >
< interface .java interface ="org.apache.tuscany.sca.domain.DomainManagerInitService" />
< binding .sca />
</ service >
< service name ="DomainManagerNodeEventService" >
< interface .java interface ="org.apache.tuscany.sca.domain.DomainManagerNodeEventService" />
< binding .ws uri ="http://localhost:8877/DomainManagerComponent/DomainManagerNodeEventService" />
</ service >
< service name ="DomainManagementService" >
< interface .java interface ="org.apache.tuscany.sca.domain.management.DomainManagementService" />
< tuscany:binding .jsonrpc uri ="http://localhost:8877/DomainManagerComponent/DomainManagementService" />
</ service >
</ component >
</ composite > 注意到上面的 < tuscany:binding .http uri ="http://localhost:8877/domain" />,這裡是這些元件注冊為相應的servlet時的uri位址,這也就要求DomainNode.java中的 private static String DEFAULT_DOMAIN_URI = "http://localhost:8877";必須是domain manager的根位址,必須和 tuscany:binding的uri保持一緻。到目前為止,我們就解釋完DomainNode的内容了,下面我們看看複雜些的CalculatorNode.java的内容。其實他的内容仔細看也不複雜,主幹部分的代碼功能主要是從指令行中接受兩個參數,一個是domainname,一個是nodename,然後根據這些資訊來啟動node,這裡跟前面的DomainNode有點類似,也是先用 SCANodeFactory的newInstance()執行個體化自身,再用 createSCANode執行個體化出一個實作了SCANode接口的 類,這裡是SCANodeImpl類,該類的初始化函數中也執行了其相應的init()函數,而init()函數則完成了對node管理元件的初始化,是根據tuscany-sca-all-1.0.1-incubating.jar檔案内部的node.composite檔案,下面将其檔案内容列出如下:
< composite xmlns ="http://www.osoa.org/xmlns/sca/1.0"
targetNamespace ="http://management"
xmlns:sample ="http://management"
xmlns:tuscany ="http://tuscany.apache.org/xmlns/sca/1.0"
name ="Management" >
< component name ="DomainManagerComponent" >
< implementation .java class ="org.apache.tuscany.sca.node.impl.DomainManagerServiceImpl" />
< reference name ="domainManager" >
< interface .java interface ="org.apache.tuscany.sca.domain.DomainManagerNodeEventService" />
< binding .ws uri ="http://localhost:8878/DomainManagerComponent/DomainManagerNodeEventService" />
</ reference >
</ component >
< component name ="NodeManagerComponent" >
< implementation .java class ="org.apache.tuscany.sca.node.impl.NodeManagerServiceImpl" />
< service name ="NodeManagerInitService" >
< interface .java interface ="org.apache.tuscany.sca.node.NodeManagerInitService" />
< binding .sca />
</ service >
< service name ="NodeManagerService" >
< interface .java interface ="org.apache.tuscany.sca.node.NodeManagerService" />
< binding .ws uri ="http://localhost:8878/NodeManagerComponent/NodeManagerService" />
</ service >
< service name ="ComponentManagerService" >
< interface .java interface ="org.apache.tuscany.sca.node.ComponentManagerService" />
< tuscany:binding .jsonrpc uri ="http://localhost:8878/NodeManagerComponent/ComponentManagerJson" />
</ service >
</ component >
< component name ="node" >
< tuscany:implementation .resource location ="webroot" />
< service name ="Resource" >
< tuscany:binding .http uri ="http://localhost:8878/node" />
</ service >
</ component >
</ composite >
通過這些資訊再與下圖相比來了解吧
Tuscany SCA案例分析(二)(連載中...)
好了,這裡有點初學者會有點糊塗,看看DomainNode.java就可以知道,先要啟動nodeB和nodeC,因為他們是被引用的服務元件,再是啟動nodeA,因為它上的CalculatorServiceComponent才能引用到相應的服務。這裡有點急了,都忘記要寫裝配檔案.composite檔案了,否則怎麼裝配這些服務呀,執行結果要等到寫完正确的裝配檔案再說,開動吧。先按下圖建立目錄和檔案,寫nodeA的composite檔案和sca-contribution.xml檔案,列出内容如下:
我在分布式SCADomain說明中說了如何加載contribution的,sca-contribution.xml裡面就說明了哪些composite可部署,Calculator.composite則告訴了該構件中有哪裡元件。
下面給出nodeA下的sca-contribution.xml和Calculator.composite内容:
<!-- sca-contribution.xml内容 -->
<? xml version="1.0" encoding="UTF-8" ?>
< contribution xmlns ="http://www.osoa.org/xmlns/sca/1.0"
targetNamespace ="http://sample"
xmlns:sample ="http://sample" >
< deployable composite ="sample:Calculator" />
</ contribution >
<!--
Calculator.composite檔案内容
-->
<? xml version="1.0" encoding="UTF-8" ?>
< composite xmlns ="http://www.osoa.org/xmlns/sca/1.0"
targetNamespace ="http://sample"
xmlns:sample ="http://sample"
name ="Calculator" >
< component name ="CalculatorServiceComponentA" >
< implementation .java class ="calculator.CalculatorServiceImpl" />
< reference name ="addService" target ="AddServiceComponentB" />
< reference name ="subtractService" target ="SubtractServiceComponentC" />
< reference name ="multiplyService" target ="MultiplyServiceComponentA" />
< reference name ="divideService" target ="DivideServiceComponentA" />
</ component >
< component name ="MultiplyServiceComponentA" >
< implementation .java class ="calculator.MultiplyServiceImpl" />
</ component >
< component name ="DivideServiceComponentA" >
< implementation .java class ="calculator.DivideServiceImpl" />
</ component >
</ composite >
每個相應的node目錄下的sca-contribution.xml内容都一樣的,不同的隻是Calculator.composite裡的内容,因為在nodeA中隻有兩個元件MultiplyServiceComponentA、DivideServiceComponentA還有些引用。其他node的Calculator.composite内容如下:
<? xml version="1.0" encoding="UTF-8" ?>
<!--
nodeB下的Calculator.composite内容
-->
< composite xmlns ="http://www.osoa.org/xmlns/sca/1.0"
targetNamespace ="http://sample"
xmlns:sample ="http://sample"
name ="Calculator" >
< component name ="AddServiceComponentB" >
< implementation .java class ="calculator.AddServiceImpl" />
</ component >
</ composite >
<? xml version="1.0" encoding="UTF-8" ?>
<!--
nodeC下的Calculator.composite内容
-->
< composite xmlns ="http://www.osoa.org/xmlns/sca/1.0"
targetNamespace ="http://sample"
xmlns:sample ="http://sample"
name ="Calculator" >
< component name ="SubtractServiceComponentC" >
< implementation .java class ="calculator.SubtractServiceImpl" />
< service name ="SubtractService" >
< binding .sca uri ="http://localhost:8086/SubtractServiceComponentC" />
</ service >
</ component >
</ composite > 好了,裝配的配置檔案都搞好了,下面我們來運作下,看看結果如何。
先從eclipse裡象啟動一般java程式一樣啟動DomainNode.java
然後再要啟動CalculatorNode.java,這裡啟動要傳入不同的參數,這裡給出啟動nodeB時打開啟動對話框配置的傳入參數圖:
啟動nodeC和nodeA的參數隻要用nodeC和nodeA替換nodeB就可以了,給出個最終結果圖吧,如下:
記住啟動的順序,先要啟動nodeB和nodeC,最後啟動nodeA呀,成功了記得告訴我,謝謝