package edu.umd.rhsmith.diads.meater.core.config;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.commons.configuration.tree.ConfigurationNode;
import edu.umd.rhsmith.diads.meater.core.app.MEaterConfigurationException;
import edu.umd.rhsmith.diads.meater.core.app.MEaterException;
import edu.umd.rhsmith.diads.meater.core.app.MEaterMain;
import edu.umd.rhsmith.diads.meater.core.app.ModuleAlreadyLoadedException;
import edu.umd.rhsmith.diads.meater.core.app.ModuleInstantiationException;
import edu.umd.rhsmith.diads.meater.core.config.components.media.MediaPathConfig;
import edu.umd.rhsmith.diads.meater.core.config.container.InstanceConfigContainer;
import edu.umd.rhsmith.diads.meater.core.config.setup.MEaterSetupConsole;
import edu.umd.rhsmith.diads.meater.core.config.setup.ops.module.AddModuleOperation;
import edu.umd.rhsmith.diads.meater.core.config.setup.ops.module.ListModulesOperation;
import edu.umd.rhsmith.diads.meater.core.config.setup.ops.module.RemoveModuleOperation;
import edu.umd.rhsmith.diads.meater.core.config.setup.ops.module.SelectModuleOperation;
import edu.umd.rhsmith.diads.meater.core.config.setup.ops.nav.NavToOperation;
import edu.umd.rhsmith.diads.meater.core.config.setup.ops.sql.EditSqlOperation;
import edu.umd.rhsmith.diads.meater.core.config.setup.ops.unit.SetupPropertiesOperation;
import edu.umd.rhsmith.diads.meater.util.Util;
public class MEaterConfig extends ConfigUnit {
/*
* --------------------------------
* entry point
* --------------------------------
*/
public static void main(String[] args) {
// interpret args
String configurationFilename;
if (args.length == 0) {
configurationFilename = MEaterSetupConsole.DEFAULT_MEATER_CONFIG_FILENAME;
} else {
configurationFilename = args[1];
}
// let's go
MEaterMain main = null;
// create an application instance
try {
main = mainFromConfigurationFile(configurationFilename);
} catch (Exception e) {
e.printStackTrace();
return;
}
// start the instance and then wait for it to stop
// FIXME handle error conditions in startup
try {
main.start();
main.awaitStopFinished();
} catch (Exception e) {
main.logSevere(Util.traceMessage(e));
System.exit(-1);
}
}
public static MEaterMain mainFromConfigurationFile(String filename)
throws ConfigurationException, MEaterException {
XMLConfiguration xml = new XMLConfiguration(filename);
MEaterConfig config = new MEaterConfig();
config.resetInternalConfiguration();
config.loadConfigurationFrom(xml);
return config.createMEaterMain(filename);
}
/*
* --------------------------------
* actual class
* --------------------------------
*/
public static final String UNAME_MEATER = "meater";
public static final String CKEY_MODULE_CLASSNAME = "moduleClass";
public static final String CKEY_MODULES = "modules";
private final MEaterGeneralConfig generalSettings;
private final Map<String, ConfigModule> modules;
private final InstanceConfigContainer<MediaPathConfig> pathContainer;
public MEaterConfig() {
super();
this.generalSettings = new MEaterGeneralConfig();
this.modules = new TreeMap<String, ConfigModule>();
// media paths
this.pathContainer = new InstanceConfigContainer<MediaPathConfig>(
UINAME_PATHS, UIDESC_PATHS);
this.pathContainer.registerConfigType(MediaPathConfig.class);
// nav to general
this.registerSetupConsoleOperation(new SetupPropertiesOperation(
OP_UINAME_GENERAL, OP_SHORTNAME_GENERAL, this.generalSettings));
// nav to media paths
this.registerSetupConsoleOperation(new NavToOperation(OP_UINAME_PATHS,
OP_SHORTNAME_PATHS, this.pathContainer));
// edit sql things
this.registerSetupConsoleOperation(new EditSqlOperation());
// module add, selection, etc
this.registerSetupConsoleOperation(new AddModuleOperation(this));
this.registerSetupConsoleOperation(new ListModulesOperation(this));
this.registerSetupConsoleOperation(new SelectModuleOperation(this));
this.registerSetupConsoleOperation(new RemoveModuleOperation(this));
}
public MEaterGeneralConfig getGeneralSettings() {
return this.generalSettings;
}
public InstanceConfigContainer<MediaPathConfig> getPathContainer() {
return pathContainer;
}
public MEaterMain createMEaterMain(String name) throws MEaterException {
MEaterMain main = new MEaterMain(MEaterMain.genName(name), this
.getGeneralSettings());
for (ConfigModule module : this.getModules()) {
module.addTo(main);
}
for (MediaPathConfig cc : this.pathContainer.getInstanceConfigs()) {
cc.createComponentInstance(main.getComponentManager());
}
return main;
}
/*
* --------------------------------
* UI
* --------------------------------
*/
@Override
public String getUiName() {
return UINAME;
}
@Override
public String getUiDescription() {
return UIDESC;
}
/*
* --------------------------------
* Modules
* --------------------------------
*/
public ConfigModule addModule(String className)
throws ModuleInstantiationException, ModuleAlreadyLoadedException {
ConfigModule module = ConfigModule.forName(className);
// can't add if we already have one by the same name
if (this.modules.containsKey(module.getModuleName())) {
throw new ModuleAlreadyLoadedException(module.getModuleName());
}
this.modules.put(module.getModuleName(), module);
module.resetInternalConfiguration();
return module;
}
public void removeModule(ConfigModule module) {
this.modules.remove(module.getModuleName());
}
public ConfigModule getModuleByName(String name) {
return this.modules.get(name);
}
public Set<ConfigModule> getModules() {
return new HashSet<ConfigModule>(this.modules.values());
}
public int getNumModules() {
return this.modules.size();
}
private void loadModuleClassesFrom(HierarchicalConfiguration config)
throws MEaterConfigurationException {
// grab all the module class names and load corresponding modules
String[] classes = config.getStringArray(CKEY_MODULE_CLASSNAME);
for (String className : classes) {
try {
this.addModule(className);
} catch (IllegalStateException e) {
// this doesn't mean anything bad, we just already had the
// module loaded
} catch (ModuleInstantiationException e) {
// this is actually bad, we got a bogus class name
throw new MEaterConfigurationException(
"Couldn't instantiate module");
}
}
}
private void saveModuleClassesTo(HierarchicalConfiguration config) {
// grab all the modules and add their class names to the config
for (ConfigModule m : this.modules.values()) {
config.addProperty(CKEY_MODULE_CLASSNAME, m.getClass().getName());
}
}
private void loadModulesFrom(HierarchicalConfiguration config)
throws MEaterConfigurationException {
// go to modules section of config
HierarchicalConfiguration modulesConfig;
try {
modulesConfig = config.configurationAt(CKEY_MODULES);
} catch (IllegalArgumentException e) {
// though not in the apache javadoc, this exception is thrown if no
// such configuration exists.
// if it doesn't exist -- no modules to load.
return;
}
// load each module we find
for (Entry<String, ConfigModule> entry : this.modules.entrySet()) {
List<HierarchicalConfiguration> mcs = modulesConfig
.configurationsAt(entry.getKey());
for (HierarchicalConfiguration mc : mcs) {
entry.getValue().loadConfigurationFrom(mc);
}
}
}
private void saveModulesTo(HierarchicalConfiguration config)
throws MEaterConfigurationException {
// create a collection to hold the modules section
Collection<ConfigurationNode> moduleNodes = new LinkedList<ConfigurationNode>();
// save all of our modules to the section
for (ConfigModule m : this.modules.values()) {
// create a configuration for the module + save it, add it
HierarchicalConfiguration mc = new HierarchicalConfiguration();
m.saveConfigurationTo(mc);
mc.getRootNode().setName(m.getModuleName());
moduleNodes.add(mc.getRootNode());
}
// add modules section to config
config.addNodes(CKEY_MODULES, moduleNodes);
}
/*
* --------------------------------
* Config operations
* --------------------------------
*/
@Override
public void resetInternalConfiguration() {
// do general settings
this.generalSettings.resetInternalConfiguration();
// do all modules
for (ConfigModule m : this.modules.values()) {
m.resetInternalConfiguration();
}
}
@Override
protected void loadInternalConfigurationFrom(
HierarchicalConfiguration config)
throws MEaterConfigurationException {
this.loadModuleClassesFrom(config);
this.loadModulesFrom(config);
this.generalSettings.loadConfigurationFrom(config);
this.pathContainer.loadConfigurationFrom(config);
}
@Override
protected void saveInternalConfigurationTo(
HierarchicalConfiguration config)
throws MEaterConfigurationException {
this.saveModuleClassesTo(config);
this.saveModulesTo(config);
this.generalSettings.saveConfigurationTo(config);
this.pathContainer.saveConfigurationTo(config);
}
/*
* --------------------------------
* Message strings
* --------------------------------
*/
private static final String UINAME = "MEater Main";
private static final String UIDESC = "This is the main configuration "
+ "element of MEater. It contains core application information such as "
+ "loaded modules and logging settings.";
private static final String OP_UINAME_PATHS = "Set up media-processing paths";
private static final String OP_UINAME_GENERAL = "Set up general MEater configuration settings";
private static final String OP_SHORTNAME_PATHS = "setup-paths";
private static final String OP_SHORTNAME_GENERAL = "setup-general";
private static final String UINAME_PATHS = "Media-processing paths";
private static final String UIDESC_PATHS = "This unit holds the media-processing paths used in this MEater configuration.";
}