[原創] Web服務部署內幕[絕對原創]
|
|
http://www.chinaunix.net 作者:simbasun??發表于:2005-04-15 01:33:25 |
【發表評論】【查看原文】【Java討論區】【關閉】 |
===============================================================================================
為了能讓web服務先跑起來,先給出一個Web服務的原型,以便于后面的討論。 我們從一個最簡單的例子開始,只給出必須的東西。
所需軟件: 1.Tomcat4.1.2 2.一個Java編譯器,jdk或JBuilder等等,這是為了編譯我們的Java源程序,于web服務無關。
所需文件: 1.sayHello.java 2.web.xml 3.server-config.xml 4.Java?Packages:?axis.jar,jaxrpc.jar,tt-bytecode.jar,wsdl4j.jar,xercesImpl.jar,xml-apis.jar
至于Tomcat怎么安裝我就不說了,網上關于Tomcat安裝的文章有很多。 這六個package,從ibm和apache的網站上都可以下得到。
只需要這些,我們就可以部署自己的Web服務了。。 下面是目錄結構: webapps/test/WEB-INF/web.xml webapps/test/WEB-INF/server-config.wsdd webapps/test/WEB-INF/classes/sayHello.class webapps/test/WEB-INF/lib/xxx.jar?---所需得六個packages
web.xml ---------------------------------------------
<?xml?version="1.0"?encoding="ISO-8859-1"?>; <!DOCTYPE?web-app?PUBLIC?"-//Sun?Microsystems,?Inc.//DTD?Web?Application?2.2//EN"?"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">;
<web-app>;
<servlet>; ????<servlet-name>;Axis</servlet-name>; ????<!--實際servlet程序,這里是AxisServlet-->; ????<servlet-class>;org.apache.axis.transport.http.AxisServlet</servlet-class>; </servlet>;
<!--?###?定義servlet和url的對應關系-->;
<servlet-mapping>; ????<servlet-name>;Axis</servlet-name>; ????<url-pattern>;/services/*</url-pattern>; </servlet-mapping>;
</web-app>;
---------------------------------------------
server-config.wsdd --------------------------------------------- <?xml?version="1.0"?encoding="UTF-8"?>; <deployment?xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"?xmlns="http://xml.apache.org/axis/wsdd/">;
<handler?type="java:org.apache.axis.handlers.http.URLMapper"?name="URLMapper"/>; ?? <service?name="sayHelloService"?provider="java:RPC">; ??<parameter?name="className"?value="sayHello"/>; ??<parameter?name="allowedMethods"?value="sayHelloTo"/>; </service>;
<transport?name="http">; ??<requestFlow>; ????<handler?type="URLMapper"/>; ??</requestFlow>; </transport>;
</deployment>; ---------------------------------------------
sayHello.java --------------------------------------------- public?class?sayHello { ??public?String?sayHelloTo(String?aname) ??{ ????return?"How?are?you,?"?+?aname; ??} } ---------------------------------------------
假設ip地址192.168.0.1,端口號是80,我們輸入下面的url得到服務列表(當然這里只有一個): http://192.168.0.1/test/services 如果你的端口號是8080,就應該輸入http://192.168.0.1:8080/test/services,后面同理。
瀏覽器顯示: —————————————— |And?now...?Some?Services?| |?sayHelloService?(wsdl)??| |??.sayHelloTo????????????|??? ——————————————
sayHelloService是我們的服務名,右側的?(wsdl)是一個鏈接指向sayHelloService的WSDL文檔, 這個文檔是由Axis自動生成的。 sayHelloTo當然就是我們的方法了。。。
點擊(wsdl)鏈接或輸入下面的url,得到WSDL: http://192.168.0.1/test/services/sayHelloService?wsdl
瀏覽器顯示sayHelloService的WSDL文檔:
<?xml?version="1.0"?encoding="UTF-8"?>; <wsdl:definitions?targetNamespace="http://192.168.0.1/test/services/sayHelloService/test/services/sayHelloService"?xmlns="http://schemas.xmlsoap.org/wsdl/"?xmlns:apachesoap="http://xml.apache.org/xml-soap"?xmlns:impl="http://192.168.0.1/test/services/sayHelloService/test/services/sayHelloService-impl"?xmlns:intf="http://192.168.0.1/test/services/sayHelloService/test/services/sayHelloService"?xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"?xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"?xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"?xmlns:xsd="http://www.w3.org/2001/XMLSchema">; ??<wsdl:message?name="sayHelloToResponse">; ????<wsdl:part?name="return"?type="xsd:string"/>; ??</wsdl:message>; ??<wsdl:message?name="sayHelloToRequest">; ????<wsdl:part?name="aname"?type="xsd:string"/>; ??</wsdl:message>; ??<wsdl:portType?name="sayHello">; ????<wsdl:operation?name="sayHelloTo"?parameterOrder="aname">; ??????<wsdl:input?message="intf:sayHelloToRequest"?name="sayHelloToRequest"/>; ??????<wsdl:output?message="intf:sayHelloToResponse"?name="sayHelloToResponse"/>; ????</wsdl:operation>; ??</wsdl:portType>; ??<wsdl:binding?name="sayHelloServiceSoapBinding"?type="intf:sayHello">; ????<wsdlsoap:binding?style="rpc"?transport="http://schemas.xmlsoap.org/soap/http"/>; ????<wsdl:operation?name="sayHelloTo">; ??????<wsdlsoap:operation?soapAction=""/>; ??????<wsdl:input?name="sayHelloToRequest">; ????????<wsdlsoap:body?encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"?namespace="http://192.168.0.1/test/services/sayHelloService/test/services/sayHelloService"?use="encoded"/>; ??????</wsdl:input>; ??????<wsdl:output?name="sayHelloToResponse">; ????????<wsdlsoap:body?encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"?namespace="http://192.168.0.1/test/services/sayHelloService/test/services/sayHelloService"?use="encoded"/>; ??????</wsdl:output>; ????</wsdl:operation>; ??</wsdl:binding>; ??<wsdl:service?name="sayHelloService">; ????<wsdl:port?binding="intf:sayHelloServiceSoapBinding"?name="sayHelloService">; ??????<wsdlsoap:address?location="http://192.168.0.1/test/services/sayHelloService"/>; ????</wsdl:port>; ??</wsdl:service>; </wsdl:definitions>;
我們甚至不用客戶端,就可以查看服務是否部署成功以及獲得返回結果 用Get方法獲得soap流,我們要用下面的url: (真正調用Web服務,用的是Post方法,這個后面會講)
http://192.168.0.1/test/services/sayHelloService?method=sayHelloTo&aname=everybody
瀏覽器顯示的是亂碼,我們點右鍵查看源文件,結果如下:
<p>;Got?response?message</p>; <?xml?version="1.0"?encoding="UTF-8"?>; <soapenv:Envelope?xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"?xmlns:xsd="http://www.w3.org/2001/XMLSchema"?xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">; ?<soapenv:Body>; ??<sayHelloToResponse?soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">; ???<sayHelloToReturn?xsi:type="xsd:string">;How?are?you,?everybody</sayHelloToReturn>; ??</sayHelloToResponse>; ?</soapenv:Body>; </soapenv:Envelope>;
這就是我們想要的結果嗎?這只是服務器端送回來的SOAP消息,不過我們想要的結果在里面。。。
為了真正調用我們的Web服務,下面給出一個Client:
import?org.apache.axis.client.Call; import?org.apache.axis.client.Service; import?javax.xml.namespace.QName;
??public?class?test ??{ ????public?static?void?main(String?[]?args) ????{ ??????try?{ ?????????????String?endpoint?=?"http://192.168.0.1/test/services/sayHelloService"; ?????????????Service??service?=?new?Service(); ?????????????Call?????call????=?(Call)?service.createCall(); ?????????????call.setTargetEndpointAddress(?new?java.net.URL(endpoint)?); ?????????????call.setOperationName(new?QName("http://sayHelloService",?"sayHelloTo")); ?????????????String?ret?=?(String)?call.invoke(?new?Object[]?{?args[0]?}?); ?????????????System.out.println(ret); ?????????}?catch?(Exception?e)?{ ?????????????e.printStackTrace(); ?????????} ????} ??}
??注意要配置好正確的classpath,確保編譯器能找的到axis.jar和jaxrpc.jar,否則編譯不會通過。 ??用下面的命令行運行這個class: ??java?test?everybody ??我們會得到:How?are?you,?everybody
??這才是我們真正想要的。。。
2.追根究底,我們的Web服務是怎樣跑起來的 ===============================================================================================
前面給出的兩個配置文件web.xml和server-config.wsdd,或許不是能一下子就看懂的。
先讓我們回顧一下servlet的映射模式。
我們知道,servlet是從javax.servlet.http.HttpServlet繼承的,在服務器端被載入JVM執行,然后向客戶端輸出html流。 servlet的web.xml文件(位于?webapps/foo/WEB-INF目錄):
<?xml?version="1.0"?encoding="UTF-8"?>; <!DOCTYPE?web-app?PUBLIC?"-//Sun?Microsystems,?Inc.//DTD?Web?Application?2.2//EN" ?????????????????????????"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">; <web-app>; <servlet-mapping>; <servlet-name>;invoker</servlet-name>; <url-pattern>;/servlet/*</url-pattern>; </servlet-mapping>; </web-app>;
invoker?servlet?其實是:org.apache.catalina.servlets.InvokerServlet 按類名提供小服務程序。例如,如果您調用?foo/servlet/HelloServlet, invoker?servlet將裝入該HelloServlet(如果它在其類路徑中的話)并執行。
初看上面的web.xml,好像只給出了一個servlet映射,而沒有定義invoker?servlet。 其實,invoker?servlet?是在tomcat的conf目錄中的web.xml中定義的:: ?<servlet>; ????????<servlet-name>;invoker</servlet-name>; ????????<servlet-class>; ??????????org.apache.catalina.servlets.InvokerServlet ????????</servlet-class>; ????????<init-param>; ????????????<param-name>;debug</param-name>; ????????????<param-value>;0</param-value>; ????????</init-param>; ????????<load-on-startup>;2</load-on-startup>; ????</servlet>;
所以,如果拋開Tomcat_HOME/conf/web.xml,我們這樣定義一個web.xml,似乎更能清楚的說明問題:
<?xml?version="1.0"?encoding="UTF-8"?>; <!DOCTYPE?web-app?PUBLIC?"-//Sun?Microsystems,?Inc.//DTD?Web?Application?2.2//EN" ?????????????????????????"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">; <web-app>; <servlet-name>;MyInvoker</servlet-name>; <servlet-class>; org.apache.catalina.servlets.InvokerServlet </servlet-class>; <init-param>; <param-name>;debug</param-name>; <param-value>;0</param-value>; </init-param>; <load-on-startup>;2</load-on-startup>; </servlet>;
<servlet-mapping>; <servlet-name>;MyInvoker</servlet-name>; <url-pattern>;/servlet/*</url-pattern>; </servlet-mapping>; </web-app>;
即所有/servlet/*?模式的url,都會交給org.apache.catalina.servlets.InvokerServlet來處理。 或者說,所有/servlet/*?模式的url,其實都是調用InvokerServlet這個類,而InvokerServlet本身也是 一個servlet,它也是從?HttpServlet?繼承而來的。
這樣,我們自己的servlet就能夠通過特定的url執行,即?/servlet/OurServlet。 當然,如果你高興,可以定義任何的?url?pattern,而不一定是?/servlet/*,這一點,正如我們后面 看到的Axis處理Soap消息的方法。
再進一步,如果不想讓?InvokerServlet?在中間“搗鬼”,我們當然可以直接定義自己的servlet:
<?xml?version="1.0"?encoding="UTF-8"?>; <!DOCTYPE?web-app?PUBLIC?"-//Sun?Microsystems,?Inc.//DTD?Web?Application?2.2//EN" ?????????????????????????"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">; <web-app>; <servlet-name>;MyInvoker2</servlet-name>; <servlet-class>; com.foo.MyServlet </servlet-class>; </servlet>;
<servlet-mapping>; <servlet-name>;MyInvoker2</servlet-name>; <url-pattern>;/AnyName/*</url-pattern>; </servlet-mapping>; </web-app>;
JSP也是一樣的道理,有了上面的分析, 看看Tomcat_HOME/conf/web.xml中的如下語句就可以JSP的處理方法了,這里就不再廢話了: .... <servlet>; ????????<servlet-name>;jsp</servlet-name>; ????????<servlet-class>;org.apache.jasper.servlet.JspServlet</servlet-class>; ????????<init-param>; ????????????<param-name>;logVerbosityLevel</param-name>; ????????????<param-value>;WARNING</param-value>; ????????</init-param>; ????????<load-on-startup>;3</load-on-startup>; </servlet>; <servlet-mapping>; ????<servlet-name>;jsp</servlet-name>; ????<url-pattern>;*.jsp</url-pattern>; </servlet-mapping>; ....
下面進入正題。
我們先來看部署Web?Service的web.xml:
<?xml?version="1.0"?encoding="ISO-8859-1"?>; <!DOCTYPE?web-app?PUBLIC?"-//Sun?Microsystems,?Inc.//DTD?Web?Application?2.2//EN"?"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">;
<web-app>;
<servlet>; ????<servlet-name>;Axis</servlet-name>; ????<!--實際servlet程序,這里是AxisServlet-->; ????<servlet-class>;org.apache.axis.transport.http.AxisServlet</servlet-class>; </servlet>;
<!--?###?定義servlet和url的對應關系-->;
<servlet-mapping>; ????<servlet-name>;Axis</servlet-name>; ????<url-pattern>;/services/*</url-pattern>; </servlet-mapping>;
</web-app>;
所有?/services/*?模式的?url?都會交給org.apache.axis.transport.http.AxisServlet處理, AxisServlet當然也是從HttpServlet繼承而來的。這就是為什么我們部署的Web服務在調用時都要在 服務名稱前加上?services/?了。
可以說,AxisServlet是所有Web服務調用的入口。 那么AxisServlet在接手Web服務調用后都做了哪些工作呢?
客戶端用call.invoke()調用web服務用的是POST,所以入口是AxisServlet.doPost... 而不是AxisServlet.doGet...
先來看看AxisServlet的doPost函數,這里只給出了關鍵語句及注釋:
????/** ?????*?Process?a?POST?to?the?servlet?by?handing?it?off?to?the?Axis?Engine. ?????*?Here?is?where?SOAP?messages?are?received ?????*?@param?req?posted?request ?????*?@param?res?respose ?????*?@throws?ServletException?trouble ?????*?@throws?IOException?different?trouble ?????*/ ?????public?void?doPost(HttpServletRequest?req,?HttpServletResponse?res) ????????throws?ServletException,?IOException ????{ msgContext?=?createMessageContext(engine,?req,?res);//獲取客戶請求信息
engine.invoke(msgContext);?//調用客戶端請求的服務
responseMsg?=?msgContext.getResponseMessage();//得到調用的返回結果
sendResponse(getProtocolVersion(req),?contentType,?res,?responseMsg);//將結果送至客戶端 ?????}
這樣一來,Web服務調用的來龍去脈就大致清楚了。。。
為了高清楚前面我們的三個url http://192.168.0.1/test/services http://192.168.0.1/test/services/sayHelloService?wsdl http://192.168.0.1/test/services/sayHelloService?method=sayHelloTo&aname=everybody 是怎樣獲得輸出結果的,再來看看AxisServlet的doGet函數,這里只給出了流程框架及注釋:
** *?Process?GET?requests.?Because?Axis?does?not?support?the?GET-style *?pseudo?execution?of?SOAP?methods,?this?handler?deals?with?queries *?of?various?kinds,?not?real?SOAP?actions. * *?@todo?for?secure?installations,?dont?stack?trace?on?faults *?@param?request?request?in *?@param?response?request?out *?@throws?ServletException *?@throws?IOException */ public?void?doGet(HttpServletRequest?req,?HttpServletResponse?res) ????????throws?ServletException,?IOException {
//如果路徑為空,比如:http://localhost/wstk/services?或?http://localhost/wstk/services/* if((pathInfo?==?null?||?pathInfo.equals(""))?&&?!realpath.endsWith(".jws"))
{ //從server-config.wsdd文件中讀取所有部署的服務信息,向向客戶端列出所有部署的服務, //包括每個服務可調用的方法。
}else //如果路徑不為空,比如:http://localhost/wstk/services/sayHelloService if(realpath?!=?null) { //如果請求wsdl,比如:http://localhost/wstk/services/sayHelloService?wsdl if(wsdlRequested) { //創建sayHelloService的WSDL文件并傳送至客戶端 }?else //這里是利用url調用Web服務的入口,比如http://192.168.0.1/test/services/sayHelloService?method=sayHelloTo&aname=everybody if(req.getParameterNames().hasMoreElements()) { //如果客戶端調用的方法正確,則Axis會調用相應的JavaBean,并把JavaBean的返回結果 //封裝為Soap消息流返回給客戶端。 } } }
而Axis怎樣找到我們所請求的JavaBean呢?答案是server-config.wsdd文件。
server-config.wsdd
<?xml?version="1.0"?encoding="UTF-8"?>; <deployment?xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"?xmlns="http://xml.apache.org/axis/wsdd/">;
<service?name="sayHelloService"?provider="java:RPC">; ??<parameter?name="className"?value="sayHello"/>; ??<parameter?name="allowedMethods"?value="sayHelloTo"/>; </service>;
<handler?type="java:org.apache.axis.handlers.http.URLMapper"?name="URLMapper"/>;
<transport?name="http">; ??<requestFlow>; ????<handler?type="URLMapper"/>; ??</requestFlow>; </transport>;
</deployment>;
WSDD是web?service?deployment?descriptor的縮寫。
最外面的<deployment>;元素指示這是WSDD,并定義了java的名字空間。
接著的?<service>;元素定義了service。一個service是一個目標鏈,包括請求request、內容提供者provider、響應response。 在這個例子中,我們指出service名字是sayHelloService?,provider是"java:RPC",它是axis?的標記,指示這是一個java的RPC?service, 而處理它的真正的class是org.apache.axis.providers.java.RPCProvider。
接著我們要在<parameter>;中告訴RPCProvider,它如何實例化并調用正確的class(如:com.foo.MyService)。 <parameter>;元素的className指示class名,allowedMethods告訴引擎那些共用的方法要通過soap來調用。 "*"表示所有的公共方法,我們也列出方法名字列表,可以空格或逗號分割它們。
|
|
|
|
|
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
---|
27 | 28 | 29 | 30 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|
導航
統計
- 隨筆: 32
- 文章: 427
- 評論: 144
- 引用: 0
常用鏈接
留言簿(5)
隨筆檔案
文章分類
文章檔案
java
工具
朋友
搜索
積分與排名
最新評論

閱讀排行榜
評論排行榜
|
|