java web service client, adding http headers

49,898

Solution 1

You can pass a map with custom headers to the BindingProvider (I believe you can set the MessageContext.HTTP_REQUEST_HEADERS property). Try creating an Authorization header and passing it in.

Solution 2

Here is the code, based on Femi's answer.

It can be a little tricky to figure out. Works beautifully!

Service jaxwsService = Service.create(wsdlURL, serviceName);
Dispatch<SOAPMessage> disp = jaxwsService.createDispatch(portName, SOAPMessage.class, Service.Mode.MESSAGE);

//Add HTTP request Headers
Map<String, List<String>> requestHeaders = new HashMap<>();
requestHeaders.put("Auth-User", Arrays.asList("BILL_GATES"));
disp.getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS, requestHeaders);

Solution 3

To supplement answers of Daniel Alexiuc and Femi here is slightly alternative way based on this documentation: https://javaee.github.io/metro/doc/user-guide/user-guide.html#adding-soap-headers-when-sending-requests

public class HelloClient {

   @WebServiceRef(wsdlLocation="http://localhost:8080/helloservice/hello?wsdl")
   static HelloService service;


   public HelloPort getHelloPort(){
       HelloPort helloPort = service.getHelloPort();

       Map<String, List<String>> requestHeaders = new HashMap<>();
       requestHeaders.put("Your_Header",Arrays.asList("Your_Header_value"));

       BindingProvider bindingProvider = (BindingProvider)helloPort;
       bindingProvider.getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS, requestHeaders);

       return helloPort;
    }
}

This will add header to http request

Solution 4

For the sake of completeness and to help others in similar situations, I'd like to illustrate the IMHO cleanest solution using the JAX-WS-handler-chain:

1) Sub-class your service-class (not the port-class) in a different (non-generated) package. Because the service-class (and its entire package) was likely generated from a WSDL, your changes to the sub-class are not lost, when you update your service-class after a WSDL change.

2) Annotate your service-sub-class like this (import javax.jws.HandlerChain):

@HandlerChain(file="HandlerChain.xml")
public class MyService extends GeneratedService {

3) Create a file called HandlerChain.xml in the same package as your service-sub-class, i.e. next to MyService with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
    <handler-chain>
        <handler>
            <handler-name>co.codewizards.example.HttpHeaderExtensionSOAPHandler</handler-name>
            <handler-class>co.codewizards.example.HttpHeaderExtensionSOAPHandler</handler-class>
        </handler>
    </handler-chain>
</handler-chains>

You may add multiple <handler> elements, btw.

And make sure that this file really ends up in your JAR! For example, when using Maven, you have to place it either in ${project}/src/main/resources/ (instead of ${project}/src/main/java/) or you have to change your build-configuration to include resources from the java-folder! I recommend the latter, because it's cumbersome to have a parallel package-structure in the resources-folder, which is often forgotten during refactorings.

4) Implement your HttpHeaderExtensionSOAPHandler -- similar to this:

import static com.google.common.base.Preconditions.*;

import java.util.*;

import javax.xml.namespace.QName;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

import co.codewizards.webservice.WebserviceContext;

public class HttpHeaderExtensionSOAPHandler implements SOAPHandler<SOAPMessageContext> {

    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        checkNotNull(context, "context");

        Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        checkNotNull(outboundProperty, "outboundProperty");

        if (outboundProperty.booleanValue()) {
            WebserviceContext<?, ?> webserviceContext = WebserviceContext.getThreadWebserviceContextOrFail();
            String something = (String) webserviceContext.___(); // my API method ;-)

            @SuppressWarnings("unchecked")
            Map<String, List<String>> requestHeaders = (Map<String, List<String>>) context.get(MessageContext.HTTP_REQUEST_HEADERS);
            if (requestHeaders == null) {
                requestHeaders = new HashMap<String, List<String>>();
                context.put(MessageContext.HTTP_REQUEST_HEADERS, requestHeaders);
            }
            requestHeaders.put(MyService.MY_CONSTANT, Collections.singletonList(something));
        }
        return true;
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) { return true; }

    @Override
    public void close(MessageContext context) { }

    @Override
    public Set<QName> getHeaders() { return Collections.emptySet(); }
}

In my example above (and in my productive code) I obtain the data to be passed into the HTTP request headers from a ThreadLocale, i.e. my current thread's context. Since this WebserviceContext is my custom class, you'll need to implement your own way to access your data.

Solution 5

when you're sending in Message mode, you can also pass MimeHeaders on SOAP Message, which will eventually translate into http headers, i.e:

soapMessage.getMimeHeaders().addHeader("Authorization","Basic [md5]")
Share:
49,898
murungu
Author by

murungu

Coding in the Netherlands, professionally in java. Python and C in my spare time. https://www.linkedin.com/profile/view?id=25194901

Updated on September 19, 2020

Comments

  • murungu
    murungu over 3 years

    Having created a java web service client using wsimport on a wsdl, I need to set the Authorization header for each soap message embedded in an http request. Having generated a subclass of javax.xml.ws.Service, how can I append an http header to each outgoing request???

  • Mark McWhirter
    Mark McWhirter almost 8 years
    I'm having some trouble with the createDispatch call: Dispatch<SOAPMessage> disp = jaxwsService.createDispatch(portName, SOAPMessage.class, Service.Mode.MESSAGE); It's complaining about no suitable method found. Is there something obvious that I have missed? I'm importing import javax.xml.soap.SOAPMessage; import javax.xml.ws.Dispatch; import javax.xml.ws.Service; import javax.xml.ws.handler.MessageContext;
  • L42
    L42 over 7 years
    Thank you! If you're as dumb as me, you might attempt to use a Map<String, String> because you read the answer too fast. Don't be like me. Use a Map<String, List<String>> and save the four hours I spent trying to debug HTTP transport error: java.lang.ClassCastException: java.lang.String cannot be cast to java.util.List.
  • alan9uo
    alan9uo over 4 years
    Excellent answer! But I have a question. In this line : Dispatch<SOAPMessage> disp = jaxwsService.createDispatch(portName, SOAPMessage.class, Service.Mode.MESSAGE); I don't know what portName is. How can I new portName instance.Thank you!