-->

How to use PayloadLoggingInterceptor and SOAPLoggi

2020-07-30 03:53发布

问题:

In my application, I am consuming a third party web-service that is provided by my client.

I have developed my application on Spring and Hibernate framework and in one module I am consuming this third party web-service url. I have generated web-service stubs using

javab2-maven-plugin

The maven plugin in declared as below in my pom.xml file :

          <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxb2-maven-plugin</artifactId>
                <version>1.5</version>
                <executions>
                    <execution>
                        <id>xjc</id>
                        <goals>
                            <goal>xjc</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!-- Package to store the generated file -->
                    <packageName>com.equifax.unsolicited.wsdl.stub</packageName>
                    <!-- Treat the input as WSDL -->
                    <wsdl>true</wsdl>
                    <!-- Input is not XML schema -->
                    <xmlschema>false</xmlschema>
                    <!-- The WSDL file that you saved earlier -->
                    <schemaFiles>Duk_CIS_Send_CreditStatus.wsdl</schemaFiles>
                    <!-- The location of the WSDL file -->
                    <schemaDirectory>${project.basedir}/src/main/wsdl</schemaDirectory>
                    <!-- The output directory to store the generated Java files -->
                    <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
                    <!-- Don't clear output directory on each run -->
                    <clearOutputDir>false</clearOutputDir>
                </configuration>
            </plugin>

And I am using the auto-generated JAXB java classes to call the web service. I have created a service bean which call the web service :

@Service("unsolicitResponseService")
public class UnsolicitResponseServiceImpl  implements UnsolicitResponseService{

    private static final Logger LOGGER = Logger.getLogger(UnsolicitResponseServiceImpl.class);

    @Autowired
    private WebServiceTemplate webServiceTemplate;


    @Override
    public void sendUnsolicitResponse() {

        LOGGER.debug("Calling Duke Web Service to Send Unsolicit Response ... ");
        try{
            ObjectFactory objecFactory = new ObjectFactory();       

            CreditStatusMsgType creditStatusMessage = objecFactory.createCreditStatusMsgType(); 
            creditStatusMessage.setMessageHeader(createMessageHeader(objecFactory));

            //WRAP THE CLASS AS THE INSTANCE OF JAXBELEMENT OTHERWISE IT WILL THROW MISSING ROOTELEMENT ERROR
            JAXBElement<CreditStatusMsgType> creditStatusMessageJaxbElement = objecFactory.createSendCreditStatus(creditStatusMessage);

            //CREATE STRING WRITER TO LOG THE REQUEST           
            Object response = this.webServiceTemplate.marshalSendAndReceive(creditStatusMessageJaxbElement);

            LOGGER.debug("Jumio Web Service Response Reponse :"+response);

            LOGGER.debug("Unsolicit Response sent to Duke Successfully.");
        }catch(Exception ex){
            LOGGER.error("Exception generated while calling Web Service  to send unsolicit Response : "+ex.getLocalizedMessage(),ex);
        }       

    }

Below is the xml configuration where I have declared interceptors to log the request and response :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:sws="http://www.springframework.org/schema/web-services"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:oxm="http://www.springframework.org/schema/oxm" 
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/web-services 
        http://www.springframework.org/schema/web-services/web-services-2.0.xsd  
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd  http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm.xsd  http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">


    <!-- DEFINE SOAP VERSION USED BY A WSDL -->
    <bean id="soapMessageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"> 
        <property name="soapVersion"> 

            <!-- FOR TEXT/XML -->   
            <util:constant static-field="org.springframework.ws.soap.SoapVersion.SOAP_11"/>          
        </property> 
    </bean>

    <!-- LOCATION OF THE GENERATED JAVA FILEs -->    
    <oxm:jaxb2-marshaller id="marshaller" contextPath="com.equifax.unsolicited.wsdl.stub"/>


    <!-- CONFIGURE THE SPRING WEB SERVICE -->
    <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate"> 
        <constructor-arg ref="soapMessageFactory"/> <property name="marshaller" ref="marshaller"/> 
        <property name="unmarshaller" ref="marshaller"/> 
        <property name="defaultUri"   value="https://partnerstg.duke-energy.com:4443/DukCISSendCreditStatus?wsdl"/> 
    </bean>


    <sws:interceptors>

        <bean id="jumioPeyLoadLoggingInterceptor" class="com.test.datasource.logging.interceptor.PayloadLoggingInterceptor">
        </bean>

        <bean id="jumioSOAPLoggingInterceptor" class="com.test.datasource.logging.interceptor.SOAPLoggingInterceptor">
        </bean>
    </sws:interceptors>

</beans>

And I also have added and new logging category to enable the logger level to DEBUG mode :

Above code is calling the web service successfully. But the interceptors are not getting called. So I am not able to log the XML request and response.

Here, I am assuming that, these interceptors will not work while consuming the service. Let me know if I am wrong here.

I am referring Spring Web Service from HERE . This website has given explanation interceptors while publishing a web-service.

Kindly let me know should we use this interceptors while consuming a web-service ? Or How should I print request and response which are JAXB-ELEMENT ?

回答1:

I am adding here solution which I have implemented. There are two ways by which we can implement this solution. I have implemented the second one from below list using JAXBContext and Marshaller.

1> Log Request/Response By interceptor.

We can not use PayloadLoggingInterceptor or SOAPLoggingInterceptor when we are consuming the web service.

We need to use ClientInterceptor when we are consuming the web service. ClientInterceptor is implemented by PayloadValidatingInterceptor class which is used to intercept the request/response and validate the it based on xsd schema.

For that we need to provide interceptor reference as below :

<bean id="MyPayloadValidatingInterceptor" class="com.equifax.ic.datasource.jumio.ws.logging.interceptor.JumioPayloadValidatingInterceptor">
        <property name="schema" value="file:WebContent/WEB-INF/schemas/account-balance-service.xsd" />
        <property name="validateRequest" value="false" />
        <property name="validateResponse" value="false" />
    </bean>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate"> 
        <constructor-arg ref="soapMessageFactory"/> <property name="marshaller" ref="marshaller"/> 
        <property name="unmarshaller" ref="marshaller"/> 
        <property name="defaultUri"   value="https://partnerstg.duke-energy.com:4443/DukCISSendCreditStatus?wsdl"/>     
        <property name="interceptors">
            <list>              
                <ref bean="MyPayloadValidatingInterceptor"/>                
            </list>         
        </property>     

    </bean>

2> Log Request/Response by using JAXBContext

This is the solution which I have implemented in my application as we should not use PayloadValidatingInterceptor only to log reqeust/response.

private void logJAXBRequest(JAXBElement<CreditStatusMsgType> creditStatusMessageJaxbElement){
        LOGGER.debug("Logging Web Service Request ...");

        StringWriter writer = null;
        StreamResult streamResult = null;
        StringBuffer buffer = null;
        try{
             writer = new StringWriter();
             streamResult = new StreamResult(writer);

             JAXBContext jaxbContext = JAXBContext.newInstance(CreditStatusMsgType.class);
             Marshaller marshaller = jaxbContext.createMarshaller();
             marshaller.marshal(creditStatusMessageJaxbElement, streamResult);

             buffer = writer.getBuffer();

             LOGGER.debug("JAXB Webservice Request : "+ buffer.toString());

             writer.close();

        }catch(Exception ex){
            LOGGER.error("Exception generated while creating XML Logs of JAXB Request :",ex);
        }
    }


回答2:

He everyone, colleagues. There are two main ways to show XML requests / responses:

  1. First of all you have to add log4j dependency into your pom.xml file:

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  2. Then you have to place log4j.properties file into a classpath of your application. When I develop SOAP services I often use Spring WS Maven artefact. Unfortunately, a usual resources folder is not created from scratch and you have to create it manually. Then you place log4j.properties file there. The contents of log4j config depends on the approach you want to use (see items below). Obtained structure is following:

  3. Use a standard Message Logging and Tracing approach & log4j.properties file. Nothing should be configured, developed, written except log4j config file contents. The contents of log4j config should be the following (use these contents as is):

    log4j.rootCategory=DEBUG, stdout
    log4j.logger.org.springframework.ws.client.MessageTracing.sent=TRACE
    log4j.logger.org.springframework.ws.client.MessageTracing.received=DEBUG
    
    log4j.logger.org.springframework.ws.server.MessageTracing=DEBUG
    
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%p [%c{3}] %m%n
    
  4. Use a PayloadLoggingInterceptor & log4j.properties file. Some config changes should be applied, but this approach is more flexible, as for me. First of all you have to add PayloadLoggingInterceptor into a MessageDispatcherServlet config file:

        <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xmlns:context="http://www.springframework.org/schema/context"      xmlns:sws="http://www.springframework.org/schema/web-services"        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd        http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
        <context:component-scan base-package="com.ln.springws"/>
    
        <sws:annotation-driven/>
    
        <sws:interceptors>
            <bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor" />
        </sws:interceptors>
    
        <sws:dynamic-wsdl id="holiday" portTypeName="HumanResource"        locationUri="http://localhost:8080/holidayService/"        targetNamespace="http://spring-ws-holidays.com/hr/definitions">
            <sws:xsd location="/WEB-INF/hr.xsd"/>
        </sws:dynamic-wsdl>
        </beans>
    

    And at last place the following contents to log4j.properties file:

    log4j.rootLogger=debug, stdout
    
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%-5p [%c] - <%m>%n
    
  5. And as a result of both approaches you will have something like that in a console: