通向架构师的道路(第十天)之Axis2 Web Service(一)
一、Axis2簡介
1.1?介紹Axis2
Axis框架來自 Apache 開放源代碼組織,它是基于Java語言的最新的 SOAP 規(guī)范(SOAP 1.2)和 SOAP withAttachments 規(guī)范(來自 Apache Group )的開放源代碼實現。有很多流行的開發(fā)工具都使用AXIS作為其實現支持Web服務的功能,例如JBuilder以及著名的Eclipse J2EE插件Lomboz。AXIS的最新版本可以從 http://ws.apache.org/axis/index.html下載。
下載下來后直接解壓即可用了。下圖是AXIS核心引擎的體系結構圖:
整個AXIS項目包括以下幾個部分:
ü?? 消息流子系統
消息流子系統提供了靈活的消息傳遞框架,這個消息傳遞框架包括處理程序、鏈、序列化程序和反序列化程序。處理程序是一個處理請求、響應和故障流的對象。處理程序可被組合在一起成為鏈,而且可以使用一個靈活的部署描述符來配置這些處理程序的順序。
ü?? 傳輸框架子系統
提供了一個傳輸框架,這個傳輸框架可以幫助您創(chuàng)建自己的可插式傳輸發(fā)送器和傳輸偵聽器。
ü?? 數據編碼子系統
AXIS完全按照 XML Schema 規(guī)范提供各種數據類型的自動序列化,并且提供功能擴展接口來使用您自己定制的序列化器和反序列化器。
ü?? 其他
AXIS完全支持 WSDL 以及日志記錄、出錯以及故障處理機制。它同時提供一些工具用來講WSDL文檔轉換成客戶端的調用框架以及根據類來產生WSDL定義文檔。
AXIS目前版本支持的標準是:W3C SOAP1.1 和 1.2;WSDL 1.1;SAAJ 1.1(SUN公司:SOAP with Attachments API for Java);JAX-RPC(SUN公司:Java?APIfor XML-Based RPC)1.0。
1.2?開發(fā)項目中如何選擇使用Axis2和其它的WebService
2? 如果你的應用程序需要支持多語言即有C++, .net, Java,你應該選擇Axis2。
2 如果應用程序是遵循?spring?哲學路線的話,ApacheCXF 或者Spring WS是一種更好的選擇,特別對嵌入式的 Web Services 來說。
一、搭建環(huán)境
第一步:
打開Eclipse,建立一個標準的Dynamic Web Project
第二步:
建完工程后,把Axis2-1.4/Axis2-1.4.1解壓開來的目錄下的axis2-web(如下圖),整個拷入你工程的WebContent目錄下:
第三步:
把Axis2-1.4/Axis2-1.4.1解壓開來的目錄下的lib目錄里的所有jar導入你工程的WEB-INF/lib目錄下,并加載入工程的classpath,如下圖
第四步:
在工程和src平級處新建一個目錄叫external-lib
第五步:
把tomcat里的用于jsp, servlet解析的兩個lib拷入此external-lib目錄,并且以“加載外部jar”的方式加入工程的classpath中去
第六步:
在工程的WEB-INF目錄下新建一個目錄叫services目錄。
第七步:
把Axis2-1.4/Axis2-1.4.1解壓開來的目錄下的repository\modules,modules這個目錄,整個拷貝到你工程的WEB-INF目錄下。
整個配完的工程結構應如下圖所示:
三、??? 第一個Axis2Webservice之HelloWorld
3.1Service端代碼
| package org.sky.axis2.helloworld; import org.apache.axiom.om.OMElement; import org.apache.axis2.AxisFault; import javax.xml.stream.XMLStreamException; public class HelloWorld { ???????? public OMElement sayHello(OMElement element) throws XMLStreamException { ?????????????????? element.build(); ?????????????????? // Secondly the OMElement should be detached from the current OMTree so ?????????????????? // that it can be attached ?????????????????? // some other OM Tree. Once detached the OmTree will remove its ?????????????????? // connections to this OMElement. ?????????????????? element.detach(); ?????????????????? return element; ???????? } } |
3.2 Service端的Web Service布署文件
然后我們在工程的WebContent\WEB-INF\services\目錄下新建一個目錄叫HelloWorld的目錄,與我們的類同名,在此目錄下再建立一個META-INF的目錄(大小寫必須完全一致),然后在META-INF目錄下新建一個services.xml,具體目錄結構如下圖所示:
Services.xml文件的內容如下:
| <service name="HelloWorld"> ???????? <parameter name="ServiceClass">org.sky.axis2.helloworld.HelloWorld</parameter> ??? <operation name="sayHello"> ??????? <messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/> ??????? <actionMapping>urn:sayHello</actionMapping> ??? </operation> </service> |
該布署文件很容易閱讀:
1)????? 它聲明了一個WebService的主類叫” org.sky.axis2.helloworld.HelloWorld”;
2)????? 該WebService有一個Web Service Method,在類中該方法名叫sayHello,它返回一個OMElement為類型的值,接受一個OMElement為類型的參數;(RawXMLINOutMessageReceiver)
3)????? sayHello這個對外暴露的WebService方法名為: urn:sayHello
修改web.xml,在里面加入這些東東:
<servlet>
??? <display-name>Apache-Axis Servlet</display-name>
??? <servlet-name>AxisServlet</servlet-name>
??? <servlet-class>org.apache.axis2.transport.http.AxisServlet</servlet-class>
? </servlet>
? <servlet-mapping>
??? <servlet-name>AxisServlet</servlet-name>
??? <url-pattern>/servlet/AxisServlet</url-pattern>
? </servlet-mapping>
? <servlet-mapping>
??? <servlet-name>AxisServlet</servlet-name>
??? <url-pattern>*.jws</url-pattern>
? </servlet-mapping>
? <servlet-mapping>
??? <servlet-name>AxisServlet</servlet-name>
??? <url-pattern>/services/*</url-pattern>
? </servlet-mapping>
? <servlet>
??? <display-name>Apache-Axis Admin Servlet Web Admin</display-name>
??? <servlet-name>AxisAdminServlet</servlet-name>
??? <servlet-class>org.apache.axis2.transport.http.AxisAdminServlet</servlet-class>
??? <load-on-startup>100</load-on-startup>
? </servlet>
? <servlet-mapping>
??? <servlet-name>AxisAdminServlet</servlet-name>
??? <url-pattern>/axis2-admin/*</url-pattern>
? </servlet-mapping>
3.3?布署Web Service
在Tomcat的webapps目錄下建立一個目錄叫Axis2Service的目錄
然后將整個工程的WebContent目錄下的所有東西,全拷貝入這個目錄內,啟動tomcat。
啟動后訪問:http://localhost:8080/Axis2Service/axis2-web/
單點:Services這個鏈接
再點這個HelloWorld鏈接
我們就可以得到我們的HelloWorld這個WebService的wsdl內容了。
三、??? 四種不同的HelloWorld客戶端
Axis2支持4種類型的客戶端:
ü?? Block(阻塞式客戶端)
傳統客戶端
ü?? Non block(非阻塞式客戶端)
又被稱為異步客戶端,傳統客戶端被調用后將會被阻塞直到操作完成。這種方式在有很多Web Service需要在一個單一的客戶端應用程序中啟動時很不可取。一種解決方法是使用Non-Blocking API來調用這些Web Services。Axis2提供給用戶一種基于回叫機制的non-blocking API。
ü?? 雙工(雙向傳輸)
以上的機制都使用單一的連接去發(fā)送請求和接收響應.這都明顯在性能上落后于使用兩條連接(單向或雙向)進行進行請求和響應的傳輸 . 因此以上的機制都不能解決長時間運行的交易, 連接將在操作還沒完成就會超時. 一種解決方案是使用分開的兩條傳輸連接來傳輸請求和響應 . 我們叫這種方案為傳輸層異步。
ü?? 雙工非阻塞(雙向且非阻塞傳輸)
下面我們就來看這四個客戶端(此處對于雙工傳輸的客戶端代碼進行忽略,因為這個寫法很簡單,我們將著重于描述雙工+非阻塞的客戶端例子,網上目前幾乎沒有能夠在axis21.4下真正能夠運行的“雙工且非阻塞”的例子或者是完整代碼)。
4.1?傳統的客戶端HelloWorldWithReturnClient.java
| package org.sky.axis2.helloworld; import java.util.Iterator; import javax.xml.namespace.QName; import org.apache.axiom.om.OMAbstractFactory; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMFactory; import org.apache.axiom.om.OMNamespace; import org.apache.axiom.om.OMNode; import org.apache.axiom.soap.SOAPBody; import org.apache.axis2.AxisFault; import org.apache.axis2.addressing.EndpointReference; import org.apache.axis2.client.Options; import org.apache.axis2.client.ServiceClient; import org.apache.axis2.databinding.utils.BeanUtil; import org.apache.axis2.engine.DefaultObjectSupplier; public class HelloWorldWithReturnClient { ???????? private static?EndpointReference targetEPR = new EndpointReference( ??????????????????????????? "http://localhost:8080/Axis2Service/services/HelloWorld"); ???????? public void sayHello() { ?????????????????? Options options = new Options(); ???????????????????options.setAction("urn:sayHello"); ?????????????????? options.setTo(targetEPR); ?????????????????? ServiceClient sender = null; ?????????????????? try { ??????????????????????????? sender = new ServiceClient(); ??????????????????????????? sender.setOptions(options); ??????????????????????????? OMFactory fac = OMAbstractFactory.getOMFactory(); ????????????????????????????OMNamespace omNs = fac ?????????????????????????????????????????????? .createOMNamespace("http://helloworld.axis2.sky.org", ""); ????????????????????????????OMElement method = fac.createOMElement("sayHello", omNs); ??????????????????????????? OMElement name = fac.createOMElement("name", omNs); ??????????????????????????? name.setText("ymk"); ??????????????????????????? method.addChild(name); ????????????????????????????method.build(); ??????????????????????????? OMElement response = sender.sendReceive(method); ??????????????????????????? System.out.println(response); ????????????????????????????OMElement element = response.getFirstChildWithName( ?????????????????????????????????????????????? new QName("http://helloworld.axis2.sky.org", "name")); ??????????????????????????? System.out.println(element.getText()); ?????????????????? } catch (AxisFault e) { ??????????????????????????? System.out.println("------Error Occured------"); ??????????????????????????? e.printStackTrace(); ?????????????????? } ???????? } ???????? public static void main(String[] args) { ?????????????????? HelloWorldWithReturnClient testClient = new HelloWorldWithReturnClient(); ?????????????????? testClient.sayHello(); ???????? } } |
注意我加粗或者是加粗標紅處的代碼,為核心代碼。
由于我們使用提Axis2的底層API來調用WebService而并不是像傻瓜式的通過一個wsdl生成Web Service調用句柄,所以我們要對一個WSDL能夠有閱讀能力。
ü???Axis2中的Webservice的入口
不是:?http://localhost:8080/Axis2Service/services/HelloWorld?wsdl這個哦,而應該是:
http://localhost:8080/Axis2Service/services/HelloWorld,不帶?wsdl。
ü???看一個wsdl中的namespace是什么:
ü???看一個wsdl中的web service方法名是什么
ü???看一個wsdl的返回結果是怎么樣的格式:
為此我們在程序里增加了一行:OMElement response =sender.sendReceive(method);
然后直接:System.out.println(response);
這樣,我們可以看到它的返回為:
根據Axis2的OMElement的解析語法我們就可以直接把這個返回的值get出來。
我們甚至可以用opensource的SOAP UI這個工具,來直接得到我們的webservice調用的返回值,并以此返回結果的格式來決定我們如何去解析這個OMElement格式的返回結果:
在工程上單擊右鍵選“AddWSDL”
在彈出對話框中直接把http://localhost:8080/Axis2Service/services/HelloWorld?wsdl加入對方框中的wsdl欄中
點OK生成webservice soap ui調用的客戶端如下圖
它會生成11Binding和12Binding兩個客戶端,我們把11Binding的節(jié)點展開,雙擊Request 1,在右邊會顯示調用的soap樣例,在<hel:sayHello>?</hel:sayHello>塊中我們把問號替換成Monica,然后點上方“綠色”運行按鈕,我們就可以得到調用結果如下圖:
因此通過SOAP UI我們也可以測試我們的Web Service。
4.2?非阻塞式HelloWorldWithReturnNonBlock.java
與傳統的阻塞式webservice調用不同的是,一旦調用"serviceClient.sendReceive(payload);",客戶端將會被阻塞直到操作完成。這種方式在有很多Web Service需要在一個單一的客戶端應用程序中啟動時很不可取。一種解決方法是使用Non-Blocking API來調用這些Web Services。Axis2提供給用戶一種基于回叫機制的non-blocking API。
很多網上使用的“回調”接口都只能在Axis21.3中才能使用,而在Axis21.4中回調接口已經被升級成一個抽象類叫AxisCallBack類了,因此90%目前網上的關于“非阻塞式”的客戶端的代碼在Axis2中都是跑不起來的,在eclipse ide中會顯示“該類/方法已經被deprecated(廢棄)”。
因此請注意看筆者的代碼,先聲明一個用于非阻塞調用的回調(callback)類
HelloWorldNonBlockCB.java
| package org.sky.axis2.helloworld; import java.util.Iterator; import javax.xml.namespace.QName; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMNode; import org.apache.axis2.client.async.AxisCallback; import org.apache.axis2.context.MessageContext; import org.apache.axis2.databinding.utils.BeanUtil; import org.apache.axis2.engine.DefaultObjectSupplier; public class HelloWorldNonBlockCB implements AxisCallback { ???????? private boolean complete = false; ???????? public void onMessage(MessageContext msgContext) { ?????????????????? System.out.println(msgContext.getEnvelope().getBody()); ?????????????????? OMElement element = msgContext.getEnvelope().getBody() ???????????????????????????????????? .getFirstElement(); ?????????????????? OMElement result = element.getFirstChildWithName(new QName( ???????????????????????????????????? "http://helloworld.axis2.sky.org", "name")); ?????????????????? System.out.println(result.getText()); ?????????????????? synchronized (this) { ??????????????????????????? this.notify(); ?????????????????? } ???????? } ???????? public boolean isComplete() { ?????????????????? return complete; ???????? } ???????? public void onFault(MessageContext msgContext) { ?????????????????? System.out.println(msgContext.getEnvelope().getBody().getFault() ???????????????????????????????????? .toString()); ?????????????????? synchronized (this) { ??????????????????????????? this.notify(); ?????????????????? } ???????? } ???????? public void onError(Exception e) { ?????????????????? e.printStackTrace(); ?????????????????? synchronized (this) { ??????????????????????????? this.notify(); ?????????????????? } ???????? } ???????? public void onComplete() { ?????????????????? this.complete = true; ?????????????????? synchronized (this) { ??????????????????????????? this.notify(); ?????????????????? } ???????? } } |
有了callback接口,我們來寫我們的webservice調用類
HelloWorldWithReturnNonBlock.java
| package org.sky.axis2.helloworld; import org.apache.axis2.addressing.EndpointReference; import org.apache.axiom.om.OMAbstractFactory; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMFactory; import org.apache.axiom.om.OMNamespace; import org.apache.axis2.AxisFault; import org.apache.axis2.addressing.EndpointReference; import org.apache.axis2.client.Options; import org.apache.axis2.client.ServiceClient; import org.apache.axis2.client.async.AxisCallback; import org.apache.axis2.context.MessageContext; public class HelloWorldWithReturnNonBlock { ???????? private static EndpointReference targetEPR = new EndpointReference( ??????????????????????????? "http://localhost:8080/Axis2Service/services/HelloWorld"); ???????? public void sayHello() { ?????????????????? OMFactory fac = OMAbstractFactory.getOMFactory(); ?????????????????? OMNamespace omNs = fac.createOMNamespace( ???????????????????????????????????? "http://helloworld.axis2.sky.org", ""); ?????????????????? OMElement method = fac.createOMElement("sayHello", omNs); ?????????????????? OMElement name = fac.createOMElement("name", omNs); ?????????????????? name.setText("ymk"); ?????????????????? method.addChild(name); ?????????????????? method.build(); ?????????????????? Options options = new Options(); ?????????????????? options.setTo(targetEPR); ?????????????????? ServiceClient sender = null; ?????????????????? try { ????????????????????????????HelloWorldNonBlockCB callback = new HelloWorldNonBlockCB(); ????????????????????????????sender = new ServiceClient(); ????????????????????????????sender.setOptions(options); ????????????????????????????sender.sendReceiveNonBlocking(method, callback); ????????????????????????????synchronized (callback) { ?????????????????????????????????????try { ??????????????????????????????????????????????callback.wait(); ?????????????????????????????????????} catch (InterruptedException e) { ??????????????????????????????????????????????e.printStackTrace(); ?????????????????????????????????????} ????????????????????????????} ?????????????????? } catch (AxisFault e) { ??????????????????????????? e.printStackTrace(); ?????????????????? } finally { ??????????????????????????? if (sender != null) ???????????????????????????????????? try { ?????????????????????????????????????????????? sender.cleanup(); ???????????????????????????????????? } catch (Exception e) { ???????????????????????????????????? } ?????????????????? } ???????? } ???????? public static void main(String[] args) { ?????????????????? HelloWorldWithReturnNonBlock testClient = new HelloWorldWithReturnNonBlock(); ?????????????????? testClient.sayHello(); ???????? } } |
注意加粗標紅處的代碼。
4.3?非阻塞式雙工HelloWorldWithReturnDualNonBlock.java
非阻塞式的Web Service客戶端代碼很簡單,在此不做任何擅述,我們就說終極的非阻塞式雙工模式的Web Service客戶端使用Axis2的API如何實現。
記住以下幾個口決:
ü?? options.setUseSeparateListener(true);
非阻塞式雙工模式,會在客戶端也打開一個監(jiān)聽器,而且一直不斷的監(jiān)聽著服務器的返回值,該進程一旦被吊用,會一直被掛在客戶端這邊。
ü?? 設置webservice客戶端模式為雙工全開
options.setTransportInProtocol(Constants.TRANSPORT_HTTP);
ü?? 先尋址,后engageModule
在雙工模式下Web Service的客戶端需要尋址,即engageModule,這個engageModule需要這樣的一個參數:
engageModule(“addressing”)
或者也可寫成:
engageModule(Constants.MODULE_ADDRESSING);
這個engageModule就是需要訪問你的工程的WEB-INF\modules\目錄下的一個叫addressing-1.4.mar的文件。
因此在調用engageModule語句之間有兩種方式來調用你的WEB-INF\modules目錄下的addressing-1.4.mar文件。
第一種方式:
| ConfigurationContext sysContext = ConfigurationContextFactory ?????????????????????????????????????? .createConfigurationContextFromFileSystem( ???????????????????????????????????????????????????????? "D:\\wspace\\Axis2Service\\WebContent\\WEB-INF", ???????????????????????????????????????????????????????? null); ????????????????????sender = new ServiceClient(sysContext, null); ??????????????????????????? sender.engageModule(Constants.MODULE_ADDRESSING); |
第二種方式:
| sender = new ServiceClient(sysContext, null); ???????? sender.engageModule(Constants.MODULE_ADDRESSING); |
在第二種方式中,不需要為new ServiceClient()指定第一個sysContext參數,但是,你必須把WEB-INF\modules\addressing-1.4.mar指定到你的工程的classpath中去,如下圖
要不然運行時會拋出下面這個exception:
org.apache.axis2.AxisFault:Unable to engage module : addressing
下面來看代碼,此處的callback我們延用4.2小節(jié)中建立的callback接口即可,此處不再重復,直接給出客戶端代碼:
| package org.sky.axis2.helloworld; import org.apache.axiom.om.OMAbstractFactory; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMFactory; import org.apache.axiom.om.OMNamespace; import org.apache.axis2.AxisFault; import org.apache.axis2.Constants; import org.apache.axis2.addressing.EndpointReference; import org.apache.axis2.client.Options; import org.apache.axis2.client.ServiceClient; import org.apache.axis2.context.ConfigurationContext; import org.apache.axis2.context.ConfigurationContextFactory; public class HelloWorldWithReturnDualNonBlock { private static EndpointReference targetEPR = new EndpointReference( ??????????????????? "http://localhost:8080/Axis2Service/services/HelloWorld"); public static boolean finish = false; public void sayHello() { ?????????? OMFactory fac = OMAbstractFactory.getOMFactory(); ?????????? OMNamespace omNs = fac.createOMNamespace( ???????????????????????????? "http://helloworld.axis2.sky.org", ""); ?????????? OMElement method = fac.createOMElement("sayHello", omNs); ?????????? OMElement name = fac.createOMElement("name", omNs); ?????????? name.setText("ymk"); ?????????? method.addChild(name); ?????????? method.build(); ?????????? Options options = new Options(); ?????????? options.setTo(targetEPR); ???????????options.setTransportInProtocol(Constants.TRANSPORT_HTTP); ???????????options.setUseSeparateListener(true); ?????????? options.setAction("urn:sayHello"); ?????????? ServiceClient sender = null; ?????????? HelloWorldNonBlockCB callback = new HelloWorldNonBlockCB(); ?????????? try { ??????????????????? sender = new ServiceClient(); ????????????????????sender.engageModule(Constants.MODULE_ADDRESSING); ????????????????????sender.setOptions(options); ????????????????????sender.sendReceiveNonBlocking(method, callback); ????????????????????synchronized (callback) { ?????????????????????????????try { ???????????????????????????????????????callback.wait(); ?????????????????????????????} catch (InterruptedException e) { ???????????????????????????????????????e.printStackTrace(); ?????????????????????????????} ????????????????????} ?????????? } catch (Exception e) { ??????????????????? e.printStackTrace(); ?????????? } finally { ??????????????????? try { ???????????????????????????? sender.cleanup(); ??????????????????? } catch (Exception e) { ??????????????????? } ?????????? } } public static void main(String[] args) { ?????????? HelloWorldWithReturnDualNonBlock testClient = new HelloWorldWithReturnDualNonBlock(); ?????????? testClient.sayHello(); } } |
4.4 運行雙工異步客戶端前的準備工作
將Axis2的安裝目錄下的conf目錄下(如我的是:D:\opensource\axis\axis2-1.4.1-bin\axis2-1.4.1\conf)的axis2.xml拷貝到你的工程的WEB-INF目錄下。
并確保這個axis2.xml里有如下語句:
<moduleref="addressing"/>
運行后顯示:
Axis2的雙工非阻塞式客戶端調用成功!完成Axis2編寫Web Service的第一天教程。
總結
以上是生活随笔為你收集整理的通向架构师的道路(第十天)之Axis2 Web Service(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 通向架构师的道路(第七天)之漫谈使用Th
- 下一篇: 通向架构师的道路(第十一天)之Axis2