All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
PluginService.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * (c) Copyright 2013 CERN *
3 * *
4 * This software is distributed under the terms of the GNU General Public *
5 * Licence version 3 (GPL Version 3), copied verbatim in the file "LICENCE". *
6 * *
7 * In applying this licence, CERN does not waive the privileges and immunities *
8 * granted to it by virtue of its status as an Intergovernmental Organization *
9 * or submit itself to any jurisdiction. *
10 \*****************************************************************************/
11 
13 
14 #include <Gaudi/PluginService.h>
15 
16 #include <dlfcn.h>
17 #include <dirent.h>
18 
19 #include <cstdlib>
20 #include <iostream>
21 #include <fstream>
22 #include <memory>
23 
24 #include <cxxabi.h>
25 #include <sys/stat.h>
26 
27 #if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
28 #define REG_SCOPE_LOCK \
29  std::lock_guard<std::recursive_mutex> _guard(m_mutex);
30 
31 namespace {
32  std::mutex registrySingletonMutex;
33 }
34 #define SINGLETON_LOCK \
35  std::lock_guard<std::mutex> _guard(::registrySingletonMutex);
36 #else
37 #define REG_SCOPE_LOCK
38 #define SINGLETON_LOCK
39 #endif
40 
41 #include <algorithm>
42 
43 namespace {
44 // string trimming functions taken from
45 // http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
46 
47  constexpr struct is_space_t {
48  bool operator()(int i) const { return std::isspace(i); }
49  } is_space {};
50 
51  // trim from start
52  static inline std::string &ltrim(std::string &s) {
53  s.erase(s.begin(),
54  std::find_if_not(s.begin(), s.end(), is_space ) );
55  return s;
56  }
57 
58  // trim from end
59  static inline std::string &rtrim(std::string &s) {
60  s.erase(std::find_if_not(s.rbegin(), s.rend(), is_space).base(),
61  s.end());
62  return s;
63  }
64  // trim from both ends
65  static inline std::string &trim(std::string &s) {
66  return ltrim(rtrim(s));
67  }
68 }
69 
70 namespace {
74  inline void factoryInfoSetHelper(std::string& dest, const std::string value,
75  const std::string& desc,
76  const std::string& id) {
77  if (dest.empty()) {
78  dest = value;
79  } else if (dest != value) {
81  "new factory loaded for '" + id + "' with different "
82  + desc + ": " + dest + " != " + value );
83  }
84  }
85 
86  struct OldStyleCnv {
87  std::string name;
88  void operator() (const char c) {
89  switch(c) {
90  case '<':
91  case '>':
92  case ',':
93  case '(':
94  case ')':
95  case ':':
96  case '.':
97  name.push_back('_'); break;
98  case '&':
99  name.push_back('r'); break;
100  case '*':
101  name.push_back('p'); break;
102  case ' ': break;
103  default:
104  name.push_back(c); break;
105  }
106  }
107  };
109  std::string old_style_name(const std::string& name) {
110  return std::for_each(name.begin(), name.end(), OldStyleCnv()).name;
111  }
112 }
113 
114 namespace Gaudi { namespace PluginService {
115 
116  Exception::Exception(std::string msg): m_msg(std::move(msg)) {}
118  const char* Exception::what() const throw() {
119  return m_msg.c_str();
120  }
121 
122  namespace Details {
123  void* getCreator(const std::string& id, const std::string& type) {
124  return Registry::instance().get(id, type);
125  }
126 
127  std::string demangle(const std::string& id) {
128  int status;
129  auto realname = std::unique_ptr<char,decltype(free)*>( abi::__cxa_demangle(id.c_str(), nullptr, nullptr, &status),
130  free );
131  if (!realname) return id;
132  return std::string{realname.get()};
133  }
134  std::string demangle(const std::type_info& id) {
135  return demangle(id.name());
136  }
137 
140  static Registry r;
141  return r;
142  }
143 
144  Registry::Registry(): m_initialized(false) {}
145 
148  if (m_initialized) return;
149  m_initialized = true;
150 #ifdef WIN32
151  const char* envVar = "PATH";
152  const char sep = ';';
153 #else
154  const char* envVar = "LD_LIBRARY_PATH";
155  const char sep = ':';
156 #endif
157  char *search_path = ::getenv(envVar);
158  if (search_path) {
159  logger().debug(std::string("searching factories in ") + envVar);
160  std::string path(search_path);
161  std::string::size_type pos = 0;
162  std::string::size_type newpos = 0;
163  while (pos != std::string::npos) {
164  std::string dirName;
165  // get the next entry in the path
166  newpos = path.find(sep, pos);
167  if (newpos != std::string::npos) {
168  dirName = path.substr(pos, newpos - pos);
169  pos = newpos+1;
170  } else {
171  dirName = path.substr(pos);
172  pos = newpos;
173  }
174  logger().debug(std::string(" looking into ") + dirName);
175  // look for files called "*.components" in the directory
176  DIR *dir = opendir(dirName.c_str());
177  if (dir) {
178  struct dirent * entry;
179  while ((entry = readdir(dir))) {
180  std::string name(entry->d_name);
181  // check if the file name ends with ".components"
182  std::string::size_type extpos = name.find(".components");
183  if ((extpos != std::string::npos) &&
184  ((extpos+11) == name.size())) {
185  std::string fullPath = (dirName + '/' + name);
186  { // check if it is a regular file
187  struct stat buf;
188  stat(fullPath.c_str(), &buf);
189  if (!S_ISREG(buf.st_mode)) continue;
190  }
191  // read the file
192  logger().debug(std::string(" reading ") + name);
193  std::ifstream factories{fullPath};
194  std::string line;
195  int factoriesCount = 0;
196  int lineCount = 0;
197  while (!factories.eof()) {
198  ++lineCount;
199  std::getline(factories, line);
200  trim(line);
201  // skip empty lines and lines starting with '#'
202  if (line.empty() || line[0] == '#') continue;
203  // look for the separator
204  auto pos = line.find(':');
205  if (pos == std::string::npos) {
206  logger().warning( "failed to parse line "
207  + fullPath + ':'
208  + std::to_string(lineCount) );
209  continue;
210  }
211  const std::string lib(line, 0, pos);
212  const std::string fact(line, pos+1);
213  m_factories.emplace(fact, FactoryInfo(lib));
214 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
215  // add an alias for the factory using the Reflex convention
216  std::string old_name = old_style_name(fact);
217  if (fact != old_name) {
218  FactoryInfo old_info(lib);
219  old_info.properties["ReflexName"] = "true";
220  m_factories.emplace(old_name, old_info);
221  }
222 #endif
223  ++factoriesCount;
224  }
225  if (logger().level() <= Logger::Debug) {
226  logger().debug( " found " + std::to_string( factoriesCount )
227  + " factories" );
228  }
229  }
230  }
231  closedir(dir);
232  }
233  }
234  }
235  }
236 
238  Registry::add(const std::string& id, void *factory,
239  const std::string& type, const std::string& rtype,
240  const std::string& className,
241  const Properties& props){
243  FactoryMap &facts = factories();
244  auto entry = facts.find(id);
245  if (entry == facts.end())
246  {
247  // this factory was not known yet
248  entry = facts.emplace( id, FactoryInfo("unknown", factory,
249  type, rtype, className, props) ).first;
250  } else {
251  // do not replace an existing factory with a new one
252  if (!entry->second.ptr) entry->second.ptr = factory;
253  factoryInfoSetHelper(entry->second.type, type, "type", id);
254  factoryInfoSetHelper(entry->second.rtype, rtype, "return type", id);
255  factoryInfoSetHelper(entry->second.className, className, "class", id);
256  }
257 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
258  // add an alias for the factory using the Reflex convention
259  std::string old_name = old_style_name(id);
260  if (id != old_name)
261  add(old_name, factory, type, rtype, className, props)
262  .properties["ReflexName"] = "true";
263 #endif
264  return entry->second;
265  }
266 
267  void* Registry::get(const std::string& id, const std::string& type) const {
269  const FactoryMap &facts = factories();
270  auto f = facts.find(id);
271  if (f != facts.end())
272  {
273 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
274  const Properties& props = f->second.properties;
275  if (props.find("ReflexName") != props.end())
276  logger().warning("requesting factory via old name '" + id + "'"
277  "use '" + f->second.className + "' instead");
278 #endif
279  if (!f->second.ptr) {
280  if (!dlopen(f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL)) {
281  logger().warning("cannot load " + f->second.library +
282  " for factory " + id);
283  char *dlmsg = dlerror();
284  if (dlmsg) logger().warning(dlmsg);
285  return nullptr;
286  }
287  f = facts.find(id); // ensure that the iterator is valid
288  }
289  if (f->second.type == type) return f->second.ptr;
290  logger().warning("found factory " + id + ", but of wrong type: " +
291  demangle(f->second.type) + " instead of " + demangle(type));
292  }
293  return nullptr; // factory not found
294  }
295 
296  const Registry::FactoryInfo& Registry::getInfo(const std::string& id) const {
298  static const FactoryInfo unknown("unknown");
299  const FactoryMap &facts = factories();
300  auto f = facts.find(id);
301  return (f != facts.end()) ? f->second : unknown;
302  }
303 
304  Registry&
305  Registry::addProperty(const std::string& id,
306  const std::string& k,
307  const std::string& v) {
309  FactoryMap &facts = factories();
310  auto f = facts.find(id);
311  if (f != facts.end()) f->second.properties[k] = v;
312  return *this;
313  }
314 
315  std::set<Registry::KeyType> Registry::loadedFactories() const {
317  const FactoryMap &facts = factories();
318  std::set<KeyType> l;
319  for (const auto& f : facts )
320  {
321  if (f.second.ptr) l.insert(f.first);
322  }
323  return l;
324  }
325 
326  void Logger::report(Level lvl, const std::string& msg) {
327  static const char* levels[] = {"DEBUG : ",
328  "INFO : ",
329  "WARNING: ",
330  "ERROR : "};
331  if (lvl >= level()) {
332  std::cerr << levels[lvl] << msg << std::endl;
333  }
334  }
335 
336  static std::unique_ptr<Logger> s_logger(new Logger);
337  Logger& logger() { return *s_logger; }
338  void setLogger(Logger* logger) { s_logger.reset(logger); }
339 
340  } // namespace Details
341 
342  void SetDebug(int debugLevel) {
343  using namespace Details;
344  Logger& l = logger();
345  if (debugLevel > 1)
346  l.setLevel(Logger::Debug);
347  else if (debugLevel > 0)
348  l.setLevel(Logger::Info);
349  else l.setLevel(Logger::Warning);
350  }
351 
352  int Debug() {
353  using namespace Details;
354  switch (logger().level()) {
355  case Logger::Debug: return 2;
356  case Logger::Info: return 1;
357  default: return 0;
358  }
359  }
360 
361 }} // namespace Gaudi::PluginService
tuple c
Definition: gaudirun.py:391
string to_string(const T &value)
Definition: mergesort.cpp:40
const FactoryInfo & getInfo(const std::string &id) const
Retrieve the FactoryInfo object for an id.
Registry & addProperty(const std::string &id, const std::string &k, const std::string &v)
Add a property to an already existing FactoryInfo object (via its id.)
#define REG_SCOPE_LOCK
Registry()
Private constructor for the singleton pattern.
list path
Definition: __init__.py:15
STL namespace.
void initialize()
Initialize the registry loading the list of factories from the .component files in the library search...
GAUDIPS_API Logger & logger()
Return the current logger instance.
#define SINGLETON_LOCK
GAUDIPS_API void * getCreator(const std::string &id, const std::string &type)
Function used to load a specific factory function.
std::set< KeyType > loadedFactories() const
Return a list of all the known and loaded factories.
std::map< KeyType, std::string > Properties
Type used for the properties implementation.
Simple logging class, just to provide a default implementation.
GAUDIPS_API int Debug()
Backward compatibility with Reflex.
void * get(const std::string &id, const std::string &type) const
Retrieve the factory for the given id.
string dest
Definition: gaudirun.py:162
const FactoryMap & factories() const
Return the known factories (loading the list if not yet done).
GAUDIPS_API std::string demangle(const std::type_info &id)
Return a canonical name for type_info object (implementation borrowed from GaudiKernel/System).
virtual void report(Level lvl, const std::string &msg)
void warning(const std::string &msg)
dictionary l
Definition: gaudirun.py:421
GAUDIPS_API void SetDebug(int debugLevel)
Backward compatibility with Reflex.
GAUDIPS_API void setLogger(Logger *logger)
Set the logger instance to use.
static Registry & instance()
Retrieve the singleton instance of Registry.
string s
Definition: gaudirun.py:245
In-memory database of the loaded factories.
FactoryInfo & add(const I &id, typename F::FuncType ptr)
Add a factory to the database.
virtual const char * what() const
list i
Definition: ana.py:128
std::map< KeyType, FactoryInfo > FactoryMap
Type used for the database implementation.
FactoryMap m_factories
Internal storage for factories.
Helper functions to set/get the application return code.
Definition: __init__.py:1
bool m_initialized
Flag recording if the registry has been initialized or not.
string type
Definition: gaudirun.py:151