/*
 * 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.Hashtable;

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

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

import org.objectweb.jonas.jtests.beans.relation.s2pkcomp.AHomeRemote;
import org.objectweb.jonas.jtests.beans.relation.s2pkcomp.ARemote;
import org.objectweb.jonas.jtests.beans.relation.s2pkcomp.BHomeRemote;
import org.objectweb.jonas.jtests.beans.relation.s2pkcomp.BRemote;
import org.objectweb.jonas.jtests.beans.relation.s2pkcomp.Pk;

/**
 * This is an advanced test suite for home interface on entity bean CMP2.
 * Test about beans with mono-valued relation and with composite Pk with same fields names.
 * Beans used: s2pkcomp
 * @author S.Chassande-Barrioz
 */
public class F_Relation_s2pkcompEC2 extends A_Cmp2Util {


    private static String BEAN_HOME_A = "relation_s2pkcomp_AHome";
    private static String BEAN_HOME_B = "relation_s2pkcomp_BHome";
    protected static AHomeRemote ahome = null;
    protected static BHomeRemote bhome = null;

    static Hashtable a2b = new Hashtable();
    static Hashtable b2a = new Hashtable();
    static Pk pkNull = new Pk("null", 0);

    static {
        a2b.put(new Pk("a",1), new Pk("b",1));
        a2b.put(new Pk("a",2), new Pk("b",2));
        a2b.put(new Pk("a",3), pkNull);
        b2a.put(new Pk("b",1), new Pk("a",1));
        b2a.put(new Pk("b",2), new Pk("a",2));
        b2a.put(new Pk("b",3), pkNull);
    }

    public F_Relation_s2pkcompEC2(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("s2pkcomp", false);
                // lookup home used in the tests
                try {
                    ahome = (AHomeRemote) PortableRemoteObject.narrow(ictx.lookup(BEAN_HOME_A),
                                                                      AHomeRemote.class);
                    bhome = (BHomeRemote) PortableRemoteObject.narrow(ictx.lookup(BEAN_HOME_B),
                                                                      BHomeRemote.class);
                } catch (NamingException e) {
                    fail("Cannot get bean home: " + e.getMessage());
                }
                // check if tables have been initialized
                try {
                    ahome.findByPrimaryKey(new Pk("a", 2));
                } catch (Exception e) {
                    // Make the initialization needed for the tests
                    try {
                        utx.begin();
                        ARemote a1 = ahome.create("a", 1);
                        ARemote a2 = ahome.create("a", 2);
                        ahome.create("a", 3);
                        bhome.create("b", 1);
                        bhome.create("b", 2);
                        bhome.create("b", 3);
                        a1.assignB(new Pk("b", 1));
                        a2.assignB(new Pk("b", 2));
                    } 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("s2pkcomp");
            }
        }
    }

    /*
     * 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 relations A to B
        for (Enumeration ea = a2b.keys(); ea.hasMoreElements();) {
            Pk apk = (Pk) (ea.nextElement());
            ARemote a = ahome.findByPrimaryKey(apk);
            Pk bpkActual = a.retrieveB();
            Pk bpkExpected = (Pk)(a2b.get(apk));
            if (pkNull.equals(bpkExpected)) {
                if (bpkActual!=null) {
                    isOk = false;
                    msgerror.append("\nWrong relation for " + apk.toString()
                                    + " (expected: null"
                                    + ", found:" + bpkActual.toString() + ")");
                }
            } else {
                if (!bpkExpected.equals(bpkActual)) {
                    isOk = false;
                    msgerror.append("\nWrong relation for " + apk.toString()
                                    + " (expected:" + bpkExpected.toString()
                                    + ", found:" + bpkActual.toString() + ")");
                }
            }
        }
        // Check relations B to A
        for (Enumeration eb = b2a.keys(); eb.hasMoreElements();) {
            Pk bpk = (Pk) (eb.nextElement());
            BRemote b = bhome.findByPrimaryKey(bpk);
            Pk apkActual = b.retrieveA();
            Pk apkExpected = (Pk)(b2a.get(bpk));
            if (pkNull.equals(apkExpected)) {
                if (apkActual!=null) {
                    isOk = false;
                    msgerror.append("\nWrong relation for " + bpk.toString()
                                    + " (expected: null"
                                    + ", found:" + apkActual.toString() + ")");
                }
            } else {
                if (!apkExpected.equals(apkActual)) {
                    isOk = false;
                    msgerror.append("\nWrong relation for " + bpk.toString()
                                    + " (expected:" + apkExpected.toString()
                                    + ", found:" + apkActual.toString() + ")");
                }
            }
        }
        return isOk;
    }

    /**
     * test coherence relation one to one bidirectionnel, 
     * A1.remove=>A1removed && B1.retreiveA()==null 
     */
    public void _testCohRemoveA(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 b1 = bhome.findByPrimaryKey(new Pk("b", 1));
        // remove the bean
        ahome.remove(new Pk("a", 1));
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        if (tx != TX_RB) {
            // Verify expected result
            assertNull("Bad coherence of relation : null expected for b1.retreiveB() found :"+b1.retrieveA(), b1.retrieveA());	
            boolean not_found=false;
            try {
                ARemote a1 = ahome.findByPrimaryKey(new Pk("a", 1));
            } catch (ObjectNotFoundException e) {
                not_found=true;
            }
            assertTrue("a-1 is not removed",not_found);
            // undo
            ahome.create("a", 1);
            ARemote a1 = ahome.findByPrimaryKey(new Pk("a", 1));
            a1.assignB(new Pk("b", 1)); 
        }     
        checkIsInitialState();
    }

    public void testCohRemoveATxNo() throws Exception {
        _testCohRemoveA(TX_NO);
    }
    public void testCohRemoveATxCall() throws Exception {
        _testCohRemoveA(TX_CALL);
    }
    public void testCohRemoveATxRb() throws Exception {
        _testCohRemoveA(TX_RB);
    }

    /**
     * test coherence relation one to one bidirectionnel, 
     * B1.remove=>B1 removed && A1.retreiveB()==null
     * Same as  _testCohRemoveB except that the called remove method is on the bean
     * instead of the home.
     */
    public void _testCohBeanRemoveB(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();
        }
        ARemote a1 = ahome.findByPrimaryKey(new Pk("a", 1));
        BRemote b1 = bhome.findByPrimaryKey(new Pk("b", 1));
        // change the relation
        b1.remove();
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        if (tx != TX_RB) {
            // Verify expected result
            assertNull("Bad coherence of relation : null expected for a1.retreiveB() found :"+a1.retrieveB(), a1.retrieveB());	
            boolean not_found=false;
            try {
                b1 = bhome.findByPrimaryKey(new Pk("b", 1));
            } catch (ObjectNotFoundException e) {
                not_found=true;
            }
            assertTrue("b-1 is not removed", not_found);
            // undo
            bhome.create("b", 1);
            a1.assignB(new Pk("b", 1));
        }     
        checkIsInitialState();
    }

    public void testCohBeanRemoveBTxNo() throws Exception {
        _testCohBeanRemoveB(TX_NO);
    }
    public void testCohBeanRemoveBTxCall() throws Exception {
        _testCohBeanRemoveB(TX_CALL);
    }
    public void testCohBeanRemoveBTxRb() throws Exception {
        _testCohBeanRemoveB(TX_RB);
    }

    /**
     * test a finder method.
     * (bug #300612)
     */
    public void _testFindByBIsNull(int tx) throws Exception { 
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        Collection ca = ahome.findByBIsNull();
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        // Verify expected result
        assertEquals("Bad result for findByBIsNull: ", 1, ca.size());
    }

    public void testFindByBIsNullTxNo() throws Exception {
        _testFindByBIsNull(TX_NO);
    }
    // temporaly commented
    public void _testFindByBIsNullTxCall() throws Exception {
        _testFindByBIsNull(TX_CALL);
    }
    // temporaly commented
    public void _testFindByBIsNullTxRb() throws Exception {
        _testFindByBIsNull(TX_RB);
    }

    /**
     * test a finder method.
     */
    public void _testFindAById1Id2(int tx) throws Exception { 
        if ((tx == TX_CALL) || (tx == TX_RB)) {
            utx.begin();
        }
        ARemote a1 = ahome.findById1Id2("a", 1);
        if (tx == TX_CALL) {
            utx.commit();
        } else if (tx == TX_RB) {
            utx.rollback();
        }
        // Verify expected result
        Pk pkA1 = a1.getId();
        assertEquals("Bad id1 of a1: ", "a", pkA1.id1);
        assertEquals("Bad id2 of a1: ", 1, pkA1.id2);
    }

    public void testFindAById1Id2TxNo() throws Exception {
        _testFindAById1Id2(TX_NO);
    }
    public void testFindAById1Id2TxCall() throws Exception {
        _testFindAById1Id2(TX_CALL);
    }
    public void testFindAById1Id2TxRb() throws Exception {
        _testFindAById1Id2(TX_RB);
    }

    public static Test suite() {
        return new TestSuite(F_Relation_s2pkcompEC2.class);
    }

    public static void main (String args[]) {
        String testtorun = null;
        // Get args
        for (int argn = 0; argn < args.length; argn++) {
            String s_arg = args[argn];
            Integer i_arg;
            if (s_arg.equals("-n")) {
                testtorun = args[++argn];
            }
        }
        if (testtorun == null) {
            junit.textui.TestRunner.run(suite());
        } else {
            junit.textui.TestRunner.run(new F_Relation_s2pkcompEC2(testtorun));
        }
    }
}

