美好的一天。我正在学习如何编写连接到外部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)和所有依赖项的最新版本。
我发现this answer但我不知道如何在我的方案中使用它。
非常感谢提前! Vojtech
修改 我的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类中的以下行导致调用服务的响应为null:
SOAPMessage message = context.getMessage();
答案 0 :(得分:8)
您谈到的答案似乎是指基于CXF的SOAP客户端实现。在这种实现中,您必须向端点添加日志记录拦截器。如果需要,可以使用Spring的某种实现,并且可以在没有任何XML的情况下配置所有内容,告诉我您是否感兴趣。
在您的情况下,如果要查看消息,可以将 org.springframework.ws.server.MessageTracing 的日志级别修改为DEBUG。
我希望这对你有所帮助,祝你有个美好的一天;)。
修改强>
经过一些研究,似乎日志配置不能与JaxWsPortProxyFactoryBean一起使用。 Spring论坛上的This topic与你的问题谈论同样的问题。因此,正如那里所建议的那样,您可以创建自己的Message Handler,以及您自己的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实现的影响要小一些。 我希望答案比我的第一个答案更有效^^。
答案 1 :(得分:2)
处理程序
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);
使用
if ((my_sock=socket(AF_LOCAL,SOCK_STREAM,0))<0){
syslog(LOG_USER|LOG_ERR,"Could not create tsdmd socket!!");
printf("Couldn't create socket \n");
exit(-1);
}
printf("Socket created \n");
/* Name the socket */
memset(&s_addr, 0,sizeof(struct sockaddr_un));
s_addr.sun_family = AF_LOCAL;
strncpy(s_addr.sun_path,"/tmp/dsock",strlen("/tmp/dsock")+1);
s_addr_size=(offsetof(struct sockaddr_un, sun_path)
+strlen(s_addr.sun_path)+1);
unlink("/tmp/dsock");
if(bind(my_sock,(struct sockaddr *)&s_addr, s_addr_size)<0)
{ printf("BIND ERROR \n");
close(my_sock);
exit(1);
}
printf("Bind successful to the socket !! \n");
if(listen(my_sock,16)==-1)
{ perror("Listen error !! \n");
exit(-1);
}
printf("Listening to socket \n");
if(connect(my_sock,(struct sockaddr *) &s_addr,s_addr_size)==-1)
{
syslog(LOG_USER|LOG_ERR,"Could not connect, will try later");
perror("connect()");
printf("Couldn't connect to socket... will try later \n");
close(my_sock);
my_sock=-1;
}
else
{
FD_SET(my_sock,&active_fd_set);
printf(" Socket connected \n");
}
printf("Trying again \n\n\n");
}
--------------------------------------------------------------------
答案 2 :(得分: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;
}