Skip to content

Instantly share code, notes, and snippets.

@danbev
Last active December 14, 2015 13:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danbev/5097502 to your computer and use it in GitHub Desktop.
Save danbev/5097502 to your computer and use it in GitHub Desktop.
Camel Controller Endpoint Implementation

Camel Integration

This document describes the task of creating a number of adapters/connectors to backend services. The requirement for this came from the desire to be able to configure routes using a "no-code" solution. This gist investigates the options for using Camel Components to make the connections to the backend services but having the original requirements in mind.

The following adapters/connectors have been requested:

  • REST
  • SOAP
  • SQL
  • JMS

Prototyping code for the examples in this gist can be found here:

Camel

The idea is to utilize Camel's existing components to connect to backend services exposing them via AeroGear Controller.

This might be obvious but worth pointing out just the same. In the controller we cannot use a complete Camel route (defined in Java or String XML) as there will be no from. The from in our case if AeroGear Controller. So we can't just have an endpoint that accepts a Camel configutation xml for instance. If Camel alone was used then it would be possible to only use complete Camel config, which is a valid approach since Camel has components that can consume http requests.

Camel components can be access remotely using the ProducerTemplate. Using the ProducerTemplate would in theory enable us to create a controller endpoint that looks something like this:

.to(CamelController.class).request("http://server/demo/cars/1", String.class);

The above would require constants to be accepted as parameters to endpoint methods, which is currently not supported upstream yet but is available here.

There are two issues that I've noticed so far:

  1. Configuration of Camel components (either via the uri for the ProducerTemplate, or using the Component directly.
  2. The passing of HTTP parameters (query, path, header, cookie, etc)

Configuring Camel Components

Most Camel Components support a lot of configuration options and this section discusses how these configuration options chould be exposed/set when used though AeroGear Controller.

Depending on the solution we pick, we have diffent options for configuring the Camel components used. If we chose to use the ProducerTemplate the configuration of the components is done using parameters in the component uri:

"http://server/demo/cars/1?httpClient.cookiePolicy=ignoreCookies"

If we use the components directly:

HttpEndpoint endpoint =  camelContext.getEndpoint(endpointUri, HttpEndpoint.class);
endpoint.setChunked(false);

CustomHttpController is an attempt of using this approach. The actual Camel HttpEndpoint used will depend on whether there is a CDI Producer or not. If a Producer exists that result of that producer will be used otherwise a default one will be created.

Passing through HTTP parameters

Regardless of the solution chosen to utilize the underlying Camel components there is the issue of parameters that need to be passed through. What I mean is that you might have a route that uses a path parameter:

route()
       .from("/cars/{id}")
       .on(RequestMethod.GET)
       .produces(JSON)
       .to(CamelController.class).request("http://server/demo/cars/", param("id"), String.class);

This example would be called with the following resource url:

curl -i --header "Accept: application/json" "http://server/camel/cars/2"

The issue here is that aerogear controller does not expose the HTTP Request/Response to the endpoints, instead an endpoint will specify that it receives a param as shown above.

In this use case AeroGear Controller is acting as a proxy for the real target servcice. The HTTP parameters are intended for the target service and these need to be passed along.

Perhaps we should add support for parsing a single param and be able to specify one like this:

.to(CamelController.class).request(param("http://localhost:8080/aerogear-controller-demo/cars/{id}"), String.class);

Where one could specify any number of parameters that will be replaced by the same values from the request. An example of this can be found in this ag-controller branch.

There will also be situations where the need to have references to instances in the Camel registry. For example. to use the sql component one would need to reference a datasource:

route()
       .from("/sql/{id}")
       .on(RequestMethod.GET)
       .consumes(HTML)
       .produces(JSON)
       .to(CamelController.class).request(param("sql:select * from car where id={id}?dataSourceRef=java:jboss/datasources/CarDS"), String.class);

Usage

This section provides an example usage from an end users perspective.

Include aerogear-controller-camel dependency:

<dependency>
       <groupId>org.jboss.aerogear</groupId>
       <artifactId>aerogear-controller-camel</artifactId>
       <version>1.0.0-SNAPSHOT</version>
       <scope>compile</scope>
</dependency>

Include the Camel Components that you project uses as dependencies:

<dependency>
       <groupId>org.apache.camel</groupId>
       <artifactId>camel-http</artifactId>
       <version>2.10.4</version>
       <scope>compile</scope>
</dependency>

Include a beans.xml so that the deployment is scanned by the CDI container upon deployment.
Create Routes using the Camel component URI:

route()
       .from("/cars/{id}")
       .on(RequestMethod.GET)
       .consumes(HTML)
       .produces(JSON)
       .to(CamelController.class).request(param("http://controller-aerogear.rhcloud.com/aerogear-controller-demo/cars/{id}"), String.class);

Concerns

We could need to change the controller to accomodate for some of the solutions proposed above, like enable an endpoint method to accept a HTTPServletRequest/HttpServletResponse, or have them injected. But I feel that doing this is very similar to what JAX-RS does, and if we are moving in that direction why not simply use JAX-RS directly?

It also feels like we have putting a routing "framework" in front on a routing framework. What I mean is that you could have a Camel route that looked like this:

public void configure() throws Exception {
    from("cfxrs://http://localhost:8080/cars")
    ...
    .to("http://some/other/server");
}

The original reqiurement for this was to have a "no-code" solution that allows for configuration of routes. The end user would not be exposed to the underlying implementation, and as I see it, Camel alone would be a better fit in that case. The configuration could be done using a web based interface and a Spring xml file could be generated that configures the Camel routes. There might be better options but I'm sure the Camel developers would be able to advise about that. The end product would be deployed as a war just like an AeroGear Controller deployment.

But one might argue that the end user wants to use AeroGear Security, but I'm sure that we could look into making AeroGear security work with Camel (if it does not already and it very well might).

I'm not saying that is does not make sense to use Camel component if one needs to connect to backend services, I think it does , and there are so many components that whatever backend I'm almost certain there is a component for it. What I'm saying is that in this case it would make more sense to provide an example/abstract class or something to help users get up running which takes care of some of the house keeping. But the end user would be in charge of creating the specific endpoint methods with the params it accepts. But this would not meet the original requirements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment