问题描述
美好的一天.我正在学习如何编写与外部SOAP RCP样式的服务的连接器.我正在使用 jaxws-maven-plugin 插件从WSDL文件生成Java类.我正在使用Spring创建Web服务客户端bean:
Good day. I'm learning how to write a connector to external SOAP RCP styled service. I'm using jaxws-maven-plugin plugin to generate Java classes from the WSDL file. And I'm using Spring to create the web service client 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编写了一个测试类.我这样打电话给客户:
To test the client I wrote a test class using JUnit. I call the client this way :
@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消息记录到控制台中.请怎么做?越简单越好.
Now I need the test to log complete outgoing and incoming SOAP messages into a console. How to do that please? The easier way the better.
我发现了使用以下系统参数的建议,但是它们对我都不起作用:
I found advices to use following system parameters but none of them work for me:
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)和所有依赖项的最新版本.
I'm using Spring configuration classes (no XMLs) and the latest releases of all dependencies.
我找到了此答案,但我不知道该如何在我的场景中使用它.
I'v found this answer but I don't know how to use it in my scenario.
非常感谢! Vojtech
Many thanks in advance! Vojtech
我的logback.xml看起来像这样,但是在控制台中我仍然看不到任何肥皂消息:
My logback.xml looks like this but I still can't see any soap messages in the console:
<?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>
父项目的pom.xml指定了这些依赖项:
EDIT 2:pom.xml of the parent project specifies these dependencies:
<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取决于:
pom.xml of module in which the test class is located depends on:
<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>
我已将LoggingHandler类添加到我的项目中:
EDIT 3:I'v added LoggingHandler class to my project:
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:
And I use it to create the web service client 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:
The result is that SOAP messages are logged but response of calling storeWS.executeQuery is NULL:
StoreResponseType response = storeWS.executeQuery(storeRequest);
如果我注释掉以下行,那么它将再次开始工作,并且为响应分配了一个对象,但是没有记录任何SOAP消息:
If I comment out following line then it starts work again and the response is assigned an object but no SOAP messages are logged:
// factoryBean.setHandlerResolver(handlerResolver());
我发现类LoggingHandler中的以下行导致调用服务的响应为空:
I have found out that following line in class LoggingHandler causes that the response of calling the service is null:
SOAPMessage message = context.getMessage();
推荐答案
您谈论的答案似乎是指基于CXF的SOAP Client实现.在这种实现中,必须将日志记录拦截器添加到端点.您可以根据需要在Spring中使用某种实现,并且可以在不使用任何XML的情况下配置所有内容,告诉我您是否有兴趣.
The answer you talk about seems to refer to CXF-based implementation of SOAP Client. In that sort of implementation, you have to add logging interceptors to endpoint. You can use some sort of implementation with Spring if you want, and you can configure everything without any XML, tell me if you are interested.
如果您要查看消息,可以将 org.springframework.ws.server.MessageTracing 的日志级别修改为DEBUG.
In your case, you can modify the log level of org.springframework.ws.server.MessageTracing to DEBUG if you want to see messages.
希望对您有所帮助,并祝您有美好的一天;).
I hope this help you, and have a good day ;).
编辑
经过一些研究,似乎日志记录配置不适用于JaxWsPortProxyFactoryBean. 本主题在Spring论坛上讨论与您相同的问题.因此,如在那边建议的那样,您可以创建自己的消息处理程序以及自己的HandlerResolver实现来记录消息.为了测试该解决方案,我使用了 docs中的Logging Handler logMessage代码. oracle.com ,然后创建一个新的HandlerChain实现,将该LoggingHandler添加到handlerChain中.最后,我将HandlerChain设置为JaxWsPortProxyFactoryBean.
After some research, it seems that logging configuration doesn't work with a JaxWsPortProxyFactoryBean. This topic on Spring forums talk about the same problem as yours. So, as suggested over there, you can create your own Message Handler, and your own HandlerResolver implementation to log your message.To test that solution, i use the Logging Handler logMessage code from docs.oracle.com, and I create a new HandlerChain implementation which add that LoggingHandler into handlerChain. Finally, I set that HandlerChain into the JaxWsPortProxyFactoryBean.
更具体地说,这是您修改后的配置:
To be more concrete, here is your modified configuration :
@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类:
and the LoggingHandler class :
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的请求消息打印到控制台中的内容...:
And finally, what that print into console with Logback for the request message ... :
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>
...以及响应消息:
... and for the response message :
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实施的影响要小.我希望答案会比我的第一个^^更有效.
It is less impacting than replace your Spring implementation with a CXF one, I think.I hope that answer will be more effective than my first one ^^.
这篇关于如何在客户端记录SOAP消息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!