隨筆 - 1, 文章 - 44, 評論 - 2, 引用 - 0
          數據加載中……

          用JAX-RPC開發Web服務: Servlet作為Web服務端點

          關鍵詞JAX-RPC ?? web服務 ?? ??????????????????????????????????????

          閱讀本文前您需要以下的知識和工具:

          • JavaTM Web Services Developer Pack 1.1,并且會使用初步使用;
          • 遼倩崾褂靡恢諩JB容器來開發、部署EJB,并且了解怎么在客戶端訪問EJB組件;
          • 對Apache axis Web服務開發工具有基本的了解;
          • 基本的Java編程知識。

          如果使用JAX-RPC開發Web服務,我們幾種選擇:

          • Servlet作為Web服務端點;
          • 無狀態會話Bean作為Web服務端點;
          • 基于消息(如JMS)的應用程序作為Web服務端點。

          本文以Servlet作為Web服務端點的情況來介紹JAX-RPC Web服務開發,關于本篇文章中案例的介紹詳見本系列文章第一篇: 《用JAXM開發Web服務》

          本文的參考資料見 參考資料

          本文的全部代碼在這里 下載

          JAX-RPC快速入門

          JAX-RPC,Java? API for XMLbased RPC,顧名思義,它是一種遠程方法調用(或者說遠程過程調用),那么它和其它的遠程方法調用(RPC, COM,CORBA,RMI)有什么區別呢?我們看一般的遠程方法調用的結構,如圖1所示。


          圖1 遠程方法調用

          綜合比較常用的遠程方法調用技術,它們有以下的共性:

          • 在客戶端和服務端有通用編程接口;
          • 在客戶端有Stub,在服務端有Tie(有的叫Skeleton);
          • 客戶端和服務端有專門的協議進行數據傳輸。

          對于通用接口的描述,比如CORBA有IDL of CORBA,Java RMI有Java RMI interface in RMI,對于XMLbased RPC來說,IDL就是WSDL(Web服務描述語言)。那么XMLbased RPC來說,什么是這個結構中的"傳輸協議",當然是SOAP,SOAP消息通過以傳輸文本為基礎的協議(HTTP、SMTP、FTP)為載體來使用的,也就是說,SOAP消息的傳輸建立在HTTP、SMTP、FTP傳輸協議之上。

          JAX-RPC的構架如下。


          圖2 JAX-RPC 的構架

          從上圖可以看出,客戶端調用的是JAX-RPC服務端點(Service Endpoint),這個服務端點是通過WSDL語言描述的。在這個體系結構中,對于客戶端,可以是JS2E、J2ME或者J2EE平臺運行環境;對于服務端,可以是J2EE1.3或者J2EE1.4容器(Servlet容器或者EJB容器)。Apache axis是一個很好的JAX-RPC運行環境實現,同時也提供了優秀的開發工具,本文將使用它進行開發。

          使用Servlet作為服務端點,本案例的基本構架如下圖所示。


          圖3 案例的基本構架

          客戶端通過SOAP消息和JAX-RPC服務端交互,JAX-RPC服務端運行在Servlet容器中,它通過調用EJB容器中的EJB組件來處理具體的業務邏輯。

          使用JAX-RPC開發Web服務,可以按照以下的步驟進行:

          1. 服務端點定義;
          2. 服務描述;
          3. 服務端點實現;
          4. 服務端點部署;
          5. 服務發布和發現。

          注意:對于服務的發布和發現,由于機制比較復雜,本文不討論,可能會在本系列文章進行專題討論。

          開發快速入門

          一個完整的JAX-RPC開發實例,將按照上面的5個步驟進行,但是我們也可以使用非常簡單的方式來發布一個Web服務。在介紹我們的案例前,讓我們用一分鐘快速開發一個Web服務。

          首先安裝好JWSDP,你可以從 http://java.sun.com/webservices下載。

          把本案例源代碼中的\src\bookservice.ear\web.war目錄拷貝到%JWSDP_HOME%\webapps目錄下,web.war文件里已經包括了Apache axis運行環境。

          在%JWSDP_HOME%\classes目錄下新建一個HelloWorld.java文件,它的代碼如下:

          例程1 最簡單的Web服務HelloWorld

          
          
          package com.hellking.webservice;
          public class HelloWorld
          {
           public String sayHello(String name)
           {
            return "Hello! "+name;
           }
          }
          

          編譯這個類,然后編輯%JWSDP_HOME%\webapps\WEB-INF\server-config.wsdd文件,找到記,在其后面加入以下內容:

          在瀏覽器例輸入:

          http://localhost:8080/web/services/HelloWorld?wsdl

          如果出現部署WSDL描述文件,那么最簡單的Web服務已經部署成功!

          下面我們使用最簡單的方式來調用這個Web服務,在瀏覽器里輸入:

          http://localhost:8080/web/services/HelloWorld?wsdl&method=sayHello&name=hellking

          那么在瀏覽器將會顯示以下內容:

          例程2 在瀏覽器里調用Web服務

          Hello! hellking

          如果結果是這樣,那么最簡單的Web服務已經部署成功,并且測試也通過了。注意"Hello!hellking"是調用Web服務返回的結果,它是我們期望的。下面我們來看一個完整的Web服務開發的例子。

          服務端點定義

          服務端點定義的工作主要是確定"服務定義接口"(Service Definition Interface),有時也叫Web服務端點接口(Web services endpoint interface)。服務端點定義有兩中方法獲得:

          • 使用某些工具從WSDL文件獲得;
          • 直接使用Java語言編寫。

          Apache axis提供了從WSDL文件中獲得Web服務端點的工具。您可以這樣使用:

          
          
          java org.apache.axis.wsdl.WSDL2Java (WSDL-file-URL)
          

          使用這個命令前先設置好以下的環境變量,后面的介紹中還會使用axis工具,它們也要這樣設置環境變量:

          
          
          SET AXIS_HOME=
          set CLASSPATH=%CLASSPATH%;
          %AXIS_HOME%/axis-1_1/lib/axis.jar; 
          %AXIS_HOME%/axis-1_1/lib/jaxrpc.jar; 
          %AXIS_HOME%/axis-1_1/lib/saaj.jar;
          %AXIS_HOME%/axis-1_1/lib/commons-logging.jar;
          %AXIS_HOME%/axis-1_1/lib/commons-discovery.jar;
          %AXIS_HOME%/axis-1_1/lib/wsdl4j.jar;.
          

          關于WSDL2Java的更詳細的使用,請參考Apache axis的User Guides( http://ws.apache.org/axis/)。

          我們這里直接使用Java編寫服務端點接口的方法。在本案例中,定義了三個業務方法,它們分別是查找所有的圖書、按書名查找圖書、按類別查找圖書。那么安照這三個業務方法,可以定義出以下的服務端點接口:

          例程3 服務端點定義(BookServiceInterface.java)

          
          
          package com.hellking.webservice.servlet;
          /**
            *@author hellking
            */
          import java.util.Collection;
          import com.hellking.webservice.BookVO;
          public interface BookServiceInterface
          {
             
             /**
              * @return Vector
              */
             public Collection getAllBooks();//查找所有的圖書
             
             /**
              * @param name
              * @return BookVO
              */
             public BookVO getTheBookDetail(String name);//按照書名查找圖書
             
             /**
              * @return Collection
              */
             public Collection getBookByCategory(String category);//按類別查找
          }
          

          上面代碼中的BookVO是一個序列化的對象,它有以下屬性,每個屬性都提供了getter和setter方法。

          例程4 BookVO的部分代碼

          
          
          public class BookVO implements java.io.Serializable
          {
             private String name;
             private String publisher;
             private float price;
             private String isbn;
             private String description;
             private String category;
             private Collection authors;   
               
             public void setName(String name)
             {
               this.name=name;
             }
          public String getName()
              {
               return this.name;
              }
          …
          }
          

          編譯好這兩個類。

          服務端點描述

          可以使用Java2WSDL從以上定義的服務端點接口中獲得服務描述(WSDL文件)。使用Apache axis工具,只要使用以下命令即可:

          
          
           java org.apache.axis.wsdl.Java2WSDL -o temp.wsdl 
          -l"http://localhost:8080/axis/services/BookServletService"
           -n "urn:BookServletService"
           -p"com.hellking.webservice" "urn:BookServletService"
           com.hellking.webservice.servlet.BookServiceInterface
           

          以上命令的解釋:

          -o:生成的WSDL文件;
          -l:Web服務的位置;
          -n:這個WSDL文件的名字空間;
          -p:包到名字空間的映射;
          

          最后一個參數是Web服務端點接口。

          使用以上命令后,將生成一個名為temp.wsdl Web服務描述文。

          服務端點實現

          有了服務描述文件,就可以使用它來生成JAX-RPC 的框架,這個框架使得我們編程變得簡單,當然您也可以直接編寫實現代碼,然后部署,但是那樣編程會變得困難。

          使用以下的命令就可以生成這個框架:

          
          
          java org.apache.axis.wsdl.WSDL2Java -o . -d Session -s -S true 
          -Nurn:BookServletService com.hellking.webservice.servlet temp.wsdl
          

          使用這個命令后將生成以下文件:

          BookServiceInterface.java:新的BookServiceInterface接口,它擴展了java.rmi.Remote接口;

          BookServiceInterfaceService.java:客戶端服務接口,用來獲得BookServiceInterface對象的引用;

          BookServiceInterfaceServiceLocator.java:在客戶端使用,主要用來服務定位;

          BookServletServiceSoapBindingImpl.java:服務端實現類,它實現了BookServiceInterface接口,服務端的業務方法實現代碼就在這里編寫;

          BookServletServiceSoapBindingSkeleton.java:服務端Skeleton;

          BookServletServiceSoapBindingStub.java:客戶端Stub;

          BookVO.java:新的BookVO序列化對象;

          deploy.wsdd:部署這個Web服務的腳本;

          undeploy.wsdd:卸載這個Web服務的腳本。

          服務端點實現類的基本框架已經生成出來了,我們的任務就是往里面增加具體的業務內容。下面我們來看具體的服務端點的實現。如例程3所示。

          例程5 服務端點實現類

          
          
          package com.hellking.webservice.servlet;
          import java.util.*;
          import javax.naming.*;
          import com.hellking.webservice.ejb.*;
          
          public class BookServletServiceSoapBindingImpl
           implements com.hellking.webservice.servlet.BookServiceInterface{
             InitialContext init=null;
           BookServiceFacadeHome facadeHome;
           
              public BookServletServiceSoapBindingImpl()
              {
               try
               {
                init=new InitialContext();
               }
               catch(Exception e)
               {
               }
              } 
                 
             //業務方法,查找所有的圖書
           public java.lang.Object[] getAllBooks() throws java.rmi.RemoteException {
                       System.out.println("getAllBooks");
          
                  try
                  {
                   Object objref = init.lookup("ejb/bookfacade");  
                   facadeHome = (BookServiceFacadeHome)javax.rmi.PortableRemoteObject.narrow(
          objref, BookServiceFacadeHome.class); 
                Collection result=facadeHome.create().getAllBook(); 
                System.out.println(result.size());
                        Object[] ret=new Object[result.size()];
                 Iterator it=result.iterator();
                int i=0;
                while(it.hasNext())
                {
                 ret[i++]=it.next();
                } 
            // System.out.println(((BookVO)ret[0]).getName());
                  return ret;   
            }
            catch(Exception e)
            {
             e.printStackTrace();
             return null;
            }  
              }
              //業務方法,按書名查找圖書
              public com.hellking.webservice.BookVO getTheBookDetail(java.lang.String in0)
           throws java.rmi.RemoteException {
                   com.hellking.webservice.BookVO ret=null;
                  try
                  {
                   Object objref = init.lookup("ejb/bookfacade");  
                   facadeHome = (BookServiceFacadeHome)javax.rmi.PortableRemoteObject.narrow(
          objref, BookServiceFacadeHome.class); 
                Collection result=facadeHome.create().getBookDetail(in0);
                Iterator it=result.iterator();
                while(it.hasNext())
                {
                 ret=( com.hellking.webservice.BookVO)it.next();
                } 
          
             }   
            catch(Exception e)
            {
            }  
            return ret;
              }
          //業務方法,按類別查找圖書
              public java.lang.Object[] getBookByCategory(java.lang.String in0)
           throws java.rmi.RemoteException {
                   try
                  {
                   Object objref = init.lookup("ejb/bookfacade");  
                   facadeHome = (BookServiceFacadeHome)javax.rmi.PortableRemoteObject.narrow(
          objref, BookServiceFacadeHome.class); 
                System.out.println(in0);
                Collection result=facadeHome.create().findByCategory(in0); 
                Object[] ret=new Object[result.size()];
                Iterator it=result.iterator();
                int i=0;
                while(it.hasNext())
                {
                 ret[i++]=it.next();
                 System.out.println(i);
                } 
             return ret;   
            }
            catch(Exception e)
            {
             e.printStackTrace();
             return null;
            }  
           }
          }
          

          可以看出,服務端點的主要任務是調用EJB組件來完成業務邏輯的。

          需要向讀者說明的是,為了和本系列第一篇文章中的客戶端框架兼容(客戶端使用的值對象是com.hellking.webservice.BookVO,而這里由WSDL2Java生成的值對象是com.hellking.webservice.servlet.BookVO)。我們需要做以下的改動:

          把生成的這些代碼中的com.hellking.webservice.servlet.BookVO全部改為com.hellking.webservice.BookVO,然后在Apache axis服務配置文件中申明這個BeanMapping,具體的聲明方法在后面介紹。

          接下來的工作是編譯服務端相關的文件:BookServletServiceSoapBindingImpl、BookServletServiceSoapBindingSkeleton、BookServiceInterfaceService、BookServiceInterface。

          服務端點部署

          啟動服務器,這個服務器可以是任何能夠運行Apache引擎Web服務器,當然最好是同時有EJB容器和EJB容器的服務器,如Webphere 、Weblogic、JBOSS,如果沒有EJB容器,還需要一個額外的EJB容器,并且需要更改BookServletServiceSoapBindingImpl中獲得上下文(InitialContext)的方法,如:

          例程6 獲得上下文環境

          
          
               Properties p = new Properties();   
               p.put(Context.INITIAL_CONTEXT_FACTORY, "xxxxx");
               p.put(Context.URL_PKG_PREFIXES, "xxxx");
               p.put(Context.PROVIDER_URL, "xxxx");
               init=new javax.naming.InitialContext(p);
               

          在控制臺中,轉到deploy.wsdd目錄下,執行以下的命令就可以完成部署:

          
          
           java org.apache.axis.client.AdminClient deploy.wsdd
           

          由于我們使用的是自己的序列化Bean對象,故要在%Web-Apps%/WEB-INF/ server-config.wsdd文件中做以下更改:
          找到

          在中間加入以下內容:

          部署后您必須確保在%Web-Apps%/WEB-INF/classes目錄下有服務端相關的類(BookServletServiceSoapBindingImpl、BookServletServiceSoapBindingSkeleton等)。

          在瀏覽器里輸入(這個地址您需要根據具體情況更改):

          http://localhost:8080/axis/services/BookServletService?wsdl

          來驗證Web服務是否已經部署成功,如果部署不成功,您可以先嘗試重新啟動服務器。

          客戶端

          如果服務端已經成功部署,下一步的工作就是編寫客戶端程序了。由于使用WSDL2Java已經生成了客戶端的框架,所以我們的任務將相對簡單了。

          客戶端編程任務主要有以下幾個:

          • 在BookServletServiceSoapBindingStub里注冊BeanMapping;
          • 編寫客戶端業務代表,這里使用了JAXRPCDelegate;
          • 更改以前的BookGUI的部分程序。

          在BookServletServiceSoapBindingStub里注冊BeanMapping

          由于在SOAP消息中使用了序列化的BookVO對象,故在BookServletServiceSoapBindingStub中要進行BeanMapping注冊。具體方法:

          找到BookServletServiceSoapBindingStub中的getAllBooks,getTheBookDetail,getBookByCategory方法,在每個方法中的

          
          
           java.lang.Object _resp = _call.invoke(new java.lang.Object[] {in0});
           

          前加入以下代碼:

          例程7 在BookServletServiceSoapBindingStub注冊BeanMapping

          
          
           QName    qn      = new QName( "BookServletService", "BookServletService" );
           _call.registerTypeMapping(com.hellking.webservice.BookVO.class, qn,                
          new org.apache.axis.encoding.ser.BeanSerializerFactory(com.hellking.webservice.BookVO.class, qn),        
           new org.apache.axis.encoding.ser.BeanDeserializerFactory(com.hellking.webservice.BookVO.class, qn));
           

          注意這里的Qname要和server-config.wsdd中描述的名稱空間一致。在中server-config.wsdd,我們使用了以下的映射:

          在編寫業務代表程序前,我們先來對Web服務做一個調用測試。在測試前您必須保證數據庫里已經有圖書信息。如果EJB和Web Application都部署好,您可以通過以下頁面來往數據庫里增加數據:

          http://localhost:8080/axis/insert_data.jsp

          測試代碼如下:

          例程8 測試Web服務

          
          
          package com.hellking.webservice.servlet;
          import java.net.URL;
          
          import javax.xml.namespace.QName;
          import javax.xml.rpc.Service;
          import javax.xml.rpc.ServiceFactory;
          import com.hellking.webservice.ejb.*;
          
          public class Client { 
              public static void main(String[] args) {      
                  try {
                      
                      BookServiceInterfaceServiceLocator locator=new BookServiceInterfaceServiceLocator();
                      
                  BookServiceInterface myProxy=locator.getBookServletService();
                      Object[] c=myProxy.getAllBooks();
                      com.hellking.webservice.BookVO 
          book=(com.hellking.webservice.BookVO)c[0];
                      System.out.println(book.getName());            
                  } catch (Exception ex) {
                      ex.printStackTrace();
                  } 
              } 
          }
          
                

          如果在控制臺里打印出某個圖書的名字,那么就驗證了客戶端和服務端的部署是正確的。在進行下面的工作前,請確保這個測試是成功的。

          編寫客戶端業務代表

          對于客戶端程序來說,業務代表直接和Web服務打交道,獲得Web服務返回的數據,并做對應的處理,然后把數據返回給GUI程序,GUI程序只負責數據顯示。業務代表的代碼如下:

          例程9 JAXRPCDelegate業務代表

          
          
          package com.hellking.webservice.servlet;
          
          import java.net.URL;
          import javax.xml.namespace.QName;
          import javax.xml.rpc.Service;
          import javax.xml.rpc.ServiceFactory;
          import com.hellking.webservice.ejb.*;
          import java.util.*;
          
          public class JAXRPCDelegate implements com.hellking.webservice.BookBusiness
          {
            BookServiceInterfaceServiceLocator locator;
               com.hellking.webservice.servlet.BookServiceInterface bookService;
               
               public JAXRPCDelegate()
            {
             try
             {
              locator=new BookServiceInterfaceServiceLocator();
                    bookService=locator.getBookServletService();
                   }
                   catch(Exception e)
                   {
                   }
              }
            
           public Collection getBookByCategory(String category)
           {
               System.out.println("by_category");
            Collection ret=new ArrayList();
            try
            {   
                      Object[] books=bookService.getBookByCategory(category);
                      System.out.println(category);
                      int i=0;
                      
                      while(true)
                      {
                       ret.add(books[i++]);
                       System.out.println(i);
                      }   
            }
            catch(Exception e)
            {
            }
            return ret; 
           }
           
           public Collection getAllBooks()
           {
            Collection ret=new ArrayList();
            try
            {   
          
                      Object[] books=bookService.getAllBooks();
                      int i=0;
                      
                      while(true)
                      {
                       ret.add(books[i++]);
                      } 
            }
            catch(Exception e)
            {
            }
            return ret;
           }
           
           public com.hellking.webservice.BookVO getTheBookDetail(String name)
           {
            System.out.println("bookdetail");
            com.hellking.webservice.BookVO ret=new com.hellking.webservice.BookVO();
            try
            {   
                     ret=bookService.getTheBookDetail(name);
            }
            catch(Exception e)
            {
            }
            return ret;  
           } 
          }
          

          和第一篇文章介紹的JAXMDelegate一樣,JAXRPCDelegate 同樣實現了BookBusiness接口,BookBusiness接口是以前設計的接口,我們在這里進行重用,這樣的好處是BookClientGUI程序幾只要做很少的更改就可以運行。

          更改以前的BookClientGUI的部分程序

          在BookGUI構造方法里增加以下內容:

          例程10 更改BookGUI程序

          
          
          public BookClientGUI()
          {
           business=new JAXRPCDelegate();
          …
          }
          

          好了,經過以上的奮戰,讓我們來看運行的結果吧。

          java com.hellking.webservice.BookClientGUI

          運行結果如圖4所示。


          圖4 運行結果.

          總結

          通過以上的介紹,相信讀者對JAX-RPC Web服務開發已經有一個比較深刻的認識。總結一下,使用JAX-RPC開發Web服務時,主要有以下的工作:

          • 服務端點定義;
          • 服務描述;
          • 服務端點實現;
          • 服務端點部署;

          下一步

          本文已經介紹了把Servlet作為Web服務端點開發Web服務的全過程,下一篇將是把EJB作為Web服務端點來開發。

          參考資料

          Apache axis User's Guides: http://ws.apache.org/axis/ Sun jwsdp-1_1-tutorial, http://java.sun.com/webservices/downloads/webservicestutorial.htmlhttp://www.ibm.com/developerworks/cn/xml/index.htmlXML & Web services專區 JAX-RPC API http://java.sun.com/webservices Jwdp1.1 http://java.sun.com/webservices 下載 樣例代碼

          關于作者
          陳亞強:北京華園天一科技有限公司高級軟件工程師,擅長J2EE技術,曾參與多個J2EE項目的設計和開發,對Web服務有很大的興趣并且有一定的項目經驗。熱愛學習,喜歡新技術。即將由電子工業出版社出版的《J2EE企業應用開發》正在最終定稿階段,目前正從事J2EE方面的開發和J2EE Web服務方面的圖書寫作。您可以通過 cyqcims@mail.tsinghua.edu.cn和他聯系。

          posted on 2006-06-23 11:01 ASONG 閱讀(298) 評論(0)  編輯  收藏 所屬分類: webservice


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 诸暨市| 闵行区| 拉萨市| 米脂县| 平凉市| 石城县| 沅陵县| 安康市| 苗栗县| 射阳县| 绩溪县| 郎溪县| 沭阳县| 新乐市| 十堰市| 山东省| 南开区| 博客| 平顶山市| 临朐县| 临汾市| 南江县| 东城区| 石首市| 阿拉尔市| 德保县| 砚山县| 榆树市| 诸暨市| 合江县| 西畴县| 阜新| 五常市| 江西省| 德兴市| 周口市| 彭泽县| 枣庄市| 武乡县| 望都县| 察哈|