package org.multibit.mbm.core.accounting; import com.google.common.base.Preconditions; /** * <p>Multi-legged transaction to provide the following to {@link Account}:</p> * <ul> * <li>Provision of a collection of entries associated with a single operation</li> * </ul> * <p>A Transaction links a withdrawal from one Account to a deposit in another.</p> * <p>The sum of all entries in a Transaction is zero to represent the * conservation of the underlying items.</p> * * @since 0.0.1 *   */ public class Transaction<T extends Entry<T>> { private final T withdrawalEntry; private final T depositEntry; private boolean isCommitted = false; /** * @param withdrawalEntry The entry to be taken from the source Account (normally -ve) * @param depositEntry The entry to be deposited in the target Account (normally +ve) */ public Transaction(T withdrawalEntry, T depositEntry) { // Apply class-specific validation rules validate(withdrawalEntry, depositEntry); // Must be OK to be here this.withdrawalEntry = withdrawalEntry; this.depositEntry = depositEntry; } protected void validate(T withdrawalEntry, T depositEntry) { // TODO Introduce an Exception providing SLF4J formatting syntax // ("Entries are not balanced. Withdrawal={}, deposit={}",withdrawalEntry.getAmount(), depositEntry.getAmount() // Validation Preconditions.checkNotNull(withdrawalEntry, "withdrawalEntry"); Preconditions.checkNotNull(depositEntry, "depositEntry"); if (withdrawalEntry.getAmount() + depositEntry.getAmount() != 0) { throw new IllegalArgumentException("Entries are not balanced"); } if (withdrawalEntry.getAccount().equals(depositEntry.getAccount())) { throw new IllegalArgumentException("Entries are are against the same account"); } } /** * All Transactions should enforce single commit behaviour but * this method not made final to allow for easier testing and * sub-classing. */ public void commit() { if (!isCommitted()) { Account<T> withdrawalAccount = withdrawalEntry.getAccount(); Account<T> depositAccount = depositEntry.getAccount(); withdrawalAccount.addEntry(withdrawalEntry); depositAccount.addEntry(depositEntry); } else { throw new IllegalStateException("Should not commit more than once."); } } /** * @return True if this transaction has been committed */ public boolean isCommitted() { return isCommitted; } /** * @return The withdrawal entry to subclasses */ protected T getWithdrawalEntry() { return withdrawalEntry; } /** * @return The withdrawal entry to subclasses */ protected T getDepositEntry() { return depositEntry; } @Override public String toString() { return String.format("Transaction[withdrawalEntry=%s, depositEntry=%s]]", withdrawalEntry.getAmount(), depositEntry.getAmount()); } }