基于Weblogic Server 8.1 ant工具開發(fā)Web Service
前言:本文不是專門講述Web Service技術(shù)的,讀者在閱讀本文之前需要具備一定的SOAP和Web Service知識基礎(chǔ),同時對Weblogic Server的使用也應(yīng)該熟悉。如果要自己動手實(shí)踐本文的例子,就需要安裝Weblogic Server 81,盡管本文是以weblogic server 81為測試環(huán)境,但是針對weblogic server 7下也是差不多的。本文只是起個拋磚引玉的作用,如果想深入研究Web Service的開發(fā),還需要參考、學(xué)習(xí)相關(guān)的資料,包括Weblogic Service的相關(guān)文檔。
一、概述
在JBuilder中也支持開發(fā)基于weblogic的web service,不過實(shí)際上在JBuilder下開發(fā)web service也是基于ant任務(wù)來生成和構(gòu)造web service的。但是,當(dāng)初筆者在一個項(xiàng)目中使用JBuilder下自動生成構(gòu)造ant腳本生成的web service時碰到了一個問題,通過JBuilder生成的web service,如果你的web service調(diào)用接口中存在一個或者多個String類型參數(shù)的時候,在生成的wsdl文件中對該接口的參數(shù)命名不會按照你的后端組件對應(yīng)方法中參數(shù)的名字,而是以string、string0、string1…等形式命名的。而在那個項(xiàng)目中需要在Delphi環(huán)境中調(diào)用web service,問題就出現(xiàn)了,string在Delphi中是關(guān)鍵詞,產(chǎn)生了沖突,不能進(jìn)行調(diào)用。于是筆者決定采用自編寫ant腳本的方式來生成和構(gòu)造web service來解決前面所述Delphi調(diào)用的問題。
BEA Weblogic提供了一些Ant任務(wù),用來幫助開發(fā)者生成、構(gòu)造一個Web服務(wù)的重要部件,(例如:序列化類、客戶端jar支持庫、以及web-services.xml描述文件),并且把一個Weblogic Web 服務(wù)的所有部分打包成一個可部署的EAR文件。
BEA Weblogic所提供的Web服務(wù)Ant任務(wù),支持從實(shí)現(xiàn)了Web Service接口的普通JAVA源文件和EJB jar生成Web Service部件,也支持從WSDL描述文件生成,同時支持基于http/https傳輸協(xié)議和JMS傳輸協(xié)議的Web Service。在這一節(jié)我們只講述通過基于一個普通JAVA類作為后端組件來實(shí)現(xiàn)的Web Service,傳輸協(xié)議使用http(基于https的方式將在后述關(guān)于Web Service安全的部分講述)。
二、使用Weblogic ant工具生成Web Service
我們先建立D:\wls_ws_demo的工作目錄,在此目錄下分別建立src、build、ddfiles、webapp、test目錄。具體用途后文會涉及到。
首先我們編寫一個實(shí)現(xiàn)了兩個Web Service接口的普通JAVA類:
package com.wnetw.ws.demo; public class HelloWorldWS{ public String sayHello(){ return "Hello World!"; } public String welcome(String name){ return "Hello " + name + ",Welcome to WebService!"; } }
上面兩個方法就不需要解釋了,很簡單。把此類按封裝包一致的路徑放置在src目錄下。
下面是本示例中ant腳本文件內(nèi)的屬性設(shè)置:<property name="build.compiler" value="modern"/> <property name="src.dir" value="src"/> <property name="build.dir" value="build"/> <property name="war.file" value="${build.dir}/ applications/HelloWorldWS.war" /> <property name="ear.file" value="${build.dir}/
applications/HelloWorldWS.ear" /> <property name="clients.lib" value="${build.dir}/
clientslib/HelloWorldWS_clients.jar"/> <property name="bea.home" value="D:/bea"/> <property name="wls.dir" value="${bea.home}/weblogic81/server"/> <property name="wlslib.dir" value="${wls.dir}/lib"/> <property name="wlsext.dir" value="${wls.dir}/ext"/> <property name="namespace" value="http://www.wnetw.com/demo/"/> <path id="classpath"> <dirset dir="${build.dir}/classes"> <include name="**"/> </dirset> <fileset dir="${wlslib.dir}"> <include name="**/weblogic.jar"/> <include name="**/webservices.jar"/> </fileset> </path> <property name="javac.fork" value="no"/> <property name="javac.debug" value="no"/> <property name="javac.optimize" value="on"/> <property name="javac.listfiles" value="yes"/> <property name="javac.failonerror" value="yes"/>
上面的屬性應(yīng)該不是很難理解,關(guān)鍵的是對于bea weblogic server安裝目錄和構(gòu)造生成文件的路徑說明,其次是對classpath的設(shè)置,需要用到的兩個weblogic庫是weblogic.jar和webservices.jar。
接著我們看看我們在本節(jié)中使用的Weblogic提供的Ant任務(wù):
1、source2wsdd
source2wsdd Ant任務(wù)最基本的功能是根據(jù)我們編寫的普通JAVA類源文件生成一個Web Service所必需的兩個部件:web-services.xml和.wsdl描述文件。
下面是針對上面HelloWorldWS.java對應(yīng)的Ant腳本:<target name="genwsdd"> <source2wsdd javaSource="${src.dir}/com/wnetw/ws/
demo/HelloWorldWS.java" ddFile="${build.dir}/wsddfiles/web-services.xml" wsdlFile="${build.dir}/wsddfiles/HelloWorldWS.wsdl" serviceURI="/HelloWorldWS"> <classpath refid="classpath"/> </source2wsdd> </target>
屬性說明
javaSource:指定web service的實(shí)現(xiàn)后端組件,這里是普通JAVA類com.wnetw.ws.demo HelloWorldWS.java。注意屬性里面是對源文件目錄路徑設(shè)置,而不是包路徑。
ddFile:生成的web service部署描述符文件web-services.xml的存放路徑。
wsdlFile:生成的.wsdl文件存放的路徑和名字。
serviceURI:客戶應(yīng)用程序調(diào)用此Web服務(wù)的URL中的Web Service URI部分。注意:必須以“/”開頭。例如:/ HelloWorldWS 。同時這個URI屬性也會成為生成的web-services.xml 部署描述符文件中<web-service>元素的uri屬性。
例如:本機(jī)訪問本web service例子的url是http://localhost:7001/ WSDemo/ HelloWorldWS
上面的serviceURI屬性就指定了上述url中的/ HelloWorldWS這一部分。
2、clientgen
clientgen可以用來生成JAVA環(huán)境下客戶端應(yīng)用調(diào)用一個Web Service客戶端jar支持庫。可以通過wsdl文件來生成,也可以通過一個包含web service實(shí)現(xiàn)的ear文件來生成。
下面是clientgen ant任務(wù)的腳本示例:<target name="genclient"> <clientgen wsdl="${build.dir}/wsddfiles/HelloWorldWS.wsdl" packageName="com.wnetw.ws.demo.client" clientJar="${clients.lib}" keepGenerated="false"> <classpath refid="classpath"/> </clientgen> </target>
這里采用從前面source2wsdd任務(wù)生成的wsdl文件來生成客戶端jar支持庫。通過wsdl屬性指定。
3、war
這是ant提供的標(biāo)準(zhǔn)任務(wù),這里與其他普通的war包有一點(diǎn)區(qū)別是,需要把web-services.xml文件打包到war中去。
說明:需要準(zhǔn)備web.xml,后面對于安全設(shè)置的時候還需要weblogic.xml文件,這里先都打包進(jìn)去,這些文件都需要提前編輯準(zhǔn)備好:
---Web.xml--- <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web
Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <mime-mapping> <extension>wsdl</extension> <mime-type>text/xml</mime-type> </mime-mapping> </web-app> ---weblogic.xml--- <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE weblogic-web-app PUBLIC "-//BEA Systems, Inc.//DTD Web
Application 7.0//EN" "http://www.bea.com/servers/wls700
/dtd/weblogic700-web-jar.dtd"> <weblogic-web-app> </weblogic-web-app>
這個文件沒設(shè)置,在后面關(guān)于安全的處理里面需要這里配置角色映射。
下面是war ant腳本示例:
<target name="genwar"> <war destfile="${war.file}" webxml="webapp/WEB-INF/web.xml"> <classes dir="${build.dir}/classes"/> <webinf dir="${build.dir}/wsddfiles"> <include name="web-services.xml"/> </webinf> <webinf dir="webapp/WEB-INF"> <include name="weblogic.xml"/> </webinf> </war> </target>
4、ear
這也是ant標(biāo)準(zhǔn)任務(wù),需要注意的是必須提前編寫application.xml文件,下面針對本文例子的application.xml文件:
<!DOCTYPE application PUBLIC '-//Sun Microsystems, Inc.//DTD J2EE
Application 1.3//EN' 'http://java.sun.com/dtd/application_1_3.dtd'> <application> <display-name></display-name> <module> <web> <web-uri>HelloWorldWS.war</web-uri> <context-root>WSDemo</context-root> </web> </module> </application>
說明:context-root元素指定此Web Service所在Web應(yīng)用的應(yīng)用根。
例如:本機(jī)訪問本web service例子的url是http://localhost:7001/
WSDemo/ HelloWorldWS
上面的context-root元素就指定了上述url中的WSDemo這一部分。
下面是本文例子的ear ant任務(wù)腳本:
<target name="genear"> <ear destfile="${ear.file}" appxml="ddfiles/application.xml"> <fileset dir="${build.dir}/applications" includes="*.war"/> </ear> </target>
核心的ant任務(wù)說明完了,下面是完整的ant腳本文件:
--- build_wls_all.xml--- <project name="wls_ws_demo" default="all" basedir="."> <property name="build.compiler" value="modern"/> <property name="src.dir" value="src"/> <property name="build.dir" value="build"/> <property name="war.file" value="${build.dir}/applications/
HelloWorldWS.war" /> <property name="ear.file" value="${build.dir}/applications/
HelloWorldWS.ear" /> <property name="clients.lib" value="${build.dir}/clientslib/
HelloWorldWS_clients.jar"/> <property name="bea.home" value="D:/bea"/> <property name="wls.dir" value="${bea.home}/weblogic81/server"/> <property name="wlslib.dir" value="${wls.dir}/lib"/> <property name="wlsext.dir" value="${wls.dir}/ext"/> <property name="namespace" value="http://www.wnetw.com/demo/"/> <path id="classpath"> <dirset dir="${build.dir}/classes"> <include name="**"/> </dirset> <fileset dir="${wlslib.dir}"> <include name="**/weblogic.jar"/> <include name="**/webservices.jar"/> </fileset> </path> <property name="javac.fork" value="no"/> <property name="javac.debug" value="no"/> <property name="javac.optimize" value="on"/> <property name="javac.listfiles" value="yes"/> <property name="javac.failonerror" value="yes"/> <target name="all" depends="clean,mdir,compile,genwsdd,
genclient,genwar,genear"/> <target name="clean"> <delete dir="${build.dir}"/> </target> <target name="mdir"> <mkdir dir="${build.dir}"/> <mkdir dir="${build.dir}/classes"/> <mkdir dir="${build.dir}/applications"/> <mkdir dir="${build.dir}/clientslib"/> <mkdir dir="${build.dir}/wsddfiles"/> </target> <target name="compile"> <javac encoding="GBK" srcdir="${src.dir}" destdir=
"${build.dir}/classes"> <classpath refid="classpath"/> </javac> </target> <target name="genwsdd"> <source2wsdd javaSource="${src.dir}/com/wnetw/ws/
demo/HelloWorldWS.java" ddFile="${build.dir}/wsddfiles/web-services.xml" wsdlFile="${build.dir}/wsddfiles/HelloWorldWS.wsdl" serviceURI="/HelloWorldWS"> <classpath refid="classpath"/> </source2wsdd> </target> <target name="genclient"> <clientgen wsdl="${build.dir}/wsddfiles/HelloWorldWS.wsdl" packageName="com.wnetw.ws.demo.client" clientJar="${clients.lib}" keepGenerated="false"> <classpath refid="classpath"/> </clientgen> </target> <target name="genwar"> <war destfile="${war.file}" webxml="webapp/WEB-INF/web.xml"> <classes dir="${build.dir}/classes"/> <webinf dir="${build.dir}/wsddfiles"> <include name="web-services.xml"/> </webinf> <webinf dir="webapp/WEB-INF"> <include name="weblogic.xml"/> </webinf> </war> </target> <target name="genear"> <ear destfile="${ear.file}" appxml="ddfiles/application.xml"> <fileset dir="${build.dir}/applications" includes="*.war"/> </ear> </target> </project>
運(yùn)行ant生成Web Service:
打開命令行窗口,轉(zhuǎn)到工作目錄D:\wls_ws_demo下,在此目錄下先運(yùn)行D:\bea\weblogic81\server\bin\setWLSEnv.cmd(此cmd文件具體路徑與你的weblogic platform81實(shí)際安裝目錄相關(guān))進(jìn)行環(huán)境設(shè)置,然后運(yùn)行:D:\bea\weblogic81\server\bin\ant.bat -buildfile build_wls_all.xml。
運(yùn)行結(jié)束,出現(xiàn)“BUILD SUCCESSFUL”,那就代表OK了。轉(zhuǎn)到工作目錄下的build目錄,你就會看到HelloWorldWS.ear這個文件。
三、測試Web Service
本節(jié)將講述對前一節(jié)里生成的Web Service HelloWorldWS進(jìn)行測試。
啟動Weblogic Server,進(jìn)入Weblogic Server控制臺,在Deployments->Applications下部署上節(jié)生成的HelloWorldWS.ear。
1、通過Weblogic自動生成的測試主頁測試
部署成功后,在瀏覽器中輸入http://localhost:7001/WSDemo/HelloWorldWS訪問Weblogic Server默認(rèn)生成的上述HelloWorldWS Web Service的測試主頁。
如下圖:
圖上列出了HelloWorldWS Web Service上的兩個方法:welcome和sayHello。
點(diǎn)擊welcome連接進(jìn)入wecome方法的測試頁,如下圖:
在上述頁面輸入“老Z”,提交后就會看到如下圖頁面:
測試的結(jié)果跟上節(jié)中的HelloWorldWS.java實(shí)現(xiàn)此方法的結(jié)果是一樣的。測試sayHello方法跟上面過程一樣。
在測試主頁中還能看到在JAVA環(huán)境下,基于clientgen ant任務(wù)生成的jar客戶端stub支持庫調(diào)用此HelloWorldWS Web服務(wù)的代碼示例。
2、使用JAVA程序調(diào)用Web Service
下面實(shí)際編寫一個java測試程序來調(diào)用上述Web Service。
--- HelloWorldWSTest.java --- import com.wnetw.was.demo.client.*; public class HelloWorldWSTest { public static void main(String[] args){ try{ HelloWorldWS_Impl ws = new HelloWorldWS_Impl("http://localhost:7001
/WSDemo/HelloWorldWS?WSDL"); HelloWorldWSPort port = ws.getHelloWorldWSPort(); System.out.println(port.welcome(“老Z”)); }catch(Exception e){ e.printStackTrace(); System.out.println(e); } } }
編譯、運(yùn)行上述測試程序的時候首先需要weblogic客戶端webservice支持庫webserviceclient.jar,還需要前面clientgen ant任務(wù)生成的jar客戶端stub支持庫HelloWorldWS_clients.jar。在下面的編譯、運(yùn)行測試程序的ant腳本中可以看到在classpath中引入了上述兩個jar。
編譯、運(yùn)行測試程序的ant腳本如下:
<project name="wls_ws_demo" default="all" basedir="."> <property name="build.compiler" value="modern"/> <property name="bea.home" value="D:/bea"/> <property name="wls.dir" value="${bea.home}/weblogic81/server"/> <property name="wlslib.dir" value="${wls.dir}/lib"/> <property name="wlsext.dir" value="${wls.dir}/ext"/> <path id="classpath"> <fileset dir="${wlslib.dir}"> <include name="**/webserviceclient.jar"/> </fileset> <fileset dir="build/clientslib"> <include name="**/HelloWorldWS_clients.jar"/> </fileset> <pathelement path="test"/> </path> <property name="javac.fork" value="no"/> <property name="javac.debug" value="no"/> <property name="javac.optimize" value="on"/> <property name="javac.listfiles" value="yes"/> <property name="javac.failonerror" value="yes"/> <target name="all" depends="compile,run"/> <target name="compile"> <javac encoding="GBK" srcdir="test" destdir="test"> <classpath refid="classpath"/> </javac> </target> <target name="run"> <java classname="HelloWorldWSTest"> <classpath refid="classpath"/> </java> </target> </project>運(yùn)行上述ant腳本后,如果成功的話,應(yīng)該得到類似下圖結(jié)果:

3、在VB下調(diào)用Web Service
下面我在VB環(huán)境下來調(diào)用下這個Web Service,筆者使用的是Visual Basic 6.0,要在VB下調(diào)用Web Service需要先安裝Microsoft SOAP toolkit。
新建一個VB工程,然后把Microsoft Soap Type Library引用進(jìn)來,如下圖:
新建一個form1,添加一個按鈕command1,在form1源代碼窗口中整個拷貝如下代碼:
Dim soap As MSSOAPLib.SoapClient Private Sub Command1_Click() MsgBox soap.sayHello() MsgBox soap.welcome("老Z") If Err <> 0 Then MsgBox "Web Service調(diào)用失敗: " + Err.Description End If End Sub Private Sub Form_Load() Set soap = New MSSOAPLib.SoapClient On Error Resume Next Call soap.mssoapinit("http://localhost:7001/WSDemo/HelloWorldWS?WSDL") If Err <> 0 Then MsgBox "初始化SOAP失敗: " + Err.Description End If End Sub
然后運(yùn)行工程,點(diǎn)擊窗口上的按鈕就開始調(diào)用前面部署的Web Service(確保Weblogic Server在運(yùn)行中),成功的話會得到如下圖的兩個MessageBox:
四、使用非內(nèi)建數(shù)據(jù)類型
前面例子中的Web Service方法中使用的參數(shù)和返回值都是String,類似String,int等數(shù)據(jù)類型是屬于Weblogic web service所支持的內(nèi)建類型,關(guān)于Weblogic web service所支持的內(nèi)建數(shù)據(jù)類型請參見:http://e-docs.bea.com/wls/docs81/webserv/implement.html#1054236
所支持的XML非內(nèi)建類型請參見:
http://e-docs.bea.com/wls/docs81/webserv/assemble.html#1060805
所支持的Java非內(nèi)建數(shù)據(jù)類型請參見:
http://e-docs.bea.com/wls/docs81/webserv/assemble.html#1068595
WebLogic Server能夠?qū)?nèi)建數(shù)據(jù)類型進(jìn)行XML與Java表示之間的轉(zhuǎn)換。但是,如果你在web service操作中使用了非內(nèi)建數(shù)據(jù)類型,那么你必須提供以下信息,以確保weblogic server能夠正確地進(jìn)行轉(zhuǎn)換。
- 用于處理數(shù)據(jù)的Java表示與XML之間的轉(zhuǎn)換的序列化類;
- 包含了數(shù)據(jù)類型Java表示的Java類;
- 數(shù)據(jù)類型的XML Schema表示;
- web-services.xml部署描述文件中的數(shù)據(jù)類型映射信息。
Weblogic Server中帶有servicegen和autotype Atn任務(wù),這兩個任務(wù)通過對web service的無狀態(tài)EJB或者Java類后端組件的內(nèi)省,從而自動生成上述部件。上述Ant任務(wù)能夠處理許多非內(nèi)建數(shù)據(jù)類型,所以大多數(shù)的開發(fā)者并不需要手工生成上述的部件。
有時,你可能也需要手工去創(chuàng)建非內(nèi)建數(shù)據(jù)類型部件。因?yàn)槟愕臄?shù)據(jù)類型可能很復(fù)雜,以致Ant任務(wù)不能正確生成前述部件。你也可能想要自己控制數(shù)據(jù)在XML和Java表示之間的轉(zhuǎn)換過程,而不依賴Weblogic Server所使用的缺省轉(zhuǎn)換程序。
本節(jié)將演示在Weblogic web service中如何處理非內(nèi)建(自定義)的數(shù)據(jù)類型。
我們先編寫一個數(shù)值Bean類UserInfo,如下:
package com.wnetw.ws.demo; import java.util.*; public class UserInfo{ private Integer userid; private String username; private String sex; private Date birthday; private int level; private double salary; private telcodes list; public UserInfo(){} public Integer getUserid(){ return userid; } public void setUserid(Integer userid){ this.userid = userid; } public String getUsername(){ return username; } public void setUsername(String username){ this.username = username; } public String getSex(){ return sex; } public void setSex(String sex){ this.sex = sex; } public Date getBirthday(){ return birthday; } public void setBirthday(Date birthday){ this.birthday = birthday; } public int getLevel(){ return level; } public void setLevel(int level){ this.level = level; } public double getSalary(){ return salary; } public void setSalary(double salary){ this.salary = salary; } public List getTelcodes(){ return telcodes; } public void setTelcodes (List telcodes){ this. telcodes = telcodes; } }
在前文中的后端組件類HelloWorldWS.java中增加一個方法:
public UserInfo getUserInfo(Integer userid){ UserInfo userinfo = new UserInfo(); userinfo.setUserid(userid); userinfo.setUsername("李澤林"); userinfo.setSex("男"); userinfo.setBirthday(new Date()); userinfo.setLevel(2); userinfo.setSalary(1000.51); List telcodes = new ArrayList(); telcodes.add("123"); telcodes.add("321"); userinfo.setTelcodes (telcodes); return userinfo; }
在這個方法里,返回值是UserInfo,這是我們前面定義的數(shù)值Bean,由于這是非內(nèi)建類型,而且也不屬于受支持的非內(nèi)建類型,所以需要我們必須自己來處理XML和UserInfo Java表示數(shù)據(jù)類型之間的轉(zhuǎn)換。
在本文的例子中,我們使用Weblogic Server的autotype任務(wù)來做這件事情。我們先在build目錄建一個autotype目錄,然后在前文中ant完整腳本中的compile任務(wù)之后增加下述腳本:
<target name="gentypeinfo"> <autotype javatypes="com.wnetw.ws.demo.UserInfo" targetNamespace="${namespace}" packageName="com.wnetw.ws.demo" destDir="${build.dir}/autotype" keepGenerated="true"> <classpath refid="classpath"/> </autotype> <copy todir="${build.dir}/classes"> <fileset dir="${build.dir}/autotype"> <include name="**/*.class"/> </fileset> </copy> </target>
autotype Ant任務(wù)有幾個常用屬性,下面簡要說明下:
javatypes:需要進(jìn)行類型轉(zhuǎn)換的非內(nèi)建(自定義)數(shù)據(jù)類型java類,注意取值是全限定類名,不需要帶上java或者class擴(kuò)展名。如果存在多個這樣的數(shù)據(jù)類型類,用逗號“,”隔開;
targetNamespace:在對數(shù)據(jù)類型映射到XML的時候使用的命名空間;
packageName:生成的序列化相關(guān)類的封裝包;
destDir:生成的序列化相關(guān)類存放的目錄;
keepGenerated:是否保留中間java源文件,取值為:true或者false。
關(guān)于autotype任務(wù)的詳細(xì)信息請參考:
http://e-docs.bea.com/wls/docs81/webserv/anttasks.html#1080062
上述ant任務(wù)成功運(yùn)行后就會生成build/autotype/目錄下生成types.xml文件以及按包封裝的數(shù)據(jù)轉(zhuǎn)換類的源文件和class文件。
由于增加了自定義數(shù)據(jù)類型,所以我們還得更新source2wsdd任務(wù)腳本,以下是增加了自定義數(shù)據(jù)類型處理后的source2wsdd任務(wù)腳本:
<target name="genwsdd"> <source2wsdd javaSource="${src.dir}/com/wnetw/ws/demo/HelloWorldWS.java" typesInfo="${build.dir}/autotype/types.xml" ddFile="${build.dir}/wsddfiles/web-services.xml" wsdlFile="${build.dir}/wsddfiles/HelloWorldWS.wsdl" serviceURI="/HelloWorldWS"> <classpath refid="classpath"/> </source2wsdd> </target>
跟以前的腳本相比,增加了typesInfo屬性來指定自定義數(shù)據(jù)類型的XML描述文件。
增加了對自定義數(shù)據(jù)類型支持后的完整腳本請參考本文代碼下載文件。
按照第一節(jié)所述方法運(yùn)行ant腳本build_wls_all.xml后,再部署build\applications\目錄下的HelloWorldWS.ear。就可以按照以前說的方法進(jìn)行測試了。
這一次在Weblogic Server自動生成的web service測試主頁:
http://localhost:7001/WSDemo/HelloWorldWS
可以發(fā)現(xiàn)多了一個叫g(shù)etUserInfo的方法連接,進(jìn)入此方法的調(diào)用測試頁面,調(diào)用此方法后就可以看到此web service方法的調(diào)用結(jié)果,以下是結(jié)果截圖:
從調(diào)用測試結(jié)果頁面可以看到,這一次的Return Value是:
com.wnetw.ws.demo.UserInfo@82d235
這正是我們的web service方法返回值類型類型的一個對象,圖中的下面也以SOAP消息的形式描述了調(diào)用的輸入和返回結(jié)果。
我們接著修改測試類HelloWorldWSTest.java,如以下:
import com.wnetw.ws.demo.client.*; import com.wnetw.ws.demo.UserInfo; public class HelloWorldWSTest { public static void main(String[] args){ try{ HelloWorldWS_Impl ws = new HelloWorldWS_Impl("http://localhost:7001
/WSDemo/HelloWorldWS?WSDL"); HelloWorldWSPort port = ws.getHelloWorldWSPort(); System.out.println(port.sayHello()); System.out.println(port.welcome("老Z")); System.out.println("開始測試自定義數(shù)據(jù)類型的返回值。。。"); UserInfo info = port.getUserInfo(100); System.out.println(info); System.out.println(info.getUsername()); }catch(Exception e){ e.printStackTrace(); System.out.println(e); } } }
看看以下代碼好像有點(diǎn)問題,UserInfo info = port.getUserInfo(123);我們在HelloWorldWS.java類中定義的對應(yīng)方法是getUserInfo(Integer userid),參數(shù)是Integer的,但是上述測試類代碼中卻使用int類型,這是正確的。我們可以把clientgen任務(wù)中的keepGenerated屬性設(shè)為true,把自動生成的java源代碼保留下來,build成功后,我們打開build\clientslib目錄下HelloWorldWS_clients.jar文件中的com.wnetw.ws.demo.client.HelloWorldWSPor.java源文件,可以看到如下代碼:
package com.wnetw.ws.demo.client; /** * Generated interface, do not edit. * * This stub interface was generated by weblogic * webservice stub gen on Sat Sep 17 16:11:21 CST 2005 */ public interface HelloWorldWSPort extends java.rmi.Remote{ /** * welcome */ public java.lang.String welcome(java.lang.String name) throws java.rmi.RemoteException ; /** * sayHello */ public java.lang.String sayHello() throws java.rmi.RemoteException ; /** * getUserInfo */ public com.wnetw.ws.demo.UserInfo getUserInfo(int userid) throws java.rmi.RemoteException ; }
其中的getUserInfo(int userid)方法是使用int參數(shù)的!如果你使用Integer類型參數(shù),反而會編譯通不過!只能認(rèn)為這是weblogic server ant任務(wù)對數(shù)據(jù)類型映射的具體實(shí)現(xiàn)了,如果你仔細(xì)看了本節(jié)前面所述對java內(nèi)建數(shù)據(jù)類型的支持列表,那么也是好理解的,因?yàn)閖ava數(shù)據(jù)類型到XML Schema數(shù)據(jù)類型映射中,java中的int和java.lang.Integer都映射到了int。所以web service服務(wù)端接收到的SOAP消息中只會是XML Schema int類型,無法區(qū)分客戶端使用的會是int或者java.lang.Integer,所以在ant工具根據(jù)wsdl文件自動生成客戶端支持類的時候就只能使用int了,沒法區(qū)分int或者java.lang.Integer。這是個有意思的問題^-^一不小心也許會在你工作中浪費(fèi)不必要的時間。當(dāng)然如果有必要,你完全可以手動修改、甚至完全自己來生成客戶端支持庫和數(shù)據(jù)類型轉(zhuǎn)換類。不過嘛,除了出于研究和特殊情況外這是沒有必要的。
我們接著看看HelloWorldWS_clients.jar中還有什么東西,發(fā)現(xiàn)有個language_builtins這樣的包,從包名也許你能猜到這是干什么的,是對java語言內(nèi)建數(shù)據(jù)類型處理的包,此包下面是util包,里面有ListCodec.class類。看看我們的UserInfo類,里面使用了List類,這個包里面的類正是用來處理java.util.List數(shù)據(jù)類型的,java.util.List屬于Weblogic server web service所支持的非內(nèi)建數(shù)據(jù)類型,也就是說不需要通過autotype明確來標(biāo)志生成相關(guān)的數(shù)據(jù)轉(zhuǎn)換類和類型信息。但是,java.util.List又有別于int、java.lang.String等wls web service所支持的內(nèi)建類型,對于java.util.List等受支持的非內(nèi)建類型由ant任務(wù)自動生動相關(guān)數(shù)據(jù)類型處理信息,不需要手工干預(yù)。對比來看,int、java.lang.String等wls web service所支持的內(nèi)建類型是直接映射,不需要數(shù)據(jù)類型轉(zhuǎn)換相關(guān)類。Java.util.List最終映射成了XML Shema SOAP Array類型。其他類型請參考:http://e-docs.bea.com/wls/docs81/webserv/assemble.html#1068595
運(yùn)行修改后的build_wls_test.xml腳本,成功的話應(yīng)該得到如下圖類似結(jié)果:
增加了自定義數(shù)據(jù)類型后,VB測試客戶端的處理也得增加一些處理來測試返回值為UserInfo的web service方法,如下面代碼:
Set Nodes = soap.getUserInfo(100)
MsgBox Nodes(0).nodeName + ":" + Nodes(0).Text
MsgBox Nodes(1).nodeName + ":" + Nodes(1).Text
MsgBox Nodes(2).nodeName + ":" + Nodes(2).xml
MsgBox Nodes(3).nodeName + ":" + Nodes(3).Text
MsgBox Nodes(4).nodeName + ":" + Nodes(4).Text
MsgBox Nodes(5).nodeName + ":" + Nodes(5).Text
MsgBox Nodes(6).nodeName
完整VB測試客戶端代碼請見本文附帶下載代碼。
五、配置Web Service安全
Weblogic Web Service包括三種不同概念的安全設(shè)置:
- 消息層安全:對SOAP消息中數(shù)據(jù)的數(shù)字簽名或者加密;
- 傳輸層安全:使用SSL來保證客戶應(yīng)用與Web Service之間連接的安全性;
- 訪問控制:指定何種用戶、組、角色被允許訪問該Web Service。
在這里我們主要針對訪問控制概念上的安全處理。
Weblogic Web Service最終是作為一個標(biāo)準(zhǔn)的J2EE ear打包文件提供進(jìn)行部署的,其中包含了一個war包,也就是說web service是以web應(yīng)用的形式提供并部署的,這從前面的章節(jié)就可以看出。
所以,針對web service的訪問控制安全處理與J2EE中對于Web資源的訪問控制處理是一樣的。具體的說就是對特定Web資源增加安全約束。具體配置就是通過在Web應(yīng)用部署描述符web.xml增加相應(yīng)的元素:需要進(jìn)行安全約束的資源集合、授權(quán)訪問的角色列表、對用戶數(shù)據(jù)的安全約束、角色映射等信息。
在這里,我們需要對前面用到的web.xml文件進(jìn)行修改,如下所示:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web
Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <mime-mapping> <extension>wsdl</extension> <mime-type>text/xml</mime-type> </mime-mapping> <security-constraint> <display-name>SecurityConstraint</display-name> <web-resource-collection> <web-resource-name>HelloWorldWS</web-resource-name> <url-pattern>/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>testrole</role-name> </auth-constraint> </security-constraint> <security-role> <role-name>testrole</role-name> </security-role> </web-app>
然后運(yùn)行ant構(gòu)造腳本,部署ear。部署成功后,你會在weblogic server運(yùn)行命令行窗口中看到如下類似信息:
<2005-9-24 下午22時03分45秒 CST> <Warning> <HTTP> <BEA-101304>
<Webapp: ServletC ontext(id=11680063,name=WSDemo,context-path=/WSDemo),
the role: testrole defined in web.xml has not been mapped to principals in
security-role-assignment in web logic.xml. Will use the rolename itself as the principal-name.>
這是因?yàn)闆]有進(jìn)行角色映射,所以直接使用角色名作為用戶名了。這只是一個警告信息,沒有關(guān)系。后面將會講述怎么進(jìn)行角色映射。
然后進(jìn)入weblogic server Console,新建一個名叫testrole的用戶。接著在左側(cè)目錄樹中一次展開Deployments-Applications- HelloWorldWS- WSDemo,在WSDemo節(jié)點(diǎn)上鼠標(biāo)右擊,選擇Define Security Policy…
在Policy Condition項(xiàng)選擇User name of the caller,點(diǎn)擊增加,在接著出現(xiàn)的窗口中填入testrole,OK之后,點(diǎn)擊上圖頁面中的Apply。接下來就可以跟以前一樣測試了。
瀏覽器中輸入http://localhost:7001/WSDemo/HelloWorldWS,這個時候會彈出來一個登陸框,如下圖:
現(xiàn)在可以看到,訪問控制起作用了。輸入testrole以及擬增加用戶的時候指定的密碼后,就能進(jìn)入到和以前一樣的測試主頁了。
上面那種使用角色名和用戶名對應(yīng)的方式顯示在實(shí)際應(yīng)用中是不方便的,因?yàn)榫唧w會有什么樣的用戶會訪問此web service在構(gòu)建時是不確定的。我們可以使用角色映射的方式來避免這個問題。
進(jìn)行角色映射需要在weblogic.xml文件中配置,下面我將對testrole映射到一個group,weblogic.xml文件如下所示:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE weblogic-web-app PUBLIC "-//BEA Systems, Inc.//DTD Web
Application 7.0//EN" "http://www.bea.com/servers/wls700/dtd
/weblogic700-web-jar.dtd"> <weblogic-web-app> <security-role-assignment> <role-name>testrole</role-name> <principal-name>test_group</principal-name> </security-role-assignment> </weblogic-web-app>
在web.xml文件中指定的授權(quán)訪問角色testrole映射到了test_group,也就是說test_group組中的所有用戶都有權(quán)訪問。這樣一來用戶授權(quán)和實(shí)現(xiàn)就解耦了。
使用ant腳本重新構(gòu)建,然后部署ear。接著進(jìn)入weblogic server console,刪除testrole用戶,新建test_group組,新建一個叫test_user的用戶,并指派給test_group組。接著按照前面一樣Define Security Policy,這一次在Policy Condition部分選擇Caller is member of the group,然后點(diǎn)Add進(jìn)入授權(quán)group指定頁面,輸入test_group,點(diǎn)增加-點(diǎn)OK,回到Define Security Policy主頁面,點(diǎn)擊Apply就好了。
然后我們在瀏覽中進(jìn)入http://localhost:7001/WSDemo/HelloWorldWS,彈出登陸框,這一次我們可以使用test_group中的任何成員用戶來登陸了,前面例子是test_user。這樣在以后,需要分配新的用戶授權(quán)訪問此Web Service的時候就知需要在Cosole在test_group中增加一個成員就行了,不需要重新構(gòu)建web service了。
加入了訪問控制后,在調(diào)用web service的時候就需要提供授權(quán)憑證了,下面是需要增加的代碼信息:
- JAVA客戶
HelloWorldWS_Impl ws = new HelloWorldWS_Impl("http://localhost:7001/WSDemo/HelloWorldWS?WSDL");
HelloWorldWSPort port = ws.getHelloWorldWSPort("test_user","test_user");
改成
HelloWorldWS_Impl ws = new HelloWorldWS_Impl();
//因?yàn)榧尤肓嗽L問控制,所以對于http://localhost:7001/WSDemo/HelloWorldWS?WSDL的訪問也需授權(quán),所以我們使用缺省構(gòu)建器,這樣就會使用客戶端支持庫jar中的靜態(tài)wsdl文件了。
HelloWorldWSPort port = ws.getHelloWorldWSPort(“test_user”, “test_user”);
//后面的參數(shù)是test_user的密碼,根據(jù)你具體的密碼更改 - VB客戶端
Call soap.mssoapinit("HelloWorldWS.wsdl")
‘由于http://localhost:7001/WSDemo/HelloWorldWS?WSDL需要授權(quán)訪問,所以我們把腳本生成的HelloWorldWS.wsdl文件直接拷貝到VB項(xiàng)目目錄下,使用這個靜態(tài)文件來初始化soap對象。
‘后面增加下屬代碼
soap.ConnectorProperty("AuthUser") = "test_user"
soap.ConnectorProperty("AuthPassword") = "test_user"
在我們運(yùn)行上述兩個測試程序的時候會發(fā)現(xiàn)調(diào)用不成功。原因接下來進(jìn)行說明。
我們打開工作目錄中下build\wsddfiles這個目錄中的HelloWorldWS.wsdl這個文件,在最后可以看到下面的service元素內(nèi)容,如下:
<service name="HelloWorldWS"> <port name="HelloWorldWSPort" binding="tns:HelloWorldWSPort"> <soap:address location="http://pls.set.the.end.point.address/"> </soap:address> </port> </service>
問題就出在這里,soap:address節(jié)點(diǎn)的location屬性有問題,因?yàn)榭蛻舳藄oap初始化后,會使用這個URL來調(diào)用本wsdl中描述的web service操作,顯然這個地址與我們部署的實(shí)際地址是不一樣的。所以我們把location屬性改為我們部署的web service實(shí)際訪問URL:
http://localhost:7001/WSDemo/HelloWorldWS。這就是上述兩個測試程序不能正確運(yùn)行的原因。
筆者也沒有找到如何在生成web service部件時設(shè)置此正確屬性的方法,正是因?yàn)樾枰薷纳鲜鰓sdl文件屬性,所以我們需要把build腳本分成兩部分來執(zhí)行,先生成相關(guān)部件,然后修改wsdl文件的上述屬性,最后才進(jìn)行打包和客戶端支持庫的生成,把build_wls_all.xml分開成了build_wls_1.xml和build_wls_2.xml兩個build腳本文件。在運(yùn)行完后build_wls_1.xml修改上述屬性,然后運(yùn)行build_wls_2.xml即可。
部署成功后,就可以測試上面兩個調(diào)用例子了,注意把修改好的wsdl文件拷貝到VB項(xiàng)目目錄中去。
如果在web.xml中<security-constraint>元素里加入下述項(xiàng)目
<user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint>
那就會強(qiáng)制要求客戶端使用https進(jìn)行訪問,其他更多信息請參考J2EE中Web應(yīng)用安全方面的資料。
六、雜項(xiàng)設(shè)置
本節(jié)要說的實(shí)際也是安全性方面的問題,只不過和一般的安全性概念不一樣,這里講的是針對在生產(chǎn)部署環(huán)境下的考慮。
1、定制主頁
在生產(chǎn)環(huán)境下,一般是不允許公開web service默認(rèn)主頁的。其次由于通過主頁:
http://localhost:7001/WSDemo/HelloWorldWS?WSDL
訪問的wsdl描述符文件是動態(tài)生成,同時加入了訪問控制安全約束后,客戶程序訪問此文件也存在問題,所以通常在生產(chǎn)環(huán)境下將禁止訪問web service默認(rèn)主頁以及動態(tài)wsdl文件,可以使用專門的靜態(tài)web站點(diǎn)來提供必要的信息,以及通過靜態(tài)web站點(diǎn)來發(fā)布wsdl。
要禁用默認(rèn)主頁以及wsdl文件,需要在web-services.xml描述符文件中進(jìn)行設(shè)置。如下所示在web-service節(jié)點(diǎn)中加入下面兩個屬性:
exposeWSDL="False"
exposeHomePage="False"
修改后類似下面示例:
。。。 <web-services> <web-service name="HelloWorldWS" targetNamespace="http://tempuri.org/" uri="/HelloWorldWS" exposeWSDL="False" exposeHomePage="False"> 。。。
這個修改也需要在運(yùn)行build_wls_1.xml之后進(jìn)行修改,才能保證應(yīng)用打包部署后使得此設(shè)置生效。
在禁止了默認(rèn)主頁和WSDL文件后,為了保證web service更新后不需要更新客戶程序的文件,所以最好建立一個靜態(tài)web站點(diǎn)來發(fā)布web service,也就是發(fā)布wsdl文件。在用于發(fā)布wsdl的web應(yīng)用中需要在web.xml中加入以下的Mime類型映射:
<mime-mapping> <extension>wsdl</extension> <mime-type>text/xml</mime-type> </mime-mapping>
2、啟用https協(xié)議
除了上一節(jié)中在web.xml中加入
<user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint>
來啟用https通訊協(xié)議外,還可以在通過在web-service.xml文件中,在web-service(注意不是web-services)節(jié)點(diǎn)中加入下面屬性:
protocol="https"
上述屬性能保證客戶端必須使用https來訪問本web service。
七、結(jié)束語
本文只是針對很小的一方面來講述基于weblogic ant任務(wù)開發(fā)web service的,只是起個拋磚引玉的作用。其次,通過本文你也能了解到web service的本質(zhì)過程,無論通過什么工具來開發(fā),本質(zhì)上都是生成基礎(chǔ)部件,然后打包。如果需要全面了解weblogic server web service開發(fā)方面的知識請參考bea文檔:
http://e-docs.bea.com/wls/docs81/webservices.html
同時本文使用的環(huán)境是window 2000 server和weblogic platform8.1英文版。
本文示例項(xiàng)目代碼可從以下地址下載:
http://www.wnetw.com/jclub_resources/technology/attachfiles/wls_ws_demo.rar
posted on 2007-02-25 10:30 風(fēng) 閱讀(2801) 評論(4) 編輯 收藏 所屬分類: 收藏