Announcement Announcement Module
Collapse
No announcement yet.
Gateway replyChannel and unexpected side effects Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Gateway replyChannel and unexpected side effects

    Hi guys,
    I've run into some unexpectd side effects when adding a named replyChannel to a gateway. The error I was recieiving is:
    "ChannelResolutionException: no output-channel or replyChannel header available".

    This occurs when a message is sent directly to the requestChannel of the gateway without being sent through the gateway itself. See config below.

    Code:
    public interface ContactGateway {
    	Contact saveContact(Contact contact);
    }
    Code:
    <int:gateway id="contactGateway" service-interface="x.y.z.ContactGateway">
      <int:method name="saveContact" 
         request-channel="contactPersistenceChannel" 
         reply-channel="updatedContactChannel" />
    </int:gateway>
    
    <int:channel id="contactPersistenceChannel" />
    
    <int:service-activator input-channel="contactPersistenceChannel" 
      output-channel="updatedContactChannel" 
      ref="contactPersistenceActivator" 
      method="saveContact" />
    
    <int:publish-subscribe-channel id="updatedContactChannel" />
    From what i can gather this is caused by the messages being bridged from the updateContactChannel (pub-sub) to the gateway's internal anonymous reply channel. i.e. "The BridgeHandler can be used as a stopper at the end of an assembly line of channels. In this setup the output channel doesn't have to be set, but if the output channel is omitted the REPLY_CHANNEL MUST be set on the message. Otherwise, a MessagingException will be thrown at runtime."

    My question is, how do I use the gateway with a named reply channel and maintain the same flow for other upstream processes that are feeding directly to the gateway's requestChannel?

    Any suggestions would be appreciated.

    Cheers,

    Jason
    Last edited by JasonSheedy; Jul 22nd, 2013, 12:59 AM.

  • #2
    work around using header enricher

    The following work around fixes the problem, but it's far from an elegant solution. Any other suggestions?

    Code:
    <int:chain input-channel="contactPersistenceChannel" output-channel="updatedContactChannel">
      <int:header-enricher>
    	 <int:header name="replyChannel" ref="nullChannel" overwrite="false"/>
      </int:header-enricher>
      <int:service-activator ref="contactPersistenceActivator" method="saveContact"/>
    </int:chain>

    Comment


    • #3
      Hi!

      Just to clarify. The first Spring Integration class citizen is Channel. In general it doesn't make sense who sends the Message to the channel and, from other side, it doesn't make sense who gets the Message from it too. . And that's why 'Messaging aqrchtecture' and 'loosely coupled' are synonyms.
      I suggest you start from theory of 'pipes and filters': http://books.google.com.ua/books/abo...sC&redir_esc=y
      The channel is place to exchange messages between endpoints.

      And now regarding your case.
      You have a gateway who send the Message to the channel 'contactPersistenceChannel'. It waits the reply in some internal TemporaryReplyChannel, which is placed in the header before sending request.
      If you declare 'reply-channel' on the gateway you should send message to that channel (but it isn't require). On the background your reply will be correlated to the TemporaryReplyChannel in the header via <bridge>. So you last config is correct, but there is no reason to declare <header-enricher> to coerce 'replyChannel' header to some specific channel, e.g. 'nullChannel'.
      If you want to have several points to start message flow on the 'contactPersistenceChannel' (<gateway>, <inbound-channel-adapter>, direct message sending etc.) you should correctly configure downstream flow.
      Albeit... Your last config is correct. It make sense to 'suffocate' the flow in the 'nullChannel', if you aren't interested in reply.

      HTH,
      Artem

      Comment


      • #4
        Hi Artem,
        Thanks for your reply. I'm am familiar with EIP am Spring certified ...

        Originally posted by Cleric View Post
        In general it doesn't make sense who sends the Message to the channel and, from other side, it doesn't make sense who gets the Message from it too. . And that's why 'Messaging aqrchtecture' and 'loosely coupled' are synonyms.
        I guess my point is that this should be handled transparently on the internal bridge linking the named replyChannel to the TemporaryReplyChannel. My upstream message flows are being broken by the dynamically created downstream Bridge. This means all upstream flows are tighly coupled to the downstream message flow.

        My suggestion is that rather than throwing an exception, the BridgeHandler should automatically ignore any messages without a replyChannel header as messages sent to the pub-sub end point do not necessarily require a replyChannel.

        So you last config is correct, but there is no reason to declare <header-enricher> to coerce 'replyChannel' header to some specific channel, e.g. 'nullChannel'.
        If you want to have several points to start message flow on the 'contactPersistenceChannel' (<gateway>, <inbound-channel-adapter>, direct message sending etc.) you should correctly configure downstream flow.
        As this flow ends in a pub-sub channel, I can't see how this is NOT correctly configured. Anyone that's interested in the messages on the pub-sub end point can subscribe regardless of the upstream process. The downstream (from the pub-sub) bridge being dynamically created by the gateway should transparently filter any message recieved from the pub-sub channel that do not meet it's expectations.

        Comment


        • #5
          In my experience, it is unusual to overload the same flow whereby sometimes you want a reply and sometimes you don't, depending on where the request initiated (gateway or some other component sending to 'contactPersistenceChannel'). I don't recall ever seeing such a question - most users that have a flow, that returns a reply, always invoke it from a component that expects a reply.

          the gateway should transparently filter any message recieved from the pub-sub channel that do not meet it's expectations
          I believe such a change would cause untold problems for other users where they inadvertently dropped the reply channel in their downstream flow - trust me - that's one of the most frequent question on this forum (missing replyChannel header).

          In fact, we recently made a change whereby we now emit a WARN log if a reply arrives too late; previously, the framework silently dropped late replies and that generated quite a number of questions from users.

          I am sure, for your use case, you wouldn't want a WARN log for each of these events.

          Given this situation, your solution to set the replyChannel (if not already present) to nullChannel is a reasonable one. Although I'd probably make it a little more self-documenting by adding overwrite="false" (even though it's the default).

          Another solution would be to add a <filter/> before the gateway's reply-channel with an expression="headers['replyChannel'] != null".

          Comment


          • #6
            HI Gary,
            Thanks for clarifying. I was really looking for the recomended approach to take for my use case.

            Originally posted by Gary Russell View Post
            In my experience, it is unusual to overload the same flow whereby sometimes you want a reply and sometimes you don't, depending on where the request initiated (gateway or some other component sending to 'contactPersistenceChannel'). I don't recall ever seeing such a question - most users that have a flow, that returns a reply, always invoke it from a component that expects a reply.
            It might help if I provide a bit of background on this. The flow ends in a pub-sub channel (updatedContactChannel) that allows any interested area of the application to be notified when contacts are updated. Contact's could be sent to the contactPersistenceChannel from several other up stream flows with different contracts. The intention was that by using the updatedContactChannel as a general purpose 'sub-flow', we can consume, then re-route, filter, transform the Contacts in any number of parallel down stream flows. Similar to a fan out exchange. Is this a bad idea?

            I believe such a change would cause untold problems for other users where they inadvertently dropped the reply channel in their downstream flow - trust me - that's one of the most frequent question on this forum (missing replyChannel header).
            Wouldn't want to break things .. breaking things is baaadd. This problem started when I added a replyChannel / return value to the ContactGateway interface. My error was not considering other messages that could be published to the dynamiclly created gateway bridge.

            Given this situation, your solution to set the replyChannel (if not already present) to nullChannel is a reasonable one. Although I'd probably make it a little more self-documenting by adding overwrite="false" (even though it's the default).

            Another solution would be to add a <filter/> before the gateway's reply-channel with an expression="headers['replyChannel'] != null".
            With that in mind, the only other option I see is to create custom flows for each upstream flow that wants to do contact persistence and forget about re-using this contact persistence flow. In our case the flow is more complex than this example. What do you think?

            Comment


            • #7
              Hello!

              Although it is out off topic but may help you:
              In my complex message flows I have a practice to modulate sub-flows and follow with DRY. For this purpose I use plain gateways, e.g.:
              HTML Code:
              <chain input-channel="input" output-channel="output">
                  <gateway request-channel="subFlowChannel"/>
              <chain>
              So that sub-flow may be available from any upstream flow.

              In your case you should understand, that gateway starts one type of flow, but if you send message to the channel directly you'll have another type. TemporaryReplyChannel is a part of gateway logic and it will work, if you don't declare 'output-channel' on the last component of your flow. The same is applied for 'reply-channel' from gateway config - on the background bridge just send message to the 'reply-channel' from header: https://github.com/SpringSource/spri...dler.java#L202.
              Of course, if you send message to the 'request-channel' directly, there is no any correlation mechanism and it doesn't make sense here: your gateway won't get a reply from this flow. And it is correct.

              IMO I don't see any issues with Framework - it is your misunderstanding on the matter

              Comment


              • #8
                IMO I don't see any issues with Framework - it is your misunderstanding on the matter
                Thanks for helping me through it. I see the benefit of encapsulating the gateway flow as you've suggested. Time to do a little re-factoring.

                Cheers

                Comment


                • #9
                  Hi guys,
                  In follow up to my refactoring efforts, I've posted a new thread here.

                  http://forum.springsource.org/showth...thin-timeout-1

                  Any help on this would be appreciated.

                  Cheers

                  Comment

                  Working...
                  X