package thaw.core;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
import java.util.Vector;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* This class manages the thaw config.
*
* @author <a href="mailto:jflesch@gmail.com">Jerome Flesch</a>
*/
public class Config {
public static String CONFIG_FILE_NAME = "thaw.conf.xml";
private final File configFile;
private final HashMap parameters; /* String (param) -> String (value) */
private final HashMap listeners; /* String (param) -> Vector -> Plugin */
private final Vector pluginNames; /* String (plugin names) */
private final Core core;
public Config(Core core, final String filename) {
this.core = core;
configFile = new File(filename);
parameters = new HashMap();
pluginNames = new Vector();
listeners = new HashMap();
}
/**
* @return null if the value doesn't exit in the config.
*/
public String getValue(final String key) {
return ((String)parameters.get(key));
}
private boolean listenChanges = false;
private Vector pluginsToReload = null;
/**
* called when majors changed will be done to the config
* and will imply some plugin reloading
*/
public void startChanges() {
listenChanges = true;
pluginsToReload = new Vector();
}
/**
* Set the value in the config.
*/
public void setValue(final String key, final String value) {
Logger.debug(this, "Setting value '"+key+"' to '"+value+"'");
String currentValue = getValue(key);
if ( (currentValue != null && !currentValue.equals(value))
|| (currentValue == null && value != null)
|| (currentValue != null && value == null) ) {
/* we get the plugin list to reload */
Vector pluginList = (Vector)listeners.get(key);
if (listenChanges && pluginList != null) {
for (Iterator it = pluginList.iterator();
it.hasNext();) {
Plugin plugin = (Plugin)it.next();
/* if the plugin is not already in the plugin list to
* reload, we add it */
if (pluginsToReload.indexOf(plugin) < 0) {
Logger.notice(this, "Will have to reload '"+plugin.getClass().getName()+"' "+
"because '"+key+"' was changed from '"+currentValue+"' to '"+value+"'");
pluginsToReload.add(plugin);
}
}
}
/* and to finish, we set the value */
if (value != null)
parameters.put(key, value);
else
parameters.remove(key);
}
}
/**
* called after startChanges. Will reload the plugin listening for the changed
* values
*/
public void applyChanges() {
for (Iterator it = pluginsToReload.iterator();
it.hasNext();) {
Plugin plugin = (Plugin)it.next();
core.getPluginManager().stopPlugin(plugin.getClass().getName());
core.getPluginManager().runPlugin(plugin.getClass().getName());
}
cancelChanges();
}
/**
* Will not undo the changes do to the values, but reset to 0 the plugin list to reload
* Use it only if you know what you're doing !
*/
public void cancelChanges() {
listenChanges = false;
pluginsToReload = null;
}
/**
* Add the plugin at the end of the plugin list.
*/
public void addPlugin(final String name) {
pluginNames.add(name);
}
/**
* Add the plugin at the end of the given position (shifting already existing).
*/
public void addPlugin(final String name, final int position) {
pluginNames.add(position, name);
}
/**
* Give a vector containing the whole list of plugins.
*/
public Vector getPluginNames() {
return pluginNames;
}
/**
* Remove the given plugin.
*/
public void removePlugin(final String name) {
for(int i = 0; i < pluginNames.size() ; i++) {
final String currentPlugin = (String)pluginNames.get(i);
if(currentPlugin.equals(name))
pluginNames.remove(i);
}
}
/**
* Load the configuration.
* @return true if success, else false.
*/
public boolean loadConfig() {
if(configFile == null) {
Logger.error(this, "loadConfig(): No file specified !");
return false;
}
if(!configFile.exists() || !configFile.canRead()) {
Logger.notice(this, "Unable to read config file '"+configFile.getPath()+"'");
return false;
}
Document xmlDoc = null;
DocumentBuilderFactory xmlFactory = null;
DocumentBuilder xmlBuilder = null;
Element rootEl = null;
xmlFactory = DocumentBuilderFactory.newInstance();
try {
xmlBuilder = xmlFactory.newDocumentBuilder();
} catch(final javax.xml.parsers.ParserConfigurationException e) {
Logger.warning(this, "Unable to load config because: "+e);
return false;
}
try {
xmlDoc = xmlBuilder.parse(configFile);
} catch(final org.xml.sax.SAXException e) {
Logger.warning(this, "Unable to load config because: "+e);
return false;
} catch(final java.io.IOException e) {
Logger.warning(this, "Unable to load config because: "+e);
return false;
}
rootEl = xmlDoc.getDocumentElement();
final NodeList params = rootEl.getElementsByTagName("param");
for(int i = 0;i < params.getLength(); i++) {
Element paramEl;
final Node paramNode = params.item(i);
if((paramNode != null) && (paramNode.getNodeType() == Node.ELEMENT_NODE)) {
paramEl = (Element)paramNode;
parameters.put(paramEl.getAttribute("name"), paramEl.getAttribute("value"));
}
}
final NodeList plugins = rootEl.getElementsByTagName("plugin");
for(int i = 0;i < plugins.getLength(); i++) {
Element pluginEl;
final Node pluginNode = plugins.item(i);
if((pluginNode != null) && (pluginNode.getNodeType() == Node.ELEMENT_NODE)) {
pluginEl = (Element)pluginNode;
pluginNames.add(pluginEl.getAttribute("name"));
}
}
return true;
}
/**
* Save the configuration.
*
* @return true if success, else false.
*/
public boolean saveConfig() {
StreamResult configOut;
if(configFile == null) {
Logger.error(this, "saveConfig(): No file specified !");
return false;
}
try {
if( (!configFile.exists() && !configFile.createNewFile())
|| !configFile.canWrite()) {
Logger.warning(this, "Unable to write config file '"+configFile.getPath()+"' (can't write)");
return false;
}
} catch(final java.io.IOException e) {
Logger.warning(this, "Error while checking perms to save config: "+e);
}
configOut = new StreamResult(configFile);
Document xmlDoc = null;
DocumentBuilderFactory xmlFactory = null;
DocumentBuilder xmlBuilder = null;
DOMImplementation impl = null;
Element rootEl = null;
xmlFactory = DocumentBuilderFactory.newInstance();
try {
xmlBuilder = xmlFactory.newDocumentBuilder();
} catch(final javax.xml.parsers.ParserConfigurationException e) {
Logger.error(this, "Unable to save configuration because: "+e.toString());
return false;
}
impl = xmlBuilder.getDOMImplementation();
xmlDoc = impl.createDocument(null, "config", null);
rootEl = xmlDoc.getDocumentElement();
final Iterator entries = parameters.keySet().iterator();
while(entries.hasNext()) {
final String entry = (String)entries.next();
final String value = (String)parameters.get(entry);
final Element paramEl = xmlDoc.createElement("param");
paramEl.setAttribute("name", entry);
paramEl.setAttribute("value", value);
rootEl.appendChild(paramEl);
}
final Iterator plugins = pluginNames.iterator();
while(plugins.hasNext()) {
final String pluginName = (String)plugins.next();
final Element pluginEl = xmlDoc.createElement("plugin");
pluginEl.setAttribute("name", pluginName);
rootEl.appendChild(pluginEl);
}
/* Serialization */
final DOMSource domSource = new DOMSource(xmlDoc);
final TransformerFactory transformFactory = TransformerFactory.newInstance();
Transformer serializer;
try {
serializer = transformFactory.newTransformer();
} catch(final javax.xml.transform.TransformerConfigurationException e) {
Logger.error(this, "Unable to save configuration because: "+e.toString());
return false;
}
serializer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");
serializer.setOutputProperty(OutputKeys.INDENT,"yes");
/* final step */
try {
serializer.transform(domSource, configOut);
} catch(final javax.xml.transform.TransformerException e) {
Logger.error(this, "Unable to save configuration because: "+e.toString());
return false;
}
return true;
}
public boolean isEmpty() {
if(parameters.keySet().size() == 0)
return true;
return false;
}
/**
* Set the value only if it doesn't exits.
*/
public void setDefaultValue(final String name, final String val) {
if (getValue(name) == null)
setValue(name, val);
}
/**
* don't override the values if already existing
*/
public void setDefaultValues() {
setDefaultValue("nodeAddress", "127.0.0.1");
setDefaultValue("nodePort", "9481");
setDefaultValue("maxSimultaneousDownloads", "-1");
setDefaultValue("maxSimultaneousInsertions", "-1");
setDefaultValue("maxUploadSpeed", "-1");
setDefaultValue("thawId", "thaw_"+Integer.toString((new Random()).nextInt(1000)));
setDefaultValue("advancedMode", "false");
setDefaultValue("userNickname", "Another anonymous");
setDefaultValue("multipleSockets", "true");
setDefaultValue("downloadLocally", "true");
setDefaultValue("sameComputer", "true");
}
public void addListener(String name, Plugin plugin) {
Vector pluginList = (Vector)listeners.get(name);
if (pluginList == null) {
pluginList = new Vector();
listeners.put(name, pluginList);
}
if (pluginList.indexOf(plugin) < 0)
pluginList.add(plugin);
}
}