1 /++
2 This module managed dynamic loading of shared libraries as Plugins.
3 
4 The plugin uses the runtime information from the framework to enforce
5 version consistency.
6 
7 +/
8 /**
9 Copyright: 2018 Mark Fisher
10 
11 License:
12 Permission is hereby granted, free of charge, to any person obtaining a copy of
13 this software and associated documentation files (the "Software"), to deal in
14 the Software without restriction, including without limitation the rights to
15 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
16 of the Software, and to permit persons to whom the Software is furnished to do
17 so, subject to the following conditions:
18 
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21 
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29 **/
30 module dxx.app.plugin;
31 
32 private import std.experimental.logger;
33 private import std.exception;
34 
35 //private import hunt.cache;
36 
37 private import dxx.util;
38 private import dxx.sys.loader;
39 
40 private import dxx.app.component;
41 private import dxx.app.extension;
42 private import dxx.app.platform;
43 private import dxx.app.properties;
44 private import dxx.app.services;
45 
46 struct PluginDependency {
47     string id;
48     string verRange;
49     bool optional;
50 }
51 
52 struct PluginDescriptor {
53     string id;
54     string pluginVersion;
55     string name;
56     string pluginDoc;
57     uint runLevel;
58     string[string] attr;
59 
60     PluginDependency[] dependencies;
61     ExtensionPointDesc[]* extensionPoints;
62     ExtensionDesc[]* extensions;
63 }
64 
65 /++
66 This struct is passed as the user data and passed to the exported methods
67 in the shared library, as defined by the `reloaded` framework.
68 
69 Most of the fields are initilised by the host platform; several however
70 are initialised by the plugin platform.
71 ++/
72 struct PluginContext {
73   // These parts filled in by the kernel
74     string delegate(string id) shared platformGetString;
75     string[] delegate(string id) shared platformGetStrings;
76     void delegate(string id,string value) shared platformSetString;
77     void delegate(string id,string[] value) shared platformSetStrings;
78 
79     void delegate (string[] typeId,void delegate (ServiceNotification),string[string]) shared platformAddServiceListener;
80     void delegate ( void delegate (ServiceNotification) ) shared platformRemoveServiceListener;
81 
82     ServiceRegistration delegate(string[] typeId,void*) shared registerService;
83     void delegate(ServiceRegistration reg) shared unregisterService;
84     //void delegate(ServiceRegistration reg) shared platformUpdateRegistration;
85 
86     ServiceReference delegate (string typeId) lookupServiceReference;
87     ServiceReference[] delegate (string typeId) lookupServiceReferences;
88     void* delegate(ServiceReference) lookupService;
89     void delegate(ServiceReference) releaseServiceReference;
90 
91     void* delegate(string id) shared platformCreateInstance;
92     void delegate(void*) shared platformDestroyInstance;
93 
94     PluginDescriptor* desc;   /// This part filled in by the plugin.
95     void* delegate(string id) shared pluginCreateInstance; /// ditto
96     void delegate(void*) shared pluginDestroyInstance; /// ditto
97 }
98 
99 /++
100 Manages a single plugin (dynamic shared library).
101 ++/
102 class PluginLoader {
103     PluginContext ctx;
104     PluginDescriptor _desc;
105     Loader loader;
106     alias loader this;
107 
108     static auto pluginFileName(string name,string path) {
109       version(Windows) {
110           return path ~ "/" ~ name ~ ".dll";
111       } else {
112           return path ~ "/lib" ~ name ~ ".so";
113       }
114 
115     }
116     void load(string path) {
117       debug(Plugin) {
118           MsgLog.info("PluginLoader load "  ~ path);
119       }
120       enforce(loader is null);
121       loader = Loader.loadModule(path,&ctx);
122       enforce(loader);
123       _desc = *pluginContext.desc;
124       pluginContext.desc = &_desc;
125       auto pl = cast(shared(PluginLoader))this;
126       pluginContext.platformCreateInstance = &pl.createInstance;
127       pluginContext.platformDestroyInstance = &pl.destroyInstance;
128       pluginContext.platformGetString = &pl.getString;
129       pluginContext.platformGetStrings = &pl.getStrings;
130       pluginContext.platformSetString = &pl.setString;
131       pluginContext.platformSetStrings = &pl.setStrings;
132 //      pluginContext.platformSetProperty = &pl.setProperty;
133     }
134     void load(string name,string path) {
135         auto p = pluginFileName(name,path);
136         this.load(p);
137     }
138     inout ref
139     auto desc() {
140         return _desc;
141     }
142     inout ref
143     auto pluginContext() {
144         return ctx;
145     }
146     shared void* createInstance(string id) {
147         auto a = TypeInfo_Class.find(id).create;
148         // TODO use the injector lookup...
149         return cast(void*)a;
150     }
151     void destroyInstance(void* t) shared {
152         destroy(t);
153     }
154     string getString(string id) shared {
155       return Properties.__("plugins." ~ pluginId ~ "." ~ id);
156     }
157     string[] getStrings(string id) shared {
158       return Properties.___("plugins." ~ pluginId ~ "." ~ id);
159     }
160     void setString(string id,string value) shared {
161       Properties.assign!string("plugins." ~ pluginId ~ "." ~ id,value);
162     }
163     void setStrings(string id,string[] value) shared {
164       Properties.assign!(string[])("plugins." ~ pluginId ~ "." ~ id,value);
165     }
166     /* ServiceRegistration registerService(string[] typeId,void* svc) shared {
167       enforce(typeId.length > 0);
168       foreach(t;typeId) {
169         shared(Registration) reg = new shared(Registration)(pluginId);
170         reg.typeId = t;
171         reg.service = cast(shared(void*))svc;
172         getRegistry.put!(shared(Registration))(reg.fullyQualifiedIdentifier,reg);
173       }
174     }
175     void unregisterService(ServiceRegistration reg) shared {
176       //getRegistry.remove(pluginId ~ "." ~ reg.id);
177       auto r = cast(shared(Registration*))reg._handle;
178       if(r !is null) {
179         getRegistry.remove(r.fullyQualifiedIdentifier);
180       }
181     }
182     ServiceReference lookupServiceReference(string typeId) shared {
183       auto r = getRegistry.get_ex!Registration(typeId);
184       if(!r.isnull) {
185         return r.createRef;
186       }
187     }
188     void* lookupService(ServiceReference reference) shared {
189     }
190     static auto getRegistry() {
191       auto manger = Properties.resolve!CacheManger;
192       static UCache registry;
193       if(registry is null) {
194         registry = manger.getCache("serviceRegistry");
195         if(registry is null) {
196           registry = manger.createCache("serviceRegistry");
197         }
198       }
199       return registry;
200     } */
201     auto pluginId() shared {
202       return (cast(shared(PluginDescriptor))_desc).id;
203     }
204 }
205 
206 class PluginRuntime(PluginType : Plugin,Param...) : PlatformRuntime!(Param) {
207     void registerPluginComponents(InjectionContainer injector) {
208         debug(Plugin) {
209             sharedLog.info("registerPluginComponents()");
210         }
211         new PluginDefault.ModuleListener().register;
212         injector.register!(Plugin,PluginType);
213         registerExtensionPoints();
214         registerExtensions();
215     }
216     override void registerAppDependencies(InjectionContainer injector) {
217         debug(Plugin) {
218             sharedLog.trace("registerAppDependencies()");
219         }
220         super.registerAppDependencies(injector);
221         registerPluginComponents(injector);
222     }
223     void registerExtensionPoints() {
224       debug(Plugin) {
225           sharedLog.trace("registerExtensionPoints()");
226       }
227     }
228     void registerExtensions() {
229       debug(Plugin) {
230           sharedLog.trace("registerExtensions()");
231       }
232     }
233 }
234 
235 mixin template registerPlugin(T : PluginRuntime!(P,Param),P : Plugin,Param ...) {
236     version(DXX_Plugin) {
237         mixin registerComponent!(T);
238     }
239 }
240 
241 interface PluginActivator {
242     void activate(PluginContext* ctx);
243     void deactivate(PluginContext* ctx);
244 }
245 
246 interface Plugin {
247 //    void init(PluginDescriptor* pluginData);
248     enum State {
249         UNINSTALLED,INSTALLED,LOADED,INITIALIZED,STARTED,STOPPED,DEINITIALIZED
250     }
251     const(PluginDescriptor)* descr();
252     void init();
253     void deinit();
254     PluginActivator activator();
255 }
256 
257 abstract class PluginDefault : Plugin {
258     static __gshared Plugin INSTANCE;
259     static __gshared PluginDescriptor DESCR;
260     static bool instantiated = false;
261 
262     PluginActivator _activator;
263 
264     ExtensionDesc[] extensions;
265     ExtensionPointDesc[] extensionPoints;
266 
267     static void setDescr(PluginDescriptor desc) {
268         DESCR = desc;
269     }
270     static auto getInstance() {
271         if(!instantiated) {
272             synchronized(PluginDefault.classinfo) {
273                 if(!INSTANCE) {
274                     INSTANCE = resolveInjector!Plugin;
275                 }
276             }
277             instantiated = true;
278         }
279         return INSTANCE;
280     }
281     //static Plugin getInstance() {
282     //    if(INSTANCE is null) {
283     //        INSTANCE = resolveInjector!Plugin;
284     //    }
285     //    return INSTANCE;
286     //}
287     static class ModuleListener : ModuleNotificationListener {
288         override shared void onInit(Module.ModuleEvent* event) {
289             debug(Plugin) {
290                 info("onInit");
291             }
292             event.mod.data!(PluginContext).desc = &DESCR;
293             //event.mod.data!(PluginContext).desc.extensionPoints = &extensionPoints;
294             //event.mod.data!(PluginContext).desc.extensions = &extensions;
295             event.mod.data!(PluginContext).pluginCreateInstance = &createInstance;
296             event.mod.data!(PluginContext).pluginDestroyInstance = &destroyInstance;
297             getInstance.init;
298         }
299         override shared void onDeinit(Module.ModuleEvent* event) {
300             debug(Plugin) {
301                 info("onDeinit");
302             }
303         }
304         override shared void onLoad(Module.ModuleEvent* event) {
305             debug(Plugin) {
306                 info("onLoad");
307             }
308         }
309         override shared void onUnload(Module.ModuleEvent* event) {
310             debug(Plugin) {
311                 info("onUnload");
312             }
313             if(getInstance.activator !is null) {
314                 getInstance.activator.deactivate(event.mod.data!PluginContext);
315             }
316         }
317         override shared void onUpdate(Module.ModuleEvent* event) {
318             debug(Plugin) {
319                 info("onUpdate");
320             }
321             if(getInstance.activator !is null) {
322                 getInstance.activator.activate(event.mod.data!PluginContext);
323             }
324         }
325         shared void* createInstance(string id) {
326             auto a = TypeInfo_Class.find(id).create;
327             // TODO use the injector lookup...
328             return cast(void*)a;
329         }
330         shared void destroyInstance(void* t) {
331             destroy(t);
332         }
333     }
334 
335     this() {
336       DESCR.extensionPoints = &extensionPoints;
337       DESCR.extensions = &extensions;
338     }
339     override void init() {
340         debug(Pugin) {
341             MsgLog.info("init");
342             MsgLog.info(descr.id);
343         }
344     }
345     override void deinit() {
346         debug(Pugin) {
347             MsgLog.info("deinit");
348             MsgLog.info(descr.id);
349         }
350     }
351     override PluginActivator activator() {
352         debug(Pugin) {
353             MsgLog.info("activator");
354             MsgLog.info(descr.id);
355         }
356         return _activator;
357     }
358     void activator(PluginActivator a) {
359         _activator = a;
360     }
361 
362     override const(PluginDescriptor)* descr() {
363         return &DESCR;
364     }
365 
366 }
367 
368 mixin template pluginMain() {
369     version(unittest) {
370     } else {
371         version(Windows) {
372             private import core.sys.windows.dll;
373             mixin SimpleDllMain;
374         } else {
375         }
376     }
377 }