基于Weblogic Server 8.1 ant工具開發Web Service
前言:本文不是專門講述Web Service技術的,讀者在閱讀本文之前需要具備一定的SOAP和Web Service知識基礎,同時對Weblogic Server的使用也應該熟悉。如果要自己動手實踐本文的例子,就需要安裝Weblogic Server 81,盡管本文是以weblogic server 81為測試環境,但是針對weblogic server 7下也是差不多的。本文只是起個拋磚引玉的作用,如果想深入研究Web Service的開發,還需要參考、學習相關的資料,包括Weblogic Service的相關文檔。
一、概述
在JBuilder中也支持開發基于weblogic的web service,不過實際上在JBuilder下開發web service也是基于ant任務來生成和構造web service的。但是,當初筆者在一個項目中使用JBuilder下自動生成構造ant腳本生成的web service時碰到了一個問題,通過JBuilder生成的web service,如果你的web service調用接口中存在一個或者多個String類型參數的時候,在生成的wsdl文件中對該接口的參數命名不會按照你的后端組件對應方法中參數的名字,而是以string、string0、string1…等形式命名的。而在那個項目中需要在Delphi環境中調用web service,問題就出現了,string在Delphi中是關鍵詞,產生了沖突,不能進行調用。于是筆者決定采用自編寫ant腳本的方式來生成和構造web service來解決前面所述Delphi調用的問題。
BEA Weblogic提供了一些Ant任務,用來幫助開發者生成、構造一個Web服務的重要部件,(例如:序列化類、客戶端jar支持庫、以及web-services.xml描述文件),并且把一個Weblogic Web 服務的所有部分打包成一個可部署的EAR文件。
BEA Weblogic所提供的Web服務Ant任務,支持從實現了Web Service接口的普通JAVA源文件和EJB jar生成Web Service部件,也支持從WSDL描述文件生成,同時支持基于http/https傳輸協議和JMS傳輸協議的Web Service。在這一節我們只講述通過基于一個普通JAVA類作為后端組件來實現的Web Service,傳輸協議使用http(基于https的方式將在后述關于Web Service安全的部分講述)。
二、使用Weblogic ant工具生成Web Service
我們先建立D:\wls_ws_demo的工作目錄,在此目錄下分別建立src、build、ddfiles、webapp、test目錄。具體用途后文會涉及到。
首先我們編寫一個實現了兩個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腳本文件內的屬性設置:<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"/>
上面的屬性應該不是很難理解,關鍵的是對于bea weblogic server安裝目錄和構造生成文件的路徑說明,其次是對classpath的設置,需要用到的兩個weblogic庫是weblogic.jar和webservices.jar。
接著我們看看我們在本節中使用的Weblogic提供的Ant任務:
1、source2wsdd
source2wsdd Ant任務最基本的功能是根據我們編寫的普通JAVA類源文件生成一個Web Service所必需的兩個部件:web-services.xml和.wsdl描述文件。
下面是針對上面HelloWorldWS.java對應的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的實現后端組件,這里是普通JAVA類com.wnetw.ws.demo HelloWorldWS.java。注意屬性里面是對源文件目錄路徑設置,而不是包路徑。
ddFile:生成的web service部署描述符文件web-services.xml的存放路徑。
wsdlFile:生成的.wsdl文件存放的路徑和名字。
serviceURI:客戶應用程序調用此Web服務的URL中的Web Service URI部分。注意:必須以“/”開頭。例如:/ HelloWorldWS 。同時這個URI屬性也會成為生成的web-services.xml 部署描述符文件中<web-service>元素的uri屬性。
例如:本機訪問本web service例子的url是http://localhost:7001/ WSDemo/ HelloWorldWS
上面的serviceURI屬性就指定了上述url中的/ HelloWorldWS這一部分。
2、clientgen
clientgen可以用來生成JAVA環境下客戶端應用調用一個Web Service客戶端jar支持庫。可以通過wsdl文件來生成,也可以通過一個包含web service實現的ear文件來生成。
下面是clientgen ant任務的腳本示例:<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任務生成的wsdl文件來生成客戶端jar支持庫。通過wsdl屬性指定。
3、war
這是ant提供的標準任務,這里與其他普通的war包有一點區別是,需要把web-services.xml文件打包到war中去。
說明:需要準備web.xml,后面對于安全設置的時候還需要weblogic.xml文件,這里先都打包進去,這些文件都需要提前編輯準備好:
---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>
這個文件沒設置,在后面關于安全的處理里面需要這里配置角色映射。
下面是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標準任務,需要注意的是必須提前編寫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應用的應用根。
例如:本機訪問本web service例子的url是http://localhost:7001/
WSDemo/ HelloWorldWS
上面的context-root元素就指定了上述url中的WSDemo這一部分。
下面是本文例子的ear ant任務腳本:
<target name="genear"> <ear destfile="${ear.file}" appxml="ddfiles/application.xml"> <fileset dir="${build.dir}/applications" includes="*.war"/> </ear> </target>
核心的ant任務說明完了,下面是完整的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>
運行ant生成Web Service:
打開命令行窗口,轉到工作目錄D:\wls_ws_demo下,在此目錄下先運行D:\bea\weblogic81\server\bin\setWLSEnv.cmd(此cmd文件具體路徑與你的weblogic platform81實際安裝目錄相關)進行環境設置,然后運行:D:\bea\weblogic81\server\bin\ant.bat -buildfile build_wls_all.xml。
運行結束,出現“BUILD SUCCESSFUL”,那就代表OK了。轉到工作目錄下的build目錄,你就會看到HelloWorldWS.ear這個文件。
三、測試Web Service
本節將講述對前一節里生成的Web Service HelloWorldWS進行測試。
啟動Weblogic Server,進入Weblogic Server控制臺,在Deployments->Applications下部署上節生成的HelloWorldWS.ear。
1、通過Weblogic自動生成的測試主頁測試
部署成功后,在瀏覽器中輸入http://localhost:7001/WSDemo/HelloWorldWS訪問Weblogic Server默認生成的上述HelloWorldWS Web Service的測試主頁。
如下圖:
圖上列出了HelloWorldWS Web Service上的兩個方法:welcome和sayHello。
點擊welcome連接進入wecome方法的測試頁,如下圖:
在上述頁面輸入“老Z”,提交后就會看到如下圖頁面:
測試的結果跟上節中的HelloWorldWS.java實現此方法的結果是一樣的。測試sayHello方法跟上面過程一樣。
在測試主頁中還能看到在JAVA環境下,基于clientgen ant任務生成的jar客戶端stub支持庫調用此HelloWorldWS Web服務的代碼示例。
2、使用JAVA程序調用Web Service
下面實際編寫一個java測試程序來調用上述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); } } }
編譯、運行上述測試程序的時候首先需要weblogic客戶端webservice支持庫webserviceclient.jar,還需要前面clientgen ant任務生成的jar客戶端stub支持庫HelloWorldWS_clients.jar。在下面的編譯、運行測試程序的ant腳本中可以看到在classpath中引入了上述兩個jar。
編譯、運行測試程序的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>運行上述ant腳本后,如果成功的話,應該得到類似下圖結果:

3、在VB下調用Web Service
下面我在VB環境下來調用下這個Web Service,筆者使用的是Visual Basic 6.0,要在VB下調用Web Service需要先安裝Microsoft SOAP toolkit。
新建一個VB工程,然后把Microsoft Soap Type Library引用進來,如下圖:
新建一個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調用失敗: " + 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
然后運行工程,點擊窗口上的按鈕就開始調用前面部署的Web Service(確保Weblogic Server在運行中),成功的話會得到如下圖的兩個MessageBox:
四、使用非內建數據類型
前面例子中的Web Service方法中使用的參數和返回值都是String,類似String,int等數據類型是屬于Weblogic web service所支持的內建類型,關于Weblogic web service所支持的內建數據類型請參見:http://e-docs.bea.com/wls/docs81/webserv/implement.html#1054236
所支持的XML非內建類型請參見:
http://e-docs.bea.com/wls/docs81/webserv/assemble.html#1060805
所支持的Java非內建數據類型請參見:
http://e-docs.bea.com/wls/docs81/webserv/assemble.html#1068595
WebLogic Server能夠對內建數據類型進行XML與Java表示之間的轉換。但是,如果你在web service操作中使用了非內建數據類型,那么你必須提供以下信息,以確保weblogic server能夠正確地進行轉換。
- 用于處理數據的Java表示與XML之間的轉換的序列化類;
- 包含了數據類型Java表示的Java類;
- 數據類型的XML Schema表示;
- web-services.xml部署描述文件中的數據類型映射信息。
Weblogic Server中帶有servicegen和autotype Atn任務,這兩個任務通過對web service的無狀態EJB或者Java類后端組件的內省,從而自動生成上述部件。上述Ant任務能夠處理許多非內建數據類型,所以大多數的開發者并不需要手工生成上述的部件。
有時,你可能也需要手工去創建非內建數據類型部件。因為你的數據類型可能很復雜,以致Ant任務不能正確生成前述部件。你也可能想要自己控制數據在XML和Java表示之間的轉換過程,而不依賴Weblogic Server所使用的缺省轉換程序。
本節將演示在Weblogic web service中如何處理非內建(自定義)的數據類型。
我們先編寫一個數值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,這是我們前面定義的數值Bean,由于這是非內建類型,而且也不屬于受支持的非內建類型,所以需要我們必須自己來處理XML和UserInfo Java表示數據類型之間的轉換。
在本文的例子中,我們使用Weblogic Server的autotype任務來做這件事情。我們先在build目錄建一個autotype目錄,然后在前文中ant完整腳本中的compile任務之后增加下述腳本:
<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任務有幾個常用屬性,下面簡要說明下:
javatypes:需要進行類型轉換的非內建(自定義)數據類型java類,注意取值是全限定類名,不需要帶上java或者class擴展名。如果存在多個這樣的數據類型類,用逗號“,”隔開;
targetNamespace:在對數據類型映射到XML的時候使用的命名空間;
packageName:生成的序列化相關類的封裝包;
destDir:生成的序列化相關類存放的目錄;
keepGenerated:是否保留中間java源文件,取值為:true或者false。
關于autotype任務的詳細信息請參考:
http://e-docs.bea.com/wls/docs81/webserv/anttasks.html#1080062
上述ant任務成功運行后就會生成build/autotype/目錄下生成types.xml文件以及按包封裝的數據轉換類的源文件和class文件。
由于增加了自定義數據類型,所以我們還得更新source2wsdd任務腳本,以下是增加了自定義數據類型處理后的source2wsdd任務腳本:
<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屬性來指定自定義數據類型的XML描述文件。
增加了對自定義數據類型支持后的完整腳本請參考本文代碼下載文件。
按照第一節所述方法運行ant腳本build_wls_all.xml后,再部署build\applications\目錄下的HelloWorldWS.ear。就可以按照以前說的方法進行測試了。
這一次在Weblogic Server自動生成的web service測試主頁:
http://localhost:7001/WSDemo/HelloWorldWS
可以發現多了一個叫getUserInfo的方法連接,進入此方法的調用測試頁面,調用此方法后就可以看到此web service方法的調用結果,以下是結果截圖:
從調用測試結果頁面可以看到,這一次的Return Value是:
com.wnetw.ws.demo.UserInfo@82d235
這正是我們的web service方法返回值類型類型的一個對象,圖中的下面也以SOAP消息的形式描述了調用的輸入和返回結果。
我們接著修改測試類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("開始測試自定義數據類型的返回值。。。"); UserInfo info = port.getUserInfo(100); System.out.println(info); System.out.println(info.getUsername()); }catch(Exception e){ e.printStackTrace(); System.out.println(e); } } }
看看以下代碼好像有點問題,UserInfo info = port.getUserInfo(123);我們在HelloWorldWS.java類中定義的對應方法是getUserInfo(Integer userid),參數是Integer的,但是上述測試類代碼中卻使用int類型,這是正確的。我們可以把clientgen任務中的keepGenerated屬性設為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參數的!如果你使用Integer類型參數,反而會編譯通不過!只能認為這是weblogic server ant任務對數據類型映射的具體實現了,如果你仔細看了本節前面所述對java內建數據類型的支持列表,那么也是好理解的,因為java數據類型到XML Schema數據類型映射中,java中的int和java.lang.Integer都映射到了int。所以web service服務端接收到的SOAP消息中只會是XML Schema int類型,無法區分客戶端使用的會是int或者java.lang.Integer,所以在ant工具根據wsdl文件自動生成客戶端支持類的時候就只能使用int了,沒法區分int或者java.lang.Integer。這是個有意思的問題^-^一不小心也許會在你工作中浪費不必要的時間。當然如果有必要,你完全可以手動修改、甚至完全自己來生成客戶端支持庫和數據類型轉換類。不過嘛,除了出于研究和特殊情況外這是沒有必要的。
我們接著看看HelloWorldWS_clients.jar中還有什么東西,發現有個language_builtins這樣的包,從包名也許你能猜到這是干什么的,是對java語言內建數據類型處理的包,此包下面是util包,里面有ListCodec.class類。看看我們的UserInfo類,里面使用了List類,這個包里面的類正是用來處理java.util.List數據類型的,java.util.List屬于Weblogic server web service所支持的非內建數據類型,也就是說不需要通過autotype明確來標志生成相關的數據轉換類和類型信息。但是,java.util.List又有別于int、java.lang.String等wls web service所支持的內建類型,對于java.util.List等受支持的非內建類型由ant任務自動生動相關數據類型處理信息,不需要手工干預。對比來看,int、java.lang.String等wls web service所支持的內建類型是直接映射,不需要數據類型轉換相關類。Java.util.List最終映射成了XML Shema SOAP Array類型。其他類型請參考:http://e-docs.bea.com/wls/docs81/webserv/assemble.html#1068595
運行修改后的build_wls_test.xml腳本,成功的話應該得到如下圖類似結果:
增加了自定義數據類型后,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包括三種不同概念的安全設置:
- 消息層安全:對SOAP消息中數據的數字簽名或者加密;
- 傳輸層安全:使用SSL來保證客戶應用與Web Service之間連接的安全性;
- 訪問控制:指定何種用戶、組、角色被允許訪問該Web Service。
在這里我們主要針對訪問控制概念上的安全處理。
Weblogic Web Service最終是作為一個標準的J2EE ear打包文件提供進行部署的,其中包含了一個war包,也就是說web service是以web應用的形式提供并部署的,這從前面的章節就可以看出。
所以,針對web service的訪問控制安全處理與J2EE中對于Web資源的訪問控制處理是一樣的。具體的說就是對特定Web資源增加安全約束。具體配置就是通過在Web應用部署描述符web.xml增加相應的元素:需要進行安全約束的資源集合、授權訪問的角色列表、對用戶數據的安全約束、角色映射等信息。
在這里,我們需要對前面用到的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> <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>
然后運行ant構造腳本,部署ear。部署成功后,你會在weblogic server運行命令行窗口中看到如下類似信息:
<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.>
這是因為沒有進行角色映射,所以直接使用角色名作為用戶名了。這只是一個警告信息,沒有關系。后面將會講述怎么進行角色映射。
然后進入weblogic server Console,新建一個名叫testrole的用戶。接著在左側目錄樹中一次展開Deployments-Applications- HelloWorldWS- WSDemo,在WSDemo節點上鼠標右擊,選擇Define Security Policy…
在Policy Condition項選擇User name of the caller,點擊增加,在接著出現的窗口中填入testrole,OK之后,點擊上圖頁面中的Apply。接下來就可以跟以前一樣測試了。
瀏覽器中輸入http://localhost:7001/WSDemo/HelloWorldWS,這個時候會彈出來一個登陸框,如下圖:
現在可以看到,訪問控制起作用了。輸入testrole以及擬增加用戶的時候指定的密碼后,就能進入到和以前一樣的測試主頁了。
上面那種使用角色名和用戶名對應的方式顯示在實際應用中是不方便的,因為具體會有什么樣的用戶會訪問此web service在構建時是不確定的。我們可以使用角色映射的方式來避免這個問題。
進行角色映射需要在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文件中指定的授權訪問角色testrole映射到了test_group,也就是說test_group組中的所有用戶都有權訪問。這樣一來用戶授權和實現就解耦了。
使用ant腳本重新構建,然后部署ear。接著進入weblogic server console,刪除testrole用戶,新建test_group組,新建一個叫test_user的用戶,并指派給test_group組。接著按照前面一樣Define Security Policy,這一次在Policy Condition部分選擇Caller is member of the group,然后點Add進入授權group指定頁面,輸入test_group,點增加-點OK,回到Define Security Policy主頁面,點擊Apply就好了。
然后我們在瀏覽中進入http://localhost:7001/WSDemo/HelloWorldWS,彈出登陸框,這一次我們可以使用test_group中的任何成員用戶來登陸了,前面例子是test_user。這樣在以后,需要分配新的用戶授權訪問此Web Service的時候就知需要在Cosole在test_group中增加一個成員就行了,不需要重新構建web service了。
加入了訪問控制后,在調用web service的時候就需要提供授權憑證了,下面是需要增加的代碼信息:
- 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();
//因為加入了訪問控制,所以對于http://localhost:7001/WSDemo/HelloWorldWS?WSDL的訪問也需授權,所以我們使用缺省構建器,這樣就會使用客戶端支持庫jar中的靜態wsdl文件了。
HelloWorldWSPort port = ws.getHelloWorldWSPort(“test_user”, “test_user”);
//后面的參數是test_user的密碼,根據你具體的密碼更改 - VB客戶端
Call soap.mssoapinit("HelloWorldWS.wsdl")
‘由于http://localhost:7001/WSDemo/HelloWorldWS?WSDL需要授權訪問,所以我們把腳本生成的HelloWorldWS.wsdl文件直接拷貝到VB項目目錄下,使用這個靜態文件來初始化soap對象。
‘后面增加下屬代碼
soap.ConnectorProperty("AuthUser") = "test_user"
soap.ConnectorProperty("AuthPassword") = "test_user"
在我們運行上述兩個測試程序的時候會發現調用不成功。原因接下來進行說明。
我們打開工作目錄中下build\wsddfiles這個目錄中的HelloWorldWS.wsdl這個文件,在最后可以看到下面的service元素內容,如下:
<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節點的location屬性有問題,因為客戶端soap初始化后,會使用這個URL來調用本wsdl中描述的web service操作,顯然這個地址與我們部署的實際地址是不一樣的。所以我們把location屬性改為我們部署的web service實際訪問URL:
http://localhost:7001/WSDemo/HelloWorldWS。這就是上述兩個測試程序不能正確運行的原因。
筆者也沒有找到如何在生成web service部件時設置此正確屬性的方法,正是因為需要修改上述wsdl文件屬性,所以我們需要把build腳本分成兩部分來執行,先生成相關部件,然后修改wsdl文件的上述屬性,最后才進行打包和客戶端支持庫的生成,把build_wls_all.xml分開成了build_wls_1.xml和build_wls_2.xml兩個build腳本文件。在運行完后build_wls_1.xml修改上述屬性,然后運行build_wls_2.xml即可。
部署成功后,就可以測試上面兩個調用例子了,注意把修改好的wsdl文件拷貝到VB項目目錄中去。
如果在web.xml中<security-constraint>元素里加入下述項目
<user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint>
那就會強制要求客戶端使用https進行訪問,其他更多信息請參考J2EE中Web應用安全方面的資料。
六、雜項設置
本節要說的實際也是安全性方面的問題,只不過和一般的安全性概念不一樣,這里講的是針對在生產部署環境下的考慮。
1、定制主頁
在生產環境下,一般是不允許公開web service默認主頁的。其次由于通過主頁:
http://localhost:7001/WSDemo/HelloWorldWS?WSDL
訪問的wsdl描述符文件是動態生成,同時加入了訪問控制安全約束后,客戶程序訪問此文件也存在問題,所以通常在生產環境下將禁止訪問web service默認主頁以及動態wsdl文件,可以使用專門的靜態web站點來提供必要的信息,以及通過靜態web站點來發布wsdl。
要禁用默認主頁以及wsdl文件,需要在web-services.xml描述符文件中進行設置。如下所示在web-service節點中加入下面兩個屬性:
exposeWSDL="False"
exposeHomePage="False"
修改后類似下面示例:
。。。 <web-services> <web-service name="HelloWorldWS" targetNamespace="http://tempuri.org/" uri="/HelloWorldWS" exposeWSDL="False" exposeHomePage="False"> 。。。
這個修改也需要在運行build_wls_1.xml之后進行修改,才能保證應用打包部署后使得此設置生效。
在禁止了默認主頁和WSDL文件后,為了保證web service更新后不需要更新客戶程序的文件,所以最好建立一個靜態web站點來發布web service,也就是發布wsdl文件。在用于發布wsdl的web應用中需要在web.xml中加入以下的Mime類型映射:
<mime-mapping> <extension>wsdl</extension> <mime-type>text/xml</mime-type> </mime-mapping>
2、啟用https協議
除了上一節中在web.xml中加入
<user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint>
來啟用https通訊協議外,還可以在通過在web-service.xml文件中,在web-service(注意不是web-services)節點中加入下面屬性:
protocol="https"
上述屬性能保證客戶端必須使用https來訪問本web service。
七、結束語
本文只是針對很小的一方面來講述基于weblogic ant任務開發web service的,只是起個拋磚引玉的作用。其次,通過本文你也能了解到web service的本質過程,無論通過什么工具來開發,本質上都是生成基礎部件,然后打包。如果需要全面了解weblogic server web service開發方面的知識請參考bea文檔:
http://e-docs.bea.com/wls/docs81/webservices.html
同時本文使用的環境是window 2000 server和weblogic platform8.1英文版。
本文示例項目代碼可從以下地址下載:
http://www.wnetw.com/jclub_resources/technology/attachfiles/wls_ws_demo.rar