bundle-manager.js 5.0 KB

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