1 /**
2 Copyright 2018 Mark Fisher
3 
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
10 
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 **/
22 module dxx.app.plugin;
23 
24 private import std.experimental.logger;
25 private import std.exception;
26 
27 private import dxx.util;
28 private import dxx.sys.loader;
29 
30 private import dxx.app.component;
31 private import dxx.app.extension;
32 private import dxx.app.platform;
33 
34 struct PluginDependency {
35     string id;
36     string verRange;
37     bool optional;
38 }
39 
40 struct PluginDescriptor {
41     string id;
42     string pluginVersion;
43     string name;
44     string pluginDoc;
45     uint runLevel;
46     string[string] attr;
47 
48     PluginDependency[] dependencies;
49     ExtensionPointDesc[]* extensionPoints;
50     ExtensionDesc[]* extensions;
51 }
52 
53 struct PluginContext {
54     PluginDescriptor* desc;
55     void* delegate(string id) shared pluginCreateInstance;
56     void delegate(void*) shared pluginDestroyInstance;
57 }
58 
59 class PluginLoader {
60     PluginContext ctx;
61     Loader loader;
62     alias loader this;
63 
64     static auto pluginFileName(string name,string path) {
65       version(Windows) {
66           return path ~ "/" ~ name ~ ".dll";
67       } else {
68           return path ~ "/lib" ~ name ~ ".so";
69       }
70 
71     }
72     void load(string path) {
73       debug(Plugin) {
74           MsgLog.info("PluginLoader load "  ~ path);
75       }
76       loader = Loader.loadModule(path,&ctx);
77       enforce(loader);
78     }
79     void load(string name,string path) {
80         auto p = pluginFileName(name,path);
81         this.load(p);
82     }
83     inout ref
84     auto desc() {
85         return ctx.desc;
86     }
87     inout ref
88     auto pluginContext() {
89         return ctx;
90     }
91 }
92 
93 class PluginRuntime(PluginType : Plugin,Param...) : PlatformRuntime!(Param) {
94     void registerPluginComponents(InjectionContainer injector) {
95         injector.register!(Plugin,PluginType);
96         new PluginDefault.ModuleListener().register;
97     }
98     override void registerAppDependencies(InjectionContainer injector) {
99         debug(Plugin) {
100             import std.experimental.logger;
101             sharedLog.info("registerAppDependencies()");
102         }
103         super.registerAppDependencies(injector);
104         registerPluginComponents(injector);
105     }
106 }
107 
108 mixin template registerPlugin(P : Plugin,Param ...) {
109     version(DXX_Plugin) {
110         mixin registerComponent!(PluginRuntime!(P,Param));
111     }
112 }
113 
114 interface PluginActivator {
115     void activate(PluginContext* ctx);
116     void deactivate(PluginContext* ctx);
117 }
118 
119 interface Plugin {
120 //    void init(PluginDescriptor* pluginData);
121     enum State {
122         UNINSTALLED,INSTALLED,LOADED,INITIALIZED,STARTED,STOPPED,DEINITIALIZED
123     }
124     const(PluginDescriptor)* descr();
125     void init();
126     void deinit();
127     PluginActivator activator();
128 }
129 
130 abstract class PluginDefault : Plugin {
131     static __gshared Plugin INSTANCE;
132     static __gshared PluginDescriptor DESCR;
133     static bool instantiated = false;
134 
135     PluginActivator _activator;
136 
137     ExtensionDesc[] extensions;
138     ExtensionPointDesc[] extensionPoints;
139 
140     static void setDescr(PluginDescriptor desc) {
141         DESCR = desc;
142     }
143     static auto getInstance() {
144         if(!instantiated) {
145             synchronized(PluginDefault.classinfo) {
146                 if(!INSTANCE) {
147                     INSTANCE = resolveInjector!Plugin;
148                 }
149             }
150             instantiated = true;
151         }
152         return INSTANCE;
153     }
154     //static Plugin getInstance() {
155     //    if(INSTANCE is null) {
156     //        INSTANCE = resolveInjector!Plugin;
157     //    }
158     //    return INSTANCE;
159     //}
160     static class ModuleListener : ModuleNotificationListener {
161         override shared void onInit(Module.ModuleEvent* event) {
162             debug(Plugin) {
163                 info("onInit");
164             }
165             event.mod.data!(PluginContext).desc = &DESCR;
166             //event.mod.data!(PluginContext).desc.extensionPoints = &extensionPoints;
167             //event.mod.data!(PluginContext).desc.extensions = &extensions;
168             event.mod.data!(PluginContext).pluginCreateInstance = &createInstance;
169             event.mod.data!(PluginContext).pluginDestroyInstance = &destroyInstance;
170             getInstance.init;
171         }
172         override shared void onDeinit(Module.ModuleEvent* event) {
173             debug(Plugin) {
174                 info("onDeinit");
175             }
176             unregister;
177         }
178         override shared void onLoad(Module.ModuleEvent* event) {
179             debug(Plugin) {
180                 info("onLoad");
181             }
182         }
183         override shared void onUnload(Module.ModuleEvent* event) {
184             debug(Plugin) {
185                 info("onUnload");
186             }
187             if(getInstance.activator !is null) {
188                 getInstance.activator.deactivate(event.mod.data!PluginContext);
189             }
190         }
191         override shared void onUpdate(Module.ModuleEvent* event) {
192             debug(Plugin) {
193                 info("onUpdate");
194             }
195             if(getInstance.activator !is null) {
196                 getInstance.activator.activate(event.mod.data!PluginContext);
197             }
198         }
199         shared void* createInstance(string id) {
200             auto a = TypeInfo_Class.find(id).create;
201             // TODO use injector
202             return cast(void*)a;
203         }
204         shared void destroyInstance(void* t) {
205             destroy(t);
206         }
207     }
208 
209     //shared static this() {
210     //    new ModuleListener().register;
211     //}
212     this() {
213       DESCR.extensionPoints = &extensionPoints;
214       DESCR.extensions = &extensions;
215     }
216     override void init() {
217         debug(Pugin) {
218             MsgLog.info("init");
219             MsgLog.info(descr.id);
220         }
221     }
222     override void deinit() {
223         debug(Pugin) {
224             MsgLog.info("deinit");
225             MsgLog.info(descr.id);
226         }
227     }
228     override PluginActivator activator() {
229         debug(Pugin) {
230             MsgLog.info("activator");
231             MsgLog.info(descr.id);
232         }
233         return _activator;
234     }
235     void activator(PluginActivator a) {
236         _activator = a;
237     }
238 
239     override const(PluginDescriptor)* descr() {
240         return &DESCR;
241     }
242 
243 }
244 
245 mixin template pluginMain() {
246     version(unittest) {
247     } else {
248         version(Windows) {
249             private import core.sys.windows.dll;
250             mixin SimpleDllMain;
251         } else {
252         }
253     }
254 }