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

import java.util.Collection;

import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import javax.transaction.RollbackException;
import javax.transaction.UserTransaction;

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

import org.objectweb.jonas.jtests.beans.cluster.Identity;
import org.objectweb.jonas.jtests.beans.cluster.IdentityHome;
import org.objectweb.jonas.jtests.util.JTestCase;

/**
 * Test clustering first level (mainly test the "shared" flag for entity beans)
 * Assume that transaction isolation is "serialized" !
 * Beans used: cluster
 * @author Philippe Durieux
 */

/**
 * A thread will work on a Home. It executes one of the predefined functions,
 * inside a transaction.
 */
class A_clthread extends Thread {
    int function;
    IdentityHome home;
    String name;
    int number;
    UserTransaction utx = F_Cluster.utx;

    public A_clthread(IdentityHome home, int function, String name, int number) {
        this.home = home;
        this.function = function;
        this.name = name;
        this.number = number;
    }

    public void run() {
        Identity id = null;

        try {
            // start transaction
            utx.begin();

            // process the required function
            switch (function) {
            case F_Cluster.READ:
                id = home.findByPrimaryKey(name);
                id.getNumber();
                break;
            case F_Cluster.WRITE:
                id = home.findByPrimaryKey(name);
                id.setNumber(number);
                break;
            case F_Cluster.CREATE:
                id = home.create(name, number);
                break;
            case F_Cluster.REMOVE:
                home.remove(name);
                break;
            }

            // commit transaction
            utx.commit();
        } catch (Exception e) {
            F_Cluster.threadError = e;
        }
    }
}

public class F_Cluster extends JTestCase {

    protected static IdentityHome home1 = null;
    protected static IdentityHome home2 = null;
    protected static IdentityHome home3 = null;

    public static Exception threadError = null;

    // possible values for function
    public static final int READ = 1;
    public static final int WRITE = 2;
    public static final int CREATE = 3;
    public static final int REMOVE = 4;

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

    protected void setUp() {
        super.setUp();
        if (home3 == null) {
            useBeans("cluster", true);
            try {
                home1 = (IdentityHome) PortableRemoteObject.narrow(ictx.lookup("clusterId_1"), IdentityHome.class);
                assertNotNull("home of clusterId_1 is null", home1);
                home2 = (IdentityHome) PortableRemoteObject.narrow(ictx.lookup("clusterId_2"), IdentityHome.class);
                assertNotNull("home of clusterId_2 is null", home2);
                home3 = (IdentityHome) PortableRemoteObject.narrow(ictx.lookup("clusterId_3"), IdentityHome.class);
                assertNotNull("home of clusterId_3 is null", home3);
            } catch (NamingException e) {
                fail("Cannot get bean home: " + e.getMessage());
            }
        }
    }

    protected void tearDown() throws Exception {
		threadError = null;
		super.tearDown();
    }

    /**
     * Basic Test to see if all works with a single bean (it should!)
     * Pre-requisite: the table has been initialized first with some
     * well-known values (name1, ...)
     */
    public void testSingle() throws Exception {
        // test finder methods
        Identity name1 = home3.findByPrimaryKey("name1");
        assertTrue(name1.getNumber() == 1000);
        Collection c1000 = home3.findByNumber(1000);
        home3.findAll();
        // test basic create/access/modify/remove operations
        Identity newname = home3.create("name28", 300);
        int nv = 200;
        newname.setNumber(nv);
        assertEquals("Wrong field value", nv, newname.getNumber());
        newname.remove();
    }

    /**
     * Test with 2 homes (no parallellism)
     */
    public void test2Home() throws Exception {
        int val1 = 31;
        int val2 = 32;
        Identity name1 = home1.create("john", val1);
        Identity name2 = home2.findByPrimaryKey("john");
        assertTrue(name2.getNumber() == val1);
        name2.setNumber(val2);
        assertEquals("Wrong field value", val2, name1.getNumber());
        name2.remove();
        name1 = home1.create("john", val1);
        home2.remove("john");
    }

    /**
     * Test isolation case 1:
     * 2 parallell transactions writing the same instance.
     * Assumes that the bean has the shared attribute.
     * This test should pass if the transaction isolation is set
     * to "serializable" for the database.
     * This test has been invalidated, because it is too much
     * dependant on the database and fails most of the time.
     */
    public void _testIso1() throws Exception {
        int val1 = random(500);
        int val2 = val1+1;
        Identity name11 = home1.findByPrimaryKey("name11");
        utx.begin();
        name11.setNumber(val1);
        // start a thread that should be put waiting for transaction finish
        A_clthread th2 = new A_clthread(home2, WRITE, "name11", val2);
        th2.start();
        // wait to let the other thread finish
        th2.join();
        // check value has not been modified (transaction isolation)
        assertEquals("Value has been modified (no transaction isolation)",
                     val1, name11.getNumber());
        try {
            utx.commit();
        } catch (RollbackException e) {
            // the second transaction passed first!
            assertEquals("Both transactions are not serialized",
                         val2, name11.getNumber());
            return;
        }
        if (threadError != null) {
            // 1st transaction passed, second rolled back
            throw new RuntimeException("error in 2nd thread", threadError);
        }
        fail("both transactions should not pass");
    }


    public static Test suite() {
        return new TestSuite(F_Cluster.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_Cluster(testtorun));
        }
    }
}
