瀏覽代碼

Creating new independent event manager, with context and channels

Luis Figueiredo 8 年之前
父節點
當前提交
f0a151f54d
共有 40 個文件被更改,包括 3806 次插入258 次删除
  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. 二進制
      test/eventual/.index.js.swp
  21. 二進制
      test/eventual/doc/.README.md.swp
  22. 9 0
      test/eventual/doc/README.md
  23. 63 0
      test/eventual/index.js
  24. 二進制
      test/eventual/lib/.eventual-context.js.swp
  25. 二進制
      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",

二進制
test/eventual/.index.js.swp


二進制
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);

二進制
test/eventual/lib/.eventual-context.js.swp


二進制
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...

文件差異過大導致無法顯示
+ 3072 - 0
test/hci-usecase/siege.out.txt