WebserviceClient mit SOAP Handler (SecurityHandler)

Generierung eines Standard Webserviceclients mit JAXWS (WS-Stub Generierung):

"%java_home%\bin\wsimport" -keep http://<Pfad zur WSDL-Beschreibung des XYZ Service>

In den folgenden Beispielklassen müssen die generierten Klassen dann noch statt der XYZ Klassen eingesetzt werden.

Die eigentliche Klasse des "WebserviceClientWithSOAPHandler":

Die Idee hinter dieser Klasse ist es einen XYZService zu instantiieren, der dann auch direkt verwendet werden soll. Dieser Service erhält aber zusätzlich einen "ClientHandlerResolver" (siehe unten) welcher vor und nach jedem Aufruf der Methoden des eigentlichen Service aufgerufen wird und das Handling von SOAP-Headern übernimmt. Von diesem Aufruf bekommt der Nutzer des Proxy nichts mit, dies ist für ihn somit transparent. Zusätzlich wird die erzeugte Service-Instanz hier noch für weitere Verwendung gepuffert, da das Erzeugen dieser manchmal viel Zeit in Anspruch nehmen kann.

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
package de.soderer;

import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.namespace.QName;

import de.soderer.XYZ;
import de.soderer.XYZService;

public class WebserviceClientWithSOAPHandler {
  private static final String SERVICE_NAMESPACE = "urn:xyzservice.soderer.de";
  private static String SERVICE_ENDPOINT_URL_CONTEXT = "services/XYZService";

  private ClientHandlerResolver clientHandlerResolver = new ClientHandlerResolver();
  
  private String serverMachine_Hostname = null;
  private int serverMachine_Port = 0;

  private XYZ servicePort = null;
  
  public WebserviceClientWithSOAPHandler(String serverMachine_Hostname, int serverMachine_Port) {
    this.serverMachine_Hostname = serverMachine_Hostname;
    this.serverMachine_Port = serverMachine_Port;
  }

  public XYZ getService() throws MalformedURLException {
    if (servicePort == null) {
      XYZService service = new XYZService(
        new URL("http", serverMachine_Hostname, serverMachine_Port, SERVICE_ENDPOINT_URL_CONTEXT),
        new QName(SERVICE_NAMESPACE, "XYZService"));
      
      service.setHandlerResolver(clientHandlerResolver);
      
      servicePort = service.getXYZPort();
    }

    if (servicePort == null)
      throw new RuntimeException("Init of Service FAILED");

    return servicePort;
  }
}

Die Hilfs-Klasse "ClientHandlerResolver":

Diese Hilfsklasse bewahrt den SecurityHandler auf und liefert ihn bei Aufruf der SOAP-Header-Erstellung zurück

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 de.soderer;

import java.util.ArrayList;
import java.util.List;

import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.handler.PortInfo;

/**
 * Resolver for the security handler.
 */

public class ClientHandlerResolver implements HandlerResolver {
  /**
   * List of handlers.
   */

  @SuppressWarnings("rawtypes")
  private List<Handler> _handlers;
  
  /**
   * Creates a new handler resolver.
   */

  @SuppressWarnings("rawtypes")
  public ClientHandlerResolver() {
    _handlers = new ArrayList<Handler>();
    _handlers.add(new SecurityHandler());
  }
  
  @SuppressWarnings("rawtypes")
  public List<Handler> getHandlerChain(PortInfo portInfo) {
    return _handlers;
  }
  
  /**
   * Gets the security handler.
   * @return  the handler.
   */

  public SecurityHandler getSecurityHandler() {
    return (SecurityHandler)_handlers.get(0);
  }
}

Die Hilfs-Klasse "SecurityHandler":

Hier passiert die eigentliche Magie. Denn jeder Aufruf einer Methode des Service-Objects kommt hier in der Phase der SOAP-Header-Erstellung vorbei. Vor und nach dem Aufruf der Service-Methoden kann also beliebiger Code ausgeführt werden. In diesem Beispiel wird ein AuthenticationToken als SOAP-Header übertragen und ein zurückgegebenes Token dann für den nächsten Aufruf zwischengespeichert.

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
96
97
98
99
100
package de.soderer;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.xml.namespace.QName;
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;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

import com.sun.xml.ws.api.message.Header;
import com.sun.xml.ws.api.message.HeaderList;
import com.sun.xml.ws.developer.JAXWSProperties;

public class SecurityHandler implements SOAPHandler<SOAPMessageContext> {
  /* Name of the header. */
  private static final QName QNAME_AUTH =
    new QName("urn:api.soderer.de", "Authentication");
  
  /* Name of the AuthenticationToken tag. */
  @SuppressWarnings("unused")
  private static final QName QNAME_AUTH_TOKEN =
    new QName("urn:api.soderer.de", "AuthenticationToken");

  /* List of headers this security handler can handle. */
  private static final Set<QName> HEADERS;
  
  /* The soap handler. */
  private SOAPHeaderElement _securityHeader = null;
  private Header _authHeader;

  static {
    HEADERS = new HashSet<QName>();
    HEADERS.add(QNAME_AUTH);
  }
  
  public Set<QName> getHeaders() {
    return HEADERS;
  }
  
  public boolean handleMessage(SOAPMessageContext smc) {
    if ((Boolean)smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY)) {
      // Outbound message
      try {
        SOAPMessage message = smc.getMessage();
        SOAPHeader sh = message.getSOAPHeader();
        
        if (_securityHeader != null) {
          if (sh == null) {
            SOAPPart part = message.getSOAPPart();
            SOAPEnvelope envelope = part.getEnvelope();
            
            sh = envelope.addHeader();
          }
          
          sh.addChildElement(_securityHeader);
        }
      }
      catch (Exception e) {
        System.out.println("Exception in securityhandler: " + e);
      }
    }
    else {
      // Inbound message
      try {
        SOAPHeader sh = smc.getMessage().getSOAPHeader();
        if (sh != null) {
          Iterator<?> it = sh.getChildElements(QNAME_AUTH);
          _securityHeader = it.hasNext() ? (SOAPHeaderElement) it.next() : null;
        }
        
        HeaderList headerList = (HeaderList)smc.get(JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY);
        _authHeader = headerList.get(QNAME_AUTH, false);
      }
      catch (Exception e) {
        System.out.println("Exception in securityhandler: " + e);
      }
    }
    
    return true;
  }
  
  public boolean handleFault(SOAPMessageContext smc) {
    return true;
  }
  
  public void close(MessageContext messageContext) {
    // Nothing to do.
  }
  
  public Header getAuthHeader() {
    return _authHeader;
  }
}

Beispiel Service Aufruf:

WebserviceClientWithSOAPHandler client = new WebserviceClientWithSOAPHandler("myhostname", 8080);
client.getService().authenticate(username, userpassword);
client.getService().xyzMethode(4711);
client.getService().xyzMethode(0815);