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:


4 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