Belle II Software  release-06-01-15
ModuleManager.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <framework/core/Module.h>
10 #include <framework/core/ModuleManager.h>
11 #include <framework/utilities/FileSystem.h>
12 #include <framework/logging/Logger.h>
13 
14 #include <boost/filesystem.hpp>
15 #include <regex>
16 
17 #include <fstream>
18 
19 using namespace Belle2;
20 using namespace std;
21 
22 #define MAP_FILE_EXTENSION ".b2modmap"
23 #define LIB_FILE_EXTENSION ".so"
24 
25 
27 {
28  static ModuleManager instance;
29  return instance;
30 }
31 
32 
34 {
35  //Only register a module proxy if it was not yet registered.
36  if (m_registeredProxyMap.count(moduleProxy->getModuleName()) == 0) {
37  m_registeredProxyMap.insert(make_pair(moduleProxy->getModuleName(), moduleProxy));
38  } else {
39  B2ERROR("There seems to be more than one module called '" << moduleProxy->getModuleName() <<
40  "'. Since module names are unique, you must rename one of them!");
41  }
42 }
43 
44 
45 void ModuleManager::addModuleSearchPath(const string& path)
46 {
47  if (FileSystem::isDir(path)) {
48  m_moduleSearchPathList.push_back(path);
49 
50  //Search the path for map files and add the contained module names to the known module names
51  auto fullPath = boost::filesystem::system_complete(boost::filesystem::path(path));
52  boost::filesystem::directory_iterator endIter;
53 
54 
55  map<string, string> moduleNameLibMap;
56  for (boost::filesystem::directory_iterator dirItr(fullPath); dirItr != endIter; ++dirItr) {
57  //Only files in the given folder are taken, subfolders are not used.
58  if (boost::filesystem::is_regular_file(dirItr->status())) {
59  if (boost::filesystem::extension(dirItr->path()) == MAP_FILE_EXTENSION) {
60  fillModuleNameLibMap(moduleNameLibMap, *dirItr);
61  }
62  }
63  }
64  //put modules into central map, if they haven't been added yet
65  m_moduleNameLibMap.insert(moduleNameLibMap.begin(), moduleNameLibMap.end());
66  }
67 }
68 
69 
70 const list<string>& ModuleManager::getModuleSearchPaths() const
71 {
72  return m_moduleSearchPathList;
73 }
74 
75 
76 const map<string, string>& ModuleManager::getAvailableModules() const
77 {
78  return m_moduleNameLibMap;
79 }
80 
81 
82 ModulePtr ModuleManager::registerModule(const string& moduleName, std::string sharedLibPath) noexcept(false)
83 {
84  auto moduleIter = m_registeredProxyMap.find(moduleName);
85 
86  // Print an error message and then raise the exception ...
87  auto error = [&moduleName](const std::string & text) -> void {
88  auto exception = ModuleNotCreatedError() << moduleName << text;
89  B2ERROR(exception.what());
90  throw exception;
91  };
92 
93  //If the proxy of the module was not already registered, load the corresponding shared library first
94  if (moduleIter == m_registeredProxyMap.end()) {
95  // no library specified, try to find it from map of known modules
96  if (sharedLibPath.empty()) {
97  auto libIter = m_moduleNameLibMap.find(moduleName);
98  if (libIter != m_moduleNameLibMap.end()) {
99  sharedLibPath = libIter->second;
100  } else {
101  error("The module is not known to the framework!");
102  }
103  }
104  // Now we have a library name (provided or determined, try to load)
105  if (!FileSystem::isFile(sharedLibPath)) {
106  error("Could not load shared library " + sharedLibPath + ", file does not exist!");
107  }
108  if (!FileSystem::loadLibrary(sharedLibPath)) {
109  error("Could not load shared library " + sharedLibPath + ".");
110  }
111  //Check if the loaded shared library file contained the module
112  moduleIter = m_registeredProxyMap.find(moduleName);
113  if (moduleIter == m_registeredProxyMap.end()) {
114  error("The shared library " + sharedLibPath + " does not contain the module!");
115  }
116  }
117 
118  //Create an instance of the module found or loaded in the previous steps and return it.
119  //The iterator cannot point to the end of the map, we checked in all branches.
120  ModulePtr currModulePtr = moduleIter->second->createModule();
121  m_createdModulesList.push_back(currModulePtr);
122  return currModulePtr;
123 }
124 
125 
127 {
128  return m_createdModulesList;
129 }
130 
131 
132 ModulePtrList ModuleManager::getModulesByProperties(const ModulePtrList& modulePathList, unsigned int propertyFlags)
133 {
134  ModulePtrList tmpModuleList;
135 
136  for (const ModulePtr& module : modulePathList)
137  if (module->hasProperties(propertyFlags)) tmpModuleList.push_back(module);
138 
139  return tmpModuleList;
140 }
141 
142 bool ModuleManager::allModulesHaveFlag(const ModulePtrList& list, unsigned int flag)
143 {
144  for (const auto& m : list) {
145  if (!m->hasProperties(flag))
146  return false;
147  }
148  return true;
149 }
150 
151 
152 //============================================================================
153 // Private methods
154 //============================================================================
155 
156 void ModuleManager::fillModuleNameLibMap(std::map<std::string, std::string>& moduleNameLibMap,
157  const boost::filesystem::directory_entry& mapPath)
158 {
159  //Check if the associated shared library file exists
160  string sharedLibPath = boost::filesystem::change_extension(mapPath, LIB_FILE_EXTENSION).string();
161  if (!FileSystem::fileExists(sharedLibPath)) {
162  B2WARNING("The shared library file: " << sharedLibPath << " doesn't exist, but is required by " << mapPath.path().string());
163  return;
164  }
165 
166  //Open the map file and parse the content line by line
167  ifstream mapFile(mapPath.path().string().c_str());
168  string currentLine;
169 
170  //Read each line of the map file and use boost regular expression to find the module name string in brackets.
171  std::regex expression("^REG_MODULE\\((.+)\\)$");
172  std::match_results<std::string::const_iterator> matchResult;
173 
174  int lineNr{0};
175  while (getline(mapFile, currentLine)) {
176  ++lineNr;
177 
178  if (!std::regex_match(currentLine, matchResult, expression)) {
179  B2ERROR("Problem parsing map file " << mapPath << ": Invalid entry in line " << lineNr << ", skipping remaining file");
180  return;
181  }
182 
183  string moduleName(matchResult[1].first, matchResult[1].second);
184  //Add result to map
185  if (moduleNameLibMap.count(moduleName) == 0) {
186  moduleNameLibMap.insert(make_pair(moduleName, sharedLibPath));
187  } else {
188  B2ERROR("There seems to be more than one module called '" << moduleName <<
189  "'. Since module names are unique, you must rename one of them!");
190  }
191  }
192 
193  //Close the map file
194  mapFile.close();
195 }
196 
197 ModuleManager::ModuleManager() = default;
198 
200 
202 {
203  m_createdModulesList.clear();
204 }
static bool loadLibrary(std::string library, bool fullname=true)
Load a shared library.
Definition: FileSystem.cc:62
static bool isFile(const std::string &filename)
Check if filename points to an existing file.
Definition: FileSystem.cc:44
static bool isDir(const std::string &filename)
Check if filename points to an existing directory.
Definition: FileSystem.cc:50
static bool fileExists(const std::string &filename)
Check if the file with given filename exists.
Definition: FileSystem.cc:31
The ModuleManager Class.
Definition: ModuleManager.h:58
void registerModuleProxy(ModuleProxyBase *moduleProxy)
Registers a module proxy.
const std::map< std::string, std::string > & getAvailableModules() const
Returns a map of all modules that were found in the module search paths.
const std::list< std::shared_ptr< Module > > & getCreatedModules() const
Returns a reference to the list of created modules.
static std::list< std::shared_ptr< Module > > getModulesByProperties(const std::list< std::shared_ptr< Module > > &modulePathList, unsigned int propertyFlags)
Returns a list of those modules which carry property flags matching the specified ones.
const std::list< std::string > & getModuleSearchPaths() const
Returns a reference to the list of the modules search filepaths.
std::shared_ptr< Module > registerModule(const std::string &moduleName, std::string sharedLibPath="") noexcept(false)
Creates an instance of a module and registers it to the ModuleManager.
static bool allModulesHaveFlag(const std::list< std::shared_ptr< Module >> &list, unsigned int flag)
Returns true if and only if all modules in list have the given flag (or list is empty).
static ModuleManager & Instance()
Exception is thrown if the requested module could not be created by the ModuleManager.
~ModuleManager()
The ModuleManager destructor.
ModuleManager()
The constructor is hidden to avoid that someone creates an instance of this class.
void reset()
Delete all created modules.
void addModuleSearchPath(const std::string &path)
Adds a new filepath to the list of filepaths which are searched for a requested module.
static void fillModuleNameLibMap(std::map< std::string, std::string > &moduleNameLibMap, const boost::filesystem::directory_entry &mapPath)
Adds the module names defined in the map file to the list of known module names.
The base module proxy class is used to create new instances of a module.
Definition: Module.h:597
const std::string & getModuleName() const
Returns the module name of the module associated to this proxy.
Definition: Module.h:630
std::shared_ptr< Module > ModulePtr
Defines a pointer to a module object as a boost shared pointer.
Definition: Module.h:40
std::list< ModulePtr > ModulePtrList
Defines a std::list of shared module pointers.
Definition: Module.h:584
Abstract base class for different kinds of events.