Permalink
Browse files

Added example of structural command validation using JSR-303

Some commands now have JSR-303 annotations, causing them to be rejected if they
are malformed. The BeanValidationInterceptor is configured as a Dispatch
Interceptor, causing the commandBus.dispatch method to throw an exception (thus
not invoking the callback) before the command is actually being processed.
  • Loading branch information...
1 parent eff02c9 commit 76aecce464dd443232542f3a442381f8075b2f7e @abuijze abuijze committed Mar 30, 2013
View
5 core-api/pom.xml
@@ -18,6 +18,11 @@
<artifactId>axon-core</artifactId>
<version>${axon.version}</version>
</dependency>
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <version>1.0.0.GA</version>
+ </dependency>
</dependencies>
</project>
View
4 ...rc/main/java/org/axonframework/samples/trader/api/orders/trades/AbstractOrderCommand.java
@@ -18,6 +18,8 @@
import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier;
+import javax.validation.constraints.Min;
+
/**
* <p>Abstract parent class for all commands that are order related.</p>
*
@@ -29,7 +31,9 @@
@TargetAggregateIdentifier
private OrderBookId orderBookId;
private TransactionId transactionId;
+ @Min(0)
private long tradeCount;
+ @Min(0)
private long itemPrice;
private OrderId orderId;
View
3 ...src/main/java/org/axonframework/samples/trader/api/portfolio/cash/DepositCashCommand.java
@@ -18,6 +18,8 @@
import org.axonframework.samples.trader.api.orders.trades.PortfolioId;
+import javax.validation.constraints.Min;
+
/**
* Adding cash to your Portfolio through a deposit
*
@@ -26,6 +28,7 @@
public class DepositCashCommand {
private PortfolioId portfolioIdentifier;
+ @Min(0)
private long moneyToAddInCents;
public DepositCashCommand(PortfolioId portfolioIdentifier, long moneyToAddInCents) {
View
3 ...src/main/java/org/axonframework/samples/trader/api/portfolio/cash/ReserveCashCommand.java
@@ -19,13 +19,16 @@
import org.axonframework.samples.trader.api.orders.trades.PortfolioId;
import org.axonframework.samples.trader.api.orders.trades.TransactionId;
+import javax.validation.constraints.Min;
+
/**
* @author Jettro Coenradie
*/
public class ReserveCashCommand {
private PortfolioId portfolioIdentifier;
private TransactionId transactionIdentifier;
+ @Min(0)
private long amountOfMoneyToReserve;
public ReserveCashCommand(PortfolioId portfolioIdentifier,
View
3 ...rc/main/java/org/axonframework/samples/trader/api/portfolio/cash/WithdrawCashCommand.java
@@ -18,12 +18,15 @@
import org.axonframework.samples.trader.api.orders.trades.PortfolioId;
+import javax.validation.constraints.Min;
+
/**
* @author Jettro Coenradie
*/
public class WithdrawCashCommand {
private PortfolioId portfolioIdentifier;
+ @Min(0)
private long amountToPayInCents;
public WithdrawCashCommand(PortfolioId portfolioIdentifier, long amountToPayInCents) {
View
3 ...java/org/axonframework/samples/trader/api/portfolio/stock/AddItemsToPortfolioCommand.java
@@ -19,6 +19,8 @@
import org.axonframework.samples.trader.api.orders.trades.OrderBookId;
import org.axonframework.samples.trader.api.orders.trades.PortfolioId;
+import javax.validation.constraints.Min;
+
/**
* Try to add new items for a specific OrderBook to the portfolio.
*
@@ -28,6 +30,7 @@
private PortfolioId portfolioIdentifier;
private OrderBookId orderBookIdentifier;
+ @Min(0)
private long amountOfItemsToAdd;
/**
View
5 ...api/src/main/java/org/axonframework/samples/trader/api/users/AuthenticateUserCommand.java
@@ -16,12 +16,17 @@
package org.axonframework.samples.trader.api.users;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
/**
* @author Jettro Coenradie
*/
public class AuthenticateUserCommand {
private final String userName;
+ @NotNull
+ @Size(min = 3)
private final char[] password;
public AuthenticateUserCommand(String userName, char[] password) {
View
7 core-api/src/main/java/org/axonframework/samples/trader/api/users/CreateUserCommand.java
@@ -18,15 +18,22 @@
import org.axonframework.common.Assert;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
/**
* Command to create a new user.
*
* @author Jettro Coenradie
*/
public class CreateUserCommand {
private UserId userId;
+ @NotNull
+ @Size(min = 3)
private String username;
private String name;
+ @NotNull
+ @Size(min = 3)
private String password;
public CreateUserCommand(UserId userId, String name, String username, String password) {
View
6 infrastructure/src/main/resources/META-INF/spring/cqrs-infrastructure-context.xml
@@ -27,7 +27,11 @@
<axon:event-bus id="eventBus"/>
- <axon:command-bus id="commandBus"/>
+ <axon:command-bus id="commandBus">
+ <axon:dispatchInterceptors>
+ <bean class="org.axonframework.commandhandling.interceptors.BeanValidationInterceptor"/>
+ </axon:dispatchInterceptors>
+ </axon:command-bus>
<bean id="eventStore" class="org.axonframework.eventstore.mongo.MongoEventStore">
<constructor-arg ref="mongoTemplate"/>
View
6 web-ui/pom.xml
@@ -140,6 +140,12 @@
<version>1.1.2</version>
</dependency>
<dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>2.5</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
View
12 web-ui/src/main/java/org/axonframework/samples/trader/webui/rest/RestController.java
@@ -19,6 +19,7 @@
import com.thoughtworks.xstream.XStream;
import org.axonframework.commandhandling.CommandBus;
import org.axonframework.commandhandling.GenericCommandMessage;
+import org.axonframework.commandhandling.StructuralCommandValidationFailedException;
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;
@@ -32,8 +33,10 @@
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import javax.servlet.http.HttpServletResponse;
/**
* Very generic controller supporting the sending of commands in an XStream serialized format. This controller also
@@ -44,6 +47,7 @@
@Controller
@RequestMapping("/rest")
public class RestController {
+
private static final Logger logger = LoggerFactory.getLogger(RestController.class);
private CommandBus commandBus;
private PortfolioQueryRepository portfolioQueryRepository;
@@ -53,7 +57,8 @@
@SuppressWarnings("SpringJavaAutowiringInspection")
@Autowired
- public RestController(CommandBus commandBus, PortfolioQueryRepository portfolioQueryRepository, OrderBookQueryRepository orderBookQueryRepository) {
+ public RestController(CommandBus commandBus, PortfolioQueryRepository portfolioQueryRepository,
+ OrderBookQueryRepository orderBookQueryRepository) {
this.portfolioQueryRepository = portfolioQueryRepository;
this.orderBookQueryRepository = orderBookQueryRepository;
this.xStream = new XStream();
@@ -63,10 +68,12 @@ public RestController(CommandBus commandBus, PortfolioQueryRepository portfolioQ
@RequestMapping(value = "/command", method = RequestMethod.POST)
public
@ResponseBody
- String mappedCommand(String command) {
+ String mappedCommand(String command, HttpServletResponse response) throws IOException {
try {
Object actualCommand = xStream.fromXML(command);
commandBus.dispatch(new GenericCommandMessage<Object>(actualCommand));
+ } catch (StructuralCommandValidationFailedException e) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "This is an invalid request.");
} catch (Exception e) {
logger.error("Problem whils deserializing an xml: {}", command, e);
return "ERROR - " + e.getMessage();
@@ -109,5 +116,4 @@ String obtainOrderBooks() {
return xStream.toXML(orderBookEntries);
}
-
}
View
9 ...in/java/org/axonframework/samples/trader/webui/security/TraderAuthenticationProvider.java
@@ -18,6 +18,7 @@
import org.axonframework.commandhandling.CommandBus;
import org.axonframework.commandhandling.GenericCommandMessage;
+import org.axonframework.commandhandling.StructuralCommandValidationFailedException;
import org.axonframework.commandhandling.callbacks.FutureCallback;
import org.axonframework.samples.trader.api.users.AuthenticateUserCommand;
import org.axonframework.samples.trader.api.users.UserAccount;
@@ -69,7 +70,13 @@ public Authentication authenticate(Authentication authentication) throws Authent
String password = String.valueOf(token.getCredentials());
FutureCallback<UserAccount> accountCallback = new FutureCallback<UserAccount>();
AuthenticateUserCommand command = new AuthenticateUserCommand(username, password.toCharArray());
- commandBus.dispatch(new GenericCommandMessage<AuthenticateUserCommand>(command), accountCallback);
+ try {
+ commandBus.dispatch(new GenericCommandMessage<AuthenticateUserCommand>(command), accountCallback);
+ // the bean validating interceptor is defined as a dispatch interceptor, meaning it is executed before
+ // the command is dispatched.
+ } catch (StructuralCommandValidationFailedException e) {
+ return null;
+ }
UserAccount account;
try {
account = accountCallback.get();

0 comments on commit 76aecce

Please sign in to comment.