Reputation: 57
I would like to enrich my HTTP/SOAP request (send with WS Outbound Gateway) with some custom HTTP Headers depending on what actually Message
contains, i.e.
Message.headers
Message.headers
existsI considered the following solutions:
ClientInterceptor
- but there is no access to
Message.header at this pointSoapHeaderMapper
- there is access to Message.header
but I do not like this idea as it is rather meant
to operate on SOAP envelope not on connection/request level. WebServiceMessageCallback
- no access to Message at this point.Also, in context of Authentication, all above solutions rely on adding necessary HTTP Authentication headers on our own, while I would like to do it in more correct way (at least in my opinion) and properly configure HttpClient
.
So at this point I finished with custom HttpComponentsMessageSender
that sets up HttpClientContext
per each request. The problem is that again there is no access to Mesage.headers
so I finished with some combination of ServiceActivator
with SPEL and ThreadLocal
as presented below.
Generally it works but... is there any other more correct path to go?
<int:service-activator expression="@basicAuthenticationMessageSender.setBasicAuth(#root, headers.username, headers.pass)" />
public class BasicAuthenticationMessageSender extends HttpComponentsMessageSender {
private static ThreadLocal<HttpClientContext> httpClientContextLocal = new ThreadLocal<>();
@Override
protected HttpContext createContext(URI uri) {
HttpClientContext httpClientContext = httpClientContextLocal.get();
// This part makes authentication preemptive:
HttpHost targetHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
AuthCache authCache = new BasicAuthCache();
authCache.put(targetHost, new BasicScheme());
httpClientContext.setAuthCache(authCache);
return httpClientContext;
}
public GenericMessage setBasicAuth(GenericMessage message, String username, String password) throws Exception {
final HttpClientContext httpClientContext = HttpClientContext.create();
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
httpClientContext.setCredentialsProvider(credentialsProvider);
httpClientContextLocal.set(httpClientContext);
return message;
}
}
Spring Integration 4.3.11
Upvotes: 1
Views: 815
Reputation: 121552
That's right. Since Spring Integration WS support is fully based on the Spring WS project, there is nothing more we can in SI what the SWS provides for us.
Anyway you can inject WebServiceMessageCallback
into the AbstractWebServiceOutboundGateway
and there get access to the ThreadLocal
variable for the Message
populated there before calling this WS Gateway.
There in the WebServiceMessageCallback
you should get access to the:
TransportContext context = TransportContextHolder.getTransportContext();
HttpUrlConnection connection = (HttpUrlConnection) context.getConnection();
connection.getConnection().addRequestProperty("hea derParameter", "headerValue");
The same TransportContext
you can access from the custom DefaultSoapHeaderMapper
though and there already even without any ThreadLocal
:
@Override
protected void populateUserDefinedHeader(String headerName, Object headerValue, SoapMessage target) {
super.populateUserDefinedHeader(headerName, headerValue, target);
TransportContext context = TransportContextHolder.getTransportContext();
HttpUrlConnection connection = (HttpUrlConnection) context.getConnection();
connection.getConnection().addRequestProperty(headerName, headerValue);
}
Upvotes: 1