otsdaq  v2_01_00
ConfigurationInfoReader.cc
1 #include "otsdaq-core/ConfigurationDataFormats/ConfigurationInfoReader.h"
2 #include "otsdaq-core/ConfigurationDataFormats/ConfigurationBase.h"
3 #include "otsdaq-core/XmlUtilities/DOMTreeErrorReporter.h"
4 #include "otsdaq-core/XmlUtilities/ConvertFromXML.h"
5 #include "otsdaq-core/Macros/StringMacros.h"
6 
7 //#include "TimeFormatter.h"
8 
9 
10 #include <xercesc/parsers/XercesDOMParser.hpp>
11 #include <xercesc/dom/DOMElement.hpp>
12 #include <xercesc/dom/DOMImplementation.hpp>
13 #include <xercesc/dom/DOMImplementationRegistry.hpp>
14 #include <xercesc/dom/DOMNodeList.hpp>
15 #include <xercesc/dom/DOMText.hpp>
16 //#include <xercesc/dom/DOMWriter.hpp>
17 
18 #include <xercesc/util/OutOfMemoryException.hpp>
19 #include <xercesc/framework/LocalFileFormatTarget.hpp>
20 
21 #include <stdexcept>
22 #include <iostream>
23 #include <sstream>
24 
25 #include <sys/stat.h>
26 #include <errno.h>
27 
28 using namespace ots;
29 
30 #undef __COUT_HDR__
31 #define __COUT_HDR__ "ConfigInfoReader"
32 
33 
34 //==============================================================================
35 ConfigurationInfoReader::ConfigurationInfoReader(bool allowIllegalColumns)
36 : allowIllegalColumns_(allowIllegalColumns)
37 {
38  initPlatform();
39  rootTag_ = xercesc::XMLString::transcode("ROOT");
40  configurationTag_ = xercesc::XMLString::transcode("CONFIGURATION");
41  configurationNameAttributeTag_ = xercesc::XMLString::transcode("Name");
42  viewTag_ = xercesc::XMLString::transcode("VIEW");
43  viewNameAttributeTag_ = xercesc::XMLString::transcode("Name");
44  viewTypeAttributeTag_ = xercesc::XMLString::transcode("Type");
45  viewDescriptionAttributeTag_ = xercesc::XMLString::transcode("Description");
46  columnTag_ = xercesc::XMLString::transcode("COLUMN");
47  columnTypeAttributeTag_ = xercesc::XMLString::transcode("Type");
48  columnNameAttributeTag_ = xercesc::XMLString::transcode("Name");
49  columnStorageNameAttributeTag_ = xercesc::XMLString::transcode("StorageName");
50  columnDataTypeAttributeTag_ = xercesc::XMLString::transcode("DataType");
51  columnDataChoicesAttributeTag_ = xercesc::XMLString::transcode("DataChoices");
52 }
53 
54 //==============================================================================
55 ConfigurationInfoReader::~ConfigurationInfoReader(void)
56 {
57  try
58  {
59  xercesc::XMLString::release( &rootTag_ );
60  xercesc::XMLString::release( &configurationTag_ );
61  xercesc::XMLString::release( &configurationNameAttributeTag_ );
62  xercesc::XMLString::release( &viewTag_ );
63  xercesc::XMLString::release( &viewNameAttributeTag_ );
64  xercesc::XMLString::release( &viewTypeAttributeTag_ );
65  xercesc::XMLString::release( &viewDescriptionAttributeTag_ );
66  xercesc::XMLString::release( &columnTag_ );
67  xercesc::XMLString::release( &columnTypeAttributeTag_ );
68  xercesc::XMLString::release( &columnNameAttributeTag_ );
69  xercesc::XMLString::release( &columnStorageNameAttributeTag_ );
70  xercesc::XMLString::release( &columnDataTypeAttributeTag_ );
71  xercesc::XMLString::release( &columnDataChoicesAttributeTag_ );
72 
73  }
74  catch( ... )
75  {
76  mf::LogError(__FILE__) << "Unknown exception encountered in TagNames destructor" << std::endl;
77  }
78  terminatePlatform();
79 }
80 
81 //==============================================================================
82 void ConfigurationInfoReader::initPlatform(void)
83 {
84  try
85  {
86  xercesc::XMLPlatformUtils::Initialize(); // Initialize Xerces infrastructure
87  }
88  catch( xercesc::XMLException& e )
89  {
90  mf::LogError(__FILE__) << "XML toolkit initialization error: " << XML_TO_CHAR(e.getMessage()) << std::endl;
91  // throw exception here to return ERROR_XERCES_INIT
92  }
93 }
94 
95 //==============================================================================
96 void ConfigurationInfoReader::terminatePlatform(void)
97 {
98  try
99  {
100  xercesc::XMLPlatformUtils::Terminate(); // Terminate after release of memory
101  }
102  catch( xercesc::XMLException& e )
103  {
104  mf::LogError(__FILE__) << "XML tolkit teardown error: " << XML_TO_CHAR(e.getMessage()) << std::endl;
105  }
106 }
107 
108 
109 //==============================================================================
110 void ConfigurationInfoReader::setAllowColumnErrors(bool setValue)
111 {
112  allowIllegalColumns_ = setValue;
113 }
114 //==============================================================================
115 const bool& ConfigurationInfoReader::getAllowColumnErrors(void)
116 {
117  return allowIllegalColumns_;
118 }
119 
120 //==============================================================================
121 bool ConfigurationInfoReader::checkViewType(std::string type)
122 {
123  std::vector<std::string> types;
124  int currentIndex = 0;
125  while( type.find(',',currentIndex) != std::string::npos )
126  {
127  types.push_back(type.substr(currentIndex,type.find(',',currentIndex)-currentIndex));
128  currentIndex = type.find(',',currentIndex)+1;
129  }
130  types.push_back(type.substr(currentIndex,type.size()));
131 
132  std::string systemType = getenv("CONFIGURATION_TYPE");
133  for(unsigned int i=0; i<types.size(); i++)
134  {
135  if( types[i] == systemType )
136  return true;
137  }
138  //In case I don't succeed let's check if maybe there is something wrong with the names
139  const unsigned int allowedNamesSize = 3;
140  const std::string allowedNames[allowedNamesSize] =
141  {"File","Database","DatabaseTest"
142  };
143  if(systemType != allowedNames[0] && systemType != allowedNames[1] && systemType != allowedNames[2] )
144  {
145  __COUT__ << "The type defined in CONFIGURATION_TYPE ("
146  << systemType << ") doesn't match with any of the allowed types: File,Database or DatabaseTest"
147  << std::endl;
148 
149  throw(std::runtime_error( "Illegal configuration type" ));
150  }
151  for(unsigned int i=0; i<types.size(); i++)
152  {
153  if(types[i] != allowedNames[0] && types[i] != allowedNames[1] && types[i] != allowedNames[2] )
154  {
155  __COUT__ << "The type defined in the info file ("
156  << types[i] << ") doesn't match with any of the allowed types: "
157  << allowedNames[0] << ", " << allowedNames[1] << " or " << allowedNames[2]
158  << std::endl;
159  throw(std::runtime_error( "Illegal Type!" ));
160  }
161  }
162 
163  return false;
164 }
165 
166 //==============================================================================
167 xercesc::DOMNode* ConfigurationInfoReader::getNode(XMLCh* tagName, xercesc::DOMNode* parent, unsigned int itemNumber)
168 {
169  return getNode(tagName, dynamic_cast< xercesc::DOMElement* >(parent), itemNumber);
170 }
171 
172 //==============================================================================
173 xercesc::DOMNode* ConfigurationInfoReader::getNode(XMLCh* tagName, xercesc::DOMElement* parent, unsigned int itemNumber)
174 {
175  xercesc::DOMNodeList* nodeList = parent->getElementsByTagName(tagName);
176  if( !nodeList )
177  {
178  throw(std::runtime_error( std::string("Can't find ") + XML_TO_CHAR(tagName) + " tag!"));
179  __COUT__ << (std::string("Can't find ") + XML_TO_CHAR(tagName) + " tag!") << std::endl;
180  }
181  // __COUT__<< "Name: " << XML_TO_CHAR(nodeList->item(itemNumber)->getNodeName()) << std::endl;
182  // if( nodeList->item(itemNumber)->getFirstChild() != 0 )
183  // __COUT__<< "Value: " << XML_TO_CHAR(nodeList->item(itemNumber)->getFirstChild()->getNodeValue()) << std::endl;
184  return nodeList->item(itemNumber);
185 }
186 
187 //==============================================================================
188 xercesc::DOMElement* ConfigurationInfoReader::getElement(XMLCh* tagName, xercesc::DOMNode* parent, unsigned int itemNumber)
189 {
190  return dynamic_cast< xercesc::DOMElement* >(getNode(tagName,parent,itemNumber));
191 }
192 
193 //==============================================================================
194 xercesc::DOMElement* ConfigurationInfoReader::getElement(XMLCh* tagName, xercesc::DOMElement* parent, unsigned int itemNumber)
195 {
196  return dynamic_cast< xercesc::DOMElement* >(getNode(tagName,parent,itemNumber));
197 }
198 
199 //==============================================================================
200 std::string ConfigurationInfoReader::read(ConfigurationBase& configuration)
201 {
202  std::string accumulatedExceptions = "";
203 
204  //KEEP For debugging... added by Gennadiy...
205  //if configuration name starts with "TestConfiguration00" then turn off the reading of information
206  // auto tmp_test_config_prefix = std::string{"TestConfiguration00"};
207  // auto tmp_config_name = configuration.getConfigurationName();
208  // if (std::equal(tmp_test_config_prefix.begin(), tmp_test_config_prefix.end(), tmp_config_name.begin())) return accumulatedExceptions;
209  //KEEP End debugging... for Gennadiy...
210 
211 
212  //These environment variables are required
213  if(getenv("CONFIGURATION_TYPE" ) == NULL) __COUT__ << "Missing env variable: CONFIGURATION_TYPE. It must be set!" << std::endl;
214  if(getenv("CONFIGURATION_DATA_PATH") == NULL) __COUT__ << "Missing env variable: CONFIGURATION_DATA_PATH. It must be set!" << std::endl;
215  if(getenv("CONFIGURATION_INFO_PATH") == NULL) __COUT__ << "Missing env variable: CONFIGURATION_INFO_PATH. It must be set!" << std::endl;
216 
217 
218  //example c++ setting of necessary environment variables
219  //setenv("CONFIGURATION_TYPE","File",1);
220  //setenv("CONFIGURATION_DATA_PATH",(std::string(getenv("USER_DATA")) + "/ConfigurationDataExamples").c_str(),1);
221  //setenv("CONFIGURATION_INFO_PATH",(std::string(getenv("USER_DATA")) + "/ConfigurationInfo").c_str(),1);
222 
223 
224  std::string configurationDataDir = std::string(getenv("CONFIGURATION_INFO_PATH")) + "/";
225  std::string configFile = configurationDataDir + configuration.getConfigurationName() + "Info.xml";
226  //__COUT__ << configFile << std::endl;
227  struct stat fileStatus;
228 
229  int iretStat = stat(configFile.c_str(), &fileStatus);
230  if( iretStat == ENOENT )
231  {
232  __SS__ << ("Path file_name does not exist, or path is an empty std::string.") << std::endl;
233  __COUT_ERR__ << ss.str();
234  throw std::runtime_error(ss.str());
235  }
236  else if( iretStat == ENOTDIR )
237  {
238  __SS__ << ("A component of the path is not a directory.") << std::endl;
239  __COUT_ERR__ << ss.str();
240  throw std::runtime_error(ss.str());
241  }
242  else if( iretStat == ELOOP )
243  {
244  __SS__ << ("Too many symbolic links encountered while traversing the path.") << std::endl;
245  __COUT_ERR__ << ss.str();
246  throw std::runtime_error(ss.str());
247  }
248  else if( iretStat == EACCES )
249  {
250  __SS__ << ("Permission denied.") << std::endl;
251  __COUT_ERR__ << ss.str();
252  throw std::runtime_error(ss.str());
253  }
254  else if( iretStat == ENAMETOOLONG )
255  {
256  __SS__ << ("File can not be read. Name too long.") << std::endl;
257  __COUT_ERR__ << ss.str();
258  throw std::runtime_error(ss.str());
259  }
260 
261  xercesc::XercesDOMParser* parser = new xercesc::XercesDOMParser;
262  // Configure DOM parser.
263  parser->setValidationScheme(xercesc::XercesDOMParser::Val_Auto);//Val_Never
264  parser->setDoNamespaces ( true );
265  parser->setDoSchema ( true );
266  parser->useCachedGrammarInParse ( false );
267 
268  DOMTreeErrorReporter* errorHandler = new DOMTreeErrorReporter() ;
269  parser->setErrorHandler(errorHandler);
270  try
271  {
272  parser->parse( configFile.c_str() );
273 
274  // no need to free this pointer - owned by the parent parser object
275  xercesc::DOMDocument* xmlDocument = parser->getDocument();
276 
277  // Get the top-level element: Name is "root". No attributes for "root"
278  xercesc::DOMElement* elementRoot = xmlDocument->getDocumentElement();
279  if( !elementRoot )
280  {
281  delete parser;
282  delete errorHandler;
283  throw(std::runtime_error( "empty XML document" ));
284  }
285 
286  //<CONFIGURATION>
287  xercesc::DOMElement* configurationElement = getElement(configurationTag_, elementRoot, 0);
288  if( configuration.getConfigurationName() != XML_TO_CHAR(configurationElement->getAttribute(configurationNameAttributeTag_)) )
289  {
290 
291  __SS__ << "In " << configFile << " the configuration name " << XML_TO_CHAR(configurationElement->getAttribute(configurationNameAttributeTag_))
292  << " doesn't match the the class configuration name " << configuration.getConfigurationName() << std::endl;
293 
294  delete parser;
295  delete errorHandler;
296  __COUT_ERR__ << "\n" << ss.str();
297  throw(std::runtime_error( ss.str()));
298  }
299  //<VIEW>
300  xercesc::DOMNodeList* viewNodeList = configurationElement->getElementsByTagName(viewTag_);
301  bool storageTypeFound = false;
302 
303  if(viewNodeList->getLength() != 1)
304  {
305 
306  __SS__ << "In " << configFile << " the configuration name " << XML_TO_CHAR(configurationElement->getAttribute(configurationNameAttributeTag_))
307  << " there must only be one view. There were " <<
308  viewNodeList->getLength() << " found." << std::endl;
309 
310  delete parser;
311  delete errorHandler;
312  __COUT_ERR__ << "\n" << ss.str();
313  throw(std::runtime_error( ss.str()));
314  }
315 
316  for( XMLSize_t view = 0; view < viewNodeList->getLength(); view++ )
317  {
318  if( !viewNodeList->item(view)->getNodeType() || viewNodeList->item(view)->getNodeType() != xercesc::DOMNode::ELEMENT_NODE )//true is not 0 && is element
319  continue;
320  xercesc::DOMElement* viewElement = dynamic_cast< xercesc::DOMElement* >( viewNodeList->item(view) );
321  std::string viewType = XML_TO_CHAR(viewElement->getAttribute(viewTypeAttributeTag_));
322  if(!checkViewType(viewType))
323  continue;
324  storageTypeFound = true;
325  configuration.getMockupViewP()->setTableName(XML_TO_CHAR(viewElement->getAttribute(viewNameAttributeTag_)));
326  xercesc::DOMNodeList* columnNodeList = viewElement->getElementsByTagName(columnTag_);
327  for( XMLSize_t column = 0; column < columnNodeList->getLength(); column++ )
328  {
329  //<COLUMN>
330  xercesc::DOMElement* columnElement = dynamic_cast< xercesc::DOMElement* >( columnNodeList->item(column) );
331  //__COUT__ << XML_TO_CHAR(columnElement->getAttribute(columnNameAttributeTag_)) << std::endl;
332 
333  //automatically delete the persistent version of the column info
334  std::string capturedException;
335  configuration.getMockupViewP()->getColumnsInfoP()->push_back(
337  XML_TO_CHAR(columnElement->getAttribute(columnTypeAttributeTag_)),
338  XML_TO_CHAR(columnElement->getAttribute(columnNameAttributeTag_)),
339  XML_TO_CHAR(columnElement->getAttribute(columnStorageNameAttributeTag_)),
340  XML_TO_CHAR(columnElement->getAttribute(columnDataTypeAttributeTag_)),
341  XML_TO_CHAR(columnElement->getAttribute(columnDataChoicesAttributeTag_)),
342  allowIllegalColumns_?&capturedException:0)); //capture exception string if allowing illegal columns
343 
344  //if error detected (this implies allowing illegal columns)
345  // accumulate and return accumulated errors at end
346  if(capturedException != "")
347  accumulatedExceptions += std::string("\n\nColumn Error:") + capturedException;
348 
349  //</COLUMN>
350  }
351 
352  //handle view description (which is actually the configuration
353  // description since only one view allowed)
354  std::string configurationDescription = XML_TO_CHAR(viewElement->getAttribute(
355  viewDescriptionAttributeTag_));
356 
357  configuration.setConfigurationDescription(
358  StringMacros::decodeURIComponent(configurationDescription));
359  //__COUT__ << "configurationDescription = " << configurationDescription << std::endl;
360 
361  //</VIEW>
362  }
363  if( !storageTypeFound )
364  {
365  __COUT__ << "The type defined in CONFIGURATION_TYPE ("
366  << getenv("CONFIGURATION_TYPE") << ") doesn't match with any of the types defined in " << configFile << std::endl;
367 
368  delete parser;
369  delete errorHandler;
370  throw(std::runtime_error( "Configuration Type mismatch!" ));
371  }
372 
373  //</CONFIGURATION>
374  }
375  catch( xercesc::XMLException& e )
376  {
377  std::ostringstream errBuf;
378  errBuf << "Error parsing file: " << XML_TO_CHAR(e.getMessage()) << std::flush;
379  }
380  delete parser;
381  delete errorHandler;
382 
383  //__COUT__ << std::endl;
384 
385  //if exceptions have been accumulated
386  // then in allowIllegalColumns mode
387  //return accumulated exception strings to next level
388  return accumulatedExceptions;
389 }
390 
391 //==============================================================================
392 //returns accumulated exception string (while allowIllegalColumns == true)
393 // otherwise "" if no exceptions
394 std::string ConfigurationInfoReader::read(ConfigurationBase* configuration)
395 {
396  return read(*configuration);
397 }