Camel development series part 6

It has been months since I wrote a blog on Camel. This has been mainly due to a lot of activity in my personal and professional life but I thought would get back on track with the blog.

In today post we will look at some basic beginner ”best practice”. Note, if someone reading disagree then please do comment as it is always interesting to receive constructive feedback.

Always name your CamelContext.

  • It does not matter if you are writing in pure java or a mix of blueprint and java. Always name your CamelContext.
  • It makes writing and reading logs easier as there is now a user friendly name to look out for.
  • It gives your reader an understanding of what this Context is supposed to do.
  • It helps to get into the spirit of naming your Camel objects such as routes.
  • Code example:

    CamelContext context = new DefaultCamelContext();
    context.setName("MyCamelContext");
    

    #Blueprint version

    <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
     
        <camelContext xmlns="http://camel.apache.org/schema/blueprint" name="MyCamelContext">
            <route>
                <from uri="timer:test" />
                <to uri="log:test" />
            </route>
        </camelContext>
     
    </blueprint>
    &nbsp;
    

    Always give a routeId to your routes.

  • Give a meaningful id to your route. Amongst others, it helps to show which routes have started when the camel context starts. Otherwise you will simply see route1, route2, route3 without know which of these match your routes.
  • It makes it much easier to write log statements if you have a routeId. Each log statement that belongs to a route can use the routeId to put the statements in their category. This makes it much easier to know which route publish which log statement.
  • It gives you an enormous benefit during testing because you can intercept, replace and do other things based on your routeId. I do a lot of replacement during tests for example I replace my eventbus route with a hardcoded response. You need a routeId for this.
  • Always give an id to statements that are configurable.

  • For example, I may send my exchange to a bean method or to a processor or to another route via direct. Give meaningful id names to each .to() statement. It makes testing so much easier as you can intercept and replace .to() endpoints and that is a big bonus during test.
  • Code example:

    from("inputUri).routeId("MyRouteId").to("outputUri).id("MyId");
    

    Keep inline processors short

  • In camel you can create inline processors to write normal java code and manipulate whatever is on the exchange or the headers. This is good for quick and dirty operations but if you are doing complex business logic or operations that should be configurable or hidden then create a Processor class instead and invoke that.
  • Keeping shorter inline processor code makes reading the camel dsl code easier as well.
  • By putting complex resuable code in a Processor class you ensure that other routes can call it as well. This means you write code once and call it from other parts of the CamelContext. You don’t then need to write same inline code everywhere.
  • Code example:

    from("activemq:myQueue").process(new Processor() {
        public void process(Exchange exchange) throws Exception {
            String payload = exchange.getIn().getBody(String.class);
            // do operations on the payload that should only happen at this //point in the route and only in this route.
    // Add simple logic for e.g. header or body manipulation.
    //Complex logic goes in separate Processor class.
           exchange.getIn().setBody("Changed body");
       }
    }).to("activemq:myOtherQueue");
    
    #Processor class
    public class MyProcessor implements Processor {
      public void process(Exchange exchange) throws Exception {
        String body = exchange.getIn().getBody().toString();
        body="changed";
    exchange.getIn().setBody(body);
    //Create a class when you need to class the Processor class from several parts of your route or routes or has configurable parts or contains complex
    business logic.
    
      }
    }
    

    Generate your Camel endpoint uris

  • Your endpoint uri whether in a from() or to() should be generated or injected rather than hardcoded.
  • Avoid from(”file://test/?fileName=test.txt”). Instead do from(fileUri) where fileUri is created in a utility class, some bean method or injected via a property. The same goes for .to() endpoints
  • Generating uri makes testing much easier because you can generate other test endpoints and simply inject those instead and never touch your main uris. If you have hardcoded uri the same is possible but cumbersome.
  • In particular if you have different uri for different environment then you cannot hardcode them otherwise you are creating different code for different environments and that is a bad practise. Here you defintely need to inject them via property file determined by some environment variable that tells you which environment the context is running in.
  • Code example:

    @PropertyInject("{{fileUri}}")
    private String fileUri;
    @PropertyInject("{{toUri}}")
    private String toUri;
    ...
    ...
    from(fileUri).to(toUri);
    

    Don’t complicate your logs

  • Your log files should contain at least two levels. One at debug level intended for technical minded people who wanted in depth knowledge of what is going on. Then you have logs at info level which should only give a brief description of what has happened. The main steps in a route should be at info level. The details of the exchange should be at debug level.
  • Do not put exchange headers, body or properties at info level at least it is of buisiness importance. Info level logs should be of the form:
    Order 123 generated. Customer request received. Message published. No technical details present.
  • At debug level you should log all aspects of the exchange to provide maximum details for debugging and troubleshooting. That means log ${headers}, ${body} and ${properties} where applicable. Because debug levels are not on by default the log files will not get massive straight away.
  • Link the log statements with the routeId so you know which route generated that statement. This makes it easier to backtrack from the logs to your code.
  • Code example:

    #Don't do this. It contains unnecessary amount of information.
    from(fileUri).routeId("MyRouteId").log(LoggingLevel.INFO,"MyRouteId", "Order ${body} received").to(toUri);
    #Log like this.
    from(fileUri).routeId("MyRouteId").log(LoggingLevel.INFO,"MyRouteId", "Order from customer received").log(LoggingLevel.DEBUG, "MyRouteId", "Order from customer with body: ${body} and headers ${headers}").to(toUri);
    

    Split your logic into several routes

  • Yes, you can create a giantic 1000 line log Camel route just as you can with normal java class but it is not considered good practice.
  • Decide how your problem can be proken into individual parts, set each parts action to be done by a specific route and chain the routes together.
  • Chaining routes means you can replace routes during tests or due to requirements changes which is harder to do when all the code is in one giant route
  • Chaining routes makes it easier to understand the different components and you can programmatically or via external commands stop individual routes for troubleshooting. You cannot do this if all the code exist in one giant route.
  • Use common sense off course. You can always start with a big route in order to check your code and then split the code in smaller routes. It is more important to get the coding working first and then get it more coherent then start with the optimization.
  • Code example:

    #Don't do this. It is a massive route.
    from(fileUri).routeId("MyRouteId").log(LoggingLevel.INFO,"MyRouteId", "Order ${body} received").to(eventBusUri).convertBodyTo(String.class).removeHeaders("eventbusHeaders").to()....;
    #Do more like this as each step is broken down and the implementation is written in individual routes
    from(fileUri).routeId("MyRouteId").to(eventBusRoute).to(processOrderRoute).to(generateReceiptRoute).to(eventbusRoute).to(saveReceiptRoute);
    
    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