Announcement Announcement Module
Collapse
No announcement yet.
ws:inbound-gateway & error-channel attribute bug Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • 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
    Yes. Even I am in the same boat. I am using regular bean defintion to define the error channel for WS Inbound gateway.

    Comment


    • #3
      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

      Comment


      • #4
        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

        Comment


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

          Comment


          • #6
            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

            Comment


            • #7
              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,

              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

              Comment


              • #8
                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

                Comment


                • #9
                  Thank you...

                  Comment

                  Working...
                  X