还是以前面的HelloWord服务为例子。
服务端:
HelloWord.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package
ch03.ts;
import
javax.jws.WebMethod;
import
javax.jws.WebParam;
import
javax.jws.WebService;
import
javax.xml.ws.Holder;
@WebService
public
interface
HelloWord {
@WebMethod
void
sayHello(
@WebParam
(name=
"name"
) String name,
@WebParam
(name=
"wh"
,mode=WebParam.Mode.INOUT) Holder<String> wh,
@WebParam
(name=
"hf"
,mode=WebParam.Mode.OUT) Holder<String> hf);
}
|
HelloWordImpl.java(注意:这里添加了@BindingType注解)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package
ch03.ts;
import
javax.jws.HandlerChain;
import
javax.jws.WebService;
import
javax.xml.ws.BindingType;
import
javax.xml.ws.Holder;
@WebService
(endpointInterface =
"ch03.ts.HelloWord"
)
@HandlerChain
(file =
"handler-chain-server.xml"
)
@BindingType
(value =
"http://java.sun.com/xml/ns/jaxws/2003/05/soap/bindings/HTTP/"
)
public
class
HelloWordImpl
implements
HelloWord {
@Override
public
void
sayHello(String name, Holder<String> wh, Holder<String> hf) {
System.out.println(name +
"!"
+ wh.value);
wh.value =
"你们好"
;
hf.value =
"同学们"
;
}
}
|
UUIDValidator.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
package
fibC;
import
java.util.Iterator;
import
java.util.Set;
import
java.util.UUID;
import
javax.xml.namespace.QName;
import
javax.xml.soap.Node;
import
javax.xml.soap.SOAPBody;
import
javax.xml.soap.SOAPConstants;
import
javax.xml.soap.SOAPEnvelope;
import
javax.xml.soap.SOAPException;
import
javax.xml.soap.SOAPFault;
import
javax.xml.soap.SOAPHeader;
import
javax.xml.soap.SOAPMessage;
import
javax.xml.ws.handler.MessageContext;
import
javax.xml.ws.handler.soap.SOAPHandler;
import
javax.xml.ws.handler.soap.SOAPMessageContext;
import
javax.xml.ws.soap.SOAPFaultException;
/**
* 服务器端验证UUID值
* @author fuhd
*/
public
class
UUIDValidator
implements
SOAPHandler<SOAPMessageContext> {
private
static
final
int
UUIDVARIANT =
2
;
//layout
private
static
final
int
UUIDVERSION =
4
;
//version
@SuppressWarnings
({
"rawtypes"
})
@Override
public
boolean
handleMessage(SOAPMessageContext context) {
Boolean resp = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if
(!resp){
try
{
SOAPMessage msg = context.getMessage();
SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
SOAPHeader hdr = env.getHeader();
if
(hdr ==
null
)
generateSOAPFault(msg,
"No message header."
);
Iterator it = hdr.extractHeaderElements(SOAPConstants.URI_SOAP_ACTOR_NEXT);
if
(it ==
null
|| !it.hasNext())
generateSOAPFault(msg,
"No header block for next actor."
);
Node next = (Node)it.next();
String value = (next ==
null
)?
null
:next.getValue();
if
(value ==
null
)
generateSOAPFault(msg,
"No UUID in header block."
);
UUID uuid = UUID.fromString(value.trim());
if
(uuid.variant() != UUIDVARIANT || uuid.version() != UUIDVERSION)
generateSOAPFault(msg,
"Bad UUID variant or version"
);
System.out.println(value.trim());
}
catch
(SOAPException e) {
e.printStackTrace();
}
}
return
true
;
}
@Override
public
boolean
handleFault(SOAPMessageContext context) {
return
true
;
}
@Override
public
void
close(MessageContext context) {}
@Override
public
Set<QName> getHeaders() {
return
null
;
}
private
void
generateSOAPFault(SOAPMessage msg,String reason){
try
{
SOAPBody body = msg.getSOAPPart().getEnvelope().getBody();
SOAPFault fault = body.addFault();
fault.setFaultString(reason);
throw
new
SOAPFaultException(fault);
}
catch
(SOAPException e) {
e.printStackTrace();
}
}
}
|
handler-chain-server.xml
1
2
3
4
5
6
7
8
9
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
standalone
=
"yes"
?>
<
javaee:handler-chains
xmlns:javaee
=
"http://java.sun.com/xml/ns/javaee"
xmlns:xsd
=
"http://www.w3.org/2001/XMLSchema"
>
<
javaee:handler-chain
>
<
javaee:handler
>
<
javaee:handler-class
>fibC.UUIDValidator</
javaee:handler-class
>
</
javaee:handler
>
</
javaee:handler-chain
>
</
javaee:handler-chains
>
|
HelloWordPublisher.java
1
2
3
4
5
6
7
8
9
|
package
ch03.ts;
import
javax.xml.ws.Endpoint;
public
class
HelloWordPublisher {
public
static
void
main(String[] args) {
Endpoint.publish(
"http://localhost:7654/ts"
,
new
HelloWordImpl());
}
}
|
我们再通过wsimport命令生成客户端代码:
1
|
% wsimport -keep -extension -p hw5 http:
//localhost
:7654
/ts
?wsdl
|
注意上面 -extension 这个参数。
生成的客户端代码有:
HelloWord.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
package
hw5;
import
javax.jws.WebMethod;
import
javax.jws.WebParam;
import
javax.jws.WebService;
import
javax.xml.bind.annotation.XmlSeeAlso;
import
javax.xml.ws.Action;
import
javax.xml.ws.Holder;
import
javax.xml.ws.RequestWrapper;
import
javax.xml.ws.ResponseWrapper;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.4-b01
* Generated source version: 2.2
*/
@WebService
(name =
"HelloWord"
, targetNamespace =
"http://ts.ch03/"
)
@XmlSeeAlso
({
ObjectFactory.
class
})
public
interface
HelloWord {
/**
* @param wh
* @param name
* @param hf
*/
@WebMethod
@RequestWrapper
(localName =
"sayHello"
, targetNamespace =
"http://ts.ch03/"
,
className =
"hw5.SayHello"
)
@ResponseWrapper
(localName =
"sayHelloResponse"
, targetNamespace =
"http://ts.ch03/"
,
className =
"hw5.SayHelloResponse"
)
@Action
(input =
"http://ts.ch03/HelloWord/sayHelloRequest"
,
output =
"http://ts.ch03/HelloWord/sayHelloResponse"
)
public
void
sayHello(
@WebParam
(name =
"name"
, targetNamespace =
""
)
String name,
@WebParam
(name =
"wh"
, targetNamespace =
""
, mode = WebParam.Mode.INOUT)
Holder<String> wh,
@WebParam
(name =
"hf"
, targetNamespace =
""
, mode = WebParam.Mode.OUT)
Holder<String> hf);
}
|
HelloWordImplService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
package
hw5;
import
java.net.MalformedURLException;
import
java.net.URL;
import
javax.jws.HandlerChain;
import
javax.xml.namespace.QName;
import
javax.xml.ws.Service;
import
javax.xml.ws.WebEndpoint;
import
javax.xml.ws.WebServiceClient;
import
javax.xml.ws.WebServiceException;
import
javax.xml.ws.WebServiceFeature;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.4-b01
* Generated source version: 2.2
*/
@WebServiceClient
(name =
"HelloWordImplService"
, targetNamespace =
"http://ts.ch03/"
,
wsdlLocation =
"http://localhost:7654/ts?wsdl"
)
@HandlerChain
(file =
"handler-chain.xml"
)
public
class
HelloWordImplService
extends
Service {
private
final
static
URL HELLOWORDIMPLSERVICE_WSDL_LOCATION;
private
final
static
WebServiceException HELLOWORDIMPLSERVICE_EXCEPTION;
private
final
static
QName HELLOWORDIMPLSERVICE_QNAME =
new
QName(
"http://ts.ch03/"
,
"HelloWordImplService"
);
static
{
URL url =
null
;
WebServiceException e =
null
;
try
{
url =
new
URL(
"http://localhost:7654/ts?wsdl"
);
}
catch
(MalformedURLException ex) {
e =
new
WebServiceException(ex);
}
HELLOWORDIMPLSERVICE_WSDL_LOCATION = url;
HELLOWORDIMPLSERVICE_EXCEPTION = e;
}
public
HelloWordImplService() {
super
(__getWsdlLocation(), HELLOWORDIMPLSERVICE_QNAME);
}
public
HelloWordImplService(WebServiceFeature... features) {
super
(__getWsdlLocation(), HELLOWORDIMPLSERVICE_QNAME, features);
}
public
HelloWordImplService(URL wsdlLocation) {
super
(wsdlLocation, HELLOWORDIMPLSERVICE_QNAME);
}
public
HelloWordImplService(URL wsdlLocation, WebServiceFeature... features) {
super
(wsdlLocation, HELLOWORDIMPLSERVICE_QNAME, features);
}
public
HelloWordImplService(URL wsdlLocation, QName serviceName) {
super
(wsdlLocation, serviceName);
}
public
HelloWordImplService(URL wsdlLocation, QName serviceName,
WebServiceFeature... features) {
super
(wsdlLocation, serviceName, features);
}
/**
* @return
* returns HelloWord
*/
@WebEndpoint
(name =
"HelloWordImplPort"
)
public
HelloWord getHelloWordImplPort() {
return
super
.getPort(
new
QName(
"http://ts.ch03/"
,
"HelloWordImplPort"
),
HelloWord.
class
);
}
/**
* @param features
* A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.
* Supported features not in the <code>features</code> parameter will have
* their default values.
* @return
* returns HelloWord
*/
@WebEndpoint
(name =
"HelloWordImplPort"
)
public
HelloWord getHelloWordImplPort(WebServiceFeature... features) {
return
super
.getPort(
new
QName(
"http://ts.ch03/"
,
"HelloWordImplPort"
),
HelloWord.
class
, features);
}
private
static
URL __getWsdlLocation() {
if
(HELLOWORDIMPLSERVICE_EXCEPTION!=
null
) {
throw
HELLOWORDIMPLSERVICE_EXCEPTION;
}
return
HELLOWORDIMPLSERVICE_WSDL_LOCATION;
}
}
|
SayHello.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
package
hw5;
import
javax.xml.bind.annotation.XmlAccessType;
import
javax.xml.bind.annotation.XmlAccessorType;
import
javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for sayHello complex type.
* <p>The following schema fragment specifies the expected content
* contained within this class.
* <pre>
* <complexType name="sayHello">
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element name="name" type="{http://www.w3.org/2001/XMLSchema}string"
* minOccurs="0"/>
* <element name="wh" type="{http://www.w3.org/2001/XMLSchema}string"
* minOccurs="0"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*/
@XmlAccessorType
(XmlAccessType.FIELD)
@XmlType
(name =
"sayHello"
, propOrder = {
"name"
,
"wh"
})
public
class
SayHello {
protected
String name;
protected
String wh;
/**
* Gets the value of the name property.
* @return
* possible object is
* {@link String }
*/
public
String getName() {
return
name;
}
/**
* Sets the value of the name property.
* @param value
* allowed object is
* {@link String }
*/
public
void
setName(String value) {
this
.name = value;
}
/**
* Gets the value of the wh property.
* @return
* possible object is
* {@link String }
*/
public
String getWh() {
return
wh;
}
/**
* Sets the value of the wh property.
* @param value
* allowed object is
* {@link String }
*/
public
void
setWh(String value) {
this
.wh = value;
}
}
|
SayHelloResponse.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
package
hw5;
import
javax.xml.bind.annotation.XmlAccessType;
import
javax.xml.bind.annotation.XmlAccessorType;
import
javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for sayHelloResponse complex type.
* <p>The following schema fragment specifies the expected content contained
* within this class.
* <pre>
* <complexType name="sayHelloResponse">
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element name="wh" type="{http://www.w3.org/2001/XMLSchema}string"
* minOccurs="0"/>
* <element name="hf" type="{http://www.w3.org/2001/XMLSchema}string"
* minOccurs="0"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*/
@XmlAccessorType
(XmlAccessType.FIELD)
@XmlType
(name =
"sayHelloResponse"
, propOrder = {
"wh"
,
"hf"
})
public
class
SayHelloResponse {
protected
String wh;
protected
String hf;
/**
* Gets the value of the wh property.
* @return
* possible object is
* {@link String }
*/
public
String getWh() {
return
wh;
}
/**
* Sets the value of the wh property.
* @param value
* allowed object is
* {@link String }
*/
public
void
setWh(String value) {
this
.wh = value;
}
/**
* Gets the value of the hf property.
* @return
* possible object is
* {@link String }
*/
public
String getHf() {
return
hf;
}
/**
* Sets the value of the hf property.
* @param value
* allowed object is
* {@link String }
*/
public
void
setHf(String value) {
this
.hf = value;
}
}
|
ObjectFactory.java与package-info.java省略。
UUIDHandler.java(注意代码中helem.setMustUnderstand(true)这句代码)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
package
fibC;
import
java.io.IOException;
import
java.util.Set;
import
java.util.UUID;
import
javax.xml.namespace.QName;
import
javax.xml.soap.SOAPConstants;
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.ws.handler.MessageContext;
import
javax.xml.ws.handler.soap.SOAPHandler;
import
javax.xml.ws.handler.soap.SOAPMessageContext;
/**
* SOAP处理程序(Handler)
* @author FUHD
*/
public
class
UUIDHandler
implements
SOAPHandler<SOAPMessageContext> {
@Override
public
boolean
handleMessage(SOAPMessageContext context) {
Boolean request = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if
(request){
UUID uuid = UUID.randomUUID();
try
{
SOAPMessage msg = context.getMessage();
SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
SOAPHeader hdr = env.getHeader();
if
(hdr ==
null
) hdr = env.addHeader();
QName qname =
new
QName(
"http://ts.ch03/"
,
"uuid"
);
SOAPHeaderElement helem = hdr.addHeaderElement(qname);
helem.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT);
helem.setMustUnderstand(
true
);
helem.addTextNode(uuid.toString());
msg.saveChanges();
msg.writeTo(System.out);
System.out.println();
}
catch
(SOAPException e1) {
e1.printStackTrace();
}
catch
(IOException e2) {
e2.printStackTrace();
}
}
return
true
;
}
@Override
public
boolean
handleFault(SOAPMessageContext context) {
try
{
context.getMessage().writeTo(System.out);
}
catch
(SOAPException e1) {
e1.printStackTrace();
}
catch
(IOException e2) {
e2.printStackTrace();
}
return
true
;
}
@Override
public
void
close(MessageContext context) {
}
@Override
public
Set<QName> getHeaders() {
return
null
;
}
}
|
handler-chain.xml
1
2
3
4
5
6
7
8
9
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
standalone
=
"yes"
?>
<
javaee:handler-chains
xmlns:javaee
=
"http://java.sun.com/xml/ns/javaee"
xmlns:xsd
=
"http://www.w3.org/2001/XMLSchema"
>
<
javaee:handler-chain
>
<
javaee:handler
>
<
javaee:handler-class
>fibC.UUIDHandler</
javaee:handler-class
>
</
javaee:handler
>
</
javaee:handler-chain
>
</
javaee:handler-chains
>
|
HelloWordClient.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package
hw5;
import
javax.xml.ws.Holder;
public
class
HelloWordClient {
public
static
void
main(String[] args) {
String name =
"老板"
;
Holder<String> wh =
new
Holder<String>();
wh.value =
"你好"
;
Holder<String> hf =
new
Holder<String>();
HelloWordImplService service =
new
HelloWordImplService();
HelloWord port = service.getPort(HelloWord.
class
);
port.sayHello(name, wh, hf);
System.out.println(hf.value +
","
+ wh.value);
}
}
|
如上例,我们只需要很少的几个步骤就可以将HelloWord服务对SOAP版本的支持由1.1版转换到1.2版。比较重要的一步就是在SIB增加注解@BindingType。
JWS还拥有一个针对标准的SOAP1.2绑定常量:
1
|
javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_BINDING
|
但是端点发布程序却不能够在此标准绑定版本下生成WSDL文档。变通方法就是使用非标准绑定,正如我们这个例子所演示的。当服务发布后,端点提示有下面的非致命警告:
1
2
|
五月
06
,
2014
2
:
32
:
13
下午 com.sun.xml.internal.ws.server.EndpointFactory generateWSDL
警告: Generating non-standard WSDL
for
the specified binding
|
在前面,wsgen工具也是利用了此种方法,但是现在wsimport通过“-extension”标识指明该命令将以非标准的方式生成客户端代理程序。如上例中:
1
|
% wsimport -keep -extension -p hw5 http:
//localhost
:7654
/ts
?wsdl
|
另外,我们在服务端使用了UUIDValidator这个Handler程序,因此修改UUIDvalidator,展示SOAP1.2的关键特点。通常在一个SOAP1.2报头中使用属性mustUnderstand。如上例,直接在setActor方法调用的后面:
1
2
|
helem.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT);
helem.setMustUnderstand(
true
);
//SOAP1.2
|
根据这个例子抓取它的一个请求消息内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?
xml
version
=
"1.0"
?>
<
S:Envelope
xmlns:S
=
"http://www.w3.org/2003/05/soap-envelope"
>
<
S:Header
>
<
uuid
xmlns
=
"http://ts.ch03/"
xmlns:env
=
"http://www.w3.org/2003/05/soap-envelope"
env:mustUnderstand
=
"true"
env:role
=
"http://schemas.xmlsoap.org/soap/actor/next"
>
03d7d564-b1b1-457a-9129-069a82acb8d6
</
uuid
>
</
S:Header
>
<
S:Body
>
<
ns2:sayHello
xmlns:ns2
=
"http://ts.ch03/"
>
<
name
xmlns
=
""
>老板</
name
>
<
wh
xmlns
=
""
>你好</
wh
>
</
ns2:sayHello
>
</
S:Body
>
</
S:Envelope
>
|
从上面请求消息可以看到:env:nustUnderstand="true"已经存在于uuid的属性中。
另外,在端点程序生成的WSDL文档的绑定部分反映了SOAP1.1改为SOAP1.2后发生的变化:
1
2
3
4
5
6
7
8
9
10
11
12
|
<
binding
name
=
"HelloWordImplPortBinding"
type
=
"tns:HelloWord"
>
<
soap12:binding
transport
=
"http://schemas.xmlsoap.org/soap/http"
style
=
"document"
/>
<
operation
name
=
"sayHello"
>
<
soap12:operation
soapAction
=
""
/>
<
input
>
<
soap12:body
use
=
"literal"
/>
</
input
>
<
output
>
<
soap12:body
use
=
"literal"
/>
</
output
>
</
operation
>
</
binding
>
|
考虑到SOAP1.1和SOAP1.2之间差别不大,以及SOAP1.1已经是事实上的标准,除非不得已采用SOAP1.2版外,持续支持SOAP1.1版本是非常有意义的。最后,SOAP1.2向后兼容SOAP1.1版。