简体   繁体   中英

Spring ws SOAP endpoint responds with missing fault details

I have soap-to-soap proxy server in spring using org.springframework.ws.* Both sides have identical wsdls.

I am able to pass the success response from external service to proxy consumer however there's a problem when fault message from external service gets returned.

The problem is my proxy server removes soap detail from the original response (I have no idea why). My goal is to pass the response from external service just as it is to proxy client without shortening. Anyone could help how to avoid fault detail being deleted? Thank you in advance.

External Server response:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
    <soap:Fault>
        <faultcode>soap:Server</faultcode>
        <faultstring>Fault occurred while processing.</faultstring>
        <detail>
            <ns2:getCFUSubscriberStateFaultBusiness xmlns="example.system.messaging.common.v1.datatypes" xmlns:ns2="example.system.ot.managepcccfu.v2.datatypes">
                <ns2:messageContext>
                    <requestId>273140800423344000</requestId>
                    <transactionId>8200</transactionId>
                    <timeLeft>10000</timeLeft>
                    <timestamp>2022-09-30T14:08:00</timestamp>
                    <user>x_turan</user>
                    <consumingComponent>urn:example.business.intr:SELFCARE3.</consumingComponent>
                    <providingService>urn:example.system.ot.managepccprovisioning.v1.service:ManagePccProvisioning</providingService>
                    <providingOperation>modifycontroffer</providingOperation>
                </ns2:messageContext>
                <ns2:messageDataBusinessException>
                    <errorCode>3001</errorCode>
                    <errorMessage>ESE Problem</errorMessage>
                </ns2:messageDataBusinessException>
            </ns2:getCFUSubscriberStateFaultBusiness>
        </detail>
    </soap:Fault>
</soap:Body>
</soap:Envelope>

Proxy Client Receives:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <SOAP-ENV:Fault>
         <faultcode>SOAP-ENV:Server</faultcode>
         <faultstring xml:lang="en">Fault occurred while processing.</faultstring>
      </SOAP-ENV:Fault>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Endpoint:

@Endpoint
public class ServiceEndpoint {
    public static final String NAMESPACE_URI="example.system.ot.managepcccfu.v2.datatypes";

    @Autowired
    CFUSoapClient soapClient;

    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCFUSubscriberState" )
    @ResponsePayload
    public GetCFUSubscriberStateResponse getCFUSubscriberState(@RequestPayload GetCFUSubscriberState request) throws GetCFUSubscriberStateFaultBusinessMessage, GetCFUSubscriberStateFaultSystemMessage {
        final GetCFUSubscriberStateResponse response = soapClient.getCFUSubscriberStateResponse(request);
        return response;
    }
}

Soap Client:

public class CFUSoapClient extends WebServiceGatewaySupport {
    public GetCFUSubscriberStateResponse getCFUSubscriberStateResponse(GetCFUSubscriberState request) throws GetCFUSubscriberStateFaultBusinessMessage {
        Object response = getWebServiceTemplate().marshalSendAndReceive(request);
        return (GetCFUSubscriberStateResponse) response;
    }
}

Config:

@Configuration
@EnableWs
public class Config extends WsConfigurerAdapter {
    @Bean
    public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
        MessageDispatcherServlet servlet = new MessageDispatcherServlet();
        servlet.setApplicationContext(applicationContext);
        return new ServletRegistrationBean(servlet, "/ws/CFU/*");
    }

    @Bean(name="CFU")
    public Wsdl11Definition defaultWsdl11Definition() {
        SimpleWsdl11Definition wsdl11Definition = new SimpleWsdl11Definition();
        wsdl11Definition.setWsdl(new ClassPathResource("/wsdl/CFU.wsdl"));
        return wsdl11Definition;
    }

    @Bean(name = "cfuDatatypesV2")
    public XsdSchema cfuDatatypesV2() {
        return new SimpleXsdSchema(
                new ClassPathResource("wsdl/cfuDatatypesV2.xsd"));
    }

    @Bean(name = "common")
    public XsdSchema common() {
        return new SimpleXsdSchema(
                new ClassPathResource("wsdl/common.xsd"));
    }

    @Bean
    public Jaxb2Marshaller marshaller() {
        Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
        jaxb2Marshaller.setContextPath("com.ot.cfu");
        return jaxb2Marshaller;
    }
    @Bean
    public CFUSoapClient soapClient() {
        CFUSoapClient client = new CFUSoapClient();
        client.setDefaultUri("http://localhost:41420/CFU");
        client.setMarshaller(marshaller());
        client.setUnmarshaller(marshaller());
//        ClientInterceptor [] interceptors = new ClientInterceptor[]{new SoapClientInterceptor()};
//        client.setInterceptors(interceptors);
        return client;
    }
}

After hours I managed to workaround this issue following wsdl definition and implemented custom exception classes wrapping the wsdl generated fault details:

@WebFault(name = "getCFUSubscriberStateFaultBusiness", targetNamespace = "example.system.ot.managepcccfu.v2.datatypes")
public class GetCFUSubscriberStateFaultBusinessMessage extends Exception {

    private GetCFUSubscriberStateFaultBusiness getCFUSubscriberStateFaultBusiness;

    public GetCFUSubscriberStateFaultBusinessMessage() {
        super();
    }

    public GetCFUSubscriberStateFaultBusinessMessage(String message) {
        super(message);
    }

    public GetCFUSubscriberStateFaultBusinessMessage(String message, java.lang.Throwable cause) {
        super(message, cause);
    }

    public GetCFUSubscriberStateFaultBusinessMessage(String message, GetCFUSubscriberStateFaultBusiness getCFUSubscriberStateFaultBusiness) {
        super(message);
        this.getCFUSubscriberStateFaultBusiness = getCFUSubscriberStateFaultBusiness;
    }

    public GetCFUSubscriberStateFaultBusinessMessage(String message, GetCFUSubscriberStateFaultBusiness getCFUSubscriberStateFaultBusiness, java.lang.Throwable cause) {
        super(message, cause);
        this.getCFUSubscriberStateFaultBusiness = getCFUSubscriberStateFaultBusiness;
    }

    public GetCFUSubscriberStateFaultBusiness getFaultInfo() {
        return this.getCFUSubscriberStateFaultBusiness;
    }
}

@WebFault(name = "getCFUSubscriberStateFaultSystem", targetNamespace = "example.system.ot.managepcccfu.v2.datatypes")
public class GetCFUSubscriberStateFaultSystemMessage extends Exception {

    private GetCFUSubscriberStateFaultSystem getCFUSubscriberStateFaultSystem;

    public GetCFUSubscriberStateFaultSystemMessage() {
        super();
    }

    public GetCFUSubscriberStateFaultSystemMessage(String message) {
        super(message);
    }

    public GetCFUSubscriberStateFaultSystemMessage(String message, java.lang.Throwable cause) {
        super(message, cause);
    }

    public GetCFUSubscriberStateFaultSystemMessage(String message, GetCFUSubscriberStateFaultSystem getCFUSubscriberStateFaultSystem) {
        super(message);
        this.getCFUSubscriberStateFaultSystem = getCFUSubscriberStateFaultSystem;
    }

    public GetCFUSubscriberStateFaultSystemMessage(String message, GetCFUSubscriberStateFaultSystem getCFUSubscriberStateFaultSystem, java.lang.Throwable cause) {
        super(message, cause);
        this.getCFUSubscriberStateFaultSystem = getCFUSubscriberStateFaultSystem;
    }

    public GetCFUSubscriberStateFaultSystem getFaultInfo() {
        return this.getCFUSubscriberStateFaultSystem;
    }
} 

I filled the received soap fault detail into exception placeholder upon SoapFaultClientException based on type of detail:

public class CFUSoapClient extends WebServiceGatewaySupport {
        public GetCFUSubscriberStateResponse getCFUSubscriberStateResponse(GetCFUSubscriberState request) throws GetCFUSubscriberStateFaultBusinessMessage, IOException, GetCFUSubscriberStateFaultSystemMessage {
            try {
                return (GetCFUSubscriberStateResponse) getWebServiceTemplate().marshalSendAndReceive(request);
            } catch (SoapFaultClientException e) {
                final Iterator<SoapFaultDetailElement> detailEntries = e.getSoapFault().getFaultDetail().getDetailEntries();
    
                if (detailEntries.hasNext()) {
                    final SoapFaultDetailElement next = detailEntries.next();
                    final Source source = next.getSource();
    
                    final Object faultDetail = getWebServiceTemplate().getUnmarshaller().unmarshal(source);
                    if (faultDetail instanceof GetCFUSubscriberStateFaultBusiness) {
                        throw new GetCFUSubscriberStateFaultBusinessMessage(e.getSoapFault().getFaultStringOrReason(), (GetCFUSubscriberStateFaultBusiness) faultDetail, e.getCause());
                    } else if (faultDetail instanceof GetCFUSubscriberStateFaultSystem) {
                        throw new GetCFUSubscriberStateFaultSystemMessage(e.getSoapFault().getFaultStringOrReason(), (GetCFUSubscriberStateFaultSystem) faultDetail, e.getCause());
                    }
                }
    
                throw new RuntimeException("Unexpected error", e);
            }
        }
    }

Eventually, I marshalled the details in the resolver class:

@Component
public class Resolver extends AbstractEndpointExceptionResolver {
    private final Jaxb2Marshaller marshaller;

    @Autowired
    public Resolver(Jaxb2Marshaller marshaller) {
        this.marshaller = marshaller;
    }

    @Override
    protected boolean resolveExceptionInternal(MessageContext messageContext, Object endpoint, Exception e) {
        if (e instanceof GetCFUSubscriberStateFaultBusinessMessage) {
            final GetCFUSubscriberStateFaultBusinessMessage getCFUSubscriberStateFaultBusinessMessage = (GetCFUSubscriberStateFaultBusinessMessage) e;
            final GetCFUSubscriberStateFaultBusiness faultInfo = getCFUSubscriberStateFaultBusinessMessage.getFaultInfo();
            final Result result = createFaultDetailResult(messageContext, getCFUSubscriberStateFaultBusinessMessage);

            marshaller.marshal(faultInfo, result);
            return true;

        } else if (e instanceof GetCFUSubscriberStateFaultSystemMessage) {
            final GetCFUSubscriberStateFaultSystemMessage getCFUSubscriberStateFaultSystemMessage = (GetCFUSubscriberStateFaultSystemMessage) e;
            final GetCFUSubscriberStateFaultSystem faultInfo = getCFUSubscriberStateFaultSystemMessage.getFaultInfo();
            final Result result = createFaultDetailResult(messageContext, getCFUSubscriberStateFaultSystemMessage);

            marshaller.marshal(faultInfo, result);
            return true;
        }

        return false;
    }

    private Result createFaultDetailResult(MessageContext messageContext, Exception exception) {
        final SoapMessage response = (SoapMessage) messageContext.getResponse();
        final SoapBody soapBody = response.getSoapBody();
        final SoapFault soapFault = soapBody.addServerOrReceiverFault(exception.getMessage(), Locale.ENGLISH);
        final SoapFaultDetail faultDetail = soapFault.addFaultDetail();

        return faultDetail.getResult();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM