Apache Camel

Apache Camel XPath Example

Processors and endpoints are an integral part of a camel routing. Camel uses Domain Specific Language (DSL) to define its routing. When it comes to defining predicates in the routing, you need a mature expression language. There are many different expression languages to choose from, some of which include Simple, EL, JXPath, Mvel, OGNL, PHP, BeanShell, JavaScript, Groovy, Python, Ruby,
XPath.

In this article, we will look into examples of XPath. We will use XPath to create a Predicate in a Message Filter.

Before we start with our example, Let’s look into the setup details.

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.

1. Dependencies

I will be showing you some examples of camel ActiveMQ so you need to add the following dependencies:

  1. camel-core – basic module of apache camel.
  2. camel-stream – We will use this to send output to the console.
  3. spring-context and camel-spring – Since we be configuring our camel context 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.javacodegeeks.camel</groupId>
	<artifactId>camelHelloWorld</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<dependencies>
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-core</artifactId>
			<version>2.15.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-stream</artifactId>
			<version>2.15.1</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>

2. XPath to filter messages

In the below example, we filter out the orders which are available with an XPath expression and then send them to an order processor bean.

OrderProcessor:

package com.javacodegeeks.camel;

public class OrderProcessor {
	public String process(String payload) {
		System.out.println("Processed " + payload);
		return payload;
	}
}

CamelXPathFilterExample:

package com.javacodegeeks.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 CamelXPathFilterExample {
	public static void main(String[] args) throws Exception {
		JndiContext jndiContext = new JndiContext();
		jndiContext.bind("orderBean", new OrderProcessor());		
		CamelContext camelContext = new DefaultCamelContext(jndiContext);
		try {
			camelContext.addRoutes(new RouteBuilder() {
				@Override
				public void configure() throws Exception {
					 from("direct:start")
	                    .choice()
	                        .when().xpath("//available = 'true'").to("bean:orderBean")
	                    .end()
	                    .to("stream:out");
				}
			});
			camelContext.start();
			ProducerTemplate template = camelContext.createProducerTemplate();
			template.sendBody("direct:start", "<order><product>laptop</product><available>true</available></order>");
			template.sendBody("direct:start", "<order><product>books</product><available>false</available></order>");
		} finally {
			camelContext.stop();
		}
	}
}

As you can see only the laptop orders get processed and the books order is skipped as they are out of stock.

Output:

Processed <order><product>laptop</product><available>true</available></order>
<order><product>laptop</product><available>true</available></order>
<order><product>books</product><available>false</available></order>

3. XPath Filtering using Spring

The above example can be defined in spring. We use <xpath> element to define the predicate.

xpathFilterApplicationContext.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="orderProcessor" class="com.javacodegeeks.camel.OrderProcessor" />
	<camelContext xmlns="http://camel.apache.org/schema/spring">
		<route>
			<from uri="direct:start" />
			<choice>
            <when>
				<xpath>//available = 'true'</xpath>
				<to uri="bean:orderProcessor" />
			</when>
			</choice>
			<to uri="stream:out"/>
		</route>
	</camelContext>

</beans>

CamelXPathFilterUsingSpring:

package com.javacodegeeks.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 CamelXPathFilterUsingSpring {
	public static final void main(String[] args) throws Exception {
		ApplicationContext appContext = new ClassPathXmlApplicationContext(
				"xpathFilterApplicationContext.xml");
		CamelContext camelContext = SpringCamelContext.springCamelContext(
				appContext, false);
		try {
			ProducerTemplate template = camelContext.createProducerTemplate();
			camelContext.start();
			template.sendBody("direct:start", "<order><product>laptop</product><available>true</available></order>");
			template.sendBody("direct:start", "<order><product>books</product><available>false</available></order>");
		} finally {
			camelContext.stop();
		}
	}
}

Output:

Processed <order><product>laptop</product><available>true</available></order>
<order><product>laptop</product><available>true</available></order>
<order><product>books</product><available>false</available></order>

4. Splitting XML Messages using XPath

In this example, we will show you how to use XPath to process fragments of XML. The orders.xml contains many orders. Each order belongs to a particular product and contains order items.  In our example below,  we extract items of a particular product.

We will use an XPath Expression within the split DSL statement to isolate the orders belonging to ‘electroincs’ product.

orders.xml:

<?xml version="1.0" encoding="UTF-8"?>
<orders>
	<order product="electronics">
		<items>
			<item>Laptop</item>
			<item>Mobile</item>
		</items>
	</order>
	<order product="books">
		<items>
			<item>Design Patterns</item>
			<item>XML</item>
		</items>
	</order>
</orders>

CamelXPathSplitExample:

package com.javacodegeeks.camel;

import java.io.FileInputStream;
import java.io.InputStream;

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

public class CamelXPathSplitExample {
	public static void main(String[] args) throws Exception {	
		CamelContext camelContext = new DefaultCamelContext();
		try {
			camelContext.addRoutes(new RouteBuilder() {
				@Override
				public void configure() throws Exception {
					from("direct:start")
					.split(xpath("//order[@product='electronics']/items/item/text()"))
					.to("stream:out")
					.end();
				}
			});
			InputStream is = new FileInputStream("src/main/resources/orders.xml");	        
			camelContext.start();
			ProducerTemplate template = camelContext.createProducerTemplate();
			template.sendBody("direct:start", is);
		} finally {
			camelContext.stop();
		}
	}
}

Output:

LaptopMobile

5. Using Namespaces with XPath

We will now modify our previous example and add a namespace to the orders.xml.

orders.xml:

<?xml version="1.0" encoding="UTF-8"?>
<orders xmlns="http://com.javacodegeeks.camel/schema/orders">
	<order product="electronics">
		<items>
			<item>Laptop</item>
			<item>Mobile</item>
		</items>
	</order>
	<order product="books">
		<items>
			<item>Design Patterns</item>
			<item>XML</item>
		</items>
	</order>
</orders>

In order for our XPath Expression to match, we will need to refer to the namespace through a prefix and associate the prefix with the namespace URI.
We can associate the namespace to the xpath() expression using namespace method. In order for our XPath Expression to match, we will need to refer to the namespace through the defined prefix.

CamelXPathSplitNamespaceExample:

package com.javacodegeeks.camel;

import java.io.FileInputStream;
import java.io.InputStream;

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

public class CamelXPathSplitNamespaceExample {
	public static void main(String[] args) throws Exception {	
		CamelContext camelContext = new DefaultCamelContext();
		try {
			camelContext.addRoutes(new RouteBuilder() {
				@Override
				public void configure() throws Exception {
					from("direct:start")
					.split(xpath("//n:order[@product='electronics']/n:items/n:item/text()")
							.namespace("n", "http://com.javacodegeeks.camel/schema/orders"))					
					.to("stream:out")
					.end();
				}
			});
			InputStream is = new FileInputStream("src/main/resources/ordersWithNamespace.xml");	        
			camelContext.start();
			ProducerTemplate template = camelContext.createProducerTemplate();
			template.sendBody("direct:start", is);
		} finally {
			camelContext.stop();
		}
	}
}

Output:

LaptopMobile

6. XML Namespaces in Spring

In the XML DSL, the namespace definition is provided on some parent XML element such as the camelContext:

xpathSplitWithNsApplicationContext.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="orderProcessor" class="com.javacodegeeks.camel.OrderProcessor" />
	<camelContext xmlns="http://camel.apache.org/schema/spring"
		xmlns:n="http://com.javacodegeeks.camel/schema/orders">
		<route>
			<from uri="direct:start" />
			<split>
				<xpath>/n:orders/n:order[@product='electronics']/n:items/n:item/text()
				</xpath>
				<to uri="stream:out" />
			</split>
		</route>
	</camelContext>

</beans>

CamelXPathSplitNamespaceUsingSpring:

package com.javacodegeeks.camel;

import java.io.FileInputStream;
import java.io.InputStream;

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 CamelXPathSplitNamespaceUsingSpring {
	public static final void main(String[] args) throws Exception {
		ApplicationContext appContext = new ClassPathXmlApplicationContext(
				"xpathSplitWithNsApplicationContext.xml");
		CamelContext camelContext = SpringCamelContext.springCamelContext(
				appContext, false);
		InputStream is = new FileInputStream("src/main/resources/ordersWithNamespace.xml");	        
		camelContext.start();
		ProducerTemplate template = camelContext.createProducerTemplate();
		template.sendBody("direct:start", is);
	}
}

Output:

LaptopMobile

7. Binding using Camel language annotations

Camel provides a way for you to use annotations to tell it how to map the message to parameters. In the below example, we use @XPat annotation to only allow processing Laptop orders.

OrderProcessor:

package com.javacodegeeks.camel;

import org.apache.camel.language.XPath;

public class OrderProcessor {
	public String process(String payload) {
		System.out.println("Processed " + payload);
		return payload;
	}
	
	public boolean processItem(@XPath("//order[@product='electronics']/items/item/text()") String item) {
		boolean condition = item != null && "Laptop".equals(item);
		if (condition) {
			System.out.println("Processing item " + item);
		}
		return condition;
	}
}

We use the above annotated method to filter the payloads.

CamelXPathAnnotationExample:

package com.javacodegeeks.camel;

import java.io.FileInputStream;
import java.io.InputStream;

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

public class CamelXPathAnnotationExample {
	public static void main(String[] args) throws Exception {	
		CamelContext camelContext = new DefaultCamelContext();
		try {
			camelContext.addRoutes(new RouteBuilder() {
				@Override
				public void configure() throws Exception {
					from("direct:start")
		            .filter().method(new OrderProcessor(), "processItem")
		                .to("stream:out");
				}
			});      
			camelContext.start();
			ProducerTemplate template = camelContext.createProducerTemplate();
			//...//order[@product='electronics']/items/item/text()
			String ordersXml = "<orders><order product=\'electronics\'><items><item>Laptop</item><item>Mobile</item></items></order></orders>";
			template.sendBody("direct:start", ordersXml);
			
			ordersXml = "<orders><order product=\'books\'><items><item>Design Patterns</item><item>XML</item></items></order></orders>";
			template.sendBody("direct:start", ordersXml);
		} finally {
			camelContext.stop();
		}
	}
}

Output:

Processing item Laptop
LaptopMobile

8. Download the Eclipse Project

This was an example about Apache Camel XPath.

Download
You can download the full source code of this example here: camelXpathExample.zip

Ram Mokkapaty

Ram holds a master's degree in Machine Design from IT B.H.U. His expertise lies in test driven development and re-factoring. He is passionate about open source technologies and actively blogs on various java and open-source technologies like spring. He works as a principal Engineer in the logistics domain.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button