/*
 * 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.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;

import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;

import junit.framework.Test;
import junit.framework.TestSuite;

import org.objectweb.jonas.jtests.beans.relation.omb.AHomeRemote;
import org.objectweb.jonas.jtests.beans.relation.omb.ARemote;
import org.objectweb.jonas.jtests.beans.relation.omb.BHomeRemote;
import org.objectweb.jonas.jtests.beans.relation.omb.BRemote;
import org.objectweb.jonas.jtests.beans.relation.omb.Front;
import org.objectweb.jonas.jtests.beans.relation.omb.FrontHome;

/**
 * For testing one-to-many bidirectional relationships
 * @author Ph. Durieux
 */
public abstract class A_omb extends A_Cmp2Util {

    public abstract AHomeRemote getAHome();
    public abstract BHomeRemote getBHome();
    protected static String BEAN_HOME_FRONT = "relation_omb_FrontHome";
    protected static FrontHome fhome = null;


    static Hashtable a2b = new Hashtable();
    static String[][] b2a = {
        {"bs0", null},
        {"bs1_1", "as1"},
        {"bs2_1", "as2"},
        {"bs2_2", "as2"},
        {"bs2_3", "as2"},
        {"bs3", null},
        {"b0", null},
        {"b1_1", "a1"},
        {"b1_2", "a1"},
        {"b1_3", "a1"},
        {"b2_1", "a2"},
        {"b2_2", "a2"},
        {"b2_3", "a2"},
        {"b3_1", "a3"}};

    static {
        a2b.put("as0", new String[]{});
        a2b.put("as1", new String[]{"bs1_1"});
        a2b.put("as2", new String[]{"bs2_1", "bs2_2", "bs2_3"});
        a2b.put("a0", new String[]{});
        a2b.put("a1", new String[]{"b1_1", "b1_2", "b1_3"});
        a2b.put("a2", new String[]{"b2_1", "b2_2", "b2_3"});
        a2b.put("a3", new String[]{"b3_1"});
        a2b.put("ax0", new String[]{});

        // Translate the String[] to a Collection of String
        for (Iterator it = a2b.keySet().iterator(); it.hasNext();) {
            String aname = (String) (it.next());
            String[] tb = (String[]) a2b.get(aname);
            ArrayList col = new ArrayList(tb.length);
            for (int i = 0; i < tb.length; i++) {
                col.add(tb[i]);
            }
            a2b.put(aname, col);
        }
    }


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

    protected boolean isInit = false;

    protected void setUp() {
        super.setUp();
        boolean ok = false;
        int nbtry = 0;
        while (!ok && nbtry < 3) {
            if (!isInit) {
                // load bean if not loaded yet
                useBeans("omb", false);
                try {
                    fhome = (FrontHome) PortableRemoteObject.narrow(ictx.lookup(BEAN_HOME_FRONT),
                                                                              FrontHome.class);
                } catch (NamingException e) {
                    fail("Cannot get bean home: " + e.getMessage());
                }
                // check if tables have been initialized
                try {
                    getAHome().findByPrimaryKey("a0");
                } catch (Exception e) {
                    try {
                        utx.begin();
                        getBHome().create("bs0");
                        getBHome().create("bs1_1");
                        getBHome().create("bs2_1");
                        getBHome().create("bs2_2");
                        getBHome().create("bs2_3");
                        getBHome().create("bs3");
                        getBHome().create("b0");
                        getBHome().create("b1_1");
                        getBHome().create("b1_2");
                        getBHome().create("b1_3");
                        getBHome().create("b2_1");
                        getBHome().create("b2_2");
                        getBHome().create("b2_3");
                        getBHome().create("b3_1");
                        getAHome().create("as0");
                        getAHome().create("as1").assignB((Collection) a2b.get("as1"));
                        getAHome().create("as2").assignB((Collection) a2b.get("as2"));
                        getAHome().create("a0");
                        getAHome().create("a1").assignB((Collection) a2b.get("a1"));
                        getAHome().create("a2").assignB((Collection) a2b.get("a2"));
                        getAHome().create("a3").assignB((Collection) a2b.get("a3"));
                        getAHome().create("ax0");
                    } catch (Exception i) {
                        fail("InitialState creation problem: " + i);
                    } finally {
                        try {
                            utx.commit();
                        } catch (Exception ii) {
                        }
                    }
                }
                isInit = true;
            }
            // Check that all is OK. Sometimes, a test has failed and has corrupted
            // the bean state in the database. We must unload and reload the bean then.
            nbtry++;
            try {
                if (initStateOK()) {
                    ok = true;
                }
            } catch (Exception e) {
            }
            if (!ok) {
                isInit = false;
                unloadBeans("omb");
            }
        }
    }

    /**
     * Check that we are in the same state as after the tables creation for thoses beans A and B
     * (ie if it is the initial state)
     */
    boolean initStateOK() throws Exception {
        boolean isOk = true;
        msgerror = new StringBuffer();
        // Check the relations A => B
        for (Enumeration ea = a2b.keys(); ea.hasMoreElements();) {
            String aname = (String) (ea.nextElement());
            ARemote a = getAHome().findByPrimaryKey(aname);
            Collection colActual = a.retrieveB();
            Collection colExpected = (Collection) (a2b.get(aname));
            if (!isCollectionEqual(colExpected, colActual)) {
                isOk = false;
                msgerror = msgerror.append("\nWrong relation for " + aname
                                           + " (expected:" + colExpected
                                           + ", found:" + colActual + ")");
            }
        }
        // Check the relation B => A
        for (int i = 0; i < b2a.length; i++) {
            BRemote b = getBHome().findByPrimaryKey(b2a[i][0]);
            String pkb = b.getId();
            String pka = b.retrieveA();
            if (b2a[i][1] == null && pka != null
                || b2a[i][1] != null && !b2a[i][1].equals(pka)) {
                isOk = false;
                msgerror = msgerror.append("\nWrong relation for " + pkb
                                           + " (expected:" + b2a[i][1]
                                           + ", found:" + pka + ")");
            }

        }
        return isOk;
    }


    /**
     * Check that the bean 'as0' has no relation.
     */
    public void tBasicGetEmptyA2B(int tx) throws Exception {
        Collection c = null;
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        ARemote a = getAHome().findByPrimaryKey("as0");
        if (tx == TX_CONT) {
            c = a.retrieveBInNewTx();
        } else {
            c = a.retrieveB();
        }
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        // checking
        checkIsInitialState();
    }

    public void testBasicGetEmptyA2BTxNo() throws Exception {
        tBasicGetEmptyA2B(TX_NO);
    }
    public void testBasicGetEmptyA2BTxCall() throws Exception {
        tBasicGetEmptyA2B(TX_CALL);
    }
    public void testBasicGetEmptyA2BTxCont() throws Exception {
        tBasicGetEmptyA2B(TX_CONT);
    }
    public void testBasicGetEmptyA2BTxRb() throws Exception {
        tBasicGetEmptyA2B(TX_RB);
    }

    /**
     * Check that the bean 'bs0' has no relation.
     */
    public void tBasicGetEmptyB2A(int tx) throws Exception {
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        String pka = null;
        BRemote b = getBHome().findByPrimaryKey("bs0");
        if (tx == TX_CONT) {
            pka = b.retrieveAInNewTx();
        } else {
            pka = b.retrieveA();
        }
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        // checking
        checkIsInitialState();
    }
    public void testBasicGetEmptyB2ANoTx() throws Exception {
        tBasicGetEmptyB2A(TX_NO);
    }
    public void testBasicGetEmptyB2ATxCall() throws Exception {
        tBasicGetEmptyB2A(TX_CALL);
    }
    public void testBasicGetEmptyB2ATxCont() throws Exception {
        tBasicGetEmptyB2A(TX_CONT);
    }
    public void testBasicGetEmptyB2ATxRb() throws Exception {
        tBasicGetEmptyB2A(TX_RB);
    }


    /**
     * Check that the bean 'as1' has only one relation with 'bs1_1'.
     */
    public void tBasicGetOneA2B(int tx) throws Exception {

        Collection c = null;
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        ARemote a = getAHome().findByPrimaryKey("as1");
        if (tx == TX_CONT) {
            c = a.retrieveBInNewTx();
        } else {
            c = a.retrieveB();
        }
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        // checking
        assertEquals("Relations size of: ", 1, c.size());
        String s = "bs1_1";
        assertTrue("Relation missing " + s + ": ", c.contains(s));
        checkIsInitialState();
    }

    public void testBasicGetOneA2BTxNo() throws Exception {
        tBasicGetOneA2B(TX_NO);
    }

    public void testBasicGetOneA2BTxCall() throws Exception {
        tBasicGetOneA2B(TX_CALL);
    }

    public void testBasicGetOneA2BTxCont() throws Exception {
        tBasicGetOneA2B(TX_CONT);
    }

    public void testBasicGetOneA2BTxRb() throws Exception {
        tBasicGetOneA2B(TX_RB);
    }


    /**
     * Check that the bean 'as2' has many relations with 'bs2_1', 'bs2_2', 'bs2_3'.
     */
    public void tBasicGetManyA2B(int tx) throws Exception {

        Collection c = null;
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        ARemote a = getAHome().findByPrimaryKey("as2");
        if (tx == TX_CONT) {
            c = a.retrieveBInNewTx();
        } else {
            c = a.retrieveB();
        }
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        // checking
        assertEquals("Relations size of: ", 3, c.size());
        String s = "bs2_1";
        assertTrue("Relation missing " + s + ": ", c.contains(s));
        s = "bs2_2";
        assertTrue("Relation missing " + s + ": ", c.contains(s));
        s = "bs2_3";
        assertTrue("Relation missing " + s + ": ", c.contains(s));
        checkIsInitialState();
    }

    public void testBasicGetManyA2BTxNo() throws Exception {
        tBasicGetManyA2B(TX_NO);
    }

    public void testBasicGetManyA2BTxCall() throws Exception {
        tBasicGetManyA2B(TX_CALL);
    }

    public void testBasicGetManyA2BTxCont() throws Exception {
        tBasicGetManyA2B(TX_CONT);
    }

    public void testBasicGetManyA2BTxRb() throws Exception {
        tBasicGetManyA2B(TX_RB);
    }


    /**
     * Set a A2B relation to empty.
     * Before: as1 <-> bs1_1
     * After:  as1     bs1_1
     * Check also the assignement rules for relationships.
     */
    public void tCohSetEmptyA2B(int tx) throws Exception {
        ArrayList ce = new ArrayList();
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        ARemote a = getAHome().findByPrimaryKey("as1");
        if (tx == TX_CONT) {
            a.assignBInNewTx(ce);
        } else {
            a.assignB(ce);
        }
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        if (tx != TX_RB) {
            // checking
            Collection ca = a.retrieveB();
            assertEquals("Relations size for as1 ", 0, ca.size());
            BRemote b = getBHome().findByPrimaryKey("bs1_1");
            assertNull("Bad relation for bs1_1", b.retrieveA());
            // undo
            a.addInB("bs1_1");
        }
        checkIsInitialState();
    }

    public void testCohSetEmptyA2BTxNo() throws Exception {
        tCohSetEmptyA2B(TX_NO);
    }

    public void testCohSetEmptyA2BTxCall() throws Exception {
        tCohSetEmptyA2B(TX_CALL);
    }

    public void testCohSetEmptyA2BTxCont() throws Exception {
        tCohSetEmptyA2B(TX_CONT);
    }

    public void testCohSetEmptyA2BTxRb() throws Exception {
        tCohSetEmptyA2B(TX_RB);
    }


    /**
     * Set a A2B relation to empty by clearing the collection.
     * Before: as1 <-> bs1_1
     * After:  as1     bs1_1
     * Check also the assignement rules for relationships.
     */
    public void tCohClearA2B(int tx) throws Exception {
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        ARemote a = getAHome().findByPrimaryKey("as1");
        if (tx == TX_CONT) {
            a.clearBInNewTx();
        } else {
            a.clearB();
        }
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        if (tx != TX_RB) {
            // checking
            Collection c = a.retrieveB();
            BRemote b = getBHome().findByPrimaryKey("bs1_1");
            String r = b.retrieveA();
            assertEquals("Expected empty collection for as1 relation: ", 0, c.size());
            assertNull("Expected no relation for bs1_1", r);
            // undo
            b.assignA("as1");
        }
        checkIsInitialState();
    }

    public void testCohClearA2BTxNo() throws Exception {
        tCohClearA2B(TX_NO);
    }

    public void testCohClearA2BTxCall() throws Exception {
        tCohClearA2B(TX_CALL);
    }

    public void testCohClearA2BTxCont() throws Exception {
        tCohClearA2B(TX_CONT);
    }

    public void testCohClearA2BTxRb() throws Exception {
        tCohClearA2B(TX_RB);
    }


    /**
     * Set an empty A2B relation with 1 element.
     * This added element was'nt already in an other relation.
     * Before: as0     b0
     * After:  as0 <-> b0
     * Check also the assignement rules for relationships.
     */
    public void tCohSetOneA2B(int tx) throws Exception {
        ArrayList ce = new ArrayList();
        ce.add("b0");
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        ARemote a = getAHome().findByPrimaryKey("as0");
        if (tx == TX_CONT) {
            a.assignBInNewTx(ce);
        } else {
            a.assignB(ce);
        }
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        if (tx != TX_RB) {
            // checking
            Collection ca = a.retrieveB();
            BRemote b = getBHome().findByPrimaryKey("b0");
            String r = b.retrieveA();
            assertTrue("Bad relations for as0 (required:" + ce + ", found:" + ca
                       + ")", isCollectionEqual(ce, ca));
            assertEquals("Bad relation for b0: ", "as0", r);
            // undo
            a.assignB(new ArrayList());
        }
        checkIsInitialState();
    }

    public void testCohSetOneA2BTxNo() throws Exception {
        tCohSetOneA2B(TX_NO);
    }

    public void testCohSetOneA2BTxCall() throws Exception {
        tCohSetOneA2B(TX_CALL);
    }

    public void testCohSetOneA2BTxCont() throws Exception {
        tCohSetOneA2B(TX_CONT);
    }

    public void testCohSetOneA2BTxRb() throws Exception {
        tCohSetOneA2B(TX_RB);
    }


    /**
     * Set a null B2A relation with 1 element.
     * This element was'nt already in an other relation.
     * Before: b0     as0
     * After:  b0 <-> as0
     * Check also the assignement rules for relationships.
     */
    public void tCohSet1B2A(int tx) throws Exception {
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        BRemote b = getBHome().findByPrimaryKey("b0");
        if (tx == TX_CONT) {
            b.assignAInNewTx("as0");
        } else {
            b.assignA("as0");
        }
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        // checking
        String r = b.retrieveA();
        ARemote a = getAHome().findByPrimaryKey("as0");
        Collection ca = a.retrieveB();
        ArrayList ce = new ArrayList();
        ce.add("b0");
        if (tx != TX_RB) {
            assertEquals("Bad relation for b0: ", "as0", r);
            assertTrue("Bad relations for as0 (required:" + ce + ", found:" + ca
                       + ")", isCollectionEqual(ce, ca));
            // undo
            b.assignA(null);
        }
        checkIsInitialState();
    }

    public void testCohSet1B2ATxNo() throws Exception {
        tCohSet1B2A(TX_NO);
    }

    public void testCohSet1B2ATxCall() throws Exception {
        tCohSet1B2A(TX_CALL);
    }

    public void testCohSet1B2ATxCont() throws Exception {
        tCohSet1B2A(TX_CONT);
    }

    public void testCohSet1B2ATxRb() throws Exception {
        tCohSet1B2A(TX_RB);
    }



    /**
     * Add an element to a empty relation.
     * Same as tCohSetOneA2B except that we add the element in the collection
     * instead of we set directly the collection.
     * Before: as0     bs0
     * After:  as0 <-> bs0
     * Check also the assignement rules for relationships.
     */
    public void tCohAddOneA2B(int tx) throws Exception {
        ArrayList ce = new ArrayList();
        ce.add("bs0");
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        ARemote a = getAHome().findByPrimaryKey("as0");
        if (tx == TX_CONT) {
            a.addInBInNewTx("bs0");
        } else {
            a.addInB("bs0");
        }
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        if (tx != TX_RB) {
            // checking
            Collection ca = a.retrieveB();
            BRemote b = getBHome().findByPrimaryKey("bs0");
            String r = b.retrieveA();
            assertTrue("Bad relations for as0 (required:" + ce + ", found:" + ca
                       + ")", isCollectionEqual(ce, ca));
            assertEquals("Bad relation for bs0", "as0", r);
            // undo
            a.assignB(new ArrayList());
        }
        checkIsInitialState();
    }

    public void testCohAddOneA2BTxNo() throws Exception {
        tCohAddOneA2B(TX_NO);
    }

    public void testCohAddOneA2BTxCall() throws Exception {
        tCohAddOneA2B(TX_CALL);
    }

    public void testCohAddOneA2BTxCont() throws Exception {
        tCohAddOneA2B(TX_CONT);
    }

    public void testCohAddOneA2BTxRb() throws Exception {
        tCohAddOneA2B(TX_RB);
    }


    /**
     * Re-Set a relation with N element.
     * Before: as1 <-> bs1_1
     *                 bs0
     *                 bs3
     * After:          bs1_1
     *         as1 <-> bs0
     *         as1 <-> bs3
     */
    public void tCohSetMultiA2B(int tx) throws Exception {
        ArrayList ce = new ArrayList(2);
        ce.add("bs0");
        ce.add("bs3");
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        ARemote a = getAHome().findByPrimaryKey("as1");
        Collection co = a.retrieveB();
        if (tx == TX_CONT) {
            a.assignBInNewTx(ce);
        } else {
            a.assignB(ce);
        }
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        if (tx != TX_RB) {
            // checking
            Collection ca = a.retrieveB();
            BRemote bs1_1 = getBHome().findByPrimaryKey("bs1_1");
            BRemote bs0 = getBHome().findByPrimaryKey("bs0");
            BRemote bs3 = getBHome().findByPrimaryKey("bs3");
            assertTrue("Bad relations for as1 (required:" + ce + ", found:" + ca
                       + ")", isCollectionEqual(ce, ca));
            assertNull("Not null relation for bs1_1", bs1_1.retrieveA());
            assertEquals("Bad relation for bs0: ", "as1", bs0.retrieveA());
            assertEquals("Bad relation for bs3: ", "as1", bs3.retrieveA());
            // undo
            a.assignB(co);
        }
        checkIsInitialState();
    }

    public void testCohSetMultiA2BTxNo() throws Exception {
        tCohSetMultiA2B(TX_NO);
    }

    public void testCohSetMultiA2BTxCall() throws Exception {
        tCohSetMultiA2B(TX_CALL);
    }

    public void testCohSetMultiA2BTxCont() throws Exception {
        tCohSetMultiA2B(TX_CONT);
    }

    public void testCohSetMultiA2BTxRb() throws Exception {
        tCohSetMultiA2B(TX_RB);
    }


    /**
     * Set a B2A relation with 1 element.
     * This element wasn't already in an other relation.
     * Before: bs1_1 <-> as1
     *                   as0
     * Change: bs1_1.assignA(as0);
     * After:  bs1_1 <-> as0
     *                   as1
     * Check also the assignement rules for relationships.
     *
     */
    public void tCohSet2B2A(int tx) throws Exception {
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        BRemote b = getBHome().findByPrimaryKey("bs1_1");
        if (tx == TX_CONT) {
            b.assignAInNewTx("as0");
        } else {
            b.assignA("as0");
        }
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        if (tx != TX_RB) {
            // checking
            String r = b.retrieveA();
            assertEquals("Bad relation for bs1_1: ", "as0", r);
            ARemote as0 = getAHome().findByPrimaryKey("as0");
            Collection cas0 = as0.retrieveB();
            ARemote as1 = getAHome().findByPrimaryKey("as1");
            Collection cas1 = as1.retrieveB();
            ArrayList ce = new ArrayList();
            ce.add("bs1_1");
            assertTrue("Bad relations for as0 (required:" + ce + ", found:" + cas0
                       + ")", isCollectionEqual(ce, cas0));
            ce = new ArrayList();
            assertTrue("Bad relations for as1 (required:" + ce + ", found:" + cas1
                       + ")", isCollectionEqual(ce, cas1));
            // undo
            b.assignA("as1");
        }
        checkIsInitialState();
    }

    public void testCohSet2B2ATxNo() throws Exception {
        tCohSet2B2A(TX_NO);
    }

    public void testCohSet2B2ATxCall() throws Exception {
        tCohSet2B2A(TX_CALL);
    }

    public void testCohSet2B2ATxCont() throws Exception {
        tCohSet2B2A(TX_CONT);
    }

    public void testCohSet2B2ATxRb() throws Exception {
        tCohSet2B2A(TX_RB);
    }


    /**
     * Set a B2A relation with 1 element.
     * This element was already in an other relation.
     * Before: bs1_1 <-> as1
     *         b3_1  <-> a3
     * Change: bs1_1.assignA(b3_1.retrieveA())
     * After:  bs1_1 <-> a3
     *         b3_1  <->
     *                   as1
     * Check also the assignement rules for relationships.
     *
     * See Spec chapter 10.3.7.3, change example: b2m.setA(b1n.getA())
     */
    public void tCohSet3B2A(int tx) throws Exception {
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        BRemote bs1_1 = getBHome().findByPrimaryKey("bs1_1");
        if (tx == TX_CONT) {
            bs1_1.assignAInNewTx("a3");
        } else {
            bs1_1.assignA("a3");
        }
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        if (tx != TX_RB) {
            // checking
            String rbs1_1 = bs1_1.retrieveA();
            ARemote as1 = getAHome().findByPrimaryKey("as1");
            Collection cas1 = as1.retrieveB();
            ARemote a3 = getAHome().findByPrimaryKey("a3");
            Collection ca3 = a3.retrieveB();
            assertEquals("Bad relation for bs1_1: ", "a3", rbs1_1);
            ArrayList ce = new ArrayList();
            ce.add("bs1_1");
            ce.add("b3_1");
            assertTrue("Bad relations for a3 (required:" + ce + ", found:" + ca3
                       + ")", isCollectionEqual(ce, ca3));
            ce = new ArrayList();
            assertTrue("Bad relations for as1 (required:" + ce + ", found:" + cas1
                       + ")", isCollectionEqual(ce, cas1));
            // undo
            bs1_1.assignA("as1");
        }
        checkIsInitialState();
    }

    public void testCohSet3B2ATxNo() throws Exception {
        tCohSet3B2A(TX_NO);
    }

    public void testCohSet3B2ATxCall() throws Exception {
        tCohSet3B2A(TX_CALL);
    }

    public void testCohSet3B2ATxCont() throws Exception {
        tCohSet3B2A(TX_CONT);
    }

    public void testCohSet3B2ATxRb() throws Exception {
        tCohSet3B2A(TX_RB);
    }


    /**
     * Remove an element in a relation.
     * Before: a3 <-> b3_1
     * Change: a3.retrieveB().remove(b3_1)
     * After:  a3     b3_1
     * See Spec chapter 10.3.7.3, change example: a1.getB().remove(b1n)
     */
    public void tCohRemoveInRelA2B(int tx) throws Exception {
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        ARemote a = getAHome().findByPrimaryKey("a3");
        if (tx == TX_CONT) {
            a.removeFromBInNewTx("b3_1");
        } else {
            a.removeFromB("b3_1");
        }
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        if (tx != TX_RB) {
            // checking
            Collection ca = a.retrieveB();
            BRemote b = getBHome().findByPrimaryKey("b3_1");
            String r = b.retrieveA();
            ArrayList ce = new ArrayList();
            assertTrue("Bad relations for a3: (required:" + ce + ", found:" + ca
                       + ")", isCollectionEqual(ce, ca));
            assertNull("Bad not null relation for b3_1", r);
            // undo
            a.addInB("b3_1");
        }
        // check to initial state
        checkIsInitialState();

    }

    public void testCohRemoveInRelA2BTxNo() throws Exception {
        tCohRemoveInRelA2B(TX_NO);
    }

    public void testCohRemoveInRelA2BTxCall() throws Exception {
        tCohRemoveInRelA2B(TX_CALL);
    }

    public void testCohRemoveInRelA2BTxCont() throws Exception {
        tCohRemoveInRelA2B(TX_CONT);
    }

    public void testCohRemoveInRelA2BTxRb() throws Exception {
        tCohRemoveInRelA2B(TX_RB);
    }


    /**
     * Remove a bean B which is in a relation.
     * Before: a3 <-> b3_1
     * change: B.remove(b3_1)
     * After:  a3
     *
     */
    public void tCohRemoveB(int tx) throws Exception {
        if (tx == TX_CONT) {
            // The transaction attribute of the remove method is TX_SUPPORT,
            // so the transaction cannot be initiate by the container
            fail("Transaction cannot be initiate by the container for this test");
        }
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        getBHome().remove("b3_1");
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        if (tx != TX_RB) {
            // checking
            ARemote a = getAHome().findByPrimaryKey("a3");
            Collection ca = a.retrieveB();
            assertEquals("Wrong relations size for a3: (expected: 0 found:"
                         + ca.size(), 0, ca.size());
            // undo
            getBHome().create("b3_1");
            a.addInBInNewTx("b3_1");
        }
        checkIsInitialState();
    }

    public void testCohRemoveBTxNo() throws Exception {
        tCohRemoveB(TX_NO);
    }

    public void testCohRemoveBTxCall() throws Exception {
        tCohRemoveB(TX_CALL);
    }

    public void testCohRemoveBTxRb() throws Exception {
        tCohRemoveB(TX_RB);
    }


    /**
     * Remove a bean B which is in a relation.
     * Same as tCohRemoveB except that the called remove method is on the bean
     * instead of the home.
     * Before: a3 <-> b3_1
     * change: B.remove(b3_1)
     * After:  a3
     *
     */
    public void tCohBeanRemoveB(int tx) throws Exception {
        if (tx == TX_CONT) {
            // The transaction attribute of the remove method is TX_SUPPORT,
            // so the transaction cannot be initiate by the container
            fail("Transaction cannot be initiate by the container for this test");
        }
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        BRemote b = getBHome().findByPrimaryKey("b3_1");
        b.remove();
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        if (tx != TX_RB) {
            // checking
            ARemote a = getAHome().findByPrimaryKey("a3");
            Collection ca = a.retrieveB();
            assertEquals("Wrong relations size for a3: (expected: 0 found:"
                         + ca.size(), 0, ca.size());
            // undo
            getBHome().create("b3_1");
            a.addInBInNewTx("b3_1");
        }
        checkIsInitialState();
    }

    public void testCohBeanRemoveBTxNo() throws Exception {
        tCohBeanRemoveB(TX_NO);
    }

    public void testCohBeanRemoveBTxCall() throws Exception {
        tCohBeanRemoveB(TX_CALL);
    }

    public void testCohBeanRemoveBTxRb() throws Exception {
        tCohBeanRemoveB(TX_RB);
    }



    /**
     * Set a empty relation A2B with 1 element.
     * This added element was already in a other relation.
     * Before: a0
     *         a3 <-> b3_1
     * Change: a0.assignB({b3_1})
     * After:  a0 <-> b3_1
     *         a3
     */
    public void tCohSetOne2A2B(int tx) throws Exception {
        ArrayList ce = new ArrayList();
        ce.add("b3_1");
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        ARemote a = getAHome().findByPrimaryKey("a0");
        if (tx == TX_CONT) {
            a.assignBInNewTx(ce);
        } else {
            a.assignB(ce);
        }
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        if (tx != TX_RB) {
            // checking
            Collection ca = a.retrieveB();
            ARemote a3 = getAHome().findByPrimaryKey("a3");
            Collection ca3 = a3.retrieveB();
            BRemote b = getBHome().findByPrimaryKey("b3_1");
            String r = b.retrieveA();
            assertTrue("Wrong relations a0 (required:" + ce + ", found:" + ca
                       + ")", isCollectionEqual(ce, ca));
            assertTrue("Wrong relations for b3_1" + ": found:" + ca3 ,
                       !ca3.contains("b3_1"));
            assertEquals("Bad relation for b3_1: ", "a0", r);
            // undo
            a.assignB(new ArrayList());
            a3.addInB("b3_1");
        }
        checkIsInitialState();
    }

    public void testCohSetOne2A2BTxNo() throws Exception {
        tCohSetOne2A2B(TX_NO);
    }

    public void testCohSetOne2A2BTxCall() throws Exception {
        tCohSetOne2A2B(TX_CALL);
    }

    public void testCohSetOne2A2BTxCont() throws Exception {
        tCohSetOne2A2B(TX_CONT);
    }

    public void testCohSetOne2A2BTxRb() throws Exception {
        tCohSetOne2A2B(TX_RB);
    }


    /**
     * Add an element to a relation A2B already multiple.
     * The element was already in a relation.
     * Check also the assignement rules for relationships.
     * Before: a1 <-> b1_1
     *            <-> b1_2
     *            <-> b1_3
     *         a3 <-> b3_1
     * Change: a1.addInB(b3_1)
     * After:  a1 <-> b1_1
     *            <-> b1_2
     *            <-> b1_3
     *            <-> b3_1
     *         a3
     *
     * See Spec chapter 10.3.7.3, change example: a1.getB().add(b2m)
     *
     */
    public void tCohAddOneInMultiA2B(int tx) throws Exception {

        ArrayList ce = new ArrayList();
        ce.add("b1_1");
        ce.add("b1_2");
        ce.add("b1_3");
        String bAdded = "b3_1";
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        ARemote a = getAHome().findByPrimaryKey("a1");
        if (tx == TX_CONT) {
            a.addInBInNewTx(bAdded);
        } else {
            a.addInB(bAdded);
        }
        ce.add(bAdded);
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        if (tx != TX_RB) {
            // checking
            Collection ca1 = a.retrieveB();
            ARemote a3 = getAHome().findByPrimaryKey("a3");
            Collection ca3 = a3.retrieveB();
            assertTrue("Bad relations for a1 (required:" + ce + ", found:" + ca1 + ")",
                       isCollectionEqual(ce, ca1));
            ce = new ArrayList();
            assertTrue("Bad relations for a3 (required:" + ce + ", found:" + ca3 + ")",
                       isCollectionEqual(ce, ca3));
            BRemote b = getBHome().findByPrimaryKey(bAdded);
            assertEquals("Bad relation for b3_1: ", "a1", b.retrieveA());
            // undo
            a3.addInB(bAdded);
        }

        checkIsInitialState();
    }

    public void testCohAddOneInMultiA2BTxNo() throws Exception {
        tCohAddOneInMultiA2B(TX_NO);
    }

    public void testCohAddOneInMultiA2BTxCall() throws Exception {
        tCohAddOneInMultiA2B(TX_CALL);
    }

    public void testCohAddOneInMultiA2BTxCont() throws Exception {
        tCohAddOneInMultiA2B(TX_CONT);
    }

    public void testCohAddOneInMultiA2BTxRb() throws Exception {
        tCohAddOneInMultiA2B(TX_RB);
    }


    /*
     * Set a relation to multiple.
     * All of element was already in an other relation.
     * Check also the assignement rules for relationships.
     * Before: a1 <-> b1_1
     *            <-> b1_2
     *            <-> b1_3
     *         a2 <-> b2_1
     *            <-> b2_2
     *            <-> b2_3
     * Change: a1.assignB(a2.retrieveB())
     * After:  a1 <-> b2_1
     *            <-> b2_2
     *            <-> b2_3
     *         a2
     *               b1_1
     *               b1_2
     *               b1_3
     *
     * Spec chapter 10.3.7.3, change example: a1.setB(a2.getB())
     *
     */
    public void tCohSetMultiBisA2B(int tx) throws Exception {
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        ARemote a1 = getAHome().findByPrimaryKey("a1");
        Collection co1 = a1.retrieveB();
        ARemote a2 = getAHome().findByPrimaryKey("a2");
        Collection co2 = a2.retrieveB();
        if (tx == TX_CONT) {
            a1.assignBInNewTx(co2);
        } else {
            a1.assignB(co2);
        }
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        if (tx != TX_RB) {
            // checking
            Collection ca1 = a1.retrieveB();
            assertTrue("Wrong relations for a1 (required:" + co2 + ", found:" + ca1 + ")", isCollectionEqual(co2, ca1));
            Collection ca2 = a2.retrieveB();
            assertEquals("Wrong relations size for a2: ", 0, ca2.size());
            BRemote b;
            b = getBHome().findByPrimaryKey("b1_1");
            assertNull("Wrong not null relation for b1_1: ", b.retrieveA());
            b = getBHome().findByPrimaryKey("b1_2");
            assertNull("Wrong not null relation for b1_2: ", b.retrieveA());
            b = getBHome().findByPrimaryKey("b1_3");
            assertNull("Wrong not null relation for b1_3: ", b.retrieveA());
            b = getBHome().findByPrimaryKey("b2_1");
            assertEquals("Wrong relation for b2_1: ", "a1", b.retrieveA());
            b = getBHome().findByPrimaryKey("b2_2");
            assertEquals("Wrong relation for b2_2: ", "a1", b.retrieveA());
            b = getBHome().findByPrimaryKey("b2_3");
            assertEquals("Wrong relation for b2_3: ", "a1", b.retrieveA());
            // Undo
            a1.assignB(co1);
            a2.assignB(co2);
        }
        checkIsInitialState();
    }

    public void testCohSetMultiBisA2BTxNo() throws Exception {
        tCohSetMultiBisA2B(TX_NO);
    }

    public void testCohSetMultiBisA2BTxCall() throws Exception {
        tCohSetMultiBisA2B(TX_CALL);
    }

    public void testCohSetMultiBisA2BTxCont() throws Exception {
        tCohSetMultiBisA2B(TX_CONT);
    }

    public void testCohSetMultiBisA2BTxRb() throws Exception {
        tCohSetMultiBisA2B(TX_RB);
    }

    /**
     * This test check it isn't allowed to reset the pk
     * and that the container throw the java.lang.IllegalStateException.
     * See spec 2.0, chapter 10.3.5, page 134.
     */
    public void testResetPkForbidden() throws Exception {
        ARemote a = getAHome().findByPrimaryKey("ax0");
        assertTrue("IllegalStateException not thrown when a pk value is reset",
                   a.testResetPkForbidden("aY0"));
    }

    /**
     * Test that we can create a bean and find it in the same transaction.
     */
    public void testCreateFindTx() throws Exception {
        utx.begin();
        ARemote project = getAHome().create("project");
        BRemote role = getBHome().create("role");
        project.addInB("role");
        getBHome().findByName("role", "project");
        utx.rollback();
        checkIsInitialState();
    }

    /**
     * Test that we can create a bean and find it in the same transaction,
     * using a method in A bean.
     */
    public void testCreateFindTx2() throws Exception {
        utx.begin();
        ARemote project = getAHome().create("project");
        project.addNewB("role");
        getBHome().findByName("role", "project");
        utx.rollback();
        checkIsInitialState();
    }

    /**
     * Reproduce the bug #300156: Error on creating Array from cmr-collection
     */
    public void testNewArrayListOnCmr() throws Exception {
        ArrayList ce = new ArrayList();
        ce.add("bs2_1");
        ce.add("bs2_2");
        ce.add("bs2_3");
        Collection ca = null;
        ARemote as2 = getAHome().findByPrimaryKey("as2");
        // Test of retrieveB()
        ca = as2.retrieveB();
        assertTrue("Bad relations for as2.retrieveB() (required:" + ce + ", found:" + ca
                   + ")", isCollectionEqual(ce, ca));
        // Test of retrieveBisB()
        ca = as2.retrieveBisB();
        assertTrue("Bad relations for as2.retrieveBisB() (required:" + ce + ", found:" + ca
                   + ")", isCollectionEqual(ce, ca));
    }

    /**
    * Ensure the javax.ejb.EJBException is thrown when trying
    * to invoke an accessor method on a deleted entitybean object
    */
    public void testRemove1() throws Exception {
        Front fb = fhome.create();
        fb.testRemove1();

    }

}
