Monday, May 14, 2012

Rest Service in Camel using CXF and Beans

This tutorial shows how to create a simple rest interface using the CXF component and beans in Camel. It is pretty straight forward but it shows all the basics. You can find the source code at: https://github.com/camelandjava/camelandjava-I

Steps:

  1. add the required dependencies in your pom
  2. create the service interface
  3. implement the interface
  4. define the bean in the camel xml context
  5. add the route for the service

1. Add dependencies:

                <!-- CXF -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-cxf</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jetty</artifactId>
<version>2.9.1</version>
</dependency>

2. Create the service interface

@Path("/service")
@Produces("text/xml")
public interface MyService {

@GET
@Path("/resource/{resourceId}")
public Response getResource(@PathParam("resourceId") String resourceId);
}


3. Implement the interface


public class MyServiceImpl implements MyService {

private HashMap<String, String> myResources;

private String xmlResponse = "<response code='200'><resource>REPLACE_ME</resource></response>";
private String xmlError = "<response code='400'><reason>REPLACE_ME</reason></response>";

/*
* (non-Javadoc)
*
* @see camel.workshop.service.MyService#getResource(java.lang.String)
*/

public MyServiceImpl() {
myResources = new HashMap<String, String>();
myResources.put("1", "Resouce one");
myResources.put("2", "Resouce two");
myResources.put("3", "Resouce three");
myResources.put("4", "Resouce four");
}

@Override
public Response getResource(String resourceId) {
String ret = myResources.get(resourceId);

if (ret == null) {
return Response.ok(replaceString("unknown resource", xmlError))
.build();
}

return Response.ok(replaceString(ret, xmlResponse)).build();
}

/**
* @param ret
* @return
*/
private String replaceString(String ret, String response) {
return response.replace("REPLACE_ME", ret);
}
}

4. Define the bean

ss
<?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:cxf="http://camel.apache.org/schema/cxf"
xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://camel.apache.org/schema/cxf
       http://camel.apache.org/schema/cxf/camel-cxf.xsd
       http://camel.apache.org/schema/spring
       http://camel.apache.org/schema/spring/camel-spring.xsd"
>

<!-- defining the bean -->
<ben id="resourceService" class="camel.workshop.service.impl.MyServiceImpl" />

<bean id="orderHandler" class="camel.workshop.bean.OrderHandler"/>

<camelContext xmlns="http://camel.apache.org/schema/spring">
<package>camel.workshop.routes</package>
</camelContext>

</beans>

5. Add the route for the service


public class RestService extends RouteBuilder {

/*
* (non-Javadoc)
*
* @see org.apache.camel.builder.RouteBuilder#configure()
*/
@Override
public void configure() throws Exception {

getContext().setTracing(true);

from("jetty:http://localhost:8032?matchOnUriPrefix=true")
.to("cxfbean:resourceService");
}

}


You can now run your service using the Main defined in RouteWithBeans and use a browser to check the response for an existing resource:

Or for one not in the set:


9 comments:

  1. Good example ..could you enhance your example to show how to support more than one webservice ?

    ReplyDelete
  2. Hi,

    If you want to have several services at localhost:8032 you probably want to follow one of the approaches below:


    Approach A)

    Define two routes:

    from("jetty:http://localhost:8032/service1?matchOnUriPrefix=true")
    .to("cxfbean:resourceService1");

    from("jetty:http://localhost:8032/service2?matchOnUriPrefix=true")
    .to("cxfbean:resourceService2");

    and add two definitions of cxf beans in the camel context using the same way shown in the post.

    Approach B)

    if you want to have several rest calls implemented in the same bean, lets say:

    - http://localhost:8032/product/something
    - http://localhost:8032/customer/something

    you can use one bean annotated with @Path("/service") and some methods specifying their own relative path:

    @Path("/")
    @Produces("text/xml")
    public interface MyService {

    @GET
    @Path("/product/{productId}")
    public Response getProduct(@PathParam("productId") String productId);

    @GET
    @Path("/resource/{customerId}")
    public Response getCustomer(@PathParam("customerId") String customerId);

    }

    It all depends on your design. I would go with A if you have services that does different things like focusing on different aspects of your application.
    Solution B it's the best one if you want to implement CRUD operations over a bean like Product or if you need methods like getProduct(id), getAllProducts() getProductByOrder(order) and so forth.

    ReplyDelete
  3. Once you're done, how do you embed the jar in Spring and run it? Please help.

    ReplyDelete
    Replies
    1. Hi

      I don't exactly understand your question... Did you check the github location? It might be helpful.
      If you want to run the api in a container you need to deploy it in a web container.

      Best

      Delete
  4. can you please give me example for POST which will accept 2 string as input . i m facing issue in decoding the http body in post . can you please provide one complete program email id: amitkumarmannur@gmail.com please .,

    ReplyDelete
  5. This comment has been removed by a blog administrator.

    ReplyDelete
  6. Hi Mariano,
    Can you provide the same example with XML configuration for the camel route.

    ReplyDelete
  7. HI,
    I am a novice in Apache Camel. Can you please explain the reason why have you created interface for web service? Why can't we
    directly create a class for that? and Can you provide some more links for apache camel tutorial

    ReplyDelete
  8. Is there any way to invoke another rest call in , if we invoke means how can i pass input to that service and how i need to catch response .

    ReplyDelete