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 }