Christian Posta — Software Blog

Become the best you can be at your profession. If you've stopped learning, you've given up.

REST endpoint for integration using Apache Camel

REST is an architectural style used for organizing resources and when applied to HTTP-based services allows building stateless, decoupled, scalable services. HTTP methods, HTTP headers, and mime-types all allow a developer to achieve the REST style. Frameworks like Jersey and Fuse Services Framework (Apache CXF) can be used to speed up the development and deployment of services trying to achieve a RESTful style, and in this blog post I’d like to discuss how to build the backend of a resource that relies on integration provided by Fuse Mediation Router also known as Apache Camel.

Just as an aside, a link that I’ve had tucked away in the recesses of my bookmarks may be of interest for those of you wondering whether your architecture is indeed RESTful or just the same highly coupled RPC style that REST tries to alleviate. Roy Fielding, who wrote his dissertation on REST, actively asserts the notion that hyerlinks within resource representations are a must for REST styles, and even further clarifies the uncertainties around implementing REST.

The source code for this sample can be found on my github repository

Fuse Mediation Router is FuseSource’s enterprise-grade, hardened version of Apache Camel that provides a comfortable DSL for describing integrations, mediations, and routing. It’s free, open-source, and has an Apache License. For those unfamiliar with Mediation Router/Camel, take a look at an introduction from Jon Anstey (co-author of Camel in Action)at DZone’s Enterprise Integration Zone: Apache Camel: Integration Nirvana.

We will be using Mediation Router to help write a simple integration between a REST endpoint and a resource files on a file system. I’ll be using camel-cxfrs component to expose the REST endpoint and will be using the camel-file component to read a directory on the file system. The intention of the sample is to describe the configuration necessary to expose the REST interface with Mediation Router, integrate with a backend somehow, transform the data into an appropriate REST response, and send back the response.

To get started, let’s focus on how to set up the REST endpoint. To do so, you would create a JAX-RS resource that describes the java methods that will act as REST endpoints. This sample code requires familiarity with Java API for RESTful Web Services aka JAX-RS. For those unfamiliar, here are some great tutorials to follow along that help to understand JAX-RS.

@Path("/customerservice/")
public class CustomerServiceResource {

// NOTE: The instance member variables will not be available to the
// Camel Exchange. They must be used as method parameters for them to
// be made available
@Context
private UriInfo uriInfo;

public CustomerServiceResource() {
}

@GET
@Path("/customers/{id}/")
@Produces("text/xml")
public Customer getCustomer(@PathParam("id") String id) {
    return null;
}

@PUT
@Path("/customers/")
public Response updateCustomer(Customer customer) {
    return null;
}

}

As you can see, the annotations are the JAX-RS annotations that describe the operations, HTTP methods, and mime-types involved with the REST endpoint. Notice, the return values are all null as this class will not actually be used to handle the requests that come in to the endpoint; the Mediation Router routes will be responsible for processing and responding. Note, however, that instance members are not available to the Mediation Router exchanges, i.e., any instance members injected via the JAX-RS @Context annotations will not be available. To make them available, add them as parameters to your methods.

Declaring the CXF-RS endpoint with Mediation Router can be done one of two ways: Directly in the endpoint configuration like this:

from("cxfrs://http://localhost:9090/route?resourceClasses=com.fusesource.samples.CustomerServiceResource")

Creating it directly in the configuration requires less xml configuration but offers limited flexibility. Another option is creating a separate bean that’s responsible for the endpoint and then referencing it within the endpoint configuration:

from("cxfrs:bean:rsServer")

The bean rsServer should be defined in the camel context. An example:

<cxf:rsServer id="rsServer" address="http://localhost:9090/route"
              serviceClass="com.fusesource.samples.CustomerServiceResource"/>

This approach allows you to decouple the endpoint configuration and allows to be quicker and less verbose in the endpoint configuration. Both options are shown in the sample code, although the first option is used.

That’s all the configuration required to expose the REST endpoint with Mediation Router. Fairly simple. The next step is to consume a file from the file system based on what comes in from the REST endpoint. The contents of the file will be returned to the client of the REST call. To do this, we use the camel-file component and enrich the Exchange with a pollEnrich call in the DSL:

.setHeader(Exchange.FILE_NAME, simple("test-${body}.xml"))
.pollEnrich("file:src/data?noop=true", 1000, new CustomerEnricher())

We cannot use any dynamic expressions in the pollEnrich call, so we set a header that the file component understands before we do the enrichment. In this case, the body of the REST message is an identifier that can be used to template the file-system resource.

Lastly, we can attach some additional processing to the route:

.process(new CustomerServiceProcessor())

The intent of the example, as described above, is to show how to configure the endpoint and attach it to further Mediation Router processing. Note, the Message Exchange Pattern (MEP) for the REST endpoint is InOut and expects a response. The example is not meant to be a complete end-to-end solution as that will vary depending on intended functionality. Please note above the links to Roy’s discussions on what REST is and is not.

If I have left something out, or you need more clarification around the example, drop me a comment and we can discuss.

Tags:

16 Responses to “REST endpoint for integration using Apache Camel”

  • [...] REST endpoint for integration using Apache Camel [...]

  • Luis says:

    Hi,
    What if you want to do a dozen of REST services? Would you create an equal number of cxf:rsServer?

    Thanks

  • Hi there, just wanted to say, I liked this blog post. It was helpful. Keep on posting!

  • Anton says:

    Hi

    I am trying your github project at https://github.com/christian-posta/file-rest-blog

    However, I am getting the following error when doing mvn install:
    Failed to execute goal on project file-rest-blog: Could not resolve dependencies for project com.fuseource.samples:file-rest-blog:jar:1.0-SNAPSHOT: The following artifacts could not be resolved: org.apache.camel:camel-cxf:jar:2.8.0-fuse-04-01, org.apache.cxf:cxf-rt-transports-http-jetty:jar:2.4.3-fuse-02-02: Failure to find org.apache.camel:camel-cxf:jar:2.8.0-fuse-04-01 in http://repo.fusesource.com/maven2 was cached in the local repository, resolution will not be reattempted until the update interval of fusesource-releases has elapsed or updates are forced -

    Can you please tell what versions of these dependencies I need?

    Many thanks – and keep up the great articles and videos!! I’m a big fan of your work!

    • admin says:

      I just tried it, it seems to work, however I have the correct settings/repos set up for maven in my settings.xml.
      let me update the pom.xml to reflect the correct URLs. After the FuseSource acquisition, some things got moved around.

      give it a shot now :)

  • Anton says:

    Thanks Christian

    Yes that fixed it.

    One more question – how do I now run it? What is the mvn command ??

    Must it be deployed into fuse?

    Thanks

  • Anton says:

    Perfect!

    Thanks Christian.

  • Anton says:

    Hi Christian,

    I have one more question.

    I have converted your project to use a camel xml route – and its working. However, when I try to add a velocity component, I get an error (see below). Any idea why this is? What do I need to do to work with Velocity? I have added it to the pom.xml (using version 2.12.3).

    ....

    And the exception is:

    Error occurred while running main from: org.apache.camel.spring.Main
    java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.camel.maven.RunMojo$1.run(RunMojo.java:415)
    at java.lang.Thread.run(Thread.java:724)
    Caused by: java.lang.NoClassDefFoundError: org/apache/camel/component/ResourceEndpoint
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:792)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at org.apache.camel.component.velocity.VelocityComponent.createEndpoint(VelocityComponent.java:46)
    at org.apache.camel.impl.DefaultComponent.createEndpoint(DefaultComponent.java:75)
    at org.apache.camel.impl.DefaultCamelContext.getEndpoint(DefaultCamelContext.java:426)
    at org.apache.camel.util.CamelContextHelper.getMandatoryEndpoint(CamelContextHelper.java:48)
    at org.apache.camel.model.RouteDefinition.resolveEndpoint(RouteDefinition.java:180)
    at org.apache.camel.impl.DefaultRouteContext.resolveEndpoint(DefaultRouteContext.java:110)
    at org.apache.camel.impl.DefaultRouteContext.resolveEndpoint(DefaultRouteContext.java:116)
    at org.apache.camel.model.SendDefinition.resolveEndpoint(SendDefinition.java:61)
    at org.apache.camel.model.SendDefinition.createProcessor(SendDefinition.java:55)
    at org.apache.camel.model.ProcessorDefinition.makeProcessor(ProcessorDefinition.java:430)
    at org.apache.camel.model.ProcessorDefinition.addRoutes(ProcessorDefinition.java:183)
    at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:817)
    at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:165)
    at org.apache.camel.impl.DefaultCamelContext.startRoute(DefaultCamelContext.java:698)
    at org.apache.camel.impl.DefaultCamelContext.startRouteDefinitions(DefaultCamelContext.java:1696)
    at org.apache.camel.impl.DefaultCamelContext.doStartCamel(DefaultCamelContext.java:1481)
    at org.apache.camel.impl.DefaultCamelContext.doStart(DefaultCamelContext.java:1373)
    at org.apache.camel.spring.SpringCamelContext.doStart(SpringCamelContext.java:169)
    at org.apache.camel.impl.ServiceSupport.start(ServiceSupport.java:67)
    at org.apache.camel.impl.ServiceSupport.start(ServiceSupport.java:54)
    at org.apache.camel.impl.DefaultCamelContext.start(DefaultCamelContext.java:1341)
    at org.apache.camel.spring.SpringCamelContext.maybeStart(SpringCamelContext.java:213)
    at org.apache.camel.spring.SpringCamelContext.onApplicationEvent(SpringCamelContext.java:108)
    at org.apache.camel.spring.CamelContextFactoryBean.onApplicationEvent(CamelContextFactoryBean.java:240)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:97)
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:303)
    at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:911)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:428)
    at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:93)
    at org.apache.camel.spring.Main.createDefaultApplicationContext(Main.java:185)
    at org.apache.camel.spring.Main.doStart(Main.java:139)
    at org.apache.camel.impl.ServiceSupport.start(ServiceSupport.java:67)
    at org.apache.camel.impl.ServiceSupport.start(ServiceSupport.java:54)
    at org.apache.camel.impl.MainSupport.run(MainSupport.java:136)
    at org.apache.camel.impl.MainSupport.run(MainSupport.java:322)
    at org.apache.camel.spring.Main.main(Main.java:72)
    … 6 more
    Caused by: java.lang.ClassNotFoundException: org.apache.camel.component.ResourceEndpoint
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    … 54 more
    *************************************

    • admin says:

      Check the versions that you’re using.
      That class is in camel-core I believe, so if there are version mismatches between what components you use and camel-core, there will be class path issues

  • Anton says:

    Thanks Christian

    That got me one step further.

    I see that you are using camel 2.8.0-fuse-04-01.
    Does this mean I cannot use any other camel components unless they are also of this version?
    Will I not be able to deploy to fuse if I use normal camel components?

    I am also trying to use the aws-ses component and am now running into the same issue.

  • Chubutin says:

    What if you want to do a dozen of REST services? Would you create an equal number of cxf:rsServer? Or can I re use the same cxfrs:Server?

    Thank you

  • ttati says:

    Hi
    the article is very useful, I wonder if you can commit the test folder as well. It would be useful to see how you tested it.
    thank you

  • Taris says:

    Thanks a lot for your blog. It really helps me dig into it. I am currently working on understanding of how this is all connected and I run into one very simple problem. I call the URL in a browser, I see log output in my java console but the camel-route is not fired (and thus, no response appears in the browser).

    Calling http://localhost:9090/customers/123/ results into console output:
    “No root resource matching request path /route/customers/123/ has been found”


Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>