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
33 changes: 33 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/
33 changes: 32 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,37 @@
<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>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>4.0.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.

69 changes: 37 additions & 32 deletions src/main/java/ch/engenius/bank/BankRunner.java
Original file line number Diff line number Diff line change
@@ -1,71 +1,76 @@
package ch.engenius.bank;

import ch.engenius.bank.domain.Account;
import ch.engenius.bank.service.BankService;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.*;
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 final BankService bankService = new BankService();


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

try {
runner.registerAccounts(accounts, defaultDeposit);
runner.sanityCheck(accounts, defaultDeposit.multiply(BigDecimal.valueOf(accounts)));
runner.runBank(iterations, accounts);
runner.sanityCheck(accounts, defaultDeposit.multiply(BigDecimal.valueOf(accounts)));
} catch (IllegalStateException e) {
System.out.println(e.getMessage());
}
}

private void runBank(int iterations, int maxAccount) {
for (int i =0; i< iterations; i++ ) {
executor.submit( ()-> runRandomOperation(maxAccount));
List<Callable<Void>> callableTasks = new ArrayList<>();

for (int i = 0; i < iterations; i++) {
Callable<Void> callableTask = () -> runRandomOperation(maxAccount);
callableTasks.add(callableTask);
}

try {
executor.shutdown();
executor.awaitTermination(100,TimeUnit.SECONDS);
executor.invokeAll(callableTasks);
} catch (InterruptedException e) {
e.printStackTrace();
}
executor.shutdown();
}

private void runRandomOperation(int maxAccount) {
double transfer = random.nextDouble()*100.0;
private Void runRandomOperation(int maxAccount) {
BigDecimal transfer = BigDecimal.valueOf(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);
bankService.transferMoney(transfer, accountInNumber, accountOutNumber);
return null;
}

private void registerAccounts(int number, int defaultMoney) {
for ( int i = 0; i < number; i++) {
bank.registerAccount(i, defaultMoney);
private void registerAccounts(int number, BigDecimal defaultMoney) {
for (int i = 0; i < number; i++) {
bankService.registerAccount(i, defaultMoney);
}
}

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

if ( sum.intValue() != totalExpectedMoney) {
throw new IllegalStateException("we got "+ sum + " != " + totalExpectedMoney +" (expected)");
if (sum.compareTo(totalExpectedMoney) != 0) {
throw new IllegalStateException("We got "+ sum + " != " + totalExpectedMoney +" (expected)");
}
System.out.println("sanity check OK");
System.out.println("Sanity check OK");
}


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

import java.math.BigDecimal;
import java.util.concurrent.locks.ReentrantLock;

public class Account {

private BigDecimal money;
private ReentrantLock lock;

public Account() {
this.money = BigDecimal.ZERO;
this.lock = new ReentrantLock();
}

public Account(BigDecimal money) {
this.lock = new ReentrantLock();
try {
validAmount(money);
this.money = money;
} catch (IllegalArgumentException e) {
this.money = BigDecimal.ZERO;
System.out.println(e.getMessage());
}
}

public BigDecimal getMoney() {
return money;
}

public void withdraw(BigDecimal amount) {
lock.lock();
try {
validAmount(amount);
if (money.subtract(amount).compareTo(BigDecimal.ZERO) < 0)
throw new IllegalStateException("Not enough credits on account");

money = money.subtract(amount);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
} finally {
lock.unlock();
}
}

public void deposit(BigDecimal amount) {
lock.lock();
try {
validAmount(amount);
money = money.add(amount);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
} finally {
lock.unlock();
}
}

private void validAmount(BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0)
throw new IllegalArgumentException("Amount should have positive value");
}
}
16 changes: 16 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,16 @@
package ch.engenius.bank.domain;

import java.util.HashMap;

public class Bank {

private HashMap<Integer, Account> accounts;

public Bank() {
this.accounts = new HashMap<>();
}

public HashMap<Integer, Account> getAccounts() {
return accounts;
}
}
40 changes: 40 additions & 0 deletions src/main/java/ch/engenius/bank/service/BankService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package ch.engenius.bank.service;

import ch.engenius.bank.domain.Account;
import ch.engenius.bank.domain.Bank;
import java.math.BigDecimal;
import java.util.NoSuchElementException;

public class BankService {

private Bank bank;

public BankService() {
this.bank = new Bank();
}

public Account registerAccount(int accountNumber, BigDecimal amount) {
Account account = new Account(amount);
bank.getAccounts().put(accountNumber, account);
return account;
}

public Account getAccount(int number) {
if (!bank.getAccounts().containsKey(number))
throw new NoSuchElementException("Account with number: " + number + " does not exist");

return bank.getAccounts().get(number);
}

public void transferMoney(BigDecimal money, int accountInNumber, int accountOutNumber) {
try {
Account accountIn = getAccount(accountInNumber);
Account accountOut = getAccount(accountOutNumber);

accountOut.withdraw(money);
accountIn.deposit(money);
} catch (NoSuchElementException e) {
System.out.println(e.getMessage());
}
}
}
41 changes: 41 additions & 0 deletions src/test/java/ch/engenius/bank/domain/AccountTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package ch.engenius.bank.domain;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class AccountTest {

private Account account;

@BeforeEach
public void setUp() {
account = new Account(BigDecimal.valueOf(1000));
}

@Test
public void shouldCreateAccount() {
assertEquals(BigDecimal.valueOf(1000), account.getMoney());
}

@Test
public void shouldAddMoney() {
account.deposit(BigDecimal.valueOf(100));
assertEquals(BigDecimal.valueOf(1100), account.getMoney());
}

@Test
public void shouldSubtractMoney() {
account.withdraw(BigDecimal.valueOf(100));
assertEquals(BigDecimal.valueOf(900), account.getMoney());
}

@Test
public void shouldThrowException_whenThereIsNotEnoughMoney() {
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> account.withdraw(BigDecimal.valueOf(2000)));
assertEquals("Not enough credits on account", exception.getMessage());
assertEquals(BigDecimal.valueOf(1000), account.getMoney());
}
}
Loading