Permalink
Browse files

Added the possibility to remotely shoot in commands using xstream and…

… a rest like interface.
  • Loading branch information...
jettro committed Mar 2, 2012
1 parent c88237b commit ce38215d5acdca0cdfd988f647675fbcdea85efc
View
@@ -11,4 +11,4 @@ build
*.iml
target/
out/*
-**/rebel.xml
+/**/rebel.xml
View
@@ -122,6 +122,24 @@
<version>${spring.security.version}</version>
</dependency>
+ <dependency>
+ <groupId>com.thoughtworks.xstream</groupId>
+ <artifactId>xstream</artifactId>
+ <version>1.3.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy-all</artifactId>
+ <version>1.8.6</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.groovy.modules.http-builder</groupId>
+ <artifactId>http-builder</artifactId>
+ <version>0.5.2</version>
+ <scope>test</scope>
+ </dependency>
+
<!-- web -->
<dependency>
<groupId>javax.servlet</groupId>
@@ -109,12 +109,18 @@ public void createItems() {
AggregateIdentifier buyer1 = createuser("Buyer One", "buyer1");
AggregateIdentifier buyer2 = createuser("Buyer two", "buyer2");
AggregateIdentifier buyer3 = createuser("Buyer three", "buyer3");
- AggregateIdentifier admin1 = createuser("Admin One", "admin1");
+ AggregateIdentifier buyer4 = createuser("Buyer four", "buyer4");
+ AggregateIdentifier buyer5 = createuser("Buyer four", "buyer5");
+ AggregateIdentifier buyer6 = createuser("Buyer four", "buyer6");
createCompanies(buyer1);
createOrderBooks();
addMoney(buyer1, 100000);
- addItems(buyer2, "Philips", 1000l);
+ addItems(buyer2, "Philips", 10000l);
+ addMoney(buyer3, 100000);
+ addItems(buyer4, "Shell", 10000l);
+ addMoney(buyer5, 100000);
+ addItems(buyer6, "Bp", 10000l);
eventStore.ensureIndexes();
}
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2012. Axon Framework
+ *
+ * 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.axonframework.samples.trader.webui.rest;
+
+import com.thoughtworks.xstream.XStream;
+import org.axonframework.commandhandling.CommandBus;
+import org.axonframework.samples.trader.query.orderbook.OrderBookEntry;
+import org.axonframework.samples.trader.query.orderbook.repositories.OrderBookQueryRepository;
+import org.axonframework.samples.trader.query.portfolio.PortfolioEntry;
+import org.axonframework.samples.trader.query.portfolio.repositories.PortfolioQueryRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Very generic controller supporting the sending of commands in an XStream serialized format. This controller also
+ * contains a few methods to obtain data in XStream format.
+ *
+ * @author Jettro Coenradie
+ */
+@Controller
+@RequestMapping("/rest")
+public class RestController {
+ private static final Logger logger = LoggerFactory.getLogger(RestController.class);
+ private CommandBus commandBus;
+ private PortfolioQueryRepository portfolioQueryRepository;
+ private OrderBookQueryRepository orderBookQueryRepository;
+
+ private XStream xStream;
+
+ @SuppressWarnings("SpringJavaAutowiringInspection")
+ @Autowired
+ public RestController(CommandBus commandBus, PortfolioQueryRepository portfolioQueryRepository, OrderBookQueryRepository orderBookQueryRepository) {
+ this.portfolioQueryRepository = portfolioQueryRepository;
+ this.orderBookQueryRepository = orderBookQueryRepository;
+ this.xStream = new XStream();
+ this.commandBus = commandBus;
+ }
+
+ @RequestMapping(value = "/command", method = RequestMethod.POST)
+ public
+ @ResponseBody
+ String mappedCommand(String command) {
+ try {
+ Object actualCommand = xStream.fromXML(command);
+ commandBus.dispatch(actualCommand);
+ } catch (Exception e) {
+ logger.error("Problem whils deserializing an xml: {}",command, e );
+ return "ERROR - " + e.getMessage();
+ }
+
+ return "OK";
+ }
+
+ @RequestMapping("/portfolio")
+ public @ResponseBody String obtainPortfolios() {
+ Iterable<PortfolioEntry> all = portfolioQueryRepository.findAll();
+ List<PortfolioEntry> portfolioEntries = new ArrayList<PortfolioEntry>();
+ for(PortfolioEntry entry: all) {
+ portfolioEntries.add(entry);
+ }
+
+ return xStream.toXML(portfolioEntries);
+ }
+
+ @RequestMapping("/portfolio/{identifier}")
+ public @ResponseBody String obtainPortfolio(@PathVariable String identifier) {
+ PortfolioEntry entry = portfolioQueryRepository.findOne(identifier);
+
+ return xStream.toXML(entry);
+ }
+
+ @RequestMapping("/orderbook")
+ public @ResponseBody String obtainOrderBooks() {
+ Iterable<OrderBookEntry> all = orderBookQueryRepository.findAll();
+ List<OrderBookEntry> orderBookEntries = new ArrayList<OrderBookEntry>();
+ for(OrderBookEntry entry: all) {
+ orderBookEntries.add(entry);
+ }
+
+ return xStream.toXML(orderBookEntries);
+ }
+
+}
@@ -30,6 +30,7 @@
<http pattern="/favicon.ico" security="none"/>
<http pattern="/style/**" security="none"/>
<http pattern="/login.jsp*" security="none"/>
+ <http pattern="/rest/**" security="none"/>
<http auto-config='true' use-expressions="true">
<intercept-url pattern="/index.html" access="isAnonymous() or isAuthenticated()"/>
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2012. Axon Framework
+ *
+ * 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.axonframework.samples.trader.test
+
+import org.axonframework.domain.StringAggregateIdentifier
+import org.axonframework.samples.trader.orders.api.transaction.StartBuyTransactionCommand
+import org.axonframework.samples.trader.orders.api.transaction.StartSellTransactionCommand
+import org.axonframework.samples.trader.query.portfolio.PortfolioEntry
+
+/**
+ * Class used to create an order based on the provided Profile. If the profile has money we place buy orders, if
+ * the profile is almost out of money we start selling stuff.
+ *
+ * @author Jettro Coenradie
+ */
+class CommandCreator {
+ Random randomFactory = new Random()
+ def orderBookEntries
+ def command
+
+ def CommandCreator(orderBookEntries) {
+ this.orderBookEntries = orderBookEntries
+ }
+
+ def createCommand(PortfolioEntry portfolio) {
+ if (portfolio.amountOfMoney-portfolio.reservedAmountOfMoney > 10000) {
+ command = new StartBuyTransactionCommand(
+ new StringAggregateIdentifier(obtainRandomOrderBook()),
+ new StringAggregateIdentifier(portfolio.identifier),
+ randomFactory.nextInt(50)+1,
+ randomFactory.nextInt(10)+1)
+ } else {
+ def availableOrderBook = obtainAvailableOrderBook(portfolio)
+ if (availableOrderBook) {
+ command = new StartSellTransactionCommand(
+ new StringAggregateIdentifier(availableOrderBook[0]),
+ new StringAggregateIdentifier(portfolio.identifier),
+ availableOrderBook[1],
+ randomFactory.nextInt(10)+1)
+ }
+ }
+
+ return command
+ }
+
+ private def obtainRandomOrderBook() {
+ return orderBookEntries[randomFactory.nextInt(orderBookEntries.size())]
+ }
+
+ private def obtainAvailableOrderBook(PortfolioEntry portfolioEntry) {
+ def amountOfOrderBooks = portfolioEntry.itemsInPossession.size()
+ def counterOrderBook = 0
+ while (counterOrderBook < amountOfOrderBooks) {
+ def identifier = portfolioEntry.itemsInPossession.keySet().toArray()[counterOrderBook]
+ def amountAvailable = portfolioEntry.itemsInPossession[identifier].amount
+ def reserved = (portfolioEntry.itemsReserved[identifier]) ? portfolioEntry.itemsReserved[identifier].amount : 0
+ if (amountAvailable > reserved) {
+ def amountToSell = (amountAvailable - reserved > 50) ? randomFactory.nextInt(50)+1 : amountAvailable - reserved
+ return [identifier, amountToSell]
+ }
+ counterOrderBook++
+ }
+ return null
+ }
+
+}
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012. Axon Framework
+ *
+ * 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.axonframework.samples.trader.test
+
+import com.thoughtworks.xstream.XStream
+import groovyx.net.http.ContentType
+import groovyx.net.http.HTTPBuilder
+import org.axonframework.samples.trader.query.portfolio.PortfolioEntry
+import org.axonframework.samples.trader.query.orderbook.OrderBookEntry
+
+/**
+ * This class serializes the command using xstream and sends it to the server.
+ *
+ * @author Jettro Coenradie
+ */
+class CommandSender {
+ def http = new HTTPBuilder('http://localhost:8080/')
+ def requestContentType = ContentType.URLENC
+ XStream xStream = new XStream()
+
+ def sendCommand(commandToSend) {
+ def xml = xStream.toXML(commandToSend)
+ def postBody = [command: xml]
+
+ http.post(path: 'rest/command', body: postBody, requestContentType: requestContentType) { resp ->
+ assert resp.statusLine.statusCode == 200
+ }
+ }
+
+ def obtainPortfolios() {
+ http.get(path: 'rest/portfolio', requestContentType: requestContentType) { resp, reader ->
+ def xmlData = reader.text
+ List<PortfolioEntry> portfolios = xStream.fromXML(xmlData)
+ return portfolios
+ }
+ }
+
+ def obtainOrderBooks() {
+ http.get(path: 'rest/orderbook', requestContentType: requestContentType) { resp, reader ->
+ def xmlData = reader.text
+ List<OrderBookEntry> orderBookEntries = xStream.fromXML(xmlData)
+ return orderBookEntries
+ }
+ }
+
+ def obtainPortfolio(identifier) {
+ http.get(path: 'rest/portfolio/' + identifier, requestContentType: requestContentType) { resp, reader ->
+ def xmlData = reader.text
+ PortfolioEntry entry = xStream.fromXML(xmlData)
+ return entry
+ }
+ }
+}
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2012. Axon Framework
+ *
+ * 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.axonframework.samples.trader.test
+
+/**
+ * For some reason groovy finds the log4j.xml from the httpbuilder jar. Therefore we configure the logging to come
+ * from a log4j.properties file on the classpath.
+ *
+ * -Dlog4j.debug=true -Dlog4j.configuration=log4j.properties
+ *
+ * @author Jettro Coenradie
+ */
+
+import org.axonframework.samples.trader.query.portfolio.PortfolioEntry
+
+def commandSender = new CommandSender()
+
+def portfolios = []
+commandSender.obtainPortfolios().each() {
+ portfolios.add it.identifier
+}
+
+def companyNames = [:]
+def orderBooks = []
+commandSender.obtainOrderBooks().each() {
+ orderBooks.add it.identifier
+ companyNames.put(it.identifier, it.companyName)
+}
+def commandCreator = new CommandCreator(orderBooks)
+
+def numUsers = portfolios.size()
+def numUser = 1;
+
+for (int i = 0; i < 1000; i++) {
+ def portfolioIdentifier = portfolios[numUser - 1]
+ PortfolioEntry portfolio = commandSender.obtainPortfolio(portfolioIdentifier)
+ def command = commandCreator.createCommand(portfolio)
+
+ println "${portfolio.userName} # ${command.tradeCount} \$ ${command.itemPrice} ${companyNames[command.orderbookIdentifier.asString()]}"
+
+ commandSender.sendCommand(command)
+
+ if (numUser < numUsers) {
+ numUser++
+ } else {
+ numUser = 1
+ }
+}
+
Oops, something went wrong.

0 comments on commit ce38215

Please sign in to comment.