Search

Chapter 4. Defining REST Services

download PDF

Abstract

Apache Camel supports multiple approaches to defining REST services. In particular, Apache Camel provides the REST DSL (Domain Specific Language), which is a simple but powerful fluent API that can be layered over any REST component and provides integration with OpenAPI.

4.1. Overview of REST in Camel

Overview

Apache Camel provides many different approaches and components for defining REST services in your Camel applications. This section provides a quick overview of these different approaches and components, so that you can decide which implementation and API best suits your requirements.

What is REST?

Representational State Transfer (REST) is an architecture for distributed applications that centers around the transmission of data over HTTP, using only the four basic HTTP verbs: GET, POST, PUT, and DELETE.

In contrast to a protocol such as SOAP, which treats HTTP as a mere transport protocol for SOAP messages, the REST architecture exploits HTTP directly. The key insight is that the HTTP protocol itself, augmented by a few simple conventions, is eminently suitable to serve as the framework for distributed applications.

A sample REST invocation

Because the REST architecture is built around the standard HTTP verbs, in many cases you can use a regular browser as a REST client. For example, to invoke a simple Hello World REST service running on the host and port, localhost:9091, you could navigate to a URL like the following in your browser:

http://localhost:9091/say/hello/Garp

The Hello World REST service might then return a response string, such as:

Hello Garp

Which gets displayed in your browser window. The ease with which you can invoke REST services, using nothing more than a standard browser (or the curl command-line utility), is one of the many reasons why the REST protocol has rapidly gained popularity.

REST wrapper layers

The following REST wrapper layers offer a simplified syntax for defining REST services and can be layered on top of different REST implementations:

REST DSL

The REST DSL (in camel-core) is a facade or wrapper layer that provides a simplified builder API for defining REST services. The REST DSL does not itself provide a REST implementation: it must be combined with an underlying REST implementation. For example, the following Java code shows how to define a simple Hello World service using the REST DSL:

rest("/say")
    .get("/hello/{name}").route().transform().simple("Hello ${header.name}");

For more details, see Section 4.2, “Defining Services with REST DSL”.

Rest component

The Rest component (in camel-core) is a wrapper layer that enables you to define REST services using a URI syntax. Like the REST DSL, the Rest component does not itself provide a REST implementation. It must be combined with an underlying REST implementation.

If you do not explicitly configure an HTTP transport component then the REST DSL automatically discovers which HTTP component to use by checking for available components on the classpath. The REST DSL looks for the default names of any HTTP components and uses the first one it finds. If there are no HTTP components on the classpath and you did not explicitly configure an HTTP transport then the default HTTP component is camel-http.

Note

The ability to automatically discover which HTTP component to use is new in Camel 2.18. It is not available in Camel 2.17.

The following Java code shows how to define a simple Hello World service using the camel-rest component:

from("rest:get:say:/hello/{name}").transform().simple("Hello ${header.name}");

REST implementations

Apache Camel provides several different REST implementations, through the following components:

Spark-Rest component

The Spark-Rest component (in camel-spark-rest) is a REST implementation that enables you to define REST services using a URI syntax. The Spark framework itself is a Java API, which is loosely based on the Sinatra framework (a Python API). For example, the following Java code shows how to define a simple Hello World service using the Spark-Rest component:

from("spark-rest:get:/say/hello/:name").transform().simple("Hello ${header.name}");

Notice that, in contrast to the Rest component, the syntax for a variable in the URI is :name instead of {name}.

Note

The Spark-Rest component requires Java 8.

Restlet component

The Restlet component (in camel-restlet) is a REST implementation that can, in principle, be layered above different transport protocols (although this component is only tested against the HTTP protocol). This component also provides an integration with the Restlet Framework, which is a commercial framework for developing REST services in Java. For example, the following Java code shows how to define a simple Hello World service using the Restlet component:

from("restlet:http://0.0.0.0:9091/say/hello/{name}?restletMethod=get")
    .transform().simple("Hello ${header.name}");

For more details, see Restlet in the Apache Camel Component Reference Guide.

Servlet component

The Servlet component (in camel-servlet) is a component that binds a Java servlet to a Camel route. In other words, the Servlet component enables you to package and deploy a Camel route as if it was a standard Java servlet. The Servlet component is therefore particularly useful, if you need to deploy a Camel route inside a servlet container (for example, into an Apache Tomcat HTTP server or into a JBoss Enterprise Application Platform container).

The Servlet component on its own, however, does not provide any convenient REST API for defining REST services. The easiest way to use the Servlet component, therefore, is to combine it with the REST DSL, so that you can define REST services with a user-friendly API.

For more details, see Servlet in the Apache Camel Component Reference Guide.

JAX-RS REST implementation

JAX-RS (Java API for RESTful Web Services) is a framework for binding REST requests to Java objects, where the Java classes must be decorated with JAX-RS annotations in order to define the binding. The JAX-RS framework is relatively mature and provides a sophisticated framework for developing REST services, but it is also somewhat complex to program.

The JAX-RS integration with Apache Camel is implemented by the CXFRS component, which is layered over Apache CXF. In outline, JAX-RS binds a REST request to a Java class using the following annotations (where this is only an incomplete sample of the many available annotations):

@Path
Annotation that can map a context path to a Java class or map a sub-path to a particular Java method.
@GET, @POST, @PUT, @DELETE
Annotations that map a HTTP method to a Java method.
@PathParam
Annotation that either maps a URI parameter to a Java method argument, or injects a URI parameter into a field.
@QueryParam
Annotation that either maps a query parameter to a Java method argument, or injects a query parameter into a field.

The body of a REST request or REST response is normally expected to be in JAXB (XML) data format. But Apache CXF also supports conversion of JSON format to JAXB format, so that JSON messages can also be parsed.

For more details, see CXFRS in the Apache Camel Component Reference Guide and Apache CXF Development Guide.

Note

The CXFRS component is not integrated with the REST DSL.

4.2. Defining Services with REST DSL

REST DSL is a facade

The REST DSL is effectively a facade that provides a simplified syntax for defining REST services in a Java DSL or an XML DSL (Domain Specific Language). REST DSL does not actually provide the REST implementation, it is just a wrapper around an existing REST implementation (of which there are several in Apache Camel).

Advantages of the REST DSL

The REST DSL wrapper layer offers the following advantages:

  • A modern easy-to-use syntax for defining REST services.
  • Compatible with multiple different Apache Camel components.
  • OpenAPI integration (through the camel-openapi-java component).

Components that integrate with REST DSL

Because the REST DSL is not an actual REST implementation, one of the first things you need to do is to choose a Camel component to provide the underlying implementation. The following Camel components are currently integrated with the REST DSL:

Note

The Rest component (part of camel-core) is not a REST implementation. Like the REST DSL, the Rest component is a facade, providing a simplified syntax to define REST services using a URI syntax. The Rest component also requires an underlying REST implementation.

Configuring REST DSL to use a REST implementation

To specify the REST implementation, you use either the restConfiguration() builder (in Java DSL) or the restConfiguration element (in XML DSL). For example, to configure REST DSL to use the Spark-Rest component, you would use a builder expression like the following in the Java DSL:

restConfiguration().component("spark-rest").port(9091);

And you would use an element like the following (as a child of camelContext) in the XML DSL:

<restConfiguration component="spark-rest" port="9091"/>

Syntax

The Java DSL syntax for defining a REST service is as follows:

rest("BasePath").Option().
    .Verb("Path").Option().[to() | route().CamelRoute.endRest()]
    .Verb("Path").Option().[to() | route().CamelRoute.endRest()]
    ...
    .Verb("Path").Option().[to() | route().CamelRoute];

Where CamelRoute is an optional embedded Camel route (defined using the standard Java DSL syntax for routes).

The REST service definition starts with the rest() keyword, followed by one or more verb clauses that handle specific URL path segments. The HTTP verb can be one of get(), head(), put(), post(), delete(), patch() or verb(). Each verb clause can use either of the following syntaxes:

  • Verb clause ending in to() keyword. For example:

    get("...").Option()+.to("...")
  • Verb clause ending in route() keyword (for embedding a Camel route). For example:

    get("...").Option()+.route("...").CamelRoute.endRest()

REST DSL with Java

In Java, to define a service with the REST DSL, put the REST definition into the body of a RouteBuilder.configure() method, just like you do for regular Apache Camel routes. For example, to define a simple Hello World service using the REST DSL with the Spark-Rest component, define the following Java code:

restConfiguration().component("spark-rest").port(9091);

rest("/say")
    .get("/hello").to("direct:hello")
    .get("/bye").to("direct:bye");

from("direct:hello")
    .transform().constant("Hello World");
from("direct:bye")
    .transform().constant("Bye World");

The preceding example features three different kinds of builder:

restConfiguration()
Configures the REST DSL to use a specific REST implementation (Spark-Rest).
rest()
Defines a service using the REST DSL. Each of the verb clauses are terminated by a to() keyword, which forwards the incoming message to a direct endpoint (the direct component splices routes together within the same application).
from()
Defines a regular Camel route.

REST DSL with XML

In XML, to define a service with the XML DSL, define a rest element as a child of the camelContext element. For example, to define a simple Hello World service using the REST DSL with the Spark-Rest component, define the following XML code (in Blueprint):

<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  <restConfiguration component="spark-rest" port="9091"/>

  <rest path="/say">
    <get uri="/hello">
      <to uri="direct:hello"/>
    </get>
    <get uri="/bye">
      <to uri="direct:bye"/>
    </get>
  </rest>

  <route>
    <from uri="direct:hello"/>
    <transform>
      <constant>Hello World</constant>
    </transform>
  </route>
  <route>
    <from uri="direct:bye"/>
    <transform>
      <constant>Bye World</constant>
    </transform>
  </route>
</camelContext>

Specifying a base path

The rest() keyword (Java DSL) or the path attribute of the rest element (XML DSL) allows you to define a base path, which is then prefixed to the paths in all of the verb clauses. For example, given the following snippet of Java DSL:

rest("/say")
    .get("/hello").to("direct:hello")
    .get("/bye").to("direct:bye");

Or given the following snippet of XML DSL:

<rest path="/say">
  <get uri="/hello">
    <to uri="direct:hello"/>
  </get>
  <get uri="/bye" consumes="application/json">
    <to uri="direct:bye"/>
  </get>
</rest>

The REST DSL builder gives you the following URL mappings:

/say/hello
/say/bye

The base path is optional. If you prefer, you could (less elegantly) specify the full path in each of the verb clauses:

rest()
    .get("/say/hello").to("direct:hello")
    .get("/say/bye").to("direct:bye");

Using Dynamic To

The REST DSL supports the toD dynamic to parameter. Use this parameter to specify URIs.

For example, in JMS a dynamic endpoint URI could be defined in the following way:

public void configure() throws Exception {
   rest("/say")
     .get("/hello/{language}").toD("jms:queue:hello-${header.language}");
}

In XML DSL, the same details would look like this:

<rest uri="/say">
  <get uri="/hello//{language}">
    <toD uri="jms:queue:hello-${header.language}"/>
  </get>
<rest>

For more information about the toD dynamic to parameter, see the section called “Dynamic To”.

URI templates

In a verb argument, you can specify a URI template, which enables you to capture specific path segments in named properties (which are then mapped to Camel message headers). For example, if you would like to personalize the Hello World application so that it greets the caller by name, you could define a REST service like the following:

rest("/say")
    .get("/hello/{name}").to("direct:hello")
    .get("/bye/{name}").to("direct:bye");

from("direct:hello")
    .transform().simple("Hello ${header.name}");
from("direct:bye")
    .transform().simple("Bye ${header.name}");

The URI template captures the text of the {name} path segment and copies this captured text into the name message header. If you invoke the service by sending a GET HTTP Request with the URL ending in /say/hello/Joe, the HTTP Response is Hello Joe.

Embedded route syntax

Instead of terminating a verb clause with the to() keyword (Java DSL) or the to element (XML DSL), you have the option of embedding an Apache Camel route directly into the REST DSL, using the route() keyword (Java DSL) or the route element (XML DSL). The route() keyword enables you to embed a route into a verb clause, with the following syntax:

RESTVerbClause.route("...").CamelRoute.endRest()

Where the endRest() keyword (Java DSL only) is a necessary punctuation mark that enables you to separate the verb clauses (when there is more than one verb clause in the rest() builder).

For example, you could refactor the Hello World example to use embedded Camel routes, as follows in Java DSL:

rest("/say")
    .get("/hello").route().transform().constant("Hello World").endRest()
    .get("/bye").route().transform().constant("Bye World");

And as follows in XML DSL:

<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  ...
  <rest path="/say">
    <get uri="/hello">
      <route>
        <transform>
          <constant>Hello World</constant>
        </transform>
      </route>
    </get>
    <get uri="/bye">
      <route>
        <transform>
          <constant>Bye World</constant>
        </transform>
      </route>
    </get>
  </rest>
</camelContext>
Note

If you define any exception clauses (using onException()) or interceptors (using intercept()) in the current CamelContext, these exception clauses and interceptors are also active in the embedded routes.

REST DSL and HTTP transport component

If you do not explicitly configure an HTTP transport component then the REST DSL automatically discovers which HTTP component to use by checking for available components on the classpath. The REST DSL looks for the default names of any HTTP components and uses the first one it finds. If there are no HTTP components on the classpath and you did not explicitly configure an HTTP transport then the default HTTP component is camel-http.

Specifying the content type of requests and responses

You can filter the content type of HTTP requests and responses using the consumes() and produces() options in Java, or the consumes and produces attributes in XML. For example, some common content types (officially known as Internet media types) are the following:

  • text/plain
  • text/html
  • text/xml
  • application/json
  • application/xml

The content type is specified as an option on a verb clause in the REST DSL. For example, to restrict a verb clause to accept only text/plain HTTP requests, and to send only text/html HTTP responses, you would use Java code like the following:

rest("/email")
    .post("/to/{recipient}").consumes("text/plain").produces("text/html").to("direct:foo");

And in XML, you can set the consumes and produces attributes, as follows:

<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  ...
  <rest path="/email">
    <post uri="/to/{recipient}" consumes="text/plain" produces="text/html">
      <to "direct:foo"/>
    </get>
  </rest>
</camelContext>

You can also specify the argument to consumes() or produces() as a comma-separated list. For example, consumes("text/plain, application/json").

Additional HTTP methods

Some HTTP server implementations support additional HTTP methods, which are not provided by the standard set of verbs in the REST DSL, get(), head(), put(), post(), delete(), patch(). To access additional HTTP methods, you can use the generic keyword, verb(), in Java DSL and the generic element, verb, in XML DSL.

For example, to implement the TRACE HTTP method in Java:

rest("/say")
    .verb("TRACE", "/hello").route().transform();

Where transform() copies the body of the IN message to the body of the OUT message, thus echoing the HTTP request.

To implement the TRACE HTTP method in XML:

<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  ...
  <rest path="/say">
    <verb uri="/hello" method="TRACE">
      <route>
        <transform/>
      </route>
    </get>
</camelContext>

Defining custom HTTP error messages

If your REST service needs to send an error message as its response, you can define a custom HTTP error message as follows:

  1. Specify the HTTP error code by setting the Exchange.HTTP_RESPONSE_CODE header key to the error code value (for example, 400, 404, and so on). This setting indicates to the REST DSL that you want to send an error message reply, instead of a regular response.
  2. Populate the message body with your custom error message.
  3. Set the Content-Type header, if required.
  4. If your REST service is configured to marshal to and from Java objects (that is, bindingMode is enabled), you should ensure that the skipBindingOnErrorCode option is enabled (which it is, by default). This is to ensure that the REST DSL does not attempt to unmarshal the message body when sending the response.

    For more details about object binding, see Section 4.3, “Marshalling to and from Java Objects”.

The following Java example shows how to define a custom error message:

// Java
// Configure the REST DSL, with JSON binding mode
restConfiguration().component("restlet").host("localhost").port(portNum).bindingMode(RestBindingMode.json);

// Define the service with REST DSL
rest("/users/")
    .post("lives").type(UserPojo.class).outType(CountryPojo.class)
        .route()
            .choice()
                .when().simple("${body.id} < 100")
                    .bean(new UserErrorService(), "idTooLowError")
                .otherwise()
                    .bean(new UserService(), "livesWhere");

In this example, if the input ID is a number less than 100, we return a custom error message, using the UserErrorService bean, which is implemented as follows:

// Java
public class UserErrorService {
    public void idTooLowError(Exchange exchange) {
        exchange.getIn().setBody("id value is too low");
        exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "text/plain");
        exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
    }
}

In the UserErrorService bean we define the custom error message and set the HTTP error code to 400.

Parameter Default Values

Default values can be specified for the headers of an incoming Camel message.

You can specify a default value by using a key word such as verbose on the query parameter. For example, in the code below, the default value is false. This means that if no other value is provided for a header with the verbose key, false will be inserted as a default.

rest("/customers/")
    .get("/{id}").to("direct:customerDetail")
    .get("/{id}/orders")
      .param()
	.name("verbose")
	.type(RestParamType.query)
	.defaultValue("false")
	.description("Verbose order details")
      .endParam()
        .to("direct:customerOrders")
    .post("/neworder").to("direct:customerNewOrder");

Wrapping a JsonParserException in a custom HTTP error message

A common case where you might want to return a custom error message is in order to wrap a JsonParserException exception. For example, you can conveniently exploit the Camel exception handling mechanism to create a custom HTTP error message, with HTTP error code 400, as follows:

// Java
onException(JsonParseException.class)
    .handled(true)
    .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(400))
    .setHeader(Exchange.CONTENT_TYPE, constant("text/plain"))
    .setBody().constant("Invalid json data");

REST DSL options

In general, REST DSL options can be applied either directly to the base part of the service definition (that is, immediately following rest()), as follows:

rest("/email").consumes("text/plain").produces("text/html")
    .post("/to/{recipient}").to("direct:foo")
    .get("/for/{username}").to("direct:bar");

In which case the specified options apply to all of the subordinate verb clauses. Or the options can be applied to each individual verb clause, as follows:

rest("/email")
    .post("/to/{recipient}").consumes("text/plain").produces("text/html").to("direct:foo")
    .get("/for/{username}").consumes("text/plain").produces("text/html").to("direct:bar");

In which case the specified options apply only to the relevant verb clause, overriding any settings from the base part.

Table 4.1, “REST DSL Options” summarizes the options supported by the REST DSL.

Table 4.1. REST DSL Options
Java DSLXML DSLDescription

bindingMode()

@bindingMode

Specifies the binding mode, which can be used to marshal incoming messages to Java objects (and, optionally, unmarshal Java objects to outgoing messages). Can have the following values: off (default), auto, json, xml, json_xml.

consumes()

@consumes

Restricts the verb clause to accept only the specified Internet media type (MIME type) in a HTTP Request. Typical values are: text/plain, text/http, text/xml, application/json, application/xml.

customId()

@customId

Defines a custom ID for JMX management.

description()

description

Document the REST service or verb clause. Useful for JMX management and tooling.

enableCORS()

@enableCORS

If true, enables CORS (cross-origin resource sharing) headers in the HTTP response. Default is false.

id()

@id

Defines a unique ID for the REST service, which is useful to define for JMX management and other tooling.

method()

@method

Specifies the HTTP method processed by this verb clause. Usually used in conjunction with the generic verb() keyword.

outType()

@outType

When object binding is enabled (that is, when bindingMode option is enabled), this option specifies the Java type that represents a HTTP Response message.

produces()

produces

Restricts the verb clause to produce only the specified Internet media type (MIME type) in a HTTP Response. Typical values are: text/plain, text/http, text/xml, application/json, application/xml.

type()

@type

When object binding is enabled (that is, when bindingMode option is enabled), this option specifies the Java type that represents a HTTP Request message.

VerbURIArgument

@uri

Specifies a path segment or URI template as an argument to a verb. For example, get(VerbURIArgument).

BasePathArgument

@path

Specifies the base path in the rest() keyword (Java DSL) or in the rest element (XML DSL).

4.3. Marshalling to and from Java Objects

Marshalling Java objects for transmission over HTTP

One of the most common ways to use the REST protocol is to transmit the contents of a Java bean in the message body. In order for this to work, you need to have a mechanism for marshalling the Java object to and from a suitable data format. The following data formats, which are suitable for encoding Java objects, are supported by the REST DSL:

JSON

JSON (JavaScript object notation) is a lightweight data format that can easily be mapped to and from Java objects. The JSON syntax is compact, lightly typed, and easy for humans to read and write. For all of these reasons, JSON has become popular as a message format for REST services.

For example, the following JSON code could represent a User bean with two property fields, id and name:

{
    "id" : 1234,
    "name" : "Jane Doe"
}
JAXB

JAXB (Java Architecture for XML Binding) is an XML-based data format that can easily be mapped to and from Java objects. In order to marshal the XML to a Java object, you must also annotate the Java class that you want to use.

For example, the following JAXB code could represent a User bean with two property fields, id and name:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<User>
  <Id>1234</Id>
  <Name>Jane Doe</Name>
</User>
Note

From Camel 2.17.0, JAXB data format and type converter supports the conversion from XML to POJO for classes, that use ObjectFactory instead of XmlRootElement. Also, the camel context should include the CamelJaxbObjectFactory property with value true. However, due to optimization the default value is false.

Integration of JSON and JAXB with the REST DSL

You could, of course, write the required code to convert the message body to and from a Java object yourself. But the REST DSL offers the convenience of performing this conversion automatically. In particular, the integration of JSON and JAXB with the REST DSL offers the following advantages:

  • Marshalling to and from Java objects is performed automatically (given the appropriate configuration).
  • The REST DSL can automatically detect the data format (either JSON or JAXB) and perform the appropriate conversion.
  • The REST DSL provides an abstraction layer, so that the code you write is not specific to a particular JSON or JAXB implementation. So you can switch the implementation later on, with minimum impact to your application code.

Supported data format components

Apache Camel provides a number of different implementations of the JSON and JAXB data formats. The following data formats are currently supported by the REST DSL:

  • JSON

    • Jackson data format (camel-jackson) (default)
    • GSon data format (camel-gson)
    • XStream data format (camel-xstream)
  • JAXB

    • JAXB data format (camel-jaxb)

How to enable object marshalling

To enable object marshalling in the REST DSL, observe the following points:

  1. Enable binding mode, by setting the bindingMode option (there are several levels at which you can set the binding mode — for details, see the section called “Configuring the binding mode”).
  2. Specify the Java type to convert to (or from), on the incoming message with the type option (required), and on the outgoing message with the outType option (optional).
  3. If you want to convert your Java object to and from the JAXB data format, you must remember to annotate the Java class with the appropriate JAXB annotations.
  4. Specify the underlying data format implementation (or implementations), using the jsonDataFormat option and/or the xmlDataFormat option (which can be specified on the restConfiguration builder).
  5. If your route provides a return value in JAXB format, you are normally expected to set the Out message of the exchange body to be an instance of a class with JAXB annotations (a JAXB element). If you prefer to provide the JAXB return value directly in XML format, however, set the dataFormatProperty with the key, xml.out.mustBeJAXBElement, to false (which can be specified on the restConfiguration builder). For example, in the XML DSL syntax:

    <restConfiguration ...>
      <dataFormatProperty key="xml.out.mustBeJAXBElement"
                          value="false"/>
      ...
    </restConfiguration>
  6. Add the required dependencies to your project build file. For example, if you are using the Maven build system and you are using the Jackson data format, you would add the following dependency to your Maven POM file:

    <?xml version="1.0" encoding="UTF-8"?>
    <project ...>
      ...
      <dependencies>
        ...
        <!-- use for json binding --> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-jackson</artifactId> </dependency>
        ...
      </dependencies>
    </project>
  7. When deploying your application to the OSGi container, remember to install the requisite feature for your chosen data format. For example, if you are using the Jackson data format (the default), you would install the camel-jackson feature, by entering the following Karaf console command:

    JBossFuse:karaf@root> features:install camel-jackson

    Alternatively, if you are deploying into a Fabric environment, you would add the feature to a Fabric profile. For example, if you are using the profile, MyRestProfile, you could add the feature by entering the following console command:

    JBossFuse:karaf@root> fabric:profile-edit --features camel-jackson MyRestProfile

Configuring the binding mode

The bindingMode option is off by default, so you must configure it explicitly, in order to enable marshalling of Java objects. TABLE shows the list of supported binding modes.

Note

From Camel 2.16.3 onwards the binding from POJO to JSon/JAXB will only happen if the content-type header includes json or xml. This allows you to specify a custom content-type if the message body should not attempt to be marshalled using the binding. This is useful if, for example, the message body is a custom binary payload.

Table 4.2. REST DSL BInding Modes
Binding ModeDescription

off

Binding is turned off (default).

auto

Binding is enabled for JSON and/or XML. In this mode, Camel auto-selects either JSON or XML (JAXB), based on the format of the incoming message. You are not required to enable both kinds of data format, however: either a JSON implementation, an XML implementation, or both can be provided on the classpath.

json

Binding is enabled for JSON only. A JSON implementation must be provided on the classpath (by default, Camel tries to enable the camel-jackson implementation).

xml

Binding is enabled for XML only. An XML implementation must be provided on the classpath (by default, Camel tries to enable the camel-jaxb implementation).

json_xml

Binding is enabled for both JSON and XML. In this mode, Camel auto-selects either JSON or XML (JAXB), based on the format of the incoming message. You are required to provide both kinds of data format on the classpath.

In Java, these binding mode values are represented as instances of the following enum type:

org.apache.camel.model.rest.RestBindingMode

There are several different levels at which you can set the bindingMode, as follows:

REST DSL configuration

You can set the bindingMode option from the restConfiguration builder, as follows:

restConfiguration().component("servlet").port(8181).bindingMode(RestBindingMode.json);
Service definition base part

You can set the bindingMode option immediately following the rest() keyword (before the verb clauses), as follows:

rest("/user").bindingMode(RestBindingMode.json).get("/{id}").VerbClause
Verb clause

You can set the bindingMode option in a verb clause, as follows:

rest("/user")
    .get("/{id}").bindingMode(RestBindingMode.json).to("...");

Example

For a complete code example, showing how to use the REST DSL, using the Servlet component as the REST implementation, take a look at the Apache Camel camel-example-servlet-rest-blueprint example. You can find this example by installing the standalone Apache Camel distribution, apache-camel-2.23.2.fuse-7_10_0-00018-redhat-00001.zip, which is provided in the extras/ subdirectory of your Fuse installation.

After installing the standalone Apache Camel distribution, you can find the example code under the following directory:

ApacheCamelInstallDir/examples/camel-example-servlet-rest-blueprint

Configure the Servlet component as the REST implementation

In the camel-example-servlet-rest-blueprint example, the underlying implementation of the REST DSL is provided by the Servlet component. The Servlet component is configured in the Blueprint XML file, as shown in Example 4.1, “Configure Servlet Component for REST DSL”.

Example 4.1. Configure Servlet Component for REST DSL

<?xml version="1.0" encoding="UTF-8"?>
<blueprint ...>

  <!-- to setup camel servlet with OSGi HttpService -->
  <reference id="httpService" interface="org.osgi.service.http.HttpService"/>

  <bean class="org.apache.camel.component.servlet.osgi.OsgiServletRegisterer"
        init-method="register"
        destroy-method="unregister">
    <property name="alias" value="/camel-example-servlet-rest-blueprint/rest"/>
    <property name="httpService" ref="httpService"/>
    <property name="servlet" ref="camelServlet"/>
  </bean>

  <bean id="camelServlet" class="org.apache.camel.component.servlet.CamelHttpTransportServlet"/>
  ...
  <camelContext xmlns="http://camel.apache.org/schema/blueprint">

    <restConfiguration component="servlet"
                       bindingMode="json"
                       contextPath="/camel-example-servlet-rest-blueprint/rest"
                       port="8181">
      <dataFormatProperty key="prettyPrint" value="true"/>
    </restConfiguration>
    ...
  </camelContext>

</blueprint>

To configure the Servlet component with REST DSL, you need to configure a stack consisting of the following three layers:

REST DSL layer
The REST DSL layer is configured by the restConfiguration element, which integrates with the Servlet component by setting the component attribute to the value, servlet.
Servlet component layer
The Servlet component layer is implemented as an instance of the class, CamelHttpTransportServlet, where the example instance has the bean ID, camelServlet.
HTTP container layer

The Servlet component must be deployed into a HTTP container. The Karaf container is normally configured with a default HTTP container (a Jetty HTTP container), which listens for HTTP requests on the port, 8181. To deploy the Servlet component to the default Jetty container, you need to do the following:

  1. Get an OSGi reference to the org.osgi.service.http.HttpService OSGi service, where this service is a standardised OSGi interface that provides access to the default HTTP server in OSGi.
  2. Create an instance of the utility class, OsgiServletRegisterer, to register the Servlet component in the HTTP container. The OsgiServletRegisterer class is a utility that simplifies managing the lifecycle of the Servlet component. When an instance of this class is created, it automatically calls the registerServlet method on the HttpService OSGi service; and when the instance is destroyed, it automatically calls the unregister method.

Required dependencies

This example has two dependencies which are of key importance to the REST DSL, as follows:

Servlet component

Provides the underlying implementation of the REST DSL. This is specified in the Maven POM file, as follows:

<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-servlet</artifactId>
  <version>${camel-version}</version>
</dependency>

And before you deploy the application bundle to the OSGi container, you must install the Servlet component feature, as follows:

JBossFuse:karaf@root> features:install camel-servlet
Jackson data format

Provides the JSON data format implementation. This is specified in the Maven POM file, as follows:

<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-jackson</artifactId>
  <version>${camel-version}</version>
</dependency>

And before you deploy the application bundle to the OSGi container, you must install the Jackson data format feature, as follows:

JBossFuse:karaf@root> features:install camel-jackson

Java type for responses

The example application passes User type objects back and forth in HTTP Request and Response messages. The User Java class is defined as shown in Example 4.2, “User Class for JSON Response”.

Example 4.2. User Class for JSON Response

// Java
package org.apache.camel.example.rest;

public class User {

    private int id;
    private String name;

    public User() {
    }

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

The User class has a relatively simple representation in the JSON data format. For example, a typical instance of this class expressed in JSON format is:

{
    "id" : 1234,
    "name" : "Jane Doe"
}

Sample REST DSL route with JSON binding

The REST DSL configuration and the REST service definition for this example are shown in Example 4.3, “REST DSL Route with JSON Binding”.

Example 4.3. REST DSL Route with JSON Binding

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           ...>
  ...
  <!-- a bean for user services -->
  <bean id="userService" class="org.apache.camel.example.rest.UserService"/>

  <camelContext xmlns="http://camel.apache.org/schema/blueprint">

    <restConfiguration component="servlet"
                       bindingMode="json"
                       contextPath="/camel-example-servlet-rest-blueprint/rest"
                       port="8181">
      <dataFormatProperty key="prettyPrint" value="true"/>
    </restConfiguration>

    <!-- defines the REST services using the  base path, /user -->
    <rest path="/user" consumes="application/json" produces="application/json">
      <description>User rest service</description>

      <!-- this is a rest GET to view a user with the given id -->
      <get uri="/{id}" outType="org.apache.camel.example.rest.User">
        <description>Find user by id</description>
        <to uri="bean:userService?method=getUser(${header.id})"/>
      </get>

      <!-- this is a rest PUT to create/update a user -->
      <put type="org.apache.camel.example.rest.User">
        <description>Updates or create a user</description>
        <to uri="bean:userService?method=updateUser"/>
      </put>

      <!-- this is a rest GET to find all users -->
      <get uri="/findAll" outType="org.apache.camel.example.rest.User[]">
        <description>Find all users</description>
        <to uri="bean:userService?method=listUsers"/>
      </get>

    </rest>

  </camelContext>

</blueprint>

REST operations

The REST service from Example 4.3, “REST DSL Route with JSON Binding” defines the following REST operations:

GET /camel-example-servlet-rest-blueprint/rest/user/{id}
Get the details for the user identified by {id}, where the HTTP response is returned in JSON format.
PUT /camel-example-servlet-rest-blueprint/rest/user
Create a new user, where the user details are contained in the body of the PUT message, encoded in JSON format (to match the User object type).
GET /camel-example-servlet-rest-blueprint/rest/user/findAll
Get the details for all users, where the HTTP response is returned as an array of users, in JSON format.

URLs to invoke the REST service

By inspecting the REST DSL definitions from Example 4.3, “REST DSL Route with JSON Binding”, you can piece together the URLs required to invoke each of the REST operations. For example, to invoke the first REST operation, which returns details of a user with a given ID, the URL is built up as follows:

http://localhost:8181
In restConfiguration, the protocol defaults to http and the port is set explicitly to 8181.
/camel-example-servlet-rest-blueprint/rest
Specified by the contextPath attribute of the restConfiguration element.
/user
Specified by the path attribute of the rest element.
/{id}
Specified by the uri attribute of the get verb element.

Hence, it is possible to invoke this REST operation with the curl utility, by entering the following command at the command line:

curl -X GET -H "Accept: application/json" http://localhost:8181/camel-example-servlet-rest-blueprint/rest/user/123

Similarly, the remaining REST operations could be invoked with curl, by entering the following sample commands:

curl -X GET -H "Accept: application/json" http://localhost:8181/camel-example-servlet-rest-blueprint/rest/user/findAll

curl -X PUT -d "{ \"id\": 666, \"name\": \"The devil\"}" -H "Accept: application/json" http://localhost:8181/camel-example-servlet-rest-blueprint/rest/user

4.4. Configuring the REST DSL

Configuring with Java

In Java, you can configure the REST DSL using the restConfiguration() builder API. For example, to configure the REST DSL to use the Servlet component as the underlying implementation:

restConfiguration().component("servlet").bindingMode("json").port("8181")
    .contextPath("/camel-example-servlet-rest-blueprint/rest");

Configuring with XML

In XML, you can configure the REST DSL using the restConfiguration element. For example, to configure the REST DSL to use the Servlet component as the underlying implementation:

<?xml version="1.0" encoding="UTF-8"?>
<blueprint ...>
  ...
  <camelContext xmlns="http://camel.apache.org/schema/blueprint">
    ...
    <restConfiguration component="servlet"
                       bindingMode="json"
                       contextPath="/camel-example-servlet-rest-blueprint/rest"
                       port="8181">
      <dataFormatProperty key="prettyPrint" value="true"/>
    </restConfiguration>
    ...
  </camelContext>

</blueprint>

Configuration options

Table 4.3, “Options for Configuring REST DSL” shows options for configuring the REST DSL using the restConfiguration() builder (Java DSL) or the restConfiguration element (XML DSL).

Table 4.3. Options for Configuring REST DSL
Java DSLXML DSLDescription

component()

@component

Specifies the Camel component to use as the REST transport (for example, servlet, restlet, spark-rest, and so on). The value can either be the standard component name or the bean ID of a custom instance. If this option is not specified, Camel looks for an instance of RestConsumerFactory on the classpath or in the bean registry.

scheme()

@scheme

The protocol to use for exposing the REST service. Depends on the underlying REST implementation, but http and https are usually supported. Default is http.

host()

@host

The hostname to use for exposing the REST service.

port()

@port

The port number to use for exposing the REST service.

Note: This setting is ignored by the Servlet component, which uses the container’s standard HTTP port instead. In the case of the Apache Karaf OSGi container, the standard HTTP port is normally 8181. It is good practice to set the port value nonetheless, for the sake of JMX and tooling.

contextPath()

@contextPath

Sets a leading context path for the REST services. This can be used with components such as Servlet, where the deployed Web application is deployed using a context-path setting.

hostNameResolver()

@hostNameResolver

If a hostname is not set explicitly, this resolver determines the host for the REST service. Possible values are RestHostNameResolver.localHostName (Java DSL) or localHostName (XML DSL), which resolves to the host name format; and RestHostNameResolver.localIp (Java DSL) or localIp (XML DSL), which resolves to the dotted decimal IP address format. From Camel 2.17 RestHostNameResolver.allLocalIp can be used to resolve to all local IP addresses.

The default is localHostName up to Camel 2.16. From Camel 2.17 the default is allLocalIp.

bindingMode()

@bindingMode

Enables binding mode for JSON or XML format messages. Possible values are: off, auto, json, xml, or json_xml. Default is off.

skipBindingOnErrorCode()

@skipBindingOnErrorCode

Specifies whether to skip binding on output, if there is a custom HTTP error code header. This allows you to build custom error messages that do not bind to JSON or XML, as successful messages would otherwise do. Default is true.

enableCORS()

@enableCORS

If true, enables CORS (cross-origin resource sharing) headers in the HTTP response. Default is false.

jsonDataFormat()

@jsonDataFormat

Specifies the component that Camel uses to implement the JSON data format. Possible values are: json-jackson, json-gson, json-xstream. Default is json-jackson.

xmlDataFormat()

@xmlDataFormat

Specifies the component that Camel uses to implement the XML data format. Possible value is: jaxb. Default is jaxb.

componentProperty()

componentProperty

Enables you to set arbitrary component level properties on the underlying REST implementation.

endpointProperty()

endpointProperty

Enables you to set arbitrary endpoint level properties on the underlying REST implementation.

consumerProperty()

consumerProperty

Enables you to set arbitrary consumer endpoint properties on the underlying REST implementation.

dataFormatProperty()

dataFormatProperty

Enables you to set arbitrary properties on the underlying data format component (for example, Jackson or JAXB). From Camel 2.14.1 onwards, you can attach the following prefixes to the property keys:

  • json.in
  • json.out
  • xml.in
  • xml.out

To restrict the property setting to a specific format type (JSON or XML) and a particular message direction (IN or OUT).

corsHeaderProperty()

corsHeaders

Enables you to specify custom CORS headers, as key/value pairs.

Default CORS headers

If CORS (cross-origin resource sharing) is enabled, the following headers are set by default. You can optionally override the default settings, by invoking the corsHeaderProperty DSL command.

Table 4.4. Default CORS Headers
Header KeyHeader Value

Access-Control-Allow-Origin

\*

Access-Control-Allow-Methods

GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH

Access-Control-Allow-Headers

Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers

Access-Control-Max-Age

3600

Enabling or disabling Jackson JSON features

You can enable or disable specific Jackson JSON features by configuring the following keys in the dataFormatProperty option:

  • json.in.disableFeatures
  • json.in.enableFeatures

For example, to disable Jackson’s FAIL_ON_UNKNOWN_PROPERTIES feature (which causes Jackson to fail if a JSON input has a property that cannot be mapped to a Java object):

restConfiguration().component("jetty")
    .host("localhost").port(getPort())
    .bindingMode(RestBindingMode.json)
    .dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES");

You can disable multiple features by specifying a comma-separated list. For example:

.dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE");

Here is an example that shows how to disable and enable Jackson JSON features in the Java DSL:

restConfiguration().component("jetty")
    .host("localhost").port(getPort())
    .bindingMode(RestBindingMode.json)
    .dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE")
    .dataFormatProperty("json.in.enableFeatures", "FAIL_ON_NUMBERS_FOR_ENUMS,USE_BIG_DECIMAL_FOR_FLOATS");

Here is an example that shows how to disable and enable Jackson JSON features in the XML DSL:

<restConfiguration component="jetty" host="localhost" port="9090" bindingMode="json">
  <dataFormatProperty key="json.in.disableFeatures" value="FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE"/>
  <dataFormatProperty key="json.in.enableFeatures" value="FAIL_ON_NUMBERS_FOR_ENUMS,USE_BIG_DECIMAL_FOR_FLOATS"/>
</restConfiguration>

The Jackson features that can be disabled or enabled correspond to the enum IDs from the following Jackson classes

4.5. OpenAPI Integration

Overview

You can use a OpenAPI service to create API documentation for any REST-defined routes and endpoints in a CamelContext file. To do this, use the Camel REST DSL with the camel-openapi-java module, which is purely Java-based. The camel-openapi-java module creates a servlet that is integrated with the CamelContext and that pulls the information from each REST endpoint to generate the API documentation in JSON or YAML format.

If you use Maven then edit your pom.xml file to add a dependency on the camel-openapi-java component:

<dependency>
   <groupId>org.apache.camel</groupId>
   <artifactId>camel-openapi-java</artifactId>
   <version>x.x.x</version>
   <!-- Specify the version of your camel-core module. -->
</dependency>

Configuring a CamelContext to enable OpenAPI

To enable the use of the OpenAPI in the Camel REST DSL, invoke apiContextPath() to set the context path for the OpenAPI-generated API documentation. For example:

public class UserRouteBuilder extends RouteBuilder {
    @Override
    public void configure() throws Exception {
        // Configure the Camel REST DSL to use the netty4-http component:
        restConfiguration().component("netty4-http").bindingMode(RestBindingMode.json)
            // Generate pretty print output:
            .dataFormatProperty("prettyPrint", "true")
            // Set the context path and port number that netty will use:
            .contextPath("/").port(8080)
            // Add the context path for the OpenAPI-generated API documentation:
            .apiContextPath("/api-doc")
                .apiProperty("api.title", "User API").apiProperty("api.version", "1.2.3")
                // Enable CORS:
                .apiProperty("cors", "true");

        // This user REST service handles only JSON files:
        rest("/user").description("User rest service")
            .consumes("application/json").produces("application/json")
            .get("/{id}").description("Find user by id").outType(User.class)
                .param().name("id").type(path).description("The id of the user to get").dataType("int").endParam()
                .to("bean:userService?method=getUser(${header.id})")
            .put().description("Updates or create a user").type(User.class)
                .param().name("body").type(body).description("The user to update or create").endParam()
                .to("bean:userService?method=updateUser")
            .get("/findAll").description("Find all users").outTypeList(User.class)
                .to("bean:userService?method=listUsers");
    }
}

OpenAPI module configuration options

The options described in the table below let you configure the OpenAPI module. Set an option as follows:

  • If you are using the camel-openapi-java module as a servlet, set an option by updating the web.xml file and specifying an init-param element for each configuration option you want to set.
  • If you are using the camel-openapi-java module from Camel REST components, set an option by invoking the appropriate RestConfigurationDefinition method, such as enableCORS(), host(), or contextPath(). Set the api.xxx options with the RestConfigurationDefinition.apiProperty() method.
OptionTypeDescription

api.contact.email

String

Email address to be used for API-related correspondence.

api.contact.name

String

Name of person or organization to contact.

api.contact.url

String

URL to a website for more contact information.

apiContextIdListing

Boolean

If your application uses more than one CamelContext object, the default behavior is to list the REST endpoints in only the current CamelContext. If you want a list of the REST endpoints in each CamelContext that is running in the JVM that is running the REST service then set this option to true. When apiContextIdListing is true then OpenAPI outputs the CamelContext IDs in the root path, for example, /api-docs, as a list of names in JSON format. To access the OpenAPI-generated documentation, append the REST context path to the CamelContext ID, for example, api-docs/myCamel. You can use the apiContextIdPattern option to filter the names in this output list.

apiContextIdPattern

String

Pattern that filters which CamelContext IDs appear in the context listing. You can specify regular expressions and use * as a wildcard. This is the same pattern matching facility as used by the Camel Intercept feature.

api.license.name

String

License name used for the API.

api.license.url

String

URL to the license used for the API.

api.path

String

Sets the path where the REST API to generate documentation for is available, for example, /api-docs. Specify a relative path. Do not specify, for example, http or https. The camel-openapi-java module calculates the absolute path at runtime in this format: protocol://host:port/context-path/api-path.

api.termsOfService

String

URL to the terms of service of the API.

api.title

String

Title of the application.

api.version

String

Version of the API. The default is 0.0.0.

base.path

String

Required. Sets the path where the REST services are available. Specify a relative path. That is, do not specify, for example, http or https. The camel-openapi-java modul calculates the absolute path at runtime in this format: protocol://host:port/context-path/base.path.

cors

Boolean

Whether to enable HTTP Access Control (CORS). This enable CORS only for viewing the REST API documentation, and not for access to the REST service. The default is false. The recommendation is to use the CorsFilter option instead, as described after this table.

host

String

Set the name of the host that the OpenAPI service is running on. The default is to calculate the host name based on localhost.

schemes

String

Protocol schemes to use. Separate multiple values with a comma, for example, "http,https". The default is http.

opeapi.version

String

OpenAPI specification version. The default is 3.0.

Obtaining JSON or YAML output

Starting with Camel 3.1, the camel-openapi-java module supports both JSON and YAML formatted output. To specify the output you want, add /openapi.json or /openapi.yaml to the request URL. If a request URL does not specify a format then the camel-openapi-java module inspects the HTTP Accept header to detect whether JSON or YAML can be accepted. If both are accepted or if none was set as accepted then JSON is the default return format.

Examples

In the Apache Camel 3.x distribution, camel-example-openapi-cdi and camel-example-openapi-java demonstrate the use of the camel-openapi-java module.

In the Apache Camel 2.x distribution, camel-example-swagger-cdi and camel-example-swagger-java demonstrate the use of the camel-swagger-java module.

Enhancing documentation generated by OpenAPI

Starting with Camel 3.1, you can enhance the documentation generated by OpenAPI by defining parameter details such as name, description, data type, parameter type and so on. If you are using XML, specify the param element to add this information. The following example shows how to provide information about the ID path parameter:

<!-- This is a REST GET request to view information for the user with the given ID: -->
<get uri="/{id}" outType="org.apache.camel.example.rest.User">
     <description>Find user by ID.</description>
     <param name="id" type="path" description="The ID of the user to get information about." dataType="int"/>
     <to uri="bean:userService?method=getUser(${header.id})"/>
</get>

Following is the same example in Java DSL:

.get("/{id}").description("Find user by ID.").outType(User.class)
   .param().name("id").type(path).description("The ID of the user to get information about.").dataType("int").endParam()
   .to("bean:userService?method=getUser(${header.id})")

If you define a parameter whose name is body then you must also specify body as the type of that parameter. For example:

<!-- This is a REST PUT request to create/update information about a user. -->
<put type="org.apache.camel.example.rest.User">
     <description>Updates or creates a user.</description>
     <param name="body" type="body" description="The user to update or create."/>
     <to uri="bean:userService?method=updateUser"/>
</put>

Following is the same example in Java DSL:

.put().description("Updates or create a user").type(User.class)
     .param().name("body").type(body).description("The user to update or create.").endParam()
     .to("bean:userService?method=updateUser")

See also: examples/camel-example-servlet-rest-tomcat in the Apache Camel distribution.

Red Hat logoGithubRedditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat Documentation

We help Red Hat users innovate and achieve their goals with our products and services with content they can trust.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

© 2024 Red Hat, Inc.