java - 返回客户端时 CXF WS 调度失败

标签 java cxf

我正在尝试使用 javax.xml.ws.Provider 使用 CXF 开发远程调度程序,如下所述:http://cxf.apache.org/docs/jax-ws-dispatch-api.html .我有一个配置了 WSAddressing、SOAP12_HTTP_BINDING 和以下 WSS4J 配置的 CXF 客户端:

客户端 WSS4J IN 拦截器:

inProps.put("action",                  "Timestamp Signature Encrypt");      
inProps.put("passwordType",            "PasswordText");
    ...

客户端 WSS4J OUT 拦截器:

protected static final String WSU_NS =
    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";                                           
protected static final String SOAP12_NS = "http://www.w3.org/2003/05/soap-envelope";
protected static final String SOAP11_NS = "http://schemas.xmlsoap.org/soap/envelope";
protected static final String WSA_NS =  "http://www.w3.org/2005/08/addressing";
protected static final String WSSE_NS="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";

String userForPswCallback = ...
Map<String, Object> props = new HashMap<String, Object>();
props.put("action",                 "UsernameToken Timestamp Signature Encrypt");
props.put("passwordType",           "PasswordText");
...
props.put("encryptionParts",        "{Content}{"+SOAP12_NS+"}Body;{Element}{"+WSSE_NS+"}UsernameToken");
props.put("signatureKeyIdentifier", "DirectReference");
props.put("signatureParts",         "{Element}{"+SOAP12_NS+"}Body;" +
                "{Element}{"+WSSE_NS+"}UsernameToken;" +
                "{Element}{"+WSA_NS+"}Action;" +
                "{Element}{"+WSA_NS+"}MessageID;" +
                "{Element}{"+WSA_NS+"}To;" +
                "{Element}{"+WSA_NS+"}ReplyTo");

在调度程序端,我发布了以下调度程序服务:

@WebServiceProvider( targetNamespace = JettyConstants.PersistenceNameSpace, 
                     serviceName=JettyConstants.PersistenceService, 
                     portName=JettyConstants.PersistencePort)
@ServiceMode (Service.Mode.MESSAGE) 
public class PersistenceProvider implements Provider<Source> {
    private static final Logger log = Logger.getLogger(PersistenceProvider.class);

    private TransformerFactory transformerFactory;

    public PersistenceProvider() {
        // Complete
    }

    private static final DocumentBuilderFactory BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
    static {
          BUILDER_FACTORY.setNamespaceAware(true);
    }

    public Source invoke(Source request) {
       try {
            DOMSource sourceInvoke = null;

            //get userName and userPassword from the interceptor chain (added in a custom interceptor)
            String userName = (String)(PhaseInterceptorChain.getCurrentMessage().get("product.username"));
            String userPassword = (String)(PhaseInterceptorChain.getCurrentMessage().get("product.password"));

            //insert extracted into a org.w3c.dom.Document
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            DocumentBuilder builder = null;
            org.w3c.dom.Document doc = null;

            try {
                builder = dbf.newDocumentBuilder();
                InputStream is = convertMessageToInputStream(request);   
                doc = builder.parse(is);                 
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            } 

            NodeList node = doc.getElementsByTagNameNS("*","Security");
            node.item(0).getParentNode().removeChild(node.item(0));

            // Add extra header to the envelop...
            ...

            sourceInvoke = new DOMSource(doc);                

            // 1. Resolve target service based on wsContext
            QName targetService = new QName(JettyConstants.PersistenceNameSpace,JettyConstants.PersistenceService);
            QName targetPort = new QName(JettyConstants.PersistenceNameSpace,JettyConstants.PersistencePort);
            String targetEndpoint = DispatchStarter.getNewVelocityDestination(false)+"/Persistence";

    /* ****************************************** INVOKE THE REMOTE SERVER *************************************** */           
            // 2. Create dispatcher
            Dispatch<Source> dispatcher = createDispatcher(targetService, targetPort, targetEndpoint);      

            // 3. Invoke target service
            Source response = dispatcher.invoke(sourceInvoke);                                         

    /* *********************************************************************************************************** */           

            // 4. Return service response to consumer
            DOMSource domS = toDOMSourceFromSAX((SAXSource)response);

            //get the doc to modify it:
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            DocumentBuilder builder = null;
            org.w3c.dom.Document doc = null;
            builder = dbf.newDocumentBuilder();
            doc = (org.w3c.dom.Document) domS.getNode();                  

            // 4.1. Take just the body content out of the response received from the server:
            NodeList body_el = doc.getElementsByTagNameNS("*", "Body").item(0).getChildNodes();

            DocumentBuilderFactory docf = DocumentBuilderFactory.newInstance();
            Document docnew = docf.newDocumentBuilder().newDocument();
            Node newNode = docnew.createElementNS(body_el.item(0).getNamespaceURI(), body_el.item(0).getLocalName());
            Node nodeComplete = docnew.adoptNode(body_el.item(0).cloneNode(true));     

            // 4.2. Create the SAXSource to send back to the client through CXF stack
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            Source xmlSource = new DOMSource(nodeComplete);
            Result outputTarget = new StreamResult(outputStream);
            TransformerFactory.newInstance().newTransformer().transform(xmlSource, outputTarget);
            InputStream is = new ByteArrayInputStream(outputStream.toByteArray());
            InputSource inputSource = new InputSource(is);
            SAXSource finalRes = new SAXSource(inputSource);             

            return finalRes;

        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    private Dispatch<Source> createDispatcher(QName serviceName, QName portName, String targetEndpoint) {
        Service service = Service.create(serviceName);         
        String actualBinding = SOAPBinding.SOAP12HTTP_BINDING;
        service.addPort(portName, actualBinding, targetEndpoint);
        Dispatch<Source> dispatcher = service.createDispatch(portName, Source.class,
                Service.Mode.MESSAGE);
        return dispatcher;
    }
}

PersistenceProvider(调度程序)与以下 WSS4JInterceptors 一起发布

Dispatcher WSS4J Dispatcher IN interceptors:

    Map<String, Object> inProps = new HashMap<String, Object>();
    inProps.put(WSHandlerConstants.ACTION, "UsernameToken Timestamp Signature Encrypt");
    inProps.put("passwordType",            "PasswordText");
    ...

Dispatcher WSS4J Dispatcher OUT interceptors:

    protected static final String WSU_NS =
        "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";

    protected static final String SOAP_NS = "http://www.w3.org/2003/05/soap-envelope";
    protected static final String WSA_NS =  "http://www.w3.org/2005/08/addressing";

    outProps.put(WSHandlerConstants.ACTION, "Timestamp Signature Encrypt");
    outProps.put("passwordType",            "PasswordText");
    ...
    outProps.put("encryptionParts",         "{Content}{"+SOAP_NS+"}Body");
    ...
    outProps.put("signatureParts",          "{Element}{" + WSU_NS + "}Timestamp;" +
                        "{Element}{"+SOAP_NS+"}Body;" +
                        "{Element}{"+WSA_NS+"}Action;" +
                        "{Element}{"+WSA_NS+"}MessageID;" +
                        "{Element}{"+WSA_NS+"}To;" +
                        "{Element}{"+WSA_NS+"}RelatesTo");

并且启用了 WSAddressingFeature。我可以将请求从我的客户端重定向到远程服务器,然后在调度程序端从远程服务器获得响应。不幸的是,当我尝试将答案发送回我的客户端时(即在 PersistenceProvider#invoke 方法的返回语句之后),我收到以下异常:

{http://dbproxyservice/}PersistenceService#{http://dispatch/}invoke has thrown exception, unwinding now
org.apache.cxf.binding.soap.SoapFault: Error creating SOAPMessage
       at org.apache.cxf.jaxws.interceptors.MessageModeOutInterceptor$MessageModeOutInterceptorInternal.handleMessage(MessageModeOutInterceptor.java:210)
       at org.apache.cxf.jaxws.interceptors.MessageModeOutInterceptor$MessageModeOutInterceptorInternal.handleMessage(MessageModeOutInterceptor.java:182)
       at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
       at org.apache.cxf.interceptor.OutgoingChainInterceptor.handleMessage(OutgoingChainInterceptor.java:77)
       at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
       at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:123)
       at org.apache.cxf.transport.http_jetty.JettyHTTPDestination.serviceRequest(JettyHTTPDestination.java:323)
       at org.apache.cxf.transport.http_jetty.JettyHTTPDestination.doService(JettyHTTPDestination.java:289)
       at org.apache.cxf.transport.http_jetty.JettyHTTPHandler.handle(JettyHTTPHandler.java:72)
       at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:942)
       at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:878)
       at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
       at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:250)
       at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:110)
       at org.eclipse.jetty.server.Server.handle(Server.java:349)
       at org.eclipse.jetty.server.HttpConnection.handleRequest(HttpConnection.java:441)
       at org.eclipse.jetty.server.HttpConnection$RequestHandler.content(HttpConnection.java:936)
       at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:893)
       at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:224)
       at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:52)
       at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:586)
       at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:44)
       at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:598)
       at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:533)
       at java.lang.Thread.run(Thread.java:662)
Caused by: com.ctc.wstx.exc.WstxEOFException: Unexpected EOF in prolog
at [row,col {unknown-source}]: [1,0]
       at com.ctc.wstx.sr.StreamScanner.throwUnexpectedEOF(StreamScanner.java:677)
       at com.ctc.wstx.sr.BasicStreamReader.handleEOF(BasicStreamReader.java:2104)
       at com.ctc.wstx.sr.BasicStreamReader.nextFromProlog(BasicStreamReader.java:2010)
       at com.ctc.wstx.sr.BasicStreamReader.next(BasicStreamReader.java:1102)
       at org.apache.cxf.staxutils.StaxUtils.copy(StaxUtils.java:551)
       at org.apache.cxf.staxutils.StaxUtils.copy(StaxUtils.java:513)
       at org.apache.cxf.staxutils.StaxUtils.copy(StaxUtils.java:467)
       at org.apache.cxf.jaxws.interceptors.MessageModeOutInterceptor$MessageModeOutInterceptorInternal.handleMessage(MessageModeOutInterceptor.java:204)
       ... 24 more
[10Dec 18:59:51,881] (doLog@372) WARN  PhaseInterceptorChain - Interceptor for {http://dbproxyservice/}PersistenceService#{http://dispatch/}invoke has thrown exception, unwinding now
java.lang.NullPointerException
       at org.apache.cxf.ws.addressing.ContextUtils.hasEmptyAction(ContextUtils.java:358)
       at org.apache.cxf.ws.addressing.MAPAggregator.assembleGeneric(MAPAggregator.java:686)
       at org.apache.cxf.ws.addressing.MAPAggregator.aggregate(MAPAggregator.java:660)
       at org.apache.cxf.ws.addressing.MAPAggregator.mediate(MAPAggregator.java:515)
       at org.apache.cxf.ws.addressing.MAPAggregator.handleMessage(MAPAggregator.java:228)
       at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
       at org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:107)
       at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:323)
       at org.apache.cxf.interceptor.OutgoingChainInterceptor.handleMessage(OutgoingChainInterceptor.java:77)
       at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
       at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:123)
       at org.apache.cxf.transport.http_jetty.JettyHTTPDestination.serviceRequest(JettyHTTPDestination.java:323)
       at org.apache.cxf.transport.http_jetty.JettyHTTPDestination.doService(JettyHTTPDestination.java:289)
       at org.apache.cxf.transport.http_jetty.JettyHTTPHandler.handle(JettyHTTPHandler.java:72)
       at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:942)
       at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:878)
       at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
       at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:250)
       at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:110)
       at org.eclipse.jetty.server.Server.handle(Server.java:349)
       at org.eclipse.jetty.server.HttpConnection.handleRequest(HttpConnection.java:441)
       at org.eclipse.jetty.server.HttpConnection$RequestHandler.content(HttpConnection.java:936)
       at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:893)
       at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:224)
       at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:52)
       at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:586)
       at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:44)
       at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:598)
       at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:533)
       at java.lang.Thread.run(Thread.java:662)

你有什么提示可以避免这个错误吗?特别是,为了通过 CXF 堆栈将调用响应发送回客户端,我需要做什么?在上面的 PersistenceProvider#invoke 的第 4 点删除从远程服务器获取的所有 header 是否正确?我这样做是因为我认为调度员的 CXF out 拦截器会在 Body 周围从头开始构建信封。对吗?

谢谢!

最佳答案

明白了!解决这个问题的方法是将 PersistenceProvider(调度程序)中的 invoke 方法返回的 javax.xml.transform.Source 转换为 DOMSource。这允许 CXF 正确地填充即将离任的肥皂信封主体。

问题解决了。全文 here !

关于java - 返回客户端时 CXF WS 调度失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13856072/

相关文章:

java - 想要使用 CAMEL、CXF 和 Karaf 访问静态资源

java - JBoss Fuse 6.3.0 CXF 引用教程与 JBoss Deveoper Studio 最新版本如 9.* 或 10.*

java - AuthenticationProvider 和 AuthenticationEntryPoint 之间的区别

java - 没有复杂类型的 Apache CXF Soap wsdl

java - 为什么 HashMap 获取的参数是 Object 而不是键的类型?

java - 如何在主线程中保持 apache Camel 上下文处于 Activity 状态

java - 如何仅为特定端点添加 Apache cxf wsrm

java - Apache CXF 从 SOAP 反序列化对象后是否可以调用后反序列化方法?

java - 编写一个名为的递归方法,该方法接受一个整数数组并以相反的排序顺序返回该数组

java - 为什么将变量放在类元素中时不会发生编译器错误