Newer
Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidKernel/ConfigService.h"
#include "MantidKernel/MantidVersion.h"
#include "MantidKernel/ParaViewVersion.h"
Janik Zikovsky
committed
#include "MantidKernel/Strings.h"
#include "MantidKernel/Logger.h"
#include "MantidKernel/FilterChannel.h"
#include "MantidKernel/StdoutChannel.h"
Roman Tolchenov
committed
#include "MantidKernel/FacilityInfo.h"
#include <Poco/Util/LoggingConfigurator.h>
#include <Poco/Util/SystemConfiguration.h>
#include <Poco/Util/PropertyFileConfiguration.h>
#include <Poco/LoggingFactory.h>
#include <Poco/Path.h>
#include <Poco/File.h>
#include <Poco/StringTokenizer.h>
#include <Poco/DOM/DOMParser.h>
#include <Poco/DOM/Document.h>
#include <Poco/DOM/Element.h>
#include <Poco/DOM/NodeList.h>
#include <Poco/Notification.h>
#include <Poco/Environment.h>
Robert Whitley
committed
#include <Poco/Process.h>
Robert Whitley
committed
#include <Poco/String.h>
#ifdef _WIN32
#pragma warning( disable: 4250 )
#endif
#include <Poco/PipeStream.h>
#include <Poco/StreamCopier.h>
Gigg, Martyn Anthony
committed
#include <boost/algorithm/string/replace.hpp>
Gigg, Martyn Anthony
committed
#include <boost/algorithm/string/join.hpp>
Gigg, Martyn Anthony
committed
#include <fstream>
#include <sstream>
#include <iostream>
#include <string>
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
Janik Zikovsky
committed
/**
* Get the welcome message for Mantid.
* @returns A string containing the welcome message for Mantid.
*/
std::string welcomeMessage()
{
return "Welcome to Mantid version " + std::string(Mantid::Kernel::MantidVersion::version()) + " - Manipulation and Analysis Toolkit for Instrument Data\n" +
"Please cite Mantid in your publications using: " + Mantid::Kernel::MantidVersion::doi();
Janik Zikovsky
committed
}
namespace { // anonymous namespace for some utility functions
/// static Logger object
Logger g_log("ConfigService");
/**
* Split the supplied string on semicolons.
*
* @param path The path to split.
* @param splitted vector to put the splitted path into.
*/
void splitPath(const std::string &path, std::vector<std::string> &splitted)
{
if (path.find(";") == std::string::npos)
{ // don't bother tokenizing
splitted.push_back(path);
return;
}
int options = Poco::StringTokenizer::TOK_TRIM + Poco::StringTokenizer::TOK_IGNORE_EMPTY;
splitted.clear();
Poco::StringTokenizer tokenizer(path, ";,", options);
Poco::StringTokenizer::Iterator iend = tokenizer.end();
splitted.reserve(tokenizer.count());
for (Poco::StringTokenizer::Iterator itr = tokenizer.begin(); itr != iend; ++itr)
{
if (!itr->empty())
{
splitted.push_back(*itr);
}
}
}
} // end of anonymous namespace
Campbell, Stuart
committed
/** Inner templated class to wrap the poco library objects that have protected
* destructors and expose them as public.
*/
template<typename T>
class ConfigServiceImpl::WrappedObject: public T
{
public:
/// The template type of class that is being wrapped
typedef T element_type;
/// Simple constructor
WrappedObject() :
T()
{
m_pPtr = static_cast<T*> (this);
}
/** Constructor with a class to wrap
Janik Zikovsky
committed
* @param F :: The object to wrap
Campbell, Stuart
committed
template<typename Field>
WrappedObject(Field& F) :
T(F)
{
m_pPtr = static_cast<T*> (this);
}
Campbell, Stuart
committed
/// Copy constructor
WrappedObject(const WrappedObject<T>& A) :
T(A)
{
m_pPtr = static_cast<T*> (this);
}
Campbell, Stuart
committed
/// Virtual destructor
virtual ~WrappedObject()
{
}
Campbell, Stuart
committed
/// 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;
}
Campbell, Stuart
committed
private:
/// Private pointer to the wrapped class
T* m_pPtr;
};
Campbell, Stuart
committed
//Back to the ConfigService class itself...
Campbell, Stuart
committed
//-------------------------------
// Private member functions
//-------------------------------
Roman Tolchenov
committed
Campbell, Stuart
committed
/// Private constructor for singleton class
ConfigServiceImpl::ConfigServiceImpl() :
m_pConf(NULL), m_pSysConfig(NULL), m_changed_keys(),
m_ConfigPaths(), m_AbsolutePaths(), m_strBaseDir(""), m_PropertyString(""),
m_properties_file_name("Mantid.properties"),
#ifdef MPI_BUILD
// Use a different user properties file for an mpi-enabled build to avoid confusion if both are used on the same filesystem
m_user_properties_file_name("Mantid-mpi.user.properties"),
#else
m_user_properties_file_name("Mantid.user.properties"),
#endif
m_DataSearchDirs(), m_UserSearchDirs(), m_InstrumentDirs(), m_instr_prefixes(), m_removedFlag("@@REMOVED@@")
Campbell, Stuart
committed
{
//getting at system details
m_pSysConfig = new WrappedObject<Poco::Util::SystemConfiguration> ;
m_pConf = 0;
//Register the FilterChannel with the Poco logging factory
Poco::LoggingFactory::defaultFactory().registerChannelClass("FilterChannel", new Poco::Instantiator<
Poco::FilterChannel, Poco::Channel>);
//Register StdChannel with Poco
Poco::LoggingFactory::defaultFactory().registerChannelClass("StdoutChannel", new Poco::Instantiator<
Poco::StdoutChannel, Poco::Channel>);
Campbell, Stuart
committed
Gigg, Martyn Anthony
committed
// Define the directory to search for the Mantid.properties file.
Janik Zikovsky
committed
Poco::File f;
Janik Zikovsky
committed
// First directory: the current working
m_strBaseDir = Poco::Path::current();
f = Poco::File(m_strBaseDir + m_properties_file_name);
Janik Zikovsky
committed
// Check the executable directory to see if it includes a mantid.properties file
Gigg, Martyn Anthony
committed
m_strBaseDir = getDirectoryOfExecutable();
Janik Zikovsky
committed
f = Poco::File(m_strBaseDir + m_properties_file_name);
if (!f.exists())
Janik Zikovsky
committed
// Last, use the MANTIDPATH environment var
if (Poco::Environment::has("MANTIDPATH"))
{
// Here we have to follow the convention of the rest of this code and add a trailing slash.
// Note: adding it to the MANTIDPATH itself will make other parts of the code crash.
m_strBaseDir = Poco::Environment::get("MANTIDPATH") + "/";
}
//Assert that the appdata and the instrument subdirectory exists
std::string appDataDir = getAppDataDir();
Poco::Path path(appDataDir);
path.pushDirectory("instrument");
Poco::File file(path);
//createdirectories will fail gracefully if it is already present
file.createDirectories();
Campbell, Stuart
committed
//Fill the list of possible relative path keys that may require conversion to absolute paths
m_ConfigPaths.insert(std::make_pair("mantidqt.python_interfaces_directory", true));
Campbell, Stuart
committed
m_ConfigPaths.insert(std::make_pair("plugins.directory", true));
m_ConfigPaths.insert(std::make_pair("pvplugins.directory", true));
Russell Taylor
committed
m_ConfigPaths.insert(std::make_pair("mantidqt.plugins.directory", true));
Campbell, Stuart
committed
m_ConfigPaths.insert(std::make_pair("instrumentDefinition.directory", true));
m_ConfigPaths.insert(std::make_pair("groupingFiles.directory", true));
m_ConfigPaths.insert(std::make_pair("maskFiles.directory", true));
m_ConfigPaths.insert(std::make_pair("colormaps.directory", true));
Campbell, Stuart
committed
m_ConfigPaths.insert(std::make_pair("requiredpythonscript.directories", true));
m_ConfigPaths.insert(std::make_pair("pythonscripts.directory", true));
m_ConfigPaths.insert(std::make_pair("pythonscripts.directories", true));
m_ConfigPaths.insert(std::make_pair("python.plugins.directories", true));
m_ConfigPaths.insert(std::make_pair("user.python.plugins.directories", true));
m_ConfigPaths.insert(std::make_pair("datasearch.directories", true));
Campbell, Stuart
committed
m_ConfigPaths.insert(std::make_pair("icatDownload.directory", true));
//attempt to load the default properties file that resides in the directory of the executable
Janik Zikovsky
committed
std::string propertiesFilesList;
Gigg, Martyn Anthony
committed
updateConfig(getPropertiesDir() + m_properties_file_name, false, false);
Janik Zikovsky
committed
propertiesFilesList = getPropertiesDir() + m_properties_file_name;
// Load the local (machine) properties file, if it exists
Poco::File localFile(getLocalFilename());
if ( localFile.exists() )
{
updateConfig(getLocalFilename(), true, false);
propertiesFilesList += ", " + getLocalFilename();
}
Janik Zikovsky
committed
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();
}
Campbell, Stuart
committed
updateFacilities();
g_log.debug() << "ConfigService created." << std::endl;
Janik Zikovsky
committed
g_log.debug() << "Configured Mantid.properties directory of application as " << getPropertiesDir()
<< std::endl;
g_log.information() << "This is Mantid version "
<< MantidVersion::version() << " revision " << MantidVersion::revision() << std::endl;
Janik Zikovsky
committed
g_log.information() << "Properties file(s) loaded: " << propertiesFilesList << std::endl;
#ifndef MPI_BUILD // There is no logging to file by default in MPI build
g_log.information() << "Logging to: " << m_logFilePath << std::endl;
Campbell, Stuart
committed
}
/** Private Destructor
* Prevents client from calling 'delete' on the pointer handed out by Instance
*/
ConfigServiceImpl::~ConfigServiceImpl()
{
//std::cerr << "ConfigService destroyed." << std::endl;
Kernel::Logger::shutdown();
delete m_pSysConfig;
delete m_pConf; // potential double delete???
clearFacilities();
Campbell, Stuart
committed
}
/** Loads the config file provided.
* If the file contains logging setup instructions then these will be used to setup the logging framework.
*
Janik Zikovsky
committed
* @param filename :: The filename and optionally path of the file to load
* @param append :: If false (default) then any previous configuration is discarded, otherwise the new keys are added, and repeated keys will override existing ones.
Campbell, Stuart
committed
*/
void ConfigServiceImpl::loadConfig(const std::string& filename, const bool append)
{
delete m_pConf;
if (!append)
{
//remove the previous property string
m_PropertyString = "";
m_changed_keys.clear();
Campbell, Stuart
committed
try
Campbell, Stuart
committed
//slurp in entire file
std::string temp;
bool good = readFile(filename, temp);
Campbell, Stuart
committed
// check if we have failed to open the file
if ((!good) || (temp == ""))
Gigg, Martyn Anthony
committed
if (filename == getUserPropertiesDir() + m_user_properties_file_name)
Campbell, Stuart
committed
//write out a fresh file
createUserPropertiesFile();
Campbell, Stuart
committed
throw Exception::FileError("Cannot open file", filename);
Campbell, Stuart
committed
}
Campbell, Stuart
committed
//store the property string
if ((append) && (m_PropertyString != ""))
{
m_PropertyString = m_PropertyString + "\n" + temp;
Campbell, Stuart
committed
else
Campbell, Stuart
committed
m_PropertyString = temp;
Campbell, Stuart
committed
} catch (std::exception& e)
Campbell, Stuart
committed
//there was a problem loading the file - it probably is not there
std::cerr << "Problem loading the configuration file " << filename << " " << e.what() << std::endl;
if (!append)
Campbell, Stuart
committed
// if we have no property values then take the default
m_PropertyString = defaultConfig();
Campbell, Stuart
committed
}
Campbell, Stuart
committed
//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
Janik Zikovsky
committed
* @param filename :: The filename of the file to read
* @param contents :: The file contents will be placed here
Campbell, Stuart
committed
* @returns A boolean indicating whether opening the file was successful
*/
bool ConfigServiceImpl::readFile(const std::string& filename, std::string & contents) const
{
std::ifstream propFile(filename.c_str(), std::ios::in);
bool good = propFile.good();
if (!good)
{
contents = "";
propFile.close();
return good;
}
Campbell, Stuart
committed
//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
*
*/
Campbell, Stuart
committed
void ConfigServiceImpl::configureLogging()
{
try
Campbell, Stuart
committed
//Ensure that the logging directory exists
m_logFilePath = getString("logging.channels.fileChannel.path");
Poco::Path logpath(m_logFilePath);
// Undocumented way to override the mantid.log path
if (Poco::Environment::has("MANTIDLOGPATH"))
Gigg, Martyn Anthony
committed
{
logpath = Poco::Path(Poco::Environment::get("MANTIDLOGPATH"));
Gigg, Martyn Anthony
committed
logpath = logpath.absolute();
m_logFilePath = logpath.toString();
}
// An absolute path makes things simpler
logpath = logpath.absolute();
Gigg, Martyn Anthony
committed
// First, try the logpath given
if (!m_logFilePath.empty())
{
try
{
// Save it for later
m_logFilePath = logpath.toString();
//make this path point to the parent directory and create it if it does not exist
Poco::Path parent = logpath;
parent.makeParent();
Poco::File(parent).createDirectories();
// Try to create or append to the file. If it fails, use the default
FILE *fp = fopen(m_logFilePath.c_str(), "a+");
if (fp == NULL)
{
Janik Zikovsky
committed
std::cerr << "Error writing to log file path given in properties file: \"" << m_logFilePath
<< "\". Will use a default path instead." << std::endl;
// Clear the path; this will make it use the default
m_logFilePath = "";
}
else
fclose(fp);
Janik Zikovsky
committed
} catch (std::exception &)
{
Janik Zikovsky
committed
std::cerr << "Error writing to log file path given in properties file: \"" << m_logFilePath
<< "\". Will use a default path instead." << std::endl;
// ERROR! Maybe the file is not writable!
// Clear the path; this will make it use the default
m_logFilePath = "";
}
}
// The path given was invalid somehow? Use a default
if (m_logFilePath.empty())
m_logFilePath = getUserPropertiesDir() + "mantid.log";
// Check whether the file can be written. The Poco::File::canWrite method does not work
// for files that don't exist, it throws an exception. It also can't be used to check for
// directory access as the Windows API doesn't return this information correctly for
// directories.
FILE *fp = fopen(m_logFilePath.c_str(), "a+");
if (!fp)
{
// if we cannot write to the default directory then set use the system temp
logpath = Poco::Path::temp() + "mantid.log";
m_logFilePath = logpath.toString();
std::cerr << "Error writing to log file path to default location: \"" << m_logFilePath
<< "\". Will use a system temp path instead: \"" << m_logFilePath << "\"" << std::endl;
}
else
fclose(fp);
// Set the line in the configuration properties.
// this'll be picked up by LoggingConfigurator (somehow)
m_pConf->setString("logging.channels.fileChannel.path", m_logFilePath);
Campbell, Stuart
committed
//make this path point to the parent directory and create it if it does not exist
logpath.makeParent();
if (!logpath.toString().empty())
Poco::File(logpath).createDirectories(); // Also creates all necessary directories
// Configure the logging framework
Campbell, Stuart
committed
Poco::Util::LoggingConfigurator configurator;
configurator.configure(m_pConf);
} catch (std::exception& e)
{
std::cerr << "Trouble configuring the logging framework " << e.what() << std::endl;
}
Campbell, Stuart
committed
}
Campbell, Stuart
committed
/**
* Searches the stored list for keys that have been loaded from the config file and may contain
* relative paths. Any it find are converted to absolute paths and stored separately
*/
void ConfigServiceImpl::convertRelativeToAbsolute()
{
if (m_ConfigPaths.empty())
return;
Campbell, Stuart
committed
m_AbsolutePaths.clear();
std::map<std::string, bool>::const_iterator send = m_ConfigPaths.end();
for (std::map<std::string, bool>::const_iterator sitr = m_ConfigPaths.begin(); sitr != send; ++sitr)
{
std::string key = sitr->first;
if (!m_pConf->hasProperty(key))
continue;
Campbell, Stuart
committed
std::string value(m_pConf->getString(key));
value = makeAbsolute(value, key);
m_AbsolutePaths.insert(std::make_pair(key, value));
}
}
/**
* Make a relative path or a list of relative paths into an absolute one.
Janik Zikovsky
committed
* @param dir :: The directory to convert
* @param key :: The key variable this relates to
* @returns A string containing an absolute path by resolving the relative directory with the executable directory
Campbell, Stuart
committed
*/
std::string ConfigServiceImpl::makeAbsolute(const std::string & dir, const std::string & key) const
{
if(dir.empty())
{
// Don't do anything for an empty value
return dir;
}
Campbell, Stuart
committed
std::string converted;
// If we have a list, chop it up and convert each one
if (dir.find_first_of(";,") != std::string::npos)
std::vector<std::string> splitted;
splitPath(dir, splitted);
std::vector<std::string>::const_iterator iend = splitted.end();
for (std::vector<std::string>::const_iterator itr = splitted.begin(); itr != iend;)
Campbell, Stuart
committed
std::string absolute = makeAbsolute(*itr, key);
if (absolute.empty())
Campbell, Stuart
committed
++itr;
}
else
{
converted += absolute;
if (++itr != iend)
Campbell, Stuart
committed
converted += ";";
Campbell, Stuart
committed
}
Campbell, Stuart
committed
// MG 05/10/09: When the Poco::FilePropertyConfiguration object reads its key/value pairs it
// treats a backslash as the start of an escape sequence. If the next character does not
// form a valid sequence then the backslash is removed from the stream. This has the effect
// of giving malformed paths when using Windows-style directories. E.g C:\Mantid ->C:Mantid
// and Poco::Path::isRelative throws an exception on this
bool is_relative(false);
try
Campbell, Stuart
committed
is_relative = Poco::Path(dir).isRelative();
} catch (Poco::PathSyntaxException&)
Campbell, Stuart
committed
{
g_log.warning() << "Malformed path detected in the \"" << key << "\" variable, skipping \"" << dir
<< "\"\n";
return "";
}
if (is_relative)
{
Gigg, Martyn Anthony
committed
const std::string propFileDir(getPropertiesDir());
Gigg, Martyn Anthony
committed
converted = Poco::Path(propFileDir).resolve(dir).toString();
Campbell, Stuart
committed
}
else
{
converted = dir;
Campbell, Stuart
committed
converted = Poco::Path(converted).makeDirectory().toString();
Campbell, Stuart
committed
// C++ doesn't have a const version of operator[] for maps so I can't call that here
std::map<std::string, bool>::const_iterator it = m_ConfigPaths.find(key);
bool required = false;
if (it != m_ConfigPaths.end())
Campbell, Stuart
committed
required = it->second;
}
Michael Whitty
committed
try
{
if (required && !Poco::File(converted).exists())
{
g_log.debug() << "Required properties path \"" << converted << "\" in the \"" << key
Michael Whitty
committed
<< "\" variable does not exist.\n";
converted = "";
}
} catch ( Poco::FileException & )
Campbell, Stuart
committed
{
g_log.debug() << "Required properties path \"" << converted << "\" in the \"" << key
Campbell, Stuart
committed
<< "\" variable does not exist.\n";
converted = "";
}
Michael Whitty
committed
Gigg, Martyn Anthony
committed
// 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.
Janik Zikovsky
committed
boost::replace_all(converted, "\\", "/");
Campbell, Stuart
committed
return converted;
}
/**
* Create the store of data search paths from the 'datasearch.directories' key within the Mantid.properties file.
* The value of the key should be a semi-colon separated list of directories
*/
void ConfigServiceImpl::cacheDataSearchPaths()
{
m_DataSearchDirs.clear();
std::string paths = getString("datasearch.directories");
//Nothing to do
if (paths.empty())
return;
splitPath(paths, m_DataSearchDirs);
Campbell, Stuart
committed
}
/**
* Create the store of user search paths from the 'usersearch.directories' key within the Mantid.properties file.
* The value of the key should be a semi-colon separated list of directories
*/
void ConfigServiceImpl::cacheUserSearchPaths()
{
m_UserSearchDirs.clear();
std::string paths = getString("usersearch.directories");
//Nothing to do
if (paths.empty())
return;
splitPath(paths, m_UserSearchDirs);
}
/**
* The path that is passed should be as returned by makeAbsolute() and
* this function will return true if that path is in the list
Janik Zikovsky
committed
* @param path :: the absolute path name to search for
* @return true if the path was found
*/
Gigg, Martyn Anthony
committed
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(), '\\', '/' );
Janik Zikovsky
committed
std::vector<std::string>::const_iterator it = std::find_if(m_DataSearchDirs.begin(),
m_DataSearchDirs.end(), std::bind2nd(std::equal_to<std::string>(), correctedPath));
Gigg, Martyn Anthony
committed
return (it != m_DataSearchDirs.end());
}
Campbell, Stuart
committed
/**
* writes a basic placeholder user.properties file to disk
* any errors are caught and logged, but not propagated
*/
void ConfigServiceImpl::createUserPropertiesFile() const
{
try
{
Janik Zikovsky
committed
std::fstream filestr((getUserPropertiesDir() + m_user_properties_file_name).c_str(),
std::fstream::out);
Campbell, Stuart
committed
filestr << "# This file can be used to override any properties for this installation." << std::endl;
filestr << "# Any properties found in this file will override any that are found in the Mantid.Properties file" << std::endl;
filestr << "# As this file will not be replaced with futher installations of Mantid it is a safe place to put " << std::endl;
Campbell, Stuart
committed
filestr << "# properties that suit your particular installation." << std::endl;
filestr << "#" << std::endl;
filestr << "# See here for a list of possible options:" << std::endl;
filestr << "# http://www.mantidproject.org/Properties_File#Mantid.User.Properties" << std::endl;
filestr << std::endl;
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
filestr << "##" << std::endl;
filestr << "## GENERAL" << std::endl;
filestr << "##" << std::endl;
filestr << std::endl;
filestr << "## Set the number of algorithm properties to retain" << std::endl;
filestr << "#algorithms.retained=90" << std::endl;
filestr << std::endl;
filestr << "## Hides catagories from the algorithm list in MantidPlot" << std::endl;
filestr << "#algorithms.catagories.hidden=Muons,Inelastic" << std::endl;
filestr << std::endl;
filestr << "## Set the maximum number of coures used to run algorithms over" << std::endl;
filestr << "#MultiThreaded.MaxCores=4" << std::endl;
filestr << std::endl;
filestr << "##" << std::endl;
filestr << "## FACILITY AND INSTRUMENT" << std::endl;
filestr << "##" << std::endl;
filestr << std::endl;
filestr << "## Sets the default facility" << std::endl;
filestr << "## e.g.: ISIS, SNS, ILL" << std::endl;
filestr << "default.facility=" << std::endl;
filestr << std::endl;
filestr << "## Stes the default instrument" << std::endl;
filestr << "## e.g. IRIS, HET, NIMROD" << std::endl;
filestr << "default.instrument=" << std::endl;
filestr << std::endl;
filestr << "##" << std::endl;
filestr << "## DIRECTORIES" << std::endl;
filestr << "##" << std::endl;
filestr << std::endl;
filestr << "## Sets a list of directories (separated by semi colons) to search for data" << std::endl;
filestr << "#datasearch.directories=../data;../isis/data" << std::endl;
filestr << std::endl;
filestr << "## Set a list (separated by semi colons) of directories to look for additional Python scripts" << std::endl;
filestr << "#pythonscripts.directories=../scripts;../docs/MyScripts" << std::endl;
filestr << std::endl;
filestr << "## Uncomment to enable archive search - ICat and Orbiter" << std::endl;
filestr << "#datasearch.searcharchive=On" << std::endl;
filestr << std::endl;
filestr << "## Sets default save directory" << std::endl;
filestr << "#defaultsave.directory=../data" << std::endl;
filestr << std::endl;
filestr << "##" << std::endl;
filestr << "## LOGGING" << std::endl;
filestr << "##" << std::endl;
filestr << std::endl;
filestr << "## Uncomment to change logging level" << std::endl;
filestr << "## Default is information" << std::endl;
filestr << "## Valid values are: error, warning, notice, information, debug" << std::endl;
filestr << "#logging.loggers.root.level=information" << std::endl;
filestr << std::endl;
filestr << "## Sets the lowest level messages to be logged to file" << std::endl;
filestr << "## Default is warning" << std::endl;
filestr << "## Valid values are: error, warning, notice, information, debug" << std::endl;
filestr << "#logging.channels.fileFilterChannel.level=debug" << std::endl;
filestr << std::endl;
filestr << "## Sets the file to write logs to" << std::endl;
filestr << "#logging.channels.fileChannel.path=../mantid.log" << std::endl;
filestr << std::endl;
filestr << "##" << std::endl;
filestr << "## MantidPlot" << std::endl;
filestr << "##" << std::endl;
filestr << std::endl;
filestr << "## Show invisible workspaces" << std::endl;
filestr << "#MantidOptions.InvisibleWorkspaces=0" << std::endl;
filestr << "## Re-use plot instances for different plot types" << std::endl;
filestr << "#MantidOptions.ReusePlotInstances=Off" << std::endl;
filestr << std::endl;
filestr << "## Uncomment to disable use of OpenGL to render unwrapped instrument views" << std::endl;
filestr << "#MantidOptions.InstrumentView.UseOpenGL=Off" << std::endl;
Campbell, Stuart
committed
filestr.close();
} catch (std::runtime_error& ex)
Campbell, Stuart
committed
{
Gigg, Martyn Anthony
committed
g_log.warning() << "Unable to write out user.properties file to " << getUserPropertiesDir()
Campbell, Stuart
committed
<< m_user_properties_file_name << " error: " << ex.what() << std::endl;
Campbell, Stuart
committed
}
Campbell, Stuart
committed
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
/**
* Provides a default Configuration string to use if the config file cannot be loaded.
* @returns The string value of default properties
*/
std::string ConfigServiceImpl::defaultConfig() const
{
std::string propFile = "# logging configuration"
"# root level message filter (drop to debug for more messages)"
"logging.loggers.root.level = debug"
"# splitting the messages to many logging channels"
"logging.loggers.root.channel.class = SplitterChannel"
"logging.loggers.root.channel.channel1 = consoleChannel"
"logging.loggers.root.channel.channel2 = fileFilterChannel"
"# output to the console - primarily for console based apps"
"logging.channels.consoleChannel.class = ConsoleChannel"
"logging.channels.consoleChannel.formatter = f1"
"# specfic filter for the file channel raising the level to warning (drop to debug for debugging)"
"logging.channels.fileFilterChannel.class= FilterChannel"
"logging.channels.fileFilterChannel.channel= fileChannel"
"logging.channels.fileFilterChannel.level= warning"
"# output to a file (For error capturing and debugging)"
"logging.channels.fileChannel.class = debug"
"logging.channels.fileChannel.path = ../logs/mantid.log"
"logging.channels.fileChannel.formatter.class = PatternFormatter"
"logging.channels.fileChannel.formatter.pattern = %Y-%m-%d %H:%M:%S,%i [%I] %p %s - %t"
"logging.formatters.f1.class = PatternFormatter"
"logging.formatters.f1.pattern = %s-[%p] %t"
"logging.formatters.f1.times = UTC";
Campbell, Stuart
committed
return propFile;
}
//-------------------------------
// Public member functions
//-------------------------------
/**
* Removes the user properties file & loads a fresh configuration
*/
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);
}
Campbell, Stuart
committed
/** Updates and existing configuration and restarts the logging
Janik Zikovsky
committed
* @param filename :: The filename and optionally path of the file to load
* @param append :: If false (default) then any previous configuration is discarded,
Campbell, Stuart
committed
* otherwise the new keys are added, and repeated keys will override existing ones.
Janik Zikovsky
committed
* @param update_caches :: If true(default) then the various property caches are updated
Campbell, Stuart
committed
*/
void ConfigServiceImpl::updateConfig(const std::string& filename, const bool append,
const bool update_caches)
{
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());
*/
Campbell, Stuart
committed
if (update_caches)
{
// Only configure logging once
configureLogging();
Campbell, Stuart
committed
//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();
Gigg, Martyn Anthony
committed
appendDataSearchDir(getString("defaultsave.directory"));
cacheUserSearchPaths();
cacheInstrumentPaths();
Campbell, Stuart
committed
}
/**
* Save the configuration to the user file
Janik Zikovsky
committed
* @param filename :: The filename for the saved configuration
* @throw std::runtime_error if the file cannot be opened
Campbell, Stuart
committed
*/
void ConfigServiceImpl::saveConfig(const std::string & filename) const
{
// Open and read the user properties file
std::string updated_file("");
std::ifstream reader(filename.c_str(), std::ios::in);
if (reader.bad())
Campbell, Stuart
committed
throw std::runtime_error("Error opening user properties file. Cannot save updated configuration.");
Campbell, Stuart
committed
std::string file_line(""), output("");
bool line_continuing(false);
while (std::getline(reader, file_line))
Campbell, Stuart
committed
if (!file_line.empty())
Campbell, Stuart
committed
char last = *(file_line.end() - 1);
if (last == '\\')
// If we are not in line continuation mode then need
// a fresh start line
Janik Zikovsky
committed
if (!line_continuing)
output = "";
Campbell, Stuart
committed
line_continuing = true;
output += file_line + "\n";
Gigg, Martyn Anthony
committed
continue;
Campbell, Stuart
committed
else if (line_continuing)
Campbell, Stuart
committed
output += file_line;
line_continuing = false;
Campbell, Stuart
committed
output = file_line;
Campbell, Stuart
committed
else
Campbell, Stuart
committed
output = "";
Gigg, Martyn Anthony
committed
updated_file += "\n";
Campbell, Stuart
committed
continue;
Robert Whitley
committed
} //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
Campbell, Stuart
committed
}
Robert Whitley
committed
else
Campbell, Stuart
committed
{
Robert Whitley
committed
key = output.substr(0,pos); //Strip the equals to get only the key
Robert Whitley
committed
//Now deal with trimming (removes spaces)
Poco::trimInPlace(key);
//Find the comments
std::string::size_type comment = key.find('#');
Robert Whitley
committed
//Check if it exists in the service using hasProperty and make sure it isn't a comment
if(comment == 0)
Campbell, Stuart
committed
{
updated_file += output;
}
else if(!hasProperty(key))
{
//Remove the key from the changed key list
m_changed_keys.erase(key);
continue;
Campbell, Stuart
committed
}
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 += key + "=" + value;
//Remove the key from the changed key list
m_changed_keys.erase(key);
Robert Whitley
committed
}
updated_file += "\n";
Robert Whitley
committed
} // End while-loop
Campbell, Stuart
committed
// Any remaining keys within the changed key store weren't present in the current user properties so append them
if (!m_changed_keys.empty())
Campbell, Stuart
committed
updated_file += "\n";
std::set<std::string>::iterator key_end = m_changed_keys.end();
for (std::set<std::string>::iterator key_itr = m_changed_keys.begin(); key_itr != key_end;)
Campbell, Stuart
committed
updated_file += *key_itr + "=";
std::string value = getString(*key_itr, false);
Poco::replaceInPlace(value,"\\","\\\\"); //replace single \ with double
updated_file += value;
Campbell, Stuart
committed
if (++key_itr != key_end)
Campbell, Stuart
committed
updated_file += "\n";
Campbell, Stuart
committed
m_changed_keys.clear();
Campbell, Stuart
committed
// Write out the new file
std::ofstream writer(filename.c_str(), std::ios_base::trunc);
if (writer.bad())
Campbell, Stuart
committed
writer.close();
g_log.error() << "Error writing new user properties file. Cannot save current configuration.\n";
throw std::runtime_error(
"Error writing new user properties file. Cannot save current configuration.");
}
Campbell, Stuart
committed
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.
*
Janik Zikovsky
committed
* @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.
Campbell, Stuart
committed
* @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)
{
std::map<std::string, std::string>::const_iterator mitr = m_AbsolutePaths.find(keyName);
if (mitr != m_AbsolutePaths.end())
Campbell, Stuart
committed
return (*mitr).second;
Campbell, Stuart
committed
std::string retVal;
try
Campbell, Stuart
committed
retVal = m_pConf->getString(keyName);
if( retVal == m_removedFlag ) retVal = "";
Campbell, Stuart
committed
} catch (Poco::NotFoundException&)
Campbell, Stuart
committed
g_log.debug() << "Unable to find " << keyName << " in the properties file" << std::endl;
retVal = "";
Campbell, Stuart
committed
return retVal;
}
Robert Whitley
committed
/** 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;
Robert Whitley
committed
std::vector<std::string> keyVector;
keyVector.reserve(rawKeys.size());
Robert Whitley
committed
try
{
m_pConf->keys(keyName,rawKeys);
// Work around a limitation of Poco < v1.4 which has no remove functionality so
// check those that have been marked with the correct flag
const size_t nraw = rawKeys.size();
for( size_t i = 0; i < nraw; ++i )
{
const std::string key = rawKeys[i];
try
{
if( m_pConf->getString(key) == m_removedFlag ) continue;
}
catch (Poco::NotFoundException&)
{
}
keyVector.push_back(key);
}
Robert Whitley
committed
}
catch (Poco::NotFoundException&)
{
g_log.debug() << "Unable to find " << keyName << " in the properties file" << std::endl;
keyVector.clear();
}
return keyVector;
}
Robert Whitley
committed
/** 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
Robert Whitley
committed
*/
void ConfigServiceImpl::remove(const std::string& rootName) const
{
try
{
// m_pConf->remove(rootName) will only work in Poco v >=1.4. Current Ubuntu and RHEL use 1.3.x
// Simulate removal by marking with a flag value
m_pConf->setString(rootName, m_removedFlag);
Robert Whitley
committed
}
catch (Poco::NotFoundException&)
{
g_log.debug() << "Unable to find " << rootName << " in the properties file" << std::endl;
}
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.
Robert Whitley
committed
* @returns Boolean value denoting whether the exists or not.
*/
bool ConfigServiceImpl::hasProperty(const std::string& rootName) const
{
// Work around a limitation of Poco < v1.4 which has no remove functionality
return m_pConf->hasProperty(rootName) && m_pConf->getString(rootName) != m_removedFlag;
Robert Whitley
committed
}
/** 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.
Robert Whitley
committed
*
* @param target :: The path to the file you wish to see whether it's an executable.
Robert Whitley
committed
* @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);
Robert Whitley
committed
if (tempFile.exists())
{
if(tempFile.canExecute())
return true;
else
return false;
}
else
return false;
}
catch(Poco::Exception&)
{
return false;
}
}
Robert Whitley
committed
/** 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.
*
Robert Whitley
committed
* @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);
Robert Whitley
committed
}
catch(Poco::SystemException &e)
{
throw std::runtime_error(e.what());
}
}
Campbell, Stuart
committed
/**
* Set a configuration property. An existing key will have its value updated.
Janik Zikovsky
committed
* @param key :: The key to refer to this property
* @param value :: The value of the property
Campbell, Stuart
committed
*/
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;
Janik Zikovsky
committed
Campbell, Stuart
committed
//Ensure we keep a correct full path
std::map<std::string, bool>::const_iterator itr = m_ConfigPaths.find(key);
if (itr != m_ConfigPaths.end())
Campbell, Stuart
committed
m_AbsolutePaths[key] = makeAbsolute(value, key);
Gigg, Martyn Anthony
committed
if (key == "datasearch.directories" )
Campbell, Stuart
committed
cacheDataSearchPaths();
else if (key == "usersearch.directories")
{
cacheUserSearchPaths();
}
else if (key == "instrumentDefinition.directory")
{
cacheInstrumentPaths();
else if (key == "defaultsave.directory")
{
Gigg, Martyn Anthony
committed
appendDataSearchDir(value);
}
Campbell, Stuart
committed
m_pConf->setString(key, value);
Janik Zikovsky
committed
m_notificationCenter.postNotification(new ValueChanged(key, value, old));
m_changed_keys.insert(key);
Campbell, Stuart
committed
}
/** Searches for a string within the currently loaded configuaration values and
* attempts to convert the values to the template type supplied.
*
Janik Zikovsky
committed
* @param keyName :: The case sensitive name of the property that you need the value of.
* @param out :: The value if found
Campbell, Stuart
committed
* @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);
Janik Zikovsky
committed
int result = Mantid::Kernel::Strings::convert(strValue, out);
Campbell, Stuart
committed
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
}
Campbell, Stuart
committed
/**
* Return the full filename of the user properties file
* @returns A string containing the full path to the user file
Campbell, Stuart
committed
*/
std::string ConfigServiceImpl::getUserFilename() const
{
Gigg, Martyn Anthony
committed
return getUserPropertiesDir() + m_user_properties_file_name;
Campbell, Stuart
committed
}
/** Searches for the string within the environment variables and returns the
* value as a string.
*
Janik Zikovsky
committed
* @param keyName :: The name of the environment variable that you need the value of.
Campbell, Stuart
committed
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
* @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 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();
}
Campbell, Stuart
committed
/** 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");
}
Campbell, Stuart
committed
/** 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
}
Gigg, Martyn Anthony
committed
/**
* 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
Gigg, Martyn Anthony
committed
{
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
Gigg, Martyn Anthony
committed
{
std::string execpath("");
const size_t LEN(1024);
// cppcheck-suppress variableScope
Gigg, Martyn Anthony
committed
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
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
Gigg, Martyn Anthony
committed
// Not yet implemented for the mac
return false;
#endif
}
/**
* Set the environment variable for the PV_PLUGIN_PATH based on where Mantid is.
*/
void ConfigServiceImpl::setParaViewPluginPath() const
{
std::string mantid_loc = this->getDirectoryOfExecutable();
Poco::Path pv_plugin_path(mantid_loc + "/pvplugins/pvplugins"); // Developer build paths
pv_plugin_path = pv_plugin_path.absolute();
g_log.debug() << "Trying " << pv_plugin_path.toString() << " as PV_PLUGIN_PATH\n";
Poco::File pv_plugin(pv_plugin_path.toString());
if (!pv_plugin.exists() || !pv_plugin.isDirectory())
{
// Installation paths
g_log.debug("ParaView plugin directory \"" + pv_plugin.path() + "\" does not exist. Trying properties file location.");
std::string user_loc = this->getString("pvplugins.directory");
if(user_loc.empty())
{
g_log.debug("No ParaView plugin directory specified in the properties file.");
return; // it didn't work
}
pv_plugin_path = Poco::Path(user_loc, "pvplugins");
pv_plugin_path = pv_plugin_path.absolute();
pv_plugin = Poco::File(pv_plugin_path.toString());
if (!pv_plugin.exists() || !pv_plugin.isDirectory())
{
g_log.debug("ParaView plugin directory \"" + pv_plugin.path() + "\" does not exist");
return; // it didn't work
}
// one of the two choices worked so set to that directory
g_log.debug("Setting PV_PLUGIN_PATH = \"" + pv_plugin.path() + "\"");
Poco::Environment::set("PV_PLUGIN_PATH", pv_plugin.path());
}
Gigg, Martyn Anthony
committed
Campbell, Stuart
committed
/**
Gigg, Martyn Anthony
committed
* 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.
Campbell, Stuart
committed
* @returns The directory to consider as the base directory, including a trailing slash
*/
Gigg, Martyn Anthony
committed
std::string ConfigServiceImpl::getPropertiesDir() const
Campbell, Stuart
committed
{
return m_strBaseDir;
}
/**
Gigg, Martyn Anthony
committed
* 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
Campbell, Stuart
committed
* so that filenames can more easily be concatenated with this
* @return the directory that Mantid should use for writing files
*/
Gigg, Martyn Anthony
committed
std::string ConfigServiceImpl::getUserPropertiesDir() const
Campbell, Stuart
committed
{
Gigg, Martyn Anthony
committed
#ifdef _WIN32
Campbell, Stuart
committed
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
}
Campbell, Stuart
committed
/**
* 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;
}
Gigg, Martyn Anthony
committed
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
/**
* 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);
}
/**
* 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;
Gigg, Martyn Anthony
committed
Poco::Path dirPath;
try
{
dirPath = Poco::Path(path);
dirPath.makeDirectory();
}
catch(Poco::PathSyntaxException &)
{
return;
}
if (!isInDataSearchList(dirPath.toString()))
Gigg, Martyn Anthony
committed
{
std::string newSearchString;
std::vector<std::string>::const_iterator it = m_DataSearchDirs.begin();
Janik Zikovsky
committed
for (; it != m_DataSearchDirs.end(); ++it)
Gigg, Martyn Anthony
committed
{
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;
}
/**
* 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
Campbell, Stuart
committed
{
return m_InstrumentDirs[m_InstrumentDirs.size()-1];
}
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
/**
* Fills the internal cache of instrument definition directories
*/
void ConfigServiceImpl::cacheInstrumentPaths()
{
m_InstrumentDirs.clear();
Poco::Path path(getAppDataDir());
path.makeDirectory();
path.pushDirectory("instrument");
std::string appdatadir = path.toString();
addDirectoryifExists(appdatadir,m_InstrumentDirs);
#ifndef _WIN32
std::string etcdatadir = "/etc/mantid/instrument";
addDirectoryifExists(etcdatadir,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;
}
Campbell, Stuart
committed
}
/**
* Load facility information from instrumentDir/Facilities.xml file if fName parameter
* is not set
Janik Zikovsky
committed
* @param fName :: An alternative file name for loading facilities information.
Campbell, Stuart
committed
*/
void ConfigServiceImpl::updateFacilities(const std::string& fName)
{
clearFacilities();
Campbell, Stuart
committed
std::string instrDir = getString("instrumentDefinition.directory");
std::string fileName = fName.empty() ? instrDir + "Facilities.xml" : fName;
// Set up the DOM parser and parse xml file
Poco::XML::DOMParser pParser;
Poco::AutoPtr<Poco::XML::Document> pDoc;
Roman Tolchenov
committed
Campbell, Stuart
committed
try
Roman Tolchenov
committed
try
{
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");
unsigned long n = pNL_facility->length();
Campbell, Stuart
committed
for (unsigned long i = 0; i < n; ++i)
Roman Tolchenov
committed
{
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())
Roman Tolchenov
committed
throw std::runtime_error("The facility definition file " + fileName + " defines no facilities");
Janik Zikovsky
committed
} catch (std::exception& e)
Roman Tolchenov
committed
{
Roman Tolchenov
committed
g_log.error(e.what());
Roman Tolchenov
committed
}
Campbell, Stuart
committed
}
/// Empty the list of facilities, deleting the FacilityInfo objects in the process
void ConfigServiceImpl::clearFacilities()
{
for (auto it = m_facilities.begin(); it != m_facilities.end(); ++it)
{
delete *it;
}
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 << "." << std::endl;
return getFacility(defaultFacility).instrument(instrumentName);
Gigg, Martyn Anthony
committed
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
std::vector<FacilityInfo*>::const_iterator it = m_facilities.begin();
for (; it != m_facilities.end(); ++it)
{
try
{
g_log.debug() << "Looking for " << instrumentName << " at " << (**it).name() << "." << std::endl;
return (**it).instrument(instrumentName);
Gigg, Martyn Anthony
committed
catch (Exception::NotFoundError &)
{
// Well the instName doesn't exist for this facility...
// Move along, there's nothing to see here...
}
}
g_log.debug("Instrument " + instrumentName + " not found");
throw Exception::NotFoundError("Instrument", instrumentName);
}
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
/** 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
Campbell, Stuart
committed
*/
const FacilityInfo& ConfigServiceImpl::getFacility() const
Campbell, Stuart
committed
{
std::string defFacility = getString("default.facility");
if (defFacility.empty())
Roman Tolchenov
committed
{
Campbell, Stuart
committed
defFacility = "ISIS";
Roman Tolchenov
committed
}
Michael Whitty
committed
}
Campbell, Stuart
committed
/**
* Get a facility
* @param facilityName :: Facility name
* @return the facility information object
Janik Zikovsky
committed
* @throw NotFoundException if the facility is not found
Campbell, Stuart
committed
*/
const FacilityInfo& ConfigServiceImpl::getFacility(const std::string& facilityName) const
Campbell, Stuart
committed
{
if (facilityName.empty())
return this->getFacility();
Campbell, Stuart
committed
std::vector<FacilityInfo*>::const_iterator it = m_facilities.begin();
for (; it != m_facilities.end(); ++it)
Roman Tolchenov
committed
{
if ((**it).name() == facilityName)
Roman Tolchenov
committed
{
Campbell, Stuart
committed
return **it;
Roman Tolchenov
committed
}
}
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
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 == false)
{
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);
Campbell, Stuart
committed
}
/*
Ammend paths to point to include the paraview core libraries.
@param path : path to add
*/
void ConfigServiceImpl::setParaviewLibraryPath(const std::string& path)
{
#ifdef _WIN32
const std::string platformPathName = "PATH";
Poco::Path existingPath;
char separator = existingPath.pathSeparator();
std::string strSeparator;
strSeparator.push_back(separator);
if(Poco::Environment::has(platformPathName))
{
existingPath = Poco::Environment::get(platformPathName);
existingPath.append(strSeparator + path);
}
else
{
existingPath = path;
}
const std::string newPath = existingPath.toString();
Poco::Environment::set(platformPathName, newPath);
#elif defined __linux__
throw std::runtime_error("Cannot dynamically set the library path on Linux");
#elif defined __APPLE__
throw std::runtime_error("Cannot dynamically set the library path on Mac");
#else
throw std::runtime_error("ConfigServiceImpl::setParaviewLibraryPath cannot determine the running platform and therefore cannot set the path to the Paraview libraries.");
#endif
/*
Extracts the string from a poco pipe and returns the numerical part.
@param pipe : input pipe.
@return the numerical part of the version string contained inside the pipe.
*/
const std::string extractVersionNumberFromPipe(const Poco::Pipe& pipe)
{
std::string versionString = "undetermined";
Poco::PipeInputStream pipeStream(pipe);
std::stringstream stringStream;
Poco::StreamCopier::copyStream(pipeStream, stringStream);
const std::string givenVersion = stringStream.str();
boost::smatch match;
boost::regex expression("(\\d+)\\.(\\d+)\\.?(\\d*)$"); // Gets the version number part.
if(boost::regex_search(givenVersion, match, expression))
{
versionString = match[0];
}
return versionString;
}
Checks to see whether paraview usage is explicitly ignored in the property file then,
quick check to determine if paraview is installed. We make the assumption
that if the executable paraview binary is on the path that the paraview libraries
will also be available on the library path, or equivalent.
@return True if paraview is available or not disabled.
*/
bool ConfigServiceImpl::quickParaViewCheck() const
{
const std::string paraviewIgnoreProperty = "paraview.ignore";
const bool ignoreParaview = hasProperty(paraviewIgnoreProperty) && atoi(getString(paraviewIgnoreProperty).c_str());
if(ignoreParaview)
{
g_log.debug("Ignoring ParaView");
g_log.debug("Checking for ParaView");
bool isAvailable = false;
try
{
//Try to run "paraview -V", which will succeed if ParaView is installed.
std::string paraviewDir = getString("paraview.path");
std::string cmd = "paraview";
if(!paraviewDir.empty())
{
Poco::Path paraviewExe = Poco::Path(paraviewDir, "paraview");
cmd = paraviewExe.toString();
}
std::vector<std::string> args;
args.push_back("-V");
Poco::Pipe outPipe, errorPipe;
Poco::ProcessHandle ph = Poco::Process::launch(cmd, args, 0, &outPipe, &errorPipe);
// Only if the paraview query returned successfully.
// Check the actual version numbers against what we expect they should be.
const std::string givenVersionNumber = extractVersionNumberFromPipe(errorPipe);
const std::string targetVersionNumber = ParaViewVersion::targetVersion();
if (givenVersionNumber == targetVersionNumber)
{
isAvailable = true;
g_log.information("ParaView is available");
// Now set the plugin path.
this->setParaViewPluginPath();
}
else
{
std::stringstream messageStream;
messageStream << "The compatible version of ParaView is " << targetVersionNumber << " but the installed version is " << givenVersionNumber;
g_log.debug(messageStream.str());
g_log.information("ParaView is not available");
std::stringstream messageStream;
messageStream << "ParaView version query failed with code: " << rc;
g_log.debug(messageStream.str());
g_log.information("ParaView is not available");
g_log.debug(e.what());
g_log.information("ParaView is not available");
}
return isAvailable;
}
/*
Quick check to determine if VATES is installed.
@return TRUE if available.
*/
bool ConfigServiceImpl::quickVatesCheck() const
{
std::string path = this->getDirectoryOfExecutable();
Poco::File dir(path);
typedef std::vector<std::string> VecFiles;
VecFiles files;
dir.list(files);
VecFiles::iterator it = files.begin();
bool found = false;
while(it != files.end())
{
std::string file = *it;
boost::regex expression("^(VatesSimpleGui)", boost::regex::icase);
if(boost::regex_search(file, expression))
{
found = true;
break;
}
++it;
}
return found;
}
Campbell, Stuart
committed
/// \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&);
Campbell, Stuart
committed
/// \endcond TEMPLATE
} // namespace Kernel
} // namespace Mantid