Coverage Report - org.apache.shiro.samples.aspectj.bank.SecureBankService
 
Classes in this File Line Coverage Branch Coverage Complexity
SecureBankService
81%
90/111
50%
11/22
2.312
 
 1  2
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.shiro.samples.aspectj.bank;
 20  
 
 21  
 import org.apache.shiro.SecurityUtils;
 22  
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 23  
 import org.apache.shiro.samples.aspectj.bank.AccountTransaction.TransactionType;
 24  
 import org.apache.shiro.subject.Subject;
 25  
 import org.slf4j.Logger;
 26  
 import org.slf4j.LoggerFactory;
 27  
 
 28  
 import java.util.ArrayList;
 29  
 import java.util.HashMap;
 30  
 import java.util.List;
 31  
 import java.util.Map;
 32  
 
 33  
 public class SecureBankService implements BankService {
 34  
 
 35  2
     private static final Logger log = LoggerFactory.getLogger(SecureBankService.class);
 36  
     private volatile boolean _isRunning;
 37  
     private final List<Account> _accounts;
 38  
     private Map<Long, Account> _accountsById;
 39  
 
 40  
     /**
 41  
      * Creates a new {@link SecureBankService} instance.
 42  
      */
 43  2
     public SecureBankService() {
 44  2
         _accounts = new ArrayList<Account>();
 45  2
         _accountsById = new HashMap<Long, Account>();
 46  2
     }
 47  
 
 48  
     /**
 49  
      * Starts this service
 50  
      */
 51  
     public void start() throws Exception {
 52  2
         _isRunning = true;
 53  2
         log.info("Bank service started");
 54  2
     }
 55  
 
 56  
     /**
 57  
      * Stop this service
 58  
      */
 59  
     public void dispose() {
 60  2
         log.info("Stopping bank service...");
 61  2
         _isRunning = false;
 62  
 
 63  4
         synchronized (_accounts) {
 64  2
             _accountsById.clear();
 65  2
             _accounts.clear();
 66  
         }
 67  
 
 68  2
         log.info("Bank service stopped");
 69  2
     }
 70  
 
 71  
     /**
 72  
      * Internal utility method that validate the internal state of this service.
 73  
      */
 74  
     protected void assertServiceState() {
 75  384
         if (!_isRunning) {
 76  0
             throw new IllegalStateException("This bank service is not running");
 77  
         }
 78  384
     }
 79  
 
 80  
     public int getAccountCount() {
 81  0
         return _accounts.size();
 82  
     }
 83  
 
 84  
     /* (non-Javadoc)
 85  
     * @see com.connectif.trilogy.root.security.BankService#createNewAccount(java.lang.String)
 86  
     */
 87  
 
 88  
     @RequiresPermissions("bankAccount:create")
 89  
     public long createNewAccount(String anOwnerName) {
 90  24
         assertServiceState();
 91  24
         log.info("Creating new account for " + anOwnerName);
 92  
 
 93  24
         synchronized (_accounts) {
 94  24
             Account account = new Account(anOwnerName);
 95  24
             account.setCreatedBy(getCurrentUsername());
 96  24
             _accounts.add(account);
 97  24
             _accountsById.put(account.getId(), account);
 98  
 
 99  24
             log.debug("Created new account: " + account);
 100  24
             return account.getId();
 101  
         }
 102  
     }
 103  
 
 104  
     /* (non-Javadoc)
 105  
     * @see com.connectif.trilogy.root.security.BankService#searchAccountIdsByOwner(java.lang.String)
 106  
     */
 107  
 
 108  
     public long[] searchAccountIdsByOwner(String anOwnerName) {
 109  0
         assertServiceState();
 110  0
         log.info("Searching existing accounts for " + anOwnerName);
 111  
 
 112  0
         ArrayList<Account> matchAccounts = new ArrayList<Account>();
 113  0
         synchronized (_accounts) {
 114  0
             for (Account a : _accounts) {
 115  0
                 if (a.getOwnerName().toLowerCase().contains(anOwnerName.toLowerCase())) {
 116  0
                     matchAccounts.add(a);
 117  
                 }
 118  
             }
 119  
         }
 120  
 
 121  0
         long[] accountIds = new long[matchAccounts.size()];
 122  0
         int index = 0;
 123  0
         for (Account a : matchAccounts) {
 124  0
             accountIds[index++] = a.getId();
 125  
         }
 126  
 
 127  0
         log.debug("Found " + accountIds.length + " account(s) matching the name " + anOwnerName);
 128  0
         return accountIds;
 129  
     }
 130  
 
 131  
     /* (non-Javadoc)
 132  
     * @see com.connectif.trilogy.root.security.BankService#getOwnerOf(long)
 133  
     */
 134  
 
 135  
     @RequiresPermissions("bankAccount:read")
 136  
     public String getOwnerOf(long anAccountId) throws AccountNotFoundException {
 137  66
         assertServiceState();
 138  66
         log.info("Getting owner of account " + anAccountId);
 139  
 
 140  66
         Account a = safellyRetrieveAccountForId(anAccountId);
 141  66
         return a.getOwnerName();
 142  
     }
 143  
 
 144  
     /* (non-Javadoc)
 145  
     * @see com.connectif.trilogy.root.security.BankService#getBalanceOf(long)
 146  
     */
 147  
 
 148  
     @RequiresPermissions("bankAccount:read")
 149  
     public double getBalanceOf(long anAccountId) throws AccountNotFoundException {
 150  94
         assertServiceState();
 151  94
         log.info("Getting balance of account " + anAccountId);
 152  
 
 153  94
         Account a = safellyRetrieveAccountForId(anAccountId);
 154  94
         return a.getBalance();
 155  
     }
 156  
 
 157  
     /* (non-Javadoc)
 158  
     * @see com.connectif.trilogy.root.security.BankService#depositInto(long, double)
 159  
     */
 160  
 
 161  
     @RequiresPermissions("bankAccount:operate")
 162  
     public double depositInto(long anAccountId, double anAmount) throws AccountNotFoundException, InactiveAccountException {
 163  18
         assertServiceState();
 164  18
         log.info("Making deposit of " + anAmount + " into account " + anAccountId);
 165  
 
 166  
         try {
 167  18
             Account a = safellyRetrieveAccountForId(anAccountId);
 168  18
             AccountTransaction tx = AccountTransaction.createDepositTx(anAccountId, anAmount);
 169  18
             tx.setCreatedBy(getCurrentUsername());
 170  18
             log.debug("Created a new transaction " + tx);
 171  
 
 172  18
             a.applyTransaction(tx);
 173  18
             log.debug("New balance of account " + a.getId() + " after deposit is " + a.getBalance());
 174  
 
 175  18
             return a.getBalance();
 176  
 
 177  0
         } catch (NotEnoughFundsException nefe) {
 178  0
             throw new IllegalStateException("Should never happen", nefe);
 179  
         }
 180  
     }
 181  
 
 182  
     /* (non-Javadoc)
 183  
     * @see com.connectif.trilogy.root.security.BankService#withdrawFrom(long, double)
 184  
     */
 185  
 
 186  
     @RequiresPermissions("bankAccount:operate")
 187  
     public double withdrawFrom(long anAccountId, double anAmount) throws AccountNotFoundException, NotEnoughFundsException, InactiveAccountException {
 188  14
         assertServiceState();
 189  14
         log.info("Making withdrawal of " + anAmount + " from account " + anAccountId);
 190  
 
 191  14
         Account a = safellyRetrieveAccountForId(anAccountId);
 192  14
         AccountTransaction tx = AccountTransaction.createWithdrawalTx(anAccountId, anAmount);
 193  14
         tx.setCreatedBy(getCurrentUsername());
 194  14
         log.debug("Created a new transaction " + tx);
 195  
 
 196  14
         a.applyTransaction(tx);
 197  10
         log.debug("New balance of account " + a.getId() + " after withdrawal is " + a.getBalance());
 198  
 
 199  10
         return a.getBalance();
 200  
     }
 201  
 
 202  
     /* (non-Javadoc)
 203  
     * @see com.connectif.trilogy.root.security.BankService#getTxHistoryFor(long)
 204  
     */
 205  
 
 206  
     @RequiresPermissions("bankAccount:read")
 207  
     public TxLog[] getTxHistoryFor(long anAccountId) throws AccountNotFoundException {
 208  94
         assertServiceState();
 209  94
         log.info("Getting transactions of account " + anAccountId);
 210  
 
 211  94
         Account a = safellyRetrieveAccountForId(anAccountId);
 212  
 
 213  94
         TxLog[] txs = new TxLog[a.getTransactions().size()];
 214  94
         int index = 0;
 215  290
         for (AccountTransaction tx : a.getTransactions()) {
 216  102
             log.debug("Retrieved transaction " + tx);
 217  
 
 218  102
             if (TransactionType.DEPOSIT == tx.getType()) {
 219  64
                 txs[index++] = new TxLog(tx.getCreationDate(), tx.getAmount(), tx.getCreatedBy());
 220  64
             } else {
 221  38
                 txs[index++] = new TxLog(tx.getCreationDate(), -1.0d * tx.getAmount(), tx.getCreatedBy());
 222  
             }
 223  
         }
 224  
 
 225  94
         return txs;
 226  
     }
 227  
 
 228  
     /* (non-Javadoc)
 229  
     * @see com.connectif.trilogy.root.security.BankService#closeAccount(long)
 230  
     */
 231  
 
 232  
     @RequiresPermissions("bankAccount:close")
 233  
     public double closeAccount(long anAccountId) throws AccountNotFoundException, InactiveAccountException {
 234  10
         assertServiceState();
 235  8
         log.info("Closing account " + anAccountId);
 236  
 
 237  8
         Account a = safellyRetrieveAccountForId(anAccountId);
 238  8
         if (!a.isActive()) {
 239  2
             throw new InactiveAccountException("The account " + anAccountId + " is already closed");
 240  
         }
 241  
 
 242  
         try {
 243  6
             AccountTransaction tx = AccountTransaction.createWithdrawalTx(a.getId(), a.getBalance());
 244  6
             tx.setCreatedBy(getCurrentUsername());
 245  6
             log.debug("Created a new transaction " + tx);
 246  6
             a.applyTransaction(tx);
 247  6
             a.setActive(false);
 248  
 
 249  6
             log.debug("Account " + a.getId() + " is now closed and an amount of " + tx.getAmount() + " is given to the owner");
 250  6
             return tx.getAmount();
 251  
 
 252  0
         } catch (NotEnoughFundsException nefe) {
 253  0
             throw new IllegalStateException("Should never happen", nefe);
 254  
         }
 255  
     }
 256  
 
 257  
     /* (non-Javadoc)
 258  
     * @see com.connectif.trilogy.root.security.BankService#isAccountActive(long)
 259  
     */
 260  
 
 261  
     @RequiresPermissions("bankAccount:read")
 262  
     public boolean isAccountActive(long anAccountId) throws AccountNotFoundException {
 263  66
         assertServiceState();
 264  66
         log.info("Getting active status of account " + anAccountId);
 265  
 
 266  66
         Account a = safellyRetrieveAccountForId(anAccountId);
 267  66
         return a.isActive();
 268  
     }
 269  
 
 270  
 
 271  
     /**
 272  
      * Internal method that safelly (concurrency-wise) retrieves an account from the id passed in.
 273  
      *
 274  
      * @param anAccountId The identifier of the account to retrieve.
 275  
      * @return The account instance retrieved.
 276  
      * @throws AccountNotFoundException If no account is found for the provided identifier.
 277  
      */
 278  
     protected Account safellyRetrieveAccountForId(long anAccountId) throws AccountNotFoundException {
 279  360
         Account account = null;
 280  720
         synchronized (_accounts) {
 281  360
             account = _accountsById.get(anAccountId);
 282  
         }
 283  
 
 284  360
         if (account == null) {
 285  0
             throw new AccountNotFoundException("No account found for the id " + anAccountId);
 286  
         }
 287  
 
 288  360
         log.info("Retrieved account " + account);
 289  360
         return account;
 290  
     }
 291  
 
 292  
     /**
 293  
      * Internal utility method to retrieve the username of the current authenticated user.
 294  
      *
 295  
      * @return The name.
 296  
      */
 297  
     protected String getCurrentUsername() {
 298  62
         Subject subject = SecurityUtils.getSubject();
 299  62
         if (subject == null || subject.getPrincipal() == null || !subject.isAuthenticated()) {
 300  0
             throw new IllegalStateException("Unable to retrieve the current authenticated subject");
 301  
         }
 302  62
         return SecurityUtils.getSubject().getPrincipal().toString();
 303  
     }
 304  
 }