Sunday, December 19, 2010

basic REST service in Apache CXF vs. Camel-CXF

This article demonstrates how to create/test a basic REST service in CXF vs. Camel-CXF. Given the range of configuration and deployment options, I'm focusing on building a basic OSGi bundle that can be deployed in Fuse 4.2 (ServiceMix)...basic knowledge of Maven, ServiceMix and Camel are assumed.

Apache CXF

For more details, see http://cxf.apache.org/docs/jax-rs.html.

Here is an overview of the steps to get a basic example running...

1. add dependencies to your pom.xml

   <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-frontend-jaxrs</artifactId>
      <version>2.3.0</version>
   </dependency>

2. setup the bundle-context.xml file

    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-http.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-http-jetty.xml" />

    <bean id="exampleBean" class="com.example.ExampleBean" />

    <jaxrs:server id="exampleService" address="http://localhost:9000/">
        <jaxrs:serviceBeans>
            <ref bean="exampleBean" />
        </jaxrs:serviceBeans>
    </jaxrs:server>

3. create a service bean class

@Path("/example")
public class ExampleBean {

    @GET
    @Path("/")
    public String ping() throws Exception {
        return "SUCCESS";
    }
}

4. deploy and test

  build the bundle using "mvn install"
  start servicemix
  deploy the bundle
  open a browser to "http://localhost:9000/example" (should see "SUCCESS")

Camel-CXF

For details, see http://camel.apache.org/cxfrs.html

Here is an overview of the steps to get a basic example running...

1. add dependencies to your pom.xml

        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-core</artifactId>
            <version>${camel.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-cxf</artifactId>
            <version>${camel.version}</version>
        </dependency>  

2. setup the bundle-context.xml file

    <camelContext trace="true" id="camelContext" xmlns="http://camel.apache.org/schema/spring">
        <package>com.example</package>
    </camelContext>

3. create a RouteBuilder class

public class ExampleRouter extends RouteBuilder {

    @Override
    public void configure() throws Exception {

        from("cxfrs://http://localhost:9000?resourceClasses=" + ExampleResource.class.getName())
            .process(new Processor() {
                public void process(Exchange exchange) throws Exception {
                    //custom processing here
                }
            })
            .setBody(constant("SUCCESS"));
        }
    }

4. create a REST Resource class

@Path("/example")
public class ExampleResource {

    @GET
    public void ping() {
        //strangely, this method is not called, only serves to configure the endpoint
    }
}

5.  deploy and test

  build bundle using "mvn install"
  start servicemix
  deploy the bundle
  open a browser to "http://localhost:9000/example" (should see "SUCCESS")

Unit Testing

To perform basic unit testing for either of these approaches, use the Apache HttpClient APIs by first adding this dependency to your pom.xml...

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.0.1</version>
        </dependency>


Then, you can use these APIs to create a basic test to validate the REST services created above...

        String url = "http://localhost:9000/example";
        HttpGet httpGet = new HttpGet(url);
        HttpClient httpclient = new DefaultHttpClient();
        HttpResponse response = httpclient.execute(httpGet);
        String responseMessage = EntityUtils.toString(response.getEntity());
        assertEquals("SUCCESS", responseMessage);
        assertEquals(200, response.getStatusLine().getStatusCode());


Summary

Overall, the approaches are very similar, but you can use various combinations of Spring XML and Java APIs to set this up.  I focused on a common approach to demonstrate the basics of each approach side-by-side.

That being said, if you have requirements for complex REST services (security, interceptors, filters, etc), I recommend grabbing a copy of Apache CXF Web Service Development and following some of the more complex examples on the Apache CXF, Camel-CXFRS pages.

In practice, I've generally used Camel-CXF because it gives you the flexibility of integrating with other Camel components and allows you to leverage the rich routing features of Camel.  I hope to cover more complex scenarios in future posts...



14 comments:

  1. This is a short and nice comparison Ben.

    I also found some of your older posts about Camel. This is really great blog posts you do. I have added the error handling summary to the articles list at the Camel website.

    I also added your blog under the Camel bloggers. I hope you don't mind this? If so then let me know and I can remove it again.

    https://cwiki.apache.org/confluence/display/CAMEL/Articles

    Regards

    Claus Ibsen

    ReplyDelete
  2. hey ben, i am still trying on this example with no luck.

    i began with the servicemix kernel package "apache-servicemix-kernel-1.1.0".
    i flollowed the quick start guide from: http://servicemix.apache.org/SMX4KNL/1-quick-start.html
    When i run the camel osgi example it prints out the frequent message :
    >>>> MyTransform set body: Sat Jan 08 22:49:13 CET 2011
    >>>> MyTransform set body: Sat Jan 08 22:49:15 CET 2011
    >>>> MyTransform set body: Sat Jan 08 22:49:17 CET 2011

    but if i try to install the servicemixTest i get the following error message:
    org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://camel.apache.org/schema/osgi]2358 Offending resource: URL [bundle://53.0:0/META-INF/spring/camel-context.xml]
    at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:68)


    next attempt was to use "apache-servicemix-4.3.0-fuse-03-00". there i changed the etc/org.apache.karaf.features.cfg to install those feature at boot time:

    ....
    #
    # Comma separated list of features to install at startup
    #
    featuresBoot=config,activemq-broker,camel,jbi-cluster,war,servicemix-cxf-bc,servicemix-file,servicemix-ftp,servicemix-http,servicemix-jms,servicemix-mail,servicemix-bean,servicemix-camel,servicemix-cxf-se,servicemix-drools,servicemix-eip,servicemix-osworkflow,servicemix-quartz,servicemix-scripting,servicemix-validation,servicemix-saxon,servicemix-wsn2005,camel,cxf,camel-cxf,camel-jetty,web,camel-http,cxf-jaxrs
    #-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    when i deploy the servicemixTest with the command "osgi:install -s mvn:edu.fhb.softarch.medialib/servicemixTest"
    I get the following error message in the logs: "No component found with scheme: cxfrs"

    ...
    Exception in thread "SpringOsgiExtenderThread-6" org.apache.camel.RuntimeCamelException: org.apache.camel.FailedToCreateRouteException: Failed to create route route1: Route[[From[cxfrs://http://localhost:9000?resourceClasses=ed... because of Failed to resolve endpoint: cxfrs://http://localhost:9000?resourceClasses=edu.fhb.softarch.medialib.ExampleResource due to: No component found with scheme: cxfrs
    at org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException(ObjectHelper.java:1133)
    at org.apache.camel.spring.SpringCamelContext.onApplicationEvent(SpringCamelContext.java:103)
    ...

    ReplyDelete
  3. hey Nils, I just tried with a clean install of 4.3 and it worked fine. I just needed to install these features...

    featuresBoot=camel,cxf,camel-cxf,camel-jetty,web,camel-http,cxf-jaxrs

    Also, here is a more simplified version of your project to try...

    http://www.zshare.net/download/850728171c624e33/

    ReplyDelete
  4. hello ben, it me again.
    we had some project changes but still need to get a rest service going.

    now i show you my steps:
    1. download this file and unpack to ~
    http://fusesource.com/product_download/fuse-esb-apache-servicemix/4-3-0-fuse-03-00/unix

    2. replace the project in eclipse with your zshare simplefied version.

    3. replace "featuresBoot=..." with "featuresBoot=camel,cxf,camel-cxf,camel-jetty,web,camel-http,cxf-jaxrs" in ~/downloadedFuse/etc/org.apache.karaf.features.cfg

    4.start fuse with ~/downloadedFuse/bin/karaf

    5. in the pom.xml of the project change the camel version from "2.2.0-fuse-01-00" to "2.5" (due maven error: The following artifacts could not be resolved: org.apache.camel:camel-cxf:jar:2.2.0-fuse-01-00, org.apache.camel:camel-core:jar:2.2.0-fuse-01-00)

    6. in the project do mvn install

    7. on the karaf shell type: "osgi:install -s mvn:edu.fhb.softarch.medialib/servicemixTest"

    8. error after a few seconds:
    Exception in thread "SpringOsgiExtenderThread-8" org.apache.camel.RuntimeCamelException: org.apache.camel.FailedToCreateRouteException: Failed to create route route2: Route[[From[cxfrs://http://localhost:9000?resourceClasses=ed... because of Failed to resolve endpoint: cxfrs://http://localhost:9000?resourceClasses=edu.fhb.softarch.medialib.ExampleResource due to: No component found with scheme: cxfrs
    at org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException(ObjectHelper.java:1133)
    at org.apache.camel.spring.SpringCamelContext.onApplicationEvent(SpringCamelContext.java:103)
    at org.
    ...


    sorry but i am trying my best ... my gues is that it has to do with the version of camel in the pom (see step 5)

    ReplyDelete
  5. Nils, the version is the issue. With Fuse ESB 4.3.0, we should use camel 2.4.0-fuse-02-00. Just need to add the Fuse Maven repo to your pom.xml file (http://repo.fusesource.com/maven2/)...

    see my updated project here (http://www.zshare.net/download/8541775080fbfde7/)

    You can use Camel 2.5.0, but you'll need to update the camel bundles in Fuse to do this.

    Also, I noticed a few other CXF warnings in the logs (see the updated camel-context.xml and pom.xml files)...

    Let me know if you are still having issues...

    ReplyDelete
  6. Nils, make sure the /etc/org.apache.karaf.features.cfg file has this featuresBoot value...

    featuresBoot=camel,cxf,camel-cxf,camel-jetty,web,camel-http,cxf-jaxrs

    In the one you posted (http://pastebin.com/FGQvKGGM), I don't see them...

    Also, before restarting SMX, delete the /data directory to force a clean restart of the container.

    ReplyDelete
  7. it is working :) deleting the data container did the job :)

    thank you sooo much ben!

    ReplyDelete
  8. Hi
    i want to download xml file using webservice.. i want to use camel cxf and java dsl
    can anyone please help me?

    thanks
    pranay

    ReplyDelete
  9. where can I download the complete project jarxrs-camel ???
    I have big trouble to deploy into servicemix.
    Thanks

    ReplyDelete
  10. Pol, are you using the following features?

    featuresBoot=camel,cxf,camel-cxf,camel-jetty,web,camel-http,cxf-jaxrs

    ReplyDelete
  11. I am using 2.8.2 apache camel and used the above camel-cxf example and see the below exception.. Appreciate any help on this...Thank You..

    [ Error startCamelContext : ] : [ Error : org.apache.camel.RuntimeCamelException: org.apache.cxf.service.factory.ServiceConstructionException
    at org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException(ObjectHelper.java:1164)
    at org.apache.camel.spring.SpringCamelContext.onApplicationEvent(SpringCamelContext.java:117)
    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:105)
    Caused by: org.apache.cxf.service.factory.ServiceConstructionException
    at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:190)
    at org.apache.camel.component.cxf.jaxrs.CxfRsConsumer.(CxfRsConsumer.java:38)
    at org.apache.camel.component.cxf.jaxrs.CxfRsEndpoint.createConsumer(CxfRsEndpoint.java:112)
    at org.apache.camel.impl.EventDrivenConsumerRoute.addServices(EventDrivenConsumerRoute.java:61)
    at org.apache.camel.impl.DefaultRoute.onStartingServices(DefaultRoute.java:75)
    at org.apache.camel.impl.RouteService.warmUp(RouteService.java:124)
    at org.apache.camel.impl.DefaultCamelContext.doWarmUpRoutes(DefaultCamelContext.java:1846)
    at org.apache.camel.impl.DefaultCamelContext.safelyStartRouteServices(DefaultCamelContext.java:1774)
    at org.apache.camel.impl.DefaultCamelContext.doStartOrResumeRoutes(DefaultCamelContext.java:1559)
    at org.apache.camel.impl.DefaultCamelContext.doStartCamel(DefaultCamelContext.java:1449)
    at org.apache.camel.impl.DefaultCamelContext.doStart(DefaultCamelContext.java:1338)
    at org.apache.camel.spring.SpringCamelContext.doStart(SpringCamelContext.java:176)
    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:1316)
    at org.apache.camel.spring.SpringCamelContext.maybeStart(SpringCamelContext.java:221)
    at org.apache.camel.spring.SpringCamelContext.onApplicationEvent(SpringCamelContext.java:115)
    ... 11 more
    Caused by: java.io.IOException: Cannot find any registered HttpDestinationFactory from the Bus.
    at org.apache.cxf.transport.http.HTTPTransportFactory.getDestination(HTTPTransportFactory.java:270)
    at org.apache.cxf.endpoint.ServerImpl.initDestination(ServerImpl.java:93)
    at org.apache.cxf.endpoint.ServerImpl.(ServerImpl.java:72)
    at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:149)
    ... 27 more

    ReplyDelete
    Replies
    1. same here .. plz help

      Delete
    2. I also faced same issue today. The solution is to add below dependency in POM. This should be ideally mentioned in article since visitors are expected to new and not knowing this.


      org.apache.cxf
      cxf-rt-transports-http
      ${cxf-version}
      test


      org.apache.cxf
      cxf-rt-transports-http-jetty
      ${cxf-version}
      test

      Delete