Chapter 46. Getting Started with the Framework

Abstract

This chapter explains the basic principles of implementing a Camel component using the API component framework, based on code generated using the camel-archetype-api-component Maven archetype.

46.1. Generate Code with the Maven Archetype

Maven archetypes

A Maven archetype is analogous to a code wizard: given a few simple parameters, it generates a complete, working Maven project, populated with sample code. You can then use this project as a template, customizing the implementation to create your own application.

The API component Maven archetype

The API component framework provides a Maven archetype, camel-archetype-api-component, that can generate starting point code for your own API component implementation. This is the recommended approach to start creating your own API component.

Prerequisites

The only prerequisites for running the camel-archetype-api-component archetype are that Apache Maven is installed and the Maven settings.xml file is configured to use the standard Fuse repositories.

Invoke the Maven archetype

To create an Example component, which uses the example URI scheme, invoke the camel-archetype-api-component archetype to generate a new Maven project, as follows:

mvn archetype:generate \
-DarchetypeGroupId=org.apache.camel.archetypes \
-DarchetypeArtifactId=camel-archetype-api-component \
-DarchetypeVersion=2.21.0.fuse-740039-redhat-00001 \
-DgroupId=org.jboss.fuse.example \
-DartifactId=camel-api-example \
-Dname=Example \
-Dscheme=example \
-Dversion=1.0-SNAPSHOT \
-DinteractiveMode=false
Note

The backslash character, \, at the end of each line represents line continuation, which works only on Linux and UNIX platforms. On Windows platforms, remove the backslash and put the arguments all on a single line.

Options

Options are provided to the archetype generation command using the syntax, -DName=Value. Most of the options should be set as shown in the preceding mvn archetype:generate command, but a few of the options can be modified, to customize the generated project. The following table shows the options that you can use to customize the generated API component project:

NameDescription

groupId

(Generic Maven option) Specifies the group ID of the generated Maven project. By default, this value also defines the Java package name for the generated classes. Hence, it is a good idea to choose this value to match the Java package name that you want.

artifactId

(Generic Maven option) Specifies the artifact ID of the generated Maven project.

name

The name of the API component. This value is used for generating class names in the generated code (hence, it is recommended that the name should start with a capital letter).

scheme

The default scheme to use in URIs for this component. You should make sure that this scheme does not conflict with the scheme of any existing Camel components.

archetypeVersion

(Generic Maven option) Ideally, this should be the Apache Camel version used by the container where you plan to deploy the component. If necessary, however, you can also modify the versions of Maven dependencies after you have generated the project.

Structure of the generated project

Assuming that the code generation step completes successfully, you should see a new directory, camel-api-example, which contains the new Maven project. If you look inside the camel-api-example directory, you will see that it has the following general structure:

camel-api-example/
    pom.xml
    camel-api-example-api/
    camel-api-example-component/

At the top level of the project is an aggregate POM, pom.xml, which is configured to build two sub-projects, as follows:

camel-api-example-api

The API sub-project (named as ArtifactId-api) holds the Java API which you are about to turn into a component. If you are basing the API component on a Java API that you wrote yourself, you can put the Java API code directly into this project.

The API sub-project can be used for one or more of the following purposes:

  • To package up the Java API code (if it is not already available as a Maven package).
  • To generate Javadoc for the Java API (providing the needed metadata for the API component framework).
  • To generate the Java API code from an API description (for example, from a WADL description of a REST API).

In some cases, however, you might not need to perform any of these tasks. For example, if the API component is based on a third-party API, which already provides the Java API and Javadoc in a Maven package. In such cases, you can delete the API sub-project.

camel-api-example-component
The component sub-project (named as ArtifactId-component) holds the implementation of the new API component. This includes the component implementation classes and the configuration of the camel-api-component-maven plug-in (which generates the API mapping classes from the Java API).

46.2. Generated API Sub-Project

Overview

Assuming that you generated a new Maven project as described in Section 46.1, “Generate Code with the Maven Archetype”, you can now find a Maven sub-project for packaging the Java API under the camel-api-example/camel-api-example-api project directory. In this section, we take a closer look at the generated example code and describe how it works.

Sample Java API

The generated example code includes a sample Java API, on which the example API component is based. The sample Java API is relatively simple, consisting of just two Hello World classes: ExampleJavadocHello and ExampleFileHello.

ExampleJavadocHello class

Example 46.1, “ExampleJavadocHello class” shows the ExampleJavadocHello class from the sample Java API. As the name of the class suggests, this particular class is used to show how you can supply mapping metadata from Javadoc.

Example 46.1. ExampleJavadocHello class

// Java
package org.jboss.fuse.example.api;

/**
 * Sample API used by Example Component whose method signatures are read from Javadoc.
 */
public class ExampleJavadocHello {

    public String sayHi() {
        return "Hello!";
    }

    public String greetMe(String name) {
        return "Hello " + name;
    }

    public String greetUs(String name1, String name2) {
            return "Hello " + name1 + ", " + name2;
    }
}

ExampleFileHello class

Example 46.2, “ExampleFileHello class” shows the ExampleFileHello class from the sample Java API. As the name of the class suggests, this particular class is used to show how you can supply mapping metadata from a signature file.

Example 46.2. ExampleFileHello class

// Java
package org.jboss.fuse.example.api;

/**
 * Sample API used by Example Component whose method signatures are read from File.
 */
public class ExampleFileHello {

    public String sayHi() {
        return "Hello!";
    }

    public String greetMe(String name) {
        return "Hello " + name;
    }

    public String greetUs(String name1, String name2) {
            return "Hello " + name1 + ", " + name2;
    }
}

Generating the Javadoc metadata for ExampleJavadocHello

Because the metadata for ExampleJavadocHello is provided as Javadoc, it is necessary to generate Javadoc for the sample Java API and install it into the camel-api-example-api Maven artifact. The API POM file, camel-api-example-api/pom.xml, configures the maven-javadoc-plugin to perform this step automatically during the Maven build.

46.3. Generated Component Sub-Project

Overview

The Maven sub-project for building the new component is located under the camel-api-example/camel-api-example-component project directory. In this section, we take a closer look at the generated example code and describe how it works.

Providing the Java API in the component POM

The Java API must be provided as a dependency in the component POM. For example, the sample Java API is defined as a dependency in the component POM file, camel-api-example-component/pom.xml, as follows:

<?xml version="1.0" encoding="UTF-8"?>
<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">

  ...
  <dependencies>
    ...
    <dependency>
      <groupId>org.jboss.fuse.example</groupId>
      <artifactId>camel-api-example-api</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
    ...
  </dependencies>
  ...
</project>

Providing the Javadoc metadata in the component POM

If you are using Javadoc metadata for all or part of the Java API, you must provide the Javadoc as a dependency in the component POM. There are two things to note about this dependency:

  • The Maven coordinates for the Javadoc are almost the same as for the Java API, except that you must also specify a classifier element, as follows:

    <classifier>javadoc</classifier>
  • You must declare the Javadoc to have provided scope, as follows:

    <scope>provided</scope>

For example, in the component POM, the Javadoc dependency is defined as follows:

<?xml version="1.0" encoding="UTF-8"?>
<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">

  ...
  <dependencies>
    ...
    <!-- Component API javadoc in provided scope to read API signatures -->
    <dependency>
      <groupId>org.jboss.fuse.example</groupId>
      <artifactId>camel-api-example-api</artifactId>
      <version>1.0-SNAPSHOT</version>
      <classifier>javadoc</classifier>
      <scope>provided</scope>
    </dependency>
    ...
  </dependencies>
  ...
</project>

Defining the file metadata for Example File Hello

The metadata for ExampleFileHello is provided in a signature file. In general, this file must be created manually, but it has quite a simple format, which consists of a list of method signatures (one on each line). The example code provides the signature file, file-sig-api.txt, in the directory, camel-api-example-component/signatures, which has the following contents:

public String sayHi();
public String greetMe(String name);
public String greetUs(String name1, String name2);

For more details about the signature file format, see the section called “Signature file metadata”.

Configuring the API mapping

One of the key features of the API component framework is that it automatically generates the code to perform API mapping. That is, generating stub code that maps endpoint URIs to method invocations on the Java API. The basic inputs to the API mapping are: the Java API, the Javadoc metadata, and/or the signature file metadata.

The component that performs the API mapping is the camel-api-component-maven-plugin Maven plug-in, which is configured in the component POM. The following extract from the component POM shows how the camel-api-component-maven-plugin plug-in is configured:

<?xml version="1.0" encoding="UTF-8"?>
<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">

  ...
  <build>
    <defaultGoal>install</defaultGoal>

    <plugins>
      ...
      <!-- generate Component source and test source -->
      <plugin>
        <groupId>org.apache.camel</groupId>
        <artifactId>camel-api-component-maven-plugin</artifactId>
        <executions>
          <execution>
            <id>generate-test-component-classes</id>
            <goals>
              <goal>fromApis</goal>
            </goals>
            <configuration>
              <apis>
                <api>
                  <apiName>hello-file</apiName>
                  <proxyClass>org.jboss.fuse.example.api.ExampleFileHello</proxyClass>
                  <fromSignatureFile>signatures/file-sig-api.txt</fromSignatureFile>
                </api>
                <api>
                  <apiName>hello-javadoc</apiName>
                  <proxyClass>org.jboss.fuse.example.api.ExampleJavadocHello</proxyClass>
                  <fromJavadoc/>
                </api>
              </apis>
            </configuration>
          </execution>
        </executions>
      </plugin>
      ...
    </plugins>
    ...
  </build>
  ...
</project>

The plug-in is configured by the configuration element, which contains a single apis child element to configure the classes of the Java API. Each API class is configured by an api element, as follows:

apiName

The API name is a short name for the API class and is used as the endpoint-prefix part of an endpoint URI.

Note

If the API consists of just a single Java class, you can leave the apiName element empty, so that the endpoint-prefix becomes redundant, and you can then specify the endpoint URI using the format shown in the section called “URI format for a single API class”.

proxyClass
The proxy class element specifies the fully-qualified name of the API class.
fromJavadoc
If the API class is accompanied by Javadoc metadata, you must indicate this by including the fromJavadoc element and the Javadoc itself must also be specified in the Maven file, as a provided dependency (see the section called “Providing the Javadoc metadata in the component POM”).
fromSignatureFile

If the API class is accompanied by signature file metadata, you must indicate this by including the fromSignatureFile element, where the content of this element specifies the location of the signature file.

Note

The signature files do not get included in the final package built by Maven, because these files are needed only at build time, not at run time.

Generated component implementation

The API component consists of the following core classes (which must be implemented for every Camel component), under the camel-api-example-component/src/main/java directory:

ExampleComponent
Represents the component itself. This class acts as a factory for endpoint instances (for example, instances of ExampleEndpoint).
ExampleEndpoint
Represents an endpoint URI. This class acts as a factory for consumer endpoints (for example, ExampleConsumer) and as a factory for producer endpoints (for example, ExampleProducer).
ExampleConsumer
Represents a concrete instance of a consumer endpoint, which is capable of consuming messages from the location specified in the endpoint URI.
ExampleProducer
Represents a concrete instance of a producer endpoint, which is capable of sending messages to the location specified in the endpoint URI.
ExampleConfiguration

Can be used to define endpoint URI options. The URI options defined by this configuration class are not tied to any specific API class. That is, you can combine these URI options with any of the API classes or methods. This can be useful, for example, if you need to declare username and password credentials in order to connect to the remote service. The primary purpose of the ExampleConfiguration class is to provide values for parameters required to instantiate API classes, or classes that implement API interfaces. For example, these could be constructor parameters, or parameter values for a factory method or class.

To implement a URI option, option, in this class, all that you need to do is implement the pair of accessor methods, getOption and setOption. The component framework automatically parses the endpoint URI and injects the option values at run time.

ExampleComponent class

The generated ExampleComponent class is defined as follows:

// Java
package org.jboss.fuse.example;

import org.apache.camel.CamelContext;
import org.apache.camel.Endpoint;
import org.apache.camel.spi.UriEndpoint;
import org.apache.camel.util.component.AbstractApiComponent;

import org.jboss.fuse.example.internal.ExampleApiCollection;
import org.jboss.fuse.example.internal.ExampleApiName;

/**
 * Represents the component that manages {@link ExampleEndpoint}.
 */
@UriEndpoint(scheme = "example", consumerClass = ExampleConsumer.class, consumerPrefix = "consumer")
public class ExampleComponent extends AbstractApiComponent<ExampleApiName, ExampleConfiguration, ExampleApiCollection> {

    public ExampleComponent() {
        super(ExampleEndpoint.class, ExampleApiName.class, ExampleApiCollection.getCollection());
    }

    public ExampleComponent(CamelContext context) {
        super(context, ExampleEndpoint.class, ExampleApiName.class, ExampleApiCollection.getCollection());
    }

    @Override
    protected ExampleApiName getApiName(String apiNameStr) throws IllegalArgumentException {
        return ExampleApiName.fromValue(apiNameStr);
    }

    @Override
    protected Endpoint createEndpoint(String uri, String methodName, ExampleApiName apiName,
                                      ExampleConfiguration endpointConfiguration) {
        return new ExampleEndpoint(uri, this, apiName, methodName, endpointConfiguration);
    }
}

The important method in this class is createEndpoint, which creates new endpoint instances. Typically, you do not need to change any of the default code in the component class. If there are any other objects with the same life cycle as this component, however, you might want to make those objects available from the component class (for example, by adding a methods to create those objects or by injecting those objects into the component).

ExampleEndpoint class

The generated ExampleEndpoint class is defined as follows:

// Java
package org.jboss.fuse.example;

import java.util.Map;

import org.apache.camel.Consumer;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
import org.apache.camel.spi.UriEndpoint;
import org.apache.camel.util.component.AbstractApiEndpoint;
import org.apache.camel.util.component.ApiMethod;
import org.apache.camel.util.component.ApiMethodPropertiesHelper;

import org.jboss.fuse.example.api.ExampleFileHello;
import org.jboss.fuse.example.api.ExampleJavadocHello;
import org.jboss.fuse.example.internal.ExampleApiCollection;
import org.jboss.fuse.example.internal.ExampleApiName;
import org.jboss.fuse.example.internal.ExampleConstants;
import org.jboss.fuse.example.internal.ExamplePropertiesHelper;

/**
 * Represents a Example endpoint.
 */
@UriEndpoint(scheme = "example", consumerClass = ExampleConsumer.class, consumerPrefix = "consumer")
public class ExampleEndpoint extends AbstractApiEndpoint<ExampleApiName, ExampleConfiguration> {

    // TODO create and manage API proxy
    private Object apiProxy;

    public ExampleEndpoint(String uri, ExampleComponent component,
                         ExampleApiName apiName, String methodName, ExampleConfiguration endpointConfiguration) {
        super(uri, component, apiName, methodName, ExampleApiCollection.getCollection().getHelper(apiName), endpointConfiguration);

    }

    public Producer createProducer() throws Exception {
        return new ExampleProducer(this);
    }

    public Consumer createConsumer(Processor processor) throws Exception {
        // make sure inBody is not set for consumers
        if (inBody != null) {
            throw new IllegalArgumentException("Option inBody is not supported for consumer endpoint");
        }
        final ExampleConsumer consumer = new ExampleConsumer(this, processor);
        // also set consumer.* properties
        configureConsumer(consumer);
        return consumer;
    }

    @Override
    protected ApiMethodPropertiesHelper<ExampleConfiguration> getPropertiesHelper() {
        return ExamplePropertiesHelper.getHelper();
    }

    protected String getThreadProfileName() {
        return ExampleConstants.THREAD_PROFILE_NAME;
    }

    @Override
    protected void afterConfigureProperties() {
        // TODO create API proxy, set connection properties, etc.
        switch (apiName) {
            case HELLO_FILE:
                apiProxy = new ExampleFileHello();
                break;
            case HELLO_JAVADOC:
                apiProxy = new ExampleJavadocHello();
                break;
            default:
                throw new IllegalArgumentException("Invalid API name " + apiName);
        }
    }

    @Override
    public Object getApiProxy(ApiMethod method, Map<String, Object> args) {
        return apiProxy;
    }
}

In the context of the API component framework, one of the key steps performed by the endpoint class is to create an API proxy. The API proxy is an instance from the target Java API, whose methods are invoked by the endpoint. Because a Java API typically consists of many classes, it is necessary to pick the appropriate API class, based on the endpoint-prefix appearing in the URI (recall that a URI has the general form, scheme://endpoint-prefix/endpoint).

ExampleConsumer class

The generated ExampleConsumer class is defined as follows:

// Java
package org.jboss.fuse.example;

import org.apache.camel.Processor;
import org.apache.camel.util.component.AbstractApiConsumer;

import org.jboss.fuse.example.internal.ExampleApiName;

/**
 * The Example consumer.
 */
public class ExampleConsumer extends AbstractApiConsumer<ExampleApiName, ExampleConfiguration> {

    public ExampleConsumer(ExampleEndpoint endpoint, Processor processor) {
        super(endpoint, processor);
    }

}

ExampleProducer class

The generated ExampleProducer class is defined as follows:

// Java
package org.jboss.fuse.example;

import org.apache.camel.util.component.AbstractApiProducer;

import org.jboss.fuse.example.internal.ExampleApiName;
import org.jboss.fuse.example.internal.ExamplePropertiesHelper;

/**
 * The Example producer.
 */
public class ExampleProducer extends AbstractApiProducer<ExampleApiName, ExampleConfiguration> {

    public ExampleProducer(ExampleEndpoint endpoint) {
        super(endpoint, ExamplePropertiesHelper.getHelper());
    }
}

ExampleConfiguration class

The generated ExampleConfiguration class is defined as follows:

// Java
package org.jboss.fuse.example;

import org.apache.camel.spi.UriParams;

/**
 * Component configuration for Example component.
 */
@UriParams
public class ExampleConfiguration {

    // TODO add component configuration properties
}

To add a URI option, option, to this class, define a field of the appropriate type, and implement a corresponding pair of accessor methods, getOption and setOption. The component framework automatically parses the endpoint URI and injects the option values at run time.

Note

This class is used to define general URI options, which can be combined with any API method. To define URI options tied to a specific API method, configure extra options in the API component Maven plug-in. See Section 47.7, “Extra Options” for details.

URI format

Recall the general format of an API component URI:

scheme://endpoint-prefix/endpoint?Option1=Value1&...&OptionN=ValueN

In general, a URI maps to a specific method invocation on the Java API. For example, suppose you want to invoke the API method, ExampleJavadocHello.greetMe("Jane Doe"), the URI would be constructed, as follows:

scheme
The API component scheme, as specified when you generated the code with the Maven archetype. In this case, the scheme is example.
endpoint-prefix

The API name, which maps to the API class defined by the camel-api-component-maven-plugin Maven plug-in configuration. For the ExampleJavadocHello class, the relevant configuration is:

<configuration>
  <apis>
    <api>
      <apiName>hello-javadoc</apiName>
      <proxyClass>org.jboss.fuse.example.api.ExampleJavadocHello</proxyClass>
      <fromJavadoc/>
    </api>
    ...
  </apis>
</configuration>

Which shows that the required endpoint-prefix is hello-javadoc.

endpoint
The endpoint maps to the method name, which is greetMe.
Option1=Value1
The URI options specify method parameters. The greetMe(String name) method takes the single parameter, name, which can be specified as name=Jane%20Doe. If you want to define default values for options, you can do this by overriding the interceptProperties method (see Section 46.4, “Programming Model”).

Putting together the pieces of the URI, we see that we can invoke ExampleJavadocHello.greetMe("Jane Doe") with the following URI:

example://hello-javadoc/greetMe?name=Jane%20Doe

Default component instance

In order to map the example URI scheme to the default component instance, the Maven archetype creates the following file under the camel-api-example-component sub-project:

src/main/resources/META-INF/services/org/apache/camel/component/example

This resource file is what enables the Camel core to identify the component associated with the example URI scheme. Whenever you use an example:// URI in a route, Camel searches the classpath to look for the corresponding example resource file. The example file has the following contents:

class=org.jboss.fuse.example.ExampleComponent

This enables the Camel core to create a default instance of the ExampleComponent component. The only time you would need to edit this file is if you refactor the name of the component class.

46.4. Programming Model

Overview

In the context of the API component framework, the main component implementation classes are derived from base classes in the org.apache.camel.util.component package. These base classes define some methods which you can (optionally) override when you are implementing your component. In this section, we provide a brief description of those methods and how you might use them in your own component implementation.

Component methods to implement

In addition to the generated method implementations (which you usually do not need to modify), you can optionally override some of the following methods in the Component class:

doStart()
(Optional) A callback to create resources for the component during a cold start. An alternative approach is to adopt the strategy of lazy initialization (creating resources only when they are needed). In fact, lazy initialization is often the best strategy, so the doStart method is often not needed.
doStop()

(Optional) A callback to invoke code while the component is stopping. Stopping a component means that all of its resources are shut down, internal state is deleted, caches are cleared, and so on.

Note

Camel guarantees that doStop is always called when the current CamelContext shuts down, even if the corresponding doStart was never called.

doShutdown
(Optional) A callback to invoke code while the CamelContext is shutting down. Whereas a stopped component can be restarted (with the semantics of a cold start), a component that gets shut down is completely finished. Hence, this callback represents the last chance to free up any resources belonging to the component.

What else to implement in the Component class?

The Component class is the natural place to hold references to objects that have the same (or similar) life cycle to the component object itself. For example, if a component uses OAuth security, it would be natural to hold references to the required OAuth objects in the Component class and to define methods in the Component class for creating the OAuth objects.

Endpoint methods to implement

You can modify some of the generated methods and, optionally, override some inherited methods in the Endpoint class, as follows:

afterConfigureProperties()

The main thing you need to do in this method is to create the appropriate type of proxy class (API class), to match the API name. The API name (which has already been extracted from the endpoint URI) is available either through the inherited apiName field or through the getApiName accessor. Typically, you would do a switch on the apiName field to create the corresponding proxy class. For example:

// Java
private Object apiProxy;
...
@Override
protected void afterConfigureProperties() {
    // TODO create API proxy, set connection properties, etc.
    switch (apiName) {
        case HELLO_FILE:
            apiProxy = new ExampleFileHello();
            break;
        case HELLO_JAVADOC:
            apiProxy = new ExampleJavadocHello();
            break;
        default:
            throw new IllegalArgumentException("Invalid API name " + apiName);
    }
}
getApiProxy(ApiMethod method, Map<String, Object> args)

Override this method to return the proxy instance that you created in afterConfigureProperties. For example:

@Override
public Object getApiProxy(ApiMethod method, Map<String, Object> args) {
    return apiProxy;
}

In special cases, you might want to make the choice of proxy dependent on the API method and arguments. The getApiProxy gives you the flexibility to take this approach, if required.

doStart()
(Optional) A callback to create resources during a cold start. Has the same semantics as Component.doStart().
doStop()
(Optional) A callback to invoke code while the component is stopping. Has the same semantics as Component.doStop().
doShutdown
(Optional) A callback to invoke code while the component is shutting down. Has the same semantics as Component.doShutdown().
interceptPropertyNames(Set<String> propertyNames)

(Optional) The API component framework uses the endpoint URI and supplied option values to determine which method to invoke (ambiguity could be due to overloading and aliases). If the component internally adds options or method parameters, however, the framework might need help in order to determine the right method to invoke. In this case, you must override the interceptPropertyNames method and add the extra (hidden or implicit) options to the propertyNames set. When the complete list of method parameters are provided in the propertyNames set, the framework will be able to identify the right method to invoke.

Note

You can override this method at the level of the Endpoint, Producer or Consumer class. The basic rule is, if an option affects both producer endpoints and consumer endpoints, override the method in the Endpoint class.

interceptProperties(Map<String,Object> properties)

(Optional) By overriding this method, you can modify or set the actual values of the options, before the API method is invoked. For example, you could use this method to set default values for some options, if necessary. In practice, it is often necessary to override both the interceptPropertyNames method and the interceptProperty method.

Note

You can override this method at the level of the Endpoint, Producer or Consumer class. The basic rule is, if an option affects both producer endpoints and consumer endpoints, override the method in the Endpoint class.

Consumer methods to implement

You can optionally override some inherited methods in the Consumer class, as follows:

interceptPropertyNames(Set<String> propertyNames)
(Optional) The semantics of this method are similar to Endpoint.interceptPropertyNames
interceptProperties(Map<String,Object> properties)
(Optional) The semantics of this method are similar to Endpoint.interceptProperties
doInvokeMethod(Map<String, Object> args)

(Optional) Overriding this method enables you to intercept the invocation of the Java API method. The most common reason for overriding this method is to customize the error handling around the method invocation. For example, a typical approach to overriding doInvokeMethod is shown in the following code fragment:

// Java
@Override
protected Object doInvokeMethod(Map<String, Object> args) {
    try {
        return super.doInvokeMethod(args);
    } catch (RuntimeCamelException e) {
        // TODO - Insert custom error handling here!
        ...
    }
}

You should invoke doInvokeMethod on the super-class, at some point in this implementation, to ensure that the Java API method gets invoked.

interceptResult(Object methodResult, Exchange resultExchange)
(Optional) Do some additional processing on the result of the API method invocation. For example, you could add custom headers to the Camel exchange object, resultExchange, at this point.
Object splitResult(Object result)

(Optional) By default, if the result of the method API invocation is a java.util.Collection object or a Java array, the API component framework splits the result into multiple exchange objects (so that a single invocation result is converted into multiple messages).

If you want to change the default behaviour, you can override the splitResult method in the consumer endpoint. The result argument contains the result of the API message invocation. If you want to split the result, you should return an array type.

Note

You can also switch off the default splitting behaviour by setting consumer.splitResult=false on the endpoint URI.

Producer methods to implement

You can optionally override some inherited methods in the Producer class, as follows:

interceptPropertyNames(Set<String> propertyNames)
(Optional) The semantics of this method are similar to Endpoint.interceptPropertyNames
interceptProperties(Map<String,Object> properties)
(Optional) The semantics of this method are similar to Endpoint.interceptProperties
doInvokeMethod(Map<String, Object> args)
(Optional) The semantics of this method are similar to Consumer.doInvokeMethod.
interceptResult(Object methodResult, Exchange resultExchange)
(Optional) The semantics of this method are similar to Consumer.interceptResult.
Note

The Producer.splitResult() method is never called, so it is not possible to split an API method result in the same way as you can for a consumer endpoint. To get a similar effect for a producer endpoint, you can use Camel’s split() DSL command (one of the standard enterprise integration patterns) to split Collection or array results.

Consumer polling and threading model

The default threading model for consumer endpoints in the API component framework is scheduled poll consumer. This implies that the API method in a consumer endpoint is invoked at regular, scheduled time intervals. For more details, see the section called “Scheduled poll consumer implementation”.

46.5. Sample Component Implementations

Overview

Several of the components distributed with Apache Camel have been implemented with the aid of the API component framework. If you want to learn more about the techniques for implementing Camel components using the framework, it is a good idea to study the source code of these component implementations.

Box.com

The Camel Box component shows how to model and invoke the third party Box.com Java SDK using the API component framework. It also demonstrates how the framework can be adapted to customize consumer polling, in order to support Box.com’s long polling API.

LinkedIn

The Camel LinkedIn component demonstrates how to wrap a REST API provided in the form of WADL and XML schemas. The implementation of this component exploits the Apache CXF wadl2java Maven plug-in to generate a Java API, which can then be wrapped using the API component framework.

This approach can be easily replicated to create a Camel component for any SaaS product or platform.

GoogleDrive

The Camel GoogleDrive component demonstrates how the API component framework can handle even Method Object style Google APIs. In this case, URI options are mapped to a method object, which is then invoked by overriding the doInvoke method in the consumer and the producer.

Olingo2

The Camel Olingo2 component demonstrates how a callback-based Asynchronous API can be wrapped using the API component framework. This example shows how asynchronous processing can be pushed into underlying resources, like HTTP NIO connections, to make Camel endpoints more resource efficient.