HoopItUp
HoopItUp

Reputation: 63

Having trouble with content based routing in Camel with a CXFRS endpoint and xPath

I am trying to create a route that is determined by the content in the REST payload using xPath. I have been successful in using routing based on the message header:

<when>
   <simple>${headers.operationName} == 'createContainerOutput'</simple>
   <bean ref="containerOutputProcessor"/>
</when>

which properly invokes the containerOutputProcessor...

but for this xPath route:

<when>
    <xpath>/*[local-name()='order-request']/@type='TrayOutput'</xpath>
    <bean ref="containerOutputProcessor"/>
</when>

I get the the exception:

org.apache.camel.NoTypeConversionAvailableException: No type converter available to convert from type: org.apache.cxf.message.MessageContentsList to the required type: org.w3c.dom.Document with value [com.mmi.ws.ContainerOutputOrderRequest@6290dc]

for this payload

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<order-request type="TrayOutput">
    <parameter name="orderName">Example Tray Output Order</parameter>
    <parameter name="enableScan">true</parameter>
    <parameter name="autoStart">false</parameter>
    <parameter name="priority">3</parameter>
    <item barcode="23990001"/>
    <item barcode="23990002"/>
</order-request>

Is this type of routing a good idea? Is there a better way to route based on what type of order-request is being submitted?

Thanks for any help/guidance you may have for me!


Here is the complete context

<?xml version='1.0' encoding='UTF-8'?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:jaxrs="http://cxf.apache.org/jaxrs"
    xmlns:cxf="http://camel.apache.org/schema/cxf"
    xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
     http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
     http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
     http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd">

  <!-- enable Spring @Component scan -->
  <context:component-scan base-package="com.mmi.ws"/>

    <!--  web service beans -->
  <bean id="containerOutputWS" class="com.mmi.ws.service.impl.ContainerOutputWSImpl" />
  <bean id="containerTypesWS" class="com.mmi.ws.service.impl.ContainerTypesWSImpl" />


  <!-- processor beans -->
  <bean id="containerOutputProcessor" class="com.mmi.ws.service.ContainerOutputProcessor" />
  <bean id="containerTypesProcessor" class="com.mmi.ws.service.ContainerTypesProcessor" />
  <bean id="unsupportedPathProcessor" class="com.mmi.ws.service.UnsupportedPathProcessor" />


  <!-- Define the real JAXRS back end service  -->
  <jaxrs:server id="restService"
                address="http://localhost:9998/sc" 
                staticSubresourceResolution="true">
    <jaxrs:serviceBeans>
      <ref bean="containerOutputWS"/>
      <ref bean="containerTypesWS"/>
    </jaxrs:serviceBeans>       
  </jaxrs:server>

  <!--  define the restful server and client endpoints -->
  <cxf:rsServer id="rsServer" address="http://localhost:9999/sc" loggingFeatureEnabled="true" loggingSizeLimit="20">
    <cxf:serviceBeans >
      <ref bean="containerOutputWS"/>
      <ref bean="containerTypesWS"/>
    </cxf:serviceBeans>
  </cxf:rsServer>

  <cxf:rsServer id="rsClient" address="http://localhost:9998/sc" loggingFeatureEnabled="true" loggingSizeLimit="20">
    <cxf:serviceBeans >
      <ref bean="containerOutputWS"/>
      <ref bean="containerTypesWS"/>
    </cxf:serviceBeans>
  </cxf:rsServer>


  <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
    <!-- 
       any classes in the below 'packageScan'packages that extends RouteBuilder
       will be added as a camel route.  At least one route is required to start the cxf web service
    -->
    <packageScan>
        <package>com.mmi.ws</package>
    </packageScan>

    <dataFormats>
     <xstream id="xstream-utf8" encoding="UTF-8"/>
     <xstream id="xstream-default"/>
    </dataFormats>

    <!-- route starts from the cxf webservice  -->
    <route streamCache="true">
       <from uri="cxfrs://bean://rsServer"/>
        <log message="XML payload to send to REST WS:${body}" />        
        <setHeader headerName="CamelCxfRsUsingHttpAPI"><constant>True</constant> </setHeader>
        <choice>
            <when>
                <simple>${headers.operationName} == 'getContainers'</simple>
                <bean ref="containerTypesProcessor"/>
            </when>
            <when>
                <xpath>/*[local-name()='order-request']/@type='TrayOutput'</xpath>
                <bean ref="containerOutputProcessor"/>
            </when>
            <otherwise>
                <bean ref="unsupportedPathProcessor"/>
                <to uri="cxfrs://bean://rsClient"/>
            </otherwise>
        </choice>
    </route>    
  </camelContext>
</beans>

and the web service class:

@Path("/container/output/")
public class ContainerOutputWSImpl 
{
    @POST
    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
    public ContainerOutputView createContainerOutput(ContainerOutputOrderRequest containerOutput) {
        // TODO Auto-generated method stub
        return new ContainerOutputView();
    }
}

and finally, the xml payload and error stack:

Address: http://localhost:9999/sc/container/output/
Encoding: ISO-8859-1
Http-Method: POST
Content-Type: application/xml
Headers: {accept-encoding=[gzip,deflate], connection=[keep-alive], Content-Length=[384], content-type=[application/xml], Host=[localhost:9999], User-Agent=[Apache-HttpClient/4.1.1 (java 1.5)]}
Payload: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<order-request type="TrayOutput">
    <parameter name="orderName">Example Tray Output Order</parameter>
    <parameter name="enableScan">true</parameter>
    <parameter name="autoStart">false</parameter>
    <parameter name="priority">3</parameter>
    <item barcode="23990001"/>
    <item barcode="23990002"/>
</order-request>

--------------------------------------
[ERROR] 2013-02-07  17:53:37.059  [qtp27633254-28: DefaultErrorHandler] Failed delivery for (MessageId: ID-PWY-EHANSEN-3070-1360288389778-0-2 on ExchangeId: ID-PWY-EHANSEN-3070-1360288389778-0-1). Exhausted after delivery attempt: 1 caught: org.apache.camel.RuntimeCamelException: org.apache.camel.NoTypeConversionAvailableException: No type converter available to convert from type: org.apache.cxf.message.MessageContentsList to the required type: org.w3c.dom.Document with value [com.mmi.ws.ContainerOutputOrderRequest@9fbbe5]
org.apache.camel.RuntimeCamelException: org.apache.camel.NoTypeConversionAvailableException: No type converter available to convert from type: org.apache.cxf.message.MessageContentsList to the required type: org.w3c.dom.Document with value [com.mmi.ws.ContainerOutputOrderRequest@9fbbe5]
    at org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException(ObjectHelper.java:1271)
    at org.apache.camel.builder.xml.XPathBuilder.getDocument(XPathBuilder.java:1027)
    at org.apache.camel.builder.xml.XPathBuilder.doInEvaluateAs(XPathBuilder.java:850)
    at org.apache.camel.builder.xml.XPathBuilder.evaluateAs(XPathBuilder.java:757)
    at org.apache.camel.builder.xml.XPathBuilder.matches(XPathBuilder.java:145)
    at org.apache.camel.processor.ChoiceProcessor.process(ChoiceProcessor.java:66)
    at org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:73)
    at org.apache.camel.processor.DelegateAsyncProcessor.processNext(DelegateAsyncProcessor.java:99)
    at org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:90)
    at org.apache.camel.management.InstrumentationProcessor.process(InstrumentationProcessor.java:73)
    at org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:73)
    at org.apache.camel.processor.DelegateAsyncProcessor.processNext(DelegateAsyncProcessor.java:99)
    at org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:90)
    at org.apache.camel.processor.interceptor.TraceInterceptor.process(TraceInterceptor.java:91)
    at org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:73)
    at org.apache.camel.processor.RedeliveryErrorHandler.processErrorHandler(RedeliveryErrorHandler.java:334)
    at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:220)
    at org.apache.camel.processor.interceptor.StreamCachingInterceptor.process(StreamCachingInterceptor.java:52)
    at org.apache.camel.processor.RouteContextProcessor.processNext(RouteContextProcessor.java:45)
    at org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:90)
    at org.apache.camel.processor.interceptor.DefaultChannel.process(DefaultChannel.java:303)
    at org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:73)
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:117)
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:80)
    at org.apache.camel.processor.RouteContextProcessor.processNext(RouteContextProcessor.java:45)
    at org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:90)
    at org.apache.camel.processor.UnitOfWorkProcessor.processAsync(UnitOfWorkProcessor.java:150)
    at org.apache.camel.processor.UnitOfWorkProcessor.process(UnitOfWorkProcessor.java:117)
    at org.apache.camel.processor.RouteInflightRepositoryProcessor.processNext(RouteInflightRepositoryProcessor.java:48)
    at org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:90)
    at org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:73)
    at org.apache.camel.processor.DelegateAsyncProcessor.processNext(DelegateAsyncProcessor.java:99)
    at org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:90)
    at org.apache.camel.management.InstrumentationProcessor.process(InstrumentationProcessor.java:73)
    at org.apache.camel.component.cxf.jaxrs.CxfRsInvoker.asyncInvoke(CxfRsInvoker.java:87)
    at org.apache.camel.component.cxf.jaxrs.CxfRsInvoker.performInvocation(CxfRsInvoker.java:57)
    at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96)
    at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:201)
    at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:102)
    at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:58)
    at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:94)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:271)
    at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
    at org.apache.cxf.transport.http_jetty.JettyHTTPDestination.serviceRequest(JettyHTTPDestination.java:355)
    at org.apache.cxf.transport.http_jetty.JettyHTTPDestination.doService(JettyHTTPDestination.java:319)
    at org.apache.cxf.transport.http_jetty.JettyHTTPHandler.handle(JettyHTTPHandler.java:72)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1074)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1010)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:255)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
    at org.eclipse.jetty.server.Server.handle(Server.java:365)
    at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:485)
    at org.eclipse.jetty.server.AbstractHttpConnection.content(AbstractHttpConnection.java:937)
    at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.content(AbstractHttpConnection.java:998)
    at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:856)
    at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:240)
    at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
    at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:627)
    at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:51)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
    at java.lang.Thread.run(Thread.java:722)

Upvotes: 1

Views: 4115

Answers (1)

Claus Ibsen
Claus Ibsen

Reputation: 55540

Before the content based router, you can convert the message to take out the payload from the internal CXF list. There is a trick with the simple language to grab the first index from the list:

<transform>
   <simple>${body[0]}</simple>
</transform>
<choice>
   ...

Do you have camel-jaxb on the classpath. If so it may work out of the box without that trick. Not sure though, as CXF is a bit special. Also dependes on what version of Camel / CXF you use. You should really mention this when you ask questions!

Upvotes: 5

Related Questions