Mantid
Loading...
Searching...
No Matches
ConfigService.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7//----------------------------------------------------------------------
8// Includes
9//----------------------------------------------------------------------
10
15#include "MantidKernel/Glob.h"
16#include "MantidKernel/Logger.h"
22#include "MantidKernel/System.h"
23
24#include <Poco/AutoPtr.h>
25#include <Poco/Channel.h>
26#include <Poco/DOM/DOMParser.h>
27#include <Poco/DOM/Document.h>
28#include <Poco/DOM/Element.h>
29#include <Poco/DOM/Node.h>
30#include <Poco/DOM/NodeList.h>
31#include <Poco/Environment.h>
32#include <Poco/Exception.h>
33#include <Poco/File.h>
34#include <Poco/Instantiator.h>
35#include <Poco/Logger.h>
36#include <Poco/LoggingFactory.h>
37#include <Poco/LoggingRegistry.h>
38#include <Poco/Path.h>
39#include <Poco/Pipe.h>
40#include <Poco/PipeStream.h>
41#include <Poco/Platform.h>
42#include <Poco/Process.h>
43#include <Poco/StreamCopier.h>
44#include <Poco/String.h>
45#include <Poco/URI.h>
46#include <Poco/Util/LoggingConfigurator.h>
47#include <Poco/Util/PropertyFileConfiguration.h>
48#include <Poco/Util/SystemConfiguration.h>
49#include <Poco/Version.h>
50
51#include <boost/algorithm/string/join.hpp>
52#include <boost/algorithm/string/trim.hpp>
53#include <boost/optional/optional.hpp>
54
55#include <algorithm>
56#include <cctype>
57#include <exception>
58#include <fstream>
59#include <functional>
60#include <iostream>
61#include <stdexcept>
62#include <utility>
63
64#ifdef __APPLE__
65#include <mach-o/dyld.h>
66#endif
67
68namespace Mantid {
73std::string welcomeMessage() {
74 return "Welcome to Mantid " + std::string(Mantid::Kernel::MantidVersion::version()) +
76 " and this release: " + Mantid::Kernel::MantidVersion::doi();
77}
78
79namespace Kernel {
80
81namespace { // anonymous namespace for some utility functions
82
84Logger g_log("ConfigService");
85
92std::vector<std::string> splitPath(const std::string &path) {
93 std::vector<std::string> splitted;
94
95 if (path.find(';') == std::string::npos) { // don't bother tokenizing
96 splitted.emplace_back(path);
97 } else {
99 Mantid::Kernel::StringTokenizer tokenizer(path, ";,", options);
100 auto iend = tokenizer.end();
101 for (auto itr = tokenizer.begin(); itr != iend; ++itr) {
102 if (!itr->empty()) {
103 splitted.emplace_back(*itr);
104 }
105 }
106 }
107 return splitted;
108}
109
110} // end of anonymous namespace
111
112//-------------------------------
113// Private member functions
114//-------------------------------
115
118 : m_pConf(nullptr), m_pSysConfig(new Poco::Util::SystemConfiguration()), m_changed_keys(), m_strBaseDir(""),
119 m_propertyString(""), m_properties_file_name("Mantid.properties"),
120#ifdef MPI_BUILD
121 // Use a different user properties file for an mpi-enabled build to avoid
122 // confusion if both are used on the same file system
123 m_user_properties_file_name("Mantid-mpi.user.properties"),
124#else
125 m_user_properties_file_name("Mantid.user.properties"),
126#endif
127 m_dataSearchDirs(), m_instrumentDirs(), m_proxyInfo(), m_isProxySet(false) {
128 // Register StdChannel with Poco
129 Poco::LoggingFactory::defaultFactory().registerChannelClass(
130 "StdoutChannel", new Poco::Instantiator<Poco::StdoutChannel, Poco::Channel>);
131
133
134 m_configPaths.insert("framework.plugins.directory");
135 m_configPaths.insert("mantidqt.plugins.directory");
136 m_configPaths.insert("instrumentDefinition.directory");
137 m_configPaths.insert("instrumentDefinition.vtpDirectory");
138 m_configPaths.insert("groupingFiles.directory");
139 m_configPaths.insert("maskFiles.directory");
140 m_configPaths.insert("colormaps.directory");
141 m_configPaths.insert("requiredpythonscript.directories");
142 m_configPaths.insert("pythonscripts.directory");
143 m_configPaths.insert("pythonscripts.directories");
144 m_configPaths.insert("python.plugins.directories");
145 m_configPaths.insert("user.python.plugins.directories");
146 m_configPaths.insert("icatDownload.directory");
147 m_configPaths.insert("datasearch.directories");
148 m_configPaths.insert("python.plugins.manifest");
149 m_configPaths.insert("python.templates.directory");
150
151 // attempt to load the default properties file that resides in the directory
152 // of the executable
153 std::string propertiesFilesList;
155 propertiesFilesList = getPropertiesDir() + m_properties_file_name;
156
157 // Load the local (machine) properties file, if it exists
158 Poco::File localFile(getLocalFilename());
159 if (localFile.exists()) {
160 updateConfig(getLocalFilename(), true, false);
161 propertiesFilesList += ", " + getLocalFilename();
162 }
163
164 if (Poco::Environment::has("MANTIDPROPERTIES")) {
165 // and then append the user properties
166 updateConfig(getUserFilename(), true, false);
167 propertiesFilesList += ", " + getUserFilename();
168 // and the extra one from the environment
169 updateConfig(Poco::Environment::get("MANTIDPROPERTIES"), true, true);
170 propertiesFilesList += ", " + Poco::Environment::get("MANTIDPROPERTIES");
171 } else {
172 // Just do the user properties
173 updateConfig(getUserFilename(), true, true);
174 propertiesFilesList += ", " + getUserFilename();
175 }
176
177 g_log.debug() << "ConfigService created.\n";
178 g_log.debug() << "Configured Mantid.properties directory of application as " << getPropertiesDir() << '\n';
179 g_log.information() << "This is Mantid version " << MantidVersion::version() << " revision "
180 << MantidVersion::revision() << '\n';
181 g_log.information() << "running on " << getComputerName() << " starting "
182 << Types::Core::DateAndTime::getCurrentTime().toFormattedString("%Y-%m-%dT%H:%MZ") << "\n";
183 g_log.information() << "Properties file(s) loaded: " << propertiesFilesList << '\n';
184
185 // Assert that the appdata and the instrument subdirectory exists
186 std::string appDataDir = getAppDataDir();
187 Poco::Path path(appDataDir);
188 path.pushDirectory("instrument");
189 Poco::File file(path);
190 // createDirectories will fail gracefully if it is already present - but will
191 // throw an error if it cannot create the directory
192 try {
193 file.createDirectories();
194 } catch (Poco::FileException &fe) {
195 g_log.error() << "Cannot create the local instrument cache directory [" << path.toString()
196 << "]. Mantid will not be able to update instrument definitions.\n"
197 << fe.what() << '\n';
198 }
199 Poco::File vtpDir(getVTPFileDirectory());
200 try {
201 vtpDir.createDirectories();
202 } catch (Poco::FileException &fe) {
203 g_log.error() << "Cannot create the local instrument geometry cache directory [" << path.toString()
204 << "]. Mantid will be slower at viewing complex instruments.\n"
205 << fe.what() << '\n';
206 }
207 // must update the cache of instrument paths
209
210 // update the facilities AFTER we have ensured that all of the directories are
211 // created and the paths updated
212 // if we don't do that first the function below will silently fail without
213 // initialising the facilities vector
214 // and Mantid will crash when it tries to access them, for example when
215 // creating the first time startup screen
217}
218
225}
226
240 // Define the directory to search for the Mantid.properties file.
241 Poco::File f;
242
243 // First directory: the current working
244 m_strBaseDir = Poco::Path::current();
245 f = Poco::File(m_strBaseDir + m_properties_file_name);
246 if (f.exists())
247 return;
248
249 // Check the executable directory to see if it includes a mantid.properties
250 // file
252 f = Poco::File(m_strBaseDir + m_properties_file_name);
253 if (f.exists())
254 return;
255
256 // Check the MANTIDPATH environment var
257 if (Poco::Environment::has("MANTIDPATH")) {
258 // Here we have to follow the convention of the rest of this code and
259 // add a trailing slash.
260 // Note: adding it to the MANTIDPATH itself will make other parts of the
261 // code crash.
262 m_strBaseDir = Poco::Environment::get("MANTIDPATH") + "/";
263 f = Poco::File(m_strBaseDir + m_properties_file_name);
264 if (f.exists())
265 return;
266 }
267
268#ifdef __APPLE__
269 // Finally, on OSX check if we're in the package directory and the .properties
270 // file just happens to be two directories up
271 auto path = Poco::Path(getDirectoryOfExecutable());
272 m_strBaseDir = path.parent().parent().parent().toString();
273#endif
274}
275
276namespace {
277// look for specific keys and throw an exception if one is found
278std::string checkForBadConfigOptions(const std::string &filename, const std::string &propertiesString) {
279 std::stringstream stream(propertiesString);
280 std::stringstream resultPropertiesString;
281 std::string line;
282 int line_num = 0;
283 while (std::getline(stream, line)) {
284 line_num += 1; // increment early
285 bool is_ok = true;
286
287 // Check for common errors. Empty lines are ok, things that are a key
288 // without a value are a critical failure. Forbidden keys are just commented
289 // out.
290 if (line.empty() || (Kernel::Strings::strip(line)[0] == '#')) {
291 // do nothing
292 } else if (line.find("FilterChannel") != std::string::npos) {
293 is_ok = false;
294 }
295
296 // Print warning to error channel and comment out offending line
297 if (!is_ok) {
298 const auto end = line.find("=");
299 g_log.warning() << "Encontered invalid key \"";
300 if (end != std::string::npos) {
301 g_log.warning() << Kernel::Strings::strip(line.substr(0, end));
302 } else {
304 }
305 g_log.warning() << "\" in " << filename << " on line " << line_num << std::endl;
306
307 // comment out the property
308 resultPropertiesString << '#';
309 }
310 // copy over the line
311 resultPropertiesString << line << '\n';
312 }
313 return resultPropertiesString.str();
314}
315} // end of anonymous namespace
316
326void ConfigServiceImpl::loadConfig(const std::string &filename, const bool append) {
327
328 if (!append) {
329 // remove the previous property string
330 m_propertyString = "";
331 m_changed_keys.clear();
332 }
333
334 try {
335 // slurp in entire file
336 std::string temp;
337 bool good = readFile(filename, temp);
338
339 // check if we have failed to open the file
340 if ((!good) || (temp.empty())) {
342 // write out a fresh file
344 } else {
345 throw Exception::FileError("Cannot open file", filename);
346 }
347 }
348
349 // verify the contents and comment out offending lines
350 temp = checkForBadConfigOptions(filename, temp);
351
352 // store the property string
353 if ((append) && (!m_propertyString.empty())) {
354 m_propertyString = m_propertyString + "\n" + temp;
355 } else {
356 m_propertyString = temp;
357 }
358 } catch (std::exception &e) {
359 // there was a problem loading the file - it probably is not there
360 g_log.error() << "Problem loading the configuration file " << filename << " " << e.what() << '\n';
361 g_log.error() << "Mantid is unable to start.\n" << std::endl;
362 throw;
363 }
364
365 // use the cached property string to initialise the POCO property file
366 std::istringstream istr(m_propertyString);
367 m_pConf = new Poco::Util::PropertyFileConfiguration(istr);
368}
369
376bool ConfigServiceImpl::readFile(const std::string &filename, std::string &contents) const {
377 std::ifstream propFile(filename.c_str(), std::ios::in);
378 bool good = propFile.good();
379 if (!good) {
380 contents = "";
381 propFile.close();
382 return good;
383 }
384
385 // slurp in entire file - extremely unlikely delimiter used as an alternate to
386 // \n
387 contents.clear();
388 getline(propFile, contents, '`');
389 propFile.close();
390 return good;
391}
392
397 try {
398 // Configure the logging framework
399 Poco::Util::LoggingConfigurator configurator;
400#if POCO_VERSION > 0x01090400
401 configurator.configure(m_pConf);
402#else
403 configurator.configure(m_pConf.get());
404#endif
405 } catch (std::exception &e) {
406 std::cerr << "Trouble configuring the logging framework " << e.what() << '\n';
407 }
408}
409
417std::string ConfigServiceImpl::makeAbsolute(const std::string &dir, const std::string &key) const {
418 if (dir.empty()) {
419 // Don't do anything for an empty value
420 return dir;
421 }
422 std::string converted;
423 // If we have a list, chop it up and convert each one
424 if (dir.find_first_of(";,") != std::string::npos) {
425 auto splitted = splitPath(dir);
426 auto iend = splitted.cend();
427 for (auto itr = splitted.begin(); itr != iend;) {
428 std::string absolute = makeAbsolute(*itr, key);
429 if (absolute.empty()) {
430 ++itr;
431 } else {
432 converted += absolute;
433 if (++itr != iend) {
434 converted += ";";
435 }
436 }
437 }
438 return converted;
439 }
440
441 // MG 05/10/09: When the Poco::FilePropertyConfiguration object reads its
442 // key/value pairs it
443 // treats a backslash as the start of an escape sequence. If the next
444 // character does not
445 // form a valid sequence then the backslash is removed from the stream. This
446 // has the effect
447 // of giving malformed paths when using Windows-style directories. E.g
448 // C:\Mantid ->C:Mantid
449 // and Poco::Path::isRelative throws an exception on this
450 bool is_relative(false);
451 try {
452 is_relative = Poco::Path(dir).isRelative();
453 } catch (Poco::PathSyntaxException &) {
454 g_log.warning() << "Malformed path detected in the \"" << key << "\" variable, skipping \"" << dir << "\"\n";
455 return "";
456 }
457 if (is_relative) {
458 const std::string propFileDir(getPropertiesDir());
459 converted = Poco::Path(propFileDir).resolve(dir).toString();
460 } else {
461 converted = dir;
462 }
463 if (Poco::Path(converted).getExtension() != "") {
464 converted = Poco::Path(converted).toString();
465 } else {
466 converted = Poco::Path(converted).makeDirectory().toString();
467 }
468 // Backward slashes cannot be allowed to go into our properties file
469 // Note this is a temporary fix for ticket #2445.
470 // Ticket #2460 prompts a review of our path handling in the config service.
471 boost::replace_all(converted, "\\", "/");
472 return converted;
473}
474
481 std::string paths = getString("datasearch.directories", true);
482 if (paths.empty()) {
483 m_dataSearchDirs.clear();
484 } else {
485 m_dataSearchDirs = splitPath(paths);
486 }
487}
488
495bool ConfigServiceImpl::isInDataSearchList(const std::string &path) const {
496 // the path produced by poco will have \ on windows, but the searchdirs will
497 // always have /
498 std::string correctedPath = path;
499 replace(correctedPath.begin(), correctedPath.end(), '\\', '/');
500
501 using std::placeholders::_1;
502 auto it = std::find_if(m_dataSearchDirs.cbegin(), m_dataSearchDirs.cend(),
503 std::bind(std::equal_to<std::string>(), _1, correctedPath));
504 return (it != m_dataSearchDirs.end());
505}
506
512 try {
513 std::fstream filestr((getUserPropertiesDir() + m_user_properties_file_name).c_str(), std::fstream::out);
514
515 filestr << "# This file can be used to override any properties for this "
516 "installation.\n";
517 filestr << "# Any properties found in this file will override any that are "
518 "found in the Mantid.Properties file\n";
519 filestr << "# As this file will not be replaced with further installations "
520 "of Mantid it is a safe place to put \n";
521 filestr << "# properties that suit your particular installation.\n";
522 filestr << "#\n";
523 filestr << "# See here for a list of possible options:\n";
524 filestr << "# "
525 "http://docs.mantidproject.org/nightly/concepts/PropertiesFile.html"
526 "\n\n";
527 filestr << "##\n";
528 filestr << "## GENERAL\n";
529 filestr << "##\n\n";
530 filestr << "## Set the maximum number of cores used to run algorithms over\n";
531 filestr << "#MultiThreaded.MaxCores=4\n\n";
532 filestr << "##\n";
533 filestr << "## FACILITY AND INSTRUMENT\n";
534 filestr << "##\n\n";
535 filestr << "## Sets the default facility\n";
536 filestr << "## e.g.: ISIS, SNS, ILL\n";
537 filestr << "default.facility=\n\n";
538 filestr << "## Sets the default instrument\n";
539 filestr << "## e.g. IRIS, HET, NIMROD\n";
540 filestr << "default.instrument=\n\n";
541 filestr << '\n';
542 filestr << "## Sets the Q.convention\n";
543 filestr << "## Set to Crystallography for kf-ki instead of default "
544 "Inelastic which is ki-kf\n";
545 filestr << "#Q.convention=Crystallography\n";
546 filestr << "##\n";
547 filestr << "## DIRECTORIES\n";
548 filestr << "##\n\n";
549 filestr << "## Sets a list of directories (separated by semi colons) to "
550 "search for data\n";
551 filestr << "#datasearch.directories=../data;../isis/data\n\n";
552 filestr << "## Set a list (separated by semi colons) of directories to "
553 "look for additional Python scripts\n";
554 filestr << "#pythonscripts.directories=../scripts;../docs/MyScripts\n\n";
555 filestr << "## Uncomment to enable archive search - ICat and Orbiter\n";
556 filestr << "#datasearch.searcharchive=On\n\n";
557 filestr << "## Sets default save directory\n";
558 filestr << "#defaultsave.directory=../data\n\n";
559 filestr << "##\n";
560 filestr << "## LOGGING\n";
561 filestr << "##\n\n";
562 filestr << "## Uncomment to change logging level\n";
563 filestr << "## Default is information\n";
564 filestr << "## Valid values are: error, warning, notice, information, debug\n";
565 filestr << "#logging.loggers.root.level=information\n\n";
566 filestr << "##\n";
567 filestr << "## MantidPlot\n";
568 filestr << "##\n\n";
569 filestr << "## Hides categories from the algorithm list in MantidPlot\n";
570 filestr << "#algorithms.catagories.hidden=Muons,Inelastic\n\n";
571 filestr << "## Show invisible workspaces\n";
572 filestr << "#MantidOptions.InvisibleWorkspaces=0\n";
573 filestr << "## Re-use plot instances for different plot types\n";
574 filestr << "#MantidOptions.ReusePlotInstances=Off\n\n";
575 filestr << "## Uncomment to disable use of OpenGL to render unwrapped "
576 "instrument views\n";
577 filestr << "#MantidOptions.InstrumentView.UseOpenGL=Off\n\n";
578 filestr << "## Muon GUI settings\n";
579 filestr << "#muon.GUI = \n";
580
581 filestr.close();
582 } catch (std::runtime_error &ex) {
583 g_log.warning() << "Unable to write out user.properties file to " << getUserPropertiesDir()
584 << m_user_properties_file_name << " error: " << ex.what() << '\n';
585 }
586}
587
588//-------------------------------
589// Public member functions
590//-------------------------------
591
596 // Remove the current user properties file and write a fresh one
597 try {
598 Poco::File userFile(getUserFilename());
599 userFile.remove();
600 } catch (Poco::Exception &) {
601 }
603
604 // Now load the original
605 const bool append = false;
606 const bool updateCaches = true;
607 updateConfig(getPropertiesDir() + m_properties_file_name, append, updateCaches);
608}
609
619void ConfigServiceImpl::updateConfig(const std::string &filename, const bool append, const bool update_caches) {
620 loadConfig(filename, append);
621
622 // Ensure that the default save directory makes sense
623 /*
624 if (!append)
625 {
626 std::string save_dir = getString("defaultsave.directory");
627 if (Poco::trimInPlace(save_dir).size() == 0)
628 setString("defaultsave.directory", Poco::Path::home());
629 }
630 */
631
632 if (update_caches) {
633 // Only configure logging once
635 // Configure search paths into a specially saved store as they will be used
636 // frequently
638 appendDataSearchDir(getString("defaultsave.directory"));
640 }
641}
642
648void ConfigServiceImpl::saveConfig(const std::string &filename) const {
649 // Open and read the user properties file
650 std::string updated_file;
651
652 std::ifstream reader(filename.c_str(), std::ios::in);
653 if (reader.bad()) {
654 throw std::runtime_error("Error opening user properties file. Cannot save "
655 "updated configuration.");
656 }
657
658 std::string file_line, output;
659 bool line_continuing(false);
660 while (std::getline(reader, file_line)) {
661 if (!file_line.empty()) {
662 char last = *(file_line.end() - 1);
663 if (last == '\\') {
664 // If we are not in line continuation mode then need
665 // a fresh start line
666 if (!line_continuing)
667 output = "";
668 line_continuing = true;
669 output += file_line + "\n";
670 continue;
671 } else if (line_continuing) {
672 output += file_line;
673 line_continuing = false;
674 } else {
675 output = file_line;
676 }
677 } else {
678 output = "";
679 updated_file += "\n";
680 continue;
681 } // end if-else
682
683 // Output is the current line in the file
684
685 // Extract the key from the current line
686 std::string key;
687 std::string::size_type pos = output.find('=');
688 if (pos == std::string::npos) {
689 key = output; // If no equals then the entire thing is the key
690 } else {
691 key = output.substr(0, pos); // Strip the equals to get only the key
692 }
693 // Now deal with trimming (removes spaces)
694 Poco::trimInPlace(key);
695
696 // Find the comments
697 std::string::size_type comment = key.find('#');
698
699 // Check if it exists in the service using hasProperty and make sure it
700 // isn't a comment
701 if (comment == 0) {
702 updated_file += output;
703 } else if (!hasProperty(key)) {
704 // Remove the key from the changed key list
705 m_changed_keys.erase(key);
706 continue;
707 } else {
708 // If it does exist make sure the value is current
709 std::string value = getString(key, false);
710 Poco::replaceInPlace(value, "\\", "\\\\"); // replace single \ with double
711 updated_file.append(key).append("=").append(value);
712 // Remove the key from the changed key list
713 m_changed_keys.erase(key);
714 }
715 updated_file += "\n";
716 } // End while-loop
717
718 // Any remaining keys within the changed key store weren't present in the
719 // current user properties so append them
720 if (!m_changed_keys.empty()) {
721 updated_file += "\n";
722 auto key_end = m_changed_keys.end();
723 for (auto key_itr = m_changed_keys.begin(); key_itr != key_end;) {
724 updated_file += *key_itr + "=";
725 std::string value = getString(*key_itr, false);
726 Poco::replaceInPlace(value, "\\", "\\\\"); // replace single \ with double
727 updated_file += value;
728 if (++key_itr != key_end) {
729 updated_file += "\n";
730 }
731 }
732 m_changed_keys.clear();
733 }
734
735 // Write out the new file
736 std::ofstream writer(filename.c_str(), std::ios_base::trunc);
737 if (writer.bad()) {
738 writer.close();
739 g_log.error() << "Error writing new user properties file. Cannot save "
740 "current configuration.\n";
741 throw std::runtime_error("Error writing new user properties file. Cannot "
742 "save current configuration.");
743 }
744
745 writer.write(updated_file.c_str(), updated_file.size());
746 writer.close();
747}
748
761std::string ConfigServiceImpl::getString(const std::string &keyName, bool pathAbsolute) const {
762 if (m_pConf->hasProperty(keyName)) {
763 std::string value = m_pConf->getString(keyName);
764 const auto key = m_configPaths.find(keyName);
765 if (pathAbsolute && key != m_configPaths.end()) {
766 value = makeAbsolute(value, keyName);
767 }
768 return value;
769 }
770
771 g_log.debug() << "Unable to find " << keyName << " in the properties file" << '\n';
772 return {};
773}
774
784std::vector<std::string> ConfigServiceImpl::getKeys(const std::string &keyName) const {
785 std::vector<std::string> rawKeys;
786 m_pConf->keys(keyName, rawKeys);
787 return rawKeys;
788}
789
795void ConfigServiceImpl::getKeysRecursive(const std::string &root, std::vector<std::string> &allKeys) const {
796 std::vector<std::string> rootKeys = getKeys(root);
797
798 if (rootKeys.empty())
799 allKeys.emplace_back(root);
800
801 for (auto &rootKey : rootKeys) {
802 std::string searchString;
803 if (root.empty()) {
804 searchString.append(rootKey);
805 } else {
806 searchString.append(root).append(".").append(rootKey);
807 }
808
809 getKeysRecursive(searchString, allKeys);
810 }
811}
812
821std::vector<std::string> ConfigServiceImpl::keys() const {
822 std::vector<std::string> allKeys;
823 getKeysRecursive("", allKeys);
824 return allKeys;
825}
826
835void ConfigServiceImpl::remove(const std::string &rootName) {
836 m_pConf->remove(rootName);
837 m_changed_keys.insert(rootName);
838}
839
846bool ConfigServiceImpl::hasProperty(const std::string &rootName) const { return m_pConf->hasProperty(rootName); }
847
856bool ConfigServiceImpl::isExecutable(const std::string &target) const {
857 try {
858 std::string expTarget = Poco::Path::expand(target);
859 Poco::File tempFile = Poco::File(expTarget);
860
861 if (tempFile.exists()) {
862 return tempFile.canExecute();
863 } else
864 return false;
865 } catch (Poco::Exception &) {
866 return false;
867 }
868}
869
882void ConfigServiceImpl::launchProcess(const std::string &programFilePath,
883 const std::vector<std::string> &programArguments) const {
884 try {
885 std::string expTarget = Poco::Path::expand(programFilePath);
886 Poco::Process::launch(expTarget, programArguments);
887 } catch (Poco::SystemException &e) {
888 throw std::runtime_error(e.what());
889 }
890}
891
897void ConfigServiceImpl::setString(const std::string &key, const std::string &value) {
898 // If the value is unchanged (after any path conversions), there's nothing to
899 // do.
900 const std::string old = getString(key);
901 if (value == old)
902 return;
903
904 // Update the internal value
905 m_pConf->setString(key, value);
906
907 // Cache things that are accessed frequently
908 if (key == "datasearch.directories") {
910 } else if (key == "instrumentDefinition.directory") {
912 } else if (key == "defaultsave.directory") {
914 } else if (key == "logging.channels.consoleChannel.class") {
915 // this key requires reloading logging for it to take effect
917 }
918
919 m_notificationCenter.postNotification(new ValueChanged(key, value, old));
920 m_changed_keys.insert(key);
921}
922
930template <typename T> boost::optional<T> ConfigServiceImpl::getValue(const std::string &keyName) {
931 std::string strValue = getString(keyName);
932 T output;
933 int result = Mantid::Kernel::Strings::convert(strValue, output);
934
935 if (result != 1) {
936 return boost::none;
937 }
938
939 return boost::optional<T>(output);
940}
941
949template <> boost::optional<bool> ConfigServiceImpl::getValue(const std::string &keyName) {
950 auto returnedValue = getValue<std::string>(keyName);
951 if (!returnedValue.is_initialized()) {
952 return boost::none;
953 }
954
955 auto &configVal = returnedValue.get();
956
957 std::transform(configVal.begin(), configVal.end(), configVal.begin(), ::tolower);
958
959 boost::trim(configVal);
960
961 bool trueString = configVal == "true";
962 bool valueOne = configVal == "1";
963 bool onOffString = configVal == "on";
964
965 // A string of 1 or true both count
966 return trueString || valueOne || onOffString;
967}
968
974#ifdef _WIN32
975 return "Mantid.local.properties";
976#else
977 return "/etc/mantid.local.properties";
978#endif
979}
980
986
994std::string ConfigServiceImpl::getEnvironment(const std::string &keyName) {
995 return m_pSysConfig->getString("system.env." + keyName);
996}
997
1002std::string ConfigServiceImpl::getOSName() { return m_pSysConfig->getString("system.osName"); }
1003
1008std::string ConfigServiceImpl::getOSArchitecture() { return m_pSysConfig->getString("system.osArchitecture"); }
1009
1014std::string ConfigServiceImpl::getComputerName() { return m_pSysConfig->getString("system.nodeName"); }
1015
1020std::string ConfigServiceImpl::getOSVersion() { return m_pSysConfig->getString("system.osVersion"); }
1021
1023bool canRead(const std::string &filename) {
1024 // check for existence of the file
1025 Poco::File pocoFile(filename);
1026 if (!pocoFile.exists()) {
1027 return false;
1028 }
1029
1030 // just return if it is readable
1031 return pocoFile.canRead();
1032}
1033
1035std::string getValueFromStdOut(const std::string &orig, const std::string &key) {
1036 size_t start = orig.find(key);
1037 if (start == std::string::npos) {
1038 return std::string();
1039 }
1040 start += key.size();
1041
1042 size_t stop = orig.find('\n', start);
1043 if (stop == std::string::npos) {
1044 return std::string();
1045 }
1046
1047 return Mantid::Kernel::Strings::strip(orig.substr(start, stop - start - 1));
1048}
1049
1056 std::string description;
1057
1058 // read os-release
1059 static const std::string OS_RELEASE("/etc/os-release");
1060 if (canRead(OS_RELEASE)) {
1061 static const std::string PRETTY_NAME("PRETTY_NAME=");
1062
1063 // open it to see if it has the magic line
1064 std::ifstream handle(OS_RELEASE.c_str(), std::ios::in);
1065
1066 // go through the file
1067 std::string line;
1068 while (std::getline(handle, line)) {
1069 if (line.find(PRETTY_NAME) != std::string::npos) {
1070 if (line.length() > PRETTY_NAME.length() + 1) {
1071 size_t length = line.length() - PRETTY_NAME.length() - 2;
1072 description = line.substr(PRETTY_NAME.length() + 1, length);
1073 }
1074 break;
1075 }
1076 }
1077
1078 // cleanup
1079 handle.close();
1080 if (!description.empty()) {
1081 return description;
1082 }
1083 }
1084
1085 // read redhat-release
1086 static const std::string REDHAT_RELEASE("/etc/redhat-release");
1087 if (canRead(REDHAT_RELEASE)) {
1088 // open it to see if it has the magic line
1089 std::ifstream handle(REDHAT_RELEASE.c_str(), std::ios::in);
1090
1091 // go through the file
1092 std::string line;
1093 while (std::getline(handle, line)) {
1094 if (!line.empty()) {
1095 description = line;
1096 break;
1097 }
1098 }
1099
1100 // cleanup
1101 handle.close();
1102 if (!description.empty()) {
1103 return description;
1104 }
1105 }
1106
1107 // try system calls
1108 std::string cmd;
1109 std::vector<std::string> args;
1110#ifdef __APPLE__
1111 cmd = "sw_vers"; // mac
1112#elif _WIN32
1113 cmd = "wmic"; // windows
1114 args.emplace_back("os"); // windows
1115 args.emplace_back("get"); // windows
1116 args.emplace_back("Caption"); // windows
1117 args.emplace_back("/value"); // windows
1118#endif
1119
1120#if defined __APPLE__ || defined _WIN32
1121 try {
1122 Poco::Pipe outPipe, errorPipe;
1123 Poco::ProcessHandle ph = Poco::Process::launch(cmd, args, nullptr, &outPipe, &errorPipe);
1124 const int rc = ph.wait();
1125 // Only if the command returned successfully.
1126 if (rc == 0) {
1127 Poco::PipeInputStream pipeStream(outPipe);
1128 std::stringstream stringStream;
1129 Poco::StreamCopier::copyStream(pipeStream, stringStream);
1130 const std::string result = stringStream.str();
1131#ifdef __APPLE__
1132 const std::string product_name = getValueFromStdOut(result, "ProductName:");
1133 const std::string product_vers = getValueFromStdOut(result, "ProductVersion:");
1134
1135 description = product_name + " " + product_vers;
1136#elif _WIN32
1137 description = getValueFromStdOut(result, "Caption=");
1138#else
1139 UNUSED_ARG(result); // only used on mac and windows
1140#endif
1141 } else {
1142 std::stringstream messageStream;
1143 messageStream << "command \"" << cmd << "\" failed with code: " << rc;
1144 g_log.debug(messageStream.str());
1145 }
1146 } catch (Poco::SystemException &e) {
1147 g_log.debug("command \"" + cmd + "\" failed");
1148 g_log.debug(e.what());
1149 }
1150#endif
1151 return description;
1152}
1153
1156 std::string username;
1157
1158 // mac and favorite way to get username on linux
1159 try {
1160 username = m_pSysConfig->getString("system.env.USER");
1161 if (!username.empty()) {
1162 return username;
1163 }
1164 } catch (Poco::NotFoundException &e) {
1165 UNUSED_ARG(e); // let it drop on the floor
1166 }
1167
1168 // windoze and alternate linux username variable
1169 try {
1170 username = m_pSysConfig->getString("system.env.USERNAME");
1171 if (!username.empty()) {
1172 return username;
1173 }
1174 } catch (Poco::NotFoundException &e) {
1175 UNUSED_ARG(e); // let it drop on the floor
1176 }
1177
1178 // give up and return an empty string
1179 return std::string();
1180}
1181
1186std::string ConfigServiceImpl::getCurrentDir() { return m_pSysConfig->getString("system.currentDir"); }
1187
1193std::string ConfigServiceImpl::getCurrentDir() const { return m_pSysConfig->getString("system.currentDir"); }
1194
1199std::string ConfigServiceImpl::getTempDir() { return m_pSysConfig->getString("system.tempDir"); }
1200
1206 const std::string applicationName = "mantid";
1207#if POCO_OS == POCO_OS_WINDOWS_NT
1208 const std::string vendorName = "mantidproject";
1209 std::string appdata = std::getenv("APPDATA");
1210 Poco::Path path(appdata);
1211 path.makeDirectory();
1212 path.pushDirectory(vendorName);
1213 path.pushDirectory(applicationName);
1214 return path.toString();
1215#else // linux and mac
1216 Poco::Path path(Poco::Path::home());
1217 path.pushDirectory("." + applicationName);
1218 return path.toString();
1219#endif
1220}
1221
1228 return Poco::Path(getPathToExecutable()).parent().toString();
1229}
1230
1237 std::string execpath;
1238 const size_t LEN(1024);
1239 char pBuf[LEN];
1240
1241#ifdef _WIN32
1242 unsigned int bytes = GetModuleFileName(NULL, pBuf, LEN);
1243#elif defined __linux__
1244 char szTmp[32];
1245 sprintf(szTmp, "/proc/%d/exe", getpid());
1246 ssize_t bytes = readlink(szTmp, pBuf, LEN);
1247#elif defined __APPLE__
1248 // Two calls to _NSGetExecutablePath required - first to get size of buffer
1249 uint32_t bytes(0);
1250 _NSGetExecutablePath(pBuf, &bytes);
1251 const int success = _NSGetExecutablePath(pBuf, &bytes);
1252 if (success < 0)
1253 bytes = 1025;
1254#endif
1255
1256 if (bytes > 0 && bytes < 1024) {
1257 pBuf[bytes] = '\0';
1258 execpath = std::string(pBuf);
1259 }
1260 return execpath;
1261}
1262
1268bool ConfigServiceImpl::isNetworkDrive([[maybe_unused]] const std::string &path) {
1269#ifdef _WIN32
1270 // if path is relative get the full one
1271 char buff[MAX_PATH];
1272 GetFullPathName(path.c_str(), MAX_PATH, buff, NULL);
1273 std::string fullName(buff);
1274 size_t i = fullName.find(':');
1275
1276 // if the full path doesn't contain a drive letter assume it's on the network
1277 if (i == std::string::npos)
1278 return true;
1279
1280 fullName.erase(i + 1);
1281 fullName += '\\'; // make sure the name has the trailing backslash
1282 UINT type = GetDriveType(fullName.c_str());
1283 return DRIVE_REMOTE == type;
1284#elif defined __linux__
1285 // This information is only present in the /proc/mounts file on linux. There
1286 // are no drives on
1287 // linux only mount locations therefore the test will have to check the path
1288 // against
1289 // entries in /proc/mounts to see if the filesystem type is NFS or SMB (any
1290 // others ????)
1291 // Each line corresponds to a particular mounted location
1292 // 1st column - device name
1293 // 2nd column - mounted location
1294 // 3rd column - filesystem type commonly ext2, ext3 for hard drives and NFS or
1295 // SMB for
1296 // network locations
1297
1298 std::ifstream mntfile("/proc/mounts");
1299 std::string txtread("");
1300 while (getline(mntfile, txtread)) {
1301 std::istringstream strm(txtread);
1302 std::string devname(""), mntpoint(""), fstype("");
1303 strm >> devname >> mntpoint >> fstype;
1304 if (!strm)
1305 continue;
1306 // I can't be sure that the file system type is always lower case
1307 std::transform(fstype.begin(), fstype.end(), fstype.begin(), toupper);
1308 // Skip the current line if the file system isn't a network one
1309 if (fstype != "NFS" && fstype != "SMB")
1310 continue;
1311 // Now we have a line containing a network filesystem and just need to check
1312 // if the path
1313 // supplied contains the mount location. There is a small complication in
1314 // that the mount
1315 // points within the file have certain characters transformed into their
1316 // octal
1317 // representations, for example spaces->040.
1318 std::string::size_type idx = mntpoint.find("\\0");
1319 if (idx != std::string::npos) {
1320 std::string oct = mntpoint.substr(idx + 1, 3);
1321 strm.str(oct);
1322 int printch(-1);
1323 strm.setf(std::ios::oct, std::ios::basefield);
1324 strm >> printch;
1325 if (printch != -1) {
1326 mntpoint = mntpoint.substr(0, idx) + static_cast<char>(printch) + mntpoint.substr(idx + 4);
1327 }
1328 // Search for this at the start of the path
1329 if (path.find(mntpoint) == 0)
1330 return true;
1331 }
1332 }
1333 return false;
1334#else
1335 // Not yet implemented for the mac
1336 return false;
1337#endif
1338}
1339
1350
1359#ifdef _WIN32
1360 return m_strBaseDir;
1361#else
1362 Poco::Path datadir(m_pSysConfig->getString("system.homeDir"));
1363 datadir.append(".mantid");
1364 // Create the directory if it doesn't already exist
1365 Poco::File(datadir).createDirectory();
1366 return datadir.toString() + "/";
1367#endif
1368}
1369
1374const std::vector<std::string> &ConfigServiceImpl::getDataSearchDirs() const { return m_dataSearchDirs; }
1375
1380void ConfigServiceImpl::setDataSearchDirs(const std::vector<std::string> &searchDirs) {
1381 std::string searchPaths = boost::join(searchDirs, ";");
1382 setDataSearchDirs(searchPaths);
1383}
1384
1390void ConfigServiceImpl::setDataSearchDirs(const std::string &searchDirs) {
1391 setString("datasearch.directories", searchDirs);
1392}
1393
1399void ConfigServiceImpl::appendDataSearchSubDir(const std::string &subdir) {
1400 if (subdir.empty())
1401 return;
1402
1403 Poco::Path subDirPath;
1404 try {
1405 subDirPath = Poco::Path(subdir);
1406 } catch (Poco::PathSyntaxException &) {
1407 return;
1408 }
1409
1410 if (!subDirPath.isDirectory() || !subDirPath.isRelative()) {
1411 return;
1412 }
1413
1414 auto newDataDirs = m_dataSearchDirs;
1415 for (const auto &path : m_dataSearchDirs) {
1416 Poco::Path newDirPath;
1417 try {
1418 newDirPath = Poco::Path(path);
1419 newDirPath.append(subDirPath);
1420 // only add new path if it isn't already there
1421 if (std::find(newDataDirs.begin(), newDataDirs.end(), newDirPath.toString()) == newDataDirs.end())
1422 newDataDirs.emplace_back(newDirPath.toString());
1423 } catch (Poco::PathSyntaxException &) {
1424 continue;
1425 }
1426 }
1427
1428 setDataSearchDirs(newDataDirs);
1429}
1430
1436void ConfigServiceImpl::appendDataSearchDir(const std::string &path) {
1437 if (path.empty())
1438 return;
1439
1440 Poco::Path dirPath;
1441 try {
1442 dirPath = Poco::Path(path);
1443 dirPath.makeDirectory();
1444 } catch (Poco::PathSyntaxException &) {
1445 return;
1446 }
1447 if (!isInDataSearchList(dirPath.toString())) {
1448 auto newSearchString = Strings::join(std::begin(m_dataSearchDirs), std::end(m_dataSearchDirs), ";");
1449 newSearchString.append(";" + path);
1450 setString("datasearch.directories", newSearchString);
1451 }
1452}
1453
1458void ConfigServiceImpl::setInstrumentDirectories(const std::vector<std::string> &directories) {
1459 m_instrumentDirs = directories;
1460}
1461
1466const std::vector<std::string> &ConfigServiceImpl::getInstrumentDirectories() const { return m_instrumentDirs; }
1467
1472const std::string ConfigServiceImpl::getInstrumentDirectory() const { return m_instrumentDirs.back(); }
1478 // Determine the search directory for XML instrument definition files (IDFs)
1479 std::string directoryName = getString("instrumentDefinition.vtp.directory");
1480
1481 if (directoryName.empty()) {
1482 Poco::Path path(getAppDataDir());
1483 path.makeDirectory();
1484 path.pushDirectory("instrument");
1485 path.pushDirectory("geometryCache");
1486 directoryName = path.toString();
1487 }
1488 return directoryName;
1489}
1501 m_instrumentDirs.clear();
1502
1503 Poco::Path path(getAppDataDir());
1504 path.makeDirectory();
1505 path.pushDirectory("instrument");
1506 const std::string appdatadir = path.toString();
1508
1509#ifndef _WIN32
1510 addDirectoryifExists("/etc/mantid/instrument", m_instrumentDirs);
1511#endif
1512
1513 // Determine the search directory for XML instrument definition files (IDFs)
1514 std::string directoryName = getString("instrumentDefinition.directory", true);
1515 if (directoryName.empty()) {
1516 // This is the assumed deployment directory for IDFs, where we need to be
1517 // relative to the
1518 // directory of the executable, not the current working directory.
1519 directoryName = Poco::Path(getPropertiesDir()).resolve("../instrument").toString();
1520 }
1521 addDirectoryifExists(directoryName, m_instrumentDirs);
1522}
1523
1531bool ConfigServiceImpl::addDirectoryifExists(const std::string &directoryName,
1532 std::vector<std::string> &directoryList) {
1533 try {
1534 if (Poco::File(directoryName).isDirectory()) {
1535 directoryList.emplace_back(directoryName);
1536 return true;
1537 } else {
1538 g_log.information("Unable to locate directory at: " + directoryName);
1539 return false;
1540 }
1541 } catch (Poco::PathNotFoundException &) {
1542 g_log.information("Unable to locate directory at: " + directoryName);
1543 return false;
1544 } catch (Poco::FileNotFoundException &) {
1545 g_log.information("Unable to locate directory at: " + directoryName);
1546 return false;
1547 }
1548}
1549
1550const std::vector<std::string> ConfigServiceImpl::getFacilityFilenames(const std::string &fName) {
1551 std::vector<std::string> returnPaths;
1552
1553 // first try the supplied file
1554 if (!fName.empty()) {
1555 const Poco::File fileObj(fName);
1556 if (fileObj.exists()) {
1557 returnPaths.emplace_back(fName);
1558 return returnPaths;
1559 }
1560 }
1561
1562 // search all of the instrument directories
1563 const auto &directoryNames = getInstrumentDirectories();
1564
1565 // only use downloaded instruments if configured to download
1566 const std::string updateInstrStr = this->getString("UpdateInstrumentDefinitions.OnStartup");
1567
1568 auto instrDir = directoryNames.begin();
1569
1570 // If we are not updating the instrument definitions
1571 // update the iterator, this means we will skip the folder in HOME and
1572 // look in the instrument folder in mantid install directory or mantid source
1573 // code directory
1574 if (!(updateInstrStr == "1" || updateInstrStr == "on" || updateInstrStr == "On") && directoryNames.size() > 1) {
1575 instrDir++;
1576 }
1577
1578 // look through all the possible files
1579 for (; instrDir != directoryNames.end(); ++instrDir) {
1580 Poco::Path p(*instrDir);
1581 p.append("Facilities.xml");
1582 std::string filename = p.toString();
1583 Poco::File fileObj(filename);
1584
1585 if (fileObj.exists())
1586 returnPaths.emplace_back(filename);
1587 }
1588
1589 if (returnPaths.size() > 0) {
1590 return returnPaths;
1591 }
1592
1593 // getting this far means the file was not found
1594 std::string directoryNamesList = boost::algorithm::join(directoryNames, ", ");
1595 throw std::runtime_error("Failed to find \"Facilities.xml\". Searched in " + directoryNamesList);
1596}
1597
1608void ConfigServiceImpl::updateFacilities(const std::string &fName) {
1610
1611 // Try to find the file. If it does not exist we will crash, and cannot read
1612 // the Facilities file
1613 const auto fileNames = getFacilityFilenames(fName);
1614 size_t attemptIndex = 0;
1615 bool success = false;
1616 while ((!success) && (attemptIndex < fileNames.size())) {
1617 const auto &fileName = fileNames[attemptIndex];
1618 try {
1619 // Set up the DOM parser and parse xml file
1620 Poco::AutoPtr<Poco::XML::Document> pDoc;
1621 try {
1622 Poco::XML::DOMParser pParser;
1623 pDoc = pParser.parse(fileName);
1624 } catch (...) {
1625 throw Kernel::Exception::FileError("Unable to parse file:", fileName);
1626 }
1627
1628 // Get pointer to root element
1629 Poco::XML::Element *pRootElem = pDoc->documentElement();
1630 if (!pRootElem->hasChildNodes()) {
1631 throw std::runtime_error("No root element in Facilities.xml file");
1632 }
1633
1634 const Poco::AutoPtr<Poco::XML::NodeList> pNL_facility = pRootElem->getElementsByTagName("facility");
1635 const size_t n = pNL_facility->length();
1636
1637 for (unsigned long i = 0; i < n; ++i) {
1638 const auto *elem = dynamic_cast<Poco::XML::Element *>(pNL_facility->item(i));
1639 if (elem) {
1640 m_facilities.emplace_back(new FacilityInfo(elem));
1641 }
1642 }
1643
1644 if (m_facilities.empty()) {
1645 throw std::runtime_error("The facility definition file " + fileName + " defines no facilities");
1646 }
1647
1648 // if we got here we have suceeded and can exit the loop
1649 success = true;
1650 } catch (std::runtime_error &ex) {
1651 // log this failure to load a file
1652 g_log.error() << "Failed to load the facilities.xml file at " << fileName << "\nIt might be corrupt. "
1653 << ex.what() << "\nWill try to load another version.\n";
1654 attemptIndex++;
1655 // move on to the next file index if available
1656 if (attemptIndex == fileNames.size()) {
1657 const std::string errorMessage = "No more Facilities.xml files can be found, Mantid will not be "
1658 "able to start, Sorry. Try reinstalling Mantid.";
1659 // This is one of the few times that both logging a messge and throwing
1660 // might make sense
1661 // as the error reporter tends to swallow the thrown message.
1662 g_log.error() << errorMessage << "\n";
1663 // Throw an exception as we have run out of files to try
1664 throw std::runtime_error(errorMessage);
1665 }
1666 }
1667 }
1668}
1669
1673 for (auto &facility : m_facilities) {
1674 delete facility;
1675 }
1676 m_facilities.clear();
1677}
1678
1685const InstrumentInfo &ConfigServiceImpl::getInstrument(const std::string &instrumentName) const {
1686
1687 // Let's first search for the instrument in our default facility
1688 std::string defaultFacility = getFacility().name();
1689
1690 if (!defaultFacility.empty()) {
1691 try {
1692 g_log.debug() << "Looking for " << instrumentName << " at " << defaultFacility << ".\n";
1693 return getFacility(defaultFacility).instrument(instrumentName);
1694 } catch (Exception::NotFoundError &) {
1695 // Well the instName doesn't exist for this facility
1696 // Move along, there's nothing to see here...
1697 }
1698 }
1699
1700 // Now let's look through the other facilities
1701 for (auto facility : m_facilities) {
1702 try {
1703 g_log.debug() << "Looking for " << instrumentName << " at " << (*facility).name() << ".\n";
1704 return (*facility).instrument(instrumentName);
1705 } catch (Exception::NotFoundError &) {
1706 // Well the instName doesn't exist for this facility...
1707 // Move along, there's nothing to see here...
1708 }
1709 }
1710
1711 const std::string errMsg = "Failed to find an instrument with this name in any facility: '" + instrumentName + "' -";
1712 g_log.debug("Instrument " + instrumentName + " not found");
1713 throw Exception::NotFoundError(errMsg, instrumentName);
1714}
1715
1719const std::vector<FacilityInfo *> ConfigServiceImpl::getFacilities() const { return m_facilities; }
1720
1724const std::vector<std::string> ConfigServiceImpl::getFacilityNames() const {
1725 auto names = std::vector<std::string>(m_facilities.size());
1726 std::transform(m_facilities.cbegin(), m_facilities.cend(), names.begin(),
1727 [](const FacilityInfo *facility) { return facility->name(); });
1728
1729 return names;
1730}
1731
1736 std::string defFacility = getString("default.facility");
1737 if (defFacility.empty()) {
1738 defFacility = " ";
1739 }
1740 return this->getFacility(defFacility);
1741}
1742
1749const FacilityInfo &ConfigServiceImpl::getFacility(const std::string &facilityName) const {
1750 if (facilityName.empty())
1751 return this->getFacility();
1752
1753 auto facility = std::find_if(m_facilities.cbegin(), m_facilities.cend(),
1754 [&facilityName](const auto f) { return f->name() == facilityName; });
1755
1756 if (facility != m_facilities.cend()) {
1757 return **facility;
1758 }
1759
1760 throw Exception::NotFoundError("Facilities", facilityName);
1761}
1762
1768void ConfigServiceImpl::setFacility(const std::string &facilityName) {
1769 const FacilityInfo *foundFacility = nullptr;
1770
1771 try {
1772 // Get facility looks up by string - so re-use that to check if the facility
1773 // is known
1774 foundFacility = &getFacility(facilityName);
1775 } catch (const Exception::NotFoundError &) {
1776 g_log.error("Failed to set default facility to be " + facilityName + ". Facility not found");
1777 throw;
1778 }
1779 assert(foundFacility);
1780 setString("default.facility", facilityName);
1781
1782 const auto &associatedInsts = foundFacility->instruments();
1783 if (associatedInsts.empty()) {
1784 throw std::invalid_argument("The selected facility has no instruments associated with it");
1785 }
1786
1787 // Update the default instrument to be one from this facility
1788 setString("default.instrument", associatedInsts[0].name());
1789}
1790
1794void ConfigServiceImpl::addObserver(const Poco::AbstractObserver &observer) const {
1795 m_notificationCenter.addObserver(observer);
1796}
1797
1801void ConfigServiceImpl::removeObserver(const Poco::AbstractObserver &observer) const {
1802 m_notificationCenter.removeObserver(observer);
1803}
1804
1805/*
1806Gets the system proxy information
1807@url A url to match the proxy to
1808@return the proxy information.
1809*/
1811 if (!m_isProxySet) {
1812 // set the proxy
1813 // first check if the proxy is defined in the properties file
1814 auto proxyHost = getValue<std::string>("proxy.host");
1815 auto proxyPort = getValue<int>("proxy.port");
1816
1817 if (proxyHost.is_initialized() && proxyPort.is_initialized()) {
1818 // set it from the config values
1819 m_proxyInfo = ProxyInfo(proxyHost.get(), proxyPort.get(), true);
1820 } else {
1821 // get the system proxy
1822 Poco::URI uri(url);
1823 Mantid::Kernel::NetworkProxy proxyHelper;
1824 m_proxyInfo = proxyHelper.getHttpProxy(uri.toString());
1825 }
1826 m_isProxySet = true;
1827 }
1828 return m_proxyInfo;
1829}
1830
1831std::string ConfigServiceImpl::getFullPath(const std::string &filename, const bool ignoreDirs,
1832 const int options) const {
1833 std::string fName = Kernel::Strings::strip(filename);
1834 g_log.debug() << "getFullPath(" << fName << ")\n";
1835 // If this is already a full path, nothing to do
1836 if (Poco::Path(fName).isAbsolute())
1837 return fName;
1838 // First try the path relative to the current directory. Can throw in some
1839 // circumstances with extensions that have wild cards
1840 try {
1841 Poco::File fullPath(Poco::Path().resolve(fName));
1842 if (fullPath.exists() && (!ignoreDirs || !fullPath.isDirectory()))
1843 return fullPath.path();
1844 } catch (std::exception &) {
1845 }
1846
1847 auto directoryNames = getDataSearchDirs();
1848 const auto &instrDirectories = getInstrumentDirectories();
1849 directoryNames.insert(directoryNames.end(), instrDirectories.begin(), instrDirectories.end());
1850 for (const auto &searchPath : directoryNames) {
1851 g_log.debug() << "Searching for " << fName << " in " << searchPath << "\n";
1852// On windows globbing is not working properly with network drives
1853// for example a network drive containing a $
1854// For this reason, and since windows is case insensitive anyway
1855// a special case is made for windows
1856#ifdef _WIN32
1857 if (fName.find("*") != std::string::npos) {
1858#endif
1859 Poco::Path path(searchPath, fName);
1860 std::set<std::string> files;
1861 Kernel::Glob::glob(path, files, options);
1862 if (!files.empty()) {
1863 Poco::File matchPath(*files.begin());
1864 if (ignoreDirs && matchPath.isDirectory()) {
1865 continue;
1866 }
1867 return *files.begin();
1868 }
1869#ifdef _WIN32
1870 } else {
1871 Poco::Path path(searchPath, fName);
1872 Poco::File file(path);
1873 if (file.exists() && !(ignoreDirs && file.isDirectory())) {
1874 return path.toString();
1875 }
1876 }
1877#endif
1878 }
1879 return "";
1880}
1881
1887void ConfigServiceImpl::setLogLevel(int logLevel, bool quiet) {
1889 if (!quiet) {
1890 g_log.log("logging set to " + Logger::PriorityNames[logLevel] + " priority",
1891 static_cast<Logger::Priority>(logLevel));
1892 }
1893}
1894
1896template DLLExport boost::optional<double> ConfigServiceImpl::getValue(const std::string &);
1897template DLLExport boost::optional<std::string> ConfigServiceImpl::getValue(const std::string &);
1898template DLLExport boost::optional<int> ConfigServiceImpl::getValue(const std::string &);
1899template DLLExport boost::optional<size_t> ConfigServiceImpl::getValue(const std::string &);
1900#ifdef _MSC_VER
1901template DLLExport boost::optional<bool> ConfigServiceImpl::getValue(const std::string &);
1902#endif
1903
1905
1906} // namespace Kernel
1907} // namespace Mantid
double value
The value of the point.
Definition: FitMW.cpp:51
#define DLLExport
Definitions of the DLLImport compiler directives for MSVC.
Definition: System.h:53
#define UNUSED_ARG(x)
Function arguments are sometimes unused in certain implmentations but are required for documentation ...
Definition: System.h:64
This is the class for the notification that is to be sent when a value has been changed in config ser...
Definition: ConfigService.h:81
const std::vector< std::string > & getInstrumentDirectories() const
Get instrument search directories.
std::string m_propertyString
The configuration properties in string format.
void setBaseDirectory()
Setup the base directory.
const std::string getVTPFileDirectory()
get the vtp file directory
const std::string m_properties_file_name
The filename of the Mantid properties file.
std::set< std::string > m_configPaths
List of config paths that may be relative.
void setInstrumentDirectories(const std::vector< std::string > &directories)
Sets instrument directories.
ConfigServiceImpl()
Private constructor for singleton class.
const FacilityInfo & getFacility() const
Get the default facility.
std::string getDirectoryOfExecutable() const
Get the directory containing the program executable.
bool addDirectoryifExists(const std::string &directoryName, std::vector< std::string > &directoryList)
Verifies the directory exists and add it to the back of the directory list if valid.
void cacheInstrumentPaths()
Create the storage of the instrument directories.
void reset()
Reset to "factory" settings. Removes current user properties.
std::string getLocalFilename() const
Return the local properties filename.
std::string getAppDataDir()
Returns the system's appdata directory.
std::string getPropertiesDir() const
Returns the directory where the Mantid.properties file is found.
std::string getOSVersion()
Returns the OS version.
std::string getOSVersionReadable()
Returns a human readable version of the OS version.
virtual ~ConfigServiceImpl()
Private Destructor Prevents client from calling 'delete' on the pointer handed out by Instance.
std::string getFullPath(const std::string &filename, const bool ignoreDirs, const int options) const
std::string getPathToExecutable() const
Get the full path to the executing program (i.e.
void loadConfig(const std::string &filename, const bool append=false)
Loads a config file.
void configureLogging()
Configures the Poco logging and starts it up.
std::string getUserPropertiesDir() const
Returns a directory to use to write out Mantid information.
bool isExecutable(const std::string &target) const
Checks to see whether the target passed is an executable file.
void saveConfig(const std::string &filename) const
Save the configuration to the user file.
boost::optional< T > getValue(const std::string &keyName)
Searches for a string within the currently loaded configuration values and attempts to convert the va...
const std::vector< std::string > & getDataSearchDirs() const
Get the list of search paths.
bool isInDataSearchList(const std::string &path) const
Returns true if the path is in the data search list.
const std::vector< std::string > getFacilityFilenames(const std::string &fName)
Determine the name of the facilities file to use.
void removeObserver(const Poco::AbstractObserver &observer) const
Remove an observer.
std::string m_strBaseDir
The directory that is considered to be the base directory.
Kernel::ProxyInfo & getProxy(const std::string &url)
Gets the proxy for the system.
std::string makeAbsolute(const std::string &dir, const std::string &key) const
Make a relative path or a list of relative paths into an absolute one.
const InstrumentInfo & getInstrument(const std::string &instrumentName="") const
Look for an instrument.
std::set< std::string > m_changed_keys
A set of property keys that have been changed.
void updateConfig(const std::string &filename, const bool append=false, const bool update_caches=true)
Wipe out the current configuration and load a new one.
void createUserPropertiesFile() const
Writes out a fresh user properties file.
std::vector< std::string > m_dataSearchDirs
Store a list of data search paths.
void updateFacilities(const std::string &fName="")
Load facility information from instrumentDir/Facilities.xml file.
const std::vector< FacilityInfo * > getFacilities() const
Get the list of facilities.
std::string getEnvironment(const std::string &keyName)
Searches for the given environment variable and returns it as a string.
void appendDataSearchDir(const std::string &path)
Adds the passed path to the end of the list of data search paths.
std::string getCurrentDir()
Returns the current directory.
void getKeysRecursive(const std::string &root, std::vector< std::string > &allKeys) const
Returns a list of all keys under a given root key.
void setLogLevel(int logLevel, bool quiet=false)
Sets the log level priority for all log channels.
void setDataSearchDirs(const std::vector< std::string > &searchDirs)
Set a list of search paths via a vector.
std::string getString(const std::string &keyName, bool pathAbsolute=true) const
Searches for a configuration property.
const std::string getInstrumentDirectory() const
Get instrument search directory.
void addObserver(const Poco::AbstractObserver &observer) const
Add an observer for a notification.
std::string getUsername()
Returns the username.
std::string getOSName()
Returns the OS name.
void setString(const std::string &key, const std::string &value)
Sets a configuration property.
void clearFacilities()
Empty the list of facilities, deleting the FacilityInfo objects in the process.
std::string getComputerName()
Returns the computer name.
std::string getOSArchitecture()
Returns the architecture.
std::string getUserFilename() const
Return the user properties filename.
Poco::AutoPtr< Poco::Util::SystemConfiguration > m_pSysConfig
the POCO system Config Object
bool m_isProxySet
whether the proxy has been populated yet
bool isNetworkDrive(const std::string &path)
Check if the path is on a network drive.
bool readFile(const std::string &filename, std::string &contents) const
Read a file and place its contents into the given string.
Poco::NotificationCenter m_notificationCenter
Handles distribution of Poco signals.
Kernel::ProxyInfo m_proxyInfo
local cache of proxy details
void setFacility(const std::string &facilityName)
Set the default facility.
void remove(const std::string &rootName)
Removes the value from a selected keyName.
void cacheDataSearchPaths()
Create the storage of the data search directories.
std::string getTempDir()
Returns the system's temp directory.
const std::string m_user_properties_file_name
The filename of the Mantid user properties file.
void appendDataSearchSubDir(const std::string &subdir)
Appends subdirectory to each of the specified data search directories.
std::vector< std::string > keys() const
Returns a list of all full keys in the config.
Poco::AutoPtr< Poco::Util::PropertyFileConfiguration > m_pConf
the POCO file config object
const std::vector< std::string > getFacilityNames() const
Get the list of facility names.
std::vector< FacilityInfo * > m_facilities
The list of available facilities.
void launchProcess(const std::string &programFilePath, const std::vector< std::string > &programArguments) const
Launches a process i.e opening a program.
std::vector< std::string > getKeys(const std::string &keyName) const
Searches for a key in the configuration property.
std::vector< std::string > m_instrumentDirs
Store a list of instrument directory paths.
bool hasProperty(const std::string &rootName) const
Checks to see whether a key has a value assigned to it.
Records the filename and the description of failure.
Definition: Exception.h:98
Exception for when an item is not found in a collection.
Definition: Exception.h:145
A class that holds information about a facility.
Definition: FacilityInfo.h:36
const std::vector< InstrumentInfo > & instruments() const
Returns a list of instruments of this facility.
Definition: FacilityInfo.h:58
const std::string & name() const
Return the name of the facility.
Definition: FacilityInfo.h:41
const InstrumentInfo & instrument(std::string iName="") const
Returns instruments with given name.
static void glob(const Poco::Path &pathPattern, std::set< std::string > &files, int options=0)
Creates a set of files that match the given pathPattern.
Definition: Glob.cpp:33
A class that holds information about an instrument.
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
static const std::string * PriorityNames
Definition: Logger.h:57
static void setLevelForAll(const int level)
Sets the log level for all Loggers created so far, including the root logger.
Definition: Logger.cpp:297
static void shutdown()
Shuts down the logging framework and releases all Loggers.
Definition: Logger.cpp:282
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
Poco::Message::Priority Priority
Definition: Logger.h:55
void log(const std::string &message, const Priority &priority)
Log a message at a given priority.
Definition: Logger.cpp:306
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
static const char * version()
The full version number.
static std::string doi()
The DOI for this release of Mantid.
static std::string paperCitation()
The citation for the Mantid paper.
static const char * revision()
The abbreviated SHA-1 of the last commit.
NetworkProxy : Network proxy utility for getting network proxy information.
Definition: NetworkProxy.h:18
ProxyInfo getHttpProxy(const std::string &targetURLString)
Get http proxy information.
ProxyInfo : Container for carrying around network proxy information.
Definition: ProxyInfo.h:17
Iterator begin()
Iterator referring to first element in the container.
@ TOK_IGNORE_EMPTY
ignore empty tokens
@ TOK_TRIM
remove leading and trailing whitespace from tokens
Iterator end()
Iterator referring to the past-the-end element in the container.
MANTID_KERNEL_DLL std::string strip(const std::string &A)
strip pre/post spaces
Definition: Strings.cpp:397
DLLExport std::string join(ITERATOR_TYPE begin, ITERATOR_TYPE end, const std::string &separator, typename std::enable_if<!(std::is_same< typename std::iterator_traits< ITERATOR_TYPE >::iterator_category, std::random_access_iterator_tag >::value)>::type *=nullptr)
Join a set or vector of (something that turns into a string) together into one string,...
Definition: Strings.h:84
int convert(const std::string &A, T &out)
Convert a string into a number.
Definition: Strings.cpp:665
std::string getValueFromStdOut(const std::string &orig, const std::string &key)
bool canRead(const std::string &filename)
Mantid::Kernel::StringTokenizer tokenizer
Definition: Material.cpp:18
Helper class which provides the Collimation Length for SANS instruments.
MANTID_KERNEL_DLL std::string welcomeMessage()
Returns the welcome message for Mantid.
Definition: Algorithm.h:30