Tag: cxfrs

Camel CXFRS and multiple @PathParam and @QueryParam arguments

Hi,

In this article, i will try to explain how to capture multiple @PathParam variables and the @QueryParam variables from your GET request. The code looks as below:

package com.examples.camel.cxf.rest.resource;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.jws.WebService;

@Path("/employee")
@WebService
public interface EmployeeIntf {

	@GET
	@Path("/getdetails/{empname}/id/{empid}")
	@Produces(MediaType.TEXT_PLAIN)
	public Response getEmployeeCredentails(@PathParam("empname") String name,
			@PathParam("empid") String id,
			@QueryParam("dept") String department);
}

And the implementation class will go as follows:

package com.examples.camel.cxf.rest.resource;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

public class EmployeeResource implements EmployeeIntf {

	public Response getEmployeeCredentails(String name, String id,
			String department) {

		StringBuilder response = new StringBuilder();
		response.append("Name\t\t:\t" + name + "\n" +
				"Id\t\t:\t" + id + "\n" +
				"Department\t\t:\t" + department + "\n");

		//code to fetch employee credentials from DB

		response.append("\nYour Credentials are: \n");
		response.append("User Name\t:\t" + name + "_admin" + "\n" );
		response.append("Password\t:\t Welcome@123");

		return Response.status(Status.OK).entity(response.toString()).build();
	}
}

If you have already tried to do this you must have noticed that only the first argument of the method is passed with the input value where as the rest of the arguments receive a null value. Below is the output that you can see when
the request is made to the above URL.
default_cxfrs_output

While using cxfrs component as a consumer in camel route, When a request is made to the server the exchange body will be of type org.apache.cxf.message.MessageContentsList. In order to process the input data, you have two options:

Option – A

Use the @Header annotation provided by the camel APIs in the web service interface definition. The web service interface will look as below in this case:

public interface EmployeeIntf {
@GET
@Path("/getdetails/{empname}/id/{empid}")
@Produces(MediaType.TEXT_PLAIN)
public Response getEmployeeCredentails(String httpPath, String httpQuery);
}

And the corresponding implementation class will be as follows:

	public Response getEmployeeCredentails(@Header("CamelHttpPath") String httpPath,
			@Header("CamelHttpQuery") String httpQuery) {	

		String response = "Input Path is : " + httpPath
				+ "\nInput Query is : " + httpQuery;

		//parse the stringss httpPath and httpQuery and read the input data.
		return Response.status(Status.OK).entity(response.toString()).build();
	}

The output in this case will look as follows:
HeaderAnnotation_Output

As you can see the problem with this approach is that programmer has to manually parse the input URL and fetch the required data. This might be a cumbersome task and is not recommended.

Option – B

Another way to deal with this problem is to use @BeanParam annotation in the method.
1. Create a Bean that will contain the list of @PathParam and @QueryParam variables.
2. Modify the method signature of your resource and specify the bean as an input variable.

This way we can get rid of manually parsing the input request URL. The sample bean class i have created to store the details are:

public class EmployeeBeanParam {

	@PathParam("empname")
	private String name;
	@PathParam("empid")
	private String id;
	@QueryParam("department")
	private String department;

        //getters and setters 

        public String toString() {
		return "Emp Name : \t" + this.name + "\n" +
				"Emp ID : \t " + this.id + "\n" +
				"Department : \t" + this.department + "\n";
	}
}

With this, the interface definition will look as below:

@GET
	@Path("/getdetails/{empname}/id/{empid}")
	@Produces(MediaType.TEXT_PLAIN)
	public Response getEmployeeCredentails(@BeanParam EmployeeBeanParam empBeanParam);

And the corresponding implementation class looks like:

public Response getEmployeeCredentails(EmployeeBeanParam empBeanParam) {

		System.out.println("In empBeanParam.....the body obj is " + empBeanParam.equals(null));

		StringBuilder response = new StringBuilder();
		response.append("Name\t\t:\t" + empBeanParam.getName() + "\n" +
				"Id\t\t:\t" + empBeanParam.getId() + "\n" +
				"Department\t\t:\t" + empBeanParam.getDepartment() + "\n");

		//code to fetch employee credentials from a persistent storage

		response.append("\nYour Credentials are: \n");
		response.append("User Name\t:\t" + empBeanParam.getName() + "_admin" + "\n" );
		response.append("Password\t:\t Welcome@123");

		return Response.status(Status.OK).entity(response.toString()).build();
	}

Now in the camel route, simply use the camel’s converTo() method provided in the Java DSL to convert the input request to the type EmployeeBeanParam. Below is the complete code of the route implemented in spring DSL:

<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"
       xmlns:jaxrs="http://cxf.apache.org/jaxrs"
       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://cxf.apache.org/jaxrs
       http://cxf.apache.org/schemas/jaxrs.xsd
       http://camel.apache.org/schema/spring
       http://camel.apache.org/schema/spring/camel-spring.xsd
    ">
     <bean id="employeeResource" class="com.examples.camel.cxf.rest.resource.EmployeeResource" />

<cxf:rsServer id="myServer" address="/myapp" loggingFeatureEnabled="true">
     	<cxf:serviceBeans>
     		<ref bean="employeeResource" />
     	</cxf:serviceBeans>
     	<cxf:providers>
     		<bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
     	</cxf:providers>
     </cxf:rsServer>

<camelContext id="context" xmlns="http://camel.apache.org/schema/spring">
    <route>
        <from uri="cxfrs:bean:myServer"/>
        <choice>
           <when>
                <simple>${header.operationName} == 'getEmployeeCredentails'</simple>
                <log message="[CAMEL-LOG-INFO]: Method invoked is ${header.operationName}"/>
                <to uri="direct:getEmpCredentials"/>
            </when>
        </choice>
    </route>
    <route>
        <from uri="direct:getEmpCredentials"/>
        <log message="Before converting the body type, Body is ***${body}***"/>
        <convertBodyTo type="com.examples.camel.beans.EmployeeBeanParam"/>
        <log message="After converting the body type, Body is ***${body}***"/>
        <bean ref="employeeResource" method="getEmployeeCredentails"/>
    </route>

</camelContext>
</beans>

Camel’s built-in data type converter (highlighted in line #40) will take care of converting the message type in exchange to the type EmployeeBeanParam before passing it to the service implementation method. The output returned in this case will look as follows:

BeanParam_Output

One other way to handle the problem is by using a custom bean / processor in between the route which will read the exchange object i.e., object of type org.apache.cxf.message.MessageContentsList which is of type ArrayList, iterate over the list and fetch the required content. This way of handling is helpful if you want to validate the URL and add more information before invoking the actual web service method.

To know on how to use the cxfrs component read my earlier blog – Using Apache Camel’s cxfrs component

If you find other ways of handling the REST requests which are easier from the ones listed above, kindly share. Hope this was helpful.

References:
https://dzone.com/articles/camel-cxf-service-multiple
http://stackoverflow.com/questions/19714684/jax-rs-and-camel-except-1st-queryparameter-all-others-are-null

Thanks,
Kalyan

REST web service using Apache Camel CXFRS component

Hi,

 

In this post, i will try to explain how to write a web service using Apache camel cxfrs component. It will be a simple Hello World web service that will accept a GET and a POST request and returns a plain text output for the GET request and json object for the POST request.

Following are the components used for this demo.

  1. Apache Camel Core
  2. Apache Camel CXFRS component
  3. Spring core, context, web, beans
  4. Jackson library – to handle JSON data
  5. Jetty server
  6. JBoss Server

1. Start by creating a maven project – Choose the archetype of webapp. The project structure will appear as follows:

camel-cxf-rs
|
|
— src
|
|
— main
|
|
— java (contains all java packages)
— resources – contains “camel-cxfrs-config.xml”
— webapp
|
|
— WEB-INF – contains “web.xml”

2. Add the dependencies for the components listed in #1 to #4 in your pom.xml file. The result will look as below.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.examples.camel.cxf.rest</groupId>
  <artifactId>camel-cxf-rest</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>camel-cxf-rest Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	<camelspring.version>2.13.2</camelspring.version>		
	<spring.version>3.2.10.RELEASE</spring.version>
  </properties>
  <dependencies>
         <!-- Camel Dependencies -->
         <dependency>
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-core</artifactId>
             <version>${camelspring.version}</version>
         </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-cxf</artifactId>
             <version>${camelspring.version}</version>
         </dependency>
         <!-- End of Camel Dependencies -->

          <!-- Spring Dependencies -->
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-core</artifactId>
             <version>${spring.version}</version>
         </dependency>
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-context</artifactId>
             <version>${spring.version}</version>
         </dependency>
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-context-support</artifactId>
             <version>${spring.version}</version>
         </dependency>
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-web</artifactId>
             <version>${spring.version}</version>
         </dependency>
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-beans</artifactId>
             <version>${spring.version}</version>
         </dependency>
         <!-- End of Spring dependencies -->

         <!-- Jackson dependencies -->
         <dependency>
             <groupId>org.codehaus.jackson</groupId>
             <artifactId>jackson-jaxrs</artifactId>
             <version>1.9.12</version>
         </dependency>
         <dependency>
             <groupId>org.codehaus.jackson</groupId>
             <artifactId>jackson-core-asl</artifactId>
             <version>1.9.12</version>
         </dependency>
         <!-- End of Jackson dependencies -->

         <!-- Jetty Server dependencies -->
         <dependency>
             <groupId>org.apache.cxf</groupId>
             <artifactId>cxf-rt-transports-http-jetty</artifactId>
             <version>2.7.11</version>
         </dependency>
         <!-- End of Jetty dependencies -->

</dependencies>

After adding the dependencies, start creating the required resources. First, we will start configuring the servlet which listens to the input HTML requests. This is configured in WEB-INF/web.xml file. The contents of the web.xml will look as below:

<web-app>
 <display-name>Apache Camel CXF Rest Web Application</display-name>
 <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:camel-cxfrs-config.xml</param-value>
 </context-param>
 <servlet>
     <servlet-name>CXF Servlet</servlet-name>
     <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
 </servlet>
 <servlet-mapping>
     <servlet-name>CXF Servlet</servlet-name>
     <url-pattern>/webapi/*</url-pattern>
 </servlet-mapping>
 	<listener>
     	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
</web-app>

Now we will start by coding the java resources that will be used to handle the REST request. There will be an interface and an implementation class (Resource). The interface will specify the web service annotations and the implementation class is used to respond to the client requests. The interface will look as below:

package com.examples.camel.cxf.rest.resource;

import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.jws.WebService;

@Path("/hello")
@WebService
public interface HelloWorldIntf
{
   @GET
   @Path("/greet")
   @Produces(MediaType.TEXT_PLAIN)
   public Response greet();

   @POST
   @Path("/sayhello")
   @Produces(MediaType.APPLICATION_JSON)
   public Response sayHello(String input);
}

And below is the implementation class. Note that for the sake of convenience, i have written a ‘Hello’ class in the same file “HelloWorldResource.java”. You may wish to write this in a different file. An object of this class is returned to the POST request in JSON format.

Also note that the “sayHello” method which accepts a POST request does not have a @Consumes annotation. Reason being, you can send any of the content type with the resource URL and the sayHello method will get invoked. We will see two examples by passing TEXT_PLAIN & XML input and see the output in both the cases.

package com.examples.camel.cxf.rest.resource;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

public class HelloWorldResource implements HelloWorldIntf
{
    public Response greet() {

       return Response.status(Status.OK).
                 entity("Hi There!!").
                    build();
    }

    public Response sayHello(String input) {
       Hello hello = new Hello();
       hello.setHello("Hello");
       hello.setName("Default User");

        if(input != null)
           hello.setName(input);

       return Response.
                 status(Status.OK).
                   entity(hello).
                     build();
    }
}

class Hello {
   private String hello;
   private String name;

   public String getHello() { return hello; }
   public void setHello(String hello) { this.hello = hello; }

   public String getName() { return name; }
   public void setName(String name) { this.name = name; }
}

 

Finally, the most important item i.e., camel configuration. The camel configuration will declare the server using the jaxrs component. This will be listening to the incoming requests. This file is named as ‘camel-cxfrs-config.xml’

 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://w3.org/2001/XMLSchema-instance"
        xmlns:cxf="http://camel.apache.org/schema/cxf"
        xmlns:jaxrs="http://cxf.apache.org/jaxrs"
        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://cxf.apache.org/jaxrs
        http://cxf.apache.org/schemas/jaxrs.xsd
        http://camel.apache.org/schema/spring
        http://camel.apache.org/schema/spring/camel-spring.xsd
 >

 <import resource="classpath:META-INF/cxf/cxf.xml" />
 <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

 <bean id="helloBean" class="com.examples.camel.cxf.rest.resource.HelloWorldResource" />

 <cxf:rsServer id="helloServer" address="/helloapp" loggingFeatureEnabled="true">
   <cxf:serviceBeans>
      <ref bean="helloBean" />
   </cxf:serviceBeans>
   <cxf:providers>
      <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
   </cxf:providers>
 </cxf:rsServer>

 <camelContext id="context" xmlns="http://camel.apache.org/schema/spring">
   <route>
     <from uri="cxfrs:bean:helloServer />
     <log message="Processing CXF route....http method ${header.CamelHttpMethod}" />
     <log message="Processing CXF route....path is ${header.CamelHttpPath}" />
     <log message="Processing CXF route....body is ${body}" />
     <choice>
       <when>
         <simple>${header.operationName} == 'sayHello'</simple>
         <to uri="direct:invokeSayHello" />
       </when>
       <when>
         <simple>${header.operationName} == 'greet'</simple>
         <to uri="direct:invokeGreet" />
       </when>
     </choice>
   </route>

   <route id="invokeSayHello">
      <from uri="direct:invokeSayHello" />
      <bean ref="helloBean" method="sayHello" />
   </route>
   <route id="invokeGreet">
      <from uri="direct:invokeGreet" />
      <bean ref="helloBean" method="greet" />
   </route>
 </camelContext>

 </beans>

The file is placed in src/main/resources. In the build path it is configured to point to the output directory target/classes. Run and deploy the war file – camel-cxf-rest.war in the server.

After the deployment, you can make requests from the browser with the below URL


 

GET Request

URL – http://localhost:8080/camel-cxf-rest/webapi/helloapp/hello/greet

Output:

Hi There!!!

In the server console log, you can see the below log message generated when the request is serviced.

14:02:22,057 INFO  [org.apache.cxf.interceptor.LoggingInInterceptor] (http-localhost/127.0.0.1:8080-5) Inbound Message
----------------------------
ID: 9
Address: http://localhost:8080/camel-cxf-rest/webapi/helloapp/hello/greet
Http-Method: GET
Content-Type: 
Headers: {Accept=[text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8], accept-encoding=[gzip, deflate, sdch], accept-language=[en-US,en;q=0.8], cache-control=[max-age=0], connection=[keep-alive], Content-Type=[null], host=[localhost:8080], user-agent=[Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36]}
--------------------------------------
14:02:22,060 INFO  [route1] (http-localhost/127.0.0.1:8080-5) [CAMEL-LOG-INFO]: processing the CXF route.....method invoked is GET
14:02:22,060 INFO  [route1] (http-localhost/127.0.0.1:8080-5) [CAMEL-LOG-INFO]: processing the CXF route.....CamelHttpPath is /hello/greet
14:02:22,061 INFO  [route1] (http-localhost/127.0.0.1:8080-5) [CAMEL-LOG-INFO]: processing the CXF route.....CamelHttpQuery is 
14:02:22,062 INFO  [route1] (http-localhost/127.0.0.1:8080-5) [CAMEL-LOG-INFO]: processing the CXF route....body of the message is 
14:02:22,062 INFO  [route1] (http-localhost/127.0.0.1:8080-5) [CAMEL-LOG-INFO]: invoked method.....greet
14:02:22,065 INFO  [org.apache.cxf.interceptor.LoggingOutInterceptor] (http-localhost/127.0.0.1:8080-5) Outbound Message
---------------------------
ID: 9
Response-Code: 200
Content-Type: text/plain
Headers: {Content-Type=[text/plain], Date=[Tue, 21 Jul 2015 08:32:22 GMT]}
Payload: Hi There!!!
--------------------------------------


 

POST Request

URL – http://localhost:8080/camel-cxf-rest/webapi/helloapp/hello/sayhello

In request body, plain text is sent  – “James Gosling”

Output:

{

hello : “Hello”,

name: “James Gosling”

}

In the server console log, you will the below messages:

14:08:18,251 INFO  [org.apache.cxf.interceptor.LoggingInInterceptor] (http-localhost/127.0.0.1:8080-5) Inbound Message
----------------------------
ID: 1
Address: http://localhost:8080/camel-cxf-rest/webapi/helloapp/hello/sayhello
Encoding: ISO-8859-1
Http-Method: POST
Content-Type: application/xml
Headers: {Accept=[*/*], accept-encoding=[gzip, deflate], accept-language=[en-US,en;q=0.8], connection=[keep-alive], Content-Length=[14], content-type=[application/xml], csp=[active], host=[localhost:8080], origin=[chrome-extension://hgmloofddffdnphfgcellkdfbfbjeloo], user-agent=[Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36]}
Payload: James Gosling

--------------------------------------
14:08:18,268 INFO  [route1] (http-localhost/127.0.0.1:8080-5) [CAMEL-LOG-INFO]: processing the CXF route.....method invoked is POST
14:08:18,270 INFO  [route1] (http-localhost/127.0.0.1:8080-5) [CAMEL-LOG-INFO]: processing the CXF route.....CamelHttpPath is /hello/sayhello
14:08:18,270 INFO  [route1] (http-localhost/127.0.0.1:8080-5) [CAMEL-LOG-INFO]: processing the CXF route.....CamelHttpQuery is 
14:08:18,270 INFO  [route1] (http-localhost/127.0.0.1:8080-5) [CAMEL-LOG-INFO]: processing the CXF route....body of the message is James Gosling

14:08:18,275 INFO  [route1] (http-localhost/127.0.0.1:8080-5) [CAMEL-LOG=INFO]: invoked method....sayHello
14:08:18,586 INFO  [org.apache.cxf.interceptor.LoggingOutInterceptor] (http-localhost/127.0.0.1:8080-5) Outbound Message
---------------------------
ID: 1
Response-Code: 200
Content-Type: application/json
Headers: {Content-Type=[application/json], Date=[Tue, 21 Jul 2015 08:38:18 GMT]}
Payload: {"hello":"Hello","name":"James Gosling\n"}
--------------------------------------

 

Now make another POST request with the XML input as the body or request payload.

URL – http://localhost:8080/camel-cxf-rest/webapi/helloapp/hello/sayhello

Request Body – 

<employee>

<name>James Gosling</name>

<department>Computers</department>

</employee>

Output:

{

“hello”:”Hello”,

“name”:”<employee>\n <name>James Gosling</name>\n <department>Computers</department>\n</employee>”

}


 

And that is all you require to write a REST web service using cxfrs component.

Before we finish, few points i would like to share with you all.

1. The declaration of the <cxf:rsServer> alone will not initialize the org.apache.cxf.endpoint.ServerImpl, when a route is written with either ‘from’ or ‘to’ using the cxfrs component, then the server’s publish address will be set to the address provided in the <cxf:rsServer> tag.

 

2. The code example of REST service provided in the book “Camel in Action” also works fine but it requires <jaxrs:server>, <cxf:rsServer> and <cxf:rsClient> tags to be present in the configuraiton xml file. Additionally, we require two different ports to serve the request. One port (say 8085) is used by the jaxrs server and the second port (say 8086) is used by the cxf:rsServer.

In such a scenario, if a valid request is sent to address published in <jaxrs:server>, client will receive a response but your route is not executed. And if the request is sent to the address published in <cxf:rsServer> then your route will be executed. In my next post, i will elaborate more on this and try to explain why we require both and .

For configuration, refer to the below link:

http://camel.apache.org/cxfrs.html

 

3. The actual resource class in your web service that points to a valid URL is never executed when the request is received initially. The web service class has to be referred somewhere in the route as a bean i.e., declare your web service class as a bean and use the bean in the ‘to’ section in your route.

 

In my next post, i will try to explain how to make a GET request that will take multiple PathParam’s and QueryParam’s from the input and return a response. Kindly provide your feedback if you find this post useful and any ideas (code examples, not just ideas) to implement this in a better way.

 

References:
https://github.com/arunma/osgicxfcamelmultiparams
http://www.consulting-notes.com/2010/12/basic-rest-service-in-apache-cxf-vs.html
https://access.redhat.com/documentation/en-US/Red_Hat_JBoss_Fuse/6.0/html/EIP_Component_Reference/files/_IDU_CXFRS.html

Thanks,

Kalyan