All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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 // string trimming functions taken from
42 // http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
43 #include <algorithm>
44 // trim from start
45 static inline std::string &ltrim(std::string &s) {
46  s.erase(s.begin(),
47  std::find_if(s.begin(), s.end(),
48  std::not1(std::ptr_fun<int, int>(std::isspace))));
49  return s;
50 }
51 
52 // trim from end
53 static inline std::string &rtrim(std::string &s) {
54  s.erase(std::find_if(s.rbegin(), s.rend(),
55  std::not1(std::ptr_fun<int, int>(std::isspace)))
56  .base(),
57  s.end());
58  return s;
59 }
60 // trim from both ends
61 static inline std::string &trim(std::string &s) {
62  return ltrim(rtrim(s));
63 }
64 
65 namespace {
69  inline void factoryInfoSetHelper(std::string& dest, const std::string value,
70  const std::string& desc,
71  const std::string& id) {
72  if (dest.empty()) {
73  dest = value;
74  } else if (dest != value) {
75  std::ostringstream o;
76  o << "new factory loaded for '" << id << "' with different "
77  << desc << ": " << dest << " != " << value;
79  }
80  }
81 
82  struct OldStyleCnv {
83  std::string name;
84  void operator() (const char c) {
85  switch(c) {
86  case '<':
87  case '>':
88  case ',':
89  case '(':
90  case ')':
91  case ':':
92  case '.':
93  name.push_back('_'); break;
94  case '&':
95  name.push_back('r'); break;
96  case '*':
97  name.push_back('p'); break;
98  case ' ': break;
99  default:
100  name.push_back(c); break;
101  }
102  }
103  };
105  std::string old_style_name(const std::string& name) {
106  return std::for_each(name.begin(), name.end(), OldStyleCnv()).name;
107  }
108 }
109 
110 namespace Gaudi { namespace PluginService {
111 
112  Exception::Exception(const std::string& msg): m_msg(msg) {}
114  const char* Exception::what() const throw() {
115  return m_msg.c_str();
116  }
117 
118  namespace Details {
119  void* getCreator(const std::string& id, const std::string& type) {
120  return Registry::instance().get(id, type);
121  }
122 
123  std::string demangle(const std::string& id) {
124  int status;
125  char* realname;
126  realname = abi::__cxa_demangle(id.c_str(), 0, 0, &status);
127  if (realname == 0) return id;
128  std::string result(realname);
129  free(realname);
130  return result;
131  }
132  std::string demangle(const std::type_info& id) {
133  return demangle(id.name());
134  }
135 
138  static Registry r;
139  return r;
140  }
141 
142  Registry::Registry(): m_initialized(false) {}
143 
146  if (m_initialized) return;
147  m_initialized = true;
148 #ifdef WIN32
149  const char* envVar = "PATH";
150  const char sep = ';';
151 #else
152  const char* envVar = "LD_LIBRARY_PATH";
153  const char sep = ':';
154 #endif
155  char *search_path = ::getenv(envVar);
156  if (search_path) {
157  logger().debug(std::string("searching factories in ") + envVar);
158  std::string path(search_path);
159  std::string::size_type pos = 0;
160  std::string::size_type newpos = 0;
161  while (pos != std::string::npos) {
162  std::string dirName;
163  // get the next entry in the path
164  newpos = path.find(sep, pos);
165  if (newpos != std::string::npos) {
166  dirName = path.substr(pos, newpos - pos);
167  pos = newpos+1;
168  } else {
169  dirName = path.substr(pos);
170  pos = newpos;
171  }
172  logger().debug(std::string(" looking into ") + dirName);
173  // look for files called "*.components" in the directory
174  DIR *dir = opendir(dirName.c_str());
175  if (dir) {
176  struct dirent * entry;
177  while ((entry = readdir(dir))) {
178  std::string name(entry->d_name);
179  // check if the file name ends with ".components"
180  std::string::size_type extpos = name.find(".components");
181  if ((extpos != std::string::npos) &&
182  ((extpos+11) == name.size())) {
183  std::string fullPath = (dirName + '/' + name);
184  { // check if it is a regular file
185  struct stat buf;
186  stat(fullPath.c_str(), &buf);
187  if (!S_ISREG(buf.st_mode)) continue;
188  }
189  // read the file
190  logger().debug(std::string(" reading ") + name);
191  std::ifstream factories(fullPath.c_str());
192  std::string line;
193  int factoriesCount = 0;
194  int lineCount = 0;
195  while (!factories.eof()) {
196  ++lineCount;
197  std::getline(factories, line);
198  trim(line);
199  // skip empty lines and lines starting with '#'
200  if (line.empty() || line[0] == '#') continue;
201  // look for the separator
202  std::string::size_type pos = line.find(':');
203  if (pos == std::string::npos) {
204  std::ostringstream o;
205  o << "failed to parse line " << fullPath
206  << ':' << lineCount;
207  logger().warning(o.str());
208  continue;
209  }
210  const std::string lib(line, 0, pos);
211  const std::string fact(line, pos+1);
212  m_factories.insert(std::make_pair(fact, FactoryInfo(lib)));
213 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
214  // add an alias for the factory using the Reflex convention
215  std::string old_name = old_style_name(fact);
216  if (fact != old_name) {
217  FactoryInfo old_info(lib);
218  old_info.properties["ReflexName"] = "true";
219  m_factories.insert(std::make_pair(old_name, old_info));
220  }
221 #endif
222  ++factoriesCount;
223  }
224  if (logger().level() <= Logger::Debug) {
225  std::ostringstream o;
226  o << " found " << factoriesCount << " factories";
227  logger().debug(o.str());
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  FactoryMap::iterator entry = facts.find(id);
245  if (entry == facts.end())
246  {
247  // this factory was not known yet
248  entry = facts.insert(std::make_pair(id,
249  FactoryInfo("unknown", factory,
250  type, rtype, className, props))).first;
251  } else {
252  // do not replace an existing factory with a new one
253  if (!entry->second.ptr) {
254  entry->second.ptr = factory;
255  }
256  factoryInfoSetHelper(entry->second.type, type, "type", id);
257  factoryInfoSetHelper(entry->second.rtype, rtype, "return type", id);
258  factoryInfoSetHelper(entry->second.className, className, "class", id);
259  }
260 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
261  // add an alias for the factory using the Reflex convention
262  std::string old_name = old_style_name(id);
263  if (id != old_name)
264  add(old_name, factory, type, rtype, className, props)
265  .properties["ReflexName"] = "true";
266 #endif
267  return entry->second;
268  }
269 
270  void* Registry::get(const std::string& id, const std::string& type) const {
272  const FactoryMap &facts = factories();
273  FactoryMap::const_iterator f = facts.find(id);
274  if (f != facts.end())
275  {
276 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
277  const Properties& props = f->second.properties;
278  if (props.find("ReflexName") != props.end())
279  logger().warning("requesting factory via old name '" + id + "'"
280  "use '" + f->second.className + "' instead");
281 #endif
282  if (!f->second.ptr) {
283  if (!dlopen(f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL)) {
284  logger().warning("cannot load " + f->second.library +
285  " for factory " + id);
286  char *dlmsg = dlerror();
287  if (dlmsg)
288  logger().warning(dlmsg);
289  return 0;
290  }
291  f = facts.find(id); // ensure that the iterator is valid
292  }
293  if (f->second.type == type) {
294  return f->second.ptr;
295  } else {
296  logger().warning("found factory " + id + ", but of wrong type: " +
297  demangle(f->second.type) + " instead of " + demangle(type));
298  }
299  }
300  return 0; // factory not found
301  }
302 
303  const Registry::FactoryInfo& Registry::getInfo(const std::string& id) const {
305  static FactoryInfo unknown("unknown");
306  const FactoryMap &facts = factories();
307  FactoryMap::const_iterator f = facts.find(id);
308  if (f != facts.end())
309  {
310  return f->second;
311  }
312  return unknown; // factory not found
313  }
314 
315  Registry&
316  Registry::addProperty(const std::string& id,
317  const std::string& k,
318  const std::string& v) {
320  FactoryMap &facts = factories();
321  FactoryMap::iterator f = facts.find(id);
322  if (f != facts.end())
323  {
324  f->second.properties[k] = v;
325  }
326  return *this;
327  }
328 
329  std::set<Registry::KeyType> Registry::loadedFactories() const {
331  const FactoryMap &facts = factories();
332  std::set<KeyType> l;
333  for (FactoryMap::const_iterator f = facts.begin();
334  f != facts.end(); ++f)
335  {
336  if (f->second.ptr)
337  l.insert(f->first);
338  }
339  return l;
340  }
341 
342  void Logger::report(Level lvl, const std::string& msg) {
343  static const char* levels[] = {"DEBUG : ",
344  "INFO : ",
345  "WARNING: ",
346  "ERROR : "};
347  if (lvl >= level()) {
348  std::cerr << levels[lvl] << msg << std::endl;
349  }
350  }
351 
352  static std::auto_ptr<Logger> s_logger(new Logger);
354  return *s_logger;
355  }
357  s_logger.reset(logger);
358  }
359 
360  } // namespace Details
361 
362  void SetDebug(int debugLevel) {
363  using namespace Details;
364  Logger& l = logger();
365  if (debugLevel > 1)
366  l.setLevel(Logger::Debug);
367  else if (debugLevel > 0)
368  l.setLevel(Logger::Info);
369  else l.setLevel(Logger::Warning);
370  }
371 
372  int Debug() {
373  using namespace Details;
374  switch (logger().level()) {
375  case Logger::Debug: return 2; break;
376  case Logger::Info: return 1; break;
377  default: return 0;
378  }
379  }
380 
381 }} // namespace Gaudi::PluginService
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
tuple c
Definition: gaudirun.py:341
Registry()
Private constructor for the singleton pattern.
void initialize()
Initialize the registry loading the list of factories from the .component files in the library search...
#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.
std::map< KeyType, std::string > Properties
Type used for the properties implementation.
Simple logging class, just to provide a default implementation.
GAUDIPS_API void SetDebug(int debugLevel)
Backward compatibility with Reflex.
string type
Definition: gaudirun.py:126
void * get(const std::string &id, const std::string &type) const
Retrieve the factory for the given id.
GAUDIPS_API std::string demangle(const std::type_info &id)
Return a canonical name for type_info object (implementation borrowed from GaudiKernel/System).
string dest
Definition: gaudirun.py:137
const FactoryMap & factories() const
Return the known factories (loading the list if not yet done).
virtual void report(Level lvl, const std::string &msg)
void warning(const std::string &msg)
GAUDI_API std::string path(const AIDA::IBaseHistogram *aida)
get the path in THS for AIDA histogram
dictionary l
Definition: gaudirun.py:365
GAUDIPS_API void * getCreator(const std::string &id, const std::string &type)
Function used to load a specific factory function.
static Registry & instance()
Retrieve the singleton instance of Registry.
string s
Definition: gaudirun.py:210
In-memory database of the loaded factories.
virtual const char * what() const
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.
This is a number of static methods for bootstrapping the Gaudi framework.
Definition: Bootstrap.h:14
Exception(const std::string &msg)
std::map< KeyType, FactoryInfo > FactoryMap
Type used for the database implementation.
FactoryMap m_factories
Internal storage for factories.
bool m_initialized
Flag recording if the registry has been initialized or not.
int line
Definition: ana.py:50