Camel development series – Part 2

CSV file map to Json file

The is the second part of the series. If you are a complete beginner go to part 1 . This series will touch upon mapping, specifically how to map from a very simple CSV (comma separated file) to a Json structure.

I will keep everything simple and add more complexity in subsequent series. So some prerequisites:

  1. The CSV file will only contain a single row. Handling mulitple rows will be in part 3 of the series.
  2. The Json structure will only contain field names and values.
  3. No error handling is done.

So, what do we want to do? We want to pick up a file from a specific folder, map the single row to an object in order to extract certain values and then map it to the output json format. Finally we save the json string to a file.

Camel vs IBM Integration on mapping

There is something to be said for those like me coming from another background in the integration world, specifically having use IBM WMB/Integration Bus. I think learning Camel and how routes and routebuilder works is somewhat ok once you get used to it. The error handling and logging is ok too, but the big jump from a conceptual point of view is how you do mapping.

Message Parsers

In the IBM Integration Bus you use parsers. Parsers are the key to mapping. You have the XMLNSC parser for parsing XML data, JSON parser for parsing JSON data and DFDL parser for parsing anything else. These are extremely powerful parsers and specifically the DFDL parser is incredible because you are able to model any kind of data as long as it can be described in some way. You can in practise model EDIFACT, MARC, or other complicated formats. Yes, it takes time but it can be done and then you use the parser to match the incoming data with the model you created. The DFDL parser can be debugged and tested as well which makes it very versatile.  What is the big drawback? Price. It is great for large corporations who don’t mind paying large license fees and have a heavy IBM presence. Off course ultimately it means you are locked down to IBM as well since it is proprietary tool. These are factors to consider when you choose your integration tool.

Everything is an object

In the Camel world, since it is java based, there is no concept of parsers or data modelling. This was a big conceptual change for me. Instead it is going back to basics and understanding that everything is an object. This includes a single row in a CSV file. That row is an object is as well. Multiple rows are simple list of objects. With objects we off course mean java objects defined in a class. Understanding this helps to understand the mapping. The big advantages are that for relatively simple formats there is built in support in Camel such as bindy or beanio so you can create your object model and do your mapping. However these components lack to the powerful modelling that exist in the DFDL parser which can model any type of data format. So for very complicated data formats you may need to go back to standard java code implemented in a Processor. There is off course an advantage to this as well. It is easy to read, it is not tied to a tool and it is cheaper. Again, this is something to consider when selecting your integration tool.

Project POM file

So let’s start with the actual code. As in series 1, I am basing this on structure of letting blueprint start my routebuilder and inject beans whilst I write the main code in java.

My POM file dependencies look as follows:

<dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-core</artifactId>
      <version>2.16.1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-blueprint</artifactId>
      <version>2.16.1</version>
    </dependency>
        <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-bindy</artifactId>
      <version>2.16.1</version>
    </dependency>
        <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-jackson</artifactId>
      <version>2.16.1</version>
    </dependency>

The two new dependencies are camel-bindy for modeling the CSV data and camel-jackson for creating the Json structure.

Blueprint file

My blueprint.xml is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        xsi:schemaLocation="        http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd        http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">

  <bean id="CsvToJsonRouteBuilder" class="org.souciance.integration.CsvToJson.CSVToJsonRouteBuilder">
  </bean>
  <bean id="IdentityToJson" class="org.souciance.integration.CsvToJson.IdentityToJson">
  </bean>

  <camelContext xmlns="http://camel.apache.org/schema/blueprint">
    <routeBuilder ref="CsvToJsonRouteBuilder" />
  </camelContext>
</blueprint>

The main thing to notice is that we are using the routeBuilder tag to reference our CsvToJsonRouteBuilder class and injecting the bean IdentityToJson which is referring to the class IdentityToJson. This class is where we map to the Json structure.

RouteBuilder class

The CsvToJsonRouteBuilder class looks like this:

package org.souciance.integration.CsvToJson;

import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.dataformat.bindy.csv.BindyCsvDataFormat;

/**
 * A bean which we use in the route
 */
public class CsvToJsonRouteBuilder extends RouteBuilder {

	@Override
	public void configure() throws Exception {
		// TODO Auto-generated method stub
		BindyCsvDataFormat bindy = new BindyCsvDataFormat(org.souciance.integration.CsvToJson.Identity.class);
		from("file:C:/test/?fileName=input.csv")
		.unmarshal(bindy)
		.to("IdentityToJson")
		.to("file:C:/test/?fileName=output.json")
		.log("done!")
		.end();

	}
}

A couple of things to notice here.

Firstly, we are creating a new bindy data format which is CSV based and we are referring to our Identity class. Then we pick up the file using the file component and the file is called input.csv.

Secondly, in the dsl we do an unmarshal(bindy) which means, we want to map the incoming data to the structure defined in the Identity class.

Thirdly, we sent the Identity object to the bean ”IdentityToJson” as defined in the blueprint.xml to map to the Json structure.

Finally we save it as as file called output.json and log ”done!”.

That is all that is needed.

Data model class : Identity

Let us look at the Identity model class where bindy is used.

package org.souciance.integration.CsvToJson;

import org.apache.camel.dataformat.bindy.annotation.CsvRecord;
import org.apache.camel.dataformat.bindy.annotation.DataField;

@CsvRecord(separator = ",")
public class Identity {

	@DataField(pos=1)
	private int identity;
	@DataField(pos=2)
	private String firstname;
	@DataField(pos=3)
	private String lastname;
	@DataField(pos=4)
	private int phone;
	@DataField(pos=5)
	private String country;
	public int getIdentity() {
		return identity;
	}
	public void setIdentity(int identity) {
		this.identity = identity;
	}
	public String getFirstname() {
		return firstname;
	}
	public void setFirstname(String firstname) {
		this.firstname = firstname;
	}
	public String getLastname() {
		return lastname;
	}
	public void setLastname(String lastname) {
		this.lastname = lastname;
	}
	public int getPhone() {
		return phone;
	}
	public void setPhone(int phone) {
		this.phone = phone;
	}
	public String getCountry() {
		return country;
	}
	public void setCountry(String country) {
		this.country = country;
	}

}

This is a very simple way of modelling data in Camel using the bindy component.

Step 1), you use the annotation @CsvRecord(separator = ”,”) to say that this is a CSV model and the separator to delimit each field is a comma. This needs to be done above the class declaration.

Step 2) you write down the fields in the CSV row. Above each field you annotate it with @DataField(pos=X) where pos is the order in which the field appears in the actual row.

Step 3) You add the getter and setters for each field.

That is all that is required for bindy for our simple row of data. For more on bindy and all the annotations you can use see here.

Mapping to Json

Finally, we have the IdentityToJson class where we map to Identity object to json format. Here is how that class looks like:

package org.souciance.integration.CsvToJson;

import java.io.ByteArrayOutputStream;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class IdentityToJson implements Processor {

@Override
public void process(Exchange exchange) throws Exception {
// TODO Auto-generated method stub

//initialize Jackson
JsonNodeFactory factory = new JsonNodeFactory(false);
JsonFactory jsonFactory = new JsonFactory();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
JsonGenerator generator = jsonFactory.createGenerator(outputStream);
ObjectMapper mapper = new ObjectMapper();

//get the Pojo with the CSV data
Identity identity = (Identity)exchange.getIn().getBody();

//map to Json
ObjectNode id = factory.objectNode();
id.put("identity", identity.getIdentity());
id.put("firstname", identity.getFirstname());
id.put("lastname", identity.getLastname());
id.put("phone", identity.getPhone());
id.put("country", identity.getCountry());

//write the json string to the exchange
mapper.writeTree(generator, id);
String json = new String(outputStream.toString());
exchange.getIn().setBody(json);
}
}

The main aspects are related to Jackson code. The first part is Jackson initialization where we create a JsonNodeFactory, JsonFactory and ByteOutputStream because we want to write the data to the stream which will be transformed to a string at the end.

In the second part we convert the body in the exchange to an instance of Identity. This is the crucial part because now we have access to the row data and can do our mapping.

The next part creates a node and starts putting in values in the json structure.

Finally we write the json string to the exchange.

Input and Output

When you run this with input.csv containing the row:
12345,souciance,eqdam rashti,012458478,Sweden

your output.json should be
{
”identity”:12345,
”firstname”:”souciance”,
”lastname”:”eqdam rashti”,
”phone”:12458478,
”country”:”Sweden”
}

Feel free to leave comments. In part 3 we will go through mapping multiple rows of data. Stay tuned!

Camel development series – Part 1

Hello again!

It has been a while since I wrote so I thought I’d start this year with some basic Camel development series. The main reason is that I feel one aspect lacking in the documentation is a good step by step introduction for some basic mapping. How do you go from Csv (comma separated format) to Json format? After all mapping is a big part of integration. They can be very simple from field to field mapping to extremely complicated business logic. Although personally adding any business logic to your integration code is very risky since now you have another place to keep track of your business domain.

Anyway, I am not sure how many parts this will be but we will start with basics and then add additional mapping complexity as well as error handling, and logging until we get a somewhat reasonable and stable integration. For this I am  hoping to add to the series once a week. Feel free to add suggestions or thoughts in the comments section!

Hello world

So let’s get started with the basics. All the code for this example can be found here https://github.com/SoucianceEqdamRashti/Integration/tree/master/HelloWorld

We want to build an extremely simple HTTP listener on a specific URL and port and returns a simple string when you call it. Nothing more complicated than that.

For my development environment I am using Eclipse Mars and importing the camel archetypes from Maven. I am also using Camel version 2.16.1. You can off course use another IDE if you prefer. That should not matter.

The first thing is my pom file and specifically my dependencies. Here is what I have imported:

  <dependencies>
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-core</artifactId>
      <version>2.16.1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-blueprint</artifactId>
      <version>2.16.1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-restlet</artifactId>
      <version>2.16.1</version>
      <type>bundle</type>
    </dependency>

As you can see most of it is standard camel core. The extra dependency is camel-restlet and we will use that for the HTTP communication.

So how do we start our camelcontext and create a route? Well there is the Java DSL and the blueprint dsl. One thing to bear in mind is where you want to deploy this. That determines a lot how you choose your dsl. Since I mainly work with Karaf I will select blueprint to create my beans and start the routebuilder but I will use the java dsl for writing the routes and all other code. This way, I get the best from both worlds.

This means when you start from scratch you should base your maven project on the archetype camel-blueprint.

So here is how my blueprint.xml looks like:

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
       http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">

  <bean id="HelloWorld" class="org.souciance.integration.helloworld.HelloWorld">      
 </bean>
  <camelContext xmlns="http://camel.apache.org/schema/blueprint">
    <routeBuilder ref="HelloWorld" />    
    </camelContext>
</blueprint>

The main part is the bean injection where we are injecting the class HelloWorld as bean into the Camel registry. The actual class name is HelloWorld and the prefix is the package name.

Since we want to write the main code in the java dsl we need to refer to it.
Hence we use the routeBuilder tag and refer to the bean ”HelloWorld” declared earlier.

That is all the blueprint we need.

In my HelloWorld class I have written the following code:

package org.souciance.integration.helloworld;

import org.apache.camel.builder.RouteBuilder;

/**
 * A bean which we use in the route
 */
public class HelloWorld extends RouteBuilder {

	@Override
	public void configure() throws Exception {
		// TODO Auto-generated method stub
		
		from("restlet:http://localhost:5000/hello")
		.log("request received")
		.setBody().simple("Hello to you too")
		.end();
		
	}
}

The main thing to pick up is that we are extending RouteBuilder and overriding the method configure() where all the action takes place. Inside the method we write our dsl code.

The actual dsl code is very simple. We listen on the url http://localhost:5000/hello, then we write a simple log text, and finally we set the response body to ”Hello to you too”. As you can see we are building an expression using the simple language to putting data in the exchange body. The simple expression language is very powerful and we will come back to it in more detail in other parts of the series.

That is all the code necessary.

How do you run it?

Well before deploying it you want to test that it works. The way I do in eclipse is the following:
1. Right click on the project.
2. Select Run As –> Maven Build
3. In the new build configuration as my goal I write camel:run.
4. If you no tests you can select skip tests.

Then simply run it with that build configuration.

If you run it and do a http get on that url you should see this in the console:

INFO: Starting the internal [HTTP/1.1] server on port 5000
[         Blueprint Extender: 1] BlueprintCamelContext          INFO  Route: route1 started and consuming from: Endpoint[http://localhost:5000/hello?restletMethods=GET]
[         Blueprint Extender: 1] BlueprintCamelContext          INFO  Total 1 routes, of which 1 is started.
[         Blueprint Extender: 1] BlueprintCamelContext          INFO  Apache Camel 2.16.1 (CamelContext: camel-1) started in 0.413 seconds
[              Restlet-26556173] route1                         INFO  request received
jan 31, 2016 1:32:04 EM org.restlet.engine.log.LogFilter afterHandle
INFO: 2016-01-31	13:32:04	0:0:0:0:0:0:0:1	-	-	5000	GET	/hello	-	200	16	0	16	http://localhost:5000	Mozilla/5.0 (Windows NT 10.0; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0	-

The log shows that we have started the camelcontext, listening on that url, then we receive a HTTP request, we write the log text ”request received” and return the response.

This is a simple example how to get started with Camel.

In the next series we will look more into mapping. Feel free to comment if you have any thoughts or questions.

Camel basic examples

Hope you guys have had a good holiday and good start to the new year. I have finally managed to get my github account organized and promised myself to put more code there to have publicly available and to access it whenever needed. This off course means that you guys can get hold of the code too 😉 So first of the link to the main account is:

https://github.com/SoucianceEqdamRashti/Integration

You will find two projects there. Let’s go through them one by one.

Heartbeat

This is a basic hello world style integration. It consists of two parts.

  1. blueprint.xml which initiates the actual integration and references the java class Heartbeat.java in bean style.
  2. Heartbeat.java which extends the RouteBuilder class and simply exposes a REST url which the client performs a GET operation and gets a simple string as a response.
  3. Import the project in your IDE and run it.
  4. Write http://0.0.0.0/heartbeat in your browser and you should get the string ”working” as response.

Blueprint code

The blueprint code can be found under OSGI/blueprint.xml.

The core idea is the following code:

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
       http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">

	<bean id="heartbeat" class="com.souciance.integration.camel.Heartbeat">
	</bean>


	<camelContext xmlns="http://camel.apache.org/schema/blueprint">
		<routeBuilder ref="heartbeat"/>
	</camelContext>

</blueprint>

This is the approach I recommend as a best practise if you are developing integrations with Camel and Blueprint. Basically let blueprint initialize and then write all your main code using the java dsl inside the RouteBuilder class.

Java code

The java code is very simple and looks as follows:

public class Heartbeat extends RouteBuilder {

    @Override
    public void configure() throws Exception {
    	from("restlet:http://0.0.0.0/heartbeat?restletMethod=GET")
    	.setBody().constant("Working")
    	.log("response sent");
    	
    }
}

We are essentially defining our ”from” to be a REST URL and the response is a constant string defined as ”Working”. Finally we write a simple log output. To access this write http://localhost/heartbeat in the browser.

This concludes the basic heartbeat integration. You could use this in more advanced setting to deploy this to your Karaf environment or elsewhere and simply let your monitoring software call this integration to act as a heartbeat monitor.

Synchronous ErrorHandler

This is a more advanced example. Here we define several concepts that are core to Camel and essential to any production ready integration. You will find the code here:
https://github.com/SoucianceEqdamRashti/Integration/tree/master/synchronousErrorHandler

Again it consists of two parts, one blueprint and one java code.

Blueprint code

The blueprint code can be found under OSGI/blueprint.xml.

The core idea is the following code:

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
       http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">

    <bean id="synchronousErrorHandler" class="com.souciance.integration.camel.synchronousErrorHandler.ErrorHandler">
    </bean>
   

  <camelContext xmlns="http://camel.apache.org/schema/blueprint">
    <routeBuilder ref="synchronousErrorHandler"/>
  </camelContext>

</blueprint>

This is identical to the previous example. The only difference is the name of the java class we are referring to.

Let’s move on the java code.

Java code

The entire java code can be seen here:

package com.souciance.integration.camel.synchronousErrorHandler;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.dataformat.JsonLibrary;
import org.apache.camel.model.rest.RestBindingMode;

import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;


/**
 * @author Souciance Eqdam Rashti
 * Showing ways of handling synchronous processing
 *
 */
public class ErrorHandler extends RouteBuilder {

    @Override
    public void configure() throws Exception {
    	
    	onException(com.fasterxml.jackson.core.JsonParseException.class).handled(true)
    	.setHeader("CamelHttpResponseCode",simple("500"))
    	.setHeader("Content-Type" ,simple("text/plain"))
    	.setBody().constant("Internal Server Error");
    	
    	onException(InvalidMediaTypeException.class).handled(true)
    	.setHeader("CamelHttpResponseCode", simple("415"))
    	.setHeader("Content-Type", simple("text/plain"))
    	.setBody().constant("Invalid Media Type");
    	    	
    	restConfiguration().component("restlet").host("localhost").port(7070).bindingMode(RestBindingMode.auto).componentProperty("chunked", "true");
    	
    	rest().path("integration").get("/object/1234").produces("application/json").bindingMode(RestBindingMode.json).to("direct:getObject");
    	
    	rest().path("integration").get("/object/12345").produces("application/json").bindingMode(RestBindingMode.auto).to("direct:getObjectError");
    	
    	rest().path("integration").post("/object").bindingMode(RestBindingMode.off).to("direct:postObject");
    	
    	from("direct:getObject")
    	.setBody().constant(createJsonString()).unmarshal().json(JsonLibrary.Jackson)    	
    	.log("get request received");
    	
    	from("direct:getObjectError")
    	.setBody().constant("sdfsdf:sdf").unmarshal().json(JsonLibrary.Jackson)
    	.log("get request error received");
    	
    	from("direct:postObject")
    	.process(new Processor() {

			@Override
			public void process(Exchange exchange) throws Exception {
				// TODO Auto-generated method stub
				String contentType=(String) exchange.getIn().getHeader("Content-Type");
				if (contentType.equals("application/json")) {
					exchange.getIn().removeHeaders("*");			
					exchange.getOut().setBody("OK");
					exchange.getOut().setHeader("Content-Type", "text/plain");
					exchange.getOut().setHeader("CamelHttpResponseCode", "201");
				}
				else {
					throw new InvalidMediaTypeException("Invalid media type received!");
				}	
			}						
    		
    	})
    	.log("Message received: ${body}");

    
    }
    
    private String createJsonString() throws JsonProcessingException {
    	ObjectMapper mapper = new ObjectMapper();
    	mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
    	dummyClassForJson jsonObj = new dummyClassForJson();
    	
    	String json = mapper.writeValueAsString(jsonObj);
    	return json;
    }
}

final class InvalidMediaTypeException extends Exception {


	public InvalidMediaTypeException() {
		
	}
	
	public InvalidMediaTypeException(String message) {
		super(message);
	}
	
}

class dummyClassForJson {
	  private String firstname="souciance";
	  private String lastname="eqdam rashti";
	  private String country="sweden";
	  private int age=34;
	  dummyClassForJson() {
	    // no-args constructor
	  }
	}

This is significantly more code than previously. Let’s go through some of the key parts.

onException(com.fasterxml.jackson.core.JsonParseException.class).handled(true)
    	.setHeader("CamelHttpResponseCode",simple("500"))
    	.setHeader("Content-Type" ,simple("text/plain"))
    	.setBody().constant("Internal Server Error");
    	
    	onException(InvalidMediaTypeException.class).handled(true)
    	.setHeader("CamelHttpResponseCode", simple("415"))
    	.setHeader("Content-Type", simple("text/plain"))
    	.setBody().constant("Invalid Media Type");
    	

The above defines the key concept of exception handling. I am defining an onException handler based on certain exceptions, set the handler to be true and then set specific headers and response body based on the error I am catching. The example also shows how to set header values, how to customize the return header and value of REST calls. The first onException returns a HTTP 500 because the Json string returned in the internal processing is not correctly formatted. However we don’t want the caller to receive the stack trace and internal error trace. That is why we set handled=true and set the HTTP response headers and body.

The second main part is the REST dsl which is new to Camel where using a dsl style code you can write REST components.

restConfiguration().component("restlet").host("localhost").port(7070).bindingMode(RestBindingMode.auto).componentProperty("chunked", "true");
    	
    	rest().path("integration").get("/object/1234").produces("application/json").bindingMode(RestBindingMode.json).to("direct:getObject");
    	
 

You see that we define a restConfiguration and define RESTLET as our main HTTP component, the host, port, and the binding mode to be auto. You can select JETTY or other instead of RESTLET and there are many other options to add to the restConfiguration. Finally using rest() you define the path, operation and the producer and consumer options and where the request should get directed to. In the example above the request goes to ”direct:getObject”.

One important part to note. RestBindingMode.json essentially means that Camel expects the request and response to be JSON. It means that if you happen to write plain text as error string Camel will still format it as Json. For example if you return OK the caller will actually see ”OK” if you set RestBindingMode.json. If you know you will return plain text set RestBindingMode.off. Then the response will be OK as well.

The final part we will go through is the POST operation.

from("direct:postObject")
    	.process(new Processor() {

			@Override
			public void process(Exchange exchange) throws Exception {
				// TODO Auto-generated method stub
				String contentType=(String) exchange.getIn().getHeader("Content-Type");
				if (contentType.equals("application/json")) {
					exchange.getIn().removeHeaders("*");			
					exchange.getOut().setBody("OK");
					exchange.getOut().setHeader("Content-Type", "text/plain");
					exchange.getOut().setHeader("CamelHttpResponseCode", "201");
				}
				else {
					throw new InvalidMediaTypeException("Invalid media type received!");
				}	
			}						
    		
    	})
    	.log("Message received: ${body}");    	

Here you see an example where we create a custom processor to investigate the incoming header on the exchange and evaluate the content-type. If the content-type is not application/json we through a custom exception. This exception is caught by the onException handler and returns a standard response back to the caller. Finally you see how we can log the body by using the ${body} syntax.

If you have any comments or questions please let me know!

Stay tuned for more examples.