001    package org.cocome.tradingsystem.testdriver;
002    
003    import java.rmi.AccessException;
004    import java.rmi.NotBoundException;
005    import java.rmi.RMISecurityManager;
006    import java.rmi.RemoteException;
007    import java.rmi.registry.LocateRegistry;
008    import java.rmi.registry.Registry;
009    import java.rmi.server.UnicastRemoteObject;
010    import java.util.HashMap;
011    import java.util.Map;
012    
013    import org.cocome.tradingsystem.external.Debit;
014    import org.cocome.tradingsystem.external.TransactionID;
015    import org.cocome.tradingsystem.systests.interfaces.IBank;
016    
017    /**
018     * Implementation of a bank that we can control (as opposed to the bank coming
019     * with the implementation).
020     * 
021     * @author Benjamin Hummel
022     * @author $Author: hummel $
023     * @version $Rev: 63 $
024     * @levd.rating GREEN Rev: 63
025     */
026    public class Bank extends UnicastRemoteObject implements IBank,
027                    org.cocome.tradingsystem.external.Bank {
028    
029            /** The offset added to create a transaction ID from a card number. */
030            private static final int TID_OFFSET = 42;
031    
032            /** Create a new Bank. */
033            protected Bank() throws RemoteException {
034                    super();
035                    register();
036            }
037    
038            /** ID for serialization in the RMI context. */
039            private static final long serialVersionUID = 1556930771225198542L;
040    
041            /** All available credit cards. */
042            private final Map<Integer, CreditCard> creditCards = new HashMap<Integer, CreditCard>();
043    
044            /** Register at RMI. */
045            private void register() throws AccessException, RemoteException {
046                    if (System.getSecurityManager() == null) {
047                            System.setSecurityManager(new RMISecurityManager());
048                    }
049                    Registry reg = LocateRegistry.getRegistry("localhost", 1098);
050                    reg.rebind("Bank", this);
051            }
052    
053            /** Unregister from RMI. */
054            public void unregister() throws AccessException, RemoteException,
055                            NotBoundException {
056                    Registry reg = LocateRegistry.getRegistry("localhost", 1098);
057                    reg.unbind("Bank");
058            }
059    
060            /** {@inheritDoc} */
061            public void createCreditCard(int cardNumber, int pinNumber,
062                            int availableMoney) {
063                    creditCards.put(cardNumber, new CreditCard(pinNumber, availableMoney));
064            }
065    
066            /** {@inheritDoc} */
067            public void deleteCreditCard(int cardNumber) {
068                    creditCards.remove(cardNumber);
069            }
070    
071            /** {@inheritDoc} */
072            public int getAvailableMoney(int cardNumber) {
073                    return creditCards.get(cardNumber).money;
074            }
075    
076            /** {@inheritDoc} */
077            public Debit debitCard(TransactionID id) throws RemoteException {
078                    int number = findCardFromTID(id);
079                    if (number < 0) {
080                            return Debit.TRANSACTION_ID_NOT_VALID;
081                    }
082    
083                    CreditCard cc = creditCards.get(number);
084                    if (cc == null) {
085                            return Debit.TRANSACTION_ID_NOT_VALID;
086                    }
087    
088                    // We have to use a simple approach here, as the reference
089                    // implementation does not care about the amount of money
090                    if (cc.money > 0) {
091                            return Debit.OK;
092                    }
093                    return Debit.NOT_ENOUGH_MONEY;
094            }
095    
096            /**
097             * Extract the card number from a transaction ID. This is not an efficient
098             * method, but the TransactionID class does not provide any access to its
099             * internals and also does not implement hashCode (although it should, when
100             * overriding equals).
101             * 
102             * @return the number of the credit card contained in this TID, or -1 if no
103             *         matching card was found.
104             */
105            private int findCardFromTID(TransactionID id) {
106                    for (int ccNumber : creditCards.keySet()) {
107                            if (new TransactionID(ccNumber + TID_OFFSET).equals(id)) {
108                                    return ccNumber;
109                            }
110                    }
111                    return -1;
112            }
113    
114            /** {@inheritDoc} */
115            public TransactionID validateCard(String cardInformation, int pinnumber)
116                            throws RemoteException {
117                    int cardNumber;
118                    try {
119                            cardNumber = Integer.parseInt(cardInformation);
120                    } catch (NumberFormatException e) {
121                            return null;
122                    }
123    
124                    CreditCard cc = creditCards.get(cardNumber);
125                    if (cc == null || cc.pin != pinnumber) {
126                            return null;
127                    }
128    
129                    // we use the card number plus an offset for the transaction ID (not
130                    // perfect, but works in this context)
131                    return new TransactionID(cardNumber + TID_OFFSET);
132            }
133    
134            /** Data storage for credit card. */
135            private static final class CreditCard {
136                    /** The pin code of the card. */
137                    private final int pin;
138    
139                    /** The available money for the card. */
140                    private int money;
141    
142                    /** Create a new card. */
143                    public CreditCard(int pinNumber, int availableMoney) {
144                            this.pin = pinNumber;
145                            this.money = availableMoney;
146                    }
147            }
148    }