Apache Camel's Bean Enterprise Integration Pattern (EIP) lets you invoke POJO methods directly within routes, seamlessly integrating custom business logic without heavy frameworks. This article presents a fully self-contained, runnable Java example using Camel Main, featuring header manipulation and enhanced logging for real-world demonstration. The code requires only camel-core on the classpath.

Key Features Demonstrated

  • Timer-triggered message flow every 2 seconds.
  • Bean method invocation with body and headers parameter binding.
  • Custom header enrichment (processor metadata, timestamps).
  • Split routes using direct for modularity.
  • Infinite runtime via Camel Main (Ctrl+C to stop).

The bean processes the message body, uppercases it, adds headers tracking processing details, and returns the transformed body.

Complete Runnable Code

Here's the entire application in a single file—compile and run directly:

import org.apache.camel.Body;
import org.apache.camel.Headers;
import org.apache.camel.builder.RouteBuilder;

import java.util.Map;

/**
 * Apache Camel example demonstrating bean method invocation in a route.
 * Uses a timer-based route that processes messages through a custom bean.
 */
public class BeanExampleApp {

    static void main(String[] args) throws Exception {
        // Create a new Camel Main instance (using fully qualified name to avoid conflict)
        org.apache.camel.main.Main main = new org.apache.camel.main.Main();

        // Add routes with bean processing
        main.configure().addRoutesBuilder(new RouteBuilder() {
            @Override
            public void configure() {
                // Route 1: Timer that fires every 2 seconds
                from("timer:beanTick?period=2000")
                        .setBody().constant("hello from timer")
                        .log("${body}")
                        .to("direct:process")
                        .log("${body}");

                // Route 2: Process the message using a bean
                from("direct:process")
                        .bean(new MyProcessor(), "process")  // Invoke bean method
                        .log("After bean: ${body}")
                        .log("Headers - ProcessedBy: ${header.ProcessedBy}, ProcessedAt: ${header.ProcessedAt}, OriginalBody: ${header.OriginalBody}");
            }
        });

        // Run Camel (will run until Ctrl+C is pressed)
        main.run(args);
    }

    /**
     * Custom processor bean that processes messages.
     * Accepts body as String and headers as Map for manipulation.
     */
    public static class MyProcessor {
        public String process(@Body String body, @Headers Map<String, Object> headers) {
            // Add custom headers
            headers.put("ProcessedBy", "MyProcessor");
            headers.put("ProcessedAt", System.currentTimeMillis());
            headers.put("OriginalBody", body);

            // Process and return the modified body
            return body.toUpperCase() + " - processed by MyProcessor";
        }
    }
}

Expected Output

Console logs repeat every 2 seconds:

hello from timer
After bean: HELLO FROM TIMER - processed by MyProcessor
Headers - ProcessedBy: MyProcessor, ProcessedAt: 1769775068470, OriginalBody: hello from timer
HELLO FROM TIMER - processed by MyProcessor

Best Practices Highlighted

  • Parameter Binding: Use annotations like @Body, @Headers, @Header("name") for type-safe access; raw types work for simple cases.

  • Method Selection: Explicitly name "process" method to avoid ambiguity with overloads.

  • Inline Beans: Perfect for simple processors; use registry lookup for complex or shared beans.

  • Route Modularity: direct: endpoints enable clean separation of concerns.

This pattern excels for transformations, validations, and custom logic in enterprise integration routes.