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 
125  static Registry r;
126  return r;
127  }
128 
129  Registry::Registry(): m_initialized(false) {}
130 
133  if (m_initialized) return;
134  m_initialized = true;
135 #ifdef WIN32
136  const char* envVar = "PATH";
137  const char sep = ';';
138 #else
139  const char* envVar = "LD_LIBRARY_PATH";
140  const char sep = ':';
141 #endif
142  char *search_path = ::getenv(envVar);
143  if (search_path) {
144  logger().debug(std::string("searching factories in ") + envVar);
145  std::string path(search_path);
146  std::string::size_type pos = 0;
147  std::string::size_type newpos = 0;
148  while (pos != std::string::npos) {
149  std::string dirName;
150  // get the next entry in the path
151  newpos = path.find(sep, pos);
152  if (newpos != std::string::npos) {
153  dirName = path.substr(pos, newpos - pos);
154  pos = newpos+1;
155  } else {
156  dirName = path.substr(pos);
157  pos = newpos;
158  }
159  logger().debug(std::string(" looking into ") + dirName);
160  // look for files called "*.components" in the directory
161  DIR *dir = opendir(dirName.c_str());
162  if (dir) {
163  struct dirent * entry;
164  while ((entry = readdir(dir))) {
165  std::string name(entry->d_name);
166  // check if the file name ends with ".components"
167  std::string::size_type extpos = name.find(".components");
168  if ((extpos != std::string::npos) &&
169  ((extpos+11) == name.size())) {
170  std::string fullPath = (dirName + '/' + name);
171  { // check if it is a regular file
172  struct stat buf;
173  stat(fullPath.c_str(), &buf);
174  if (!S_ISREG(buf.st_mode)) continue;
175  }
176  // read the file
177  logger().debug(std::string(" reading ") + name);
178  std::ifstream factories(fullPath.c_str());
179  std::string line;
180  int factoriesCount = 0;
181  int lineCount = 0;
182  while (!factories.eof()) {
183  ++lineCount;
184  std::getline(factories, line);
185  trim(line);
186  // skip empty lines and lines starting with '#'
187  if (line.empty() || line[0] == '#') continue;
188  // look for the separator
189  std::string::size_type pos = line.find(':');
190  if (pos == std::string::npos) {
191  std::ostringstream o;
192  o << "failed to parse line " << fullPath
193  << ':' << lineCount;
194  logger().warning(o.str());
195  continue;
196  }
197  const std::string lib(line, 0, pos);
198  const std::string fact(line, pos+1);
199  m_factories.insert(std::make_pair(fact, FactoryInfo(lib)));
200 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
201  // add an alias for the factory using the Reflex convention
202  std::string old_name = old_style_name(fact);
203  if (fact != old_name) {
204  FactoryInfo old_info(lib);
205  old_info.properties["ReflexName"] = "true";
206  m_factories.insert(std::make_pair(old_name, old_info));
207  }
208 #endif
209  ++factoriesCount;
210  }
211  if (logger().level() <= Logger::Debug) {
212  std::ostringstream o;
213  o << " found " << factoriesCount << " factories";
214  logger().debug(o.str());
215  }
216  }
217  }
218  closedir(dir);
219  }
220  }
221  }
222  }
223 
225  Registry::add(const std::string& id, void *factory,
226  const std::string& type, const std::string& rtype,
227  const std::string& className,
228  const Properties& props){
230  FactoryMap &facts = factories();
231  FactoryMap::iterator entry = facts.find(id);
232  if (entry == facts.end())
233  {
234  // this factory was not known yet
235  entry = facts.insert(std::make_pair(id,
236  FactoryInfo("unknown", factory,
237  type, rtype, className, props))).first;
238  } else {
239  // do not replace an existing factory with a new one
240  if (!entry->second.ptr) {
241  entry->second.ptr = factory;
242  }
243  factoryInfoSetHelper(entry->second.type, type, "type", id);
244  factoryInfoSetHelper(entry->second.rtype, rtype, "return type", id);
245  factoryInfoSetHelper(entry->second.className, className, "class", id);
246  }
247 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
248  // add an alias for the factory using the Reflex convention
249  std::string old_name = old_style_name(id);
250  if (id != old_name)
251  add(old_name, factory, type, rtype, className, props)
252  .properties["ReflexName"] = "true";
253 #endif
254  return entry->second;
255  }
256 
257  void* Registry::get(const std::string& id, const std::string& type) const {
259  const FactoryMap &facts = factories();
260  FactoryMap::const_iterator f = facts.find(id);
261  if (f != facts.end())
262  {
263 #ifdef GAUDI_REFLEX_COMPONENT_ALIASES
264  const Properties& props = f->second.properties;
265  if (props.find("ReflexName") != props.end())
266  logger().warning("requesting factory via old name '" + id + "'"
267  "use '" + f->second.className + "' instead");
268 #endif
269  if (!f->second.ptr) {
270  if (!dlopen(f->second.library.c_str(), RTLD_LAZY | RTLD_GLOBAL)) {
271  logger().warning("cannot load " + f->second.library +
272  " for factory " + id);
273  char *dlmsg = dlerror();
274  if (dlmsg)
275  logger().warning(dlmsg);
276  return 0;
277  }
278  f = facts.find(id); // ensure that the iterator is valid
279  }
280  if (f->second.type == type) {
281  return f->second.ptr;
282  } else {
283  logger().warning("found factory " + id + ", but of wrong type: " +
284  f->second.type + " instead of " + type);
285  }
286  }
287  return 0; // factory not found
288  }
289 
290  const Registry::FactoryInfo& Registry::getInfo(const std::string& id) const {
292  static FactoryInfo unknown("unknown");
293  const FactoryMap &facts = factories();
294  FactoryMap::const_iterator f = facts.find(id);
295  if (f != facts.end())
296  {
297  return f->second;
298  }
299  return unknown; // factory not found
300  }
301 
302  Registry&
303  Registry::addProperty(const std::string& id,
304  const std::string& k,
305  const std::string& v) {
307  FactoryMap &facts = factories();
308  FactoryMap::iterator f = facts.find(id);
309  if (f != facts.end())
310  {
311  f->second.properties[k] = v;
312  }
313  return *this;
314  }
315 
316  std::set<Registry::KeyType> Registry::loadedFactories() const {
318  const FactoryMap &facts = factories();
319  std::set<KeyType> l;
320  for (FactoryMap::const_iterator f = facts.begin();
321  f != facts.end(); ++f)
322  {
323  if (f->second.ptr)
324  l.insert(f->first);
325  }
326  return l;
327  }
328 
329  std::string demangle(const std::type_info& id) {
330  int status;
331  char* realname;
332  realname = abi::__cxa_demangle(id.name(), 0, 0, &status);
333  if (realname == 0) return id.name();
334  std::string result(realname);
335  free(realname);
336  return result;
337  }
338 
339  void Logger::report(Level lvl, const std::string& msg) {
340  static const char* levels[] = {"DEBUG : ",
341  "INFO : ",
342  "WARNING: ",
343  "ERROR : "};
344  if (lvl >= level()) {
345  std::cerr << levels[lvl] << msg << std::endl;
346  }
347  }
348 
349  static std::auto_ptr<Logger> s_logger(new Logger);
351  return *s_logger;
352  }
354  s_logger.reset(logger);
355  }
356 
357  } // namespace Details
358 
359  void SetDebug(int debugLevel) {
360  using namespace Details;
361  Logger& l = logger();
362  if (debugLevel > 1)
363  l.setLevel(Logger::Debug);
364  else if (debugLevel > 0)
365  l.setLevel(Logger::Info);
366  else l.setLevel(Logger::Warning);
367  }
368 
369  int Debug() {
370  using namespace Details;
371  switch (logger().level()) {
372  case Logger::Debug: return 2; break;
373  case Logger::Info: return 1; break;
374  default: return 0;
375  }
376  }
377 
378 }} // 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:15
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