Ready, Set, Go - Getting started with Tuscany

来源:互联网 发布:人工智能板块股票龙头 编辑:程序博客网 时间:2024/05/14 10:00

安装Tuscany的Eclipse插件

※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※

英文原文版权归原作者所有, 本译文转载请注明出处!

译者:abigfrog 联系:QQ:800736, MSN:J2EE@HOTMAIL.COM

※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※

    首先,启动Eclipse,并依次点击菜单:Help -> Software Updates -> Find and Install,选择“Search for new features to install”,点击下一步:

 

在接下来的对话框中,点击“New Remote Site…”创建一个新的站点,命名为“Tuscany”,其地址为:http://people.apache.org/~jsdelfino/tuscany/tools/updatesite/
确认选择了我们刚刚新增的一个远程站点,并点击“Finish”按钮
   
现在,项目如下:
 
组装服务
现在我们有了所有的服务实现,现在将要把它们组合在以前,以提供商店服务,集成配置保存在一个后缀为.composite的文件中。
src目录新建一个store.composite文件,将下面的内容复制进去:
<?xml version="1.0" encoding="UTF-8"?>
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
    xmlns:t
="http://tuscany.apache.org/xmlns/sca/1.0"
    xmlns:s
="http://store"
    name
="store">
    
<!—原文如此,错误
    <component name="store"
>
        
<t:implementation.widget location="ufservices/store.html"/>
        
<service name="Widget">
            
<t:binding.http/>
        
</service>
                
<reference name="catalog" target="Catalog">
             
<t:binding.jsonrpc/>
         
</reference>
         
<reference name="shoppingCart" target="ShoppingCart">
             
<t:binding.atom/>
         
</reference>
    
</component>
    -->
    
<!—正确的应该这样 -->
    
<component name="store">
        
<t:implementation.resource location="ufservices"/>
        
<service name="Resource">
            
<t:binding.http/>
        
</service>
    
</component>
    
<!—正确的应该这样 -->
    
<component name="Catalog">
        
<implementation.java class="services.CatalogImpl"/>
        
<property name="currencyCode">USD</property>
        
<service name="Catalog">
            
<t:binding.jsonrpc/>
        
</service>
        
<reference name="currencyConverter" target="CurrencyConverter"/>
    
</component>
    
<component name="ShoppingCart">
        
<implementation.java class="services.ShoppingCartImpl"/>
        
<service name="Collection">
            
<t:binding.atom/>
        
</service>
    
</component>
    
<component name="CurrencyConverter">
        
<implementation.java class="services.CurrencyConverterImpl"/>
    
</component>
</composite>
 
现在,项目是这个样子的:
 
译者注:根据对发布包中例程的观察,发现,此教程中少了两个关键的js脚本文件:binding-atom.js、binding-jsonrpc.js,分别是提供atom绑定和jsonrpc绑定服务的,这里附上代码:
Binding-atom.js源程序:
function AtomClient(uri) {
    
    
this.uri=uri;
    
    
this.get = function(id, responseFunction) {
        
var xhr = this.createXMLHttpRequest();
        xhr.onreadystatechange 
= function() {
            
if (xhr.readyState == 4) {
                
if (xhr.status == 200) {
                    
if (responseFunction !== null) {
                        responseFunction(xhr.responseXML);
                    }
                } 
else {
                    alert(
"get - Error getting data from the server");
                }
            }
        };
        xhr.open(
"GET", uri + id, true);
        xhr.send(
null);
    };    
    
this.post = function (entry, responseFunction) {
        
var xhr = this.createXMLHttpRequest();
        xhr.onreadystatechange 
= function() {
            
if (xhr.readyState == 4) {
                
if (xhr.status == 201) {
                    
if (responseFunction !== null) {
                        responseFunction(xhr.responseXML);
                    }
                } 
else {
                    alert(
"post - Error getting data from the server");
                }
            }
        };
        xhr.open(
"POST", uri, true);
        xhr.setRequestHeader(
"Content-Type""application/atom+xml");
        xhr.send(entry);
    };
    
this.put = function (id, entry, responseFunction) {
        
var xhr = this.createXMLHttpRequest();
        xhr.onreadystatechange 
= function() {
            
if (xhr.readyState == 4) {
                
if (xhr.status == 200) {
                    
if (responseFunction !== null) {
                        responseFunction(xhr.responseXML);
                    }
                } 
else {
                    alert(
"put - Error getting data from the server");
                }
            }
        };
        xhr.open(
"PUT", uri + id, true);
        xhr.setRequestHeader(
"Content-Type""application/atom+xml");
        xhr.send(entry);
    };
    
this.del = function (id, responseFunction) {       
        
var xhr = this.createXMLHttpRequest();
        xhr.onreadystatechange 
= function() {
            
if (xhr.readyState == 4) {
                
if (xhr.status == 200) {
                    
if (responseFunction !== null) {
                        responseFunction();
                    }
                } 
else {
                    alert(
"delete - Error getting data from the server");
                }
            }
        };
        xhr.open(
"DELETE", uri + id, true);        
        xhr.send(
null);
    };
    
    
this.createXMLHttpRequest = function () {
        
try {return new XMLHttpRequest();} catch(e) {}      
        
try {return new ActiveXObject("Msxml2.XMLHTTP");} catch(e) {}
        
try {return new ActiveXObject("Microsoft.XMLHTTP");} catch(e) {}
        alert(
"XML http request not supported");
        
return null;
    };
}

bindingatom 
= "loaded";
 
译者注:此js脚本经过译者修正,原脚本文件在eclipse中会报一些语法错误。
 
binding-jsonrpc.js源程序:
escapeJSONChar =
function escapeJSONChar(c)
{
    
if(c == """ || c == "/"return "/" + c;
    
else if (c == ""return "/b";
    
else if (c == " "return "/f";
    
else if (c == " "return "/n";
    
else if (c == " "return "/r";
    
else if (c == " "return "/t";
    
var hex = c.charCodeAt(0).toString(16);
    
if(hex.length == 1return "/u000" + hex;
    
else if(hex.length == 2return "/u00" + hex;
    
else if(hex.length == 3return "/u0" + hex;
    
else return "/u" + hex;
};


/* encode a string into JSON format */

escapeJSONString 
=
function escapeJSONString(s)
{
    
/* The following should suffice but Safari's regex is b0rken
       (doesn't support callback substitutions)
       return """ + s.replace(/([^ -]|[/"])/g,
       escapeJSONChar) + """;
    
*/

    
/* Rather inefficient way to do it */
    
var parts = s.split("");
    
for(var i=0; i < parts.length; i++) {
    
var c =parts[i];
    
if(c == '"' ||
       c 
== '/' ||
       c.charCodeAt(
0< 32 ||
       c.charCodeAt(
0>= 128)
        parts[i] 
= escapeJSONChar(parts[i]);
    }
    
return """ + parts.join(""+ """;
};


/* Marshall objects to JSON format */

toJSON 
= function toJSON(o)
{
    
if(o == null) {
    
return "null";
    } 
else if(o.constructor == String) {
    
return escapeJSONString(o);
    } 
else if(o.constructor == Number) {
    
return o.toString();
    } 
else if(o.constructor == Boolean) {
    
return o.toString();
    } 
else if(o.constructor == Date) {
    
return '{javaClass: "java.util.Date", time: ' + o.valueOf() +'}';
    } 
else if(o.constructor == Array) {
    
var v = [];
    
for(var i = 0; i < o.length; i++) v.push(toJSON(o[i]));
    
return "[" + v.join(""+ "]";
    } 
else {
    
var v = [];
    
for(attr in o) {
        
if(o[attr] == null) v.push(""" + attr + "": null");
        
else if(typeof o[attr] == "function"); /* skip */
        
else v.push(escapeJSONString(attr) + "" + toJSON(o[attr]));
    }
    
return "{" + v.join(""+ "}";
    }
};


/* JSONRpcClient constructor */

JSONRpcClient 
=
function JSONRpcClient_ctor(serverURL, user, pass, objectID)
{
    
this.serverURL = serverURL;
    
this.user = user;
    
this.pass = pass;
    
this.objectID = objectID;

    
/* Add standard methods */
    
if(this.objectID) {
    
this._addMethods(["listMethods"]);
    
var req = this._makeRequest("listMethods", []);
    } 
else {
    
this._addMethods(["system.listMethods"]);
    
var req = this._makeRequest("system.listMethods", []);
    }
    
var m = this._sendRequest(req);
    
this._addMethods(m);
};


/* JSONRpcCLient.Exception */

JSONRpcClient.Exception 
=
function JSONRpcClient_Exception_ctor(code, message, javaStack)
{
    
this.code = code;
    
var name;
    
if(javaStack) {
    
this.javaStack = javaStack;
    
var m = javaStack.match(/^([^:]*)/);
    
if(m) name = m[0];
    }
    
if(name) this.name = name;
    
else this.name = "JSONRpcClientException";
    
this.message = message;
};

JSONRpcClient.Exception.CODE_REMOTE_EXCEPTION 
= 490;
JSONRpcClient.Exception.CODE_ERR_CLIENT 
= 550;
JSONRpcClient.Exception.CODE_ERR_PARSE 
= 590;
JSONRpcClient.Exception.CODE_ERR_NOMETHOD 
= 591;
JSONRpcClient.Exception.CODE_ERR_UNMARSHALL 
= 592;
JSONRpcClient.Exception.CODE_ERR_MARSHALL 
= 593;

JSONRpcClient.Exception.prototype 
= new Error();

JSONRpcClient.Exception.prototype.toString 
=
function JSONRpcClient_Exception_toString(code, msg)
{
    
return this.name + "" + this.message;
};


/* Default top level exception handler */

JSONRpcClient.default_ex_handler 
=
function JSONRpcClient_default_ex_handler(e) { alert(e); };


/* Client settable variables */

JSONRpcClient.toplevel_ex_handler 
= JSONRpcClient.default_ex_handler;
JSONRpcClient.profile_async 
= false;
JSONRpcClient.max_req_active 
= 1;
JSONRpcClient.requestId 
= 1;


/* JSONRpcClient implementation */

JSONRpcClient.prototype._createMethod 
=
function JSONRpcClient_createMethod(methodName)
{
    
var fn=function()
    {
    
var args = [];
    
var callback = null;
    
for(var i=0;i<arguments.length;i++) args.push(arguments[i]);

/*    TUSCANY change callback to be last arg instead of first to match binding.ajax
    if(typeof args[0] == "function") callback = args.shift();
*/
    
if(typeof args[arguments.length-1== "function") callback = args.pop();

    
var req = fn.client._makeRequest.call(fn.client, fn.methodName,
                          args, callback);
    
if(callback == null) {
        
return fn.client._sendRequest.call(fn.client, req);
    } 
else {
        JSONRpcClient.async_requests.push(req);
        JSONRpcClient.kick_async();
        
return req.requestId;
    }
    };
    fn.client 
= this;
    fn.methodName 
= methodName;
    
return fn;
};

JSONRpcClient.prototype._addMethods 
=
function JSONRpcClient_addMethods(methodNames)
{
    
for(var i=0; i<methodNames.length; i++) {
    
var obj = this;
    
var names = methodNames[i].split(".");
    
for(var n=0; n<names.length-1; n++) {
        
var name = names[n];
        
if(obj[name]) {
        obj 
= obj[name];
        } 
else {
        obj[name]  
= new Object();
        obj 
= obj[name];
        }
    }
    
var name = names[names.length-1];
    
if(!obj[name]) {
        
var method = this._createMethod(methodNames[i]);
        obj[name] 
= method;
    }
    }
};

JSONRpcClient._getCharsetFromHeaders 
=
function JSONRpcClient_getCharsetFromHeaders(http)
{
    
try {
    
var contentType = http.getResponseHeader("Content-type");
    
var parts = contentType.split(/s*;s*/);
    
for(var i =0; i < parts.length; i++) {
        
if(parts[i].substring(08== "charset=")
        
return parts[i].substring(8, parts[i].length);
    }
    } 
catch (e) {}
    
return "UTF-8"/* default */
};

/* Async queue globals */
JSONRpcClient.async_requests 
= [];
JSONRpcClient.async_inflight 
= {};
JSONRpcClient.async_responses 
= [];
JSONRpcClient.async_timeout 
= null;
JSONRpcClient.num_req_active 
= 0;

JSONRpcClient._async_handler 
=
function JSONRpcClient_async_handler()
{
    JSONRpcClient.async_timeout 
= null;

    
while(JSONRpcClient.async_responses.length > 0) {
    
var res = JSONRpcClient.async_responses.shift();
    
if(res.canceled) continue;
    
if(res.profile) res.profile.dispatch = new Date();
    
try {
        res.cb(res.result, res.ex, res.profile);
    } 
catch(e) {
        JSONRpcClient.toplevel_ex_handler(e);
    }
    }

    
while(JSONRpcClient.async_requests.length > 0 &&
      JSONRpcClient.num_req_active 
< JSONRpcClient.max_req_active) {
    
var req = JSONRpcClient.async_requests.shift();
    
if(req.canceled) continue;
    req.client._sendRequest.call(req.client, req);
    }
};

JSONRpcClient.kick_async 
=
function JSONRpcClient_kick_async()
{
    
if(JSONRpcClient.async_timeout == null)
    JSONRpcClient.async_timeout 
=
        setTimeout(JSONRpcClient._async_handler, 
0);
};

JSONRpcClient.cancelRequest 
=
function JSONRpcClient_cancelRequest(requestId)
{
    
/* If it is in flight then mark it as canceled in the inflight map
       and the XMLHttpRequest callback will discard the reply. 
*/
    
if(JSONRpcClient.async_inflight[requestId]) {
    JSONRpcClient.async_inflight[requestId].canceled 
= true;
    
return true;
    }

    
/* If its not in flight yet then we can just mark it as canceled in
       the the request queue and it will get discarded before being sent. 
*/
    
for(var i in JSONRpcClient.async_requests) {
    
if(JSONRpcClient.async_requests[i].requestId == requestId) {
        JSONRpcClient.async_requests[i].canceled 
= true;
        
return true;
    }
    }

    
/* It may have returned from the network and be waiting for its callback
       to be dispatched, so mark it as canceled in the response queue
       and the response will get discarded before calling the callback. 
*/
    
for(var i in JSONRpcClient.async_responses) {
    
if(JSONRpcClient.async_responses[i].requestId == requestId) {
        JSONRpcClient.async_responses[i].canceled 
= true;
        
return true;
    }
    }

    
return false;
};

JSONRpcClient.prototype._makeRequest 
=
function JSONRpcClient_makeRequest(methodName, args, cb)
{
    
var req = {};
    req.client 
= this;
    req.requestId 
= JSONRpcClient.requestId++;

    
var obj = {};
    obj.id 
= req.requestId;
    
if (this.objectID)
    obj.method 
= ".obj#" + this.objectID + "." + methodName;
    
else
    obj.method 
= methodName;
    obj.params 
= args;

    
if (cb) req.cb = cb;
    
if (JSONRpcClient.profile_async)
    req.profile 
= { "submit"new Date() };
    req.data 
= toJSON(obj);

    
return req;
};

JSONRpcClient.prototype._sendRequest 
=
function JSONRpcClient_sendRequest(req)
{
    
if(req.profile) req.profile.start = new Date();

    
/* Get free http object from the pool */
    
var http = JSONRpcClient.poolGetHTTPRequest();
    JSONRpcClient.num_req_active
++;

    
/* Send the request */
    
if (typeof(this.user) == "undefined") {
    http.open(
"POST"this.serverURL, (req.cb != null));
    } 
else {
    http.open(
"POST"this.serverURL, (req.cb != null), this.user, this.pass);
    }

    
/* setRequestHeader is missing in Opera 8 Beta */
    
try { http.setRequestHeader("Content-type""text/plain"); } catch(e) {}

    
/* Construct call back if we have one */
    
if(req.cb) {
    
var self = this;
    http.onreadystatechange 
= function() {
        
if(http.readyState == 4) {
        http.onreadystatechange 
= function () {};
        
var res = { "cb": req.cb, "result"null"ex"null};
        
if (req.profile) {
            res.profile 
= req.profile;
            res.profile.end 
= new Date();
        }
        
try { res.result = self._handleResponse(http); }
        
catch(e) { res.ex = e; }
        
if(!JSONRpcClient.async_inflight[req.requestId].canceled)
            JSONRpcClient.async_responses.push(res);
        
delete JSONRpcClient.async_inflight[req.requestId];
        JSONRpcClient.kick_async();
        }
    };
    } 
else {
    http.onreadystatechange 
= function() {};
    }

    JSONRpcClient.async_inflight[req.requestId] 
= req;
    
    
try {
    http.send(req.data);
    } 
catch(e) {
    JSONRpcClient.poolReturnHTTPRequest(http);
    JSONRpcClient.num_req_active
--;
    
throw new JSONRpcClient.Exception
        (JSONRpcClient.Exception.CODE_ERR_CLIENT, 
"Connection failed");
    }

    
if(!req.cb) return this._handleResponse(http);
};

JSONRpcClient.prototype._handleResponse 
=
function JSONRpcClient_handleResponse(http)
{
    
/* Get the charset */
    
if(!this.charset) {
    
this.charset = JSONRpcClient._getCharsetFromHeaders(http);
    }

    
/* Get request results */
    
var status, statusText, data;
    
try {
    status 
= http.status;
    statusText 
= http.statusText;
    data 
= http.responseText;
    } 
catch(e) {
    JSONRpcClient.poolReturnHTTPRequest(http);
    JSONRpcClient.num_req_active
--;
    JSONRpcClient.kick_async();
    
throw new JSONRpcClient.Exception
        (JSONRpcClient.Exception.CODE_ERR_CLIENT, 
"Connection failed");
    }

    
/* Return http object to the pool; */
    JSONRpcClient.poolReturnHTTPRequest(http);
    JSONRpcClient.num_req_active
--;

    
/* Unmarshall the response */
    
if(status != 200) {
    
throw new JSONRpcClient.Exception(status, statusText);
    }
    
var obj;
    
try {
    eval(
"obj = " + data);
    } 
catch(e) {
    
throw new JSONRpcClient.Exception(550"error parsing result");
    }
    
if(obj.error)
    
throw new JSONRpcClient.Exception(obj.error.code, obj.error.msg,
                      obj.error.trace);
    
var res = obj.result;

    
/* Handle CallableProxy */
    
if(res && res.objectID && res.JSONRPCType == "CallableReference")
    
return new JSONRpcClient(this.serverURL, this.user,
                 
this.pass, res.objectID);

    
return res;
};


/* XMLHttpRequest wrapper code */

/* XMLHttpRequest pool globals */
JSONRpcClient.http_spare 
= [];
JSONRpcClient.http_max_spare 
= 8;

JSONRpcClient.poolGetHTTPRequest 
=
function JSONRpcClient_pool_getHTTPRequest()
{
    
if(JSONRpcClient.http_spare.length > 0) {
    
return JSONRpcClient.http_spare.pop();
    }
    
return JSONRpcClient.getHTTPRequest();
};

JSONRpcClient.poolReturnHTTPRequest 
=
function JSONRpcClient_poolReturnHTTPRequest(http)
{
    
if(JSONRpcClient.http_spare.length >= JSONRpcClient.http_max_spare)
    
delete http;
    
else
    JSONRpcClient.http_spare.push(http);
};

JSONRpcClient.msxmlNames 
= [ "MSXML2.XMLHTTP.5.0",
                 
"MSXML2.XMLHTTP.4.0",
                 
"MSXML2.XMLHTTP.3.0",
                 
"MSXML2.XMLHTTP",
                 
"Microsoft.XMLHTTP" ];

JSONRpcClient.getHTTPRequest 
=
function JSONRpcClient_getHTTPRequest()
{
    
/* Mozilla XMLHttpRequest */
    
try {
    JSONRpcClient.httpObjectName 
= "XMLHttpRequest";
    
return new XMLHttpRequest();
    } 
catch(e) {}

    
/* Microsoft MSXML ActiveX */
    
for (var i=0;i < JSONRpcClient.msxmlNames.length; i++) {
    
try {
        JSONRpcClient.httpObjectName 
= JSONRpcClient.msxmlNames[i];
        
return new ActiveXObject(JSONRpcClient.msxmlNames[i]);
    } 
catch (e) {}
    }

    
/* None found */
    JSONRpcClient.httpObjectName 
= null;
    
throw new JSONRpcClient.Exception(0"Can't create XMLHttpRequest object");
};

bindingjsonrpc 
= "loaded";
 
现在项目工程如下:
 
启动服务
编写Build文件:
<project name="store" default="compile">
    
<property name="test.class" value="launch.Launch" />
    
<property name="test.jar"   value="sample-store.jar" />
    
    
<target name="init">
        
<mkdir dir="target/classes"/>
    
</target>
    
    
<target name="compile" depends="init">
        
<javac srcdir="src"
               destdir
="target/classes"
               debug
="on"
               source
="1.6"
               target
="1.6"
               encoding
="UTF-8">
            
<classpath>
                
<pathelement location="lib/tuscany-sca-manifest.jar"/>
            
</classpath>
        
</javac> 
        
<copy todir="target/classes">
            
<fileset dir="resources"/>
        
</copy>
        
<jar destfile="target/${test.jar}" basedir="target/classes">
            
<manifest>
                
<attribute name="Main-Class" value="${test.class}" /> 
            
</manifest>
        
</jar>        
    
</target>    
    
    
<target name="run-classes">
        
<java classname="${test.class}"
              fork
="true">
            
<classpath>
                
<pathelement path="target/classes"/>
                
<pathelement location="lib/tuscany-sca-manifest.jar"/>
            
</classpath>
        
</java>
    
</target>
    
    
<target name="run">
        
<java classname="${test.class}"
              fork
="true">
            
<classpath>
                
<pathelement path="target/${test.jar}"/>
                
<pathelement location="lib/tuscany-sca-manifest.jar"/>
            
</classpath>
        
</java>        
    
</target>    
    
    
<target name="clean">
        
<delete quiet="true" includeemptydirs="true">
            
<fileset dir="target"/>
        
</delete>
    
</target>
</project>
现在我们将要启动我们创建的服务。新建一个launch包,在该包内新建一个Launch类,选中“public static void main(String[] args)”以为该类创建一个main方法,代码如下:
package launch;
import org.apache.tuscany.sca.host.embedded.SCADomain;
public class Launch {
    
public static void main(String[] args) throws Exception {
        System.out.println(
"Starting ...");
        SCADomain scaDomain 
= SCADomain.newInstance("store.composite");
        System.out.println(
"store.composite ready for big business !!!");
        System.out.println();
        System.in.read();
        scaDomain.close();
    }
}
现在项目如下:
 
祝贺你,你完成了你的第一个集成服务应用程序,现在是时候启动它了。
 
使用服务
在本步骤你将启动刚刚创建的服务应用。
首先,选中刚刚创建的Launch类,然后在右键菜单中选中Run As Java Application,那么Tuscany就会开始将store添加到域(domain)。
Eclipse的控制台将要输出如下信息:
 
接下来,就可以通过浏览器查看了:
http://localhost:8080/ufs/store.html
译者注:经译者测试,只有在Firefox浏览器上本程序的添加商品以及其他功能才正常,在IE浏览器上开始商品列表亦不能显示,在修正了binding-atom.js脚本的一些错误以后才显示出来,但提交功能仍不正常,经反复测试,发现在store.html文件中的shoppingCart_getResponse()函数中,var entries = feed.getElementsByTagName("entry");没有取得值:
function shoppingCart_getResponse(feed) {
        
if (feed != null) {
            
var entries = feed.getElementsByTagName("entry");              
            
var list = "";
            alert(entries.length);
            
for (var i=0; i<entries.length; i++) {
                
var item = entries[i].getElementsByTagName("content")[0].firstChild.nodeValue;
                list 
+= item + ' <br>';
            }
            document.getElementById(
"shoppingCart").innerHTML = list;
            document.getElementById(
'total').innerHTML = feed.getElementsByTagName("subtitle")[0].firstChild.nodeValue;
        }
    }
 
IE中如下:
 
下面是firefox中,运行正常,原因尚在分析中。
 
你可以在Catalog中选择水果,然后放入你的购物篮中。
注意:当你首次添加项目时浏览器会要求你输入用户名和密码,这时都输入admin。
 
因为ShoppingCart服务是通过Atom绑定的方式进行绑定的,也可以点击(feed)连接通过ATOM feed查看购物卡内容:
 
返回前一页:
 
最后,你可以结账以完成购物:
选择“Apache Tuscany SCA Tools”,并在接下来的窗口一路点击Next,直到点击Finish为止
 
接受该插件的使用协议(Plugin License)
 
接下来点击“Install All”
 
当被要求重启Eclipse的时候,点击“Yes”按钮
 
创建你的第一个集成服务应用程序
下面的图就是我们将要创建的应用的组件图

该应用是由四个服务组成的,这个应用是一个在线商店,这里有一个目录服务,你可以通过它取得项目名单,依靠currencyCode属性来判断是以美元还是欧元提供项目价格,目录服务本身不去做货币换算的工作,相反它引用了一个CurrencyConverter服务去完成这个工作。接下来,这里还有一个ShoppingCart服务,从目录选择了的项目可以被加进去(购物车),它是使用REST实现的;Catalog使用的是一个JSONRPC绑定,ShoppingCart呢则是通过ATOM进行绑定,最后,就是提供给用户的、基于浏览器的界面服务的Store组件了,Store服务组件分别通过JSONRPCATOM绑定使用CatalogShoppingCart服务。

 

创建一个Java工程

这一步你要在Eclipse中建立一个Java工程,点击工具栏上的New Java Project按钮 来打开新建工程的对话框,接下来输入store作为工程名称,并选择Create separate

folders for sources and class files

点击下一步按钮,在接下来的窗口打开Libraries选项卡,点击Add Library...按钮以添加Tuscany Libraray到工程中:

最后,点击Finish按钮结束工程创建。

 

构建服务

首先,创建两个包目录,以便在接下来的步骤中存放服务的实现。选择“store”工程,点击工具栏上的新建Java包按钮 启动创建包对话框。接下来,输入services作为包名,点击Finish按钮关闭对话框。

 

重复刚才的步骤,创建另外一个名为ufservices的包,工程现在看起来应该类似这跟样子:

记下来,会在services包内放入普通的服务,ufservices内是界面相关服务。

 

Catalog

现在,准备创建Catalog服务的接口和实现。

选择“services”包,接下来在工具栏的下拉菜单中点击“New Java Class” 按钮 和“New Java Interface”按钮 ,在弹出的对话框中以“Catalog”作为接口名,并点击Finish关闭对话框,这时在Java编辑器中就会打开新增的Java接口,将下面这段Java接口代码Copy-Paste到编辑器内:

package services;
import org.osoa.sca.annotations.Remotable;
@Remotable
public interface Catalog {
    String[] get();
}

再次选中“services”包,点击“New Java Class”按钮 ,在弹出的对话框中以“CatalogImpl”作为类的名字,将“Catalog”作为该类的实现接口,点击Finish完成。

Java编辑器中会打开我们新增的类,将下面的代码复制过去:

 

package services;
import java.util.ArrayList;
import java.util.List;
import org.osoa.sca.annotations.Init;
import org.osoa.sca.annotations.Property;
import org.osoa.sca.annotations.Reference;
public class CatalogImpl implements Catalog {
    @Property
    
public String currencyCode = "USD";
    @Reference
    
public CurrencyConverter currencyConverter;
    
private List<String> catalog = new ArrayList<String>();
    @Init
    
public void init() {
        String currencySymbol 
= currencyConverter.getCurrencySymbol(currencyCode);
        catalog.add(
"Apple - " + currencySymbol +
        currencyConverter.getConversion(
"USD", currencyCode, 2.99f));
        catalog.add(
"Orange - " + currencySymbol +
        currencyConverter.getConversion(
"USD", currencyCode, 3.55f));
        catalog.add(
"Pear - " + currencySymbol +
        currencyConverter.getConversion(
"USD", currencyCode, 1.55f));
    }
    
public String[] get() {
        String[] catalogArray 
= new String[catalog.size()];
        catalog.toArray(catalogArray);
        
return catalogArray;
    }
}

 

当完成这些步骤以后,store项目看起来会类似这样:

 

注意,CatalogImpl上面有一个红叉,因为它引用了我们还没由实现的CurrencyConverter接口。
 
CurrencyConverter

在这一步,我们将创建CurrencyConverter的接口和实现,首先,按照之前的方式建立接口和类文件,并将下面的代码复制过去:

package services;
import org.osoa.sca.annotations.Remotable;
@Remotable
public interface CurrencyConverter {
    
public float getConversion(String fromCurrenycCode,
    String toCurrencyCode, 
float amount);
    
public String getCurrencySymbol(String currencyCode);
}
 
下面,我们来创建上面这跟接口的实现类CurrencyConverterImpl:
package services;
public class CurrencyConverterImpl implements CurrencyConverter {
    
public float getConversion(String fromCurrencyCode,
        String toCurrencyCode, 
float amount) {
        
if (toCurrencyCode.equals("USD"))
            
return amount;
        
else             if (toCurrencyCode.equals("EUR"))
                
return amount*0.7256f;
        
return 0;
    }
    
public String getCurrencySymbol(String currencyCode) {
        
if (currencyCode.equals("USD"))
            
return "$";
        
else
            
if (currencyCode.equals("EUR"))
                
return "";
        
return "?";
    }
}
 
现在,项目看起来是这样:
 
ShoppingCart
在这里我们要创建ShoppingCart的实现类。
仍然在services包内创建我们的实现类,代码如下:
package services;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.apache.tuscany.sca.binding.feed.collection.Collection;
import org.apache.tuscany.sca.binding.feed.collection.NotFoundException;
import com.sun.syndication.feed.atom.Content;
import com.sun.syndication.feed.atom.Entry;
import com.sun.syndication.feed.atom.Feed;
import com.sun.syndication.feed.atom.Link;
public class ShoppingCartImpl implements Collection {
    
// needs to change to instance var once conversation scope works
    private static Map<String, Entry> cart = new HashMap<String, Entry>();
    
public Feed getFeed() {
        Feed feed 
= new Feed();
        feed.setTitle(
"shopping cart");
        Content subtitle 
= new Content();
        subtitle.setValue(
"Total : " + getTotal());
        feed.setSubtitle(subtitle);
        feed.getEntries().addAll(cart.values());
        
return feed;
    }
    
public Entry get(String id) throws NotFoundException {
        
return cart.get(id);
    }
    
public Entry post(Entry entry) {
        String id 
= "cart-" + UUID.randomUUID().toString();
        entry.setId(id);
        Link link 
= new Link();
        link.setRel(
"edit");
        link.setHref(
"" + id);
        entry.getOtherLinks().add(link);
        link 
= new Link();
        link.setRel(
"alternate");
        link.setHref(
"" + id);
        entry.getAlternateLinks().add(link);
        entry.setCreated(
new Date());
        cart.put(id, entry);
        
return entry;
    }
        
public void put(String id, Entry entry) throws NotFoundException {
        entry.setUpdated(
new Date());
        cart.put(id, entry);
    }
    
public void delete(String id) throws NotFoundException {
        
if (id.equals(""))
            cart.clear();
        
else
            cart.remove(id);
    }     
private String getTotal() {
        
float total = 0;
        String symbol 
= "";
        
if (!cart.isEmpty()) {
            Entry entry 
= cart.values().iterator().next();
            String item 
= ((Content)entry.getContents().get(0)).getValue();
            symbol 
= item.substring(item.indexOf("-")+2, item.indexOf("-")+3);
        }
        
for (Entry entry : cart.values()) {
            String item 
= ((Content)entry.getContents().get(0)).getValue();
            total 
+= Float.valueOf(item.substring(item.indexOf("-")+3));
        }
        
return symbol + String.valueOf(total);
    }
}
注意:由于Tuscany的会话支持目前还实现,购物篮在这里通过一个hack来实现,cart被定义成一个静态域。
目前,项目看起来如下:
 
Store
这里将创建一个用户界面服务,通过浏览器为我们创建的其他服务提供交互的接口和界面。在ufservices内新建一个文件store.html,代码如下:
<html>
<head>
<title>Store</TITLE>

<script type="text/javascript" src="store.js"></script>

<script language="JavaScript">

    
//Reference
    catalog = (new JSONRpcClient("../Catalog")).Catalog;
    
//Reference
    shoppingCart = new AtomClient("../ShoppingCart");


    
function catalog_getResponse(items) {
        
var catalog = "";
        
for (var i=0; i<items.length; i++)
            catalog 
+= '<input name="items" type="checkbox" value="' +
                        items[i] 
+ '">' + items[i]+ ' <br>';
        document.getElementById(
'catalog').innerHTML=catalog;
    }

    
function shoppingCart_getResponse(feed) {
        
if (feed != null) {
            
var entries = feed.getElementsByTagName("entry");
            
var list = "";
            
for (var i=0; i<entries.length; i++) {
                
var item = entries[i].getElementsByTagName("content")[0].firstChild.nodeValue;
                list 
+= item + ' <br>';
            }
            document.getElementById(
"shoppingCart").innerHTML = list;
            document.getElementById(
'total').innerHTML = feed.getElementsByTagName("subtitle")[0].firstChild.nodeValue;
        }
    }
    
function shoppingCart_postResponse(entry) {
        shoppingCart.get(
"", shoppingCart_getResponse);
    }


    
function addToCart() {
        
var items  = document.catalogForm.items;
        
var j = 0;
        
for (var i=0; i<items.length; i++)
            
if (items[i].checked) {
                
var entry = '<entry xmlns="http://www.w3.org/2005/Atom"><title>cart-item</title><content type="text">'+items[i].value+'</content></entry>'
                shoppingCart.post(entry, shoppingCart_postResponse);
                items[i].checked 
= false;
            }
    }
    
function checkoutCart() {
        document.getElementById(
'store').innerHTML='<h2>' +
                
'Thanks for Shopping With Us!</h2>'+
                
'<h2>Your Order</h2>'+
                
'<form name="orderForm" action="store.html">'+
                    document.getElementById(
'shoppingCart').innerHTML+
                    
'<br>'+
                    document.getElementById(
'total').innerHTML+
                    
'<br>'+
                    
'<br>'+
                    
'<input type="submit" value="Continue Shopping">'+
                
'</form>';
        shoppingCart.del(
""null);
    }
    
function deleteCart() {
        shoppingCart.del(
""null);
        document.getElementById(
'shoppingCart').innerHTML = "";
        document.getElementById(
'total').innerHTML = "";
    }

    catalog.get(catalog_getResponse);
    shoppingCart.get(
"", shoppingCart_getResponse);
</script>

</head>

<body>
<h1>Store</h1>
  
<div id="store">
       
<h2>Catalog</h2>
       
<form name="catalogForm">
        
<div id="catalog" ></div>
        
<br>
        
<input type="button" onClick="addToCart()"  value="Add to Cart">
       
</form>

     
<br>

       
<h2>Your Shopping Cart</h2>
       
<form name="shoppingCartForm">
        
<div id="shoppingCart"></div>
        
<br>
        
<div id="total"></div>
        
<br>
        
<input type="button" onClick="checkoutCart()" value="Checkout">
        
<input type="button" onClick="deleteCart()" value="Empty">
           
<href="../ShoppingCart/">(feed)</a>
    
</form>
  
</div>
</body>
</html>