Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Apache Camel Developer's Cookbook

You're reading from   Apache Camel Developer's Cookbook For Apache Camel developers, this is the book you'll always want to have handy. It's stuffed full of great recipes that are designed for quick practical application. Expands your Apache Camel abilities immediately.

Arrow left icon
Product type Paperback
Published in Dec 2013
Publisher Packt
ISBN-13 9781782170303
Length 424 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Toc

Table of Contents (14) Chapters Close

Preface 1. Structuring Routes FREE CHAPTER 2. Message Routing 3. Routing to Your Code 4. Transformation 5. Splitting and Aggregating 6. Parallel Processing 7. Error Handling and Compensation 8. Transactions and Idempotency 9. Testing 10. Monitoring and Debugging 11. Security 12. Web Services Index

Reusing routing logic through template routes

One of the key advantages of using Java for defining routes is the ability to define the same, or similar, routing logic multiple times in your integrations, while changing key elements.

Consider the case of a route that:

  • Consumes bulk order data from CSV files in an input directory
  • Splits it into individual orders
  • Extracts the date of each order, formatted specific to the country, and converts it to a universal one
  • Places an order confirmation into another directory

Now consider that you may have orders from dozens of different countries, with different order and confirmation directories, and different date formats.

You could write similar routes dozens of times, but that is going to create a maintenance problem. Alternatively, using Camel's Java DSL, you can write the common routing logic once, and then use dependency injection to vary the values that are different when you instantiate the route.

This recipe will show you a strategy for creating Camel routes that can be created with different values at runtime, parameterizing your common routing logic.

Getting ready

Define your route within a RouteBuilder as usual, only this time make the start and end URIs, as well as any beans involved in the processing, properties of the RouteBuilder class:

public class OrderProcessingRouteBuilder extends RouteBuilder {
  String inputUri;
  String outputUri;
  private OrderFileNameProcessor orderFileNameProcessor;

  @Override
  public void configure() throws Exception {
    from(inputUri)
      // split into individual lines
      .split(body(String.class).tokenize("\n"))
        .process(orderFileNameProcessor)
        .log("Writing file: ${header.CamelFileName}")
        .to(outputUri)
      .end();
  }
}

Note

Note that the URI variables are defined as package scoped. This will help us to test the class later.

The Java code for this recipe is located in the org.camelcookbook.structuringroutes.templating package. The Spring XML files are located under src/main/resources/META-INF/spring and prefixed with templating.

How to do it...

Use property setters on your RouteBuilder implementation to instantiate multiple instances of the route with different property values injected.

  1. Add setters for the properties:
    public void setInputDirectory(String inputDirectory) {
      inputUri = "file://" + inputDirectory;
    }
    public void setOutputDirectory(String outputDirectory) {
      outputUri = "file://" + outputDirectory;
    }
    public void setOrderFileNameProcessor(
        OrderFileNameProcessor orderFileNameProcessor) {
      this.orderFileNameProcessor = orderFileNameProcessor;
    }

    Tip

    A useful trick is to construct the endpoint URIs within the setters. This way, when instantiating the RouteBuilder, you only have to worry about which directories to use, not about the various additional attributes that you want to repeat for the file component each time.

  2. Validate that the mandatory bean properties were set. The following code uses the org.apache.commons.lang.Validate class to check for nulls and empty Strings:
    @PostConstruct
    public void checkMandatoryProperties() {
      Validate.notEmpty(inputUri, "inputUri is empty");
      Validate.notEmpty(outputUri, "outputUri is empty");
      Validate.notNull(orderFileNameProcessor,
                       "orderFileNameProcessor is null");
    }

    Tip

    If you are using the RouteBuilder from Spring, add a @PostConstruct method to check that all of the properties have been set. This way, if all of the fields have not been initialized correctly, the application will refuse to start up.

  3. To complete the integration we need to add a Processor that parses dates from a line of CSV text, changes the date to a universal format, and sets a header with the output filename. We encapsulate this logic in a class whose instances vary by a date format that is injected. The source for this class is available in the example code under: org.camelcookbook.structuringroutes.templating.OrderFileNameProcessor.
  4. In your Spring XML file, you can now create multiple instances of this class:
    <bean id="dateFirstOrderFileNameProcessor"
          class="org.camelcookbook.structuringroutes.templating.OrderFileNameProcessor">
      <property name="countryDateFormat" value="dd-MM-yyyy"/>
    </bean>
    
    <bean id="monthFirstOrderFileNameProcessor"
          class="org.camelcookbook.structuringroutes.templating.OrderFileNameProcessor">
      <property name="countryDateFormat" value="MM-dd-yyyy"/>
    </bean>
  5. We now have all of the pieces in place to perform the same integration for a number of countries that use different input and output directories, and date formats, within their order files. We can now go ahead and instantiate the RouteBuilders and inject them into the Camel context:
    <bean id="ukOrdersRouteBuilder"
          class="org.camelcookbook.structuringroutes.templating.OrderProcessingRouteBuilder">
      <property name="inputDirectory"
                value="/orders/in/UK"/>
      <property name="outputDirectory"
                value="/orders/out/UK"/>
      <property name="orderFileNameProcessor"
                ref="dateFirstOrderFileNameProcessor"/>
    </bean>
    
    <bean id="usOrdersRouteBuilder"
          class="org.camelcookbook.structuringroutes.templating.OrderProcessingRouteBuilder">
    <property name="inputDirectory"
              value="/orders/in/US"/>
      <property name="outputDirectory"
                value="/orders/out/US"/>
      <property name="orderFileNameProcessor"
                ref="monthFirstOrderFileNameProcessor"/>
    </bean>
    
    <camelContext 
        xmlns="http://camel.apache.org/schema/spring">
      <routeBuilder ref="ukOrdersRouteBuilder"/>
      <routeBuilder ref="usOrdersRouteBuilder"/>
    </camelContext>

How it works...

By treating our RouteBuilder implementation as just another bean to use in a Spring context, we were able to instantiate it multiple times, introducing varying behavior by changing the injected values. In the future, if we were to change the routing logic, perhaps by adding more logging, it would all be done in one place in the code.

When we defined our URI properties in the RouteBuilder, we set them as package scoped. This is a handy strategy that allows us to inject endpoint types from within the same package that are not file: endpoints, which are set when our public setter methods are used. Since test classes are typically co-located in the same package, this allows us to initialize our RouteBuilder with more easily testable endpoints:

OrderFileNameProcessor processor = new OrderFileNameProcessor();
processor.setCountryDateFormat("dd-MM-yyyy");

OrderProcessingRouteBuilder routeBuilder =
    new OrderProcessingRouteBuilder();
routeBuilder.inputUri = "direct:in";
routeBuilder.outputUri = "mock:out";
routeBuilder.setOrderFileNameProcessor(processor);

See Chapter 9, Testing, for more details on testing.

See also

You have been reading a chapter from
Apache Camel Developer's Cookbook
Published in: Dec 2013
Publisher: Packt
ISBN-13: 9781782170303
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime