/*
 * 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.rmi.RemoteException;
import java.util.Enumeration;

import javax.ejb.DuplicateKeyException;
import javax.ejb.EJBException;
import javax.ejb.FinderException;
import javax.ejb.Handle;
import javax.ejb.RemoveException;
import javax.rmi.PortableRemoteObject;
import javax.transaction.RollbackException;

import org.objectweb.jonas.jtests.beans.annuaire.Personne;
import org.objectweb.jonas.jtests.beans.annuaire.PersonneHome;
import org.objectweb.jonas.jtests.util.JTestCase;

/**
 * This is an advanced test suite for home interface on entity bean CMP.
 * Beans used: annuaire
 * @author Philippe Coq, Philippe Durieux, Helene Joanin (jonas team)
 */
public abstract class A_AdvancedHomeEC extends JTestCase {

    String mynum = "638";
    String myname = "Philippe Durieux";

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

    protected void setUp() {
        super.setUp();
    }

    /**
     * return PersonneHome, that can be either CMP v1 or CMP v2 bean.
     */
    abstract public PersonneHome getHome();

    /**
     * test du findAll, returning an Enumeration.
     * Some of the beans are already in the database, some other are created.
     */
    public void testFindAllEnum() throws Exception {
        Enumeration e = null;
        e = getHome().findAll();
        int count = 0;
        while (e.hasMoreElements()) {
            count++;
            Personne p = (Personne)
                PortableRemoteObject.narrow(e.nextElement(), Personne.class);
        }
        assertEquals("Wrong number  of bean (case 1)", 10, count);
        Personne p1 = getHome().create("Paul Morgan", "1234256");
        Personne p2 = getHome().create("Jean Richard", "1234356");
        Personne p3 = getHome().create("Jean-Paul Landry", "1234556");
        e = getHome().findAll();
        count = 0;
        while (e.hasMoreElements()) {
            count++;
            Personne p = (Personne)
                PortableRemoteObject.narrow(e.nextElement(), Personne.class);
        }
        assertEquals("Wrong number  of bean (case 2)", 13, count);
        // cleaning
        p1.remove();
        p2.remove();
        p3.remove();
        e = getHome().findAll();
        count = 0;
        while (e.hasMoreElements()) {
            count++;
            Personne p = (Personne) PortableRemoteObject.narrow(e.nextElement(), Personne.class);
        }
        assertEquals("Wrong number  of bean (case 3)", 10, count);
    }

    /**
     * test Create inside a Transaction.
     */
    public void testCreateInTx() throws Exception {
        utx.begin();
        try {
            getHome().create("Duke Ellington", "12235446");
            utx.commit();
            Personne p = getHome().findByNom("Duke Ellington");
            p.remove();
            utx.begin();
            getHome().create("Louis Armstrong", "12035446");
        } catch (Exception e) {
            fail(e.getMessage());
        } finally {
            utx.rollback();
        }
        try {
            getHome().findByNom("Louis Armstrong");
            fail("Should not find it");
        } catch (FinderException e) {
        }
    }

    /**
     * test a simple Create inside a Transaction.
     */
    public void testSimpleCreateInTx() throws Exception {
        utx.begin();
        try {
            getHome().create("Mike", "12235846");
        } catch (Exception e) {
            fail(e.getMessage());
        } finally {
            utx.commit();
        }
        Personne p = getHome().findByNom("Mike");
        p.remove();
    }

    /**
     * test a simple Remove
     * Find an instance already in database and remove it.
     */
    public void testSimpleRemove() throws Exception {
        Personne p = getHome().findByNom("Adriana Danes");
        p.remove();
        getHome().create("Adriana Danes", "777"); // cleaning
    }

    /**
     * test remove twice
     */
    public void testRemoveTwice() throws Exception {
        Personne p = getHome().create("Andre", "77710"); // first create a new element
        p.remove();
        try {
            p.remove();
            fail("Should not be able to remove this object twice");
        } catch (RemoteException e) {
            // OK
        } catch (Exception e) {
            fail("Bad exception raised: " + e);
        }
    }

    /**
     * test remove twice in same transaction
     */
    public void testRemoveTwiceTx() throws Exception {
        Personne p = getHome().create("Andre", "77710"); // first create a new element
        utx.begin();
        try {
            p.remove();
            p.remove();
            fail("Should not be able to remove this object twice");
        } catch (RemoteException e) {
            // OK: normal case because 2nd remove must fail
        } catch (Exception e) {
            fail("Bad exception raised: " + e);
        } finally {
            try {
                utx.commit();
            } catch (RollbackException e) {
                // Possibly go here, since 2nd remove failed.
                // clean object (1st remove has been rolled back!)
                // Don't use p because instance should have been discarded.
                getHome().remove("Andre");
            }
        }
    }

    /**
     * Test Create + Business Method inside the same Tx
     */
    public void testCreateAndBusiness() throws Exception {
        String num = "00000001";
        Personne p = null;
        utx.begin();
        try {
            p = getHome().create("Lionel Hampton", "92235446");
            p.setNumero(num);
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            utx.commit();
        }
        assertTrue(num.equals(p.getNumero()));
        p.remove();		// cleaning
    }

    /**
     * Test remove(pk): create / find / remove(pk) / find
     */
    public void testRemovePk() throws Exception {
        Personne p = null;
        String newman = "Xavier Spengler";
        p = getHome().create(newman, "6");
        getHome().findByPrimaryKey(newman);
        getHome().remove(newman);
        try {
            getHome().findByPrimaryKey(newman);
            fail("Should not exist anymore");
        }
        catch (FinderException e) {
        }
    }

    /**
     * Test remove(handle): create / find / remove(handle) / find
     * CMP2: bug #300419
     */
    public void testRemoveHandle() throws Exception {
        Personne p = null;
        String newman = "Xavier Spengler";
        p = getHome().create(newman, "6");
        Handle h = p.getHandle();
        getHome().remove(h);
        try {
            getHome().findByPrimaryKey(newman);
            fail("Should not exist anymore");
        } catch (FinderException e) {
        }
    }

    /**
     * Test remove(pk) inside transaction:
     * create / find / remove(pk) / find
     */
    public void testRemovePkInTx() throws Exception {
        Personne p = null;
        String newman = "Xavier Spengler";
        p = getHome().create(newman, "6");
        getHome().findByPrimaryKey(newman);
        utx.begin();
        try {
            getHome().remove(newman);
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            utx.commit();
        }
        try {
            getHome().findByPrimaryKey(newman);
            fail("Should not exist anymore");
        }
        catch (FinderException e) {
        }
    }

    /**
     * Test remove(pk) of a non-existent pk.
     */
    public void testRemovePkNonExistent() throws Exception {
        String man = "NonExistent";
        try {
            getHome().remove(man);
            fail("No RemoteException");
        }
        catch (RemoteException e) {
        }
    }

    /**
     * Test remove(pk) of a non-existent pk inside transaction.
     * See ejb2.1 18.3.1
     */
    public void testRemovePkNonExistentInTx() throws Exception {
        String man = "NonExistent";
        utx.begin();
        try {
            getHome().remove(man);
            fail("No RemoteException");
        }
        catch (RemoteException e) {
        } finally {
            try {
                utx.commit();
                fail("transaction should be marked as rollback");
            } catch (RollbackException e) {
            }
        }
    }

    /**
     * Test bean.remove :
     * create / find / remove / find
     */
    public void testRemove() throws Exception {
        Personne p = null;
        String newman = "Jeannot";
        p = getHome().create(newman, "5");
        getHome().findByPrimaryKey(newman);
        p.remove();
        try {
            getHome().findByPrimaryKey(newman);
            fail("Should not exist anymore");
        }
        catch (FinderException e) {
        }
    }

    /**
     * Test bean.remove :
     * create / find / remove / bussiness method
     * Ensure the javax.ejb.EJBException is thrown when trying
     * to invoke an accessor method on a deleted entitybean object
     */
    public void testRemove2() throws Exception {
        Personne p = null;
        String newman = "Jeannot2";
        p = getHome().create(newman, "5");
        getHome().findByPrimaryKey(newman);
        p.remove();
        try {
            p.getNumero();
            fail("Should not exist anymore");
        }
        catch (Exception e) {
        }
    }

    /**
     * Test remove by PK in a tx rolled back
     * we modify the bean instance outside tx, then we check that
     * the bean state has not been changed by the rolled back remove
     */
    public void testRemovePKRB() throws Exception {
        String newnum = "344";
        String name = "remove-by-pk";
        Personne p = getHome().create(name, "420");
        p.setNumero(newnum);
        utx.begin();
        getHome().remove(name);
        utx.rollback();
        try {
            assertEquals("Lost modified value", newnum, p.getNumero());
        } finally {
            // cleaning
            getHome().remove(name);
        }
    }

    /**
     * Test remove in a tx rolled back
     * we modify the bean instance outside tx, then we check that
     * the bean state has not been changed by the rolled back remove
     */
    public void testRemoveRB() throws Exception {
        String newnum = "345";
        String name = "remove-instance";
        Personne p = getHome().create(name, "421");
        p.setNumero(newnum);
        utx.begin();
        p.remove();
        utx.rollback();
        try {
            assertEquals("Lost modified value", newnum, p.getNumero());
        } finally {
            // cleaning
            //p.remove();
            getHome().remove(name);
        }
    }

    /**
     * Test bean.remove inside a tx:
     * create / find / remove / find
     */
    public void testRemoveInTx() throws Exception {
        Personne p = null;
        String newman = "Jeannot";
        p = getHome().create(newman, "5");
        getHome().findByPrimaryKey(newman);
        utx.begin();
        try {
            p.remove();
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            utx.commit();
        }
        try {
            getHome().findByPrimaryKey(newman);
            fail("Should not exist anymore");
        }
        catch (FinderException e) {
        }
    }

    /**
     * Test finder in transactions
     */
    public void testFinderInTx() throws Exception {
        Personne p = null;

        // FindByPrimaryKey
        for (int i = 0; i < 5; i++) {
            utx.begin();
            try {
                p = getHome().findByPrimaryKey(myname);
            }
            catch (Exception e) {
                fail(e.getMessage());
            }
            finally {
                utx.commit();
            }
        }
        assertTrue(p.getNumero().equals(mynum));

        // FindByNum
        for (int i = 0; i < 5; i++) {
            utx.begin();
            try {
                p = getHome().findByNom(myname);
            }
            catch (Exception e) {
                fail(e.getMessage());
            }
            finally {
                utx.commit();
            }
        }
        assertTrue(p.getNumero().equals(mynum));

        // FindAll
        for (int i = 0; i < 5; i++) {
            utx.begin();
            try {
                getHome().findAll();
            }
            catch (Exception e) {
                fail(e.getMessage());
            }
            finally {
                utx.commit();
            }
        }
    }

    /**
     * Test create + remove in transactions
     */
    public void testCreateRemoveInTx() throws Exception {
        for (int i = 0; i < 20; i++) {
            utx.begin();
            try {
                getHome().create("Eric Paire", "500");
                getHome().remove("Eric Paire");
            }
            catch (Exception e) {
                fail(e.getMessage());
            }
            finally {
                utx.commit();
            }
        }
    }

    /**
     * test 2 findByPrimaryKey in the same tx, with an update
     * between the 2 calls. We shall see the modifications, even before
     * the commit occurs.
     */
    public void testFindTwice() throws Exception {
        String num = "0032";
        String oldnum = "";
        utx.begin();
        Personne p = null;
        try {
            p = getHome().findByPrimaryKey(myname);
            oldnum = p.getNumero();
            p.setNumero(num);
            Personne p2 = getHome().findByPrimaryKey(myname);
            assertTrue(p2.getNumero().equals(num));
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            utx.rollback();
        }
        p = getHome().findByPrimaryKey(myname);
        assertTrue(p.getNumero().equals(oldnum));
    }

    /**
     * Test DuplicateKeyException.
     * Create in an implicit tx
     * known bug #300836: remove it from the list
     */
    public void _testDuplicateKeyTx() throws Exception {
        getHome().findByPrimaryKey(myname);
        try {
            getHome().create(myname, "700");
            fail("DuplicateKeyException not raised");
        } catch (DuplicateKeyException e) {
        } catch (Exception e) {
            fail("Bad Exception raised:" + e);
        }
    }

    /**
     * Test DuplicateKeyException.
     * Create outside tx
     * known bug #300836: remove it from the list
     */
    public void _testDuplicateKey() throws Exception {
        try {
            getHome().create(myname, "700", true);
            fail("DuplicateKeyException not raised");
        } catch (DuplicateKeyException e) {
        } catch (Exception e) {
            fail("Bad exception raised: " + e);
        }
    }

    public void testDuplicateKey2() throws Exception {
        getHome().findByPrimaryKey(myname);
        try {
            getHome().create(myname, "700", true);
            fail("DuplicateKeyException not raised");
        } catch (DuplicateKeyException e) {
            // should go here
        } catch (Exception e) {
            // go here in jonas-3-3-6
        }
        utx.begin();
        Personne p0 = getHome().findByPrimaryKey(myname);
        utx.commit();
        String num = p0.getNumero();
        assertTrue("bad value: " + num + ", should be: " + mynum, num.equals(mynum));
    }

    public void testRemoveCreateTx() throws Exception {
        utx.begin();
        try {
            getHome().remove(myname);
            getHome().create(myname, mynum, true);
        } finally {
            utx.commit();
        }
        getHome().findByPrimaryKey(myname);
    }

    /**
     * Verify that we can create an entity with a null name
     *
     */
    public void testCreateNull() throws Exception {
        getHome().create("nullv", null);
        getHome().remove("nullv");	// cleaning
    }

    /**
     * Test many Create calls
     */
    public void testManyCreate() throws Exception {
        int nbCreate = 20;
        for (int i = 1; i <= nbCreate; i++) {
            Personne p = getHome().create("manycreate" + i, "num" + i, true);
        }
        for (int i = 1; i <= nbCreate; i++) {
            getHome().remove("manycreate" + i);
        }
    }

    public void testRemoveCreate() throws Exception {
        Personne p1 = null;
        String man1 = "Count";
        utx.begin();
        getHome().create(man1, "1");
        utx.commit();
        p1 = getHome().findByNom(man1);
        p1.remove();
        getHome().create(man1, "1");
        p1 = getHome().findByNom(man1);
        p1.remove();
    }

    public void testRemoveCreate2() throws Exception {
        Personne p1 = null;
        String man1 = "Duke";
        p1 = getHome().create(man1, "1");
        p1.remove();
        p1 = getHome().create(man1, "1");
        p1.remove();
    }

    public void testRemoveCreate3() throws Exception {
        Personne p4 = null;
        String man4 = "Dexter";
        p4 = getHome().create(man4, "4");
        getHome().findByPrimaryKey(man4);
        getHome().remove(man4);
        p4 = getHome().create(man4, "44");
        getHome().remove(man4);
    }

    /**
     * Combination of other tests
     * This test the transaction isolation in entity bean container
     * this test must never hang
     */
    public void testIsolation() throws Exception {
        Personne p0 = null;
        Personne p1 = null;
        Personne p2 = null;
        Personne p3 = null;
        Personne p4 = null;
        Personne p5 = null;
        String n = null;
        String man1 = "Duke Ellington";
        String man2 = "Louis Armstrong";
        String man3 = "Lionel Hampton";
        String man4 = "Dexter Gordon";
        String man5 = "Bill Evans";

        // man1
        utx.begin();
        try {
            getHome().create(man1, "1");
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            utx.commit();
        }
        p1 = getHome().findByNom(man1);

        // man2
        utx.begin();
        try {
            getHome().create(man2, "2");
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            utx.rollback();
        }

        // man3
        utx.begin();
        try {
            p3 = getHome().create(man3, "3");
            p3.setNumero("33");
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            utx.commit();
        }
        assertTrue(p3.getNumero().equals("33"));

        // cleaning
        p1.remove();
        p3.remove();

        // man4
        p4 = getHome().create(man4, "4");
        getHome().findByPrimaryKey(man4);
        getHome().remove(man4);
        p4 = getHome().create(man4, "44");
        getHome().findByPrimaryKey(man4);
        utx.begin();
        try {
            getHome().remove(man4);
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            utx.commit();
        }

        // man5
        p5 = getHome().create(man5, "5");
        getHome().findByPrimaryKey(man5);
        p5.remove();
        p5 = getHome().create(man5, "5");
        getHome().findByPrimaryKey(man5);
        utx.begin();
        try {
            p5.remove();
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            utx.commit();
        }

        // existing person
        for (int i = 0; i < 5; i++) {
            utx.begin();
            try {
                p0 = getHome().findByPrimaryKey(myname);
            }
            catch (Exception e) {
                fail(e.getMessage());
            }
            finally {
                utx.commit();
            }
        }
        assertTrue(p0.getNumero().equals(mynum));

        // findall
        for (int i = 0; i < 5; i++) {
            utx.begin();
            try {
                getHome().findAll();
            }
            catch (Exception e) {
                fail(e.getMessage());
            }
            finally {
                utx.commit();
            }
        }

        // create + remove
        for (int i = 0; i < 20; i++) {
            utx.begin();
            try {
                getHome().create(man2, "500");
                getHome().remove(man2);
            }
            catch (Exception e) {
                fail(e.getMessage());
            }
            finally {
                utx.commit();
            }
        }

        // findByPrimaryKey twice
        utx.begin();
        try {
            p0 = getHome().findByPrimaryKey(myname);
            p0.setNumero("0");
            p2 = getHome().findByPrimaryKey(myname);
            assertTrue(p2.getNumero().equals("0"));
        }
        catch (Exception e) {
            fail(e.getMessage());
        }
        finally {
            utx.rollback();
        }

    }

}
