Question safe withdraw/deposit using AspectJ - java

I have question regarding making a bankAccount class implement safe withdraw/deposit function. So far it will print log when you make a withdraw or deposit to the bankAccount class. Anyway my question is how to implement the safety e.g. you cannot withdraw more money than what you have currently in your bankAccount. If I'm not allowed to implement that safety in the bankAccount class, and want to implement it to an AspectJ.
I have the following now. As can be seen the withdraw is done regardless if the if-statement is true or false. Therefore I had to in the else statement deposit back the amount of money, so it would not turn negative. Can this be done in some nicer way possibly?
pointcut checking(BankAccount ba, float x):
call(* BankAccount.withdraw(..)) && target(ba) && args(x);
before(BankAccount b, float x) : checking(b, x) {
if(b.getBalance() >= x) {
System.out.println("Account changing. $" + x + " withdrawn...");
} else {
System.out.println("Account does not have. $" + x + " to withdrawn...");
b.deposit(x);
}
}

I'd say this would be better handled by an around advice, which can prevent proceeding to the normal invocation and substitute some other action instead if the transaction shouldn't be allowed.
The code for the around advice should be basically similar to what you wrote for before, except in the if block you'd have to call proceed to continue into the normal execution, and in the else block you'd no longer need the call to deposit.

Related

Try-Catch statement Java

public void payForMeal(double amount) throws Exception {
if (balance - amount < 0 && amount - balance <= creditLimit) {
this.strStatus = "Using Credit";
double newBalance = balance - amount;
balance = newBalance;
throw new ArithmeticException("\n\n-----------------------------\n" + "You must top-up your balance\n" + "Your new balance is: " + balance + "\n" + "You are: " + strStatus + "\n" + "-----------------------------\n");
}//if
else if (amount > creditLimit && balance < amount) {
throw new ArithmeticException("\n\n----------------------\n" + "Cost of meal exceeds the credit limit." + "\n----------------------\n");
}//elseif
else {
double newBalance = balance - amount;
balance = newBalance;
transCount++;
}//else
}//payForMeal
when balance is 2 and payForMeal is set to 8 the following prints before the program crashes:
Displaying Account Details:
Cost of meal exceeds the credit limit.
Customer ID: 200
Name: Joe
Balance: 2.0
Minimum TopUp: 2.0
Account Status: Valid
Credit Limit: 5.0
Transaction Count: 0
How can I add a try-catch to stop the programming from crashing but still print out the errors, thanks
You should wrap the method which throws the error with the try catch, like so
// ... some code
try {
payForMeal(amount);
} catch (ArithmeticException e) {
System.out.log("An error occurred trying to pay for the meal: " + e.getMessage());
}
// ... more code
How you handle the error is for you to decide.
Try to do less in your methods. Programming is about problem decomposition and design.
Have a checkBalanceSufficient method that returns a result rather than doing this within your code. Check what kind of data it needs to return. Don't put in any print statements, that's for your main method or UI related classes & methods.
Don't reuse ArithmeticException. The calculations are fine, it is the result that you are not happy with. So you need to define your own higher level exception instead (programming an exception is really easy, just extend Exception). Preferably your code will never throw an exception due to problems with the input though; you can handle bad input within separate methods early in your code.
If there is any higher level code (i.e. code that implements a use case) within a catch clause then you are already doing things wrong.

JUnit testing for a method when else statement outputs error message as String [duplicate]

This question already has answers here:
JUnit test for System.out.println()
(14 answers)
Closed 4 years ago.
I'm working on a program that processes bank transactions. My withdraw method subtracts the amount from the balance and if there aren't enough funds for the withdrawal, an error message is outputted to the screen. The error message is the case I am having trouble testing.
public void withdraw(double amt) {
double bal = getBalance();
if(bal >= amt) {
super.setBalance(bal - amt);
if(bal < minBalance) {
super.setBalance(bal - amt - maintFee);
}
} else {
System.out.println("Error: Not enough funds for withdraw");
}
}
These are my JUnit tests for this method currently. I just need help on testWithdrawThree(). Thank you!
#Test
void testWithdrawOne() {
Savings s = new Savings(500.00, 30.00, "111", "Andrew Green", 1000.00);
s.withdraw(200);
assertEquals(800.00, s.getBalance());
}
#Test
void testWithdrawTwo() {
Savings s = new Savings(500.00, 30.00, "111", "Andrew Green", 400.00);
s.withdraw(200.00);
assertEquals(170.00, s.getBalance());
}
#Test
void testWithdrawThree() {
Savings s = new Savings(500.00, 30.00, "111", "Andrew Green", 400.00);
s.withdraw(600.00);
//Need to check that program will output "Error: Not enough funds for withdrawal"
}
Best approach in my eyes is doing a refactoring as suggested by Yoni already. Alternatively to changing the signature of the method (maybe it's not possible because it's defined in an interface), your original method can call another method that you pass the PrintStream to be used:
public void withdraw(double amt) {
performWithdraw(amt, System.out);
}
void performWithdraw(double amt, PrintStream errorStream) {
double bal = getBalance();
if(bal >= amt) {
super.setBalance(bal - amt);
if(bal < minBalance) {
super.setBalance(bal - amt - maintFee);
}
} else {
errorStream.println("Error: Not enough funds for withdraw");
}
}
Your test class (residing in the same package and therfor able to access performWithdraw) would look something like this:
#Test
void testInvalidWithdraw() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos, true, "utf8");
Savings s = new Savings(500.00, 30.00, "111", "Andrew Green", 400.00);
s.performWithdraw(600.00, ps);
assertEquals(400d, s.getBalance());
assertEquals("check error message", "Error: Not enough funds for withdraw", baos.toString("utf8"));
}
BTW: You should test that the balance is unchanged (I added a corresponding assertEquals in my example) and you should check edge cases as well, i.e. check that you get the error message when withdrawing 400.01. Edge cases should also be checked for the cases where mainenance fees are charged or aren't.
BTW2: Using double for monetary amounts is a Bad Thing[TM]. For learning JUnit it's OK but for Real Applications[TM] you should e.g. use BigDecimal instead.
There are a few ways that you can go about this:
You can take ownership of the System.out stream, as explained in this answer and Andrea mentioned in the comment. I don't think this is a very good route to go because it means that you won't be able to run your tests in parallel since System.out is a shared resource across the JVM.
You can refactor your code - maybe your method should return an error if the withdrawal is not successful? You are basically swallowing the error rather than reporting it.
You can test that the balance did not change after the invocation of the method without sufficient funds. I think this is actually the interesting behavior of the system that you want to test, isn't it? Testing the error message that's printed seems superficial. Maybe it will give you coverage for that line of code but is it really the meaning that you want to capture in your test? Is anyone really going to look at the output stream in production and look for this message? This is again another argument to refactor your code ...
Also, a side note: it would go a long way to name your test methods with meaningful names, e.g. testWithdrawWithoutFunds, instead of the generic names testOne, testTwo, etc.

Improving methods in Java

I have recently started a course where the main language we are learning at the moment is Java.
I have been tasked with creating a program that allows people to vote on two candidates - the program then counts the votes, and depending on how man votes have been made depends on what is displayed.
Here is the part I am concerned with at the moment:
public String printResults(){
if(candidate1Votes == 0 && candidate2Votes == 0)
{
System.out.println ("No votes cast, results cannot be displayed.");
return "No votes cast, results cannot be displayed.";
}
else if(this.completed == false)
{
System.out.println ("Voting has not finished");
return "Voting has not finished";
}
else if(this.completed == true)
{
System.out.println ("Voting has finished, no more votes will be allowed.");
return "Voting has finished, no more votes will be allowed";
}
{
double totalVotes = this.candidate1Votes + this.candidate2Votes;
double cand1Share = (double) this.candidate1Votes/totalVotes*100;
double cand2Share = (double) this.candidate2Votes/totalVotes*100;
System.out.format(candidate1 + " received %3.1f percent of the votes\n", cand1Share);
System.out.format(candidate2 + " received %3.1f percent of the votes\n", cand2Share);
return "v";
}
}
Originally I used void in this method, but part of our task was to then change it to a string value. This is where I am struggling - once I set completed to true, it is still allowing me to cast votes. I know that this code is incomplete but I can't finish it as I am unsure what to do! These were the next parts to the questions.
Modify your printResults method so that it applies the first two rules. Note that the value of the completed field indicates whether or not voting is complete. The method should be modified to return a String which indicates whether printing has been successful.
Modify your vote method to apply the third rule.
Test your methods by creating an instance and doing the following – before
doing each test note the result you expect to get, and compare this with what you actually get:
• Try to print results immediately
• Cast votes for both candidates and try to print results
• Set the completed field to true by calling setCompleted
• Try to cast a vote for a candidate
• Print the results
I am new to this (this is my first year) and have managed to do okay in my books to get this far, however any help on this next issue would be greatly appreciated!
First of your code is unnecessary complicated, which makes it hard to read/enhance. It can easily simplified, like
public String printResults(){
if(candidate1Votes == 0 && candidate2Votes == 0) {
System.out.println ("No votes cast, results cannot be displayed.");
return "No votes cast, results cannot be displayed.";
} // you returned ... NO need for ELSE!
if(this.completed == false) {
System.out.println ("Voting has not finished");
return "Voting has not finished";
}
// it is very clear here that completed must be true!
double totalVotes = this.candidate1Votes + this.candidate2Votes;
double cand1Share = (double) this.candidate1Votes/totalVotes*100;
double cand2Share = (double) this.candidate2Votes/totalVotes*100;
System.out.format(candidate1 + " received %3.1f percent of the votes\n", cand1Share);
System.out.format(candidate2 + " received %3.1f percent of the votes\n", cand2Share);
return "v";
}
Probably that easier-to-read code is all that you need to get you going!
Looking at the code the last block will never be reached because either you have no votes or you have votes and in that case completed will be either true or false and will thus reach always one of the else ifs and they all return a string. So I wonder why how you can cast any votes at all.
You could also post the code where you call printResults and setCompleted to see where the problem lies.
Some more hints for improving your code:
Sometimes you have the opening bracket on the same line and sometimes on the next. You should probably choose one style
It is not necessary to surround the last code block with brackets
if (this.completed == true) and else if (this.completed == false) is a bit redundant and can be written like: if (this.completed) and if (!this.completed). Also you can write
if (this.completed) {
...
} else {
....
}
because if completed is not true it can only be false.
Instead of writing every String two times and having to edit it two times in case you want to change something you could also do the following:
String msg = "Voting has not finished"
System.out.println(msg);
return msg;

Reflection: Why are there methods like setAccessible()?

Just wondering, why did the people who invented Java write methods like setAccessible(boolean flag), which makes the access-modifiers (specially private) useless and cannot protect fields, methods, and constructors from being reached? Look at the following simple example:
public class BankAccount
{
private double balance = 100.0;
public boolean withdrawCash(double cash)
{
if(cash <= balance)
{
balance -= cash;
System.out.println("You have withdrawn " + cash + " dollars! The new balance is: " + balance);
return true;
}
else System.out.println("Sorry, your balance (" + balance + ") is less than what you have requested (" + cash + ")!");
return false;
}
}
import java.lang.reflect.Field;
public class Test
{
public static void main(String[] args) throws Exception
{
BankAccount myAccount = new BankAccount();
myAccount.withdrawCash(150);
Field f = BankAccount.class.getDeclaredFields()[0];
f.setAccessible(true);
f.set(myAccount, 1000000); // I am a millionaire now ;)
myAccount.withdrawCash(500000);
}
}
OUTPUT:
Sorry, your balance (100.0) is less than what you have requested
(150.0)! You have withdrawn 500000.0 dollars! The new balance is: 500000.0
Because some code is trusted code -- i.e., if a local application wants to do this, maybe it's not a big deal. For untrusted code, though -- i.e., an applet, or a web start application, or RMI stubs, or any other downloaded code -- there's a SecurityManager in place, which (generally based on a policy file) has the opportunity to say "Sorry, Charlie" and deny the setAccessible() request.
Well, once you have released a Java program, anyone is free to reverse engineer, or de-compile, it anyways, so if someone wanted it badly enough, they would probably be able to access your your "privates" anyway.
What you can do however, is to forbid any foreign code to access your stuff in your runtime. That is, if you're for instance using someone else's code you could disable reflections, access to files etc before those libraries are used.
Search for ClassLoader and Security Manager to find out more. Here's something that looks relevant.

Java Error When attempting to use the return from a method as an item in an if statement

I keep getting the following errors:
Cannot find symbol
Variable find
cannot find symbol
method getdata(int)
I am sure I am making this way more difficult than it is, but I am not sure how make this work so that the return from searching through the array, can be seen and evaluated by the if statement.
//assigns manager identification
manID = keyboard.nextInt();
//Fibonacci binary array for passwords
int[] passWArray = {00000000,00000001,00000001,00000010,00000011,00000101,00001000,00001101};
//item = find.getdata(manID);
if (getdata(manID) != -1)
{
//Do work here
dblPayRate = 10.85;
dblGrossPay = (intHours * dblPayRate) + (15.00);
dblTaxes = dblGrossPay * 0.19;
dblGrossPay -= dblTaxes;
//Print information to user
System.out.print("\n\n$" + df2.format(dblTaxes) +
" was withheld from this paycheck in taxes after working "+ intHours + " hours.\n\n");
System.out.print("The amount \"Employer Here\" owes you is $" + df2.format(dblGrossPay) + "\n");
}
else
{
// Dialog box for incorrect password
JOptionPane.showMessageDialog(null, "Invalid Entry! Contact the BOFH!");
//exits program (Note: needed for any JOptionPane programs)
System.exit(0);
}
}// end of long if statement for >50 hours
}//end of main method
public int find(int[] passWArray, int manID)
{
//search for manID in passWArray array
for (int index = 0; index < passWArray.length; index++)
if ( passWArray[index] == manID )
return manID;
//-1 indicates the value was not found
return -1;
}// end of find method
Change
if (getdata(manID) != -1)
into
if (find(passWArray , manID) != -1)
BTW those numbers don't magically become binary because they only contain 0's and 1's. Here's a hint:
int thirteen = Integer.parseInt("00001101", 2)
EDIT: in response to your next error
For now make the method static:
public static int find(int[] passWArray, int manID)
Eventually you might want to think about your 'Object-Oriented design' and just use the main() method as an entry point. Within main you create an instance of a class and let it do its work. In this way you can use the powers of O-O like encapsulation and inheritance and don't have to make everything static.
EDIT2: Afterthought
Your program seems to have the following 'actions':
user interaction
authentication
calculation
And there seem to be the following 'things' in your domain:
user
password
keyboard
display (command line and screen)
calculation
A good rule of thumb for an O-O design is to convert some of the 'things' and 'actions' already present in your domain into classes. A good class has a single responsibility and shares as little as possible of its data and methods with other classes (this is called information hiding).
Here's a class diagram that comes to mind:
User (represents a user, contains a single field 'password')
Authenticator (authenticates a user, contains the list of allowed passwords)
Console (all user interaction, either use System.out/in or Swing, but don't mix them)
Calculator (it calculates shit)

Categories

Resources