CXF REST — provides integration for linking with JAX-RS based RESTful services
The cxfrs:
component provides integration with Apache CXF for connecting to
JAX-RS services hosted in CXF.
![]() | Note |
---|---|
When using CXF as a consumer, the CAMEL:CXF Bean Component allows you to factor out how message payloads are received from their processing as a RESTful or SOAP web service. This provides the possibility of using a multitude of transports to consume web services. The bean component's configuration is also simpler and provides the fastest method to implement web services using Camel and CXF. |
Maven users need to add the following dependency to their pom.xml
for
this component:
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-cxf</artifactId> <version>x.x.x</version> <!-- use the same version as your Camel core version --> </dependency>
CXF REST endpoints support two URI formats:
cxfrs://address
?options
Where address
represents the CXF endpoint's
address
cxfrs:bean:rsEndpoint
Where rsEndpoint
represents the Spring bean's name
which represents the CXFRS client or server
Using either format, you can append options to the URI like this:
cxfrs:bean:cxfEndpoint?resourceClasses=org.apache.camel.rs.Example
Maven users will need to add a dependency on camel-cxf
to their poms as
shown in Example 2, “Apache CXF dependency”.
Example 2. Apache CXF dependency
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-cxf</artifactId> <version>x.x.x</version> <!-- use the same version as your Camel core version --> </dependency>
If you want to learn about Apache CXF dependencies, see the WHICH-JARS text file.
Table 6, “CXF REST options” lists the options for a CXF REST endpoint. None of these options are required.
Table 6. CXF REST options
Name | Description |
---|---|
resourceClasses
| Specifies the resource classes you want to export as a REST service. Separate multiple classes with a comma. For example:
|
httpClientAPI
|
Apache Camel 2.1 onwards: When true, the CxfRsProducer uses the HttpClientAPI to invoke the service. Defaults to For example: |
synchronous
|
Apache Camel 2.5 onwards: This option lets CxfRsConsumer decide to use the sync or async API to do the underlying work. When false, tries to use async API. Defaults to For example: |
throwExceptionOnFailure |
Apache Camel 2.6 onwards: This option tells the CxfRsProducer to inspect return codes and generates an Exception if the return code is larger than 207. Defaults to For example: |
maxClientCacheSize
| Apache Camel 2.6 onwards: This option allows you to configure the maximum size of the cache. You can set the In message header,
The implementation caches CXF clients or
Defaults to For example: |
setDefaultBus
|
Apache Camel 2.9.0. onwards: Sets the default bus when the CXF endpoint creates a bus by itself. Defaults to For example: |
bus
|
Apache Camel 2.9.0. onwards: Instructs CXF Bus Factory to create a default bus. You use For example: |
bindingStyle
|
Apache Camel 2.11 onwards:. Specifies how requests and responses are mapped to/from Camel. The two possible values are:
For example: |
You can also configure the CXF REST endpoint through the Spring configuration. Since there are lots of differences between the CXF REST client and CXF REST Server, we provide different configurations for them. Please check out the schema file and CXF REST user guide for more information.
In camel-cxf schema file, there are two elements for the REST endpoint definition. cxf:rsServer for REST consumer, cxf:rsClient for REST producer. You can find a Apache Camel REST service route configuration example here.
<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 "> <!-- Defined the real JAXRS back end service --> <jaxrs:server id="restService" address="http://localhost:9002/rest" staticSubresourceResolution="true"> <jaxrs:serviceBeans> <ref bean="customerService"/> </jaxrs:serviceBeans> </jaxrs:server> <!--bean id="jsonProvider" class="org.apache.cxf.jaxrs.provider.JSONProvider"/--> <bean id="customerService" class="org.apache.camel.component.cxf.jaxrs.testbean.CustomerService" /> <!-- Defined the server endpoint to create the cxf-rs consumer --> <cxf:rsServer id="rsServer" address="http://localhost:9000/route" serviceClass="org.apache.camel.component.cxf.jaxrs.testbean.CustomerService" loggingFeatureEnabled="true" loggingSizeLimit="20" skipFaultLogging="true"/> <!-- Defined the client endpoint to create the cxf-rs consumer --> <cxf:rsClient id="rsClient" address="http://localhost:9002/rest" serviceClass="org.apache.camel.component.cxf.jaxrs.testbean.CustomerService" loggingFeatureEnabled="true" skipFaultLogging="true"/> <!-- The camel route context --> <camelContext id="camel" xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="cxfrs://bean://rsServer"/> <!-- We can remove this configure as the CXFRS producer is using the HttpAPI by default --> <setHeader headerName="CamelCxfRsUsingHttpAPI"> <constant>True</constant> </setHeader> <to uri="cxfrs://bean://rsClient"/> </route> </camelContext> </beans>
Available as of Camel 2.11
The Default
binding style is rather low-level, requiring the user
to manually process the MessageContentsList
object coming into the
route. Thus, it tightly couples the route logic with the method signature and parameter
indices of the JAX-RS operation. Somewhat inelegant, difficult and error-prone.
In contrast, the SimpleConsumer
binding style performs the
following mappings, in order to make the request data more
accessible to you within the Camel Message:
JAX-RS Parameters (@HeaderParam, @QueryParam, etc.) are injected as IN message headers. The header name matches the value of the annotation.
The request entity (POJO or other type) becomes the IN message body. If a
single entity cannot be identified in the JAX-RS method signature, it falls back
to the original MessageContentsList
.
Binary @Multipart
body parts become IN message attachments,
supporting DataHandler
, InputStream
,
DataSource
and CXF's Attachment
class.
Non-binary @Multipart
body parts are mapped as IN message
headers. The header name matches the Body Part name.
Additionally, the following rules apply to the Response mapping:
If the message body type is different to
javax.ws.rs.core.Response
(user-built response), a new
Response
is created and the message body is set as the
entity (so long it's not null). The response status code is taken from the
Exchange.HTTP_RESPONSE_CODE
header, or defaults to 200 OK
if not present.
If the message body type is equal to
javax.ws.rs.core.Response
, it means that the user has
built a custom response, and therefore it is respected and it becomes the final
response.
In all cases, Camel headers permitted by custom or default
HeaderFilterStrategy
are added to the HTTP
response.
This binding style can be activated by setting the bindingStyle
parameter in the consumer endpoint to value SimpleConsumer
:
from("cxfrs:bean:rsServer?bindingStyle=SimpleConsumer") .to("log:TEST?showAll=true");
Below is a list of method signatures along with the expected result from the Simple binding.
public Response doAction(BusinessObject
request);
Request payload is placed in IN message body,
replacing the original MessageContentsList.
public Response doAction(BusinessObject request,
@HeaderParam("abcd") String abcd, @QueryParam("defg") String
defg);
Request payload placed in IN message body, replacing the
original MessageContentsList. Both request params mapped as IN message headers with
names abcd and defg.
public Response doAction(@HeaderParam("abcd") String
abcd, @QueryParam("defg") String defg);
Both request params
mapped as IN message headers with names abcd and defg. The original MessageContentsList
is preserved, even though it only contains the 2 parameters.
public Response doAction(@Multipart(value="body1")
BusinessObject request, @Multipart(value="body2") BusinessObject
request2);
The first parameter is transferred as a header
with name body1, and the second one is mapped as header body2. The original
MessageContentsList is preserved as the IN message body.
public Response doAction(InputStream
abcd);
The InputStream is unwrapped from the
MessageContentsList and preserved as the IN message body.
public Response doAction(DataHandler
abcd);
The DataHandler is unwrapped from the
MessageContentsList and preserved as the IN message body.
Given a JAX-RS resource class with this method:
@POST @Path("/customers/{type}") public Response newCustomer(Customer customer, @PathParam("type") String type, @QueryParam("active") @DefaultValue("true") boolean active) { return null; }
Serviced by the following route:
from("cxfrs:bean:rsServer?bindingStyle=SimpleConsumer") .recipientList(simple("direct:${header.operationName}")); from("direct:newCustomer") .log("Request: type=${header.type}, active=${header.active}, customerData=${body}");
The following HTTP request with XML payload (given that the Customer DTO is JAXB-annotated):
POST /customers/gold?active=true Payload: <Customer> <fullName>Raul Kripalani</fullName> <country>Spain</country> <project>Apache Camel</project> </Customer>
Will print the message:
Request: type=gold, active=true, customerData=<Customer.toString() representation>
For more examples on how to process requests and write responses can be found here.
CXF JAXRS front end
implements the JAXRS(JSR311) API, so
we can export the resources classes as a REST service. And we leverage the CXF Invoker
API to turn a REST request into a normal Java object method invocation.
Unlike the camel-restlet
, you don't need to specify the URI template
within your restlet endpoint, CXF take care of the REST request URI to resource class
method mapping according to the JSR311 specification. All you need to do in Apache Camel is
delegate this method request to a right processor or endpoint.
Here is an example of a CXFRS route:
private static final String CXF_RS_ENDPOINT_URI = "cxfrs://http://localhost:" + CXT + "/rest?resourceClasses=org.apache.camel.component.cxf.jaxrs.testbean.CustomerServiceResource"; protected RouteBuilder createRouteBuilder() throws Exception { return new RouteBuilder() { public void configure() { errorHandler(new NoErrorHandlerBuilder()); from(CXF_RS_ENDPOINT_URI).process(new Processor() { public void process(Exchange exchange) throws Exception { Message inMessage = exchange.getIn(); // Get the operation name from in message String operationName = inMessage.getHeader(CxfConstants.OPERATION_NAME, String.class); if ("getCustomer".equals(operationName)) { String httpMethod = inMessage.getHeader(Exchange.HTTP_METHOD, String.class); assertEquals("Get a wrong http method", "GET", httpMethod); String path = inMessage.getHeader(Exchange.HTTP_PATH, String.class); // The parameter of the invocation is stored in the body of in message String id = inMessage.getBody(String.class); if ("/customerservice/customers/126".equals(path)) { Customer customer = new Customer(); customer.setId(Long.parseLong(id)); customer.setName("Willem"); // We just put the response Object into the out message body exchange.getOut().setBody(customer); } else { if ("/customerservice/customers/400".equals(path)) { // We return the remote client IP address this time org.apache.cxf.message.Message cxfMessage = inMessage.getHeader(CxfConstants.CAMEL_CXF_MESSAGE, org.apache.cxf.message.Message.class); ServletRequest request = (ServletRequest) cxfMessage.get("HTTP.REQUEST"); String remoteAddress = request.getRemoteAddr(); Response r = Response.status(200).entity("The remoteAddress is " + remoteAddress).build(); exchange.getOut().setBody(r); return; } if ("/customerservice/customers/123".equals(path)) { // send a customer response back Response r = Response.status(200).entity("customer response back!").build(); exchange.getOut().setBody(r); return; } if ("/customerservice/customers/456".equals(path)) { Response r = Response.status(404).entity("Can't found the customer with uri " + path).build(); throw new WebApplicationException(r); } else { throw new RuntimeCamelException("Can't found the customer with uri " + path); } } } if ("updateCustomer".equals(operationName)) { assertEquals("Get a wrong customer message header", "header1;header2", inMessage.getHeader("test")); String httpMethod = inMessage.getHeader(Exchange.HTTP_METHOD, String.class); assertEquals("Get a wrong http method", "PUT", httpMethod); Customer customer = inMessage.getBody(Customer.class); assertNotNull("The customer should not be null.", customer); // Now you can do what you want on the customer object assertEquals("Get a wrong customer name.", "Mary", customer.getName()); // set the response back exchange.getOut().setBody(Response.ok().build()); } } }); } }; }
The corresponding resource class used to configure the endpoint is defined as an interface:
@Path("/customerservice/") public interface CustomerServiceResource { @GET @Path("/customers/{id}/") Customer getCustomer(@PathParam("id") String id); @PUT @Path("/customers/") Response updateCustomer(Customer customer); }
![]() | Important |
---|---|
The resource class is used to configure the JAXRS properties only. The methods will not be executed during the routing of messages to the endpoint, the route itself is responsible for all processing instead. |
CXF JAXRS front end
implements a proxy
based client API, with this API you can invoke the remote REST service
through a proxy. camel-cxfrs
producer is based on this proxy
API. So, you just need to specify the operation name in the message header
and prepare the parameter in the message body, camel-cxfrs
producer will
generate right REST request for you.
Here is an example
Exchange exchange = template.send("direct://proxy", new Processor() { public void process(Exchange exchange) throws Exception { exchange.setPattern(ExchangePattern.InOut); Message inMessage = exchange.getIn(); setupDestinationURL(inMessage); // set the operation name inMessage.setHeader(CxfConstants.OPERATION_NAME, "getCustomer"); // using the proxy client API inMessage.setHeader(CxfConstants.CAMEL_CXF_RS_USING_HTTP_API, Boolean.FALSE); // set a customer header inMessage.setHeader("key", "value"); // set the parameters , if you just have one parameter // camel will put this object into an Object[] itself inMessage.setBody("123"); } }); // get the response message Customer response = (Customer) exchange.getOut().getBody(); assertNotNull("The response should not be null ", response); assertEquals("Get a wrong customer id ", String.valueOf(response.getId()), "123"); assertEquals("Get a wrong customer name", response.getName(), "John"); assertEquals("Get a wrong response code", 200, exchange.getOut().getHeader(Exchange.HTTP_RESPONSE_CODE)); assertEquals("Get a wrong header value", "value", exchange.getOut().getHeader("key"));
CXF JAXRS front end
also provides a http
centric client API, You can also invoke this API from
camel-cxfrs
producer. You need to specify the HTTP_PATH and Http
method and let the the producer know to use the HTTP centric client by using the URI
option httpClientAPI or set the message header with
CxfConstants.CAMEL_CXF_RS_USING_HTTP_API
. You can turn the response
object to the type class that you specify with
CxfConstants.CAMEL_CXF_RS_RESPONSE_CLASS
.
Exchange exchange = template.send("direct://http", new Processor() { public void process(Exchange exchange) throws Exception { exchange.setPattern(ExchangePattern.InOut); Message inMessage = exchange.getIn(); setupDestinationURL(inMessage); // using the http central client API inMessage.setHeader(CxfConstants.CAMEL_CXF_RS_USING_HTTP_API, Boolean.TRUE); // set the Http method inMessage.setHeader(Exchange.HTTP_METHOD, "GET"); // set the relative path inMessage.setHeader(Exchange.HTTP_PATH, "/customerservice/customers/123"); // Specify the response class , cxfrs will use InputStream as the response object type inMessage.setHeader(CxfConstants.CAMEL_CXF_RS_RESPONSE_CLASS, Customer.class); // set a customer header inMessage.setHeader("key", "value"); // since we use the Get method, so we don't need to set the message body inMessage.setBody(null); } }); // get the response message Customer response = (Customer) exchange.getOut().getBody(); assertNotNull("The response should not be null ", response); assertEquals("Get a wrong customer id ", String.valueOf(response.getId()), "123"); assertEquals("Get a wrong customer name", response.getName(), "John"); assertEquals("Get a wrong response code", 200, exchange.getOut().getHeader(Exchange.HTTP_RESPONSE_CODE)); assertEquals("Get a wrong header value", "value", exchange.getOut().getHeader("key"));
From Apache Camel 2.1, we also support to specify the query parameters from CXFRS URI for the CXFRS HTTP centric client.
Exchange exchange = template.send("cxfrs://http://localhost:" + getPort2() + "/" + getClass().getSimpleName() + "/testQuery?httpClientAPI=true&q1=12&q2=13"
To support the Dynamical routing, you can override the URI's query parameters by
using the CxfConstants.CAMEL_CXF_RS_QUERY_MAP
header to set the parameter
map for it.
Map<String, String> queryMap = new LinkedHashMap<String, String>(); queryMap.put("q1", "new"); queryMap.put("q2", "world"); inMessage.setHeader(CxfConstants.CAMEL_CXF_RS_QUERY_MAP, queryMap);