Skip to content
This repository has been archived by the owner on Dec 14, 2021. It is now read-only.

Commit

Permalink
Added the possibility to remotely shoot in commands using xstream and…
Browse files Browse the repository at this point in the history
… a rest like interface.
  • Loading branch information
jettro committed Mar 2, 2012
1 parent c88237b commit ce38215
Show file tree
Hide file tree
Showing 9 changed files with 365 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ build
*.iml
target/
out/*
**/rebel.xml
/**/rebel.xml
18 changes: 18 additions & 0 deletions web-ui/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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()"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}

}
Original file line number Diff line number Diff line change
@@ -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
}
}
}
Original file line number Diff line number Diff line change
@@ -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
}
}

Loading

0 comments on commit ce38215

Please sign in to comment.