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 | |