otsdaq  v2_05_02_indev
ConfigurationSupervisorBase.cc
1 #include "otsdaq/CoreSupervisors/ConfigurationSupervisorBase.h"
2 
3 #include "otsdaq/TablePlugins/XDAQContextTable.h"
4 
5 using namespace ots;
6 
7 //==============================================================================
8 // getConfigurationStatusXML
9 void ConfigurationSupervisorBase::getConfigurationStatusXML(HttpXmlDocument& xmlOut, ConfigurationManagerRW* cfgMgr)
10 {
11  std::map<std::string /*type*/, std::pair<std::string /*groupName*/, TableGroupKey>> activeGroupMap = cfgMgr->getActiveTableGroups();
12 
13  for(auto& type : activeGroupMap)
14  {
15  xmlOut.addTextElementToData(type.first + "-ActiveGroupName", type.second.first);
16  xmlOut.addTextElementToData(type.first + "-ActiveGroupKey", type.second.second.toString());
17  //__SUP_COUT__ << "ActiveGroup " << type.first << " " << type.second.first << "("
18  //<< type.second.second << ")" << __E__;
19  }
20 
21  // always add version tracking bool
22  xmlOut.addTextElementToData("versionTracking", ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
23 
24 } // end getConfigurationStatusXML()
25 
26 //==============================================================================
27 // handleCreateTableXML
28 //
29 // Save the detail of specific table specified
30 // by tableName and version
31 // ...starting from dataOffset
32 //
33 // Note: if starting version is -1 start from mock-up
34 void ConfigurationSupervisorBase::handleCreateTableXML(HttpXmlDocument& xmlOut,
35  ConfigurationManagerRW* cfgMgr,
36  const std::string& tableName,
37  TableVersion version,
38  bool makeTemporary,
39  const std::string& data,
40  const int& dataOffset,
41  const std::string& author,
42  const std::string& comment,
43  bool sourceTableAsIs,
44  bool lookForEquivalent) try
45 {
46  //__COUT__ << "handleCreateTableXML: " << tableName << " version: " <<
47  // version
48  // << " dataOffset: " << dataOffset << __E__;
49 
50  //__COUT__ << "data: " << data << __E__;
51 
52  // create temporary version from starting version
53  if(!version.isInvalid()) // if not using mock-up, make sure starting version is
54  // loaded
55  {
56  try
57  {
58  cfgMgr->getVersionedTableByName(tableName, version);
59  }
60  catch(...)
61  {
62  // force to mockup
63  version = TableVersion();
64  }
65  }
66 
67  TableBase* table = cfgMgr->getTableByName(tableName);
68 
69  // check that the source version has the right number of columns
70  // if there is a mismatch, start from mockup
71  if(!version.isInvalid()) // if not using mock-up, then the starting version is the
72  // active one
73  {
74  // compare active to mockup column counts
75  if(table->getViewP()->getDataColumnSize() != table->getMockupViewP()->getNumberOfColumns() || table->getViewP()->getSourceColumnMismatch() != 0)
76  {
77  __COUT__ << "table->getViewP()->getNumberOfColumns() " << table->getViewP()->getNumberOfColumns() << __E__;
78  __COUT__ << "table->getMockupViewP()->getNumberOfColumns() " << table->getMockupViewP()->getNumberOfColumns() << __E__;
79  __COUT__ << "table->getViewP()->getSourceColumnMismatch() " << table->getViewP()->getSourceColumnMismatch() << __E__;
80  __COUT_INFO__ << "Source view v" << version << " has a mismatch in the number of columns, so using mockup as source." << __E__;
81  version = TableVersion(); // invalid = mockup
82  }
83  }
84 
85  // create a temporary version from the source version
86  TableVersion temporaryVersion = table->createTemporaryView(version);
87 
88  __COUT__ << "\t\ttemporaryVersion: " << temporaryVersion << __E__;
89 
90  TableView* cfgView = table->getTemporaryView(temporaryVersion);
91 
92  int retVal;
93  try
94  {
95  // returns -1 on error that data was unchanged
96  retVal = sourceTableAsIs ? 0 : cfgView->fillFromCSV(data, dataOffset, author);
97 
98  if(retVal == 1) // data was same but columns are different!
99  {
100  __COUT__ << "Data was the same, but columns have changed!" << __E__;
101  __COUTV__(sourceTableAsIs);
102  __COUTV__(lookForEquivalent);
103  }
104 
105  cfgView->setURIEncodedComment(comment);
106  __COUT__ << "Table comment was set to:\n\t" << cfgView->getComment() << __E__;
107  }
108  catch(...) // erase temporary view before re-throwing error
109  {
110  __COUT__ << "Caught error while editing. Erasing temporary version." << __E__;
111  table->eraseView(temporaryVersion);
112  throw;
113  }
114 
115  // Note: be careful with any further table operations at this point..
116  // must catch errors and erase temporary version on failure.
117 
118  // only consider it an error if source version was persistent version
119  // allow it if source version is temporary and we are making a persistent version now
120  // also, allow it if version tracking is off.
121  if(retVal < 0 && (!version.isTemporaryVersion() || makeTemporary) && ConfigurationInterface::isVersionTrackingEnabled())
122  {
123  if(!version.isInvalid() && // if source version was mockup, then consider it
124  // attempt to create a blank table
125  !version.isScratchVersion()) // if source version was scratch, then consider
126  // it attempt to make it persistent
127  {
128  __SS__ << "No rows were modified! No reason to fill a view with same content." << __E__;
129  __COUT_ERR__ << "\n" << ss.str();
130  // delete temporaryVersion
131  table->eraseView(temporaryVersion);
132  __SS_THROW__;
133  }
134  else if(version.isInvalid())
135  __COUT__ << "This was interpreted as an attempt to create a blank table." << __E__;
136  else if(version.isScratchVersion())
137  __COUT__ << "This was interpreted as an attempt to make a persistent "
138  "version of the scratch table."
139  << __E__;
140  else
141  {
142  __SS__;
143  __THROW__(ss.str() + "impossible!");
144  }
145  }
146  else if(retVal < 0 && (version.isTemporaryVersion() && !makeTemporary))
147  {
148  __COUT__ << "Allowing the static data because this is converting from "
149  "temporary to persistent version."
150  << __E__;
151  }
152  else if(retVal < 0 && !ConfigurationInterface::isVersionTrackingEnabled())
153  {
154  __COUT__ << "Allowing the static data because version tracking is OFF." << __E__;
155  }
156  else if(retVal < 0)
157  {
158  __SS__ << "This should not be possible! Fatal error." << __E__;
159  // delete temporaryVersion
160  table->eraseView(temporaryVersion);
161  __SS_THROW__;
162  }
163 
164  // note: if sourceTableAsIs, accept equivalent versions
165  ConfigurationSupervisorBase::saveModifiedVersionXML(xmlOut,
166  cfgMgr,
167  tableName,
168  version,
169  makeTemporary,
170  table,
171  temporaryVersion,
172  false /*ignoreDuplicates*/,
173  lookForEquivalent || sourceTableAsIs /*lookForEquivalent*/);
174 } // end handleCreateTableXML()
175 catch(std::runtime_error& e)
176 {
177  __COUT__ << "Error detected!\n\n " << e.what() << __E__;
178  xmlOut.addTextElementToData("Error", "Error saving new view!\n " + std::string(e.what()));
179 }
180 catch(...)
181 {
182  __COUT__ << "Error detected!\n\n " << __E__;
183  xmlOut.addTextElementToData("Error", "Error saving new view! ");
184 } // end handleCreateTableXML() catch
185 
186 //==============================================================================
187 // saveModifiedVersionXML
188 //
189 // once source version has been modified in temporary version
190 // this function finishes it off.
191 TableVersion ConfigurationSupervisorBase::saveModifiedVersionXML(HttpXmlDocument& xmlOut,
192  ConfigurationManagerRW* cfgMgr,
193  const std::string& tableName,
194  TableVersion originalVersion,
195  bool makeTemporary,
196  TableBase* table,
197  TableVersion temporaryModifiedVersion,
198  bool ignoreDuplicates,
199  bool lookForEquivalent)
200 {
201  bool foundEquivalent;
202  TableVersion newAssignedVersion = cfgMgr->saveModifiedVersion(
203  tableName, originalVersion, makeTemporary, table, temporaryModifiedVersion, ignoreDuplicates, lookForEquivalent, &foundEquivalent);
204 
205  xmlOut.addTextElementToData("savedName", tableName);
206  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
207 
208  // xmlOut.addTextElementToData("savedName", tableName);
209  // xmlOut.addTextElementToData("savedVersion", duplicateVersion.toString());
210  if(foundEquivalent)
211  {
212  xmlOut.addTextElementToData("foundEquivalentVersion", "1");
213  xmlOut.addTextElementToData(tableName + "_foundEquivalentVersion", "1");
214  }
215  return newAssignedVersion;
216  //
217  // bool needToEraseTemporarySource =
218  // (originalVersion.isTemporaryVersion() && !makeTemporary);
219  //
220  // // check for duplicate tables already in cache
221  // if(!ignoreDuplicates)
222  // {
223  // __COUT__ << "Checking for duplicate tables..." << __E__;
224  //
225  // TableVersion duplicateVersion;
226  //
227  // {
228  // //"DEEP" checking
229  // // load into cache 'recent' versions for this table
230  // // 'recent' := those already in cache, plus highest version numbers not
231  // // in cache
232  // const std::map<std::string, TableInfo>& allTableInfo =
233  // cfgMgr->getAllTableInfo(); // do not refresh
234  //
235  // auto versionReverseIterator =
236  // allTableInfo.at(tableName).versions_.rbegin(); // get reverse iterator
237  // __COUT__ << "Filling up cached from " << table->getNumberOfStoredViews()
238  // << " to max count of " << table->MAX_VIEWS_IN_CACHE << __E__;
239  // for(; table->getNumberOfStoredViews() < table->MAX_VIEWS_IN_CACHE &&
240  // versionReverseIterator != allTableInfo.at(tableName).versions_.rend();
241  // ++versionReverseIterator)
242  // {
243  // __COUT__ << "Versions in reverse order " << *versionReverseIterator
244  // << __E__;
245  // try
246  // {
247  // cfgMgr->getVersionedTableByName(
248  // tableName, *versionReverseIterator); // load to cache
249  // }
250  // catch(const std::runtime_error& e)
251  // {
252  // __COUT__ << "Error loadiing historical version, but ignoring: "
253  // << e.what() << __E__;
254  // }
255  // }
256  // }
257  //
258  // __COUT__ << "Checking duplicate..." << __E__;
259  //
260  // duplicateVersion = table->checkForDuplicate(
261  // temporaryModifiedVersion,
262  // (!originalVersion.isTemporaryVersion() && !makeTemporary)
263  // ? TableVersion()
264  // : // if from persistent to persistent, then include original version in
265  // // search
266  // originalVersion);
267  //
268  // if(lookForEquivalent && !duplicateVersion.isInvalid())
269  // {
270  // // found an equivalent!
271  // __COUT__ << "Equivalent table found in version v" << duplicateVersion
272  // << __E__;
273  //
274  // // if duplicate version was temporary, do not use
275  // if(duplicateVersion.isTemporaryVersion() && !makeTemporary)
276  // {
277  // __COUT__ << "Need persistent. Duplicate version was temporary. "
278  // "Abandoning duplicate."
279  // << __E__;
280  // duplicateVersion = TableVersion(); // set invalid
281  // }
282  // else
283  // {
284  // // erase and return equivalent version
285  //
286  // // erase modified equivalent version
287  // cfgMgr->eraseTemporaryVersion(tableName, temporaryModifiedVersion);
288  //
289  // // erase original if needed
290  // if(needToEraseTemporarySource)
291  // cfgMgr->eraseTemporaryVersion(tableName, originalVersion);
292  //
293  // xmlOut.addTextElementToData("savedName", tableName);
294  // xmlOut.addTextElementToData("savedVersion", duplicateVersion.toString());
295  // xmlOut.addTextElementToData("foundEquivalentVersion", "1");
296  // xmlOut.addTextElementToData(tableName + "_foundEquivalentVersion", "1");
297  //
298  // __COUT__ << "\t\t equivalent AssignedVersion: " << duplicateVersion
299  // << __E__;
300  //
301  // return duplicateVersion;
302  // }
303  // }
304  //
305  // if(!duplicateVersion.isInvalid())
306  // {
307  // __SS__ << "This version of table '" << tableName
308  // << "' is identical to another version currently cached v"
309  // << duplicateVersion << ". No reason to save a duplicate." << __E__;
310  // __COUT_ERR__ << "\n" << ss.str();
311  //
312  // // delete temporaryModifiedVersion
313  // table->eraseView(temporaryModifiedVersion);
314  // __SS_THROW__;
315  // }
316  //
317  // __COUT__ << "Check for duplicate tables complete." << __E__;
318  // }
319  //
320  // if(makeTemporary)
321  // __COUT__ << "\t\t**************************** Save as temporary table version"
322  // << __E__;
323  // else
324  // __COUT__ << "\t\t**************************** Save as new table version" << __E__;
325  //
326  // TableVersion newAssignedVersion =
327  // cfgMgr->saveNewTable(tableName, temporaryModifiedVersion, makeTemporary);
328  //
329  // if(needToEraseTemporarySource)
330  // cfgMgr->eraseTemporaryVersion(tableName, originalVersion);
331  //
332  // xmlOut.addTextElementToData("savedName", tableName);
333  // xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
334  //
335  // __COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
336  // return newAssignedVersion;
337 } // end saveModifiedVersionXML()
338 
339 //==============================================================================
340 // handleCreateTableGroupXML
341 //
342 // Save a new TableGroup:
343 // Search for existing TableGroupKeys for this TableGroup
344 // Append a "bumped" system key to name
345 // Save based on list of tableName/TableVersion
346 //
347 // tableList parameter is comma separated table name and version
348 //
349 // Note: if version of -1 (INVALID/MOCKUP) is given and there are no other existing
350 // table versions... a new table version is generated using the mockup table.
351 //
352 // Table Version Alias Handling:
353 // Allow table versions to be specified as an alias with ALIAS: preamble. Aliased
354 // versions will be translated according to the active backbone at activation
355 // time.
356 //
357 //
358 void ConfigurationSupervisorBase::handleCreateTableGroupXML(HttpXmlDocument& xmlOut,
359  ConfigurationManagerRW* cfgMgr,
360  const std::string& groupName,
361  const std::string& tableList,
362  bool allowDuplicates,
363  bool ignoreWarnings,
364  const std::string& groupComment,
365  bool lookForEquivalent) try
366 {
367  __COUT__ << "handleCreateTableGroupXML \n";
368 
369  xmlOut.addTextElementToData("AttemptedNewGroupName", groupName);
370 
371  // make sure not using partial tables or anything weird when creating the group
372  // so start from scratch and load backbone, but allow errors
373  std::string accumulatedWarnings;
374  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(true, &accumulatedWarnings);
375  __COUT_WARN__ << "Ignoring these errors: " << accumulatedWarnings << __E__;
376  cfgMgr->loadConfigurationBackbone();
377 
378  std::map<std::string /*tableName*/, std::map<std::string /*aliasName*/, TableVersion /*version*/>> versionAliases = cfgMgr->getVersionAliases();
379  // for(const auto& aliases : versionAliases)
380  // for(const auto& alias : aliases.second)
381  // __COUT__ << aliases.first << " " << alias.first << " " << alias.second
382  // << __E__;
383 
384  std::map<std::string /*name*/, TableVersion /*version*/> groupMembers;
385  std::map<std::string /*name*/, std::string /*alias*/> groupAliases;
386 
387  std::string name, versionStr, alias;
388  TableVersion version;
389  auto c = tableList.find(',', 0);
390  auto i = c;
391  i = 0; // auto used to get proper index/length type
392  while(c < tableList.length())
393  {
394  // add the table and version pair to the map
395  name = tableList.substr(i, c - i);
396  i = c + 1;
397  c = tableList.find(',', i);
398  if(c == std::string::npos) // missing version list entry?!
399  {
400  __SS__ << "Incomplete Table Name-Version pair!" << __E__;
401  __COUT_ERR__ << "\n" << ss.str();
402  xmlOut.addTextElementToData("Error", ss.str());
403  return;
404  }
405 
406  versionStr = tableList.substr(i, c - i);
407  i = c + 1;
408  c = tableList.find(',', i);
409 
410  //__COUT__ << "name: " << name << __E__;
411  //__COUT__ << "versionStr: " << versionStr << __E__;
412 
413  // check if version is an alias and convert
414  if(versionStr.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0)
415  {
416  alias = versionStr.substr(ConfigurationManager::ALIAS_VERSION_PREAMBLE.size());
417 
418  __COUT__ << "Found alias " << name << " " << versionStr << __E__;
419 
420  // convert alias to version
421  if(versionAliases.find(name) != versionAliases.end() && versionAliases[name].find(alias) != versionAliases[name].end())
422  {
423  version = versionAliases[name][alias];
424  __COUT__ << "version alias '" << alias << "'translated to: " << version << __E__;
425 
426  groupAliases[name] = alias;
427  }
428  else
429  {
430  __SS__ << "version alias '" << versionStr.substr(ConfigurationManager::ALIAS_VERSION_PREAMBLE.size())
431  << "' was not found in active version aliases!" << __E__;
432  __COUT_ERR__ << "\n" << ss.str();
433  xmlOut.addTextElementToData("Error", ss.str());
434  return;
435  }
436  }
437  else
438  version = TableVersion(versionStr);
439 
440  if(version.isTemporaryVersion())
441  {
442  __SS__ << "Groups can not be created using temporary member tables. "
443  << "Table member '" << name << "' with temporary version '" << version << "' is illegal." << __E__;
444  xmlOut.addTextElementToData("Error", ss.str());
445  return;
446  }
447 
448  // enforce that table exists
449  if(allTableInfo.find(name) == allTableInfo.end())
450  {
451  __SS__ << "Groups can not be created using mock-up member tables of "
452  "undefined tables. "
453  << "Table member '" << name << "' is not defined." << __E__;
454  xmlOut.addTextElementToData("Error", ss.str());
455  return;
456  }
457 
458  if(version.isMockupVersion())
459  {
460  // if mockup, then generate a new persistent version to use based on mockup
461  TableBase* table = cfgMgr->getTableByName(name);
462  // create a temporary version from the mockup as source version
463  TableVersion temporaryVersion = table->createTemporaryView();
464  __COUT__ << "\t\ttemporaryVersion: " << temporaryVersion << __E__;
465 
466  // if other versions exist check for another mockup, and use that instead
467  __COUT__ << "Creating version from mock-up for name: " << name << " inputVersionStr: " << versionStr << __E__;
468 
469  // set table comment
470  table->getTemporaryView(temporaryVersion)->setComment("Auto-generated from mock-up.");
471 
472  // finish off the version creation
473  version = ConfigurationSupervisorBase::saveModifiedVersionXML(xmlOut,
474  cfgMgr,
475  name,
476  TableVersion() /*original source is mockup*/,
477  false /* makeTemporary */,
478  table,
479  temporaryVersion /*temporary modified version*/,
480  false /*ignore duplicates*/,
481  true /*look for equivalent*/);
482 
483  __COUT__ << "Using mockup version: " << version << __E__;
484  }
485 
486  //__COUT__ << "version: " << version << __E__;
487  groupMembers[name] = version;
488  } // end member verification loop
489 
490  __COUTV__(StringMacros::mapToString(groupAliases));
491 
492  if(!allowDuplicates)
493  {
494  __COUT__ << "Checking for duplicate groups..." << __E__;
495  TableGroupKey foundKey = cfgMgr->findTableGroup(groupName, groupMembers, groupAliases);
496 
497  if(!foundKey.isInvalid())
498  {
499  // return found equivalent key
500  xmlOut.addTextElementToData("TableGroupName", groupName);
501  xmlOut.addTextElementToData("TableGroupKey", foundKey.toString());
502 
503  if(lookForEquivalent)
504  {
505  __COUT__ << "Found equivalent group key (" << foundKey << ") for " << groupName << "." << __E__;
506  // allow this equivalent group to be the response without an error
507  xmlOut.addTextElementToData("foundEquivalentKey", "1"); // indicator
508 
509  // insert get table info
510  handleGetTableGroupXML(xmlOut, cfgMgr, groupName, foundKey, ignoreWarnings);
511  return;
512  }
513  else // treat as error, if not looking for equivalent
514  {
515  __COUT__ << "Treating duplicate group as error." << __E__;
516  __SS__ << ("Failed to create table group: " + groupName + ". It is a duplicate of an existing group key (" + foundKey.toString() + ")");
517  __COUT_ERR__ << ss.str() << __E__;
518  xmlOut.addTextElementToData("Error", ss.str());
519  return;
520  }
521  }
522 
523  __COUT__ << "Check for duplicate groups complete." << __E__;
524  }
525 
526  // check the group for errors before creating group
527  try
528  {
529  cfgMgr->loadMemberMap(groupMembers);
530 
531  std::string accumulateErrors = "";
532  for(auto& groupMemberPair : groupMembers)
533  {
534  TableView* cfgViewPtr = cfgMgr->getTableByName(groupMemberPair.first)->getViewP();
535  if(cfgViewPtr->getDataColumnSize() != cfgViewPtr->getNumberOfColumns() ||
536  cfgViewPtr->getSourceColumnMismatch() != 0) // check for column size mismatch
537  {
538  const std::set<std::string> srcColNames = cfgViewPtr->getSourceColumnNames();
539  __SS__ << "\n\nThere were errors found in loading a member table " << groupMemberPair.first << ":v" << cfgViewPtr->getVersion()
540  << ". Please see the details below:\n\n"
541  << "The source column size was found to be " << srcColNames.size() << ", and the current number of columns for this table is "
542  << cfgViewPtr->getNumberOfColumns() << ". This resulted in a count of " << cfgViewPtr->getSourceColumnMismatch()
543  << " source column mismatches, and a count of " << cfgViewPtr->getSourceColumnMissing() << " table entries missing in "
544  << cfgViewPtr->getNumberOfRows() << " row(s) of data." << __E__;
545 
546  ss << "\n\nSource column names were as follows:\n";
547  char index = 'a';
548  for(auto& srcColName : srcColNames)
549  ss << "\n\t" << index++ << ". " << srcColName;
550  ss << __E__;
551 
552  std::set<std::string> destColNames = cfgViewPtr->getColumnStorageNames();
553  ss << "\n\nCurrent table column names are as follows:\n";
554  index = 'a';
555  for(auto& destColName : destColNames)
556  ss << "\n\t" << index++ << ". " << destColName;
557  ss << __E__;
558 
559  __COUT_ERR__ << "\n" << ss.str();
560  xmlOut.addTextElementToData("Error", ss.str());
561  return;
562  }
563  }
564  }
565  catch(std::runtime_error& e)
566  {
567  __SS__ << "Failed to create table group: " << groupName << ".\nThere were problems loading the chosen members:\n\n" << e.what() << __E__;
568  __COUT_ERR__ << "\n" << ss.str();
569  xmlOut.addTextElementToData("Error", ss.str());
570  return;
571  }
572  catch(...)
573  {
574  __SS__ << "Failed to create table group: " << groupName << __E__;
575  __COUT_ERR__ << "\n" << ss.str();
576  xmlOut.addTextElementToData("Error", ss.str());
577  return;
578  }
579 
580  // check the tree for warnings before creating group
581  std::string accumulateTreeErrs;
582  cfgMgr->getChildren(&groupMembers, &accumulateTreeErrs);
583  if(accumulateTreeErrs != "")
584  {
585  __COUT_WARN__ << "\n" << accumulateTreeErrs << __E__;
586  if(!ignoreWarnings)
587  {
588  xmlOut.addTextElementToData("TreeErrors", accumulateTreeErrs);
589  return;
590  }
591  }
592 
593  TableGroupKey newKey;
594  try
595  {
596  __COUT__ << "Saving new group..." << __E__;
597  newKey = cfgMgr->saveNewTableGroup(groupName, groupMembers, groupComment, &groupAliases);
598  }
599  catch(std::runtime_error& e)
600  {
601  __COUT_ERR__ << "Failed to create table group: " << groupName << __E__;
602  __COUT_ERR__ << "\n\n" << e.what() << __E__;
603  xmlOut.addTextElementToData("Error", "Failed to create table group: " + groupName + ".\n\n" + e.what());
604  return;
605  }
606  catch(...)
607  {
608  __COUT_ERR__ << "Failed to create table group: " << groupName << __E__;
609  xmlOut.addTextElementToData("Error", "Failed to create table group: " + groupName);
610  return;
611  }
612 
613  // insert get table info
614  __COUT__ << "Loading new table group..." << __E__;
615  handleGetTableGroupXML(xmlOut, cfgMgr, groupName, newKey, ignoreWarnings);
616 
617 } // end handleCreateTableGroupXML()
618 catch(std::runtime_error& e)
619 {
620  __COUT__ << "Error detected!\n\n " << e.what() << __E__;
621  xmlOut.addTextElementToData("Error", "Error saving table group! " + std::string(e.what()));
622 }
623 catch(...)
624 {
625  __COUT__ << "Unknown Error detected!\n\n " << __E__;
626  xmlOut.addTextElementToData("Error", "Error saving table group! ");
627 } // end handleCreateTableGroupXML() catch
628 
629 //==============================================================================
630 // handleGetTableGroupXML
631 //
632 // give the detail of specific table specified
633 // groupKey=-1 returns latest
634 //
635 // Find historical group keys
636 // and figure out all member configurations versions
637 
638 //
639 // return this information
640 // <group name=xxx key=xxx>
641 // <historical key=xxx>
642 // <historical key=xxx>
643 // ....
644 // <table name=xxx version=xxx />
645 // <historical version=xxx>
646 // <historical version=xxx>
647 // ...
648 // </table>
649 // <table name=xxx version=xxx>
650 // ...
651 // </table>
652 void ConfigurationSupervisorBase::handleGetTableGroupXML(
653  HttpXmlDocument& xmlOut, ConfigurationManagerRW* cfgMgr, const std::string& groupName, TableGroupKey groupKey, bool ignoreWarnings) try
654 {
655  //char tmpIntStr[100];
656  xercesc::DOMElement *parentEl, *configEl;
657 
658  // steps:
659  // if invalid key, get latest key
660  // get specific group with key
661  // give member names and versions
662  // get all table groups to locate historical keys
663  // get all groups to find historical keys
664 
665  // std::set<std::string /*name+version*/> allGroups =
666  // cfgMgr->getConfigurationInterface()->getAllTableGroupNames(groupName);
667  // std::string name;
668  // TableGroupKey key;
669  // //put them in a set to sort them as TableGroupKey defines for operator<
670  // std::set<TableGroupKey> sortedKeys;
671  // for(auto& group: allGroups)
672  // {
673  // //now uses database filter
674  // TableGroupKey::getGroupNameAndKey(group,name,key);
675  // //if(name == groupName)
676  // sortedKeys.emplace(key);
677  // }
678 
679  const GroupInfo& groupInfo = cfgMgr->getGroupInfo(groupName);
680  const std::set<TableGroupKey>& sortedKeys = groupInfo.keys_; // rename
681 
682  if(groupKey.isInvalid() || // if invalid or not found, get latest
683  sortedKeys.find(groupKey) == sortedKeys.end())
684  {
685  // report error if group key not found
686  if(!groupKey.isInvalid())
687  {
688  // attempt to reload all group info and power through
689  std::string accumulatedWarnings;
690  /*const std::map<std::string, TableInfo>& allTableInfo = */cfgMgr->getAllTableInfo(true, &accumulatedWarnings);
691  __COUT_WARN__ << "Attempting info refresh. Ignoring these errors: " << accumulatedWarnings << __E__;
692 
693  __SS__ << "Group key " << groupKey << " was not found for group '" << groupName << "!'" << __E__;
694  // xmlOut.addTextElementToData("Error", ss.str());
695 
696  ss << "Found keys: " << __E__;
697  for(auto& key : sortedKeys)
698  ss << "\t" << key << __E__;
699  __COUT__ << ss.str() << __E__;
700  }
701  else
702  {
703  if(sortedKeys.size())
704  groupKey = *sortedKeys.rbegin();
705  __COUT__ << "Group key requested was invalid or not found, going with latest " << groupKey << __E__;
706  }
707  }
708 
709  xmlOut.addTextElementToData("TableGroupName", groupName);
710  xmlOut.addTextElementToData("TableGroupKey", groupKey.toString());
711 
712  // add all other sorted keys for this groupName
713  for(auto& keyInOrder : sortedKeys)
714  xmlOut.addTextElementToData("HistoricalTableGroupKey", keyInOrder.toString());
715 
716  parentEl = xmlOut.addTextElementToData("TableGroupMembers", "");
717 
718  // get specific group with key
719  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
720  std::map<std::string /*name*/, std::string /*alias*/> groupMemberAliases;
721 
722  __COUT__ << "groupName=" << groupName << __E__;
723  __COUT__ << "groupKey=" << groupKey << __E__;
724 
725  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
726  std::map<std::string, TableInfo>::const_iterator it;
727 
728  // load group so comments can be had
729  // and also group metadata (author, comment, createTime)
730  try
731  {
732  std::string groupAuthor, groupComment, groupCreationTime, groupTypeString;
733  std::string accumulateTreeErrors;
734 
735  __COUTV__(ignoreWarnings);
736  cfgMgr->loadTableGroup(groupName,
737  groupKey,
738  false /*doActivate*/,
739  &memberMap,
740  0 /*progressBar*/,
741  ignoreWarnings ? 0 : /*accumulateTreeErrors*/
742  &accumulateTreeErrors,
743  &groupComment,
744  &groupAuthor,
745  &groupCreationTime,
746  false /*doNotLoadMember*/,
747  &groupTypeString,
748  &groupMemberAliases);
749 
750  if(accumulateTreeErrors != "")
751  {
752  __COUTV__(accumulateTreeErrors);
753  xmlOut.addTextElementToData("TreeErrors", accumulateTreeErrors);
754  }
755 
756  xmlOut.addTextElementToData("TableGroupAuthor", groupAuthor);
757  xmlOut.addTextElementToData("TableGroupComment", groupComment);
758  xmlOut.addTextElementToData("TableGroupCreationTime", groupCreationTime);
759  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
760  }
761  catch(const std::runtime_error& e)
762  {
763  __SS__ << "Table group \"" + groupName + "(" + groupKey.toString() + ")" + "\" members can not be loaded!\n\n" + e.what() << __E__;
764  __COUT_ERR__ << ss.str();
765  xmlOut.addTextElementToData("Error", ss.str());
766  // return;
767  }
768  catch(...)
769  {
770  __SS__ << "Table group \"" + groupName + "(" + groupKey.toString() + ")" + "\" members can not be loaded!" << __E__;
771  __COUT_ERR__ << ss.str();
772  xmlOut.addTextElementToData("Error", ss.str());
773  // return;
774  }
775 
776  __COUTV__(StringMacros::mapToString(groupMemberAliases));
777 
778  std::map<std::string, std::map<std::string, TableVersion>> versionAliases = cfgMgr->getVersionAliases();
779 
780  __COUT__ << "# of table version aliases: " << versionAliases.size() << __E__;
781 
782  // Seperate loop to get name and version
783  for(auto& memberPair : memberMap)
784  {
785  xmlOut.addTextElementToParent("MemberName", memberPair.first, parentEl);
786 
787  // if member is in groupMemberAliases, then alias version
788  if(groupMemberAliases.find(memberPair.first) != groupMemberAliases.end())
789  configEl =
790  xmlOut.addTextElementToParent("MemberVersion",
791  ConfigurationManager::ALIAS_VERSION_PREAMBLE + groupMemberAliases[memberPair.first], // return the ALIAS:<alias>
792  parentEl);
793  else
794  configEl = xmlOut.addTextElementToParent("MemberVersion", memberPair.second.toString(), parentEl);
795 
796  it = allTableInfo.find(memberPair.first);
797  if(it == allTableInfo.end())
798  {
799  xmlOut.addTextElementToData("Error", "Table \"" + memberPair.first + "\" can not be retrieved!");
800  continue;
801  }
802 
803  if(versionAliases.find(it->first) != versionAliases.end())
804  for(auto& aliasVersion : versionAliases[it->first])
805  xmlOut.addTextElementToParent("TableExistingVersion", ConfigurationManager::ALIAS_VERSION_PREAMBLE + aliasVersion.first, configEl);
806 
807  for(auto& version : it->second.versions_)
808  // if(version == memberPair.second) continue; //CHANGED by RAR on 11/14/2016
809  // (might as well show all versions in list to avoid user confusion) else
810  xmlOut.addTextElementToParent("TableExistingVersion", version.toString(), configEl);
811  }
812  // Seperate loop just for getting the Member Comment
813  for(auto& memberPair : memberMap)
814  {
815  //__COUT__ << "\tMember table " << memberPair.first << ":" <<
816  // memberPair.second << __E__;
817 
818  // xmlOut.addTextElementToParent("MemberName", memberPair.first, parentEl);
819  // if(commentsLoaded)
820  xmlOut.addTextElementToParent("MemberComment", allTableInfo.at(memberPair.first).tablePtr_->getView().getComment(), parentEl);
821  // else
822  // xmlOut.addTextElementToParent("MemberComment", "", parentEl);
823 
824  // __COUT__ << "\tMember table " << memberPair.first << ":" <<
825  // memberPair.second << __E__;
826 
827  // configEl = xmlOut.addTextElementToParent("MemberVersion",
828  // memberPair.second.toString(), parentEl);
829 
830  /* it = allTableInfo.find(memberPair.first);
831  if(it == allTableInfo.end())
832  {
833  xmlOut.addTextElementToData("Error","Table \"" +
834  memberPair.first +
835  "\" can not be retrieved!");
836  return;
837  }
838  */
839  // include aliases for this table
840  /*if(versionAliases.find(it->first) != versionAliases.end())
841  for (auto& aliasVersion:versionAliases[it->first])
842  xmlOut.addTextElementToParent("TableExistingVersion",
843  ConfigurationManager::ALIAS_VERSION_PREAMBLE + aliasVersion.first,
844  configEl);
845 
846  for (auto& version:it->second.versions_)
847  //if(version == memberPair.second) continue; //CHANGED by RAR on 11/14/2016
848  (might as well show all versions in list to avoid user confusion)
849  //else
850  xmlOut.addTextElementToParent("TableExistingVersion",
851  version.toString(), configEl);
852  */
853  }
854 
855 } // end handleGetTableGroupXML()
856 catch(std::runtime_error& e)
857 {
858  __SS__ << ("Error!\n\n" + std::string(e.what())) << __E__;
859  __COUT_ERR__ << "\n" << ss.str();
860  xmlOut.addTextElementToData("Error", ss.str());
861 }
862 catch(...)
863 {
864  __SS__ << ("Error!\n\n") << __E__;
865  __COUT_ERR__ << "\n" << ss.str();
866  xmlOut.addTextElementToData("Error", ss.str());
867 } // end handleGetTableGroupXML() catch
868 
869 //==============================================================================
870 bool ConfigurationSupervisorBase::handleAddDesktopIconXML(HttpXmlDocument& xmlOut,
871  ConfigurationManagerRW* cfgMgr,
872  const std::string& iconCaption,
873  const std::string& iconAltText,
874  const std::string& iconFolderPath,
875  const std::string& iconImageURL,
876  const std::string& iconWindowURL,
877  const std::string& iconPermissions,
878  std::string windowLinkedApp /*= ""*/,
879  unsigned int windowLinkedAppLID /*= 0*/,
880  bool enforceOneWindowInstance /*= false*/,
881  const std::string& windowParameters /*= ""*/) try
882 {
883  cfgMgr->getAllTableInfo(true /*refresh*/);
884 
885  const std::string& author = cfgMgr->getUsername();
886 
887  __COUTV__(author);
888  __COUTV__(iconCaption);
889  __COUTV__(iconAltText);
890  __COUTV__(iconFolderPath);
891  __COUTV__(iconImageURL);
892  __COUTV__(iconWindowURL);
893  __COUTV__(iconPermissions);
894  __COUTV__(windowLinkedApp);
895  __COUTV__(windowLinkedAppLID);
896  __COUTV__(enforceOneWindowInstance);
897 
898  __COUTV__(windowParameters); // map: CSV list
899 
900  // steps:
901  // activate active context
902  // modify desktop table and desktop parameters table
903  // save, activate, and modify alias
904  // just to match syntax in ConfiguratGUI
905  // tmpCfgMgr.activateTableGroup(
906  // tmpCfgMgr.getActiveGroupName(ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT),
907  // tmpCfgMgr.getActiveGroupKey(ConfigurationManager::ACTIVE_GROUP_NAME_CONTEXT)
908  // );
909 
910  cfgMgr->restoreActiveTableGroups(true /*throwErrors*/, "" /*pathToActiveGroupsFile*/, true /*onlyLoadIfBackboneOrContext*/
911  );
912 
913  const std::string backboneGroupName = cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::BACKBONE_TYPE);
914 
915  GroupEditStruct contextGroupEdit(ConfigurationManager::GroupType::CONTEXT_TYPE, cfgMgr);
916 
917  // Steps:
918  // - Create record in DesktopIconTable
919  // - Create parameter records in DesktopWindowParameterTable
920  // - Create new Context group
921  // - Update Aliases from old Context group to new Context group
922  // - Activate new group
923 
924  TableEditStruct& iconTable = contextGroupEdit.getTableEditStruct(DesktopIconTable::ICON_TABLE, true /*markModified*/);
925  TableEditStruct& parameterTable = contextGroupEdit.getTableEditStruct(DesktopIconTable::PARAMETER_TABLE, true /*markModified*/);
926  TableEditStruct& appTable = contextGroupEdit.getTableEditStruct(ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME);
927 
928  // Create record in DesktopIconTable
929  try
930  {
931  unsigned int row;
932  std::string iconUID;
933 
934  // create icon record
935  row = iconTable.tableView_->addRow(author, true /*incrementUniqueData*/, "generatedIcon");
936  iconUID = iconTable.tableView_->getDataView()[row][iconTable.tableView_->getColUID()];
937 
938  __COUTV__(row);
939  __COUTV__(iconUID);
940 
941  // set icon status true
942  iconTable.tableView_->setValueAsString("1", row, iconTable.tableView_->getColStatus());
943 
944  // set caption value
945  iconTable.tableView_->setURIEncodedValue(iconCaption, row, iconTable.tableView_->findCol(DesktopIconTable::COL_CAPTION));
946  // set alt text value
947  iconTable.tableView_->setURIEncodedValue(iconAltText, row, iconTable.tableView_->findCol(DesktopIconTable::COL_ALTERNATE_TEXT));
948  // set force one instance value
949  iconTable.tableView_->setValueAsString(
950  enforceOneWindowInstance ? "1" : "0", row, iconTable.tableView_->findCol(DesktopIconTable::COL_FORCE_ONLY_ONE_INSTANCE));
951  // set permissions value
952  iconTable.tableView_->setURIEncodedValue(iconPermissions, row, iconTable.tableView_->findCol(DesktopIconTable::COL_PERMISSIONS));
953  // set image URL value
954  iconTable.tableView_->setURIEncodedValue(iconImageURL, row, iconTable.tableView_->findCol(DesktopIconTable::COL_IMAGE_URL));
955  // set window URL value
956  iconTable.tableView_->setURIEncodedValue(iconWindowURL, row, iconTable.tableView_->findCol(DesktopIconTable::COL_WINDOW_CONTENT_URL));
957  // set folder value
958  iconTable.tableView_->setURIEncodedValue(iconFolderPath, row, iconTable.tableView_->findCol(DesktopIconTable::COL_FOLDER_PATH));
959 
960  // create link to icon app
961  if(windowLinkedAppLID > 0)
962  {
963  __COUTV__(windowLinkedAppLID);
964 
965  int appRow = appTable.tableView_->findRow(appTable.tableView_->findCol(XDAQContextTable::colApplication_.colId_), windowLinkedAppLID);
966  windowLinkedApp = appTable.tableView_->getDataView()[appRow][appTable.tableView_->getColUID()];
967  __COUT__ << "Found app by LID: " << windowLinkedApp << __E__;
968  } // end linked app LID handling
969 
970  if(windowLinkedApp != "" && windowLinkedApp != "undefined" && windowLinkedApp != TableViewColumnInfo::DATATYPE_STRING_DEFAULT)
971  {
972  // first check that UID exists
973  // if not, interpret as app class type and
974  // check for unique 'enabled' app with class type
975  __COUTV__(windowLinkedApp);
976 
977  if(!windowLinkedAppLID) // no need to check if LID lookup happened already
978  {
979  try
980  {
981  windowLinkedApp = StringMacros::decodeURIComponent(windowLinkedApp);
982  /* int appRow = */appTable.tableView_->findRow(appTable.tableView_->getColUID(), windowLinkedApp);
983  }
984  catch(const std::runtime_error& e)
985  {
986  // attempt to treat like class, and take first match
987  try
988  {
989  int appRow = appTable.tableView_->findRow(appTable.tableView_->findCol(XDAQContextTable::colApplication_.colClass_), windowLinkedApp);
990  windowLinkedApp = appTable.tableView_->getDataView()[appRow][appTable.tableView_->getColUID()];
991  }
992  catch(...)
993  {
994  // failed to treat like class, so throw original
995  __SS__ << "Failed to create an icon linking to app '" << windowLinkedApp << ".' The following error occurred: " << e.what() << __E__;
996  appTable.tableView_->print(ss);
997  __SS_THROW__;
998  }
999  }
1000  }
1001  __COUTV__(windowLinkedApp);
1002 
1003  iconTable.tableView_->setValueAsString(
1004  ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME, row, iconTable.tableView_->findCol(DesktopIconTable::COL_APP_LINK));
1005  iconTable.tableView_->setValueAsString(windowLinkedApp, row, iconTable.tableView_->findCol(DesktopIconTable::COL_APP_LINK_UID));
1006  } // end create app link
1007 
1008  // parse parameters
1009  std::map<std::string, std::string> parameters;
1010 
1011  __COUTV__(windowParameters);
1012  StringMacros::getMapFromString(windowParameters, parameters);
1013 
1014  // create link to icon parameters
1015  if(parameters.size())
1016  {
1017  // set parameter link table
1018  iconTable.tableView_->setValueAsString(DesktopIconTable::PARAMETER_TABLE, row, iconTable.tableView_->findCol(DesktopIconTable::COL_PARAMETER_LINK));
1019  // set parameter link Group ID
1020  iconTable.tableView_->setValueAsString(iconUID + "_Parameters", row, iconTable.tableView_->findCol(DesktopIconTable::COL_PARAMETER_LINK_GID));
1021 
1022  __COUTV__(StringMacros::mapToString(parameters));
1023 
1024  unsigned int gidCol = parameterTable.tableView_->findCol(DesktopIconTable::COL_PARAMETER_GID);
1025 
1026  // remove all existing records from groupID (e.g. parameters leftover from manual manipulations)
1027  std::vector<unsigned int /*row*/> rowsInGroup = parameterTable.tableView_->getGroupRows(gidCol, iconUID + "_Parameters" /*groupID*/);
1028 
1029  __COUTV__(StringMacros::vectorToString(rowsInGroup));
1030 
1031  // go through vector backwards to maintain row integrity
1032  for(unsigned int r = rowsInGroup.size() - 1; r < rowsInGroup.size(); --r)
1033  parameterTable.tableView_->removeRowFromGroup(rowsInGroup[r], gidCol, iconUID + "_Parameters" /*groupID*/, true /*deleteRowIfNoGroupLeft*/);
1034 
1035  // create new parameters
1036  for(const auto& parameter : parameters)
1037  {
1038  // create parameter record
1039  row = parameterTable.tableView_->addRow(author, true /*incrementUniqueData*/, "generatedParameter");
1040 
1041  // set parameter status true
1042  parameterTable.tableView_->setValueAsString("1", row, parameterTable.tableView_->getColStatus());
1043  // set parameter Group ID
1044  parameterTable.tableView_->setValueAsString(iconUID + "_Parameters", row, gidCol);
1045  // set parameter key
1046  parameterTable.tableView_->setURIEncodedValue(parameter.first, row, parameterTable.tableView_->findCol(DesktopIconTable::COL_PARAMETER_KEY));
1047  // set parameter value
1048  parameterTable.tableView_->setURIEncodedValue(parameter.second, row, parameterTable.tableView_->findCol(DesktopIconTable::COL_PARAMETER_VALUE));
1049  } // end parameter loop
1050 
1051  std::stringstream ss;
1052  parameterTable.tableView_->print(ss);
1053  __COUT__ << ss.str();
1054 
1055  parameterTable.tableView_->init(); // verify new table (throws runtime_errors)
1056 
1057  } // end create parameters link
1058 
1059  std::stringstream ss;
1060  iconTable.tableView_->print(ss);
1061  __COUT__ << ss.str();
1062 
1063  iconTable.tableView_->init(); // verify new table (throws runtime_errors)
1064  }
1065  catch(...)
1066  {
1067  __COUT__ << "Icon table errors while saving. Erasing all newly "
1068  "created table versions."
1069  << __E__;
1070 
1071  throw; // re-throw
1072  } // end catch
1073 
1074  __COUT__ << "Edits complete for new desktop icon, now making persistent tables." << __E__;
1075 
1076  // all edits are complete and tables verified
1077 
1078  // Remaining steps:
1079  // save tables
1080  // save new context group and activate it
1081  // check for aliases ...
1082  // if tables aliased.. update table aliases in backbone
1083  // if context group aliased, update group aliases in backbone
1084  // if backbone modified, save group and activate it
1085 
1086  TableGroupKey newContextKey;
1087  bool foundEquivalentContextKey;
1088  TableGroupKey newBackboneKey;
1089  bool foundEquivalentBackboneKey;
1090 
1091  contextGroupEdit.saveChanges(contextGroupEdit.originalGroupName_,
1092  newContextKey,
1093  &foundEquivalentContextKey,
1094  true /*activateNewGroup*/,
1095  true /*updateGroupAliases*/,
1096  true /*updateTableAliases*/,
1097  &newBackboneKey,
1098  &foundEquivalentBackboneKey);
1099 
1100  xmlOut.addTextElementToData("contextGroupName", contextGroupEdit.originalGroupName_);
1101  xmlOut.addTextElementToData("contextGroupKey", newContextKey.toString());
1102 
1103  xmlOut.addTextElementToData("backboneGroupName", backboneGroupName);
1104  xmlOut.addTextElementToData("backboneGroupKey", newBackboneKey.toString());
1105 
1106  // always add active table groups to xml response
1107  ConfigurationSupervisorBase::getConfigurationStatusXML(xmlOut, cfgMgr);
1108 
1109  return true;
1110  //---------------------------------------------------
1111 
1112  if(0)
1113  {
1114  // save map of group members get context members active table versions
1115  std::map<std::string, TableVersion> contextGroupMembers;
1116  std::map<std::string, TableVersion> backboneGroupMembers;
1117  {
1118  std::map<std::string, TableVersion> activeTables = cfgMgr->getActiveVersions();
1119  for(auto& table : cfgMgr->getContextMemberNames())
1120  try
1121  {
1122  __COUT__ << table << " v" << activeTables.at(table) << __E__;
1123  contextGroupMembers[table] = activeTables.at(table);
1124  }
1125  catch(...)
1126  {
1127  __SS__ << "Error! Could not find Context member table '" << table << ".' All Context members must be present to add a desktop icon."
1128  << __E__;
1129  __SS_THROW__;
1130  }
1131  for(auto& table : cfgMgr->getBackboneMemberNames())
1132  try
1133  {
1134  __COUT__ << table << " v" << activeTables.at(table) << __E__;
1135  backboneGroupMembers[table] = activeTables.at(table);
1136  }
1137  catch(...)
1138  {
1139  __SS__ << "Error! Could not find Backbone member table '" << table << ".' All Backbone members must be present to add a desktop icon."
1140  << __E__;
1141  __SS_THROW__;
1142  }
1143  }
1144 
1145  const std::string contextGroupName = cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONTEXT_TYPE);
1146  const TableGroupKey originalContextGroupKey = cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONTEXT_TYPE);
1147  const std::string backboneGroupName = cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::BACKBONE_TYPE);
1148  const TableGroupKey originalBackboneGroupKey = cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::BACKBONE_TYPE);
1149 
1150  __COUTV__(contextGroupName);
1151  __COUTV__(originalContextGroupKey);
1152  __COUTV__(backboneGroupName);
1153  __COUTV__(originalBackboneGroupKey);
1154 
1155  if(contextGroupName == "" || originalContextGroupKey.isInvalid())
1156  {
1157  __SS__ << "Error! No active Context group found. "
1158  "There must be an active Context group to add a Desktop Icon."
1159  << __E__;
1160  __SS_THROW__;
1161  }
1162 
1163  // Steps:
1164  // - Create record in DesktopIconTable
1165  // - Create parameter records in DesktopWindowParameterTable
1166  // - Create new Context group
1167  // - Update Aliases from old Context group to new Context group
1168  // - Activate new group
1169 
1170  TableEditStruct iconTable(DesktopIconTable::ICON_TABLE,
1171  cfgMgr); // Table ready for editing!
1172  TableEditStruct parameterTable(DesktopIconTable::PARAMETER_TABLE,
1173  cfgMgr); // Table ready for editing!
1174  TableEditStruct appTable(ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME,
1175  cfgMgr); // Table ready for editing!
1176 
1177  // Create record in DesktopIconTable
1178  try
1179  {
1180  unsigned int row;
1181  std::string iconUID;
1182 
1183  // create icon record
1184  row = iconTable.tableView_->addRow(author, true /*incrementUniqueData*/, "generatedIcon");
1185  iconUID = iconTable.tableView_->getDataView()[row][iconTable.tableView_->getColUID()];
1186 
1187  __COUTV__(row);
1188  __COUTV__(iconUID);
1189 
1190  // set icon status true
1191  iconTable.tableView_->setValueAsString("1", row, iconTable.tableView_->getColStatus());
1192 
1193  // set caption value
1194  iconTable.tableView_->setURIEncodedValue(iconCaption, row, iconTable.tableView_->findCol(DesktopIconTable::COL_CAPTION));
1195  // set alt text value
1196  iconTable.tableView_->setURIEncodedValue(iconAltText, row, iconTable.tableView_->findCol(DesktopIconTable::COL_ALTERNATE_TEXT));
1197  // set force one instance value
1198  iconTable.tableView_->setValueAsString(
1199  enforceOneWindowInstance ? "1" : "0", row, iconTable.tableView_->findCol(DesktopIconTable::COL_FORCE_ONLY_ONE_INSTANCE));
1200  // set permissions value
1201  iconTable.tableView_->setURIEncodedValue(iconPermissions, row, iconTable.tableView_->findCol(DesktopIconTable::COL_PERMISSIONS));
1202  // set image URL value
1203  iconTable.tableView_->setURIEncodedValue(iconImageURL, row, iconTable.tableView_->findCol(DesktopIconTable::COL_IMAGE_URL));
1204  // set window URL value
1205  iconTable.tableView_->setURIEncodedValue(iconWindowURL, row, iconTable.tableView_->findCol(DesktopIconTable::COL_WINDOW_CONTENT_URL));
1206  // set folder value
1207  iconTable.tableView_->setURIEncodedValue(iconFolderPath, row, iconTable.tableView_->findCol(DesktopIconTable::COL_FOLDER_PATH));
1208 
1209  // create link to icon app
1210  if(windowLinkedAppLID > 0)
1211  {
1212  __COUTV__(windowLinkedAppLID);
1213 
1214  int appRow = appTable.tableView_->findRow(appTable.tableView_->findCol(XDAQContextTable::colApplication_.colId_), windowLinkedAppLID);
1215  windowLinkedApp = appTable.tableView_->getDataView()[appRow][appTable.tableView_->getColUID()];
1216  __COUT__ << "Found app by LID: " << windowLinkedApp << __E__;
1217  } // end linked app LID handling
1218 
1219  if(windowLinkedApp != "" && windowLinkedApp != "undefined" && windowLinkedApp != TableViewColumnInfo::DATATYPE_STRING_DEFAULT)
1220  {
1221  // first check that UID exists
1222  // if not, interpret as app class type and
1223  // check for unique 'enabled' app with class type
1224  __COUTV__(windowLinkedApp);
1225 
1226  if(!windowLinkedAppLID) // no need to check if LID lookup happened already
1227  {
1228  try
1229  {
1230  /*int appRow =*/ appTable.tableView_->findRow(appTable.tableView_->getColUID(), windowLinkedApp);
1231  }
1232  catch(const std::runtime_error& e)
1233  {
1234  // attempt to treat like class, and take first match
1235  try
1236  {
1237  int appRow =
1238  appTable.tableView_->findRow(appTable.tableView_->findCol(XDAQContextTable::colApplication_.colClass_), windowLinkedApp);
1239  windowLinkedApp = appTable.tableView_->getDataView()[appRow][appTable.tableView_->getColUID()];
1240  }
1241  catch(...)
1242  {
1243  // failed to treat like class, so throw original
1244  __SS__ << "Failed to create an icon linking to app '" << windowLinkedApp << ".' The following error occurred: " << e.what()
1245  << __E__;
1246  __SS_THROW__;
1247  }
1248  }
1249  }
1250  __COUTV__(windowLinkedApp);
1251 
1252  iconTable.tableView_->setValueAsString(
1253  ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME, row, iconTable.tableView_->findCol(DesktopIconTable::COL_APP_LINK));
1254  iconTable.tableView_->setValueAsString(windowLinkedApp, row, iconTable.tableView_->findCol(DesktopIconTable::COL_APP_LINK_UID));
1255  } // end create app link
1256 
1257  // parse parameters
1258  std::map<std::string, std::string> parameters;
1259 
1260  __COUTV__(windowParameters);
1261  StringMacros::getMapFromString(windowParameters, parameters);
1262 
1263  // create link to icon parameters
1264  if(parameters.size())
1265  {
1266  // set parameter link table
1267  iconTable.tableView_->setValueAsString(
1268  DesktopIconTable::PARAMETER_TABLE, row, iconTable.tableView_->findCol(DesktopIconTable::COL_PARAMETER_LINK));
1269  // set parameter link Group ID
1270  iconTable.tableView_->setValueAsString(iconUID + "_Parameters", row, iconTable.tableView_->findCol(DesktopIconTable::COL_PARAMETER_LINK_GID));
1271 
1272  __COUTV__(StringMacros::mapToString(parameters));
1273 
1274  for(const auto& parameter : parameters)
1275  {
1276  // create parameter record
1277  row = parameterTable.tableView_->addRow(author, true /*incrementUniqueData*/, "generatedParameter");
1278 
1279  // set parameter status true
1280  parameterTable.tableView_->setValueAsString("1", row, parameterTable.tableView_->getColStatus());
1281  // set parameter Group ID
1282  parameterTable.tableView_->setValueAsString(
1283  iconUID + "_Parameters", row, parameterTable.tableView_->findCol(DesktopIconTable::COL_PARAMETER_GID));
1284  // set parameter key
1285  parameterTable.tableView_->setURIEncodedValue(
1286  parameter.first, row, parameterTable.tableView_->findCol(DesktopIconTable::COL_PARAMETER_KEY));
1287  // set parameter value
1288  parameterTable.tableView_->setURIEncodedValue(
1289  parameter.second, row, parameterTable.tableView_->findCol(DesktopIconTable::COL_PARAMETER_VALUE));
1290  } // end parameter loop
1291 
1292  std::stringstream ss;
1293  parameterTable.tableView_->print(ss);
1294  __COUT__ << ss.str();
1295 
1296  parameterTable.tableView_->init(); // verify new table (throws runtime_errors)
1297 
1298  } // end create parameters link
1299 
1300  std::stringstream ss;
1301  iconTable.tableView_->print(ss);
1302  __COUT__ << ss.str();
1303 
1304  iconTable.tableView_->init(); // verify new table (throws runtime_errors)
1305  }
1306  catch(...)
1307  {
1308  __COUT__ << "Icon table errors while saving. Erasing all newly "
1309  "created table versions."
1310  << __E__;
1311  if(iconTable.createdTemporaryVersion_) // if temporary version created here
1312  {
1313  __COUT__ << "Erasing temporary version " << iconTable.tableName_ << "-v" << iconTable.temporaryVersion_ << __E__;
1314  // erase with proper version management
1315  cfgMgr->eraseTemporaryVersion(iconTable.tableName_, iconTable.temporaryVersion_);
1316  }
1317 
1318  if(parameterTable.createdTemporaryVersion_) // if temporary version created here
1319  {
1320  __COUT__ << "Erasing temporary version " << parameterTable.tableName_ << "-v" << parameterTable.temporaryVersion_ << __E__;
1321  // erase with proper version management
1322  cfgMgr->eraseTemporaryVersion(parameterTable.tableName_, parameterTable.temporaryVersion_);
1323  }
1324 
1325  if(appTable.createdTemporaryVersion_) // if temporary version created here
1326  {
1327  __COUT__ << "Erasing temporary version " << appTable.tableName_ << "-v" << appTable.temporaryVersion_ << __E__;
1328  // erase with proper version management
1329  cfgMgr->eraseTemporaryVersion(appTable.tableName_, appTable.temporaryVersion_);
1330  }
1331 
1332  throw; // re-throw
1333  } // end catch
1334 
1335  __COUT__ << "Edits complete for new desktop icon, now making persistent tables." << __E__;
1336 
1337  // all edits are complete and tables verified
1338 
1339  // Remaining steps:
1340  // save tables
1341  // save new context group and activate it
1342  // check for aliases ...
1343  // if tables aliased.. update table aliases in backbone
1344  // if context group aliased, update group aliases in backbone
1345  // if backbone modified, save group and activate it
1346 
1347  __COUT__ << "Original version is " << iconTable.tableName_ << "-v" << iconTable.originalVersion_ << __E__;
1348  __COUT__ << "Original version is " << parameterTable.tableName_ << "-v" << parameterTable.originalVersion_ << __E__;
1349 
1350  contextGroupMembers[DesktopIconTable::ICON_TABLE] =
1351  ConfigurationSupervisorBase::saveModifiedVersionXML(xmlOut,
1352  cfgMgr,
1353  iconTable.tableName_,
1354  iconTable.originalVersion_,
1355  true /*make temporary*/,
1356  iconTable.table_,
1357  iconTable.temporaryVersion_,
1358  true /*ignoreDuplicates*/); // make temporary version to save persistent version properly
1359  contextGroupMembers[DesktopIconTable::PARAMETER_TABLE] =
1360  ConfigurationSupervisorBase::saveModifiedVersionXML(xmlOut,
1361  cfgMgr,
1362  parameterTable.tableName_,
1363  parameterTable.originalVersion_,
1364  true /*make temporary*/,
1365  parameterTable.table_,
1366  parameterTable.temporaryVersion_,
1367  true /*ignoreDuplicates*/); // make temporary version to save persistent version properly
1368 
1369  __COUT__ << "Temporary target version is " << iconTable.tableName_ << "-v" << contextGroupMembers[DesktopIconTable::ICON_TABLE] << "-v"
1370  << iconTable.temporaryVersion_ << __E__;
1371  __COUT__ << "Temporary target version is " << parameterTable.tableName_ << "-v" << contextGroupMembers[DesktopIconTable::PARAMETER_TABLE] << "-v"
1372  << parameterTable.temporaryVersion_ << __E__;
1373 
1374  contextGroupMembers[DesktopIconTable::ICON_TABLE] =
1375  ConfigurationSupervisorBase::saveModifiedVersionXML(xmlOut,
1376  cfgMgr,
1377  iconTable.tableName_,
1378  iconTable.originalVersion_,
1379  false /*make temporary*/,
1380  iconTable.table_,
1381  iconTable.temporaryVersion_,
1382  false /*ignoreDuplicates*/,
1383  true /*lookForEquivalent*/); // save persistent version properly
1384  contextGroupMembers[DesktopIconTable::PARAMETER_TABLE] =
1385  ConfigurationSupervisorBase::saveModifiedVersionXML(xmlOut,
1386  cfgMgr,
1387  parameterTable.tableName_,
1388  parameterTable.originalVersion_,
1389  false /*make temporary*/,
1390  parameterTable.table_,
1391  parameterTable.temporaryVersion_,
1392  false /*ignoreDuplicates*/,
1393  true /*lookForEquivalent*/); // save persistent version properly
1394 
1395  __COUT__ << "Final target version is " << iconTable.tableName_ << "-v" << contextGroupMembers[DesktopIconTable::ICON_TABLE] << __E__;
1396  __COUT__ << "Final target version is " << parameterTable.tableName_ << "-v" << contextGroupMembers[DesktopIconTable::PARAMETER_TABLE] << __E__;
1397 
1398  for(auto& table : contextGroupMembers)
1399  {
1400  __COUT__ << table.first << " v" << table.second << __E__;
1401  }
1402 
1403  __COUT__ << "Checking for duplicate Context groups..." << __E__;
1404  TableGroupKey newContextKey = cfgMgr->findTableGroup(contextGroupName, contextGroupMembers);
1405 
1406  if(!newContextKey.isInvalid())
1407  {
1408  __COUT__ << "Found equivalent group key (" << newContextKey << ") for " << contextGroupName << "." << __E__;
1409  xmlOut.addTextElementToData(contextGroupName + "_foundEquivalentKey",
1410  "1"); // indicator
1411  }
1412  else
1413  {
1414  newContextKey = cfgMgr->saveNewTableGroup(contextGroupName, contextGroupMembers);
1415  __COUT__ << "Saved new Context group key (" << newContextKey << ") for " << contextGroupName << "." << __E__;
1416  }
1417 
1418  xmlOut.addTextElementToData("contextGroupName", contextGroupName);
1419  xmlOut.addTextElementToData("contextGroupKey", newContextKey.toString());
1420 
1421  // check for aliases of original group key and original table version
1422 
1423  __COUT__ << "Original version is " << iconTable.tableName_ << "-v" << iconTable.originalVersion_ << __E__;
1424  __COUT__ << "Original version is " << parameterTable.tableName_ << "-v" << parameterTable.originalVersion_ << __E__;
1425 
1426  bool groupAliasChange = false;
1427  bool tableAliasChange = false;
1428 
1429  { // check group aliases ... a la
1430  // ConfigurationGUISupervisor::handleSetGroupAliasInBackboneXML
1431 
1432  TableBase* table = cfgMgr->getTableByName(ConfigurationManager::GROUP_ALIASES_TABLE_NAME);
1433  TableVersion originalVersion = backboneGroupMembers[ConfigurationManager::GROUP_ALIASES_TABLE_NAME];
1434  TableVersion temporaryVersion = table->createTemporaryView(originalVersion);
1435  TableView* tableView = table->getTemporaryView(temporaryVersion);
1436 
1437  // unsigned int col;
1438  unsigned int row = 0;
1439 
1440  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
1441  cfgMgr->getNode(ConfigurationManager::GROUP_ALIASES_TABLE_NAME).getChildren();
1442  std::string groupName, groupKey;
1443  for(auto& aliasNodePair : aliasNodePairs)
1444  {
1445  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
1446  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
1447 
1448  __COUT__ << "Group Alias: " << aliasNodePair.first << " => " << groupName << "(" << groupKey << "); row=" << row << __E__;
1449 
1450  if(groupName == contextGroupName && TableGroupKey(groupKey) == originalContextGroupKey)
1451  {
1452  __COUT__ << "Found alias! Changing group key." << __E__;
1453 
1454  groupAliasChange = true;
1455 
1456  tableView->setValueAsString(newContextKey.toString(), row, tableView->findCol("GroupKey"));
1457  }
1458 
1459  ++row;
1460  }
1461 
1462  if(groupAliasChange)
1463  {
1464  std::stringstream ss;
1465  tableView->print(ss);
1466  __COUT__ << ss.str();
1467 
1468  // save or find equivalent
1469  backboneGroupMembers[ConfigurationManager::GROUP_ALIASES_TABLE_NAME] =
1470  ConfigurationSupervisorBase::saveModifiedVersionXML(xmlOut,
1471  cfgMgr,
1472  table->getTableName(),
1473  originalVersion,
1474  false /*makeTemporary*/,
1475  table,
1476  temporaryVersion,
1477  false /*ignoreDuplicates*/,
1478  true /*lookForEquivalent*/);
1479 
1480  __COUT__ << "Original version is " << table->getTableName() << "-v" << originalVersion << " and new version is v"
1481  << backboneGroupMembers[ConfigurationManager::GROUP_ALIASES_TABLE_NAME] << __E__;
1482  }
1483 
1484  } // end group alias check
1485 
1486  { // check version aliases
1487 
1488  TableBase* table = cfgMgr->getTableByName(ConfigurationManager::VERSION_ALIASES_TABLE_NAME);
1489  TableVersion originalVersion = backboneGroupMembers[ConfigurationManager::VERSION_ALIASES_TABLE_NAME];
1490  TableVersion temporaryVersion = table->createTemporaryView(originalVersion);
1491  TableView* tableView = table->getTemporaryView(temporaryVersion);
1492 
1493  //unsigned int col;
1494  unsigned int row = 0;
1495 
1496  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
1497  cfgMgr->getNode(ConfigurationManager::VERSION_ALIASES_TABLE_NAME).getChildren();
1498  std::string tableName, tableVersion;
1499  for(auto& aliasNodePair : aliasNodePairs)
1500  {
1501  tableName = aliasNodePair.second.getNode("TableName").getValueAsString();
1502  tableVersion = aliasNodePair.second.getNode("Version").getValueAsString();
1503 
1504  __COUT__ << "Table Alias: " << aliasNodePair.first << " => " << tableName << "-v" << tableVersion << "" << __E__;
1505 
1506  if(tableName == DesktopIconTable::ICON_TABLE && TableVersion(tableVersion) == iconTable.originalVersion_)
1507  {
1508  __COUT__ << "Found alias! Changing icon table version alias." << __E__;
1509 
1510  tableAliasChange = true;
1511 
1512  tableView->setValueAsString(contextGroupMembers[DesktopIconTable::ICON_TABLE].toString(), row, tableView->findCol("Version"));
1513  }
1514  else if(tableName == DesktopIconTable::PARAMETER_TABLE && TableVersion(tableVersion) == parameterTable.originalVersion_)
1515  {
1516  __COUT__ << "Found alias! Changing icon parameter table version alias." << __E__;
1517 
1518  tableAliasChange = true;
1519 
1520  tableView->setValueAsString(contextGroupMembers[DesktopIconTable::PARAMETER_TABLE].toString(), row, tableView->findCol("Version"));
1521  }
1522 
1523  ++row;
1524  }
1525 
1526  if(tableAliasChange)
1527  {
1528  std::stringstream ss;
1529  tableView->print(ss);
1530  __COUT__ << ss.str();
1531 
1532  // save or find equivalent
1533  backboneGroupMembers[ConfigurationManager::VERSION_ALIASES_TABLE_NAME] =
1534  ConfigurationSupervisorBase::saveModifiedVersionXML(xmlOut,
1535  cfgMgr,
1536  table->getTableName(),
1537  originalVersion,
1538  false /*makeTemporary*/,
1539  table,
1540  temporaryVersion,
1541  false /*ignoreDuplicates*/,
1542  true /*lookForEquivalent*/);
1543 
1544  __COUT__ << "Original version is " << table->getTableName() << "-v" << originalVersion << " and new version is v"
1545  << backboneGroupMembers[ConfigurationManager::VERSION_ALIASES_TABLE_NAME] << __E__;
1546  }
1547 
1548  } // end table version alias check
1549 
1550  // if backbone modified, save group and activate it
1551  if(groupAliasChange || tableAliasChange)
1552  {
1553  for(auto& table : backboneGroupMembers)
1554  {
1555  __COUT__ << table.first << " v" << table.second << __E__;
1556  }
1557  }
1558 
1559  __COUT__ << "Checking for duplicate Backbone groups..." << __E__;
1560  TableGroupKey newBackboneKey = cfgMgr->findTableGroup(backboneGroupName, backboneGroupMembers);
1561 
1562  if(!newBackboneKey.isInvalid())
1563  {
1564  __COUT__ << "Found equivalent group key (" << newBackboneKey << ") for " << backboneGroupName << "." << __E__;
1565  xmlOut.addTextElementToData(backboneGroupName + "_foundEquivalentKey", "1" /*indicator*/);
1566  }
1567  else
1568  {
1569  newBackboneKey = cfgMgr->saveNewTableGroup(backboneGroupName, backboneGroupMembers);
1570  __COUT__ << "Saved new Backbone group key (" << newBackboneKey << ") for " << backboneGroupName << "." << __E__;
1571  }
1572 
1573  xmlOut.addTextElementToData("backboneGroupName", backboneGroupName);
1574  xmlOut.addTextElementToData("backboneGroupKey", newBackboneKey.toString());
1575 
1576  // Now need to activate Context and Backbone group
1577  __COUT__ << "Activating Context group key (" << newContextKey << ") for " << contextGroupName << "." << __E__;
1578  __COUT__ << "Activating Backbone group key (" << newBackboneKey << ") for " << backboneGroupName << "." << __E__;
1579 
1580  // acquire all active groups and ignore errors, so that activateTableGroup does not
1581  // erase other active groups
1582  cfgMgr->restoreActiveTableGroups(false /*throwErrors*/, "" /*pathToActiveGroupsFile*/, false /*onlyLoadIfBackboneOrContext*/
1583  );
1584 
1585  // activate group
1586  cfgMgr->activateTableGroup(contextGroupName, newContextKey);
1587  cfgMgr->activateTableGroup(backboneGroupName, newBackboneKey);
1588 
1589  // always add active table groups to xml response
1590  ConfigurationSupervisorBase::getConfigurationStatusXML(xmlOut, cfgMgr);
1591  }
1592  return true;
1593 } // end handleAddDesktopIconXML()
1594 catch(std::runtime_error& e)
1595 {
1596  __COUT__ << "Error detected!\n\n " << e.what() << __E__;
1597  xmlOut.addTextElementToData("Error", "Error adding Desktop Icon! " + std::string(e.what()));
1598  return false;
1599 }
1600 catch(...)
1601 {
1602  __COUT__ << "Unknown Error detected!\n\n " << __E__;
1603  xmlOut.addTextElementToData("Error", "Error adding Desktop Icon! ");
1604  return false;
1605 } // end handleAddDesktopIconXML() catch