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.
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:
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:
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