Apache Camel RecipientList Examples

0

Based on the message type, we may want to send the message to one or more recipients. Consider the case of order delivery. If the customer has paid extra for a speedy delivery then we may want to route the customer order details to more than one department so that they coordinate with each other and deliver the order as soon as possible.

In case of publish-subscribe Channels you have more than one recipient subscribed to a channel but than it is static and is not dynamic as in the above example.

Recipient List is a kind of processor which inspects an incoming message and determines the list of desired recipients, and then forward the message to all channels associated
with the recipients in the list.

In in this article, we will some examples of camel’s recipient list.

This example uses the following frameworks:

  1. Maven 3.2.3
  2. Apache Camel 2.15.1
  3. Spring 4.1.5.RELEASE
  4. Eclipse  as the IDE, version Luna 4.4.1.

Dependencies

We are just relying camel’s core components, the spring based components and the logger component in case you want to log something so our pom.xml consists of:

  1. camel-core – camel core components like timer, bean etc
  2. slf4j-api – in case you want to use log
  3. slf4j-log4j12 – if you want to use log4j as the slf4j implementation
  4. camel-stream – for printing the messages to console
  5. spring-context – for spring support
  6. camel-spring – include it if you want to define route in spring

pom.xml:

<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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.javarticles.camel</groupId>
	<artifactId>camelRecipientList</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<dependencies>
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-core</artifactId>
			<version>2.15.2</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.12</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.12</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.1.5.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-spring</artifactId>
			<version>2.15.1</version>
		</dependency>		
	</dependencies>
</project>

Recipient List Example

In the below example, we send a message related to an employee to a list of recipients.
The following route will take the list of recipients from a header named departments. You will use the recipientList DSL statement to extract the list of endpoints from the expression.

from("direct:start").recipientList(header("departments"));

We set the recipient list to the header as we initiate the route.

template.sendBodyAndHeader("direct:start", "Sam Joined", "departments", "direct:account,direct:hr,direct:manager");

Note each recipient is separated from the next by a comma.

CamelRecipientListExample:

package com.javarticles.camel;

import org.apache.camel.CamelContext;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;

public class CamelRecipientListExample {
    public static final void main(String[] args) throws Exception {
        CamelContext camelContext = new DefaultCamelContext();
        try {
            camelContext.addRoutes(new RouteBuilder() {
                public void configure() {
                    from("direct:start")
                    .recipientList(header("departments"));
                    
                    from("direct:account")
                    .log("Account department notified '${body}'");
                    
                    from("direct:hr")
                    .log("HR department notified '${body}'");
                    
                    from("direct:manager")
                    .log("Manager notified '${body}'");
                }
            });
            ProducerTemplate template = camelContext.createProducerTemplate();
            camelContext.start();
            template.sendBodyAndHeader("direct:start", "Sam Joined",
                    "departments", "direct:account,direct:hr,direct:manager");
        } finally {
            camelContext.stop();
        }
    }
}

Output:

11:21| INFO | MarkerIgnoringBase.java 95 | Account department notified 'Sam Joined'
11:21| INFO | MarkerIgnoringBase.java 95 | HR department notified 'Sam Joined'
11:21| INFO | MarkerIgnoringBase.java 95 | Manager notified 'Sam Joined'

You can add a processor which contains the custom logic to figure out the recipient list based on the message content.

CamelRecipientListHeaderProcessorExample:

package com.javarticles.camel;

import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;

public class CamelRecipientListHeaderProcessorExample {
    public static final void main(String[] args) throws Exception {
        CamelContext camelContext = new DefaultCamelContext();
        try {
            camelContext.addRoutes(new RouteBuilder() {
                public void configure() {
                    from("direct:start")
                    .process(new Processor() {
                        public void process(Exchange exchange) throws Exception {
                            String recipients = "direct:hr";
                            String employeeAction =
                            exchange.getIn().getHeader("employee_action", String.class);
                             if (employeeAction.equals("new")) {
                                recipients += ",direct:account,direct:manager";
                             } else if (employeeAction.equals("resigns")) {
                                 recipients += ",direct:account";
                             }
                             exchange.getIn().setHeader("departments", recipients);
                           }
                      })
                      .recipientList(header("departments"));
                    
                    
                    from("direct:account")
                    .log("Account department notified '${body}'");
                    
                    from("direct:hr")
                    .log("HR department notified '${body}'");
                    
                    from("direct:manager")
                    .log("Manager notified '${body}'");
                }
            });
            ProducerTemplate template = camelContext.createProducerTemplate();
            camelContext.start();
            System.out.println("************************");
            template.sendBodyAndHeader("direct:start", "Sam Joined",
                    "employee_action", "new");
            System.out.println("************************");
            template.sendBodyAndHeader("direct:start", "John Resigned",
                    "employee_action", "resigns");
            System.out.println("************************");
        } finally {
            camelContext.stop();
        }
    }
}

Output:

************************
15:35| INFO | MarkerIgnoringBase.java 95 | HR department notified 'Sam Joined'
15:35| INFO | MarkerIgnoringBase.java 95 | Account department notified 'Sam Joined'
15:35| INFO | MarkerIgnoringBase.java 95 | Manager notified 'Sam Joined'
************************
15:35| INFO | MarkerIgnoringBase.java 95 | HR department notified 'John Resigned'
15:35| INFO | MarkerIgnoringBase.java 95 | Account department notified 'John Resigned'
************************

Using Tokenizer in RecipientList

You can change the character used to delimit the list of endpoints in the String through the tokenize() function.
In the below example, the recipients are delimited by >.

CamelRecipientListTokenizerExample:

package com.javarticles.camel;

import org.apache.camel.CamelContext;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;

public class CamelRecipientListTokenizerExample {
    public static final void main(String[] args) throws Exception {
        CamelContext camelContext = new DefaultCamelContext();
        try {
            camelContext.addRoutes(new RouteBuilder() {
                public void configure() {
                    from("direct:start")
                    .recipientList(header("departments").tokenize(">"));
                    
                    from("direct:account")
                    .log("Account department notified '${body}'");
                    
                    from("direct:hr")
                    .log("HR department notified '${body}'");
                    
                    from("direct:manager")
                    .log("Manager notified '${body}'");
                }
            });
            ProducerTemplate template = camelContext.createProducerTemplate();
            camelContext.start();
            template.sendBodyAndHeader("direct:start", "Sam Joined",
                    "departments", "direct:account>direct:hr>direct:manager");
        } finally {
            camelContext.stop();
        }
    }
}

Output:

11:24| INFO | MarkerIgnoringBase.java 95 | Account department notified 'Sam Joined'
11:24| INFO | MarkerIgnoringBase.java 95 | HR department notified 'Sam Joined'
11:24| INFO | MarkerIgnoringBase.java 95 | Manager notified 'Sam Joined'

If you don’t call the tokenizer(">") method, you will end up with exception failing the route.

Caused by: org.apache.camel.component.direct.DirectConsumerNotAvailableException: No consumers available on endpoint: Endpoint[direct://account%3Edirect:hr%3Edirect:manager]. Exchange[Message: Sam Joined]

Ignore Invalid Endpoint in Recipient List

If one of the endpoint URIs named by your Expression is unrecognized, Camel will throw an org.apache.camel.ResolveEndpointFailedException exception.

Caused by: org.apache.camel.ResolveEndpointFailedException: Failed to resolve endpoint: some://unknownEndpoint due to: No component found with scheme: some

The exception is pointing that the endpoint URIs that was returned by the recipient list expression contained an endpoint that could not be resolved by Camel.
You can relax this constraint by using the ignoreInvalidEndpoints attribute:

CamelRecipientListIgnoreInvalidEndpointsExample:

package com.javarticles.camel;

import org.apache.camel.CamelContext;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;

public class CamelRecipientListIgnoreInvalidEndpointsExample {
    public static final void main(String[] args) throws Exception {
        CamelContext camelContext = new DefaultCamelContext();
        try {
            camelContext.addRoutes(new RouteBuilder() {
                public void configure() {
                    from("direct:start")
                    .recipientList(header("departments")).ignoreInvalidEndpoints();
                    
                    from("direct:account")
                    .log("Account department notified '${body}'");
                    
                    from("direct:hr")
                    .log("HR department notified '${body}'");
                }
            });
            ProducerTemplate template = camelContext.createProducerTemplate();
            camelContext.start();
            template.sendBodyAndHeader("direct:start", "Sam Joined",
                    "departments", "direct:account,direct:hr,some:unknownEndpoint");
        } finally {
            camelContext.stop();
        }
    }
}

Output:

11:27| INFO | MarkerIgnoringBase.java 95 | Account department notified 'Sam Joined'
11:27| INFO | MarkerIgnoringBase.java 95 | HR department notified 'Sam Joined'

Dynamic Recipient List using Bean

When you want to dynamically decide a list of endpoints that an individual message should be sent to, you can write the custom logic in a bean.
We want to use an Employee object as the payload. Depending upon the type of action on the employee object, we build the recipient list.

  1. If employee is new, we want to send a message to accounts department, HR and the manager
  2. If employee is on leave, recipient list is manager and HR
  3. If employee is promoted, recipient list is HR and accounts
  4. If an employee resigns, recipient list is accounts and HR
Camel Recipient List

Camel Recipient List

The recipentList statement can also process any iterable type, so instead of a comma separated recipient list, you could just simply return an array, a java.util.Collection or java.util.Iterator.

Here is the employee bean. We have kept it simple. It has a boolean member for each action and it also has a message member which contains more details about the action.

Employee:

package com.javarticles.camel;

public class Employee {
    private boolean isResigning;
    private boolean isNew;
    private boolean isOnLeave;
    private boolean isPromoted;
    private String msg;
    private String name;
    
    public Employee(String name) {
        this.name = name;
    }
    
    public void setResigning(boolean isResigning) {
        this.isResigning = isResigning;
    }

    public void setNew(boolean isNew) {
        this.isNew = isNew;
    }

    public void setOnLeave(boolean isOnLeave) {
        this.isOnLeave = isOnLeave;
    }

    public void setPromoted(boolean isPromoted) {
        this.isPromoted = isPromoted;
    }

    public boolean isResigning() {
        return isResigning;
    }

    public boolean isNew() {
        return isNew;
    }

    public boolean isOnLeave() {
        return isOnLeave;
    }

    public boolean isPromoted() {
        return isPromoted;
    }
    
    public void setMessage(String msg) {
        this.msg = msg;
    }
    
    public String toString() {
        return "Employee " + name + " " + msg;
    }
}

Here is the custom logic that decides the recipient list based on the employee action.

EmployeeRouter:

package com.javarticles.camel;

public class EmployeeRouter {
    public String[] routeEmployee(Employee emp) {
        if (emp.isResigning()) {
            return new String[]{"direct:account", "direct:hr"};
        }
        if (emp.isNew()) {
            return new String[]{"direct:manager", "direct:account", "direct:hr"};
        }
        if (emp.isOnLeave()) {
            return new String[]{"direct:manager", "direct:hr"};
        }
        if (emp.isPromoted()) {
            return new String[]{"direct:hr", "direct:account"};
        }
        
        return new String[]{"direct:hr"};
    }
}

We will test the recipient list for each employee action.

CamelRecipientListRouterExample:

package com.javarticles.camel;

import org.apache.camel.CamelContext;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.util.jndi.JndiContext;

public class CamelRecipientListRouterExample {
    public static final void main(String[] args) throws Exception {
        JndiContext jndiContext = new JndiContext();
        jndiContext.bind("empRouter", new EmployeeRouter());
        CamelContext camelContext = new DefaultCamelContext(jndiContext);
        try {
            camelContext.addRoutes(new RouteBuilder() {
                public void configure() {
                    from("direct:start")
                    .recipientList(method("empRouter", "routeEmployee"));
                    
                    from("direct:account")
                    .log("Account department notified '${body}'");
                    
                    from("direct:hr")
                    .log("HR department notified '${body}'");
                    
                    from("direct:manager")
                    .log("Manager notified '${body}'");
                }
            });
            ProducerTemplate template = camelContext.createProducerTemplate();
            camelContext.start();
            System.out.println("************************");
            Employee sam = new Employee("Sam");
            sam.setNew(true);
            sam.setMessage("Joined");
            
            template.sendBody("direct:start", sam);
            
            System.out.println("************************");
            
            Employee john = new Employee("John");
            john.setOnLeave(true);
            john.setMessage("On Leave");
            template.sendBody("direct:start", john);
            
            System.out.println("************************");
            
            Employee roy = new Employee("Roy");
            roy.setPromoted(true);
            roy.setMessage("Promoted");
            template.sendBody("direct:start", roy);
            
            System.out.println("************************");
            
            Employee ram = new Employee("Ram");
            ram.setResigning(true);
            ram.setMessage("Resigning");
            template.sendBody("direct:start", ram);
            
            System.out.println("************************");
        } finally {
            camelContext.stop();
        }
    }
}

Output:

************************
11:28| INFO | MarkerIgnoringBase.java 95 | Manager notified 'Employee Sam Joined'
11:28| INFO | MarkerIgnoringBase.java 95 | Account department notified 'Employee Sam Joined'
11:28| INFO | MarkerIgnoringBase.java 95 | HR department notified 'Employee Sam Joined'
************************
11:28| INFO | MarkerIgnoringBase.java 95 | Manager notified 'Employee John On Leave'
11:28| INFO | MarkerIgnoringBase.java 95 | HR department notified 'Employee John On Leave'
************************
11:28| INFO | MarkerIgnoringBase.java 95 | HR department notified 'Employee Roy Promoted'
11:28| INFO | MarkerIgnoringBase.java 95 | Account department notified 'Employee Roy Promoted'
************************
11:28| INFO | MarkerIgnoringBase.java 95 | Account department notified 'Employee Ram Resigning'
11:28| INFO | MarkerIgnoringBase.java 95 | HR department notified 'Employee Ram Resigning'
************************

Recipient List using Spring

In this example, we will show you the above recipientList example using DSL defined in spring.
We set the recipient list using <recipientList> tag. If you want to ignore the unresolved endpoints set attribute ignoreInvalidEndpoints to true.
The expression based on which the recipient list is built is added as child tag to recipientList. Here it is based on the header so we add <header> as child element.

<recipientList ignoreInvalidEndpoints="true">
    <header>departments</header>
</recipientList>

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
       ">
	<bean id="empRouter" class="com.javarticles.camel.EmployeeRouter" />

	<camelContext xmlns="http://camel.apache.org/schema/spring">
		<route>
			<from uri="direct:start" />
			<setHeader headerName="departments">
				<method ref="empRouter" method="routeEmployee" />
			</setHeader>
			<recipientList ignoreInvalidEndpoints="true">
				<header>departments</header>
			</recipientList>
		</route>

		<route>
			<from uri="direct:account" />
			<log message="Account department notified '${body}"/>
		</route>

		<route>
			<from uri="direct:hr" />
			<log message="HR department notified '${body}"/>
		</route>

		<route>
			<from uri="direct:manager" />
			<log message="Manager notified '${body}"/>
		</route>

	</camelContext>

</beans>

CamelRecipientListExampleUsingSpring:

package com.javarticles.camel;

import org.apache.camel.CamelContext;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.spring.SpringCamelContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class CamelRecipientListExampleUsingSpring {
    public static final void main(String[] args) throws Exception {
        ApplicationContext appContext = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        CamelContext camelContext = SpringCamelContext.springCamelContext(
                appContext, false);
        try {
            ProducerTemplate template = camelContext.createProducerTemplate();           
            camelContext.start();
            System.out.println("************************");
            Employee sam = new Employee("Sam");
            sam.setNew(true);
            sam.setMessage("Joined");
            
            template.sendBody("direct:start", sam);
            
            System.out.println("************************");
            
            Employee john = new Employee("John");
            john.setOnLeave(true);
            john.setMessage("On Leave");
            template.sendBody("direct:start", john);
            
            System.out.println("************************");
            
            Employee roy = new Employee("Roy");
            roy.setPromoted(true);
            roy.setMessage("Promoted");
            template.sendBody("direct:start", roy);
            
            System.out.println("************************");
            
            Employee ram = new Employee("Ram");
            ram.setResigning(true);
            ram.setMessage("Resigning");
            template.sendBody("direct:start", ram);
            
            System.out.println("************************");
        } finally {
            camelContext.stop();
        }
    }
}

Output:

************************
15:57| INFO | MarkerIgnoringBase.java 95 | Manager notified 'Employee Sam Joined
15:57| INFO | MarkerIgnoringBase.java 95 | Account department notified 'Employee Sam Joined
15:57| INFO | MarkerIgnoringBase.java 95 | HR department notified 'Employee Sam Joined
************************
15:57| INFO | MarkerIgnoringBase.java 95 | Manager notified 'Employee John On Leave
15:57| INFO | MarkerIgnoringBase.java 95 | HR department notified 'Employee John On Leave
************************
15:57| INFO | MarkerIgnoringBase.java 95 | HR department notified 'Employee Roy Promoted
15:57| INFO | MarkerIgnoringBase.java 95 | Account department notified 'Employee Roy Promoted
************************
15:57| INFO | MarkerIgnoringBase.java 95 | Account department notified 'Employee Ram Resigning
15:57| INFO | MarkerIgnoringBase.java 95 | HR department notified 'Employee Ram Resigning
************************

Download the source code

This was an example about Camel Recipient List.

You can download the source code here: camelRecipientListExample.zip
Share.

Comments are closed.