3 #include <fwk/CentralConfig.h>
4 #include <fwk/GITGlobalRevision.h>
5 #include <fwk/VModule.h>
7 #include <utl/ReaderErrorReporter.h>
8 #include <utl/ErrorLogger.h>
9 #include <utl/Reader.h>
10 #include <utl/XercesUtil.h>
11 #include <utl/StringCompare.h>
13 #include <fwk/RunController.h>
15 #include "CentralConfig/Md5Fingerprints.icc"
18 #include <xercesc/dom/DOM.hpp>
24 #include <boost/filesystem.hpp>
25 #include <boost/process.hpp>
32 using namespace xercesc;
33 namespace bfs = boost::filesystem;
46 switch (n.getNodeType()) {
47 case DOMNode::TEXT_NODE:
49 case DOMNode::COMMENT_NODE:
52 return "!!!unhandled DOMNode type " + to_string(n.getNodeType()) +
"!!!";
62 for (
const auto& kv : attr)
63 os <<
' ' << kv.first <<
"=\"" << kv.second <<
'\"';
72 return dladdr((
void*)
GetLibraryPath, &dlInfo) ? dlInfo.dli_fname :
"";
81 if (libPath && libPath[0])
82 fInstallPath = bfs::path(libPath).parent_path().parent_path().string();
88 delete fBootstrapReader;
90 for (
const auto&
c : fConfigMap)
91 delete c.second.GetReader();
93 fgIsInitialized =
false;
111 const bool fingerprintFatal,
114 fgValidate = validate;
120 static string bootstrapFile;
122 if (!fgIsInitialized) {
127 fgIsInitialized =
true;
128 bootstrapFile = name;
130 }
else if (bootstrapFile != name) {
132 INFO(
"You have requested the instance of CentralConfig "
133 "specifying a bootstrap file. "
134 "However, in a previous instantiation, you requested "
135 "a different bootstrap file. This is not "
136 "allowed. Not changing the configuraton.");
146 const bool fingerprintFatal,
149 const string b = fgInstance->GetBootstrapUri();
150 fgInstance =
nullptr;
151 fgIsInitialized =
false;
152 if (bootstrapFileName.empty())
162 if (fBootstrapReader)
165 fBootstrapReader =
new Reader(bootstrapFile, Reader::eSCHEMA);
170 if (severityBranch) {
173 severityBranch.
GetData(severity);
174 if (severity ==
"debug")
176 else if (severity ==
"info")
178 else if (severity ==
"warning")
180 else if (severity ==
"error")
182 else if (severity ==
"fatal")
191 const string& name = ccB.GetName();
192 if (name ==
"centralConfig" || name ==
"defaultConfig")
194 else if (name ==
"parameterOverrides")
195 ReplaceParameters(ccB);
216 "The first time you call CentralConfig::GetInstance(...), "
217 "you MUST pass the name of the bootstrap configuration file"
231 const Reader*
const reader = GetReader(
id);
236 msg <<
"Cannot find Configuration file for <configLink> with id=\""
248 fUsedConfigs.emplace(
id);
250 const auto it = fConfigMap.find(
id);
252 if (it != fConfigMap.end()) {
254 Reader*
const reader = it->second.GetReader();
259 msg <<
"You requested a Reader for a file format "
260 "which cannot be accessed by the Reader "
262 << it->second.fXLink <<
"' of type "
263 << it->second.fFileType;
269 msg <<
"Cannot find requested Id: '" <<
id <<
'\'';
280 if (fBootstrapReader)
281 return fBootstrapReader->GetUri();
308 typedef map<string, list<string>> OvConfig;
314 OvConfig overriddenConfigs;
318 if (cB.GetName() ==
"configLink") {
324 const auto it = attMap.find(
"id");
325 if (it != attMap.end())
328 const auto it = attMap.find(
"ID");
329 if (it != attMap.end())
332 AbortParse(
"<configLink> found without 'id' attribute.");
338 const auto it = attMap.find(
"type");
339 if (it == attMap.end())
340 AbortParse(
"<configLink> found without 'type' attribute.");
344 const auto it = attMap.find(
"xlink:href");
345 if (it == attMap.end())
346 AbortParse(
"<configLink> found without 'xlink:href' attribute.");
347 configLink.
fXLink = it->second;
353 if (configLink.
fXLink[0] ==
'#') {
355 const string path = configLink.
fXLink.substr(1);
357 Branch branch = Find(path);
360 const string mybranch =
AsString(branch);
364 AbortParse(
"Failure to parse from input string. " + ex.
GetMessage());
367 }
else if (!fgValidate) {
370 msg <<
"Explicit request to parse a file without validation."
371 "Filename = " << configLink.
fXLink;
375 AbortParse(
"Failure to parse with (validation switched off). " + ex.
GetMessage());
387 const auto previousConfigLink = fConfigMap.find(
id);
389 if (previousConfigLink != fConfigMap.end()) {
391 if (previousConfigLink->second.fXLink != configLink.
fXLink) {
393 auto& ov = overriddenConfigs[id];
395 ov.push_back(previousConfigLink->second.fXLink);
396 ov.push_back(configLink.
fXLink);
399 delete previousConfigLink->second.fReader;
400 fConfigMap.erase(previousConfigLink);
405 fConfigMap.emplace(
id, configLink);
412 if (!overriddenConfigs.empty()) {
414 msg <<
"The following configuration files have been overridden. "
415 "Files sorted from original to newest override:";
416 for (
const auto&
c : overriddenConfigs) {
417 const string&
id =
c.first;
418 const list<string>& files =
c.second;
421 msg <<
"\n * Id: '" <<
id <<
"', files:";
422 const unsigned int n = files.size();
424 for (
const auto& f : files) {
427 msg <<
"(original )";
429 msg <<
"(replaced by)";
432 msg <<
" '" << f <<
'\'';
439 static bool checkedAlready =
false;
440 if (!checkedAlready) {
441 checkedAlready =
true;
443 map<string, set<string>> dupBrCl;
444 for (
auto it = fConfigMap.begin(), end = fConfigMap.end(); it != end; ++it) {
445 if (
const Reader*
const ri = it->second.GetReader()) {
446 const string& topBranchName = ri->GetTopBranch().GetName();
447 for (
auto jt =
next(it); jt != end; ++jt) {
448 if (
const Reader*
const rj = jt->second.GetReader()) {
449 if (topBranchName == rj->GetTopBranch().GetName()) {
450 auto& d = dupBrCl[topBranchName];
458 if (!dupBrCl.empty()) {
460 for (
const auto& d : dupBrCl) {
461 warn <<
"The same top branch <" << d.first <<
"> has been "
462 "encountered in the following <configLink>s with ids:";
463 for (
const auto&
c : d.second)
485 const auto atts = cB.GetAttributes();
486 const auto idIt = atts.find(
"id");
487 if (idIt != atts.end()) {
488 const auto configIt = fConfigMap.find(idIt->second);
489 if (configIt != fConfigMap.end()) {
490 const ConfigLink& readerLink = configIt->second;
495 if (replacementBranch)
496 DescendAndReplace(replacementBranch, originalBranch);
499 msg <<
"The <configLink> with attributes " <<
AsXML(cB.GetAttributes())
500 <<
" has no children. No configuration data will be replaced for that <configLink>.";
507 Reader validationReader(readerFromStringInput, Reader::eSCHEMA);
511 msg <<
"The <parameterOverrides> portion of the bootstrap file specifies "
512 "a <configLink> with " <<
AsXML(cB.GetAttributes()) <<
", "
513 "but no matching <configLink> is found in the bootstrap file.";
537 while (replacementBranch) {
539 const string replacementBranchName = replacementBranch.
GetName();
540 const string originalBranchName = originalBranch.
GetName();
546 replacementBranchAtts.erase(
"unit");
547 replacementBranchAtts.erase(
"UNIT");
550 (replacementBranchName != originalBranchName ||
551 replacementBranchAtts != originalBranchAtts)) {
552 originalBranch = originalBranch.
GetSibling(replacementBranchName, replacementBranchAtts);
555 if (!originalBranch) {
557 msg <<
"Failure while attempting to override configuration parameters. "
558 "An override was requested for a branch "
559 "<" << replacementBranchName <<
AsXML(replacementBranchAtts) <<
"> "
561 "<" << originalBranchName <<
AsXML(originalBranchAtts) <<
"> "
562 "was found instead at this place in the original configuration file.";
567 string replacementData;
568 replacementBranch.
GetData(replacementData);
571 if (replacementBranch.
GetFirstChild() && !replacementData.empty()) {
573 msg <<
"Problem encountered while trying to replace branch "
574 "<" << originalBranchName <<
AsXML(originalBranchAtts) <<
"> "
576 "<" << replacementBranchName <<
AsXML(replacementBranchAtts) <<
">. "
577 "Note that replacing a branch, which contains both data and "
578 "sub-branches, is not supported.";
583 if (!replacementData.empty()) {
587 msg <<
"Problem encountered while trying to replace branch "
588 "<" << originalBranchName <<
AsXML(originalBranchAtts) <<
"> "
590 "<" << replacementBranchName <<
AsXML(replacementBranchAtts) <<
">. "
591 "Cannot replace a branch which contains both data and sub-branches.";
598 if (dom->getChildNodes()->getLength() != 1) {
600 msg <<
"The data in the original branch "
601 "<" << originalBranchName <<
AsXML(originalBranchAtts) <<
"> "
602 "is unexpectedly split-up into several strings (probably by a comment) and thus "
603 "cannot be simply replaced with the string of the replacement branch "
604 "<" << replacementBranchName <<
AsXML(replacementBranchAtts) <<
">. "
605 "Please remove any comments in the data string of the original branch.\n"
606 "original branch = ";
607 auto c = dom->getFirstChild();
612 for ( ;
c;
c =
c->getNextSibling())
615 msg <<
"\nreplacement branch = \"" << replacementBranch.
Get<
string>() <<
'"';
620 const auto doc = dom->getOwnerDocument();
622 const auto replacementText =
623 doc->createTextNode(
XercesPtrX(XMLString::transcode(replacementData.c_str())).Get());
625 dom->replaceChild(replacementText, dom->getFirstChild())->release();
628 DOMAttr* replacementUnitAtt =
nullptr;
629 DOMElement*
const replacementElement =
dynamic_cast<DOMElement*
>(replacementBranch.
GetDOMNode());
630 const XMLCh* replacementUnit =
nullptr;
632 if (replacementElement) {
633 const XercesPtrX attrName(XMLString::transcode(
"unit"));
634 replacementUnitAtt = replacementElement->getAttributeNode(attrName.
Get());
635 if (!replacementUnitAtt) {
636 const XercesPtrX attrName(XMLString::transcode(
"UNIT"));
637 replacementUnitAtt = replacementElement->getAttributeNode(attrName.
Get());
640 if (replacementUnitAtt)
641 replacementUnit = replacementUnitAtt->getValue();
643 DOMAttr* originalUnitAtt =
nullptr;
644 DOMElement*
const originalElement =
dynamic_cast<DOMElement*
>(originalBranch.
GetDOMNode());
646 if (originalElement) {
647 const XercesPtrX attrName(XMLString::transcode(
"unit"));
648 originalUnitAtt = originalElement->getAttributeNode(attrName.
Get());
649 if (!originalUnitAtt) {
650 const XercesPtrX attrName(XMLString::transcode(
"UNIT"));
651 originalUnitAtt = originalElement->getAttributeNode(attrName.
Get());
654 if (originalUnitAtt && replacementUnitAtt)
655 originalUnitAtt->setValue(replacementUnit);
662 if (replacementChild && originalChild)
663 DescendAndReplace(replacementChild, originalChild);
674 if (!fConfigTimeFingerMap.size())
675 FillConfigTimeFingerMap();
677 list<string> multipleMD5List;
679 for (
const auto& runConfig : fConfigMap) {
681 if (fMd5Excludes.find(runConfig.first) != fMd5Excludes.end())
684 const string& linkName = runConfig.second.GetLink();
690 if (linkName.empty() || linkName[0] ==
'#')
693 const auto cmd =
"openssl dgst -md5 " + linkName;
695 namespace bp = boost::process;
697 bp::child pipe(cmd, bp::std_out > is);
698 typedef std::istreambuf_iterator<char> IstrIt;
699 const std::string fullOut{IstrIt{is}, IstrIt{}};
701 if (pipe.exit_code())
702 WARNING(str(boost::format(
"checksum command `%1%` returned status %2%") % cmd % pipe.exit_code()));
704 if (fullOut.empty()) {
705 WARNING(
"received no output from '" + cmd +
"'");
709 string runFinger = fullOut.substr(fullOut.find(
"=") + 1);
710 boost::trim(runFinger);
717 const auto start = linkName.rfind(
"/") + 1;
718 const auto length = linkName.rfind(
".xml") - start;
719 const string strippedName = linkName.substr(start, length);
721 const auto fileMatch = fConfigTimeFingerMap.equal_range(strippedName);
722 int nFilenameMatches = 0;
723 bool fingerprintMatch =
false;
724 for (
auto fingerIt = fileMatch.first; fingerIt != fileMatch.second; ++fingerIt) {
726 if (fingerIt->second == runFinger)
727 fingerprintMatch =
true;
730 if (!nFilenameMatches)
731 fNoMd5List.push_back(strippedName);
732 else if (nFilenameMatches > 1)
733 multipleMD5List.push_back(strippedName);
734 else if (!fingerprintMatch)
735 fMismatchedMd5List.push_back(strippedName);
740 if (!fNoMd5List.empty()) {
742 msg <<
"No configuration-time MD5 fingerprint found for the following configuration files. "
743 <<
"Checking the validity of these configuration files is impossible.";
744 for (
const auto&
file : fNoMd5List)
745 msg <<
"\n - " <<
file;
750 if (!multipleMD5List.empty()) {
752 msg <<
"More than one configuration-time md5 fingerprint found corresponding to each of the "
753 <<
"following configuration files. If at least one matching fingerprint is found, I "
754 <<
"will continue execution. But you have been warned.";
755 for (
const auto&
file : multipleMD5List)
756 msg <<
"\n - " <<
file;
761 if (!fMismatchedMd5List.empty()) {
763 msg <<
"Incorrect MD5 fingerprints were found for the following configuration files. "
764 <<
"This implies this configuration file has been altered.";
765 for (
const auto&
file : fMismatchedMd5List)
766 msg <<
"\n - " <<
file;
767 if (fingerprintFatal) {
779 ofstream
out(fileName.c_str());
790 if (fConfigInfoIsValid)
791 return fConfigInfo.str();
792 fConfigInfoIsValid =
true;
799 fConfigInfo <<
"<?xml version='1.0' encoding='iso-8859-1'?>\n"
800 "<bootstrap xmlns:xlink=\"http://www.auger.org/schema/types\">\n"
802 "<softwareVersion> " << OFFLINE_PACKAGE_STRING <<
" </softwareVersion>\n"
803 "<GITGlobalRevision> " << GITGlobalRevision::GetInstance().GetId() <<
" </GITGlobalRevision>\n";
805 if (fNoMd5List.size()) {
806 fConfigInfo <<
"<noMd5> <!-- no md5 fingerprint was found for these config files -->\n";
807 for (
const auto&
file : fNoMd5List)
808 fConfigInfo <<
" " <<
file <<
'\n';
809 fConfigInfo <<
"</noMd5>\n";
811 if (fMismatchedMd5List.size()) {
812 fConfigInfo <<
"<mismatchedMd5> "
813 "<!-- the md5 fingerprints for these config files do not match the defaults -->\n";
814 for (
const auto&
file : fMismatchedMd5List)
815 fConfigInfo <<
" " <<
file <<
'\n';
816 fConfigInfo <<
"</mismatchedMd5>\n";
819 fConfigInfo <<
"<moduleVersions>\n";
821 const auto& usedModules = RunController::GetInstance().GetUsedModuleNames();
822 for (
const auto& name : usedModules) {
823 VModule&
mod = RunController::GetInstance().GetModule(name);
826 " <logicalName> " << name <<
" </logicalName>\n"
833 fConfigInfo <<
"</moduleVersions>\n"
837 fConfigInfo <<
"</centralConfig>\n";
839 for (
const auto& conf : fUsedConfigs) {
841 const auto used = fConfigMap.find(conf);
842 if (used != fConfigMap.end()) {
850 fConfigInfo <<
'<' << conf <<
">\n"
851 " <" << topB.
GetName() <<
">\n";
856 fConfigInfo <<
" </" << topB.
GetName() <<
">\n"
857 "</" << conf <<
">\n";
863 fConfigInfo <<
"</bootstrap>\n";
865 return fConfigInfo.str();
874 for (
auto ccB = fBootstrapReader->GetTopBranch().GetFirstChild(); ccB; ccB = ccB.GetNextSibling()) {
875 if (ccB.GetName() ==
"centralConfig" || ccB.GetName() ==
"defaultConfig") {
876 ostringstream attributes;
877 for (
auto cLink = ccB.GetFirstChild(); cLink; cLink = cLink.GetNextSibling()) {
878 const auto attr = cLink.GetAttributes();
879 const string&
id = attr.find(
"id")->second;
880 for (
const auto&
a : attr) {
881 attributes <<
' ' <<
a.first <<
"=\"";
882 if (
a.first ==
"xlink:href")
883 attributes <<
'#' << id;
885 attributes <<
a.second;
888 for (
const auto&
c : fUsedConfigs) {
890 fConfigInfo <<
" <" << cLink.GetName() << attributes.str() <<
"/>\n";
906 while (path.find(
"/") != string::npos) {
907 name = path.substr(0, path.find(
"/"));
908 path = path.substr(path.find(
"/") + 1);
919 stringstream contents;
921 contents << string(indent,
' ') <<
'<' << branch.
GetName();
923 stringstream attrString;
925 attrString <<
' ' <<
a.first <<
"=\"" <<
a.second <<
'\"';
926 contents << attrString.str() <<
'>';
931 contents <<
' ' << branch.
Get<
string>() <<
' ' <<
"</" << branch.
GetName() <<
">\n";
936 contents <<
AsString(child, indent + indentIncrement, indentIncrement);
939 contents << string(indent,
' ') <<
"</" << branch.
GetName() <<
">\n";
942 return contents.str();
964 msg <<
"Aborting configuration because of parse error. " <<
s;
Branch GetTopBranch() const
void SetWarning(const std::string &wrn)
IdIterator IdsEnd()
Id's end.
static bool fgIsInitialized
void DescendAndReplace(utl::Branch replacement, utl::Branch &original)
Class to handle routing and writing of error messages.
void CheckFingerprints(const bool fingerprintFatal)
std::string AsString(const XMLCh *const xStr)
container for a file, file type id, and corresponding utl::Reader
void SetMinSeverity(const ESeverityLevel severity)
Set minimal severity for reporting.
std::string String() const
Dump the branch into a string.
Base class for exceptions arising because configuration data are not valid.
boost::transform_iterator< InternalIdFunctor, InternalConfigIterator, std::string > IdIterator
IdIterator returns a pointer to a config link Id.
xercesc::DOMNode * GetDOMNode() const
bool is(const double a, const double b)
std::map< std::string, std::string > AttributeMap
std::string GetConfig()
Get configuration in a string.
string AsString(DOMNode &n)
void FillMap(const utl::Branch &branch)
#define INFO(message)
Macro for logging informational messages.
static void Reset(const std::string &bootstrapFileName, const bool fingerprintFatal=false, const bool validate=true)
vector< t2list > out
output of the algorithm: a list of clusters
Branch GetChild(const std::string &childName) const
Get child of this Branch by child name.
std::string GetVersionInfo(const VersionInfoType v) const
Retrieve different sorts of module version info.
Exception for errors encountered when parsing XML.
bool StringEquivalent(const std::string &a, const std::string &b, Predicate p)
Utility to compare strings for equivalence. It takes a predicate to determine the equivalence of indi...
static CentralConfig * fgInstance
utl::Branch Find(std::string path)
Branch GetTopBranch() const
Get the top Branch (represents same entity as document node)
Base class to report exceptions in IO.
const char * GetLibraryPath()
AttributeMap GetAttributes() const
Get a map<string, string> containing all the attributes of this Branch.
Branch GetNextSibling() const
Get next sibling of this branch.
Utility for parsing XML files.
Iterator next(Iterator it)
Class representing a document branch.
void AbortParse(const std::string &s="")
#define DEBUGLOG(message)
Macro for logging debugging messages.
void ReplaceParameters(const utl::Branch &)
void ReadConfig(const std::string &bootstrapFile)
#define WARNING(message)
Macro for logging warning messages.
void GetData(bool &b) const
Overloads of the GetData member template function.
utl::Reader * GetReader() const
std::string GetName() const
function to get the Branch name
string AsXML(const AttributeMap &attr)
std::string AsString(const utl::Branch &branch, const int indent=0, const int indentIncrement=2)
const utl::Reader * GetReader(const std::string &id)
Get the Reader for moduleConfigLink with given id (XML files)
std::string GetBootstrapUri()
static CentralConfig * GetInstance()
Use this the first time you get an instance of central configuration.
XercesPtr< XMLCh > XercesPtrX
Main configuration utility.
Branch GetFirstChild() const
Get first child of this Branch.
void WriteConfig(const std::string &fileName="")
Get the link name for moduleConfigLink with given id (any)
Branch GetSibling(const std::string &childName) const
Get sibling by name.
double mod(const double d, const double periode)
#define ERROR(message)
Macro for logging error messages.
IdIterator IdsBegin()
Id's begin.
const std::string & GetMessage() const
Retrieve the message from the exception.
utl::Branch GetTopBranch(const std::string &id)
Get top branch for moduleConfigLink with given id (XML files)