2 Replies Latest reply on Apr 24, 2013 9:57 AM by davsclaus

    Camel, CXF REST endpoints, and route builders; Am I doing it right?

    dazbert42

      Hi,

       

      I'm attempting to use Camel as an endpoint for a RESTful interface, and while I've managed to get the behaviour I want; I'm wondering whether I've missed something in the way my components are wired together as the mechanism I have used seems overly error prone.

       

      In this instance I've used the Camel-Spring archetype within the Fuse IDE as the basis for my project.  I'm running fuse-esb-full-7.1.0.fuse-047.

       

      Apologies in advance for the formatting.  I couldn't find markup for a "code block".

       

      My camel-context.xml file contains the following configuration information:

      -


       

      -


       

      The GetThingInterface is an empty class with a bunch of annotations as follows:

      -


      @Path("/getthing/")

      public class GetThingInterface {

           @GET @Path("/one")

           public String getSingleThingByQuery(@QueryParam("machineId") String machineId) {

                return null;

           }

                 

           @GET @Path("/many")

           public String getManyThingByQuery(@QueryParam("machineId") String machineId) {

                return null;

           }

      }

      -


       

      The GetThingImplementation is equally exciting.  There's no annotations here because they are not used by anything.

      -


      public class GetThingImplementation {

           public String getSingleThingByQuery(String machineId) {

                return "You got 1 " + machineId;

           }

       

           public String getManyThingByQuery(String machineId) {

                return "You got MANY " + machineId + "s";

           }

      }

      -


       

      The route builder is where things get really interesting.

      If I use the route:

      from("cxfrs://bean://thingServer")

      .to("bean:rsThingService");

       

      I will always get an Ambiguous Method Exception as Camel doesn't know which method (getSingleThingByQuery or getManyThingByQuery) on my bean to invoke.

       

      I did try the following to see if I could get the method invocation to occur automagically; but the header value isn't expanded.

      .to("bean:rsThingService?method=${header.operationname}")

       

      However, if I use the following route:

       

      from("cxfrs://bean://thingServer")

      .choice()

      .when(header("operationname").isEqualTo("getSingleThingByQuery"))

      .to("bean:rsThingService?method=getSingleThingByQuery")

      .otherwise()

      .to("bean:rsThingService?method=getManyThingByQuery");

       

      I get the behaviour I expect.  Camel is smart enough to pass the "machineId" parameter within the REST request into the method.

       

      My question is, is there a better way?  Having to explicitly reference methods by name seems overly error prone and fragile.

       

      -


       

      A secondary question is about the

       

      Where double bean A and B were individual route builders, one for the /bin/getthing/one path and the other for the /bin/getthing/many path.  Weirdly although I didn't see any errors, the second route builder simply replaced the first.  So rather than having both routes available only the last one defined would work.

       

      -


       

      As an aside I did discover that if you attempt to define multiple cxf:rsServer elements with id values that use numbers, when the package is loaded into Fuse you get a duplicate ID error.  e.g. When I defined the following I got a duplicate ID "thingServer" error.

      <cxf:rsServer id="thingServer1" address="http://localhost:8111/cxf/bin"

        serviceClass="com.oink.CXFEndPointTests.singlebean.GetThingInterface1"/>

       

      <cxf:rsServer id="thingServer2" address="http://localhost:8111/cxf/bin"

        serviceClass="com.oink.CXFEndPointTests.singlebean.GetThingInterface2"/>

       

      Changing the id values to thingServerA and thingServerB works around this behaviour.

       

       

      All thoughts gratefully received.

        • 1. Re: Camel, CXF REST endpoints, and route builders; Am I doing it right?
          dazbert42

          There is a better way to handle the method invocation.

           

          If change the route to:

          from("cxfrs://bean://thingServer")

          .setHeader("CamelBeanMethodName", simple("${header.operationName}") )

          .to("bean:rsThingService");

           

          The correct method gets invoked on the rsThingService as the method to invoke has already been parsed into the header value operationName.

           

           

          My current bug bear is that if I have a method with more than one parameter ...

           

          @GET @Path("/it")

          public String getIt(@QueryParam("id") String it, @QueryParam("ref") String ref) {

          return null;

          }

           

          .. making the following request /cxf/bin/it?id=fish&ref=cake will result in a method call of getIt( "fish", null ).

           

          I cannot find any way of having the Camel route automatically pass the second parameter into the getIt() method.  Any ideas?

          • 2. Re: Camel, CXF REST endpoints, and route builders; Am I doing it right?
            davsclaus

            Do it the other way around. Let CXF expose the REST service and from the java code in the REST impl class, you can call Camel routes if you need using the ProducerTemplate API.