About Mule, content-based routing and namespaces

Some of our Mule ESB applications make use of content-based routing when processing a message. To implement this in Mule we use a filtering-router with a jaxen-filter. The most simple example looks like this:

1
2
3
4
5
6
7
8
...
<outbound>
  <filtering-router>
    <vm:outbound-endpoint ref="out-queue" />
     <xm:jaxen-filter pattern="//firstname" expectedValue="Pascal"/>
  </filtering-router>
</outbound>
...

You can also use a XPath function in your jaxen-filter like this (if you want to check a certain element is in an XML message regardless the value of the element):

1
2
3
4
5
6
7
8
...
<outbound>
  <filtering-router>
    <vm:outbound-endpoint ref="out-queue" />
    <xm:jaxen-filter pattern="local-name(//Relations)" expectedValue="Relations"/>
  </filtering-router>
</outbound>
...


The problem starts if you have to declare namespaces in your XPath expression. You can use namespaces in your XPath expression by declaring a namespace-manager in your mule-config, like this:

1
2
3
4
5
6
7
8
9
10
11
12
...
 <xm:namespace-manager includeConfigNamespaces="false">
   <xm:namespace prefix="ns1" uri="http://www.redstream.nl/relations/v1_1"/>
 </xm:namespace-manager>
...
   <outbound>
     <filtering-router>
       <vm:outbound-endpoint ref="out-queue" />
       <xm:jaxen-filter pattern="local-name(//ns1:Relations)" expectedValue="Relations"/>
     </filtering-router>
   </outbound>
...

But if you run this example you will get the following exception:

org.jaxen.UnresolvableException: No Such Function {http://www.mulesource.org/schema/mule/core/2.2}:local-name
at org.jaxen.SimpleFunctionContext.getFunction(SimpleFunctionContext.java:127)
at org.jaxen.ContextSupport.getFunction(ContextSupport.java:242)
at org.jaxen.Context.getFunction(Context.java:216)
at org.jaxen.expr.DefaultFunctionCallExpr.evaluate(DefaultFunctionCallExpr.java:172)
at org.jaxen.expr.DefaultXPathExpr.asList(DefaultXPathExpr.java:102)
at org.jaxen.BaseXPath.selectNodesForContext(BaseXPath.java:674)
at org.jaxen.BaseXPath.selectSingleNodeForContext(BaseXPath.java:698)
at org.jaxen.BaseXPath.stringValueOf(BaseXPath.java:279)
at org.jaxen.BaseXPath.valueOf(BaseXPath.java:254)
at org.mule.module.xml.filters.JaxenFilter.accept(JaxenFilter.java:184)
at org.mule.module.xml.filters.JaxenFilter.accept(JaxenFilter.java:118)
at org.mule.routing.outbound.FilteringOutboundRouter.isMatch(FilteringOutboundRouter.java:119)
at org.mule.routing.outbound.DefaultOutboundRouterCollection. route(DefaultOutboundRouterCollection.java:72)
at org.mule.service.AbstractService.dispatchToOutboundRouter(AbstractService.java:867)
at org.mule.model.seda.SedaService.dispatchToOutboundRouter(SedaService.java:561)
at org.mule.model.seda.SedaService$ComponentStageWorker.doRun(SedaService.java:583)
at org.mule.work.AbstractMuleEventWork.run(AbstractMuleEventWork.java:41)
at org.mule.work.WorkerContext.run(WorkerContext.java:310)
at edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor. runWorker(ThreadPoolExecutor.java:1061)
at edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor$Worker. run(ThreadPoolExecutor.java:575)
at java.lang.Thread.run(Thread.java:637)

Although it is logged as warning it makes the filter to return no matching values and the XML message is discarded (although I use the jaxen-filter here this also goes for the jxpath-filter and the xpath-filter).

The solution for this is quite simple: just give the XPath function a unique prefix (one that is not mapped to a namespace). So if you modify the last example and make the jaxen-filter like:
<xm:jaxen-filter pattern="fn:local-name(//ns1:Relations)" expectedValue="Relations"/>
or
<xm:jaxen-filter pattern="abc:local-name(//ns1:Relations)" expectedValue="Relations"/>
it will work.
You just have to make sure the prefix you use isn’t used in the declared namespace-manager because then the function is looked up (and not found) in the supplied XML document.

tags: ,

About Pascal Alma

Pascal started as an Oracle Developer in 1997 and developed numerous applications with Oracle Designer/Developer and PL/SQL. Since 2001 Pascal becomes more and more active with the development of software at the Java/J2EE platform. Nowadays Pascal is a senior JEE Developer/ Architect and has a lot of experience with several open source initiatives/ frameworks especially within the Enterprise Integration area. Besides these technical skills Pascal is a big Scrum enthusiastic.

Comments are closed.