Gerold Broser
Gerold Broser

Reputation: 14792

Why is Camel sending Message body content rather than the Message itself when using the Bean component?

I created a Java class for EIP Event Messages. I expected to receive a Message at Observer.accept(Object o) when using Camel's Bean component but I get an Event instead. While this is convenient since I don't have to extract the Event from the Message's body myself I don't have access to the message's possible headers and attachments with this.

EventMessageTest.java

package name.igb.eip.integration.styles.test;

import java.time.Instant;
import java.util.List;
import java.util.Vector;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.impl.engine.DefaultCamelContextNameStrategy;
import org.apache.camel.model.RouteDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class EventMessageTest {

    static final Logger log = LoggerFactory.getLogger( EventMessageTest.class );

    public static void main( final String... args ) throws Exception {

        try ( CamelContext cc = new DefaultCamelContext(); ) {

            cc.setNameStrategy( new DefaultCamelContextNameStrategy( "Messaging Sample" ) );

            cc.getRegistry().bind( "Subject", new Subject( "Subject" ) );
            cc.getRegistry().bind( "Observer", new Observer( "Observer" ) );

            final List<Observer> observers = new Vector<>();
            observers.add( new Observer( "Observer 1" ) );
            observers.add( new Observer( "Observer 2" ) );
            observers.add( new Observer( "Observer 3" ) );

            cc.addRoutes( new RouteBuilder() {

                @Override
                public void configure() {
                    final RouteDefinition route =
                        from( "timer:Event?repeatCount=1" )
                            .log( "creating endpoint " + Subject.class.getSimpleName() + "..." )
                            .bean( Subject.class ) // Subject.get() returns an Event
                            .log( "creating..." )
                            .process().message( m -> {
                                log.info( "...{}<{}>",
                                    m, m.getBody( Event.class ).getClass().getSimpleName() );
                            } )
                            .routeId( "Event route" );

                    for ( final Observer observer : observers ) {
                        log.info( "creating endpoint {}...", observer.id );
                        route
                            .to( "bean:Observer" ) // this sends Event to Observer
                            // this alternative sends Message to Observer N
                            .process().message( m -> {
                                observer.accept( m );
                            } );
                    }
                }
            } ); // addRoutes( RouteBuilder() )

            cc.start();
            Thread.sleep( 2000 );
            cc.stop();
            cc.close();
        }
        catch ( final Exception e ) {
            e.printStackTrace();
        }
    } // main()

} // EventMessageTest

class Subject implements Supplier<Event> {

    String id;

    Subject( final String id ) {
        this.id = id;
    }

    @Override
    @Handler
    public Event get() {
        return new Event();
    }

} // Subject

class Event {
    String id = Instant.now().toString();
} // Event

class Observer implements Consumer<Object> {

    String id;

    Observer( final String id ) throws Exception {
        this.id = id;
    }

    Observer setId( final String id ) {
        this.id = id;
        return this;
    }

    @Override
    @Handler
    public void accept( final Object o ) {
        // Why is o an Event and not a Message here if Observer is created
        // by RouteDefinition.to( "bean:Observer" )?
        EventMessageTest.log.info( "\n{} receiving {}...",
            id, o.getClass().getSimpleName() );
    }

} // Observer

Output

19:24:13.404 [main] INFO  n.i.e.i.s.test.EventMessageTest$1 - creating endpoint Observer 1...
19:24:13.408 [main] INFO  n.i.e.i.s.test.EventMessageTest$1 - creating endpoint Observer 2...
19:24:13.408 [main] INFO  n.i.e.i.s.test.EventMessageTest$1 - creating endpoint Observer 3...
19:24:14.553 [Camel (camel-1) thread #1 - timer://Event] INFO  Event route - creating endpoint Subject...
19:24:14.559 [Camel (camel-1) thread #1 - timer://Event] INFO  Event route - creating...
19:24:14.559 [Camel (camel-1) thread #1 - timer://Event] INFO  n.i.e.i.s.test.EventMessageTest$1 - ...Message<Event>
19:24:14.560 [Camel (camel-1) thread #1 - timer://Event] INFO  n.i.e.i.styles.test.EventMessageTest - 
Observer receiving Event...
19:24:14.560 [Camel (camel-1) thread #1 - timer://Event] INFO  n.i.e.i.styles.test.EventMessageTest - 
Observer 1 receiving DefaultMessage...
19:24:14.560 [Camel (camel-1) thread #1 - timer://Event] INFO  n.i.e.i.styles.test.EventMessageTest - 
Observer receiving Event...
19:24:14.560 [Camel (camel-1) thread #1 - timer://Event] INFO  n.i.e.i.styles.test.EventMessageTest - 
Observer 2 receiving DefaultMessage...
19:24:14.560 [Camel (camel-1) thread #1 - timer://Event] INFO  n.i.e.i.styles.test.EventMessageTest - 
Observer receiving Event...
19:24:14.560 [Camel (camel-1) thread #1 - timer://Event] INFO  n.i.e.i.styles.test.EventMessageTest - 
Observer 3 receiving DefaultMessage...

Is this a bug or a feature (and why if it's the latter)?

If I change the following two lines to:

...
class Observer implements Consumer<Message> {
...
    public void accept( final Message o ) {
...

I get:

java.lang.ClassCastException: class name.igb.eip.integration.styles.test.Event
        cannot be cast to class org.apache.camel.Message (name.igb.eip.integration.styles.test.Event
        and org.apache.camel.Message are in unnamed module of loader 'app')
    at name.igb.eip.integration.styles.test.Observer.accept(EventMessageTest.java:1)
  ...

so apparently Camel doesn't look at the Consumer's type parameter.

POM (for MRE testing)

<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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>name.igb</groupId>
    <artifactId>eip</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.release>23</maven.compiler.release>
    
        <javaee.version>8.0.1</javaee.version>
        <jakarta.enterprise.cdi-api.version>4.1.0</jakarta.enterprise.cdi-api.version>
        <weld-se.version>6.0.0.Final</weld-se.version>
        <camel.version>4.8.2</camel.version>
        <slf4j.version>2.0.16</slf4j.version>
        <logback.version>1.5.15</logback.version>
        <mockito.version>5.14.2</mockito.version>
        <hierarchy-maven-plugin.version>1.4</hierarchy-maven-plugin.version>
    </properties>

    <dependencies>
    
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>${javaee.version}</version>
        </dependency>
        <dependency>
        <groupId>jakarta.enterprise</groupId>
        <artifactId>jakarta.enterprise.cdi-api</artifactId>
        <version>${jakarta.enterprise.cdi-api.version}</version>
        <scope>provided</scope>
        </dependency>
        <dependency>
        <groupId>org.jboss.weld.se</groupId>
        <artifactId>weld-se-shaded</artifactId>
        <version>${weld-se.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-core</artifactId>
            <version>${camel.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-test-junit5</artifactId>
            <version>${camel.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-componentdsl</artifactId>
            <version>${camel.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-direct</artifactId>
            <version>${camel.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-groovy</artifactId>
            <version>${camel.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-mail</artifactId>
            <version>${camel.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-jms</artifactId>
            <version>${camel.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-sjms</artifactId>
            <version>${camel.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-sjms2</artifactId>
            <version>${camel.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>${logback.version}</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>

        <dependency>
            <groupId>org.mockito</groupId>
            <!-- artifactId>mockito-core</artifactId -->
            <artifactId>mockito-core</artifactId>
            <version>${mockito.version}</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <defaultGoal>install</defaultGoal>
        <plugins>

            <!-- allows the route to be run via 'mvn camel:run' -->
            <plugin>
                <groupId>org.apache.camel</groupId>
                <artifactId>camel-maven-plugin</artifactId>
                <version>${camel.version}</version>
            </plugin>

            <plugin>
                <groupId>com.github.exampledriven</groupId>
                <artifactId>hierarchy-maven-plugin</artifactId>
                <version>${hierarchy-maven-plugin.version}</version>
            </plugin>

        </plugins>
    </build>

</project>

Upvotes: 0

Views: 100

Answers (0)

Related Questions