Browse Source

Creating new independent event manager, with context and channels

Luis Figueiredo 10 năm trước cách đây
mục cha
commit
f0a151f54d
40 tập tin đã thay đổi với 3806 bổ sung và 258 xóa
  1. 5 0
      .tern-project
  2. 1 0
      bundles/hci-config/index.js
  3. 0 0
      bundles/core-installer/hit-counter.js
  4. 0 0
      bundles/core-installer/index.js
  5. 0 0
      bundles/core-loader/clitable.js
  6. 34 50
      bundles/hci-loader/index.js
  7. 17 24
      bundles/hci-monitor/index.js
  8. 8 4
      bundles/hci-shell/index.js
  9. 50 0
      doc/CHANGES.md
  10. 71 0
      doc/TODO.md
  11. 60 0
      doc/performance.js
  12. 1 2
      doc/spec.md
  13. 4 4
      lib/bundle-context.js
  14. 15 56
      lib/bundle-manager.js
  15. 26 0
      lib/utils/benchmark.js
  16. 20 0
      lib/utils/clear-require.js
  17. 18 0
      lib/utils/read-packagejson.js
  18. 29 46
      lib/xevents.js
  19. 1 1
      package.json
  20. BIN
      test/eventual/.index.js.swp
  21. BIN
      test/eventual/doc/.README.md.swp
  22. 9 0
      test/eventual/doc/README.md
  23. 63 0
      test/eventual/index.js
  24. BIN
      test/eventual/lib/.eventual-context.js.swp
  25. BIN
      bundles/hci-shell/.index.js.swp
  26. 128 0
      test/eventual/lib/eventual-context.js
  27. 19 0
      test/eventual/lib/eventual.js
  28. 0 2
      test/hci-usecase/app/index.js
  29. 4 2
      test/hci-usecase/bundles/hci-http-monitor/controllers/status.js
  30. 12 10
      test/hci-usecase/bundles/hci-http-monitor/index.js
  31. 3 4
      test/hci-usecase/bundles/hci-http/index.js
  32. 7 8
      test/hci-usecase/bundles/memory-monitor/index.js
  33. 0 40
      test/hci-usecase/bundles/memory-tester/index.js
  34. 5 3
      test/hci-usecase/bundles/shell-test/index.js
  35. 12 0
      test/hci-usecase/bundles/stress-test/http-test/index.js
  36. 107 0
      test/hci-usecase/bundles/stress-test/index.js
  37. 0 0
      test/hci-usecase/bundles/stress-test/memory-test-unit/.index.js.swo
  38. 2 2
      test/hci-usecase/bundles/memory-tester/memory-test-unit/index.js
  39. 3 0
      test/hci-usecase/siege.err.txt
  40. 3072 0
      test/hci-usecase/siege.out.txt

+ 5 - 0
.tern-project

@@ -0,0 +1,5 @@
+{
+  "plugins": {
+    "node": {}
+  }
+}

+ 1 - 0
bundles/hci-config/index.js

@@ -9,6 +9,7 @@ var configActivator = {
 	test() {
 		console.log("Testing the configuration");
 	}
+	
 }
 
 

bundles/hci-installer/hit-counter.js → bundles/core-installer/hit-counter.js


bundles/hci-installer/index.js → bundles/core-installer/index.js


bundles/hci-loader/clitable.js → bundles/core-loader/clitable.js


+ 34 - 50
bundles/hci-loader/index.js

@@ -16,7 +16,8 @@ var loaderActivator = {
 			return;
 		}
 
-		context.on('hci-installer:installed',() => {
+		// Check this out its wrong!?
+		context.on('core-installer:installed',() => {
 			this.loadBundles();	
 		});
 
@@ -38,9 +39,10 @@ var loaderActivator = {
 		var plugName = dirname;
 		this.manager.register({name:plugName, bundle:plugPath});
 	},
+
 	registerShell(context) {
-		var prefix = context.prefix(".*:cmd");
-		prefix.on('lb',(args) => {
+		var prefix = context.prefix(".*:cmd"); // Any cmd sender
+		prefix.on('lb',(req,res) => {
 			var tbl = [];
 			for(var k in context.manager.registry) {
 				var v = context.manager.registry[k];
@@ -62,71 +64,53 @@ var loaderActivator = {
 				});
 			}
 			tbl = tbl.sort((a,b) => { return a.id - b.id});
-			log.info("\n"+clitable(tbl));
+			res.write(clitable(tbl));
+			//log.info("\n"+clitable(tbl));
 		});
-		prefix.on('stop',(args) => {
-			if(args.length < 2) {
-				log.error("Not enough parameters");
-				return;
+
+		function commonGetBundle(req,res,nargs) {
+			if(req.args < nargs) {
+				res.write("Not enough parameters\n");
+				return null;
 			}
-			var bcontext = context.manager.get(args[1]);
-			if(bcontext == null) {
-				log.error("Bundle not found");
-				return;
+			var bcontext = context.manager.get(req.args[0]);
+			if(!bcontext) {
+				res.write("Bundle not found\n");
 			}
+			return bcontext;
+		}
+		prefix.on('stop',(req,res) => {
+			var bcontext = commonGetBundle(req,res,1);
+			if(bcontext == null) { return; }
 			bcontext.stop();
 
 		});
-		prefix.on('start',(args) => {
-			if(args.length < 2) {
-				log.error("Not enough parameters");
-				return;
-			}
-			var bcontext = context.manager.get(args[1]);
-			if(bcontext == null) {
-				log.error("Bundle not found");
-				return;
-			}
+		prefix.on('start',(req,res) => {
+			var bcontext = commonGetBundle(req,res,1);
+			if(bcontext == null) { return; }
 			bcontext.start();
 		});
 
-		prefix.on('reload',(args) => {
-			if(args.length < 2) {
-				log.error("Not enough parameters");
-				return;
-			}
-			var bcontext = context.manager.get(args[1]);
-			if(bcontext == null) {
-				log.error("Bundle not found");
-				return;
-			}
+		prefix.on('reload',(req,res) => {
+			var bcontext = commonGetBundle(req,res,1);
+			if(bcontext == null) { return; }
 			var modulePath = bcontext.modulePath;
-			
 			context.manager.unregister(bcontext);
 			context.manager.register({id:bcontext.id, name:bcontext.name, bundle:bcontext.modulePath});
 		});
-		prefix.on('unload',(args) => {
-			if(args.length < 2) {
-				log.error("Not enough parameters");
-				return;
-			}
-			var bcontext = context.manager.get(args[1]);
-			if(bcontext == null) {
-				log.error("Bundle not found");
-				return;
-			}
-			var modulePath = bcontext.modulePath;
-			
+		prefix.on('unload',(req,res) => {
+			var bcontext = commonGetBundle(req,res,1);
+			if(bcontext == null) { return; }
 			context.manager.unregister(bcontext);
 		});
 		
-		prefix.on('load',(args) => {
-			if(args.length < 2) {
-				log.error("Not enough parameters");
+		prefix.on('load',(req,res) => {
+			if(req.args.length < 1) {
+				res.write("Not enough parameters\n");
 				return;
 			}
-			log.info("Loading module from: " + process.env.PWD);
-			context.manager.load(process.env.PWD + "/" + args[1]);
+			res.write("Loading module from: " + process.env.PWD +"\n");
+			context.manager.load(process.env.PWD + "/" + req.args[0]);
 			//context.manager.register({name:args[1], bundle:path.resolve(path.join(process.env.PWD, args[2]))});
 		});
 	

+ 17 - 24
bundles/hci-monitor/index.js

@@ -13,7 +13,6 @@ function ntokb(n) {
 }
 
 
-
 var monitor = {
 
 	bundleStart(context) {
@@ -28,35 +27,29 @@ var monitor = {
 	},
 	registerCommands(context) {
 		// listen from command from ourselves
-		context.on('.*:cmd:stat',(args) => {
-			/*var r = {
-				numberOfModules: Object.keys(context.manager.registry).length,
-				numberOfListeners: context.manager.events.listeners.length,
-				listeners: context.manager.events.listeners.map((l) => l.name),
-				memory: process.memoryUsage()
-			}
-			context.with('prettyjson').do((pj) => {
-				log.info(pj.render(r));
-			});*/
-			this.showStat();
+		context.on('.*:cmd:stat',(req,res,e) => {
+			this.showStat(res); // Should pass stdout
 		});
-		context.on('.*:cmd:gc',(args) => {
+		context.on('.*:cmd:gc',(req,res,e) => {
 			gc();
 		});
 	},
-	showStat() {
+	showStat(res) {
 		var context = this.context;
 		var obj = process.memoryUsage();
-		log.info("Memory Rss: " + ntokb(obj.rss)
-							 +" Total: " + ntokb(obj.heapTotal)
-							 +" Used: " + ntokb(obj.heapUsed));
-		log.info(
-			"Modules registered: " + context.manager.stat.modulesRegistered,
-			"Modules UnRegistered: " + context.manager.stat.modulesUnRegistered,
-			"events emitted: " + context.manager.events.stat.emitted,
-			"events called: " + context.manager.events.stat.called,
-			"events tried: " + context.manager.events.stat.tried
-		);
+		if(res == undefined) res = process.stdout;
+
+		res.write("Memory Rss: " + ntokb(obj.rss)
+				 +" Total: " + ntokb(obj.heapTotal)
+				 +" Used: " + ntokb(obj.heapUsed) +"\n");
+
+		res.write("Events: " + context.manager.events.listeners.length + "\n");
+		// Weird?
+		res.write("Modules registered: " + context.manager.stat.modulesRegistered
+						+" Modules UnRegistered: " + context.manager.stat.modulesUnRegistered
+						+" events emitted: " + context.manager.events.stat.emitted
+						+" events called: " + context.manager.events.stat.called
+						+" events tried: " + context.manager.events.stat.tried + "\n");
 	},
 	// Human readable stat
 	getStat() {

+ 8 - 4
bundles/hci-shell/index.js

@@ -17,10 +17,14 @@ var shellActivator = {
 	
 		this.cli.on('line',(cmd) => {
 			if(cmd.length == "") {this.cli.prompt(); return;}
-			var args = cmd.split(/\s/);
-			context.emit("cmd:" + args[0],args).done(()=> {
-				this.cli.prompt();	
-			});
+			var [cmd,...args] = cmd.split(/\s/);
+			var req = { cmd:cmd, args:args }
+			var res = this.stdout;
+			context
+				.emit("cmd:" + cmd,req,res)
+				.done(()=> {
+					this.cli.prompt();	
+				});
 		});
 
 		this.cli.prompt();

+ 50 - 0
doc/CHANGES.md

@@ -0,0 +1,50 @@
+### Event system changed to multiple arguments:
+```
+	context.emit('cmd:ls',1,2,3);
+	context.on('module:ls',(a,b,c,evt) {
+		evt.count
+	});
+```
+
+### Major code cleanup 1, organized some smaller modules
+
+### removed .do callback from with 
+	before:
+```javascript
+	context.with('module1','module2')
+		.do((module1,module2) => {
+			// Do things with modules
+		}).else(() => {
+			// Module does not exists
+		});
+```
+	after:
+```javascript
+	context.with('module1','module2',(module) => {
+		// Do things with modules
+	}).else(() => {
+		// Modules does not exists
+	});
+```
+	
+### Created method call wrapper
+	before:
+```javascript
+		context.with('module',(module) => {
+			module.method(arg);
+		});
+```
+	after:
+```javascript
+	// String is regular expression
+	var ret = context.call('module:method',arg);
+	var ret2 = context.call('(module1|module2):method',arg);
+```
+	ret result:
+	ret: [ {name:'module1', result: ...}],
+	ret2: [ {name:'module1', result: ...}, {name: 'module2', result:...}]
+
+although it only works with flat methods (i.e methods exported in module.exports)
+
+	
+	

+ 71 - 0
doc/TODO.md

@@ -0,0 +1,71 @@
+
+###
+* Possible bug, toggleOn if a module is registered, and unloaded, toggleOn remains?
+* 
+* A bug seems to exist that if module is stopped it keeps emitting events
+		find a way to prevent events from being emitted if stopped
+
+* Global parameter conf key
+	set listening port for http, public path register
+	figure if use a public key set on manager or a simple module to handle configuration
+	create a Load path method
+
+* Consider returning a events on context as in
+	context.events.on('hci-http');
+		instead of
+	context.on('hci-http');
+	
+	context.events.on('hci-http:start');
+	context.manager.with('pak1','pak2').do().else();
+
+* Reduce the Context complexity having proper names 
+
+* consider the command context for proper commandEntry, check restcli how its done
+* Consider creating a contextual link for the possibility of modules add data to other modules
+	but when one of those is removed it wont leave any traces, either by name (persistent) or ctx
+
+### Done
+* transform this to a module and separate the app, as in remove index.js from starting manager
+* Create method to solve module name:
+	manager.load(path);
+	if( file == 'index.js') {
+		pick last directory
+	} else {
+		path.basename(file,".js"); file without extension
+	}
+
+* Analyse request as a express:
+created as regular expressions and matches
+```
+context.on('package:req:(.*)',(arr,e) => {
+	e.match;
+});
+```
+'core-shell', emits 
+core-shell:cmd:ls
+context.on(context, "cmd:{cmd}",(e) => {
+	e.param.cmd
+})
+
+
+* change start and stop to bundleStart and bundleStop, this way we can mix with other modules with methods
+named start and stop respectively
+* Do a performance check on the eventemitter, and check the profiler for eventual leaks
+		Clearing require still seems to be problematic still checking
+* Separate loader commands into loader module that will use the core-cli
+* Add an else to manager.with or an err parameter at start of the 
+```
+	context.with(["prettyjson","bullshit"], function(err,prettyjson,bullshit) {
+		if(err) return;
+	});
+```
+solution:
+created a special return type:
+context.width("entry","entry2").do(()=> {
+		dependencies solved
+}).else(()=> {
+		missing dependency
+});
+
+
+

+ 60 - 0
doc/performance.js

@@ -0,0 +1,60 @@
+Event manager - revamp
+
+	//Current status:
+	//flat event listener
+	var ctx = {};
+	function callback() {};	
+	// ctx allows for easy removal of several events
+	events.on(ctx,'core-shell:cmd:ls',callback);
+	events.on(ctx,'hci-http:req:/monitor/?(.*)',callback);
+
+	events.listeners = [
+			(Listener): { context,'core-shell:cmd:ls',callback,order}
+			(Listener): { context,'hci-http:req:/monitor/?(.*)',callback,order}
+	];
+
+
+	//So when an event is fired, the listeners will be iterated and checked 
+	context.emit(name,...args);
+	events.emit('core-shell:cmd:ls',1,2);
+	
+// Contextual idea
+//
+	var ctx = events.createContext();
+	ctx.on('core-shell:cmd:ls',callback);
+
+
+
+
+
+
+
+
+
+
+
+
+
+10000 listeners testing cases
+
+
+Goal:
+	Maintain listeners even if module is gone
+	improve performance on listener matching
+
+
+//
+var subscription = context.subscribe('hci-http')
+subscription.on('get:/monitor/?(.*)');
+subscription.on('post:/monitor');
+
+
+
+
+
+
+
+Right now if we receive a request from /monitor/api we broadcast it to anyone that's listening for hci-http
+meaning with 
+
+as of hci-http-monitor

+ 1 - 2
doc/spec.md

@@ -1,5 +1,3 @@
-
-
 # Request response idea:
 
 Module gets loaded, and request 
@@ -37,5 +35,6 @@ Conclusion:
 		context.with("clock").do((clock) => {
 			clock.time();
 		})
+		var ret = context.call("module-name:method",1,2,3);
 
 

+ 4 - 4
lib/bundle-context.js

@@ -82,7 +82,7 @@ class BundleContext /*extends XEventEmitter*/ {
 			log.warn("Instance is turned on trying to turn off first");
 			this.stop();
 		}
-		process.nextTick(() => {
+		//process.nextTick(() => {
 			try {
 				this._bundleContext.instanceStart(this);
 			} catch(e) {
@@ -91,7 +91,7 @@ class BundleContext /*extends XEventEmitter*/ {
 			this.manager.events.toggleOn(this.name,this.instance);
 			this.info.state = 1;
 			this.info.startDate = new Date();
-		});
+		//});
 	}
 
 	stop() {
@@ -115,8 +115,8 @@ class BundleContext /*extends XEventEmitter*/ {
 	removeListener(ename) {
 		this.manager.events.removeListener(this,ename);
 	}
-	emit(ename,arg,cb) {
-		return this.manager.events.emit(this.name+":"+ename,arg,cb);
+	emit(ename,...args) {
+		return this.manager.events.emit(this.name+":"+ename,...args);
 	}
 	toggleOn(ename,arg) {
 		this.manager.events.toggleOn(this.name+":"+ename,arg);

+ 15 - 56
lib/bundle-manager.js

@@ -1,47 +1,13 @@
-var log = require('hlogger').createLogger('bundle-manager');
-var path = require('path');
-var fs = require('fs');
-
-var BundleContext = require('./bundle-context');
-var XEventEmitter = require('./xevents');
-
-
-
-// Registry can be part of the package.json
-//
-
-function clearRequire(mod,resolve) {
-	
-	var modr = (resolve==undefined)?require.resolve(mod):mod;
-	var cache = require.cache[modr];
+var path 						= require('path'),
+		fs 							= require('fs'),
+		XEventEmitter 	= require('./utils/xevents'),
+		clearRequire 		= require('./utils/clear-require'),
+		readPackageJson = require('./utils/read-packagejson');
 
-	// Recursivelly unload children too
-	var cc = [...cache.children];
-	cc.forEach((c) => {
-		clearRequire(c.id,false);
-	});
-
-
-	var ci;
-	if((ci = cache.parent.children.indexOf(cache)) != -1) {
-		cache.parent.children.splice(ci,1);
-	}
-	delete require.cache[modr];
-}
+var	BundleContext = require('./bundle-context');
+var log = require('hlogger').createLogger('bundle-manager');
 
-function getPackageJson(inpath) {
-	var pathParts = path.resolve(inpath).split(path.sep);
 
-	while(pathParts.length > 0) {
-		var fullPath = path.join("/", path.join.apply(path,pathParts),"/package.json"); 
-		if(fs.existsSync(fullPath)) {
-			return JSON.parse(fs.readFileSync(fullPath));
-		}
-		pathParts.pop();
-	}
-	return null;
-	
-}
 function solveBundleName(inpath) {
 	var name = path.basename(inpath);
 	if(name == "index.js") {
@@ -58,7 +24,7 @@ class BundleManager {
 	constructor(opts) {
 		// Control events	
 		this.config = opts || {};
-
+		
 		this.events = new XEventEmitter();
 		// Global channel
 		this.globalEvents = new XEventEmitter();
@@ -138,8 +104,8 @@ class BundleManager {
 
 		if(typeof(bundle) == "string") {
 			var modPath= path.resolve(bundle);
+			var modInfo = readPackageJson(bundle);
 
-			var modInfo = getPackageJson(bundle);
 			try {
 				mod = require(modPath);
 			} catch(e) {
@@ -153,15 +119,11 @@ class BundleManager {
 		} 
 		
 
-		/*if(mod.bundleActivator !=undefined ) {
-			instance = mod.bundleActivator;
-		} else {*/
-			if(mod.bundleFactory != undefined) {
-				instance = new mod.bundleFactory()
-			} else {
-				instance = mod;
-			}
-		/*}*/
+		if(mod.bundleFactory != undefined) {
+			instance = new mod.bundleFactory()
+		} else {
+			instance = mod;
+		}
 
 		id = id || this.generateId();
 		// Simplify this
@@ -180,17 +142,14 @@ class BundleManager {
 		this.registry[name] = context;
 		//this.registry.push(context);
 
-
 		if(autostart != false) {
 			context.start();
 		}
-
 	}
 	unregister(obj) {
-
 		var context = this.get(obj);
 		if(context == null) {
-			log.error("Context not found");
+			log.error("Context not found for: " + obj);
 			return;
 		}
 

+ 26 - 0
lib/utils/benchmark.js

@@ -0,0 +1,26 @@
+
+class Benchmark {
+
+	start() {
+		this.tstart = process.hrtime();
+		return this;
+	}
+	mark() {
+		this.diff = process.hrtime(this.tstart);
+		this.tstart = process.hrtime();
+		
+		return this;
+	}
+	toString() {
+		var msdiff = ( this.diff[0] * 1000000 + this.diff[1] / 1000 ) /1000;
+		if(msdiff > 1000) 
+			return  (msdiff / 1000).toFixed(2) + "s";
+		else {
+			return msdiff.toFixed(2) +"ms";
+		}
+		return "NaN";
+	}
+}
+
+
+module.exports = Benchmark;

+ 20 - 0
lib/utils/clear-require.js

@@ -0,0 +1,20 @@
+module.exports = function clearRequire(mod,resolve) {
+	
+	var modr = (resolve==undefined)?require.resolve(mod):mod;
+	var cache = require.cache[modr];
+
+	// Recursivelly unload children too
+	var cc = [...cache.children];
+	cc.forEach((c) => {
+		clearRequire(c.id,false);
+	});
+
+
+	var ci;
+	if((ci = cache.parent.children.indexOf(cache)) != -1) {
+		cache.parent.children.splice(ci,1);
+	}
+	delete require.cache[modr];
+}
+
+

+ 18 - 0
lib/utils/read-packagejson.js

@@ -0,0 +1,18 @@
+var path = require('path');
+var fs = require('fs');
+
+
+module.exports = function readPackageJson(inpath) {
+	var pathParts = path.resolve(inpath).split(path.sep);
+
+	while(pathParts.length > 0) {
+		var fullPath = path.join("/", path.join.apply(path,pathParts),"/package.json"); 
+		if(fs.existsSync(fullPath)) {
+			return JSON.parse(fs.readFileSync(fullPath));
+		}
+		pathParts.pop();
+	}
+	return null;
+	
+}
+

+ 29 - 46
lib/xevents.js

@@ -8,7 +8,7 @@ class XPrefix {
 		this.prefix = prefix || ""; 
 	}
 	on(name,cb) {
-		this.event.on(this.ctx,this.prefix + ":" + name,cb);
+		this.event.on(this.ctx,this.prefix + ":" + name, cb);
 		return this;
 	}
 }
@@ -47,6 +47,7 @@ class XEventEmitter {
 
 		//this.listeners = new Map();
 		this.listeners = [];
+
 		this.auto = {};
 		this.stat = {
 			emitted:0,
@@ -62,27 +63,7 @@ class XEventEmitter {
 		} else {
 			[ctx,evtName,cb,order] = args;
 		}
-
-		// Manipulate the evtName as in:
-		//
-	
-		/*var re = /[^\\]{(.*?)}/;
-		var paramMap = [];
-		do {	
-			var res = evtName.match(re)
-			if(res != null) {
-				paramMap.push(res[1]);
-				// The match + first char of the match
-				evtName = evtName.replace(re,res[0][0]+"(.*)");
-			}
-		}while(res != null);
-		console.log("Resulting evtName: " + evtName);*/
-
 		var listener = new XEventListener(ctx,evtName,cb,order);
-		//listener.paramMap = paramMap;
-
-
-
 		this.listeners.push(listener);
 		//this.listeners.push(entry);
 	
@@ -91,7 +72,8 @@ class XEventEmitter {
 		Object.keys(this.auto).map((v) => {
 			var match = v.match(re);
 			if(v.match(re)) {
-				this.process([{match:match,listener:listener}],this.auto[v]);
+				var tocall = [{match:match,listener:listener}];
+				this.process(tocall,this.auto[v]); // No callback
 			}
 		});
 	
@@ -114,18 +96,21 @@ class XEventEmitter {
 	}
 	
 	// 1 arg only
-	toggleOn(name,arg) {
-		this.auto[name]= arg;
-		this.emit(name,arg);
+	toggleOn(name,...args) {
+		// Should be contextual if we remove context toggle will remain
+		this.auto[name] = args; // should store array
+		this.emit(name,...args);
 	}
 
 	toggleOff(name) {
 		delete this.auto[name];
 	}
 
-	process(tocall,args,cb) {
+	process(tocall,nargs,cb) {
 		var self=this;
-		
+	
+		var args = nargs || [];	 // or empty
+		// Common for all for now	
 		var evt = new XEvent(next,args);
 
 		this.stat.emitted++;
@@ -145,7 +130,7 @@ class XEventEmitter {
 			// Stack overflow risk? maybe put this in process.tick();
 			var v = iter.next();
 			if(v.done) {
-				if(cb != undefined) cb(evt);
+				if(cb != undefined) cb(evt); // Done callback with evt only
 				return;
 			}
 			// Create evt here with some global event chaining iteraction	
@@ -154,7 +139,7 @@ class XEventEmitter {
 				evt.match = entry.match;
 				evt.args = args;
 				self.stat.called++;
-				entry.listener.callback(args,evt);
+				entry.listener.callback(...args,evt);
 				evt.count++;
 			}catch(e) {
 				console.log(e);
@@ -171,6 +156,7 @@ class XEventEmitter {
 		var ret = [];
 		this.listeners.forEach((v) => {
 			var re = new RegExp( "^" + v.name+ "$" );
+			//var re = v.name;
 			var matchres = name.match(re);
 			if(matchres && (ctx == undefined || ctx == v.ctx) ) {
 				ret.push({
@@ -182,35 +168,33 @@ class XEventEmitter {
 		return ret;
 	}
 
-	emit(name,args) {
+	// Multiple argument
+	// Should work like emit("event",1,2,3,4,5);
+	emit(name,...args) {
 		this.stat.tried++;
 		var tocall = this.search(name);
+
+		var evt,callback,dotrigger = 0;
+		function trigger(ievt) {
+			evt = ievt; // Will call
+			if(callback)callback(evt);
+		}
 		// Mem leak???
 		var ret = {
-			param: null,
-			callback: null,
-			// Setup callback
-			trigger: function(args,evt) {
-				this.param = [args,evt];
-				this.dotrigger = 1;
-				if(this.callback) {this.callback(args,evt);}
-			},
 			done: function(cb) {
-				if(this.dotrigger) {
-					cb(...param); 
-				}
-				this.callback = cb;
+				if(evt) { cb(evt); }
+				callback = cb;
 			}
 		}
 		// Test
-		this.process(tocall,args,(evt) => { ret.trigger(args,evt) });
+		this.process(tocall,args, trigger );
 		return ret ;
 	}
 	// Why?
-	emitTo(ctx,name,args) {
+	/*emitTo(ctx,name,args) {
 		var tocall = this.search(name,ctx)
 		this.process(tocall,arg,cb);
-	}
+	}*/
 	// Alias
 	on(ctx,match,cb) {
 		this.addListener(ctx,match,cb);
@@ -220,7 +204,6 @@ class XEventEmitter {
 		this.addListener(ctx,match,cb,1000);
 		return this;
 	}
-
 	prefix(ctx,prefix) {
 		return new XPrefix(ctx,this,prefix);
 	}

+ 1 - 1
package.json

@@ -8,7 +8,7 @@
     "email": "luisf@hexasoftware.com"
   },
   "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "test": "cd test/hci-usecase; node app"
   },
   "keywords": [
     "osgi",

BIN
test/eventual/.index.js.swp


BIN
test/eventual/doc/.README.md.swp


+ 9 - 0
test/eventual/doc/README.md

@@ -0,0 +1,9 @@
+
+
+// ctx, channels, etc
+
+
+Each Context would have a default channel named after him
+
+// Targeted events:
+each context could have contexts can only fire to them selfs

+ 63 - 0
test/eventual/index.js

@@ -0,0 +1,63 @@
+const Eventual = require('./lib/eventual.js');
+const Benchmark = require('../../lib/utils/benchmark');
+
+function makeid()
+{
+    var text = "";
+    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+    for( var i=0; i < 5; i++ )
+        text += possible.charAt(Math.floor(Math.random() * possible.length));
+
+    return text;
+}
+
+
+
+
+var events = new Eventual();
+var cevt= events.createContext();
+var ctx2 = events.createContext();
+
+function stressEvents() {
+	// 1 million
+	var ncount = 1000000;
+	console.log("Adding",ncount,"events");
+	for(var i = 0;i<ncount;i++) {
+		// Create random contexts too
+		var evtName = makeid() + ":test";
+		cevt.on(evtName,(a,b,c,evt) => {
+			console.log("evt: " , evt);
+			console.log("Args: " + a,b,c);	
+		});
+	}
+	// Second context
+	console.log("Adding events to second context");
+	for(var i = 0;i<ncount;i++) {
+		ctx2.on('test:test',(a,b,c) => { });
+	};
+}
+
+stressEvents();
+
+ctx2.on('c1:ping',() => {
+	console.log("Pong from ctx2");
+});
+
+var pingbm = new Benchmark();
+var pctx = events.createContext();
+pctx .on('c1:ping',() => {
+		console.log("Pong: " + pingbm.mark(), "Contexts:",events._events.ctx.length);
+	})
+
+var test = 0;
+setInterval(() => {
+	pingbm.start();
+	console.log("Sending ping");
+	pctx.broadcast('c1:ping');
+	if( test > 5) {
+		cevt.destroy();
+		ctx2.destroy();
+	}
+	test++;
+},1000);

BIN
test/eventual/lib/.eventual-context.js.swp


BIN
bundles/hci-shell/.index.js.swp


+ 128 - 0
test/eventual/lib/eventual-context.js

@@ -0,0 +1,128 @@
+
+/** Concept each channel can have child channels ??**/
+const chs = ':';
+
+
+class EventualChannel {
+
+	constructor(name,parent) {
+		this.name = name;
+		this.parent = parent;
+		
+		this.callbacks = [], // Local callbacks
+		this.childs = {}
+		this.listeners = [];
+	}
+
+	// createChannel based on name
+	//
+	getChannel(name,create) { // Should return listeners to mess up
+		var [child, ...sub]= name.split(chs);
+		if(this.childs[child] == undefined) {
+			if(create==undefined) return []; // Maybe empty?
+			this.childs[child] = new EventualChannel(this);
+		}
+		if(sub.length != 0) {	 // We could do this without recursion
+			return this.childs[child].getChannel(sub.join(chs),create);
+		}
+		return this.childs[child].listeners;
+	}
+	getChannel2(name,create) {
+		var nodes = name.split(chs);
+		var cur = this;
+		nodes.forEach((v) => {
+			if( cur.childs[v] == undefined) {
+				if(create == undefined) return []; // Empty
+				cur.childs[v] = new EventualChannel(name,this);
+			}
+			cur = cur.childs[v]; // next;
+		});
+
+		return cur.listeners;
+	}
+	search(name) {
+		return this.getChannel2(name);
+	}
+	// Registers
+	on(name,cb) {
+		var listeners = this.getChannel2(name,true);
+		listeners.push(cb);
+	}
+	emit(name,args) {
+		var listeners = this.search(name);
+		listeners.forEach((l) => {
+			l(args);
+		});
+
+		// Something to process callback as the xevents
+	}
+
+}
+
+class EventualContext2 extends EventualChannel {
+		constructor(eventual) {
+			super();
+			this.eventual = eventual;
+		}
+		broadcast(name,...args) {
+			this.eventual._events.ctx.forEach((c) => {
+				c.emit(name,...args);
+			});
+		}
+
+		destroy() {
+			this.childs = null;
+			this.listeners = null;
+			var i = this.eventual._events.ctx.indexOf(this);
+			if(i!=-1) this.eventual._events.ctx.splice(i,1);
+		}
+}
+
+
+class EventualContext extends EventualChannel {
+	constructor(eventual) {
+		super();
+		this.eventual = eventual;
+		// Contextual listeners, it will invalidate regexp?
+		this.listeners = [];
+	}
+	process(tocall,args,donecb) {
+		tocall.forEach((l) => {
+			l.callback(...args);
+		});
+	}
+	broadcast(name,...args) {
+		this.eventual._events.ctx.forEach((c) => {
+			c.emit(name,...args);
+		});
+	}
+	/** flat model **/
+	on(name,cb) {
+		// Any listener will be placed here
+		this.listeners.push({
+			name: name,
+			callback:cb
+		});
+		return this;
+	}
+	emit(name,...args) {
+		// We need to go trought all contexts in Eventual
+		var tocall = [];
+		this.eventual._events.ctx.forEach((c) => {
+			c.listeners.forEach((l) =>  {
+				if(l.name == name) {
+					tocall.push(l);
+				}
+			})
+		});
+		this.process(tocall,name,args);
+	}/**/
+
+	destroy() {
+		var i = this.eventual._events.ctx.indexOf(this);
+		if(i!=-1) this.eventual._events.ctx.splice(i,1);
+		this.listeners = null;
+	}
+}
+
+module.exports = EventualContext;

+ 19 - 0
test/eventual/lib/eventual.js

@@ -0,0 +1,19 @@
+const EventualContext = require('./eventual-context');
+
+
+class Eventual {
+	constructor() {
+		this._events = {
+			ctx: []
+		};
+	}
+
+	createContext() {
+		var ret = new EventualContext(this);
+		this._events.ctx.push(ret);
+		return ret;
+	}
+}
+
+module.exports = Eventual;
+

+ 0 - 2
test/hci-usecase/app/index.js

@@ -1,7 +1,5 @@
 var BundleManager = require('../../../');
 
-
 var manager = new BundleManager({runPath: __dirname + "/../bundles"});
 
-
 manager.loadDefaultBundles();

+ 4 - 2
test/hci-usecase/bundles/hci-http-monitor/controllers/status.js

@@ -7,8 +7,10 @@ module.exports = {
 		// Wait call?
 		//
 		// Get first result	
-		var [ret] = context.manager.call("hci-monitor:getStat");
-		done(ret.result);
+		var [ret] = context.manager.call("core-monitor:getStat");
+		if(ret != undefined) {
+			done(ret.result);
+		}	
 		
 	},
 	bundles(context,done) {

+ 12 - 10
test/hci-usecase/bundles/hci-http-monitor/index.js

@@ -1,7 +1,8 @@
+var mime = require('mime-types'),
+		path = require('path'),
+		fs = require('fs');
+
 var log = require('hlogger').createLogger('hci-http-test');
-var mime = require('mime-types');
-var path = require('path');
-var fs = require('fs');
 
 function loadControllers() {
 	var targetPath = __dirname + "/controllers";
@@ -20,10 +21,13 @@ function loadControllers() {
 function bundleStart(context) {
 	var controllers = loadControllers();
 
-	var channel = context.prefix('hci-http:req');
-
-	channel.on('/monitor/api/(.*)',([req,res],e) => {
+	context.on('hci-http:stop',() => {
+		console.log("Stopping http monitor");
+		context.stop();
+	});
 
+	var channel = context.prefix('hci-http:req');
+	channel.on('/monitor/api/(.*)',(req,res,e)=> {
 		var [conName,conMethod = 'index',...params] = e.match[1].split(/\//);	
 		var con = controllers[conName];
 		if(con == undefined || con[conMethod] == undefined) {
@@ -44,7 +48,7 @@ function bundleStart(context) {
 	});
 
 	// Static server for path / monitor
-	channel.on('/monitor/?(.*)',([req,res],e) => {
+	channel.on('/monitor/?(.*)',(req,res,e) => {
 		if(e.count != 0) { // Something else executed
 			return;
 		}
@@ -66,16 +70,14 @@ function bundleStart(context) {
 			res.statusCode = 404;
 			return;
 		}
-
 		// MimeTypes??
-		
 		var mimeType = mime.lookup(fullPath);
 		res.writeHead(200,{'Content-type':mimeType});
 		var s = fs.createReadStream(fullPath);
 		s.pipe(res);
 	});
 
-
 }
 
+
 module.exports.bundleStart = bundleStart;

+ 3 - 4
test/hci-usecase/bundles/hci-http/index.js

@@ -2,11 +2,11 @@ const http = require('http');
 const log = require('hlogger').createLogger('hci-http');
 
 var activator = {
+
 	start(context) {
 		this.server = http.createServer((req,res) => {
-
-			context.emit('req:'+req.url,[req,res]) //??
-						 .done(([req,res],evt) => {
+			context.emit('req:'+req.url,req,res) //??
+						 .done((evt) => {
 							 // Array of results
 							 if(res.statusCode == 404 || evt.count == 0) {
 									res.writeHead(404,"Not found");
@@ -21,7 +21,6 @@ var activator = {
 		this.server.listen(3500);
 		
 		this.clients = {};
-
 		this.server.on('connection',(conn) => {
 			var key = conn.remoteAddress + ':' + conn.remotePort;
 			this.clients[key] = conn;

+ 7 - 8
test/hci-usecase/bundles/memory-monitor/index.js

@@ -2,30 +2,29 @@ var log = require('hlogger').createLogger('memory-monitor');
 
 var activator = {
 	start(context) {
-
 		context
 			.prefix('core-shell:cmd')
-			.on('memory',([,sub,time]) => {
-				if(sub == "start") {
+			.on('memory',(req,res) => {
+				if(req.cmd == "start") {
 					var itime = time || itime;
 					context.setInterval(() => {
-						context.call('core-monitor:showStat');
+						context.call('core-monitor:showStat',res);
 					},itime);
 				}
-			}).on('cache',([,search])=> {
+			}).on('cache',(req,res)=> {
 				if(search) {
 					Object.keys(require.cache).forEach((v) => {
 						var re = new RegExp(search);
 						if(re.test(v)) {
-							log.info("Found module: " + v + " parent: " , v.parent);
+							res.write("Found module: " + v + " parent: " , v.parent );
 						}
 						var req = require.cache[v];
 						if(req.parent && re.test(req.parent.id)	) {
-							log.info("Found in parent: " + v + " - " + req.parent.id);
+							res.write("Found in parent: " + v + " - " + req.parent.id);
 						}
 						req.children.forEach((c) => {
 							if(re.test(c.id)) {
-								log.info("Found on child of: " +v + " - " + c.id);
+								res.write("Found on child of: " +v + " - " + c.id);
 							}
 						});
 					});

+ 0 - 40
test/hci-usecase/bundles/memory-tester/index.js

@@ -1,40 +0,0 @@
-var log = require('hlogger').createLogger('monitor-test');
-
-function ntokb(n) {
-	var suffix = ["B","KB","MB","GB"];
-	var si = 0;
-	while( n> 1024 && si< suffix.length) {
-		n/=1024;
-		si++;
-	}
-	return n.toFixed(2) + " " +suffix[si];
-}
-
-
-var activator = {
-	start(context) {
-		context.on('core-shell:cmd:test',() => {
-			this.startTest(context);
-		});
-	},
-	startTest(context) {
-		log.info("Starting memory-test");
-		var btoggle = false;
-
-		context.setInterval(function() {
-			if(btoggle == false ) {
-				context.manager.load(__dirname + "./memory-test-unit");
-			}else {
-				context.manager.unregister("memory-test-unit");
-			}
-			btoggle = !btoggle;
-		},500);
-		
-	},
-	stop(context) {
-	}
-}
-
-module.exports.bundleActivator = activator;
-
-

+ 5 - 3
test/hci-usecase/bundles/shell-test/index.js

@@ -6,12 +6,14 @@ function bundleStart(context) {
 	log.info("Command provider installed");
 
 	// Create a bash and transport commands
-	context.after('.*:cmd:.*',(args,e) => {
+	context.after('.*:cmd:.*',(req,res,e) => {
 		if(e.count >0) return;
 
 		e.wait();	
-		var proc = child.exec(args.join(" "));
-		proc.stdout.pipe(process.stdout);
+		var shcmd = req.cmd + " " + req.args.join(" ");
+		var proc = child.exec(shcmd);
+		res.write("\n");
+		proc.stdout.pipe(res);
 		proc.stderr.pipe(process.stderr);
 
 		proc.on('close',function() {

+ 12 - 0
test/hci-usecase/bundles/stress-test/http-test/index.js

@@ -0,0 +1,12 @@
+
+var activator = {
+	start(context) {
+		context.on('hci-http:req:/test',(req,res) => {
+			res.end('Hello word');
+		});
+	}
+}
+
+
+
+module.exports.bundleActivator = activator;

+ 107 - 0
test/hci-usecase/bundles/stress-test/index.js

@@ -0,0 +1,107 @@
+var child = require('child_process');
+var fs = require('fs');
+
+function hrToStr(diff) {
+		var ldateDiff = ( diff[0] * 1000000 + diff[1] / 1000 ) / 1000;
+		var ldateDiffStr = "";
+		if(ldateDiff > 1000) 
+			ldateDiffStr = (ldateDiff / 1000).toFixed(2) + "s";
+		else {
+			ldateDiffStr = ldateDiff.toFixed(2) +"ms";
+		}
+	return ldateDiffStr;
+}
+
+module.exports.bundleActivator = {
+
+	// Couple of tests
+	start(context) {
+		context.manager.load(__dirname + "/http-test");
+		this.context = context;
+		context.prefix('core-shell:cmd')
+				.on('test',(req,res,e) => {
+					var cmds = ["events","cpu","mem","ping","siege"]; 
+					var [subcmd,...args] = req.args;
+					if(cmds.indexOf(subcmd) == -1) {
+						res.write("Wrong\n");	
+						res.write("Available tests:" , cmds + "\n");
+						return;
+					};
+
+					this[subcmd](req,res,e);
+				});
+
+		var lastPing; 
+		context.on('stress-test:ping',() => {
+			lastPing = process.hrtime();
+			context.emit('pong')
+		});
+		context.on('stress-test:pong',() => {
+			var now = hrToStr(process.hrtime(lastPing));
+			console.log("Time taken to poing: " + now);
+		});
+	},
+	ping() {
+		this.context.emit('ping');
+	},
+	events(req,res,e) {
+		var count = 1000000;
+		for(;count;count--) {
+			this.context.on('stress-test:test:event',() => { var arr = new Array(5);	});
+		}
+	},
+
+	mem(req,res,e) {
+		var context = this.context;
+
+		var ntime = 1000; // 1 second
+		var mark = new Date();
+		var now;
+		this.count=0;
+		do {
+			this.context.manager.load(__dirname + "/memory-test-unit");
+			this.context.manager.unregister("memory-test-unit");
+			this.count++;
+			now = new Date();
+		} while((now - mark)< ntime); // 500 ms
+
+		res.write("Operations: " + this.count + "\n");
+
+		// Loop with tick
+		//
+		if(arg == "start") {
+			var toggle = false;
+			this.ci = context.setInterval(() => {
+				if(toggle) {
+					this.context.manager.load(__dirname + "/memory-test-unit");
+				}else {
+					this.context.manager.unregister("memory-test-unit");
+				}
+			},5000);
+		}
+	},
+
+	siege(req,res,e) {
+	
+		e.wait();
+		res.write("Doing 10s test!\n");
+		var proc = child.exec('siege -t10s http://127.0.0.1:3500/test');
+		res.write("\n");	
+		//proc.stdout.pipe(res);
+		proc.stderr.pipe(process.stderr);
+		
+		proc.on('close',() => {
+			res.write("cmd done\n");
+			e.done();
+		});
+	}
+
+
+
+
+
+
+
+
+
+}

test/hci-usecase/bundles/memory-tester/memory-test-unit/.index.js.swo → test/hci-usecase/bundles/stress-test/memory-test-unit/.index.js.swo


+ 2 - 2
test/hci-usecase/bundles/memory-tester/memory-test-unit/index.js

@@ -2,7 +2,7 @@ var log = require('hlogger').createLogger('jspak.Config');
 
 class BigObject {
 	constructor() {
-		this.temp = new Array(1e5);
+		this.temp = new Array(1e3);
 	}
 }
 
@@ -22,7 +22,7 @@ var activator = {
 			this.increaseMemory();
 		}
 
-		context.on('core-shell:cmd:ls',(cmd,e) => {
+		context.on('core-shell:cmd:ls',(req,res,e) => {
 			e.wait();
 			context.with(["mod1","prettyjson"],(mod1,pj) => {
 				log.info("Doing ls, we retrieved stuff");

+ 3 - 0
test/hci-usecase/siege.err.txt

@@ -0,0 +1,3 @@
+** SIEGE 4.0.2
+** Preparing 25 concurrent users for battle.
+The server is now under siege...

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 3072 - 0
test/hci-usecase/siege.out.txt