Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### IntelliJ IDEA ###
.idea
target
40 changes: 37 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,44 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<groupId>ch.engenius</groupId>
<artifactId>accounts</artifactId>
<version>1.0-SNAPSHOT</version>


<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
31 changes: 0 additions & 31 deletions src/main/java/ch/engenius/bank/Account.java

This file was deleted.

18 changes: 0 additions & 18 deletions src/main/java/ch/engenius/bank/Bank.java

This file was deleted.

97 changes: 54 additions & 43 deletions src/main/java/ch/engenius/bank/BankRunner.java
Original file line number Diff line number Diff line change
@@ -1,71 +1,82 @@
package ch.engenius.bank;


import ch.engenius.bank.domain.Bank;
import ch.engenius.bank.domain.BankAccount;
import ch.engenius.bank.exceptions.BankMoneyAmountException;

import java.math.BigDecimal;
import java.util.Random;
import java.util.HashMap;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public class BankRunner {

private static final ExecutorService executor = Executors.newFixedThreadPool(8);

private final Random random = new Random(43);
private final Bank bank = new Bank();
private static final int NUMBER_OF_BANK_ACCOUNTS = 1000;
private static final int NUMBER_OF_ITERATIONS = 1000;
private static final BigDecimal DEFAULT_DEPOSIT = BigDecimal.valueOf(100);
public static final BigDecimal TOTAL_BANK_MONEY_AMOUNT = DEFAULT_DEPOSIT.multiply(BigDecimal.valueOf(NUMBER_OF_BANK_ACCOUNTS));
private static final ExecutorService executorService = Executors.newFixedThreadPool(8);
private final Bank bank;

public BankRunner() {
HashMap<UUID, BankAccount> accounts = new HashMap<>();
this.bank = new Bank(accounts);
}

public static void main(String[] args) {
BankRunner runner = new BankRunner();
int accounts = 100;
int defaultDeposit = 1000;
int iterations = 10000;
runner.registerAccounts(accounts, defaultDeposit);
runner.sanityCheck(accounts, accounts*defaultDeposit);
runner.runBank(iterations, accounts);
runner.sanityCheck(accounts, accounts*defaultDeposit);
runner.registerAccounts();
runner.bankMoneyAmountCheck();
runner.runTransactions();
runner.bankMoneyAmountCheck();
}

private void registerAccounts() {
IntStream.range(0, NUMBER_OF_BANK_ACCOUNTS).forEach(k ->
bank.registerBankAccount(UUID.randomUUID(), BankRunner.DEFAULT_DEPOSIT)
);
}

private void runBank(int iterations, int maxAccount) {
for (int i =0; i< iterations; i++ ) {
executor.submit( ()-> runRandomOperation(maxAccount));
private void bankMoneyAmountCheck() {
BigDecimal currentTotalAmount = bank.getAccounts().values()
.stream()
.map(BankAccount::getMoneyAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);

if (TOTAL_BANK_MONEY_AMOUNT.compareTo(currentTotalAmount) != 0) {
throw new BankMoneyAmountException("Bank money amount check failed.");
}
System.out.println("Bank money amount check successful.");
}

private void runTransactions() {
for (int i = 0; i < NUMBER_OF_ITERATIONS; i++) {
executorService.submit(this::transferMoneyFromAndToRandomBankAccount);
}
executorService.shutdown();
try {
executor.shutdown();
executor.awaitTermination(100,TimeUnit.SECONDS);
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("InterruptedException occurred: " + e);
executorService.shutdownNow();
}
}

private void runRandomOperation(int maxAccount) {
double transfer = random.nextDouble()*100.0;
int accountInNumber = random.nextInt(maxAccount);
int accountOutNumber = random.nextInt(maxAccount);
Account accIn =bank.getAccount(accountInNumber);
Account accOut =bank.getAccount(accountOutNumber);
accIn.deposit(transfer);
accOut.withdraw(transfer);
}
private void transferMoneyFromAndToRandomBankAccount() {
BigDecimal transferMoneyAmount = BigDecimal.valueOf(Math.random() * DEFAULT_DEPOSIT.doubleValue());

private void registerAccounts(int number, int defaultMoney) {
for ( int i = 0; i < number; i++) {
bank.registerAccount(i, defaultMoney);
UUID accountInId = bank.getRandomBankAccountId();
UUID accountOutId = bank.getRandomBankAccountId();
while (accountInId.equals(accountOutId)) {
accountOutId = bank.getRandomBankAccountId();
}
}

private void sanityCheck( int accountMaxNumber, int totalExpectedMoney) {
BigDecimal sum = IntStream.range(0, accountMaxNumber)
.mapToObj( bank::getAccount)
.map ( Account::getMoneyAsBigDecimal)
.reduce( BigDecimal.ZERO, BigDecimal::add);

if ( sum.intValue() != totalExpectedMoney) {
throw new IllegalStateException("we got "+ sum + " != " + totalExpectedMoney +" (expected)");
}
System.out.println("sanity check OK");
bank.transferMoney(accountOutId, accountInId, transferMoneyAmount);
}


}
44 changes: 44 additions & 0 deletions src/main/java/ch/engenius/bank/domain/Bank.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ch.engenius.bank.domain;

import ch.engenius.bank.exceptions.NotFoundException;
import lombok.*;

import java.math.BigDecimal;
import java.util.*;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Bank {

private HashMap<UUID, BankAccount> accounts = new HashMap<>();

public void registerBankAccount(UUID accountNumber, BigDecimal initialMoneyAmount) {
BankAccount account = new BankAccount();
account.setMoneyAmount(initialMoneyAmount);
account.setAccountNumber(accountNumber);
this.accounts.put(accountNumber, account);
}

@Synchronized
public void transferMoney(UUID bankAccountOutId, UUID bankAccountInId, BigDecimal moneyAmount) {
BankAccount bankAccountOut = getBankAccount(bankAccountOutId);
BankAccount bankAccountIn = getBankAccount(bankAccountInId);

bankAccountOut.withdraw(moneyAmount);
bankAccountIn.deposit(moneyAmount);
}

public BankAccount getBankAccount(UUID accountNumber) {
if (this.accounts.get(accountNumber) == null) {
throw new NotFoundException("Bank account not found.");
}
return this.accounts.get(accountNumber);
}

public UUID getRandomBankAccountId() {
List<UUID> accountIds = new ArrayList<>(getAccounts().keySet());
return accountIds.get(new Random().nextInt(accountIds.size()));
}
}
30 changes: 30 additions & 0 deletions src/main/java/ch/engenius/bank/domain/BankAccount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package ch.engenius.bank.domain;

import ch.engenius.bank.exceptions.NotEnoughCreditsException;
import lombok.*;

import java.math.BigDecimal;
import java.util.UUID;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class BankAccount {

private UUID accountNumber;
private BigDecimal moneyAmount;

@Synchronized
public void withdraw(BigDecimal amount) {
if ((moneyAmount.compareTo(amount)) < 0) {
throw new NotEnoughCreditsException("Not enough credits on bank account.");
}
setMoneyAmount(moneyAmount.subtract(amount));
}

@Synchronized
public void deposit(BigDecimal amount) {
setMoneyAmount(moneyAmount.add(amount));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package ch.engenius.bank.exceptions;

public class BankMoneyAmountException extends RuntimeException {

public BankMoneyAmountException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package ch.engenius.bank.exceptions;

public class NotEnoughCreditsException extends RuntimeException {

public NotEnoughCreditsException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package ch.engenius.bank.exceptions;

public class NotFoundException extends RuntimeException {

public NotFoundException(String message) {
super(message);
}
}
47 changes: 47 additions & 0 deletions src/main/test/ch/engenius/bank/domain/BankAccountTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package ch.engenius.bank.domain;

import ch.engenius.bank.exceptions.NotEnoughCreditsException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.util.UUID;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class BankAccountTest {

private static final BigDecimal DEFAULT_MONEY_AMOUNT_TEN = BigDecimal.TEN;
private static final BigDecimal WITHDRAW_MONEY_AMOUNT_FIVE = BigDecimal.valueOf(5);
private static final BigDecimal MONEY_DEPOSIT = BigDecimal.TEN;
private final BankAccount bankAccount = new BankAccount();

@BeforeEach
public void setUp() {
bankAccount.setAccountNumber(UUID.randomUUID());
bankAccount.setMoneyAmount(DEFAULT_MONEY_AMOUNT_TEN);
}

@Test
public void shouldThrowNotEnoughCreditsExceptionWhenTryingToWithdraw() {
try {
bankAccount.withdraw(DEFAULT_MONEY_AMOUNT_TEN.multiply(BigDecimal.TEN));
} catch (NotEnoughCreditsException e) {
assertEquals("Not enough credits on bank account.", e.getMessage());
}
}

@Test
public void shouldWithdrawSuccessfully() {
bankAccount.withdraw(WITHDRAW_MONEY_AMOUNT_FIVE);

assertEquals(DEFAULT_MONEY_AMOUNT_TEN.subtract(WITHDRAW_MONEY_AMOUNT_FIVE), bankAccount.getMoneyAmount());
}

@Test
public void shouldDepositMoneySuccessfully() {
bankAccount.deposit(MONEY_DEPOSIT);

assertEquals(DEFAULT_MONEY_AMOUNT_TEN.add(MONEY_DEPOSIT), bankAccount.getMoneyAmount());
}
}
Loading