Monday May 21, 2007
Using JAX-WS 2.1 Addressing Features
This techtip will focus on using new features in the Maintenance Release of JAX-WS 2.1 for WS-Addressing support based on the Web Services Addressing 1.0 Core and Web Services Addressing 1.0 SOAP Binding specifications. In this example the server will highlight the use of the javax.xml.ws.soap.Addressing annotation for enabling addressing on the server-side and the use of the javax.xml.ws.Action and javax.xml.ws.FaultAction annotations for explicit association of WS-Addressing Action message addressing properties with input, output and fault messages. The client will highlight the use of javax.xml.ws.WebServiceFeature and javax.xml.ws.soap.AddressingFeature for enabling addressing on the client-side. Both client and server will also show how to obtain an EnpointReference (EPR) for the Endpoint.
The example code below was developed and tested using a GlassFish implementation.
Developing a Web Service to use the new Web Service Addressing features in JAX-WS 2.1
Below is an example of a web service implementation class which uses the Web Service Addressing features. To write a portable endpoint with this version of JAX-WS, an endpoint MUST explicitly specify what WS-Addressing Actions are to be used via the Action and FaultAction annotations. After the W3C WG on WS-Addressing has specified how the use of WS-Addressing is specified in the WSDL, and what the default value must be for Action headers, a future version of JAX-WS will remove these requirements. The annotation @Addressing indicates that this service wants to enable WS-Addressing. The required property is explicitly set to specify that WS-Addressing headers MUST be present on incoming messages. At runtime, WS-Addressing headers MUST be consumed by the receiver and produced by the sender. The Action and FaultAction annotations explicitly associate the WS-Addressing Action message addressing properties on the input and output messages on the addNumbersExplicitInputOutputAction method and on the input, output and fault messages on the addNumbersExplicitFaultAction method. A W3CEnpointReference is obtained from the WebServiceContext via the getEndpointReference() method call as shown in the getEPR() method call.
The server-side implementation also contains a handler ServerSOAPHandler that just dumps out the SOAPMessage from the handleMessage and handleFault methods to show the WS-Addressing header information.
Server-Side Implementation Code Example
AddNumbersImpl.java
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package action.server;
import javax.annotation.Resource;
import javax.jws.WebService;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.Action;
import javax.xml.ws.FaultAction;
import javax.xml.ws.BindingType;
import javax.jws.HandlerChain;
import javax.xml.ws.soap.Addressing;
import javax.xml.ws.soap.SOAPBinding;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
@WebService(
name="AddNumbers",
portName="AddNumbersPort",
targetNamespace="http://addnumbers.org",
serviceName="AddNumbersService"
)
@HandlerChain(name="", file="server-handler.xml")
@BindingType(value=SOAPBinding.SOAP11HTTP_BINDING)
@Addressing(enabled=true, required=true)
public class AddNumbersImpl {
@Resource
WebServiceContext wsc;
public W3CEndpointReference getEPR() {
return (W3CEndpointReference) wsc.getEndpointReference();
}
@Action(
input="http://addnumbers.org/input",
output="http://addnumbers.org/output")
public int addNumbersExplicitInputOutputAction(int number1, int number2) throws AddNumbersException {
return addNumbers(number1, number2);
}
@Action(
input="input",
output="output",
fault={
@FaultAction(className=AddNumbersException.class, value="http://addnumbers.org/fault/addnumbers")
})
public int addNumbersExplicitFaultAction(int number1, int number2) throws AddNumbersException {
return addNumbers(number1, number2);
}
private int addNumbers(int number1, int number2) throws AddNumbersException {
if (number1 < 0 || number2 < 0) {
throw new AddNumbersException("Negative numbers can't be added!",
"Numbers: " + number1 + ", " + number2);
}
return number1 + number2;
}
}
ServerSOAPHandler.java
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package action.server;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.MessageContext;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.Text;
import javax.xml.namespace.QName;
import java.util.Set;
import java.io.ByteArrayOutputStream;
public class ServerSOAPHandler implements SOAPHandler {
private String getMessageEncoding(SOAPMessage msg) throws SOAPException {
String encoding = "utf-8";
if (msg.getProperty(SOAPMessage.CHARACTER_SET_ENCODING) != null) {
encoding = msg.getProperty(SOAPMessage.CHARACTER_SET_ENCODING).toString();
}
return encoding;
}
private void dumpSOAPMessage(SOAPMessage msg) {
if(msg == null) {
System.out.println("SOAP Message is null");
return;
}
System.out.println("");
System.out.println("--------------------");
System.out.println("DUMP OF SOAP MESSAGE");
System.out.println("--------------------");
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
msg.writeTo(baos);
System.out.println(baos.toString(getMessageEncoding(msg)));
} catch(Exception e) {
e.printStackTrace();
}
}
public boolean handleMessage(SOAPMessageContext context) {
System.out.println("ServerSOAPHandler.handleMessage");
boolean outbound = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) {
System.out.println("Direction=outbound");
} else {
System.out.println("Direction=inbound");
}
try {
SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
dumpSOAPMessage(msg);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
public boolean handleFault(SOAPMessageContext context) {
System.out.println("ServerSOAPHandler.handleFault");
boolean outbound = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) {
System.out.println("Direction=outbound");
} else {
System.out.println("Direction=inbound");
}
if (!outbound) {
try {
SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
dumpSOAPMessage(msg);
if (context.getMessage().getSOAPBody().getFault() != null) {
String detailName = null;
try {
detailName = context.getMessage().getSOAPBody().getFault().getDetail().getFirstChild().getLocalName();
System.out.println("detailName="+detailName);
} catch(Exception e) {}
}
} catch (SOAPException e) {
e.printStackTrace();
}
}
return true;
}
public Set getHeaders() {
return null;
}
public void close(MessageContext messageContext) {
}
}
Developing a Client to use the new Web Service Addressing features in JAX-WS 2.1
Below is an example of a web service client which uses the Web Service Addressing features. To write a portable client, the client MUST explicitly enable addressing via the use of the AddressingFeature class, and for each invocation, the client MUST explicitly set the BindingProvider.SOAPACTION_URI_PROPERTY to the explicit Action value for the input message. After the W3C WG on WS-Addressing has specified how the use of WS-Addressing is specified in the WSDL, and what the default value must be for Action headers, a future version of JAX-WS will remove these requirements. A W3CEnpointReference is obtained from the BindingProvider via the getEndpointReference() method call as shown in the getEPR() method call.
The client-side implementation also contains a handler ClientSOAPHandler that just dumps out the SOAPMessage from the handleMessage and handleFault methods to show the WS-Addressing header information.
Client-Side Implementation Code Example
Client.java
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package action.client;
import javax.xml.ws.WebServiceRef;
import javax.xml.ws.WebServiceFeature;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.soap.AddressingFeature;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import java.io.ByteArrayOutputStream;
import javax.xml.transform.stream.StreamResult;
public class Client {
@WebServiceRef
static AddNumbersService service;
private WebServiceFeature[] enabledRequiredwsf = {new AddressingFeature(true, true)};
AddNumbers port;
private AddNumbers getPort()
{
System.out.println("Obtain port from service");
port = service.getPort(AddNumbers.class, enabledRequiredwsf);
return port;
}
private W3CEndpointReference getEPR()
{
if (port == null)
return null;
System.out.println("Obtain EPR from port");
BindingProvider bp = (BindingProvider)port;
W3CEndpointReference epr = (W3CEndpointReference)bp.getEndpointReference();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
epr.writeTo(new StreamResult(baos));
System.out.println(baos.toString());
return epr;
}
private void setSOAPActionURI(Object o, String action) {
BindingProvider bp = (BindingProvider)o;
java.util.Map requestContext=bp.getRequestContext();
if(requestContext == null) {
System.err.println("setSOAPActionURI:getRequestContext() returned null");
} else {
requestContext.put(BindingProvider.SOAPACTION_URI_PROPERTY, action);
}
}
public static void main(String[] args) {
try {
Client client = new Client();
client.getPort();
client.getEPR();
client.callAddNumbersExplicitInputOutputAction();
client.callAddNumbersExplicitFaultAction();
} catch(Exception e) {
e.printStackTrace();
}
}
public void callAddNumbersExplicitInputOutputAction() {
try {
System.out.println("Invoking addNumbersExplicitInputOutputAction operation ...");
setSOAPActionURI(port, "http://addnumbers.org/input");
int result = port.addNumbersExplicitInputOutputAction(10, 10);
System.out.println("result="+result);
} catch(Exception e) {
e.printStackTrace();
}
}
public void callAddNumbersExplicitFaultAction() {
try {
System.out.println("Invoking addNumbersExplicitFaultAction operation ...");
setSOAPActionURI(port, "input");
int result = port.addNumbersExplicitFaultAction(-10, 10);
System.out.println("result="+result);
} catch(Exception e) {
e.printStackTrace();
}
}
}
ClientSOAPHandler.java
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package action.client;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.MessageContext;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.Text;
import javax.xml.namespace.QName;
import java.util.Set;
import java.io.ByteArrayOutputStream;
public class ClientSOAPHandler implements SOAPHandler {
private String getMessageEncoding(SOAPMessage msg) throws SOAPException {
String encoding = "utf-8";
if (msg.getProperty(SOAPMessage.CHARACTER_SET_ENCODING) != null) {
encoding = msg.getProperty(SOAPMessage.CHARACTER_SET_ENCODING).toString();
}
return encoding;
}
private void dumpSOAPMessage(SOAPMessage msg) {
if(msg == null) {
System.out.println("SOAP Message is null");
return;
}
System.out.println("");
System.out.println("--------------------");
System.out.println("DUMP OF SOAP MESSAGE");
System.out.println("--------------------");
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
msg.writeTo(baos);
System.out.println(baos.toString(getMessageEncoding(msg)));
} catch(Exception e) {
e.printStackTrace();
}
}
public boolean handleMessage(SOAPMessageContext context) {
System.out.println("ClientSOAPHandler.handleMessage");
boolean outbound = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) {
System.out.println("Direction=outbound");
} else {
System.out.println("Direction=inbound");
}
try {
SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
dumpSOAPMessage(msg);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
public boolean handleFault(SOAPMessageContext context) {
System.out.println("ClientSOAPHandler.handleFault");
boolean outbound = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) {
System.out.println("Direction=outbound");
} else {
System.out.println("Direction=inbound");
}
if (!outbound) {
try {
SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
dumpSOAPMessage(msg);
if (context.getMessage().getSOAPBody().getFault() != null) {
String detailName = null;
try {
detailName = context.getMessage().getSOAPBody().getFault().getDetail().getFirstChild().getLocalName();
System.out.println("detailName="+detailName);
} catch(Exception e) {}
}
} catch (SOAPException e) {
e.printStackTrace();
}
}
return true;
}
public Set getHeaders() {
return null;
}
public void close(MessageContext messageContext) {
}
}
Below is the HTTP Request and Response messages captured that shows the WS-Addressing information:
Here is the request/response messages of the addNumbersExplicitInputOutputAction invocation.
Request
POST /J2WDLACTION/jaxws/AddNumbers HTTP/1.1
SOAPAction: "http://addnumbers.org/input"
Content-Type: text/xml; charset=utf-8
Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
User-Agent: Java/1.5.0_06
Host: localhost:2020
Connection: keep-alive
Content-Length: 788
<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Header><To xmlns="http://www.w3.org/2005/08/addressing">http://localhost:2020/J2WDLACTION/jaxws/AddNumbers</To><Action xmlns="http://www.w3.org/2005/08/addressing">http://addnumbers.org/input</Action><ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
<Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
</ReplyTo><MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:f0d1d72e-26b8-4fd3-bff4-98fe6b11ebf0</MessageID></S:Header><S:Body><ns3:addNumbersExplicitInputOutputAction xmlns:ns2="http://www.w3.org/2005/08/addressing" xmlns:ns3="http://addnumbers.org"><arg0 xmlns="">10</arg0><arg1 xmlns="">10</arg1></ns3:addNumbersExplicitInputOutputAction></S:Body></S:Envelope>
Response
HTTP/1.1 200 OK
X-Powered-By: Servlet/2.5
Server: Sun Java System Application Server 9.1
Content-Type: text/xml;charset=utf-8
Transfer-Encoding: chunked
Date: Wed, 18 Apr 2007 16:17:00 GMT
2f3
<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Header><To xmlns="http://www.w3.org/2005/08/addressing">http://www.w3.org/2005/08/addressing/anonymous</To><Action xmlns="http://www.w3.org/2005/08/addressing">http://addnumbers.org/output</Action><MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:ffed49c7-3efb-474c-92ba-a801ee1ae203</MessageID><RelatesTo xmlns="http://www.w3.org/2005/08/addressing">uuid:f0d1d72e-26b8-4fd3-bff4-98fe6b11ebf0</RelatesTo></S:Header><S:Body><ns3:addNumbersExplicitInputOutputActionResponse xmlns:ns2="http://www.w3.org/2005/08/addressing" xmlns:ns3="http://addnumbers.org"><return xmlns="">20</return></ns3:addNumbersExplicitInputOutputActionResponse></S:Body></S:Envelope>
Here is the request/response messages of the addNumbersExplicitFaultAction invocation.
Request
POST /J2WDLACTION/jaxws/AddNumbers HTTP/1.1
SOAPAction: "input"
Content-Type: text/xml; charset=utf-8
Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
User-Agent: Java/1.5.0_06
Host: localhost:2020
Connection: keep-alive
Content-Length: 755
<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Header><To xmlns="http://www.w3.org/2005/08/addressing">http://localhost:2020/J2WDLACTION/jaxws/AddNumbers</To><Action xmlns="http://www.w3.org/2005/08/addressing">input</Action><ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
<Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
</ReplyTo><MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:e2a978c0-f727-457d-a35a-88ce66270715</MessageID></S:Header><S:Body><ns3:addNumbersExplicitFaultAction xmlns:ns2="http://www.w3.org/2005/08/addressing" xmlns:ns3="http://addnumbers.org"><arg0 xmlns="">-10</arg0><arg1 xmlns="">10</arg1></ns3:addNumbersExplicitFaultAction></S:Body></S:Envelope>
Response
HTTP/1.1 500 Internal Server Error
X-Powered-By: Servlet/2.5
Server: Sun Java System Application Server 9.1
Content-Type: text/xml;charset=utf-8
Transfer-Encoding: chunked
Date: Wed, 18 Apr 2007 16:17:01 GMT
Connection: close
2000
<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Header><To xmlns="http://www.w3.org/2005/08/addressing">http://www.w3.org/2005/08/addressing/anonymous</To><Action xmlns="http://www.w3.org/2005/08/addressing">http://addnumbers.org/fault/addnumbers</Action><MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:e92daf6b-7416-4715-b73b-48937a4262c8</MessageID><RelatesTo xmlns="http://www.w3.org/2005/08/addressing">uuid:e2a978c0-f727-457d-a35a-88ce66270715</RelatesTo></S:Header><S:Body><ns2:Fault xmlns:ns2="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns3="http://www.w3.org/2003/05/soap-envelope"><faultcode>ns2:Server</faultcode><faultstring>Negative numbers can't be added!</faultstring><detail><ns3:AddNumbersException xmlns:ns3="http://addnumbers.org" xmlns:ns2="http://www.w3.org/2005/08/addressing"><detail>Numbers: -10, 10</detail><message>Negative numbers can't be added!</message></ns3:AddNumbersException></detail><ns2:Fault></S:Body></S:Envelope>
Running the Sample Code
A sample package accompanies this tip. It provides an example that demonstrates the techniques covered in the tip. The sample package includes the source code for the example, required descriptor files, and build scripts.
Code for the sample after extracting its contents is under:
- src/action
For a working example of this client and server sample code download here.
Steps to install and run the sample:
Step 1. Download GlassFish from the GlassFish Project page that has
JAX-WS 2.1 integrated.
Step 2. Set the following environment variables:
ANT_HOME - path to location of Ant installation. This example uses Ant 1.6.5.
JAVA_HOME - path to location of JDK5.0 installation.
Also, add the Ant location to your PATH environment variable.
Step 3. Download the sample package and extract its contents.
To get started using this tech tip all you have to edit is the
following file:
o techtip/build/common.properties
The main properties to set are the ones below based on your
GlassFish installation properties. You can check any others
that may need tweeking based on your setup.
<!-- Main Properties to Set -->
<!-- Only these need be set -->
<property name="jdk.home" value="/files/java/jdk1.5/jdk1.5"/>
<property name="appserver.home" value="/sun/appserver9.1"/>
<property name="tests.home" value="/tmp/techtip"/>
<property name="webserver.host" value="localhost"/>
<property name="webserver.port" value="8001"/>
<property name="httpproxy.host" value=""/>
<property name="httpproxy.port" value=""/>
Step 4. Start the GlassFish Application Server
Step 5. cd techtip/src/action
Step 6. Type the following to build, deploy and run the sample:
ant
Step 7. Or read it in as a netbeans project by opening project under
techtip/src/action and use netbeans to clean/build/run project.
Posted at 09:28AM May 21, 2007 by artfish in Sun | Comments[3]
Hello Art! Thanks for this excellent example. I have 2 questions:
1) Is it possible to use SoapHandlers in jdk 1.6_04 or 1.6_05 - without separately downloading GlassFish? I'm using wsimport to generate web service proxy code in jdk 1.6 for my project and I don't want to avoid using an additional package like GlassFish if possible.
2) I think I understand the ClientSoapHandler, but I don't understand how it is installed. I don't see a reference to it in Client.java or anywhere else. How is the handler set?
thanks very much,
Andrew
Posted by Andrew Luxner on March 25, 2008 at 12:59 PM EDT #
Sorry for the late reply.
1) This example is based on GlassFish and the build/infrastructure assumes GlassFish container for deployment. You could tweek the ant scripts to remove the GlassFish dependancies and use whatever container you are using if you like. You would however need the standalone JAX-WS 2.1 RI code to build.
2) The AddNumbersImpl.java class defines the HandlerChain annotation for the server-side handler.
The client customization file handles the setting of the handler via the (wsdl/customfile-client.xml) which defines the client soap handler on the client side.
Posted by Art on July 20, 2008 at 10:06 AM EDT #
Could you help me with something?
I can not call directly from my web to the services for security reasons. So at the end I have to put a routing service between the UI and the business services.
I have thought the following for accomplishing this:
Get the message, extract the ws-addressing TO destination and resend it to it.
Is there a way to do it without generating the service skeleton, you know, the same @webservice class but with "resend" functions.
Could you provide me some code?
Thanks in advance.
Posted by Pablo Castilla on April 08, 2009 at 10:57 AM EDT #