/*
 * JBoss, Home of Professional Open Source
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.cache.loader;

import junit.textui.TestRunner;
import org.jboss.cache.Fqn;
import org.jboss.cache.TreeCache;
import org.jboss.cache.misc.TestingUtil;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Random;

/**
 * Tests JDBCCacheLoader
 *
 * @author <a href="mailto:hmesha@novell.com">Hany Mesha</a>
 */
public class JDBCCacheLoaderPerfTest extends AbstractCacheLoaderTestBase
{
    private TreeCache cache;
    

    private Random random = new Random();

    private static final int NUM_TIMES_PER_THREAD = 10;
    private static final int NUM_THREADS = 10;
    private static final String threadNamePrefix = "TesterThread-";
    
    private long[] putStat = new long[NUM_THREADS*NUM_TIMES_PER_THREAD];
    private long[] getStat = new long[NUM_THREADS*NUM_TIMES_PER_THREAD];
    private long[] removeStat = new long[NUM_THREADS*NUM_TIMES_PER_THREAD];

    protected void setUp() throws Exception
    {
        if (cache != null) tearDown();
        cache = new TreeCache();
        cache.setCacheMode("local");
        cache.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
        configureCache();
        cache.startService();
    }

    protected void tearDown() throws Exception
    {
        if (cache != null)
        {
            cache.remove("/");
            cache.stopService();
            cache = null;
            
            putStat = null;
            getStat = null;
            removeStat = null;

            // go a gc() to clear up unused threads from each test
            System.gc();
            TestingUtil.sleepThread(1000);
        }
    }

    protected void configureCache() throws Exception
    {
       Properties prop = new Properties();
       try{
          prop.load(this.getClass().getClassLoader().getResourceAsStream("cache-jdbc.properties"));
       }catch(Exception e){
          System.out.println("Error loading jdbc properties ");
       }
       String props = "cache.jdbc.driver =" + prop.getProperty("cache.jdbc.driver") + "\n" +
                      "cache.jdbc.url=" + prop.getProperty("cache.jdbc.url") +"\n" +
                      "cache.jdbc.user=" + prop.getProperty("cache.jdbc.user") + "\n" +
                      "cache.jdbc.password=" + prop.getProperty("cache.jdbc.password") + "\n" +
                      "cache.jdbc.node.type=" + prop.getProperty("cache.jdbc.node.type");
       cache.setCacheLoaderConfiguration( getSingleCacheLoaderConfig("", "org.jboss.cache.loader.JDBCCacheLoader", props, false, true, false) );
    }
    
    public void testPut() throws Exception
    {
        Thread threads[] = new Thread[NUM_THREADS];

        for (int i=0; i<NUM_THREADS; i++)
        {
            long putStatSeg[] = new long [NUM_TIMES_PER_THREAD]; 
            threads[i] = new Thread()
            {
              public void run()
              {
                try
                {
                    Fqn base = new Fqn(new Object[]{"Threads", getName()});
                    long start = System.currentTimeMillis();
                    for (int j=0; j<NUM_TIMES_PER_THREAD; j++)
                    {
                        cache.put(base, "key"+j, "value"+j);
                        long timeTaken = System.currentTimeMillis() - start;
                        putStat[j] = timeTaken;
                    }
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
              }
            };
        }

        // start threads
        startThreads(threads);

        // wait for all the threads to join
        waitForThreads(threads);

        printResults("JDBC PUT", putStat);
    }
    
    public void testGet() throws Exception
    {
        Thread threads[] = new Thread[NUM_THREADS];

        for (int i = 0; i < NUM_THREADS; i++)
        {
            threads[i] = new Thread()
            {
                public void run()
                {
                    try
                    {
                        Fqn fqn = Fqn.fromString("/Threads/" + getName());
                        long start = System.currentTimeMillis();
                        for (int j = 0; j < NUM_TIMES_PER_THREAD; j++)
                        {
                            cache.get(fqn, "key"+j);
                            long timeTaken = System.currentTimeMillis() - start;
                            getStat[j]=timeTaken;                            
                        }
                        
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                }
            };
        }

        // start threads
        startThreads(threads);

        // wait for all the threads to join
        waitForThreads(threads);

        // printResults
        printResults("JDBC GET", getStat);
    }

    public void testRemove() throws Exception
    {
        Thread threads[] = new Thread[NUM_THREADS];

        for (int i = 0; i < NUM_THREADS; i++)
        {
            threads[i] = new Thread()
            {
                public void run()
                {
                    try
                    {
                        Fqn fqn = Fqn.fromString("/Threads/" + getName());
                        long start = System.currentTimeMillis();
                        for (int j = 0; j < NUM_TIMES_PER_THREAD; j++)
                        {
                            cache.remove(fqn, "key"+j);
                            cache.evict(fqn);
                            long timeTaken = System.currentTimeMillis() - start;
                            removeStat[j] = timeTaken;
                        }
                        
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                }
            };
        }

        // start threads
        startThreads(threads);

        // wait for all the threads to join
        waitForThreads(threads);

        printResults("JDBC REMOVE", removeStat);
    }

    private void startThreads(Thread[] threads)
    {
        for (int i = 0; i < NUM_THREADS; i++)
        {
            // add a random delay here.
            randomSleep();
            threads[i].setName(threadNamePrefix + i);
            threads[i].start();
        }
    }

    private void waitForThreads(Thread[] threads)
    {
        for (int i = 0; i < NUM_THREADS; i++)
        {
            try
            {
                threads[i].join();
            }
            catch (InterruptedException e)
            {
            }
        }
    }

    private void randomSleep()
    {
        TestingUtil.sleepThread(random.nextInt(1000));
    }

    private void printResults(String name)
    {
        
    }
    
    private void printResults(String name, long[] stats)
    {
       Map results = getStats(stats);
       
       System.out.println("------------------------------------");
       System.out.println("  (all times in ms)");
       System.out.println();
       System.out.println("  Test name:         " + name);
       System.out.println("  NumThreads:        " + NUM_THREADS);
       System.out.println("  NumLoopsPerThread: " + NUM_TIMES_PER_THREAD);
       System.out.println("  Total time:        " + results.get("total"));
       System.out.println("  Avg time:          " + ((Long) results.get("total")).longValue() / stats.length);
       System.out.println("  shortest time:     " + ((Long)results.get("shortest")));
       System.out.println("  longest time:      " + ((Long) results.get("longest")));
       
       System.out.println("------------------------------------");
    }
    
    public Map getStats ( long[] stats ){
       long statTotal = 0;
       long largest = 0 ;
       long smallest = stats[0];
       for (int i = 0; i < stats.length; i++) {
           statTotal = statTotal + stats[i];
           if (stats[i]>largest) largest = stats[i];
           if (stats[i]<smallest) smallest = stats[i];
       }

       Map results = new HashMap();
       results.put( "total",new Long( statTotal) );
       results.put( "shortest", new Long(smallest));
       results.put( "longest", new Long (largest));

       return results;
   }

    public static void main(String[] args)
    {
        TestRunner.run(JDBCCacheLoaderPerfTest.class);
    }
}
