/*
 * @author : Umesh Kulkarni
 * @version 1.0
 *
 * Development Environment : Oracle9i JDeveloper
 *
 * Name of the File : UserManagementSessionFacadeBean.java
 *
 * Creation / Modification History
 *    Umesh           26-Apr-2002        Created
 *    Pushkala        01-Aug-2003        Modified and added Timer functionality
 *
 */
package oracle.otnsamples.ibfbs.usermanagement.ejb;

// Import Required Packages
import javax.naming.InitialContext; // JNDI
import java.rmi.RemoteException;    // RMI

// EJB Classes
import javax.ejb.CreateException;
import javax.ejb.FinderException;

// Utility Classes
import java.util.Collection;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Date;

// Timer related classes
import javax.ejb.TimedObject;
import javax.ejb.TimerService;
import javax.ejb.Timer;
import javax.ejb.TimerHandle;
import javax.ejb.SessionContext;

import oracle.toplink.exceptions.DatabaseException;

import oracle.otnsamples.ibfbs.admin.ejb.MailServiceHome;
import oracle.otnsamples.ibfbs.admin.ejb.MailService;

// UserManagement Classes
import oracle.otnsamples.ibfbs.usermanagement.ejb.AlertsLocal;
import oracle.otnsamples.ibfbs.usermanagement.ejb.PreferencesLocal;
import oracle.otnsamples.ibfbs.usermanagement.ejb.AlertsHomeLocal;
import oracle.otnsamples.ibfbs.usermanagement.ejb.PreferencesHomeLocal;
import oracle.otnsamples.ibfbs.usermanagement.ejb.UserAccountHomeLocal;
import oracle.otnsamples.ibfbs.usermanagement.ejb.UserAccountLocal;
import oracle.otnsamples.ibfbs.usermanagement.ejb.AlertsInfo;
import oracle.otnsamples.ibfbs.usermanagement.ejb.AccountInfo;
import oracle.otnsamples.ibfbs.usermanagement.ejb.TimerInfo;
import oracle.otnsamples.ibfbs.usermanagement.ejb.PreferencesInfo;

// TradeManagement Classes
import oracle.otnsamples.ibfbs.trademanagement.ejb.TradeDetailsHomeLocal;
import oracle.otnsamples.ibfbs.trademanagement.ejb.TradeDetailsLocal;


/**
 * This Class is the Bean implementation of UserManagementSession Bean.
 * This Session Facade Bean coordinates all the User Management Related
 * Functionality and client invokes all this functionality only through
 * this Session Bean.
 *
 * In order to provide the User Management Functionality such as
 *   a) Create a New Account, b) Add/Delete/Change Preferences
 *   c) Add/Delete/Change Alerts etc, this Bean takes the help of "UserAccount"
 *      Local EJB Object.
 *
 * UserAccount Local EJB Object has 1:N relationship with other Local entity
 * beans namely PreferencesBean, AlertsBean, PortfolioBean and TradeDetails Bean.
 * 
 * This Bean also implements the EJB2.1 feature : Timer Service on StateLess 
 * Session Bean (SLSB) by implementing the TimedObject and ejbTimeOut method() 
 * on it. The FBS Administrator is associated with a Timer to send periodic 
 * Report of the Trading activity on the site. The timeout interval can be set 
 * by logging on as FBS Adminstrator.
 * 
 * @see UserHelper.java
 */
public class UserManagementSessionFacadeBean
    implements javax.ejb.SessionBean, TimedObject {

  // Reference to Local Home Interface of UserAccountBean
  private   UserAccountHomeLocal  userAccountHomeLocal  = null; 

  // Reference to Local Home Interface of PreferencesBean
  private   PreferencesHomeLocal  preferencesHomeLocal  = null; 

  // Reference to Local Home Interface of AlertsBean                                        
  private   AlertsHomeLocal       alertsHomeLocal       = null; 

  // Reference to Local Home Interface of TradeDetailsBean                                    
  private   TradeDetailsHomeLocal tradedetailsHomeLocal = null; 
  
  private   String                notificationMailId    = null;

  // Handle of the Timer (Serializable)
  private   TimerHandle           timerHandle           = null; 

  // Session Context 
  private   SessionContext        sctx;   
  
  private   UserHelper            helper                = null;

  /**
   * Method which is called by the EJB Container, when the client invokes
   * create() method on the Home Interface namely
   * 'UserManagementSessionHomeRemote.java. This method initializes the
   * values of the references to Home Interfaces for later use.
   *
   * @since 1.0
   */
  public void ejbCreate() {
    try {
      InitialContext ic = new InitialContext();

      // Look Up the Local Home Interfaces
      userAccountHomeLocal =
      (UserAccountHomeLocal)ic.lookup("java:comp/env/ejb/UserAccountHomeLocal");

      preferencesHomeLocal =
      (PreferencesHomeLocal)ic.lookup("java:comp/env/ejb/PreferencesHomeLocal");

      alertsHomeLocal      =
      (AlertsHomeLocal) ic.lookup("java:comp/env/ejb/AlertsHomeLocal");

      tradedetailsHomeLocal =
      (TradeDetailsHomeLocal)ic.lookup("java:comp/env/ejb/TradeDetailsHomeLocal");

      helper = UserHelper.getInstance();
      
      notificationMailId = (String) ic.lookup("NTFY_MAIL");
      
    } catch (javax.naming.NamingException ne) {
      ne.printStackTrace();
    }
  }

  /**
   * Method to create a New User Account. After creating the new User Account,
   * the account Number is returned back. Note that the parameters passed to
   * this method are Value Objects.
   *
   * @param  contactInfo The Contact Information Value Object
   *         For more information @see ContactInfo.java
   * @param  accountInfo The account Information Value Object
   *         For more information @see AccountInfo.java
   * @return Account Number of created Account.
   * @exception RemoteException Exception raised when error occurs during
   *           creating a new account
   * @since 1.0

   */
  public Integer createNewAccount(ContactInfo contactInfo,
                                  AccountInfo accountInfo) throws RemoteException {

    Integer accountNumber = new Integer(0);

    try {
      // Get the Next Account Number Value from the Database Sequence
      accountNumber = helper.getNextID("ACCOUNTNUMBER_SEQ");

      // Create the New Account By invoking Method on the
      // UserAccount Local EJB Object
      UserAccountLocal userAccountLocal = userAccountHomeLocal.create(accountNumber,
                           accountInfo.getPassword(),
                           contactInfo.getFirstName(),
                           contactInfo.getLastName(),
                           contactInfo.getOrganization(),
                           contactInfo.getAddress(),
                           contactInfo.getCity(),
                           contactInfo.getState(),
                           contactInfo.getCountry(),
                           contactInfo.getPhone(),
                           accountInfo.getAccountBalance(),
                           contactInfo.getEmail(),
                           accountInfo.getUserType(),
                           accountInfo.getLinesPerPage(),
                           accountInfo.getAlertMode(),
                           contactInfo.getMobileEmail());

    } catch (CreateException ex) {  // Trap Creation Errors
      System.err.println("Create Exception = " + ex.toString());
      accountNumber = new Integer(-1);
      throw new RemoteException("Exception While creating account : " +
           ex.toString());
    } catch (DatabaseException ex) { // Trap SQL Errors
      throw new RemoteException("SQL Exception While Creating New Account : " +
          ex.toString());
    }

    return accountNumber;
  }

  /**
   * Method to get Contact Information for a particular User. Note that the
   * parameters returned by this method is ContactInfo Value Object.
   *
   * @param accountNumber Account Number of the User Account.
   * @return  The Contact Information Value Object for the given user.
   *          For more information @see ContactInfo.java
   * @exception RemoteException If error occurs during getting Contact Info
   *
   * @since 1.0

   */
  public ContactInfo getContactInfo(Integer accountNumber) throws RemoteException {

    ContactInfo contactInfo = null;
    try {
      // Find the Appropriate User Account Bean
      UserAccountLocal userAccountLocal = userAccountHomeLocal.findByPrimaryKey(accountNumber);

      // Construct the Value Object contactInfo to be passed back to the client
      contactInfo = new ContactInfo(userAccountLocal.getFirstName(),
                                    userAccountLocal.getLastName(),
                                    userAccountLocal.getOrganization(),
                                    userAccountLocal.getAddress(),
                                    userAccountLocal.getCity(),
                                    userAccountLocal.getState(),
                                    userAccountLocal.getCountry(),
                                    userAccountLocal.getPhone(),
                                    userAccountLocal.getEmail(),
                                    userAccountLocal.getMobileEmail());
    } catch (FinderException ex) {  // Trap Any Errors While Finding Bean
      System.err.println("Finder Exception " + ex.toString());
      throw new RemoteException("Finder Exception While Getting Contact Info : " +
           ex.toString());
    }

    return contactInfo;  // return Contact Information
  }

  /**
   * Method to initialize Timer on the SLSB associated with Admin user 
   * to send periodic Trade updates to the Administrator.
   *
   * @param timeout Timeout value of the timer
   * @param info    Timer information
   *
   * @since 1.0
   */
  private void initializeTimer(long timeout, String info) {

    try {

      // Get an instance of TimerService associated with the 
      // SessionContext of the SLSB
      TimerService ts = sctx.getTimerService();

      // If there are existing Timer associated with this Bean,
      // Cancel it before initializing another one
      checkAndCancelTimers(ts.getTimers());

      long initialDuration  = timeout;
      long intervalDuration = timeout;

      // Create Timer with required interval
      // The timeout value is also set in the infor field for convenience
      Timer timer = ts.createTimer(initialDuration, intervalDuration, 
                                   info + " Timeout : " + timeout);

      // Store the TimerHandle which is seriablizable and which can be used 
      // to retrieve the timer values whenever required later
      timerHandle = timer.getHandle();

      System.out.println("Timer created at " +
                         new Date(System.currentTimeMillis()) +
                         " with a timeout: " + intervalDuration +
                         " ms and with info: " + info);

    } catch (Exception ex) {
      ex.printStackTrace();
    }

    return;

  }

  /**
   * Method to check and cancel timers if any on the SLSB.
   *
   * @param allTimers  Collection of all timers associated with this SLSB 
   *                   obtained from the TimerService
   *
   * @since 1.0
   */
  private void checkAndCancelTimers(Collection allTimers) {

    Iterator timerIter = allTimers.iterator(); // Set an iterator

    // Iterate through the collection
    while ( timerIter.hasNext() ) {

      Timer  timer = (Timer) timerIter.next(); // Get next timer

      try {
        timer.cancel(); // Cancel existing timers if any
      } catch (IllegalStateException ise) {
        // Ignore already cancelled timers
        System.out.println("Error cancelling timer : "+ise.toString());
      }
    }

  }

  /**
   * This method implements the callback method defined on the TimedObject 
   * interface. This method is called by the EJB container when the timer 
   * expires (goes off). This is used by the EJB container to deliver 
   * timer expiration notifications.
   * 
   * This method contains all the business logic to send timed reports of the 
   * trading activity on FBS site to the FBS Administrator.
   * 
   * Note : This method has to be implemented by an entity bean or stateless 
   * session bean or message-driven bean class which implements the TimedObject 
   * interface to process the timer expirations. 
   *
   * @param timer  The Timer instance which has expired
   *
   * @since 1.0
   */
  public void ejbTimeout(Timer timer) {

    Date   sysDate   = new Date(System.currentTimeMillis());
    Date   toDate    = new Date(System.currentTimeMillis() - timer.getTimeRemaining());

    // Get the timer information
    String timerInfo = (String) timer.getInfo();
    
    System.out.println("ejbTimeout() called at: " +
                        sysDate + " with info: " + timerInfo);

    // Call the method to send mail containing information of the 
    // trading activity on the FBS site to the administrator 
    sendMail(timerInfo, toDate, sysDate);

    return;
  }

  /**
   * Method to set the New Value of Contact Information for a particular User.
   * Note that the one of the parameter passed to this method is ContactInfo
   * Value Object.
   *
   * @param accountNumber Account Number of the User Account.
   * @param contactInfo  The Contact Information Value Object for the given user.
   *                     For more information @see ContactInfo.java
   * @return Status of this operation
   * @exception RemoteException If error occurs during setting Contact Info
   * @since 1.0

   */
  public String setContactInfo(Integer accountNumber, ContactInfo contactInfo)
       throws RemoteException {

    // Initialize Status Message
    String retMessage = "NOSUCCESS";
    try {

      // Find the User Account Local Entity Bean by passing the account Number
      // Primary Key
      UserAccountLocal userAccountLocal = userAccountHomeLocal.findByPrimaryKey(accountNumber);

      // Set the Contact Information
      userAccountLocal.setFirstName(contactInfo.getFirstName());
      userAccountLocal.setLastName(contactInfo.getLastName());
      userAccountLocal.setOrganization(contactInfo.getOrganization());
      userAccountLocal.setAddress(contactInfo.getAddress());
      userAccountLocal.setCity(contactInfo.getCity());
      userAccountLocal.setState(contactInfo.getState());
      userAccountLocal.setCountry(contactInfo.getCountry());
      userAccountLocal.setPhone(contactInfo.getPhone());
      userAccountLocal.setEmail(contactInfo.getEmail());
      userAccountLocal.setMobileEmail(contactInfo.getMobileEmail());

      retMessage = "SUCCESS";
    } catch (FinderException ex) {  // Trap Finder Errors
      System.out.println("Finder Exception " + ex.toString());
      throw new RemoteException("Finder Exception While Setting Contact Info : " +
           ex.toString());
    }

    return retMessage;  // Return Status Message
  }

  /**
   * Method to get Account Information for a particular User. Note that the
   * parameter returned by this method is AccountInfo Value Object.
   *
   * @param accountNumber Account Number of the User Account.
   * @return  The Account Information Value Object for the given user.
   *          For more information @see AccountInfo.java
   * @exception RemoteException If error occurs during getting Account Info
   * @since 1.0
   */
  public AccountInfo getAccountInfo(Integer accountNumber) throws RemoteException {

    AccountInfo accountInfo = null;
    try {

      // Find the User Account Local Entity Bean by passing the account Number
      // Primary Key
      UserAccountLocal userAccountLocal = userAccountHomeLocal.findByPrimaryKey(accountNumber);

      // Construct the Value Object which will be returned back.
      accountInfo = new AccountInfo(userAccountLocal.getPassword(),
                                    userAccountLocal.getUserType(),
                                    userAccountLocal.getAlertMode(),
                                    userAccountLocal.getAccountBalance(),
                                    userAccountLocal.getLinesPerPage());
    } catch (FinderException ex) {  // Trap Finder Errors
      System.err.println("Finder Exception " + ex.toString());
      throw new RemoteException("Finder Exception While Getting Account Info : " +
           ex.toString());
    }

    return accountInfo;
  }

  /**
   * Method to set the New Value of Account Information for a particular User.
   * Note that the one of the parameter passed to this method is AccountInfo
   * Value Object.
   *
   * @param accountNumber Account Number of the User Account.
   * @param accountInfo   The Account Information Value Object for the given user.
   *                      For more information @see AccountInfo.java
   * @return Status of this operation
   * @exception RemoteException If error occurs during setting Contact Info
   * @since 1.0

   */
  public String setAccountInfo(Integer accountNumber, AccountInfo accountInfo)
    throws RemoteException {

    // Initialize the Status Message
    String retMessage = "NOSUCCESS";
    try {

      // Find the User Account Local Entity Bean by passing the
      // Account Number Primary Key
      UserAccountLocal userAccountLocal = 
          userAccountHomeLocal.findByPrimaryKey(accountNumber);

      // Update the Account Information
      userAccountLocal.setAlertMode(accountInfo.getAlertMode());
      userAccountLocal.setLinesPerPage(accountInfo.getLinesPerPage());

      retMessage = "SUCCESS";
    } catch (FinderException ex) {  // Trap Errors While Finding EJB Object
      System.err.println("Finder Exception " + ex.toString());
      throw new RemoteException("Finder Exception While Setting Contact Info : " +
           ex.toString());
    }

    return retMessage;
  }

  /**
   * Method to get Timer Information for admin User. Note that the
   * parameter returned by this method is TimerInfo Value Object.
   *
   * @return  The Timer Information Value Object for the admin user.
   *          For more information @see TimerInfo.java
   * @exception RemoteException If error occurs during getting Timer Info
   * @since 1.0
   */
  public TimerInfo getAdminTimerInfo() throws RemoteException {

    TimerInfo timerInfo = null;

    if (timerHandle != null) {
      // If timer is already set, get the Timer Values 

      // When Timer is set for SLSB, the TimerHandle which is serializable 
      // is set in the bean. This timerhandle can be used at any point of time 
      // to get all the required information about the timer.
      Timer timer = timerHandle.getTimer();

      // The timeout value for the timer is set in the Info field of the timer.
      // Get the timeout value from the info field and convert the timeout in 
      // milliseconds to corresponding hour and minute values to be displayed 
      // to the user. 
      String totalinfo   = (String)timer.getInfo();
      String timeoutinfo = totalinfo.substring(totalinfo.indexOf(" : ") + 3);
      int    timeout     = Integer.parseInt(timeoutinfo);
      int    totalmins   = timeout / (60 * 1000);
      int    hour        = totalmins / 60;
      int    mins        = totalmins - (hour * 60);

      // Create TimerInfo value object with the hour and minute values
      // and return this to the user
      timerInfo = new TimerInfo(new Integer(hour), new Integer(mins));

    } else {
      // If timer is not already set, return the default values

      timerInfo = new TimerInfo(new Integer(0), new Integer(15));

    }

    return timerInfo;

  }

  /**
   * Method to retrieve Preferences for a given User Account. As a single user
   * account can have multiple preferences, this method returns a collection
   * of PreferencesInfo Value Object.
   *
   * @param accountNumber Account Number of the User Account.
   * @return A Collection of PreferencesInfo Value Object. This represents
   *         all the preferences for the User Account.
   * @exception RemoteException If error occurs during getting preferences
   * @since 1.0
   */
  public Collection getPreferences(Integer accountNumber) throws RemoteException {
    Collection allPreferences = new ArrayList();
    try {
      // Find the User Account Local Entity Bean by passing the
      // Account Number Primary Key
      UserAccountLocal userAccountLocal = userAccountHomeLocal.findByPrimaryKey(accountNumber);

      // Get all the preferences and Loop through them
      Iterator prefLocalIter = (userAccountLocal.getPreferences()).iterator();

      while (prefLocalIter.hasNext()) {
        // Get the next handle to PreferencesBean Local EJB Object
        PreferencesLocal preferencesLocal =
          (PreferencesLocal) prefLocalIter.next();

        // Add a new instance of PreferencesInfo Value Object to final list
        allPreferences.add(new PreferencesInfo(preferencesLocal.getSymbol(),
                                               preferencesLocal.getPrefType()));
      }
    } catch (FinderException ex) {  // Trap Errors While Finding EJB Objects
      System.err.println("Get Preferences Method : Finder Exception "
                         + ex.toString());
      throw new RemoteException("Finder Exception While Getting Preferences = " +
           ex.toString());

    }

    return allPreferences;  // Return All Preferences
  }

  /**
   * Method to add a Preference for a given User Account.
   *
   * @param accountNumber Account Number of the User Account.
   * @param preferencesInfo Value Object for Preferences Information
   * @return Status of this Operation
   * @exception RemoteException If error occurs during adding Preferences
   * @since 1.0

   */
  public String addPreferences(Integer accountNumber,
         PreferencesInfo preferencesInfo) throws RemoteException {
    // Flag indicating whether the Symbol Already Exists.
    boolean    symbolPresent  = false;
    boolean    validSymbol = false;
    ArrayList allPrefIter = null;
    String message = null;

    try {
      // Find the User Account Local Entity Bean by passing the account Number
      // Primary Key
      UserAccountLocal userAccountLocal = 
        userAccountHomeLocal.findByPrimaryKey(accountNumber);

      // Check Whether a Valid Symbol
      validSymbol = helper.checkSymbolExists(preferencesInfo.getSymbol());

      // Get All the Preferences for the User Account and loop through them
      allPrefIter = new ArrayList(userAccountLocal.getPreferences());
      int size = allPrefIter.size();
      int i    = 0;
      while (i < size ) {

        // Get the next handle to PreferencesBean Local EJB Object
        PreferencesLocal prefLocal = (PreferencesLocal) allPrefIter.get(i);

        // Check whether the Symbol already exists in the given set of
        // preferences
        if (prefLocal.getSymbol().equals(preferencesInfo.getSymbol())) {
          symbolPresent = true;
          //break;
        }

        allPrefIter.set(i,prefLocal);
        i++;
      }

      // If Symbol was not already present then add this preference to the
      // existing list of Preferences
      if (!symbolPresent && validSymbol) {
        PreferencesLocal preferencesLocal =
          preferencesHomeLocal.create(helper.getNextID("PREFERENCES_SEQ"),
                                      accountNumber,
                                      preferencesInfo.getSymbol(),
                                      preferencesInfo.getPreferenceType());

        allPrefIter.add(preferencesLocal);
      }

      userAccountLocal.setPreferences(allPrefIter);
    } catch (FinderException ex) {  // Trap the errors while finding the EJB
      System.err.println("Finder Exception : Add Preferences Method : "
                         + ex.toString());
      throw new RemoteException("Finder Exception While Adding Preferences = " +
           ex.toString());

    } catch (CreateException ex) {  // Trap Errors While Creating EJB
      System.err.println("Create Exception : Add Preferences Method : "
                         + ex.toString());
      throw new RemoteException("Create Exception While Adding Preferences = " +
           ex.toString());

    } catch (DatabaseException ex) { // Trap SQL Errors
      throw new RemoteException("SQL Exception While Creating New Account : " +
          ex.toString());
    }

    // If Symbol Already Exists then return a proper message
    if (!symbolPresent && validSymbol) {
      message = "SUCCESS";
    } else if (symbolPresent && validSymbol) {
      message = "Symbol Already Present. Can not add. Change it";
    } else if (!validSymbol) {
      message = "Improper Symbol Value. Please Enter proper Symbol Value";
    }
    return message;
  }


  /**
   * Method to Update a Single Preference Record for a given User Account.
   *
   * @param accountNumber Account Number of the User Account.
   * @param prefInfo      Value Object Encapsulating Updated preferences
   *                      Information
   * @return  Status of this Operation
   * @exception RemoteException If error occurs during Changing Preferences Info
   * @since 1.0

   */
  public String changePreferencesInfo(Integer accountNumber,
                PreferencesInfo prefInfo) throws RemoteException {
    String     message        = "SUCCESS";
    try {
      // Find the User Account Local Entity Bean by passing the account Number
      // Primary Key
      UserAccountLocal userAccountLocal = 
        userAccountHomeLocal.findByPrimaryKey(accountNumber);

      // Loop through all the preferences
      ArrayList prefIter = new ArrayList(userAccountLocal.getPreferences());
      int size = prefIter.size();
      int i = 0;
      while ( i < size) {

        // Get the next handle to PreferencesBean Local EJB Object
        PreferencesLocal pl = (PreferencesLocal) prefIter.get(i);

        //  Check whether the passed Symbol is same as Preferences Symbol
        if (pl.getSymbol().equals(prefInfo.getSymbol())) {

          // Update the Preference Type
          pl.setPrefType(prefInfo.getPreferenceType());
        }

        // Add to the final list of preferences
        prefIter.set(i,pl);
        i++;
      }

      // Set the preferences as a final list
      userAccountLocal.setPreferences(prefIter);
    } catch (FinderException ex) {  // Trap errors while finding EJB Object
      System.err.println("Change Preferences Info Method : Finder Exception "
                         + ex.toString());

      message = "NOSUCCESS";
      throw new RemoteException("Finder Exception While Changing Pref Info = " +
           ex.toString());

    }

    return message;
  }


  /**
   * Method to Delete a Preference from a given User Account.
   *
   * @param accountNumber Account Number of the User Account.
   * @return Status of this Operation
   * @exception RemoteException If error occurs during deleting Preferences Info

   * @since 1.0
   */
  public String deletePreference(Integer accountNumber, String symbol)
    throws RemoteException {
    String     message        = "SUCCESS";
    Collection allPreferences = new ArrayList();

    try {
      // Find the User Account Local Entity Bean by passing the account Number
      // Primary Key
      UserAccountLocal userAccountLocal = 
        userAccountHomeLocal.findByPrimaryKey(accountNumber);

      // Get All the Preferences for the User Account and loop through them
      Iterator prefIter = (userAccountLocal.getPreferences()).iterator();
      while (prefIter.hasNext()) {

        // Get the next handle to PreferencesBean Local EJB Object
        PreferencesLocal pl = (PreferencesLocal) prefIter.next();

        // Check if the passed Symbol is same as the symbol in Preferences Info
        // If not, add to final list.
        if (!pl.getSymbol().equals(symbol)) {
          allPreferences.add(pl);
        }
      }

      // Set the final Set Of Preferences for User Account
      userAccountLocal.setPreferences(allPreferences);
    } catch (FinderException ex) {  // Trap Errors While Finding EJB Object
      System.err.println("Delete Preferences Info Method : Finder Exception "
                         + ex.toString());

      message = "NOSUCCESS";
      throw new RemoteException("Finder Exception While Deleting Preferences " +
           ex.toString());

    }

    return message;
  }

  /**
   * Method to retrieve Alerts for a given User Account. As a single user
   * account can have multiple alerts, this method returns a collection
   * of AlertsInfo Value Object.
   *
   * @param accountNumber Account Number of the User Account.
   * @return A Collection of AlertsInfo Value Object. This represents all
   *         the alerts for the User Account.
   * @exception RemoteException If error occurs during getting Alerts Info

   * @since 1.0
   */
  public Collection getAlerts(Integer accountNumber) throws RemoteException {
    Collection allAlerts = new ArrayList();
    try {
      // Find the User Account Local Entity Bean by passing the
      // Account Number Primary Key
      UserAccountLocal userAccountLocal = 
        userAccountHomeLocal.findByPrimaryKey(accountNumber);

      // Loop through all the Alerts
      Iterator alertIter = (userAccountLocal.getAlerts()).iterator();

      while (alertIter.hasNext()) {
        // Get the next handle to AlertsBean Local EJB Object
        AlertsLocal al = (AlertsLocal) alertIter.next();

        // Add a new instance of AlertsInfo Value Object to final list
        allAlerts.add(new AlertsInfo(al.getSymbol(), al.getMinLimit(),
                                     al.getMaxLimit()));
      }
    } catch (FinderException ex) {  // Trap Errors While Finding EJB Object
      System.err.println("Get Alerts Method : Finder Exception "
                         + ex.toString());
      throw new RemoteException("Finder Exception While Getting Alerts = " +
           ex.toString());
    }

    return allAlerts;  // Return All Alerts
  }


  /**
   * Method to add an Alert for a given User Account.
   *
   * @param accountNumber Account Number of the User Account.
   * @return Status of this Operation
   * @exception RemoteException If error occurs during adding Alert Info
   * @since 1.0
   */
  public String addAlert(Integer accountNumber, AlertsInfo alertsInfo)
    throws RemoteException {
    boolean    symbolPresent = false;
    boolean    validSymbol = false;
    String     message = null;

    try {
      // Find the User Account Local Entity Bean by passing the account Number
      // Primary Key
      UserAccountLocal userAccountLocal = 
        userAccountHomeLocal.findByPrimaryKey(accountNumber);

      // Check Whether a Valid Symbol
      validSymbol = helper.checkSymbolExists(alertsInfo.getSymbol());


      // Get All the Alerts for this User Account and loop through them
      ArrayList allAlertsIter = new ArrayList(userAccountLocal.getAlerts());
      int size = allAlertsIter.size();
      int i = 0;
      while (i < size) {

        // Get the next handle to AlertsBean Local EJB Object
        AlertsLocal al = (AlertsLocal) allAlertsIter.get(i);

        // Check whether the Symbol already exists in the given set of
        // Alerts
        if (al.getSymbol().equals(alertsInfo.getSymbol())) {
          symbolPresent = true;
          //break;
        }

        allAlertsIter.set(i,al);
        i++;
      }

      // If Symbol was not already present then add this Alert to the
      // existing list of Alerts
      if (!symbolPresent && validSymbol ) {
        AlertsLocal alertsLocal = 
            alertsHomeLocal.create(helper.getNextID("ALERTS_SEQ"),
                                   accountNumber,
                                   alertsInfo.getSymbol(),
                                   alertsInfo.getMinLimit(),
                                   alertsInfo.getMaxLimit());

        allAlertsIter.add(alertsLocal);
      }

      userAccountLocal.setAlerts(allAlertsIter);
    } catch (FinderException ex) {  // Trap the errors while finding the EJB
      System.err.println("Finder Exception : Add Alerts Method : "
                         + ex.toString());
      throw new RemoteException("Finder Exception While Adding Alert = " +
           ex.toString());
    } catch (CreateException ex) {  // Trap the errors while creating the EJB
      System.err.println("Create Exception : Add Alerts Method : "
                         + ex.toString());
      throw new RemoteException("Create Exception While Adding Alert = " +
           ex.toString());
    } catch (DatabaseException ex) { // Trap SQL Errors
      throw new RemoteException("SQL Exception While Creating New Account : " +
          ex.toString());
    }

    // If Symbol Already Exists then return a proper message
    if (!symbolPresent && validSymbol) {
      message = "SUCCESS";
    } else if (symbolPresent && validSymbol) {
      message = "Symbol Already Present. Can not add. Change it";
    } else if (!validSymbol) {
      message = "Improper Symbol Value. Please Enter proper Symbol Value";
    }

    return message;
  }



  /**
   * Method to Update a Single Alert Record for a given User Account.
   *
   * @param accountNumber Account Number of the User Account.
   * @param alertInfo Value Object for Alert Information to be updated.
   * @return Status of this Operation
   * @exception RemoteException If error occurs during Changing Alert Info
   * @since 1.0
   */
  public String changeAlertInfo(Integer accountNumber, AlertsInfo alertInfo)
    throws RemoteException {
    String     message   = "SUCCESS";

    try {
      // Find the User Account Local Entity Bean by passing the account Number
      // Primary Key
      UserAccountLocal userAccountLocal = 
        userAccountHomeLocal.findByPrimaryKey(accountNumber);

      // Get All the Alerts
      ArrayList alertIter = new ArrayList(userAccountLocal.getAlerts());
      int size = alertIter.size();
      int i    = 0;
      // Loop through all the alerts
      while ( i < size) {
        // Get the next handle to AlertsBean Local EJB Object
        AlertsLocal al = (AlertsLocal) alertIter.get(i);

        // Check whether the passed Symbol is same as Alerts Symbol
        if (al.getSymbol().equals(alertInfo.getSymbol())) {

          // Update the Values of Max Limit and Min Limit
          al.setMaxLimit(alertInfo.getMaxLimit());
          al.setMinLimit(alertInfo.getMinLimit());
        }

        // Add Alert Back to the final List
        alertIter.set(i,al);
        i++;
      }

      // Set the Alerts
      userAccountLocal.setAlerts(alertIter);
    } catch (FinderException ex) {
      // Trap Errors While Finding Local EJB Objects
      System.err.println("Change Alert Info Method : Finder Exception "
                         + ex.toString());
      message = "NOSUCCESS";
      throw new RemoteException("Finder Exception While Changing Alert Info = " +
                                ex.toString());

    }

    return message;
  }

  /**
   * Method to Delete an Alert for the given User Account.
   *
   * @param accountNumber Account Number of the User Account.
   * @return Status of this Operation
   * @exception RemoteException If error occurs during deleting Alert
   * @since 1.0
   */
  public String deleteAlert(Integer accountNumber, String symbol)
    throws RemoteException {

    String     message   = "SUCCESS";
    Collection allAlerts = new ArrayList();

    try {
      // Find the User Account Local Entity Bean by passing the
      // Account Number Primary Key
      UserAccountLocal userAccountLocal = 
        userAccountHomeLocal.findByPrimaryKey(accountNumber);

      // Get All the Alerts for the User Account
      Iterator alertIter = userAccountLocal.getAlerts().iterator();
      // Loop through all the alerts
      while ( alertIter.hasNext()) {
        // Get the next handle to AlertsBean Local EJB Object
        AlertsLocal al = (AlertsLocal) alertIter.next();

        // Check if the passed Symbol is same as the symbol in Alerts Info
        // If no, add it to the final list.
        if (!al.getSymbol().equals(symbol)) {
           allAlerts.add(al);
        }
      }

      // Set the Value of Alerts
      userAccountLocal.setAlerts(allAlerts);
    } catch (FinderException ex) {  // Trap Errors While Finding EJB Object
      System.out.println("Delete Alert Info Method : Finder Exception "
                         + ex.toString());

      message = "NOSUCCESS";
      throw new RemoteException("Finder Exception While Deleting Alerts = " +
           ex.toString());

    }

    return message;
  }


  /**
   * Method to check whether the password provided by the user is correct.
   * @param accountNumber Account Number of the User Account.
   * @param password User Supplied Password
   * @return Boolean Flag indicating whether user provided Valid Password
   * @exception RemoteException If error occurs during Validating User
   * @since 1.0
   */
  public boolean validUser(Integer accountNumber, String password)
    throws RemoteException {

    boolean valid = false;

    try {
      // Find the User Account Local Entity Bean by passing the
      // Account Number Primary Key
      UserAccountLocal userAccountLocal = 
        userAccountHomeLocal.findByPrimaryKey(accountNumber);

      // Get the User's Password stored in the database
      String userPassword = userAccountLocal.getPassword();

      //Check whether the passwords are the same
      if (userPassword.equalsIgnoreCase(password)) {
        valid = true;
      }
    } catch (FinderException ex) {  // Trap Errors While Finding EJB Object
      throw new RemoteException("Invalid Account Number. " + 
                                "Please Enter Valid Account Number. ");
    }

    return valid;
  }

  /**
   * Method to change User's Password Value.
   * @param accountNumber Account Number of the User Account.
   * @param newPassword   New Password Value
   * @return Status of this Operation
   * @exception RemoteException If error occurs during changing Password Info
   * @since 1.0
   */
  public String changePassword(Integer accountNumber, String newPassword, 
                               String oldPassword)
    throws RemoteException {

    String result = "NOSUCCESS";

    try {
      // Check whether user has entered old password correctly
      if (this.validUser(accountNumber,oldPassword)) {

        // Find the User Account Local Entity Bean by passing the account Number
        // Primary Key
        UserAccountLocal userAccountLocal = 
          userAccountHomeLocal.findByPrimaryKey(accountNumber);

        // Set the New Password
        userAccountLocal.setPassword(newPassword);

        result = "SUCCESS";
      }
    } catch (FinderException ex) {  // Trap Errors While Finding EJB Object
      System.out.println("Valid User Method : " + ex.toString());
      throw new RemoteException("Finder Exception While Changing Password = " +
        ex.toString());

    }

    return result;
  }

  /**
   * Method to call the EJB-QL method of UserAccountBean and get the Hashtable
   * with AccountNumber and Password details of the user.
   *
   * @param email Email is input by the user.
   * @return Hashtable with AccountNumber and Password
   *
   * @exception RemoteException Remote Error when getting a User Account Information
   * @since 1.0
   */
  public Hashtable getUserAccount(String email) throws RemoteException {
    Hashtable userHash = new Hashtable();

    try {
      // Find the User Account Local Entity Bean by passing the Email
      Collection uaColl = userAccountHomeLocal.findByEmail(email);
      Iterator   uaIter = uaColl.iterator();
      if (uaIter.hasNext()) {
        UserAccountLocal userAccountLocal = (UserAccountLocal) uaIter.next();

        // Add the queried values to the Hashtable
        userHash.put("ACCOUNTNUMBER", userAccountLocal.getAccountNumber());
        userHash.put("PASSWORD", userAccountLocal.getPassword());
        userHash.put("FIRSTNAME", userAccountLocal.getFirstName());
        userHash.put("LASTNAME", userAccountLocal.getLastName());
      }
    } catch (FinderException ex) {  // Trap Errors While Finding EJB Object
      System.out.println("Finder Exception in getUserAccount : "
                         + ex.toString());
      throw new RemoteException("Finder Exception While Getting User Account = " +
           ex.toString());

    }

    return userHash;
  }

  /**
   * Method to call the EJB-QL method of UserAccountBean to check whether
   * Admin account has been created. Creates a new account if Admin user
   * does not exist and initializes Timer for TradeDetails Updates
   * with default values of '0' hour and '15' minutes.
   *
   * @return accountNumber of Admin user (if new account was created)
   *
   * @exception RemoteException if creating Admin account fails
   * @since 1.0
   */
  public Integer createAdminAccount() throws RemoteException {

    int accountNumber = 0;

    try {

      // Find the User Account Local Entity Bean by passing the UserType as 'A'
      Collection uaColl = userAccountHomeLocal.findByUserType("A");

      if (uaColl.iterator().hasNext())  {

        // Account already exists, do nothing

      } else {

        // Create Admin account

        // Admin account is assumed to be One (always)
        UserAccountLocal userAccountLocal
                   = userAccountHomeLocal.create(helper.getNextID("ACCOUNTNUMBER_SEQ"),
                                                 "welcome",
                                                 "John","Doe","IBFBS"," "," ",
                                                 " "," "," ",0,"admin@fbs.com",
                                                 "A",new Integer(0),"E"," ");
        accountNumber = 1;

        // Initialize Timer Values to default
        int hour = 0;
        int mins = 15;

            // Calculate interval in milli seconds
        long timeout = (hour * 60 + mins) * 60 * 1000;

        // Initialize Timer on SLSB for Tradedetails updates
        this.initializeTimer(timeout, "Tradedetails Updates");

      }

    } catch (FinderException findEx) {  // Handle Errors While Finding EJB Object

       System.out.println("Finder Exception while creating admin account : "
                           + findEx.toString());

       accountNumber = -1;

    } catch(CreateException createEx) {

       System.out.println("Create Exception while creating admin account : "
                           + createEx.toString());

       accountNumber = -1;

    } catch (Exception ex) {

      throw new RemoteException("Generic Exception while creating admin account : "
                                + ex.toString());

    }

    return new Integer(accountNumber);

  }


  /**
   * This method gets all the TradeDetails between the input dates, calls the 
   * format method to format the message and then creates an instance of the 
   * MailService bean to send detailed mail to the Administrator.
   * 
   * @param   timerInfo   Information about the timer 
   * @param   toDate      Start Date 
   * @param   sysDate     End Date
   *
   * @since 1.0
   */
  private void sendMail(String timerInfo, Date toDate, Date sysDate) {

    Collection tdColl  = null;

    try  {
      // Execute an EJB-QL to retrieve all tradedetails BETWEEN the input dates
      tdColl = (Collection) tradedetailsHomeLocal.TradeDetails(toDate, sysDate);
    } catch (Exception ex) {
      System.out.println(" Exception while ejbSelectTradeDetails "+ex);
    }

    if (tdColl != null) {
      // Iterate through the tradedetails collection 
      Iterator tdIter  = tdColl.iterator();

      if (tdIter.hasNext()) {
        // Format Message
        String      message     = formatHTMLMessage(tdColl, toDate, sysDate);
        MailService mailService = null;

        try {
          InitialContext ic = new InitialContext();
          // Initialize the MailSender
          MailServiceHome mailServiceHome =
            (MailServiceHome) ic.lookup("MailService");

          mailService = mailServiceHome.create();

          // Send mail the Administrator's mailid specified in ConnectionParams
          mailService.sendMail(notificationMailId,
                               "tradeupdates@fbs.com",
                               " TradeDetails Updates ",
                               message);

        } catch (Exception ex) {
          System.out.println(
                 " Couldn't initialize mail service to send trade details updates :"+
                 ex.toString());
        }
      }
    }
  }

  /**
   * This method prepares the HTML format of the message to be sent to the 
   * FBS administrator about the trading activity.
   * 
   * @param   tdColl      Collection of trade details
   * @param   toDate      Start Date 
   * @param   sysDate     End Date
   * 
   * @return  The formatted HTML string
   *
   * @since 1.0
   */
  private String formatHTMLMessage(Collection tdColl,
                                   Date toDate, Date sysDate) {

    StringBuffer sb = new StringBuffer();

    sb.append("<html><body>");

    sb.append("<table width=\"80%\"><tr><td>");
    sb.append("Hello, <br><br>  Details of all the Stock Trades carried out " );
    sb.append("BETWEEN ");
    sb.append(toDate);
    sb.append(" AND ");
    sb.append(sysDate);
    sb.append("   are as follows : ");
    sb.append("</td></tr></table><br>");

    sb.append("<table width=\"80%\" border=\"1\" >");

    // Set the iterator for the Collection
    Iterator tdIter  = tdColl.iterator();

    int rowCount = 0; // initialize row counter

    // Iterate through the collection
    while (tdIter.hasNext()) {

       // Retrieve each tradedetails record
      TradeDetailsLocal tdv = (TradeDetailsLocal)tdIter.next();

      rowCount += 1;

      // Display the Table header only once when the row count is 1
      if (rowCount == 1) {

        String tabHeader[] = new String[] {
                                            "SYMBOL",
                                            "ACTION",
                                            "PRICE ($)",
                                            "QUANTITY"
                                          };

        sb.append("<tr> ");
        for (int count=0; count < 4 ; count++) {
          sb.append("   <th> ");
          sb.append("      <div align=\"center\">  ");
          sb.append(tabHeader[count]);
          sb.append("      </div>  ");
          sb.append("   </th>  ");
        }
        sb.append("</tr> ");

      }

      // Display the values in the Collection
      sb.append("<tr> ");

      sb.append("<td height=\"30\"> ");
      sb.append("  <div align=\"left\"> ");
      sb.append(tdv.getSymbol());
      sb.append("  </div>");
      sb.append("</td>");

      sb.append("<td height=\"30\"> ");
      sb.append("  <div align=\"left\"> ");

      String action = tdv.getAction();
      if (action.equals("B")) action = "BOUGHT";
      else if (action.equals("S")) action = "SOLD";

      sb.append(action);
      sb.append("  </div>");
      sb.append("</td>");

      sb.append("<td height=\"30\"> ");
      sb.append("  <div align=\"right\"> ");
      sb.append(tdv.getPrice());
      sb.append("  </div>");
      sb.append("</td>");
      sb.append("<td height=\"30\"> ");
      sb.append("  <div align=\"right\"> ");
      sb.append(tdv.getQuantity());
      sb.append("  </div>");
      sb.append("</td>");

      sb.append("</tr> ");
    }

    sb.append("</table>");

    sb.append("<br><br>");
    sb.append("<table width=\"80%\"><tr><td>");
    sb.append("Report Time : ");
    sb.append(new Date());
    sb.append("<br><br>-- EJBTimer Reporting.");
    sb.append("<br><br><B>Note : </B>This Report is automatically generated by ");
    sb.append("EJB Container with the Timer values set by the Administrator. ");
    sb.append("To change this : Log on as FBS Administrator ");
    sb.append("and Set the required Report Interval. <br>");
    sb.append("</td></tr></table>");

    sb.append("</body></html>");

    return sb.toString();

  }

  /**
   * Method to call the EJB-QL method of UserAccountBean to check whether
   * Admin account has been created. If Admin user already exists,
   * initializes Timer for TradeDetails Updates with the input values.
   * 
   * @param timerInfo  TimerInfo value object
   *
   * @return String indicating if the updation of Timer was successful or not
   *
   * @exception RemoteException if updating Admin account fails
   * @since 1.0
   */
  public String updateAdminAccount(TimerInfo timerInfo)
      throws RemoteException {

    // Initialize the Status Message
    String retMessage = "NOSUCCESS";

    try {

      // Find the User Account Local Entity Bean by passing the UserType as 'A'
      Collection uaColl = userAccountHomeLocal.findByUserType("A");

      if (uaColl.iterator().hasNext())  {

        int hour = timerInfo.getTimerHours().intValue();
        int mins = timerInfo.getTimerMinutes().intValue();

        if ( (hour > 0) || (mins > 0) ) {

            // Calculate interval in milli seconds
          long timeout = (hour * 60 + mins) * 60 * 1000;

          // Re-Initialize Timer on SLSB for Tradedetails updates
          this.initializeTimer(timeout, "Tradedetails Updates");

          retMessage = "SUCCESS";

        }

      } else {

        // Account does not exist
        throw new RemoteException("Admin Account does not exist : ");

      }

    } catch (FinderException findEx) {  // Handle Errors While Finding EJB Object

       System.out.println("Finder Exception while updating admin account : "
                           + findEx.toString());

    } catch (Exception ex) {

      throw new RemoteException("Genric Exception while creating admin account : "
                         + ex.toString());

    }

    return retMessage;

  }

  /**
   * Other Standard Call Back Methods
   */
  public void ejbRemove() {}

  public void ejbActivate() {}

  public void ejbPassivate() {}

  public void setSessionContext(javax.ejb.SessionContext cntx) {
    this.sctx = cntx;
  }

  public void unsetSessionContext() {
    this.sctx = null;
  }
}