content: /* The contents of this file are subject to the terms
   * of the Common Development and Distribution License
   * (the License). You may not use this file except in
   * compliance with the License.
   *
   * You can obtain a copy of the License at
   * https://faban.dev.java.net/public/CDDLv1.0.html or
   * install_dir/license.txt
   * See the License for the specific language governing
   * permission and limitations under the License.
   *
   * When distributing Covered Code, include this CDDL
   * Header Notice in each file and include the License file
   * at faban/src/legal/CDDLv1.0.txt.
   * If applicable, add the following below the CDDL Header,
   * with the fields enclosed by brackets [] replaced by
   * your own identifying information:
   * "Portions Copyrighted [year] [name of copyright owner]"
   *
   * $Id$
   *
   * Copyright 2005-2010 Sun Microsystems Inc. All Rights Reserved
   */
  package com.sun.faban.driver.engine;
  
  import com.sun.faban.common.ParamReader;
  import com.sun.faban.common.Tools;
  import com.sun.faban.driver.ConfigurationException;
  import com.sun.faban.driver.RunControl;
  import org.w3c.dom.CDATASection;
  import org.w3c.dom.Document;
  import org.w3c.dom.Element;
  
  import javax.xml.xpath.XPath;
  import javax.xml.xpath.XPathConstants;
  import java.io.*;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.net.URLClassLoader;
  import java.util.StringTokenizer;
  import java.util.logging.Handler;
  
  import static com.sun.faban.common.Tools.JAVAC;
  /**
   * RunInfo
   * This class contains the run parameters used for the run.
   * They directly reflect the run configuration.
   *
   * sdo 10/17/08: Add support for https into driver template
   */
  public class RunInfo implements Serializable {
  
  	private static final long serialVersionUID = 1L;
  	
  	/** Namespace URI for Faban in general. */
  	public static final String FABANURI = "http://faban.sunsource.net/ns/faban";
  
      /** Namespace URI for the Faban driver. */
      public static final String DRIVERURI =
                                  "http://faban.sunsource.net/ns/fabandriver";
      /** The results directory. */
      public String resultsDir;
      
      /** Scale of the run. */
      public int scale = 1;
      
      /** Whether audit is on or not. This is not used. */
      public boolean audit = false;
      
      /** The run ID. */
      public String runId;
      
      /** Ramp Up, in seconds. */
      public int rampUp;
      
      /** Ramp Down, in seconds. */
      public int rampDown;
      
      /** Steady State, in seconds. */
      public int stdyState;
  
      /** Use varable load or not. */
      public boolean variableLoad = false;
  
      /** Variable load input file. */
      public String variableLoadFile;
  
      /** The load adjuster for variable load. */
      public VariableLoadHandler variableLoadHandler;
  
      /** Whether all threads start simultaneously. */
      public boolean simultaneousStart = false;
      
      /** Whether agents start in parallel. */
      public boolean parallelAgentThreadStart = false;
      
      /**
       * Milliseconds between thread start.
       */
      public int msBetweenThreadStart = 200;
    
      /** The benchmark start time, relative to the timer. */
      public int benchStartTime = Integer.MAX_VALUE;
  
      /** The actual millisec start time. */
      public long start = Long.MAX_VALUE;	// benchStartTime in ms actual time
  
      /** The maximum run time, used only for cycle runs. */
      public int maxRunTime = 6;  // 6 hrs
  
      /** The time interval for graphing. */
      public int graphInterval = 10; // 30 seconds
  
      /** Whether the runtime stats are enabled. */
      public boolean runtimeStatsEnabled = false;
  
      /** Interval for runtime stats. */
      public int runtimeStatsInterval = 10;
  
      /** The current driver config object. */
      public DriverConfig driverConfig;
  
      /** The current agent information. */
      public AgentInfo agentInfo;
  
      /** fhb bytes defining a class. */
      public byte[] defBytes;
              
      transient DriverConfig[] driverConfigs;
  
      private static transient ConfigurationReader reader;
      private static transient RunInfo instance;
  
      /** The log handler. */
      public transient Handler logHandler;
  
      private RunInfo() {
      	super();
      }
  
      /**
       * Obtains the defining class name from the configuration file.
       * @return The name of the defining class
       * @throws Exception An error occurred reading the configuration
       */
      public static String getDefiningClassName() throws Exception {
          reader = new ConfigurationReader();
          return reader.getDefiningClassName();
      }
  
      /**
       * Reads all the configuration parameters from the file and combines
       * it with the benchmark definition.
       * @param benchDef The benchmark definition
       * @return The resulting RunInfo structure
       * @throws Exception If there is an error reading the configuration,
       *         or it mesmatches the definition
       */
      public static RunInfo read(BenchmarkDefinition benchDef) throws Exception {
          RunInfo runInfo = reader.getRunInfo(benchDef);
          reader = null;
          instance = runInfo;
          return runInfo;
      }
  
      /**
       * Obtains the singleton RunInfo instance for this JVM.
       * @return The singleton instance or null if it has not been initialized
       */
      public static RunInfo getInstance() {
          return instance;
      }
  
      /**
       * The method postDeserialize re-establishes the non-serializable fields.
       * @throws ClassNotFoundException
       */
      public void postDeserialize() throws ClassNotFoundException{
          if (instance == null) {
  			instance = this;
  		}
  
          if (driverConfig.driverClass == null) {
              try{
                  //first try the default class loader
                  ClassLoader cl = this.getClass().getClassLoader();
                  driverConfig.driverClass=cl.loadClass(driverConfig.className);
                  
              }catch(ClassNotFoundException cnfe){
                  //do nothing, check defBytes
              }
          }
          //If we couldn't load the class, driverClass is still null
          //Try again, this time check defBytes
          if(driverConfig.driverClass == null){
              
              String tempDir = System.getProperty("faban.tmpdir");
              
              if(tempDir==null){
                  tempDir = System.getProperty("java.io.tmpdir");
              }
              
              File classFileDir = new File(tempDir);
              
              String classFileName = new StringBuilder(tempDir)
              .append(driverConfig.className).append(".class").toString();
              
              FileOutputStream fos=null;
              
              try {
                  
                  File classFile = new File(classFileName);
                  fos = new FileOutputStream(classFile);
                  fos.write(this.defBytes);
                  fos.flush();
                  fos.close();
                  
                  URL url[]= new URL[1];
                  
                  url[0] = classFileDir.toURI().toURL();
                  
                  URLClassLoader loader = new URLClassLoader(url,
                          this.getClass().getClassLoader());
                  
                  driverConfig.driverClass = loader.loadClass(driverConfig.className);
                  
              }catch (MalformedURLException ex) {
                  throw new ClassNotFoundException(ex.getMessage());
              }catch(IOException ioex){
                  throw new ClassNotFoundException(ioex.getMessage());
              }finally{
                  try{
                      fos.close();
                  }catch(Exception ex){
                      //if fos cannot be closed, leave it alone.
                  }
              }
          }
          BenchmarkDefinition.refillMethod(driverConfig.driverClass,
                                           driverConfig.preRun);
          BenchmarkDefinition.refillMethod(driverConfig.driverClass,
                                           driverConfig.postRun);
  
          BenchmarkDefinition.refillOperations(
                  driverConfig.driverClass, driverConfig.mix[0].operations);
          if (driverConfig.mix[1] != null) {
  			BenchmarkDefinition.refillOperations(
  			        driverConfig.driverClass, driverConfig.mix[1].operations);
  		}
  
      }
  
  
      /**
       * Information passed to individual agents. This changes from
       * one to another agent.
       */
      public static class AgentInfo implements Serializable {
          
  		private static final long serialVersionUID = 1L;
  		/** Agent number. */
  		public int agentNumber;
          /** Starting thread number. */
          public int startThreadNumber;
          /** Number of threads. */
          public int threads;
          /** Scale of the agent. */
          public double agentScale;
          /** Type of the agent. */
          public transient String agentType;
      }
  
      /**
       * The {@link DriverConfig} for a specific driver.
       */
      public static class DriverConfig extends BenchmarkDefinition.Driver {
          
  		private static final long serialVersionUID = 1L;
  		
  		/**
  		 * The reference to runControl.
  		 */
  		public RunControl runControl;
  		
  		/** Number of agents. */
          public int numAgents = -1; // Defaults to the actual number of agents.
  
          /** Number of threads. */
          public int numThreads = -1; // Overrides the threadPerScale
  
          /** Graph bucket interval. Overrides the bucket interval of the run. */
          int graphInterval;
  
          /** Target for runtime stats, currently unused. */
          public String runtimeStatsTarget;
  
          /** Root element of the configuration DOM tree. */
          public Element rootElement;
  
          /** Driver level variable load file. */
          public String variableLoadFile;
  
          /** Property element of the configuration DOM tree. */
          public Element properties;
  
          DriverConfig(BenchmarkDefinition.Driver driverDef) {
              name = driverDef.name;
              metric = driverDef.metric;
              opsUnit = driverDef.opsUnit;
              threadPerScale = driverDef.threadPerScale;
              responseTimeUnit = driverDef.responseTimeUnit;
              if (driverDef.preRun != null) {
  				preRun = (BenchmarkDefinition.DriverMethod)
                          driverDef.preRun.clone();
  			}
              if (driverDef.postRun != null) {
  				postRun = (BenchmarkDefinition.DriverMethod)
                          driverDef.postRun.clone();
  			}
              if (driverDef.mix[1] != null) {
  				mix[1] = (Mix) driverDef.mix[1].clone();
  			}
              mix[0] = (Mix) driverDef.mix[0].clone();
  
              if (driverDef.initialDelay[1] != null) {
  				initialDelay[1] = (Cycle) driverDef.initialDelay[1].clone();
  			}
              initialDelay[0] = (Cycle) driverDef.initialDelay[0].clone();
  
              // Copy operation references into a flat array.
              int totalOps = driverDef.operations.length;
              operations = new BenchmarkDefinition.Operation[totalOps];
              for (int j = 0; j < mix[0].operations.length; j++) {
  				operations[j] = mix[0].operations[j];
  			}
              if (mix[1] != null) {
  				for (int j = 0; j < mix[1].operations.length; j++) {
  					operations[j + mix[0].operations.length] =
                              mix[1].operations[j];
  				}
  			}
  
              className = driverDef.className;
              driverClass = driverDef.driverClass;
  
              percentiles = driverDef.percentiles;
              pctString = driverDef.pctString;
              pctSuffix = driverDef.pctSuffix;
              maxPercentile = driverDef.maxPercentile;
          }
      }
  
      /**
       * Obtains the run configuration by reading the configuration file.
       *
       * @author Akara Sucharitakul
       */
      static class ConfigurationReader {
  
          String configFileName;
          String definingClassName = null;
          Element rootElement;
          Object runConfigNode;
          XPath xp;
          
  
          ConfigurationReader() throws Exception {
              configFileName = System.getProperty("benchmark.config");
              if (configFileName == null) {
  				throw new IOException("Property \"benchmark.config\" not set.");
  			}
  
              ParamReader reader = new ParamReader(configFileName, true);
              Document doc = reader.getDocument();
              xp = reader.getXPath();
              rootElement = doc.getDocumentElement();
              String rootNS = rootElement.getNamespaceURI();
              String rootName = rootElement.getLocalName();
  
              if (FABANURI.equals(rootNS) && "runConfig".equals(rootName)) {
  				runConfigNode = rootElement;
  			} else {
  				runConfigNode = xp.evaluate("fa:runConfig", rootElement,
                          XPathConstants.NODE);
  			}
              if (runConfigNode == null) {
  				throw new ConfigurationException(
                          "Cannot find <fa:runConfig> element.");
  			}
              definingClassName = xp.evaluate("@definition", runConfigNode);
              
              //if the defining class is null, the benchmark definition needs 
              //to be created based on the information in the driverConfig node.
              if(definingClassName==null || "".equals(definingClassName.trim())){
                  definingClassName = createDefinition(runConfigNode);
              }
          }
          
          private String getRequestLagTime(Object node) throws Exception {
              Element rltNode = (Element) xp.evaluate("fd:requestLagTime/*[1]",
                      node, XPathConstants.NODE);
              
              if(rltNode == null){
                  return "";
              }
              String requestLagType = rltNode.getLocalName();
              String namespace = rltNode.getNamespaceURI();
              if (!DRIVERURI.equals(namespace)) {
  				throw new ConfigurationException("Namespace " + namespace +
                              " unexpected under element fd:requestLagTime");
  			}
  
              StringBuilder sb = new StringBuilder();
              
              if ("FixedTime".equalsIgnoreCase(requestLagType)) {
                  sb.append("@FixedTime(");
                  sb.append("cycleType=CycleType.").append(
                          xp.evaluate("fd:cycleType",
                          rltNode).toUpperCase()).append(",");
                  sb.append("cycleTime=").append(xp.evaluate("fd:cycleTime",
                          rltNode)).append(",");
                  sb.append("cycleDeviation=").append(
                          xp.evaluate("fd:cycleDeviation",rltNode));
                  sb.append(")");
                  
              } else if ("Uniform".equalsIgnoreCase(requestLagType)) {
                  sb.append("@Uniform(");
                  sb.append("cycleType=CycleType.").append(
                          xp.evaluate("fd:cycleType",
                          rltNode).toUpperCase()).append(",");
                  sb.append("cycleMin=").append(xp.evaluate("fd:cycleMin",
                          rltNode)).append(",");
                  sb.append("cycleMax=").append(xp.evaluate("fd:cycleMax",
                          rltNode)).append(",");
                  sb.append("cycleDeviation=").append(
                          xp.evaluate("fd:cycleDeviation", rltNode));
                  sb.append(")");
                  
              } else if ("NegativeExponential".equalsIgnoreCase(requestLagType)) {
                  sb.append("@NegativeExponential(");
                  sb.append("cycleType=CycleType.").append(
                          xp.evaluate("fd:cycleType",
                          rltNode).toUpperCase()).append(",");
                  sb.append("cycleMean=").append(
                          xp.evaluate("fd:cycleMean",rltNode)).append(",");
                  sb.append("cycleMax=").append(
                          xp.evaluate("fd:cycleMax",rltNode)).append(",");
                  sb.append("cycleDeviation=").append(
                          xp.evaluate("fd:cycleDeviation",rltNode));
                  sb.append(")");
                  
              }
              //At this point we have the requestLagTime annotation.
              return sb.toString(); 
             
          }
  
          private static enum TransportProvider {
  
              // We can add more providers here.
              SUN ("com.sun.faban.driver.transport.sunhttp.SunHttpTransport"),
  
              APACHE3 ("com.sun.faban.driver.transport.hc3.ApacheHC3Transport");
  
  
              final String providerClass;
  
              TransportProvider(String providerClass) {
                  this.providerClass = providerClass;
              }
  
              static TransportProvider getProvider() {
                  String providerStr = System.getProperty(
                          "fhb.http.provider", "apache3").toUpperCase();
                  for (TransportProvider provider : TransportProvider.values()) {
                      if (provider.name().equals(providerStr))
                          return provider;
                  }
                  // Default is APACHE3
                  return APACHE3;
              }
          }
  
          // Used to create the operation-specific information for various
          // types of requests
          private static abstract class RunInfoDefinition {
              boolean doSubst;
              boolean isBinary;
              String url;
              String data;
              String kbps = "-1";
              String headers = "";
  
              abstract String getURL(int opNum);
  
              String doTiming(int opNum, TransportProvider provider) {
                  if (provider == TransportProvider.SUN &&
                          url.startsWith("https"))
                      return "ctx.recordTime();";
                  return "";
              }
  
              String getKbps(int opNum) {
                  if (kbps == null)
                      return "";
                  if (Integer.parseInt(kbps) < 0)
                      return "";
                  return "httpTransport.setDownloadSpeed(" + kbps + ");\n";
              }
  
              String getHeaders(int opNum) {
                 return headers;
              }
              
              abstract String getPostRequest(int opNum) throws Exception;
              
              abstract String getStatics(int opNum) throws Exception;
  
              public void init(boolean doSubst, boolean isBinary,
                               String url, String data, String kbps, String accept) {
                  this.doSubst = doSubst;
                  this.isBinary = isBinary;
                  this.url = url;
                  this.data = data;
   				if (kbps != null) {
   					this.kbps = kbps;
                  }
                  if (accept != null && accept.length() > 0) {
                      StringBuilder sb = new StringBuilder();
                      sb.append("headers.put(\"Accept-Encoding\", \"");
                      sb.append(accept);
                      sb.append("\");");
                      this.headers = sb.toString();
                  }
              }
  
              @SuppressWarnings("cast")
  			protected String makeBinaryStaticBlock(byte[] b, int opNum) {
                  StringBuilder sb = new StringBuilder();
                  sb.append("private static byte[] post_data_");
                  sb.append(opNum);
                  sb.append(" = { ");
                  for (int bi = 0; bi < b.length; bi++) {
                      sb.append((int) b[bi]);
                      if (bi != b.length - 1) {
  						sb.append(", ");
  					}
                  }
                  sb.append(" }; ");
                  return sb.toString();
              }
  
              protected String makeCharStaticBlock(char[] c, int opNum) {
                  StringBuilder sb = new StringBuilder();
                  sb.append("private static char[] post_data_");
                  sb.append(opNum);
                  sb.append(" = { ");
                  for (int ci = 0; ci < c.length; ci++) {
                      sb.append((int) c[ci]);
                      if (ci != c.length - 1) {
  						sb.append(", ");
  					}
                  }
                  sb.append(" };\n");
                  sb.append("private static String post_string_");
                  sb.append(opNum);
                  sb.append(" = doEncode(new String(post_data_");
                  sb.append(opNum);
                  sb.append("));\n");
                  return sb.toString();
              }
  
              protected String generateRandomData(String inputString) {
                  StringBuilder output = new StringBuilder();
                  String[] strArr = inputString.split("@@");
                  for (int i = 0; i < strArr.length; i++) {
                      if (strArr[i].startsWith("faban.getRandom")) {
                          String tmp[] = strArr[i].split(",");
                          String low = tmp[0].split("\\(")[1];
                          String high = tmp[1].split("\\)")[0];
  
                          if (strArr[i].startsWith("faban.getRandomString")) {
                              strArr[i] = new StringBuilder("random.makeAString("
                              ).append(low).append(",").append(high).append(
                                      ")").toString();
                          } else if(strArr[i].startsWith("faban.getRandomInt")) {
                              strArr[i] = new StringBuilder("random.random(").append(
                                      low).append(",").append(high).append(")"
                                      ).toString();
                          }
                          output.append(".append(").append(strArr[i]).append(")");
                      } else {
                          output.append(".append(\"").
                                  append(strArr[i]).append("\")");
                      }
                  }
                  return output.toString();
              }
          }
  
          private static class RunInfoPostFileDefinition
                  extends RunInfoDefinition {
  
  			String getStatics(int opNum) throws Exception {
                  if (isBinary) {
                      FileInputStream fis = new FileInputStream(data);
                      byte[] b = new byte[fis.available()];
                      if (fis.read(b) != b.length) {
  						throw new IOException("Short read of data file " + data);
  					}
                      fis.close();
                      return makeBinaryStaticBlock(b, opNum);
                  } else if (!doSubst) {
                      FileReader fr = new FileReader(data);
                      char[] c = new char[(int) new File(data).length()];
                      if (fr.read(c) != c.length) {
  						throw new IOException("Short read of data file " + data);
  					}
                      fr.close();
                      return makeCharStaticBlock(c, opNum);
                  } else {
  					return "";
  				}
              }
  
  			String getURL(int opNum) {
                  return new StringBuilder("\"").append(url).
                          append("\"").toString();
              }
  
  			String getPostRequest(int opNum) throws Exception {
                  if (isBinary) {
                      return new StringBuilder(", post_data_").append(opNum).
                                                                      toString();
                  }
                  if (!doSubst) {
                      return new StringBuilder(", post_string_").append(opNum).
                                                                      toString();
                  }
                  BufferedReader br = new BufferedReader(new FileReader(data));
                  String s = br.readLine();
                  br.close();
                  String requestString = generateRandomData(s);
                  StringBuilder sb = new StringBuilder(
                                                  ", new StringBuilder(\"\")");
                  sb.append(requestString);
                  sb.append(".toString()");
                  return sb.toString();
              }
          }
  
          private static class RunInfoPostDataDefinition
                  extends RunInfoPostFileDefinition {
              @Override
  			String getStatics(int opNum) {
                  if (isBinary) {
  					return makeBinaryStaticBlock(data.getBytes(), opNum);
  				} else if (!doSubst) {
  					return makeCharStaticBlock(data.toCharArray(), opNum);
  				} else {
  					return "";
  				}
              }
  
              @Override
  			String getPostRequest(int opNum) throws Exception {
                  if (isBinary || !doSubst) {
  					return super.getPostRequest(opNum);
  				}
                  String requestString = generateRandomData(data);
                  StringBuilder sb = new StringBuilder(
                                                  ", new StringBuilder(\"\")");
                  sb.append(requestString);
                  sb.append(".toString()");
                  return sb.toString();
              }
          }
  
          private static class RunInfoGetDefinition extends RunInfoDefinition {
  
  			String getStatics(int opNum) {
                  if (doSubst) {
  					return "";
  				}
                  StringBuilder sb = new StringBuilder(
                          "private static String get_string_");
                  sb.append(opNum);
                  sb.append(" = \"");
                  sb.append(url);
                  sb.append(data);
                  sb.append("\"");
                  sb.append(";");
                  return sb.toString();
              }
  
  			String getURL(int opNum) {
                  if (doSubst) {
                      String requestString = generateRandomData(data);
                      return new StringBuilder(
                              "new StringBuilder(\"").append(url).append("\")").
                              append(requestString).append(".toString()").toString();
                  }  
                  return new StringBuilder("get_string_").append(opNum).toString();
              }
  
  			String getPostRequest(int opNum) {
                  return "";
              }
          }
  
          private String createDefinition(Object runConfigNode) throws Exception {
              Element benchDefNode = (Element) xp.evaluate(
                                      "fd:benchmarkDefinition", runConfigNode,
                                      XPathConstants.NODE);
  
              if (benchDefNode == null) {
  				return null;
  			}
  
              String definingClassName= xp.evaluate("fd:driverConfig/@name",
                      runConfigNode);
  
              //Get the cycleTime annotation
              Element driverConfigNode = (Element)xp.evaluate("fd:driverConfig",
                      runConfigNode, XPathConstants.NODE);
              String requestLagTime = getRequestLagTime(driverConfigNode);
  
              /**Load the template from file
               * Template file is small, but is there a better way to read this?
               **/
              String line = null;
              BufferedReader br = null; 
              InputStream is = null;
  
              try{
                  ClassLoader cl = this.getClass().getClassLoader();
                  is = cl.getResourceAsStream("driver_template");
                  br = new BufferedReader(new InputStreamReader(is));
              }catch(Exception ex){
                  //what to do?
                  ex.printStackTrace();
                  throw new ConfigurationException(ex);
              }
  
              StringBuilder sb = new StringBuilder();
  
              while((line=br.readLine())!=null){
                  if(!line.equals("\n")){
                      sb.append(line).append("\n");
                  }
              }
  
              String template = sb.toString();
  
              is.close();
  
              // Obtain the provider.
              TransportProvider provider = TransportProvider.getProvider();
  
              /**Get the operation token out of the template.
               * Use this to create multiple operations/functions that will 
               * replace the token in the java file
               **/
              String opTemplate = template.substring(
                      (template.indexOf("#operation")+10),
                      template.indexOf("operation#"));
              StringBuilder operations = new StringBuilder();
  
              Element operationNode = null;
              int i=1;
  
              while( (operationNode=(Element)xp.evaluate(
                      ("fd:driverConfig/fd:operation["+ i +"]"),
                      runConfigNode, XPathConstants.NODE)) != null){
  
                  /*
                  * There are many different options here. First, the operation
                  * is either POST or GET. In either case, the subst element
                  * indicates whether or not random strings are present in the
                  * payload data (for backward compatibility, it is assumed
                  * that random data is present).
                  *
                  * For POST, we can read the data from a file, and we can
                  * encode the data as form-encoded or binary (octet-stream)
                  */
                  boolean isPost = false;
                  boolean isBinary = false;
                  boolean isFile = false;
                  boolean doSubst = true;
  
                  String requestLagTimeOverride = getRequestLagTime(operationNode);
                  String operationName = xp.evaluate("fd:name", operationNode);
                  String url = xp.evaluate("fd:url", operationNode);
                  String max90th = xp.evaluate("fd:max90th", operationNode);
  				String kbps = xp.evaluate("fd:kbps", operationNode);
                  String accept = xp.evaluate("fd:accept", operationNode);
  
                  String requestString="";
  
                  Element requestNode = (Element)xp.evaluate("fd:get",
                          operationNode, XPathConstants.NODE);
  
                  if (requestNode == null) {
                      //Can't have both post & get either, but if both are there,
                      // will assume you meant GET.
  
                      requestNode = (Element) xp.evaluate("fd:post",
                              operationNode, XPathConstants.NODE);
  
                      if (requestNode != null) {
                          isPost = true;
                          if ("true".equalsIgnoreCase(
                                  requestNode.getAttributeNS(null, "file"))) {
  							isFile = true;
  						}
                          if ("true".equalsIgnoreCase(
                                  requestNode.getAttributeNS(null, "binary"))) {
  							isBinary = true;
  						}
                      } else {
                          throw new ConfigurationException("<operation> " +
                                              "must have a either a get/post");
                      }
                      //Can't have both post & get either, but if there,
                      // will assume you meant GET.
                  }
                  if ("false".equalsIgnoreCase(
                                      requestNode.getAttributeNS(null, "subst"))) {
  					doSubst = false;
  				}
                  if (isBinary && doSubst) {
  					throw new ConfigurationException("<operation> " +
                              "cannot be both binary and perform substitution");
  				}
                  requestString = requestNode.getNodeValue();
                  if (requestString == null) {
                      CDATASection cDataNode =
                              (CDATASection)requestNode.getFirstChild();
                      if(cDataNode!=null){
                          requestString = cDataNode.getNodeValue();
                      }
                  }
                  if (requestString == null) {
  					requestString = "";
  				}
  
                  if(requestLagTimeOverride==null) {
  					requestLagTimeOverride="";
  				}
                  if(operationName==null) {
  					throw new ConfigurationException(
                              "<operation> must have a <name> ");
  				}
                  if(url==null) {
  					throw new ConfigurationException(
                              "<operation> must have a <url>");
  				}
  
  
                  RunInfoDefinition rid;
                  if (isPost) {
                      if (isFile) {
                          rid = new RunInfoPostFileDefinition();
                      } else {
  						rid = new RunInfoPostDataDefinition();
  					}
                  } else {
  					rid = new RunInfoGetDefinition();
  				}
                  rid.init(doSubst, isBinary, url, requestString, kbps, accept);
  
                  //Create the benchmark Operation annotation
                  StringBuilder bmop = new StringBuilder(
                          "@BenchmarkOperation(name = \"").append(operationName);
                  bmop.append("\", percentileLimits={ ").
                       append(max90th).append(", ").
                       append(max90th).append(", ").
                       append(max90th).append("}, ");
  				if (provider == TransportProvider.SUN &&
                          url.startsWith("https"))
  					bmop.append("timing = com.sun.faban.driver.Timing.MANUAL");
  				else
                      bmop.append("timing = com.sun.faban.driver.Timing.AUTO");
                  bmop.append(")");
  
                  String opTemplateClone =  new String(opTemplate);
                  //replace tokens with actual content
                  opTemplateClone = opTemplateClone.replaceAll(
                          "@RequestLagTime@", requestLagTimeOverride);
                  opTemplateClone = opTemplateClone.replaceAll(
                          "@Operations@", bmop.toString());
                  opTemplateClone = opTemplateClone.replaceAll(
                          "@operationName@", operationName);
                  opTemplateClone = opTemplateClone.replaceAll(
                          "@url@", rid.getURL(i));
                  opTemplateClone = opTemplateClone.replaceAll(
                          "@postRequest@", rid.getPostRequest(i));
                  opTemplateClone = opTemplateClone.replaceAll(
                          "@Statics@", rid.getStatics(i));
  				opTemplateClone = opTemplateClone.replaceAll(
  						"@doKbps@", rid.getKbps(i));
  				opTemplateClone = opTemplateClone.replaceAll(
  						"@doHeaders@", rid.getHeaders(i));
  				opTemplateClone = opTemplateClone.replaceAll(
   						"@doTiming@", rid.doTiming(i, provider));
                  
                  operations.append(opTemplateClone);
                  
                  i++;
              }
              
              String benchmarkDef = getBenchmarkDefinition(benchDefNode);
              
              //replace tokens in template
              template = template.replaceFirst("#operation(.*\\n*)*operation#",
                      operations.toString());
              template = template.replaceFirst("@RequestLagTime@",
                      requestLagTime);
              template = template.replaceFirst("@BenchmarkDefinition@",
                      benchmarkDef);
              template = template.replaceAll("@DriverClassName@",
                      definingClassName);
              template = template.replaceFirst("@ProviderClass@",
                                               provider.providerClass);
              template = template.replaceFirst("@BenchmarkDriverName@",
                      definingClassName);
              
              String tmpDir = System.getProperty("faban.tmpdir");
              
              if(tmpDir==null){
                  tmpDir = System.getProperty("java.io.tmpdir");
              }
              
              if(!tmpDir.endsWith(File.separator)){
                  tmpDir = tmpDir+File.separator;
              }
                  
              String className = new StringBuilder(tmpDir).
                      append(definingClassName).append(".java").toString();
              
              try{
                  //convert class name to filename?
                  PrintWriter pw = new PrintWriter(new BufferedWriter(
                          new FileWriter(className)));
                  pw.print(template);
                  pw.flush();
              }catch(Exception ex){
                  ex.printStackTrace();
                  throw new ConfigurationException(ex);
              }
  
              String classpath = System.getProperty("java.class.path");
  
              String arg[] = new String[] { "-classpath", classpath, className };
              int errorCode = -1;
  
              errorCode = compile(arg);
  
              if(errorCode != 0){
                  throw new ConfigurationException(
                          "unable to compile generated driver file. " +
                          "check output for errors"); 
              }
  
              
              return definingClassName;
              
          }
  
          private String getBenchmarkDefinition(Object benchDefNode)
                  throws Exception{
              //to do error checking -- name is required?
              String defName      = xp.evaluate("fd:name", benchDefNode);
              String version      = xp.evaluate("fd:version", benchDefNode);
              String metric       = xp.evaluate("fd:metric", benchDefNode);
              String scaleName    = xp.evaluate("fd:scaleName", benchDefNode);
              String scaleUnit    = xp.evaluate("fd:scaleUnit", benchDefNode);
              
              
              StringBuilder sb = new StringBuilder("@BenchmarkDefinition (");
              sb.append("name = \"").append(defName).append("\",");
              sb.append("version = \"").append(version).append("\",");
              sb.append("metric = \"").append(metric).append("\",");
              sb.append("scaleName = \"").append(scaleName).append("\",");
              sb.append("scaleUnit = \"").append(scaleUnit).append("\",");
              sb.append("configPrecedence = true)");
              
              return sb.toString();
          }
  
          String getDefiningClassName() throws Exception {
              return definingClassName;
          }
  
          RunInfo getRunInfo(BenchmarkDefinition benchDef)
                  throws Exception {
  
              RunInfo runInfo = new RunInfo();
              String v = xp.evaluate("fa:scale", runConfigNode);
              if (v != null && v.length() > 0) {
  				try {
                      runInfo.scale = Integer.parseInt(v);
                  } catch (NumberFormatException e) {
                      throw new ConfigurationException(
                              "<scale> must be an integer.");
                  }
  			}
  
              v = xp.evaluate("fa:runControl/fa:rampUp", runConfigNode);
              if (v == null || v.length() == 0) {
  				throw new ConfigurationException(
                          "Element <rampUp> not found.");
  			}
              try {
                  runInfo.rampUp = Integer.parseInt(v);
              } catch (NumberFormatException e) {
                  throw new ConfigurationException(
                          "<rampUp> must be an integer.");
              }
  
              v = xp.evaluate("fa:runControl/fa:steadyState", runConfigNode);
              if (v == null || v.length() == 0) {
  				throw new ConfigurationException(
                          "Element <steadyState> not found.");
  			}
              try {
                  runInfo.stdyState = Integer.parseInt(v);
              } catch (NumberFormatException e) {
                  throw new ConfigurationException(
                          "<steadyState> must be an integer.");
              }
  
              v = xp.evaluate("fa:runControl/fa:rampDown", runConfigNode);
              if (v == null || v.length() == 0) {
  				throw new ConfigurationException(
                          "Element <rampDown> not found.");
  			}
              try {
                  runInfo.rampDown = Integer.parseInt(v);
              } catch (NumberFormatException e) {
                  throw new ConfigurationException(
                          "<rampDown> must be an integer.");
              }
  
              v = xp.evaluate("fa:runControl/fa:variableLoad", runConfigNode);
              if (v != null && v.length() > 0) {
                try {
                  runInfo.variableLoad = relaxedParseBoolean(v);
                } catch (NumberFormatException e) {
                  throw new ConfigurationException(
                      "<variableLoad> must be true or false.");
                }
              }
  
              if (runInfo.variableLoad) {
                  runInfo.variableLoadFile = xp.evaluate(
                          "fa:runControl/fa:variableLoadFile", runConfigNode);
              }
  
              runInfo.resultsDir = xp.evaluate("fd:outputDir", runConfigNode);
              if (runInfo.resultsDir == null || runInfo.resultsDir.length() == 0) {
  				throw new ConfigurationException(
                          "Element <outputDir> not found.");
  			}
  
              v = xp.evaluate("fd:audit", runConfigNode);
              if (v != null && v.length() > 0) {
  				try {
                      runInfo.audit = relaxedParseBoolean(v);
                  } catch (NumberFormatException e) {
                      throw new ConfigurationException(
                              "<audit> must be true or false.");
                  }
  			}
  
              v = xp.evaluate("fd:threadStart/fd:delay", runConfigNode);
              if (v != null && v.length() > 0) {
  				try {
                      runInfo.msBetweenThreadStart = Integer.parseInt(v);
                  } catch (NumberFormatException e) {
                      throw new ConfigurationException(
                              "<delay> must be an integer.");
                  }
  			}
  
              v = xp.evaluate("fd:threadStart/fd:simultaneous", runConfigNode);
              if (v != null && v.length() > 0) {
  				try {
                      runInfo.simultaneousStart = relaxedParseBoolean(v);
                  } catch (NumberFormatException e) {
                      throw new ConfigurationException(
                              "<simultaneous> must be true or false.");
                  }
  			}
  
              v = xp.evaluate("fd:threadStart/fd:parallel", runConfigNode);
              if (v != null && v.length() > 0) {
  				try {
                      runInfo.parallelAgentThreadStart = relaxedParseBoolean(v);
                  } catch (NumberFormatException e) {
                      throw new ConfigurationException(
                              "<parallel> must be true or false.");
                  }
  			}
  
              v = xp.evaluate("fd:stats/fd:maxRunTime", runConfigNode);
              if (v!= null && v.length() > 0) {
  				try {
                      runInfo.maxRunTime = Integer.parseInt(v);
                  } catch (NumberFormatException e) {
                      throw new ConfigurationException(
                              "<maxRunTime> must be an integer.");
                  }
  			}
  
              v = xp.evaluate("fd:stats/fd:interval", runConfigNode);
              if (v!= null && v.length() > 0) {
  				try {
                      runInfo.graphInterval = Integer.parseInt(v);
                  } catch (NumberFormatException e) {
                      throw new ConfigurationException(
                              "<interval> must be an integer.");
                  }
  			}
  
              v = xp.evaluate("fd:runtimeStats/@enabled", runConfigNode);
              if (v != null && v.length() > 0) {
  				try {
                      runInfo.runtimeStatsEnabled = relaxedParseBoolean(v);
                  } catch (Exception e) {
                      throw new ConfigurationException(
                              "<runtimeStats enabled=[true|false]>");
                  }
  			}
  
              v = xp.evaluate("fd:runtimeStats/fd:interval", runConfigNode);
              if (v != null && v.length() > 0) {
  				try {
                      runInfo.runtimeStatsInterval = Integer.parseInt(v);
                  } catch (NumberFormatException e) {
                      throw new ConfigurationException(
                              "<interval> must be an integer.");
                  }
  			}
  
              runInfo.driverConfigs = new DriverConfig[benchDef.drivers.length];
              for (int i = 0; i < benchDef.drivers.length; i++) {
                  DriverConfig driverConfig =
                          new DriverConfig(benchDef.drivers[i]);
                  Element driverConfigNode = (Element) xp.evaluate(
                          "fd:driverConfig[@name=\"" + driverConfig.name + "\"][1]",
                          runConfigNode, XPathConstants.NODE);
  
                  driverConfig.runControl = benchDef.runControl;
                  if (driverConfigNode == null) {
  					throw new ConfigurationException("Element " +
                              "<driverConfig name=\"" + driverConfig.name +
                              "\"> not found.");
  				}
  
                  v = xp.evaluate("fd:agents", driverConfigNode);
  
                  // Note that the agents field has two valid formats:
                  // 1. A single integer
                  // 2. One or more host:count fields
                  // The harness is interested in the host/count. What we need
                  // is a simple count. Just add'em up.
                  if (v != null && v.length() > 0) {
                      StringTokenizer t = new StringTokenizer(v, " ,");
                      driverConfig.numAgents = 0;
                      while (t.hasMoreTokens()) {
                          v = t.nextToken().trim();
                          if (v.length() == 0) {
  							continue;
  						}
                          int idx = v.indexOf(':');
                          if (++idx > 0) {
  							v = v.substring(idx);
  						}
                          if (v.length() == 0) {
  							continue;
  						}
                          try {
                              driverConfig.numAgents += Integer.parseInt(v);
                          } catch (NumberFormatException e) {
  
                              throw new ConfigurationException("<agents> " +
                                      "must be an integer or in the format " +
                                      "host:agents where agents is an integer. " +
                                      "Found: " + v);
                          }
                      }
                  }
  
                  v = xp.evaluate("fd:threads", driverConfigNode);
                  if (v != null && v.length() > 0) {
  					try {
                          driverConfig.numThreads = Integer.parseInt(v);
                      } catch (NumberFormatException e) {
                          throw new ConfigurationException(
                                  "<threads> must be an integer.");
                      }
  				}
  
                  v = xp.evaluate("fd:stats/fd:interval", driverConfigNode);
                  if (v!= null && v.length() > 0) {
  					try {
                          driverConfig.graphInterval = Integer.parseInt(v);
                      } catch (NumberFormatException e) {
                          throw new ConfigurationException(
                                  "<interval> must be an integer.");
                      }
  				} else {
  					driverConfig.graphInterval = runInfo.graphInterval;
  				}
  
                  if (runInfo.runtimeStatsEnabled) {
                      driverConfig.runtimeStatsTarget = xp.evaluate(
                              "fd:runtimeStats/@target", driverConfigNode);
                      if (driverConfig.runtimeStatsTarget != null) {
                          driverConfig.runtimeStatsTarget =
                                  driverConfig.runtimeStatsTarget.trim();
                          if (driverConfig.runtimeStatsTarget.length() == 0)
                              driverConfig.runtimeStatsTarget = null;
                      }
                  }
  
                  if (runInfo.variableLoad) {
                      driverConfig.variableLoadFile = xp.evaluate(
                              "fd:variableLoadFile", driverConfigNode);
                      if (driverConfig.variableLoadFile == null ||
                          driverConfig.variableLoadFile.length() == 0) {
                          driverConfig.variableLoadFile = runInfo.variableLoadFile;
                      }
                      if (driverConfig.variableLoadFile == null ||
                           driverConfig.variableLoadFile.length() == 0) {
                           throw new ConfigurationException(
                                  "Element <variableLoadFile> not found.");
                      }
                  }
  
  
                  driverConfig.rootElement = rootElement;
                  driverConfig.properties = (Element) xp.evaluate("fd:properties",
                          driverConfigNode, XPathConstants.NODE);
                  driverConfig.mix[0].configure(driverConfigNode);
                  driverConfig.mix[0].configureCycles(driverConfigNode);
                  driverConfig.mix[0].normalize();
                  runInfo.driverConfigs[i] = driverConfig;
                  
                  //need to store the class bytes for a remote agent
                  //if the driver is an http driver
                  
                  InputStream is = null;
                  String defClassName = getDefiningClassName();
                  try{
                      //check whether this class is defined in this class loader
  
                      ClassLoader cl = this.getClass().getClassLoader();
                      cl.loadClass(defClassName);
                      
                  }catch(ClassNotFoundException cnfe){
                  
                      String tempDir = System.getProperty("faban.tmpdir");
                      
                      if(tempDir==null){
                          tempDir = System.getProperty("java.io.tmpdir");
                      }
                      
                      File classFile = new File(tempDir);
                      
                      URL url[]= new URL[1];
                      
                      try {
                          url[0] = classFile.toURI().toURL();
                      } catch (MalformedURLException ex) {
                          throw new ConfigurationException(ex);
                      }
                      
                      URLClassLoader loader = new URLClassLoader(url, BenchmarkDefinition.class.getClassLoader());
                      
                      try{
                          
                          loader.loadClass(defClassName);
                          is = loader.getResourceAsStream(defClassName+".class");
                          
                          runInfo.defBytes = new byte[is.available()];
                          is.read(runInfo.defBytes);
                          is.close();
                          System.out.println("Bytes Read from class :"+runInfo.defBytes.length);
                          
                      }catch(ClassNotFoundException cnfex){
                          throw new ConfigurationException(cnfex);
                      }catch(IOException ioex){
                          throw new ConfigurationException(ioex);
                      }
                  }
              }
              return runInfo;
          }
  
          static boolean relaxedParseBoolean(String str) {
              String newStr = str.toLowerCase();
              boolean retVal;
              if ("true".equals(newStr) || "t".equals(newStr)
                  || "yes".equals(newStr) || "y".equals(newStr)
                  || "1".equals(newStr)) {
  				retVal = true;
  			} else if ("false".equals(newStr) || "f".equals(newStr)
                  || "no".equals(newStr) || "n".equals(newStr)
                  || "0".equals(newStr)) {
  				retVal = false;
  			} else {
  				throw new NumberFormatException("Boolean " + str +
                          " not recognized!");
  			}
              return retVal;
          }
  
          private static int compile ( String[] arguments )
              throws IOException {
              String javac = Tools.getToolsInstance().executable(JAVAC);
              Process proc = Runtime.getRuntime().exec (
                      command(javac, arguments),
                      null,
                      cwd() );
              return proc.exitValue();
          }
  
          private static File cwd() {
              return new File("");
          }
  
          private static String[] command(String javac, String[] args) {
              return new String[] { javac, args[0], args[1], args[2]};
          }
      }
  }
  