/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2004 Bull S.A.
 * Contact: jonas-team@objectweb.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package eb;

import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Vector;

import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
import javax.ejb.FinderException;
import javax.ejb.ObjectNotFoundException;
import javax.ejb.RemoveException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

/**
 * AccountExplBean is an entity bean with "bean managed persistence". The state
 * of an instance is stored into a relational database. The following table
 * should exist: create table ACCOUNT (ACCNO integer primary key, CUSTOMER
 * varchar(30), BALANCE number(15,4)); Note : In order to keep the code
 * readable, not all database errors have been handled. In particular the
 * closing of the statement and of the connexion would have being put in a
 * "finally" block.
 * @author JOnAS team
 */

public class AccountExplBean implements EntityBean {

    private DataSource dataSource = null;

    // Keep the reference on the EntityContext
    protected EntityContext entityContext;

    // Object state - must de public (EJB spec.)
    public Integer accno;

    public String customer;

    public double balance;

    /* ========================= ejbCreate methods ============================ */

    /**
     * There must be one ejbCreate() method per create() method on the Home
     * interface, and with the same signature.
     * @param accno account number
     * @param customer customer name
     * @param balance initial balance
     * @return pk primary key
     * @exception CreateException If the instance could not perform the function
     *            requested by the container
     */
    public Integer ejbCreate(int val_accno, String val_customer, double val_balance) throws CreateException {

        // Init object state
        accno = new Integer(val_accno);
        customer = val_customer;
        balance = val_balance;

        Connection conn = null;
        PreparedStatement stmt = null;
        try {
            // get a connection for this transaction context
            conn = getConnection();

            // create object in DB
            stmt = conn.prepareStatement("insert into accountsample_ (accno_, customer_, balance_) values (?, ?, ?)");
            stmt.setInt(1, accno.intValue());
            stmt.setString(2, customer);
            stmt.setDouble(3, balance);
            stmt.executeUpdate();

        } catch (SQLException e) {
            throw new CreateException("Failed to create bean in database: " + e);
        } finally {
            try {
                if (stmt != null) {
                    //close statement
                    stmt.close();
                }
                if (conn != null) {
                    //release connection
                    conn.close();
                }
            } catch (Exception ignore) {
            }
        }
        // Return the primary key
        return accno;
    }

    /**
     * Each ejbCreate method should have a matching ejbPostCreate method
     */
    public void ejbPostCreate(int val_accno, String val_customer, double val_balance) {
        // Nothing to be done for this simple example.
    }

    /*
     * ====================== javax.ejb.EntityBean implementation
     * =================
     */

    /**
     * A container invokes this method when the instance is taken out of the
     * pool of available instances to become associated with a specific EJB
     * object. This method transitions the instance to the ready state. This
     * method executes in an unspecified transaction context.
     * @exception EJBException Thrown by the method to indicate a failure caused
     *            by a system-level error.
     */
    public void ejbActivate() {
        // Nothing to be done for this simple example.
    }

    /**
     * A container invokes this method to instruct the instance to synchronize
     * its state by loading it state from the underlying database. This method
     * always executes in the proper transaction context.
     * @exception EJBException Thrown by the method to indicate a failure caused
     *            by a system-level error.
     * @exception RemoteException - This exception is defined in the method
     *            signature to provide backward compatibility for enterprise
     *            beans written for the EJB 1.0 specification. Enterprise beans
     *            written for the EJB 1.1 and higher specification should throw
     *            the javax.ejb.EJBException instead of this exception.
     */
    public void ejbLoad() {
        Connection conn = null;
        PreparedStatement stmt = null;
        try {
            // get a connection for this transaction context
            conn = getConnection();

            // find account in DB
            stmt = conn.prepareStatement("select customer_,balance_ from accountsample_ where accno_=?");
            Integer pk = (Integer) entityContext.getPrimaryKey();
            stmt.setInt(1, pk.intValue());
            ResultSet rs = stmt.executeQuery();
            if (!rs.next()) {
                throw new EJBException("Failed to load bean from database");
            }

            // update object state
            accno = pk;
            customer = rs.getString("customer_");
            balance = rs.getDouble("balance_");

        } catch (SQLException e) {
            throw new EJBException("Failed to load bean from database " + e);
        } finally {
            try {
                if (stmt != null) {
                    //close statement
                    stmt.close();
                }
                if (conn != null) {
                    //release connection
                    conn.close();
                }
            } catch (Exception ignore) {
            }
        }
    }

    /**
     * A container invokes this method on an instance before the instance
     * becomes disassociated with a specific EJB object. After this method
     * completes, the container will place the instance into the pool of
     * available instances. This method executes in an unspecified transaction
     * context.
     * @exception EJBException Thrown by the method to indicate a failure caused
     *            by a system-level error.
     */
    public void ejbPassivate() {
        // Nothing to be done for this simple example.
    }

    /**
     * A container invokes this method before it removes the EJB object that is
     * currently associated with the instance. This method is invoked when a
     * client invokes a remove operation on the enterprise Bean's home interface
     * or the EJB object's remote interface. This method transitions the
     * instance from the ready state to the pool of available instances. This
     * method is called in the transaction context of the remove operation.
     * @exception EJBException Thrown by the method to indicate a failure caused
     *            by a system-level error.
     * @exception RemoteException - This exception is defined in the method
     *            signature to provide backward compatibility for enterprise
     *            beans written for the EJB 1.0 specification. Enterprise beans
     *            written for the EJB 1.1 and higher specification should throw
     *            the javax.ejb.EJBException instead of this exception.
     * @exception RemoveException The enterprise Bean does not allow destruction
     *            of the object.
     */
    public void ejbRemove() throws RemoveException {
        Connection conn = null;
        PreparedStatement stmt = null;
        try {
            // get a connection for this transaction context
            conn = getConnection();

            // delete Object in DB
            stmt = conn.prepareStatement("delete from accountsample_ where accno_=?");
            Integer pk = (Integer) entityContext.getPrimaryKey();
            stmt.setInt(1, pk.intValue());
            stmt.executeUpdate();

        } catch (SQLException e) {
            throw new RemoveException("Failed to delete bean from database" + e);
        } finally {
            try {
                if (stmt != null) {
                    //close statement
                    stmt.close();
                }
                if (conn != null) {
                    //release connection
                    conn.close();
                }
            } catch (Exception ignore) {
            }
        }
    }

    /**
     * A container invokes this method to instruct the instance to synchronize
     * its state by storing it to the underlying database. This method always
     * executes in the proper transaction context.
     * @exception EJBException Thrown by the method to indicate a failure caused
     *            by a system-level error.
     * @exception RemoteException - This exception is defined in the method
     *            signature to provide backward compatibility for enterprise
     *            beans written for the EJB 1.0 specification. Enterprise beans
     *            written for the EJB 1.1 and higher specification should throw
     *            the javax.ejb.EJBException instead of this exception.
     */
    public void ejbStore() {
        Connection conn = null;
        PreparedStatement stmt = null;
        try {
            // get a connection for this transaction context
            conn = getConnection();

            // store Object state in DB
            stmt = conn.prepareStatement("update accountsample_ set customer_=?,balance_=? where accno_=?");
            stmt.setString(1, customer);
            stmt.setDouble(2, balance);
            Integer pk = (Integer) entityContext.getPrimaryKey();
            stmt.setInt(3, pk.intValue());
            stmt.executeUpdate();

        } catch (SQLException e) {
            throw new EJBException("Failed to store bean to database " + e);
        } finally {
            try {
                if (stmt != null) {
                    //close statement
                    stmt.close();
                }
                if (conn != null) {
                    //release connection
                    conn.close();
                }
            } catch (Exception ignore) {
            }
        }
    }

    /**
     * Sets the associated entity context. The container invokes this method on
     * an instance after the instance has been created. This method is called in
     * an unspecified transaction context.
     * @param ctx - An EntityContext interface for the instance. The instance
     *        should store the reference to the context in an instance variable.
     * @exception EJBException Thrown by the method to indicate a failure caused
     *            by a system-level error.
     * @exception RemoteException - This exception is defined in the method
     *            signature to provide backward compatibility for enterprise
     *            beans written for the EJB 1.0 specification. Enterprise beans
     *            written for the EJB 1.1 and higher specification should throw
     *            the javax.ejb.EJBException instead of this exception.
     */
    public void setEntityContext(EntityContext ctx) {

        // Keep the entity context in object
        entityContext = ctx;

    }

    /**
     * Unsets the associated entity context. The container calls this method
     * before removing the instance. This is the last method that the container
     * invokes on the instance. The Java garbage collector will eventually
     * invoke the finalize() method on the instance. This method is called in an
     * unspecified transaction context.
     * @exception EJBException Thrown by the method to indicate a failure caused
     *            by a system-level error.
     * @exception RemoteException - This exception is defined in the method
     *            signature to provide backward compatibility for enterprise
     *            beans written for the EJB 1.0 specification. Enterprise beans
     *            written for the EJB 1.1 and higher specification should throw
     *            the javax.ejb.EJBException instead of this exception.
     */
    public void unsetEntityContext() {
        entityContext = null;
    }

    /* ============================ ejbFind methods =========================== */

    /**
     * There must be one ejbFind method per find method on the Home interface,
     * and with the same signature. ejbFindByPrimaryKey is the only mandatory
     * ejbFind method.
     * @param pk The primary key
     * @return pk The primary key
     * @exception FinderException - Failed to execute the query.
     * @exception ObjectNotFoundException - Object not found for this primary
     *            key.
     */
    public Integer ejbFindByPrimaryKey(Integer pk) throws ObjectNotFoundException, FinderException {

        Connection conn = null;
        PreparedStatement stmt = null;
        try {
            // get a connection for this transaction context
            conn = getConnection();

            // lookup for this primary key in DB
            stmt = conn.prepareStatement("select accno_ from accountsample_ where accno_=?");
            stmt.setInt(1, pk.intValue());
            ResultSet rs = stmt.executeQuery();
            if (!rs.next()) {
                throw new javax.ejb.ObjectNotFoundException();
            }

        } catch (SQLException e) {
            throw new javax.ejb.FinderException("Failed to executeQuery " + e);
        } finally {
            try {
                if (stmt != null) {
                    //close statement
                    stmt.close();
                }
                if (conn != null) {
                    //release connection
                    conn.close();
                }
            } catch (Exception ignore) {
            }
        }

        // return primary key
        return pk;
    }

    /**
     * Find Account by its account number
     * @return pk The primary key
     * @exception FinderException - Failed to execute the query.
     * @exception ObjectNotFoundException - Object not found for this account
     *            number
     */
    public Integer ejbFindByNumber(int accno) throws ObjectNotFoundException, FinderException {

        Connection conn = null;
        PreparedStatement stmt = null;
        try {
            // get a connection for this transaction context
            conn = getConnection();
            // lookup for this primary key in DB
            stmt = conn.prepareStatement("select accno_ from accountsample_ where accno_=?");
            stmt.setInt(1, accno);
            ResultSet rs = stmt.executeQuery();
            if (!rs.next()) {
                throw new javax.ejb.ObjectNotFoundException();
            }

        } catch (SQLException e) {
            throw new javax.ejb.FinderException("Failed to executeQuery " + e);
        } finally {
            try {
                if (stmt != null) {
                    //close statement
                    stmt.close();
                }
                if (conn != null) {
                    //release connection
                    conn.close();
                }
            } catch (Exception ignore) {
            }
        }

        // return a primary key for this account
        return new Integer(accno);
    }

    /**
     * Creates an enumeration of primary keys for all accounts
     * @return pkv The primary keys
     * @exception FinderException - Failed to execute the query.
     */
    public Enumeration ejbFindAllAccounts() throws FinderException {
        Connection conn = null;
        PreparedStatement stmt = null;
        Vector pkv = new Vector();
        try {
            // get a connection for this transaction context
            conn = getConnection();

            // Lookup for all accounts in DB
            stmt = conn.prepareStatement("select accno_ from accountsample_");
            ResultSet rs = stmt.executeQuery();

            // Build the vector of primary keys
            while (rs.next()) {
                Integer pk = new Integer(rs.getInt("accno_"));
                pkv.addElement((Object) pk);
            }

        } catch (SQLException e) {
            throw new javax.ejb.FinderException("Failed to executeQuery " + e);
        } finally {
            try {
                if (stmt != null) {
                    //close statement
                    stmt.close();
                }
                if (conn != null) {
                    //release connection
                    conn.close();
                }
            } catch (Exception ignore) {
            }
        }

        // return primary keys
        return pkv.elements();
    }

    /**
     * @return the connection from the dataSource
     * @exception EJBException Thrown by the method if the dataSource is not
     *            found in the naming.
     * @exception SQLException may be thrown by dataSource.getConnection()
     */
    private Connection getConnection() throws EJBException, SQLException {
        if (dataSource == null) {
            // Finds DataSource from JNDI
            Context initialContext = null;
            try {
                initialContext = new InitialContext();
                dataSource = (DataSource) initialContext.lookup("java:comp/env/jdbc/AccountExplDs");
            } catch (Exception e) {
                System.out.println("Cannot lookup dataSource" + e);
                throw new javax.ejb.EJBException("Cannot lookup dataSource ");
            }
        }
        return dataSource.getConnection();
    }

    /*
     * ========================= Account implementation
     * ============================
     */

    /**
     * Business method for returning the balance.
     * @return balance
     */
    public double getBalance() {

        return balance;
    }

    /**
     * Business method for updating the balance.
     * @param d balance to update
     */
    public void setBalance(double d) {

        balance = balance + d;
    }

    /**
     * Business method for returning the customer.
     * @return customer
     */
    public String getCustomer() {

        return customer;
    }

    /**
     * Business method for changing the customer name.
     * @param c customer to update
     */
    public void setCustomer(String c) {

        customer = c;
    }

    /**
     * Business method to get the Account number
     */
    public int getNumber() {
        return accno.intValue();
    }
}