Merge pull request #14 from spring-tom/master

Added Spring Integration AMQP config to Cafe Sample
This commit is contained in:
Gunnar Hillert
2011-12-14 09:21:06 -08:00
21 changed files with 761 additions and 34 deletions

View File

@@ -7,14 +7,25 @@
<version>2.0.0</version>
<name>Spring Integration Cafe Sample</name>
<properties>
<spring.integration.version>2.0.5.RELEASE</spring.integration.version>
<spring.integration.version>2.1.0.M3</spring.integration.version>
<spring.core.version>3.1.0.RC1</spring.core.version>
<log4j.version>1.2.16</log4j.version>
<junit.version>4.7</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.core.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.core.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
<artifactId>spring-integration-amqp</artifactId>
<version>${spring.integration.version}</version>
</dependency>
<dependency>
@@ -22,6 +33,17 @@
<artifactId>spring-integration-stream</artifactId>
<version>${spring.integration.version}</version>
</dependency>
<!-- Jackson JSON Mapper -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>

View File

@@ -8,7 +8,7 @@ The domain is that of a Cafe, and the basic flow is depicted in the following di
|==========| -->| |
orders drinks / | prepareHotDrink() |
Place Order ->Cafe->|======|->OrderSplitter->|======|->DrinkRouter | |
\ coldDeinks | prepareColdDrink() |
\ coldDrinks | prepareColdDrink() |
|==========| -->| |
|____________________|
@@ -26,11 +26,24 @@ Instructions for running the CafeDemo sample
-------------------------------------------------------------------------------
1. The example comes with two identical configurations. One is ANNOTATION-based anther is XML-based
2. To run this sample simply execute the CafeDemo* test classes
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.
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:
You shoudl see the output similar to this:
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

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,16 +20,18 @@ import java.util.List;
/**
* @author Marius Bogoevici
* @author Tom McCuch
*/
public class Delivery {
private static final String SEPARATOR = "-----------------------";
private List<Drink> deliveredDrinks;
private int orderNumber;
// Default constructor required by Jackson Java JSON-processor
public Delivery() {}
public Delivery(List<Drink> deliveredDrinks) {
assert(deliveredDrinks.size() > 0);
@@ -42,10 +44,18 @@ public class Delivery {
return orderNumber;
}
public void setOrderNumber(int orderNumber) {
this.orderNumber = orderNumber;
}
public List<Drink> getDeliveredDrinks() {
return deliveredDrinks;
}
public void setDeliveredDrinks(List<Drink> deliveredDrinks) {
this.deliveredDrinks = deliveredDrinks;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer(SEPARATOR + "\n");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package org.springframework.integration.samples.cafe;
/**
* @author Marius Bogoevici
* @author Tom McCuch
*/
public class Drink {
@@ -30,10 +31,13 @@ public class Drink {
private int orderNumber;
public Drink(int orderNumber, DrinkType drinkType, boolean hot, int shots) {
// Default constructor required by Jackson Java JSON-processor
public Drink() {}
public Drink(int orderNumber, DrinkType drinkType, boolean iced, int shots) {
this.orderNumber = orderNumber;
this.drinkType = drinkType;
this.iced = hot;
this.iced = iced;
this.shots = shots;
}
@@ -42,6 +46,34 @@ public class Drink {
return orderNumber;
}
public void setOrderNumber(int orderNumber) {
this.orderNumber = orderNumber;
}
public boolean isIced() {
return this.iced;
}
public void setIced(boolean iced) {
this.iced = iced;
}
public DrinkType getDrinkType() {
return this.drinkType;
}
public void setDrinkType(DrinkType drinkType) {
this.drinkType = drinkType;
}
public int getShots() {
return this.shots;
}
public void setShots(int shots) {
this.shots = shots;
}
@Override
public String toString() {
return (iced?"Iced":"Hot") + " " + drinkType.toString() + ", " + shots + " shots.";

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,27 +22,39 @@ import java.util.List;
/**
* @author Mark Fisher
* @author Marius Bogoevici
* @author Tom McCuch
*/
public class Order {
private List<OrderItem> orderItems = new ArrayList<OrderItem>();
/** the order number used for tracking */
private int number;
// Default constructor required by Jackson Java JSON-processor
public Order() {}
public Order(int number) {
this.number = number;
}
public void addItem(DrinkType drinkType, int shots, boolean iced) {
this.orderItems.add(new OrderItem(this, drinkType, shots, iced));
this.orderItems.add(new OrderItem(this.number, drinkType, shots, iced));
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public List<OrderItem> getItems() {
return this.orderItems;
}
public void setItems(List<OrderItem> orderItems) {
this.orderItems = orderItems;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ package org.springframework.integration.samples.cafe;
/**
* @author Mark Fisher
* @author Marius Bogoevici
* @author Tom McCuch
*/
public class OrderItem {
@@ -28,34 +29,52 @@ public class OrderItem {
private boolean iced = false;
private final Order order;
/** the order this item is tied to */
private int orderNumber;
// Default constructor required by Jackson Java JSON-processor
public OrderItem() {}
public OrderItem(Order order, DrinkType type, int shots, boolean iced) {
this.order = order;
public OrderItem(int orderNumber, DrinkType type, int shots, boolean iced) {
this.orderNumber = orderNumber;
this.type = type;
this.shots = shots;
this.iced = iced;
}
public Order getOrder() {
return this.order;
public int getOrderNumber() {
return this.orderNumber;
}
public void setOrderNumber(int orderNumber) {
this.orderNumber = orderNumber;
}
public boolean isIced() {
return this.iced;
}
public int getShots() {
public void setIced(boolean iced) {
this.iced = iced;
}
public int getShots() {
return shots;
}
public DrinkType getDrinkType() {
public void setShots(int shots) {
this.shots = shots;
}
public DrinkType getDrinkType() {
return this.type;
}
public String toString() {
public void setDrinkType(DrinkType type) {
this.type = type;
}
public String toString() {
return ((this.iced) ? "iced " : "hot ") + this.shots + " shot " + this.type;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,9 @@ import org.springframework.stereotype.Component;
/**
* @author Mark Fisher
* @author Marius Bogoevici
* @author Tom McCuch
*/
@Component
public class Barista {
private static Logger logger = Logger.getLogger(Barista.class);
@@ -54,8 +56,8 @@ public class Barista {
Thread.sleep(this.hotDrinkDelay);
logger.info(Thread.currentThread().getName()
+ " prepared hot drink #" + hotDrinkCounter.incrementAndGet() + " for order #"
+ orderItem.getOrder().getNumber() + ": " + orderItem);
return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(), orderItem.isIced(),
+ orderItem.getOrderNumber() + ": " + orderItem);
return new Drink(orderItem.getOrderNumber(), orderItem.getDrinkType(), orderItem.isIced(),
orderItem.getShots());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -69,8 +71,8 @@ public class Barista {
Thread.sleep(this.coldDrinkDelay);
logger.info(Thread.currentThread().getName()
+ " prepared cold drink #" + coldDrinkCounter.incrementAndGet() + " for order #"
+ orderItem.getOrder().getNumber() + ": " + orderItem);
return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(), orderItem.isIced(),
+ orderItem.getOrderNumber() + ": " + orderItem);
return new Drink(orderItem.getOrderNumber(), orderItem.getDrinkType(), orderItem.isIced(),
orderItem.getShots());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,9 @@ import org.springframework.integration.samples.cafe.OrderItem;
/**
* @author Mark Fisher
* @author Marius Bogoevici
* @author Tom McCuch
*/
public class Barista {
private static Logger logger = Logger.getLogger(Barista.class);
private long hotDrinkDelay = 5000;
@@ -50,8 +52,8 @@ public class Barista {
Thread.sleep(this.hotDrinkDelay);
logger.info(Thread.currentThread().getName()
+ " prepared hot drink #" + hotDrinkCounter.incrementAndGet() + " for order #"
+ orderItem.getOrder().getNumber() + ": " + orderItem);
return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(), orderItem.isIced(),
+ orderItem.getOrderNumber() + ": " + orderItem);
return new Drink(orderItem.getOrderNumber(), orderItem.getDrinkType(), orderItem.isIced(),
orderItem.getShots());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -64,8 +66,8 @@ public class Barista {
Thread.sleep(this.coldDrinkDelay);
logger.info(Thread.currentThread().getName()
+ " prepared cold drink #" + coldDrinkCounter.incrementAndGet() + " for order #"
+ orderItem.getOrder().getNumber() + ": " + orderItem);
return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(), orderItem.isIced(),
+ orderItem.getOrderNumber() + ": " + orderItem);
return new Drink(orderItem.getOrderNumber(), orderItem.getDrinkType(), orderItem.isIced(),
orderItem.getShots());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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;
/**
* Provides the 'main' method for running the Cafe Demo store front
* application using AMQP. Before running, be sure to have a
* RabbitMQ broker started on localhost:5672 configured with the default
* guest | guest client credentials on the / vHost. When an order is
* placed, the Cafe store front will publish that order on the cafe-orders
* exchange to be processed.
* <p/>
* The relevant components are defined within the configuration files:
* ("cafeDemo-amqp-xml.xml", "cafeDemo-amqp-config-xml.xml").
* <p/>
* If deploying in SpringSource dmServer, the relevant ApplicationContext
* configuration is in the META-INF/spring directory instead.
*
* @author Tom McCuch
*/
public class CafeDemoAppAmqp {
/**
* 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 <= 100; 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) {
AbstractApplicationContext context =
CafeDemoAppUtilities.loadProfileContext(
"/META-INF/spring/integration/amqp/cafeDemo-amqp-xml.xml",
CafeDemoAppAmqp.class,CafeDemoAppUtilities.DEV);
order(context, 100);
context.close();
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.samples.cafe.xml;
import java.io.IOException;
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;
/**
* Provides the 'main' method for running the Cafe Demo Cold Drink
* Barista application using AMQP. Before running, be sure to have a
* RabbitMQ broker started on localhost:5672 configured with the default
* guest | guest client credentials on the / vHost. When a drink order
* is placed on the cold-drinks queue, the Barista will prepare the drink
* and reply to the reply-to queue set by the sender.
* <p/>
* The relevant components are defined within the configuration files:
* ("cafeDemo-amqp-baristaCold-xml.xml", "cafeDemo-amqp-config-xml.xml").
* <p/>
* If deploying in SpringSource dmServer, the relevant ApplicationContext
* configuration is in the META-INF/spring directory instead.
*
* @author Tom McCuch
*/
public class CafeDemoAppBaristaColdAmqp {
public static void main(String[] args) {
AbstractApplicationContext context =
CafeDemoAppUtilities.loadProfileContext(
"/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");
try {
System.in.read();
} catch (IOException e) {
context.close();
}
context.close();
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.samples.cafe.xml;
import java.io.IOException;
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;
/**
* Provides the 'main' method for running the Cafe Demo Hot Drink
* Barista application using AMQP. Before running, be sure to have a
* RabbitMQ broker started on localhost:5672 configured with the default
* guest | guest client credentials on the / vHost. When a drink order
* is placed on the hot-drinks queue, the Barista will prepare the drink
* and reply to the reply-to queue set by the sender.
* <p/>
* The relevant components are defined within the configuration files:
* ("cafeDemo-amqp-baristaHot-xml.xml", "cafeDemo-amqp-config-xml.xml").
* <p/>
* If deploying in SpringSource dmServer, the relevant ApplicationContext
* configuration is in the META-INF/spring directory instead.
*
* @author Tom McCuch
*/
public class CafeDemoAppBaristaHotAmqp {
public static void main(String[] args) {
AbstractApplicationContext context =
CafeDemoAppUtilities.loadProfileContext(
"/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");
try {
System.in.read();
} catch (IOException e) {
context.close();
}
context.close();
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.samples.cafe.xml;
import java.io.IOException;
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;
/**
* Provides the 'main' method for running the Cafe Demo Operations
* application using AMQP. Before running, be sure to have a RabbitMQ
* broker started on localhost:5672 configured with the default
* guest | guest client credentials on the / vHost. When an order is
* placed on the new-orders queue, the Cafe Operations app will split
* the order into order line items, route them to either the coldDrink
* or hotDrink Barista, aggregate the prepared drinks returned from the
* Barista using a Waiter, and publish the delivered order on the
* cafe-deliveries exchange.
* <p/>
* The relevant components are defined within the configuration files:
* ("cafeDemo-amqp-operations-xml.xml", "cafeDemo-amqp-config-xml.xml").
* <p/>
* Before starting this app, be sure to start the CafeDemoAppBaristaCold
* and CafeDemoAppBaristaHot apps first.
*
* If deploying in SpringSource dmServer, the relevant ApplicationContext
* configuration is in the META-INF/spring directory instead.
*
* @author Tom McCuch
*/
public class CafeDemoAppOperationsAmqp {
public static void main(String[] args) {
AbstractApplicationContext context =
CafeDemoAppUtilities.loadProfileContext(
"/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");
try {
System.in.read();
} catch (IOException e) {
context.close();
}
context.close();
}
}

View File

@@ -0,0 +1,36 @@
package org.springframework.integration.samples.cafe.xml;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.integration.samples.cafe.Cafe;
import org.springframework.integration.samples.cafe.DrinkType;
import org.springframework.integration.samples.cafe.Order;
public class CafeDemoAppUtilities {
/** spring profile for running locally */
public static final String DEV = "dev";
/** spring profile for running in cloud foundry */
public static final String CLOUD = "cloud";
/**
*
* @param path path to the file
* @param targetClass the class who's classloader we will use to laod the context file
* @param profile a profile name
* @return the spring context
*/
public static AbstractApplicationContext loadProfileContext(String path, Class targetClass, String profile) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.getEnvironment().setActiveProfiles(profile);
ctx.setClassLoader(targetClass.getClassLoader());
ctx.load(path);
ctx.refresh();
return ctx;
}
}

View File

@@ -0,0 +1,52 @@
<?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-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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://schema.cloudfoundry.org/spring
http://schema.cloudfoundry.org/spring/cloudfoundry-spring-0.8.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
http://www.springframework.org/schema/integration/amqp
http://www.springframework.org/schema/integration/amqp/spring-integration-amqp.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd
http://www.springframework.org/schema/integration/stream
http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd">
<!-- rabbit connection factory, rabbit template, and rabbit admin -->
<import resource="classpath:META-INF/spring/integration/amqp/cafeDemo-amqp-config-xml.xml" />
<!-- To receive an AMQP Message from a Queue, and respond to its reply-to address, configure an inbound-gateway. -->
<int-amqp:inbound-gateway
id="coldDrinksBarista"
request-channel="coldJsonDrinks"
queue-names="cold-drinks"
connection-factory="rabbitConnectionFactory" />
<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>
<!-- rabbit exchanges, queues, and bindings used by this app -->
<rabbit:topic-exchange name="cafe-drinks" auto-delete="true" durable="true">
<rabbit:bindings>
<rabbit:binding queue="cold-drinks" pattern="drink.cold"/>
<rabbit:binding queue="all-cold-drinks" pattern="drink.cold"/>
</rabbit:bindings>
</rabbit:topic-exchange>
<rabbit:queue name="cold-drinks" auto-delete="true" durable="true"/>
<rabbit:queue name="all-cold-drinks" auto-delete="true" durable="true"/>
</beans>

View File

@@ -0,0 +1,52 @@
<?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-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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://schema.cloudfoundry.org/spring
http://schema.cloudfoundry.org/spring/cloudfoundry-spring-0.8.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
http://www.springframework.org/schema/integration/amqp
http://www.springframework.org/schema/integration/amqp/spring-integration-amqp.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd
http://www.springframework.org/schema/integration/stream
http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd">
<!-- rabbit connection factory, rabbit template, and rabbit admin -->
<import resource="classpath:META-INF/spring/integration/amqp/cafeDemo-amqp-config-xml.xml" />
<!-- To receive an AMQP Message from a Queue, and respond to its reply-to address, configure an inbound-gateway. -->
<int-amqp:inbound-gateway
id="hotDrinksBarista"
request-channel="hotJsonDrinks"
queue-names="hot-drinks"
connection-factory="rabbitConnectionFactory" />
<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>
<!-- rabbit exchanges, queues, and bindings used by this app -->
<rabbit:topic-exchange name="cafe-drinks" auto-delete="true" durable="true">
<rabbit:bindings>
<rabbit:binding queue="hot-drinks" pattern="drink.hot"/>
<rabbit:binding queue="all-hot-drinks" pattern="drink.hot"/>
</rabbit:bindings>
</rabbit:topic-exchange>
<rabbit:queue name="hot-drinks" auto-delete="true" durable="true"/>
<rabbit:queue name="all-hot-drinks" auto-delete="true" durable="true"/>
</beans>

View File

@@ -0,0 +1,39 @@
<?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:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:cloud="http://schema.cloudfoundry.org/spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://schema.cloudfoundry.org/spring
http://schema.cloudfoundry.org/spring/cloudfoundry-spring-0.8.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd">
<!-- Set up the AmqpTemplate/RabbitTemplate: -->
<rabbit:template id="amqpTemplate"
connection-factory="rabbitConnectionFactory" reply-timeout="10000" />
<!-- Request that queues, exchanges and bindings be automatically declared
on the broker: -->
<rabbit:admin connection-factory="rabbitConnectionFactory" />
<!-- profiles must be the last element sin the file -->
<!-- Obtain a connection to the RabbitMQ via cloudfoundry-runtime: -->
<beans profile="cloud">
<cloud:rabbit-connection-factory
id="rabbitConnectionFactory" />
</beans>
<!-- connect to the local broker using the default user name and password -->
<beans profile="dev">
<bean id="rabbitConnectionFactory"
class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<constructor-arg value="localhost" />
<property name="username" value="guest" />
<property name="password" value="guest" />
</bean>
</beans>
</beans>

View File

@@ -0,0 +1,118 @@
<?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-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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://schema.cloudfoundry.org/spring
http://schema.cloudfoundry.org/spring/cloudfoundry-spring-0.8.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
http://www.springframework.org/schema/integration/amqp
http://www.springframework.org/schema/integration/amqp/spring-integration-amqp.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd
http://www.springframework.org/schema/integration/stream
http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd">
<!-- rabbit connection factory, rabbit template, and rabbit admin -->
<import resource="classpath:META-INF/spring/integration/amqp/cafeDemo-amqp-config-xml.xml" />
<!-- intercept messages on these channels while still allowing them to continue -->
<int:wire-tap channel="logger" pattern="orders,coldDrinks,hotDrinks,preparedDrinks,deliveries"/>
<int:logging-channel-adapter id="logger" log-full-message="true" level="INFO"/>
<!-- To receive AMQP Messages from a Queue, configure an inbound-channel-adapter -->
<int-amqp:inbound-channel-adapter queue-names="new-orders" channel="jsonOrders" connection-factory="rabbitConnectionFactory" acknowledge-mode="AUTO" />
<int:json-to-object-transformer id="json-to-order" input-channel="jsonOrders" output-channel="preOrders" type="org.springframework.integration.samples.cafe.Order" />
<int:splitter input-channel="preOrders" expression="payload.items" output-channel="preDrinks" apply-sequence="true"/>
<int:header-enricher input-channel="preDrinks" output-channel="drinks">
<int:header name="ICED" expression="payload.isIced()"/>
</int:header-enricher>
<int:object-to-json-transformer id="drink-to-json" input-channel="drinks" output-channel="jsonDrinks" />
<int:router input-channel="jsonDrinks" expression="headers.ICED ? 'coldDrinks' : 'hotDrinks'"/>
<int:channel id="coldDrinks">
<int:queue/>
</int:channel>
<!-- Default poller -->
<int:poller default="true" fixed-rate="100"/>
<int:channel id="hotDrinks">
<int:queue/>
</int:channel>
<!-- To send AMQP Messages to an Exchange and receive back a response from a remote client, configure an outbound-gateway -->
<int-amqp:outbound-gateway
id="coldDrinksBarista"
request-channel="coldDrinks"
reply-channel="preparedJsonDrinks"
exchange-name="cafe-drinks"
routing-key="drink.cold"
amqp-template="amqpTemplate" />
<!-- To send AMQP Messages to an Exchange and receive back a response from a remote client, configure an outbound-gateway -->
<int-amqp:outbound-gateway
id="hotDrinksBarista"
request-channel="hotDrinks"
reply-channel="preparedJsonDrinks"
exchange-name="cafe-drinks"
routing-key="drink.hot"
amqp-template="amqpTemplate" />
<int:channel id="preparedJsonDrinks"/>
<int:json-to-object-transformer id="json-to-drink" input-channel="preparedJsonDrinks" output-channel="preparedDrinks" type="org.springframework.integration.samples.cafe.Drink"/>
<int:aggregator input-channel="preparedDrinks" method="prepareDelivery" output-channel="preDeliveries">
<bean class="org.springframework.integration.samples.cafe.xml.Waiter"/>
</int:aggregator>
<int:channel id="preDeliveries" />
<int:header-enricher input-channel="preDeliveries" output-channel="deliveries">
<int:header name="NUMBER" expression="payload.getOrderNumber()" />
</int:header-enricher>
<int:object-to-json-transformer id="delivery-to-json" input-channel="deliveries" output-channel="jsonDeliveries"/>
<int:channel id="jsonDeliveries" />
<!-- To send AMQP Messages to an Exchange, configure an outbound-channel-adapter. -->
<int-amqp:outbound-channel-adapter
id="deliveredOrders"
channel="jsonDeliveries"
amqp-template="amqpTemplate"
exchange-name="cafe-deliveries"
routing-key-expression="'delivery.'+headers.NUMBER" />
<bean id="waiter" class="org.springframework.integration.samples.cafe.xml.Waiter"/>
<!-- rabbit exchanges, queues, and bindings used by this app -->
<rabbit:topic-exchange name="cafe-drinks" auto-delete="true" durable="true">
<rabbit:bindings>
<rabbit:binding queue="all-drinks" pattern="drink.*"/>
</rabbit:bindings>
</rabbit:topic-exchange>
<rabbit:queue name="all-drinks" auto-delete="true" durable="true"/>
<rabbit:fanout-exchange name="cafe-deliveries" auto-delete="false" durable="true">
<rabbit:bindings>
<rabbit:binding queue="all-deliveries" />
</rabbit:bindings>
</rabbit:fanout-exchange>
<rabbit:queue name="all-deliveries" auto-delete="false" durable="true"/>
</beans>

View File

@@ -0,0 +1,56 @@
<?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-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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://schema.cloudfoundry.org/spring
http://schema.cloudfoundry.org/spring/cloudfoundry-spring-0.8.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
http://www.springframework.org/schema/integration/amqp
http://www.springframework.org/schema/integration/amqp/spring-integration-amqp.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd
http://www.springframework.org/schema/integration/stream
http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd">
<!-- rabbit connection factory, rabbit template, and rabbit admin -->
<import resource="classpath:META-INF/spring/integration/amqp/cafeDemo-amqp-config-xml.xml" />
<!-- spring integration flow -->
<int:gateway id="cafe" service-interface="org.springframework.integration.samples.cafe.Cafe" />
<int:channel id="orders"/>
<int:header-enricher input-channel="orders" output-channel="newOrders">
<int:header name="NUMBER" expression="payload.getNumber()"/>
</int:header-enricher>
<int:object-to-json-transformer input-channel="newOrders" output-channel="jsonNewOrders" />
<int:channel id="jsonNewOrders" />
<!-- To send AMQP Messages to an Exchange, configure an outbound-channel-adapter. -->
<int-amqp:outbound-channel-adapter
channel="jsonNewOrders"
exchange-name="cafe-orders"
routing-key-expression="'order.'+headers.NUMBER"
amqp-template="amqpTemplate" />
<!-- rabbit exchanges, queues, and bindings used by this app -->
<rabbit:topic-exchange name="cafe-orders" auto-delete="false" durable="true">
<rabbit:bindings>
<rabbit:binding queue="new-orders" pattern="order.*"/>
<rabbit:binding queue="all-orders" pattern="order.*"/>
</rabbit:bindings>
</rabbit:topic-exchange>
<rabbit:queue name="new-orders" auto-delete="false" durable="true"/>
<rabbit:queue name="all-orders" auto-delete="false" durable="true"/>
</beans>

View File

@@ -12,22 +12,27 @@
<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 -->
<channel id="orders"/>
<splitter input-channel="orders" expression="payload.items" output-channel="drinks"/>
<!-- The router sends different drink orders on different paths -->
<channel id="drinks"/>
<router input-channel="drinks" expression="payload.iced ? 'coldDrinks' : 'hotDrinks'"/>
<!-- individual order items are processed by the barista -->
<channel id="coldDrinks">
<queue capacity="10"/>
</channel>
<service-activator input-channel="coldDrinks" ref="barista" method="prepareColdDrink" output-channel="preparedDrinks"/>
<!-- individual order items are processed by the barista -->
<channel id="hotDrinks">
<queue capacity="10"/>
</channel>
<service-activator input-channel="hotDrinks" ref="barista" method="prepareHotDrink" output-channel="preparedDrinks"/>
<!-- drink order items are aggregated in a call to the waiter -->
<channel id="preparedDrinks"/>
<aggregator input-channel="preparedDrinks" method="prepareDelivery" output-channel="deliveries">
<beans:bean class="org.springframework.integration.samples.cafe.xml.Waiter"/>

View File

@@ -6,7 +6,7 @@
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p: %c - %m%n" />
<param name="ConversionPattern" value="%t %-5p: %c - %m%n" />
</layout>
</appender>
@@ -15,6 +15,14 @@
<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>