/**
*
*/
package edu.brown.benchmark;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map.Entry;
import org.apache.log4j.Logger;
import org.voltdb.VoltProcedure;
import org.voltdb.catalog.Catalog;
import org.voltdb.compiler.VoltProjectBuilder;
import org.voltdb.utils.BuildDirectoryUtils;
import org.voltdb.utils.CatalogUtil;
import org.voltdb.utils.JarReader;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.utils.ClassUtil;
import edu.brown.utils.FileUtil;
import edu.brown.utils.ProjectType;
/**
* @author pavlo
*/
public abstract class AbstractProjectBuilder extends VoltProjectBuilder {
private static final Logger LOG = Logger.getLogger(AbstractProjectBuilder.class);
private static final LoggerBoolean debug = new LoggerBoolean();
private static final LoggerBoolean trace = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug, trace);
}
protected final Class<? extends AbstractProjectBuilder> base_class;
protected final Class<? extends VoltProcedure> procedures[];
protected final Class<?> supplementals[];
protected final String partitioning[][];
private URL ddlURL;
protected final File parameterMappings;
protected final TransactionFrequencies txn_frequencies = new TransactionFrequencies();
public static class TransactionFrequencies extends HashMap<Class<? extends VoltProcedure>, Integer> {
private static final long serialVersionUID = 1L;
public int getTotal() {
int total = 0;
for (Integer freq : this.values()) {
assert(freq >= 0);
total += freq;
} // FOR
return (total);
}
}
/**
* Constructor
* @param project_name
* @param base_class
* @param procedures
* @param partitioning
*/
public AbstractProjectBuilder(String project_name, Class<? extends AbstractProjectBuilder> base_class, Class<? extends VoltProcedure> procedures[], String partitioning[][]) {
this(project_name, base_class, procedures, partitioning, new Class<?>[0], true);
}
/**
* Full Constructor
* @param project_name
* @param base_class
* @param procedures
* @param partitioning
* @param supplementals
* @param fkeys
*/
public AbstractProjectBuilder(String project_name, Class<? extends AbstractProjectBuilder> base_class, Class<? extends VoltProcedure> procedures[], String partitioning[][], Class<?> supplementals[], boolean fkeys) {
super(project_name);
this.base_class = base_class;
this.procedures = procedures;
this.partitioning = partitioning;
this.supplementals = supplementals;
this.ddlURL = this.base_class.getResource(this.getDDLName(true));
this.parameterMappings = this.getParameterMappings();
}
public void addTransactionFrequency(Class<? extends VoltProcedure> procClass, int frequency) {
this.txn_frequencies.put(procClass, frequency);
}
public String getTransactionFrequencyString() {
StringBuilder sb = new StringBuilder();
String add = "";
for (Entry<Class<? extends VoltProcedure>, Integer> e : txn_frequencies.entrySet()) {
sb.append(add).append(e.getKey().getSimpleName()).append(":").append(e.getValue());
add = ",";
}
return (sb.toString());
}
/**
* Use the given DDL string as the schema for this project
* This will cause the ProjectBuilder to write DDL out to a temporary file
* @param ddl
*/
public File setDDLContents(String ddl) {
File f = FileUtil.writeStringToTempFile(ddl, "sql", true);
try {
this.ddlURL = f.toURI().toURL();
} catch (MalformedURLException ex) {
throw new RuntimeException(ex);
}
if (debug.val)
LOG.debug("Wrote DDL contents to '" + f.getAbsolutePath() + "'");
return (f);
}
public final URL getDDLURL(boolean fkeys) {
return (this.ddlURL);
}
public final String getDDLName(boolean fkeys) {
// return (this.project_name + "-ddl" + (fkeys ? "-fkeys" : "") + ".sql");
return (this.project_name + "-ddl" + ".sql");
}
public final File getParameterMappings() {
File file = null;
String name = this.project_name + ".mappings";
URL url = this.base_class.getResource(name);
if (debug.val)
LOG.debug(this.project_name.toUpperCase() + ": " + url);
if (url != null) {
file = new File(url.getPath());
}
return (file);
}
/**
* Get the base file name for this benchmark's project jar
* The file name will include a test suffix if it is being used in unit tests
* @param unitTest
* @return
*/
public final String getJarName(boolean unitTest) {
return (this.project_name + (unitTest ? "-test" : "") + ".jar");
}
public String getJarDirectory() {
return BuildDirectoryUtils.getBuildDirectoryPath();
}
/**
* Get the full jar path for this project
* @param unitTest
* @return
*/
public final File getJarPath(boolean unitTest) {
String testDir = this.getJarDirectory();
return (new File(testDir + File.separator + this.getJarName(unitTest)));
}
public final void addDefaultProcedures() {
addProcedures(this.procedures);
}
/**
* Add the default schema to this project.
*/
public final void addDefaultSchema() {
addSchema(this.ddlURL);
}
/**
* Add the default partitioning to this project
*/
public final void addDefaultPartitioning() {
if (this.partitioning != null && this.partitioning.length > 0) {
for (String i[] : this.partitioning) {
addTablePartitionInfo(i[0], i[1]);
} // FOR
}
}
@Override
public void addAllDefaults() {
addDefaultProcedures();
addDefaultSchema();
addDefaultPartitioning();
if (this.parameterMappings != null)
addParameterMappings(this.parameterMappings);
}
/**
* Get a pointer to a compiled catalog for the benchmark for all the procedures.
*/
public Catalog createCatalog() throws IOException {
return createCatalog(true, true);
}
/**
*
* @param fkeys
* @param full_catalog
* @return
* @throws IOException
*/
@SuppressWarnings("unchecked")
public Catalog createCatalog(boolean fkeys, boolean full_catalog) throws IOException {
// compile a catalog
if (full_catalog) {
this.addProcedures(this.procedures);
} else {
// The TPC-E catalog takes a long time load, so we have the ability
// to just compile the schema and the first procedure to make things load faster
this.addProcedures(this.procedures[0]);
}
addDefaultSchema();
addDefaultPartitioning();
if (this.parameterMappings != null)
addParameterMappings(this.parameterMappings);
String catalogJar = this.getJarPath(true).getAbsolutePath();
try {
boolean status = compile(catalogJar);
assert (status);
} catch (Exception ex) {
throw new RuntimeException("Failed to create " + project_name + " catalog [" + catalogJar + "]", ex);
}
Catalog catalog = new Catalog();
try {
// read in the catalog
String serializedCatalog = JarReader.readFileFromJarfile(catalogJar, CatalogUtil.CATALOG_FILENAME);
// create the catalog (that will be passed to the ClientInterface
catalog.execute(serializedCatalog);
} catch (Exception ex) {
throw new RuntimeException("Failed to load " + project_name + " catalog [" + catalogJar + "]", ex);
}
return catalog;
}
public Catalog getFullCatalog(boolean fkeys) throws IOException {
return (this.createCatalog(fkeys, true));
}
public Catalog getSchemaCatalog(boolean fkeys) throws IOException {
return (this.createCatalog(fkeys, false));
}
public static AbstractProjectBuilder getProjectBuilder(ProjectType type) {
String pb_className = String.format("%s.%sProjectBuilder", type.getPackageName(), type.getBenchmarkPrefix());
if (debug.val)
LOG.debug("Dynamically creating project builder for " + type + ": " + pb_className);
final AbstractProjectBuilder pb = (AbstractProjectBuilder)ClassUtil.newInstance(pb_className,
new Object[]{ }, new Class<?>[]{ });
assert(pb != null) : "Invalid ProjectType " + type;
return (pb);
}
}