/*
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999 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 org.objectweb.jonas.jtests.clients.entity;

import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;

import javax.ejb.FinderException;
import javax.ejb.RemoveException;


import org.objectweb.jonas.jtests.beans.ebasic.Simple;
import org.objectweb.jonas.jtests.beans.ebasic.SimpleHome;
import org.objectweb.jonas.jtests.util.JTestCase;

/**
 * test cases common to both suites CMP and BMP.
 */
public abstract class A_BasicHomeInterface extends JTestCase {

    protected static SimpleHome home = null;

    public A_BasicHomeInterface(String name) {
        super(name);
    }

    /**
     * init environment:
     * - load beans
     * - create/init database for entities.
     */
    protected void setUp() {
        super.setUp();
        useBeans("ebasic", true);
    }

    /**
     * return SimpleHome, that can be either BMP or CMP bean.
     */
    abstract public SimpleHome getHome();

    /**
     *  testFindByPK verify that findByPrimaryKey find an existing entityBean
     *  pre condition: an element with "pk1" as primary key must exist in the database
     *
     *  findByPrimaryKey("testFindByPK") must pass.
     */
    public void testFindByPK() throws Exception {
        getHome().findByPrimaryKey("pk1");
    }

    /**
     * testFindUnexistingPK verify that findByPrimaryKey throw Finder Exception
     * when the specified entity beab doesn't exist.
     * pre condition there is no element with "pk999" in the table
     *
     * findByPrimaryKey("testFindUnexistingPK") must throw Finder Exception
     */
    public void testFindUnexistingPK() throws Exception {
        try {
            getHome().findByPrimaryKey("pk999");
            fail("findByPrimaryKey must throw ObjectNotFound Exception");
        } catch(FinderException e) {
        }
    }

    /**
     * testOtherFinder verify that we can use finder method other than findByPrimaryKey
     * pre condition an element with "pk2" as primary key must exist in the database
     * findByTestName("testOtherFinder") must pass
     *
     */
    public void testOtherFinder() throws Exception {
        getHome().findByTestName("pk2");
    }

    /**
     * testFinderEnumObjNotFound verify that a finder method that can return a Enumeration
     *            return an empty enumeration where there is no matching bean
     * pre condition there is no elements with NumTest = 999
     *
     */
    public void testFinderEnumObjNotFound() throws Exception {
        Simple    entity = null;
        Enumeration listOfEntity = null;

        listOfEntity = getHome().findInfoForNum(999);
        if (listOfEntity.hasMoreElements())
            fail("findInfoForNum must return an empty enumeration");

    }

    /**
     * testCreateNewEntity verify that we can create a new entityBean
     * We create a new entity testCreateNewEntity, 20, 6
     * the findByTestName("testCreateNewEntity") must pass and the resulting must be equals to 20
     * pre condition the testCreateNewEntity element must not exist
     *
     */
    public void testCreateNewEntity() throws Exception {
        getHome().create("pk100", 20, 6);
        Simple entity2 = getHome().findByTestName("pk100");
        assertEquals(20, entity2.getInfo());
        // cleaning
        entity2.remove();
    }

    /*
     * Simpler tests, for debugging only :
     * - cannot be passed twice
     * - are not independant each others
     * Use Jadmin to check instance counts are OK !
     */
    public void essaiC1() throws Exception {
        getHome().create("pke1", 20, 6);
    }
    public void essaiA1() throws Exception {
        getHome().findByPrimaryKey("pke1").getInfo();
    }
    public void essaiA1C() throws Exception {
        utx.begin();
        getHome().findByPrimaryKey("pke1").getInfo();
        utx.commit();
    }
    public void essaiF1() throws Exception {
        getHome().findByPrimaryKey("pke1");
    }
    public void essaiR1() throws Exception {
        getHome().remove("pke1");
    }
    public void essaiR1C() throws Exception {
        utx.begin();
        getHome().remove("pke1");
        utx.commit();
    }
    public void essaiC2() throws Exception {
        getHome().create("pke2", 20, 6);
    }
    public void essaiA2() throws Exception {
        getHome().findByPrimaryKey("pke2").getInfo();
    }
    public void essaiA2C() throws Exception {
        utx.begin();
        getHome().findByPrimaryKey("pke2").getInfo();
        utx.commit();
    }
    public void essaiF2() throws Exception {
        getHome().findByTestName("pke2");
    }
    public void essaiR2() throws Exception {
        getHome().findByTestName("pke2").remove();
    }
    public void essaiR2C() throws Exception {
        utx.begin();
        getHome().findByTestName("pke2").remove();
        utx.commit();
    }

    /*
     * testCreateRolledBack verify that we cannot access to a bean whose
     *          creation has been rolledback by a finder method.
     */
    public void testCreateRolledBack() throws Exception {
        // pre condition the pk110 element must not exist
        try {
            getHome().findByTestName("pk110");
            debug("First remove existing element pk110");
            getHome().remove("pk110");
        } catch(FinderException e) {
            // normal case.
        } catch(RemoveException e) {
            fail(e.getMessage());
        }
        utx.begin();
        try {
            Simple entity1 = getHome().create("pk110", 30, 7);
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            utx.rollback();
        }

        try {
            getHome().findByTestName("pk110");
            fail("element should not be found");
        } catch(FinderException e) {
        }
    }

    /*
     * testCreateRolledBack verify that we cannot access to a bean whose
     *          creation has been rolledback by findByPrimaryKey.
     */
    public void testCreateRolledBackPK() throws Exception {
        // pre condition the pk110 element must not exist
        try {
            getHome().findByTestName("pk110");
            debug("First remove existing element pk110");
            getHome().remove("pk110");
        } catch(FinderException e) {
            // normal case.
        } catch(RemoveException e) {
            fail(e.getMessage());
        }
        utx.begin();
        try {
            Simple entity1 = getHome().create("pk110", 30, 7);
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            utx.rollback();
        }

        try {
            getHome().findByPrimaryKey("pk110");
            fail("element should not be found");
        } catch(FinderException e) {
        }
    }

    /*
     * testRemoveViaEJBHome verify it is possible to remove an entity bean via the home object
     * pre condition the testRemoveViaEJBHome element must exist
     *
     */
    public void testRemoveViaEJBHome() throws Exception {
        getHome().remove("pk4");
        try {
            getHome().findByTestName("pk4");
            fail("not removed");
        } catch (FinderException e) {
        }
        // cleaning
        getHome().create("pk4", 40, 8);
    }

    /**
     * test remove by PK twice.
     * test that a removeByPrimaryKey can be followed by a create of the same entity.
     */
    public void testRemoveByPKTwice() throws Exception {
        getHome().create("pkn4", 40, 8);
        getHome().remove("pkn4");
        getHome().create("pkn4", 40, 8);
        getHome().remove("pkn4");
    }

    /**
     * test remove by EJBObject twice.
     * test that a remove can be followed by a create of the same entity.
     */
    public void testRemoveTwice() throws Exception {
        Simple entity = getHome().create("pkn5", 50, 8);
        entity.remove();
        entity = getHome().create("pkn5", 50, 8);
        entity.remove();
    }

    /*
     * testRemoveViaEJBObject verify it is possible to remove an entity bean via the EJBObject
     * pre condition the testRemoveViaEJBObject element must exist
     */
    public void testRemoveViaEJBObject() throws Exception {
        Simple entity = getHome().findByPrimaryKey("pk5");
        entity.remove();
        try {
            getHome().findByPrimaryKey("pk5");
            fail("not removed");
        } catch (FinderException e) {
        }
        // cleaning
        getHome().create("pk5", 50, 8);
    }

    /**
     * testRemoveInsideTransaction Verify that after a remove inside a transaction,
     * the bean cannot be found anymore
     * pre condition the pk4 instance must exist.
     */
    public void testRemoveInsideTransaction() throws Exception {
        Simple entity1 = getHome().findByPrimaryKey("pk4");
        utx.begin();
        try {
            entity1.remove();
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            utx.commit();
        }
        utx.begin();
        try {
            getHome().findByPrimaryKey("pk4");
            fail("should not exist anymore");
        } catch(FinderException e) {
        } finally {
            utx.rollback();
        }
        // cleaning
        getHome().create("pk4", 40, 8);
    }

    /**
     * same test without second tx.
     */
    public void testRemoveInTransaction() throws Exception {
        Simple entity1 = getHome().findByPrimaryKey("pk4");
        utx.begin();
        try {
            entity1.remove();
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            utx.commit();
        }
        try {
            getHome().findByPrimaryKey("pk4");
            fail("should not exist anymore");
        } catch(FinderException e) {
        }
        // cleaning
        getHome().create("pk4", 40, 8);
    }

    /**
     * test remove by PK in transaction
     */
    public void testHomeRemoveCommitted() throws Exception {
        utx.begin();
        try {
            getHome().remove("pk4");
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            utx.commit();
        }
        try {
            getHome().findByPrimaryKey("pk4");
            fail("should not exist anymore");
        } catch(FinderException e) {
        }
        // cleaning
        getHome().create("pk4", 40, 8);
    }

    /**
     * testRemoveRolledBack verify that we can access to a bean after remove has been rolledback
     * pre condition the testRemoveRolledBack  must exist
     */
    public void testRemoveRolledBack() throws Exception {
        Simple entity1 = getHome().findByPrimaryKey("pk6");
        utx.begin();
        try {
            entity1.remove();
        }
        catch (Exception e) {
            utx.rollback();
            fail(e.getMessage());
        }
        try {
            // Here we verify we cannot acces to the removed bean
            // (the transaction is not yet rolled back")
            getHome().findByPrimaryKey("pk6");
            fail("should not exist anymore at this point");
        } catch(FinderException e) {
        } finally {
            utx.rollback();
        }
        Simple entity3 = getHome().findByTestName("pk6");
        assertEquals(60, entity3.getInfo());
    }

    /**
     * testHomeRemoveRolledBack verify that we can access to a bean after remove has been rolledback
     * it is the same that testRemoveRolledBack but with home.remove(pk);
     * pre condition the testHomeRemoveRolledBack  must exist
     */
    public void testHomeRemoveRolledBack() throws Exception {
        Simple entity1 = getHome().findByPrimaryKey("pk7");
        utx.begin();
        try {
            getHome().remove("pk7");
        }
        catch (Exception e) {
            utx.rollback();
            fail(e.getMessage());
        }
        try {
            // Here we verify we cannot acces to the removed bean
            // (the transaction is not yet rolled back")
            getHome().findByPrimaryKey("pk7");
            fail("should not exist anymore at this point");
        } catch(FinderException e) {
        } finally {
            utx.rollback();
        }
        Simple entity3 = getHome().findByTestName("pk7");
        assertEquals(70, entity3.getInfo());
    }

    /**
     * testFinderEnum verify a finder method that return a Enumeration
     * pre condition there are 3 elements with num =4
     *     all of them have a field info equals to 10
     */
    public void testFinderEnum() throws Exception {
        Simple entity = null;
        Enumeration listOfEntity = getHome().findInfoForNum(4);
        int nb = 0;
        while (listOfEntity.hasMoreElements()) {
            entity = (Simple)javax.rmi.PortableRemoteObject.narrow(listOfEntity.nextElement(), Simple.class);
            assertEquals(10, entity.getInfo());
            nb++;
        }
        assertEquals(3, nb);
    }

    /**
     * Verify that we can create an entity and retrieve it by
     * a finder method inside the same transaction
     */
    public void testCreateFindUserTx() throws Exception {
        utx.begin();
        try {
            Simple e1 = getHome().create("pk120", 32, 7);
            Simple e2 = getHome().findByTestName("pk120");
            assertTrue(e2.isIdentical(e1));
        }
        catch (Exception e) {
            fail(e.getMessage());
        } finally {
            utx.rollback();
        }
    }


    /**
     * testFinderCollection  verify a finder method that return a Collection
     * pre condition there are 4 elements with c_numtest =4  (pk4,...,pk7)
     *     findInCollection returns all the beans where c_numtest = 4
     * this test is equivalent to testcase2 in SimpleTest in finder_col
     */
    public void testFinderCollection() throws Exception {
        Simple entity = null;
        Collection  cListEntity  = getHome().findInCollection();
        int nb = 0;
        Iterator icListEntity = cListEntity.iterator();
        while(icListEntity.hasNext()) {
            entity = (Simple) javax.rmi.PortableRemoteObject.narrow(icListEntity.next(), Simple.class);
            nb++;
        }
        assertEquals(4, nb);
    }

    /**
     * Test that the removed is no more seen after the rollback
     */
    public void testFindAfterRBR() throws Exception {
        getHome().findByPrimaryKey("pk8");
        utx.begin();
        getHome().remove("pk8");
        utx.rollback();
        getHome().findByTestName("pk8");
    }

    /**
     * test loop on finder method (findByPrimaryKey)
     */
    public void testLoopFindByPK() throws Exception {
        for (int i = 0; i < 20; i++) {
            getHome().findByPrimaryKey("pk9");
        }
    }

    /**
     * test loop on other finder method
     */
    public void testLoopFinder() throws Exception {
        for (int i = 0; i < 20; i++) {
            getHome().findByTestName("pk10");
        }
    }

    /**
     * test loopback on non reentrant bean in same tx
     */
    public void testLoopBackTx() throws Exception {
        Simple s = getHome().findByPrimaryKey("pk9");
        assertTrue(s.loopBackTx());
    }

    /**
     * test that we can access the instance twice in the same transaction,
     * even if the bean is non reentrant.
     */
    public void testAccessTwiceTx() throws Exception {
        Simple s = getHome().findByPrimaryKey("pk9");
        utx.begin();
        try {
            s.getNumTest();
            s.getNumTest();
        } finally {
            utx.commit();
        }
    }

    /**
     * test that we can access the instance twice in the same transaction,
     * even if the bean is non reentrant.
     */
    public void testFindAccessTx() throws Exception {
        utx.begin();
        try {
            Simple s = getHome().findByPrimaryKey("pk9");
            s.getNumTest();
        } finally {
            utx.commit();
        }
    }

    /**
     * test loopback on non reentrant bean outside tx
     * Invalid this test because loopback with no transaction context
     * is not clearly specified in EJB spec.
     */
    public void _testLoopBack() throws Exception {
        Simple s = getHome().findByPrimaryKey("pk10");
        assertTrue(s.loopBack());
    }
}
