package edu.hawaii.ics.yucheng;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Properties;
/**
* An immutable class that contains the configuration information for a cluster
* of nodes and a catalog. They are parsed from the 'clustercfg' file.
*
* @author Cheng Jade
* @assignment ICS 421 Assignment 2-2
* @date Feb 29, 2010
* @bugs None
*/
public final class Configuration {
// The catalog node configuration
private final ConfigurationNode catalog;
// The table name
private final String tableName;
// The partition method
private final String partitionMethod;
// The partition column
private final String partionColumn;
// The partition number
private final int partionNumber;
// The array of nodes configurations.
private final ArrayList<ParameterPair> partitionList = new ArrayList<ParameterPair>();
// The array of nodes configurations.
private final ArrayList<ConfigurationNode> nodeList = new ArrayList<ConfigurationNode>();
// method to add a node in a configuration list.
public boolean addNode(ConfigurationNode node){
assert null != node;
for (int i = 0; i < nodeList.size(); i++)
if (nodeList.get(i).equals(node))
return false;
nodeList.add(node);
return true;
}
/**
* Initializes a new instance of the Configuration class.
*
* @param path
* The path to a file that contains configuration data.
*
* @throws NullPointerException
* @throws ProgramException
* Thrown if it cann't read the DDL command file, or the
* specified path is null.
*/
public Configuration(final String path) throws ProgramException {
// Throw an exception if the specified path is null.
if (null == path)
throw new NullPointerException("path");
// Declare a Properties object and load it with the specified file.
final Properties properties = getProperties(path);
// Obtain the catalog configuration.
this.catalog = new ConfigurationNode(properties, "catalog");
// Obtain the table name.
this.tableName = properties.getProperty("tablename");
// Obtain the partition method.
this.partitionMethod = properties.getProperty("partition.method");
// Obtain the partition column.
this.partionColumn = properties.getProperty("partition.column");
String partionNumberText = null;
if (this.partitionMethod.equalsIgnoreCase("hash"))
partionNumberText= properties.getProperty("partition.param1");
else
if (this.partitionMethod.equalsIgnoreCase("range"))
partionNumberText= properties.getProperty("numnodes");
else
throw new ProgramException("invalid partition method.");
if (null == partionNumberText)
throw new ProgramException(
"can't find partition number field in the configuration file.");
try {
partionNumber = Integer.parseInt(partionNumberText);
} catch (final NumberFormatException e) {
throw new ProgramException("Cannot parse number of partitions.", e);
}
if (this.partitionMethod.equalsIgnoreCase("hash")) {
for (int i = 0; i < this.partionNumber; i++)
partitionList.add(new ParameterPair(properties.getProperty("partition.param1")));
return;
}
for (int i = 0; i < this.partionNumber; i++)
partitionList.add(new ParameterPair(properties, i + 1));
}
/**
* Returns the catalog configuration.
*
* @return The catalog configuration.
*/
public ConfigurationNode getCatalog() {
return this.catalog;
}
// added when doing A2
public ConfigurationNode getNode(int index) {
return this.nodeList.get(index);
}
// added when doing A2
public ParameterPair getPartition(int index) {
return this.partitionList.get(index);
}
// added when doing A2
public String getTableName() {
return this.tableName;
}
// added when doing A2
public String getPartitionMethod() {
return this.partitionMethod;
}
// added when doing A2
public String getPartitionColumn() {
return this.partionColumn;
}
// added when doing A2
public int nodeListsize() {
return this.nodeList.size();
}
// added when doing A2
public int partitionListsize() {
return this.partitionList.size();
}
/**
* Returns a readable version of the contents of the configuration.
*
* @return A readable version of the contents of the configuration.
*/
@Override
public String toString() {
// If the directory is empty, return a special note.
if (this.partitionList.size() == 0)
return "The partition list is empty.\n";
// Loop over each item, and add it to the string builder.
final StringBuilder builder = new StringBuilder();
builder.append("The configuration file contains:\n\n");
builder.append("Catalog: \n");
builder.append(this.catalog + "\n\n");
builder.append("Table Name: ");
builder.append(this.tableName + "\n");
builder.append("Partition Method: ");
builder.append(this.partitionMethod + "\n");
builder.append("Partition Column: ");
builder.append(this.partionColumn + "\n");
builder.append("Partition Number: ");
builder.append(this.partionNumber);
for (int i = 0; i < this.partionNumber; i++) {
builder.append("\n\nPartition ");
builder.append(i + 1);
builder.append(": \n");
builder.append(partitionList.get(i));
}
if (nodeList.size() == 0)
builder.append("\n\nConfiguration node list is empty.");
else
for (int i = 0; i < this.nodeList.size(); i++) {
builder.append("\n\nNode ");
builder.append(i + 1);
builder.append(": \n");
builder.append(nodeList.get(i));
}
// Return the string created above.
return builder.toString();
}
/**
* Open a file, check for errors, and return a loaded properties object.
*
* @param path
* The path to the properties file.
*
* @return A loaded properties object.
*
* @throws ProgramException
* Thrown if there is a problem reading the configuration file.
*/
private static Properties getProperties(final String path)
throws ProgramException {
assert null != path;
try {
final FileInputStream file = new FileInputStream(path);
try {
final Properties properties = new Properties();
properties.load(file);
return properties;
} finally {
file.close();
}
} catch (final IOException e) {
throw new ProgramException("Cannot read configuration file.", e);
}
}
/**
* The test main, the entry point when testing this class.
*
* @param args
* The command line arguments.
*/
public static void main(final String[] args) {
assert null != args;
// Print usage information, if wrong number of arguments were used.
if (args.length != 1) {
final String name = Configuration.class.getSimpleName();
System.err.println("Usage: java " + name + " <path>");
System.err.println(" <path> path to a configuration file");
System.exit(1);
return;
}
// Declare a Configuration object and populate it with data from a file
// that is specified as the only command line argument.
try {
assert null != args[0];
System.out.println(new Configuration(args[0]));
} catch (final ProgramException e) {
System.err.println(e.getMessage());
System.exit(1);
return;
}
// Exit cleanly while debugging from Eclipse.
System.exit(0);
}
}