Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Direct

The Direct component routes an Exchange between two routes in the same CamelContext over an in-memory channel. The Producer blocks until the Consumer's Pipeline finishes. No serialization, no network. The transformed Exchange returns to the caller.

The multi-route-direct example wires a timer-driven producer and a transform consumer:

use camel_api::body::Body;
use camel_api::Value;
use camel_builder::RouteBuilder;
use camel_component_direct::DirectComponent;
use camel_component_log::LogComponent;
use camel_component_timer::TimerComponent;
use camel_core::context::CamelContext;

#[tokio::main]
async fn main() -> Result<(), camel_api::CamelError> {
    let mut ctx = CamelContext::builder().build().await.unwrap();
    ctx.register_component(TimerComponent::new());
    ctx.register_component(LogComponent::new());
    ctx.register_component(DirectComponent::new());

    // Route A: timer -> direct:pipeline
    let route_a = RouteBuilder::from("timer:tick?period=1000")
        .route_id("route-a")
        .set_header("source", Value::String("timer".into()))
        .to("direct:pipeline")
        .build()?;

    // Route B: direct:pipeline -> uppercase -> log
    let route_b = RouteBuilder::from("direct:pipeline")
        .route_id("route-b")
        .map_body(|body: Body| {
            if let Some(text) = body.as_text() {
                Body::Text(text.to_uppercase())
            } else {
                body
            }
        })
        .to("log:output?showBody=true")
        .build()?;

    ctx.add_route_definition(route_a).await?;
    ctx.add_route_definition(route_b).await?;
    ctx.start().await?;
    Ok(())
}
YAML equivalent
routes:
  - id: route-a
    from: "timer:tick?period=1000"
    steps:
      - set_header:
          key: "source"
          value: "timer"
      - to: "direct:pipeline"
  - id: route-b
    from: "direct:pipeline"
    steps:
      - to: "log:output?showBody=true"

Both routes use the same direct:pipeline name. The endpoint name must match on the producer and consumer sides.

URI

direct:<name>[?timeout_ms=30000][&failIfNoConsumers=true]
ParameterRequiredDefaultDescription
timeout_msno30000Producer call() timeout in milliseconds
failIfNoConsumersnotrueReject the call when no Consumer is registered for the name
blocknotrueReserved for non-blocking send (TODO(DIR-001))
exchangePatternno(none)Reserved for pattern override (TODO(DIR-005))

The endpoint name must not be empty and must not contain whitespace. The Component rejects both at Endpoint creation.

Consumer

from: "direct:name" registers a DirectConsumer in the shared registry. The Consumer starts a background loop that receives Exchanges from the in-memory channel. It submits each Exchange to the Route's Pipeline through send_and_wait. The reply carries the transformed Exchange or the failure.

One Consumer per name. A second Consumer on the same name returns CamelError::EndpointCreationFailed because the registry already holds an open channel. Routes that want many workers must use a different name, or pick a Component that supports fanout (SEDA with multipleConsumers=true).

The Consumer removes its registry entry on stop() or when the cancellation token fires. The next Producer call fails with EndpointCreationFailed until a new route registers.

Producer

to: "direct:name" builds a DirectProducer. The Producer holds one in-flight call at a time through a bounded semaphore. poll_ready checks the registry and acquires a permit. call hands the Exchange to the Consumer's channel and awaits the reply.

failIfNoConsumers=true (default) rejects the call when no Consumer is registered. Set it to false to let the Producer race against late registration. The Producer still waits for the Consumer to receive the Exchange, so false does not give a fire-and-forget guarantee. Use SEDA for that.

The default timeout_ms is 30 000. A timeout returns CamelError::ProcessorError with a timed out message. The error propagates through the route's error handler.

Request-Reply

Direct is the natural request-reply Component for in-process calls. The Producer blocks until the Consumer's Pipeline finishes. Steps that follow to: "direct:name" see the transformed Exchange. Steps that set the body inside the Consumer's Route reach the caller. A timer that sends to a Direct endpoint and logs the result is a synchronous in-process function call.

Error handling

The DirectConsumer reports unhandled pipeline failures through the b-prime:direct:send-and-wait metric and logs at error! (ADR-0012 category b'). Producer send failures (no Consumer, channel closed, reply dropped) are category (a) handler-owned. The Producer logs them at warn!. The route's error handler owns the operational signal.

Reference: Direct crate CONTEXT. Example source: examples/multi-route-direct.