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