0

I want to set a dynamically generated HTTP header on each SOAP JAX-WS request.

If I wanted to set the same HTTP header on each JAX-WS request I could use the technique here, i.e.

public class MyApplicationClass {
    // Inject an instance of the service's port-type.
    @WebServiceRef(EchoService.class)
    private EchoPortType port;

    // This method will invoke  the web service operation and send transport headers on the request.
    public void invokeService() {

        // Set up the Map that will contain the request headers.
        Map<String, Object> requestHeaders = new HashMap<String, Object>();
        requestHeaders.put(“MyHeader1”, “This is a string value”);
        requestHeaders.put(“MyHeader2”, new Integer(33));
        requestHeaders.put(“MyHeader3”, new Boolean(true));

        // Set the Map as a property on the RequestContext.
        BindingProvider bp = (BindingProvider) port;
        bp.getRequestContext().put(com.ibm.websphere.webservices.Constants.REQUEST_TRANSPORT_PROPERTIES, requestHeaders);

        // Invoke the web services operation.
        String result = port.echoString(“Hello, world!”);
    }
}

However, here I want to use a different HTTP header for each request. Essentially I want to include a X-RequestId header or similar with a random value, so the receiving server can distinguish between requests duplicated on a timeout either by the Java client or (worse) an inline HTTP proxy.

Moreover, it JAX-WS retries the same call, I don't want it to regenerate the header (obviously).

Note that my application is already covered in the equivalent of port.echoString (lots of calls to the web service). I can't manually change the header in front of each such call because:

  • they share the same binding provider, and this would not be thread-safe (i.e. user A could change the header, user B could change the header, then user A could call, then user B could call, and the same header be passed)
  • this would require modification all over the code.

What I want to do is add something to the class that serialises each request, to add the header at serialisation time.

Questions that are related but are not duplicates:

Community
  • 1
  • 1
abligh
  • 24,573
  • 4
  • 47
  • 84

1 Answers1

1

As for the unique value aspect, you can use the JDK's UUID class to create a GUID:

requestHeaders.put("X-RequestId", java.util.UUID.randomUUID().toString());

As for the clarified thread safety concern, based on the JAX-WS specification (JSR-224) section 9.3 I'd suggest using a JAX-WS client handler to do this as the handler spec identifies a thread safe mechanism: MessageContext:

9.3.3 Handler Implementation Considerations

Handler instances may be pooled by a JAX-WS runtime system. All instances of a specific handler are considered equivalent by a JAX-WS runtime system and any instance may be chosen to handle a particular message. Different handler instances may be used to handle each message of an MEP. Different threads may be used for each handler in a handler chain, for each message in an MEP or any combination of the two. Handlers should not rely on thread local state to share information. Handlers should instead use the message context, see section 9.4.

You can write one central handler class and attach it to the BindingProvider's handler chain to avoid changing all the places you invoke the service operation across the application. You can add a handler to the handler chain programmatically or via the @HandlerChain annotation companion to @WebServiceRef

This post describes using the handler framework's MessageContext to set outbound http headers like you want. However in your case you want to set the X-RequestId with the UUID value discussed above.

Community
  • 1
  • 1
Scott Heaberlin
  • 3,364
  • 1
  • 23
  • 22
  • Right, but my application already calls `port.echoString` all over the place (many hundreds of calls). Your way is essentially the same as what's in the question, i.e. the UUID would be the same on each call (unless I manually add the header before each call which would require huge modifications and would not be thread safe) – abligh Jul 23 '15 at 07:36
  • The updated question clarifies a bit; are you saying `MyApplicationClass` is a shared instance across threads, or that you have lots of classes in your application that all have their own `@WebServiceRef` to `EchoService` (or both)?. Also, what jax-ws implementation? If CXF, there is apparently a way to make request context use thread locals: http://cxf.apache.org/faq.html#FAQ-AreJAX-WSclientproxiesthreadsafe? – Scott Heaberlin Jul 23 '15 at 08:53
  • Updated answer to recommend JAX-WS handler and message context instead of binding provider request context. As for a way to do this universally with zero changes to at least where the binding provider is created/injected via annotation, I am not sure. If your application only has one/a few `@WebServiceRef` for this service then the handler chain approach does solve this. – Scott Heaberlin Jul 23 '15 at 09:43
  • I think this is (now) the correct answer. In the meantime I found `sun.net.http.retryPost = false` which is also useful (in the case where it's the client retrying rather than an intermediate proxy). Slightly ironic that the answer you point to is the one that generates a very static header! – abligh Jul 23 '15 at 10:11
  • Really confused at how I can input request specific headers value this way – TheBakker Mar 06 '20 at 16:10