Spring Integration Samples
Starting with the current release of Spring Integration the samples are distributed as independent
Maven-based projects (http://maven.apache.org/) to minimize the setup time.
Since each project is also an Eclipse-based project, they can be imported as is using "Import existing project wizard".
If you prefer another IDE, configuration should be very trivial, since a special Maven profile was setup to download all
of the required dependencies for all samples. Detailed instructions on how to build and run the samples are provided in
the README.txt file located in the samples directory of the main distribution.
The Cafe Sample
In this section, we will review a sample application that is included in the Spring Integration
distribution. This sample is inspired by one of the samples featured in Gregor Hohpe's
Ramblings.
The domain is that of a Cafe, and the basic flow is depicted in the following diagram:
The Order object may contain multiple OrderItems. Once the order
is placed, a Splitter will break the composite order message into a single message per
drink. Each of these is then processed by a Router that determines whether the drink is hot
or cold (checking the OrderItem object's 'isIced' property). The
Barista prepares each drink, but hot and cold drink preparation are handled by two
distinct methods: 'prepareHotDrink' and 'prepareColdDrink'. The prepared drinks are then sent to the Waiter where
they are aggregated into a Delivery object.
Here is the XML configuration:
]]>
As you can see, each Message Endpoint is connected to input and/or output channels. Each endpoint will manage
its own Lifecycle (by default endpoints start automatically upon initialization - to prevent that add the
"auto-startup" attribute with a value of "false"). Most importantly, notice that the objects are simple POJOs
with strongly typed method arguments. For example, here is the Splitter:
split(Order order) {
return order.getItems();
}
}]]>
In the case of the Router, the return value does not have to be a MessageChannel
instance (although it can be). As you see in this example, a String-value representing the channel name is
returned instead.
Now turning back to the XML, you see that there are two <service-activator> elements. Each of these
is delegating to the same Barista instance but different methods: 'prepareHotDrink'
or 'prepareColdDrink' corresponding to the two channels where order items have been routed.
As you can see from the code excerpt above, the barista methods have different delays (the hot drinks take 5
times as long to prepare). This simulates work being completed at different rates. When the
CafeDemo 'main' method runs, it will loop 100 times sending a single hot drink and a
single cold drink each time. It actually sends the messages by invoking the 'placeOrder' method on the Cafe
interface. Above, you will see that the <gateway> element is specified in the configuration file. This
triggers the creation of a proxy that implements the given 'service-interface' and connects it to a channel.
The channel name is provided on the @Gateway annotation of the Cafe interface.
public interface Cafe {
@Gateway(requestChannel="orders")
void placeOrder(Order order);
}
Finally, have a look at the main() method of the CafeDemo itself.
0) {
context = new FileSystemXmlApplicationContext(args);
}
else {
context = new ClassPathXmlApplicationContext("cafeDemo.xml", CafeDemo.class);
}
Cafe cafe = (Cafe) context.getBean("cafe");
for (int i = 1; i <= 100; i++) {
Order order = new Order(i);
order.addItem(DrinkType.LATTE, 2, false);
order.addItem(DrinkType.MOCHA, 3, true);
cafe.placeOrder(order);
}
}]]>
To run this sample as well as 8 others, refer to the README.txt within the "samples" directory
of the main distribution as described at the beginning of this chapter.
When you run cafeDemo, you will see that the cold drinks are initially prepared more quickly than the hot drinks.
Because there is an aggregator, the cold drinks are effectively limited by the rate of the hot drink preparation.
This is to be expected based on their respective delays of 1000 and 5000 milliseconds. However, by configuring a
poller with a concurrent task executor, you can dramatically change the results. For example, you could use a
thread pool executor with 5 workers for the hot drink barista while keeping the cold drink barista as it is:
]]>
]]>
]]>]]>
Also, notice that the worker thread name is displayed with each invocation. You will see that the hot drinks are
prepared by the task-executor threads. If you provide a much shorter poller interval (such as 100 milliseconds),
then you will notice that occasionally it throttles the input by forcing the task-scheduler (the caller) to invoke
the operation.
In addition to experimenting with the poller's concurrency settings, you can also add the 'transactional'
sub-element and then refer to any PlatformTransactionManager instance within the context.
The xml messaging sample
The xml messaging sample in the org.springframework.integration.samples.xml illustrates how to use
some of the provided components which deal with xml payloads. The sample uses the idea of processing an order for books
represented as xml.
First the order is split into a number of messages, each one representing a single order item using
the XPath splitter component.
]]>
A service activator is then used to pass the message into a stock checker POJO. The order item document is enriched with information
from the stock checker about order item stock level. This enriched order item message is then used to route the message. In the
case where the order item is in stock the message is routed to the warehouse. The XPath router makes use of a
MapBasedChannelResolver which maps the XPath evaluation result to a channel reference.
]]>
Where the order item is not in stock the message is transformed using
xslt into a format suitable for sending to the supplier.
]]>