1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */
2
3 /* AbiSource Application Framework
4 * Copyright (C) 2001 AbiSource, Inc.
5 * Copyright (C) 2001 Dom Lachowicz <cinamod@hotmail.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301 USA.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "ut_exception.h"
28 #include "ut_vector.h"
29 #include "ut_assert.h"
30 #include "ut_debugmsg.h"
31
32 #include "xap_ModuleManager.h"
33 #include "xap_App.h"
34 #include "xap_Prefs.h"
35 #include "ut_string_class.h"
36 #include "ut_path.h"
37
38 // the loader manages instances of one of these target classes
39
40 #if defined (TOOLKIT_WIN)
41 #include "xap_Win32Module.h"
42 #define MODULE_CLASS XAP_Win32Module
43 #elif defined (TOOLKIT_COCOA)
44 #include "xap_CocoaModule.h"
45 #define MODULE_CLASS XAP_CocoaModule
46 #else
47 #include "xap_UnixModule.h"
48 #define MODULE_CLASS XAP_UnixModule
49 #endif
50
51 // log information about plugin loading into the <log> section of AbiWord.profile
52 // (we save the prefs file after each call, so as to maximise the information we have in
53 // case a plugin crashes and the crash handler does not get a chance to save it for us
54 #define XAP_MODULE_MANAGER_LOAD_LOG(msg1, msg2) \
55 if(XAP_App::getApp() && XAP_App::getApp()->getPrefs()) \
56 { \
57 UT_String __s; \
58 UT_String_sprintf(__s, "(L%d): %s %s", __LINE__, msg1, msg2); \
59 UT_DEBUGMSG(("%s\n",__s.c_str())); \
60 XAP_App::getApp()->getPrefs()->log("XAP_ModuleManager::loadModule", __s.c_str());\
61 XAP_App::getApp()->getPrefs()->savePrefsFile(); \
62 }
63
64 /*!
65 * Protected destructor creates an instance of this module class
66 */
XAP_ModuleManager()67 XAP_ModuleManager::XAP_ModuleManager ()
68 {
69 m_modules = new UT_GenericVector<XAP_Module*> (11);
70 }
71
72 /*!
73 * Private destructor
74 */
~XAP_ModuleManager()75 XAP_ModuleManager::~XAP_ModuleManager ()
76 {
77 UT_VECTOR_PURGEALL (MODULE_CLASS *, (*m_modules));
78 delete m_modules;
79 }
80
81 /*!
82 * Acquire a handle to an instance of this module loader class
83 */
instance()84 XAP_ModuleManager & XAP_ModuleManager::instance ()
85 {
86 static XAP_ModuleManager me;
87 return me;
88 }
89
90 /*!
91 * Request that the ModuleManager load the module represented by szFilename.
92 *
93 * \param szFilename - the .dll or .so on your system that you wish to load
94 *
95 * \return true if loaded successfully, false otherwise
96 */
loadModule(const char * szFilename)97 bool XAP_ModuleManager::loadModule (const char * szFilename)
98 {
99 UT_ASSERT (szFilename);
100
101 if ( szFilename == 0) return false;
102 if (*szFilename == 0) return false;
103
104 XAP_MODULE_MANAGER_LOAD_LOG("loading", szFilename)
105
106 // check to see if plugin is already loaded
107
108 XAP_Module* pModuleLoop = 0;
109 const UT_GenericVector<class XAP_Module *> *pVec = enumModules();
110
111 for (UT_sint32 i = 0; i < pVec->size(); i++)
112 {
113 pModuleLoop = (XAP_Module *)pVec->getNthItem (i);
114
115 char * moduleName = 0;
116 if(pModuleLoop && pModuleLoop->getModuleName(&moduleName))
117 {
118 if (!strcmp(UT_basename(szFilename), UT_basename(moduleName)))
119 {
120 // already loaded, don't attempt to load again and exit quietly
121 FREEP(moduleName);
122 return true;
123 }
124 FREEP(moduleName);
125 }
126 }
127
128
129 XAP_Module * pModule = 0;
130 UT_TRY
131 {
132 pModule = new MODULE_CLASS;
133 }
134 UT_CATCH (...)
135 {
136 pModule = 0;
137 }
138 if (pModule == 0) return false;
139
140 if (!pModule->load (szFilename))
141 {
142 UT_DEBUGMSG (("Failed to load module %s\n", szFilename));
143 XAP_MODULE_MANAGER_LOAD_LOG("failed to load", szFilename)
144
145 char * errorMsg = 0;
146 if (pModule->getErrorMsg (&errorMsg))
147 {
148 UT_DEBUGMSG (("Reason: %s\n", errorMsg));
149 XAP_MODULE_MANAGER_LOAD_LOG("error msg", errorMsg)
150 FREEP (errorMsg);
151 }
152 delete pModule;
153 return false;
154 }
155
156 /* assign the module's creator to be us, etc.
157 */
158 pModule->setLoaded (true);
159 pModule->setCreator (this);
160
161 if (!pModule->registerThySelf ())
162 {
163 UT_DEBUGMSG (("Failed to register module %s\n", szFilename));
164 XAP_MODULE_MANAGER_LOAD_LOG("failed to register", szFilename)
165
166 char * errorMsg = 0;
167 if (pModule->getErrorMsg (&errorMsg))
168 {
169 UT_DEBUGMSG (("Reason: %s\n", errorMsg?errorMsg:"unknown"));
170 XAP_MODULE_MANAGER_LOAD_LOG("error msg",
171 errorMsg?errorMsg:"Unknown")
172 FREEP (errorMsg);
173 }
174 pModule->unload ();
175 delete pModule;
176 return false;
177 }
178 if (m_modules->addItem (pModule)) // an error occurred...
179 {
180 XAP_MODULE_MANAGER_LOAD_LOG("could not add", szFilename)
181 pModule->unregisterThySelf ();
182 pModule->unload ();
183 delete pModule;
184 return false;
185 }
186
187 /* we (somehow :^) got here. count our blessings and return
188 */
189 XAP_MODULE_MANAGER_LOAD_LOG("success", szFilename)
190 return true;
191 }
192
193 /*!
194 * Load builtin plugins.
195 */
loadPreloaded(XAP_Plugin_Registration fnRegister,XAP_Plugin_Registration fnDeregister,XAP_Plugin_VersionCheck fnSupportsVersion)196 bool XAP_ModuleManager::loadPreloaded (XAP_Plugin_Registration fnRegister,
197 XAP_Plugin_Registration fnDeregister,
198 XAP_Plugin_VersionCheck fnSupportsVersion)
199 {
200 UT_ASSERT (fnRegister && fnDeregister && fnSupportsVersion);
201
202 if (!(fnRegister && fnDeregister && fnSupportsVersion)) return false;
203
204 XAP_Module * pModule = 0;
205 UT_TRY
206 {
207 pModule = new MODULE_CLASS;
208 }
209 UT_CATCH (...)
210 {
211 pModule = 0;
212 }
213 if (pModule == 0) return false;
214
215 if (!pModule->setSymbols (fnRegister, fnDeregister, fnSupportsVersion)) // huh?
216 {
217 delete pModule;
218 return false;
219 }
220
221 /* assign the module's creator to be us, etc.
222 */
223 pModule->setLoaded (true);
224 pModule->setCreator (this);
225
226 if (!pModule->registerThySelf ())
227 {
228 UT_DEBUGMSG (("Failed to register preloaded module\n"));
229 delete pModule;
230 return false;
231 }
232 if (m_modules->addItem (pModule)) // an error occurred...
233 {
234 UT_DEBUGMSG (("oops! out of memory?\n"));
235 pModule->unregisterThySelf ();
236 delete pModule;
237 return false;
238 }
239
240 /* we (somehow :^) got here. count our blessings and return
241 */
242 return true;
243 }
244
245 /*!
246 * Unload a module
247 *
248 * \param pModule - a valid module that you want to unload
249 *
250 * WARNING: zero or more plugins/modules may be unloaded as a result of
251 * calling XAP_ModuleManager::unloadModule since the plugin may be unloaded
252 * already or other plugins may depend on this one and be unloaded
253 * automatically.
254 *
255 * Don't assume anything, therefore, about the UT_Vector returned by
256 * XAP_ModuleManager::enumModules
257 */
unloadModule(XAP_Module * pModule)258 void XAP_ModuleManager::unloadModule (XAP_Module * pModule)
259 {
260 UT_ASSERT (pModule);
261 if (pModule == 0) return;
262
263 UT_ASSERT (pModule->getCreator () == this);
264 if (pModule->getCreator () != this) return;
265
266 UT_sint32 ndx = m_modules->findItem (pModule);
267 if (ndx == -1)
268 {
269 UT_ASSERT (UT_SHOULD_NOT_HAPPEN);
270 UT_DEBUGMSG (("Could not unload module\n"));
271 return;
272 }
273 unloadModule (ndx);
274 }
275
276 /* for use by unloadAllPlugins, unloadModule only!
277 */
unloadModule(UT_sint32 ndx)278 void XAP_ModuleManager::unloadModule (UT_sint32 ndx)
279 {
280 UT_return_if_fail(m_modules != NULL);
281
282 XAP_Module * pModule = m_modules->getNthItem (ndx);
283
284 m_modules->deleteNthItem (ndx);
285
286 // we're less picky when unloading than we are when loading
287 // the (necessarily true) assumptions are that
288 //
289 // 1) we were the one who loaded the module
290 // 2) registerThySelf() worked
291 //
292 // so it had better damn well work in the opposite direction!
293
294 pModule->unregisterThySelf ();
295 pModule->setLoaded (false);
296
297 bool module_unloaded = pModule->unload ();
298 UT_UNUSED (module_unloaded);
299 UT_ASSERT (module_unloaded);
300
301 delete pModule;
302 }
303
304 /*!
305 * Enumerate the modules loaded thus far
306 *
307 * \return a vector containing XAP_Module*'s
308 */
enumModules() const309 const UT_GenericVector<XAP_Module*> * XAP_ModuleManager::enumModules () const
310 {
311 // TODO: perhaps we should clone this
312 return m_modules;
313 }
314
315 /*!
316 * Unload all currently loaded plugins, in order of last added to first
317 *
318 * \return nothing
319 */
unloadAllPlugins()320 void XAP_ModuleManager::unloadAllPlugins ()
321 {
322 UT_return_if_fail(m_modules != NULL);
323
324 /* make sure all the plugins are unloaded (reverse order loaded)
325 *
326 * Note: can no longer assume that XAP_ModuleManager::unloadModule()
327 * will unload only one module.
328 */
329 while (UT_sint32 count = m_modules->getItemCount ())
330 {
331 unloadModule (count - 1);
332
333 if (m_modules->getItemCount () == count) // huh?
334 {
335 UT_ASSERT (UT_SHOULD_NOT_HAPPEN);
336 break;
337 }
338 }
339 }
340