INTSAMPLES-60 - Add JMS-backed Cafe Application Sample

* Add idea folder to .gitignore
* Add an embedded ActiveMQ
* Add activemq data to ignore list
* Use JMS-backed channels
* Add the sample description to README.md
This commit is contained in:
Christian Posta
2012-01-28 13:19:04 -07:00
committed by Gunnar Hillert
parent 23ed55d02b
commit a46d0a3b57
50 changed files with 746 additions and 52 deletions

2
.gitignore vendored
View File

@@ -13,3 +13,5 @@ derby.log
.idea
activemq-data
.settings/

View File

@@ -150,7 +150,7 @@
<arguments>
<argument>-classpath</argument>
<classpath />
<argument>org.springframework.integration.samples.cafe.demo.CafeDemoApp</argument>
<argument>org.springframework.integration.samples.org.springframework.integration.samples.cafe.demo.CafeDemoApp</argument>
<argument>${lang}</argument>
</arguments>
<successCodes>
@@ -181,7 +181,7 @@
<arguments>
<argument>-classpath</argument>
<classpath />
<argument>org.springframework.integration.samples.cafe.demo.ControlBusMain</argument>
<argument>org.springframework.integration.samples.org.springframework.integration.samples.cafe.demo.ControlBusMain</argument>
</arguments>
<successCodes>

View File

@@ -1,7 +1,7 @@
Cafe Sample Application
=======================
The Cafe sample emulates a simple operation of the Coffee shop when modeled using Enterprise Integration Patterns (EIP). It 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 Cafe sample emulates a simple operation of the Coffee shop when modeled using Enterprise Integration Patterns (EIP). It is inspired by one of the samples featured in Gregor Hohpe's Ramblings (see Starbucks Does Not Use Two-Phase Commit: http://www.eaipatterns.com/ramblings/18_starbucks.html). The domain is that of a Cafe, and the basic flow is depicted in the following diagram:
Barista
@@ -23,43 +23,36 @@ The Order object may contain multiple OrderItems. Once the order is placed, a **
The prepared drinks are then sent to the Waiter where they are aggregated into a Delivery object.
## Instructions for running the CafeDemo sample
## Cafe Sample Implementations
There are currently three implementations of the cafe sample:
1. The example comes with two identical configurations. One is ANNOTATION-based another is XML-based
1. Using Spring Integration channels and components
2. Using AMQP/RabbitMQ to demonstrate a distributed architecture
3. Using JMS/ActiveMQ to demonstrate jms-backed queues and a distributed architecture
2. To run this sample simply execute the CafeDemoApp test classes in the **org.springframework.integration.samples.cafe.xml** or **org.springframework.integration.samples.cafe.annotation** package.
All three implementations follow the same flow described above. See each one's README.md file for more details about the respective implementations.
3. The example also provides an alternative configuration that uses AMQP channels to distribute the components in the **CafeDemo** sample. To run this alternative configuration of the sample, be sure to have a RabbitMQ broker started on localhost:5672 configured with the default guest|guest client credentials on the / vHost, then execute the following test classes in order:
1. **cafeDemoAppBaristaColdAmqp** - starts the Cold Drink Barista
2. **cafeDemoAppBaristaHotAmqp** - starts the Hot Drink Barista
3. **cafeDemoAppAmqp** - starts the Cafe Storefront (Places 100 orders on the orders queue)
4. **cafeDemoAppOperationsAmqp** - starts the Cafe Operations (OrderSplitter, DrinkRouter, PreparedDrinkAggregator)
**Note**: All AMQP exchanges, queues, and bindings needed for this sample are defined within the different xml config files that support the above test classes.
Upon running any of the alternatives, you should see the output similar to this:
INFO : org.springframework.integration.samples.cafe.annotation.Barista - task-scheduler-1 prepared cold drink #1 for order #1: iced 3 shot MOCHA
INFO : org.springframework.integration.samples.cafe.annotation.Barista - task-scheduler-1 prepared cold drink #2 for order #2: iced 3 shot MOCHA
INFO : org.springframework.integration.samples.cafe.annotation.Barista - task-scheduler-1 prepared cold drink #3 for order #3: iced 3 shot MOCHA
INFO : org.springframework.integration.samples.cafe.annotation.Barista - task-scheduler-1 prepared cold drink #4 for order #4: iced 3 shot MOCHA
INFO : org.springframework.integration.samples.cafe.annotation.Barista - task-scheduler-2 prepared hot drink #1 for order #1: hot 2 shot LATTE
INFO : Barista - task-scheduler-1 prepared cold drink #1 for order #1: iced 3 shot MOCHA
INFO : Barista - task-scheduler-1 prepared cold drink #2 for order #2: iced 3 shot MOCHA
INFO : Barista - task-scheduler-1 prepared cold drink #3 for order #3: iced 3 shot MOCHA
INFO : Barista - task-scheduler-1 prepared cold drink #4 for order #4: iced 3 shot MOCHA
INFO : Barista - task-scheduler-2 prepared hot drink #1 for order #1: hot 2 shot LATTE
-----------------------
Order #1
Iced MOCHA, 3 shots.
Hot LATTE, 2 shots.
-----------------------
INFO : org.springframework.integration.samples.cafe.annotation.Barista - task-scheduler-1 prepared cold drink #5 for order #5: iced 3 shot MOCHA
INFO : org.springframework.integration.samples.cafe.annotation.Barista - task-scheduler-1 prepared cold drink #6 for order #6: iced 3 shot MOCHA
INFO : org.springframework.integration.samples.cafe.annotation.Barista - task-scheduler-1 prepared cold drink #7 for order #7: iced 3 shot MOCHA
INFO : org.springframework.integration.samples.cafe.annotation.Barista - task-scheduler-1 prepared cold drink #8 for order #8: iced 3 shot MOCHA
INFO : org.springframework.integration.samples.cafe.annotation.Barista - task-scheduler-1 prepared cold drink #9 for order #9: iced 3 shot MOCHA
INFO : org.springframework.integration.samples.cafe.annotation.Barista - task-scheduler-2 prepared hot drink #2 for order #2: hot 2 shot LATTE
INFO : Barista - task-scheduler-1 prepared cold drink #5 for order #5: iced 3 shot MOCHA
INFO : Barista - task-scheduler-1 prepared cold drink #6 for order #6: iced 3 shot MOCHA
INFO : Barista - task-scheduler-1 prepared cold drink #7 for order #7: iced 3 shot MOCHA
INFO : Barista - task-scheduler-1 prepared cold drink #8 for order #8: iced 3 shot MOCHA
INFO : Barista - task-scheduler-1 prepared cold drink #9 for order #9: iced 3 shot MOCHA
INFO : Barista - task-scheduler-2 prepared hot drink #2 for order #2: hot 2 shot LATTE
-----------------------
Order #2
Iced MOCHA, 3 shots.
Hot LATTE, 2 shots.
-----------------------
Happy integration :-)

View File

@@ -0,0 +1,29 @@
Cafe Sample Application - AMQP Implementation
=======================
See the parent-level README.md for more details, but the flow of the implementation should follow this diagram:
Barista
hotDrinks ____________________
|==========| -->| |
orders drinks / | prepareHotDrink() |
Place Order ->Cafe->|======|->OrderSplitter->|======|->DrinkRouter | |
\ coldDrinks | prepareColdDrink() |
|==========| -->| |
|____________________|
Legend: |====| - channels
## Instructions for running the CafeDemo AMQP sample
### Distributed components
To run this alternative configuration of the sample, be sure to have a RabbitMQ broker started on localhost:5672 configured with the default guest|guest client credentials on the / vHost, then execute the following test classes in order:
1. **cafeDemoAppBaristaColdAmqp** - starts the Cold Drink Barista
2. **cafeDemoAppBaristaHotAmqp** - starts the Hot Drink Barista
3. **cafeDemoAppAmqp** - starts the Cafe Storefront (Places 100 orders on the orders queue)
4. **cafeDemoAppOperationsAmqp** - starts the Cafe Operations (OrderSplitter, DrinkRouter, PreparedDrinkAggregator)
**Note**: All AMQP exchanges, queues, and bindings needed for this sample are defined within the different xml config files that support the above test classes.

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>cafe</artifactId>
<groupId>org.springframework.integration.samples</groupId>
<version>2.1.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>cafe-amqp</artifactId>
<name>Cafe - With AMQP Message Broker</name>
<description>
This module implements the cafe sample using spring-integration components backed
by an AMQP message broker for message persistence and component distribution. This
sample uses RabbitMQ broker, but any AMQP message broker can be used. For an example
using JMS, see the JMS implemenation of the cafe sample.
</description>
<dependencies>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>cafe-si</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-amqp</artifactId>
<version>${spring.integration.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -57,7 +57,7 @@ public class CafeDemoAppAmqp {
public static void main(String[] args) {
AbstractApplicationContext context =
CafeDemoAppUtilities.loadProfileContext(
"/META-INF/spring/integration/amqp/cafeDemo-amqp-xml.xml",
"/META-INF/spring/integration/amqp/cafeDemo-amqp-xml.xml",
CafeDemoAppAmqp.class,CafeDemoAppUtilities.DEV);
order(context, 100);
context.close();

View File

@@ -41,7 +41,7 @@ public class CafeDemoAppBaristaColdAmqp {
public static void main(String[] args) {
AbstractApplicationContext context =
CafeDemoAppUtilities.loadProfileContext(
"/META-INF/spring/integration/amqp/cafeDemo-amqp-baristaCold-xml.xml",
"/META-INF/spring/integration/amqp/cafeDemo-amqp-baristaCold-xml.xml",
CafeDemoAppBaristaColdAmqp.class,CafeDemoAppUtilities.DEV);
System.out.println("Press Enter/Return in the console to exit the Barista Cold App");

View File

@@ -41,7 +41,7 @@ public class CafeDemoAppBaristaHotAmqp {
public static void main(String[] args) {
AbstractApplicationContext context =
CafeDemoAppUtilities.loadProfileContext(
"/META-INF/spring/integration/amqp/cafeDemo-amqp-baristaHot-xml.xml",
"/META-INF/spring/integration/amqp/cafeDemo-amqp-baristaHot-xml.xml",
CafeDemoAppBaristaHotAmqp.class,CafeDemoAppUtilities.DEV);
System.out.println("Press Enter/Return in the console to exit the Barista Hot App");

View File

@@ -47,7 +47,7 @@ public class CafeDemoAppOperationsAmqp {
public static void main(String[] args) {
AbstractApplicationContext context =
CafeDemoAppUtilities.loadProfileContext(
"/META-INF/spring/integration/amqp/cafeDemo-amqp-operations-xml.xml",
"/META-INF/spring/integration/amqp/cafeDemo-amqp-operations-xml.xml",
CafeDemoAppOperationsAmqp.class,CafeDemoAppUtilities.DEV);
System.out.println("Press Enter/Return in the console to exit the Cafe Operations App");

View File

@@ -5,7 +5,7 @@
xmlns:int-amqp="http://www.springframework.org/schema/integration/amqp"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:stream="http://www.springframework.org/schema/integration/stream"
xmlns:cloud="http://schema.cloudfoundry.org/spring"
xmlns:cloud="http://schema.cloudfoundry.org /spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://schema.cloudfoundry.org/spring

View File

@@ -0,0 +1,31 @@
Cafe Sample Application - JMS Implementation
=======================
See the parent-level README.md for more details, but the flow of the implementation should follow this diagram:
Barista
hotDrinks ____________________
|==========| -->| |
orders drinks / | prepareHotDrink() |
Place Order ->Cafe->|======|->OrderSplitter->|======|->DrinkRouter | |
\ coldDrinks | prepareColdDrink() |
|==========| -->| |
|____________________|
Legend: |====| - channels
## Instructions for running the CafeDemo JMS sample
### Distributed components
To run this configuration, start an instance of ActiveMQ with the openwire/TCP connector available on the default port (61616). There are no credentials of which to be aware. Please execute the following classes in order:
1. **CafeDemoAppBaristaColdActiveMQ - starts the ColdDrink Barista
2. **CafeDemoAppBaristaHotActiveMQ - starts the HotDrink Barista
3. **CafeDemoAppOperationsActiveMQ - starts the Cafe Operations (order splitter, drink router, etc).
4. **CafeDemoAppAcitveMQ - places the orders
### JMS backed components
See **CafeDemoActiveMQBackedChannels** for an example of how to use the JMS-backed channels. No need to start an external ActiveMQ because one is started internally

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.integration.samples</groupId>
<artifactId>cafe</artifactId>
<version>2.1.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>cafe-jms</artifactId>
<name>Cafe - With JMS Message Broker</name>
<description>
This module implements the cafe sample using spring-integration components backed
by a JMS broker for persistent messaging as well as component distribution. This
sample uses ActiveMQ as the JMS broker, but any JMS-compliant broker can be
used. For an example using AMQP, see the AMQP implementation of the cafe sample.
</description>
<properties>
<activemq.version>5.4.3</activemq.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>${activemq.version}</version>
</dependency>
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-jms</artifactId>
<version>${spring.integration.version}</version>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>cafe-si</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,47 @@
package org.springframework.integration.samples.cafe.xml;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.samples.cafe.Cafe;
import org.springframework.integration.samples.cafe.DrinkType;
import org.springframework.integration.samples.cafe.Order;
import java.io.IOException;
/**
* Main class for running the Cafe sample with JMS-backed (ActiveMQ) channels. Once the application
* is running, simply press <return> or any other key to end the application. To fully experience the
* benefits of this solution, try halting/exiting the program in the middle of running it, comment out the
* call to place new orders (the order() function call) and watch that the processing still continues
* where it left off when you halted it. This is because the messages are persisted in the ActiveMQ queues
*
* @author ceposta
*/
public class CafeDemoActiveMQBackedChannels {
/**
* place some orders
* @param context spring context
* @param count the number of standard orders
*/
public static void order(AbstractApplicationContext context, int count){
Cafe cafe = (Cafe) context.getBean("cafe");
for (int i = 1; i <= count; i++) {
Order order = new Order(i);
order.addItem(DrinkType.LATTE, 2, false);
order.addItem(DrinkType.MOCHA, 3, true);
cafe.placeOrder(order);
}
}
public static void main(String[] args) throws InterruptedException, IOException {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring/integration/activemq/cafeDemo-amq-config.xml",
"/META-INF/spring/integration/activemq/cafeDemo-amq-jms-backed.xml");
// comment this out to run the sample without placing any new orders on the queue
order(context, 25);
System.in.read();
context.close();
}
}

View File

@@ -0,0 +1,41 @@
package org.springframework.integration.samples.cafe.xml;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.samples.cafe.Cafe;
import org.springframework.integration.samples.cafe.DrinkType;
import org.springframework.integration.samples.cafe.Order;
import java.io.IOException;
/**
* Main class for sending orders that will be handled in separate, distributed
* processes. See the README.md file for more information on the order in which
* to start the processes
*
* @author ceposta
*/
public class CafeDemoAppActiveMQ {
/**
* place some orders
* @param context spring context
* @param count the number of standard orders
*/
public static void order(AbstractApplicationContext context, int count){
Cafe cafe = (Cafe) context.getBean("cafe");
for (int i = 1; i <= count; i++) {
Order order = new Order(i);
order.addItem(DrinkType.LATTE, 2, false);
order.addItem(DrinkType.MOCHA, 3, true);
cafe.placeOrder(order);
}
}
public static void main(String[] args) throws InterruptedException, IOException {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring/integration/activemq/cafeDemo-amq-config.xml",
"/META-INF/spring/integration/activemq/cafeDemo-amq-xml.xml");
order(context, 25);
context.close();
}
}

View File

@@ -0,0 +1,29 @@
package org.springframework.integration.samples.cafe.xml;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.samples.cafe.Cafe;
import org.springframework.integration.samples.cafe.DrinkType;
import org.springframework.integration.samples.cafe.Order;
import java.io.IOException;
/**
* Main class for starting up the distributed task of handling/creating the Cold
* beverages. See the README.md file for more information on the order in which
* to start the processes
*
* @author ceposta
*/
public class CafeDemoAppBaristaColdActiveMQ {
public static void main(String[] args) throws InterruptedException, IOException {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring/integration/activemq/cafeDemo-amq-config.xml",
"/META-INF/spring/integration/activemq/cafeDemo-amq-baristaCold-xml.xml");
System.out.println("Press Enter/Return to exit");
System.in.read();
context.close();
}
}

View File

@@ -0,0 +1,26 @@
package org.springframework.integration.samples.cafe.xml;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
/**
* Main class for starting up the distributed task of handling/creating the Hot¡
* beverages. See the README.md file for more information on the order in which
* to start the processes
*
* @author ceposta
*/
public class CafeDemoAppBaristaHotActiveMQ {
public static void main(String[] args) throws InterruptedException, IOException {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring/integration/activemq/cafeDemo-amq-config.xml",
"/META-INF/spring/integration/activemq/cafeDemo-amq-baristaHot-xml.xml");
System.out.println("Press Enter/Return to exit");
System.in.read();
context.close();
}
}

View File

@@ -0,0 +1,29 @@
package org.springframework.integration.samples.cafe.xml;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
/**
* Main class for starting up the distributed task of processing orders including
* splitting the orders, sending them to the hot/cold baristas, aggregating the orders
* and delivering them to the waiters.
*
* See the README.md file for more information on the order in which
* to start the processes
*
* @author ceposta
*/
public class CafeDemoAppOperationsActiveMQ {
public static void main(String[] args) throws InterruptedException, IOException {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring/integration/activemq/cafeDemo-amq-config.xml",
"/META-INF/spring/integration/activemq/cafeDemo-amq-operations.xml");
System.out.println("Press Enter/Return to exit");
System.in.read();
context.close();
}
}

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd">
<!-- The name of the queue to which to connect to receive cold drink orders-->
<bean id="coldDrinksQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="org.springframework.integration.samples.cafe.drinks.cold"/>
</bean>
<!-- The gateway responsible for request/reply semantics -->
<int-jms:inbound-gateway request-channel="coldJsonDrinks" request-destination="coldDrinksQueue"/>
<int:channel id="coldJsonDrinks" />
<!-- The chain of processing for this barista -->
<int:chain input-channel="coldJsonDrinks">
<int:json-to-object-transformer type="org.springframework.integration.samples.cafe.OrderItem"/>
<int:service-activator method="prepareColdDrink">
<bean class="org.springframework.integration.samples.cafe.xml.Barista" />
</int:service-activator>
<int:object-to-json-transformer />
</int:chain>
</beans>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd">
<!-- The name of the queue to which to connect to receive cold drink orders-->
<bean id="hotDrinksQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="org.springframework.integration.samples.cafe.drinks.hot"/>
</bean>
<!-- The gateway responsible for request/reply semantics -->
<int-jms:inbound-gateway request-channel="hotJsonDrinks" request-destination="hotDrinksQueue"/>
<int:channel id="hotJsonDrinks" />
<!-- The chain of processing for this barista -->
<int:chain input-channel="hotJsonDrinks">
<int:json-to-object-transformer type="org.springframework.integration.samples.cafe.OrderItem"/>
<int:service-activator method="prepareHotDrink">
<bean class="org.springframework.integration.samples.cafe.xml.Barista" />
</int:service-activator>
<int:object-to-json-transformer />
</int:chain>
</beans>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd ">
<!-- Set up the connection factory. You must name the bean "connectionFactory" for the JMS-backed
channels to automatically find it. Otherwise, you must specify the connection when you declare the
channel -->
<bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"/>
</bean>
</property>
<property name="sessionCacheSize" value="10"/>
<property name="cacheProducers" value="false"/>
</bean>
</beans>

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:stream="http://www.springframework.org/schema/integration/stream"
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
xmlns:amq="http://activemq.apache.org/schema/core"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/stream http://www.springframework.org/schema/integration/stream/spring-integration-stream-2.1.xsd
http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.4.2.xsd">
<!-- Set up a live broker with a transport connector at port 61616 (default amq port)
We imported the ActiveMQ schema so we can use the schema support. When this application shuts down,
so does the broker. You can omit the embedded declaration here and set up the broker stand alone and
use the standard ActiveMQ HighAvailability mechanism. You can also use the peer:// scheme to link up
multiple embedded brokers. See the docs for more: http://activemq.apache.org/peer-transport-reference.html
-->
<amq:broker brokerName="cafe-broker">
<amq:transportConnectors>
<amq:transportConnector name="openwire" uri="tcp://localhost:61616"/>
</amq:transportConnectors>
</amq:broker>
<!--Define an error channel that is backed by ActiveMQ. Note, by design, the error channel that's
created by default by SI if you don't declare one specifically will use the pub/sub messaging domain. Because
of that, i've chosen to keep the same messaging domain when defining the error channel explicitly. Note,
you can leave out this declaration, but then your error channel won't be backed by jms and can't participate
in the same advantages as those that are backed by JMS -->
<int-jms:publish-subscribe-channel id="errorChannel" topic-name="org.springframework.integration.samples.cafe.errors"/>
<int:gateway id="cafe" service-interface="org.springframework.integration.samples.cafe.Cafe"/>
<!-- each order has a collection of order items that is split apart to be processed -->
<int-jms:channel id="orders" queue-name="org.springframework.integration.samples.cafe.orders" />
<int:splitter input-channel="orders" expression="payload.items" output-channel="drinks"/>
<!-- The router sends different drink orders on different paths -->
<int-jms:channel id="drinks" queue-name="org.springframework.integration.samples.cafe.drinks"/>
<int:router input-channel="drinks" expression="payload.iced ? 'coldDrinks' : 'hotDrinks'"/>
<!-- individual order items are processed by the barista -->
<!-- Note, these channels were defined as "pollable" in the original SI config files. To achieve the
same thing with a JMS-backed channel, set the "message-driven" property to "false"-->
<int-jms:channel id="coldDrinks" queue-name="org.springframework.integration.samples.cafe.drinks.cold" message-driven="false"/>
<int:service-activator input-channel="coldDrinks" ref="barista" method="prepareColdDrink" output-channel="preparedDrinks"/>
<!-- individual order items are processed by the barista -->
<!-- Note, these channels were defined as "pollable" in the original SI config files. To achieve the
same thing with a JMS-backed channel, set the "message-driven" property to "false"-->
<int-jms:channel id="hotDrinks" queue-name="org.springframework.integration.samples.cafe.drinks.hot" message-driven="false"/>
<int:service-activator input-channel="hotDrinks" ref="barista" method="prepareHotDrink" output-channel="preparedDrinks"/>
<!-- drink order items are aggregated in a call to the waiter -->
<int-jms:channel id="preparedDrinks" queue-name="org.springframework.integration.samples.cafe.drinks.prepared"/>
<int-jms:channel id="deliveriesChannel" queue-name="org.springframework.integration.samples.cafe.deliveres"/>
<int:aggregator input-channel="preparedDrinks" method="prepareDelivery" output-channel="deliveriesChannel">
<bean class="org.springframework.integration.samples.cafe.xml.Waiter"/>
</int:aggregator>
<stream:stdout-channel-adapter id="deliveries" channel="deliveriesChannel"/>
<bean id="barista" class="org.springframework.integration.samples.cafe.xml.Barista"/>
<int:poller id="poller" default="true" fixed-delay="1000"/>
</beans>

View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:stream="http://www.springframework.org/schema/integration/stream"
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/stream http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd
http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd">
<!--Create a reference to the queue on the broker that has the new orders. Note, just because
we create a new bean with the cafe.orders destination name does not mean we actually create a new
queue on the broker. The queue will only be created if it doesn't exist. It will be pointed to
if it does exist. Don't need to "check whether a queue exists" before creating it -->
<bean id="cafeOrdersQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="org.springframework.integration.samples.cafe.orders"/>
</bean>
<int-jms:inbound-channel-adapter id="newOrdersInChannelAdapter" destination="cafeOrdersQueue" channel="jsonNewOrders"/>
<int:channel id="jsonNewOrders"/>
<int:chain input-channel="jsonNewOrders">
<int:json-to-object-transformer type="org.springframework.integration.samples.cafe.Order"/>
<int:splitter expression="payload.items" apply-sequence="true" />
<int:header-enricher>
<int:header name="ICED" expression="payload.isIced()" />
</int:header-enricher>
<int:object-to-json-transformer />
<int:router expression="headers.ICED ? 'coldDrinks' : 'hotDrinks'" />
</int:chain>
<!-- Default poller -->
<int:poller default="true" fixed-rate="100"/>
<int:channel id="coldDrinks">
<int:queue/>
</int:channel>
<bean id="coldDrinksQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="org.springframework.integration.samples.cafe.drinks.cold"/>
</bean>
<int-jms:outbound-gateway id="coldDrinksBarista"
request-channel="coldDrinks"
reply-channel="preparedJsonDrinks"
request-destination="coldDrinksQueue"
receive-timeout="30000"
reply-timeout="30000"/>
<int:channel id="hotDrinks">
<int:queue/>
</int:channel>
<bean id="hotDrinksQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="org.springframework.integration.samples.cafe.drinks.hot"/>
</bean>
<int-jms:outbound-gateway id="hotDrinksBarista"
request-channel="hotDrinks"
reply-channel="preparedJsonDrinks"
request-destination="hotDrinksQueue"
receive-timeout="30000"
reply-timeout="30000"/>
<int:channel id="preparedJsonDrinks"/>
<int:chain input-channel="preparedJsonDrinks" output-channel="deliverDrinks">
<int:json-to-object-transformer type="org.springframework.integration.samples.cafe.Drink"/>
<int:aggregator method="prepareDelivery">
<bean class="org.springframework.integration.samples.cafe.xml.Waiter"/>
</int:aggregator>
<int:header-enricher>
<int:header name="NUMBER" expression="payload.getOrderNumber()"/>
</int:header-enricher>
</int:chain>
<int:channel id="deliverDrinks"/>
<stream:stdout-channel-adapter id="deliveriesStdout" channel="deliverDrinks"/>
</beans>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:stream="http://www.springframework.org/schema/integration/stream"
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/stream http://www.springframework.org/schema/integration/stream/spring-integration-stream-2.1.xsd
http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd">
<int:gateway id="cafe" service-interface="org.springframework.integration.samples.cafe.Cafe"/>
<int:channel id="orders"/>
<int:chain input-channel="orders" output-channel="jsonNewOrders">
<int:header-enricher>
<int:header name="NUMBER" expression="payload.getNumber()" />
</int:header-enricher>
<int:object-to-json-transformer />
</int:chain>
<int:channel id="jsonNewOrders"/>
<!--After we've converted the orders to json, we send it on its way through a JMS outbound
channel adapter. This is a one-way fire and forget approach. The processing will continue
on the otherside of the JMS queue to which the channel adapter is connected -->
<bean id="cafeOrdersQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="org.springframework.integration.samples.cafe.orders"/>
</bean>
<int-jms:outbound-channel-adapter id="jmsOrdersOutChannelAdapter" channel="jsonNewOrders" destination="cafeOrdersQueue"/>
</beans>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- Appenders -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%t %-5p: %c - %m%n" />
</layout>
</appender>
<!-- Loggers -->
<logger name="org.springframework">
<level value="warn" />
</logger>
<logger name="org.springframework.amqp">
<level value="info" />
</logger>
<logger name="org.springframework.integration">
<level value="info" />
</logger>
<logger name="org.springframework.integration.samples">
<level value="debug" />
</logger>
<!-- Root Logger -->
<root>
<priority value="warn" />
<appender-ref ref="console" />
</root>
</log4j:configuration>

View File

@@ -0,0 +1,27 @@
Cafe Sample Application - Pure SI Implementation
=======================
See the parent-level README.md for more details, but the flow of the implementation should follow this diagram:
Barista
hotDrinks ____________________
|==========| -->| |
orders drinks / | prepareHotDrink() |
Place Order ->Cafe->|======|->OrderSplitter->|======|->DrinkRouter | |
\ coldDrinks | prepareColdDrink() |
|==========| -->| |
|____________________|
Legend: |====| - channels
## Instructions for running the CafeDemo sample
The example comes with two identical configurations. One is ANNOTATION-based another is XML-based
To run this sample simply execute the CafeDemoApp classes in the **org.springframework.integration.samples.cafe.xml** or **org.springframework.integration.samples.cafe.annotation** package.
See the configuration files in the **META-INF/spring/integration** path.

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>cafe</artifactId>
<groupId>org.springframework.integration.samples</groupId>
<version>2.1.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>cafe-si</artifactId>
<packaging>jar</packaging>
<name>Cafe - Pure Spring Integration</name>
<description>
This module implements the cafe sample using spring-integration components.
The channels that are used are in-memory channels and there is no distribution of
the processes (i.e., all of the processing happens within the same JVM and there
is no message broker). To see the cafe sample with a message broker to help distribute
the components, see the AMQP or the JMS sample.
</description>
</project>

View File

@@ -16,13 +16,14 @@
package org.springframework.integration.samples.cafe;
import java.io.Serializable;
import java.util.List;
/**
* @author Marius Bogoevici
* @author Tom McCuch
*/
public class Delivery {
public class Delivery implements Serializable{
private static final String SEPARATOR = "-----------------------";

View File

@@ -16,11 +16,13 @@
package org.springframework.integration.samples.cafe;
import java.io.Serializable;
/**
* @author Marius Bogoevici
* @author Tom McCuch
*/
public class Drink {
public class Drink implements Serializable{
private boolean iced;

View File

@@ -16,10 +16,12 @@
package org.springframework.integration.samples.cafe;
import java.io.Serializable;
/**
* @author Mark Fisher
*/
public enum DrinkType {
public enum DrinkType implements Serializable{
ESPRESSO,
LATTE,

View File

@@ -16,6 +16,7 @@
package org.springframework.integration.samples.cafe;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@@ -24,7 +25,7 @@ import java.util.List;
* @author Marius Bogoevici
* @author Tom McCuch
*/
public class Order {
public class Order implements Serializable{
private List<OrderItem> orderItems = new ArrayList<OrderItem>();

View File

@@ -16,12 +16,14 @@
package org.springframework.integration.samples.cafe;
import java.io.Serializable;
/**
* @author Mark Fisher
* @author Marius Bogoevici
* @author Tom McCuch
*/
public class OrderItem {
public class OrderItem implements Serializable {
private DrinkType type;

View File

@@ -18,10 +18,10 @@ package org.springframework.integration.samples.cafe.annotation;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.log4j.Logger;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.samples.cafe.Drink;
import org.springframework.integration.samples.cafe.OrderItem;
import org.apache.log4j.Logger;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.stereotype.Component;
/**

View File

@@ -16,9 +16,9 @@
package org.springframework.integration.samples.cafe.annotation;
import org.springframework.integration.samples.cafe.OrderItem;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.Router;
import org.springframework.integration.samples.cafe.OrderItem;
/**
* @author Mark Fisher

View File

@@ -18,10 +18,10 @@ package org.springframework.integration.samples.cafe.annotation;
import java.util.List;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.Splitter;
import org.springframework.integration.samples.cafe.Order;
import org.springframework.integration.samples.cafe.OrderItem;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.Splitter;
/**
* @author Mark Fisher

View File

@@ -18,11 +18,11 @@ package org.springframework.integration.samples.cafe.annotation;
import java.util.List;
import org.springframework.integration.samples.cafe.Delivery;
import org.springframework.integration.samples.cafe.Drink;
import org.springframework.integration.annotation.Aggregator;
import org.springframework.integration.annotation.CorrelationStrategy;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.samples.cafe.Delivery;
import org.springframework.integration.samples.cafe.Drink;
/**
* @author Marius Bogoevici

View File

@@ -18,9 +18,9 @@ package org.springframework.integration.samples.cafe.xml;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.log4j.Logger;
import org.springframework.integration.samples.cafe.Drink;
import org.springframework.integration.samples.cafe.OrderItem;
import org.apache.log4j.Logger;
/**
* @author Mark Fisher

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- Appenders -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%t %-5p: %c - %m%n" />
</layout>
</appender>
<!-- Loggers -->
<logger name="org.springframework">
<level value="warn" />
</logger>
<logger name="org.springframework.amqp">
<level value="info" />
</logger>
<logger name="org.springframework.integration">
<level value="info" />
</logger>
<logger name="org.springframework.integration.samples">
<level value="debug" />
</logger>
<!-- Root Logger -->
<root>
<priority value="warn" />
<appender-ref ref="console" />
</root>
</log4j:configuration>

View File

@@ -4,8 +4,14 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.integration.samples</groupId>
<artifactId>cafe</artifactId>
<version>2.1.0.BUILD-SNAPSHOT</version>
<name>Samples (Applications) - Cafe Sample</name>
<packaging>pom</packaging>
<version>2.1.0.BUILD-SNAPSHOT</version>
<modules>
<module>cafe-jms</module>
<module>cafe-si</module>
<module>cafe-amqp</module>
</modules>
<name>Samples (Applications) - Cafe Sample</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.integration.version>2.1.0.RELEASE</spring.integration.version>
@@ -24,11 +30,6 @@
<artifactId>spring-context</artifactId>
<version>${spring.core.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-amqp</artifactId>
<version>${spring.integration.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-stream</artifactId>