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.

Annonser

Kommentera

Fyll i dina uppgifter nedan eller klicka på en ikon för att logga in:

WordPress.com Logo

Du kommenterar med ditt WordPress.com-konto. Logga ut / Ändra )

Twitter-bild

Du kommenterar med ditt Twitter-konto. Logga ut / Ändra )

Facebook-foto

Du kommenterar med ditt Facebook-konto. Logga ut / Ändra )

Google+ photo

Du kommenterar med ditt Google+-konto. Logga ut / Ändra )

Ansluter till %s