Results 1 to 9 of 9

Thread: ws:inbound-gateway & error-channel attribute bug

  1. #1
    Join Date
    Jan 2009
    Location
    Ukraine, Kharkov
    Posts
    645

    Default ws:inbound-gateway & error-channel attribute bug

    Hello
    I implemented a Contract First Web-Service application based on Spring-WS & Spring-Integration. My customer doesn't wish to receive SOAP-Foults. And I must build the valid response with error description in specific tag.
    I know Spring-Integration has got a error-handling pattern with channel which can have error handling subscribers.
    So, I read the source of SimpleWebServiceInboundGateway class. By inheritance he has got a property
    Code:
    private volatile MessageChannel errorChannel;
    But the configuration by Namespace of it doesn't allow to add an attribute error-channel="errorChannel" to ws:inbound-gateway description.
    So, I continue to read sources. And the WebServiceInboundGatewayParser class by inheritence has ability to parse that attribute:
    Code:
    String errorChannel = element.getAttribute("error-channel");
    But the spring-integration-ws-2.0.xsd hasn't got a description of attribute error-channel in complex type of inbound-gateway.
    I think it is a bug and must be fixed.
    For example: the spring-integration-jms-2.0.xsd has got description of
    HTML Code:
    <xsd:attribute name="error-channel" type="xsd:string">
    in complex type of inbound-gateway.
    But I solve the problem with workaround:
    HTML Code:
    <beans:bean id="ws-inbound-gateway" class="org.springframework.integration.ws.SimpleWebServiceInboundGateway"
                    p:requestChannel-ref="input"
                    p:extractPayload="true"
                    p:errorChannel-ref="errorChannel"/>
    Thank you
    and Happy New Year and Marry Christmas

  2. #2
    Join Date
    Jun 2006
    Location
    Smithfield, RI
    Posts
    51

    Default

    Yes. Even I am in the same boat. I am using regular bean defintion to define the error channel for WS Inbound gateway.

  3. #3
    Join Date
    Oct 2005
    Location
    Boston, MA
    Posts
    2,844

    Default

    We had originally decided to leave out that 'error-channel' option for the WS outbound gateway since there is already an Exception-handling mechanism as part of the protocol (i.e. using Faults). However, if I understand correctly, these 2 use-cases are specifically avoiding the use of Faults - so that *any* response to the caller would appear "successful". Presumably, there is still some type of message format that reveals an error had occurred on the server side. Could you please provide a bit more detail about the motivation for that approach?

    Thanks,
    Mark

  4. #4
    Join Date
    Jan 2009
    Location
    Ukraine, Kharkov
    Posts
    645

    Default

    Hello.
    Thank you, Mark, for your reply.
    I'm giving more details about my service as you ask:
    1. I repeat: the contract (XSD) for my service was provided from my customer. So I've got the contract-first architecture.
    2. In all response SOAP-messages there is a block in body:
    HTML Code:
     <complexType name="ErrorInformationType">
            <sequence>
                <element name="errorCode" type="string" nillable="true"/>
                <element name="errorDescription" type="string"/>
                <element name="errorSeverity" type="tns:ErrorSeverityType"/>
                <element name="errorCategory" type="tns:ErrorCategoryType"/>
            </sequence>
        </complexType>
    3. So, my customer doesn't want to receive exceptions from me - he wants have some error code and user-friendly description of that. I think, he shows this info in his web-client and covers a message with css by errorSeverity and errorCategory
    4. The biggest part of my exceptions occur in DB layer by calling of stored procedures. So, I must parse the SQLException and take a bite of error code and error-message. At last I must build the regular SOAP-response and add to that the ErrorInformation tag as described previously

    I know about exception-handling with SOAP-Foult and know about EndpointExceptionResolver in Srping-WS project. And at first I try to implement required solution with CustomEndpointExceptionResolver. But in request and response messages in messageContext are AxiomSoapMessage type and it is difficult to parse them and build messages with axiom API. And I found this solution not smart.

    By using the solution with error-channel I implement the ErrorUnwrapper Transformer:
    Code:
    public class ErrorUnwrapper extends AbstractTransformer {
        @Override
        protected Object doTransform(Message<?> errorMessage) throws Exception {
            MessagingException exception = (MessagingException) errorMessage.getPayload();
            Message<?> failedMessage = exception.getFailedMessage();
            Response response = (Response) failedMessage.getHeaders().get("response");
            response.getHeaderData().setErrorInformation(buildErrorInformation(exception));
            return MessageBuilder.withPayload(response).copyHeaders(failedMessage.getHeaders())
                    .setReplyChannel((MessageChannel) errorMessage.getHeaders().getReplyChannel())
                    .build();
        }
    
        private ErrorInformationType buildErrorInformation(MessagingException exception) {
            ErrorInformationType errorInformation = new ErrorInformationType();
            SQLException sqlException = findSQLException(exception);
            if (sqlException != null) {
                errorInformation.setErrorDescription(sqlException.getMessage());
                errorInformation.setErrorCode("" + sqlException.getErrorCode());
                errorInformation.setErrorCategory(ErrorCategory.BUSI);
                errorInformation.setErrorSeverity(ErrorSeverity.E);
            } else {
                errorInformation.setErrorDescription(ExceptionUtils.getFullStackTrace(exception));
                errorInformation.setErrorCode("-1");
                errorInformation.setErrorCategory(ErrorCategory.TECH);
                errorInformation.setErrorSeverity(ErrorSeverity.E);
            }
    
            return errorInformation;
        }
    
        private SQLException findSQLException(Exception e) {
            List<Exception> causes = new ArrayList<Exception>();
            for (Exception cause = e; cause != null; cause = getCause(cause)) {
                causes.add(cause);
            }
            for (int i = causes.size() - 1; i >= 0; i--) {
                Exception cause = causes.get(i);
                if (cause instanceof SQLException) {
                    return (SQLException) cause;
                }
            }
            return null;
        }
    
        private Exception getCause(Exception exception) {
            Throwable cause = exception.getCause();
            return cause == exception || !(cause instanceof Exception) ? null : (Exception) cause;
        }
    }
    So, I think, it is more flexible solution to work with simple objects than parse XML with specific APIs.

    Thank you

  5. #5
    Join Date
    Oct 2005
    Location
    Boston, MA
    Posts
    2,844

    Default

    Thanks for providing so much information about your use-case. Could you please open an issue in JIRA for this?

  6. #6
    Join Date
    Jan 2009
    Location
    Ukraine, Kharkov
    Posts
    645

    Default

    Thank you, Mark for your replies.
    Here is an issue in JIRA with attachment
    https://jira.springframework.org/browse/INT-1729.

    And also i forgot abount one functionality in my service.
    It is a loggin subsystem. I must log all incoming & outcoming XML-messages as one request-response entry with specific info from header in request body into additional table columns.
    For this solution I use wire-tap interceptor before providing the SI-message to SOAP-message by default reply from ws-inbound-gateway
    So, it is some more plus to use error-handling by SI and to define beans by specific namespaces

    Thank you.

    Artem Bilan

  7. #7

    Default

    Thanks for sharing the info, Will it be possible for you to share the wiring in config file as i am also trying to build something similar. Also what is the type of your Response object below is it javax.xml.ws.Response?

    Thank you,

    Quote Originally Posted by Cleric View Post
    Hello.
    Thank you, Mark, for your reply.
    I'm giving more details about my service as you ask:
    1. I repeat: the contract (XSD) for my service was provided from my customer. So I've got the contract-first architecture.
    2. In all response SOAP-messages there is a block in body:
    HTML Code:
     <complexType name="ErrorInformationType">
            <sequence>
                <element name="errorCode" type="string" nillable="true"/>
                <element name="errorDescription" type="string"/>
                <element name="errorSeverity" type="tns:ErrorSeverityType"/>
                <element name="errorCategory" type="tns:ErrorCategoryType"/>
            </sequence>
        </complexType>
    3. So, my customer doesn't want to receive exceptions from me - he wants have some error code and user-friendly description of that. I think, he shows this info in his web-client and covers a message with css by errorSeverity and errorCategory
    4. The biggest part of my exceptions occur in DB layer by calling of stored procedures. So, I must parse the SQLException and take a bite of error code and error-message. At last I must build the regular SOAP-response and add to that the ErrorInformation tag as described previously

    I know about exception-handling with SOAP-Foult and know about EndpointExceptionResolver in Srping-WS project. And at first I try to implement required solution with CustomEndpointExceptionResolver. But in request and response messages in messageContext are AxiomSoapMessage type and it is difficult to parse them and build messages with axiom API. And I found this solution not smart.

    By using the solution with error-channel I implement the ErrorUnwrapper Transformer:
    Code:
    public class ErrorUnwrapper extends AbstractTransformer {
        @Override
        protected Object doTransform(Message<?> errorMessage) throws Exception {
            MessagingException exception = (MessagingException) errorMessage.getPayload();
            Message<?> failedMessage = exception.getFailedMessage();
            Response response = (Response) failedMessage.getHeaders().get("response");
            response.getHeaderData().setErrorInformation(buildErrorInformation(exception));
            return MessageBuilder.withPayload(response).copyHeaders(failedMessage.getHeaders())
                    .setReplyChannel((MessageChannel) errorMessage.getHeaders().getReplyChannel())
                    .build();
        }
    
        private ErrorInformationType buildErrorInformation(MessagingException exception) {
            ErrorInformationType errorInformation = new ErrorInformationType();
            SQLException sqlException = findSQLException(exception);
            if (sqlException != null) {
                errorInformation.setErrorDescription(sqlException.getMessage());
                errorInformation.setErrorCode("" + sqlException.getErrorCode());
                errorInformation.setErrorCategory(ErrorCategory.BUSI);
                errorInformation.setErrorSeverity(ErrorSeverity.E);
            } else {
                errorInformation.setErrorDescription(ExceptionUtils.getFullStackTrace(exception));
                errorInformation.setErrorCode("-1");
                errorInformation.setErrorCategory(ErrorCategory.TECH);
                errorInformation.setErrorSeverity(ErrorSeverity.E);
            }
    
            return errorInformation;
        }
    
        private SQLException findSQLException(Exception e) {
            List<Exception> causes = new ArrayList<Exception>();
            for (Exception cause = e; cause != null; cause = getCause(cause)) {
                causes.add(cause);
            }
            for (int i = causes.size() - 1; i >= 0; i--) {
                Exception cause = causes.get(i);
                if (cause instanceof SQLException) {
                    return (SQLException) cause;
                }
            }
            return null;
        }
    
        private Exception getCause(Exception exception) {
            Throwable cause = exception.getCause();
            return cause == exception || !(cause instanceof Exception) ? null : (Exception) cause;
        }
    }
    So, I think, it is more flexible solution to work with simple objects than parse XML with specific APIs.

    Thank you

  8. #8
    Join Date
    Jan 2009
    Location
    Ukraine, Kharkov
    Posts
    645

    Default

    Hi!

    Sorry for delay.
    what is the type of your Response object below
    It's just a JavaBean with JAXB annotations, generated from XSD:
    Code:
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "response", propOrder = {
            "headerData",
            "transactionData"
    })
    @XmlRootElement(name = "response")
    public class Response {
    
        @XmlElement(required = true)
        protected HeaderDataType headerData;
    
        protected Object transactionData;
    
        @XmlAttribute(required = true)
        protected String responseElement;
    ...
    And here is a snapshot from my config:
    HTML Code:
    <bean class="org.springframework.ws.server.endpoint.mapping.UriEndpointMapping"
    		  p:defaultEndpoint-ref="ws-inbound-gateway"/>
    
    <ws:inbound-gateway id="ws-inbound-gateway" request-channel="input" extract-payload="true"
    						error-channel="errorChannel"/>
    
    <chain input-channel="input">
    	<transformer ref="xmlTransformer" method="toDOMSource"/>
    
    	<si-xml:xpath-header-enricher>
    	        <si-xml:header name="requestOperation" xpath-expression="local-name(/*)"/>
    		<si-xml:header name="headerData" xpath-expression="//headerData" evaluation-type="NODE_RESULT"/>
    	</si-xml:xpath-header-enricher>
    
    	<service-activator ref="prepareRequestAction"/>
    		
    	<si-xml:xslt-transformer xsl-resource="classpath:transactionDataTransform.xsl"
    								 result-transformer="resultToStringTransformer"
    								 xslt-param-headers="requestOperation"/>
    	<!--Aafter XSL-transform on empty transactionData it returns only "xml-declaration"-->
    	<transformer expression="!payload.endsWith('?>') ? @marshaller.unmarshal(new org.springframework.xml.transform.StringSource(payload)) : ''"/>
    	<header-value-router header-name="requestOperation"/>
    </chain>
    
    <chain input-channel="transformResponse">
    	<si-xml:marshalling-transformer marshaller="marshaller" result-transformer="resultToStringTransformer"/>
    	<si-xml:xslt-transformer xsl-resource="classpath:responseTransform.xsl"/>
    </chain>
    
    <transformer input-channel="errorChannel" output-channel="transformResponse" ref="errorUnwrapper"/>
    prepareRequestAction has this code (Groovy):
    Code:
     @ServiceActivator
        Object execute(Message<?> message) {
    
            def httpServletRequest = RequestContextHolder.requestAttributes.request
    
            def headers = new HashMap<String, Object>(message.headers)
            def payload = message.payload
    
            headers.actionName = httpServletRequest.getHeader('SOAPAction')
            headers.requestUrl = httpServletRequest.requestURL.toString()
            headers.requestXml = xmlTransformer.sourceToString(payload)
            headers.headerData = marshaller.unmarshal(new DOMSource(headers.headerData))
            headers.b2gResponse = new com.my.proj.model.Response(
                    headerData: headers.headerData,
                    responseElement: headers.requestOperation - 'Request' + 'Response'
            )
    
            return MessageBuilder.withPayload(payload).copyHeaders(headers).build()
        }
    As you see in most cases Spring Integration does the work for me .

    Take care,
    Artem

  9. #9

    Default

    Thank you...

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •