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???
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.
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);
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
. –
Delaney 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! –
Budd 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.
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
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.
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]")
© 2022 - 2024 — McMap. All rights reserved.