package org.objectweb.jonas.jtests.beans.jdbc;

import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Hashtable;
import java.util.Iterator;

import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.ejb.SessionSynchronization;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import javax.sql.DataSource;

import org.ow2.jonas.lib.util.Log;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;


/**
 * Stateful Session Bean that manages directly jdbc connections
 */
public class ManagerSY implements SessionBean, SessionSynchronization {

    static private Logger logger = null;
    SessionContext ejbContext;

    // ------------------------------------------------------------------
    // The state of this Stateful Session Bean
    // ------------------------------------------------------------------
    int conb = 100;
    InitialContext ictx = null;
    DataSource ds = null;
    Hashtable clist = new Hashtable(); // Connection List

    // ------------------------------------------------------------------
    // SessionSynchronization implementation
    // ------------------------------------------------------------------

    public void afterBegin() {
        logger.log(BasicLevel.DEBUG, "");
    }

    public void beforeCompletion() {
        logger.log(BasicLevel.DEBUG, "");
    }

    public void afterCompletion(boolean committed) {
        logger.log(BasicLevel.DEBUG, "");
    }

    // ------------------------------------------------------------------
    // SessionBean implementation
    // ------------------------------------------------------------------

    /**
     * Make here all initialisations needed in this stateful session bean
     */
    public void setSessionContext(SessionContext ctx) {
        if( logger == null)
            logger = Log.getLogger("org.objectweb.jonas_tests");
        logger.log(BasicLevel.DEBUG, "");
        ejbContext = ctx;
        ds = getDataSource("java:comp/env/jdbc/mydb");
        // Get a Connection now
        try {
            Connection c = ds.getConnection();
            clist.put(new Integer(0), c);
        } catch (SQLException e) {
            throw new EJBException("Cannot get first Connection:"+e);
        }
    }
	
    public void ejbRemove() {
        logger.log(BasicLevel.DEBUG, "");
        // Close all opened connections
        for (Iterator i = clist.values().iterator(); i.hasNext(); ) {
            Connection c = (Connection) i.next();
            try {
                c.close();
            }  catch (SQLException e) {
                logger.log(BasicLevel.ERROR, "Cannot close Connection:" + e);
            }
        }
    }
	
    public void ejbCreate() throws CreateException {
        logger.log(BasicLevel.DEBUG, "");
    }

    public void ejbPassivate() {
        logger.log(BasicLevel.DEBUG, "");
    }

    public void ejbActivate() {
        logger.log(BasicLevel.DEBUG, "");
    }
    
    // ------------------------------------------------------------------
    // Manager implementation
    // ------------------------------------------------------------------

    /**
     * open and close a Connection
     */
    public boolean openCloseConnection() throws RemoteException {
        logger.log(BasicLevel.DEBUG, "");
        Connection c = null;
        try {
            c = ds.getConnection();
        } catch (SQLException e) {
            logger.log(BasicLevel.ERROR, "Cannot get a Connection:"+e);
            return false;
        }
        try {
            c.close();
        } catch (SQLException e) {
            logger.log(BasicLevel.ERROR, "Cannot close Connection:"+e);
            return false;
        }
        return true;
    }

    /**
     * return the number associated to the connection opened,
     * or 0 if it failed.
     */
    public int openConnection() throws RemoteException {
        logger.log(BasicLevel.DEBUG, "");
        Connection c = null;
        try {
            c = ds.getConnection();
        } catch (SQLException e) {
            logger.log(BasicLevel.ERROR, "Cannot get a Connection:"+e);
            return 0;
        }
        // choose a connection number
        conb++;
        clist.put(new Integer(conb), c);
        logger.log(BasicLevel.DEBUG, "connection number="+conb);
        return conb;
    }

    /**
     * Return true if close Connection worked
     */
    public boolean closeConnection(int nb) throws RemoteException {
        logger.log(BasicLevel.DEBUG, "connection number="+conb);
        Connection c = (Connection) clist.remove(new Integer(nb));
        if (c == null) {
            logger.log(BasicLevel.ERROR, "Unknown Connection");
            return false;
        }
        try {
            c.close();
        } catch (SQLException e) {
            logger.log(BasicLevel.ERROR, "Cannot close Connection:"+e);
            return false;
        }
        boolean isclosed = false;
        try {
            isclosed = c.isClosed();
        } catch (SQLException e) {
            logger.log(BasicLevel.ERROR, "Cannot check if closed:"+e);
            return false;
        }
        return isclosed;
    }

    // ------------------------------------------------------------------
    // private methods
    // ------------------------------------------------------------------

    /*
     * get the DataSource given its name in JNDI
     */
    private DataSource getDataSource(String db) {

        // lookup the DataSource in the initial context
        DataSource ds = null;
        try {
            // get initial context
            if (ictx == null) {
                ictx = new InitialContext();
            }
            ds = (DataSource) PortableRemoteObject.narrow(ictx.lookup(db), DataSource.class);
        } catch (NamingException e) {
            logger.log(BasicLevel.ERROR, "Cannot lookup datasource "+db+": "+e);
            throw new EJBException("Cannot access DataSource");
        }

        // return it
        return ds;
    }

}
