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 #include "boost/algorithm/string/trim.hpp"
43 
44 
45 namespace {
49  inline void factoryInfoSetHelper(std::string& dest, const std::string value,
50  const std::string& desc,
51  const std::string& id) {
52  if (dest.empty()) {
53  dest = value;
54  } else if (dest != value) {
56  "new factory loaded for '" + id + "' with different "
57  + desc + ": " + dest + " != " + value );
58  }
59  }
60 
61  struct OldStyleCnv {
62  std::string name;
63  void operator() (const char c) {
64  switch(c) {
65  case '<':
66  case '>':
67  case ',':
68  case '(':
69  case ')':
70  case ':':
71  case '.':
72  name.push_back('_'); break;
73  case '&':
74  name.push_back('r'); break;
75  case '*':
76  name.push_back('p'); break;
77  case ' ': break;
78  default:
79  name.push_back(c); break;
80  }
81  }
82  };
84  std::string old_style_name(const std::string& name) {
85  return std::for_each(name.begin(), name.end(), OldStyleCnv()).name;
86  }
87 }
88 
89 namespace Gaudi { namespace PluginService {
90 
91  Exception::Exception(std::string msg): m_msg(std::move(msg)) {}
92  Exception::~Exception() throw() {}
93  const char* Exception::what() const throw() {
94  return m_msg.c_str();
95  }
96 
97  namespace Details {
98  void* getCreator(const std::string& id, const std::string& type) {
99  return Registry::instance().get(id, type);
100  }
101 
102  std::string demangle(const std::string& id) {
103  int status;
104  auto realname = std::unique_ptr<char,decltype(free)*>( abi::__cxa_demangle(id.c_str(), 0, 0, &status),
105  free );
106  if (!realname) return id;
107  return std::string{realname.get()};
108  }
109  std::string demangle(const std::type_info& id) {
110  return demangle(id.name());
111  }
112 
115  static Registry r;
116  return r;
117  }
118 
119  Registry::Registry(): m_initialized(false) {}
120 
123  if (m_initialized) return;
124  m_initialized = true;
125 #ifdef WIN32
126  const char* envVar = "PATH";
127  const char sep = ';';
128 #else
129  const char* envVar = "LD_LIBRARY_PATH";
130  const char sep = ':';
131 #endif
132  char *search_path = ::getenv(envVar);
133  if (search_path) {
134  logger().debug(std::string("searching factories in ") + envVar);
135  std::string path(search_path);
136  std::string::size_type pos = 0;
137  std::string::size_type newpos = 0;
138  while (pos != std::string::npos) {
139  std::string dirName;
140  // get the next entry in the path
141  newpos = path.find(sep, pos);
142  if (newpos != std::string::npos) {
143  dirName = path.substr(pos, newpos - pos);
144  pos = newpos+1;
145  } else {
146  dirName = path.substr(pos);
147  pos = newpos;
148  }
149  logger().debug(std::string(" looking into ") + dirName);
150  // look for files called "*.components" in the directory
151  DIR *dir = opendir(dirName.c_str());
152  if (dir) {
153  struct dirent * entry;
154  while ((entry = readdir(dir))) {
155  std::string name(entry->d_name);
156  // check if the file name ends with ".components"
157  std::string::size_type extpos = name.find(".components");
158  if ((extpos != std::string::npos) &&
159  ((extpos+11) == name.size())) {
160  std::string fullPath = (dirName + '/' + name);
161  { // check if it is a regular file
162  struct stat buf;
163  stat(fullPath.c_str(), &buf);
164  if (!S_ISREG(buf.st_mode)) continue;
165  }
166  // read the file
167  logger().debug(std::string(" reading ") + name);
168  std::ifstream factories{fullPath};
169  std::string line;
170  int factoriesCount = 0;
171  int lineCount = 0;
172  while (!factories.eof()) {
173  ++lineCount;
174  std::getline(factories, line);
175  boost::algorithm::trim(line);
176  // skip empty lines and lines starting with '#'
177  if (line.empty() || line[0] == '#') continue;
178  // look for the separator
179  auto pos = line.find(':');
180  if (pos == std::string::npos) {
181  logger().warning( "failed to parse line "
182  + fullPath + ':'
183  + std::to_string(lineCount) );
184  continue;
185  }
186  const std::string lib(line, 0, pos);
187  const std::string fact(line, pos+1);
188  m_factories.emplace(fact, FactoryInfo(lib));
189 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
190  // add an alias for the factory using the Reflex convention
191  std::string old_name = old_style_name(fact);
192  if (fact != old_name) {
193  FactoryInfo old_info(lib);
194  old_info.properties["ReflexName"] = "true";
195  m_factories.emplace(old_name, old_info);
196  }
197 #endif
198  ++factoriesCount;
199  }
200  if (logger().level() <= Logger::Debug) {
201  logger().debug( " found " + std::to_string( factoriesCount )
202  + " factories" );
203  }
204  }
205  }
206  closedir(dir);
207  }
208  }
209  }
210  }
211 
213  Registry::add(const std::string& id, void *factory,
214  const std::string& type, const std::string& rtype,
215  const std::string& className,
216  const Properties& props){
218  FactoryMap &facts = factories();
219  auto entry = facts.find(id);
220  if (entry == facts.end())
221  {
222  // this factory was not known yet
223  entry = facts.emplace( id, FactoryInfo("unknown", factory,
224  type, rtype, className, props) ).first;
225  } else {
226  // do not replace an existing factory with a new one
227  if (!entry->second.ptr) entry->second.ptr = factory;
228  factoryInfoSetHelper(entry->second.type, type, "type", id);
229  factoryInfoSetHelper(entry->second.rtype, rtype, "return type", id);
230  factoryInfoSetHelper(entry->second.className, className, "class", id);
231  }
232 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
233  // add an alias for the factory using the Reflex convention
234  std::string old_name = old_style_name(id);
235  if (id != old_name)
236  add(old_name, factory, type, rtype, className, props)
237  .properties["ReflexName"] = "true";
238 #endif
239  return entry->second;
240  }
241 
242  void* Registry::get(const std::string& id, const std::string& type) const {
244  const FactoryMap &facts = factories();
245  auto f = facts.find(id);
246  if (f != facts.end())
247  {
248 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
249  const Properties& props = f->second.properties;
250  if (props.find("ReflexName") != props.end())
251  logger().warning("requesting factory via old name '" + id + "'"
252  "use '" + f->second.className + "' instead");
253 #endif
254  if (!f->second.ptr) {
255  if (!dlopen(f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL)) {
256  logger().warning("cannot load " + f->second.library +
257  " for factory " + id);
258  char *dlmsg = dlerror();
259  if (dlmsg) logger().warning(dlmsg);
260  return nullptr;
261  }
262  f = facts.find(id); // ensure that the iterator is valid
263  }
264  if (f->second.type == type) return f->second.ptr;
265  logger().warning("found factory " + id + ", but of wrong type: " +
266  demangle(f->second.type) + " instead of " + demangle(type));
267  }
268  return nullptr; // factory not found
269  }
270 
271  const Registry::FactoryInfo& Registry::getInfo(const std::string& id) const {
273  static const FactoryInfo unknown("unknown");
274  const FactoryMap &facts = factories();
275  auto f = facts.find(id);
276  return (f != facts.end()) ? f->second : unknown;
277  }
278 
279  Registry&
280  Registry::addProperty(const std::string& id,
281  const std::string& k,
282  const std::string& v) {
284  FactoryMap &facts = factories();
285  auto f = facts.find(id);
286  if (f != facts.end()) f->second.properties[k] = v;
287  return *this;
288  }
289 
290  std::set<Registry::KeyType> Registry::loadedFactories() const {
292  const FactoryMap &facts = factories();
293  std::set<KeyType> l;
294  for (const auto& f : facts )
295  {
296  if (f.second.ptr) l.insert(f.first);
297  }
298  return l;
299  }
300 
301  void Logger::report(Level lvl, const std::string& msg) {
302  static const char* levels[] = {"DEBUG : ",
303  "INFO : ",
304  "WARNING: ",
305  "ERROR : "};
306  if (lvl >= level()) {
307  std::cerr << levels[lvl] << msg << std::endl;
308  }
309  }
310 
311  static std::unique_ptr<Logger> s_logger(new Logger);
312  Logger& logger() { return *s_logger; }
313  void setLogger(Logger* logger) { s_logger.reset(logger); }
314 
315  } // namespace Details
316 
317  void SetDebug(int debugLevel) {
318  using namespace Details;
319  Logger& l = logger();
320  if (debugLevel > 1)
321  l.setLevel(Logger::Debug);
322  else if (debugLevel > 0)
323  l.setLevel(Logger::Info);
324  else l.setLevel(Logger::Warning);
325  }
326 
327  int Debug() {
328  using namespace Details;
329  switch (logger().level()) {
330  case Logger::Debug: return 2;
331  case Logger::Info: return 1;
332  default: return 0;
333  }
334  }
335 
336 }} // namespace Gaudi::PluginService
tuple c
Definition: gaudirun.py:392
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:422
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.
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
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.
int line
Definition: ana.py:50
string type
Definition: gaudirun.py:151