//---------------------------------------------------------------------- // Includes //---------------------------------------------------------------------- #include "MantidKernel/ConfigService.h" #include "MantidKernel/DateAndTime.h" #include "MantidKernel/MantidVersion.h" #include "MantidKernel/Strings.h" #include "MantidKernel/Logger.h" #include "MantidKernel/StdoutChannel.h" #include "MantidKernel/System.h" #include "MantidKernel/Exception.h" #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 <MantidKernel/StringTokenizer.h> #include <Poco/DOM/DOMParser.h> #include <Poco/DOM/Document.h> #include <Poco/DOM/NodeList.h> #include <Poco/Environment.h> #include <Poco/Process.h> #include <Poco/URI.h> #include <Poco/AutoPtr.h> #include <Poco/Channel.h> #include <Poco/DOM/Element.h> #include <Poco/DOM/Node.h> #include <Poco/Exception.h> #include <Poco/Instantiator.h> #include <Poco/Pipe.h> #include <Poco/Platform.h> #include <Poco/String.h> #include <Poco/Logger.h> #include <Poco/LoggingRegistry.h> #include <Poco/PipeStream.h> #include <Poco/StreamCopier.h> #include <boost/algorithm/string/join.hpp> #include <boost/regex.hpp> #include <algorithm> #include <cctype> #include <exception> #include <fstream> #include <functional> #include <iostream> #include <stdexcept> #include <utility> #ifdef __APPLE__ #include <mach-o/dyld.h> #endif namespace Mantid { /** * 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(); } namespace Kernel { 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. * @returns vector containing the split path. */ std::vector<std::string> splitPath(const std::string &path) { std::vector<std::string> splitted; if (path.find(';') == std::string::npos) { // don't bother tokenizing splitted.push_back(path); } else { int options = Mantid::Kernel::StringTokenizer::TOK_TRIM + Mantid::Kernel::StringTokenizer::TOK_IGNORE_EMPTY; Mantid::Kernel::StringTokenizer tokenizer(path, ";,", options); auto iend = tokenizer.end(); for (auto itr = tokenizer.begin(); itr != iend; ++itr) { if (!itr->empty()) { splitted.push_back(*itr); } } } return splitted; } } // end of anonymous namespace /** 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 { public: /// The template type of class that is being wrapped using element_type = T; /// Simple constructor WrappedObject() : T() { m_pPtr = static_cast<T *>(this); } /** Constructor with a class to wrap * @param F :: The object to wrap * * Note that this constructor can hide the copy constructor because it takes * precedence over the copy constructor if supplied with a non-const * WrappedObject argument. However, it just calls the base class copy * constructor and sets m_pPtr, so the behaviour is the same as the copy * constructor. */ template <typename Field> explicit WrappedObject(Field &F) : T(F) { m_pPtr = static_cast<T *>(this); } /// Overloaded * operator returns the wrapped object pointer const T &operator*() const { return *m_pPtr; } /// Overloaded * operator returns the wrapped object pointer T &operator*() { return m_pPtr; } /// Overloaded -> operator returns the wrapped object pointer const T *operator->() const { return m_pPtr; } /// Overloaded -> operator returns the wrapped object pointer T *operator->() { return m_pPtr; } private: /// Private pointer to the wrapped class T *m_pPtr; }; // Back to the ConfigService class itself... //------------------------------- // Private member functions //------------------------------- /// Private constructor for singleton class ConfigServiceImpl::ConfigServiceImpl() : m_pConf(nullptr), m_pSysConfig(nullptr), 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 file system 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_proxyInfo(), m_isProxySet(false) { // getting at system details m_pSysConfig = new WrappedObject<Poco::Util::SystemConfiguration>; m_pConf = nullptr; // Register StdChannel with Poco Poco::LoggingFactory::defaultFactory().registerChannelClass( "StdoutChannel", new Poco::Instantiator<Poco::StdoutChannel, Poco::Channel>); setBaseDirectory(); // Fill the list of possible relative path keys that may require conversion to // absolute paths m_ConfigPaths.emplace("mantidqt.python_interfaces_directory", true); m_ConfigPaths.emplace("framework.plugins.directory", true); m_ConfigPaths.emplace("pvplugins.directory", false); m_ConfigPaths.emplace("mantidqt.plugins.directory", false); m_ConfigPaths.emplace("instrumentDefinition.directory", true); m_ConfigPaths.emplace("instrumentDefinition.vtpDirectory", true); m_ConfigPaths.emplace("groupingFiles.directory", true); m_ConfigPaths.emplace("maskFiles.directory", true); m_ConfigPaths.emplace("colormaps.directory", true); m_ConfigPaths.emplace("requiredpythonscript.directories", true); m_ConfigPaths.emplace("pythonscripts.directory", true); m_ConfigPaths.emplace("pythonscripts.directories", true); m_ConfigPaths.emplace("python.plugins.directories", true); m_ConfigPaths.emplace("user.python.plugins.directories", true); m_ConfigPaths.emplace("datasearch.directories", true); m_ConfigPaths.emplace("icatDownload.directory", true); // attempt to load the default properties file that resides in the directory // of the executable std::string propertiesFilesList; updateConfig(getPropertiesDir() + m_properties_file_name, false, false); propertiesFilesList = getPropertiesDir() + m_properties_file_name; // Load the local (machine) properties file, if it exists Poco::File localFile(getLocalFilename()); if (localFile.exists()) { updateConfig(getLocalFilename(), true, false); propertiesFilesList += ", " + getLocalFilename(); } if (Poco::Environment::has("MANTIDPROPERTIES")) { // and then append the user properties updateConfig(getUserFilename(), true, false); propertiesFilesList += ", " + getUserFilename(); // and the extra one from the environment updateConfig(Poco::Environment::get("MANTIDPROPERTIES"), true, true); propertiesFilesList += ", " + Poco::Environment::get("MANTIDPROPERTIES"); } else { // Just do the user properties updateConfig(getUserFilename(), true, true); propertiesFilesList += ", " + getUserFilename(); } g_log.debug() << "ConfigService created.\n"; g_log.debug() << "Configured Mantid.properties directory of application as " << getPropertiesDir() << '\n'; g_log.information() << "This is Mantid version " << MantidVersion::version() << " revision " << MantidVersion::revision() << '\n'; g_log.information() << "running on " << getComputerName() << " starting " << Types::Core::DateAndTime::getCurrentTime() .toFormattedString("%Y-%m-%dT%H:%MZ") << "\n"; g_log.information() << "Properties file(s) loaded: " << propertiesFilesList << '\n'; // 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 - but will // throw an error if it cannot create the directory try { file.createDirectories(); } catch (Poco::FileException &fe) { g_log.error() << "Cannot create the local instrument cache directory [" << path.toString() << "]. Mantid will not be able to update instrument definitions.\n" << fe.what() << '\n'; } Poco::File vtpDir(getVTPFileDirectory()); try { vtpDir.createDirectories(); } catch (Poco::FileException &fe) { g_log.error() << "Cannot create the local instrument geometry cache directory [" << path.toString() << "]. Mantid will be slower at viewing complex instruments.\n" << fe.what() << '\n'; } // must update the cache of instrument paths cacheInstrumentPaths(); // update the facilities AFTER we have ensured that all of the directories are // created and the paths updated // if we don't do that first the function below will silently fail without // initialising the facilities vector // and Mantid will crash when it tries to access them, for example when // creating the first time startup screen updateFacilities(); } /** Private Destructor * Prevents client from calling 'delete' on the pointer handed out by Instance */ ConfigServiceImpl::~ConfigServiceImpl() { // std::cerr << "ConfigService destroyed.\n"; Kernel::Logger::shutdown(); delete m_pSysConfig; delete m_pConf; // potential double delete??? clearFacilities(); } /** * Set the base directory path so we can file the Mantid.properties file. * * This will search for the base directory that contains the .properties file * by checking the following places: * - The current working directory * - The executable directory * - The directory defined by the MANTIDPATH enviroment var * - OSX only: the directory two directories up from the executable (which * is the base on the OSX package. * */ void ConfigServiceImpl::setBaseDirectory() { // Define the directory to search for the Mantid.properties file. 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()) return; // Check the executable directory to see if it includes a mantid.properties // file m_strBaseDir = getDirectoryOfExecutable(); f = Poco::File(m_strBaseDir + m_properties_file_name); if (f.exists()) return; // Check 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. m_strBaseDir = Poco::Environment::get("MANTIDPATH") + "/"; f = Poco::File(m_strBaseDir + m_properties_file_name); if (f.exists()) return; } #ifdef __APPLE__ // Finally, on OSX check if we're in the package directory and the .properties // file just happens to be two directories up auto path = Poco::Path(getDirectoryOfExecutable()); m_strBaseDir = path.parent().parent().parent().toString(); #endif } namespace { // look for specific keys and throw an exception if one is found std::string checkForBadConfigOptions(const std::string &filename, const std::string &propertiesString) { std::stringstream stream(propertiesString); std::stringstream resultPropertiesString; std::string line; int line_num = 0; while (std::getline(stream, line)) { line_num += 1; // increment early bool is_ok = true; // Check for common errors. Empty lines are ok, things that are a key // without a value are a critical failure. Forbidden keys are just commented // out. if (line.empty() || (Kernel::Strings::strip(line)[0] == '#')) { // do nothing } else if (line.find("FilterChannel") != std::string::npos) { is_ok = false; } // Print warning to error channel and comment out offending line if (!is_ok) { const auto end = line.find("="); std::cerr << "Encontered invalid key \""; if (end != std::string::npos) { std::cerr << Kernel::Strings::strip(line.substr(0, end)); } else { std::cerr << Kernel::Strings::strip(line); } std::cerr << "\" in " << filename << " on line " << line_num << std::endl; // comment out the property resultPropertiesString << '#'; } // copy over the line resultPropertiesString << line << '\n'; } return resultPropertiesString.str(); } } // end of anonymous namespace /** Loads the config file provided. * If the file contains logging setup instructions then these will be used to *setup the logging framework. * * @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. */ void ConfigServiceImpl::loadConfig(const std::string &filename, const bool append) { delete m_pConf; if (!append) { // remove the previous property string m_PropertyString = ""; m_changed_keys.clear(); } try { // slurp in entire file std::string temp; bool good = readFile(filename, temp); // check if we have failed to open the file if ((!good) || (temp.empty())) { if (filename == getUserPropertiesDir() + m_user_properties_file_name) { // write out a fresh file createUserPropertiesFile(); } else { throw Exception::FileError("Cannot open file", filename); } } // verify the contents and comment out offending lines temp = checkForBadConfigOptions(filename, temp); // store the property string if ((append) && (!m_PropertyString.empty())) { m_PropertyString = m_PropertyString + "\n" + temp; } else { m_PropertyString = temp; } } catch (std::exception &e) { // there was a problem loading the file - it probably is not there std::cerr << "Problem loading the configuration file " << filename << " " << e.what() << '\n'; std::cerr << "Mantid is unable to start.\n" << std::endl; throw; } // use the cached property string to initialise the POCO property file std::istringstream istr(m_PropertyString); m_pConf = new WrappedObject<Poco::Util::PropertyFileConfiguration>(istr); } /** * Read a file and place its contents into the given string * @param filename :: The filename of the file to read * @param contents :: The file contents will be placed here * @returns A boolean indicating whether opening the file was successful */ bool ConfigServiceImpl::readFile(const std::string &filename, std::string &contents) const { std::ifstream propFile(filename.c_str(), std::ios::in); bool good = propFile.good(); if (!good) { contents = ""; propFile.close(); return good; } // slurp in entire file - extremely unlikely delimiter used as an alternate to // \n contents.clear(); getline(propFile, contents, '`'); propFile.close(); return good; } /** Configures the Poco logging and starts it up * */ void ConfigServiceImpl::configureLogging() { try { // Configure the logging framework Poco::Util::LoggingConfigurator configurator; configurator.configure(m_pConf); } catch (std::exception &e) { std::cerr << "Trouble configuring the logging framework " << e.what() << '\n'; } } /** * 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 */ void ConfigServiceImpl::convertRelativeToAbsolute() { if (m_ConfigPaths.empty()) return; 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) { std::string key = sitr->first; if (!m_pConf->hasProperty(key)) continue; std::string value(m_pConf->getString(key)); value = makeAbsolute(value, key); m_AbsolutePaths.emplace(key, value); } } /** * Make a relative path or a list of relative paths into an absolute one. * @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 */ 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; } std::string converted; // If we have a list, chop it up and convert each one if (dir.find_first_of(";,") != std::string::npos) { auto splitted = splitPath(dir); auto iend = splitted.cend(); for (auto itr = splitted.begin(); itr != iend;) { std::string absolute = makeAbsolute(*itr, key); if (absolute.empty()) { ++itr; } else { converted += absolute; if (++itr != iend) { converted += ";"; } } } return converted; } // 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 // and Poco::Path::isRelative throws an exception on this bool is_relative(false); try { is_relative = Poco::Path(dir).isRelative(); } catch (Poco::PathSyntaxException &) { g_log.warning() << "Malformed path detected in the \"" << key << "\" variable, skipping \"" << dir << "\"\n"; return ""; } if (is_relative) { const std::string propFileDir(getPropertiesDir()); converted = Poco::Path(propFileDir).resolve(dir).toString(); } else { converted = dir; } converted = Poco::Path(converted).makeDirectory().toString(); // C++ doesn't have a const version of operator[] for maps so I can't call // that here auto it = m_ConfigPaths.find(key); bool required = false; if (it != m_ConfigPaths.end()) { 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"; converted = ""; } } catch (Poco::FileException &) { g_log.debug() << "Required properties path \"" << converted << "\" in the \"" << key << "\" variable does not exist.\n"; converted = ""; } // Backward slashes cannot be allowed to go into our properties file // Note this is a temporary fix for ticket #2445. // Ticket #2460 prompts a review of our path handling in the config service. boost::replace_all(converted, "\\", "/"); return converted; } /** * Create the store of data search paths from the 'datasearch.directories' key * within the Mantid.properties file. * The value of the key should be a semi-colon separated list of directories */ void ConfigServiceImpl::cacheDataSearchPaths() { std::string paths = getString("datasearch.directories"); if (paths.empty()) { m_DataSearchDirs.clear(); } else { m_DataSearchDirs = splitPath(paths); } } /** * 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()) { m_UserSearchDirs.clear(); } else { m_UserSearchDirs = splitPath(paths); } } /** * The path that is passed should be as returned by makeAbsolute() and * this function will return true if that path is in the list * @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(), '\\', '/'); auto it = std::find_if(m_DataSearchDirs.cbegin(), m_DataSearchDirs.cend(), std::bind2nd(std::equal_to<std::string>(), correctedPath)); return (it != m_DataSearchDirs.end()); } /** * 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(), std::fstream::out); filestr << "# This file can be used to override any properties for this " "installation.\n"; filestr << "# Any properties found in this file will override any that are " "found in the Mantid.Properties file\n"; filestr << "# As this file will not be replaced with further installations " "of Mantid it is a safe place to put \n"; filestr << "# properties that suit your particular installation.\n"; filestr << "#\n"; filestr << "# See here for a list of possible options:\n"; filestr << "# " "http://docs.mantidproject.org/nightly/concepts/PropertiesFile.html" "\n\n"; filestr << "##\n"; filestr << "## GENERAL\n"; filestr << "##\n\n"; filestr << "## Set the number of algorithm properties to retain\n"; filestr << "#algorithms.retained=90\n\n"; filestr << "## Set the maximum number of cores used to run algorithms over\n"; filestr << "#MultiThreaded.MaxCores=4\n\n"; filestr << "##\n"; filestr << "## FACILITY AND INSTRUMENT\n"; filestr << "##\n\n"; filestr << "## Sets the default facility\n"; filestr << "## e.g.: ISIS, SNS, ILL\n"; filestr << "default.facility=\n\n"; filestr << "## Sets the default instrument\n"; filestr << "## e.g. IRIS, HET, NIMROD\n"; filestr << "default.instrument=\n\n"; filestr << '\n'; filestr << "## Sets the Q.convention\n"; filestr << "## Set to Crystallography for kf-ki instead of default " "Inelastic which is ki-kf\n"; filestr << "#Q.convention=Crystallography\n"; filestr << "##\n"; filestr << "## DIRECTORIES\n"; filestr << "##\n\n"; filestr << "## Sets a list of directories (separated by semi colons) to " "search for data\n"; filestr << "#datasearch.directories=../data;../isis/data\n\n"; filestr << "## Set a list (separated by semi colons) of directories to " "look for additional Python scripts\n"; filestr << "#pythonscripts.directories=../scripts;../docs/MyScripts\n\n"; filestr << "## Uncomment to enable archive search - ICat and Orbiter\n"; filestr << "#datasearch.searcharchive=On\n\n"; filestr << "## Sets default save directory\n"; filestr << "#defaultsave.directory=../data\n\n"; filestr << "##\n"; filestr << "## LOGGING\n"; filestr << "##\n\n"; filestr << "## Uncomment to change logging level\n"; filestr << "## Default is information\n"; filestr << "## Valid values are: error, warning, notice, information, debug\n"; filestr << "#logging.loggers.root.level=information\n\n"; filestr << "##\n"; filestr << "## MantidPlot\n"; filestr << "##\n\n"; filestr << "## Hides categories from the algorithm list in MantidPlot\n"; filestr << "#algorithms.catagories.hidden=Muons,Inelastic\n\n"; filestr << "## Show invisible workspaces\n"; filestr << "#MantidOptions.InvisibleWorkspaces=0\n"; filestr << "## Re-use plot instances for different plot types\n"; filestr << "#MantidOptions.ReusePlotInstances=Off\n\n"; filestr << "## Uncomment to disable use of OpenGL to render unwrapped " "instrument views\n"; filestr << "#MantidOptions.InstrumentView.UseOpenGL=Off\n"; filestr.close(); } catch (std::runtime_error &ex) { g_log.warning() << "Unable to write out user.properties file to " << getUserPropertiesDir() << m_user_properties_file_name << " error: " << ex.what() << '\n'; } } //------------------------------- // Public member functions //------------------------------- /** * Removes the user properties file & loads a fresh configuration */ void ConfigServiceImpl::reset() { // Remove the current user properties file and write a fresh one try { Poco::File userFile(getUserFilename()); userFile.remove(); } catch (Poco::Exception &) { } createUserPropertiesFile(); // Now load the original const bool append = false; const bool updateCaches = true; updateConfig(getPropertiesDir() + m_properties_file_name, append, updateCaches); } /** Updates and existing configuration and restarts the logging * @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 */ void ConfigServiceImpl::updateConfig(const std::string &filename, const bool append, const bool update_caches) { 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()); } */ if (update_caches) { // Only configure logging once configureLogging(); // Ensure that any relative paths given in the configuration file are // relative to the correct directory convertRelativeToAbsolute(); // Configure search paths into a specially saved store as they will be used // frequently cacheDataSearchPaths(); appendDataSearchDir(getString("defaultsave.directory")); cacheUserSearchPaths(); cacheInstrumentPaths(); } } /** * Save the configuration to the user file * @param filename :: The filename for the saved configuration * @throw std::runtime_error if the file cannot be opened */ void ConfigServiceImpl::saveConfig(const std::string &filename) const { // 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."); } std::string file_line, output; bool line_continuing(false); while (std::getline(reader, file_line)) { if (!file_line.empty()) { char last = *(file_line.end() - 1); if (last == '\\') { // If we are not in line continuation mode then need // a fresh start line if (!line_continuing) output = ""; line_continuing = true; output += file_line + "\n"; continue; } else if (line_continuing) { output += file_line; line_continuing = false; } else { output = file_line; } } else { output = ""; updated_file += "\n"; continue; } // end if-else // Output is the current line in the file // 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) Poco::trimInPlace(key); // Find the comments 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; } else { // 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.append(key).append("=").append(value); // Remove the key from the changed key list m_changed_keys.erase(key); } updated_file += "\n"; } // 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()) { updated_file += "\n"; auto key_end = m_changed_keys.end(); for (auto key_itr = m_changed_keys.begin(); key_itr != key_end;) { updated_file += *key_itr + "="; std::string value = getString(*key_itr, false); Poco::replaceInPlace(value, "\\", "\\\\"); // replace single \ with double updated_file += value; if (++key_itr != key_end) { updated_file += "\n"; } } m_changed_keys.clear(); } // Write out the new file std::ofstream writer(filename.c_str(), std::ios_base::trunc); if (writer.bad()) { 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."); } 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 * 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 */ std::string ConfigServiceImpl::getString(const std::string &keyName, bool use_cache) const { if (use_cache) { auto mitr = m_AbsolutePaths.find(keyName); if (mitr != m_AbsolutePaths.end()) { return (*mitr).second; } } std::string retVal; try { retVal = m_pConf->getString(keyName); } catch (Poco::NotFoundException &) { g_log.debug() << "Unable to find " << keyName << " in the properties file" << '\n'; retVal = ""; } return retVal; } /** Searches for keys within the currently loaded configuaration values and * returns them as strings in a vector. * * @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 * a key or it couldn't be found. */ std::vector<std::string> ConfigServiceImpl::getKeys(const std::string &keyName) const { std::vector<std::string> rawKeys; m_pConf->keys(keyName, rawKeys); return rawKeys; } /** * Recursively gets a list of all config options from a given root node. * * @return Vector containing all config options */ void ConfigServiceImpl::getKeysRecursive( const std::string &root, std::vector<std::string> &allKeys) const { std::vector<std::string> rootKeys = getKeys(root); if (rootKeys.empty()) allKeys.push_back(root); for (auto &rootKey : rootKeys) { std::string searchString; if (root.empty()) { searchString.append(rootKey); } else { searchString.append(root).append(".").append(rootKey); } getKeysRecursive(searchString, allKeys); } } /** * Recursively gets a list of all config options. * * This function is needed as Boost Python does not like calling function with * default arguments. * * @return Vector containing all config options */ std::vector<std::string> ConfigServiceImpl::keys() const { std::vector<std::string> allKeys; getKeysRecursive("", allKeys); return allKeys; } /** Removes a key from the memory stored properties file and inserts the key *into the * changed key list so that when the program calls saveConfig the properties *file will * be the same and not contain the key no more * * @param rootName :: The key that is to be deleted */ void ConfigServiceImpl::remove(const std::string &rootName) const { m_pConf->remove(rootName); m_changed_keys.insert(rootName); } /** Checks to see whether the given key exists. * * @param rootName :: The case sensitive key that you are looking to see if *exists. * @returns Boolean value denoting whether the exists or not. */ bool ConfigServiceImpl::hasProperty(const std::string &rootName) const { return m_pConf->hasProperty(rootName); } /** Checks to see whether the given file target is an executable one and it *exists. * This method will expand environment variables found in the given file path. * * @param target :: The path to the file you wish to see whether it's an *executable. * @returns Boolean value denoting whether the file is an executable or not. */ bool ConfigServiceImpl::isExecutable(const std::string &target) const { try { std::string expTarget = Poco::Path::expand(target); Poco::File tempFile = Poco::File(expTarget); if (tempFile.exists()) { return tempFile.canExecute(); } else return false; } catch (Poco::Exception &) { return false; } } /** Runs a command line string to open a program. The function can take program *arguments. * i.e it can load in a file to the program on startup. * * This method will expand environment variables found in the given file path. * * @param programFilePath :: The directory where the program is located. * @param programArguments :: The arguments that the program can take on *startup. For example, * the file to load up. */ void ConfigServiceImpl::launchProcess( const std::string &programFilePath, const std::vector<std::string> &programArguments) const { try { std::string expTarget = Poco::Path::expand(programFilePath); Poco::Process::launch(expTarget, programArguments); } catch (Poco::SystemException &e) { throw std::runtime_error(e.what()); } } /** * Set a configuration property. An existing key will have its value updated. * @param key :: The key to refer to this property * @param value :: The value of the property */ void ConfigServiceImpl::setString(const std::string &key, const std::string &value) { // If the value is unchanged (after any path conversions), there's nothing to // do. const std::string old = getString(key); if (value == old) return; // Ensure we keep a correct full path std::map<std::string, bool>::const_iterator itr = m_ConfigPaths.find(key); if (itr != m_ConfigPaths.end()) { m_AbsolutePaths[key] = makeAbsolute(value, key); } if (key == "datasearch.directories") { cacheDataSearchPaths(); } else if (key == "usersearch.directories") { cacheUserSearchPaths(); } else if (key == "instrumentDefinition.directory") { cacheInstrumentPaths(); } else if (key == "defaultsave.directory") { appendDataSearchDir(value); } m_pConf->setString(key, value); m_notificationCenter.postNotification(new ValueChanged(key, value, old)); m_changed_keys.insert(key); } /** Searches for a string within the currently loaded configuaration values and * attempts to convert the values to the template type supplied. * * @param keyName :: The case sensitive name of the property that you need the *value of. * @param out :: The value if found * @returns A success flag - 0 on failure, 1 on success */ template <typename T> int ConfigServiceImpl::getValue(const std::string &keyName, T &out) { std::string strValue = getString(keyName); int result = Mantid::Kernel::Strings::convert(strValue, out); return result; } /** * Return the full filename of the local properties file. * @returns A string containing the full path to the local file. */ std::string ConfigServiceImpl::getLocalFilename() const { #ifdef _WIN32 return "Mantid.local.properties"; #else return "/etc/mantid.local.properties"; #endif } /** * Return the full filename of the user properties file * @returns A string containing the full path to the user file */ std::string ConfigServiceImpl::getUserFilename() const { return getUserPropertiesDir() + m_user_properties_file_name; } /** Searches for the string within the environment variables and returns the * value as a string. * * @param keyName :: The name of the environment variable that you need the *value of. * @returns The string value of the property */ std::string ConfigServiceImpl::getEnvironment(const std::string &keyName) { return m_pSysConfig->getString("system.env." + keyName); } /** Gets the name of the host operating system * * @returns The name pf the OS version */ std::string ConfigServiceImpl::getOSName() { return m_pSysConfig->getString("system.osName"); } /** Gets the name of the computer running Mantid * * @returns The name of the computer */ std::string ConfigServiceImpl::getOSArchitecture() { return m_pSysConfig->getString("system.osArchitecture"); } /** Gets the name of the operating system Architecture * * @returns The operating system architecture */ std::string ConfigServiceImpl::getComputerName() { return m_pSysConfig->getString("system.nodeName"); } /** Gets the name of the operating system version * * @returns The operating system version */ std::string ConfigServiceImpl::getOSVersion() { return m_pSysConfig->getString("system.osVersion"); } /// @returns true if the file exists and can be read bool canRead(const std::string &filename) { // check for existence of the file Poco::File pocoFile(filename); if (!pocoFile.exists()) { return false; } // just return if it is readable return pocoFile.canRead(); } /// @returns the value associated with the key. std::string getValueFromStdOut(const std::string &orig, const std::string &key) { size_t start = orig.find(key); if (start == std::string::npos) { return std::string(); } start += key.size(); size_t stop = orig.find('\n', start); if (stop == std::string::npos) { return std::string(); } return Mantid::Kernel::Strings::strip(orig.substr(start, stop - start - 1)); } /** * Gets the name of the operating system version in a human readable form. * * @returns The operating system desciption */ std::string ConfigServiceImpl::getOSVersionReadable() { std::string description; // read os-release static const std::string OS_RELEASE("/etc/os-release"); if (canRead(OS_RELEASE)) { static const std::string PRETTY_NAME("PRETTY_NAME="); // open it to see if it has the magic line std::ifstream handle(OS_RELEASE.c_str(), std::ios::in); // go through the file std::string line; while (std::getline(handle, line)) { if (line.find(PRETTY_NAME) != std::string::npos) { if (line.length() > PRETTY_NAME.length() + 1) { size_t length = line.length() - PRETTY_NAME.length() - 2; description = line.substr(PRETTY_NAME.length() + 1, length); } break; } } // cleanup handle.close(); if (!description.empty()) { return description; } } // read redhat-release static const std::string REDHAT_RELEASE("/etc/redhat-release"); if (canRead(REDHAT_RELEASE)) { // open it to see if it has the magic line std::ifstream handle(REDHAT_RELEASE.c_str(), std::ios::in); // go through the file std::string line; while (std::getline(handle, line)) { if (!line.empty()) { description = line; break; } } // cleanup handle.close(); if (!description.empty()) { return description; } } // try system calls std::string cmd; std::vector<std::string> args; #ifdef __APPLE__ cmd = "sw_vers"; // mac #elif _WIN32 cmd = "wmic"; // windows args.emplace_back("os"); // windows args.emplace_back("get"); // windows args.emplace_back("Caption"); // windows args.emplace_back("/value"); // windows #endif if (!cmd.empty()) { try { Poco::Pipe outPipe, errorPipe; Poco::ProcessHandle ph = Poco::Process::launch(cmd, args, nullptr, &outPipe, &errorPipe); const int rc = ph.wait(); // Only if the command returned successfully. if (rc == 0) { Poco::PipeInputStream pipeStream(outPipe); std::stringstream stringStream; Poco::StreamCopier::copyStream(pipeStream, stringStream); const std::string result = stringStream.str(); #ifdef __APPLE__ const std::string product_name = getValueFromStdOut(result, "ProductName:"); const std::string product_vers = getValueFromStdOut(result, "ProductVersion:"); description = product_name + " " + product_vers; #elif _WIN32 description = getValueFromStdOut(result, "Caption="); #else UNUSED_ARG(result); // only used on mac and windows #endif } else { std::stringstream messageStream; messageStream << "command \"" << cmd << "\" failed with code: " << rc; g_log.debug(messageStream.str()); } } catch (Poco::SystemException &e) { g_log.debug("command \"" + cmd + "\" failed"); g_log.debug(e.what()); } } return description; } /// @returns The name of the current user as reported by the environment. std::string ConfigServiceImpl::getUsername() { std::string username; // mac and favorite way to get username on linux try { username = m_pSysConfig->getString("system.env.USER"); if (!username.empty()) { return username; } } catch (Poco::NotFoundException &e) { UNUSED_ARG(e); // let it drop on the floor } // windoze and alternate linux username variable try { username = m_pSysConfig->getString("system.env.USERNAME"); if (!username.empty()) { return username; } } catch (Poco::NotFoundException &e) { UNUSED_ARG(e); // let it drop on the floor } // give up and return an empty string return std::string(); } /** Gets the absolute path of the current directory containing the dll * * @returns The absolute path of the current directory containing the dll */ std::string ConfigServiceImpl::getCurrentDir() { return m_pSysConfig->getString("system.currentDir"); } /** Gets the absolute path of the current directory containing the dll. Const *version. * * @returns The absolute path of the current directory containing the dll */ std::string ConfigServiceImpl::getCurrentDir() const { return m_pSysConfig->getString("system.currentDir"); } /** Gets the absolute path of the temp directory * * @returns The absolute path of the temp directory */ std::string ConfigServiceImpl::getTempDir() { return m_pSysConfig->getString("system.tempDir"); } /** Gets the absolute path of the appdata directory * * @returns The absolute path of the appdata directory */ std::string ConfigServiceImpl::getAppDataDir() { const std::string applicationName = "mantid"; #if POCO_OS == POCO_OS_WINDOWS_NT const std::string vendorName = "mantidproject"; std::string appdata = std::getenv("APPDATA"); Poco::Path path(appdata); path.makeDirectory(); path.pushDirectory(vendorName); path.pushDirectory(applicationName); return path.toString(); #else // linux and mac Poco::Path path(Poco::Path::home()); path.pushDirectory("." + applicationName); return path.toString(); #endif } /** * Get the directory containing the program executable * @returns A string containing the path of the directory * containing the executable, including a trailing slash */ std::string ConfigServiceImpl::getDirectoryOfExecutable() const { return Poco::Path(getPathToExecutable()).parent().toString(); } /** * Get the full path to the executing program (i.e. whatever Mantid is embedded * in) * @returns A string containing the full path the the executable */ std::string ConfigServiceImpl::getPathToExecutable() const { std::string execpath; const size_t LEN(1024); // cppcheck-suppress variableScope char pBuf[LEN]; #ifdef _WIN32 unsigned int bytes = GetModuleFileName(NULL, pBuf, LEN); #elif defined __linux__ char szTmp[32]; sprintf(szTmp, "/proc/%d/exe", getpid()); ssize_t bytes = readlink(szTmp, pBuf, LEN); #elif defined __APPLE__ // Two calls to _NSGetExecutablePath required - first to get size of buffer uint32_t bytes(0); _NSGetExecutablePath(pBuf, &bytes); const int success = _NSGetExecutablePath(pBuf, &bytes); if (success < 0) bytes = 1025; #endif if (bytes > 0 && bytes < 1024) { pBuf[bytes] = '\0'; execpath = std::string(pBuf); } return execpath; } /** * Check if the path is on a network drive * @param path :: The path to be checked * @return True if the path is on a network drive. */ bool ConfigServiceImpl::isNetworkDrive(const std::string &path) { #ifdef _WIN32 // if path is relative get the full one char buff[MAX_PATH]; GetFullPathName(path.c_str(), MAX_PATH, buff, NULL); std::string fullName(buff); size_t i = fullName.find(':'); // if the full path doesn't contain a drive letter assume it's on the network if (i == std::string::npos) return true; fullName.erase(i + 1); fullName += '\\'; // make sure the name has the trailing backslash UINT type = GetDriveType(fullName.c_str()); return DRIVE_REMOTE == type; #elif defined __linux__ // This information is only present in the /proc/mounts file on linux. There // are no drives on // linux only mount locations therefore the test will have to check the path // against // entries in /proc/mounts to see if the filesystem type is NFS or SMB (any // others ????) // Each line corresponds to a particular mounted location // 1st column - device name // 2nd column - mounted location // 3rd column - filesystem type commonly ext2, ext3 for hard drives and NFS or // SMB for // network locations std::ifstream mntfile("/proc/mounts"); std::string txtread(""); while (getline(mntfile, txtread)) { std::istringstream strm(txtread); std::string devname(""), mntpoint(""), fstype(""); strm >> devname >> mntpoint >> fstype; if (!strm) continue; // I can't be sure that the file system type is always lower case std::transform(fstype.begin(), fstype.end(), fstype.begin(), toupper); // Skip the current line if the file system isn't a network one if (fstype != "NFS" && fstype != "SMB") continue; // Now we have a line containing a network filesystem and just need to check // if the path // supplied contains the mount location. There is a small complication in // that the mount // points within the file have certain characters transformed into their // octal // representations, for example spaces->040. std::string::size_type idx = mntpoint.find("\\0"); if (idx != std::string::npos) { std::string oct = mntpoint.substr(idx + 1, 3); strm.str(oct); int printch(-1); strm.setf(std::ios::oct, std::ios::basefield); strm >> printch; if (printch != -1) { mntpoint = mntpoint.substr(0, idx) + static_cast<char>(printch) + mntpoint.substr(idx + 4); } // Search for this at the start of the path if (path.find(mntpoint) == 0) return true; } } return false; #else UNUSED_ARG(path); // Not yet implemented for the mac return false; #endif } /** * Gets the directory that we consider to be the directory containing the * Mantid.properties file. * Basically, this is the either the directory pointed to by MANTIDPATH or the * directory of the current * executable if this is not set. * @returns The directory to consider as the base directory, including a * trailing slash */ std::string ConfigServiceImpl::getPropertiesDir() const { return m_strBaseDir; } /** * Return the directory that Mantid should use for writing any files it needs so * that * this is kept separated to user saved files. A trailing slash is appended * so that filenames can more easily be concatenated with this * @return the directory that Mantid should use for writing files */ std::string ConfigServiceImpl::getUserPropertiesDir() const { #ifdef _WIN32 return m_strBaseDir; #else Poco::Path datadir(m_pSysConfig->getString("system.homeDir")); datadir.append(".mantid"); // Create the directory if it doesn't already exist Poco::File(datadir).createDirectory(); return datadir.toString() + "/"; #endif } /** * Return the list of search paths * @returns A vector of strings containing the defined search directories */ const std::vector<std::string> &ConfigServiceImpl::getDataSearchDirs() const { return m_DataSearchDirs; } /** * Set a list of search paths via a vector * @param searchDirs :: A list of search directories */ void ConfigServiceImpl::setDataSearchDirs( const std::vector<std::string> &searchDirs) { std::string searchPaths = boost::join(searchDirs, ";"); setDataSearchDirs(searchPaths); } /** * Set a list of search paths via a string * @param searchDirs :: A string containing a list of search directories * separated by a semi colon (;). */ void ConfigServiceImpl::setDataSearchDirs(const std::string &searchDirs) { setString("datasearch.directories", searchDirs); } /** * Appends the passed subdirectory path to the end of each of data * search dirs and adds these new dirs to data search directories * @param subdir :: the subdirectory path to add (relative) */ void ConfigServiceImpl::appendDataSearchSubDir(const std::string &subdir) { if (subdir.empty()) return; Poco::Path subDirPath; try { subDirPath = Poco::Path(subdir); } catch (Poco::PathSyntaxException &) { return; } if (!subDirPath.isDirectory() || !subDirPath.isRelative()) { return; } auto newDataDirs = m_DataSearchDirs; for (const auto &path : m_DataSearchDirs) { Poco::Path newDirPath; try { newDirPath = Poco::Path(path); newDirPath.append(subDirPath); newDataDirs.push_back(newDirPath.toString()); } catch (Poco::PathSyntaxException &) { continue; } } setDataSearchDirs(newDataDirs); } /** * Adds the passed path to the end of the list of data search paths * the path name must be absolute * @param path :: the absolute path to add */ void ConfigServiceImpl::appendDataSearchDir(const std::string &path) { if (path.empty()) return; Poco::Path dirPath; try { dirPath = Poco::Path(path); dirPath.makeDirectory(); } catch (Poco::PathSyntaxException &) { return; } if (!isInDataSearchList(dirPath.toString())) { std::string newSearchString; std::vector<std::string>::const_iterator it = m_DataSearchDirs.begin(); for (; it != m_DataSearchDirs.end(); ++it) { newSearchString.append(*it); newSearchString.append(";"); } newSearchString.append(path); setString("datasearch.directories", newSearchString); } } /** * Return the list of user search paths * @returns A vector of strings containing the defined search directories */ const std::vector<std::string> &ConfigServiceImpl::getUserSearchDirs() const { return m_UserSearchDirs; } /** * Sets the search directories for XML instrument definition files (IDFs) * @param directories An ordered list of paths for instrument searching */ void ConfigServiceImpl::setInstrumentDirectories( const std::vector<std::string> &directories) { m_InstrumentDirs = directories; } /** * Return the search directories for XML instrument definition files (IDFs) * @returns An ordered list of paths for instrument searching */ const std::vector<std::string> & ConfigServiceImpl::getInstrumentDirectories() const { return m_InstrumentDirs; } /** * Return the base search directories for XML instrument definition files (IDFs) * @returns a last entry of getInstrumentDirectories */ const std::string ConfigServiceImpl::getInstrumentDirectory() const { return m_InstrumentDirs.back(); } /** * Return the search directory for vtp files * @returns a path */ const std::string ConfigServiceImpl::getVTPFileDirectory() { // Determine the search directory for XML instrument definition files (IDFs) std::string directoryName = getString("instrumentDefinition.vtpDirectory"); if (directoryName.empty()) { Poco::Path path(getAppDataDir()); path.makeDirectory(); path.pushDirectory("instrument"); path.pushDirectory("geometryCache"); directoryName = path.toString(); } return directoryName; } /** * Fills the internal cache of instrument definition directories and creates * The %appdata%/mantidproject/mantid or $home/.mantid directory. * * This will normally contain from Index 0 * - The download directory (win %appdata%/mantidproject/mantid/instrument) * (linux $home/.mantid/instrument ) * - The user instrument area /etc/mantid/instrument (not on windows) * - The install directory/instrument */ void ConfigServiceImpl::cacheInstrumentPaths() { m_InstrumentDirs.clear(); Poco::Path path(getAppDataDir()); path.makeDirectory(); path.pushDirectory("instrument"); const std::string appdatadir = path.toString(); addDirectoryifExists(appdatadir, m_InstrumentDirs); #ifndef _WIN32 addDirectoryifExists("/etc/mantid/instrument", m_InstrumentDirs); #endif // Determine the search directory for XML instrument definition files (IDFs) std::string directoryName = getString("instrumentDefinition.directory"); if (directoryName.empty()) { // This is the assumed deployment directory for IDFs, where we need to be // relative to the // directory of the executable, not the current working directory. directoryName = Poco::Path(getPropertiesDir()).resolve("../instrument").toString(); } addDirectoryifExists(directoryName, m_InstrumentDirs); } /** * Verifies the directory exists and add it to the back of the directory list if * valid * @param directoryName the directory name to add * @param directoryList the list to add the directory to * @returns true if the directory was valid and added to the list */ bool ConfigServiceImpl::addDirectoryifExists( const std::string &directoryName, std::vector<std::string> &directoryList) { try { if (Poco::File(directoryName).isDirectory()) { directoryList.push_back(directoryName); return true; } else { g_log.information("Unable to locate directory at: " + directoryName); return false; } } catch (Poco::PathNotFoundException &) { g_log.information("Unable to locate directory at: " + directoryName); return false; } catch (Poco::FileNotFoundException &) { g_log.information("Unable to locate directory at: " + directoryName); return false; } } std::string ConfigServiceImpl::getFacilityFilename(const std::string &fName) { // first try the supplied file if (!fName.empty()) { const Poco::File fileObj(fName); if (fileObj.exists()) { return fName; } } // search all of the instrument directories const std::vector<std::string> directoryNames = getInstrumentDirectories(); // only use downloaded instruments if configured to download const std::string updateInstrStr = this->getString("UpdateInstrumentDefinitions.OnStartup"); auto instrDir = directoryNames.begin(); // If we are not updating the instrument definitions // update the iterator, this means we will skip the folder in HOME and // look in the instrument folder in mantid install directory or mantid source // code directory if (!(updateInstrStr == "1" || updateInstrStr == "on" || updateInstrStr == "On")) { instrDir++; } // look through all the possible files for (; instrDir != directoryNames.end(); ++instrDir) { Poco::Path p(*instrDir); p.append("Facilities.xml"); std::string filename = p.toString(); Poco::File fileObj(filename); // stop when you find the first one if (fileObj.exists()) return filename; } // getting this far means the file was not found std::string directoryNamesList = boost::algorithm::join(directoryNames, ", "); throw std::runtime_error("Failed to find \"Facilities.xml\". Searched in " + directoryNamesList); } /** * Load facility information from instrumentDir/Facilities.xml file if fName * parameter is not set. * * If any of the steps fail, we cannot sensibly recover, because the * Facilities.xml file is missing or corrupted. * * @param fName :: An alternative file name for loading facilities information. * @throws std::runtime_error :: If the file is not found or fails to parse */ void ConfigServiceImpl::updateFacilities(const std::string &fName) { clearFacilities(); // Try to find the file. If it does not exist we will crash, and cannot read // the Facilities file std::string fileName = getFacilityFilename(fName); // Set up the DOM parser and parse xml file Poco::AutoPtr<Poco::XML::Document> pDoc; try { Poco::XML::DOMParser pParser; pDoc = pParser.parse(fileName); } catch (...) { throw Kernel::Exception::FileError("Unable to parse file:", fileName); } // Get pointer to root element Poco::XML::Element *pRootElem = pDoc->documentElement(); if (!pRootElem->hasChildNodes()) { throw std::runtime_error("No root element in Facilities.xml file"); } Poco::AutoPtr<Poco::XML::NodeList> pNL_facility = pRootElem->getElementsByTagName("facility"); size_t n = pNL_facility->length(); for (unsigned long i = 0; i < n; ++i) { Poco::XML::Element *elem = dynamic_cast<Poco::XML::Element *>(pNL_facility->item(i)); if (elem) { m_facilities.push_back(new FacilityInfo(elem)); } } if (m_facilities.empty()) { throw std::runtime_error("The facility definition file " + fileName + " defines no facilities"); } } /// Empty the list of facilities, deleting the FacilityInfo objects in the /// process void ConfigServiceImpl::clearFacilities() { for (auto &facility : m_facilities) { delete facility; } m_facilities.clear(); } /** * Returns instruments with given name * @param instrumentName Instrument name * @return the instrument information object * @throw NotFoundError if iName was not found */ const InstrumentInfo & ConfigServiceImpl::getInstrument(const std::string &instrumentName) const { // Let's first search for the instrument in our default facility std::string defaultFacility = ConfigService::Instance().getFacility().name(); if (!defaultFacility.empty()) { try { g_log.debug() << "Looking for " << instrumentName << " at " << defaultFacility << ".\n"; return getFacility(defaultFacility).instrument(instrumentName); } catch (Exception::NotFoundError &) { // Well the instName doesn't exist for this facility // Move along, there's nothing to see here... } } // Now let's look through the other facilities for (auto facility : m_facilities) { try { g_log.debug() << "Looking for " << instrumentName << " at " << (*facility).name() << ".\n"; return (*facility).instrument(instrumentName); } catch (Exception::NotFoundError &) { // Well the instName doesn't exist for this facility... // Move along, there's nothing to see here... } } const std::string errMsg = "Failed to find an instrument with this name in any facility: '" + instrumentName + "' -"; g_log.debug("Instrument " + instrumentName + " not found"); throw Exception::NotFoundError(errMsg, instrumentName); } /** Gets a vector of the facility Information objects * @return A vector of FacilityInfo objects */ const std::vector<FacilityInfo *> ConfigServiceImpl::getFacilities() const { return m_facilities; } /** Gets a vector of the facility names * @return A vector of the facility Names */ const std::vector<std::string> ConfigServiceImpl::getFacilityNames() const { auto names = std::vector<std::string>(m_facilities.size()); auto itFacilities = m_facilities.begin(); auto itNames = names.begin(); for (; itFacilities != m_facilities.end(); ++itFacilities, ++itNames) { *itNames = (**itFacilities).name(); } return names; } /** Get the default facility * @return the facility information object */ const FacilityInfo &ConfigServiceImpl::getFacility() const { std::string defFacility = getString("default.facility"); if (defFacility.empty()) { defFacility = "ISIS"; } return this->getFacility(defFacility); } /** * Get a facility * @param facilityName :: Facility name * @return the facility information object * @throw NotFoundException if the facility is not found */ const FacilityInfo & ConfigServiceImpl::getFacility(const std::string &facilityName) const { if (facilityName.empty()) return this->getFacility(); for (auto facility : m_facilities) { if ((*facility).name() == facilityName) { return *facility; } } throw Exception::NotFoundError("Facilities", facilityName); } /** * Set the default facility * @param facilityName the facility name * @throw NotFoundException if the facility is not found */ void ConfigServiceImpl::setFacility(const std::string &facilityName) { bool found = false; // Look through the facilities for a matching one. std::vector<FacilityInfo *>::const_iterator it = m_facilities.begin(); for (; it != m_facilities.end(); ++it) { if ((**it).name() == facilityName) { // Found the facility found = true; // So it's safe to set it as our default setString("default.facility", facilityName); } } if (!found) { g_log.error("Failed to set default facility to be " + facilityName + ". Facility not found"); throw Exception::NotFoundError("Facilities", facilityName); } } /** Add an observer to a notification @param observer :: Reference to the observer to add */ void ConfigServiceImpl::addObserver( const Poco::AbstractObserver &observer) const { m_notificationCenter.addObserver(observer); } /** Remove an observer @param observer :: Reference to the observer to remove */ void ConfigServiceImpl::removeObserver( const Poco::AbstractObserver &observer) const { m_notificationCenter.removeObserver(observer); } /* Gets the system proxy information @url A url to match the proxy to @return the proxy information. */ Kernel::ProxyInfo &ConfigServiceImpl::getProxy(const std::string &url) { if (!m_isProxySet) { // set the proxy // first check if the proxy is defined in the properties file std::string proxyHost; int proxyPort; if ((getValue("proxy.host", proxyHost) == 1) && (getValue("proxy.port", proxyPort) == 1)) { // set it from the config values m_proxyInfo = ProxyInfo(proxyHost, proxyPort, true); } else { // get the system proxy Poco::URI uri(url); Mantid::Kernel::NetworkProxy proxyHelper; m_proxyInfo = proxyHelper.getHttpProxy(uri.toString()); } m_isProxySet = true; } return m_proxyInfo; } /** Sets the log level priority for all logging channels * @param logLevel the integer value of the log level to set, 1=Critical, 7=Debug * @param quiet If true then no message regarding the level change is emitted */ void ConfigServiceImpl::setLogLevel(int logLevel, bool quiet) { Mantid::Kernel::Logger::setLevelForAll(logLevel); if (!quiet) { g_log.log("logging set to " + Logger::PriorityNames[logLevel] + " priority", static_cast<Logger::Priority>(logLevel)); } } /// \cond TEMPLATE template DLLExport int ConfigServiceImpl::getValue(const std::string &, double &); template DLLExport int ConfigServiceImpl::getValue(const std::string &, std::string &); template DLLExport int ConfigServiceImpl::getValue(const std::string &, int &); template DLLExport int ConfigServiceImpl::getValue(const std::string &, std::size_t &); /// \endcond TEMPLATE } // namespace Kernel } // namespace Mantid