bundle-manager.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. var log = require('hlogger').createLogger('bundle-manager');
  2. var path = require('path');
  3. var fs = require('fs');
  4. var BundleContext = require('./bundle-context');
  5. var XEventEmitter = require('./xevents');
  6. // Registry can be part of the package.json
  7. //
  8. function clearRequire(mod,resolve) {
  9. var modr = (resolve==undefined)?require.resolve(mod):mod;
  10. var cache = require.cache[modr];
  11. // Recursivelly unload children too
  12. var cc = [...cache.children];
  13. cc.forEach((c) => {
  14. clearRequire(c.id,false);
  15. });
  16. var ci;
  17. if((ci = cache.parent.children.indexOf(cache)) != -1) {
  18. cache.parent.children.splice(ci,1);
  19. }
  20. delete require.cache[modr];
  21. }
  22. function getPackageJson(inpath) {
  23. var pathParts = path.resolve(inpath).split(path.sep);
  24. while(pathParts.length > 0) {
  25. var fullPath = path.join("/", path.join.apply(path,pathParts),"/package.json");
  26. if(fs.existsSync(fullPath)) {
  27. return JSON.parse(fs.readFileSync(fullPath));
  28. }
  29. pathParts.pop();
  30. }
  31. return null;
  32. }
  33. function solveBundleName(inpath) {
  34. var name = path.basename(inpath);
  35. if(name == "index.js") {
  36. name = path.basename(path.dirname(inpath));
  37. } else {
  38. name = path.basename(inpath,".js");
  39. }
  40. return name;
  41. };
  42. // Common registry
  43. class BundleManager {
  44. constructor(opts) {
  45. // Control events
  46. this.config = opts || {};
  47. this.events = new XEventEmitter();
  48. // Global channel
  49. this.globalEvents = new XEventEmitter();
  50. this.stat = {
  51. modulesRegistered:0,
  52. modulesUnRegistered:0
  53. };
  54. this.registry = {};
  55. //this.registry = [];
  56. }
  57. generateId() {
  58. var id=0;
  59. // Slow -- by fetching all ids everytime
  60. do { id++; } while(this.get(id) != null); // Slow but one timer
  61. return id;
  62. }
  63. loadDefaultBundles() {
  64. var corePath = path.join(__dirname,"../bundles");
  65. fs.readdir(corePath, (err,dir) => {
  66. for( var file of dir ) {
  67. this.load(path.join(corePath,file));
  68. }
  69. });
  70. }
  71. getBundleByInstance(instance) {
  72. //return this.registry.find((el) => el.instance == instance);
  73. for(var k in this.registry) {
  74. if(this.registry[k].instance == instance) {
  75. return this.registry[k];
  76. }
  77. }
  78. return null;
  79. }
  80. getBundleById(id) {
  81. //return this.registry.find((el) => el.id == id);
  82. for(var k in this.registry) {
  83. if(this.registry[k].id == id) {
  84. return this.registry[k];
  85. }
  86. }
  87. return null;
  88. }
  89. getBundle(name) {
  90. //return this.registry.find((el) => el.name == name);
  91. return this.registry[name];
  92. }
  93. get(obj) {
  94. if(isNaN(obj) == false) {
  95. return this.getBundleById(obj);
  96. }
  97. if(typeof(obj) == "string") {
  98. return this.getBundle(obj);
  99. }
  100. if(obj instanceof BundleContext) {
  101. return obj;
  102. }
  103. return this.getBundleByInstance(obj);
  104. }
  105. load(modPath) {
  106. var modName = solveBundleName(modPath);
  107. this.register({name: modName,bundle:modPath});
  108. }
  109. register(opts) {
  110. var {id,name,bundle,autostart} = opts;
  111. var instance;
  112. var modulePath;
  113. var mod = bundle;
  114. var version = "unknown";
  115. if(this.getBundle(name) != undefined) {
  116. log.error(name + " Already exists");
  117. return;
  118. }
  119. if(typeof(bundle) == "string") {
  120. var modPath= path.resolve(bundle);
  121. var modInfo = getPackageJson(bundle);
  122. try {
  123. mod = require(modPath);
  124. } catch(e) {
  125. console.error(e);
  126. return;
  127. }
  128. if(mod == undefined) {
  129. log.error("Undefined module at: " + bundle);
  130. }
  131. modulePath = modPath;
  132. }
  133. /*if(mod.bundleActivator !=undefined ) {
  134. instance = mod.bundleActivator;
  135. } else {*/
  136. if(mod.bundleFactory != undefined) {
  137. instance = new mod.bundleFactory()
  138. } else {
  139. instance = mod;
  140. }
  141. /*}*/
  142. id = id || this.generateId();
  143. // Simplify this
  144. var context = new BundleContext({
  145. id: id,
  146. name:name,
  147. manager:this,
  148. instance: instance,
  149. modulePath:modulePath,
  150. pkgInfo: modInfo,
  151. version:version
  152. });
  153. this.stat.modulesRegistered++;
  154. this.registry[name] = context;
  155. //this.registry.push(context);
  156. if(autostart != false) {
  157. context.start();
  158. }
  159. }
  160. unregister(obj) {
  161. var context = this.get(obj);
  162. if(context == null) {
  163. log.error("Context not found");
  164. return;
  165. }
  166. context.stop();
  167. // Unload require
  168. if(context.modulePath != undefined) {
  169. clearRequire(context.modulePath);
  170. }
  171. /*this.registry = this.registry.filter((ctx) => {
  172. return ctx != context;
  173. });*/
  174. delete this.registry[context.name];
  175. this.stat.modulesUnRegistered++;
  176. }
  177. // Batch call
  178. call(name,args) {
  179. var [ctxName, method] = name.split(":")
  180. var matchCtx = new RegExp(ctxName);
  181. var fretList = [];
  182. for(var ctxn in this.registry) {
  183. var ctx = this.registry[ctxn];
  184. if(!matchCtx.test(ctx.name)) {
  185. continue;
  186. }
  187. var ret = { name:ctx.name}
  188. if(ctx.instance[method] == undefined || typeof(ctx.instance[method]) != "function") {
  189. ret.result = 0;
  190. ret.error = "method not found";
  191. } else {
  192. ret.result = ctx.instance[method](args);
  193. }
  194. fretList.push(ret);
  195. }
  196. return fretList;
  197. }
  198. with(args,cb) {
  199. var mods = args;
  200. /*if(typeof(args[args.length-1]) != "function") {
  201. throw new Error("Deprecated having callback on argument");
  202. }*/
  203. if(!Array.isArray(args)) {
  204. mods = [args];
  205. }
  206. // Move this to manager directly
  207. var iParam = [];
  208. var ret = { // we could create generic out of this, its not a promise
  209. success: true,
  210. /*do: function(cb) {
  211. if(this.success) cb.apply(cb,iParam);
  212. return this;
  213. },*/
  214. else: function(cb) {
  215. if(!this.success)cb();
  216. return this;
  217. }
  218. }
  219. for(var v of mods) {
  220. if(v == undefined) throw new Error("What?");
  221. var ctx = this.get(v);
  222. if(ctx == null || ctx.info.state != 1) {
  223. ret.success = false;
  224. break;
  225. } // Early bail
  226. iParam.push(ctx.instance);
  227. }
  228. if(ret.success) cb(...iParam);
  229. return ret;
  230. }
  231. }
  232. module.exports = BundleManager;