Timo
Timo

Reputation: 2242

How can I call multiple beans that all implement a specific interface?

I have a set of beans that implement an interface. I want to multicast an incoming message to an explicit (sub)set of these beans using a method from this interface. The method call will result in a List<Object> for each bean. I want to aggregate all the lists into one big list and pass that on to the next route.

My first approach was to create a route for each bean that passes the result to the aggregation route. However I think I lose the type-safety that I tried to enforce with the interface. (In other words, I cannot prevent other people from adding beans to the multicast that do not implement the interface. I believe this could lead to an error in my aggregation strategy at runtime (e.g. body is not a list).)

The Interface:

public interface Reporter {
    List<Object> report();
}

An example of the routes I created for each bean that implements the interface:

<route>
    <from uri="direct:bean1"/>
    <bean ref="reporterOfSomething" method="report"/>
    <to uri="seda:aggregate"/>
</route>

My current approach is to create a list bean with a value-type attribute referencing the Reporter interface and adding the implementing beans to this list. This way I have my types checked by the scheme validation, not just when the route is called. However I couldn't figure out how to multicast the incoming message to each bean in the list.

<util:list id="reporters" value-type="Reporter"><!-- i.e. the interface -->
    <ref bean="reporterOfSomething"/>
    <ref bean="reporterOfSomethingElse"/>
</util:list>

So I have the feeling that I'm either overcomplicating things or just not using the right constructs.

How can I call all these beans and have some form of type-safety (e.g. Intellij complains that I violate the scheme when I pass a bean of the wrong type or the application could crash during startup or some other way that will cause a compile/scheme/initialization error)?

Upvotes: 2

Views: 1428

Answers (3)

Peter Keller
Peter Keller

Reputation: 7646

Define a processor that iterates over the reporting beans and aggregates the results of the single reporter beans:

public class ReporterProcessor implements Processor {
    private List<Reporter> reporters;
    @Override
    public void process(final Exchange exchange) throws Exception {
        List<Object> aggregatedList = new ArrayList<>();
        for (final ReporterBean reporter : reporters) {
            aggregatedList.add(reporter.report(exchange));
        }
        exchange.getIn().setBody(aggregatedList);
    }
    public void setReporters(final List<Reporter> reporters) {
        this.reporters = reporters;
    }
}

Camel route definition:

<util:list id="reporterList" value-type="Reporter"> <!-- i.e. the interface -->
    <ref bean="reporterOfSomething"/>
    <ref bean="reporterOfSomethingElse"/>
</util:list>

<bean id="reporterProcessor" class="ReporterProcessor">
    <property name="reporters" ref="reporterList" />
</bean>

<camelContext xmlns="http://camel.apache.org/schema/spring">
    <route>
        <from uri="direct:bean1"/>
        <process ref="reporterProcessor"/>
        <to uri="seda:aggregate"/>
    </route>
</camelContext>

That way, all the results of the single reporter beans are aggregated and passed to seda:aggregate.

Upvotes: 1

user3001
user3001

Reputation: 3497

Maybe you can use an observer pattern, like described at Wikipedia: http://en.wikipedia.org/wiki/Observer_pattern

It is designed for passing messages around. Otherwise you also can have a look at CompleteableFuture which can send messages (a)synchronously on completion.

Upvotes: 0

Related Questions