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