Reputation: 4463
I'm struggling with jax-ws and custom soapHandler.
I need to put one context call parameter (i.e. userID) and get it on a CustomHandler for audit purpose.
I tried the requestContext way but since the map isshared for all the request the solution was not thread safe:
client method call put some parameter on request context:
....
Map<String, Object> requestContext = provider.getRequestContext();
requestContext.put("userID", userId);
Handler
@Override
public boolean handleMessage(SOAPMessageContext context) {
String userId = (String) context.get("userID);
return true;
}
This was not thread safe since the instance context is unique.
So I tried to add the contextual parameter in the CustomSoapHandler instance:
@Override
public void deleteCard(String userId, String cardId, String multichannelId ) {
DataPowerSOAPHandler handler = new DataPowerSOAPHandler(multichannelId);
List<Handler> handlerChain = null;
try {
BindingProvider provider = (BindingProvider) userWalletService;
handlerChain = provider.getBinding().getHandlerChain();
logger.debug("handlerChain size {}", handlerChain.size());
handlerChain.add(handler);
provider.getBinding().setHandlerChain(handlerChain);
userWalletService.deleteCard(userId, cardId);
} finally {
if (handlerChain != null && handlerChain.size() >0 ) {
handlerChain.remove(handler);
}
}
}
So i modified the soap handler as follows:
public class DataPowerSOAPHandler implements SOAPHandler<SOAPMessageContext> {
private String multichannelId;
@Override
public boolean handleMessage(SOAPMessageContext context) {
System.out.println("multichannelId");
return true;
}
In this way I create a custom SoapHandler instance for every client request.
I think is thread safe in this way, the only thing that make me think is the add/remove of handlerChain in the client call....
Any suggestion?
Upvotes: 0
Views: 3205
Reputation: 4463
Since the message context way was not thread safe I used the headers way.
So I put the common properties in the message context (shared between every ws client call)
Map<String, Object> requestContext = provider.getRequestContext();
requestContext.put(MESSAGE_CONTEXT_KEY, new SetefiMasterpassDataPowerRequest());
And I put the context property on every ws client call using the downcast of the binding provider to the reference implementation:
....
WSBindingProvider provider = (WSBindingProvider) userWalletService;
provider.setOutboundHeaders(Headers.create(new QName(MULTICHANNEL_ID_KEY), multichannelId));
...
Then in the handler I got the properties in 2 different ways; from the message context:
DataPowerRequest dataPowerRequest = (DataPowerRequest) context.get(MESSAGE_CONTEXT_KEY);
and from the message header:
SOAPEnvelope envelope = context.getMessage().getSOAPPart().getEnvelope();
SOAPHeader envelopeHeader = envelope.getHeader();
NodeList multichannelIdNode = envelopeHeader.getElementsByTagNameNS("*", MULTICHANNEL_ID_KEY);
String multichannelId = null;
if (multichannelIdNode != null && multichannelIdNode.item(0)!= null && multichannelIdNode.item(0).getChildNodes() != null) {
Node item = multichannelIdNode.item(0);
multichannelId = item.getChildNodes().item(0).getNodeValue();
item.getParentNode().removeChild(item);
}
This was the best way I found to solve the problem.
Upvotes: 1
Reputation: 3424
If you store state within your handler instance (e.g. as an instance variable) you run the risk of thread safety issues. For example, two separate soap messages handled by the same handler instance - if the application uses a static instance of the web service binding provider (stub) and it has a handler chain attached, those object instances could be used for multiple web service request/responses.
However, the design intent of MessageContext itself is to provide thread safety across the handler chain in a loosely-coupled fashion for the duration of individual message processing by having a MessageContext
instance per message that is passed to each handler in the chain.
It's analogous to the servlet world - think HttpServletRequest
: if an HttpServlet
or a ServletFilter
(much like a JAX-WS soap handler) has an instance variable, that variable is shared across user http requests to that servlet instance and is therefore not threadsafe. However, two browser requests always equals two HttpServletRequest
objects passed to the service()
method family (doGet()
, etc), so any user or request-specific state values can be safely stored as request attributes and read by other dispatched/forwarded servlets or post-processing filters. This is how JAX-WS handlers work; they are effectively filters.
Upvotes: 2