※SOAPBody
try {
// 查API javax.xml.soap的MessageFactory和SOAPMessage就會有以下的順序
MessageFactory factory = MessageFactory.newInstance();
SOAPMessage msg = factory.createMessage();
SOAPPart part = msg.getSOAPPart();
SOAPEnvelope envlope = part.getEnvelope();
SOAPBody body = envlope.getBody();
// <xx:bMethod xmlns:xx="!@#" />
SOAPBodyElement element = body.addBodyElement(new QName("!@#", "bMethod", "xx"));
element.setValue("1"); // element.setTextContent("1");
element.addChildElement("p1").setValue("2");
element.addChildElement("p2").setValue("3");
msg.writeTo(System.out);
} catch (SOAPException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
--------------------
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<xx:bMethod xmlns:xx="!@#">
1
<p1>2</p1>
<p2>3</p2>
</xx:bMethod>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
※上面的測試是不需要啟動伺服器的
※主要程式
※IMessage.java
package webservice;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import jaxb.Dept;
@WebService
public interface IMessage {
@WebResult(name = "dept") // 使用JAXB的unmarshal時,一定要叫dept,才能轉換,否則會出預期xxx的錯
public Dept aMethod(@WebParam(name = "dept") Dept d);// 使用XPath的NodeList時,一定要叫dept(預設是arg0),這樣XML才會正確,否則都找不到資料
/*
@WebResult的name裡面不要有空格,將SOAPMessage傳進來時,
例如將「bFunctionReturn」改成「b function return」之後,
會出「關聯元素類型 "b" 的屬性名稱 "function"
之後必須緊接 ' = ' 字元。」的錯
*/
@WebResult(name = "bFunctionReturn")
public String bMethod(@WebParam(name = "num") Integer i);
@WebResult(name = "dept") // 名稱要對應到QName,然後給HeaderElement
public Dept cMethod(@WebParam(name = "headerName", header = true) String headInfo);
}
※MessageImpl.java
package webservice;
import java.util.ArrayList;
import java.util.List;
import javax.jws.WebService;
import jaxb.Dept;
import jaxb.Employee;
@WebService(endpointInterface = "webservice.IMessage")
public class MessageImpl implements IMessage {
@Override
public Dept aMethod(Dept dept) {
System.out.println("a方法" + dept);
return dept;
}
@Override
public String bMethod(Integer i) {
System.out.println("b方法");
return i + "b";
}
@Override
public Dept cMethod(String headInfo) {
System.out.println("headInfo=" + headInfo);
List<Employee> list = new ArrayList<>();
list.add(new Employee("e1", 1));
list.add(new Employee("e2", 2));
return new Dept("d1", 123, list);
}
}
※MyServer.java
package webservice;
import javax.xml.ws.Endpoint;
public class MyServer {
public static void main(String[] args) {
Endpoint.publish("http://localhost:8888/aaa/bbb/ccc", new MessageImpl());
System.out.println("server is start...");
}
}
※打開xxx~~~?wsdl的網頁
<definitions
...
targetNamespace="http://webservice/"
name="MessageImplService">
...
<service name="MessageImplService">
<port name="MessageImplPort" binding="tns:MessageImplPortBinding">
<soap:address location="http://localhost:8888/aaa/bbb/ccc"/>
</port>
</service>
</definitions>
※會用到definitions標籤的targetNamespace和name、還有service的子標籤port的name屬性
※測試Service.Mode.MESSAGE
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
String ns = "http://webservice/";
try {
URL url = new URL("http://localhost:8888/aaa/bbb/ccc?wsdl");
QName sqname = new QName(ns, "MessageImplService");
Service service = Service.create(url, sqname);
QName qname = new QName(ns, "MessageImplPort");
Dispatch<SOAPMessage> dispatch = service.createDispatch(qname, SOAPMessage.class, Service.Mode.MESSAGE);
MessageFactory factory = MessageFactory.newInstance();
SOAPMessage msg = factory.createMessage();
SOAPPart part = msg.getSOAPPart();
SOAPEnvelope envlope = part.getEnvelope();
SOAPBody body = envlope.getBody();
SOAPBodyElement element = body.addBodyElement(new QName(ns, "bMethod", "xx"));
// 不打num會接收不到,物件會接收「null」字串;基本型別接收「0」
element.addChildElement("num").setValue("2");
msg.writeTo(System.out);
System.out.println();
// 將msg傳到server
SOAPMessage resMsg = dispatch.invoke(msg);
resMsg.writeTo(System.out);
System.out.println();
// 從server傳回來的訊息
Document doc = resMsg.getSOAPPart().getEnvelope().getBody().extractContentAsDocument();
NodeList nodeList = doc.getElementsByTagName("bFunctionReturn");
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
System.out.println(node.getTextContent()); // 2b
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (SOAPException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
--------------------
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<xx:bMethod xmlns:xx="http://webservice/">
<num>2</num>
</xx:bMethod>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
--------------------
<S:Envelope
xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<S:Body>
<ns2:bMethodResponse xmlns:ns2="http://webservice/">
<bFunctionReturn>2b</bFunctionReturn>
</ns2:bMethodResponse>
</S:Body>
</S:Envelope>
※
※Service.Mode.PAYLOAD
import java.io.StringReader;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.NodeList;
import jaxb.Dept;
import jaxb.Employee;
public void serviceModePAYLOADExample() {
String ns = "http://webservice/";
try {
URL url = new URL("http://localhost:8888/aaa/bbb/ccc?wsdl");
QName sqname = new QName(ns, "MessageImplService");
Service service = Service.create(url, sqname);
QName qname = new QName(ns, "MessageImplPort");
Dispatch<Source> dispatch = service.createDispatch(qname, Source.class, Service.Mode.PAYLOAD);
List<Employee> list = new ArrayList<>();
list.add(new Employee("Bruce", 123));
list.add(new Employee("Jacky", 456));
list.add(new Employee("Mary", 789));
Dept dept = new Dept("information", 2, list);
JAXBContext jaxb = JAXBContext.newInstance(Dept.class);
Marshaller ms = jaxb.createMarshaller();
// 去掉前面的<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
ms.setProperty(Marshaller.JAXB_FRAGMENT, true);
StringWriter out = new StringWriter();
ms.marshal(dept, out);
StringBuffer payload = new StringBuffer();
payload.append("<xxx:aMethod xmlns:xxx=\"");
payload.append(ns);
payload.append("\">");
payload.append(out.toString());
payload.append("</xxx:aMethod>");
System.out.println(payload);
StreamSource rs = new StreamSource(new StringReader(payload.toString()));
Source response = (Source) dispatch.invoke(rs);
Transformer tran = TransformerFactory.newInstance().newTransformer();
DOMResult result = new DOMResult();
tran.transform(response, result);
XPath xpath = XPathFactory.newInstance().newXPath();
// 如果是找dept它的子元素,能找到,但unmarshal會有錯誤
NodeList nodeList = (NodeList) xpath.evaluate("//dept", result.getNode(), XPathConstants.NODESET);
System.out.println(nodeList.getLength());
for (int i = 0; i < nodeList.getLength(); i++) {
System.out.println(nodeList.item(i));
Dept ru = (Dept) jaxb.createUnmarshaller().unmarshal(nodeList.item(i));
System.out.println(ru.getName() + System.getProperty("line.separator"));
for (Employee emp : ru.getEmps()) {
System.out.println(emp.getName());
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (JAXBException e) {
e.printStackTrace();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerFactoryConfigurationError | TransformerException e) {
e.printStackTrace();
} catch (XPathExpressionException e) {
e.printStackTrace();
}
}
※
※SOAP Header測試
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import jaxb.Dept;
public void soapHeadTest() {
String ns = "http://webservice/";
try {
URL url = new URL("http://localhost:8888/aaa/bbb/ccc?wsdl");
QName sqname = new QName(ns, "MessageImplService");
Service service = Service.create(url, sqname);
QName qname = new QName(ns, "MessageImplPort");
Dispatch<SOAPMessage> dispatch = service.createDispatch(qname, SOAPMessage.class, Service.Mode.MESSAGE);
MessageFactory factory = MessageFactory.newInstance();
SOAPMessage msg = factory.createMessage();
SOAPPart part = msg.getSOAPPart();
SOAPEnvelope envlope = part.getEnvelope();
SOAPBody body = envlope.getBody();
SOAPHeader header = envlope.getHeader();
if (header == null) {
header = envlope.addHeader();
}
SOAPHeaderElement headElement = header.addHeaderElement(new QName(ns, "headerName", "xx"));
headElement.setValue("header value!");
body.addBodyElement(new QName(ns, "cMethod", "xx"));
msg.writeTo(System.out);
System.out.println();
// 將msg傳到server
SOAPMessage resMsg = dispatch.invoke(msg);
resMsg.writeTo(System.out);
System.out.println();
// 從server傳回來的訊息
Document doc = resMsg.getSOAPPart().getEnvelope().getBody().extractContentAsDocument();
// Document doc = resMsg.getSOAPBody().extractContentAsDocument();
NodeList nodeList = doc.getElementsByTagName("dept");
JAXBContext jaxb = JAXBContext.newInstance(Dept.class);
for (int i = 0; i < nodeList.getLength(); i++) {
Dept dept = (Dept) jaxb.createUnmarshaller().unmarshal(nodeList.item(i));
System.out.println(dept.getName());
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (SOAPException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JAXBException e) {
e.printStackTrace();
}
}
※
※Exception
加入Exception後,會在wsdl的網址出現fault的標籤
@WebResult(name = "dept")
public Dept dMethod(@WebParam(name = "name") String name, @WebParam(name = "did") int did) throws DeptException;
--------------------
@Override
public Dept dMethod(String name, int did) throws DeptException {
if ("xxx".equals(name) && did == 111) {
List<Employee> list = new ArrayList<>();
list.add(new Employee("e1", 1));
list.add(new Employee("e2", 2));
Dept dept = new Dept("xxx", 111, list);
return dept;
}
/*
* 使用RuntimeException,也會在伺服器端的console報錯,因為DeptException在wsdl的網頁有截取(fault
* );而RuntimeException沒有
* 只要有在wsdl有設定Exception,截取到就會直接往客戶端拋,所以伺服器端沒有截取錯誤訊息
* 如沒有在wsdl設定,則伺服器端和客戶端接會拋出訊息
*/
// throw new RuntimeException(name + " and " + did + " not match!" +
// System.getProperty("line.separator"));
throw new DeptException(name + " and " + did + " not match!" + System.getProperty("line.separator"));
}
※name是xxx,did是111才不會出錯,但這裡要測試出錯,所以待會的測試類不要輸入這一組
※DeptException.java
public class DeptException extends Exception {
public DeptException() {
super();
}
public DeptException(String arg0, Throwable arg1, boolean arg2, boolean arg3) {
super(arg0, arg1, arg2, arg3);
}
public DeptException(String message, Throwable cause) {
super(message, cause);
}
public DeptException(String message) {
super(message);
}
public DeptException(Throwable cause) {
super(cause);
}
}
※這裡的建構子都是用IDE產生的
※測試類
String ns = "http://webservice/";
try {
URL url = new URL("http://localhost:8888/aaa/bbb/ccc?wsdl");
QName sqname = new QName(ns, "MessageImplService");
Service service = Service.create(url, sqname);
QName qname = new QName(ns, "MessageImplPort");
Dispatch<SOAPMessage> dispatch = service.createDispatch(qname, SOAPMessage.class, Service.Mode.MESSAGE);
MessageFactory factory = MessageFactory.newInstance();
SOAPMessage msg = factory.createMessage();
SOAPPart part = msg.getSOAPPart();
SOAPEnvelope envlope = part.getEnvelope();
SOAPBody body = envlope.getBody();
SOAPBodyElement element = body.addBodyElement(new QName(ns, "dMethod", "xx"));
element.addChildElement("name").setValue("xxx");// 不打num會接收null;但如果是int會接收0
element.addChildElement("did").setValue("222");
msg.writeTo(System.out);
System.out.println();
// 將msg傳到server
SOAPMessage resMsg = dispatch.invoke(msg);
resMsg.writeTo(System.out);
System.out.println();
// 從server傳回來的訊息
Document doc = resMsg.getSOAPBody().extractContentAsDocument();
NodeList nodeList = doc.getElementsByTagName("dept");
JAXBContext jaxb = JAXBContext.newInstance(Dept.class);
for (int i = 0; i < nodeList.getLength(); i++) {
Dept dept = (Dept) jaxb.createUnmarshaller().unmarshal(nodeList.item(i));
for (Employee e : dept.getEmps()) {
System.out.println(e.getName());
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (SOAPException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JAXBException e) {
e.printStackTrace();
} catch (SOAPFaultException e) {
// 不可截取DeptException,使用wsimport就可以知道
System.out.println(e.getMessage());// call DeptException(String)
}
※
※Handler
LogicalHandler:獲取SOAPBody的訊息
SOAPHandler:獲取SOAPMessage的訊息
伺服器和客戶端都可以設定很多LH的和SH
客戶端都先處理LH,全部的LH處理完才處理SH
伺服器端則相反
@WebService(endpointInterface = "webservice.IMessage")
@HandlerChain(file="/soap/handler/handlers.xml")
public class MessageImpl implements IMessage {
// ...
}
※這裡的@HandlerChain裡的xml路徑只要抓的到,在啟伺服器時才不會出錯
※handlers.xml
<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
<handler-chain>
<handler>
<handler-name>ooo.xxx.z1</handler-name>
<handler-class>soap.handler.Handler1</handler-class>
</handler>
<handler>
<handler-name>ooo.xxx.z2</handler-name>
<handler-class>soap.handler.Handler2</handler-class>
</handler>
</handler-chain>
</handler-chains>
※寫了兩支處理handler的xml,所以也要實作出來,將上面的網址複製下來改即可
※Handler1.xml
public class Handler1 implements SOAPHandler<SOAPMessageContext> {
@Override
public void close(MessageContext arg0) {
System.out.println("s1 close");
}
@Override
public boolean handleFault(SOAPMessageContext arg0) {
System.out.println("s1 handleFault");
return true;
}
@Override
public boolean handleMessage(SOAPMessageContext arg0) {
System.out.println("s1 handleMessage");
return true;
}
@Override
public Set<QName> getHeaders() {
System.out.println("s1 getHeaders");
return null;
}
}
※
※Handler2.java
public class Handler2 implements SOAPHandler<SOAPMessageContext> {
@Override
public void close(MessageContext arg0) {
System.out.println("s2 close");
}
@Override
public boolean handleFault(SOAPMessageContext arg0) {
System.out.println("s2 handleFault");
return true;
}
@Override
public boolean handleMessage(SOAPMessageContext arg0) {
System.out.println("s2 handleMessage");
return true;
}
@Override
public Set<QName> getHeaders() {
System.out.println("s2 getHeaders");
return null;
}
}
※測試時還是用Exception的測試類
※沒有錯時的結果為:
s1 getHeaders
s2 getHeaders
server is start...
s2 handleMessage
s1 handleMessage
s1 handleMessage
s2 handleMessage
s1 close
s2 close
※有錯時的結果為:
s1 getHeaders
s2 getHeaders
server is start...
s2 handleMessage
s1 handleMessage
s1 handleFault
s2 handleFault
s1 close
s2 close
※可以發現在啟何服器時都會先執行兩支的getHeaders方法,結束也都會執行close
※有錯時才會執行handleFault方法
※要注意handleMessage和其他是相反的,先執行s2;後執行s1
沒錯時,一支handleMessage是request和response,共兩次;有錯時只會執行一次
※handleMessage執行完才會執行Exception;使用RuntimeException還是會在伺服器端報錯