前面寫了個JAX-WS的小例子,看到用JAVA6開發WebService確實很簡單,也很方便,不過前面也說了,JAVA有三種WebService規範,JAX-WS是其中一種,現在來看看JAXM&SAAJ。
最近在做一個接口平臺的項目,接口嘛,當然得涉及到對WebService的接口了,我們計劃做成一個通用的平臺,通過配置文件進行配置後就可以動態對某一個接口進行調用,但像前面的例子那樣,每次都要生成一堆客戶端代碼,這可受不了。如果調用的接口唯一,生成一次客戶端代碼當然沒問題,但如果要調用的接口是動態的,這就不好辦了。因此,我需要了解SOAP更多底層的細節,由我自己來組織SOAP中的內容而不是完全由代碼生成器生成。
仍使用前面例子中的服務器端:
接口:
- package com.why.server;
- import javax.jws.WebParam;
- import javax.jws.WebService;
- import javax.jws.soap.SOAPBinding;
- import javax.xml.ws.soap.MTOM;
- /**
- *
- * @author why
- *
- */
- @WebService(name="Hello")
- @SOAPBinding(style = SOAPBinding.Style.RPC)
- public interface Hello {
- public void printContext();
- public Customer selectCustomerByName(@WebParam(name = "c",header=true)Customer customer);
- public Customer selectMaxAgeCustomer(Customer c1, Customer c2);
- }
實現類:
- package com.why.server;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.text.ParseException;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.Set;
- import javax.activation.DataHandler;
- import javax.activation.FileDataSource;
- import javax.annotation.Resource;
- import javax.jws.WebService;
- import javax.xml.ws.WebServiceContext;
- import javax.xml.ws.handler.MessageContext;
- import javax.xml.ws.soap.MTOM;
- /**
- *
- * 通過@MTOM註解啓動MTOM傳輸方式,使用CXF實現時,這個註解放在接口或者實現類上都可以,使用JDK1.6自帶實現時,需標註在實現類上
- * @author why
- *
- */
- @WebService(serviceName="HelloService",portName="HelloServicePort",targetNamespace="http://service.why.com/",endpointInterface="com.why.server.Hello")
- @MTOM
- public class HelloImpl implements Hello {
- @Resource
- private WebServiceContext context;
- @Override
- public void printContext(){
- MessageContext ctx = context.getMessageContext();
- Set<String> set = ctx.keySet();
- for (String key : set) {
- System.out.println("{" + key + "," + ctx.get(key) +"}");
- try {
- System.out.println("key.scope=" + ctx.getScope(key));
- } catch (Exception e) {
- System.out.println(key + " is not exits");
- }
- }
- }
- @Override
- public Customer selectCustomerByName(Customer customer) {
- if("why".equals(customer.getName())){
- customer.setId(1);
- try {
- customer.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse("1985-10-07"));
- } catch (ParseException e) {
- e.printStackTrace();
- }
- customer.setImageData(new DataHandler(new FileDataSource(new File("c:"+ File.separator + "why.jpg"))));
- }else{
- customer.setId(2);
- customer.setBirthday(new Date());
- customer.setImageData(new DataHandler(new FileDataSource(new File("c:"+ File.separator + "origin.jpg"))));
- }
- return customer;
- }
- @Override
- public Customer selectMaxAgeCustomer(Customer c1, Customer c2) {
- try {
- // 輸出接收到的附件
- System.out.println("c1.getImageData().getContentType()=" + c1.getImageData().getContentType());
- InputStream is = c1.getImageData().getInputStream();
- OutputStream os = new FileOutputStream("c:\\temp1.jpg");
- byte[] bytes = new byte[1024];
- int c;
- while ((c = is.read(bytes)) != -1) {
- os.write(bytes, 0, c);
- }
- os.close();
- System.out.println("c2.getImageData().getContentType()=" + c2.getImageData().getContentType());
- is = c2.getImageData().getInputStream();
- os = new FileOutputStream("c:\\temp2.jpg");
- bytes = new byte[1024];
- while ((c = is.read(bytes)) != -1) {
- os.write(bytes, 0, c);
- }
- os.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- if (c1.getBirthday().getTime() > c2.getBirthday().getTime()){
- return c2;
- }
- else{
- return c1;
- }
- }
- }
Customer類:
- package com.why.server;
- import java.util.Date;
- import javax.activation.DataHandler;
- import javax.xml.bind.annotation.XmlAccessType;
- import javax.xml.bind.annotation.XmlAccessorType;
- import javax.xml.bind.annotation.XmlMimeType;
- import javax.xml.bind.annotation.XmlRootElement;
- /**
- *
- * @author why
- *
- */
- @XmlRootElement(name = "Customer")
- @XmlAccessorType(XmlAccessType.FIELD)
- public class Customer {
- private long id;
- private String name;
- private Date birthday;
- @XmlMimeType("application/octet-stream")
- private DataHandler imageData;
- public long getId() {
- return id;
- }
- public void setId(long id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public Date getBirthday() {
- return birthday;
- }
- public void setBirthday(Date birthday) {
- this.birthday = birthday;
- }
- public DataHandler getImageData() {
- return imageData;
- }
- public void setImageData(DataHandler imageData) {
- this.imageData = imageData;
- }
- }
發佈:
- package com.why.server;
- import javax.xml.ws.Endpoint;
- /**
- *
- * @author why
- *
- */
- public class SoapServer {
- public static void main(String[] args) {
- Endpoint.publish("http://localhost:8080/helloService",new HelloImpl());
- }
- }
這次不生成客戶端類,而是通過自己組織SOAP消息,向服務器發送請求。首先,我們需要一個到WebService服務的連接(就像Connection之於JDBC),通過javax.xml.soap.SOAPConnectionFactory的createConnection()可以獲得一個WebService連接。獲得連接之後,我們就可以組織我們的SOAP消息了。通過javax.xml.soap.MessageFactory的createMessage()方法,獲得一個javax.xml.soap.SOAPMessage,SOAPMessage就是我們SOAP消息的入口。我們知道,SOAP其實就是一個XML,有了SOAPMessage這個入口,剩下的就是對XML的組織和解析了。對於SOAP消息的各個部分,SOAPMessage都有對應的接口:
- // 獲取SOAP連接工廠
- SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance();
- // 從SOAP連接工廠創建SOAP連接對象
- SOAPConnection connection = factory.createConnection();
- // 獲取消息工廠
- MessageFactory mFactory = MessageFactory.newInstance();
- // 從消息工廠創建SOAP消息對象
- SOAPMessage message = mFactory.createMessage();
- // 創建SOAPPart對象
- SOAPPart part = message.getSOAPPart();
- // 創建SOAP信封對象
- SOAPEnvelope envelope = part.getEnvelope();
- // 創建SOAPHeader對象
- SOAPHeader header = message.getSOAPHeader();
- // 創建SOAPBody對
- SOAPBody body = envelope.getBody();
把我們需要傳遞的參數組織好,通過connection.call方法進行對WebService的調用,他仍然會給我們返回一個SOAPMessage對象,對應服務器端的三個函數,我分別寫了對應的三個方法對其進行調用,以下是我的客戶端類:
- package com.why.client;
- import java.io.ByteArrayOutputStream;
- import java.io.FileOutputStream;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.net.URL;
- import java.util.Iterator;
- import java.util.UUID;
- import javax.activation.DataHandler;
- import javax.activation.FileDataSource;
- import javax.xml.namespace.QName;
- import javax.xml.soap.AttachmentPart;
- import javax.xml.soap.MessageFactory;
- import javax.xml.soap.SOAPBody;
- import javax.xml.soap.SOAPBodyElement;
- import javax.xml.soap.SOAPConnection;
- import javax.xml.soap.SOAPConnectionFactory;
- import javax.xml.soap.SOAPElement;
- import javax.xml.soap.SOAPEnvelope;
- import javax.xml.soap.SOAPHeader;
- import javax.xml.soap.SOAPHeaderElement;
- import javax.xml.soap.SOAPMessage;
- import javax.xml.soap.SOAPPart;
- /**
- *
- * @author why
- *
- */
- public class SoapClient {
- public static void main(String[] args) throws Exception{
- printContext();
- selectCustomerByName();
- selectMaxAgeCustomer();
- }
- /**
- * 調用一個無參函數
- * @throws Exception
- */
- public static void printContext() throws Exception{
- // 獲取SOAP連接工廠
- SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance();
- // 從SOAP連接工廠創建SOAP連接對象
- SOAPConnection connection = factory.createConnection();
- // 獲取消息工廠
- MessageFactory mFactory = MessageFactory.newInstance();
- // 從消息工廠創建SOAP消息對象
- SOAPMessage message = mFactory.createMessage();
- // 創建SOAPPart對象
- SOAPPart part = message.getSOAPPart();
- // 創建SOAP信封對象
- SOAPEnvelope envelope = part.getEnvelope();
- // 創建SOAPHeader對象
- SOAPHeader header = message.getSOAPHeader();
- // 創建SOAPBody對象
- SOAPBody body = envelope.getBody();
- // 創建XML的根元素
- SOAPBodyElement bodyElementRoot = body.addBodyElement(new QName("http://server.why.com/", "printContext", "ns1"));
- // 訪問Web服務地址
- SOAPMessage reMessage = connection.call(message, new URL("http://127.0.0.1:8080/helloService"));
- // 控制檯輸出返回的SOAP消息
- OutputStream os = System.out;
- reMessage.writeTo(os);
- connection.close();
- }
- /**
- * 調用一個在soap:HEADER中傳遞參數的函數
- * @throws Exception
- */
- public static void selectCustomerByName() throws Exception{
- // 獲取SOAP連接工廠
- SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance();
- // 從SOAP連接工廠創建SOAP連接對象
- SOAPConnection connection = factory.createConnection();
- // 獲取消息工廠
- MessageFactory mFactory = MessageFactory.newInstance();
- // 從消息工廠創建SOAP消息對象
- SOAPMessage message = mFactory.createMessage();
- // 創建SOAPPart對象
- SOAPPart part = message.getSOAPPart();
- // 創建SOAP信封對象
- SOAPEnvelope envelope = part.getEnvelope();
- // 創建SOAPHeader對象
- SOAPHeader header = message.getSOAPHeader();
- // 創建SOAPBody對象
- SOAPBody body = envelope.getBody();
- // 創建XML的根元素
- SOAPHeaderElement headerElementRoot = header.addHeaderElement(new QName("http://server.why.com/", "c", "ns1"));
- SOAPBodyElement bodyElementRoot = body.addBodyElement(new QName("http://server.why.com/", "selectCustomerByName", "ns1"));
- headerElementRoot.addChildElement(new QName("name")).addTextNode("why");
- // 訪問Web服務地址
- SOAPMessage reMessage = connection.call(message, new URL("http://127.0.0.1:8080/helloService"));
- // 控制檯輸出返回的SOAP消息
- OutputStream os = System.out;
- reMessage.writeTo(os);
- // 輸出SOAP消息中的附件
- Iterator<AttachmentPart> it = reMessage.getAttachments();
- while (it.hasNext()) {
- InputStream ins = it.next().getDataHandler().getInputStream();
- byte[] b = new byte[ins.available()];
- OutputStream ous = new FileOutputStream("c:\\aaa.jpg");
- while (ins.read(b) != -1) {
- ous.write(b);
- }
- ous.close();
- }
- connection.close();
- }
- /**
- * 調用一個在soap:Body中傳遞參數的函數
- * @throws Exception
- */
- public static void selectMaxAgeCustomer() throws Exception{
- // 獲取SOAP連接工廠
- SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance();
- // 從SOAP連接工廠創建SOAP連接對象
- SOAPConnection connection = factory.createConnection();
- // 獲取消息工廠
- MessageFactory mFactory = MessageFactory.newInstance();
- // 從消息工廠創建SOAP消息對象
- SOAPMessage message = mFactory.createMessage();
- // 創建SOAPPart對象
- SOAPPart part = message.getSOAPPart();
- // 創建SOAP信封對象
- SOAPEnvelope envelope = part.getEnvelope();
- // 創建SOAPHeader對象
- SOAPHeader header = message.getSOAPHeader();
- // 創建SOAPBody對象
- SOAPBody body = envelope.getBody();
- // 設置Content-Type
- MimeHeaders hd = message.getMimeHeaders();
- hd.setHeader("Content-Type", "application/xop+xml; charset=utf-8; type=\"text/xml\"");
- // 創建XML的根元素
- SOAPBodyElement bodyElementRoot = body.addBodyElement(new QName("http://server.why.com/", "selectMaxAgeCustomer", "ns1"));
- // 創建Customer實例1
- SOAPElement elementC1 = bodyElementRoot.addChildElement(new QName("arg0"));
- elementC1.addChildElement(new QName("id")).addTextNode("1");
- elementC1.addChildElement(new QName("name")).addTextNode("A");
- elementC1.addChildElement(new QName("birthday")).addTextNode("1989-01-28T00:00:00.000+08:00");
- // 創建附件對象
- AttachmentPart attachment = message.createAttachmentPart(new DataHandler(new FileDataSource("c:\\c1.jpg")));
- // 設置Content-ID
- attachment.setContentId("<" + UUID.randomUUID().toString() + ">");
- attachment.setMimeHeader("Content-Transfer-Encoding", "binary");
- message.addAttachmentPart(attachment);
- SOAPElement elementData = elementC1.addChildElement(new QName("imageData"));
- // 添加XOP支持
- elementData.addChildElement(
- new QName("http://www.w3.org/2004/08/xop/include", "Include","xop"))
- .addAttribute(new QName("href"),"cid:" + attachment.getContentId().replaceAll("<", "").replaceAll(">", ""));
- // 創建Customer實例2
- SOAPElement elementC2 = bodyElementRoot.addChildElement(new QName("arg1"));
- elementC2.addChildElement(new QName("id")).addTextNode("2");
- elementC2.addChildElement(new QName("name")).addTextNode("B");
- elementC2.addChildElement(new QName("birthday")).addTextNode("1990-01-28T00:00:00.000+08:00");
- AttachmentPart attachment2 = message.createAttachmentPart(new DataHandler(new FileDataSource("c:\\c2.jpg")));
- attachment2.setContentId("<" + UUID.randomUUID().toString() + ">");
- message.addAttachmentPart(attachment2);
- SOAPElement elementData2 = elementC2.addChildElement(new QName("imageData"));
- elementData2.addChildElement(
- new QName("http://www.w3.org/2004/08/xop/include", "Include","xop"))
- .addAttribute(new QName("href"),"cid:" + attachment2.getContentId().replaceAll("<", "").replaceAll(">", ""));
- // 控制檯輸出發送的SOAP消息
- OutputStream os = new ByteArrayOutputStream();
- message.writeTo(os);
- String soapStr = os.toString();
- System.out.println("\n@@@@@@@@@@@@@@@@@@\n"+soapStr+"\n@@@@@@@@@@@@@@@@@@");
- // 訪問Web服務地址
- SOAPMessage reMessage = connection.call(message, new URL("http://127.0.0.1:8080/helloService"));
- // 控制檯輸出返回的SOAP消息
- OutputStream baos = new ByteArrayOutputStream();
- reMessage.writeTo(baos);
- String soapStr2 = baos.toString();
- System.out.println("\n#############\n"+soapStr2+"\n################");
- // // 輸出SOAP消息中的第一個子元素的元素名稱
- System.out.println("\n<<<<<<<<<<<<<<<<<<<" + reMessage.getSOAPBody().getFirstChild().getLocalName());
- // 輸出SOAP消息中的附件
- Iterator<AttachmentPart> it = reMessage.getAttachments();
- while (it.hasNext()) {
- InputStream ins = it.next().getDataHandler().getInputStream();
- byte[] b = new byte[ins.available()];
- OutputStream ous = new FileOutputStream("c:\\bbb.jpg");
- while (ins.read(b) != -1) {
- ous.write(b);
- }
- ous.close();
- }
- connection.close();
- }
- }
使用SAAJ創建附件時,需設置Content-Type=application/xop+xml; charset=utf-8; type="text/xml",否則服務器端獲取不到這個附件,查看發送給服務器端的SOAP消息可以看到,默認Content-Type被置爲text/xml; charset=utf-8,因此,需在代碼中加入:
- MimeHeaders hd = message.getMimeHeaders();
- hd.setHeader("Content-Type", "application/xop+xml; charset=utf-8; type=\"text/xml\"");
SOAPMessage有一個writeTo(OutputStream os)方法,可以將整個SOAP消息的內容寫入一個輸出流中,我們可以截獲這個輸出流的內容進行分析或再次整理。
附件是我的工程(2010-11-15更新)
轉載自 :http://wuhongyu.iteye.com/blog/810571