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

用JAXM開發Web服務

編輯:關於JAVA

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

JavaTM Web Services Developer Pack 1.1,並且會使用初步使用;

至少會使用一種EJB容器來開發、部署EJB,並且了解怎麼在客戶端訪問EJB組件;

一般的Java編程知識。

在J2EE平台裡,要開發一個Web服務,我們通常有兩種選擇:

使用JAX-RPC(Java API for XML-based RPC)

使用JAXM(Java API for XML Messaging)

作為對JAXM開發技術的入門,本文先不比較它們的技術特點。我將結合一個具體的案例來討論JAXM的開發技術方方面面。

JAXM相關概念介紹

通常我們說的JAXM API,它包括兩個包:

Javax.xml.soap:它是發送SOAP消息的基本包,主要包含了發送帶有附件的SOAP消息的API(SOAP with Attachments API for Java ,SAAJ)。它是SOAP消息的基本包,它為構建SOAP包和解析SOAP包提供了重要的支持。它包含了發送請求-響應消息相關的API。

Javax.xml.messaging:定義了JAXM的規范,包含了發送和接收消息所需的API。

JAXM包含了以下幾個概念:消息(Message)、連接(Connection)、消息提供者(Messaging providers)。

消息

JAXM消息遵循SOAP標准,我們可以通過JAXM API方便的創建SOAP 消息。有兩種類型的消息,帶附件的消息和不帶附加的消息。不帶附件的消息結構如圖1所示。

如圖1所示,在SAAJ API中,它使用SOAPMessage類來代表SOAPMessage,相應的,使用SOAPPart類來代表SOAPPart,SOAPBody類代表SOAP Body。

圖1 不帶附件的SOAP消息

其中Header和SOAPFault是可選的,Header可以多個,Body只有一個,如果有SOAP Fault,那麼它一定在SOAP Body後面。帶附加的SOAP消息如圖2所示。

圖2 帶附件SOAP消息

可以看出,一個SOAP消息可以有一個或者多個附件。SAAJ API使用AttachmentPart類來代表SOAP消息的附件。每個AttachmentPart有一個MIME Header來表示附件的類型。

連接

有兩種類型的連接,它們是:

消息發送者到接收者的直接連接,javax.xml.soap.SOAPConnection表示了這種類型的連接,由於它是點對點的,所以比較容易使用,即使不在Servlet或者J2EE容器裡也能使用;

到消息提供者的連接,javax.xml.messaging.ProviderConnection表示了這種連接,這種方式需要消息提供者,消息發送者和消息使用者通過消息提供者來交互。

消息提供者

消息提供者主要負責傳送消息,它把消息路由到目的地,一個消息發出後,可能要經過多個消息提供者才能到達目的地。

如果使用MessageProvider,可以達到以下的目的:

除了能夠發送request-response類型的消息外,還可以發送One-way(單向)消息;

(消息)客戶端有時也可以作為服務端來使用。

案例介紹

在本文,我將結合一個具體的案例來介紹JAXM Web服務的開發。此案例具體情況如下。

某圖書城決定使用Web服務來對外提供圖書信息查詢服務,圖書城現有的系統運行在J2EE平台上,客戶端通過JAXM來使用圖書城提供的Web服務。系統的體系結構如圖3所示:

圖3 系統體系結構

客戶端可以是一般的java GUI程序(當然也可以是JSP、Servlet等)。客戶端通過SOAP消息和Servlet容器裡運行的JAXM Servlet進行交互,JAXM Servlet是服務提供者,EJB容器裡運行的是業務組件,它們為JAXM Servlet提供服務。

客戶端請求傳遞的過程如圖4所示:

圖4 請求傳遞的過程

可以看出,客戶端通過SOAP和JAXM 服務端通信,JAXM使用EJB組件來獲得業務服務。

系統為客戶端提供了三種查詢服務:查詢所有圖書,按類別查詢圖書,按圖書名搜索某本特定的圖書。這三種服務分別有服務端的三個JAXM Servlet實現。它們是:

ListAllBook:查詢所有的圖書信息;

ListByCategory:按類別查詢圖書信息;

BookDetail:查詢某個特定名稱的圖書信息。

客戶端是用Swing編寫的GUI界面,使用界面如圖5所示。

圖5 客戶端界面

客戶端和服務端傳輸圖書信息時采用例程1所示的格式。

例程1 傳輸圖書信息的格式(book.dtd)

<!ELEMENT books(book*)>
<!ELEMENT book (name,publisher,price,author+,category,description)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT publisher (#PCDATA)>
<!ELEMENT price (#PCDATA)>
<!ELEMENT author (#PCDATA)>
<!ELEMENT category (#PCDATA)>
<!ELEMENT description (#PCDATA)>
<!ATTLIST book id CDATA #REQUIRED>

這個信息包含在SOAP消息的Body裡,按照這個格式,傳輸的SOAP消息結構如例程2所示。

例程2 傳輸的SOAP消息的格式(book.msg)

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
   <soap-env:Header/>
   <soap-env:Body>
     <books:GetAllBooks xmlns:books="http://hellking.webservice.com">
      <books:book id="2-1234-4455-4">
   <books:name>J2EE企業應用開發</books:name>
   <books:publisher>電子工業出版社</books:publisher>
   <books:price>60</books:price>
   <books:category>計算機類</books:category>
   <books:description>非常好的介紹J2EE企業應用開發的書</books:description>
   <books:author>陳亞強</books:author>
   <books:author>劉曉華</books:author>
    </books:book>
</books:GetAllBooks>
  </soap-env:Body>
</soap-env:Envelope>

為了傳輸數據的便利,我把圖書信息用一個專門的值對象來表示,如例程3所示。

例程3 BookVO值對象

package com.hellking.webservice;
import java.util.Collection;
public class BookVO implements java.io.Serializable
{
  private String name;//圖書名字
  private String publisher;//圖書出版社
  private float price;//圖書價格
  private String isbn;//圖書ISBN
  private String description;//圖書的簡介
  private String category;//圖書的類別
  private Collection authors;//圖書的作者,因一本書可以有多個作者,故把它表示成Collection。
public void setName(String name)
  {
   this.name=name;
  }
public String getName()
  {
   return this.name;
  }
…其它的getter和setter方法

可以看出,BookVO其實是和例程1中的DTD是對應的。需要指出的是,BookVO可以使用JAXB(Java API for XML Binding)中的工具來生成。

EJB組件介紹

本案例使用了兩個EJB組件,它們分別是BookEntityEJB和BookServiceFacadeEJB。其中BookEntityEJB是實體Bean,它代表了每本書的詳細信息;BookServiceFacadeEJB為有狀態會話Bean,它是一個會話門面,為JAXM Servlet提供業務服務。BookServiceFacadeEJB組件的遠程接口如例程4所示。

例程4 BookServiceFacadeEJB組件的遠程接口

package com.hellking.webservice.ejb;
import java.rmi.RemoteException;
import javax.ejb.*;
public interface BookServiceFacade extends EJBObject
{
   /**
   * @J2EE_METHOD -- getAllBook,查找所有的書
   */
   public java.util.Collection getAllBook  () throws RemoteException;

   /**
   * @J2EE_METHOD -- findByCategory,按類別查找
   */
   public java.util.Collection findByCategory  (String category) throws RemoteException;
/**
   * @J2EE_METHOD -- getBookDetail  ,按名字查找
   */
   public java.util.Collection getBookDetail  (String name) throws RemoteException;
}

可以看出,它提供了三個業務服務,分別是getAllBook(),findByCategory(String category),getBookDetail(String name)。這三個業務方法返回的都是java.util.Collection。其實,getBookDetail( String name )方法返回的應該是一個值對象,但是為了方便統一處理,也通過處理讓它返回java.util.Collection類型,這一點以後的代碼中體現出來。

在BookEntityEJB Home接口也提供了對應的查找方法,如例程5所示。

例程5 BookEntityEJB的Home接口

package com.hellking.webservice.ejb;
import java.rmi.RemoteException;
import javax.ejb.*;
public interface BookEntityHome extends EJBHome
{
   public BookEntity findByPrimaryKey  (String primaryKey)
         throws RemoteException, FinderException;

   public BookEntity create(String isbn) throws RemoteException, CreateException;

   /**
   * @J2EE_METHOD -- getAllBook,查找所有的書
   */
public java.util.Collection findAllBook  ()
         throws RemoteException, FinderException;

/**
   * @J2EE_METHOD -- findByCategory,按類別查找
   */
   public java.util.Collection findByCategory  (String category)
         throws RemoteException, FinderException;

/**
   * @J2EE_METHOD -- getBookDetail  ,按名字查找
  */
public BookEntity findByName  (String name)
         throws RemoteException, FinderException;

}

開發服務端

下面開發服務端,我們前面說過,服務端共有三個JAXM Servlet,它們分別提供三種不同的查詢服務。

由於使用了點對點的消息模型,故服務端需要實現javax.xml.messaging. ReqRespListener接口,並且需要繼承javax.xml.messaging.JAXMServlet類。javax.xml.messaging.JAXMServlet是一個Servlet,它為開發消息服務的Servlet提供了一個框架。需要指出的是,javax.xml.messaging. ReqRespListener接口定義了一個

public SOAPMessage onMessage (SOAPMessage message)

方法,故我們開發的JAXM服務端Servlet必須實現這個方法。 onMessage 方法就是當此Servlet接收到SOAPMessage時激發的方法,它通過此方法對外界提供服務(我們可以把這個方法簡單的比喻成普通的HttpServlet中的doGet()、doPost()方法,HttpServlet正是通過doGet()、doPost()來為客戶端提供服務)。

ListAllBook的部分代碼如例程6所示。

例程6 ListAllBook的部分代碼

public class ListAllBook extends JAXMServlet implements ReqRespListener
{
   public void init  (ServletConfig servletConfig) throws ServletException
   {
   super.init(servletConfig);
   }
   public SOAPMessage onMessage  (SOAPMessage message)
   {
  System.out.println("from ListAllBook Servlet:receive a message");
  try
  {
  System.out.println("from ListAllBook Servlet:");
  message.writeTo(System.out);//在控制台打印收到的消息
       //調用其它類來實現業務方法
       SOAPMessage msg=new XMLBusinessDelegate().listAllBook();
       System.out.println("this is the reply.....");
       msg.writeTo(System.out);
       msg.saveChanges();//注意,在返回消息之前要調用這個方法
       return msg;//返回消息
     }
     catch(Exception ex)
     {
     ex.printStackTrace();
      //處理錯誤….
     return null;
     }

   }
}

在這個例子裡,由於接收到的消息不含任何參數,故沒有對它進行處理,一般情況下,接收的消息是有參數的,並且服務端需要使用這個參數來調用業務層組件,如當用戶按類別查找圖書時,服務端需要獲得類別的名字,例程7是按類別查找圖書的服務端Servlet的部分代碼,我們看服務端是怎麼獲得客戶端的請求參數。

例程7 ListByCategory的部分代碼

public SOAPMessage onMessage (SOAPMessage message)
   {
  try
  {
    …
  SOAPEnvelope env=message.getSOAPPart().getEnvelope();
  Iterator it=env.getBody().getChildElements(
env.createName("books","GetBookByCategory","http://hellking.webservice.com"));
  SOAPElement books=(SOAPElement)it.next();

  Iterator it2=books.getChildElements(
env.createName("category","GetBookByCategory","http://hellking.webservice.com"));
  String category=((SOAPElement)it2.next()).getValue();
        //這裡的category是從SOAP 消息中讀出的參數
  SOAPMessage msg=new XMLBusinessDelegate().listByCategory(category);
       msg.saveChanges();
       return msg;//返回處理消息
     }
     catch(Exception ex)
     {
     ex.printStackTrace();
     //處理錯誤….
     return null;//如果出錯,一般返回SOAPFault,這裡簡化了。
     }
   }

SAAJ API為解析SOAP 消息提供了很好的支持,注意上面的黑體子,它是讀取獲得查找圖書類別參數category的代碼,這個參數是客戶端設置的,在以後我們將看到客戶端怎麼設置這個參數。

總結一下,JAXM服務端Servlet處理消息的步驟是:

獲得消息(onMessage)

讀取消息中需要的參數

利用參數調用對應的業務處理

構建響應SOAP消息

返回處理後的消息

從上面的例子可以看出,JAXM服務端Servlet並沒有處理具體的業務,而是把業務處理交給一個叫XMLBusinessDelegate業務代表的類來處理。

我們來看一下XMLBusinessDelegate是怎麼來進行業務處理的。如例程8所示。

例程8 XMLBusinessDelegate的部分代碼


public class XMLBusinessDelegate
{
InitialContext init=null;
  BookServiceFacadeHome facadeHome;
OTDEngine otd;//對象到數據的轉換器
public XMLBusinessDelegate()throws NamingException
{
  init=this.getInitialContext();
otd=new BeanToSOAPEngine(); //生成對象到數據轉換器實例   
}
public static InitialContext getInitialContext() throws javax.naming.NamingException
  {
    //更據不同的EJB容器,使用不同的url和連接工廠來獲得上下文,然後返回…
}
//業務方法,按類別查找圖書
public SOAPMessage listByCategory(String category)
  {
   try
  {

   Object objref = init.lookup("ejb/bookservicefacade");
     facadeHome = (BookServiceFacadeHome)
javax.rmi.PortableRemoteObject.narrow(objref, BookServiceFacadeHome.class);
  System.out.println("call jboss======>>");
  Collection result=facadeHome.create().findByCategory(category);//調用業務方法
  System.out.println("get result======>>");
  System.out.println(result.size());
   // 使用BeanToSOAPEngine把調用結果轉換成SOAP消息
otd.init(result,"GetAllBooks");
  SOAPMessage ret=otd.getResult();
  return ret;
   }
   catch(Exception e)
   {
    e.printStackTrace();
    return null;
   }
  }

可以看出,XMLBusinessDelegate只是調用EJB組件的業務方法,然後把構建SOAP消息的任務交給BeanToSOAPEngine,BeanToSOAPEngine是負責把包含了BookVO 的Collection轉換成SOAP消息的專門的類。BeanToSOAPEngine的部分代碼如例程9所示。

例程9 BeanToSOAPEngine的部分代碼


public class BeanToSOAPEngine implements OTDEngine
{
Collection bookVos;//要處理的信息
SOAPMessage msg;//待返回的消息
String type;//type為返回消息的名字空間,如GetBookByCategory
public BeanToSOAPEngine()
{
  try
  {
  MessageFactory mf = MessageFactory.newInstance();//獲得MessageFactory的實例
  msg = mf.createMessage();//從MessageFactory建立一個空的Message
  }
  catch(Exception ex)
  {
  ex.printStackTrace();
  }
}
public void init()
{
this.bookVos=c;
  this.type=type;
}
public SOAPMessage getResult()
{
  build();
  return msg;
}
public void build()
{
  try
  {
     SOAPPart part = msg.getSOAPPart();
     SOAPEnvelope envelope = part.getEnvelope();
     SOAPBody body = envelope.getBody();
       //創建body名字空間
     Name bodyName=envelope.createName(type,"books","http://hellking.webservice.com" );
   SOAPBodyElement books=body.addBodyElement(bodyName);//增加body

//以下程序把Collection中的BookVO轉化成SOAP消息
Iterator it=bookVos.iterator();
    while(it.hasNext())
    {
    BookVO bookvo=(BookVO)it.next();

        //構建book   
    Name bookName=envelope.createName(
"book","books","http://hellking.webservice.com");
   SOAPElement book=books.addChildElement(bookName);
   book.addAttribute(envelope.createName("id"),bookvo.getIsbn());

   //構建name
Name elName=envelope.createName(
"name","books","http://hellking.webservice.com");
   book.addChildElement(elName).addTextNode(bookvo.getName());

   //構建publisher
   Name elPublisher=envelope.createName(
"publisher","books","http://hellking.webservice.com");
   book.addChildElement(elPublisher).addTextNode(bookvo.getPublisher());

   //構建price
   Name elPrice=envelope.createName(
"price","books","http://hellking.webservice.com");
   book.addChildElement(elPrice).addTextNode(
new Float(bookvo.getPrice()).toString());

        //構建category
   Name elCategory=envelope.createName(
"category","books","http://hellking.webservice.com");
   book.addChildElement(elCategory).addTextNode(bookvo.getCategory());
   //構建description
   Name elDescription=envelope.createName(
"description","books","http://hellking.webservice.com");
   book.addChildElement(elDescription).addTextNode(bookvo.getDescription());
   //author本來就是一個Collection,故要特別處理
   Collection author_c=bookvo.getAuthors();
   Iterator au_it=author_c.iterator();
   while(au_it.hasNext())
   {
   String strAuth=(String)au_it.next();
   //增加一個author
   Name elAuth=envelope.createName(
"author","books","http://hellking.webservice.com");
     book.addChildElement(elAuth).addTextNode(strAuth);
    }
  }
  msg.writeTo(System.out); //打印消息
  }
  catch(Exception ex)
  {
  ex.printStackTrace();
  }
}

按照上面的代碼,BeanToSOAPEngine構建的SOAP消息應該和例程2的結構一致。

總結一下,構建SOAP消息時,按以下步驟進行:

獲得MessageFactory 實例

利用MessageFactory 創建空的SOAPMessage

創建Header(可選)

創建body

創建body的名字空間

創建body的子元素

創建子元素名字空間

循環創建子子元素

把子元素增加到父元素裡

往Message裡增加body元素

創建附件(可選)

開發客戶端

客戶端和服務端通信,按以下的步驟進行:

創建 SOAP 連接

創建 SOAP 消息

在SOAP消息裡增加數據

發送消息

對SOAP應答進行處理

下面我們就按照這個步驟來一步步討論SOAP客戶端開發種種問題。在本案例中,和服務端進行交互的客戶端是通過一個叫JAXMDelegate的類來進行的。GUI客戶程序通過調用JAXMDelegate來獲得查詢結果(具體的數據傳輸機制和設計模式見本系列第二篇文章)。首先我們來看怎麼創建SOAP連接。

創建 SOAP 連接

由於我們使用的是點對點的消息發送模型,所以連接的類型是SOAPConnection。如例程10所示。

例程10 創建連接

package com.hellking.webservice;
import javax.xml.soap.*;

public class JAXMDelegate implements BookBusiness
{
SOAPConnection con =null;
EndpointLocator locator=new EndpointLocator();
Collection allbook;//cache
public JAXMDelegate()
{
  allbook=new ArrayList();
  try
  {
  SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance();
        con = scf.createConnection();
     }
     catch(Exception e)
     {
     e.printStackTrace();
     }
   }

在上面的例子中,SOAPConnection是通過SOAPConnectionFactory來創建的。

創建 SOAP 消息

接下來是創建消息,在本案例中,有一個專門的方法來創建消息,如11所示。

例程11 創建消息


//target為目標名字空間,name為查詢某本特定書的書字,category為要查詢的類別
public SOAPMessage createMessage(String target,String name,String category)
   {
   try
   {
      MessageFactory mf = MessageFactory.newInstance();
      SOAPMessage msg = mf.createMessage();
    SOAPPart sp = msg.getSOAPPart();

  SOAPEnvelope envelope = sp.getEnvelope();
  //SOAPHeader hdr = envelope.createSOAPHeader();

  SOAPBody body = envelope.getBody();
  // AttachmentPart attachment = msg.createAttachmentPart();
msg.saveChanges();
   …
  }
  catch(Exception e)
  {
  e.printStackTrace();
  return null;
  }
}

可以看出創建消息的步驟,首先通過MessageFactory來創建一個消息實例,然後依次創建SOAPEnvelope、SOAPHeader、SOAPBody、AttachmentPart,最後調用msg.saveChanges()來保存消息的變化。

在SOAP消息裡增加數據

在SOAP消息裡增加數據如例程12所示。

例程12 往SOAP消息裡增加數據

//創建名字空間
Name bodyName=envelope.createName("books",target,"http://hellking.webservice.com");
  //增加body元素
SOAPBodyElement gpp=body.addBodyElement(bodyName);

  if(category!=null)
  {
   //增加category
gpp.addChildElement("category").addTextNode(category);
  }

  if(name!=null)
  {
   gpp.addChildElement("name").addTextNode(name);
  }
  msg.saveChanges();
  return msg;

如果例程11中傳入的category參數不為空,那麼創建好的消息結構應該如例程13所示。

例程13 客戶端創建的消息的結構

<?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>
  <GetBookByCategory:books xmlns:GetBookByCategory="http://hellking.webservice.com">
  <GetBookByCategory:category>computer</GetBookByCategory:category>
  </GetBookByCategory:books>
</soapenv:Body>
</soapenv:Envelope>

注意上面的computer為客戶端要查詢的參數。如果覺得上面填充消息的過程過於復雜,我們也可以從文件填充消息內容,如例程14所示。

例程14 從文件填充消息內容

import javax.xml.soap.SOAPElement;
import java.io.FileInputStream;
import javax.xml.transform.stream.StreamSource;

StreamSource preppedMsgSrc = new StreamSource(
         new FileInputStream("e://msgs//book_getBycategory.msg"));
     soapPart.setContent(preppedMsgSrc);

可以看出,從文件裡填充消息比較快速。這兩種方法各有所長,因為如果客戶端的請求的種類特別多的話,特別是請求帶有參數的話,那麼我們要為每一種請求都預先寫一個SOAP消息文件,可能是不符合實際的;但是如果客戶端請求的類型比較固定,那麼事先編寫好SOAP消息然後再調用不失是一種好的選擇。

發送消息

發送消息相對比較簡單,首先要獲得一個Endpoint,這個Endpoint就是要發送的消息的目標(也就是接收消息Servlet的url),然後就發送消息,如例程15所示。

例程15 發送SOAP消息

EndpointLocator locator=new EndpointLocator();

try
{
SOAPMessage msg=createMessage("GetBookByCategory",null,category);
   String endpoint=locator.getBookByCategory_Endpoint();
SOAPMessage reply=con.call(msg , new URL(endpoint));
   …
}

EndpointLocator是終端定位器,調用getBookByCategory_Endpoint()它返回的結果將是"http://localhost:8080/jaxm_jaxrpc/listbycategory",這個地址就是ListByCategory Servlet的RUL,更據不同的設置來確定。由於使用的是點對點的消息發送模型,調用con.call()後返回的也是SOAPMessage。

對SOAP應答進行處理

接下來對消息進行處理,因為不管查詢的結果如何,返回的消息都是例程2所示的結構,故我們使用一個專門的類來把SOAP消息轉化成包含有BookVO的Collection,這個類是SOAPToBeanEngine,SOAPToBeanEngine的部分代碼如例程16所示。

例程16 SOAPToBeanEngine的部分代碼

package com.hellking.webservice;
import javax.xml.messaging.*;

public class SOAPToBeanEngine implements DTOEngine
{
SOAPMessage reply;
Collection bookVos;//轉換後的結果,用Collection表示
   //構造方法,reply為要轉換的SOAP消息
public SOAPToBeanEngine(SOAPMessage reply)
{
  this.reply=reply;
}

public Collection getResult()
{
  build();
  return bookVos;
}
   //build為具體轉換的方法
   public void build()
   {
   try
   {

    Collection ret=new ArrayList();
    //System.out.println(reply.getSOAPPart().getEnvelope().getBody().getElementName());
    Iterator child=reply.getSOAPPart().getEnvelope().getBody().getChildElements();①
    SOAPElement bookall=(SOAPElement)child.next();②
    Iterator books=bookall.getChildElements();③
    SOAPEnvelope env=reply.getSOAPPart().getEnvelope();

    SOAPElement temps;
    Name name;

    while(books.hasNext())
    {

    BookVO bookVo=new BookVO();
    temps=(SOAPElement)books.next();
    String id=(String)(
     (temps.getAttributeValue(
     (Name)temps.getAllAttributes().next()
               )
     ));
    bookVo.setIsbn(id);④
    name=env.createName("name","books","http://hellking.webservice.com" );⑤
    bookVo.setName(
     ((SOAPElement)temps.getChildElements(name).next())
     .getValue());
    //System.out.println(bookVo.getName());

    name=env.createName("price" ,"books", "http://hellking.webservice.com" );
    bookVo.setPrice(Float.parseFloat(
     ((SOAPElement)temps.getChildElements(name).next())
     .getValue()));

    name=env.createName("category", "books", "http://hellking.webservice.com" );
    bookVo.setCategory(
     ((SOAPElement)temps.getChildElements(name).next())
     .getValue());

    name=env.createName("publisher","books","http://hellking.webservice.com" );
    bookVo.setPublisher(
     ((SOAPElement)temps.getChildElements(name).next())
     .getValue());

    name=env.createName("description","books","http://hellking.webservice.com" );
    bookVo.setDescription(
     ((SOAPElement)temps.getChildElements(name).next())
     .getValue());
    // bookVo.setDescription("kdjfkdjfj");

    name=env.createName("author","books","http://hellking.webservice.com" );
    Collection au=new ArrayList();
    Iterator auth=temps.getChildElements(name);
    while(auth.hasNext())
    {

     SOAPElement tempp=(SOAPElement)auth.next();

     //System.out.println("author:"+tempp.getValue());
     au.add(tempp.getValue());
    }
    bookVo.setAuthors(au);
    ret.add(bookVo);
    }
    bookVos=ret;
   }
   catch(Exception ex)
   {
    ex.printStackTrace();
  //錯誤處理…
   }
  }
}

注意上面例子中的①之類的標號,對應如圖6所示的元素。

圖6 SOAP消息

如上所示,在開發中,為了減少層之間的耦合性,我們一般不把SOAP消息直接發送到GUI客戶端,而是先處理,把它轉換成Java的基本數據類型或者Collection等類型。

GUI客戶端

最後我們看一下GUI客戶端怎麼使用JAXMDelegate來調用業務。

例程17 BookClientGUI部分代碼


public class BookClientGUI
{
JTable table;//表格

int amount;
   Collection books;//表示的數據
   BookBusiness business;
JButton search,findAll,findByCategory;
public BookClientGUI()
{
  business=new JAXMDelegate();//生成一個JAXMDelegate,
  books=business.getAllBooks();//獲得所有的圖書信息
  //amount=books.size();
  //System.out.println(amount);
}
public static void main(String[] args)
{
  new BookClientGUI().go();
}
public void go()
{
findByCategory=new JButton("按類別查找");
findByCategory.addMouseListener(new _MouseListener());

}
public void showResult()
{
  clearTable();
    Iterator book_i=books.iterator();
   …
}
public void clearTable()
{

}
class _MouseListener extends MouseAdapter
{
  public void mouseClicked(MouseEvent e)
  {
if(((JButton)e.getSource()).getLabel().equals("按類別查找"))
  {
   books=business.getBookByCategory((String)jTextField.getText());
   showResult();
  }
   …
}
}

JAXMDelegate實現了BookBusiness接口,BookClientGUI持有BookBusiness的實例,它通過這個實例來獲得信息。BookBusiness返回的信息都是java.util.Collection,這樣,給我們編程帶來了極大的便利性。

總結

本文結合一個具體的案例,介紹了使用JAXM來構造Web服務的方法。需要強調的是,如果使用點對點的消息發送模型,那麼服務端Servlet必須實現ReqRespListener接口,onMessage()方法將是開發服務端Servlet的重點任務。客戶端編程中,將按照以下步驟進行:

創建 SOAP 連接

創建 SOAP 消息

在SOAP消息裡增加數據

發送消息

對SOAP應答進行處理

下一步

經過以上的逐步的解釋,相信讀者對JAXM編程已經有一個比較深入發了解。您可以在這裡。

可以看出,通過使用一定的設計模式和接口,我們可以減少各層之間的耦合,在下一篇中,我將繼續深入分析JAXM設計的體系結構和模式。

本文配套源碼

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