我正在与jax-ws和自定义soapHandler斗争。
我需要输入一个上下文调用参数(即userID)并将其放在CustomHandler上以供审核。
我尝试了requestContext方式,但由于地图是针对所有请求的共享,因此解决方案不是线程安全的:
客户端方法调用在请求上下文中放置了一些参数:
....
Map<String, Object> requestContext = provider.getRequestContext();
requestContext.put("userID", userId);
处理程序
@Override
public boolean handleMessage(SOAPMessageContext context) {
String userId = (String) context.get("userID);
return true;
}
这不是线程安全的,因为实例上下文是唯一的。
所以我尝试在CustomSoapHandler实例中添加contextual参数:
@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);
}
}
}
所以我按如下方式修改了soap处理程序:
public class DataPowerSOAPHandler implements SOAPHandler<SOAPMessageContext> {
private String multichannelId;
@Override
public boolean handleMessage(SOAPMessageContext context) {
System.out.println("multichannelId");
return true;
}
通过这种方式,我为每个客户端请求创建了一个自定义SoapHandler实例。
我认为以这种方式是线程安全的,唯一让我想到的是在客户端调用中添加/删除handlerChain ....
有什么建议吗?
答案 0 :(得分:1)
如果将状态存储在处理程序实例中(例如作为实例变量),则存在线程安全问题的风险。例如,由同一个处理程序实例处理的两个单独的soap消息 - 如果应用程序使用Web服务绑定提供程序(存根)的静态实例并且它附加了处理程序链,那么这些对象实例可用于多个Web服务请求/响应。
然而,MessageContext本身的设计意图是通过{{1>以{松散耦合的方式在处理程序链中提供线程安全,以便在单个消息处理期间每个消息传递给链中每个处理程序的实例。
它类似于servlet世界 - 想想MessageContext
:如果HttpServletRequest
或HttpServlet
(很像JAX-WS soap处理程序)有一个实例变量,那么该共享变量跨用户对该servlet实例的http请求,因此不是线程安全的。但是,两个浏览器请求总是等于传递给ServletFilter
方法系列(HttpServletRequest
等)的两个service()
对象,因此任何用户或特定于请求的状态值都可以安全地存储为请求属性并由其他调度/转发的servlet或后处理过滤器读取。这就是JAX-WS处理程序的工作方式;它们是有效的过滤器。
答案 1 :(得分:1)
由于消息上下文方式不是线程安全的,我使用了标题方式。
所以我将公共属性放在消息上下文中(在每个ws客户端调用之间共享)
Map<String, Object> requestContext = provider.getRequestContext();
requestContext.put(MESSAGE_CONTEXT_KEY, new SetefiMasterpassDataPowerRequest());
我将context属性放在每个ws客户端调用上,使用绑定提供程序的downcast到参考实现:
....
WSBindingProvider provider = (WSBindingProvider) userWalletService;
provider.setOutboundHeaders(Headers.create(new QName(MULTICHANNEL_ID_KEY), multichannelId));
...
然后在处理程序中,我以两种不同的方式获得了属性;来自消息上下文:
DataPowerRequest dataPowerRequest = (DataPowerRequest) context.get(MESSAGE_CONTEXT_KEY);
并从邮件标题中获取:
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);
}
这是我找到解决问题的最好方法。