A Technology Blog About Code Development, Architecture, Operating System, Hardware, Tips and Tutorials for Developers.

Tuesday, December 4, 2012

JAX-WS Web Service Handler (Client)

6:35:00 PM Posted by Satish , , , , , , No comments
SOAP handler is a SOAP message interceptor, which is able to intercept incoming or outgoing SOAP message and manipulate its values. For example, attach a SOAP handler in client side, which will inject authentication credential into the SOAP header block for every outgoing SOAP message that is send by the client. In server side, attach another SOAP handler, to retrieve back the authentication credential in SOAP header block from every incoming SOAP message. In this way we can validate the incoming requests and secure our system. In this article we are going inject the authentication credential in header to access the web service. We are going to create the client to access the service explained in my previous article  JAX-WS Web Service Handler (Server).

For this tutorial I will be using the following tools
  • JDK 7
  • Eclipse Juno
In this article, I will show you how to create a SOAP handler, to inject the credential in SOAP header block for every outgoing SOAP message to access the web service we created in JAX-WS Web Service Handler (Server). We are going to do the following thing here.
  • Create the client for the web service
  • Create SOAP Handler
  • Handler Configuration
  • Handler Mapping
  • Tracing the out going and incoming messages in success and failure case.
Create Web Service:

As I have already wrote an article to demonstrate how to  create web service client, I will not be explaining it again . Please refer Web Service Client to import and create web service client.

Create Soap Handler:

In order to create a SOAP handler, we have to implement javax.xml.ws.handler.soap.SOAPHandler, same way we did while writing handler for end point at server. We have to give implementation in handleMessage(). In this method we are going to populate the header with authentication credentials.

 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
package com.techiekernel.ws.jaxws.handler;

import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Set;
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;

public class CredentialProvider implements SOAPHandler<SOAPMessageContext> {

 private static final String USER_NAME = "foo";
 private static final String PASSWORD = "bar";
 
 public boolean handleMessage(SOAPMessageContext context) {

  System.out.println("handleMessage() called.");

  Boolean isRequest = (Boolean) context
    .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);

  // if this is a request, true for outbound messages, false for inbound
  if (isRequest) {

   try {
    SOAPMessage soapMsg = context.getMessage();
    SOAPEnvelope soapEnv = soapMsg.getSOAPPart().getEnvelope();
    SOAPHeader soapHeader = soapEnv.getHeader();

    // if no header, add one
    if (soapHeader == null) {
     soapHeader = soapEnv.addHeader();
    }

    // add a soap header, username
    QName qnameUser = new QName("http://document.jaxws.ws.techiekernel.com/", "userName");
    SOAPHeaderElement soapHeaderElementUser = soapHeader
      .addHeaderElement(qnameUser);

    soapHeaderElementUser.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT);
    soapHeaderElementUser.addTextNode(USER_NAME);
    
    // add a soap header, username
    QName qnamePassword = new QName("http://document.jaxws.ws.techiekernel.com/", "password");
    SOAPHeaderElement soapHeaderElementPassword = soapHeader
      .addHeaderElement(qnamePassword);

    soapHeaderElementPassword.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT);
    soapHeaderElementPassword.addTextNode(PASSWORD);
    
    soapMsg.saveChanges();

    // tracking
    soapMsg.writeTo(System.out);

   } catch (SOAPException e) {
    System.err.println(e);
   } catch (IOException e) {
    System.err.println(e);
   }

  }

  // continue other handler chain
  return true;
 }

 public boolean handleFault(SOAPMessageContext context) {
  System.out.println("handleFault() called.");
  return true;
 }

 public void close(MessageContext context) {
  System.out.println("close() called");
 }

 public Set<QName> getHeaders() {
  System.out.println("getHeaders() called.");
  return null;
 }
}

Handler Configuration:

Let’s create define the handler in a handler mapping file and name it as handler.xml.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?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>com.techiekernel.ws.jaxws.handler.CredentialProvider</javaee:handler-class>
    </javaee:handler>
  </javaee:handler-chain>
</javaee:handler-chains>

Handler Mapping:

While importing the WSLD, some stubs will be get created at the client. To attach above SOAP handler, just annotate the service class with @HandlerChain and specify the SOAP handler file name inside. In our case following class we have to consider. We have to keep the hander configuration file i.e. handler.xml in the class path.

 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
package com.techiekernel.ws.jaxws.document;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceFeature;
import javax.jws.HandlerChain;


/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.1.7-b01-
 * Generated source version: 2.1
 * 
 */
@WebServiceClient(name = "FooBarImplService", targetNamespace = "http://document.jaxws.ws.techiekernel.com/", wsdlLocation = "http://localhost:8080/webservice-JAX-WS-handler/foobar?wsdl")
@HandlerChain(file="handler.xml")
public class FooBarImplService
    extends Service
{

    private final static URL FOOBARIMPLSERVICE_WSDL_LOCATION;
    private final static Logger logger = Logger.getLogger(com.techiekernel.ws.jaxws.document.FooBarImplService.class.getName());

    static {
        URL url = null;
        try {
            URL baseUrl;
            baseUrl = com.techiekernel.ws.jaxws.document.FooBarImplService.class.getResource(".");
            url = new URL(baseUrl, "http://localhost:8080/webservice-JAX-WS-handler/foobar?wsdl");
        } catch (MalformedURLException e) {
            logger.warning("Failed to create URL for the wsdl Location: 'http://localhost:8080/webservice-JAX-WS-handler/foobar?wsdl', retrying as a local file");
            logger.warning(e.getMessage());
        }
        FOOBARIMPLSERVICE_WSDL_LOCATION = url;
    }

    public FooBarImplService(URL wsdlLocation, QName serviceName) {
        super(wsdlLocation, serviceName);
    }

    public FooBarImplService() {
        super(FOOBARIMPLSERVICE_WSDL_LOCATION, new QName("http://document.jaxws.ws.techiekernel.com/", "FooBarImplService"));
    }

    /**
     * 
     * @return
     *     returns FooBarImpl
     */
    @WebEndpoint(name = "FooBarImplPort")
    public FooBarImpl getFooBarImplPort() {
        return super.getPort(new QName("http://document.jaxws.ws.techiekernel.com/", "FooBarImplPort"), FooBarImpl.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 FooBarImpl
     */
    @WebEndpoint(name = "FooBarImplPort")
    public FooBarImpl getFooBarImplPort(WebServiceFeature... features) {
        return super.getPort(new QName("http://document.jaxws.ws.techiekernel.com/", "FooBarImplPort"), FooBarImpl.class, features);
    }

}

Tracing SOAP Messages:

Let's first see how the SOAP message looks in case of a success interaction.

Client Request SOAP Message:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
 <S:Header>
  <userName xmlns="http://document.jaxws.ws.techiekernel.com/"
   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
   SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next">foo</userName>
  <password xmlns="http://document.jaxws.ws.techiekernel.com/"
   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
   SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next">bar</password>
 </S:Header>
 <S:Body>
  <ns2:callFooBar xmlns:ns2="http://document.jaxws.ws.techiekernel.com/">
   <arg0>Satish</arg0>
  </ns2:callFooBar>
 </S:Body>
</S:Envelope>

Server Response SOAP Message:

1
2
3
4
5
6
7
8
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
 <S:Header />
 <S:Body>
  <ns2:callFooBar xmlns:ns2="http://document.jaxws.ws.techiekernel.com/">
   <arg0>Satish</arg0>
  </ns2:callFooBar>
 </S:Body>
</S:Envelope>

Now let change the password to something else to check, how the handler works.

Client Request SOAP Message:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
 <S:Header>
  <userName xmlns="http://document.jaxws.ws.techiekernel.com/"
   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
   SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next">foo</userName>
  <password xmlns="http://document.jaxws.ws.techiekernel.com/"
   xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
   SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next">bar1</password>
 </S:Header>
 <S:Body>
  <ns2:callFooBar xmlns:ns2="http://document.jaxws.ws.techiekernel.com/">
   <arg0>Satish</arg0>
  </ns2:callFooBar>
 </S:Body>
</S:Envelope>

Server Response SOAP Message:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
 <S:Header />
 <S:Body>
  <ns2:callFooBar xmlns:ns2="http://document.jaxws.ws.techiekernel.com/">
   <arg0>Satish</arg0>
  </ns2:callFooBar>
  <S:Fault>
   <faultcode>S:Server</faultcode>
   <faultstring>Authentication Failed</faultstring>
  </S:Fault>
 </S:Body>
</S:Envelope>


Source Code:

You can pull the code from GitHub. The shared code contains the full implementation.

0 comments:

Post a Comment