/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2008, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This 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 (at your option) any later version.
 *
 * This software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.test.perf.ejb;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.NumberFormat;
import javax.naming.InitialContext;

import org.jboss.test.perf.interfaces.PerfResult;
import org.jboss.test.perf.interfaces.Probe;
import org.jboss.test.perf.interfaces.ProbeHome;
import org.jboss.test.perf.interfaces.ProbeLocal;
import org.jboss.test.perf.interfaces.ProbeLocalHome;
import org.jboss.test.util.Debug;
import org.jboss.test.util.ejb.SessionSupport;

/** A session bean that tests intra-VM EJB call invocation overhead. The
runProbeTests method accepts the number of iterations and returns a simple
html report showing the output of each test run against the Probe session.
 
@author Scott.Stark@jboss.org
@version $Revision: 81036 $
*/
public class PerfTestSessionBean extends SessionSupport 
{
   private static NumberFormat fmt = NumberFormat.getInstance();
   static
   {
      fmt.setMinimumFractionDigits(3);
      fmt.setMaximumFractionDigits(3);
   }

   /** Run the unit tests using Probe and return a report as a string
    */
   public PerfResult runProbeTests(int iterationCount)
   {
      StringBuffer results = new StringBuffer("<h1>runProbeTests("+iterationCount+")</h1><pre>\n");
      int testCount = 0;
      int failureCount = 0;

      PerfResult result = new PerfResult();
      try
      {
         testCount ++;
         testProbeTimings(iterationCount, results);
      }
      catch(Exception e)
      {
         failureCount ++;
         formatException(e, "testTimings", results);
         result.error = e;
      }
      results.append('\n');

      results.append("\nTotal tests: "+testCount);
      results.append("\nTotal failures: "+failureCount);
      results.append("\n</pre>");
      result.report = results.toString();
      return result;
   }
   /** Run the unit tests using ProbeLocal and return a report as a string
    */
   public PerfResult runProbeLocalTests(int iterationCount)
   {
      StringBuffer results = new StringBuffer("<h1>runProbeLocalTests("+iterationCount+")</h1><pre>\n");
      int testCount = 0;
      int failureCount = 0;

      PerfResult result = new PerfResult();
      try
      {
         testCount ++;
         testProbeLocalTimings(iterationCount, results);
      }
      catch(Exception e)
      {
         failureCount ++;
         formatException(e, "testTimings", results);
         result.error = e;
      }
      results.append('\n');

      results.append("\nTotal tests: "+testCount);
      results.append("\nTotal failures: "+failureCount);
      results.append("\n</pre>");
      result.report = results.toString();
      return result;
   }

   private void testProbeTimings(int iterationCount, StringBuffer results) throws Exception
   {
      results.append("\n+++ testTimings()\n");
      Object obj = new InitialContext().lookup("java:comp/env/ejb/ProbeHome");
      Class homeClass = obj.getClass();
      ProbeHome home = null;
      results.append("ProbeHome Proxy class info:\n");
      Debug.displayClassInfo(homeClass, results);
      results.append("Local ProbeHome.class info:\n");
      Debug.displayClassInfo(ProbeHome.class, results);
      home = (ProbeHome) obj;

      results.append("\nFound ProbeHome");
      Probe bean = home.create();
      results.append("\nCreated Probe");
      warmup(bean, results);
      noop(bean, iterationCount, results);
      ping(bean, iterationCount, results);
      echo(bean, iterationCount, results);
   }
   private void testProbeLocalTimings(int iterationCount, StringBuffer results) throws Exception
   {
      results.append("\n+++ testTimings()\n");
      Object obj = new InitialContext().lookup("java:comp/env/ejb/ProbeLocalHome");
      Class homeClass = obj.getClass();         
      ProbeLocalHome home = null;
      results.append("ProbeLocalHome Proxy class info:\n");
      Debug.displayClassInfo(homeClass, results);
      results.append("Local ProbeLocalHome.class info:\n");
      Debug.displayClassInfo(ProbeLocalHome.class, results);

      home = (ProbeLocalHome) obj;
      results.append("\nFound ProbeLocalHome");
      ProbeLocal bean = home.create();
      results.append("\nCreated ProbeLocal");
      warmup(bean, results);
      noop(bean, iterationCount, results);
      ping(bean, iterationCount, results);
      echo(bean, iterationCount, results);
   }

   private void warmup(Probe bean, StringBuffer results) throws Exception
   {
      bean.noop();
      bean.ping("Ping");
      bean.echo("Echo");
   }
   private void warmup(ProbeLocal bean, StringBuffer results) throws Exception
   {
      bean.noop();
      bean.ping("Ping");
      bean.echo("Echo");
   }
   private void noop(Probe bean, int iterationCount, StringBuffer results) throws Exception
   {
      results.append("\nStarting "+iterationCount+" noop() invocations");
      long start = System.currentTimeMillis();
      for(int n = 0; n < iterationCount; n ++)
         bean.noop();
      long end = System.currentTimeMillis();
      long elapsed = end - start;
      float avgTime = elapsed;
      avgTime /= iterationCount;
      results.append("\n"+iterationCount+" noop() invocations = "+elapsed+" ms, "
         + fmt.format(avgTime)+" ms/noop");
   }
   private void noop(ProbeLocal bean, int iterationCount, StringBuffer results) throws Exception
   {
      results.append("\nStarting "+iterationCount+" noop() invocations");
      long start = System.currentTimeMillis();
      for(int n = 0; n < iterationCount; n ++)
         bean.noop();
      long end = System.currentTimeMillis();
      long elapsed = end - start;
      float avgTime = elapsed;
      avgTime /= iterationCount;
      results.append("\n"+iterationCount+" noop() invocations = "+elapsed+" ms, "
         + fmt.format(avgTime)+" ms/noop");
   }

   private void ping(Probe bean, int iterationCount, StringBuffer results) throws Exception
   {
      results.append("\nStarting "+iterationCount+" ping(PING) invocations");
      long start = System.currentTimeMillis();
      for(int n = 0; n < iterationCount; n ++)
         bean.ping("PING");
      long end = System.currentTimeMillis();
      long elapsed = end - start;      
      float avgTime = elapsed;
      avgTime /= iterationCount;
      results.append("\n"+iterationCount+" ping() invocations = "+elapsed+" ms, "
         + fmt.format(avgTime)+" ms/ping");
   }
   private void ping(ProbeLocal bean, int iterationCount, StringBuffer results) throws Exception
   {
      results.append("\nStarting "+iterationCount+" ping(PING) invocations");
      long start = System.currentTimeMillis();
      for(int n = 0; n < iterationCount; n ++)
         bean.ping("PING");
      long end = System.currentTimeMillis();
      long elapsed = end - start;      
      float avgTime = elapsed;
      avgTime /= iterationCount;
      results.append("\n"+iterationCount+" ping() invocations = "+elapsed+" ms, "
         + fmt.format(avgTime)+" ms/ping");
   }

   private void echo(Probe bean, int iterationCount, StringBuffer results) throws Exception
   {
      results.append("\nStarting "+iterationCount+" echo(ECHO) invocations");
      long start = System.currentTimeMillis();
      for(int n = 0; n < iterationCount; n ++)
      {
         String echo = bean.echo("ECHO");
      }
      long end = System.currentTimeMillis();
      long elapsed = end - start;
      float avgTime = elapsed;
      avgTime /= iterationCount;
      results.append("\n"+iterationCount+" echo() invocations = "+elapsed+" ms, "
         + fmt.format(avgTime)+" ms/echo");
   }
   private void echo(ProbeLocal bean, int iterationCount, StringBuffer results) throws Exception
   {
      results.append("\nStarting "+iterationCount+" echo(ECHO) invocations");
      long start = System.currentTimeMillis();
      for(int n = 0; n < iterationCount; n ++)
      {
         String echo = bean.echo("ECHO");
      }
      long end = System.currentTimeMillis();
      long elapsed = end - start;
      float avgTime = elapsed;
      avgTime /= iterationCount;
      results.append("\n"+iterationCount+" echo() invocations = "+elapsed+" ms, "
         + fmt.format(avgTime)+" ms/echo");
   }

   private void formatException(Throwable t, String testName, StringBuffer results)
   {
      StringWriter sw = new StringWriter();
      PrintWriter pw = new PrintWriter(sw);
      t.printStackTrace(pw);
      results.append("\n"+testName+" failed:\n");
      results.append(sw.toString());
   }

}
