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
   * http://www.sun.com/cddl/cddl.html or
   * install_dir/legal/LICENSE
   * 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 install_dir/legal/LICENSE.
   * 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-2009 Sun Microsystems Inc. All Rights Reserved
   */
  package com.sun.faban.driver.engine;
  
  import com.sun.faban.driver.ConfigurationException;
  import com.sun.faban.driver.CycleType;
  import com.sun.faban.driver.DefinitionException;
  import com.sun.faban.driver.util.Random;
  
  import org.w3c.dom.Element;
  import org.w3c.dom.NodeList;
  
  import java.io.Serializable;
  import java.lang.annotation.Annotation;
  import java.util.logging.Logger;
  
  /**
   * Super class of all distributions.
   */
  public abstract class Cycle implements Serializable, Cloneable {
  
      CycleType cycleType = CycleType.CYCLETIME;
      double cycleDeviation;
  
      protected transient Logger logger;
  
      public Cycle() {
          logger = Logger.getLogger(this.getClass().getName());
      }
  
      protected Logger getLogger() {
      	return logger;
      }
     
      /**
       * Makes a deep copy of this cycle object.
       * @return A deep copy of this cycle object.
       * @see java.lang.Object#clone()
       */
      @Override
  	public Object clone() {
          Object clone = null;
          try {
              clone = super.clone();
          } catch (CloneNotSupportedException e) {
              // we implement cloneable and just swallow this exception.
              // It should never happen.
          }
          return clone;
      }
      
      static void setCycles(BenchmarkDefinition.Operation[] operations,
                            Class<?> driverClass) throws DefinitionException {
          Cycle classCycle = null;
          Annotation[] annotations = driverClass.getAnnotations();
          for (int i = 0; i < annotations.length; i++) {
              String typeName = annotations[i].annotationType().getName();
              int cnBegin = typeName.lastIndexOf('.');
              ++cnBegin;
              String annotationName = typeName.substring(cnBegin);
              String pkgName = typeName.substring(0, cnBegin);
              String cycleName = pkgName + "engine." + annotationName;
              Cycle cycle = null;
              try {
                  cycle = Class.forName(cycleName).
                          asSubclass(Cycle.class).newInstance();
              } catch (Exception e) {
                  // If the annotation is not a cycle, we just ignore it here.
                  // It is the responsibility of the corresponding facility to
                  // pick it up.
                  continue;
              }
              if (classCycle != null) {
  				throw new DefinitionException("Duplicate class cycle " +
                          "annotation @" + annotationName);
  			}
              cycle.init(annotations[i]);
              classCycle = cycle;
          }
  
          // Now we make the same set of tests at the operation level
          for (BenchmarkDefinition.Operation o : operations) {
  
              annotations = o.m.getAnnotations();
              for (int i = 0; i < annotations.length; i++) {
                  String typeName = annotations[i].annotationType().getName();
                  int cnBegin = typeName.lastIndexOf('.');
                  ++cnBegin;
                  String annotationName = typeName.substring(cnBegin);
                  String pkgName = typeName.substring(0, cnBegin);
                  String cycleName = pkgName + "engine." + annotationName;
                  Cycle cycle = null;
                  try {
                      cycle = Class.forName(cycleName).
                              asSubclass(Cycle.class).newInstance();
                  } catch (Exception e) {
                      // If the annotation is not a cycle, we just ignore it here.
                      // It is the responsibility of the corresponding facility to
                      // pick it up.
                      continue;
                  }
                  if (o.cycle != null) {
  					throw new DefinitionException("Duplicate operation cycle " +
                              "annotation for operation " +
                              o.name + " @" + annotationName);
  				}
                  cycle.init(annotations[i]);
                  o.cycle = cycle;
              }
  
              // Finally, we need to test for no cycle at all and handle the case
              if (o.cycle == null) {
  				o.cycle = classCycle;
  			}
  
              if (o.cycle == null) {
  				throw new DefinitionException("No cycle distribution " +
                          "annotation for operation " + o.name);
  			}
          }
      }
  
       /**
       * Initializes this cycle according to the annotation.
       * @param a The annotation
       * @throws DefinitionException If there is an error in the annotation
       */
      public abstract void init(Annotation a) throws DefinitionException;
  
      /**
       * Randoms/calculates the delay time for a thread based on its
       * supplied random number generator and the actual conditions in the
       * distribution.
       *
       * @param random        The random number generator used
       * @return The delay time
       */
      public abstract long getDelay(Random random);
  
      /**
       * Provides the maximum value to be represented inside a histogram.
       * @return The max reasonable delay to be presented in the output histogram.
       */
      public abstract double getHistogramMax();
  
      public final void configure(Element e) throws ConfigurationException {
          NodeList nl = e.getElementsByTagNameNS(
                          RunInfo.DRIVERURI, "cycleType");
          if (nl.getLength() > 1) {
              String msg = "Bad cycleType definition; must have only one per cycle Time";
              getLogger().severe(msg);
              ConfigurationException ce = new ConfigurationException(msg);
              getLogger().throwing(getClass().getName(), "configure", ce);
              throw ce;
          }
          if (nl.getLength() == 1) {
              String s = nl.item(0).getFirstChild().getNodeValue();
              if ("CYCLETIME".equals(s)) {
                  cycleType = CycleType.CYCLETIME;
              } else if ("THINKTIME".equals(s)) {
                  cycleType = CycleType.THINKTIME;
              }
              else getLogger().warning("Ignoring unknown cycletype " + s);
          }
          nl = e.getElementsByTagNameNS(
  		RunInfo.DRIVERURI, "cycleDeviation");
          if (nl.getLength() > 1) {
              String msg = "Bad cycleDeviation definition; must have only one per cycle Time";
              getLogger().severe(msg);
              ConfigurationException ce = new ConfigurationException(msg);
              getLogger().throwing(getClass().getName(), "configure", ce);
              throw ce;
          }
          if (nl.getLength() == 1) {
              cycleDeviation = Double.parseDouble(nl.item(0).getFirstChild().getNodeValue());
  	}
  	configureSubclass(e);
      }
  
      protected void configureSubclass(Element e) throws ConfigurationException {
          throw new UnsupportedOperationException(this.getClass().getName() + 
  	     " does not support overriding configuration information");
      }
  }
  