文章分類:Java編程文章分類:JavaEye 關鍵字: web service jax-ws saaj jaxb java
Web Service簡介及開發(fā)實例
作者:岳鄉(xiāng)成
本文檔實例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。由于實例工程太大,不能放博客上,如有需要聯(lián)系我,MSN:xiangcheng.yue@163.com。
QQ號碼:827650367。
一、 Web Service的簡介
1、 什么是Web Service
Web services是建立可互操作的分布式應用程序的新平臺。
Web service平臺是一套標準,它定義了應用程序如何在Web上實現(xiàn)互操作性。你可以用任何你喜歡的語言,在任何你喜歡的平臺上寫Web service ,只要我們可以通過Web service標準對這些服務進行查詢和訪問。
Web service平臺需要一套協(xié)議來實現(xiàn)分布式應用程序的創(chuàng)建。任何平臺都有它的數(shù)據(jù)表示方法和類型系統(tǒng)。要實現(xiàn)互操作性,Web service平臺必須提供一套標準的類型系統(tǒng),用于溝通不同平臺、編程語言和組件模型中的不同類型系統(tǒng)。
基礎的 Web Services 平臺是 XML + HTTP。
HTTP 協(xié)議是最常用的因特網(wǎng)協(xié)議。
XML 提供了一種可用于不同的平臺和編程語言之間的語言。
Web services 平臺是簡單的可共同操作的消息收發(fā)框架。它仍然缺少許多諸如安全和路由等重要的特性。但是,一旦 SOAP 變得更加高級,這些事項就會得到解決。
Web services 有望使應用程序更加容易通信。
★ Web services 把 Web 應用程序提升到了另外一個層面
通過使用 Web services,您的應用程序可向全世界發(fā)布功能或消息。
Web services 使用 XML 來編解碼數(shù)據(jù),并使用 SOAP 借由開放的協(xié)議來傳輸數(shù)據(jù)。
通過 Web services,您的會計部門的 Win 2k 服務器可與 IT 供應商的 UNIX 服務器進行連接。
★ Web services 有兩種類型的應用
可重復使用的應用程序組件
有一些功能是不同的應用程序常常會用到的。那么為什么要周而復始地開發(fā)它們呢?
Web services 可以把應用程序組件作為服務來提供,比如匯率轉換、天氣預報或者甚至是語言翻譯等等。
比較理想的情況是,每種應用程序組件只有一個最優(yōu)秀的版本,這樣任何人都可以在其應用程序中使用它。
連接現(xiàn)有的軟件
通過為不同的應用程序提供一種鏈接其數(shù)據(jù)的途徑,Web services有助于解決協(xié)同工作的問題。
通過使用 Web services,您可以在不同的應用程序與平臺之間來交換數(shù)據(jù)。
★ Web Services 擁有兩種基本的元素。
它們是:SOAP及WSDL
(1)什么是 SOAP?
? SOAP 指簡易對象訪問協(xié)議
? SOAP 是一種通信協(xié)議
? SOAP 用于應用程序之間的通信
? SOAP 是一種用于發(fā)送消息的格式
? SOAP 被設計用來通過因特網(wǎng)進行通信
? SOAP 獨立于平臺
? SOAP 獨立于語言
? SOAP 基于 XML
? SOAP 很簡單并可擴展
? SOAP 允許您繞過防火墻
? SOAP 將作為 W3C 標準來發(fā)展
(2)什么是 WSDL?
WSDL 是基于 XML 的用于描述 Web Services 以及如何訪問 Web Services 的語言。
? WSDL 指網(wǎng)絡服務描述語言
? WSDL 使用 XML 編寫
? WSDL 是一種 XML 文檔
? WSDL 用于描述網(wǎng)絡服務
? WSDL 也可用于定位網(wǎng)絡服務
? WSDL 還不是 W3C 標準
2、 什么是JWS
JWS(Java Web Service)是Java應用平臺上專門用于開發(fā)Web服務系統(tǒng)及面向服務系統(tǒng)的產品,它的最新版本是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等。
面向服務的系統(tǒng)往往由多個具有不同的子功能的獨立組件構成,通過他們之間良好的相互協(xié)作,可以實現(xiàn)復雜的需求。面向服務系統(tǒng)的這個特點,要求獨立組建之間有公共的接口,這些用于交換數(shù)據(jù)的公共接口有良好的定義。由于要實現(xiàn)組件之間的數(shù)據(jù)通信,這些具有良好定義的接口就必須要被別的組件識別并正確理解,才能實現(xiàn)協(xié)作。
在定義了公共的接口后,還存在具體的數(shù)據(jù)交換問題,即雙方需要遵循一個共同的數(shù)據(jù)交換標準,這個數(shù)據(jù)交換標準稱為協(xié)議。要在獨立的組件之間進行通信,需要一系列標準來嚴格規(guī)定數(shù)據(jù)通信的格式和規(guī)則。
Web Service的出現(xiàn)解決了上述問題,利用WSDL定義統(tǒng)一的接口格式,用SOAP消息統(tǒng)一輸入/輸出參數(shù)的統(tǒng)一格式。SOAP消息可以由多種途徑傳遞,比如,通過HTTP,SMTP及JMS協(xié)議傳遞。以HTTP為例,在服務使用端,WSDL的接口定義可以通過HTTP-GET請求獲取,而SOAP應答消息及回復消息的傳輸可以通過HTTP-POST請求來實現(xiàn)。這樣,基于WSDL和SOAP消息機制就可以滿足面向服務應用系統(tǒng)開發(fā)的需求。
Web Service平臺架構主要由三部分構成:序列化及反序列化子系統(tǒng)、調用子系統(tǒng)及發(fā)布子系統(tǒng)。這三個子系統(tǒng)并不受具體語言的限制,也不受平臺和框架的限制,無論使用Java語言還是.NET語言,無論使用Axis平臺還是JWS來開發(fā)web服務,都會涉及這三個最基本的功能模塊。
(1)序列化及反序列化
在JMS中將一個Java對象轉化為一個XML元素的過程,稱為序列化。反之將一個XML元素轉化為相應的Java
對象的過程,稱為反序列化。序列化和反序列化的過程要依賴于Java類和XML-Scheme之間的映射關系,JWS有獨立
的序列化和反序列化子系統(tǒng)用來負責完成這些映射及轉化。
在web服務的客戶端,序列化過程將參數(shù)轉化為xml結點,進而封裝成Soap請求消息,發(fā)送至服務器端的web服務端點。獲得返回值時,反序列化過程啟動,它將返回值從SOAP消息中指定的xml結點中取出,然后將它轉化為客戶端相對應的Java對象。
轉化規(guī)則的定義在JWS中是通過JAXB的注釋來完成的。新版的JAXB簡化了綁定規(guī)則的描述,它允許直接將規(guī)則以注解的形式寫入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)服務調用的過程
在面向服務的分布式系統(tǒng)中,一般將傳統(tǒng)的客戶服務器框架中的客戶端Client稱為服務的消費者,而將服務器端稱為服務的提供者。
按照現(xiàn)在的web服務標準,一個服務被調用時,在服務的提供端大致的處理過程如下:
①接受并預處理SOAP請求消息,例如效驗,處理SOAP消息報文頭。
②從消息中獲取該消息希望調用的接口和具體操作。
③利用web服務提供的支持,找到具體的實現(xiàn)對象,并調用該對象的接口。這個對象可以由不同的語言實現(xiàn)。JWS支持從WSDL到Java的映射,可以通過WSDL找到與它相對應的Java服務端點實現(xiàn)類。
④使用序列化工具的反序列化過程,將SOAP請求消息中的服務請求參數(shù)取出,傳遞給步驟3中的目標對象的相應函數(shù)。
⑤目標Java對象執(zhí)行相應的操作,將計算的結果以對象形式返回。
⑥使用序列化工具的序列化過程,根據(jù)wsdl中的定義,將步驟5中的結果對象序列化成XML元素,并封裝到SOAP回復消
息中。
⑦將步驟6中的SOAP回復消息發(fā)送回服務調用端。
與之對應的,在服務使用端的調用過程如下:
①首先創(chuàng)建服務端點接口對象SEI(Service Endpoint Interface),在Web服務客戶端一般都會有相應的工廠類完成SEI對象的實例化。在JWS中,SEI對象一般是由Java代理來實現(xiàn)的。
②客戶端通過SEI調用其中封裝的web服務接口。
③利用序列化工具的序列化過程,根據(jù)WSDL的定義,將客戶端的調用接口的參數(shù)轉化成XML元素,再將該元素封裝在SOAP請求消息里。
④在同步模式下,在SOAP請求消息發(fā)出后,客戶端會等待SOAP應答消息;異步模式下,客戶端順序執(zhí)行后續(xù)代碼,直到通過監(jiān)聽器接收到SOAP請求消息里。
⑤解析從服務器端獲得的SOAP應答消息,使用序列化工具中的反序列化過程,將SOAP應答消息中的數(shù)據(jù)轉化成客戶端對象,該對象的值就是被調用服務的返回值。
(3)web服務的發(fā)布
以JMS為例,web服務發(fā)布系統(tǒng)的主要功能
①以URL的形式公開被發(fā)布的WEB服務的WSDL文件,并綁定SOAP請求消息和Java目標類。
②發(fā)布JAVA目標文件(例如Java Object文件,WAR文件,JAR文件及相關配置文件等)。
③配置序列化和反序列化子系統(tǒng)。
④配置web服務端點監(jiān)聽器和SOAP消息預處理進程。
二、 web service的幾種開發(fā)及調用方式
(1)用EJB容器模型開發(fā)Web Service,用wsimport工具生成輔助類進行調用。
實例如下(實例中應用的EJB容器為Jboss,所有代碼是基于JBoss Seam架構來編寫的):
①服務的提供者端<提供服務端的代碼>:
服務的提供者端的任務是暴露一個web service以供外部使用,本例的web service是通過JavaEE平臺提供的@java.jws.WebService注解來實現(xiàn)的。
以下代碼的路徑是VSS上 前期調查文件夾下SOAweb service用EJB容器模型開發(fā)Web Service,用wsimport工具生成輔助類進行調用telebooksrcorgjbossseamexamplebookingwsimpl,用EJB容器模型開發(fā)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注解,實現(xiàn)遠程接口。。*/
@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上前期調查文件夾下SOAweb service用EJB容器模型開發(fā)Web Service,用wsimport工具生成輔助類進行調用telebooksrcorgjbossseamexamplebookingws。
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文件,執(zhí)行desploy業(yè)務。部署完成后,通過JBoss管理平臺查看剛才部署的Web Service,輸入http://localhost:8080/jbossws/進入JBoss Web Service的查看界面,可以看到View a list of deployed services鏈接,單擊View a list of deployed services鏈接后,可以查看已經發(fā)布的Web Service的URL地址,單擊該地址就可以查看生成的WSDL文。
這就是服務提供端的主要代碼,先用注解定義了一個web服務,這個web服務提供了兩個方法,這兩個方法可以接收從遠程傳過來的參數(shù)。服務的消費著可以通過上面的URL地址訪問該服務。
(2)服務的消費者端<使用服務端的代碼>
要調用web服務,我們可以通過好多方式,其中的一種方式是通過wsimport工具(在JDK1.6中,Sun已經為用戶提供了wsimport工具)生成輔助類來調用web服務。
在VSS上web Service文件夾中有一個叫做jaxws-ri的文件包里面包含了wsimport工具,本例的輔助類就是用它直接生成的。要用wsimport工具生成輔助類,首先要在dos環(huán)境下打開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上如下路徑下;
SOAWeb Servic用EJB容器模型開發(fā)Web Service,用wsimport工具生成輔助類進行調用localbooksrcorgjbossseamexamplebooking
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的幾個重要的點,通過以上各步就可以訪問遠程發(fā)布的web服務,完整的過程請參考Vss上Dome工程。
(2)利用SAAJ協(xié)議動態(tài)生成SOAP消息訪問服務提供者端的web service。
第一種方式是利用wsimpot工具來生成輔助類來實現(xiàn)對服務提供者端的web service的訪問,由于生成的輔助類特別多,而且靈活性差。下面我們介紹用SAAJ協(xié)議動態(tài)生成SOAP消息來訪問服務提供者端的web service。
①服務提供者端的代碼
該類的在VSS上:
EHRSOASOA(V1.2)Request endpoint(192.168.1.112)bookingsrcorgjbossseamexamplebookingwsimpl下
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)方法接收遠程的的參數(shù),通過該參數(shù)查找符合條件的Hotel對象;updateHotelObject(Hotel h)方法用來保存修改后的對象。
②服務調用者端的代碼
該類的在VSS上:
EHRSOA SOA(V1.2)Request endpoint(192.168.1.112)bookingsrcorgjbossseamexamplebookingsoaesbimpl下
本例使用了Jboss ESB,其實服務調用端首先發(fā)送請求到Jboss ESB服務器上,Jboss ESB服務器上也定義了一些web服務,來接受這些請求,接受到請求消息后,對消息進行進一步的加工或者直接把消息轉發(fā)給真正的消息提供者端。
下面的代碼展示了服務消費端如何通過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()方法生成要發(fā)送的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)bookingsrcorgjbossseamexamplebookingcommomesbimpl
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{
//傳送參數(shù)需要創(chuàng)建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);
//傳送參數(shù)新建一個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{
//傳送參數(shù)需要創(chuàng)建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);
//傳送參數(shù)新建一個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類型的參數(shù),所以要把soap消息進行格式轉換。在服務的消費端通過以下的代碼調用格式轉換方法:
TransformMessageFormat tmf = new TransformMessageFormatImpl();
String stringSOAPMessage = tmf.transformToString(sm);
格式轉換的類和方法如下:
該類的路徑在:
SOA(V1.2)Request endpoint(192.168.1.112)bookingsrcorgjbossseamexamplebookingcommomformattransformimpl
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消息發(fā)送給Jboss ESB服務器,在服務消費者端的調用過程代碼如下:
SendSoapMessageToEsb smtEsb = new SendSoapMessageToEsbImpl();
Object o = smtEsb.sendMessageToJBRListener("http", "8765", stringSOAPMessage);
發(fā)送消息到Jboss ESB服務器上的類和方法如下:
該類在VSS上的路徑如下:
SOA(V1.2)Request endpoint(192.168.1.112)bookingsrcorgjbossseamexamplebookingcommomesbimpl
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上發(fā)布的Web服務的URL地址。
我們接著看調用端的代碼,從Jboss ESB的返回值(其實是Jboss ESB服務器上發(fā)布的Web服務再通過轉發(fā)調用服務提供者端的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)bookingsrcorgjbossseamexamplebookingcommomformattransformimpl
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規(guī)范進行這樣的轉換,要進行兩步:
①用@XmlRootElement注解標注要轉換成的Java類。
Java類如下:
這個Java類在VSS上的路徑是:
SOA(V1.2)Request endpoint(192.168.1.112)bookingsrcorgjbossseamexamplebookingcommomcollectionbinder
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實現(xiàn)Web Service開發(fā)的詳細過程,文檔只是一個參考的作用,要靈活掌握這種方式要翻越其他的資料,認真思考。
這個實例中牽涉到了JBoss ESB服務器,我沒有進行詳細的介紹,我會在下一個文檔中仔細討論該部分內容。要很好的理解該實例請參考我的Jboss ESB的文檔及Vss上的相關代碼。
以上是通過Jboss ESB服務器作為中間件轉發(fā)從消息消費端到消息使用端的的消息的過程,SOA(V1.2)這個文件夾下的工程中還包括了一個服務消費者端直接訪問服務提供者端的實例。即:
該類在VSS上的路徑為:
SOA(V1.2)Request endpoint(192.168.1.112)bookingsrcorgjbossseamexamplebookingsoaesbimpl
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() {}
}
該過程是服務消費者端修改了從服務提供者端檢索到的數(shù)據(jù),并對其進行了修改再保持到服務提供者端。具體的過程和上例查不多,請詳細研究在VSS上的代碼。值得注意的是該例中用到了JAXB的解組功能。
以上就是我實現(xiàn)Web Service的兩種方法,因為能力有限,所以寫的不好,僅供參考。希望對大家有幫助,謝謝。
愛華網(wǎng)


