程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 在Apache Geronimo應用程序中實現和部署Web服務

在Apache Geronimo應用程序中實現和部署Web服務

編輯:關於JAVA

現在,無論您的企業應用程序是部署在小型家庭商店中,還是跨多個領域的企 業基礎設施的一部分,無論應用程序是否與他人協作,它都必須支持 Web 服務。

途徑

現代企業往往是非常復雜的系統,每天要面對許多機會和挑 戰。許多機會涉及到與其他系統和企業進行集成或協作。因此,明智的企業應用 程序開發人員要確保別的應用程序可以輕松地訪問自己的應用程序。Web 服務就 是提供這種可訪問性的好方式。

Apache Geronimo 應用服務器支持 Web 服務以及最新 Java™ 2 Platform, Enterprise Edition(J2EE)規范的其 他特性。為了演示 Geronimo 應用服務器中的 Web 服務支持,IBM Advanced Technology Solutions 團隊已經決定改進一個軟件模擬程序,這個程序模擬了簡 單的銀行場景,是為以前的一篇 developerWorks 文章 “使用 Geronimo 構建安全的企業基礎設施”(developerWorks,2005 年 7 月)開發的。

本文使用的業務場景實現一個資金轉帳用例。零售銀行客戶(用戶)可以訪問 這個應用程序,將資金從自己的帳戶轉出來,審計員可以監督所有銀行事務。現 有的資金轉帳應用程序得到了改進,允許 Web 服務使用 Simple Object Access Protocol/Hypertext Transfer Protocol(SOAP/HTTP)和簡單的 Web 服務客戶 機來提供後端功能。

什麼是 Web 服務?

Web 服務是一段應用程序 業務邏輯,可以使用普遍存在的 Web 協議和數據格式(比如 HTTP 和 SOAP)來 執行。自從 J2EE 1.4 發布以來,Web 服務已經融入了 J2EE 中。在 J2EE 上下 文中,Web 服務被認為是後端實現的外觀(facade) —— 後端實現 可能是 Enterprise JavaBean(EJB)或 servlet。下面的工件組成了 Web 服務 :

一個 Web Service Definition Language(Web 服務定義語言,WSDL)文檔, 描述服務接口和端口。

一個服務端點接口,它由服務器部分實現,用於在客戶機上調用服務方法。來 自 WSDL 的綁定映射到這個端點接口,WSDL 綁定是 WSDL 的一部分,定義了 Web 服務的協議。

Java API for XML-based RPC(JAX-RPC)描述符,包含從 XML 到 Java 技術 的不同映射,比如將來自 WSDL 文檔的 XML Schema Definition(XSD)類型映射 到 Java 類型,以及將 XML 元素映射到端點接口方法參數。

服務實現 —— EJB 或 servlet。

Web 服務部署描述符 webservices.xml。

以下幾節開發這些工件,為 Geronimo 構建一個支持 Web 服務的應用程序。

創建 Web 服務

首先,開發 Web 服務本身。為此,必須在同一個文件夾中創建以下工件。( 稍後給出每個步驟的細節。)

生成或開發 WSDL。在這個例子中,使用來自 Sun J2EE 參考實現的 wscompile 工具。

開發端點接口。它是基於 EJB 遠程接口開發的。

用 JAX-RPC 映射將 WSDL 類型映射為 Java 類型。JAX-RPC 映射是使用 wscompile 工具生成的。

實現端點接口。

在部署描述符中配置 Web 服務引用。這包括在 web.xml 部署描述符中添加一 節。

下面詳細描述每個步驟。

第一個步驟是在 WSDL 文件中描述 Web 服務。在這個例子中,因為 Web 服務 提供的功能與業務邏輯 EJB 相同,所以可以從 EJB 接口生成 WSDL,然後進行手 工編輯。[注意,到編寫本文時,Geronimo 對於構建 J2EE Web 服務還沒有完善 的工具支持(Axis 特定的 Web 服務例外,這種 Web 服務可以使用 Axis 工具生 成]。

要從端點接口生成 WSDL,必須先開發一個接口。這個接口聲明 Web 服務實現 的方法。JAX-RPC 規范對這個接口有一些限制。簡單地說,這個接口應該擴展 java.rmi.Remote —— 所有方法都聲明為拋出 java.rmi.RemoteException。最 後,參數和返回值是 JAX-RPC 兼容的(在我們的例子中,它們是具有默認構造函 數的 JavaBean 和原始類型)。簡單地說,在示例應用程序中,這個接口是 EJB 的遠程接口,但是用 java.rmi.Remote 替換了父接口 javax.ejb.EJBObject,並 刪除了調用者名稱參數。

在實現 Web 服務的端點接口之後,就可以生成 WSDL了。下一節描述這個過程 。

從端點接口生成 WSDL

當前,Geronimo 應用服務器沒有提供生成 WSDL 文檔的標准工具集。但是, Sun 站點上的 J2EE 1.4 工具可以滿足我們的需要。用於生成各種 Web 服務相關 工件的工具稱為 wscompile,位於 Sun J2EE 發行版的 bin 子目錄中。

要生成 WSDL 文檔,應該為 wscompile 創建一個配置文件。清單 1 顯示了這 個配置文件:

清單 1. wscompile 工具的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://java.sun.com/xml/ns/jax- rpc/ri/config">
  <service name="BusinessLogic"
       targetNamespace="http://ibm.com/samples/workdev/v1/wsdl"
       typeNamespace="http://ibm.com/samples/workdev/v1/types"
       packageName="com.ibm.workdev.v1.interfaces">
   <interface  name="com.ibm.workdev.v1.interfaces.BusinessLogicEndpoint"/>
  </service>
</configuration>

可以看出這個文件的結構很簡單。它包含名稱空間、包和服務端點接口引用的 聲明。清單 2 顯示了 wscompile 的調用字符串:

清單 2. 調用 wscompile

%SUN_J2EE_HOME%\bin\wscompile -classpath  <APP_CLASSES>;%GERONIMO_HOME%\repository\geronimo-
spec\jars\geronimo-spec-j2ee-1.4-rc4.jar -gen:server - f:documentliteral config.xml

清單 2 中的 SUN_J2EE_HOME 是 Sun J2EE SDK 的主目錄。APP_CLASSES 是指 定應用程序類(包括端點接口類)的目錄。GERONIMO_HOME 是 Geronimo 的主目 錄。注意,這個工具不只生成 WSDL,還生成幾個支持類。

下一步是更新生成的 WSDL,使元素的名稱在業務上下文中更容易理解。 wscompile 沒有保留方法參數名,而是將它們轉換為 long_2(參數類型加參數位 置)這樣的名稱,這將 for? 聲明為方法的第二個參數,類型為 long。例如, performTransfer 操作的第二個參數可以重新命名為 amount。

接下來,確保所有消息都正好有一個部分。對於沒有返回值的方法, wscompile 生成沒有部分的消息。這個錯誤需要糾正。為了糾正這個錯誤,必須 聲明一個包含空序列的 XML 元素,並用這個元素將一個部分添加到消息中。清單 3 演示了如何聲明這樣的 XML 元素:

清單 3. XSD 響應類型

<element name="performTransferResponse">
  <complexType>
   <sequence/>
  </complexType>
</element>

下面是來自消息聲明的片段:

清單 4. WSDL 消息

<message  name="BusinessLogicEndpoint_performTransferResponse">
   <part name="result" element="ns2:performTransferResponse"/>
</message>

另外一種辦法是從頭開發 WSDL 文檔。注意,Eclipse WTP 項目包含一個方便 的 WSDL 文檔編輯器。

創建 JAX-RPC 映射

在生成 WSDL 之後,需要為方法參數創建 JAX-RPC 映射文件和值類,然後再 創建返回值。它們可以用前面提到的 wscompile 工具生成。

這種情況的 wscompile 配置見下面的 清單 5:

清單 5. wscompile 的配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://java.sun.com/xml/ns/jax- rpc/ri/config">
<wsdl
   location="file:///BusinessLogic.wsdl"
   packageName="com.ibm.workdev.v1.interfaces"/>
</configuration>

調用行如下:

清單 6. 調用 wscompile

%SUN_J2EE_HOME%\bin\wscompile -s src-gen -keep - mapping
mapping.xml -classpath <SAMECLASSPATH> -gen:client - f:documentliteral
config.xml

這個工具的類路徑與生成 WSDL 時一樣。src-gen 是應該放置 JAX-RPC bean 源類的目錄。注意,需要在生成 WSDL 之前創建這個目錄。最好將它與應用程序 源目錄分開,因為可能已經存在一些值類,wscompile 會覆蓋它們。

並不是生成的所有文件都是必要的 —— 只需要缺失的 bean 的源代碼。例如 ,在這個應用程序中,不需要 Account JavaBean 的源代碼,因為它已經存在了 。針對不同序列化器的其他源代碼也是不需要的,因為它們是 Sun J2EE 實現所 特有的。

wscompile 生成的 JAX-RPC 需要一些後期手工處理。首先,所有 wsdl- message-part-name 元素的值為 parameters。必須將它們重新命名以匹配來自部 分元素序列的對應元素名(封裝的文檔/字面格式的每個部分有具有復雜類型的元 素,等於元素序列)。

例如,清單 7 顯示 XSD 類型:

清單 7. 顯示 XSD 類型的 WSDL 片段

<complexType name="registerUser">
  <sequence>
   <element name="username" type="string" nillable="true"/>
   <element name="password" type="string" nillable="true"/>
  </sequence>
</complexType>

清單 8 顯示消息部分中方法參數的映射:

清單 8. 方法參數的 JAX-RPC 映射

<method-param-parts-mapping>
  <param-position>0</param-position>
  <param-type>java.lang.String</param-type>
  <wsdl-message-mapping>
   <wsdl-message  xmlns:wsdlMsgNS="http://ibm.com/samples/workdev/v1/wsdl">
    wsdlMsgNS:BusinessLogicEndpoint_registerUser
   </wsdl-message>
   <wsdl-message-part-name>username</wsdl-message-part- name>
   <parameter-mode>IN</parameter-mode>
  </wsdl-message-mapping>
</method-param-parts-mapping>

注意,文檔/包裝樣式中的 wsdl-message-part-name 提供了元素名,而不是 消息部分本身的名稱。

另一個問題是,wscompile 為返回類型是 void 的方法的返回值生成 JAX-RPC 映射(在 wsdl-return-value-mapping 元素中描述)。這些 wsdl-return- value-mapping 元素應該刪除(否則在部署期間會拋出 NullPointerException) 。正確的行為在 Web services for J2EE, Version 1.0 規范中還不明確(單向 方式在這裡不適合,因為調用可能失敗,所以結果對於客戶機很重要)。

部署 Web 服務

在下一步中,應該開發 webservices.xml 部署描述符。它將 WSDL、JAX-RPC 映射、處理程序和後端實現 bean 結合在一起。

為了演示 Web 服務處理程序的使用,我們實現了 WS-Security 的一部分功能 。因為處理程序在方法級授權之後執行,所以處理程序不能直接用於身份驗證( 例如,這個業務邏輯 bean 無法公開為通過定制的處理程序執行身份驗證的 Web 服務)。所以,在這個例子中,Web 服務代表外觀,後端 bean 不要求授權。在 執行時,這個 bean 獲得安全處理程序捕獲的憑證,執行手工身份驗證,並以經 過身份驗證的客戶機的身份調用 BusinessLogic EJB 方法。

服務器端處理程序在 webservices.xml 中指定。這個處理程序是一個實現 javax.xml.rpc.handler.Handler 接口的類。這個處理程序類的實例用於過濾所 有到達的請求、發出的響應和錯誤。

客戶端處理程序在 web.xml 部署描述符中的服務引用中指定。

服務器處理程序 WSSecurityServerHandler 被開發成示例應用程序的一部分 ,它從到達的 SOAP 請求中提取用戶名和密碼,並將它們放到線程本地變量中。 這是目前將數據傳輸到 Web 服務後端 servlet 的惟一一種可靠的方式。Web 服 務後端 servlet 使用這些憑證進行身份驗證,並使用 Geronimo 特定的身份驗證 代碼以經過身份驗證的主體的身份調用 BusinessLogic EJB [因為只使用 Java Authentication and Authorization Service(JAAS)身份驗證是不夠的]。

清單 9 中的片段展示如何從安全處理程序接收主體的憑證,並以這個主體的 身份調用 EJB 方法:

清單 9. 作為某個安全主體調用業務邏輯

// username and password are obtained from the  security handler
LoginContext ctx = createLoginContext(username, password);
ctx.login();
try {
   Set set = ctx.getSubject().getPrincipals(
     org.apache.geronimo.security.IdentificationPrincipal.class);
   IdentificationPrincipal idp = (IdentificationPrincipal)  set.iterator().next();
   Subject subject =
     org.apache.geronimo.security.ContextManager.getRegisteredSubject (idp.getId());
   org.apache.geronimo.security.ContextManager.setCurrentCaller (subject);
   return (Order[])Subject.doAs(subject, new  PrivilegedExceptionAction() {
     public Object run() throws Exception {
       BusinessLogic logic = BusinessLogicUtil.getHome ().create();
       return logic.getOrders();
     } 
   });
} finally {
   ctx.logout();
}

這個片段不能轉移到處理程序代碼中,因為業務方法(在這個例子中是 getOrders)應該以另一個主體的身份運行(doAs 調用),而處理程序在調用服 務方法之前執行。

清單 10 展示了為這個應用程序開發的示例 Web 服務部署描述符:

清單 10. Web 服務部署描述符

<?xml version="1.0" encoding="UTF-8"?>
<webservices xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
  http://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd"version="1 .1">
  <webservice-description>
   <webservice-description-name>BusinessLogic</webservice- description-name>
   <wsdl-file>WEB-INF/BusinessLogic.wsdl</wsdl-file>
   <jaxrpc-mapping-file>WEB-INF/jaxrpc-mapping.xml</jaxrpc- mapping-file>
   <port-component>
    <port-component-name>BusinessLogicEndpointPort</port- component-name>
    <wsdl-port  xmlns:ns="http://ibm.com/samples/workdev/v1/wsdl">
     ns:BusinessLogicEndpointPort
    </wsdl-port>
    <service-endpoint-interface>
     com.ibm.workdev.v1.interfaces.BusinessLogicEndpoint
    </service-endpoint-interface>
    <service-impl-bean>
     <servlet-link>BusinessLogicServiceServlet</servlet- link>
    </service-impl-bean>
    <handler>
     <handler-name>ServerSecurityHandler</handler-name>
     <handler-class>
      com.ibm.workdev.v1.web.wssec.WSSecurityServerHandler
     </handler-class>
     <soap-header  xmlns:wssec="http://schemas.xmlsoap.org/ws/2003/06/secext">
      wssec:Security
     </soap-header>
    </handler>
   </port-component>
  </webservice-description>
</webservices>

部署 Web 服務客戶機存根

部署 Web 服務客戶機存根很簡單明了。執行以下步驟來准備所需的部署描述 符和代碼:

通過 URL http://localhost:8080/workdev/service?WSDL 獲得 WSDL。

開發端點接口。

用 JAX-RPC 映射將 WSDL 類型映射到 Java 類型。

在部署描述符中配置 Web 服務引用。

第 2 步和第 3 步已經在上面描述過了。它們幾乎與開發 Web 服務本身時的 過程一樣。差異是在第 3 步中為 wscompile 工具設置 gen:client 選項,而不 是 gen:server(這個選項讓 wscompile 工具生成服務接口和其他工件)。實際 上,JAX-RPC 映射和 bean 可以取自 Web 服務實現。只需要一個額外的工件,即 服務接口(它擴展 javax.xml.rpc.Service)。

服務引用放在 web.xml 中(注意,web.xml 的規范版本應該是 2.4)。清單 11 所示的片段是取自 Funds Transfer 應用程序的例子:

清單 11. 在 web.xml 部署描述符中引用 Web 服務

<service-ref>
  <service-ref-name>service/BusinessLogicService</service-ref- name>
  <service- interface>com.ibm.workdev.v1.web.BusinessLogicService</service- interface>
  <wsdl-file>WEB-INF/BusinessLogic.wsdl</wsdl-file>
  <jaxrpc-mapping-file>WEB-INF/jaxrpc-mapping.xml</jaxrpc- mapping-file>
  <service-qname  xmlns:servicens="http://ibm.com/samples/workdev/v1/wsdl">
   servicens:BusinessLogic 
  </service-qname>
  <handler>
   <handler-name>ClientSecurityHandler</handler-name>
   <handler- class>com.ibm.workdev.v1.web.wssec.WSSecurityClientHandler</handl er-class>
   <soap-header  xmlns:wssec="http://schemas.xmlsoap.org/ws/2003/06/secext">
    wssec:Security
   </soap-header>
  </handler>
</service-ref>

現在已經開發了 Web 服務部署描述符,可以通過 Java Naming and Directory Interface(JNDI)獲取 Web 服務實例了。清單 12 給出一個例子:

清單 12. 調用 Web 服務

InitialContext ctx = new  InitialContext();
BusinessLogicService service =
   (BusinessLogicService) ctx.lookup ("java:comp/env/service/BusinessLogicService");
BusinessLogicEndpoint endpoint =  service.getBusinessLogicEndpointPort();
// Invoke methods on  the endpoint interface...

在 Funds Transfer 應用程序 中,有一個調度器 servlet(WebServiceClientServlet),它與 JavaServer Pages™(JSP)一起提供一個調用 Web 服務方法的示例接口。

為什 麼不使用 Eclipse WTP?

到撰寫本文時,Eclipse Foundation 已經發布 了 WebTools Project 0.7,這個版本只為 Apache Geronimo 和 Web 服務提供了 最低限度的支持。盡管這個工具很有發展潛力,但是它目前缺少一些重要的特性 。

缺少的一種特性是將 Enterprise Application ARchive(EAR)文件部 署到 Geronimo 的能力。WTP 0.7 只能部署模塊,比如 Web、EJB 等等。不幸的 是,本文描述的應用程序需要這種功能,因為 J2EE 應用程序部署描述符(EAR 描述符)包含重要的信息。部署所有其他模塊需要這些信息,比如數據庫配置、 Java Messaging Services(JMS)配置和安全配置。因此,WTP 工具 Web 服務生 成向導無法正確地生成服務,本文中這個應用程序的開發無法利用這種向導。

在 Geronimo 早期版本中遇到的另一個問題在 WTP 中也存在 —— 向導對於 Web 服務只支持 Axis 運行時。這導致創建與 J2EE 不兼容的 Web 服務,並在項目源代碼中生成許多文件。另外,這些向導不支持為 服務和 Web 服務客戶機指定 SOAP 處理程序。這導致手工編輯處理程序,這很費 事兒。

開發 Web 服務及其客戶機時,更好的辦法是更充分地支持 JSR 109 規范。當前,WTP 還沒有完整地支持這個規范,因此限制了 Web 服務、Web 服務客戶機及其處理程序在 Project Explorer 樹中的表現;所以只能查看這些 信息,不能編輯。如果 WTP 提供豐富的編輯器和 Web 服務部署描述符(比如 webservices.xml、webservicesclient.xml 和 JAX RPC 映射描述符),也可能 會有幫助。

結束語

本文描述了如何使用 Geronimo 應用服務器提 供的 Web 服務功能來構建支持 Web 服務的應用程序。它說明了這種應用服務器 依賴於 J2EE 1.4 規范,為構建 Web 服務及其客戶機提供了許多功能。

值得注意的是,盡管到撰寫本文時 Geronimo 應用服務器還沒有提供正式發布的 開發工具集,但是 Eclipse WTP 項目已經開始開發這些工具了。

總之, 這種應用服務器已經展現出對 J2EE 標准健壯的支持,以後一定會在中小型企業 IT 項目中占有一席之地。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved