// MosconeST.java
// Stateful Session Bean

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

import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.SQLException;

import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import javax.sql.DataSource;
import javax.transaction.NotSupportedException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;

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 transactions inside the bean.
 * This type of bean must NOT implement SessionSynchronization.
 */
public class MosconeST implements SessionBean {

    static private Logger logger = null;
    SessionContext ejbContext;

    // state of the sessionbean
    Context ictx = null;
    UserTransaction ut = null;
    Connection cnx = null;
    ResultSet rs = null;
    Statement st = null;

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

    /**
     * Set the associated session context. The container calls this method
     * after the instance creation.
     * The enterprise Bean instance should store the reference to the context
     * object in an instance variable.
     * This method is called with no transaction context.
     *
     * @param sessionContext A SessionContext interface for the instance.
     * @throws EJBException Thrown by the method to indicate a failure caused by
     * a system-level error.
     */
    public void setSessionContext(SessionContext ctx) {
        if( logger == null)
            logger = Log.getLogger("org.objectweb.jonas_tests");
        logger.log(BasicLevel.DEBUG, "");
        ejbContext = ctx;
    }

    /**
     * A container invokes this method before it ends the life of the session object.
     * This happens as a result of a client's invoking a remove operation, or when a
     *  container decides to terminate the session object after a timeout.
     * This method is called with no transaction context.
     *
     * @throws EJBException Thrown by the method to indicate a failure caused by
     * a system-level error.
     */
    public void ejbRemove() {
        logger.log(BasicLevel.DEBUG, "");
    }

    private void getConnection() throws RemoteException {
        try {
            ictx = new InitialContext();
            DataSource ds = (DataSource) PortableRemoteObject.narrow(ictx.lookup("jdbc_1"), DataSource.class);
            cnx = ds.getConnection();
        } catch (Exception e) {
            throw new RemoteException("cannot get connection: " + e);
        }
    }

    private void closeConnection() throws RemoteException {
        try {
            if (cnx != null) {
                cnx.close();
            }
        } catch (Exception e) {
            throw new RemoteException("cannot close connection: " + e);
        }
    }

    /**
     * The Session bean must define 1 or more ejbCreate methods.
     *
     * @throws CreateException Failure to create a session EJB object.
     */
    public void ejbCreate() throws CreateException {
        logger.log(BasicLevel.DEBUG, "");
    }


    /**
     * A container invokes this method on an instance before the instance
     * becomes disassociated with a specific EJB object.
     */
    public void ejbPassivate() {
        logger.log(BasicLevel.DEBUG, "");
    }

    /**
     * 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.
     */
    public void ejbActivate() {
        logger.log(BasicLevel.DEBUG, "");
    }

    // ------------------------------------------------------------------
    // Moscone implementation
    // ------------------------------------------------------------------

    /**
     * The following method start a transaction that will be continued
     * in other methods of this bean.
     */
    public void tx_start() throws RemoteException {
        logger.log(BasicLevel.DEBUG, "");

        // Obtain the UserTransaction interface
        try {
            ut = ejbContext.getUserTransaction();
        } catch (IllegalStateException e) {
            logger.log(BasicLevel.ERROR, "Can't get UserTransaction");
            throw new RemoteException("Can't get UserTransaction:", e);
        }

        // Start a global transaction
        try {
            ut.begin();
        } catch (NotSupportedException e) {
            logger.log(BasicLevel.ERROR, "Can't start Transaction");
            throw new RemoteException("Can't start Transaction:", e);
        } catch (SystemException e) {
            logger.log(BasicLevel.ERROR, "Can't start Transaction");
            throw new RemoteException("Can't start Transaction:", e);
        }
    }

    /**
     * This method commits the current transaction, started previously by tx_start().
     */
    public void tx_commit() throws RemoteException {
        logger.log(BasicLevel.DEBUG, "");

        // Commit this GLOBAL transaction
        try {
            ut.commit();
        } catch (Exception e) {
            logger.log(BasicLevel.ERROR, "Can't commit Transaction");
            throw new RemoteException("Can't commit Transaction:", e);
        }
    }

    /**
     * This method rolls back the current transaction, started previously by tx_start().
     */
    public void tx_rollback() throws RemoteException {
        logger.log(BasicLevel.DEBUG, "");

        // Roll back this GLOBAL transaction
        try {
            ut.rollback();
        } catch (Exception e) {
            logger.log(BasicLevel.ERROR, "Can't rollback Transaction");
            throw new RemoteException("Can't rollback Transaction:", e);
        }
    }

    /**
     * This method open a connection before starting transactions.
     */
    public void moscone1() throws RemoteException {
        logger.log(BasicLevel.DEBUG, "");

        getConnection();

        // Obtain the UserTransaction interface
        try {
            ut = ejbContext.getUserTransaction();
        } catch (IllegalStateException e) {
            logger.log(BasicLevel.ERROR, "Can't get UserTransaction");
            throw new RemoteException("Can't get UserTransaction:", e);
        }

        // Start a global transaction
        try {
            ut.begin();
        } catch (NotSupportedException e) {
            logger.log(BasicLevel.ERROR, "Can't start Transaction");
            throw new RemoteException("Can't start Transaction:", e);
        } catch (SystemException e) {
            logger.log(BasicLevel.ERROR, "Can't start Transaction");
            throw new RemoteException("Can't start Transaction:", e);
        }

        // work with connection.
        try {
            String ret = null;
            st = cnx.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            rs = st.executeQuery("SELECT * FROM JT2_MARCHE");
            if (rs.first()) {
                do {
                    ret += rs.getInt("IDMAR") + ":" + rs.getString("NOM") + "\n";
                } while(rs.next());
            }
            st.close();
        } catch (SQLException e) {
            throw new RemoteException("Error working on database: " + e);
        }

        // Commit this GLOBAL transaction
        try {
            ut.commit();
        } catch (Exception e) {
            logger.log(BasicLevel.ERROR, "Can't commit Transaction");
            throw new RemoteException("Can't commit Transaction:", e);
        }

        closeConnection();
    }

    /**
     * This method open a connection before starting transactions.
     */
    public void moscone2() throws RemoteException {
        logger.log(BasicLevel.DEBUG, "");

        int count = 0;

        getConnection();

        // Obtain the UserTransaction interface
        try {
            ut = ejbContext.getUserTransaction();
        } catch (IllegalStateException e) {
            logger.log(BasicLevel.ERROR, "Can't get UserTransaction");
            throw new RemoteException("Can't get UserTransaction:", e);
        }

        // Start a global transaction
        try {
            ut.begin();
        } catch (NotSupportedException e) {
            logger.log(BasicLevel.ERROR, "Can't start Transaction");
            throw new RemoteException("Can't start Transaction:", e);
        } catch (SystemException e) {
            logger.log(BasicLevel.ERROR, "Can't start Transaction");
            throw new RemoteException("Can't start Transaction:", e);
        }

        try {
            // Count the number of  lines
            st = cnx.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            rs = st.executeQuery("SELECT * FROM JT2_MARCHE");

            if (rs.first()) {
                do {
                    count++;
                } while(rs.next());
            }

            // try to add a line
            st.execute("insert into JT2_MARCHE values(5, 'Philippe Morris')");

            st.close();
        } catch (SQLException e) {
            throw new RemoteException("Error working on database: " + e);
        }

        // Rollback this GLOBAL transaction: The line should not be added!
        try {
            ut.rollback();
        } catch (Exception e) {
            logger.log(BasicLevel.ERROR, "Can't commit Transaction");
            throw new RemoteException("Can't commit Transaction:", e);
        }

        try {
            // Count the number of  lines again
            int count2 = 0;
            st = cnx.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            rs = st.executeQuery("SELECT * FROM JT2_MARCHE");

            if (rs.first()) {
                do {
                    count2++;
                } while(rs.next());
            }

            st.close();

            // Check that we did not change the database
            if (count2 > count) {
                throw new RemoteException("Bad result on this test:" + count2);
            }
        } catch (SQLException e) {
            throw new RemoteException("Error working on database: " + e);
        }

        closeConnection();
    }

}
