Wie werden SOAP-Nachrichten auf der Clientseite protokolliert?

Schönen Tag. Ich lerne, wie ein Connector für einen externen SOAP-RCP-gestalteten Dienst geschrieben wird. Ich benutze jaxws-maven-plugin Plugin zum Generieren von Java-Klassen aus der WSDL-Datei. Und ich benutze Spring, um die Web-Service-Client-Bean zu erstellen:

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

Um den Client zu testen, habe ich eine Testklasse mit JUnit geschrieben. Ich rufe den Kunden so an:

@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);
        ...
    }
}

Now Ich brauche den Test, um vollständige ausgehende und eingehende SOAP-Nachrichten in einer Konsole zu protokollieren. Wie machst du das bitte? Je einfacher, desto besser.

Ich habe Hinweise zur Verwendung der folgenden Systemparameter gefunden, aber keine davon funktioniert für mich:

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

Ich verwende Spring-Konfigurationsklassen (keine XMLs) und die neuesten Versionen aller Abhängigkeiten.

Ich habe gefundendiese Antwort aber ich weiß nicht, wie ich es in meinem Szenario verwenden soll.

Vielen Dank im Voraus! Vojtech

BEARBEITEN Meine logback.xml sieht so aus, aber in der Konsole werden immer noch keine Nachrichten angezeigt:

<?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 des übergeordneten Projekts gibt diese Abhängigkeiten an:

<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 des Moduls, in dem sich die Testklasse befindet, hängt ab von:

<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: Ich habe meinem Projekt die LoggingHandler-Klasse hinzugefügt:

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

Und ich benutze es, um die Web-Service-Client-Bean zu erstellen:

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

Das Ergebnis ist, dass SOAP-Nachrichten protokolliert werden, die Antwort des Aufrufs von storeWS.executeQuery jedoch NULL ist:

StoreResponseType response = storeWS.executeQuery(storeRequest);

Wenn ich die folgende Zeile auskommentiere, wird die Arbeit wieder aufgenommen und der Antwort wird ein Objekt zugewiesen, es werden jedoch keine SOAP-Nachrichten protokolliert:

// factoryBean.setHandlerResolver(handlerResolver());

Ich habe herausgefunden, dass die folgende Zeile in der Klasse LoggingHandler dazu führt, dass die Antwort beim Aufrufen des Dienstes null ist:

SOAPMessage message = context.getMessage();

Antworten auf die Frage(6)

Ihre Antwort auf die Frage