// Bank.java
/*
Creates a bunch of accounts and uses threads
to post transactions to the accounts concurrently.
*/
import java.io.*;
import java.util.*;
public class Bank {
public static final int ACCOUNTS = 20; // number of accounts
private Account[] accounts;
private Buffer buffer;
private ArrayList<String> badTransactions;
private Object badTransactionLock;
private boolean willTrackBad;
private int limit;
public Bank() {
this(0);
willTrackBad = false;
}
public Bank(int limit) {
buffer = new Buffer();
initializeAccounts();
badTransactions = new ArrayList<String>();
badTransactionLock = new Object();
this.limit = limit;
willTrackBad = true;
}
/*
Reads transaction data (from/to/amt) from a file for processing.
(provided code)
*/
public void readFile(String file) {
try {
BufferedReader reader = new BufferedReader(new FileReader(file));
// Use stream tokenizer to get successive words from file
StreamTokenizer tokenizer = new StreamTokenizer(reader);
while (true) {
int read = tokenizer.nextToken();
if (read == StreamTokenizer.TT_EOF) break; // detect EOF
int from = (int)tokenizer.nval;
tokenizer.nextToken();
int to = (int)tokenizer.nval;
tokenizer.nextToken();
int amount = (int)tokenizer.nval;
buffer.add(new Transaction(from, to, amount));
}
}
catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
/*
Processes one file of transaction data
-fork off workers
-read file into the buffer
-wait for the workers to finish
*/
public void processFile(String file, int numWorkers) {
// fork off the workers
Thread[] workers = new Thread[numWorkers];
for(int i = 0; i < numWorkers; i++) {
workers[i] = new Thread(new Worker());
workers[i].start();
}
// read the file
readFile(file);
// add a null for each worker
for(int i = 0; i < numWorkers; i++)
buffer.add(null);
// Join all of the workers
try {
for(int i = 0; i < numWorkers; i++) {
workers[i].join();
}
} catch (Exception ignored) {}
}
/*
* gets the limit
*/
public int getLimit() {
return limit;
}
/*
* indicates if the bank will track bad transactions
*/
public boolean willTrackBad() {
return willTrackBad;
}
/*
* adds a bad transaction
*/
public void addBadTransaction(int from, int to, int amount, int balance) {
synchronized(badTransactionLock) {
badTransactions.add("from:" + from + " to:" + to + " amt:" + amount + " bal:" + balance);
}
}
/**
* prints the summary of the accounts in the bank
*/
public void summary() {
for(int i = 0; i < ACCOUNTS; i++)
System.out.println(accounts[i].toString());
if(willTrackBad) {
System.out.println("Bad transactions...");
for(int i = 0; i < ACCOUNTS; i++)
System.out.println(badTransactions.get(i));
}
}
/*
Looks at commandline args and calls Bank processing.
*/
public static void main(String[] args) {
// deal with command-lines args
if (args.length == 0) {
System.out.println("Args: transaction-file [num-workers [limit]]");
System.exit(1);
}
String file = args[0];
int numWorkers = 1;
int limit = 0;
if (args.length >= 2) {
numWorkers = Integer.parseInt(args[1]);
if(args.length >= 3) {
limit = Integer.parseInt(args[2]);
}
}
// YOUR CODE HERE
Bank bank = new Bank(limit);
long startTime = System.currentTimeMillis();
bank.processFile(file, numWorkers);
long endTime = System.currentTimeMillis();
bank.summary();
System.out.println("Done");
System.out.println("Took " + (endTime - startTime) + "ms");
}
// -------------- Private ---------------- //
private void initializeAccounts() {
accounts = new Account[ACCOUNTS];
for(int i = 0; i < ACCOUNTS; i++)
accounts[i] = new Account(this, i);
}
private class Worker implements Runnable {
/*
* loop the worker executes
*/
public void run() {
Transaction t;
while((t = buffer.remove()) != null) {
int amount = t.amount;
int from = t.from;
int to = t.to;
Bank bank = accounts[from].getBank();
int bal = accounts[from].change(-amount);
if(bank.willTrackBad() && bal < bank.getLimit()) bank.addBadTransaction(from, to, amount, bal);
accounts[to].change(amount);
}
}
}
}