6

再会。我正在学习如何编写连接到外部 SOAP RCP 样式服务的连接器。我正在使用jaxws-maven-plugin插件从 WSDL 文件生成 Java 类。我正在使用 Spring 创建 Web 服务客户端 bean:

@Configuration
public class StoreServiceConfig {

    @Bean
    public StoreWS storeWS() throws IOException {
        JaxWsPortProxyFactoryBean factoryBean = new JaxWsPortProxyFactoryBean();
        factoryBean.setServiceInterface(StoreWS.class);
        factoryBean.setWsdlDocumentUrl(new ClassPathResource("/wsdl/StoreWS.wsdl").getURL());
        factoryBean.setNamespaceUri("urn_store");
        factoryBean.setServiceName("StoreWSService");
        factoryBean.setEndpointAddress("https://10.34.45.82/storeservice/services/StoreWS");
        factoryBean.setUsername("testuser");
        factoryBean.setPassword("testpassword");
        factoryBean.afterPropertiesSet();
        return (StoreWS) factoryBean.getObject();
    }
}

为了测试客户端,我使用 JUnit 编写了一个测试类。我这样称呼客户:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = StoreServiceTestConfig.class)
public class StoreServiceImplTest {
    @Autowired
    private StoreWS storeWS;
    ...
    @Test
    public void testExecuteQuery() throws Exception {
        ...
        StoreResponseType response = storeWS.executeQuery(storeRequest);
        ...
    }
}

现在我需要测试将完整的传出和传入 SOAP 消息记录到控制台中。请问该怎么做?方法越简单越好。

我找到了使用以下系统参数的建议,但它们都不适合我:

com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=true
com.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true
com.sun.xml.ws.transport.local.LocalTransportPipe.dump=true
com.sun.xml.ws.transport.http.HttpAdapter.dump=true

我正在使用 Spring 配置类(无 XML)和所有依赖项的最新版本。

我找到了这个答案,但我不知道如何在我的场景中使用它。

提前谢谢了!沃杰科技

编辑: 我的 logback.xml 看起来像这样,但我仍然在控制台中看不到任何肥皂消息:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg %n
            </Pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>DEBUG</level>
        </filter>
    </appender>
    <logger name="org.springframework.ws.client.MessageTracing">
        <level value="DEBUG" />
        <appender-ref ref="consoleAppender" />
    </logger>
    <logger name="org.springframework.ws.server.MessageTracing">
        <level value="DEBUG" />
        <appender-ref ref="consoleAppender" />
    </logger>
    <root>
        <level value="DEBUG" />
        <appender-ref ref="consoleAppender" />
    </root>
</configuration>

编辑 2:父项目的 pom.xml 指定这些依赖项:

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring.version>4.1.2.RELEASE</spring.version>
    <spring-ws-core.version>2.2.0.RELEASE</spring-ws-core.version>
    <slf4j.version>1.7.7</slf4j.version>
    <logback.version>1.1.2</logback.version>
    <junit.version>4.12</junit.version>
    <commons-logging.version>1.1.1</commons-logging.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.ws</groupId>
            <artifactId>spring-ws-core</artifactId>
            <version>${spring-ws-core.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- LOGGING -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>${commons-logging.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>
        <!-- TESTING -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

测试类所在模块的 pom.xml 依赖于:

<dependencies>
    <dependency>
        <groupId>${project.groupId}</groupId>
        <artifactId>vinweb-connector-ws</artifactId>
    </dependency>
    <dependency>
        <groupId>${project.groupId}</groupId>
        <artifactId>vinweb-connector-api</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
    </dependency>
    <!-- LOGGING -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
    </dependency>
    <!-- TESTING -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
    </dependency>
</dependencies>

编辑 3: 我已将 LoggingHandler 类添加到我的项目中:

package storeservice.log;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.xml.namespace.QName;
import javax.xml.soap.MimeHeader;
import javax.xml.soap.MimeHeaders;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;

@Component
public class LoggingHandler implements SOAPHandler<SOAPMessageContext> {

    private final Logger LOG = LoggerFactory.getLogger(LoggingHandler.class);

    @Override
    public Set<QName> getHeaders() {
        return Collections.emptySet();
    }

    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        logMessage(context, "SOAP message : ");
        return true;
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
        logMessage(context, "SOAP error : ");
        return true;
    }

    @Override
    public void close(MessageContext context) {
    }

    private void logMessage(SOAPMessageContext context, String type) {
        try {
            if(LOG.isDebugEnabled()) {
                Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
                if(outboundProperty) {
                    LOG.debug("Outbound " + type);
                } else {
                    LOG.debug("Inbound " + type);
                }

                SOAPMessage message = context.getMessage();

                // Print out the MIME headers
                MimeHeaders headers = message.getMimeHeaders();
                Iterator<MimeHeader> headersIterator = headers.getAllHeaders();
                MimeHeader mimeHeader;
                LOG.debug("  Mime headers :");
                while(headersIterator.hasNext()) {
                    mimeHeader = headersIterator.next();
                    LOG.debug("    " + mimeHeader.getName() + " : " + mimeHeader.getValue());
                }

                // Print out the message body
                LOG.debug("  Message body :");
                try(OutputStream outStream = new ByteArrayOutputStream()) {
                    message.writeTo(outStream);
                    LOG.debug("    " + outStream.toString());
                }
            }
        } catch (Exception e){
            if(LOG.isErrorEnabled()) {
                LOG.error("Error logging SOAP message", e);
            }
        }
    }
}

我用它来创建 Web 服务客户端 bean:

@Configuration
public class StoreServiceConfig {

    @Autowired
    private LoggingHandler loggingHandler;

    @Bean
    public StoreWS storeWS() throws IOException {
        JaxWsPortProxyFactoryBean factoryBean = new JaxWsPortProxyFactoryBean();
        factoryBean.setServiceInterface(StoreWS.class);
        factoryBean.setWsdlDocumentUrl(new ClassPathResource("/wsdl/StoreWS.wsdl").getURL());
        factoryBean.setNamespaceUri("urn_store");
        factoryBean.setServiceName("StoreWSService");
        factoryBean.setEndpointAddress("https://10.34.45.82/storeservice/services/StoreWS");
        factoryBean.setUsername("testuser");
        factoryBean.setPassword("testpassword");

        factoryBean.setHandlerResolver(handlerResolver());

        factoryBean.afterPropertiesSet();
        return (StoreWS) factoryBean.getObject();
    }

    @Bean
    public HandlerResolver handlerResolver() {
        return new HandlerResolver() {
            @Override
            public List<Handler> getHandlerChain(PortInfo portInfo) {
                List<Handler> handlerChain = new ArrayList<>();
                handlerChain.add(loggingHandler);
                return handlerChain;
            }
        };
    }
}

结果是记录了 SOAP 消息,但调用 storeWS.executeQuery 的响应为 NULL:

StoreResponseType response = storeWS.executeQuery(storeRequest);

如果我注释掉以下行,那么它会再次开始工作,并且会为响应分配一个对象,但不会记录任何 SOAP 消息:

// factoryBean.setHandlerResolver(handlerResolver());

我发现 LoggingHandler 类中的以下行导致调用服务的响应为空:

SOAPMessage message = context.getMessage();
4

3 回答 3

10

您所说的答案似乎是指基于 CXF 的 SOAP Client 实现。在这种实现中,您必须将日志拦截器添加到端点。如果你愿意,你可以使用 Spring 的某种实现,并且你可以在没有任何 XML 的情况下配置所有内容,如果你有兴趣,请告诉我。

在您的情况下,如果您想查看消息,可以将org.springframework.ws.server.MessageTracing的日志级别修改为 DEBUG。

我希望这对您有所帮助,并祝您有美好的一天;)。


编辑

经过一些研究,日志配置似乎不适用于 JaxWsPortProxyFactoryBean。 Spring论坛上的这个主题与您讨论的问题相同。因此,正如那里建议的那样,您可以创建自己的消息处理程序和自己的 HandlerResolver 实现来记录您的消息。为了测试该解决方案,我使用了docs.oracle.com中的 Logging Handler logMessage 代码,并创建了一个新的 HandlerChain 实现,该实现将该 LoggingHandler 添加到 handlerChain 中。最后,我将该 HandlerChain 设置为 JaxWsPortProxyFactoryBean。

更具体地说,这是您修改后的配置:

@Configuration
public class StoreServiceConfig {

    @Bean
    public StoreWS storeWS() throws IOException {
      JaxWsPortProxyFactoryBean factoryBean = new JaxWsPortProxyFactoryBean();
      factoryBean.setServiceInterface(StoreWS.class);
      factoryBean.setWsdlDocumentUrl(new ClassPathResource("/wsdl/StoreWS.wsdl").getURL());
      factoryBean.setNamespaceUri("urn_store");
      factoryBean.setServiceName("StoreWSService");
      factoryBean.setEndpointAddress("https://10.34.45.82/storeservice/services/StoreWS");
      factoryBean.setUsername("testuser");
      factoryBean.setPassword("testpassword");

      factoryBean.setHandlerResolver(handlerResolver());

      factoryBean.afterPropertiesSet();
      return (StoreWS) factoryBean.getObject();
  }

  public HandlerResolver handlerResolver() {
    return new HandlerResolver() {

        public List<Handler> getHandlerChain(PortInfo portInfo) {
            List<Handler> handlerChain = new ArrayList<Handler>();
            handlerChain.add(new LoggingHandler());
            return handlerChain;
        }

    };
 }

}

和 LoggingHandler 类:

public class LoggingHandler implements SOAPHandler<SOAPMessageContext> {

  private final org.slf4j.Logger LOG = LoggerFactory
        .getLogger("SOAPMessageLoggingHandler");

  public void close(MessageContext context) {
     }

  public boolean handleFault(SOAPMessageContext context) {
    logMessage(context, "SOAP Error is : ");
    return true;
  }

  public boolean handleMessage(SOAPMessageContext context) {
    logMessage(context, "SOAP Message is : ");
    return true;
  }

  public Set<QName> getHeaders() {
    return Collections.EMPTY_SET;
  }

  private boolean logMessage(MessageContext mc, String type) {
    try {
        if (LOG.isDebugEnabled()) {
            LOG.debug(type);
            SOAPMessage msg = ((SOAPMessageContext) mc)
                    .getMessage();

            // Print out the Mime Headers
            MimeHeaders mimeHeaders = msg.getMimeHeaders();
            Iterator mhIterator = mimeHeaders.getAllHeaders();
            MimeHeader mh;
            String header;
            LOG.debug("  Mime Headers:");
            while (mhIterator.hasNext()) {
                mh = (MimeHeader) mhIterator.next();
                header = new StringBuffer(" Name: ")
                        .append(mh.getName()).append(" Value: ")
                        .append(mh.getValue()).toString();
                LOG.debug(header);
            }

            LOG.debug(" SOAP Message: ");
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            msg.writeTo(baos);
            LOG.debug("   " + baos.toString());
            baos.close();
        }

        return true;
    } catch (Exception e) {
        if (LOG.isErrorEnabled()) {
            LOG.error("Error logging SOAP message",
                    e);
        }

        return false;
    }
  }
}

最后,使用 Logback 将请求消息打印到控制台的内容...:

    17:29:22 [main] DEBUG SOAPMessageLoggingHandler - SOAP Message is :  
    17:29:22 [main] DEBUG SOAPMessageLoggingHandler -   Mime Headers: 
    17:29:22 [main] DEBUG SOAPMessageLoggingHandler -  Name: Accept Value: text/xml, text/html
    17:29:22 [main] DEBUG SOAPMessageLoggingHandler -  Name: Content-Type Value: text/xml; charset=utf-8 
    17:29:22 [main] DEBUG SOAPMessageLoggingHandler -  Name: Content-Length Value: 246 
    17:29:22 [main] DEBUG SOAPMessageLoggingHandler -  SOAP Message:  
    17:29:22 [main] DEBUG SOAPMessageLoggingHandler -    <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><S:Body><ns2:executeQuery xmlns:ns2="org.test"><arg0>test</arg0></ns2:executeQuery></S:Body></S:Envelope> 

...对于响应消息:

    17:29:22 [main] DEBUG SOAPMessageLoggingHandler - SOAP Message is :  
    17:29:22 [main] DEBUG SOAPMessageLoggingHandler -   Mime Headers: 
    17:29:22 [main] DEBUG SOAPMessageLoggingHandler -  Name: Accept Value: text/xml, text/html
    17:29:22 [main] DEBUG SOAPMessageLoggingHandler -  Name: Content-Type Value: text/xml; charset=utf-8 
    17:29:22 [main] DEBUG SOAPMessageLoggingHandler -  Name: Content-Length Value: 266 
    17:29:22 [main] DEBUG SOAPMessageLoggingHandler -  SOAP Message:  
    17:29:22 [main] DEBUG SOAPMessageLoggingHandler -    <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><S:Body><ns2:executeQueryResponse xmlns:ns2="org.test"><return>test</return></ns2:executeQueryResponse></S:Body></S:Envelope> 

我认为,它比用 CXF 替换 Spring 实现的影响要小。我希望这个答案会比我的第一个更有效^^。

于 2014-12-14T10:45:01.350 回答
2

处理程序

public class MyServiceLogHandler implements SOAPHandler<SOAPMessageContext> {

    Logger handlerLog = LogManager.getLogger(MyServiceLogHandler.class);

    public boolean handleMessage(SOAPMessageContext context) {
        SOAPMessage message= context.getMessage();
        boolean isOutboundMessage=  (Boolean)context.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);

        if(isOutboundMessage){
            handlerLog.debug("OUTBOUND MESSAGE\n");

        }else{
            handlerLog.debug("INBOUND MESSAGE\n");
        }

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            message.writeTo(baos);
            handlerLog.debug("   " + baos.toString());
            baos.close();
            return true;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return false;
    }

    public boolean handleFault(SOAPMessageContext context) {
        SOAPMessage message= context.getMessage();
        try {
            message.writeTo(System.out);
        } catch (SOAPException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return false;
    }

    public void close(MessageContext context) {
        // TODO Auto-generated method stub

    }

    public Set<QName> getHeaders() {
        // TODO Auto-generated method stub
        return null;
    }

}

采用

import javax.xml.ws.handler.Handler;
import java.util.List;
import javax.xml.ws.Binding;
import javax.xml.ws.BindingProvider;

Binding binding = ((BindingProvider) bookService).getBinding();
        List<Handler> handlerList = binding.getHandlerChain();
        handlerList.add(new MyServiceLogHandler());
        binding.setHandlerChain(handlerList);
于 2017-09-01T22:11:32.103 回答
0

public boolean handleMessage(SOAPMessageContext context) {
        SOAPMessage message= context.getMessage();
        boolean isOutboundMessage=  (Boolean)context.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);

        if(isOutboundMessage){
            handlerLog.debug("OUTBOUND MESSAGE\n");

        }else{
            handlerLog.debug("INBOUND MESSAGE\n");
        }

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            message.writeTo(baos);
            handlerLog.debug("   " + baos.toString());
            baos.close();
            return true;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return false;
    }
于 2017-05-10T16:31:53.097 回答