Welcome to bytebang » The blog about all and nothing » Content based message routing with apache camel

Content based message routing with apache camel

Jun 18 2017

The Problem

Lets say you run a web shop where the orders are processed automatically by apache camel. The items which are selled are assembled from make and buy items.
Make items have to be produced in your shop-floor and buy items have to be bought at one of your suppliers.

Ideally you would like to have a mechanism which splits up your order into two lists:

  • One list of items which have to be produced and
  • another list which with items which should be purchased.

Every list should then be forwarded to a different route within apache camel (where either manufacturing instructions or purchases are created and forwarded to the supplier).

The Solution

The solution to this problem is called 'content based message routing'. There is a fully functional example of such an example in our github repository. The next few paragraphs will give you an idea how this code works.

Datamodel

The data model is simple.

https://yuml.me/ac1259b6

We have an abstract base class called Item which is the parent of the BuyItem and the PurchaseItem class. An Order represents an order which comes from the webshop and it consists of a list of MakeItems and BuyItems along with some utility methods.

http://yuml.me/88048a17

Routes

Apache camel provides a woute called 'direct:webshop' which is able to process such orders. Its purpose is to read out the Items, to pack them into separate exchanges and to set the ItemType header information which is processed in the next step. Finally it forwards the newly created exchange to another route called 'direct:itemprocessor'. This is where the actual magic happens:

from("direct:itemprocessor").routeId("itemprocessor").description("Processes the Items")
.choice().when(header("ItemType").isEqualToIgnoreCase("MakeItem"))  // Check if the Item is a makeitem
.to("direct:factory")      // Then build it
.when(header("ItemType").isEqualToIgnoreCase("BuyItem"))  // Check if the Item is a BuyItem
.to("direct:purchase")      // Then purchase it
.otherwise()        // otherwise
.log("~~~~~~~ DOING NOTHING WITH ${body} ~~~~~~~")  // do nothing
.end();

Depending on the header field ItemType is sends the Item either to the 'direct:factory' route or to the 'direct:purchase' route where just a log message is written. This would be the place where the type-specific business logic would be placed.

Testrun

The following order:

Order pump_assembly = new Order();
pump_assembly.addItem(new BuyItem("GearMotor", "An electromotor", 45))
pump_assembly.addItem(new MakeItem("PumpHousing-UpperPart", "Molded steel housing", 4))
pump_assembly.addItem(new MakeItem("Shaft", "40 mm steel shaft", 1))
pump_assembly.addItem(new BuyItem("RubberFeet", "Rubber feet to damp vibrations", 0.1))
pump_assembly.addItem(new BuyItem("RubberFeet", "Rubber feet to damp vibrations", 0.1))
pump_assembly.addItem(new BuyItem("Bearing", "Ring bearing", 0.75))
pump_assembly.addItem(new MakeItem("PumpHousing-LowerPart", "Molded steel housing", 2));

produces the following output:

Buyer purchases the following item :BuyItem(name=GearMotor, description=An electromotor, price=45.0)
The factory is producing :MakeItem(name=PumpHousing-UpperPart, description=Molded steel housing, hoursToBuild=4)
The factory is producing :MakeItem(name=Shaft, description=40 mm steel shaft, hoursToBuild=1)
Buyer purchases the following item :BuyItem(name=RubberFeet, description=Rubber feet to damp vibrations, price=0.1)
Buyer purchases the following item :BuyItem(name=RubberFeet, description=Rubber feet to damp vibrations, price=0.1)
Buyer purchases the following item :BuyItem(name=Bearing, description=Ring bearing, price=0.75)
The factory is producing :MakeItem(name=PumpHousing-LowerPart, description=Molded steel housing, hoursToBuild=2)

Bingo - Thats exactly what we where looking for.

Happy coding

Get Social


(c) 2024, by bytebang e.U. - Impressum - Datenschutz / Nutzungsbedingungen
-