package study.projectbank.account.ejb;
import java.math.BigDecimal;
import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.ejb.SessionSynchronization;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import study.projectbank.account.Account;
import study.projectbank.account.AccountException;
import study.projectbank.process.deposit.DepositTransaction;
import study.projectbank.util.MessageSender;
import study.projectbank.util.QueueMessageSender;
import study.projectbank.util.SQLUtils;
public class AccountFacadeBean implements SessionBean, SessionSynchronization {
private static final long serialVersionUID = 576795362784291888L;
private static final String ACCOUNT_QUERY = "SELECT AGENCY_NUMBER,ACCOUNT_NUMBER,NAME,BALANCE FROM ACCOUNT WHERE AGENCY_NUMBER=? AND ACCOUNT_NUMBER=?";
private static final String ACCOUNT_REFRESH_QUERY = "SELECT NAME,BALANCE FROM ACCOUNT WHERE AGENCY_NUMBER=? AND ACCOUNT_NUMBER=?";
private static final String ACCOUNT_UPDATE = "UPDATE ACCOUNT SET NAME=?,BALANCE=? WHERE AGENCY_NUMBER=? AND ACCOUNT_NUMBER=?";
private static final String ACCOUNT_UPDATE_BALANCE = "UPDATE ACCOUNT SET BALANCE=? WHERE AGENCY_NUMBER=? AND ACCOUNT_NUMBER=?";
private static final String ACCOUNT_WITHDRAW_INSERT_LOG = "INSERT INTO WITHDRAW (AGENCY_NUMBER,ACCOUNT_NUMBER,AMOUNT,DATE_WITHDRAW) VALUES (?,?,?,?)";
private SessionContext ctx;
private Context homeCtx;
private static final String JNDI_QC_FACTORY = "java:comp/env/jms/QueueConnectionFactory";
private static final String JNDI_QUEUE_DEPOSIT_PROCESS = "java:comp/env/jms/depositProccess";
private DataSource dataSource;
// we could use Entity Bean but is obsolete, so we can make this bean a
// session facade
private Account account;
public void setSessionContext(SessionContext ctx) throws EJBException, RemoteException {
this.ctx = ctx;
try {
Context initialCtx = new InitialContext();
homeCtx = (Context) initialCtx.lookup("java:comp/env");
} catch(NamingException e) {
e.printStackTrace();
throw new EJBException(e);
}
}
public void prepareResources() {
try {
this.dataSource = (DataSource) homeCtx.lookup("jdbc/BankDB");
} catch(NamingException e) {
e.printStackTrace();
throw new EJBException(e);
}
}
public void releaseResources() {
this.dataSource = null;
this.homeCtx = null;
this.ctx = null;
}
public void ejbCreate(long agencyNumber, long accountNumber) throws CreateException {
if(agencyNumber <= 0) {
throw new CreateException("Invalid agency.");
} else if(accountNumber <= 0) {
throw new CreateException("Invalid account number.");
}
prepareResources();
// search the account in the DB
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rst = null;
try {
conn = dataSource.getConnection();
stmt = conn.prepareStatement(ACCOUNT_QUERY);
stmt.setLong(1, agencyNumber);
stmt.setLong(2, accountNumber);
rst = stmt.executeQuery();
if(rst != null && rst.next()) {
this.account = new Account();
this.account.setAgencyNumber(rst.getLong("AGENCY_NUMBER"));
this.account.setAccountNumber(rst.getLong("ACCOUNT_NUMBER"));
this.account.setName(rst.getString("NAME"));
this.account.setBalance(rst.getDouble("BALANCE"));
} else {
throw new CreateException("Account not found, check the agency and account number.");
}
} catch(SQLException e) {
e.printStackTrace();
throw new CreateException("Error at creation of new account");
} finally {
SQLUtils.close(rst);
SQLUtils.close(stmt);
SQLUtils.close(conn);
}
}
public void ejbActivate() throws EJBException, RemoteException {
prepareResources();
}
public void ejbPassivate() throws EJBException, RemoteException {
releaseResources();
}
public void ejbRemove() throws EJBException, RemoteException {
releaseResources();
}
public void afterBegin() throws EJBException, RemoteException {
// search the account in the DB
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rst = null;
try {
conn = dataSource.getConnection();
stmt = conn.prepareStatement(ACCOUNT_REFRESH_QUERY);
stmt.setLong(1, account.getAgencyNumber());
stmt.setLong(2, account.getAccountNumber());
rst = stmt.executeQuery();
if(rst != null && rst.next()) {
this.account.setName(rst.getString("NAME"));
this.account.setBalance(rst.getDouble("BALANCE"));
} else {
throw new EJBException("Account not found!");
}
} catch(SQLException e) {
e.printStackTrace();
throw new EJBException("Error at refresh data", e);
} finally {
SQLUtils.close(rst);
SQLUtils.close(stmt);
SQLUtils.close(conn);
}
}
public void beforeCompletion() throws EJBException, RemoteException {
// update the account in the DB
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rst = null;
try {
conn = dataSource.getConnection();
stmt = conn.prepareStatement(ACCOUNT_UPDATE);
stmt.setString(1, account.getName());
stmt.setDouble(2, account.getBalance());
stmt.setLong(3, account.getAgencyNumber());
stmt.setLong(4, account.getAccountNumber());
if(stmt.executeUpdate() != 1) {
ctx.setRollbackOnly();
}
} catch(SQLException e) {
e.printStackTrace();
throw new EJBException("Error at reload of the balance");
} finally {
SQLUtils.close(rst);
SQLUtils.close(stmt);
SQLUtils.close(conn);
}
}
public void afterCompletion(boolean committed) throws EJBException, RemoteException {
}
public double getBalance() {
return account.getBalance();
}
public boolean withdraw(double amount) throws AccountException {
if(amount <= 0) {
throw new AccountException("Invalid amount");
} else if(account.getBalance() - amount < 0) {
throw new AccountException("Insufficiency balance");
}
System.out.println("\n\n==================");
System.out.println("Starting a withdraw of " + amount);
// does the withdraw
BigDecimal bdBalance = new BigDecimal(String.valueOf(account.getBalance()));
bdBalance.setScale(2, BigDecimal.ROUND_HALF_EVEN);
BigDecimal amountWithdraw = new BigDecimal(String.valueOf(amount));
amountWithdraw.setScale(2, BigDecimal.ROUND_HALF_EVEN);
BigDecimal newBalance = bdBalance.subtract(amountWithdraw);
newBalance.setScale(2, BigDecimal.ROUND_HALF_EVEN);
account.setBalance(newBalance.doubleValue());
// search the account in the DB and refresh the data
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rst = null;
try {
conn = dataSource.getConnection();
// update the balance
System.out.println("Updating the balance");
stmt = conn.prepareStatement(ACCOUNT_UPDATE_BALANCE);
stmt.setDouble(1, account.getBalance());
stmt.setLong(2,account.getAgencyNumber());
stmt.setLong(3, account.getAccountNumber());
if(stmt.executeUpdate() != 1) { // must update only one account
ctx.setRollbackOnly();
return false;
}
SQLUtils.close(stmt);
// register log of withdraw
System.out.println("Inserting the log");
stmt = conn.prepareStatement(ACCOUNT_WITHDRAW_INSERT_LOG);
stmt.setLong(1, account.getAgencyNumber());
stmt.setLong(2, account.getAccountNumber());
stmt.setDouble(3, amount);
stmt.setDate(4, new Date(System.currentTimeMillis()));
if(stmt.executeUpdate() != 1) {
ctx.setRollbackOnly();
return false;
}
return true;
} catch(SQLException e) {
e.printStackTrace();
throw new EJBException("Error at reload of the balance");
} finally {
System.out.println("Withdraw ended");
System.out.println("==================\n");
SQLUtils.close(rst);
SQLUtils.close(stmt);
SQLUtils.close(conn);
}
}
public void deposit(double amount, String name) throws AccountException {
if(amount <= 0) {
throw new AccountException("Invalid amount");
}
if(name == null) {
name = "";
}
DepositTransaction deposit = new DepositTransaction(account.getAgencyNumber(), account.getAccountNumber(), amount, name);
// gets JMS sender to send the deposit to Deposit Proccess' JMS
MessageSender sender = null;
try {
// I'm getting IllegalStateException because J2EE by default use only 1 non-XA Resource
// (J2EE 1.3 release note section "New Treatment of Non-XA Data Transactions")
// to correct use need change de datasource to XA in the resource.properties.
sender = new QueueMessageSender(JNDI_QC_FACTORY, JNDI_QUEUE_DEPOSIT_PROCESS);
sender.sendObject(deposit);
} catch(Exception e) {
e.printStackTrace();
//ctx.setRollbackOnly();
throw new AccountException("Error at deposit");
} finally {
if(sender != null) {
sender.close();
}
}
}
}