1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head><meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
<title>C-Pluff C API: Main program</title>
<link href="doxygen.css" rel="stylesheet" type="text/css">
<link href="tabs.css" rel="stylesheet" type="text/css">
</head><body>
<!-- Generated by Doxygen 1.5.1 -->
<div class="tabs">
<ul>
<li><a href="index.html"><span>Main Page</span></a></li>
<li><a href="modules.html"><span>Modules</span></a></li>
<li><a href="annotated.html"><span>Data Structures</span></a></li>
<li><a href="files.html"><span>Files</span></a></li>
<li><a href="pages.html"><span>Related Pages</span></a></li>
</ul></div>
<h1><a class="anchor" name="cMainProgram">Main program</a></h1><h2><a class="anchor" name="cMainProgramOverview">
Overview</a></h2>
The main program is the part of executable that is located outside the plug-in framework. The main program is responsible for setting up the plug-in framework and for loading the desired set of <a class="el" href="plugin.html">plug-ins</a>. The main program should preferably be very thin, a mere plug-in loader, because it can not fully participate in plug-in interaction. C-Pluff distribution provides a plug-in loader, cpluff-loader, which can be used as a generic main program for arbitrary plug-in collections.<h2><a class="anchor" name="cMainProgramResponsibilities">
Responsibilities</a></h2>
The main program has several responsibilities:<p>
<ul>
<li><a class="el" href="cMainProgram.html#cMainProgramInitFramework">initializing the plug-in framework</a></li><li><a class="el" href="cMainProgram.html#cMainProgramCreateContext">creating a plug-in context</a></li><li><a class="el" href="cMainProgram.html#cMainProgramLoad">loading plug-ins</a></li><li><a class="el" href="cMainProgram.html#cMainProgramExec">controlling plug-in execution</a></li><li><a class="el" href="cMainProgram.html#cMainProgramChange">changing plug-in configuration</a> (opt.)</li><li><a class="el" href="cMainProgram.html#cMainProgramDestroyFramework">destroying the plug-in framework</a></li></ul>
<h3><a class="anchor" name="cMainProgramInitFramework">
Initializing the plug-in framework</a></h3>
Plug-in framework, or the C-Pluff library, must be initialized before its services can be used. Initialization is not a thread-safe operation and should generally be done by the main program before any additional plug-in framework accessing threads are started. Initialization is done by calling <a class="el" href="group__cFuncsInit.html#gc72ffd50777c3e898dae661c67b04ba9">cp_init</a>. Additionally, the main program can use <a class="el" href="group__cFuncsInit.html#gc794f61edbd896327fabddad2b3d2fd5">cp_set_fatal_error_handler</a> to register a function that is called when a fatal error occurs. A fatal error is one that prevents the framework from continuing operation. For example, errors in operating system locking operations and a NULL pointer being passed as an argument which is expected to have a non-NULL value are fatal erors.<p>
Here is an example of possible initialization code.<p>
<div class="fragment"><pre class="fragment"><span class="preprocessor"> #include <locale.h></span>
<span class="preprocessor"> #include <<a class="code" href="cpluff_8h.html">cpluff.h</a>></span>
<span class="keywordtype">void</span> handle_fatal_error(<span class="keyword">const</span> <span class="keywordtype">char</span> *msg) {
<span class="comment">// ... log error, flush logs, send bug report, etc. ...</span>
fprintf(stderr, <span class="stringliteral">"A fatal error occurred: %s\n"</span>, msg);
abort();
}
<span class="keywordtype">void</span> initialize(<span class="keywordtype">void</span>) {
<a class="code" href="group__cEnums.html#gd6a8106d281ffa4b1f43fe8e3effc7da">cp_status_t</a> status;
setlocale(LC_ALL, <span class="stringliteral">""</span>);
<a class="code" href="group__cFuncsInit.html#gc794f61edbd896327fabddad2b3d2fd5">cp_set_fatal_error_handler</a>(handle_fatal_error);
status = <a class="code" href="group__cFuncsInit.html#gc72ffd50777c3e898dae661c67b04ba9">cp_init</a>();
<span class="keywordflow">if</span> (status != <a class="code" href="group__cEnums.html#ggd6a8106d281ffa4b1f43fe8e3effc7da5ffbe0fe80af75e699e4b04909912f7a">CP_OK</a>) {
<span class="comment">// ... handle initialization failure ...</span>
}
}
</pre></div><h3><a class="anchor" name="cMainProgramCreateContext">
Creating a plug-in context</a></h3>
A plug-in context represents the co-operation environment of a set of plug-ins from the perspective of a particular participating plug-in or the perspective of the main program. From main program perspective, a plug-in context is a container for a set of plug-ins. A plug-in can interact with other plug-ins in the same container.<p>
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.<p>
A main program creates a plug-in context, to be used as a container for plugins, using <a class="el" href="group__cFuncsContext.html#g7d239c4fc07cf7d3095a20900444ad62">cp_create_context</a>.<p>
<div class="fragment"><pre class="fragment"><span class="preprocessor"> #include <<a class="code" href="cpluff_8h.html">cpluff.h</a>></span>
<a class="code" href="group__cTypedefsOpaque.html#gcb1aa0619dcefa746383c5e0833b62e7">cp_context_t</a> *ctx;
<span class="keywordtype">void</span> create_context(<span class="keywordtype">void</span>) {
<a class="code" href="group__cEnums.html#gd6a8106d281ffa4b1f43fe8e3effc7da">cp_status_t</a> status;
ctx = <a class="code" href="group__cFuncsContext.html#g7d239c4fc07cf7d3095a20900444ad62">cp_create_context</a>(&status);
<span class="keywordflow">if</span> (ctx == NULL) {
<span class="comment">// ... handle initialization failure ...</span>
}
}
</pre></div><h3><a class="anchor" name="cMainProgramLoad">
Loading plug-ins</a></h3>
An extensible application is made of plug-ins that can be added and removed dynamically. The plug-ins are loaded by the main program using the services provided by the framework. The framework provides couple of alternative ways of loading plug-ins.<p>
As a lowest level operation, the main program can load individual plug-ins from known locations using <a class="el" href="group__cFuncsPlugin.html#gcb92588ad3b48dab5e9487698f6ef437">cp_load_plugin_descriptor</a> and <a class="el" href="group__cFuncsPlugin.html#gc862fd9be2bad2e0dfaafa6216ad34d4">cp_install_plugin</a>. Here is example code that loads a set of plug-ins from file system locations listed in a file.<p>
<div class="fragment"><pre class="fragment"><span class="preprocessor"> #include <stdio.h></span>
<span class="preprocessor"> #include <<a class="code" href="cpluff_8h.html">cpluff.h</a>></span>
<span class="keyword">extern</span> <a class="code" href="group__cTypedefsOpaque.html#gcb1aa0619dcefa746383c5e0833b62e7">cp_context_t</a> *ctx;
<span class="keyword">static</span> <span class="keyword">const</span> <span class="keywordtype">char</span> pluginListFile[] = <span class="stringliteral">"/etc/example/plugins.list"</span>;
<span class="keywordtype">void</span> load_plugins(<span class="keywordtype">void</span>) {
<span class="keywordtype">char</span> plugindir[128];
FILE *lf;
<span class="comment">// Open plug-in list file</span>
lf = fopen(pluginListFile, <span class="stringliteral">"r"</span>);
<span class="keywordflow">if</span> (lf == NULL) {
<span class="comment">// ... handle loading failure ...</span>
}
<span class="comment">// Load each listed plug-in</span>
<span class="keywordflow">while</span> (fgets(plugindir, 128, lf) != NULL) {
<a class="code" href="structcp__plugin__info__t.html">cp_plugin_info_t</a> *plugininfo;
<a class="code" href="group__cEnums.html#gd6a8106d281ffa4b1f43fe8e3effc7da">cp_status_t</a> status;
<span class="keywordtype">int</span> i;
<span class="comment">// Remove possible trailing newline from plug-in location</span>
<span class="keywordflow">for</span> (i = 0; plugindir[i + 1] != <span class="charliteral">'\0'</span>; i++);
<span class="keywordflow">if</span> (plugindir[i] == <span class="charliteral">'\n'</span>) {
plugindir[i] = <span class="charliteral">'\0'</span>;
}
<span class="comment">// Load plug-in descriptor</span>
plugininfo = <a class="code" href="group__cFuncsPlugin.html#gcb92588ad3b48dab5e9487698f6ef437">cp_load_plugin_descriptor</a>(ctx, plugindir, &status);
<span class="keywordflow">if</span> (pinfo == NULL) {
<span class="comment">// ... handle loading failure ...</span>
}
<span class="comment">// Install plug-in descriptor</span>
status = <a class="code" href="group__cFuncsPlugin.html#gc862fd9be2bad2e0dfaafa6216ad34d4">cp_install_plugin</a>(ctx, plugininfo);
<span class="keywordflow">if</span> (status != <a class="code" href="group__cEnums.html#ggd6a8106d281ffa4b1f43fe8e3effc7da5ffbe0fe80af75e699e4b04909912f7a">CP_OK</a>) {
<span class="comment">// ... handle loading failure ...</span>
}
<span class="comment">// Release plug-in descriptor information</span>
<a class="code" href="group__cFuncsPluginInfo.html#gbac266df43a4850cf43224aa7b863942">cp_release_info</a>(ctx, plugininfo);
}
<span class="comment">// Close plug-in list file</span>
fclose(lf);
}
</pre></div><p>
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 <a class="el" href="group__cFuncsContext.html#g998d8350e64a129101f9da687ff5713b">cp_register_pcollection</a>. Plug-ins of the collection can then be scanned and loaded using <a class="el" href="group__cFuncsPlugin.html#ga9603cd8d153b0ce192ac7b6e56779af">cp_scan_plugins</a>. Here is example code loading plug-ins from a plug-in collection.<p>
<div class="fragment"><pre class="fragment"><span class="preprocessor"> #include <<a class="code" href="cpluff_8h.html">cpluff.h</a>></span>
<span class="keyword">extern</span> <a class="code" href="group__cTypedefsOpaque.html#gcb1aa0619dcefa746383c5e0833b62e7">cp_context_t</a> *ctx;
<span class="keyword">static</span> <span class="keyword">const</span> <span class="keywordtype">char</span> pluginCollectionDir[] = <span class="stringliteral">"/etc/example/plugins"</span>;
<span class="keywordtype">void</span> load_plugins(<span class="keywordtype">void</span>) {
<a class="code" href="group__cEnums.html#gd6a8106d281ffa4b1f43fe8e3effc7da">cp_status_t</a> status;
status = <a class="code" href="group__cFuncsContext.html#g998d8350e64a129101f9da687ff5713b">cp_register_pcollection</a>(ctx, pluginCollectionDir);
<span class="keywordflow">if</span> (status != <a class="code" href="group__cEnums.html#ggd6a8106d281ffa4b1f43fe8e3effc7da5ffbe0fe80af75e699e4b04909912f7a">CP_OK</a>) {
<span class="comment">// ... handle loading failure ...</span>
}
status = <a class="code" href="group__cFuncsPlugin.html#ga9603cd8d153b0ce192ac7b6e56779af">cp_scan_plugins</a>(ctx, 0);
<span class="keywordflow">if</span> (status != <a class="code" href="group__cEnums.html#ggd6a8106d281ffa4b1f43fe8e3effc7da5ffbe0fe80af75e699e4b04909912f7a">CP_OK</a>) {
<span class="comment">// ... handle loading failure ...</span>
<span class="comment">// (notice that some plug-ins might have been loaded)</span>
}
}
</pre></div><h3><a class="anchor" name="cMainProgramExec">
Controlling plug-in execution</a></h3>
The main program controls plug-in execution by starting and stopping plug-ins and by executing run functions registered by plug-ins. Additionally, the main program can pass startup arguments to plug-ins.<p>
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.<p>
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.<p>
The following example code shows how a main program might initialize plug-in startup arguments using <a class="el" href="group__cFuncsPluginExec.html#g9233fa8a46e408044030d2d767f1fda4">cp_set_context_args</a>, start the core plug-in using <a class="el" href="group__cFuncsPlugin.html#g2456b7740351c2530376ffb3a5ab7d92">cp_start_plugin</a> and then execute plug-in run functions using <a class="el" href="group__cFuncsPluginExec.html#g4668727a5b1c9f0531f6d5dc77fa9f9f">cp_run_plugins</a>.<p>
<div class="fragment"><pre class="fragment"><span class="preprocessor"> #include <<a class="code" href="cpluff_8h.html">cpluff.h</a>></span>
<span class="keyword">extern</span> <a class="code" href="group__cTypedefsOpaque.html#gcb1aa0619dcefa746383c5e0833b62e7">cp_context_t</a> *ctx;
<span class="keyword">static</span> <span class="keyword">const</span> <span class="keywordtype">char</span> corePluginId[] = <span class="stringliteral">"org.example.core"</span>;
<span class="keywordtype">void</span> run_plugins(<span class="keywordtype">char</span> *argv[]) {
<a class="code" href="group__cEnums.html#gd6a8106d281ffa4b1f43fe8e3effc7da">cp_status_t</a> status;
<span class="comment">// Set plug-in startup arguments</span>
<a class="code" href="group__cFuncsPluginExec.html#g9233fa8a46e408044030d2d767f1fda4">cp_set_context_args</a>(ctx, argv);
<span class="comment">// Start the core plug-in, possibly activating other plug-ins as well</span>
status = <a class="code" href="group__cFuncsPlugin.html#g2456b7740351c2530376ffb3a5ab7d92">cp_start_plugin</a>(ctx, corePluginId);
<span class="keywordflow">if</span> (status != <a class="code" href="group__cEnums.html#ggd6a8106d281ffa4b1f43fe8e3effc7da5ffbe0fe80af75e699e4b04909912f7a">CP_OK</a>) {
<span class="comment">// ... handle startup failure ...</span>
}
<span class="comment">// Execute plug-ins until there is no more work to be done</span>
<a class="code" href="group__cFuncsPluginExec.html#g4668727a5b1c9f0531f6d5dc77fa9f9f">cp_run_plugins</a>(ctx);
}
<span class="keywordtype">int</span> main(<span class="keywordtype">int</span> argc, <span class="keywordtype">char</span> *argv[]) {
<span class="comment">// ... do initialization and load plug-ins ...</span>
run_plugins(argv);
<span class="comment">// ... do destruction ...</span>
}
</pre></div><p>
Alternatively, if the main program has some operations it must perform as part of the main loop, the call to <a class="el" href="group__cFuncsPluginExec.html#g4668727a5b1c9f0531f6d5dc77fa9f9f">cp_run_plugins</a> can be replaced by code using <a class="el" href="group__cFuncsPluginExec.html#g7e212bc93d6588fc09995f310548c929">cp_run_plugins_step</a> like in the following example.<p>
<div class="fragment"><pre class="fragment"> <span class="keywordtype">void</span> mainloop(<span class="keywordtype">void</span>) {
<span class="keywordtype">int</span> finished = 0;
<span class="keywordflow">while</span> (!finished) {
<span class="comment">// ... do main program specific operations ...</span>
finished = !<a class="code" href="group__cFuncsPluginExec.html#g7e212bc93d6588fc09995f310548c929">cp_run_plugins_step</a>(ctx);
}
}
</pre></div><h3><a class="anchor" name="cMainProgramChange">
Changing plug-in configuration</a></h3>
C-Pluff has been designed to allow dynamic changes to the plug-in configuration, that is plug-ins being added or removed without shutting down the application or the framework. It is the responsibility of the main program to manage such changes if the application is to support dynamic configuration changes.<p>
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 <a class="el" href="group__cFuncsPlugin.html#ga9603cd8d153b0ce192ac7b6e56779af">cp_scan_plugins</a> 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 <a class="el" href="group__cScanFlags.html#gb5996fbc57c7ec3bb538bc80a3ebfc40">CP_SP_STOP_ALL_ON_INSTALL</a> and <a class="el" href="group__cScanFlags.html#g7ca04507561932ae293e81c3636768b7">CP_SP_RESTART_ACTIVE</a> 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.<p>
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 <a class="el" href="group__cFuncsPlugin.html#ga9603cd8d153b0ce192ac7b6e56779af">cp_scan_plugins</a> with <a class="el" href="group__cScanFlags.html#g1d4b72334d60f1401e6616da54e4d6f1">CP_SP_UPGRADE</a> and <a class="el" href="group__cScanFlags.html#g7ca04507561932ae293e81c3636768b7">CP_SP_RESTART_ACTIVE</a> 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, <a class="el" href="group__cScanFlags.html#g72cdcd1181d60fd3caf5d0e0dd59c33c">CP_SP_STOP_ALL_ON_UPGRADE</a> can be added to restart all active plug-ins.<p>
Deleting plug-ins must be done by first stopping and unloading the plug-in to be deleted using <a class="el" href="group__cFuncsPlugin.html#g2249552dc67bb7893f81babee4a27454">cp_uninstall_plugin</a>. The the plug-in can be removed from the plug-in collection.<h3><a class="anchor" name="cMainProgramDestroyFramework">
Destroying the plug-in framework</a></h3>
The plug-in framework can be destroyed and all resources released by calling <a class="el" href="group__cFuncsInit.html#g8dfbe51a3b7f23368a4ddf0d9987180e">cp_destroy</a> as many times as <a class="el" href="group__cFuncsInit.html#gc72ffd50777c3e898dae661c67b04ba9">cp_init</a> has been called. This is not a thread-safe operation and should generally be done by the main program just before application exits. The destroy function stops and unloads all plug-ins and destroys all plug-in contexts before destroying the core framework.<p>
Individual plug-in contexts can be destroyed by calling <a class="el" href="group__cFuncsContext.html#g754ff895992fab0aae81a09c727e5868">cp_destroy_context</a>. The destroy function stops and unloads all plug-ins before destroying the context itself. <hr size="1">
<p class="footer">
<a href="http://www.c-pluff.org/">C-Pluff</a>, a plug-in framework for C<br>
Copyright 2007 <a href="http://www.jlehtinen.net/">Johannes Lehtinen</a>
</p>
<p class="generated-by">
Generated on Fri Apr 6 15:40:55 2007 for C-Pluff C API by <a href="http://www.doxygen.org/index.html"><img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.1
</p>
</body>
</html>
|