【问题标题】:jax-ws soaphandler thread safejax-ws soaphandler 线程安全
【发布时间】:2014-08-07 07:23:27
【问题描述】:

我正在为 jax-ws 和自定义soapHandler 苦苦挣扎。

我需要放置一个上下文调用参数(即用户 ID)并将其放在 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 实例中添加上下文参数:

@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....

有什么建议吗?

【问题讨论】:

  • 我在同一问题上使用了本地线程。

标签: multithreading jax-ws


【解决方案1】:

如果您将状态存储在处理程序实例中(例如,作为实例变量),您将面临线程安全问题的风险。例如,由同一个处理程序实例处理的两个单独的肥皂消息 - 如果应用程序使用 Web 服务绑定提供程序(存根)的静态实例并且附加了处理程序链,则这些对象实例可用于多个 Web 服务请求/回复。

但是,MessageContext 本身的设计意图是在处理程序链中以松散耦合的方式提供线程安全,以在单个消息处理期间通过每个 MessageContext 实例传递给链中每个处理程序的消息。

这类似于 servlet 世界 - 想想 HttpServletRequest:如果 HttpServletServletFilter(很像 JAX-WS 肥皂处理程序)有一个实例变量,那么该变量在用户 http 请求之间共享servlet 实例,因此不是线程安全的。但是,两个浏览器请求始终等于传递给service() 方法族(doGet() 等)的两个HttpServletRequest 对象,因此任何用户或特定于请求的状态值都可以安全地存储为请求属性并被其他调度/转发的 servlet 或后处理过滤器。这就是 JAX-WS 处理程序的工作方式;它们是有效的过滤器。

【讨论】:

  • 正如我所说并在我的皮肤上测试 MessageContext 方式不是线程安全的。如果你在 ws 客户端中放置一个属性并在处理程序中获取它,那么每个不同的方法调用都共享相同的消息上下文和相同的映射。
【解决方案2】:

由于消息上下文方式不是线程安全的,所以我使用了标头方式。

所以我把通用属性放在消息上下文中(在每个 ws 客户端调用之间共享)

Map<String, Object> requestContext = provider.getRequestContext();
requestContext.put(MESSAGE_CONTEXT_KEY, new SetefiMasterpassDataPowerRequest());

并且我使用绑定提供程序的向下转换将上下文属性放在每个 ws 客户端调用到参考实现:

....
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);
}

这是我找到的解决问题的最佳方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-22
    • 2012-05-25
    • 1970-01-01
    • 1970-01-01
    • 2012-05-22
    • 1970-01-01
    • 1970-01-01
    • 2012-04-15
    相关资源
    最近更新 更多