在這個(gè)系列教程的第一部分我們介紹了有關(guān)Web Services的基本概念,包括SOAP及WSDL。我們?cè)跇O短的時(shí)間來開發(fā)了一個(gè)Web Service,在開發(fā)過程中我們講解了SOAP消息、實(shí)現(xiàn)java web service客戶端及WSDL的結(jié)構(gòu)。在這篇文章中我們將就SOAP的復(fù)雜類型、錯(cuò)誤處理及遠(yuǎn)程對(duì)象引用等內(nèi)容做探討。
[i]提示:[/i]如果你還沒有下載用于創(chuàng)建我們示例程序的軟件,請(qǐng)參考第一部分安裝一節(jié)。你還需要下載示例源程序。假設(shè)你將下載的包解壓縮至c:\wasp_demo目錄下。所有例子的java源程序均可在解壓縮之后的文件目錄的src子目錄下找到,它們位于com.systinet.demos包中。除非你具備SOAP及WSDL的開發(fā)經(jīng)驗(yàn),否則我們建議你先讀本系列教程的第一部分。
SOAP及復(fù)雜類型 到目錄為止,我們開發(fā)的web services僅使用簡(jiǎn)單的數(shù)據(jù)類型如string、int、doubles?,F(xiàn)在讓我們來看看復(fù)雜數(shù)據(jù)類型是怎樣轉(zhuǎn)化成SOAP消息的。
SOAP協(xié)議推薦了所謂的SOAP編碼方案將編程語言的復(fù)雜類型轉(zhuǎn)化成XML。通常,如下的轉(zhuǎn)化是自動(dòng)進(jìn)行的:
- Java 2 的簡(jiǎn)單類型
- 符合JavaBesna規(guī)范的自定義類。所有公有的變量及getters/setters都通過Java內(nèi)省序列化器來轉(zhuǎn)化成XML。
如下示例演示了JavaBean的序列化及Java 2集合類的序列化。
我們將向這個(gè)Web Service傳送一個(gè)簡(jiǎn)單的名為OrderRequest數(shù)據(jù)結(jié)構(gòu)。OrderRequest是一個(gè)極為簡(jiǎn)單的JavaBean,其中包含了對(duì)自有變量symbol、limitPrice、volume的賦值及取值方法。這個(gè)Web Service的processOrder方法接收OrderReqesut作為其唯一的參數(shù)。隨后將向你展示怎樣在SOAP消息中表示OrderRequest這個(gè)數(shù)據(jù)結(jié)構(gòu)。服務(wù)的getOrders方法將服務(wù)接收到的所有訂單作為一個(gè)集合(collection)返回給客戶端。在java的類文件里,getOrders方法的返回類型為java.util.Hashtable,隨后將介紹這個(gè)數(shù)據(jù)類型在XML中是怎樣表示的。
我們繼續(xù)在股票市場(chǎng)上轉(zhuǎn)悠,現(xiàn)在來實(shí)現(xiàn)一個(gè)簡(jiǎn)單的股票交易(買股票)的Web Service。 package com.systinet.demos.mapping;
public class OrderService {
private java.util.HashMap orders = new java.util.HashMap(); public String processOrder(OrderRequest order) { String result = "PROCESSING ORDER"; Long id = new Long(System.currentTimeMillis()); result += "\n----------------------------"; result += "\nID: "+id; result += "\nTYPE: "+ ((order.getType()==order.ORDER_TYPE_SELL)?("SELL"):("BUY")); result += "\nSYMBOL: "+order.getSymbol(); result += "\nLIMIT PRICE: "+order.getLimitPrice(); result += "\nVOLUME: "+order.getVolume(); this.orders.put(id,order); return result; } public java.util.HashMap getOrders() { return this.orders; }
}
Figure 1: Complex types handling example (OrderService.java)
[i]提示[/i]:你可以在示例源碼解壓縮后的bin目錄下找到所有的腳本(scripts)。 執(zhí)行deployMapping.bat腳本以編譯及布署這個(gè)買股票的服務(wù)。客戶端程序簡(jiǎn)單地創(chuàng)建兩個(gè)購買請(qǐng)求并將它們發(fā)送給web service。然后客戶端程序獲取一個(gè)包含了兩個(gè)購買請(qǐng)求信息的Hashtable請(qǐng)將它們顯示在控制臺(tái)上。讓我們來看一看客戶端代碼,我們又一次在科技股上投機(jī): package com.systinet.demos.mapping;
import org.idoox.wasp.Context; import org.idoox.webservice.client.WebServiceLookup;
public final class TradingClient {
public static void main( String[] args ) throws Exception { WebServiceLookup lookup = (WebServiceLookup)Context.getInstance(Context.WEBSERVICE_LOOKUP); OrderServiceProxy service = (OrderServiceProxy)lookup.lookup("http://localhost:6060/MappingService/",OrderServiceProxy.class);
com.systinet.demos.mapping.struct.OrderRequest order = new com.systinet.demos.mapping.struct.OrderRequest(); order.symbol = "SUNW"; order.type = com.systinet.demos.mapping.OrderRequest.ORDER_TYPE_BUY; order.limitPrice = 10; order.volume = 100000; String result = service.processOrder(order); System.out.println(result); order = new com.systinet.demos.mapping.struct.OrderRequest(); order.symbol = "BEAS"; order.type = com.systinet.demos.mapping.OrderRequest.ORDER_TYPE_BUY; order.limitPrice = 13; order.volume = 213000; result = service.processOrder(order); System.out.println(result); java.util.HashMap orders = service.getOrders(); java.util.Iterator iter = orders.keySet().iterator(); while(iter.hasNext()) { Long id = (Long)iter.next(); OrderRequest req = (OrderRequest)orders.get(id); System.out.println("\n----------------------------"); System.out.println("\nID: "+id); System.out.println("\nTYPE: "+ ((req.getType()==com.systinet.demos.mapping.OrderRequest.ORDER_TYPE_SELL)?("SELL"):("BUY"))); System.out.println("\nSYMBOL: "+req.getSymbol()); System.out.println("\nLIMIT PRICE: "+req.getLimitPrice()); System.out.println("\nVOLUME: "+req.getVolume()); } }
}
Figure 2: Ordering client source code (TradingClient.java) 深入研討復(fù)雜數(shù)據(jù)類型的映射(Complex type mapping) 首先要介紹的是我們發(fā)布Web Service時(shí)產(chǎn)生的WSDL文件。如果你已經(jīng)布署了這個(gè)mapping service(譯者注:買股票服務(wù)的服務(wù)名),你可以通過如下鏈接查看其WSDL文件http://localhost:6060/MappingService/.
在這個(gè)教程的第一部分我們說過,WSDL描述了一個(gè)Web Service提供什么功能(WHAT部分),如何與其交互--如何調(diào)用它(HOW部分),以及它所在的地址(WHERE部分)。WSDL提供一個(gè)結(jié)構(gòu)化的機(jī)制用于描述它所提供的功能、它能處理的消息格式(formats)、它支持的協(xié)議及這個(gè)Web Service實(shí)例所在的地址。在我們的例子中,最值得關(guān)注的是OrderRequest這個(gè)java類是怎樣被映射成XML的:
<xsd:complexType name="OrderRequest"> <xsd:sequence> <xsd:element name="limitPrice" type="xsd:double"/> <xsd:element name="symbol" type="xsd:string"/> <xsd:element name="type" type="xsd:short"/> <xsd:element name="volume" type="xsd:long"/> </xsd:sequence> </xsd:complexType> 可以看到,OrderRequest被映射成一個(gè)簡(jiǎn)單數(shù)據(jù)類型的集合。從getOrders方法返回的HashMap被映射成從http://idoox.com/containers:HashMap導(dǎo)入的類型。我們的WSDL文件導(dǎo)入了如下的定義: <complexType name="HashMap"> <sequence> <element name="item" minOccurs="0" maxOccurs="unbounded"> <complexType> <sequence> <element name="key" type="anyType" /> <element name="value" type="anyType" /> </sequence> </complexType> </element> </sequence> </complexType> 現(xiàn)在讓我們來看一下客戶端與Web Service交互的SOAP消息。在一個(gè)HTTP瀏覽器中打開管理控制臺(tái),按一下刷新按鈕,在控制臺(tái)的MappingService區(qū)按一下"Enable"鏈接。接著,執(zhí)行runMappingClient.bat腳本以運(yùn)行客戶端程序,請(qǐng)注意交互時(shí)的SOAP消息。如下示例了對(duì)processOrder方法調(diào)用的SOAP消息,其中包含了一個(gè)OrderRequest實(shí)例參數(shù): <?xml version="1.0" encoding="UTF-8"?> <ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/"> <ns0:Body ns0:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"> <ns0:processOrder xmlns:ns0= "http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/mapping/OrderService"> <p0 xsi:type= "ns1:OrderRequest" xmlns:ns1="http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/mapping/"> <limitPrice xsi:type="xsd:double">10.0</limitPrice> <symbol xsi:type="xsd:string">SUNW</symbol> <type xsi:type="xsd:short">1</type> <volume xsi:type="xsd:long">100000</volume> </p0> </ns0:processOrder> </ns0:Body> </ns0:Envelope> 下面示例的是getOrders方法返回時(shí)的SOAP消息(包含購買請(qǐng)求信息的HashMap):
<?xml version="1.0" encoding="UTF-8"?> <ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/"> <ns0:Body ns0:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"> <ns0:getOrdersResponse xmlns:ns0= "http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/mapping/OrderService"> <response xsi:type="ns1:HashMap" xmlns:ns1="http://idoox.com/containers"> <item> <key xsi:type="xsd:long">1006209071080</key> <value xsi:type= "ns2:com.systinet.demos.mapping.OrderRequest" xmlns:ns2="http://idoox.com/package/"> <volume xsi:type="xsd:long">100000</volume> <symbol xsi:type="xsd:string">SUNW</symbol> <limitPrice xsi:type="xsd:double">10.0</limitPrice> <type xsi:type="xsd:short">1</type> </value> </item> <item> <key xsi:type="xsd:long">1006209071130</key> <value xsi:type="ns3:com.systinet.demos.mapping.OrderRequest" xmlns:ns3="http://idoox.com/package/"> <volume xsi:type="xsd:long">213000</volume> <symbol xsi:type="xsd:string">BEAS</symbol> <limitPrice xsi:type="xsd:double">13.0</limitPrice> <type xsi:type="xsd:short">1</type> </value> </item></response> </ns0:getOrdersResponse></ns0:Body> </ns0:Envelope> Java至XML的映射直接明了??梢钥吹酵鈱拥腍ashMap元素包含了多個(gè)key及value元素。注意到有一個(gè)OrderReqeust的數(shù)據(jù)類型在內(nèi)部的XML定義中。
最后我們可以運(yùn)行undeployMapping.bat以解除對(duì)剛才這個(gè)Web Service的布署。
SOAP錯(cuò)誤處理
當(dāng)服務(wù)器遇到錯(cuò)誤時(shí),SOAP定義了一個(gè)所謂的SOAP Fault的XML結(jié)構(gòu)來代表這個(gè)錯(cuò)誤。在本教程的第一部分我們簡(jiǎn)短地介紹過錯(cuò)誤消息,現(xiàn)在讓我們深入地鉆研一下。SOAP Fault包括三個(gè)基本的元素(element):
- FAULTCODE 它包含一個(gè)錯(cuò)誤的編碼或ID。
- FAULTSTRING 它包含對(duì)錯(cuò)誤的簡(jiǎn)單描述。
- DETAIL 對(duì)錯(cuò)誤的比較詳細(xì)的描述。
為了演示錯(cuò)誤消息的處理,我們?cè)谙惹暗墓善眻?bào)價(jià)的例子中增加一些異常。在getQuote方法中我們提供對(duì)三種股票的報(bào)價(jià),對(duì)于其它的股票,將拋出StockNotFoundException異常: package com.systinet.demos.fault;
public class StockQuoteService {
public double getQuote(String symbol) throws StockNotFoundException { if(symbol!=null && symbol.equalsIgnoreCase("SUNW")) return 10; if(symbol!=null && symbol.equalsIgnoreCase("MSFT")) return 50; if(symbol!=null && symbol.equalsIgnoreCase("BEAS")) return 11; throw new StockNotFoundException("Stock symbol "+symbol+" not found."); } public java.util.LinkedList getAvailableStocks() { java.util.LinkedList list = new java.util.LinkedList(); list.add("SUNW"); list.add("MSFT"); list.add("BEAS"); return list; }
}
Figure 3: SOAP web service Java source (StockQuoteService.java)
執(zhí)行deployFault.bat以布署這個(gè)web service。在一個(gè)HTTP瀏覽器中打開管理控制臺(tái),按一下刷新按鈕,在控制臺(tái)的StockQuoteService區(qū)按一下"Enable"鏈接。
在瀏覽器中打開http://localhost:6060/StockQuoteService/ 以顯示布署時(shí)產(chǎn)生的WSDL文件,請(qǐng)注意SOAP Fault消息在WSDL中的定義: <wsdl: message name=‘StockQuoteService_getQuote_com.systinet.demos.fault.StockNotFoundException_Fault‘> <wsdl:part name=‘idoox-java-mapping.com.systinet.demos.fault.StockNotFoundException‘ type=‘xsd:string‘/> </wsdl:message> 在WSDL的port type元素中,F(xiàn)ault消息是這樣被getQuote操作所引用的:
<wsdl:operation name=‘getQuote‘ parameterOrder=‘p0‘> <wsdl:input name=‘getQuote‘ message=‘tns:StockQuoteService_getQuote_Request‘/> <wsdl:output name=‘getQuote‘ message=‘tns:StockQuoteService_getQuote_Response‘/> <wsdl:fault name=‘getQuote_fault1‘ message=‘tns:StockQuoteService_getQuote_com.systinet.demos.fault.StockNotFoundException_Fault‘/> </wsdl:operation> 如下是binding元素的片段:
<wsdl:operation name=‘getQuote‘> <soap:operation soapAction=‘‘ style=‘rpc‘/> <wsdl:input name=‘getQuote‘> <soap:body use=‘encoded‘ encodingStyle=‘http://schemas.xmlsoap.org/soap/encoding/‘ namespace=‘http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/fault/‘/> </wsdl:input> <wsdl:output name=‘getQuote‘> <soap:body use=‘encoded‘ encodingStyle=‘http://schemas.xmlsoap.org/soap/encoding/‘ namespace=‘http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/fault/‘/> </wsdl:output> <wsdl:fault name=‘getQuote_fault1‘> <soap:fault name=‘getQuote_fault1‘ use=‘encoded‘ encodingStyle=‘http://schemas.xmlsoap.org/soap/encoding/‘ namespace=‘http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/fault/‘/> </wsdl:fault> </wsdl:operation>
看得出來,當(dāng)一個(gè)服務(wù)器端錯(cuò)誤產(chǎn)生時(shí),這個(gè)錯(cuò)誤被映射成一個(gè)簡(jiǎn)單的SOAP消息,然后返回給客戶端。
接下來讓我們創(chuàng)建一個(gè)簡(jiǎn)單的web service客戶程序: package com.systinet.demos.fault;
import org.idoox.wasp.Context; import org.idoox.webservice.client.WebServiceLookup;
public final class StockClient {
public static void main( String[] args ) throws Exception { // lookup service WebServiceLookup lookup = (WebServiceLookup)Context.getInstance(Context.WEBSERVICE_LOOKUP); // bind to StockQuoteService StockQuoteServiceProxy quoteService = (StockQuoteServiceProxy)lookup.lookup( "http://localhost:6060/StockQuoteService/", StockQuoteServiceProxy.class );
// use StockQuoteService System.out.println("Getting available stocks"); System.out.println("------------------------"); java.util.LinkedList list = quoteService.getAvailableStocks(); java.util.Iterator iter = list.iterator(); while(iter.hasNext()) { System.out.println(iter.next()); } System.out.println(""); System.out.println("Getting SUNW quote"); System.out.println("------------------------"); System.out.println("SUNW "+quoteService.getQuote("SUNW")); System.out.println(""); System.out.println("Getting IBM quote (warning, this one doesn‘t exist, so we will get an exception)"); System.out.println("------------------------"); System.out.println("SUNW "+quoteService.getQuote("IBM")); System.out.println("");
}
}
Figure 4: SOAP client Java source (StockClient.java) 我們需要產(chǎn)生客戶端的Java 接口,編譯這些java類,然后運(yùn)行客戶端程序。所有這些工作都包含在runFaultClient.bat腳本里。
我們的股票報(bào)價(jià)系統(tǒng)所含的股票種類不多,它不包含IBM。執(zhí)行客戶端程序里,客戶端將首先顯示所有可獲取股價(jià)的股票名,然后獲取SUNW的股票價(jià)格,當(dāng)想獲得IBM的股票價(jià)格時(shí),將拋出一個(gè)StockNotFound異常說“Stock symbol IBM not found"。請(qǐng)打開管理控制臺(tái),點(diǎn)擊show SOAP conversation鏈接,一個(gè)新窗口被打開,顯示如下的消息(高亮顯示的是重要的消息部分):
==== INPUT ==== http://localhost:6060/StockQuoteService/ ==== 11/14/01 4:44 PM = <?xml version="1.0" encoding="UTF-8"?> <ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/"> <ns0:Body ns0:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"> [i] <ns0:getQuote xmlns:ns0="http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/fault/"> <p0 xsi:type="xsd:string">IBM</p0> </ns0:getQuote>[/i] </ns0:Body> </ns0:Envelope> ==== CLOSE =====================================================================
==== OUTPUT ==== http://localhost:6060/StockQuoteService/ ====================== <?xml version="1.0" encoding="UTF-8"?> <ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/"> <ns0:Body ns0:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"> [i] <ns0:Fault xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/"> <faultcode>ns0:Server</faultcode> <faultstring>Stock symbol IBM not found.</faultstring> <detail xmlns:ijm="urn:idoox-java-mapping"> <ijm:idoox-java-mapping.com.systinet.demos.fault.StockNotFoundException> <ijm:stack-trace> com.systinet.demos.fault.StockNotFoundException: Stock symbol IBM not found. at com.systinet.demos.fault.StockQuoteService.getQuote(StockQuoteService.java:24) at java.lang.reflect.Method.invoke(Native Method) at com.idoox.wasp.server.adaptor.JavaAdaptorInvoker.invokeService(JavaAdaptorInvoker.java:387) at com.idoox.wasp.server.adaptor.JavaAdaptorInvoker.invoke(JavaAdaptorInvoker.java:239) at com.idoox.wasp.server.adaptor.JavaAdaptorImpl.dispatch(JavaAdaptorImpl.java:164) at com.idoox.wasp.server.AdaptorTemplate.dispatch(AdaptorTemplate.java:178) at com.idoox.wasp.server.ServiceConnector.dispatch(ServiceConnector.java:217) at com.idoox.wasp.server.ServiceManager.dispatch(ServiceManager.java:231) at com.idoox.wasp.server.ServiceManager$DispatcherConnHandler.handlePost(ServiceManager.java:1359) at com.idoox.transport.http.server.Jetty$WaspHttpHandler.handle(Jetty.java:94) at com.mortbay.HTTP.HandlerContext.handle(HandlerContext.java:1087) at com.mortbay.HTTP.HttpServer.service(HttpServer.java:675) at com.mortbay.HTTP.HttpConnection.service(HttpConnection.java:457) at com.mortbay.HTTP.HttpConnection.handle(HttpConnection.java:317) at com.mortbay.HTTP.SocketListener.handleConnection(SocketListener.java:99) at com.mortbay.Util.ThreadedServer.handle(ThreadedServer.java:254) at com.mortbay.Util.ThreadPool$PoolThreadRunnable.run(ThreadPool.java:601) at java.lang.Thread.run(Thread.java:484) </ijm:stack-trace> </ijm:idoox-java-mapping.com.systinet.demos.fault.StockNotFoundException> </detail> </ns0:Fault>[/i] </ns0:Body> </ns0:Envelope> ==== CLOSE ===================================================================== 請(qǐng)注意其中的FAULT結(jié)構(gòu)。FAULTCODE包含所產(chǎn)生的錯(cuò)誤編碼,F(xiàn)AULTSTRING元素?cái)y帶了這個(gè)異常消息,而DETAIL元素包含在棧中跟蹤到的異常。所有的SOAP錯(cuò)誤消息都遵從這種基本的格式。
最后,執(zhí)行updeployFault.bat以解除剛才服務(wù)的布署。
遠(yuǎn)程引用 遠(yuǎn)程引用是一種用于許多分布式對(duì)象系統(tǒng)中的結(jié)構(gòu),如RMI、CORBA及DCOM。假定你有一個(gè)調(diào)用服務(wù)器端對(duì)象的客戶程序。下面解釋它們是如何工作的。假設(shè)服務(wù)器端對(duì)象創(chuàng)建一個(gè)新的對(duì)象,且它需要將這個(gè)對(duì)象傳給遠(yuǎn)程的客戶端(如Figure 5 所示)。它可以選擇傳值(by value)或傳引用(by reference)的方式來傳遞這個(gè)對(duì)象。如果選擇傳值傳遞,需要將整個(gè)對(duì)象傳過去;如果是傳引用傳遞,則整整是傳遞了指向這個(gè)對(duì)象的指針。遠(yuǎn)程引用是工作在網(wǎng)絡(luò)環(huán)境下的引用。遠(yuǎn)程引用在許多分布式設(shè)計(jì)模式中受到批判,特別是工廠模式(Facotry pattern)中。因?yàn)檫@個(gè)特性與許多分布式計(jì)算應(yīng)用相矛盾,不是所有的SOAP實(shí)現(xiàn)支持這個(gè)它。
現(xiàn)在讓我們來看一個(gè)遠(yuǎn)程引用的例子。在一個(gè)Order Web Service中定義一個(gè)createLineItem方法。這個(gè)方法用于創(chuàng)建一個(gè)新的LineItem對(duì)象,這個(gè)對(duì)象包含所購產(chǎn)品的類別、產(chǎn)品價(jià)格及購買數(shù)量的信息。Order Web Service包含許多LineItem對(duì)象的引用。LineItem對(duì)象需要返回給客戶端程序給供客戶端獲取信息使用。
Figure 5: Remote references
實(shí)現(xiàn)簡(jiǎn)單的遠(yuǎn)程引用 我們將創(chuàng)建一個(gè)新的例子以演示遠(yuǎn)程引用特性。我們用從股市上賺的錢來買一些商品。首先定義兩個(gè)接口:Order及LineItem??蛻舳藢⑹褂眠@兩個(gè)接口來引用遠(yuǎn)程對(duì)象: package com.systinet.demos.interref;
public interface LineItem extends java.rmi.Remote {
public String getID(); public long getCount(); public String getProductID(); public void close();
}
Figure 6: LineItem interface
package com.systinet.demos.interref;
public interface Order {
public LineItem addItem(String productID, long count); public LineItem getItem(String id); public void removeItem(String id); }
Figure 7: Order interface 注意到LineItem接口繼承至java.rmi.Remote接口。這是在WASP中操作遠(yuǎn)程引用的最簡(jiǎn)單方法。除此之外,IineItem接口是非常好懂的。Order接口的addItem方法創(chuàng)建一個(gè)新的購買項(xiàng)(order item)并將其返回。getItem返回一個(gè)已存在的項(xiàng)目(item)而removeItem則從買單中刪除一個(gè)指定的項(xiàng)目(item)。
現(xiàn)在讓我們來實(shí)現(xiàn)這兩個(gè)接口:
package com.systinet.demos.interref;
import org.idoox.webservice.server.WebServiceContext; import org.idoox.webservice.server.LifeCycleService;
public class LineItemImpl implements LineItem {
private String pid; private String id; private long count; public LineItemImpl(String pid, long count) { System.err.println("Creating new LineItem."); this.id = pid+System.currentTimeMillis(); this.pid = pid; this.count = count; } public void close() { System.err.println("close()"); WebServiceContext context = WebServiceContext.getInstance(); LifeCycleService lc = context.getLifeCycleService(); lc.disposeServiceInstance(this); } public long getCount() { System.err.println("getCount()"); return this.count; } public String getProductID() { System.err.println("getProductID()"); return this.pid; } public String getID() { System.err.println("getID()"); return this.id; } }
Figure 8: LineItem implementation
package com.systinet.demos.interref;
public class OrderImpl implements Order {
private java.util.HashMap items = new java.util.HashMap();
public LineItem getItem(String id) { return (LineItem)this.items.get(id); }
public LineItem addItem(java.lang.String pid, long count) { LineItem item = new LineItemImpl(pid, count); this.items.put(item.getID(), item); return item; }
public void removeItem(java.lang.String id) { LineItem item = (LineItem)this.items.remove(id); item.close(); }
}
Figure 9: Order implementation
執(zhí)行deployInterref.bat以布署這個(gè)web service。
這是標(biāo)準(zhǔn)的實(shí)現(xiàn)。客戶端代碼也是很標(biāo)準(zhǔn)的實(shí)現(xiàn)法:
package com.systinet.demos.interref;
import javax.wsdl.QName;
import org.idoox.wasp.Context; import org.idoox.webservice.client.WebServiceLookup;
public final class OrderClient {
public static void main( String[] args ) throws Exception { // lookup service WebServiceLookup lookup = (WebServiceLookup)Context.getInstance(Context.WEBSERVICE_LOOKUP); Order order = (Order)lookup.lookup("http://localhost:6060/OrderService/", new QName("http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/interref/", "OrderService"), "OrderImpl", Order.class); String id1 = order.addItem("THNKPDT23", 2).getID(); String id2 = order.addItem("THNKPDT22", 2).getID(); System.out.println("ID1 "+id1); System.out.println("ID2 "+id2); LineItem item = order.getItem(id1); System.out.println("Line ITEM"); System.out.println("---------"); System.out.println("ID: "+item.getID()); System.out.println("Product ID: "+item.getProductID()); System.out.println("Count: "+item.getCount()); item = order.getItem(id2); System.out.println("Line ITEM"); System.out.println("---------"); System.out.println("ID: "+item.getID()); System.out.println("Product ID: "+item.getProductID()); System.out.println("Count: "+item.getCount());
}
}
Figure 10: Order client
這個(gè)簡(jiǎn)單的客戶端程序創(chuàng)建了購買服務(wù)(ordering web service)的動(dòng)態(tài)代理(proxy),然后創(chuàng)建兩個(gè)買單項(xiàng)(order item):THNKPDT23及THNKPDT22。兩個(gè)買單項(xiàng)都在服務(wù)端動(dòng)態(tài)地創(chuàng)建,客戶端只是獲得他們的引用(reference)。這是一個(gè)我們先前提到的工廠模式的蠻好的例子。在我們的例子中,購買服務(wù)(ordering service)充當(dāng)了買單項(xiàng)(order item)的工廠。
請(qǐng)注意買單項(xiàng)(line item)是有狀態(tài)的(stateful),因?yàn)樗鼈儽4嬗匈I單項(xiàng)數(shù)據(jù)。
刪除遠(yuǎn)程引用 不像無狀態(tài)的web service,有狀態(tài)的web service需要特別的代碼以鈍化。在我們的例子中我們使用一個(gè)特定的清除器。我們調(diào)用LifeCycle這個(gè)系統(tǒng)服務(wù)的disposeServiceInstance方法。請(qǐng)看如下的代碼:
public void close() { System.err.println("close()"); WebServiceContext context = WebServiceContext.getInstance(); LifeCycleService lc = context.getLifeCycleService(); lc.disposeServiceInstance(this); } 最后執(zhí)行undeployInterref.bat以解除我們剛才這個(gè)web service的布署。
下一步做什么? 在這部分我們研究了一下SOAP的復(fù)雜類型、SOAP錯(cuò)誤消息以及遠(yuǎn)程對(duì)象引用。現(xiàn)在我們已經(jīng)很好地理解了SOAP、WSDL及創(chuàng)建與使用web service的過程。我們希望能使這些東西顯示簡(jiǎn)單易懂。在第三部分,我們將關(guān)注于web service的安全問題。
同時(shí)我們非常歡迎各種反饋、評(píng)論及意見。請(qǐng)聯(lián)系: tutorial@systinet.com .
(原文地址:http://www.theserverside.com/resources/article.jsp?l=Systinet-web-services-part-2)
|