/* 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.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;
/**
* Superclass of all mixes.
* @author Akara Sucharitakul
*/
public abstract class Mix implements Serializable, Cloneable {
BenchmarkDefinition.Operation[] operations;
double deviation;
/** The actual class name of the implementing subclass. */
protected String className;
private transient Logger logger;
/**
* Factory for obtaining the correct mix from the driver class.
* This factory has the knowledge about each mix and how to
* instantiate it.
* @param driverClass The originating driver class
* @return An instance of the appropriate mix subclass
* @throws DefinitionException If there is an error in the definition
*/
public static Mix getMix(Class<?> driverClass) throws DefinitionException {
Mix returnMix = 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 mixName = pkgName + "engine." + annotationName;
Mix mix = null;
try {
mix = Class.forName(mixName).
asSubclass(Mix.class).newInstance();
} catch (Exception e) {
// If the annotation is not a mix, we just ignore it here.
// It is the responsibility of the corresponding facility to
// pick it up.
continue;
}
if (returnMix != null) {
throw new DefinitionException("Duplicate mix annotation @" +
annotationName);
}
mix.init(driverClass, annotations[i]);
returnMix = mix;
}
// Now handle the default where no mix is specified
// The default is a flat mix with all operations equal
if (returnMix == null) {
FlatMix mix = new FlatMix();
mix.init(driverClass);
returnMix = mix;
}
returnMix.normalize();
return returnMix;
}
/**
* Protected constructor ensures Mix cannot be constructed explicitly.
*/
protected Mix() {
className = getClass().getName();
}
/**
* Instead of accessing the logger directly, we'll have to
* access it through this method all the time as serialization
* may cause the logger to become null.
* @return The logger for this class
*/
protected Logger getLogger() {
if (logger == null) {
logger = Logger.getLogger(className);
}
return logger;
}
/**
* Creates and returns a copy of this object. The precise meaning
* of "copy" may depend on the class of the object. The general
* intent is that, for any object <tt>x</tt>, the expression:
* <blockquote>
* <pre>
* x.clone() != x</pre></blockquote>
* will be true, and that the expression:
* <blockquote>
* <pre>
* x.clone().getClass() == x.getClass()</pre></blockquote>
* will be <tt>true</tt>, but these are not absolute requirements.
* While it is typically the case that:
* <blockquote>
* <pre>
* x.clone().equals(x)</pre></blockquote>
* will be <tt>true</tt>, this is not an absolute requirement.
* <p/>
* By convention, the returned object should be obtained by calling
* <tt>super.clone</tt>. If a class and all of its superclasses (except
* <tt>Object</tt>) obey this convention, it will be the case that
* <tt>x.clone().getClass() == x.getClass()</tt>.
* <p/>
* By convention, the object returned by this method should be independent
* of this object (which is being cloned). To achieve this independence,
* it may be necessary to modify one or more fields of the object returned
* by <tt>super.clone</tt> before returning it. Typically, this means
* copying any mutable objects that comprise the internal "deep structure"
* of the object being cloned and replacing the references to these
* objects with references to the copies. If a class contains only
* primitive fields or references to immutable objects, then it is usually
* the case that no fields in the object returned by <tt>super.clone</tt>
* need to be modified.
* <p/>
* The method <tt>clone</tt> for class <tt>Object</tt> performs a
* specific cloning operation. First, if the class of this object does
* not implement the interface <tt>Cloneable</tt>, then a
* <tt>CloneNotSupportedException</tt> is thrown. Note that all arrays
* are considered to implement the interface <tt>Cloneable</tt>.
* Otherwise, this method creates a new instance of the class of this
* object and initializes all its fields with exactly the contents of
* the corresponding fields of this object, as if by assignment; the
* contents of the fields are not themselves cloned. Thus, this method
* performs a "shallow copy" of this object, not a "deep copy" operation.
* <p/>
* The class <tt>Object</tt> does not itself implement the interface
* <tt>Cloneable</tt>, so calling the <tt>clone</tt> method on an object
* whose class is <tt>Object</tt> will result in throwing an
* exception at run time.
*
* @return a clone of this instance.
* @see Cloneable
*/
@Override
public Object clone() {
Mix clone = null;
try {
clone = (Mix) super.clone();
for (int i = 0; i < operations.length; i++) {
clone.operations[i] = (BenchmarkDefinition.Operation)
operations[i].clone();
}
} catch (CloneNotSupportedException e) {
// Noop. This is not possible as we are implementing cloneable.
}
return clone;
}
/**
* Initializes this mix according to the annotation.
* @param driverClass The driver class annotating this mix
* @param a The annotation
* @throws DefinitionException If there is an error in the annotation
*/
public abstract void init(Class<?> driverClass, Annotation a)
throws DefinitionException;
/**
* Configures/overrides the mix from the driverConfig DOM node
* read from the configuration file.
*
* @param driverConfigNode The driverConfig DOM node
* @throws ConfigurationException If there is a configuration error
*/
public abstract void configure(Element driverConfigNode)
throws ConfigurationException;
/**
* Normalizes the mix so it is ready for use in selections.
* This normalization adjusts each mix to be between 0.0d and 1.0d
* and the sum of each selectable mix to be 1.0d
*/
public abstract void normalize();
/**
* Returns the flat mix representation of this mix. This is used for
* consistent reporting.
* @return The flat mix representation.
*/
public abstract FlatMix flatMix();
/**
* Obtains the per-thread and per-driver instance selector.
* @param random The per-thread random value generator
* @return The selector to be used by the driver
*/
public abstract Selector selector(Random random);
/**
* The per-thread selector is used in the AgentThread for selection of
* the operation.
*/
public abstract static class Selector {
/**
* The select method selects the operation to run next.
* @return The operation index selected to run next
*/
public abstract int select();
/**
* Resets the selector's state to start at the first op,
* if applicable.
*/
public abstract void reset();
}
/**
* Allows the cycle times for each operation in the mix to be overridden
* by the configuration file. It is assumed that the cycle times for the
* operations have already been initialized (from the annotations, when
* the init method was called).
*
* Ideally this could be part of the configure() method, but the
* implementation here is common to all Mix classes, while the configure
* method is specific to each Mix.
*
* The format is as follows:
* <pre>
* <cycleTime>
* <classname>fully-qualified class name</classname>
* <values>
* ... xml fields to set specific value for mix ...
* </values>
* <name>operation name</name>
* ... can have 0 or more names ...
* ... if no name is specified, apply to all operations ...
* </cycleTime>
* .. can have 0 or more cycle times ...
* </pre>
*/
public void configureCycles(Element driverConfigNode) throws ConfigurationException {
NodeList cycleTimes = driverConfigNode.getElementsByTagNameNS(
RunInfo.DRIVERURI,
"cycleTime");
int nCycles = cycleTimes.getLength();
for (int i = 0; i < nCycles; i++) {
Element cycleTime = (Element) cycleTimes.item(i);
NodeList nl = cycleTime.getElementsByTagNameNS(
RunInfo.DRIVERURI, "classname");
if (nl.getLength() != 1) {
String msg = "Badly configured cycle time; you must have one and only one classname";
getLogger().severe(msg);
ConfigurationException ce = new ConfigurationException(msg);
getLogger().throwing(className, "configure", ce);
throw ce;
}
Cycle cycle;
try {
String className = nl.item(0).getFirstChild().getNodeValue();
Class clazz = Class.forName(className);
cycle = (Cycle) clazz.newInstance();
cycle.configure(cycleTime);
} catch (Exception e) {
String msg = "Can't create cycle";
getLogger().severe(msg);
ConfigurationException ce = new ConfigurationException(msg, e);
getLogger().throwing(className, "configure", ce);
throw ce;
}
NodeList names = cycleTime.getElementsByTagNameNS(
RunInfo.DRIVERURI, "name");
int nNames = names.getLength();
if (nNames == 0) {
// Apply to all operations
for (int curOp = 0; curOp < operations.length; curOp++) {
operations[curOp].cycle = cycle;
}
}
else for (int curName = 0; curName < nNames; curName++) {
String name = names.item(curName).getFirstChild().getNodeValue();
boolean found = false;
for (int curOp = 0; curOp < operations.length; curOp++) {
if (operations[curOp].name.equals(name)) {
found = true;
operations[curOp].cycle = cycle;
break;
}
}
if (!found) {
getLogger().warning("No benchmark operation found for " + name);
}
}
}
}
}