| TradeManagementSessionFacadeBean.java |
/*
* @author : Pushkala
* @version : 1.0
*
* Development Environment : Oracle9i JDeveloper
*
* Name of the File : TradeManagementSessionFacadeBean.java
*
* Creation / Modification History
* Pushkala 26-Apr-2002 Created
* Elangovan 19-Aug-2003 Modified
*
*/
package oracle.otnsamples.ibfbs.trademanagement.ejb;
// Utility Packages
import java.util.Collection;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.sql.SQLException;
import java.rmi.RemoteException;
// EJB Packages
import javax.ejb.SessionContext;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import javax.ejb.RemoveException;
import javax.jms.QueueSession;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueConnection;
import javax.jms.Queue;
import javax.jms.Message;
import javax.jms.JMSException;
import javax.naming.InitialContext;
import oracle.toplink.exceptions.DatabaseException;
// Required TradeManagement Classes
import oracle.otnsamples.ibfbs.trademanagement.ejb.PortfolioLocal;
import oracle.otnsamples.ibfbs.trademanagement.ejb.PortfolioHomeLocal;
import oracle.otnsamples.ibfbs.trademanagement.ejb.TradeDetailsLocal;
import oracle.otnsamples.ibfbs.trademanagement.ejb.TradeDetailsHomeLocal;
import oracle.otnsamples.ibfbs.trademanagement.ejb.PortfolioInfo;
import oracle.otnsamples.ibfbs.trademanagement.ejb.TradeDetailsInfo;
import oracle.otnsamples.ibfbs.trademanagement.ejb.TradeHelper;
import oracle.otnsamples.ibfbs.trademanagement.ejb.PageByPage;
import oracle.otnsamples.ibfbs.toplink.Symbol;
import oracle.otnsamples.ibfbs.usermanagement.ejb.UserAccountLocal;
import oracle.otnsamples.ibfbs.usermanagement.ejb.UserAccountHomeLocal;
/**
* This Class contains the bean implementation for the TradeManagement
* Stateful Session EJB. This contains all the methods required for Trade
* Management in the application. This Session Facade Bean coordinates all
* the Trade Management Related Functionality and client invokes all this
* functionality only through this Session Bean.
*
* The SessionFacadeDesign pattern is used here to provide a simple interface
* to the complex subsystem of enterprise beans and to reduce communication
* and dependencies between client objects and enterprise beans.
*
* The methods in Session Facade Bean provide the following functionality :
* a) Getting / Setting Portfolio
* b) Getting / Setting TradeDetails
* c) Adding Portfolio / TradeDetails
* d) Updating Portfolio / TradeDetails
* e) Rollback of Portfolio / TradeDetails
* f) Interacting with UserManagement EJBs to get User Account Information,
* Contact Information
*
* UserAccount Local EJB Object has 1:N relationship with other Local
* Entity Beans namely PreferencesBean, AlertsBean, PortfolioBean and
* TradeDetails Bean. This forms a wrapper to the two Entity Beans
* PortfolioBean and TradeDetails Bean. It acts as a connecting link
* between the TradeManagementHelper class and the entity beans.
*
* @see TradeHelper.java
*/
public class TradeManagementSessionFacadeBean implements javax.ejb.SessionBean {
/**
* Instance variable to hold reference to User Account local home interface
* Note that using Home interfaces, we can get references to remote interfaces
*/
private UserAccountHomeLocal userAccountHomeLocal;
/** Instance variable to hold reference to Trade Details local home interface */
private TradeDetailsHomeLocal tradeDetailsHomeLocal;
/** Instance variable to hold reference to Portfolio local home interface */
private PortfolioHomeLocal portfolioHomeLocal;
/** Session Context object */
private SessionContext sc;
/** Trade Helper */
private TradeHelper tradeHelper = null;
/** Exchange Queue Connection factory instance */
private QueueConnectionFactory exchageQConnfactory = null;;
/** Exchange Queue handle */
private Queue exchangeQueue = null;
/**
* This is the method called by the EJB Container when
* TradeManagementSessionFacadeBean is instantiated ie,. when the client
* invokes the create() method on the Home interface
* TradeManagementSessionHomeRemote. Here we call the initialize method
* to set the objects required for TradeManagement processing.
* @since 1.0
*/
public void ejbCreate() {
initialize();
}
/**
* This method sets the initial context, and initializes the values of
* references to Home Interfaces. This also sets the DataSource object.
* @since 1.0
*/
private void initialize() {
try {
InitialContext ic = new InitialContext();
// Look up the Home interface of Entity Beans used in this Session Bean
userAccountHomeLocal = (UserAccountHomeLocal)
ic.lookup("java:comp/env/ejb/UserAccountHomeLocal");
tradeDetailsHomeLocal = (TradeDetailsHomeLocal)
ic.lookup("java:comp/env/ejb/TradeDetailsHomeLocal");
portfolioHomeLocal = (PortfolioHomeLocal)
ic.lookup("java:comp/env/ejb/PortfolioHomeLocal");
tradeHelper = TradeHelper.getInstance();
} catch(javax.naming.NamingException ne) {
System.err.println(" TMSFB.ejbCreate(): Error during JNDI lookup : "+ne.toString());
} catch(Exception ex) {
System.err.println(" TMSFB.ejbCreate(): Generic Error : "+ex.toString());
}
}
/**
* This method constructs a new Collection from the input collection,
* since we need to send the following details to the client
* 1) boolean value to indicate if the page need iteration
* 2) Size of the Collection
* 3) boolean value to indicate if the records have previous set
* 4) boolean value to indicate if the records have next set
* 5) Portfolio Details Collection
* @param allRecords Collection of all records
* @param recordNumber Record Number
* @param linesPerPage Lines per page
* @since 1.0
*/
private static Collection createNewCollection(Collection allRecords,
String recordNumber,
int linesPerPage) {
Collection newColl = new ArrayList();
// Size of the input collection
int collSize = allRecords.size();
if (recordNumber == null) {
recordNumber = "1";
}
boolean needsPageIteration = false;
// Page Iteration is required only if number of rows
// retrieved exceeds linesPerPage
if (collSize == 0 || collSize <= linesPerPage) {
needsPageIteration = false;
} else {
needsPageIteration = true;
}
if (needsPageIteration) {
PageByPage pbp = new PageByPage();
// Get the collection before getting hasPrev, hasNext
// since the variables are set by the getCollection method
Collection temp = pbp.getCollection((List)allRecords,
recordNumber,
linesPerPage);
newColl.add(pbp.hasPrevRecords());
newColl.add(pbp.hasNextRecords());
newColl.add(temp);
} else {
newColl.add("false");
newColl.add("false");
newColl.add(allRecords);
}
return newColl;
}
/**
* This method calls the methods of the Entity Beans to get the Portfolio
* details corresponding to the input User Account Number and returns the
* Portfolio details as a Collection.
*
* @param accountNumber Account Number
* @param recordNumber Record Number
* @param linesPerPage Lines Per Page
* @return The Collection of Portfolio Details for the input Account Number
* @exception RemoteException If error occurs while getting the details
* @since 1.0
*/
public Collection getPortfolio(Integer accountNumber, String recordNumber,
int linesPerPage)
throws RemoteException {
Collection allPortfolio = new ArrayList();
Collection pfColl = null;
try {
// Find the User Account Entity for the input account number
UserAccountLocal userAccountLocal =
userAccountHomeLocal.findByPrimaryKey(accountNumber);
// Get the Collection of Portfolio corresponding to the user account
pfColl = (Collection)userAccountLocal.getPortfolio();
// Create an iterator for the above Collection
Iterator portfolioLocalIter = pfColl.iterator();
while(portfolioLocalIter.hasNext()) {
// If the iterator has more values, get the next portfolio information
PortfolioLocal pf = (PortfolioLocal)portfolioLocalIter.next();
if(pf.getQuantity().intValue() > 0) {
Symbol s = tradeHelper.getSymbol(pf.getSymbol());
// If the Portfolio Quantity is greater than 0,
// Add the Portfolio information to a new collection
allPortfolio.add(new PortfolioInfo(pf.getLineNo(), pf.getQuantity(),
pf.getPrice(), pf.getSymbol(),
s.getCompanyname(),
pf.getPurchaseDate(),
pf.getPurchaseMode(),
pf.getTradeId()));
}
}
} catch(FinderException ex) {
throw (new RemoteException("Finder Exception in get Portfolio : " +
ex.toString()));
} catch(DatabaseException ex) {
throw (new RemoteException("SQL Exception in get Portfolio : " +
ex.toString()));
}
// return the new collection created
return createNewCollection(allPortfolio, recordNumber, linesPerPage);
}
/**
* This method gets the Portfolio Valuation Information for the input
* Account Number. A User can have multiple Portfolio details, so a
* collection of Portfolio Valuation Information is returned.
*
* @param accountNumber Account Number
* @param recordNumber Record Number
* @param linesPerPage Lines Per Page
* @return The Collection of Portfolio Valuation Information for the
* input Account Number
* @exception RemoteException
* @since 1.0
*/
public Collection getPortfolioValuation(Integer accountNumber,
String recordNumber,
int linesPerPage)
throws RemoteException {
Collection pfValue = new ArrayList();
Collection pfColl = null;
float totalAssetValue = 0; // The Total Asset Value
try {
// Find the User Account Entity for the input account number
UserAccountLocal userAccountLocal =
userAccountHomeLocal.findByPrimaryKey(accountNumber);
// Get the Collection of Portfolio corresponding to the user account
pfColl = (Collection)userAccountLocal.getPortfolio();
// Create an iterator for the above Collection
Iterator portfolioLocalIter = pfColl.iterator();
while(portfolioLocalIter.hasNext()) {
// If the iterator has more values, get the next portfolio information
PortfolioLocal pf = (PortfolioLocal)portfolioLocalIter.next();
if(pf.getQuantity().intValue() > 0) {
// If the Portfolio Quantity is greater than 0,
// Add the Portfolio information to a new collection
float currPrice = tradeHelper.getLatestRate(pf.getSymbol()).getLowprice().floatValue();
float buyValue = pf.getQuantity().floatValue() * pf.getPrice();
float currValue = pf.getQuantity().floatValue() * currPrice;
float gainorloss = currValue - buyValue;
float percent = (gainorloss / buyValue) * 100;
Symbol s = tradeHelper.getSymbol(pf.getSymbol());
pfValue.add(new PortfolioValue(pf.getLineNo(), pf.getQuantity(),
pf.getPrice(), pf.getSymbol(),
s.getCompanyname(),
pf.getPurchaseDate(),
pf.getPurchaseMode(),
pf.getTradeId(), currPrice,
gainorloss, percent, currValue));
// Compute the Total Asset Value
totalAssetValue = totalAssetValue + currValue;
}
}
} catch(FinderException ex) {
throw (new RemoteException("Finder Exception in get Portfolio : " +
ex.toString()));
} catch(DatabaseException ex) {
throw (new RemoteException("SQL Exception in get Portfolio : " +
ex.toString()));
}
Collection retColl = createNewCollection(pfValue, recordNumber, linesPerPage);
retColl.add(new Float(totalAssetValue));
// return the new collection created
return retColl;
}
/**
* This method calls the methods on the Entity Beans to get the Trade details
* corresponding to the input User Account Number and returns the details
* as a Collection.
*
* @param accountNumber Account Number
* @param recordNumber Record Number
* @param linesPerPage Lines Per Page
* @return The Collection of Trade Details for the input Account Number
* @exception RemoteException If error occurs while getting the details
* @since 1.0
*/
public Collection getTradeDetails(Integer accountNumber, String recordNumber,
int linesPerPage)
throws RemoteException {
Collection allTrades = new ArrayList();
Collection tdColl = null;
try {
// Find the User Account Entity for the input account number
UserAccountLocal userAccountLocal =
userAccountHomeLocal.findByPrimaryKey(accountNumber);
// Get the Collection of TradeDetails corresponding to the user account
tdColl = (Collection)userAccountLocal.getTradeDetails();
// Create an iterator for the above Collection
Iterator tradesIter = tdColl.iterator();
while(tradesIter.hasNext()) {
// If the iterator has more values, get the next tradedetails info
TradeDetailsLocal td = (TradeDetailsLocal)tradesIter.next();
String action = "Sold";
// Set the full expansion of action depending on the value
if(td.getAction().equals("B")) {
action = "Bought";
} else if(td.getAction().equals("T")) {
action = "Transferred";
}
// Add the TradeDetails information to a new collection
allTrades.add(new TradeDetailsInfo(td.getTradeId(), action,
td.getQuantity(), td.getTradeDate(),
td.getPrice(), td.getSymbol(),
td.getOrderType(), td.getStatus(),
td.getExchangeId()));
}
} catch(FinderException ex) {
throw (new RemoteException("Finder Exception in get trade details : " +
ex.toString()));
}
// return the new collection created
return createNewCollection(allTrades, recordNumber, linesPerPage);
}
/**
* This method calls the methods on the Entity Bean and returns the Trade
* details for the input trade id as a Value Object which reduces the
* network traffic between the client and the server.
*
* @param tradeId Trade Id
* @return The Trade Details information as a Value Object TradeDetailsInfo
* @exception RemoteException If error occurs while getting the details
* @since 1.0
*/
private TradeDetailsInfo getTradeDetailsInfo(Integer tradeId)
throws RemoteException {
TradeDetailsInfo tdInfo = null;
try {
// Find the Trade Details Entity for the input trade id
TradeDetailsLocal tradeDetailsLocal =
tradeDetailsHomeLocal.findByPrimaryKey(tradeId);
// Create a new object with the obtained Trade Details Entity
tdInfo = new TradeDetailsInfo(tradeDetailsLocal.getTradeId(),
tradeDetailsLocal.getAction(),
tradeDetailsLocal.getQuantity(),
tradeDetailsLocal.getTradeDate(),
tradeDetailsLocal.getPrice(),
tradeDetailsLocal.getSymbol(),
tradeDetailsLocal.getOrderType(),
tradeDetailsLocal.getStatus(),
tradeDetailsLocal.getExchangeId());
} catch(FinderException ex) {
throw (new RemoteException("Finder Exception in get trade details info : "
+ ex.toString()));
}
return tdInfo;
}
/**
* This method calls the methods on the Entity Bean to insert a new record
* in the PORTFOLIO table.
*
* @param accountNumber Account Number
* @param pfInfo Portfolio Information
* @return The Line Number of the new Portfolio record created
* @exception RemoteException If error occurs while adding Portfolio
* @since 1.0
*/
private Integer addPortfolio(Integer accountNumber,
PortfolioInfo pfInfo)
throws RemoteException {
Collection pfColl = null;
Integer nextLineNo = new Integer(0);
int rowCount = 0;
try {
// Find the User Account Entity for the input account number
UserAccountLocal userAccountLocal =
userAccountHomeLocal.findByPrimaryKey(accountNumber);
// Get the Collection of Portfolio corresponding to the user account
pfColl = (Collection)userAccountLocal.getPortfolio();
rowCount = pfColl.size();
// Next Line Number is the next row count
nextLineNo = new Integer(rowCount + 1);
// get the next portfolio id
Integer nextPortfolioId = tradeHelper.getNextID("PORTFOLIO_SEQ");
if(nextPortfolioId.intValue() == 0) {
throw (new CreateException("Unable to set Portfolio details, " +
"PORTFOLIO_SEQ not found : "));
}
// Create a new Portfolio Entity
PortfolioLocal portfolioLocal =
portfolioHomeLocal.create(accountNumber,
nextPortfolioId,
nextLineNo,
pfInfo.getQuantity(),
pfInfo.getPrice(),
pfInfo.getSymbol(),
pfInfo.getPurchaseDate(),
pfInfo.getPurchaseMode(),
pfInfo.getTradeId());
// add the entity to the new collection
pfColl.add(portfolioLocal);
// Set the collection to the User Account Entity
userAccountLocal.setPortfolio(pfColl);
} catch(FinderException ex) {
throw (new RemoteException("Finder Exception in Add Portfolio : " +
ex.toString()));
} catch(CreateException ex) {
throw (new RemoteException("Create Exception in Add Portfolio : " +
ex.toString()));
} catch(DatabaseException ex) {
throw (new RemoteException("SQL Exception in Add Portfolio : " +
ex.toString()));
}
return nextLineNo;
}
/**
* This method is used to update the quantity of stocks in the PORTFOLIO
* Table once some quantity or all the stocks are sold. This method is
* called when the user chooses to sell stocks.
*
* @param accountNumber Account Number
* @param pfInfo Portfolio Information
* @return String indicating the success of the update operation
* @exception RemoteException If error occurs while updating Portfolio
* @since 1.0
*/
private String updatePortfolio(Integer accountNumber,
PortfolioInfo pfInfo)
throws RemoteException {
Collection allPortfolio = new ArrayList();
Collection pfColl = null;
String retValue = "SUCCESS";
boolean found = false;
try {
// Find the User Account Entity for the input account number
UserAccountLocal userAccountLocal =
userAccountHomeLocal.findByPrimaryKey(accountNumber);
// Get the Collection of Portfolio corresponding to the user account
pfColl = (Collection)userAccountLocal.getPortfolio();
// Create an iterator for the above Collection
Iterator pfIter = pfColl.iterator();
boolean set = true;
while (pfIter.hasNext()) {
// If the iterator has more values, get the next portfolio information
PortfolioLocal pf = (PortfolioLocal)pfIter.next();
if (pf.getLineNo().equals(pfInfo.getLineNo())) {
found = true;
if (pf.getQuantity().intValue() >= pfInfo.getQuantity().intValue()) {
// Update the quantity of the line number
// which is in the input portfolio collection
int newQty = 0;
newQty = pf.getQuantity().intValue() -
pfInfo.getQuantity().intValue();
if (newQty == 0) {
pf.remove();
set = false;
} else {
pf.setQuantity(new Integer(newQty)); // Set the new quantity
}
} else {
retValue = "INSUFFICIENTQUANTITY";
}
}
if (set) {
allPortfolio.add(pf); // Add the record to the new collection
} else {
set = true;
}
}
// Set the collection to the User Account Entity
userAccountLocal.setPortfolio(allPortfolio);
} catch(FinderException ex) {
throw (new RemoteException("Finder Exception in update Portfolio : " +
ex.toString()));
} catch(RemoveException ex) {
throw (new RemoteException("Remove Exception in update Portfolio : " +
ex.toString()));
}
if (!found) {
retValue = "RECORDNOTFOUND";
}
return retValue;
}
/**
* This method is called while creating a new record in the TRADEDETAILS
* table. This is called whenever any 'Buy', 'Sell' or 'Transfer'
* transaction is carried out by a user. The new record is created for the
* input user account number with the input tradedetails information.
*
* @param accountNumber Account Number
* @param tdInfo Trade Details Information
* @return The Next Trade Id of the Trade Details record created.
* @exception RemoteException If error occurs while adding TradeDetails
* @since 1.0
*/
private Integer addTradeDetails(Integer accountNumber,
TradeDetailsInfo tdInfo)
throws RemoteException {
Collection tdColl = null;
Integer nextTradeId = new Integer(0);
try {
// Find the User Account Entity for the input account number
UserAccountLocal userAccountLocal =
userAccountHomeLocal.findByPrimaryKey(accountNumber);
// Get the Collection of TradeDetails corresponding to the user account
tdColl = (Collection)userAccountLocal.getTradeDetails();
// Get the next trade id number
nextTradeId = tradeHelper.getNextID("TRADE_SEQ");
Symbol s = tradeHelper.getSymbol(tdInfo.getSymbol());
// Create a new Trade Details entity
TradeDetailsLocal tradeDetailsLocal =
tradeDetailsHomeLocal.create(nextTradeId, accountNumber, tdInfo.getAction(),
tdInfo.getQuantity(), tdInfo.getTradeDate(),
tdInfo.getPrice(), tdInfo.getSymbol(),
tdInfo.getOrderType(), tdInfo.getStatus(),
new Integer(s.getExchangeid().intValue()));
// Add the Trade details Entity to the new collection
tdColl.add(tradeDetailsLocal);
// Set the collection to the User Account Entity
userAccountLocal.setTradeDetails(tdColl);
} catch(FinderException ex) {
throw (new RemoteException("Finder Exception in Add Trade Details : " +
ex.toString()));
} catch(CreateException ex) {
throw (new RemoteException("Create Exception in Add Trade Details : " +
ex.toString()));
} catch(DatabaseException ex) {
throw (new RemoteException("SQL Exception in Add Trade Details : " +
ex.toString()));
}
return nextTradeId;
}
/**
* This method is used to check the available balance in the user account
* whenever a 'Buy' or 'Sell' transaction is carried out by the user.
*
* @param accountNumber Account Number
* @param amount Amount to be added or subtracted
* @return Boolean value indicating whether the account balance can be
* updated or not
* @exception RemoteException If error occurs while checking account balance
* @since 1.0
*/
private boolean checkBalance(Integer accountNumber, float amount)
throws RemoteException {
boolean flag = false;
try {
// Find the User Account Entity for the input account number
UserAccountLocal userAccountLocal =
userAccountHomeLocal.findByPrimaryKey(accountNumber);
// Get the account balance for the user
float availableBalance = userAccountLocal.getAccountBalance();
// Check if there is enough balance
if (amount <= availableBalance) {
flag = true;
}
} catch(FinderException ex) {
throw (new RemoteException("Finder Exception in Update Balance : " +
ex.toString()));
}
return flag;
}
/**
* This method is used to update the available balance in the user account
* whenever a 'Buy' or 'Sell' transaction is carried out by the user. If the
* input operation is 'ADD', the input amount is added to the User's account.
* Else if it is 'SUB', the balance is checked for availability before
* subtracting the amount.
*
* @param accountNumber Account Number
* @param amount Amount to be added or subtracted
* @param operation Valid Values : 'ADD', 'SUB'
* @return Boolean value indicating whether the account balance was
* updated or not
* @exception RemoteException If error occurs while updating account balance
* @since 1.0
*/
private boolean checkAndUpdateBalance(Integer accountNumber,
float amount,
String operation)
throws RemoteException {
boolean flag = false;
try {
// Find the User Account Entity for the input account number
UserAccountLocal userAccountLocal =
userAccountHomeLocal.findByPrimaryKey(accountNumber);
// Get the account balance for the user
float availableBalance = userAccountLocal.getAccountBalance();
if(operation.equals("SUB")) {
// In case of subtraction, check if there is enough balance
if(amount <= availableBalance) {
// subtract amount
userAccountLocal.setAccountBalance(availableBalance - amount);
flag = true;
}
} else {
if(operation.equals("ADD")) {
// add amount
userAccountLocal.setAccountBalance(availableBalance + amount);
flag = true;
}
}
} catch(FinderException ex) {
throw (new RemoteException("Finder Exception in Update Balance : " +
ex.toString()));
}
return flag;
}
/**
* This method is called from TradeManagementHelper when the user buys
* stock using the application. If the User has enough balance to carry out
* the transaction, the trade amount is deducted from his account and
* Portfolio Details, Trade Details are added. The Order Details are sent by
* calling the method sendOrderDetails. If the available balance for the user
* is not sufficient for the transaction, corresponding message is returned.
* If everything goes through fine, 'SUCCESS' message is returned.
* Else On 'FAILURE', corresponding message is returned.
*
* @param accountNo Account Number
* @param quantity Quantity of stocks bought
* @param symbol Stock Symbol
* @param isTraded Boolean indicating if the symbol is traded or not
* @param userEmail Email id of the Logged in User
* @param systemDate Formatted String containing value of System Date
* @return The return message indicates whether the buy transaction
* was a success or failure.
* @exception RemoteException If error occurs while adding details
* @exception SQLException If error occurs while getting data
* @since 1.0
*/
public String buyStock(Integer accountNo, Integer quantity,
String symbol, boolean isTraded,
String userEmail, Hashtable connHash, String systemDate)
throws RemoteException, SQLException {
String retMessage = "FAILURE";
float price = tradeHelper.getLatestRate(symbol).getLowprice().floatValue();
// Additional check though the check exists in Helper class itself
if (price == 0) {
retMessage = "Invalid Symbol, Unable to get the Stock Rate for the Symbol";
return retMessage;
}
float amount = quantity.floatValue() * price; // Calculate the trade amount
Date sysDate = new Date(); // Current Date
boolean updateFlag = false;
// The following steps are involved in the buy transaction
// 1) If the User's Account Balance is sufficient for the trade,
// the trade amount is deducted from the available balance
// and further trade processing is carried out as in Step 2)
// Else the corresponding error message is returned
// 2) TradeDetails, Portfolio record is created with the stock information
// 3) The Order Details are sent for further processing
// 4) If the send is successful, the buy transaction is complete
// and 'SUCCESS' message is returned, Else the corresponding
// error message is returned.
// The buy transaction is not complete until all the steps are carried
// out successfully. Hence if any exception occurs during the transaction,
// the previous steps have to be rolled back. This is achieved using the
// setRollbackOnly() method on the SessionContext. This ensures that in
// case of exception or failure, no changes are committed to the database.
try {
// Check the available balance, the return flag indicates
// whether the balance can be updated or not
updateFlag = checkBalance(accountNo, amount);
} catch(RemoteException re) {
throw new RemoteException("Remote Exception while checking balance : " +
re.toString());
}
int retVal;
if(updateFlag) { // If the available balance permitted the transaction
Integer nextTradeId = new Integer(0);
// Create TradeDetailsInfo object
TradeDetailsInfo tdInfo = new TradeDetailsInfo(null, "B", quantity,
sysDate, price, symbol,
"M", "O", null);
try {
// Add new trade details info for the account number and return the trade id
nextTradeId = addTradeDetails(accountNo, tdInfo);
} catch(RemoteException re) {
sc.setRollbackOnly();
throw new RemoteException("Remote Exception while adding tradedetails: "
+ re.toString());
} catch(Exception ex) {
sc.setRollbackOnly();
throw new RemoteException("Exception while adding tradedetails: " +
ex.toString());
}
Integer nextId = new Integer(0);
// If the trade id has a valid value
if(nextTradeId.intValue() > 0) {
// Create PortfolioInfo object
PortfolioInfo pfInfo = new PortfolioInfo(null, quantity, price,
symbol, null, sysDate,
"P", nextTradeId);
try {
// Add new portfolio info for the account number and return the next id
nextId = addPortfolio(accountNo, pfInfo);
} catch(RemoteException re) {
sc.setRollbackOnly();
throw new RemoteException("Remote Exception while adding Portfolio : "
+ re.toString());
}
try {
// The Order Details are sent to the Exchange.
// The Trade is successful only if the send is successful
retVal = sendOrderDetails(accountNo, symbol, "B",
quantity, isTraded, userEmail,
connHash, systemDate);
if (retVal < 0) {
sc.setRollbackOnly();
retMessage = "Failed to contact another FBS " +
"while trying to buy the stock";
}
} catch(RemoteException re) {
retVal = -1;
sc.setRollbackOnly();
throw new RemoteException("Remote Exception while adding Portfolio : "
+ re.toString());
}
if (retVal >= 0) {
boolean checkFlag = checkAndUpdateBalance(accountNo, amount, "SUB");
// Check if available balance permits the transaction to complete
// Return flag indicates whether the balance was updated or not
if (checkFlag) {
retMessage = "SUCCESS";
} else {
retMessage = "Cannot Proceed with the transaction, " +
"Not enough Account Balance";
sc.setRollbackOnly();
}
}
}
if (retMessage.equals("FAILURE")) {
retMessage = "Failed to carry out the transactions";
}
} else {
retMessage = "Cannot Proceed with the transaction, " +
"Not enough Account Balance";
}
return retMessage;
}
/**
* This method is called by TradeManagementHelper and is used by the
* AdminHelper to transfer stocks from a corporate to an individual.
* Portfolio details and Trade Details are added.
* The Order Details are sent by calling the method sendOrderDetails.
*
* @param toAccountNumber Account Number to which Stocks are
* to be transferred
* @param symbol Stock Symbol
* @param qty Quantity
* @param price Price
* @param isTraded Boolean indicating if the symbol
* is traded or not
* @param userEmail Email id of the Logged in User
* @param systemDate Formatted String containing value of System Date
* @return An integer indicating success or failure,
* 0 indicates success and -1 indicates failure
* @exception RemoteException If error occurs while adding details
* @since 1.0
*/
public Integer corporateTransfer(Integer toAccountNumber, String symbol,
Integer qty, Float price, boolean isTraded,
String userEmail, Hashtable connHash,
String systemDate)
throws RemoteException {
Integer nextTradeId = new Integer(0);
int retVal = 0;
try {
Date sysDate = new Date(); // Current Date
// Calculate the trade amount
float amount = qty.intValue() * price.floatValue();
// The corporate transfer process involves the following steps :
// 1) New TradeDetails and Portfolio are created
// 2) The Order Details are sent for further processing
// 3) If the send is successful, the transaction is complete and
// '0' is returned indicating success, Else -1 is returned.
// The corporate transfer transaction is not complete until all the steps
// are carried out successfully. Hence if any exception occurs during the
// transaction, the previous steps have to be rolled back. This is achieved
// using the setRollbackOnly() method on the SessionContext. So in case of
// exception or failure, the transactions are rolled back.
// Create TradeDetailsInfo object
TradeDetailsInfo tdInfo = new TradeDetailsInfo(null, "T", qty, sysDate,
price.floatValue(), symbol,
"M", "O", null);
// Add new trade details info for the account number
// and return the next trade id
nextTradeId = addTradeDetails(toAccountNumber, tdInfo);
// Create PortfolioInfo object
PortfolioInfo pfInfo = new PortfolioInfo(null, qty, price.floatValue(),
symbol, null, sysDate, "P",
nextTradeId);
Integer nextId;
// Add new portfolio info for the account number and return the next id
nextId = addPortfolio(toAccountNumber, pfInfo);
// The Order Details are sent to the Exchange.
// The Trade is successful only if the send is successful
retVal = sendOrderDetails(toAccountNumber, symbol, "T", qty, isTraded,
userEmail, connHash, systemDate);
if (retVal < 0) {
sc.setRollbackOnly();
nextTradeId = new Integer(0);
}
} catch(RemoteException ex) {
throw new RemoteException("Exception in corporate transfer " +
"while sending order details : \n" +
ex.toString());
}
return nextTradeId;
}
/**
* This method is called from TradeManagementHelper when the user sells stock
* using the application. The trade amount is calculated after getting the
* current stock price. Trade Details are added. Portfolio details are updated.
* The User's Available balance is updated depending on whether the stock was
* bought by the user or obtained through corporate transfer. In case of
* corporate transfer only the difference between the current stock value and
* the stock value when it was transferred (ie,. the profit) is updated.
* In case the profit is negative, the balance remains unchanged.
* Order Details are sent by calling the method sendOrderDetails.
*
* @param accountNo User Account Number
* @param qtyValues String Array of Stock Quantity Values
* @param lineNos String Array of Line Numbers
* @param symbols String Array of Stock Symbols
* @param tradeId String Array of Trade Id
* @param isTradedFlag String Array containing 'Y' if the symbol is traded
* or 'N' if not
* @param userEmail Email id of the Logged in User
* @param systemDate Formatted String containing value of System Date
* @return The return message is a string which contains details
* about the failure or success of the sell transactions.
* @exception RemoteException If error occurs while adding details
* @exception SQLException If error occurs while getting data
* @since 1.0
*/
public String sellStock(Integer accountNo, String[] qtyValues,
String[] lineNos, String[] symbols,
String[] tradeId, String[] isTradedFlag,
String userEmail, Hashtable connHash,
String systemDate)
throws RemoteException, SQLException {
Integer quantity = new Integer(0);
int retVal = 0;
String retMessage = "";
StringBuffer sb = new StringBuffer();
Date sysDate = new Date(); // Current Date
for (int i = 0;i < lineNos.length;i++) {
retMessage = "FAILURE";
if (!qtyValues[i].equals("")) {
Integer nextTradeId = new Integer(0);
float price = tradeHelper.getLatestRate(symbols[i]).getLowprice().floatValue();
try {
quantity = new Integer(qtyValues[i]);
} catch (NumberFormatException nfe) {
retMessage = "INVALIDQUANTITY";
sb.append("Failed to sell stocks of '");
sb.append(symbols[i]);
sb.append("' : Invalid Quantity! \n");
}
String retValue = "FAILURE";
TradeDetailsInfo tdInfo = null;
// The following steps are involved in the sell transaction
// 1) TradeDetails, Portfolio record is created with the stock information
// 2) The User's Account Balance is updated with the trade amount
// 3) The Order Details are sent for further processing
// 4) If the send is successful, the sell transaction is complete,
// and 'SUCCESS' message is appended to the return string,
// Else the corresponding error message is appended.
// 5) The above steps are repeated for all the records.
// The sell transaction is not complete until all the steps are
// carried out successfully. Hence if any exception occurs during
// the transaction, the previous steps have to be rolled back.
// This is achieved using the setRollbackOnly() method on the SessionContext.
// So in case of exception or failure, the transactions are rolled back.
if (!(retMessage.equals("INVALIDQUANTITY"))) {
// Proceed further only if the quantity is valid
// Create TradeDetailsInfo object
tdInfo = new TradeDetailsInfo(null, "S", quantity,
sysDate, price,
symbols[i], "M",
"O", null);
try {
// Add new trade details info for the account number
// and return the trade id
nextTradeId = addTradeDetails(accountNo, tdInfo);
} catch(RemoteException re) {
sc.setRollbackOnly();
throw new RemoteException("Remote Exception while adding " +
"tradedetails sellStock: " +
re.toString());
}
// Create PortfolioInfo object
PortfolioInfo pfInfo = new PortfolioInfo(Integer.valueOf(lineNos[i]),
quantity, price, symbols[i],
null, sysDate, "P", nextTradeId);
// If the trade id has a valid value
if (nextTradeId.intValue() > 0) {
try {
// Update portfolio info for the Account Number
retValue = updatePortfolio(accountNo, pfInfo);
} catch(RemoteException re) {
sc.setRollbackOnly();
throw new RemoteException("Remote Exception while updating " +
"Portfolio details sellStock: " +
re.toString());
}
if (retValue.equals("INSUFFICIENTQUANTITY") ||
retValue.equals("RECORDNOTFOUND")) {
sc.setRollbackOnly();
sb.append("Failed to sell stocks of '");
sb.append(symbols[i]);
sb.append("' : Stocks possibly sold in another transaction! \n");
}
}
}
if (retValue.equals("SUCCESS")) {
boolean updateFlag = false;
float amount = 0;
tdInfo = null;
try {
// Get the TradeDetailsInfo corresponding to the stock which is being sold
tdInfo = getTradeDetailsInfo(new Integer(tradeId[i]));
} catch(RemoteException re) {
sc.setRollbackOnly();
throw new RemoteException("Remote Exception while getting TradeDetails Info : "
+ re.toString());
}
// Calculate amount depending on whether the stocks were purchased or
// transferred through corporate transfer
// In case of purchase, amount = (quantity * current price)
// In case of corporate transfer,
// amount = (quantity * (current price - buy price)
if (tdInfo.getAction().equals("B")) {
amount = quantity.floatValue() * price;
} else if(tdInfo.getAction().equals("T")) {
// Stocks transferred through corporate transfer
amount = quantity.floatValue() * (price - tdInfo.getPrice());
// Only profit has to be considered
if (amount < 0) {
amount = 0;
}
}
try {
// Check the available balance and add the trade amount
// Return flag indicates whether the balance was updated or not
updateFlag = checkAndUpdateBalance(accountNo, amount, "ADD");
} catch(RemoteException re) {
sc.setRollbackOnly();
throw new RemoteException("Remote Exception while updating balance : "
+ re.toString());
}
boolean isTraded = false;
if (isTradedFlag[i].equals("Y")) {
isTraded = true;
}
// The Order Details are sent to the Exchange.
// The Trade is successful only if the send is successful
retVal = sendOrderDetails(accountNo, symbols[i], "S", quantity,
isTraded, userEmail, connHash, systemDate);
if (retVal < 0) {
// If send is not successful, the transaction has to be rolled back
sc.setRollbackOnly();
} else {
retMessage = "SUCCESS";
}
if(retMessage.equals("SUCCESS")) {
sb.append("Transaction completed. ");
sb.append(qtyValues[i]);
sb.append(" stocks of '");
sb.append(symbols[i]);
sb.append("' sold at the price : $");
sb.append(price);
sb.append(" per share \n");
} else {
sb.append("Failed to sell stocks of '");
sb.append(symbols[i]);
sb.append("' \n");
}
}
}
}
return sb.toString();
}
/**
* This method is called when any user transaction ie,. 'Buy', 'Sell' or
* 'Transfer' happens. The trade details are sent to the stock exchange. The
* Stock exchange is a JMS queue which provides asynchronous processing for
* conducting trade. The exchange process the trade request and sends a
* notification to the user.
*
* @param accountNo User Account Number
* @param symbol Stock Symbol
* @param tradeType Trade Type
* Valid Values : 'B' for 'Buy', 'S' for 'Sell', 'T' for 'Transfer'
* @param quantity Quantity
* @return Returns an int indicating success or failure :
* 0 for success and -1 for failure
* @exception RemoteException If error occurs while sending the Order Details
* @since 1.0
*/
private int sendOrderDetails(Integer accountNo, String symbol,
String tradeType, Integer quantity,
boolean isTraded, String userEmail,
Hashtable connHash, String tradeDt)
throws RemoteException {
int retVal = 0;
if (isTraded) {
QueueSession session = null;
QueueConnection qconn = null;
try {
// Send a message to the exchange with trade details
if(exchageQConnfactory == null || exchangeQueue == null) {
InitialContext ic = new InitialContext();
exchageQConnfactory = (QueueConnectionFactory)ic.lookup(
(String)connHash.get("EXCHQCONNFACTORY"));
exchangeQueue = (Queue)ic.lookup((String)connHash.get("EXCHQUEUE"));
}
// Create Queue Connection
qconn = exchageQConnfactory.createQueueConnection();
qconn.start();
session = qconn.createQueueSession(false, 1);
// Create a message and set the trade details
Message message = session.createMessage();
message.setStringProperty("toAddress", userEmail);
message.setStringProperty("symbol", symbol);
message.setStringProperty("tradeDate", tradeDt);
message.setStringProperty("tradeType", tradeType);
message.setStringProperty("qty", quantity.toString());
message.setJMSExpiration(18000L);
// Send the message
session.createSender(exchangeQueue).send(message);
} catch (Exception ex) {
throw new RemoteException(" sendOrderDetails failed :"+
ex.toString());
} finally {
try {
if(session != null)
session.close();
if(qconn != null)
qconn.close();
} catch(JMSException je) {
retVal = -1;
throw new RemoteException("JMSException : while closing session" + je.toString());
}
}
} else {
retVal = -2;
}
return retVal;
}
/**
* Other Standard Call Back Methods
* @since 1.0
*/
public void ejbRemove() {}
public void ejbActivate() {
initialize(); // To avoid de-serialization errors
}
public void ejbPassivate() {}
public void setSessionContext(SessionContext cntx) {
sc = cntx;
}
}