Red Hat Training

A Red Hat training course is available for Red Hat Fuse

Chapter 40. Endpoint Interface

Abstract

This chapter describes how to implement the Endpoint interface, which is an essential step in the implementation of a Apache Camel component.

40.1. The Endpoint Interface

Overview

An instance of org.apache.camel.Endpoint type encapsulates an endpoint URI, and it also serves as a factory for Consumer, Producer, and Exchange objects. There are three different approaches to implementing an endpoint:

  • Event-driven
  • scheduled poll
  • polling

These endpoint implementation patterns complement the corresponding patterns for implementing a consumer — see Section 41.2, “Implementing the Consumer Interface”.

Figure 40.1, “Endpoint Inheritance Hierarchy” shows the relevant Java interfaces and classes that make up the Endpoint inheritance hierarchy.

Figure 40.1. Endpoint Inheritance Hierarchy

Endpoint inheritance hierarchy

The Endpoint interface

Example 40.1, “Endpoint Interface” shows the definition of the org.apache.camel.Endpoint interface.

Example 40.1. Endpoint Interface

package org.apache.camel;

public interface Endpoint {
    boolean isSingleton();

    String getEndpointUri();

    String getEndpointKey();

    CamelContext getCamelContext();
    void setCamelContext(CamelContext context);

    void configureProperties(Map options);

    boolean isLenientProperties();

    Exchange createExchange();
    Exchange createExchange(ExchangePattern pattern);
    Exchange createExchange(Exchange exchange);

    Producer createProducer() throws Exception;

    Consumer createConsumer(Processor processor) throws Exception;
    PollingConsumer createPollingConsumer() throws Exception;
}

Endpoint methods

The Endpoint interface defines the following methods:

  • isSingleton() — Returns true, if you want to ensure that each URI maps to a single endpoint within a CamelContext. When this property is true, multiple references to the identical URI within your routes always refer to a single endpoint instance. When this property is false, on the other hand, multiple references to the same URI within your routes refer to distinct endpoint instances. Each time you refer to the URI in a route, a new endpoint instance is created.
  • getEndpointUri() — Returns the endpoint URI of this endpoint.
  • getEndpointKey() — Used by org.apache.camel.spi.LifecycleStrategy when registering the endpoint.
  • getCamelContext() — return a reference to the CamelContext instance to which this endpoint belongs.
  • setCamelContext() — Sets the CamelContext instance to which this endpoint belongs.
  • configureProperties() — Stores a copy of the parameter map that is used to inject parameters when creating a new Consumer instance.
  • isLenientProperties() — Returns true to indicate that the URI is allowed to contain unknown parameters (that is, parameters that cannot be injected on the Endpoint or the Consumer class). Normally, this method should be implemented to return false.
  • createExchange() — An overloaded method with the following variants:

    • Exchange createExchange() — Creates a new exchange instance with a default exchange pattern setting.
    • Exchange createExchange(ExchangePattern pattern) — Creates a new exchange instance with the specified exchange pattern.
    • Exchange createExchange(Exchange exchange) — Converts the given exchange argument to the type of exchange needed for this endpoint. If the given exchange is not already of the correct type, this method copies it into a new instance of the correct type. A default implementation of this method is provided in the DefaultEndpoint class.
  • createProducer() — Factory method used to create new Producer instances.
  • createConsumer() — Factory method to create new event-driven consumer instances. The processor argument is a reference to the first processor in the route.
  • createPollingConsumer() — Factory method to create new polling consumer instances.

Endpoint singletons

In order to avoid unnecessary overhead, it is a good idea to create a single endpoint instance for all endpoints that have the same URI (within a CamelContext). You can enforce this condition by implementing isSingleton() to return true.

Note

In this context, same URI means that two URIs are the same when compared using string equality. In principle, it is possible to have two URIs that are equivalent, though represented by different strings. In that case, the URIs would not be treated as the same.

40.2. Implementing the Endpoint Interface

Alternative ways of implementing an endpoint

The following alternative endpoint implementation patterns are supported:

Event-driven endpoint implementation

If your custom endpoint conforms to the event-driven pattern (see Section 38.1.3, “Consumer Patterns and Threading”), it is implemented by extending the abstract class, org.apache.camel.impl.DefaultEndpoint, as shown in Example 40.2, “Implementing DefaultEndpoint”.

Example 40.2. Implementing DefaultEndpoint

import java.util.Map;
import java.util.concurrent.BlockingQueue;

import org.apache.camel.Component;
import org.apache.camel.Consumer;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
import org.apache.camel.impl.DefaultEndpoint;
import org.apache.camel.impl.DefaultExchange;

public class CustomEndpoint extends DefaultEndpoint { 1

    public CustomEndpoint(String endpointUri, Component component) { 2
        super(endpointUri, component);
        // Do any other initialization...
    }

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

    public Consumer createConsumer(Processor processor) throws Exception { 4
        return new CustomConsumer(this, processor);
    }

    public boolean isSingleton() {
        return true;
    }

    // Implement the following methods, only if you need to set exchange properties.
    //
    public Exchange createExchange() { 5
        return this.createExchange(getExchangePattern());
    }

    public Exchange createExchange(ExchangePattern pattern) {
        Exchange result = new DefaultExchange(getCamelContext(), pattern);
        // Set exchange properties
        ...
        return result;
    }
}
1
Implement an event-driven custom endpoint, CustomEndpoint, by extending the DefaultEndpoint class.
2
You must have at least one constructor that takes the endpoint URI, endpointUri, and the parent component reference, component, as arguments.
3
Implement the createProducer() factory method to create producer endpoints.
4
Implement the createConsumer() factory method to create event-driven consumer instances.
5
In general, it is not necessary to override the createExchange() methods. The implementations inherited from DefaultEndpoint create a DefaultExchange object by default, which can be used in any Apache Camel component. If you need to initialize some exchange properties in the DefaultExchange object, however, it is appropriate to override the createExchange() methods here in order to add the exchange property settings.
Important

Do not override the createPollingConsumer() method.

The DefaultEndpoint class provides default implementations of the following methods, which you might find useful when writing your custom endpoint code:

  • getEndpointUri() — Returns the endpoint URI.
  • getCamelContext() — Returns a reference to the CamelContext.
  • getComponent() — Returns a reference to the parent component.
  • createPollingConsumer() — Creates a polling consumer. The created polling consumer’s functionality is based on the event-driven consumer. If you override the event-driven consumer method, createConsumer(), you get a polling consumer implementation for free.
  • createExchange(Exchange e) — Converts the given exchange object, e, to the type required for this endpoint. This method creates a new endpoint using the overridden createExchange() endpoints. This ensures that the method also works for custom exchange types.

Scheduled poll endpoint implementation

If your custom endpoint conforms to the scheduled poll pattern (see Section 38.1.3, “Consumer Patterns and Threading”) it is implemented by inheriting from the abstract class, org.apache.camel.impl.ScheduledPollEndpoint, as shown in Example 40.3, “ScheduledPollEndpoint Implementation”.

Example 40.3. ScheduledPollEndpoint Implementation

import org.apache.camel.Consumer;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
import org.apache.camel.ExchangePattern;
import org.apache.camel.Message;
import org.apache.camel.impl.ScheduledPollEndpoint;

public class CustomEndpoint extends ScheduledPollEndpoint {  1

    protected CustomEndpoint(String endpointUri, CustomComponent component) { 2
        super(endpointUri, component);
        // Do any other initialization...
    }

    public Producer createProducer() throws Exception { 3
        Producer result = new CustomProducer(this);
        return result;
    }

    public Consumer createConsumer(Processor processor) throws Exception { 4
        Consumer result = new CustomConsumer(this, processor);
        configureConsumer(result); 5
        return result;
    }

    public boolean isSingleton() {
        return true;
    }

    // Implement the following methods, only if you need to set exchange properties.
    //
    public Exchange createExchange() { 6
        return this.createExchange(getExchangePattern());
    }

    public Exchange createExchange(ExchangePattern pattern) {
        Exchange result = new DefaultExchange(getCamelContext(), pattern);
        // Set exchange properties
        ...
        return result;
    }
}
1
Implement a scheduled poll custom endpoint, CustomEndpoint, by extending the ScheduledPollEndpoint class.
2
You must to have at least one constructor that takes the endpoint URI, endpointUri, and the parent component reference, component, as arguments.
3
Implement the createProducer() factory method to create a producer endpoint.
4
Implement the createConsumer() factory method to create a scheduled poll consumer instance.
5
The configureConsumer() method, defined in the ScheduledPollEndpoint base class, is responsible for injecting consumer query options into the consumer. See the section called “Consumer parameter injection”.
6
In general, it is not necessary to override the createExchange() methods. The implementations inherited from DefaultEndpoint create a DefaultExchange object by default, which can be used in any Apache Camel component. If you need to initialize some exchange properties in the DefaultExchange object, however, it is appropriate to override the createExchange() methods here in order to add the exchange property settings.
Important

Do not override the createPollingConsumer() method.

Polling endpoint implementation

If your custom endpoint conforms to the polling consumer pattern (see Section 38.1.3, “Consumer Patterns and Threading”), it is implemented by inheriting from the abstract class, org.apache.camel.impl.DefaultPollingEndpoint, as shown in Example 40.4, “DefaultPollingEndpoint Implementation”.

Example 40.4. DefaultPollingEndpoint Implementation

import org.apache.camel.Consumer;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
import org.apache.camel.ExchangePattern;
import org.apache.camel.Message;
import org.apache.camel.impl.DefaultPollingEndpoint;

public class CustomEndpoint extends DefaultPollingEndpoint {
    ...
    public PollingConsumer createPollingConsumer() throws Exception {
        PollingConsumer result = new CustomConsumer(this);
        configureConsumer(result);
        return result;
    }

    // Do NOT implement createConsumer(). It is already implemented in DefaultPollingEndpoint.
    ...
}

Because this CustomEndpoint class is a polling endpoint, you must implement the createPollingConsumer() method instead of the createConsumer() method. The consumer instance returned from createPollingConsumer() must inherit from the PollingConsumer interface. For details of how to implement a polling consumer, see the section called “Polling consumer implementation”.

Apart from the implementation of the createPollingConsumer() method, the steps for implementing a DefaultPollingEndpoint are similar to the steps for implementing a ScheduledPollEndpoint. See Example 40.3, “ScheduledPollEndpoint Implementation” for details.

Implementing the BrowsableEndpoint interface

If you want to expose the list of exchange instances that are pending in the current endpoint, you can implement the org.apache.camel.spi.BrowsableEndpoint interface, as shown in Example 40.5, “BrowsableEndpoint Interface”. It makes sense to implement this interface if the endpoint performs some sort of buffering of incoming events. For example, the Apache Camel SEDA endpoint implements the BrowsableEndpoint interface — see Example 40.6, “SedaEndpoint Implementation”.

Example 40.5. BrowsableEndpoint Interface

package org.apache.camel.spi;

import java.util.List;

import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;

public interface BrowsableEndpoint extends Endpoint {
    List<Exchange> getExchanges();
}

Example

Example 40.6, “SedaEndpoint Implementation” shows a sample implementation of SedaEndpoint. The SEDA endpoint is an example of an event-driven endpoint. Incoming events are stored in a FIFO queue (an instance of java.util.concurrent.BlockingQueue) and a SEDA consumer starts up a thread to read and process the events. The events themselves are represented by org.apache.camel.Exchange objects.

Example 40.6. SedaEndpoint Implementation

package org.apache.camel.component.seda;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;

import org.apache.camel.Component;
import org.apache.camel.Consumer;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
import org.apache.camel.impl.DefaultEndpoint;
import org.apache.camel.spi.BrowsableEndpoint;

public class SedaEndpoint extends DefaultEndpoint implements BrowsableEndpoint { 1
    private BlockingQueue<Exchange> queue;

    public SedaEndpoint(String endpointUri, Component component, BlockingQueue<Exchange> queue) { 2
        super(endpointUri, component);
        this.queue = queue;
    }

    public SedaEndpoint(String uri, SedaComponent component, Map parameters) { 3
        this(uri, component, component.createQueue(uri, parameters));
    }

    public Producer createProducer() throws Exception { 4
        return new CollectionProducer(this, getQueue());
    }

    public Consumer createConsumer(Processor processor) throws Exception { 5
        return new SedaConsumer(this, processor);
    }

    public BlockingQueue<Exchange> getQueue() { 6
        return queue;
    }

    public boolean isSingleton() { 7
        return true;
    }

    public List<Exchange> getExchanges() { 8
        return new ArrayList<Exchange> getQueue());
    }
}
1
The SedaEndpoint class follows the pattern for implementing an event-driven endpoint by extending the DefaultEndpoint class. The SedaEndpoint class also implements the BrowsableEndpoint interface, which provides access to the list of exchange objects in the queue.
2
Following the usual pattern for an event-driven consumer, SedaEndpoint defines a constructor that takes an endpoint argument, endpointUri, and a component reference argument, component.
3
Another constructor is provided, which delegates queue creation to the parent component instance.
4
The createProducer() factory method creates an instance of CollectionProducer, which is a producer implementation that adds events to the queue.
5
The createConsumer() factory method creates an instance of SedaConsumer, which is responsible for pulling events off the queue and processing them.
6
The getQueue() method returns a reference to the queue.
7
The isSingleton() method returns true, indicating that a single endpoint instance should be created for each unique URI string.
8
The getExchanges() method implements the corresponding abstract method from BrowsableEndpoint.