Handling exceptions in Apache Camel

Apache Camel is an open source framework for building applications using standard integration patterns. One of the main features of the Apache Camel is the DSL or the Domain Specific Language that it provides for defining mediation and routing rules. A developer can use this DSL through Java, Spring or Scala  APIs.

The Java APIs are implemented using a "fluent" design pattern. For a novice Camel programmer, it might be a little hard to understand how the fluent design pattern fit withing the mainline Java code. In this blog, we will look at the exception handling within the Camel routes that are built using Java fluent APIs.

Following is a simple example of building Camel routes using Java APIs.

final class HttpExample {
  private HttpExample() {
  }

  public static void main(String[] args) throws Exception {
    CamelContext context = new DefaultCamelContext();
    final String url = args[0]; // get the target URL from the command line

    context.addRoutes(new RouteBuilder() {
      public void configure() {
        from("direct:start")
          .setHeader(Exchange.HTTP_METHOD, constant("GET"))
          .to(url)
          .process(new HttpExampleResponse() {
              public void process(Exchange x) {
                String resp = (String)x.getIn().getBody(String.class);
                x.getOut().setBody(resp);
              }
          });
      }
    });

    ProducerTemplate template = context.createProducerTemplate();
    context.start();

    String resp = (String)template.requestBody("direct:start", "test");
    System.out.println(resp);

    Thread.sleep(1000);
    context.stop();
  }
}

The above code will work fine as long as the target URL is reachable. How do we handle the exception that will be thrown if the target URL is not reachable?

You can have a try-catch block around the template.requestBody() call but there are a couple of drawbacks. First, we will not be able to take any route specific actions such as sending the call to another endpoint since the exception is caught outside the camel route. Second, we will lose access to the Exchange object which has all the information about inbound and outbound message.

The better way to handle an exception is using an onException() clause in the route definition. You can have multiple onException() clauses for different types of exceptions. Each onException() clause can have its own processor to handle the Exception. Here is the modified example with an onException() clause.

final class HttpExample {
  private HttpExample() {
  }

  public static void main(String[] args) throws Exception {
    CamelContext context = new DefaultCamelContext();
    final String url = args[0]; // get the target URL from the command line

    context.addRoutes(new RouteBuilder() {
      public void configure() {
        onException(UnknownHostException.class)
          .handled(true)
          .process(new Processor() {
            public void process(Exchange x) throws Exception {
              UnknownHostException exception = x.getProperty(Exchange.EXCEPTION_CAUGHT, UnknownHostException.class);

              if (exception != null) {
                String body = exception.getMessage();
                x.getOut().setBody(body);
              }
            }
          });

        from("direct:start")
          .setHeader(Exchange.HTTP_METHOD, constant("GET"))
          .to(url)
          .process(new HttpExampleResponse() {
            public void process(Exchange x) {
              String resp = (String)x.getIn().getBody(String.class);
              x.getOut().setBody(resp);
            }
          });
      }
    });

    ProducerTemplate template = context.createProducerTemplate();
    context.start();

    String resp = (String)template.requestBody("direct:start", "test");
    System.out.println(resp);

    Thread.sleep(1000);
    context.stop();
  }
}

Comments