Here is an example of possible initialization code.
#include <locale.h> #include <cpluff.h> void handle_fatal_error(const char *msg) { // ... log error, flush logs, send bug report, etc. ... fprintf(stderr, "A fatal error occurred: %s\n", msg); abort(); } void initialize(void) { cp_status_t status; setlocale(LC_ALL, ""); cp_set_fatal_error_handler(handle_fatal_error); status = cp_init(); if (status != CP_OK) { // ... handle initialization failure ... } }
An extensible application can have more than one plug-in container but usually one container should suffice. Due to the nature of C programs, plug-ins deployed to different containers are not very well insulated from each other. For example, global variables provided by a plug-in in one container are visible to all plug-ins in all containers. Also, by placing all plug-ins in the same container they can more efficiently share common base components which themselves might provide extensibility.
A main program creates a plug-in context, to be used as a container for plugins, using cp_create_context.
#include <cpluff.h> cp_context_t *ctx; void create_context(void) { cp_status_t status; ctx = cp_create_context(&status); if (ctx == NULL) { // ... handle initialization failure ... } }
As a lowest level operation, the main program can load individual plug-ins from known locations using cp_load_plugin_descriptor and cp_install_plugin. Here is example code that loads a set of plug-ins from file system locations listed in a file.
#include <stdio.h> #include <cpluff.h> extern cp_context_t *ctx; static const char pluginListFile[] = "/etc/example/plugins.list"; void load_plugins(void) { char plugindir[128]; FILE *lf; // Open plug-in list file lf = fopen(pluginListFile, "r"); if (lf == NULL) { // ... handle loading failure ... } // Load each listed plug-in while (fgets(plugindir, 128, lf) != NULL) { cp_plugin_info_t *plugininfo; cp_status_t status; int i; // Remove possible trailing newline from plug-in location for (i = 0; plugindir[i + 1] != '\0'; i++); if (plugindir[i] == '\n') { plugindir[i] = '\0'; } // Load plug-in descriptor plugininfo = cp_load_plugin_descriptor(ctx, plugindir, &status); if (pinfo == NULL) { // ... handle loading failure ... } // Install plug-in descriptor status = cp_install_plugin(ctx, plugininfo); if (status != CP_OK) { // ... handle loading failure ... } // Release plug-in descriptor information cp_release_info(ctx, plugininfo); } // Close plug-in list file fclose(lf); }
Alternatively, the main program can register and load plug-in collections. A plug-in collection is a file system directory which includes individual plug-ins in subdirectories, one plug-in in each subdirectory. Plug-in collections can be registered with a plug-in context using cp_register_pcollection. Plug-ins of the collection can then be scanned and loaded using cp_scan_plugins. Here is example code loading plug-ins from a plug-in collection.
#include <cpluff.h> extern cp_context_t *ctx; static const char pluginCollectionDir[] = "/etc/example/plugins"; void load_plugins(void) { cp_status_t status; status = cp_register_pcollection(ctx, pluginCollectionDir); if (status != CP_OK) { // ... handle loading failure ... } status = cp_scan_plugins(ctx, 0); if (status != CP_OK) { // ... handle loading failure ... // (notice that some plug-ins might have been loaded) } }
When plug-ins are installed they are not yet activated and their runtime library is not even loaded at that point. The main program typically activates plug-ins by starting a main plug-in responsible for user interface or core application logic. This plug-in then implicitly causes other plug-ins to be activated via dependencies and by dynamically resolving symbols provided by other plug-ins. Plug-ins recursively activate each other until all initially needed plug-ins have been started. Some plug-ins might be activated at a later time when their functionality is needed, for example due to user action.
If a plug-in needs to perform background operations, that is operations executed outside the invocation of plug-in provided interface functions, then it can either start a new thread or it can register a run function. A run function is a function that is typically executed as part of the main loop by the main program.
The following example code shows how a main program might initialize plug-in startup arguments using cp_set_context_args, start the core plug-in using cp_start_plugin and then execute plug-in run functions using cp_run_plugins.
#include <cpluff.h> extern cp_context_t *ctx; static const char corePluginId[] = "org.example.core"; void run_plugins(char *argv[]) { cp_status_t status; // Set plug-in startup arguments cp_set_context_args(ctx, argv); // Start the core plug-in, possibly activating other plug-ins as well status = cp_start_plugin(ctx, corePluginId); if (status != CP_OK) { // ... handle startup failure ... } // Execute plug-ins until there is no more work to be done cp_run_plugins(ctx); } int main(int argc, char *argv[]) { // ... do initialization and load plug-ins ... run_plugins(argv); // ... do destruction ... }
Alternatively, if the main program has some operations it must perform as part of the main loop, the call to cp_run_plugins can be replaced by code using cp_run_plugins_step like in the following example.
void mainloop(void) { int finished = 0; while (!finished) { // ... do main program specific operations ... finished = !cp_run_plugins_step(ctx); } }
Adding plug-ins is straightforward because there is no need to consider dependencies of active plug-ins. For example, if one uses plug-in collections as introduced above then new plug-ins can be deployed under the plug-in collection directory while the application is running and the main program can load them incrementally by calling cp_scan_plugins again. This call might be activated by some user interface element, for example a plug-in manager component which just downloaded and installed new plug-ins as requested by the user. The flags CP_SP_STOP_ALL_ON_INSTALL and CP_SP_RESTART_ACTIVE orred together can be used to cause all active plug-ins to be restarted if they do not otherwise notice the extensions provided by new plug-ins.
Upgrading plug-ins is almost as straightforward because the C-Pluff framework manages plug-in dependencies (assuming the plug-ins have declared their dependencies properly). The new version of a plug-in can be deployed under the plug-in collection directory in a new subdirectory parallel to the old version while the application is running. The main program can then call cp_scan_plugins with CP_SP_UPGRADE and CP_SP_RESTART_ACTIVE orred together. This will stop the old version of the upgraded plug-in (implicitly stopping all plug-ins that depend on it), unload the plug-in from the framework, install the new version of the plug-in and finally restart plug-ins that were active before the operation. The old version of the plug-in can now be removed from the plug-in collection. Again, CP_SP_STOP_ALL_ON_UPGRADE can be added to restart all active plug-ins.
Deleting plug-ins must be done by first stopping and unloading the plug-in to be deleted using cp_uninstall_plugin. The the plug-in can be removed from the plug-in collection.
Individual plug-in contexts can be destroyed by calling cp_destroy_context. The destroy function stops and unloads all plug-ins before destroying the context itself.
Generated on Fri Apr 6 15:40:55 2007 for C-Pluff C API by 1.5.1