Web Service簡介及開發實例
Web Service簡介及開發實例
作者:嶽鄉成
本文檔實例Dome基於的技術是:JSF + Jboss-seam-2.1.1.GA. + Jboss 4.2.3 GA + EJB 3.0 + Jboss ESB + My-SQL-5.0.8 + JDK 1.6。由於實例工程太大,不能放博客上,如有需要聯繫我,MSN:[email protected]。
QQ號碼:827650367。
一、 Web Service的簡介
1、 什麼是Web Service
Web services是建立可互操作的分佈式應用程序的新平臺。
Web service平臺是一套標準,它定義了應用程序如何在Web上實現互操作性。你可以用任何你喜歡的語言,在任何你喜歡的平臺上寫Web service ,只要我們可以通過Web service標準對這些服務進行查詢和訪問。
Web service平臺需要一套協議來實現分佈式應用程序的創建。任何平臺都有它的數據表示方法和類型系統。要實現互操作性,Web service平臺必須提供一套標準的類型系統,用於溝通不同平臺、編程語言和組件模型中的不同類型系統。
基礎的 Web Services 平臺是 XML + HTTP。
HTTP 協議是最常用的因特網協議。
XML 提供了一種可用於不同的平臺和編程語言之間的語言。
Web services 平臺是簡單的可共同操作的消息收發框架。它仍然缺少許多諸如安全和路由等重要的特性。但是,一旦 SOAP 變得更加高級,這些事項就會得到解決。
Web services 有望使應用程序更加容易通信。
★ Web services 把 Web 應用程序提升到了另外一個層面
通過使用 Web services,您的應用程序可向全世界發佈功能或消息。
Web services 使用 XML 來編解碼數據,並使用 SOAP 藉由開放的協議來傳輸數據。
通過 Web services,您的會計部門的 Win 2k 服務器可與 IT 供應商的 UNIX 服務器進行連接。
★ Web services 有兩種類型的應用
可重複使用的應用程序組件
有一些功能是不同的應用程序常常會用到的。那麼爲什麼要週而復始地開發它們呢?
Web services 可以把應用程序組件作爲服務來提供,比如匯率轉換、天氣預報或者甚至是語言翻譯等等。
比較理想的情況是,每種應用程序組件只有一個最優秀的版本,這樣任何人都可以在其應用程序中使用它。
連接現有的軟件
通過爲不同的應用程序提供一種鏈接其數據的途徑,Web services有助於解決協同工作的問題。
通過使用 Web services,您可以在不同的應用程序與平臺之間來交換數據。
★ Web Services 擁有兩種基本的元素。
它們是:SOAP及WSDL
(1)什麼是 SOAP?
SOAP 指簡易對象訪問協議
SOAP 是一種通信協議
SOAP 用於應用程序之間的通信
SOAP 是一種用於發送消息的格式
SOAP 被設計用來通過因特網進行通信
SOAP 獨立於平臺
SOAP 獨立於語言
SOAP 基於 XML
SOAP 很簡單並可擴展
SOAP 允許您繞過防火牆
SOAP 將作爲 W3C 標準來發展
(2)什麼是 WSDL?
WSDL 是基於 XML 的用於描述 Web Services 以及如何訪問 Web Services 的語言。
WSDL 指網絡服務描述語言
WSDL 使用 XML 編寫
WSDL 是一種 XML 文檔
WSDL 用於描述網絡服務
WSDL 也可用於定位網絡服務
WSDL 還不是 W3C 標準
2、 什麼是JWS
JWS(Java Web Service)是Java應用平臺上專門用於開發Web服務系統及面向服務系統的產品,它的最新版本是2.0,Java EE 5和Java SE 6都對JWS 2.0提供支持。
在JWS 2.0,Java定義了一系列新的標準,JMS本身也包含了一些工具,如JAX-WS 2.0,JAXB 2.0,JAXP 1.4,SAAJ 1.3以及WS-Metadata等。
面向服務的系統往往由多個具有不同的子功能的獨立組件構成,通過他們之間良好的相互協作,可以實現複雜的需求。面向服務系統的這個特點,要求獨立組建之間有公共的接口,這些用於交換數據的公共接口有良好的定義。由於要實現組件之間的數據通信,這些具有良好定義的接口就必須要被別的組件識別並正確理解,才能實現協作。
在定義了公共的接口後,還存在具體的數據交換問題,即雙方需要遵循一個共同的數據交換標準,這個數據交換標準稱爲協議。要在獨立的組件之間進行通信,需要一系列標準來嚴格規定數據通信的格式和規則。
Web Service的出現解決了上述問題,利用WSDL定義統一的接口格式,用SOAP消息統一輸入/輸出參數的統一格式。SOAP消息可以由多種途徑傳遞,比如,通過HTTP,SMTP及JMS協議傳遞。以HTTP爲例,在服務使用端,WSDL的接口定義可以通過HTTP-GET請求獲取,而SOAP應答消息及回覆消息的傳輸可以通過HTTP-POST請求來實現。這樣,基於WSDL和SOAP消息機制就可以滿足面向服務應用系統開發的需求。
Web Service平臺架構主要由三部分構成:序列化及反序列化子系統、調用子系統及發佈子系統。這三個子系統並不受具體語言的限制,也不受平臺和框架的限制,無論使用Java語言還是.NET語言,無論使用Axis平臺還是JWS來開發web服務,都會涉及這三個最基本的功能模塊。
(1)序列化及反序列化
在JMS中將一個Java對象轉化爲一個XML元素的過程,稱爲序列化。反之將一個XML元素轉化爲相應的Java
對象的過程,稱爲反序列化。序列化和反序列化的過程要依賴於Java類和XML-Scheme之間的映射關係,JWS有獨立
的序列化和反序列化子系統用來負責完成這些映射及轉化。
在web服務的客戶端,序列化過程將參數轉化爲xml結點,進而封裝成Soap請求消息,發送至服務器端的web服務端點。獲得返回值時,反序列化過程啓動,它將返回值從SOAP消息中指定的xml結點中取出,然後將它轉化爲客戶端相對應的Java對象。
轉化規則的定義在JWS中是通過JAXB的註釋來完成的。新版的JAXB簡化了綁定規則的描述,它允許直接將規則以註解的形式寫入Java類。
例如:
@Entity
@Name("hotel")
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
@XmlType(name="", propOrder = {
"id",
"img",
"name",
"address",
"city",
"state",
"zip",
"country",
"price",
"ipAddress"
})
@XmlRootElement(name="Hotel")
public class Hotel implements Serializable
{
private Long id;
private String img;
private String name;
private String address;
private String city;
private String state;
private String zip;
private String country;
private BigDecimal price;
private String ipAddress = "192.168.1.112";
@Id @GeneratedValue
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
@Length(max = 50)
@NotNull
public String getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
@Length(max=50) @NotNull
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
@Length(max=100) @NotNull
public String getAddress()
{
return address;
}
public void setAddress(String address)
{
this.address = address;
}
@Length(max=40) @NotNull
public String getCity()
{
return city;
}
public void setCity(String city)
{
this.city = city;
}
@Length(min=4, max=6) @NotNull
public String getZip()
{
return zip;
}
public void setZip(String zip)
{
this.zip = zip;
}
@Length(min=2, max=10) @NotNull
public String getState()
{
return state;
}
public void setState(String state)
{
this.state = state;
}
@Length(min=2, max=40) @NotNull
public String getCountry()
{
return country;
}
public void setCountry(String country)
{
this.country = country;
}
@Column(precision=6, scale=2)
public BigDecimal getPrice()
{
return price;
}
public void setPrice(BigDecimal price)
{
this.price = price;
}
@Transient
public String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
@Override
public String toString()
{
return "Hotel(" + name + "," + address + "," + city + "," + zip + ")";
}
}
(2)服務調用的過程
在面向服務的分佈式系統中,一般將傳統的客戶服務器框架中的客戶端Client稱爲服務的消費者,而將服務器端稱爲服務的提供者。
按照現在的web服務標準,一個服務被調用時,在服務的提供端大致的處理過程如下:
①接受並預處理SOAP請求消息,例如效驗,處理SOAP消息報文頭。
②從消息中獲取該消息希望調用的接口和具體操作。
③利用web服務提供的支持,找到具體的實現對象,並調用該對象的接口。這個對象可以由不同的語言實現。JWS支持從WSDL到Java的映射,可以通過WSDL找到與它相對應的Java服務端點實現類。
④使用序列化工具的反序列化過程,將SOAP請求消息中的服務請求參數取出,傳遞給步驟3中的目標對象的相應函數。
⑤目標Java對象執行相應的操作,將計算的結果以對象形式返回。
⑥使用序列化工具的序列化過程,根據wsdl中的定義,將步驟5中的結果對象序列化成XML元素,並封裝到SOAP回覆消
息中。
⑦將步驟6中的SOAP回覆消息發送回服務調用端。
與之對應的,在服務使用端的調用過程如下:
①首先創建服務端點接口對象SEI(Service Endpoint Interface),在Web服務客戶端一般都會有相應的工廠類完成SEI對象的實例化。在JWS中,SEI對象一般是由Java代理來實現的。
②客戶端通過SEI調用其中封裝的web服務接口。
③利用序列化工具的序列化過程,根據WSDL的定義,將客戶端的調用接口的參數轉化成XML元素,再將該元素封裝在SOAP請求消息裏。
④在同步模式下,在SOAP請求消息發出後,客戶端會等待SOAP應答消息;異步模式下,客戶端順序執行後續代碼,直到通過監聽器接收到SOAP請求消息裏。
⑤解析從服務器端獲得的SOAP應答消息,使用序列化工具中的反序列化過程,將SOAP應答消息中的數據轉化成客戶端對象,該對象的值就是被調用服務的返回值。
(3)web服務的發佈
以JMS爲例,web服務發佈系統的主要功能
①以URL的形式公開被髮布的WEB服務的WSDL文件,並綁定SOAP請求消息和Java目標類。
②發佈JAVA目標文件(例如Java Object文件,WAR文件,JAR文件及相關配置文件等)。
③配置序列化和反序列化子系統。
④配置web服務端點監聽器和SOAP消息預處理進程。
二、 web service的幾種開發及調用方式
(1)用EJB容器模型開發Web Service,用wsimport工具生成輔助類進行調用。
實例如下(實例中應用的EJB容器爲Jboss,所有代碼是基於JBoss Seam架構來編寫的):
①服務的提供者端<提供服務端的代碼>:
服務的提供者端的任務是暴露一個web service以供外部使用,本例的web service是通過JavaEE平臺提供的@java.jws.WebService註解來實現的。
以下代碼的路徑是VSS上 前期調查文件夾下SOA\web service\用EJB容器模型開發Web Service,用wsimport工具生成輔助類進行調用\tele\book\src\org\jboss\seam\example\booking\ws\impl,用EJB容器模型開發Web Service,用wsimport工具生成輔助類進行調用文件夾下包括兩個文件夾:local,tele。tele文件夾下放服務的提供者端的工程和打好的EAR包。local文件夾下放服務的消費者端的工程和打好的EAR包。
package org.jboss.seam.example.booking.ws.impl;
import static org.jboss.seam.ScopeType.SESSION;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.jws.WebService;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.example.booking.Hotel;
import org.jboss.seam.example.booking.User;
import org.jboss.seam.example.booking.ws.HotelObject;
@Stateless
/**實例是使用了兩個JBoss應用服務器,該代碼是服務提供者端的代碼,所以使用了@Remote註解,實現遠程接口。。*/
@Remote (HotelObject.class)
@WebService
public class HotelObjectImpl implements HotelObject {
@PersistenceContext
private EntityManager em;
public List<Hotel> getHotelObject(String hotelName) {
List<Hotel> results = em.createQuery("select h from Hotel h where h.name like"+ " '%"+hotelName+"%'")
.getResultList();
if(results.size()>0){
return results;
}
return null;
}
public String updateHotelObject(Hotel h){
try{
System.out.println(h);
System.out.println(h.getName());
em.merge(h);
}catch(Exception e ){
e.printStackTrace();
return "error";
}
return "success";
}
}
上面的類繼承了一個接口,接口類的路徑是:VSS上前期調查文件夾下SOA\web service\用EJB容器模型開發Web Service,用wsimport工具生成輔助類進行調用\tele\book\src\org\jboss\seam\example\booking\ws。
package org.jboss.seam.example.booking.ws;
import java.util.List;
import org.jboss.seam.example.booking.Hotel;
public interface HotelObject {
public List<Hotel> getHotelObject(String hotelName);
public String updateHotelObject(Hotel h);
}
上面的@WebService註解指名這是一個Web Service。打開Ant文件,執行desploy業務。部署完成後,通過JBoss管理平臺查看剛纔部署的Web Service,輸入http://localhost:8080/jbossws/進入JBoss Web Service的查看界面,可以看到View a list of deployed services鏈接,單擊View a list of deployed services鏈接後,可以查看已經發布的Web Service的URL地址,單擊該地址就可以查看生成的WSDL文。
這就是服務提供端的主要代碼,先用註解定義了一個web服務,這個web服務提供了兩個方法,這兩個方法可以接收從遠程傳過來的參數。服務的消費着可以通過上面的URL地址訪問該服務。
(2)服務的消費者端<使用服務端的代碼>
要調用web服務,我們可以通過好多方式,其中的一種方式是通過wsimport工具(在JDK1.6中,Sun已經爲用戶提供了wsimport工具)生成輔助類來調用web服務。
在VSS上web Service文件夾中有一個叫做jaxws-ri的文件包裏面包含了wsimport工具,本例的輔助類就是用它直接生成的。要用wsimport工具生成輔助類,首先要在dos環境下打開wsimport工具所在的目錄,比如wsimport工具的本地路徑:
Dos下打開wsimport工具所在的路徑。
在wsimport路徑下輸入如下內容:
wsimport.bat -keep -d ../build/classes/client http://192.168.1.106:8080/WsHelloWorld/HelloWorldBean?wsdl
其中../build/classes/client表示生成的輔助類存放的路徑,http://192.168.1.106:8080/WsHelloWorld/HelloWorldBean?wsdl表示要調用的web服務的WSDL文地址,其中192.168.1.106表示服務提供端的IP地址。
生成的輔助類如下圖所示:
把生成的輔助java類拷貝到我們的工程中
然後實例化一個生成的輔助類的對象,通過該對象調用服務提供者提供的方法<即getHotelObjec()>,去檢索符合條件的記錄。
該類位於VSS上如下路徑下;
SOA\Web Servic\用EJB容器模型開發Web Service,用wsimport工具生成輔助類進行調用\local\book\src\org\jboss\seam\example\booking
package org.jboss.seam.example.booking;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Factory;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.datamodel.DataModel;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.example.booking.ws.impl.Hotel;
import org.jboss.seam.example.booking.ws.impl.HotelObjectImpl;
import org.jboss.seam.example.booking.ws.impl.HotelObjectImplService;
@Stateful
@Name("teledataHotelSearch")
@Scope(ScopeType.SESSION)
@Restrict("#{identity.loggedIn}")
public class TeledateHotelSearchingAction implements TeledateHotelSearching
{
@PersistenceContext
private EntityManager em;
private String searchString;
@DataModel
private List<Hotel> telehotels;
public void find()
{
queryHotels();
}
private void queryHotels() {
/**實例化生成的輔助類的對象,通過該對象調用遠程的web service。*/
HotelObjectImplService service;
try
{
service = new HotelObjectImplService();
HotelObjectImpl hotelObjectImpl = service.getHotelObjectImplPort();
List<Hotel> result = hotelObjectImpl.getHotelObject(this.getSearchString());
System.out.println(result.size());
if(result.size()>0){
telehotels = result;
}else{
telehotels =null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getSearchString()
{
return searchString;
}
public void setSearchString(String searchString)
{
this.searchString = searchString;
}
@Remove
public void destroy() {}
@Override
public void cancel() {
// TODO 自動生成的方法存根
}
}
還有一個類是爲了保存修改了的服務提供者(即遠程的Web Service)返回的Hotel對象,必須調用服務提供者提供的保存Hotel對象的方法<即updateHotelObject ()>。
package org.jboss.seam.example.booking;
import static javax.persistence.PersistenceContextType.EXTENDED;
import java.util.Calendar;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.core.Events;
import org.jboss.seam.example.booking.ws.impl.HotelObjectImpl;
import org.jboss.seam.example.booking.ws.impl.HotelObjectImplService;
import org.jboss.seam.faces.FacesMessages;
import org.jboss.seam.log.Log;
@Stateful
@Name("saveTeteDataHotel")
@Restrict("#{identity.loggedIn}")
public class SaveTeteDataHotelAction implements SaveTeteDataHotel
{
@Begin
public void seveHotel(Hotel hotel)
{
HotelObjectImplService service;
try
{
service = new HotelObjectImplService();
HotelObjectImpl hotelObjectImpl = service.getHotelObjectImplPort();
String result = hotelObjectImpl.updateHotelObject(this.conversionHotel(hotel));
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
public org.jboss.seam.example.booking.ws.impl.Hotel conversionHotel(Hotel hotel){
org.jboss.seam.example.booking.ws.impl.Hotel h = new org.jboss.seam.example.booking.ws.impl.Hotel();
h.setAddress(hotel.getAddress());
h.setCity(hotel.getCity());
h.setCountry(hotel.getCountry());
h.setId(hotel.getId());
h.setImg(hotel.getImg());
h.setName(hotel.getName());
h.setPrice(hotel.getPrice());
h.setState(hotel.getState());
h.setZip(hotel.getZip());
return h;
}
@End
public void cancel() {}
@Remove
public void destroy() {}
}
以上類中有一點值得注意,就是服務消費者中的Hotel對象不能直接用於保存,必須經過轉換,轉換成生成的輔助類中的org.jboss.seam.example.booking.ws.impl.Hotel類,才能進行保存,上面的conversionHotel(Hotel hotel)方法就是爲了進行該轉換。
以上就是該種使用web service的幾個重要的點,通過以上各步就可以訪問遠程發佈的web服務,完整的過程請參考Vss上Dome工程。
(2)利用SAAJ協議動態生成SOAP消息訪問服務提供者端的web service。
第一種方式是利用wsimpot工具來生成輔助類來實現對服務提供者端的web service的訪問,由於生成的輔助類特別多,而且靈活性差。下面我們介紹用SAAJ協議動態生成SOAP消息來訪問服務提供者端的web service。
①服務提供者端的代碼
該類的在VSS上:
EHR\SOA\SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\ws\impl下
package org.jboss.seam.example.booking.ws.impl;
import static org.jboss.seam.ScopeType.SESSION;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.example.booking.Hotel;
import org.jboss.seam.example.booking.User;
import org.jboss.seam.example.booking.ws.HotelObject;
@Stateless
@Remote (HotelObject.class)
@WebService(name = "HotelObject", targetNamespace = "http://tower/ehr_DEV")
public class HotelObjectImpl implements HotelObject {
@PersistenceContext
private EntityManager em;
@WebResult(name="Hotel")
public List<Hotel> getHotelObject(String hotelName) {
List<Hotel> results = em.createQuery("select h from Hotel h where h.name like"+ " '%"+hotelName+"%'")
.getResultList();
if(results.size()>0){
return results;
}
return null;
}
public String updateHotelObject(Hotel h){
try{
System.out.println(h);
System.out.println(h.getName());
em.merge(h);
}catch(Exception e ){
e.printStackTrace();
return "error";
}
return "success";
}
}
該web服務提供了兩個方法,getHotelObject(String hotelName)方法接收遠程的的參數,通過該參數查找符合條件的Hotel對象;updateHotelObject(Hotel h)方法用來保存修改後的對象。
②服務調用者端的代碼
該類的在VSS上:
EHR\SOA\ SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\soa\esb\impl下
本例使用了Jboss ESB,其實服務調用端首先發送請求到Jboss ESB服務器上,Jboss ESB服務器上也定義了一些web服務,來接受這些請求,接受到請求消息後,對消息進行進一步的加工或者直接把消息轉發給真正的消息提供者端。
下面的代碼展示了服務消費端如何通過ESB服務器,去調用服務提供者端提供的服務。
package org.jboss.seam.example.booking.soa.esb.impl;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.soap.SOAPMessage;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.datamodel.DataModel;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.example.booking.Hotel;
import org.jboss.seam.example.booking.commom.collectionbinder.ListHotel;
import org.jboss.seam.example.booking.commom.esb.BuildSoapMessage;
import org.jboss.seam.example.booking.commom.esb.SendSoapMessageToEsb;
import org.jboss.seam.example.booking.commom.esb.impl.BuildSoapMessageImpl;
import org.jboss.seam.example.booking.commom.esb.impl.SendSoapMessageToEsbImpl;
import org.jboss.seam.example.booking.commom.formattransform.TransformMessageFormat;
import org.jboss.seam.example.booking.commom.formattransform.TransformString;
import org.jboss.seam.example.booking.commom.formattransform.impl.TransformMessageFormatImpl;
import org.jboss.seam.example.booking.commom.formattransform.impl.TransformStringImpl;
import org.jboss.seam.example.booking.soa.esb.TeledateHotelSearching;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
@Stateful
@Name("teledataHotelSearch")
@Scope(ScopeType.SESSION)
@Restrict("#{identity.loggedIn}")
public class TeledateHotelSearchingImpl implements TeledateHotelSearching
{
@PersistenceContext
private EntityManager em;
private String searchString;
@DataModel
private List<Hotel> telehotels;
public void find()
{
getHotelObject(this.getSearchString());
}
public void getHotelObject(String searchString) {
try{
BuildSoapMessage bsm = new BuildSoapMessageImpl();
SOAPMessage sm = bsm.getMessage("sayGoodbye", "http://webservice_producer/goodbyeworld", searchString);
TransformMessageFormat tmf = new TransformMessageFormatImpl();
String stringSOAPMessage = tmf.transformToString(sm);
SendSoapMessageToEsb smtEsb = new SendSoapMessageToEsbImpl();
Object o = smtEsb.sendMessageToJBRListener("http", "8765", stringSOAPMessage);
System.out.println("wo de ma ya!!!"+o.toString());
TransformString tfs = new TransformStringImpl();
Document doc = tfs.transformStringToDocument(o.toString());
NodeList nodeList = doc.getElementsByTagName("ListHotel");
Element element = (Element)nodeList.item(0);
String returnContent = element.getTextContent();
if(returnContent.length()>0){
String returnContentAnd = "<ListHotel>"+returnContent+"</ListHotel>";
InputStream inputStream = new ByteArrayInputStream(returnContentAnd.getBytes());
JAXBContext context = JAXBContext.newInstance(ListHotel.class);
try {
Unmarshaller um = context.createUnmarshaller();
ListHotel hl = (ListHotel)um.unmarshal(inputStream);
System.out.println("I'm King!"+hl.getElements());
telehotels = hl.getElements();
} catch (JAXBException e) {
e.printStackTrace();
}
}else{
telehotels = null;
}
}catch(Exception e){
e.printStackTrace();
telehotels = null;
} catch (Throwable e) {
e.printStackTrace();
}
}
public String getSearchString()
{
return searchString;
}
public void setSearchString(String searchString)
{
this.searchString = searchString;
}
@Remove
public void destroy() {}
@Override
public void cancel() {
searchString = "";
telehotels = null;
}
}
在getHotelObject(String searchString)方法中,首先通過BuildSoapMessage類提供的getMessage()方法生成要發送的SOAP消息。(調用的代碼如下:)
BuildSoapMessage bsm = new BuildSoapMessageImpl();
SOAPMessage sm = bsm.getMessage("sayGoodbye", "http://webservice_producer/goodbyeworld", searchString);
BuildSoapMessage類及代碼如下:
該類的在vss上的路徑如下:
SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\commom\esb\impl
package org.jboss.seam.example.booking.commom.esb.impl;
import java.util.HashMap;
import java.util.Map;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.Name;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import org.jboss.seam.example.booking.commom.esb.BuildSoapMessage;
public class BuildSoapMessageImpl implements BuildSoapMessage{
public static final SOAPConnection getSOAPConnection(){
SOAPConnectionFactory soapConnFactory;
SOAPConnection connection;
try {
soapConnFactory = SOAPConnectionFactory.newInstance();
connection = soapConnFactory.createConnection();
return connection;
} catch (UnsupportedOperationException e) {
e.printStackTrace();
return null;
} catch (SOAPException e) {
e.printStackTrace();
return null;
}
}
public static final SOAPMessage getSOAPMessage(){
MessageFactory messageFactory;
SOAPMessage message;
try {
messageFactory = MessageFactory.newInstance();
message = messageFactory.createMessage();
return message;
} catch (SOAPException e) {
e.printStackTrace();
return null;
}
}
public Name getName(String param){
SOAPFactory soapFactory;
try {
soapFactory = SOAPFactory.newInstance();
Name name = soapFactory.createName(param); //"arg0"
return name;
} catch (SOAPException e) {
e.printStackTrace();
return null;
}
}
public SOAPElement getBodyElement(SOAPMessage message,String param1,String param2,String param3){
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope envelope;
SOAPBody body;
SOAPElement bodyElement;
try {
envelope = soapPart.getEnvelope();
body = envelope.getBody();
bodyElement = body.addChildElement(envelope.createName(param1,//"sayHello" ,
param1,//"ns1",
param1));//"http://webservice_consumer1/helloworld"));
bodyElement.setEncodingStyle(SOAPConstants.URI_NS_SOAP_ENCODING);
return bodyElement;
} catch (SOAPException e) {
e.printStackTrace();
return null;
}
}
public SOAPElement addTextNode(SOAPMessage message,String param1,String param2,String param3){
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope envelope;
SOAPBody body;
SOAPElement bodyElement;
try {
envelope = soapPart.getEnvelope();
body = envelope.getBody();
bodyElement = body.addChildElement(envelope.createName(param1,//"sayHello" ,
param1,//"ns1",
param1));//"http://webservice_consumer1/helloworld"));
bodyElement.setEncodingStyle(SOAPConstants.URI_NS_SOAP_ENCODING);
return bodyElement;
} catch (SOAPException e) {
e.printStackTrace();
return null;
}
}
public SOAPMessage getMessage(String methodName,String targetNamespace,String messageContext) throws SOAPException,Exception{
//傳送參數需要創建Name
SOAPFactory soapFactory = SOAPFactory.newInstance();
//Next, create the actual message
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage message = messageFactory.createMessage();
//獲得一個SOAPPart對象
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope envelope = soapPart.getEnvelope();
SOAPBody body = envelope.getBody();
//Create the main element and namespace
SOAPElement bodyElement =
body.addChildElement(envelope.createName(methodName ,
"tower",
targetNamespace));
bodyElement.setEncodingStyle(SOAPConstants.URI_NS_SOAP_ENCODING);
//傳送參數新建一個Name對象
Name name = soapFactory.createName("message");
SOAPElement symbol = bodyElement.addChildElement(name);
symbol.addTextNode(messageContext);
message.saveChanges();
return message;
}
public SOAPMessage getMessage(String methodName,String targetNamespace,HashMap<String,String> hm) throws SOAPException,Exception{
//傳送參數需要創建Name
SOAPFactory soapFactory = SOAPFactory.newInstance();
//Next, create the actual message
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage message = messageFactory.createMessage();
//獲得一個SOAPPart對象
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope envelope = soapPart.getEnvelope();
SOAPBody body = envelope.getBody();
//Create the main element and namespace
SOAPElement bodyElement =
body.addChildElement(envelope.createName(methodName ,
"ns1",
targetNamespace));
bodyElement.setEncodingStyle(SOAPConstants.URI_NS_SOAP_ENCODING);
//傳送參數新建一個Name對象
Name name = soapFactory.createName("arg0");
SOAPElement symbol = bodyElement.addChildElement(name);
for (Map.Entry<String, String> m : hm.entrySet()) {
System.out.println("HashMap" + m.getKey() + ":" + m.getValue());
Name symbolName = soapFactory.createName(m.getKey());
SOAPElement symbolNameSOAPElemen = symbol.addChildElement(symbolName);
symbolNameSOAPElemen.addTextNode(m.getValue());
}
message.saveChanges();
return message;
}
}
在本例中Jboss esb服務器提供的web服務只能接受String類型的參數,所以要把soap消息進行格式轉換。在服務的消費端通過以下的代碼調用格式轉換方法:
TransformMessageFormat tmf = new TransformMessageFormatImpl();
String stringSOAPMessage = tmf.transformToString(sm);
格式轉換的類和方法如下:
該類的路徑在:
SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\commom\formattransform\impl
package org.jboss.seam.example.booking.commom.formattransform.impl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import org.jboss.internal.soa.esb.util.StreamUtils;
import org.jboss.seam.example.booking.commom.formattransform.TransformMessageFormat;
public class TransformMessageFormatImpl implements TransformMessageFormat{
public String transformToString(SOAPMessage sm) throws SOAPException, IOException{
ByteArrayOutputStream s = new ByteArrayOutputStream();
sm.writeTo(s);
byte[] buf=s.toByteArray();
ByteArrayInputStream bin=new ByteArrayInputStream(buf);
String msg = new String(StreamUtils.readStream(bin));
return msg;
}
}
把轉換後的Soap消息發送給Jboss ESB服務器,在服務消費者端的調用過程代碼如下:
SendSoapMessageToEsb smtEsb = new SendSoapMessageToEsbImpl();
Object o = smtEsb.sendMessageToJBRListener("http", "8765", stringSOAPMessage);
發送消息到Jboss ESB服務器上的類和方法如下:
該類在VSS上的路徑如下:
SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\commom\esb\impl
package org.jboss.seam.example.booking.commom.esb.impl;
import org.jboss.remoting.Client;
import org.jboss.remoting.InvokerLocator;
import org.jboss.seam.example.booking.commom.esb.SendSoapMessageToEsb;
public class SendSoapMessageToEsbImpl implements SendSoapMessageToEsb {
public static String ESBSERVICEIP="192.168.1.101";
public Object sendMessageToJBRListener(String protocol, String port, String message) throws Throwable {
String locatorURI = protocol + "://"+ESBSERVICEIP+":" + port;
InvokerLocator locator = new InvokerLocator(locatorURI);
Client remotingClient = null;
try {
remotingClient = new Client(locator);
remotingClient.connect();
Object response = remotingClient.invoke(message);
return response;
} finally {
if(remotingClient != null) {
remotingClient.disconnect();
}
}
}
}
public static String ESBSERVICEIP="192.168.1.101";這個IP地址表示Jboss ESB服務器的IP地址。
String locatorURI = protocol + "://"+ESBSERVICEIP+":" + port;這行代碼可以拼成訪問Jboss ESB上發佈的Web服務的URL地址。
我們接着看調用端的代碼,從Jboss ESB的返回值(其實是Jboss ESB服務器上發佈的Web服務再通過轉發調用服務提供者端的Web服務,從而得到返回值,服務提供者端的返回值首先把值傳回Jboss ESB服務器,Jboss ESB服務器再把值返回到服務調用端。)也是一個Object對象,我們要把這個Object對象轉換爲我們應用中的真實的對象(比如例子中的ListHotel對象),這個轉換我使用了JAXB標準,這個標準主要用於.xml和Java對象之間的互轉,因爲返回值是SOAP消息,是.xml形式的,同過JAXB標準可以很方便的轉換成我們想要的java類(比如ListHotel),在調用端的代碼如下:
TransformString tfs = new TransformStringImpl();
Document doc = tfs.transformStringToDocument(o.toString());
NodeList nodeList = doc.getElementsByTagName("ListHotel");
Element element = (Element)nodeList.item(0);
String returnContent = element.getTextContent();
if(returnContent.length()>0){
String returnContentAnd = "<ListHotel>"+returnContent+"</ListHotel>";
InputStream inputStream = new ByteArrayInputStream(returnContentAnd.getBytes());
JAXBContext context = JAXBContext.newInstance(ListHotel.class);
try {
Unmarshaller um = context.createUnmarshaller();
ListHotel hl = (ListHotel)um.unmarshal(inputStream);
System.out.println("I'm King!"+hl.getElements());
telehotels = hl.getElements();
} catch (JAXBException e) {
e.printStackTrace();
}
}else{
telehotels = null;
}
}catch(Exception e){
e.printStackTrace();
telehotels = null;
} catch (Throwable e) {
e.printStackTrace();
}
從Jboss ESB服務器的返回值先要經過轉換,把它轉換爲Document類型,代碼如下:
TransformString tfs = new TransformStringImpl();
Document doc = tfs.transformStringToDocument(o.toString());
轉換的類和方法如下:
該類在VSS上的路徑如下:
SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\commom\formattransform\impl
package org.jboss.seam.example.booking.commom.formattransform.impl;
import java.io.IOException;
import java.io.StringReader;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.jboss.seam.example.booking.commom.formattransform.TransformString;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class TransformStringImpl implements TransformString{
public Document transformStringToDocument(String s){
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(s)));
return doc;
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public String[] getObjectArrayByESBReturnContent(String s){
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(s)));
NodeList nodeList = doc.getElementsByTagName("return");
Element element = (Element)nodeList.item(0);
String returnContent = element.getTextContent();
if(returnContent.length()>0){
String[] getObjectContest = returnContent.split("</O>");
return getObjectContest;
}else{
return null;
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public String[] getPropertyArrayByStringObject(String returnContent){
if(returnContent.length()>0){
String[] getObjectContest = returnContent.split("</A&V>");
return getObjectContest;
}else{
return null;
}
}
}
轉換後的Document對象要進行一下截取,重新組裝變成適合JAXB轉換的XML形式,這個過程如下:
NodeList nodeList = doc.getElementsByTagName("ListHotel");
Element element = (Element)nodeList.item(0);
String returnContent = element.getTextContent();
String returnContentAnd = "<ListHotel>"+returnContent+"</ListHotel>";
InputStream inputStream = new ByteArrayInputStream(returnContentAnd.getBytes());
把返回值轉換爲先轉換爲InputStream對象,這個InputStream對象就符合轉換的形式了,可以進行轉換了。
要利用JAXB規範進行這樣的轉換,要進行兩步:
①用@XmlRootElement註解標註要轉換成的Java類。
Java類如下:
這個Java類在VSS上的路徑是:
SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\commom\collectionbinder
package org.jboss.seam.example.booking.commom.collectionbinder;
import java.util.List;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import org.jboss.seam.example.booking.Hotel;
@XmlRootElement(name="ListHotel")
public class ListHotel {
private List<Hotel> hotel;
public ListHotel() {
}
public ListHotel(List<Hotel> hotel) {
this.hotel = hotel;
}
@XmlElementRef
public List<Hotel> getElements() {
return hotel;
}
public void setElements(List<Hotel> hotel) {
this.hotel = hotel;
}
}
②通過JAXB提供的編組機制,把xml字符轉換爲Java對象。代碼如下:
JAXBContext context = JAXBContext.newInstance(ListHotel.class);
try {
Unmarshaller um = context.createUnmarshaller();
ListHotel hl = (ListHotel)um.unmarshal(inputStream);
System.out.println("I'm King!"+hl.getElements());
telehotels = hl.getElements();
} catch (JAXBException e) {
e.printStackTrace();
}
以上就是我用SAAJ實現Web Service開發的詳細過程,文檔只是一個參考的作用,要靈活掌握這種方式要翻越其他的資料,認真思考。
這個實例中牽涉到了JBoss ESB服務器,我沒有進行詳細的介紹,我會在下一個文檔中仔細討論該部分內容。要很好的理解該實例請參考我的Jboss ESB的文檔及Vss上的相關代碼。
以上是通過Jboss ESB服務器作爲中間件轉發從消息消費端到消息使用端的的消息的過程,SOA(V1.2)這個文件夾下的工程中還包括了一個服務消費者端直接訪問服務提供者端的實例。即:
該類在VSS上的路徑爲:
SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\soa\esb\impl
package org.jboss.seam.example.booking.soa.esb.impl;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.example.booking.Hotel;
import org.jboss.seam.example.booking.commom.esb.BuildSoapMessage;
import org.jboss.seam.example.booking.commom.esb.SendSoapMessage;
import org.jboss.seam.example.booking.commom.esb.impl.BuildSoapMessageImpl;
import org.jboss.seam.example.booking.commom.esb.impl.SendSoapMessageImpl;
import org.jboss.seam.example.booking.commom.formattransform.TransformMethodName;
import org.jboss.seam.example.booking.commom.formattransform.impl.TransformMethodNameImpl;
import org.jboss.seam.example.booking.soa.esb.TeteDataHotelSave;
@Stateful
@Name("saveTeteDataHotel")
@Restrict("#{identity.loggedIn}")
public class TeteDataHotelSaveImpl implements TeteDataHotelSave
{
@In(required=false)
@Out(required=false)
private Hotel hotel;
@Begin
public void selectHotel(Hotel selectedHotel)
{
//Hotel h = new Hotel();
hotel = selectedHotel;
}
public void seveHotel(Hotel hotel)
{
String ipAddress = hotel.getIpAddress();
System.out.println("yuexiangcheng,test!!!!!========"+ipAddress);
TransformMethodName tmn = new TransformMethodNameImpl();
Field[] f = hotel.getClass().getDeclaredFields();
HashMap<String,String> hm = new HashMap();
for(int i = 0;i< f.length;i++){
try {
String attributeName = f[i].getName();
System.out.println("attributeNameattributeNameattributeNameattributeNameattributeNameattributeName"+attributeName);
String motherName = tmn.getGetMethodByAttribute(attributeName);
Method method = hotel.getClass().getDeclaredMethod(motherName,null);
String attributeValue = method.invoke(hotel, null).toString();
System.out.println("attributeValueattributeValueattributeValueattributeValueattributeValueattributeValue"+attributeValue);
hm.put(attributeName, attributeValue);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
BuildSoapMessage bsm = new BuildSoapMessageImpl();
SendSoapMessage ssm = new SendSoapMessageImpl();
try {
SOAPMessage sm = bsm.getMessage("updateHotelObject", "http://tower/ehr_DEV", hm);
SOAPMessage result = ssm.send(sm, "http://"+ipAddress+":8080/jboss-seam-booking-jboss-seam-booking/HotelObjectImpl");
TransformerFactory transformerFactory =
TransformerFactory.newInstance();
Transformer transformer =
transformerFactory.newTransformer();
Source sourceContent = result.getSOAPPart().getContent();
StreamResult resultPrint = new StreamResult(System.out);
transformer.transform(sourceContent, resultPrint);
System.out.println();
} catch (SOAPException e) {
// TODO 自動生成 catch 塊
e.printStackTrace();
} catch (Exception e) {
// TODO 自動生成 catch 塊
e.printStackTrace();
}
}
@End
public void cancel() {}
@Remove
public void destroy() {}
}
該過程是服務消費者端修改了從服務提供者端檢索到的數據,並對其進行了修改再保持到服務提供者端。具體的過程和上例查不多,請詳細研究在VSS上的代碼。值得注意的是該例中用到了JAXB的解組功能。
以上就是我實現Web Service的兩種方法,因爲能力有限,所以寫的不好,僅供參考。希望對大家有幫助,謝謝。
作者:嶽鄉成
本文檔實例Dome基於的技術是:JSF + Jboss-seam-2.1.1.GA. + Jboss 4.2.3 GA + EJB 3.0 + Jboss ESB + My-SQL-5.0.8 + JDK 1.6。由於實例工程太大,不能放博客上,如有需要聯繫我,MSN:[email protected]。
QQ號碼:827650367。
一、 Web Service的簡介
1、 什麼是Web Service
Web services是建立可互操作的分佈式應用程序的新平臺。
Web service平臺是一套標準,它定義了應用程序如何在Web上實現互操作性。你可以用任何你喜歡的語言,在任何你喜歡的平臺上寫Web service ,只要我們可以通過Web service標準對這些服務進行查詢和訪問。
Web service平臺需要一套協議來實現分佈式應用程序的創建。任何平臺都有它的數據表示方法和類型系統。要實現互操作性,Web service平臺必須提供一套標準的類型系統,用於溝通不同平臺、編程語言和組件模型中的不同類型系統。
基礎的 Web Services 平臺是 XML + HTTP。
HTTP 協議是最常用的因特網協議。
XML 提供了一種可用於不同的平臺和編程語言之間的語言。
Web services 平臺是簡單的可共同操作的消息收發框架。它仍然缺少許多諸如安全和路由等重要的特性。但是,一旦 SOAP 變得更加高級,這些事項就會得到解決。
Web services 有望使應用程序更加容易通信。
★ Web services 把 Web 應用程序提升到了另外一個層面
通過使用 Web services,您的應用程序可向全世界發佈功能或消息。
Web services 使用 XML 來編解碼數據,並使用 SOAP 藉由開放的協議來傳輸數據。
通過 Web services,您的會計部門的 Win 2k 服務器可與 IT 供應商的 UNIX 服務器進行連接。
★ Web services 有兩種類型的應用
可重複使用的應用程序組件
有一些功能是不同的應用程序常常會用到的。那麼爲什麼要週而復始地開發它們呢?
Web services 可以把應用程序組件作爲服務來提供,比如匯率轉換、天氣預報或者甚至是語言翻譯等等。
比較理想的情況是,每種應用程序組件只有一個最優秀的版本,這樣任何人都可以在其應用程序中使用它。
連接現有的軟件
通過爲不同的應用程序提供一種鏈接其數據的途徑,Web services有助於解決協同工作的問題。
通過使用 Web services,您可以在不同的應用程序與平臺之間來交換數據。
★ Web Services 擁有兩種基本的元素。
它們是:SOAP及WSDL
(1)什麼是 SOAP?
SOAP 指簡易對象訪問協議
SOAP 是一種通信協議
SOAP 用於應用程序之間的通信
SOAP 是一種用於發送消息的格式
SOAP 被設計用來通過因特網進行通信
SOAP 獨立於平臺
SOAP 獨立於語言
SOAP 基於 XML
SOAP 很簡單並可擴展
SOAP 允許您繞過防火牆
SOAP 將作爲 W3C 標準來發展
(2)什麼是 WSDL?
WSDL 是基於 XML 的用於描述 Web Services 以及如何訪問 Web Services 的語言。
WSDL 指網絡服務描述語言
WSDL 使用 XML 編寫
WSDL 是一種 XML 文檔
WSDL 用於描述網絡服務
WSDL 也可用於定位網絡服務
WSDL 還不是 W3C 標準
2、 什麼是JWS
JWS(Java Web Service)是Java應用平臺上專門用於開發Web服務系統及面向服務系統的產品,它的最新版本是2.0,Java EE 5和Java SE 6都對JWS 2.0提供支持。
在JWS 2.0,Java定義了一系列新的標準,JMS本身也包含了一些工具,如JAX-WS 2.0,JAXB 2.0,JAXP 1.4,SAAJ 1.3以及WS-Metadata等。
面向服務的系統往往由多個具有不同的子功能的獨立組件構成,通過他們之間良好的相互協作,可以實現複雜的需求。面向服務系統的這個特點,要求獨立組建之間有公共的接口,這些用於交換數據的公共接口有良好的定義。由於要實現組件之間的數據通信,這些具有良好定義的接口就必須要被別的組件識別並正確理解,才能實現協作。
在定義了公共的接口後,還存在具體的數據交換問題,即雙方需要遵循一個共同的數據交換標準,這個數據交換標準稱爲協議。要在獨立的組件之間進行通信,需要一系列標準來嚴格規定數據通信的格式和規則。
Web Service的出現解決了上述問題,利用WSDL定義統一的接口格式,用SOAP消息統一輸入/輸出參數的統一格式。SOAP消息可以由多種途徑傳遞,比如,通過HTTP,SMTP及JMS協議傳遞。以HTTP爲例,在服務使用端,WSDL的接口定義可以通過HTTP-GET請求獲取,而SOAP應答消息及回覆消息的傳輸可以通過HTTP-POST請求來實現。這樣,基於WSDL和SOAP消息機制就可以滿足面向服務應用系統開發的需求。
Web Service平臺架構主要由三部分構成:序列化及反序列化子系統、調用子系統及發佈子系統。這三個子系統並不受具體語言的限制,也不受平臺和框架的限制,無論使用Java語言還是.NET語言,無論使用Axis平臺還是JWS來開發web服務,都會涉及這三個最基本的功能模塊。
(1)序列化及反序列化
在JMS中將一個Java對象轉化爲一個XML元素的過程,稱爲序列化。反之將一個XML元素轉化爲相應的Java
對象的過程,稱爲反序列化。序列化和反序列化的過程要依賴於Java類和XML-Scheme之間的映射關係,JWS有獨立
的序列化和反序列化子系統用來負責完成這些映射及轉化。
在web服務的客戶端,序列化過程將參數轉化爲xml結點,進而封裝成Soap請求消息,發送至服務器端的web服務端點。獲得返回值時,反序列化過程啓動,它將返回值從SOAP消息中指定的xml結點中取出,然後將它轉化爲客戶端相對應的Java對象。
轉化規則的定義在JWS中是通過JAXB的註釋來完成的。新版的JAXB簡化了綁定規則的描述,它允許直接將規則以註解的形式寫入Java類。
例如:
@Entity
@Name("hotel")
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
@XmlType(name="", propOrder = {
"id",
"img",
"name",
"address",
"city",
"state",
"zip",
"country",
"price",
"ipAddress"
})
@XmlRootElement(name="Hotel")
public class Hotel implements Serializable
{
private Long id;
private String img;
private String name;
private String address;
private String city;
private String state;
private String zip;
private String country;
private BigDecimal price;
private String ipAddress = "192.168.1.112";
@Id @GeneratedValue
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
@Length(max = 50)
@NotNull
public String getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
@Length(max=50) @NotNull
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
@Length(max=100) @NotNull
public String getAddress()
{
return address;
}
public void setAddress(String address)
{
this.address = address;
}
@Length(max=40) @NotNull
public String getCity()
{
return city;
}
public void setCity(String city)
{
this.city = city;
}
@Length(min=4, max=6) @NotNull
public String getZip()
{
return zip;
}
public void setZip(String zip)
{
this.zip = zip;
}
@Length(min=2, max=10) @NotNull
public String getState()
{
return state;
}
public void setState(String state)
{
this.state = state;
}
@Length(min=2, max=40) @NotNull
public String getCountry()
{
return country;
}
public void setCountry(String country)
{
this.country = country;
}
@Column(precision=6, scale=2)
public BigDecimal getPrice()
{
return price;
}
public void setPrice(BigDecimal price)
{
this.price = price;
}
@Transient
public String getIpAddress() {
return ipAddress;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
@Override
public String toString()
{
return "Hotel(" + name + "," + address + "," + city + "," + zip + ")";
}
}
(2)服務調用的過程
在面向服務的分佈式系統中,一般將傳統的客戶服務器框架中的客戶端Client稱爲服務的消費者,而將服務器端稱爲服務的提供者。
按照現在的web服務標準,一個服務被調用時,在服務的提供端大致的處理過程如下:
①接受並預處理SOAP請求消息,例如效驗,處理SOAP消息報文頭。
②從消息中獲取該消息希望調用的接口和具體操作。
③利用web服務提供的支持,找到具體的實現對象,並調用該對象的接口。這個對象可以由不同的語言實現。JWS支持從WSDL到Java的映射,可以通過WSDL找到與它相對應的Java服務端點實現類。
④使用序列化工具的反序列化過程,將SOAP請求消息中的服務請求參數取出,傳遞給步驟3中的目標對象的相應函數。
⑤目標Java對象執行相應的操作,將計算的結果以對象形式返回。
⑥使用序列化工具的序列化過程,根據wsdl中的定義,將步驟5中的結果對象序列化成XML元素,並封裝到SOAP回覆消
息中。
⑦將步驟6中的SOAP回覆消息發送回服務調用端。
與之對應的,在服務使用端的調用過程如下:
①首先創建服務端點接口對象SEI(Service Endpoint Interface),在Web服務客戶端一般都會有相應的工廠類完成SEI對象的實例化。在JWS中,SEI對象一般是由Java代理來實現的。
②客戶端通過SEI調用其中封裝的web服務接口。
③利用序列化工具的序列化過程,根據WSDL的定義,將客戶端的調用接口的參數轉化成XML元素,再將該元素封裝在SOAP請求消息裏。
④在同步模式下,在SOAP請求消息發出後,客戶端會等待SOAP應答消息;異步模式下,客戶端順序執行後續代碼,直到通過監聽器接收到SOAP請求消息裏。
⑤解析從服務器端獲得的SOAP應答消息,使用序列化工具中的反序列化過程,將SOAP應答消息中的數據轉化成客戶端對象,該對象的值就是被調用服務的返回值。
(3)web服務的發佈
以JMS爲例,web服務發佈系統的主要功能
①以URL的形式公開被髮布的WEB服務的WSDL文件,並綁定SOAP請求消息和Java目標類。
②發佈JAVA目標文件(例如Java Object文件,WAR文件,JAR文件及相關配置文件等)。
③配置序列化和反序列化子系統。
④配置web服務端點監聽器和SOAP消息預處理進程。
二、 web service的幾種開發及調用方式
(1)用EJB容器模型開發Web Service,用wsimport工具生成輔助類進行調用。
實例如下(實例中應用的EJB容器爲Jboss,所有代碼是基於JBoss Seam架構來編寫的):
①服務的提供者端<提供服務端的代碼>:
服務的提供者端的任務是暴露一個web service以供外部使用,本例的web service是通過JavaEE平臺提供的@java.jws.WebService註解來實現的。
以下代碼的路徑是VSS上 前期調查文件夾下SOA\web service\用EJB容器模型開發Web Service,用wsimport工具生成輔助類進行調用\tele\book\src\org\jboss\seam\example\booking\ws\impl,用EJB容器模型開發Web Service,用wsimport工具生成輔助類進行調用文件夾下包括兩個文件夾:local,tele。tele文件夾下放服務的提供者端的工程和打好的EAR包。local文件夾下放服務的消費者端的工程和打好的EAR包。
package org.jboss.seam.example.booking.ws.impl;
import static org.jboss.seam.ScopeType.SESSION;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.jws.WebService;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.example.booking.Hotel;
import org.jboss.seam.example.booking.User;
import org.jboss.seam.example.booking.ws.HotelObject;
@Stateless
/**實例是使用了兩個JBoss應用服務器,該代碼是服務提供者端的代碼,所以使用了@Remote註解,實現遠程接口。。*/
@Remote (HotelObject.class)
@WebService
public class HotelObjectImpl implements HotelObject {
@PersistenceContext
private EntityManager em;
public List<Hotel> getHotelObject(String hotelName) {
List<Hotel> results = em.createQuery("select h from Hotel h where h.name like"+ " '%"+hotelName+"%'")
.getResultList();
if(results.size()>0){
return results;
}
return null;
}
public String updateHotelObject(Hotel h){
try{
System.out.println(h);
System.out.println(h.getName());
em.merge(h);
}catch(Exception e ){
e.printStackTrace();
return "error";
}
return "success";
}
}
上面的類繼承了一個接口,接口類的路徑是:VSS上前期調查文件夾下SOA\web service\用EJB容器模型開發Web Service,用wsimport工具生成輔助類進行調用\tele\book\src\org\jboss\seam\example\booking\ws。
package org.jboss.seam.example.booking.ws;
import java.util.List;
import org.jboss.seam.example.booking.Hotel;
public interface HotelObject {
public List<Hotel> getHotelObject(String hotelName);
public String updateHotelObject(Hotel h);
}
上面的@WebService註解指名這是一個Web Service。打開Ant文件,執行desploy業務。部署完成後,通過JBoss管理平臺查看剛纔部署的Web Service,輸入http://localhost:8080/jbossws/進入JBoss Web Service的查看界面,可以看到View a list of deployed services鏈接,單擊View a list of deployed services鏈接後,可以查看已經發布的Web Service的URL地址,單擊該地址就可以查看生成的WSDL文。
這就是服務提供端的主要代碼,先用註解定義了一個web服務,這個web服務提供了兩個方法,這兩個方法可以接收從遠程傳過來的參數。服務的消費着可以通過上面的URL地址訪問該服務。
(2)服務的消費者端<使用服務端的代碼>
要調用web服務,我們可以通過好多方式,其中的一種方式是通過wsimport工具(在JDK1.6中,Sun已經爲用戶提供了wsimport工具)生成輔助類來調用web服務。
在VSS上web Service文件夾中有一個叫做jaxws-ri的文件包裏面包含了wsimport工具,本例的輔助類就是用它直接生成的。要用wsimport工具生成輔助類,首先要在dos環境下打開wsimport工具所在的目錄,比如wsimport工具的本地路徑:
Dos下打開wsimport工具所在的路徑。
在wsimport路徑下輸入如下內容:
wsimport.bat -keep -d ../build/classes/client http://192.168.1.106:8080/WsHelloWorld/HelloWorldBean?wsdl
其中../build/classes/client表示生成的輔助類存放的路徑,http://192.168.1.106:8080/WsHelloWorld/HelloWorldBean?wsdl表示要調用的web服務的WSDL文地址,其中192.168.1.106表示服務提供端的IP地址。
生成的輔助類如下圖所示:
把生成的輔助java類拷貝到我們的工程中
然後實例化一個生成的輔助類的對象,通過該對象調用服務提供者提供的方法<即getHotelObjec()>,去檢索符合條件的記錄。
該類位於VSS上如下路徑下;
SOA\Web Servic\用EJB容器模型開發Web Service,用wsimport工具生成輔助類進行調用\local\book\src\org\jboss\seam\example\booking
package org.jboss.seam.example.booking;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Factory;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.datamodel.DataModel;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.example.booking.ws.impl.Hotel;
import org.jboss.seam.example.booking.ws.impl.HotelObjectImpl;
import org.jboss.seam.example.booking.ws.impl.HotelObjectImplService;
@Stateful
@Name("teledataHotelSearch")
@Scope(ScopeType.SESSION)
@Restrict("#{identity.loggedIn}")
public class TeledateHotelSearchingAction implements TeledateHotelSearching
{
@PersistenceContext
private EntityManager em;
private String searchString;
@DataModel
private List<Hotel> telehotels;
public void find()
{
queryHotels();
}
private void queryHotels() {
/**實例化生成的輔助類的對象,通過該對象調用遠程的web service。*/
HotelObjectImplService service;
try
{
service = new HotelObjectImplService();
HotelObjectImpl hotelObjectImpl = service.getHotelObjectImplPort();
List<Hotel> result = hotelObjectImpl.getHotelObject(this.getSearchString());
System.out.println(result.size());
if(result.size()>0){
telehotels = result;
}else{
telehotels =null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getSearchString()
{
return searchString;
}
public void setSearchString(String searchString)
{
this.searchString = searchString;
}
@Remove
public void destroy() {}
@Override
public void cancel() {
// TODO 自動生成的方法存根
}
}
還有一個類是爲了保存修改了的服務提供者(即遠程的Web Service)返回的Hotel對象,必須調用服務提供者提供的保存Hotel對象的方法<即updateHotelObject ()>。
package org.jboss.seam.example.booking;
import static javax.persistence.PersistenceContextType.EXTENDED;
import java.util.Calendar;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.core.Events;
import org.jboss.seam.example.booking.ws.impl.HotelObjectImpl;
import org.jboss.seam.example.booking.ws.impl.HotelObjectImplService;
import org.jboss.seam.faces.FacesMessages;
import org.jboss.seam.log.Log;
@Stateful
@Name("saveTeteDataHotel")
@Restrict("#{identity.loggedIn}")
public class SaveTeteDataHotelAction implements SaveTeteDataHotel
{
@Begin
public void seveHotel(Hotel hotel)
{
HotelObjectImplService service;
try
{
service = new HotelObjectImplService();
HotelObjectImpl hotelObjectImpl = service.getHotelObjectImplPort();
String result = hotelObjectImpl.updateHotelObject(this.conversionHotel(hotel));
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
public org.jboss.seam.example.booking.ws.impl.Hotel conversionHotel(Hotel hotel){
org.jboss.seam.example.booking.ws.impl.Hotel h = new org.jboss.seam.example.booking.ws.impl.Hotel();
h.setAddress(hotel.getAddress());
h.setCity(hotel.getCity());
h.setCountry(hotel.getCountry());
h.setId(hotel.getId());
h.setImg(hotel.getImg());
h.setName(hotel.getName());
h.setPrice(hotel.getPrice());
h.setState(hotel.getState());
h.setZip(hotel.getZip());
return h;
}
@End
public void cancel() {}
@Remove
public void destroy() {}
}
以上類中有一點值得注意,就是服務消費者中的Hotel對象不能直接用於保存,必須經過轉換,轉換成生成的輔助類中的org.jboss.seam.example.booking.ws.impl.Hotel類,才能進行保存,上面的conversionHotel(Hotel hotel)方法就是爲了進行該轉換。
以上就是該種使用web service的幾個重要的點,通過以上各步就可以訪問遠程發佈的web服務,完整的過程請參考Vss上Dome工程。
(2)利用SAAJ協議動態生成SOAP消息訪問服務提供者端的web service。
第一種方式是利用wsimpot工具來生成輔助類來實現對服務提供者端的web service的訪問,由於生成的輔助類特別多,而且靈活性差。下面我們介紹用SAAJ協議動態生成SOAP消息來訪問服務提供者端的web service。
①服務提供者端的代碼
該類的在VSS上:
EHR\SOA\SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\ws\impl下
package org.jboss.seam.example.booking.ws.impl;
import static org.jboss.seam.ScopeType.SESSION;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.example.booking.Hotel;
import org.jboss.seam.example.booking.User;
import org.jboss.seam.example.booking.ws.HotelObject;
@Stateless
@Remote (HotelObject.class)
@WebService(name = "HotelObject", targetNamespace = "http://tower/ehr_DEV")
public class HotelObjectImpl implements HotelObject {
@PersistenceContext
private EntityManager em;
@WebResult(name="Hotel")
public List<Hotel> getHotelObject(String hotelName) {
List<Hotel> results = em.createQuery("select h from Hotel h where h.name like"+ " '%"+hotelName+"%'")
.getResultList();
if(results.size()>0){
return results;
}
return null;
}
public String updateHotelObject(Hotel h){
try{
System.out.println(h);
System.out.println(h.getName());
em.merge(h);
}catch(Exception e ){
e.printStackTrace();
return "error";
}
return "success";
}
}
該web服務提供了兩個方法,getHotelObject(String hotelName)方法接收遠程的的參數,通過該參數查找符合條件的Hotel對象;updateHotelObject(Hotel h)方法用來保存修改後的對象。
②服務調用者端的代碼
該類的在VSS上:
EHR\SOA\ SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\soa\esb\impl下
本例使用了Jboss ESB,其實服務調用端首先發送請求到Jboss ESB服務器上,Jboss ESB服務器上也定義了一些web服務,來接受這些請求,接受到請求消息後,對消息進行進一步的加工或者直接把消息轉發給真正的消息提供者端。
下面的代碼展示了服務消費端如何通過ESB服務器,去調用服務提供者端提供的服務。
package org.jboss.seam.example.booking.soa.esb.impl;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.soap.SOAPMessage;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.datamodel.DataModel;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.example.booking.Hotel;
import org.jboss.seam.example.booking.commom.collectionbinder.ListHotel;
import org.jboss.seam.example.booking.commom.esb.BuildSoapMessage;
import org.jboss.seam.example.booking.commom.esb.SendSoapMessageToEsb;
import org.jboss.seam.example.booking.commom.esb.impl.BuildSoapMessageImpl;
import org.jboss.seam.example.booking.commom.esb.impl.SendSoapMessageToEsbImpl;
import org.jboss.seam.example.booking.commom.formattransform.TransformMessageFormat;
import org.jboss.seam.example.booking.commom.formattransform.TransformString;
import org.jboss.seam.example.booking.commom.formattransform.impl.TransformMessageFormatImpl;
import org.jboss.seam.example.booking.commom.formattransform.impl.TransformStringImpl;
import org.jboss.seam.example.booking.soa.esb.TeledateHotelSearching;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
@Stateful
@Name("teledataHotelSearch")
@Scope(ScopeType.SESSION)
@Restrict("#{identity.loggedIn}")
public class TeledateHotelSearchingImpl implements TeledateHotelSearching
{
@PersistenceContext
private EntityManager em;
private String searchString;
@DataModel
private List<Hotel> telehotels;
public void find()
{
getHotelObject(this.getSearchString());
}
public void getHotelObject(String searchString) {
try{
BuildSoapMessage bsm = new BuildSoapMessageImpl();
SOAPMessage sm = bsm.getMessage("sayGoodbye", "http://webservice_producer/goodbyeworld", searchString);
TransformMessageFormat tmf = new TransformMessageFormatImpl();
String stringSOAPMessage = tmf.transformToString(sm);
SendSoapMessageToEsb smtEsb = new SendSoapMessageToEsbImpl();
Object o = smtEsb.sendMessageToJBRListener("http", "8765", stringSOAPMessage);
System.out.println("wo de ma ya!!!"+o.toString());
TransformString tfs = new TransformStringImpl();
Document doc = tfs.transformStringToDocument(o.toString());
NodeList nodeList = doc.getElementsByTagName("ListHotel");
Element element = (Element)nodeList.item(0);
String returnContent = element.getTextContent();
if(returnContent.length()>0){
String returnContentAnd = "<ListHotel>"+returnContent+"</ListHotel>";
InputStream inputStream = new ByteArrayInputStream(returnContentAnd.getBytes());
JAXBContext context = JAXBContext.newInstance(ListHotel.class);
try {
Unmarshaller um = context.createUnmarshaller();
ListHotel hl = (ListHotel)um.unmarshal(inputStream);
System.out.println("I'm King!"+hl.getElements());
telehotels = hl.getElements();
} catch (JAXBException e) {
e.printStackTrace();
}
}else{
telehotels = null;
}
}catch(Exception e){
e.printStackTrace();
telehotels = null;
} catch (Throwable e) {
e.printStackTrace();
}
}
public String getSearchString()
{
return searchString;
}
public void setSearchString(String searchString)
{
this.searchString = searchString;
}
@Remove
public void destroy() {}
@Override
public void cancel() {
searchString = "";
telehotels = null;
}
}
在getHotelObject(String searchString)方法中,首先通過BuildSoapMessage類提供的getMessage()方法生成要發送的SOAP消息。(調用的代碼如下:)
BuildSoapMessage bsm = new BuildSoapMessageImpl();
SOAPMessage sm = bsm.getMessage("sayGoodbye", "http://webservice_producer/goodbyeworld", searchString);
BuildSoapMessage類及代碼如下:
該類的在vss上的路徑如下:
SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\commom\esb\impl
package org.jboss.seam.example.booking.commom.esb.impl;
import java.util.HashMap;
import java.util.Map;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.Name;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import org.jboss.seam.example.booking.commom.esb.BuildSoapMessage;
public class BuildSoapMessageImpl implements BuildSoapMessage{
public static final SOAPConnection getSOAPConnection(){
SOAPConnectionFactory soapConnFactory;
SOAPConnection connection;
try {
soapConnFactory = SOAPConnectionFactory.newInstance();
connection = soapConnFactory.createConnection();
return connection;
} catch (UnsupportedOperationException e) {
e.printStackTrace();
return null;
} catch (SOAPException e) {
e.printStackTrace();
return null;
}
}
public static final SOAPMessage getSOAPMessage(){
MessageFactory messageFactory;
SOAPMessage message;
try {
messageFactory = MessageFactory.newInstance();
message = messageFactory.createMessage();
return message;
} catch (SOAPException e) {
e.printStackTrace();
return null;
}
}
public Name getName(String param){
SOAPFactory soapFactory;
try {
soapFactory = SOAPFactory.newInstance();
Name name = soapFactory.createName(param); //"arg0"
return name;
} catch (SOAPException e) {
e.printStackTrace();
return null;
}
}
public SOAPElement getBodyElement(SOAPMessage message,String param1,String param2,String param3){
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope envelope;
SOAPBody body;
SOAPElement bodyElement;
try {
envelope = soapPart.getEnvelope();
body = envelope.getBody();
bodyElement = body.addChildElement(envelope.createName(param1,//"sayHello" ,
param1,//"ns1",
param1));//"http://webservice_consumer1/helloworld"));
bodyElement.setEncodingStyle(SOAPConstants.URI_NS_SOAP_ENCODING);
return bodyElement;
} catch (SOAPException e) {
e.printStackTrace();
return null;
}
}
public SOAPElement addTextNode(SOAPMessage message,String param1,String param2,String param3){
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope envelope;
SOAPBody body;
SOAPElement bodyElement;
try {
envelope = soapPart.getEnvelope();
body = envelope.getBody();
bodyElement = body.addChildElement(envelope.createName(param1,//"sayHello" ,
param1,//"ns1",
param1));//"http://webservice_consumer1/helloworld"));
bodyElement.setEncodingStyle(SOAPConstants.URI_NS_SOAP_ENCODING);
return bodyElement;
} catch (SOAPException e) {
e.printStackTrace();
return null;
}
}
public SOAPMessage getMessage(String methodName,String targetNamespace,String messageContext) throws SOAPException,Exception{
//傳送參數需要創建Name
SOAPFactory soapFactory = SOAPFactory.newInstance();
//Next, create the actual message
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage message = messageFactory.createMessage();
//獲得一個SOAPPart對象
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope envelope = soapPart.getEnvelope();
SOAPBody body = envelope.getBody();
//Create the main element and namespace
SOAPElement bodyElement =
body.addChildElement(envelope.createName(methodName ,
"tower",
targetNamespace));
bodyElement.setEncodingStyle(SOAPConstants.URI_NS_SOAP_ENCODING);
//傳送參數新建一個Name對象
Name name = soapFactory.createName("message");
SOAPElement symbol = bodyElement.addChildElement(name);
symbol.addTextNode(messageContext);
message.saveChanges();
return message;
}
public SOAPMessage getMessage(String methodName,String targetNamespace,HashMap<String,String> hm) throws SOAPException,Exception{
//傳送參數需要創建Name
SOAPFactory soapFactory = SOAPFactory.newInstance();
//Next, create the actual message
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage message = messageFactory.createMessage();
//獲得一個SOAPPart對象
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope envelope = soapPart.getEnvelope();
SOAPBody body = envelope.getBody();
//Create the main element and namespace
SOAPElement bodyElement =
body.addChildElement(envelope.createName(methodName ,
"ns1",
targetNamespace));
bodyElement.setEncodingStyle(SOAPConstants.URI_NS_SOAP_ENCODING);
//傳送參數新建一個Name對象
Name name = soapFactory.createName("arg0");
SOAPElement symbol = bodyElement.addChildElement(name);
for (Map.Entry<String, String> m : hm.entrySet()) {
System.out.println("HashMap" + m.getKey() + ":" + m.getValue());
Name symbolName = soapFactory.createName(m.getKey());
SOAPElement symbolNameSOAPElemen = symbol.addChildElement(symbolName);
symbolNameSOAPElemen.addTextNode(m.getValue());
}
message.saveChanges();
return message;
}
}
在本例中Jboss esb服務器提供的web服務只能接受String類型的參數,所以要把soap消息進行格式轉換。在服務的消費端通過以下的代碼調用格式轉換方法:
TransformMessageFormat tmf = new TransformMessageFormatImpl();
String stringSOAPMessage = tmf.transformToString(sm);
格式轉換的類和方法如下:
該類的路徑在:
SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\commom\formattransform\impl
package org.jboss.seam.example.booking.commom.formattransform.impl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import org.jboss.internal.soa.esb.util.StreamUtils;
import org.jboss.seam.example.booking.commom.formattransform.TransformMessageFormat;
public class TransformMessageFormatImpl implements TransformMessageFormat{
public String transformToString(SOAPMessage sm) throws SOAPException, IOException{
ByteArrayOutputStream s = new ByteArrayOutputStream();
sm.writeTo(s);
byte[] buf=s.toByteArray();
ByteArrayInputStream bin=new ByteArrayInputStream(buf);
String msg = new String(StreamUtils.readStream(bin));
return msg;
}
}
把轉換後的Soap消息發送給Jboss ESB服務器,在服務消費者端的調用過程代碼如下:
SendSoapMessageToEsb smtEsb = new SendSoapMessageToEsbImpl();
Object o = smtEsb.sendMessageToJBRListener("http", "8765", stringSOAPMessage);
發送消息到Jboss ESB服務器上的類和方法如下:
該類在VSS上的路徑如下:
SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\commom\esb\impl
package org.jboss.seam.example.booking.commom.esb.impl;
import org.jboss.remoting.Client;
import org.jboss.remoting.InvokerLocator;
import org.jboss.seam.example.booking.commom.esb.SendSoapMessageToEsb;
public class SendSoapMessageToEsbImpl implements SendSoapMessageToEsb {
public static String ESBSERVICEIP="192.168.1.101";
public Object sendMessageToJBRListener(String protocol, String port, String message) throws Throwable {
String locatorURI = protocol + "://"+ESBSERVICEIP+":" + port;
InvokerLocator locator = new InvokerLocator(locatorURI);
Client remotingClient = null;
try {
remotingClient = new Client(locator);
remotingClient.connect();
Object response = remotingClient.invoke(message);
return response;
} finally {
if(remotingClient != null) {
remotingClient.disconnect();
}
}
}
}
public static String ESBSERVICEIP="192.168.1.101";這個IP地址表示Jboss ESB服務器的IP地址。
String locatorURI = protocol + "://"+ESBSERVICEIP+":" + port;這行代碼可以拼成訪問Jboss ESB上發佈的Web服務的URL地址。
我們接着看調用端的代碼,從Jboss ESB的返回值(其實是Jboss ESB服務器上發佈的Web服務再通過轉發調用服務提供者端的Web服務,從而得到返回值,服務提供者端的返回值首先把值傳回Jboss ESB服務器,Jboss ESB服務器再把值返回到服務調用端。)也是一個Object對象,我們要把這個Object對象轉換爲我們應用中的真實的對象(比如例子中的ListHotel對象),這個轉換我使用了JAXB標準,這個標準主要用於.xml和Java對象之間的互轉,因爲返回值是SOAP消息,是.xml形式的,同過JAXB標準可以很方便的轉換成我們想要的java類(比如ListHotel),在調用端的代碼如下:
TransformString tfs = new TransformStringImpl();
Document doc = tfs.transformStringToDocument(o.toString());
NodeList nodeList = doc.getElementsByTagName("ListHotel");
Element element = (Element)nodeList.item(0);
String returnContent = element.getTextContent();
if(returnContent.length()>0){
String returnContentAnd = "<ListHotel>"+returnContent+"</ListHotel>";
InputStream inputStream = new ByteArrayInputStream(returnContentAnd.getBytes());
JAXBContext context = JAXBContext.newInstance(ListHotel.class);
try {
Unmarshaller um = context.createUnmarshaller();
ListHotel hl = (ListHotel)um.unmarshal(inputStream);
System.out.println("I'm King!"+hl.getElements());
telehotels = hl.getElements();
} catch (JAXBException e) {
e.printStackTrace();
}
}else{
telehotels = null;
}
}catch(Exception e){
e.printStackTrace();
telehotels = null;
} catch (Throwable e) {
e.printStackTrace();
}
從Jboss ESB服務器的返回值先要經過轉換,把它轉換爲Document類型,代碼如下:
TransformString tfs = new TransformStringImpl();
Document doc = tfs.transformStringToDocument(o.toString());
轉換的類和方法如下:
該類在VSS上的路徑如下:
SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\commom\formattransform\impl
package org.jboss.seam.example.booking.commom.formattransform.impl;
import java.io.IOException;
import java.io.StringReader;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.jboss.seam.example.booking.commom.formattransform.TransformString;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class TransformStringImpl implements TransformString{
public Document transformStringToDocument(String s){
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(s)));
return doc;
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public String[] getObjectArrayByESBReturnContent(String s){
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(s)));
NodeList nodeList = doc.getElementsByTagName("return");
Element element = (Element)nodeList.item(0);
String returnContent = element.getTextContent();
if(returnContent.length()>0){
String[] getObjectContest = returnContent.split("</O>");
return getObjectContest;
}else{
return null;
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public String[] getPropertyArrayByStringObject(String returnContent){
if(returnContent.length()>0){
String[] getObjectContest = returnContent.split("</A&V>");
return getObjectContest;
}else{
return null;
}
}
}
轉換後的Document對象要進行一下截取,重新組裝變成適合JAXB轉換的XML形式,這個過程如下:
NodeList nodeList = doc.getElementsByTagName("ListHotel");
Element element = (Element)nodeList.item(0);
String returnContent = element.getTextContent();
String returnContentAnd = "<ListHotel>"+returnContent+"</ListHotel>";
InputStream inputStream = new ByteArrayInputStream(returnContentAnd.getBytes());
把返回值轉換爲先轉換爲InputStream對象,這個InputStream對象就符合轉換的形式了,可以進行轉換了。
要利用JAXB規範進行這樣的轉換,要進行兩步:
①用@XmlRootElement註解標註要轉換成的Java類。
Java類如下:
這個Java類在VSS上的路徑是:
SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\commom\collectionbinder
package org.jboss.seam.example.booking.commom.collectionbinder;
import java.util.List;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import org.jboss.seam.example.booking.Hotel;
@XmlRootElement(name="ListHotel")
public class ListHotel {
private List<Hotel> hotel;
public ListHotel() {
}
public ListHotel(List<Hotel> hotel) {
this.hotel = hotel;
}
@XmlElementRef
public List<Hotel> getElements() {
return hotel;
}
public void setElements(List<Hotel> hotel) {
this.hotel = hotel;
}
}
②通過JAXB提供的編組機制,把xml字符轉換爲Java對象。代碼如下:
JAXBContext context = JAXBContext.newInstance(ListHotel.class);
try {
Unmarshaller um = context.createUnmarshaller();
ListHotel hl = (ListHotel)um.unmarshal(inputStream);
System.out.println("I'm King!"+hl.getElements());
telehotels = hl.getElements();
} catch (JAXBException e) {
e.printStackTrace();
}
以上就是我用SAAJ實現Web Service開發的詳細過程,文檔只是一個參考的作用,要靈活掌握這種方式要翻越其他的資料,認真思考。
這個實例中牽涉到了JBoss ESB服務器,我沒有進行詳細的介紹,我會在下一個文檔中仔細討論該部分內容。要很好的理解該實例請參考我的Jboss ESB的文檔及Vss上的相關代碼。
以上是通過Jboss ESB服務器作爲中間件轉發從消息消費端到消息使用端的的消息的過程,SOA(V1.2)這個文件夾下的工程中還包括了一個服務消費者端直接訪問服務提供者端的實例。即:
該類在VSS上的路徑爲:
SOA(V1.2)\Request endpoint(192.168.1.112)\booking\src\org\jboss\seam\example\booking\soa\esb\impl
package org.jboss.seam.example.booking.soa.esb.impl;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.example.booking.Hotel;
import org.jboss.seam.example.booking.commom.esb.BuildSoapMessage;
import org.jboss.seam.example.booking.commom.esb.SendSoapMessage;
import org.jboss.seam.example.booking.commom.esb.impl.BuildSoapMessageImpl;
import org.jboss.seam.example.booking.commom.esb.impl.SendSoapMessageImpl;
import org.jboss.seam.example.booking.commom.formattransform.TransformMethodName;
import org.jboss.seam.example.booking.commom.formattransform.impl.TransformMethodNameImpl;
import org.jboss.seam.example.booking.soa.esb.TeteDataHotelSave;
@Stateful
@Name("saveTeteDataHotel")
@Restrict("#{identity.loggedIn}")
public class TeteDataHotelSaveImpl implements TeteDataHotelSave
{
@In(required=false)
@Out(required=false)
private Hotel hotel;
@Begin
public void selectHotel(Hotel selectedHotel)
{
//Hotel h = new Hotel();
hotel = selectedHotel;
}
public void seveHotel(Hotel hotel)
{
String ipAddress = hotel.getIpAddress();
System.out.println("yuexiangcheng,test!!!!!========"+ipAddress);
TransformMethodName tmn = new TransformMethodNameImpl();
Field[] f = hotel.getClass().getDeclaredFields();
HashMap<String,String> hm = new HashMap();
for(int i = 0;i< f.length;i++){
try {
String attributeName = f[i].getName();
System.out.println("attributeNameattributeNameattributeNameattributeNameattributeNameattributeName"+attributeName);
String motherName = tmn.getGetMethodByAttribute(attributeName);
Method method = hotel.getClass().getDeclaredMethod(motherName,null);
String attributeValue = method.invoke(hotel, null).toString();
System.out.println("attributeValueattributeValueattributeValueattributeValueattributeValueattributeValue"+attributeValue);
hm.put(attributeName, attributeValue);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
BuildSoapMessage bsm = new BuildSoapMessageImpl();
SendSoapMessage ssm = new SendSoapMessageImpl();
try {
SOAPMessage sm = bsm.getMessage("updateHotelObject", "http://tower/ehr_DEV", hm);
SOAPMessage result = ssm.send(sm, "http://"+ipAddress+":8080/jboss-seam-booking-jboss-seam-booking/HotelObjectImpl");
TransformerFactory transformerFactory =
TransformerFactory.newInstance();
Transformer transformer =
transformerFactory.newTransformer();
Source sourceContent = result.getSOAPPart().getContent();
StreamResult resultPrint = new StreamResult(System.out);
transformer.transform(sourceContent, resultPrint);
System.out.println();
} catch (SOAPException e) {
// TODO 自動生成 catch 塊
e.printStackTrace();
} catch (Exception e) {
// TODO 自動生成 catch 塊
e.printStackTrace();
}
}
@End
public void cancel() {}
@Remove
public void destroy() {}
}
該過程是服務消費者端修改了從服務提供者端檢索到的數據,並對其進行了修改再保持到服務提供者端。具體的過程和上例查不多,請詳細研究在VSS上的代碼。值得注意的是該例中用到了JAXB的解組功能。
以上就是我實現Web Service的兩種方法,因爲能力有限,所以寫的不好,僅供參考。希望對大家有幫助,謝謝。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.