diff --git a/helloworld/.classpath b/helloworld/.classpath new file mode 100644 index 0000000..575d9ea --- /dev/null +++ b/helloworld/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/helloworld/.project b/helloworld/.project new file mode 100644 index 0000000..018645d --- /dev/null +++ b/helloworld/.project @@ -0,0 +1,29 @@ + + + spring-rabbit-helloworld + a simple Hello World example + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.springframework.ide.eclipse.core.springbuilder + + + + + org.maven.ide.eclipse.maven2Builder + + + + + + org.springframework.ide.eclipse.core.springnature + org.eclipse.jdt.core.javanature + org.maven.ide.eclipse.maven2Nature + + diff --git a/helloworld/.springBeans b/helloworld/.springBeans new file mode 100644 index 0000000..0c014a9 --- /dev/null +++ b/helloworld/.springBeans @@ -0,0 +1,20 @@ + + + 1 + + + + + + + + + + + true + false + + + + + diff --git a/helloworld/pom.xml b/helloworld/pom.xml new file mode 100644 index 0000000..3572dfa --- /dev/null +++ b/helloworld/pom.xml @@ -0,0 +1,174 @@ + + + 4.0.0 + org.springframework.samples.spring + spring-rabbit-helloworld + 1.0.0.BUILD-SNAPSHOT + jar + Spring AMQP Hello World + http://www.springframework.org + + + + + true + 3.0.3.RELEASE + 1.0.0.BUILD-SNAPSHOT + + + + strict + + false + + + + + + + + org.springframework.amqp + spring-amqp-core + ${spring.amqp.version} + + + org.springframework.amqp + spring-rabbit + ${spring.amqp.version} + + + org.springframework.amqp + spring-rabbit-admin + ${spring.amqp.version} + + + com.rabbitmq + amqp-client + 1.7.2 + + + + org.springframework + spring-test + ${spring.framework.version} + test + + + org.springframework + spring-context-support + ${spring.framework.version} + runtime + + + org.springframework + spring-aop + ${spring.framework.version} + + + org.springframework + spring-oxm + ${spring.framework.version} + + + aspectj + aspectjrt + 1.5.4 + + + aspectj + aspectjweaver + 1.5.4 + + + + + junit + junit + 4.7 + test + + + + log4j + log4j + 1.2.14 + runtime + + + + + + + + + com.springsource.bundlor + com.springsource.bundlor.maven + 1.0.0.M1B + + + maven-assembly-plugin + false + + + project + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + 1.5 + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + **/*Tests.java + + + **/Abstract*.java + + + + + + + + + Codehaus + http://repository.codehaus.org/ + + false + + + + + http://www.springframework.org/download + + staging + file:///${user.dir}/target/staging/org.springframework.batch.archetype/${pom.artifactId} + + + spring-release + Spring Release Repository + file:///${user.dir}/target/staging/release + + + spring-snapshot + Spring Snapshot Repository + file:///${user.dir}/target/staging/snapshot + + + diff --git a/helloworld/src/main/java/org/springframework/amqp/helloworld/Consumer.java b/helloworld/src/main/java/org/springframework/amqp/helloworld/Consumer.java new file mode 100644 index 0000000..078823b --- /dev/null +++ b/helloworld/src/main/java/org/springframework/amqp/helloworld/Consumer.java @@ -0,0 +1,19 @@ +package org.springframework.amqp.helloworld; + +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +public class Consumer { + + public static void main(String[] args) { + //ApplicationContext ctx = new AnnotationConfigApplicationContext(ConsumerConfiguration.class); + + + ApplicationContext ctx = new AnnotationConfigApplicationContext(RabbitConfiguration.class); + RabbitTemplate rabbitTemplate = (RabbitTemplate) ctx.getBean(RabbitTemplate.class); + + System.out.println("Received " + rabbitTemplate.receiveAndConvert()); + + } +} diff --git a/helloworld/src/main/java/org/springframework/amqp/helloworld/ConsumerConfiguration.java b/helloworld/src/main/java/org/springframework/amqp/helloworld/ConsumerConfiguration.java new file mode 100644 index 0000000..65dfa94 --- /dev/null +++ b/helloworld/src/main/java/org/springframework/amqp/helloworld/ConsumerConfiguration.java @@ -0,0 +1,20 @@ +package org.springframework.amqp.helloworld; + +import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; +import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ConsumerConfiguration extends RabbitConfiguration { + + @Bean + public SimpleMessageListenerContainer listenerContainer() { + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); + container.setConnectionFactory(connectionFactory()); + container.setQueueName(helloWorldQueue.getName()); + container.setMessageListener(new MessageListenerAdapter(new HelloWorldHandler())); + return container; + } + +} diff --git a/helloworld/src/main/java/org/springframework/amqp/helloworld/HelloWorldHandler.java b/helloworld/src/main/java/org/springframework/amqp/helloworld/HelloWorldHandler.java new file mode 100644 index 0000000..8ed9744 --- /dev/null +++ b/helloworld/src/main/java/org/springframework/amqp/helloworld/HelloWorldHandler.java @@ -0,0 +1,8 @@ +package org.springframework.amqp.helloworld; + +public class HelloWorldHandler { + + public void handleMessage(String text) { + System.out.println("Received: " + text); + } +} diff --git a/helloworld/src/main/java/org/springframework/amqp/helloworld/Producer.java b/helloworld/src/main/java/org/springframework/amqp/helloworld/Producer.java new file mode 100644 index 0000000..2068d06 --- /dev/null +++ b/helloworld/src/main/java/org/springframework/amqp/helloworld/Producer.java @@ -0,0 +1,21 @@ +package org.springframework.amqp.helloworld; + +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +public class Producer { + + /** + * @param args + */ + public static void main(String[] args) { + ApplicationContext ctx = new AnnotationConfigApplicationContext(RabbitConfiguration.class); + + RabbitTemplate rabbitTemplate = (RabbitTemplate) ctx.getBean(RabbitTemplate.class); + rabbitTemplate.convertAndSend("Hello World"); + System.out.println("Sent Hello World."); + + } + +} diff --git a/helloworld/src/main/java/org/springframework/amqp/helloworld/RabbitConfiguration.java b/helloworld/src/main/java/org/springframework/amqp/helloworld/RabbitConfiguration.java new file mode 100644 index 0000000..1db595f --- /dev/null +++ b/helloworld/src/main/java/org/springframework/amqp/helloworld/RabbitConfiguration.java @@ -0,0 +1,89 @@ +package org.springframework.amqp.helloworld; + +import javax.annotation.PostConstruct; + +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.rabbit.admin.config.AbstractRabbitConfiguration; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitTemplate; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RabbitConfiguration extends AbstractRabbitConfiguration { + + protected Queue helloWorldQueue = new Queue("hello.world.queue"); + + @Bean + public ConnectionFactory connectionFactory() { + CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost"); + connectionFactory.setUsername("guest"); + connectionFactory.setPassword("guest"); + return connectionFactory; + } + + @Override + public RabbitTemplate rabbitTemplate() { + RabbitTemplate template = new RabbitTemplate(connectionFactory()); + //The routing key is set to the name of the queue by the broker for the default exchange. + template.setDefaultRoutingKey(helloWorldQueue.getName()); + //Where we will synchronously receive messages from + template.setDefaultReceiveQueue(helloWorldQueue); + return template; + } + + @PostConstruct + private void configureBroker() { + declare(helloWorldQueue); + } + + /* + @Bean + public Queue helloWorldQueue() + { + return declare(helloWorldQueue); + }*/ + + /* + //Each queue is bound to the default direct exchange + @Bean + public Binding binding() { + return declare(new Binding(helloWorldQueue(), defaultDirectExchange())); + }*/ + + /* + @Bean + public TopicExchange helloExchange() { + return declare(new TopicExchange("hello.world.exchange")); + }*/ + + /* + + public Queue declareUniqueQueue(String namePrefix) { + Queue queue = new Queue(namePrefix + "-" + UUID.randomUUID()); + rabbitAdminTemplate().declareQueue(queue); + return queue; + } + + // if the default exchange isn't configured to your liking.... + @Bean Binding declareP2PBinding(Queue queue, DirectExchange exchange) { + return declare(new Binding(queue, exchange, queue.getName())); + } + + @Bean Binding declarePubSubBinding(String queuePrefix, FanoutExchange exchange) { + return declare(new Binding(declareUniqueQueue(queuePrefix), exchange)); + } + + @Bean Binding declarePubSubBinding(UniqueQueue uniqueQueue, TopicExchange exchange) { + return declare(new Binding(uniqueQueue, exchange)); + } + + @Bean Binding declarePubSubBinding(String queuePrefix, TopicExchange exchange, String routingKey) { + return declare(new Binding(declareUniqueQueue(queuePrefix), exchange, routingKey)); + }*/ + + + +} diff --git a/helloworld/src/site/site.xml b/helloworld/src/site/site.xml new file mode 100644 index 0000000..4bba09c --- /dev/null +++ b/helloworld/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/helloworld/src/test/resources/commons-logging.properties b/helloworld/src/test/resources/commons-logging.properties new file mode 100644 index 0000000..51cd942 --- /dev/null +++ b/helloworld/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/helloworld/src/test/resources/log4j.properties b/helloworld/src/test/resources/log4j.properties new file mode 100644 index 0000000..9687c70 --- /dev/null +++ b/helloworld/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 + diff --git a/stocks/.classpath b/stocks/.classpath new file mode 100644 index 0000000..575d9ea --- /dev/null +++ b/stocks/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/stocks/.project b/stocks/.project new file mode 100644 index 0000000..1632bb0 --- /dev/null +++ b/stocks/.project @@ -0,0 +1,29 @@ + + + spring-rabbit-stocks + sample of a basic trading app using RabbitMQ + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.springframework.ide.eclipse.core.springbuilder + + + + + org.maven.ide.eclipse.maven2Builder + + + + + + org.springframework.ide.eclipse.core.springnature + org.eclipse.jdt.core.javanature + org.maven.ide.eclipse.maven2Nature + + diff --git a/stocks/pom.xml b/stocks/pom.xml new file mode 100644 index 0000000..e782d24 --- /dev/null +++ b/stocks/pom.xml @@ -0,0 +1,185 @@ + + + 4.0.0 + org.springframework.samples.spring + spring-rabbit-stocks + 1.0.0.CI-SNAPSHOT + jar + Spring Rabbit Stocks + http://www.springframework.org + + + + + true + 3.0.3.RELEASE + 1.0.0.BUILD-SNAPSHOT + + + + strict + + false + + + + + + + + org.springframework.amqp + spring-amqp-core + ${spring.amqp.version} + + + org.springframework.amqp + spring-rabbit + ${spring.amqp.version} + + + org.springframework.amqp + spring-rabbit-admin + ${spring.amqp.version} + + + com.rabbitmq + amqp-client + 1.7.2 + + + + org.springframework + spring-test + ${spring.framework.version} + test + + + org.springframework + spring-context-support + ${spring.framework.version} + runtime + + + org.springframework + spring-aop + ${spring.framework.version} + + + org.springframework + spring-oxm + ${spring.framework.version} + + + cglib + cglib-nodep + 2.2 + + + aspectj + aspectjrt + 1.5.4 + + + aspectj + aspectjweaver + 1.5.4 + + + + + junit + junit + 4.7 + test + + + + log4j + log4j + 1.2.14 + runtime + + + + com.jgoodies + forms + 1.2.1 + + + + + + + + + com.springsource.bundlor + com.springsource.bundlor.maven + 1.0.0.M1B + + + maven-assembly-plugin + false + + + project + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + 1.5 + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + **/*Tests.java + + + **/Abstract*.java + + + + + + + + + Codehaus + http://repository.codehaus.org/ + + false + + + + + http://www.springframework.org/download + + staging + file:///${user.dir}/target/staging/org.springframework.batch.archetype/${pom.artifactId} + + + spring-release + Spring Release Repository + file:///${user.dir}/target/staging/release + + + spring-snapshot + Spring Snapshot Repository + file:///${user.dir}/target/staging/snapshot + + + diff --git a/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/config/AbstractStockAppRabbitConfiguration.java b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/config/AbstractStockAppRabbitConfiguration.java new file mode 100644 index 0000000..ab6fcd6 --- /dev/null +++ b/stocks/src/main/java/org/springframework/amqp/rabbit/stocks/config/AbstractStockAppRabbitConfiguration.java @@ -0,0 +1,98 @@ +/* + * 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; + +import org.springframework.amqp.core.TopicExchange; +import org.springframework.amqp.rabbit.admin.config.AbstractRabbitConfiguration; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.support.converter.JsonMessageConverter; +import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Provides shared configuration between Client and Server. + *

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 +