Spring Integration
  1. Spring Integration
  2. INT-2808

XPath Router. Conditional routing based on the value of the xpath expression.

    Details

    • Type: Improvement Improvement
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: None
    • Fix Version/s: General Backlog
    • Component/s: XML
    • Labels:
      None

      Description

      As a developer I would like to be able to configure XPath Router to do conditional routing based on the XPath value.

      For example, when having a message

      <passener>
        <age>2</age>
      </passenger>
      

      a router should be able to route the message to one of the configured channels based on the evaluated expression, e.g.

      <xpath-router id="passengerRouter" input-channel="passengerChannel">
          <recipient channel="infantChannel" expression="/passenger/age le 1"/>
          <recipient channel="adultChannel" expression="/passenger/age gt 12"/>
      </xpath-router>
      

        Activity

        Hide
        Oleg Zhurakousky added a comment -

        I would personally rephrase this issue as I believe the requirement is more general and goes beyond the XPath (even though it was originated from the XPath related forum post). The main point of this requirement is to be able to use more complex evaluation of the routing result during its mapping to a channel. Today mapping is simply done based on 'equals' thus eliminating the possibility of mapping result based on let's say its range (e.g., from 1 to 20 go to channel A)

        Show
        Oleg Zhurakousky added a comment - I would personally rephrase this issue as I believe the requirement is more general and goes beyond the XPath (even though it was originated from the XPath related forum post). The main point of this requirement is to be able to use more complex evaluation of the routing result during its mapping to a channel. Today mapping is simply done based on 'equals' thus eliminating the possibility of mapping result based on let's say its range (e.g., from 1 to 20 go to channel A)
        Hide
        Janis Kazakovs added a comment -

        Does make sense to me. Frankly, I do no have prior knowledge of Spring Integration. The above case was the first one I was trying to implement.

        I know that Apache Camel uses the notion of Predicate, which can be integrated into the Message Filter of Content Based Filter for various data format. I do not know the architecture of the Spring Integration, but it makes sense to abstract the notion of predicate, otherwise you end up building XPath, JSON, bean, map and etc. content based routers.

        Also, looking at the example from the post with the recipient-list-router (i.e.

        <int:recipient channel="infant" selector-expression="headers['passengerAge'] le 1" />

        ), it would be more straightforward and intuitive to use the XPath expression in the selector-expression (e.g.

        selector-expression="/passenger/age le 1" />

        ), rather then using content enricher to store a temporary variable. This seems to me unnecessary step or a workaround that a user of a framework should make in order to implement a uses case. Instead it should be supported by framework itself.

        Show
        Janis Kazakovs added a comment - Does make sense to me. Frankly, I do no have prior knowledge of Spring Integration. The above case was the first one I was trying to implement. I know that Apache Camel uses the notion of Predicate, which can be integrated into the Message Filter of Content Based Filter for various data format. I do not know the architecture of the Spring Integration, but it makes sense to abstract the notion of predicate, otherwise you end up building XPath, JSON, bean, map and etc. content based routers. Also, looking at the example from the post with the recipient-list-router (i.e. < int :recipient channel= "infant" selector-expression= "headers['passengerAge'] le 1" /> ), it would be more straightforward and intuitive to use the XPath expression in the selector-expression (e.g. selector-expression= "/passenger/age le 1" /> ), rather then using content enricher to store a temporary variable. This seems to me unnecessary step or a workaround that a user of a framework should make in order to implement a uses case. Instead it should be supported by framework itself.
        Hide
        Mark Fisher added a comment -

        One advantage even in the workaround is that the XPath expression itself is only evaluated ONE time. Ideally we would like to preserve that advantage while also removing the need for exposing any temporary variable to the end-user (as the workaround does unfortunately).

        Also, as you have pointed out in the forum, we should provide a way to do this on the simple <xpath-router> itself, but I do like the idea of also having an XPath version of recipient-list-router where multiple XPath expressions may be evaluated instead-of/in-addition-to any SpEL expressions as done on the "simple" (non-xpath) recipient-list-router.

        Show
        Mark Fisher added a comment - One advantage even in the workaround is that the XPath expression itself is only evaluated ONE time. Ideally we would like to preserve that advantage while also removing the need for exposing any temporary variable to the end-user (as the workaround does unfortunately). Also, as you have pointed out in the forum, we should provide a way to do this on the simple <xpath-router> itself, but I do like the idea of also having an XPath version of recipient-list-router where multiple XPath expressions may be evaluated instead-of/in-addition-to any SpEL expressions as done on the "simple" (non-xpath) recipient-list-router.
        Hide
        Mark Fisher added a comment -

        Just to add... if we go with Oleg's suggestion of adding "expression" (as an alternative to simple "value" matching) on the <mapping> sub-elements, then this type of behavior could also be reused for any router. For the XPath case, it would satisfy the 2 points I made above (not requiring multiple XPath expression evaluations AND not exposing any new variables/headers to the end-user directly). For example:

        <xpath-router input-channel="passengers" expression="/passenger/age">
           <mapping expression="#result le 1" channel="infants"/>
           <mapping expression="#result gt 12" channel="adults"/>
        </xpath-router>
        
        Show
        Mark Fisher added a comment - Just to add... if we go with Oleg's suggestion of adding "expression" (as an alternative to simple "value" matching) on the <mapping> sub-elements, then this type of behavior could also be reused for any router. For the XPath case, it would satisfy the 2 points I made above (not requiring multiple XPath expression evaluations AND not exposing any new variables/headers to the end-user directly). For example: <xpath-router input-channel= "passengers" expression= "/passenger/age" > <mapping expression= "#result le 1" channel= "infants" /> <mapping expression= "#result gt 12" channel= "adults" /> </xpath-router>
        Hide
        Janis Kazakovs added a comment -

        I will try to complicate the case. What if in order to evaluate a bolean-valued function I need to use more then one xpath expression. For example, the age of an infant may vary per country.

        <passenger>
        	<age/>
        	<country/>
        </passenger>
        

        As a result, desired expression could look as follows:

        (/passenger/age le 1 and /passeger/country = NL) or (/passenger/age le 2 and /passeger/country = UK)

        . In this case, having a single xpath expression on xpath-router declaration, the result of which is used later on route mapping, may not suffice.

        Show
        Janis Kazakovs added a comment - I will try to complicate the case. What if in order to evaluate a bolean-valued function I need to use more then one xpath expression. For example, the age of an infant may vary per country. <passenger> <age/> <country/> </passenger> As a result, desired expression could look as follows: (/passenger/age le 1 and /passeger/country = NL) or (/passenger/age le 2 and /passeger/country = UK) . In this case, having a single xpath expression on xpath-router declaration, the result of which is used later on route mapping, may not suffice.
        Hide
        Oleg Zhurakousky added a comment -

        I don't think you are complicating it at all. The only thing that has became more complex is an expression itself which still returns a boolean value.
        With SpEL (Spring Expression Language) if expression were to get to complex you may want to delegate to a bean (e.g., expression="@myBean.process(#result)"), thus making expression itself simple while what it does anything but.

        Show
        Oleg Zhurakousky added a comment - I don't think you are complicating it at all. The only thing that has became more complex is an expression itself which still returns a boolean value. With SpEL (Spring Expression Language) if expression were to get to complex you may want to delegate to a bean (e.g., expression="@myBean.process(#result)"), thus making expression itself simple while what it does anything but.
        Hide
        Artem Bilan added a comment -

        Hi, all!
        +1 to 'expression' attribute as boolean evaluation with '#result' as root element. With mutually exclusive to 'value' attribute. And enhanced with BeanFactoryResolver too...

        Show
        Artem Bilan added a comment - Hi, all! +1 to 'expression' attribute as boolean evaluation with '#result' as root element. With mutually exclusive to 'value' attribute. And enhanced with BeanFactoryResolver too...
        Hide
        Artem Bilan added a comment -

        After introduction #xpath() SpEL function (https://jira.springsource.org/browse/INT-3140) a nested XPath 'Predicate' will be achieved with new expression attribute for router's mapping.

        Show
        Artem Bilan added a comment - After introduction #xpath() SpEL function ( https://jira.springsource.org/browse/INT-3140 ) a nested XPath 'Predicate' will be achieved with new expression attribute for router's mapping .
        Show
        Artem Bilan added a comment - The initial commit: https://github.com/artembilan/spring-integration/commit/400461b1978413978207c179ff26c25e065cca19

          People

          • Assignee:
            Artem Bilan
            Reporter:
            Janis Kazakovs
          • Votes:
            1 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

            • Created:
              Updated: