The abstract method configureRabbitTemplate lets the Client and Server further customize
+ * the rabbit template to their specific needs.
+ *
+ * @author Mark Pollack
+ * @author Mark Fisher
+ */
+@Configuration
+public abstract class AbstractStockAppRabbitConfiguration extends AbstractRabbitConfiguration {
+
+ /**
+ * Shared topic exchange used for publishing any market data (e.g. stock quotes)
+ */
+ protected static String MARKET_DATA_EXCHANGE_NAME = "app.stock.marketdata";
+
+ /**
+ * The server-side consumer's queue that provides point-to-point semantics for stock requests.
+ */
+ protected static String STOCK_REQUEST_QUEUE_NAME = "app.stock.request";
+
+ /**
+ * Key that clients will use to send to the stock request queue via the default direct exchange.
+ */
+ protected static String STOCK_REQUEST_ROUTING_KEY = STOCK_REQUEST_QUEUE_NAME;
+
+ //protected static TopicExchange MARKET_DATA_EXCHANGE = new TopicExchange(MARKET_DATA_EXCHANGE_NAME);
+
+
+ protected abstract void configureRabbitTemplate(RabbitTemplate template);
+
+ @Bean
+ public ConnectionFactory connectionFactory() {
+ //TODO make it possible to customize in subclasses.
+ CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
+ connectionFactory.setUsername("guest");
+ connectionFactory.setPassword("guest");
+ connectionFactory.setChannelCacheSize(10);
+ return connectionFactory;
+ }
+
+ @Bean
+ public RabbitTemplate rabbitTemplate() {
+ RabbitTemplate template = new RabbitTemplate(connectionFactory());
+ template.setMessageConverter(jsonMessageConverter());
+ configureRabbitTemplate(template);
+ return template;
+ }
+
+ @Bean
+ public MessageConverter jsonMessageConverter() {
+ return new JsonMessageConverter();
+ }
+
+
+// @PostConstruct
+// public void declareExchange()
+// {
+// declare(this.MARKET_DATA_EXCHANGE);
+// }
+
+
+ @Bean
+ public TopicExchange marketDataExchange() {
+ return declare(new TopicExchange(MARKET_DATA_EXCHANGE_NAME));
+ }
+
+
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/config/RoutingKey.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/config/RoutingKey.java
new file mode 100644
index 0000000..2623aa7
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/config/RoutingKey.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.config;
+
+/**
+ * Enumerations for the RoutingKeys used in the application to allow for a more fluent API
+ * style when configuring the broker in code.
+ *
+ * @author Mark Pollack
+ */
+public enum RoutingKey {
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/config/client/RabbitClientConfiguration.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/config/client/RabbitClientConfiguration.java
new file mode 100644
index 0000000..d281f98
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/config/client/RabbitClientConfiguration.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.config.client;
+
+import javax.annotation.PostConstruct;
+
+import org.springframework.amqp.core.Binding;
+import org.springframework.amqp.core.BindingBuilder;
+import org.springframework.amqp.core.UniquelyNamedQueue;
+
+import static org.springframework.amqp.core.BindingBuilder.*;
+import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
+import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
+import org.springframework.amqp.rabbit.stocks.config.AbstractStockAppRabbitConfiguration;
+import org.springframework.amqp.rabbit.stocks.gateway.RabbitStockServiceGateway;
+import org.springframework.amqp.rabbit.stocks.gateway.StockServiceGateway;
+import org.springframework.amqp.rabbit.stocks.handler.ClientHandler;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Configures RabbitTemplate and creates the Trader queue and binding for the client.
+ *
+ * @author Mark Pollack
+ * @author Mark Fisher
+ */
+@Configuration
+public class RabbitClientConfiguration extends AbstractStockAppRabbitConfiguration {
+
+ @Value("${stocks.quote.pattern}")
+ private String marketDataRoutingKey;
+
+ @Autowired
+ private ClientHandler clientHandler;
+
+
+ // Create the Queue definitions that write up the Message listener container
+
+ //private Queue marketDataQueue = new UniquelyNamedQueue("mktdata");
+
+ //private Queue traderJoeQueue = new UniquelyNamedQueue("joe");
+
+ /**
+ * The client's template will by default send to the exchange defined
+ * in {@link AbstractRabbitConfiguration.rabbitTemplate()}
+ * with the routing key {@link AbstractStockAppRabbitConfiguration#STOCK_REQUEST_QUEUE_NAME}
+ *
+ * The default exchange will delivery to a queue whose name matches the routing key value.
+ */
+ @Override
+ public void configureRabbitTemplate(RabbitTemplate rabbitTemplate) {
+ rabbitTemplate.setDefaultRoutingKey(STOCK_REQUEST_QUEUE_NAME);
+ }
+
+ @Bean
+ public StockServiceGateway stockServiceGateway() {
+ RabbitStockServiceGateway gateway = new RabbitStockServiceGateway();
+ gateway.setRabbitTemplate(rabbitTemplate());
+ gateway.setDefaultReplyToQueue(traderJoeQueue());
+ return gateway;
+ }
+
+ @Bean
+ public SimpleMessageListenerContainer messageListenerContainer() {
+ SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory());
+ container.setQueues(marketDataQueue(), traderJoeQueue());
+ //container.setConcurrentConsumers(5); // note, now set to size of channel cache in CachingConnectionFactory by default
+ container.setMessageListener(messageListenerAdapter());
+ return container;
+
+ //container(using(connectionFactory()).listenToQueues(marketDataQueue(), traderJoeQueue()).withListener(messageListenerAdapter()).
+ }
+
+ @Bean
+ public MessageListenerAdapter messageListenerAdapter() {
+ return new MessageListenerAdapter(clientHandler, jsonMessageConverter());
+ }
+
+
+ // Broker Configuration
+
+// @PostConstruct
+// public void declareClientBrokerConfiguration() {
+// declare(marketDataQueue);
+// declare(new Binding(marketDataQueue, MARKET_DATA_EXCHANGE, marketDataRoutingKey));
+// declare(traderJoeQueue);
+// // no need to bind traderJoeQueue as it is automatically bound to the default direct exchanage, which is what we will use
+//
+// //add as many declare statements as needed like a script.
+// }
+
+ @Bean
+ public Queue marketDataQueue() {
+ return declareQueue();
+ }
+
+ /**
+ * Binds to the market data exchange. Interested in any stock quotes.
+ * @return
+ */
+ @Bean
+ public Binding marketDataBinding() {
+ return declare(new Binding(marketDataQueue(), marketDataExchange(), marketDataRoutingKey));
+
+ // Using BindingBuilder
+ //return declareBinding(from(marketDataQueue()).to(marketDataExchange()).with(marketDataRoutingKey));
+ }
+
+ /**
+ * This queue does not need a binding, since it relies on the default exchange.
+ */
+ @Bean
+ public Queue traderJoeQueue() {
+ return declareQueue();
+ }
+
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/config/server/RabbitServerConfiguration.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/config/server/RabbitServerConfiguration.java
new file mode 100644
index 0000000..3411349
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/config/server/RabbitServerConfiguration.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.config.server;
+
+import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.amqp.rabbit.stocks.config.AbstractStockAppRabbitConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Configures RabbitTemplate for the server.
+ *
+ * @author Mark Pollack
+ * @author Mark Fisher
+ */
+@Configuration
+public class RabbitServerConfiguration extends AbstractStockAppRabbitConfiguration {
+
+ /**
+ * The server's template will by default send to the topic exchange named
+ * {@link AbstractStockAppRabbitConfiguration#MARKET_DATA_EXCHANGE_NAME}.
+ */
+ public void configureRabbitTemplate(RabbitTemplate rabbitTemplate) {
+ rabbitTemplate.setDefaultExchange(MARKET_DATA_EXCHANGE_NAME);
+ }
+
+ /**
+ * We don't need to define any binding for the stock request queue, since it's relying
+ * on the default (no-name) direct exchange to which every queue is implicitly bound.
+ */
+ @Bean
+ public Queue stockRequestQueue() {
+ return declare(new Queue(STOCK_REQUEST_QUEUE_NAME));
+ }
+
+
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/domain/Quote.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/domain/Quote.java
new file mode 100644
index 0000000..96efabc
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/domain/Quote.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.domain;
+
+/**
+ * Domain object representing a stock quote.
+ *
+ * @author Mark Fisher
+ */
+public class Quote {
+
+ private Stock stock;
+ private String price;
+
+ public Quote() {
+ }
+
+ public Quote(Stock stock, String price) {
+ this.stock = stock;
+ this.price = price;
+ }
+
+ public Stock getStock() {
+ return this.stock;
+ }
+
+ public void setStock(Stock stock) {
+ this.stock = stock;
+ }
+
+ public String getPrice() {
+ return price;
+ }
+
+ public void setPrice(String price) {
+ this.price = price;
+ }
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/domain/Stock.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/domain/Stock.java
new file mode 100644
index 0000000..ac7faac
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/domain/Stock.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.domain;
+
+/**
+ * @author Mark Fisher
+ */
+public class Stock {
+
+ private String ticker;
+
+ private StockExchange stockExchange;
+
+ public String getTicker() {
+ return ticker;
+ }
+
+ public void setTicker(String ticker) {
+ this.ticker = ticker;
+ }
+
+ public StockExchange getStockExchange() {
+ return stockExchange;
+ }
+
+ public void setStockExchange(StockExchange stockExchange) {
+ this.stockExchange = stockExchange;
+ }
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/domain/StockExchange.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/domain/StockExchange.java
new file mode 100644
index 0000000..d7bfc6e
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/domain/StockExchange.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.domain;
+
+/**
+ * Enumeration for Stock Exchanges.
+ *
+ * @author Mark Fisher
+ */
+public enum StockExchange {
+
+ nyse, nasdaq;
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/domain/TradeRequest.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/domain/TradeRequest.java
new file mode 100644
index 0000000..7bac7e3
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/domain/TradeRequest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.domain;
+
+import java.math.BigDecimal;
+
+/**
+ * Simple trade request 'data' object. No functionality in this 'domain' class.
+ * @author Mark Pollack
+ *
+ */
+public class TradeRequest {
+
+ private String ticker;
+
+ private long quantity;
+
+ private BigDecimal price;
+
+ private String orderType;
+
+ private String accountName;
+
+ private boolean buyRequest;
+
+ private String userName;
+
+ private String requestId;
+
+ public String getTicker() {
+ return ticker;
+ }
+
+ public void setTicker(String ticker) {
+ this.ticker = ticker;
+ }
+
+ public long getQuantity() {
+ return quantity;
+ }
+
+ public void setQuantity(long quantity) {
+ this.quantity = quantity;
+ }
+
+ public BigDecimal getPrice() {
+ return price;
+ }
+
+ public void setPrice(BigDecimal price) {
+ this.price = price;
+ }
+
+ public String getOrderType() {
+ return orderType;
+ }
+
+ public void setOrderType(String orderType) {
+ this.orderType = orderType;
+ }
+
+ public String getAccountName() {
+ return accountName;
+ }
+
+ public void setAccountName(String accountName) {
+ this.accountName = accountName;
+ }
+
+ public boolean isBuyRequest() {
+ return buyRequest;
+ }
+
+ public void setBuyRequest(boolean buyRequest) {
+ this.buyRequest = buyRequest;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/domain/TradeResponse.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/domain/TradeResponse.java
new file mode 100644
index 0000000..8770a7f
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/domain/TradeResponse.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.domain;
+
+import java.math.BigDecimal;
+
+/**
+ * Simple trade request 'data' object. No functionality in this 'domain' class.
+ * @author Mark Pollack
+ *
+ */
+public class TradeResponse {
+
+ private String ticker;
+
+ private long quantity;
+
+ private BigDecimal price;
+
+ private String orderType;
+
+ private String confirmationNumber;
+
+ private boolean error;
+
+ private String errorMessage;
+
+ public String getTicker() {
+ return ticker;
+ }
+
+ public void setTicker(String ticker) {
+ this.ticker = ticker;
+ }
+
+ public long getQuantity() {
+ return quantity;
+ }
+
+ public void setQuantity(long quantity) {
+ this.quantity = quantity;
+ }
+
+ public BigDecimal getPrice() {
+ return price;
+ }
+
+ public void setPrice(BigDecimal price) {
+ this.price = price;
+ }
+
+ public String getOrderType() {
+ return orderType;
+ }
+
+ public void setOrderType(String orderType) {
+ this.orderType = orderType;
+ }
+
+ public String getConfirmationNumber() {
+ return confirmationNumber;
+ }
+
+ public void setConfirmationNumber(String confirmationNumber) {
+ this.confirmationNumber = confirmationNumber;
+ }
+
+ public boolean isError() {
+ return error;
+ }
+
+ public void setError(boolean error) {
+ this.error = error;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ @Override
+ public String toString() {
+ return "TradeResponse [confirmationNumber=" + confirmationNumber
+ + ", error=" + error + ", errorMessage=" + errorMessage
+ + ", orderType=" + orderType + ", price=" + price
+ + ", quantity=" + quantity + ", ticker=" + ticker + "]";
+ }
+
+
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/gateway/MarketDataGateway.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/gateway/MarketDataGateway.java
new file mode 100644
index 0000000..2ee93d4
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/gateway/MarketDataGateway.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.gateway;
+
+/**
+ * Gateway interface for sending market data to clients
+ * @author Mark Pollack
+ *
+ */
+public interface MarketDataGateway {
+
+ void sendMarketData();
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/gateway/RabbitMarketDataGateway.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/gateway/RabbitMarketDataGateway.java
new file mode 100644
index 0000000..3299105
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/gateway/RabbitMarketDataGateway.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.gateway;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.amqp.rabbit.core.support.RabbitGatewaySupport;
+import org.springframework.amqp.rabbit.stocks.domain.Quote;
+import org.springframework.amqp.rabbit.stocks.domain.Stock;
+import org.springframework.amqp.rabbit.stocks.domain.StockExchange;
+
+/**
+ * Rabbit implementation of the {@link MarketDataGateway} for sending Market data.
+ *
+ * @author Mark Pollack
+ * @author Mark Fisher
+ */
+public class RabbitMarketDataGateway extends RabbitGatewaySupport implements MarketDataGateway {
+
+ private static Log logger = LogFactory.getLog(RabbitMarketDataGateway.class);
+
+ private static final Random random = new Random();
+
+ private final List stocks = new ArrayList();
+
+
+ public RabbitMarketDataGateway() {
+ this.stocks.add(new MockStock("AAPL", StockExchange.nasdaq, 255));
+ this.stocks.add(new MockStock("CSCO", StockExchange.nasdaq, 22));
+ this.stocks.add(new MockStock("DELL", StockExchange.nasdaq, 15));
+ this.stocks.add(new MockStock("GOOG", StockExchange.nasdaq, 500));
+ this.stocks.add(new MockStock("INTC", StockExchange.nasdaq, 22));
+ this.stocks.add(new MockStock("MSFT", StockExchange.nasdaq, 29));
+ this.stocks.add(new MockStock("ORCL", StockExchange.nasdaq, 24));
+ this.stocks.add(new MockStock("CAJ", StockExchange.nyse, 43));
+ this.stocks.add(new MockStock("F", StockExchange.nyse, 12));
+ this.stocks.add(new MockStock("GE", StockExchange.nyse, 18));
+ this.stocks.add(new MockStock("HMC", StockExchange.nyse, 32));
+ this.stocks.add(new MockStock("HPQ", StockExchange.nyse, 48));
+ this.stocks.add(new MockStock("IBM", StockExchange.nyse, 130));
+ this.stocks.add(new MockStock("TM", StockExchange.nyse, 76));
+ }
+
+
+ public void sendMarketData() {
+ Quote quote = generateFakeQuote();
+ Stock stock = quote.getStock();
+ logger.info("Sending Market Data for " + stock.getTicker());
+ String routingKey = "app.stock.quotes."+ stock.getStockExchange() + "." + stock.getTicker();
+ getRabbitTemplate().convertAndSend(routingKey, quote);
+ }
+
+ private Quote generateFakeQuote() {
+ MockStock stock = this.stocks.get(random.nextInt(this.stocks.size()));
+ String price = stock.randomPrice();
+ return new Quote(stock, price);
+ }
+
+
+ private static class MockStock extends Stock {
+
+ private final int basePrice;
+ private final DecimalFormat twoPlacesFormat = new DecimalFormat("0.00");
+
+ private MockStock(String ticker, StockExchange stockExchange, int basePrice) {
+ this.setTicker(ticker);
+ this.setStockExchange(stockExchange);
+ this.basePrice = basePrice;
+ }
+
+ private String randomPrice() {
+ return this.twoPlacesFormat.format(this.basePrice + Math.abs(random.nextGaussian()));
+ }
+ }
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/gateway/RabbitStockServiceGateway.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/gateway/RabbitStockServiceGateway.java
new file mode 100644
index 0000000..f7c2ecb
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/gateway/RabbitStockServiceGateway.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.gateway;
+
+import java.io.UnsupportedEncodingException;
+import java.util.UUID;
+
+import org.springframework.amqp.AmqpException;
+import org.springframework.amqp.core.Address;
+import org.springframework.amqp.core.Message;
+import org.springframework.amqp.core.MessagePostProcessor;
+import org.springframework.amqp.core.Queue;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.amqp.rabbit.core.support.RabbitGatewaySupport;
+import org.springframework.amqp.rabbit.stocks.domain.TradeRequest;
+
+/**
+ * Rabbit implementation of {@link StockServiceGateway} to send trade requests to an external
+ * process.
+ * @author Mark Pollack
+ *
+ */
+public class RabbitStockServiceGateway extends RabbitGatewaySupport implements
+ StockServiceGateway {
+
+ private String defaultReplyToQueue;
+
+ public void setDefaultReplyToQueue(String defaultReplyToQueue) {
+ this.defaultReplyToQueue = defaultReplyToQueue;
+ }
+
+ public void setDefaultReplyToQueue(Queue defaultReplyToQueue) {
+ this.defaultReplyToQueue = defaultReplyToQueue.getName();
+ }
+
+
+ public void send(TradeRequest tradeRequest) {
+
+ getRabbitTemplate().convertAndSend(tradeRequest, new MessagePostProcessor() {
+
+ public Message postProcessMessage(Message message) throws AmqpException {
+ message.getMessageProperties().setReplyTo(new Address(defaultReplyToQueue));
+ //bytes = ((String) object).getBytes(this.defaultCharset);
+ try {
+ message.getMessageProperties().setCorrelationId(UUID.randomUUID().toString().getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return message;
+ }
+
+ });
+
+ }
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/gateway/StockServiceGateway.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/gateway/StockServiceGateway.java
new file mode 100644
index 0000000..e46610b
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/gateway/StockServiceGateway.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.gateway;
+
+import org.springframework.amqp.rabbit.stocks.domain.TradeRequest;
+
+/**
+ * Gateway interface that sends trades to an external process.
+ * @author Mark Pollack
+ *
+ */
+public interface StockServiceGateway {
+
+
+ void send(TradeRequest tradeRequest);
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/handler/ClientHandler.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/handler/ClientHandler.java
new file mode 100644
index 0000000..e7138e7
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/handler/ClientHandler.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.handler;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.amqp.rabbit.stocks.domain.Quote;
+import org.springframework.amqp.rabbit.stocks.domain.Stock;
+import org.springframework.amqp.rabbit.stocks.domain.TradeResponse;
+import org.springframework.amqp.rabbit.stocks.ui.StockController;
+
+/**
+ * POJO handler that receives market data and trade responses. Calls are delegated to the UI controller.
+ *
+ * @author Mark Pollack
+ * @author Mark Fisher
+ */
+public class ClientHandler {
+
+ private static Log log = LogFactory.getLog(ClientHandler.class);
+
+ private StockController stockController;
+
+ public StockController getStockController() {
+ return stockController;
+ }
+
+ public void setStockController(StockController stockController) {
+ this.stockController = stockController;
+ }
+
+ public void handleMessage(Quote quote) {
+ Stock stock = quote.getStock();
+ log.info("Received market data. Ticker = " + stock.getTicker() + ", Price = " + quote.getPrice());
+ stockController.displayQuote(quote);
+ }
+
+ public void handleMessage(TradeResponse tradeResponse) {
+ log.info("Received trade repsonse. [" + tradeResponse + "]");
+ stockController.UpdateTrade(tradeResponse);
+ }
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/handler/ServerHandler.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/handler/ServerHandler.java
new file mode 100644
index 0000000..9911b58
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/handler/ServerHandler.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.handler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.amqp.rabbit.stocks.domain.TradeRequest;
+import org.springframework.amqp.rabbit.stocks.domain.TradeResponse;
+import org.springframework.amqp.rabbit.stocks.service.CreditCheckService;
+import org.springframework.amqp.rabbit.stocks.service.ExecutionVenueService;
+import org.springframework.amqp.rabbit.stocks.service.TradingService;
+import org.springframework.util.StringUtils;
+
+
+/**
+ * POJO handler that receives trade requests and sends back a trade response. Main application
+ * logic sits here which coordinates between {@link ExecutionVenueService}, {@link CreditCheckService},
+ * and {@link TradingService}.
+ *
+ * @author Mark Pollack
+ *
+ */
+public class ServerHandler {
+
+ private ExecutionVenueService executionVenueService;
+
+ private CreditCheckService creditCheckService;
+
+ private TradingService tradingService;
+
+
+
+ public ServerHandler(ExecutionVenueService executionVenueService,
+ CreditCheckService creditCheckService,
+ TradingService tradingService) {
+ this.executionVenueService = executionVenueService;
+ this.creditCheckService = creditCheckService;
+ this.tradingService = tradingService;
+ }
+
+ public TradeResponse handleMessage(TradeRequest tradeRequest)
+ {
+ TradeResponse tradeResponse;
+ List errors = new ArrayList();
+ if (creditCheckService.canExecute(tradeRequest, errors))
+ {
+ tradeResponse = executionVenueService.executeTradeRequest(tradeRequest);
+ }
+ else
+ {
+ tradeResponse = new TradeResponse();
+ tradeResponse.setError(true);
+ tradeResponse.setErrorMessage(StringUtils.arrayToCommaDelimitedString(errors.toArray()));
+
+ }
+ tradingService.processTrade(tradeRequest, tradeResponse);
+ return tradeResponse;
+ }
+
+
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/CreditCheckService.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/CreditCheckService.java
new file mode 100644
index 0000000..9bbaed1
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/CreditCheckService.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.service;
+
+import java.util.List;
+
+import org.springframework.amqp.rabbit.stocks.domain.TradeRequest;
+
+/**
+ * Credit service to see if the incoming trade can be processed. If it can not be processed
+ * a false value is returned and the error list contains information as to what went wrong.
+ *
+ * @author Mark Pollack
+ *
+ */
+public interface CreditCheckService {
+
+ boolean canExecute(TradeRequest tradeRequest, List errors);
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/ExecutionVenueService.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/ExecutionVenueService.java
new file mode 100644
index 0000000..7896ca9
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/ExecutionVenueService.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.service;
+
+import org.springframework.amqp.rabbit.stocks.domain.TradeRequest;
+import org.springframework.amqp.rabbit.stocks.domain.TradeResponse;
+
+/**
+ * Executes the trade request, creating a Trade response. See the code flow in {@link ServerHandler} for
+ * its usage.
+ * @author Mark Pollack
+ *
+ */
+public interface ExecutionVenueService {
+
+ TradeResponse executeTradeRequest(TradeRequest request);
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/TradingService.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/TradingService.java
new file mode 100644
index 0000000..e3889ad
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/TradingService.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.service;
+
+import org.springframework.amqp.rabbit.stocks.domain.TradeRequest;
+import org.springframework.amqp.rabbit.stocks.domain.TradeResponse;
+
+/**
+ * Trading Service to process trade requests and response. This is the place to perform
+ * any trade processing after executions. See code flow in {@link ServerHandler}.
+ *
+ * @author Mark Pollack
+ *
+ */
+public interface TradingService {
+
+ void processTrade(TradeRequest request, TradeResponse response);
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/stubs/CreditCheckServiceStub.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/stubs/CreditCheckServiceStub.java
new file mode 100644
index 0000000..4507fe1
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/stubs/CreditCheckServiceStub.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.service.stubs;
+
+import java.util.List;
+
+import org.springframework.amqp.rabbit.stocks.domain.TradeRequest;
+import org.springframework.amqp.rabbit.stocks.service.CreditCheckService;
+
+/***
+ * An implementation that always returns true (just like real-life for a mortgage check :)
+ *
+ * @author Mark Pollack
+ *
+ */
+public class CreditCheckServiceStub implements CreditCheckService {
+
+ public boolean canExecute(TradeRequest tradeRequest, List errors) {
+ return true;
+ }
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/stubs/ExecutionVenueServiceStub.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/stubs/ExecutionVenueServiceStub.java
new file mode 100644
index 0000000..e7d6b06
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/stubs/ExecutionVenueServiceStub.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.service.stubs;
+
+import java.math.BigDecimal;
+import java.util.Random;
+import java.util.UUID;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.amqp.rabbit.stocks.domain.TradeRequest;
+import org.springframework.amqp.rabbit.stocks.domain.TradeResponse;
+import org.springframework.amqp.rabbit.stocks.service.ExecutionVenueService;
+
+/**
+ * Execute the trade, setting the execution price to changing value in line with what the market data feed is producing.
+ *
+ * @author Mark Pollack
+ *
+ */
+public class ExecutionVenueServiceStub implements ExecutionVenueService {
+
+ private static Log log = LogFactory.getLog(ExecutionVenueServiceStub.class);
+
+ private Random random = new Random();
+
+ public TradeResponse executeTradeRequest(TradeRequest request) {
+ TradeResponse response = new TradeResponse();
+ response.setOrderType(request.getOrderType());
+ response.setPrice(calculatePrice(request.getTicker(), request.getQuantity(), request.getOrderType(), request.getPrice(), request.getUserName()));
+ response.setQuantity(request.getQuantity());
+ response.setTicker(request.getTicker());
+ response.setConfirmationNumber(UUID.randomUUID().toString());
+
+
+ try {
+ log.info("Sleeping 2 seconds to simulate processing..");
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {
+ log.error("Didn't finish sleeping", e);
+ }
+ return response;
+ }
+
+ private BigDecimal calculatePrice(String ticker, long quantity,
+ String orderType, BigDecimal price, String userName) {
+ // provide as sophisticated implementation...for now all the same price.
+ if (orderType.compareTo("LIMIT") == 0)
+ {
+ return price;
+ }
+ else
+ {
+ //in line with market data implementation
+ return new BigDecimal(22 + Math.abs(gaussian()));
+ }
+ }
+
+ private double gaussian() {
+ return random.nextGaussian();
+ }
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/stubs/TradingServiceStub.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/stubs/TradingServiceStub.java
new file mode 100644
index 0000000..1cbe3bb
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/service/stubs/TradingServiceStub.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.service.stubs;
+
+import org.springframework.amqp.rabbit.stocks.domain.TradeRequest;
+import org.springframework.amqp.rabbit.stocks.domain.TradeResponse;
+import org.springframework.amqp.rabbit.stocks.service.TradingService;
+
+/**
+ * No-op implementation
+ * @author Mark Pollack
+ *
+ */
+public class TradingServiceStub implements TradingService {
+
+ public void processTrade(TradeRequest request, TradeResponse response) {
+
+ }
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/ui/StockController.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/ui/StockController.java
new file mode 100644
index 0000000..4af6719
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/ui/StockController.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.ui;
+
+import org.springframework.amqp.rabbit.stocks.domain.Quote;
+import org.springframework.amqp.rabbit.stocks.domain.TradeRequest;
+import org.springframework.amqp.rabbit.stocks.domain.TradeResponse;
+import org.springframework.amqp.rabbit.stocks.gateway.StockServiceGateway;
+
+/**
+ * Basic controller for the UI.
+ * TODO: Fix that the UI can receive events before it's panel has been initialized.
+ *
+ * @author Mark Pollack
+ * @author Mark Fisher
+ */
+public class StockController {
+
+ private StockPanel stockPanel;
+
+ private StockServiceGateway stockServiceGateway;
+
+
+ public StockPanel getStockPanel() {
+ return stockPanel;
+ }
+
+ public void setStockPanel(StockPanel stockPanel) {
+ this.stockPanel = stockPanel;
+ }
+
+ public StockServiceGateway getStockServiceGateway() {
+ return stockServiceGateway;
+ }
+
+ public void setStockServiceGateway(StockServiceGateway stockServiceGateway) {
+ this.stockServiceGateway = stockServiceGateway;
+ }
+
+ // "Actions"
+
+ public void sendTradeRequest(String text) {
+ String[] tokens = text.split("\\s");
+ String quantityString = tokens[0];
+ String ticker = tokens[1];
+ int quantity = Integer.parseInt(quantityString);
+ TradeRequest tr = new TradeRequest();
+ tr.setAccountName("ACCT-123");
+ tr.setBuyRequest(true);
+ tr.setOrderType("MARKET");
+ tr.setTicker(ticker);
+ tr.setQuantity(quantity);
+ tr.setRequestId("REQ-1");
+ tr.setUserName("Joe Trader");
+ tr.setUserName("Joe");
+ stockServiceGateway.send(tr);
+ }
+
+ public void displayQuote(Quote quote) {
+ //TODO race condition with message delivery and initalization... use @Configurable?
+ if (stockPanel != null) {
+ stockPanel.displayQuote(quote);
+ }
+ }
+
+ public void UpdateTrade(TradeResponse tradeResponse) {
+ stockPanel.update(tradeResponse);
+ }
+
+}
diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/ui/StockPanel.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/ui/StockPanel.java
new file mode 100644
index 0000000..66626fa
--- /dev/null
+++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/ui/StockPanel.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks.ui;
+
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.text.DecimalFormat;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.amqp.rabbit.stocks.domain.Quote;
+import org.springframework.amqp.rabbit.stocks.domain.Stock;
+import org.springframework.amqp.rabbit.stocks.domain.TradeResponse;
+
+import com.jgoodies.forms.layout.CellConstraints;
+import com.jgoodies.forms.layout.FormLayout;
+
+/**
+ * A typical poor mans UI to drive the application.
+ *
+ * @author Mark Pollack
+ * @author Mark Fisher
+ */
+public class StockPanel extends JPanel {
+
+ private static Log log = LogFactory.getLog(StockPanel.class);
+
+ private JTextField tradeRequestTextField;
+ private JButton tradeRequestButton;
+ private JTextArea marketDataTextArea;
+ private StockController stockController;
+
+ private DecimalFormat frmt = new DecimalFormat("$0.00");
+
+ public StockPanel(StockController controller) {
+ this.stockController = controller;
+ controller.setStockPanel(this);
+ this.setBorder(BorderFactory.createTitledBorder("Stock Form"));
+
+ FormLayout formLayout = new FormLayout("pref, 150dlu", // columns
+ "pref, fill:100dlu:grow"); // rows
+ setLayout(formLayout);
+ CellConstraints c = new CellConstraints();
+
+ tradeRequestButton = new JButton("Send Trade Request");
+ add(tradeRequestButton, c.xy(1, 1));
+
+ tradeRequestTextField = new JTextField("");
+ add(tradeRequestTextField, c.xy(2, 1));
+
+ add(new JLabel("Market Data"), c.xy(1, 2));
+
+ marketDataTextArea = new JTextArea();
+ JScrollPane sp = new JScrollPane(marketDataTextArea);
+ sp.setSize(200, 300);
+
+ add(sp, c.xy(2, 2));
+
+ tradeRequestTextField.addFocusListener(new FocusListener() {
+ public void focusLost(FocusEvent e) {
+ }
+ public void focusGained(FocusEvent e) {
+ tradeRequestTextField.setText("");
+ tradeRequestTextField.setForeground(Color.BLACK);
+ }
+ });
+
+ tradeRequestButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ sendTradeRequest();
+ }
+ });
+ }
+
+ private void sendTradeRequest() {
+ try {
+ stockController.sendTradeRequest(tradeRequestTextField.getText());
+ tradeRequestTextField.setForeground(Color.GRAY);
+ tradeRequestTextField.setText("Request Pending...");
+ log.info("Sent trade request.");
+ }
+ catch (Exception ex) {
+ tradeRequestTextField.setForeground(Color.RED);
+ tradeRequestTextField.setText("Required Format: 100 TCKR");
+ }
+ }
+
+ public static void main(String[] a) {
+ JFrame f = new JFrame("Rabbit Stock Demo");
+ f.setDefaultCloseOperation(2);
+ f.add(new StockPanel(new StockController()));
+ f.pack();
+ f.setVisible(true);
+ }
+
+ public void displayQuote(final Quote quote) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ Stock stock = quote.getStock();
+ marketDataTextArea.append(stock.getTicker() + " " + quote.getPrice() + "\n");
+ }
+ });
+ }
+
+ public void update(final TradeResponse tradeResponse) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ tradeRequestTextField.setForeground(Color.GREEN);
+ tradeRequestTextField.setText("Confirmed. "
+ + tradeResponse.getTicker() + " "
+ + frmt.format(tradeResponse.getPrice().doubleValue()));
+ }
+ });
+ }
+
+}
diff --git a/stocks/src/main/resources/client-bootstrap-config.xml b/stocks/src/main/resources/client-bootstrap-config.xml
new file mode 100644
index 0000000..3a2f9cd
--- /dev/null
+++ b/stocks/src/main/resources/client-bootstrap-config.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/stocks/src/main/resources/client-handlers.xml b/stocks/src/main/resources/client-handlers.xml
new file mode 100644
index 0000000..94a5d61
--- /dev/null
+++ b/stocks/src/main/resources/client-handlers.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/stocks/src/main/resources/client-messaging.xml b/stocks/src/main/resources/client-messaging.xml
new file mode 100644
index 0000000..6c942a6
--- /dev/null
+++ b/stocks/src/main/resources/client-messaging.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/stocks/src/main/resources/client.properties b/stocks/src/main/resources/client.properties
new file mode 100644
index 0000000..077f318
--- /dev/null
+++ b/stocks/src/main/resources/client.properties
@@ -0,0 +1 @@
+stocks.quote.pattern=app.stock.quotes.nasdaq.*
\ No newline at end of file
diff --git a/stocks/src/main/resources/server-bootstrap-config.xml b/stocks/src/main/resources/server-bootstrap-config.xml
new file mode 100644
index 0000000..129730d
--- /dev/null
+++ b/stocks/src/main/resources/server-bootstrap-config.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/stocks/src/main/resources/server-handlers.xml b/stocks/src/main/resources/server-handlers.xml
new file mode 100644
index 0000000..3f97d4d
--- /dev/null
+++ b/stocks/src/main/resources/server-handlers.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/stocks/src/main/resources/server-jmx.xml b/stocks/src/main/resources/server-jmx.xml
new file mode 100644
index 0000000..c61a8b8
--- /dev/null
+++ b/stocks/src/main/resources/server-jmx.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/stocks/src/main/resources/server-messaging.xml b/stocks/src/main/resources/server-messaging.xml
new file mode 100644
index 0000000..ec76190
--- /dev/null
+++ b/stocks/src/main/resources/server-messaging.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/stocks/src/main/resources/server-services.xml b/stocks/src/main/resources/server-services.xml
new file mode 100644
index 0000000..dea7027
--- /dev/null
+++ b/stocks/src/main/resources/server-services.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/stocks/src/site/site.xml b/stocks/src/site/site.xml
new file mode 100644
index 0000000..4bba09c
--- /dev/null
+++ b/stocks/src/site/site.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ Spring Batch: ${project.name}
+ index.html
+
+
+
+ org.springframework.maven.skins
+ maven-spring-skin
+ 1.0.5
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/stocks/src/test/java/org/springframework/amqp/rabbit/stocks/Client.java b/stocks/src/test/java/org/springframework/amqp/rabbit/stocks/Client.java
new file mode 100644
index 0000000..94131f7
--- /dev/null
+++ b/stocks/src/test/java/org/springframework/amqp/rabbit/stocks/Client.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks;
+
+
+import javax.swing.JFrame;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.junit.Test;
+import org.springframework.amqp.rabbit.stocks.ui.StockController;
+import org.springframework.amqp.rabbit.stocks.ui.StockPanel;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * Main client application, can run as app or unit test.
+ * @author Mark Pollack
+ *
+ */
+public class Client {
+
+ private static Log log = LogFactory.getLog(Client.class);
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) {
+ new Client().run();
+ }
+
+ @Test
+ public void run() {
+
+ ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext("client-bootstrap-config.xml");
+
+ StockController controller = ac.getBean(StockController.class);
+
+ JFrame f = new JFrame("Rabbit Stock Demo");
+ f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ //TODO consider @Configurable
+ f.add(new StockPanel(controller));
+ f.pack();
+ f.setVisible(true);
+ }
+
+
+
+
+}
diff --git a/stocks/src/test/java/org/springframework/amqp/rabbit/stocks/Server.java b/stocks/src/test/java/org/springframework/amqp/rabbit/stocks/Server.java
new file mode 100644
index 0000000..3c55c9c
--- /dev/null
+++ b/stocks/src/test/java/org/springframework/amqp/rabbit/stocks/Server.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2002-2010 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.amqp.rabbit.stocks;
+
+import org.junit.Test;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * Server application than can be run as an app or unit test.
+ *
+ * @author Mark Pollack
+ */
+public class Server {
+
+ public static void main(String[] args) {
+ new Server().run();
+ }
+
+ @Test
+ public void run() {
+ new ClassPathXmlApplicationContext("server-bootstrap-config.xml");
+ }
+
+}
diff --git a/stocks/src/test/resources/commons-logging.properties b/stocks/src/test/resources/commons-logging.properties
new file mode 100644
index 0000000..51cd942
--- /dev/null
+++ b/stocks/src/test/resources/commons-logging.properties
@@ -0,0 +1,7 @@
+# Use Log4j
+priority=1
+org.apache.commons.logging.LogFactory=org.apache.commons.logging.impl.LogFactoryImpl
+org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
+
+# Configuration file of the log
+#log4j.configuration=file:log4j-rabbit-stocks.properties
diff --git a/stocks/src/test/resources/log4j.properties b/stocks/src/test/resources/log4j.properties
new file mode 100644
index 0000000..9687c70
--- /dev/null
+++ b/stocks/src/test/resources/log4j.properties
@@ -0,0 +1,11 @@
+log4j.rootCategory=INFO, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+
+#log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
+log4j.appender.stdout.layout.ConversionPattern=%-5p [%40.40c{4}]: %m%n
+
+log4j.category.org.springframework.amqp.rabbit=DEBUG
+log4j.category.org.springframework.beans.factory=INFO
+