| 1 | // |
| 2 | // ClassLoader.h |
| 3 | // |
| 4 | // Library: Foundation |
| 5 | // Package: SharedLibrary |
| 6 | // Module: ClassLoader |
| 7 | // |
| 8 | // Definition of the ClassLoader class. |
| 9 | // |
| 10 | // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. |
| 11 | // and Contributors. |
| 12 | // |
| 13 | // SPDX-License-Identifier: BSL-1.0 |
| 14 | // |
| 15 | |
| 16 | |
| 17 | #ifndef Foundation_ClassLoader_INCLUDED |
| 18 | #define Foundation_ClassLoader_INCLUDED |
| 19 | |
| 20 | |
| 21 | #include "Poco/Foundation.h" |
| 22 | #include "Poco/MetaObject.h" |
| 23 | #include "Poco/Manifest.h" |
| 24 | #include "Poco/SharedLibrary.h" |
| 25 | #include "Poco/Mutex.h" |
| 26 | #include "Poco/Exception.h" |
| 27 | #include <map> |
| 28 | |
| 29 | |
| 30 | namespace Poco { |
| 31 | |
| 32 | |
| 33 | template <class Base> |
| 34 | class ClassLoader |
| 35 | /// The ClassLoader loads C++ classes from shared libraries |
| 36 | /// at runtime. It must be instantiated with a root class |
| 37 | /// of the loadable classes. |
| 38 | /// For a class to be loadable from a library, the library |
| 39 | /// must provide a Manifest of all the classes it contains. |
| 40 | /// The Manifest for a shared library can be easily built |
| 41 | /// with the help of the macros in the header file |
| 42 | /// "Foundation/ClassLibrary.h". |
| 43 | /// |
| 44 | /// Starting with POCO release 1.3, a class library can |
| 45 | /// export multiple manifests. In addition to the default |
| 46 | /// (unnamed) manifest, multiple named manifests can |
| 47 | /// be exported, each having a different base class. |
| 48 | /// |
| 49 | /// There is one important restriction: one instance of |
| 50 | /// ClassLoader can only load one manifest from a class |
| 51 | /// library. |
| 52 | { |
| 53 | public: |
| 54 | typedef AbstractMetaObject<Base> Meta; |
| 55 | typedef Manifest<Base> Manif; |
| 56 | typedef void (*InitializeLibraryFunc)(); |
| 57 | typedef void (*UninitializeLibraryFunc)(); |
| 58 | typedef bool (*BuildManifestFunc)(ManifestBase*); |
| 59 | |
| 60 | struct LibraryInfo |
| 61 | { |
| 62 | SharedLibrary* pLibrary; |
| 63 | const Manif* pManifest; |
| 64 | int refCount; |
| 65 | }; |
| 66 | typedef std::map<std::string, LibraryInfo> LibraryMap; |
| 67 | |
| 68 | class Iterator |
| 69 | /// The ClassLoader's very own iterator class. |
| 70 | { |
| 71 | public: |
| 72 | typedef std::pair<std::string, const Manif*> Pair; |
| 73 | |
| 74 | Iterator(const typename LibraryMap::const_iterator& it) |
| 75 | { |
| 76 | _it = it; |
| 77 | } |
| 78 | Iterator(const Iterator& it) |
| 79 | { |
| 80 | _it = it._it; |
| 81 | } |
| 82 | ~Iterator() |
| 83 | { |
| 84 | } |
| 85 | Iterator& operator = (const Iterator& it) |
| 86 | { |
| 87 | _it = it._it; |
| 88 | return *this; |
| 89 | } |
| 90 | inline bool operator == (const Iterator& it) const |
| 91 | { |
| 92 | return _it == it._it; |
| 93 | } |
| 94 | inline bool operator != (const Iterator& it) const |
| 95 | { |
| 96 | return _it != it._it; |
| 97 | } |
| 98 | Iterator& operator ++ () // prefix |
| 99 | { |
| 100 | ++_it; |
| 101 | return *this; |
| 102 | } |
| 103 | Iterator operator ++ (int) // postfix |
| 104 | { |
| 105 | Iterator result(_it); |
| 106 | ++_it; |
| 107 | return result; |
| 108 | } |
| 109 | inline const Pair* operator * () const |
| 110 | { |
| 111 | _pair.first = _it->first; |
| 112 | _pair.second = _it->second.pManifest; |
| 113 | return &_pair; |
| 114 | } |
| 115 | inline const Pair* operator -> () const |
| 116 | { |
| 117 | _pair.first = _it->first; |
| 118 | _pair.second = _it->second.pManifest; |
| 119 | return &_pair; |
| 120 | } |
| 121 | |
| 122 | private: |
| 123 | typename LibraryMap::const_iterator _it; |
| 124 | mutable Pair _pair; |
| 125 | }; |
| 126 | |
| 127 | ClassLoader() |
| 128 | /// Creates the ClassLoader. |
| 129 | { |
| 130 | } |
| 131 | |
| 132 | virtual ~ClassLoader() |
| 133 | /// Destroys the ClassLoader. |
| 134 | { |
| 135 | for (typename LibraryMap::const_iterator it = _map.begin(); it != _map.end(); ++it) |
| 136 | { |
| 137 | delete it->second.pLibrary; |
| 138 | delete it->second.pManifest; |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | void loadLibrary(const std::string& path, const std::string& manifest) |
| 143 | /// Loads a library from the given path, using the given manifest. |
| 144 | /// Does nothing if the library is already loaded. |
| 145 | /// Throws a LibraryLoadException if the library |
| 146 | /// cannot be loaded or does not have a Manifest. |
| 147 | /// If the library exports a function named "pocoInitializeLibrary", |
| 148 | /// this function is executed. |
| 149 | /// If called multiple times for the same library, |
| 150 | /// the number of calls to unloadLibrary() must be the same |
| 151 | /// for the library to become unloaded. |
| 152 | { |
| 153 | FastMutex::ScopedLock lock(_mutex); |
| 154 | |
| 155 | typename LibraryMap::iterator it = _map.find(path); |
| 156 | if (it == _map.end()) |
| 157 | { |
| 158 | LibraryInfo li; |
| 159 | li.pLibrary = 0; |
| 160 | li.pManifest = 0; |
| 161 | li.refCount = 1; |
| 162 | try |
| 163 | { |
| 164 | li.pLibrary = new SharedLibrary(path); |
| 165 | li.pManifest = new Manif(); |
| 166 | std::string pocoBuildManifestSymbol("pocoBuildManifest" ); |
| 167 | pocoBuildManifestSymbol.append(manifest); |
| 168 | if (li.pLibrary->hasSymbol("pocoInitializeLibrary" )) |
| 169 | { |
| 170 | InitializeLibraryFunc initializeLibrary = (InitializeLibraryFunc) li.pLibrary->getSymbol("pocoInitializeLibrary" ); |
| 171 | initializeLibrary(); |
| 172 | } |
| 173 | if (li.pLibrary->hasSymbol(pocoBuildManifestSymbol)) |
| 174 | { |
| 175 | BuildManifestFunc buildManifest = (BuildManifestFunc) li.pLibrary->getSymbol(pocoBuildManifestSymbol); |
| 176 | if (buildManifest(const_cast<Manif*>(li.pManifest))) |
| 177 | _map[path] = li; |
| 178 | else |
| 179 | throw LibraryLoadException(std::string("Manifest class mismatch in " ) + path, manifest); |
| 180 | } |
| 181 | else throw LibraryLoadException(std::string("No manifest in " ) + path, manifest); |
| 182 | } |
| 183 | catch (...) |
| 184 | { |
| 185 | delete li.pLibrary; |
| 186 | delete li.pManifest; |
| 187 | throw; |
| 188 | } |
| 189 | } |
| 190 | else |
| 191 | { |
| 192 | ++it->second.refCount; |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | void loadLibrary(const std::string& path) |
| 197 | /// Loads a library from the given path. Does nothing |
| 198 | /// if the library is already loaded. |
| 199 | /// Throws a LibraryLoadException if the library |
| 200 | /// cannot be loaded or does not have a Manifest. |
| 201 | /// If the library exports a function named "pocoInitializeLibrary", |
| 202 | /// this function is executed. |
| 203 | /// If called multiple times for the same library, |
| 204 | /// the number of calls to unloadLibrary() must be the same |
| 205 | /// for the library to become unloaded. |
| 206 | /// |
| 207 | /// Equivalent to loadLibrary(path, ""). |
| 208 | { |
| 209 | loadLibrary(path, "" ); |
| 210 | } |
| 211 | |
| 212 | void unloadLibrary(const std::string& path) |
| 213 | /// Unloads the given library. |
| 214 | /// Be extremely cautious when unloading shared libraries. |
| 215 | /// If objects from the library are still referenced somewhere, |
| 216 | /// a total crash is very likely. |
| 217 | /// If the library exports a function named "pocoUninitializeLibrary", |
| 218 | /// this function is executed before it is unloaded. |
| 219 | /// If loadLibrary() has been called multiple times for the same |
| 220 | /// library, the number of calls to unloadLibrary() must be the same |
| 221 | /// for the library to become unloaded. |
| 222 | { |
| 223 | FastMutex::ScopedLock lock(_mutex); |
| 224 | |
| 225 | typename LibraryMap::iterator it = _map.find(path); |
| 226 | if (it != _map.end()) |
| 227 | { |
| 228 | if (--it->second.refCount == 0) |
| 229 | { |
| 230 | if (it->second.pLibrary->hasSymbol("pocoUninitializeLibrary" )) |
| 231 | { |
| 232 | UninitializeLibraryFunc uninitializeLibrary = (UninitializeLibraryFunc) it->second.pLibrary->getSymbol("pocoUninitializeLibrary" ); |
| 233 | uninitializeLibrary(); |
| 234 | } |
| 235 | delete it->second.pManifest; |
| 236 | it->second.pLibrary->unload(); |
| 237 | delete it->second.pLibrary; |
| 238 | _map.erase(it); |
| 239 | } |
| 240 | } |
| 241 | else throw NotFoundException(path); |
| 242 | } |
| 243 | |
| 244 | const Meta* findClass(const std::string& className) const |
| 245 | /// Returns a pointer to the MetaObject for the given |
| 246 | /// class, or a null pointer if the class is not known. |
| 247 | { |
| 248 | FastMutex::ScopedLock lock(_mutex); |
| 249 | |
| 250 | for (typename LibraryMap::const_iterator it = _map.begin(); it != _map.end(); ++it) |
| 251 | { |
| 252 | const Manif* pManif = it->second.pManifest; |
| 253 | typename Manif::Iterator itm = pManif->find(className); |
| 254 | if (itm != pManif->end()) |
| 255 | return *itm; |
| 256 | } |
| 257 | return 0; |
| 258 | } |
| 259 | |
| 260 | const Meta& classFor(const std::string& className) const |
| 261 | /// Returns a reference to the MetaObject for the given |
| 262 | /// class. Throws a NotFoundException if the class |
| 263 | /// is not known. |
| 264 | { |
| 265 | const Meta* pMeta = findClass(className); |
| 266 | if (pMeta) |
| 267 | return *pMeta; |
| 268 | else |
| 269 | throw NotFoundException(className); |
| 270 | } |
| 271 | |
| 272 | Base* create(const std::string& className) const |
| 273 | /// Creates an instance of the given class. |
| 274 | /// Throws a NotFoundException if the class |
| 275 | /// is not known. |
| 276 | { |
| 277 | return classFor(className).create(); |
| 278 | } |
| 279 | |
| 280 | Base& instance(const std::string& className) const |
| 281 | /// Returns a reference to the sole instance of |
| 282 | /// the given class. The class must be a singleton, |
| 283 | /// otherwise an InvalidAccessException will be thrown. |
| 284 | /// Throws a NotFoundException if the class |
| 285 | /// is not known. |
| 286 | { |
| 287 | return classFor(className).instance(); |
| 288 | } |
| 289 | |
| 290 | bool canCreate(const std::string& className) const |
| 291 | /// Returns true if create() can create new instances |
| 292 | /// of the class. |
| 293 | { |
| 294 | return classFor(className).canCreate(); |
| 295 | } |
| 296 | |
| 297 | void destroy(const std::string& className, Base* pObject) const |
| 298 | /// Destroys the object pObject points to. |
| 299 | /// Does nothing if object is not found. |
| 300 | { |
| 301 | classFor(className).destroy(pObject); |
| 302 | } |
| 303 | |
| 304 | bool isAutoDelete(const std::string& className, Base* pObject) const |
| 305 | /// Returns true if the object is automatically |
| 306 | /// deleted by its meta object. |
| 307 | { |
| 308 | return classFor(className).isAutoDelete(pObject); |
| 309 | } |
| 310 | |
| 311 | const Manif* findManifest(const std::string& path) const |
| 312 | /// Returns a pointer to the Manifest for the given |
| 313 | /// library, or a null pointer if the library has not been loaded. |
| 314 | { |
| 315 | FastMutex::ScopedLock lock(_mutex); |
| 316 | |
| 317 | typename LibraryMap::const_iterator it = _map.find(path); |
| 318 | if (it != _map.end()) |
| 319 | return it->second.pManifest; |
| 320 | else |
| 321 | return 0; |
| 322 | } |
| 323 | |
| 324 | const Manif& manifestFor(const std::string& path) const |
| 325 | /// Returns a reference to the Manifest for the given library |
| 326 | /// Throws a NotFoundException if the library has not been loaded. |
| 327 | { |
| 328 | const Manif* pManif = findManifest(path); |
| 329 | if (pManif) |
| 330 | return *pManif; |
| 331 | else |
| 332 | throw NotFoundException(path); |
| 333 | } |
| 334 | |
| 335 | bool isLibraryLoaded(const std::string& path) const |
| 336 | /// Returns true if the library with the given name |
| 337 | /// has already been loaded. |
| 338 | { |
| 339 | return findManifest(path) != 0; |
| 340 | } |
| 341 | |
| 342 | Iterator begin() const |
| 343 | { |
| 344 | FastMutex::ScopedLock lock(_mutex); |
| 345 | |
| 346 | return Iterator(_map.begin()); |
| 347 | } |
| 348 | |
| 349 | Iterator end() const |
| 350 | { |
| 351 | FastMutex::ScopedLock lock(_mutex); |
| 352 | |
| 353 | return Iterator(_map.end()); |
| 354 | } |
| 355 | |
| 356 | private: |
| 357 | LibraryMap _map; |
| 358 | mutable FastMutex _mutex; |
| 359 | }; |
| 360 | |
| 361 | |
| 362 | } // namespace Poco |
| 363 | |
| 364 | |
| 365 | #endif // Foundation_ClassLoader_INCLUDED |
| 366 | |