Newer
Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidKernel/ConfigService.h"
#include "MantidKernel/MantidVersion.h"
#include "MantidKernel/ParaViewVersion.h"
Janik Zikovsky
committed
#include "MantidKernel/Strings.h"
#include "MantidKernel/Logger.h"
#include "MantidKernel/FilterChannel.h"
#include "MantidKernel/StdoutChannel.h"
Roman Tolchenov
committed
#include "MantidKernel/FacilityInfo.h"
#include "MantidKernel/NetworkProxy.h"
#include <Poco/Util/LoggingConfigurator.h>
#include <Poco/Util/SystemConfiguration.h>
#include <Poco/Util/PropertyFileConfiguration.h>
#include <Poco/LoggingFactory.h>
#include <Poco/Path.h>
#include <Poco/File.h>
#include <Poco/StringTokenizer.h>
#include <Poco/DOM/DOMParser.h>
#include <Poco/DOM/Document.h>
#include <Poco/DOM/Element.h>
#include <Poco/DOM/NodeList.h>
#include <Poco/Environment.h>
#include <Poco/File.h>
Robert Whitley
committed
#include <Poco/Process.h>
Robert Whitley
committed
#include <Poco/String.h>
#include <Poco/PipeStream.h>
#include <Poco/StreamCopier.h>
Gigg, Martyn Anthony
committed
#include <boost/algorithm/string/replace.hpp>
Gigg, Martyn Anthony
committed
#include <boost/algorithm/string/join.hpp>
Gigg, Martyn Anthony
committed
#include <fstream>
#include <sstream>
#include <iostream>
#include <string>
Janik Zikovsky
committed
/**
* Get the welcome message for Mantid.
* @returns A string containing the welcome message for Mantid.
*/
std::string welcomeMessage() {
return "Welcome to Mantid " +
std::string(Mantid::Kernel::MantidVersion::version()) +
"\nPlease cite: " + Mantid::Kernel::MantidVersion::paperCitation() +
" and this release: " + Mantid::Kernel::MantidVersion::doi();
Janik Zikovsky
committed
}
namespace { // anonymous namespace for some utility functions
/// static Logger object
Logger g_log("ConfigService");
/**
* Split the supplied string on semicolons.
*
* @param path The path to split.
* @param splitted vector to put the splitted path into.
*/
void splitPath(const std::string &path, std::vector<std::string> &splitted) {
if (path.find(";") == std::string::npos) { // don't bother tokenizing
splitted.push_back(path);
return;
}
int options =
Poco::StringTokenizer::TOK_TRIM + Poco::StringTokenizer::TOK_IGNORE_EMPTY;
splitted.clear();
Poco::StringTokenizer tokenizer(path, ";,", options);
Poco::StringTokenizer::Iterator iend = tokenizer.end();
splitted.reserve(tokenizer.count());
for (Poco::StringTokenizer::Iterator itr = tokenizer.begin(); itr != iend;
++itr) {
if (!itr->empty()) {
splitted.push_back(*itr);
}
}
}
} // end of anonymous namespace
Campbell, Stuart
committed
/** Inner templated class to wrap the poco library objects that have protected
* destructors and expose them as public.
*/
template <typename T> class ConfigServiceImpl::WrappedObject : public T {
Campbell, Stuart
committed
public:
/// The template type of class that is being wrapped
typedef T element_type;
/// Simple constructor
WrappedObject() : T() { m_pPtr = static_cast<T *>(this); }
Campbell, Stuart
committed
/** Constructor with a class to wrap
Janik Zikovsky
committed
* @param F :: The object to wrap
template <typename Field> WrappedObject(Field &F) : T(F) {
m_pPtr = static_cast<T *>(this);
Campbell, Stuart
committed
}
Campbell, Stuart
committed
/// Copy constructor
WrappedObject(const WrappedObject<T> &A) : T(A) {
m_pPtr = static_cast<T *>(this);
Campbell, Stuart
committed
}
Campbell, Stuart
committed
/// Virtual destructor
Campbell, Stuart
committed
/// Overloaded * operator returns the wrapped object pointer
const T &operator*() const { return *m_pPtr; }
Campbell, Stuart
committed
/// Overloaded * operator returns the wrapped object pointer
Campbell, Stuart
committed
/// Overloaded -> operator returns the wrapped object pointer
const T *operator->() const { return m_pPtr; }
Campbell, Stuart
committed
/// Overloaded -> operator returns the wrapped object pointer
Campbell, Stuart
committed
private:
/// Private pointer to the wrapped class
Campbell, Stuart
committed
};
// Back to the ConfigService class itself...
Campbell, Stuart
committed
//-------------------------------
// Private member functions
//-------------------------------
Roman Tolchenov
committed
Campbell, Stuart
committed
/// Private constructor for singleton class
ConfigServiceImpl::ConfigServiceImpl()
: m_pConf(NULL), m_pSysConfig(NULL), m_changed_keys(), m_ConfigPaths(),
m_AbsolutePaths(), m_strBaseDir(""), m_PropertyString(""),
m_properties_file_name("Mantid.properties"),
#ifdef MPI_BUILD
// Use a different user properties file for an mpi-enabled build to avoid
// confusion if both are used on the same filesystem
m_user_properties_file_name("Mantid-mpi.user.properties"),
#else
m_user_properties_file_name("Mantid.user.properties"),
#endif
m_DataSearchDirs(), m_UserSearchDirs(), m_InstrumentDirs(),
m_instr_prefixes(), m_removedFlag("@@REMOVED@@"), m_proxyInfo(),
m_isProxySet(false) {
// getting at system details
m_pSysConfig = new WrappedObject<Poco::Util::SystemConfiguration>;
Campbell, Stuart
committed
m_pConf = 0;
// Register the FilterChannel with the Poco logging factory
Poco::LoggingFactory::defaultFactory().registerChannelClass(
"FilterChannel",
new Poco::Instantiator<Poco::FilterChannel, Poco::Channel>);
// Register StdChannel with Poco
Poco::LoggingFactory::defaultFactory().registerChannelClass(
"StdoutChannel",
new Poco::Instantiator<Poco::StdoutChannel, Poco::Channel>);
Campbell, Stuart
committed
Gigg, Martyn Anthony
committed
// Define the directory to search for the Mantid.properties file.
Janik Zikovsky
committed
Poco::File f;
// First directory: the current working
m_strBaseDir = Poco::Path::current();
f = Poco::File(m_strBaseDir + m_properties_file_name);
if (!f.exists()) {
// Check the executable directory to see if it includes a mantid.properties
// file
Gigg, Martyn Anthony
committed
m_strBaseDir = getDirectoryOfExecutable();
Janik Zikovsky
committed
f = Poco::File(m_strBaseDir + m_properties_file_name);
Janik Zikovsky
committed
// Last, use the MANTIDPATH environment var
if (Poco::Environment::has("MANTIDPATH")) {
// Here we have to follow the convention of the rest of this code and
// add a trailing slash.
// Note: adding it to the MANTIDPATH itself will make other parts of the
// code crash.
Janik Zikovsky
committed
m_strBaseDir = Poco::Environment::get("MANTIDPATH") + "/";
}
// Assert that the appdata and the instrument subdirectory exists
std::string appDataDir = getAppDataDir();
Poco::Path path(appDataDir);
path.pushDirectory("instrument");
Poco::File file(path);
// createdirectories will fail gracefully if it is already present
file.createDirectories();
// Fill the list of possible relative path keys that may require conversion to
// absolute paths
m_ConfigPaths.insert(
std::make_pair("mantidqt.python_interfaces_directory", true));
Campbell, Stuart
committed
m_ConfigPaths.insert(std::make_pair("plugins.directory", true));
m_ConfigPaths.insert(std::make_pair("pvplugins.directory", true));
Russell Taylor
committed
m_ConfigPaths.insert(std::make_pair("mantidqt.plugins.directory", true));
Campbell, Stuart
committed
m_ConfigPaths.insert(std::make_pair("instrumentDefinition.directory", true));
m_ConfigPaths.insert(std::make_pair("groupingFiles.directory", true));
m_ConfigPaths.insert(std::make_pair("maskFiles.directory", true));
m_ConfigPaths.insert(std::make_pair("colormaps.directory", true));
m_ConfigPaths.insert(
std::make_pair("requiredpythonscript.directories", true));
Campbell, Stuart
committed
m_ConfigPaths.insert(std::make_pair("pythonscripts.directory", true));
m_ConfigPaths.insert(std::make_pair("pythonscripts.directories", true));
m_ConfigPaths.insert(std::make_pair("python.plugins.directories", true));
m_ConfigPaths.insert(std::make_pair("user.python.plugins.directories", true));
m_ConfigPaths.insert(std::make_pair("datasearch.directories", true));
Campbell, Stuart
committed
m_ConfigPaths.insert(std::make_pair("icatDownload.directory", true));
// attempt to load the default properties file that resides in the directory
// of the executable
Janik Zikovsky
committed
std::string propertiesFilesList;
Gigg, Martyn Anthony
committed
updateConfig(getPropertiesDir() + m_properties_file_name, false, false);
Janik Zikovsky
committed
propertiesFilesList = getPropertiesDir() + m_properties_file_name;
// Load the local (machine) properties file, if it exists
Poco::File localFile(getLocalFilename());
updateConfig(getLocalFilename(), true, false);
propertiesFilesList += ", " + getLocalFilename();
}
if (Poco::Environment::has("MANTIDPROPERTIES")) {
// and then append the user properties
Janik Zikovsky
committed
updateConfig(getUserFilename(), true, false);
propertiesFilesList += ", " + getUserFilename();
// and the extra one from the environment
Janik Zikovsky
committed
updateConfig(Poco::Environment::get("MANTIDPROPERTIES"), true, true);
propertiesFilesList += ", " + Poco::Environment::get("MANTIDPROPERTIES");
Janik Zikovsky
committed
// Just do the user properties
updateConfig(getUserFilename(), true, true);
propertiesFilesList += ", " + getUserFilename();
}
Campbell, Stuart
committed
updateFacilities();
g_log.debug() << "ConfigService created." << std::endl;
g_log.debug() << "Configured Mantid.properties directory of application as "
<< getPropertiesDir() << std::endl;
g_log.information() << "This is Mantid version " << MantidVersion::version()
<< " revision " << MantidVersion::revision() << std::endl;
g_log.information() << "Properties file(s) loaded: " << propertiesFilesList
<< std::endl;
#ifndef MPI_BUILD // There is no logging to file by default in MPI build
g_log.information() << "Logging to: " << m_logFilePath << std::endl;
Campbell, Stuart
committed
}
/** Private Destructor
* Prevents client from calling 'delete' on the pointer handed out by Instance
*/
ConfigServiceImpl::~ConfigServiceImpl() {
// std::cerr << "ConfigService destroyed." << std::endl;
Campbell, Stuart
committed
Kernel::Logger::shutdown();
delete m_pSysConfig;
delete m_pConf; // potential double delete???
clearFacilities();
Campbell, Stuart
committed
}
/** Loads the config file provided.
* If the file contains logging setup instructions then these will be used to
*setup the logging framework.
Campbell, Stuart
committed
*
Janik Zikovsky
committed
* @param filename :: The filename and optionally path of the file to load
* @param append :: If false (default) then any previous configuration is
*discarded, otherwise the new keys are added, and repeated keys will override
*existing ones.
Campbell, Stuart
committed
*/
void ConfigServiceImpl::loadConfig(const std::string &filename,
const bool append) {
Campbell, Stuart
committed
delete m_pConf;
if (!append) {
// remove the previous property string
Campbell, Stuart
committed
m_PropertyString = "";
m_changed_keys.clear();
Campbell, Stuart
committed
std::string temp;
bool good = readFile(filename, temp);
Campbell, Stuart
committed
// check if we have failed to open the file
if ((!good) || (temp == "")) {
if (filename == getUserPropertiesDir() + m_user_properties_file_name) {
// write out a fresh file
Campbell, Stuart
committed
createUserPropertiesFile();
Campbell, Stuart
committed
throw Exception::FileError("Cannot open file", filename);
Campbell, Stuart
committed
}
// store the property string
if ((append) && (m_PropertyString != "")) {
Campbell, Stuart
committed
m_PropertyString = m_PropertyString + "\n" + temp;
Campbell, Stuart
committed
m_PropertyString = temp;
// there was a problem loading the file - it probably is not there
std::cerr << "Problem loading the configuration file " << filename << " "
<< e.what() << std::endl;
if (!append) {
Campbell, Stuart
committed
// if we have no property values then take the default
m_PropertyString = defaultConfig();
Campbell, Stuart
committed
}
// use the cached property string to initialise the POCO property file
Campbell, Stuart
committed
std::istringstream istr(m_PropertyString);
m_pConf = new WrappedObject<Poco::Util::PropertyFileConfiguration>(istr);
Campbell, Stuart
committed
}
/**
* Read a file and place its contents into the given string
Janik Zikovsky
committed
* @param filename :: The filename of the file to read
* @param contents :: The file contents will be placed here
Campbell, Stuart
committed
* @returns A boolean indicating whether opening the file was successful
*/
bool ConfigServiceImpl::readFile(const std::string &filename,
std::string &contents) const {
Campbell, Stuart
committed
std::ifstream propFile(filename.c_str(), std::ios::in);
bool good = propFile.good();
Campbell, Stuart
committed
contents = "";
propFile.close();
return good;
}
Campbell, Stuart
committed
// slurp in entire file - extremely unlikely delimiter used as an alternate to
// \n
Campbell, Stuart
committed
contents.clear();
getline(propFile, contents, '`');
propFile.close();
return good;
}
/** Configures the Poco logging and starts it up
*
*/
void ConfigServiceImpl::configureLogging() {
try {
// Ensure that the logging directory exists
m_logFilePath = getString("logging.channels.fileChannel.path");
Poco::Path logpath(m_logFilePath);
if (Poco::Environment::has("MANTIDLOGPATH")) {
logpath = Poco::Path(Poco::Environment::get("MANTIDLOGPATH"));
Gigg, Martyn Anthony
committed
logpath = logpath.absolute();
m_logFilePath = logpath.toString();
}
// An absolute path makes things simpler
logpath = logpath.absolute();
Gigg, Martyn Anthony
committed
// First, try the logpath given
if (!m_logFilePath.empty()) {
try {
// Save it for later
m_logFilePath = logpath.toString();
// make this path point to the parent directory and create it if it does
// not exist
Poco::Path parent = logpath;
parent.makeParent();
Poco::File(parent).createDirectories();
// Try to create or append to the file. If it fails, use the default
FILE *fp = fopen(m_logFilePath.c_str(), "a+");
if (fp == NULL) {
std::cerr
<< "Error writing to log file path given in properties file: \""
<< m_logFilePath << "\". Will use a default path instead."
<< std::endl;
// Clear the path; this will make it use the default
m_logFilePath = "";
fclose(fp);
std::cerr
<< "Error writing to log file path given in properties file: \""
<< m_logFilePath << "\". Will use a default path instead."
<< std::endl;
// ERROR! Maybe the file is not writable!
// Clear the path; this will make it use the default
m_logFilePath = "";
}
}
// The path given was invalid somehow? Use a default
m_logFilePath = getUserPropertiesDir() + "mantid.log";
// Check whether the file can be written. The Poco::File::canWrite method
// does not work
// for files that don't exist, it throws an exception. It also can't be
// used to check for
// directory access as the Windows API doesn't return this information
// correctly for
// directories.
FILE *fp = fopen(m_logFilePath.c_str(), "a+");
if (!fp) {
// if we cannot write to the default directory then set use the system
// temp
logpath = Poco::Path::temp() + "mantid.log";
m_logFilePath = logpath.toString();
std::cerr << "Error writing to log file path to default location: \""
<< m_logFilePath
<< "\". Will use a system temp path instead: \""
<< m_logFilePath << "\"" << std::endl;
} else
fclose(fp);
// Set the line in the configuration properties.
// this'll be picked up by LoggingConfigurator (somehow)
m_pConf->setString("logging.channels.fileChannel.path", m_logFilePath);
// make this path point to the parent directory and create it if it does not
// exist
Campbell, Stuart
committed
logpath.makeParent();
if (!logpath.toString().empty()) {
Poco::File(logpath)
.createDirectories(); // Also creates all necessary directories
// Configure the logging framework
Campbell, Stuart
committed
Poco::Util::LoggingConfigurator configurator;
configurator.configure(m_pConf);
std::cerr << "Trouble configuring the logging framework " << e.what()
<< std::endl;
}
Campbell, Stuart
committed
}
Campbell, Stuart
committed
/**
* Searches the stored list for keys that have been loaded from the config file
* and may contain
* relative paths. Any it find are converted to absolute paths and stored
* separately
Campbell, Stuart
committed
*/
void ConfigServiceImpl::convertRelativeToAbsolute() {
Campbell, Stuart
committed
if (m_ConfigPaths.empty())
return;
Campbell, Stuart
committed
m_AbsolutePaths.clear();
std::map<std::string, bool>::const_iterator send = m_ConfigPaths.end();
for (std::map<std::string, bool>::const_iterator sitr = m_ConfigPaths.begin();
sitr != send; ++sitr) {
Campbell, Stuart
committed
std::string key = sitr->first;
if (!m_pConf->hasProperty(key))
continue;
Campbell, Stuart
committed
std::string value(m_pConf->getString(key));
value = makeAbsolute(value, key);
m_AbsolutePaths.insert(std::make_pair(key, value));
}
}
/**
* Make a relative path or a list of relative paths into an absolute one.
Janik Zikovsky
committed
* @param dir :: The directory to convert
* @param key :: The key variable this relates to
* @returns A string containing an absolute path by resolving the relative
* directory with the executable directory
Campbell, Stuart
committed
*/
std::string ConfigServiceImpl::makeAbsolute(const std::string &dir,
const std::string &key) const {
if (dir.empty()) {
// Don't do anything for an empty value
return dir;
}
Campbell, Stuart
committed
std::string converted;
// If we have a list, chop it up and convert each one
if (dir.find_first_of(";,") != std::string::npos) {
std::vector<std::string> splitted;
splitPath(dir, splitted);
std::vector<std::string>::const_iterator iend = splitted.end();
for (std::vector<std::string>::const_iterator itr = splitted.begin();
itr != iend;) {
Campbell, Stuart
committed
std::string absolute = makeAbsolute(*itr, key);
Campbell, Stuart
committed
++itr;
Campbell, Stuart
committed
converted += absolute;
Campbell, Stuart
committed
converted += ";";
Campbell, Stuart
committed
}
// MG 05/10/09: When the Poco::FilePropertyConfiguration object reads its
// key/value pairs it
// treats a backslash as the start of an escape sequence. If the next
// character does not
// form a valid sequence then the backslash is removed from the stream. This
// has the effect
// of giving malformed paths when using Windows-style directories. E.g
// C:\Mantid ->C:Mantid
Campbell, Stuart
committed
// and Poco::Path::isRelative throws an exception on this
bool is_relative(false);
Campbell, Stuart
committed
is_relative = Poco::Path(dir).isRelative();
}
catch (Poco::PathSyntaxException &) {
g_log.warning() << "Malformed path detected in the \"" << key
<< "\" variable, skipping \"" << dir << "\"\n";
Campbell, Stuart
committed
return "";
}
Gigg, Martyn Anthony
committed
const std::string propFileDir(getPropertiesDir());
Gigg, Martyn Anthony
committed
converted = Poco::Path(propFileDir).resolve(dir).toString();
Campbell, Stuart
committed
converted = dir;
Campbell, Stuart
committed
converted = Poco::Path(converted).makeDirectory().toString();
// C++ doesn't have a const version of operator[] for maps so I can't call
// that here
Campbell, Stuart
committed
std::map<std::string, bool>::const_iterator it = m_ConfigPaths.find(key);
bool required = false;
Campbell, Stuart
committed
required = it->second;
}
try {
if (required && !Poco::File(converted).exists()) {
g_log.debug() << "Required properties path \"" << converted
<< "\" in the \"" << key << "\" variable does not exist.\n";
Michael Whitty
committed
converted = "";
}
}
catch (Poco::FileException &) {
g_log.debug() << "Required properties path \"" << converted
<< "\" in the \"" << key << "\" variable does not exist.\n";
Campbell, Stuart
committed
converted = "";
}
Gigg, Martyn Anthony
committed
// Backward slashes cannot be allowed to go into our properties file
// Note this is a temporary fix for ticket #2445.
Gigg, Martyn Anthony
committed
// Ticket #2460 prompts a review of our path handling in the config service.
Janik Zikovsky
committed
boost::replace_all(converted, "\\", "/");
Campbell, Stuart
committed
return converted;
}
/**
* Create the store of data search paths from the 'datasearch.directories' key
* within the Mantid.properties file.
Campbell, Stuart
committed
* The value of the key should be a semi-colon separated list of directories
*/
void ConfigServiceImpl::cacheDataSearchPaths() {
Campbell, Stuart
committed
m_DataSearchDirs.clear();
std::string paths = getString("datasearch.directories");
Campbell, Stuart
committed
if (paths.empty())
return;
splitPath(paths, m_DataSearchDirs);
Campbell, Stuart
committed
}
* Create the store of user search paths from the 'usersearch.directories' key
* within the Mantid.properties file.
* The value of the key should be a semi-colon separated list of directories
*/
void ConfigServiceImpl::cacheUserSearchPaths() {
m_UserSearchDirs.clear();
std::string paths = getString("usersearch.directories");
if (paths.empty())
return;
splitPath(paths, m_UserSearchDirs);
}
/**
* The path that is passed should be as returned by makeAbsolute() and
* this function will return true if that path is in the list
Janik Zikovsky
committed
* @param path :: the absolute path name to search for
* @return true if the path was found
*/
bool ConfigServiceImpl::isInDataSearchList(const std::string &path) const {
// the path produced by poco will have \ on windows, but the searchdirs will
// always have /
std::string correctedPath = path;
replace(correctedPath.begin(), correctedPath.end(), '\\', '/');
std::vector<std::string>::const_iterator it =
std::find_if(m_DataSearchDirs.begin(), m_DataSearchDirs.end(),
std::bind2nd(std::equal_to<std::string>(), correctedPath));
Gigg, Martyn Anthony
committed
return (it != m_DataSearchDirs.end());
}
Campbell, Stuart
committed
/**
* writes a basic placeholder user.properties file to disk
* any errors are caught and logged, but not propagated
*/
void ConfigServiceImpl::createUserPropertiesFile() const {
try {
std::fstream filestr(
(getUserPropertiesDir() + m_user_properties_file_name).c_str(),
Janik Zikovsky
committed
std::fstream::out);
Campbell, Stuart
committed
filestr << "# This file can be used to override any properties for this "
"installation." << std::endl;
filestr << "# Any properties found in this file will override any that are "
"found in the Mantid.Properties file" << std::endl;
filestr << "# As this file will not be replaced with futher installations "
"of Mantid it is a safe place to put " << std::endl;
filestr << "# properties that suit your particular installation."
<< std::endl;
filestr << "#" << std::endl;
filestr << "# See here for a list of possible options:" << std::endl;
filestr << "# "
"http://www.mantidproject.org/"
"Properties_File#Mantid.User.Properties" << std::endl;
filestr << std::endl;
filestr << "##" << std::endl;
filestr << "## GENERAL" << std::endl;
filestr << "##" << std::endl;
filestr << std::endl;
filestr << "## Set the number of algorithm properties to retain"
<< std::endl;
filestr << "#algorithms.retained=90" << std::endl;
filestr << std::endl;
filestr << "## Hides catagories from the algorithm list in MantidPlot"
<< std::endl;
filestr << "#algorithms.catagories.hidden=Muons,Inelastic" << std::endl;
filestr << std::endl;
filestr << "## Set the maximum number of coures used to run algorithms over"
<< std::endl;
filestr << "#MultiThreaded.MaxCores=4" << std::endl;
filestr << std::endl;
filestr << "##" << std::endl;
filestr << "## FACILITY AND INSTRUMENT" << std::endl;
filestr << "##" << std::endl;
filestr << std::endl;
filestr << "## Sets the default facility" << std::endl;
filestr << "## e.g.: ISIS, SNS, ILL" << std::endl;
filestr << "default.facility=" << std::endl;
filestr << std::endl;
filestr << "## Stes the default instrument" << std::endl;
filestr << "## e.g. IRIS, HET, NIMROD" << std::endl;
filestr << "default.instrument=" << std::endl;
filestr << std::endl;
filestr << "##" << std::endl;
filestr << "## DIRECTORIES" << std::endl;
filestr << "##" << std::endl;
filestr << std::endl;
filestr << "## Sets a list of directories (separated by semi colons) to "
"search for data" << std::endl;
filestr << "#datasearch.directories=../data;../isis/data" << std::endl;
filestr << std::endl;
filestr << "## Set a list (separated by semi colons) of directories to "
"look for additional Python scripts" << std::endl;
filestr << "#pythonscripts.directories=../scripts;../docs/MyScripts"
<< std::endl;
filestr << "## Uncomment to enable archive search - ICat and Orbiter"
<< std::endl;
filestr << "#datasearch.searcharchive=On" << std::endl;
filestr << std::endl;
filestr << "## Sets default save directory" << std::endl;
filestr << "#defaultsave.directory=../data" << std::endl;
filestr << std::endl;
filestr << "##" << std::endl;
filestr << "## LOGGING" << std::endl;
filestr << "##" << std::endl;
filestr << std::endl;
filestr << "## Uncomment to change logging level" << std::endl;
filestr << "## Default is information" << std::endl;
filestr << "## Valid values are: error, warning, notice, information, debug"
<< std::endl;
filestr << "#logging.loggers.root.level=information" << std::endl;
filestr << std::endl;
filestr << "## Sets the lowest level messages to be logged to file"
<< std::endl;
filestr << "## Default is warning" << std::endl;
filestr << "## Valid values are: error, warning, notice, information, debug"
<< std::endl;
filestr << "#logging.channels.fileFilterChannel.level=debug" << std::endl;
filestr << std::endl;
filestr << "## Sets the file to write logs to" << std::endl;
filestr << "#logging.channels.fileChannel.path=../mantid.log" << std::endl;
filestr << std::endl;
filestr << "##" << std::endl;
filestr << "## MantidPlot" << std::endl;
filestr << "##" << std::endl;
filestr << std::endl;
filestr << "## Show invisible workspaces" << std::endl;
filestr << "#MantidOptions.InvisibleWorkspaces=0" << std::endl;
filestr << "## Re-use plot instances for different plot types" << std::endl;
filestr << "#MantidOptions.ReusePlotInstances=Off" << std::endl;
filestr << std::endl;
filestr << "## Uncomment to disable use of OpenGL to render unwrapped "
"instrument views" << std::endl;
filestr << "#MantidOptions.InstrumentView.UseOpenGL=Off" << std::endl;
Campbell, Stuart
committed
filestr.close();
g_log.warning() << "Unable to write out user.properties file to "
<< getUserPropertiesDir() << m_user_properties_file_name
<< " error: " << ex.what() << std::endl;
Campbell, Stuart
committed
}
Campbell, Stuart
committed
/**
* Provides a default Configuration string to use if the config file cannot be
* loaded.
Campbell, Stuart
committed
* @returns The string value of default properties
*/
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
std::string ConfigServiceImpl::defaultConfig() const {
std::string propFile =
"# logging configuration"
"# root level message filter (drop to debug for more messages)"
"logging.loggers.root.level = debug"
"# splitting the messages to many logging channels"
"logging.loggers.root.channel.class = SplitterChannel"
"logging.loggers.root.channel.channel1 = consoleChannel"
"logging.loggers.root.channel.channel2 = fileFilterChannel"
"# output to the console - primarily for console based apps"
"logging.channels.consoleChannel.class = ConsoleChannel"
"logging.channels.consoleChannel.formatter = f1"
"# specfic filter for the file channel raising the level to warning "
"(drop to debug for debugging)"
"logging.channels.fileFilterChannel.class= FilterChannel"
"logging.channels.fileFilterChannel.channel= fileChannel"
"logging.channels.fileFilterChannel.level= warning"
"# output to a file (For error capturing and debugging)"
"logging.channels.fileChannel.class = debug"
"logging.channels.fileChannel.path = ../logs/mantid.log"
"logging.channels.fileChannel.formatter.class = PatternFormatter"
"logging.channels.fileChannel.formatter.pattern = %Y-%m-%d %H:%M:%S,%i "
"[%I] %p %s - %t"
"logging.formatters.f1.class = PatternFormatter"
"logging.formatters.f1.pattern = %s-[%p] %t"
"logging.formatters.f1.times = UTC";
Campbell, Stuart
committed
return propFile;
}
//-------------------------------
// Public member functions
//-------------------------------
/**
* Removes the user properties file & loads a fresh configuration
*/
// Remove the current user properties file and write a fresh one
Poco::File userFile(getUserFilename());
userFile.remove();
}
}
createUserPropertiesFile();
const bool append = false;
const bool updateCaches = true;
updateConfig(getPropertiesDir() + m_properties_file_name, append,
updateCaches);
Campbell, Stuart
committed
/** Updates and existing configuration and restarts the logging
Janik Zikovsky
committed
* @param filename :: The filename and optionally path of the file to load
* @param append :: If false (default) then any previous configuration is
* discarded,
* otherwise the new keys are added, and repeated keys will
* override existing ones.
* @param update_caches :: If true(default) then the various property caches
* are updated
Campbell, Stuart
committed
*/
void ConfigServiceImpl::updateConfig(const std::string &filename,
const bool append,
const bool update_caches) {
Campbell, Stuart
committed
loadConfig(filename, append);
// Ensure that the default save directory makes sense
/*
if (!append)
{
std::string save_dir = getString("defaultsave.directory");
if (Poco::trimInPlace(save_dir).size() == 0)
setString("defaultsave.directory", Poco::Path::home());
*/
// Only configure logging once
configureLogging();
// Ensure that any relative paths given in the configuration file are
// relative to the correct directory
Campbell, Stuart
committed
convertRelativeToAbsolute();
// Configure search paths into a specially saved store as they will be used
// frequently
Campbell, Stuart
committed
cacheDataSearchPaths();
Gigg, Martyn Anthony
committed
appendDataSearchDir(getString("defaultsave.directory"));
cacheUserSearchPaths();
cacheInstrumentPaths();
Campbell, Stuart
committed
}
/**
* Save the configuration to the user file
Janik Zikovsky
committed
* @param filename :: The filename for the saved configuration
* @throw std::runtime_error if the file cannot be opened
Campbell, Stuart
committed
*/
void ConfigServiceImpl::saveConfig(const std::string &filename) const {
Campbell, Stuart
committed
// Open and read the user properties file
std::string updated_file("");
std::ifstream reader(filename.c_str(), std::ios::in);
if (reader.bad()) {
throw std::runtime_error("Error opening user properties file. Cannot save "
"updated configuration.");
Campbell, Stuart
committed
std::string file_line(""), output("");
bool line_continuing(false);
while (std::getline(reader, file_line)) {
if (!file_line.empty()) {
Campbell, Stuart
committed
char last = *(file_line.end() - 1);
// If we are not in line continuation mode then need
// a fresh start line
Janik Zikovsky
committed
if (!line_continuing)
output = "";
Campbell, Stuart
committed
line_continuing = true;
output += file_line + "\n";
Gigg, Martyn Anthony
committed
continue;
Campbell, Stuart
committed
output += file_line;
line_continuing = false;
Campbell, Stuart
committed
output = file_line;
Campbell, Stuart
committed
output = "";
Gigg, Martyn Anthony
committed
updated_file += "\n";
Campbell, Stuart
committed
continue;
Robert Whitley
committed
// Output is the current line in the file
Robert Whitley
committed
// Extract the key from the current line
std::string key;
std::string::size_type pos = output.find('=');
if (pos == std::string::npos) {
key = output; // If no equals then the entire thing is the key
} else {
key = output.substr(0, pos); // Strip the equals to get only the key
// Now deal with trimming (removes spaces)
Robert Whitley
committed
Poco::trimInPlace(key);
Robert Whitley
committed
std::string::size_type comment = key.find('#');
// Check if it exists in the service using hasProperty and make sure it
// isn't a comment
if (comment == 0) {
updated_file += output;
} else if (!hasProperty(key)) {
// Remove the key from the changed key list
m_changed_keys.erase(key);
continue;
// If it does exist make sure the value is current
std::string value = getString(key, false);
Poco::replaceInPlace(value, "\\", "\\\\"); // replace single \ with double
updated_file += key + "=" + value;
// Remove the key from the changed key list
m_changed_keys.erase(key);
Robert Whitley
committed
}
Robert Whitley
committed
} // End while-loop
// Any remaining keys within the changed key store weren't present in the
// current user properties so append them
if (!m_changed_keys.empty()) {
Campbell, Stuart
committed
updated_file += "\n";
std::set<std::string>::iterator key_end = m_changed_keys.end();
for (std::set<std::string>::iterator key_itr = m_changed_keys.begin();
key_itr != key_end;) {
Campbell, Stuart
committed
updated_file += *key_itr + "=";
std::string value = getString(*key_itr, false);
Poco::replaceInPlace(value, "\\", "\\\\"); // replace single \ with double
updated_file += value;
Campbell, Stuart
committed
updated_file += "\n";
Campbell, Stuart
committed
m_changed_keys.clear();
Campbell, Stuart
committed
// Write out the new file
std::ofstream writer(filename.c_str(), std::ios_base::trunc);
Campbell, Stuart
committed
writer.close();
g_log.error() << "Error writing new user properties file. Cannot save "
"current configuration.\n";
throw std::runtime_error("Error writing new user properties file. Cannot "
"save current configuration.");
Campbell, Stuart
committed
}
Campbell, Stuart
committed
writer.write(updated_file.c_str(), updated_file.size());
writer.close();
}
/** Searches for a string within the currently loaded configuaration values and
* returns the value as a string. If the key is one of those that was a
*possible relative path
Campbell, Stuart
committed
* then the local store is searched first.
*
* @param keyName :: The case sensitive name of the property that you need the
*value of.
* @param use_cache :: If true, the local cache of directory names is queried
*first.
* @returns The string value of the property, or an empty string if the key
*cannot be found
Campbell, Stuart
committed
*/
std::string ConfigServiceImpl::getString(const std::string &keyName,
bool use_cache) const {
if (use_cache) {
std::map<std::string, std::string>::const_iterator mitr =
m_AbsolutePaths.find(keyName);
if (mitr != m_AbsolutePaths.end()) {
Campbell, Stuart
committed
return (*mitr).second;
Campbell, Stuart
committed
std::string retVal;
Campbell, Stuart
committed
retVal = m_pConf->getString(keyName);
if (retVal == m_removedFlag)
retVal = "";
}
catch (Poco::NotFoundException &) {
g_log.debug() << "Unable to find " << keyName << " in the properties file"
<< std::endl;
Campbell, Stuart
committed
retVal = "";
Campbell, Stuart
committed
return retVal;
}
Robert Whitley
committed
/** Searches for keys within the currently loaded configuaration values and
Robert Whitley
committed
*
* @param keyName :: The case sensitive name of the property that you need the
*key for.
* @returns The string value of each key within a vector, or an empty vector if
*there isn't
Robert Whitley
committed
* a key or it couldn't be found.
*/
std::vector<std::string>
ConfigServiceImpl::getKeys(const std::string &keyName) const {
std::vector<std::string> rawKeys;
Robert Whitley
committed
std::vector<std::string> keyVector;
keyVector.reserve(rawKeys.size());
try {
m_pConf->keys(keyName, rawKeys);
// Work around a limitation of Poco < v1.4 which has no remove functionality
// so
// check those that have been marked with the correct flag
const size_t nraw = rawKeys.size();
const std::string key = rawKeys[i];
try {
if (m_pConf->getString(key) == m_removedFlag)
continue;
}
keyVector.push_back(key);
}
Robert Whitley
committed
}
g_log.debug() << "Unable to find " << keyName << " in the properties file"
<< std::endl;
Robert Whitley
committed
keyVector.clear();
}
return keyVector;
}