2014-12-14 6 views
4

Добрый день. Я изучаю, как писать коннектор для внешней службы SOAP RCP. Я использую плагин jaxws-maven-plugin для создания классов Java из файла WSDL. И я использую Spring для создания клиента веб-службы компонент:Как регистрировать SOAP-сообщения на стороне клиента?

@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 (не XMLs) и последние выпуски всех зависимостей.

Я нашел this answer, но я не знаю, как его использовать в моем сценарии.

Большое спасибо заранее! Войтех

EDIT: Мой 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> 

EDIT 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> 

EDIT 3: I'v добавил 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); 
      } 
     } 
    } 
} 

И я использую его для создания клиента веб-службы боба:

@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(); 

ответ

7

Ответ, о котором вы говорите, похоже, относится к реализации SOAP-клиента на основе CXF. В такой реализации вы должны добавить перехватчики регистрации в конечную точку. Вы можете использовать какую-то реализацию с Spring, если хотите, и можете настроить все без каких-либо XML, скажите мне, если вы заинтересованы.

В вашем случае вы можете изменить уровень журнала org.springframework.ws.server.MessageTracing в DEBUG, если вы хотите видеть сообщения.

Надеюсь, это поможет вам, и у вас будет хороший день;).


EDIT

После некоторых исследований, кажется, что выход конфигурации не работает с JaxWsPortProxyFactoryBean. This topic на Весенних форумах говорят о той же проблеме, что и ваша. Итак, как было предложено там, вы можете создать свой собственный обработчик сообщений и свою собственную реализацию HandlerResolver для регистрации вашего сообщения. Чтобы проверить это решение, я использую код LogMessage журнала Logging Handler от docs.oracle.com, и я создаю новую реализацию 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> 

Это меньше, чем ударной нагрузки заменить реализацию Spring с CXF один, я думаю. Я надеюсь, что ответ будет более эффективным, чем мой первый ^^.

+0

Да пожалуйста, я заинтересован в опции Spring. Я настроил уровень журнала org.springframework.ws.server.MessageTracing и org.springframework.ws.client.MessageTracing для DEBUG (см. Изменение в моем вопросе), но я до сих пор не вижу никаких мыльных сообщений в консоли. Спасибо. – Vojtech

+0

Вы добавили зависимость регистрации сообщений Commons (1.1 или выше, в соответствии с документацией) к вашему пути к классам? В противном случае вы можете увидеть любой журнал Spring, если вы установите root-logger на уровень DEBUG? Возможно, это больше проблема конфигурации/зависимости регистратора, чем конфигурация webservice. – KevinHol

+0

Я редактирую свой первый ответ. Попробуйте это решение, прежде чем исследовать конфигурацию ведения журнала. – KevinHol

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; 
    } 
1

Handler

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

 Смежные вопросы

  • Нет связанных вопросов^_^