package org.mockejb.test;


import java.util.Collection;

import javax.naming.*;
import com.mockrunner.mock.ejb.MockUserTransaction;

import org.mockejb.*;
import org.mockejb.jndi.*;
import org.mockejb.interceptor.*;


/**
 * Demonstrates the use of JDBC and transactions with MockEJB. 
 * 
 * @author Alexander Ananiev
 */
public class JdbcAndTransactionTest  extends OptionalCactusTestCase {

    // State of this test case. These variables are initialized by setUp method
    private SampleService sampleService;
    private SampleServiceHome sampleServiceHome;
    private Context context;

    // Aspect system used by this test
    private AspectSystem aspectSystem;
    

    private MockUserTransaction mockTransaction;

    /**
     * Constructor for JdbcTransactionTest.
     * @param name name of the test
     */
    public JdbcAndTransactionTest(String name) {
        super(name);
    }
    
    /**
     * Deploys and creates EJBs needed for our tests.
     */    
    public void setUp() throws Exception {

        aspectSystem =  AspectSystemFactory.getAspectSystem();
        
        // inside the app server use its InitialContext
        if ( isRunningOnServer() ) {
            context = new InitialContext( );
        }    
        // if the test runs outside of the app server
        else {
            MockContextFactory.setAsInitial();
            // create the initial context that will be used for binding EJBs
            context = new InitialContext( );
            // Create an instance of the MockContainer
            MockContainer mockContainer = new MockContainer( context );

            /* Create deployment descriptor of our sample bean.
             * MockEjb does not support XML descriptors.
             */
            SessionBeanDescriptor sampleBeanDescriptor = 
                new SessionBeanDescriptor( SampleService.JNDI_NAME, 
                SampleServiceHome.class, SampleService.class, SampleServiceBean.class );
            // Deploy operation simply creates Home and binds it to JNDI
            mockContainer.deploy( sampleBeanDescriptor );

            // StatelssSampleBean calls SampleHelperBean, so we need to deploy it too
    
            SessionBeanDescriptor helperBeanDescriptor = 
                new SessionBeanDescriptor( SampleServiceBean.HELPER_BEAN_JNDI_NAME, 
                SampleHelperHome.class, SampleHelper.class, SampleHelperBean.class );
            mockContainer.deploy( helperBeanDescriptor );

            /* Prepare the data source if we are not running in the app server.
             * We assume that the app server has the data source pre-configured.
             * You don't have to do it that way, the code will work in both cases, 
             * however it is "cleaner" to rely on the infrastructure provided by the 
             * app server for in-container testing. Also, the data source inside the 
             * app server may point to a completely different database. 
             */

            // Instantiate DataSource implementation
            
            // You can use Jakarta DBCP
            //org.apache.commons.dbcp.BasicDataSource ds = new org.apache.commons.dbcp.BasicDataSource();
            //ds.setDriverClassName("com.mysql.jdbc.Driver");
            
            // You can also use DataSource implementation that comes with MySQL driver
            com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource();
        
            // Or, in case of Oracle driver:
            // oracle.jdbc.pool.OracleDataSource ds = oracle.jdbc.pool.OracleDataSource();
            
            // point to the database - we use the default MySql database.
            ds.setUrl("jdbc:mysql://localhost:3306/test");
            
            // add to the context
            context.rebind("java:comp/env/jdbc/SampleDataSource", ds);
            
            // we use MockTransaction outside of the app server            
            mockTransaction = new MockUserTransaction();
            context.rebind("javax.transaction.UserTransaction", mockTransaction );            
        }

        // All EJBs are now deployed
        
        // To get the Sample bean we use the standard J2EE routine
       
        // Lookup the home
        SampleServiceHome sampleHome = (SampleServiceHome)context.lookup( SampleService.JNDI_NAME );
        
        // create the bean
        sampleService = sampleHome.create();

    }

    /**
     * Performs the necessary cleanup by restoring the system properties that
     * were modified by MockContextFactory.setAsInitial().
     * This is needed in case if the test runs inside the container, so it would
     * not affect the tests that run after it.  
     */
    public void tearDown() {
        
        MockContextFactory.revertSetAsInitial();
    }

    /**
     * Tests EJB interaction with the database without transactions, i.e, 
     * the transactional policy is "Suppports".
     */  
    public void testJdbc() throws Exception {
        
        /* Read something from the database
         * We assume the existence of the "test_table" with a column "name".
         */
        Collection values = sampleService.selectFromTable( "test_table", "name");
        assertNotNull(values);
    }
    
    /**
     * Demonstrates the use of transactions with MockEJB. 
     * Outside of the container, we use MockTransaction, inside we can rely on 
     * the real transaction support. Inside the container we can't really test much. 
     */
    public void testTransactions() throws Exception {

        // it does not make sense to run this test on the server since we can't test much 
        if ( isRunningOnServer() ){
            return;
        }        
        
        // set our policy
        aspectSystem.add( new ClassPatternPointcut("org.mockejb.test"), 
            new TransactionManager( TransactionPolicy.REQUIRED ));
        
        Collection values = sampleService.selectFromTable( "test_table", "name");
        assertNotNull(values);
        
        assertTrue( mockTransaction.wasBeginCalled() );
        assertTrue( mockTransaction.wasCommitCalled() );
        
        // Call the method that explicitly rolls back the transaction using ejb context        
        mockTransaction.reset();
        
        sampleService.rollbackSampleTransaction();
        
        assertTrue( mockTransaction.wasBeginCalled() );
        assertTrue( mockTransaction.wasRollbackOnlyCalled() );
        assertTrue( mockTransaction.wasRollbackCalled() );

        // TODO: test other policies
    
    }
    
    

}