diff --git a/coffee/http-listener.coffee b/coffee/http-listener.coffee index 6a81008..271f5e1 100644 --- a/coffee/http-listener.coffee +++ b/coffee/http-listener.coffee @@ -34,6 +34,7 @@ Initializes the HTTP listener and its request handler. ### exports = module.exports = ( args ) => @log = args.logger + @shutDownSystem = args[ 'shutdown-function' ] requestHandler args initRouting args[ 'http-port' ] module.exports @@ -101,17 +102,6 @@ initRouting = ( port ) => @shutDownSystem() -### -Adds the shutdown handler to the admin commands. - -@param {function} fshutDown -@public addShutdownHandler( *fShutDown* ) -### -exports.addShutdownHandler = ( fShutDown ) => - @shutDownSystem = fShutDown - requestHandler.addShutdownHandler fShutDown - - # ### # Shuts down the http listener. # There's no way to gracefully stop express from running, thus we diff --git a/coffee/logging.coffee b/coffee/logging.coffee index d8ab66e..35a73c1 100644 --- a/coffee/logging.coffee +++ b/coffee/logging.coffee @@ -26,9 +26,12 @@ Returns a bunyan logger according to the given arguments. ### exports.getLogger = ( args ) => emptylog = + trace: () -> + debug: () -> info: () -> warn: () -> error: () -> + fatal: () -> # `args` holds the configuration settings for the logging, see either CLI arguments # in [webapi-eca](webapi-eca.html) or the configuration parameters in [config](config.html). args = args ? {} diff --git a/coffee/request-handler.coffee b/coffee/request-handler.coffee index fb15e78..bb8c58e 100644 --- a/coffee/request-handler.coffee +++ b/coffee/request-handler.coffee @@ -14,9 +14,6 @@ Request Handler # - [Persistence](persistence.html) db = require './persistence' -# - [Module Manager](module-manager.html) -mm = require './module-manager' - # - Node.js Modules: [fs](http://nodejs.org/api/fs.html), # [path](http://nodejs.org/api/path.html) and # [querystring](http://nodejs.org/api/querystring.html) @@ -30,36 +27,19 @@ mustache = require 'mustache' crypto = require 'crypto-js' # Prepare the user command handlers which are invoked via HTTP requests. -objUserCmds = - 'store_action': mm.storeActionModule - 'get_actionmodules': mm.getAllActionModules - 'store_event': mm.storeEventModule - 'get_eventmodules': mm.getAllEventModules - 'store_rule': mm.storeRule -objAdminCmds = {} - -exports = module.exports = ( args ) -> +exports = module.exports = ( args ) => log = args.logger + @userRequestHandler = args[ 'request-service' ] + @objAdminCmds = + shutdown: ( args, answerHandler ) -> + answerHandler.answerSuccess 'Shutting down... BYE!' + setTimeout args[ 'shutdown-function' ], 500 db args - mm args - mm.addDBLink db users = JSON.parse fs.readFileSync path.resolve __dirname, '..', 'config', 'users.json' db.storeUser user for user in users module.exports -### -This allows the parent to add the shutdown handler. -The shutdown function will be called if the admin command shutdown is issued. - -@public addShutdownHandler( *fShutdown* ) -@param {function} fShutdown -### -exports.addShutdownHandler = ( fShutdown ) => - objAdminCmds.shutdown = ( args, answerHandler ) -> - answerHandler.answerSuccess 'Shutting down... BYE!' - setTimeout fShutdown, 500 - ### Handles possible events that were posted to this server and pushes them into the @@ -260,10 +240,13 @@ exports.handleUserCommand = ( req, resp ) -> req.on 'end', -> obj = qs.parse body console.log obj - if typeof objUserCmds[obj.command] is 'function' - objUserCmds[obj.command] req.session.user, obj, answerHandler req, resp - else - resp.send 404, 'Command unknown!' + @userRequestHandler req.session.user, obj, ( err, obj) -> + console.log 'user request handler sent answer!' + console.log obj + if !err + resp.send 'yay!' + else + resp.send 404, 'Command unknown!' ### Handles the admin command requests. diff --git a/coffee/webapi-eca.coffee b/coffee/webapi-eca.coffee index 3af43cb..8fdb88e 100644 --- a/coffee/webapi-eca.coffee +++ b/coffee/webapi-eca.coffee @@ -21,12 +21,18 @@ conf = require './config' # - [Persistence](persistence.html) db = require './persistence' +# - [ECA Components Manager](components-manager.html) +cm = require './components-manager' + # - [Engine](engine.html) engine = require './engine' # - [HTTP Listener](http-listener.html) http = require './http-listener' +# - [Event Poller](event-poller.html) *(will be forked into a child process)* +nameEP = 'event-poller' + # - Node.js Modules: [fs](http://nodejs.org/api/fs.html), # [path](http://nodejs.org/api/path.html) # and [child_process](http://nodejs.org/api/child_process.html) @@ -80,12 +86,6 @@ opt = 'n': alias : 'nolog', describe: 'Set this if no output shall be generated' -# `-n`, `--nolog`: Set this if no output shall be generated - 'u': - alias : 'unit-test-flag', - describe: """Set this if you are running the unit tests. This will cause the - system to not call process.exit() at the end of the shutDown routine - in order to get rid of the express server that would keep running""" # now fetch the CLI arguments and exit if the help has been called. argv = optimist.usage( usage ).options( opt ).argv @@ -105,7 +105,6 @@ init = => console.error 'FAIL: Config file not ready! Shutting down...' process.exit() - @isUnitTest = argv.u || false logconf = conf.getLogConf() if argv.m logconf[ 'mode' ] = argv.m @@ -132,6 +131,8 @@ init = => @log.info 'RS | Initialzing DB' db args # > We only proceed with the initialization if the DB is ready + #TODO eventually we shouldn't let each module load its own persistence + #module, but hand this one through them via the args... db.isConnected ( err ) => if err @log.error 'RS | No DB connection, shutting down system!' @@ -141,19 +142,9 @@ init = => # > Initialize all required modules with the args object. @log.info 'RS | Initialzing engine' engine args - @log.info 'RS | Initialzing http listener' - # We give the HTTP listener the ability to shutdown the whole system - http.addShutdownHandler shutDown - http args - # > Distribute handlers between modules to link the application. - @log.info 'RS | Passing handlers to engine' - engine.addPersistence db - @log.info 'RS | Passing handlers to http listener' - #TODO engine pushEvent needs to go into redis queue - #TODO loadAction and addRule will be removed - #mm.addHandlers db, engine.loadActionModule, engine.addRule - @log.info 'RS | Forking child process for the event poller' + # Start the event poller. The module manager will emit events for it + @log.info 'RS | Forking a child process for the event poller' cliArgs = [ args.logconf['mode'] args.logconf['io-level'] @@ -161,8 +152,25 @@ init = => args.logconf['file-path'] args.logconf['nolog'] ] - poller = cp.fork path.resolve( __dirname, 'event-poller' ), cliArgs + poller = cp.fork path.resolve( __dirname, nameEP ), cliArgs + # after the engine and the event poller have been initialized we can + # initialize the module manager and register event listener functions + # from engine and event poller + @log.info 'RS | Initialzing module manager' + cm args + cm.addListener 'newRule', ( evt ) -> + poller.send evt + cm.addListener 'newRule', ( evt ) -> + engine.internalEvent evt + + @log.info 'RS | Initialzing http listener' + # The request handler passes certain requests to the module manager + args[ 'request-service' ] = cm.processRequest + # We give the HTTP listener the ability to shutdown the whole system + args[ 'shutdown-function' ] = shutDown + http args + ### Shuts down the server. @@ -170,6 +178,7 @@ Shuts down the server. ### shutDown = () => @log.warn 'RS | Received shut down command!' + # db?.shutDown() engine?.shutDown() # We need to call process.exit() since the express server in the http-listener # can't be stopped gracefully. Why would you stop this system anyways!?? diff --git a/js-coffee/module-manager.js b/js-coffee/components-manager.js similarity index 88% rename from js-coffee/module-manager.js rename to js-coffee/components-manager.js index 45b35e8..b6548c5 100644 --- a/js-coffee/module-manager.js +++ b/js-coffee/components-manager.js @@ -7,22 +7,35 @@ > then compiled into node modules and rules */ -'use strict'; +// # **Loads Modules:** + +// # - [Persistence](persistence.html) var fs = require('fs'), path = require('path'), - log, - db, funcLoadAction, funcLoadRule; + db = require('./persistence'), + events = require('events'), + log, ee, + eventHandlers = [], + funcLoadAction, funcLoadRule; exports = module.exports = function(args) { args = args || {}; + ee = new events.EventEmitter(); log = args.logger; + db(args); return module.exports; }; -exports.addDBLink = function(db_link) { - db = db_link; -}; +exports.addListener = function( evt, eh ) { + ee.addListener( evt, eh ); + //TODO as soon as an event handler is added it needs to receive the full list of existing and activated rules +} + +exports.processRequest = function( user, obj, cb ) { + console.log('module manager needs to process request: '); + console.log(obj.command); +} exports.requireFromString = function(src, name, dir) { if(!dir) dir = __dirname; @@ -174,6 +187,10 @@ exports.storeRule = function (objUser, obj, answHandler) { } db.getEventModule(objRule.event.split('->')[0], cbEventModule(lst)); db.storeRule(objRule.id, objUser.username, obj.data); + ee.emit('newRule', objRule); + // for( var i = 0; i < eventHandlers.length; i++ ) { + // eventHandlers[i]( objRule ); + // } } catch(err) { answHandler.answerError(err.message); log.error('MM', err); diff --git a/js-coffee/engine.js b/js-coffee/engine.js index 6484be4..ad699d0 100644 --- a/js-coffee/engine.js +++ b/js-coffee/engine.js @@ -10,10 +10,14 @@ var path = require('path'), exports = module.exports = function( args ) { log = args.logger; - mm = require('./module-manager')(args); + mm = require('./components-manager')(args); return module.exports; }; +exports.internalEvent = function( evt ) { + console.log('engine got internal event'); + console.log(evt); +} /* * Initialize the rules engine which initializes the module loader. * @param {Object} db_link the link to the db, see [db\_interface](db_interface.html) diff --git a/js-coffee/event-poller.js b/js-coffee/event-poller.js index 818479a..4f7d59e 100644 --- a/js-coffee/event-poller.js +++ b/js-coffee/event-poller.js @@ -31,7 +31,7 @@ function init() { log = logger.getLogger(logconf); var args = { logger: log }; - (ml = require('./module-manager'))(args); + (ml = require('./components-manager'))(args); (db = require('./persistence'))(args); initAdminCommands(); initMessageActions(); @@ -113,13 +113,15 @@ function initMessageActions() { if(typeof(func) === 'function') func(args); }; - process.on('message', function(strProps) { - var arrProps = strProps.split('|'); - if(arrProps.length < 2) log.error('EP', 'too few parameter in message!'); - else { - var func = listMessageActions[arrProps[0]]; - if(func) func(arrProps); - } + process.on('message', function( obj ) { + console.log( 'message: '); + console.log (obj); + // var arrProps = obj .split('|'); + // if(arrProps.length < 2) log.error('EP', 'too few parameter in message!'); + // else { + // var func = listMessageActions[arrProps[0]]; + // if(func) func(arrProps); + // } }); // very important so the process doesnt linger on when the paren process is killed diff --git a/js-coffee/http-listener.js b/js-coffee/http-listener.js index c737684..2a72556 100644 --- a/js-coffee/http-listener.js +++ b/js-coffee/http-listener.js @@ -34,6 +34,7 @@ HTTP Listener exports = module.exports = function(args) { _this.log = args.logger; + _this.shutDownSystem = args['shutdown-function']; requestHandler(args); initRouting(args['http-port']); return module.exports; @@ -92,17 +93,4 @@ HTTP Listener }); }; - /* - Adds the shutdown handler to the admin commands. - - @param {function} fshutDown - @public addShutdownHandler( *fShutDown* ) - */ - - - exports.addShutdownHandler = function(fShutDown) { - _this.shutDownSystem = fShutDown; - return requestHandler.addShutdownHandler(fShutDown); - }; - }).call(this); diff --git a/js-coffee/logging.js b/js-coffee/logging.js index 87d7bb6..8ec11a8 100644 --- a/js-coffee/logging.js +++ b/js-coffee/logging.js @@ -20,9 +20,12 @@ exports.getLogger = function(args) { var e, emptylog, opt; emptylog = { + trace: function() {}, + debug: function() {}, info: function() {}, warn: function() {}, - error: function() {} + error: function() {}, + fatal: function() {} }; args = args != null ? args : {}; if (args.nolog) { diff --git a/js-coffee/request-handler.js b/js-coffee/request-handler.js index bc7ea0c..8047c41 100644 --- a/js-coffee/request-handler.js +++ b/js-coffee/request-handler.js @@ -11,13 +11,11 @@ Request Handler (function() { - var answerHandler, crypto, db, exports, fs, getHandlerFileAsString, getHandlerPath, getIncludeFileAsString, mm, mustache, objAdminCmds, objUserCmds, path, qs, renderPage, sendLoginOrPage, + var answerHandler, crypto, db, exports, fs, getHandlerFileAsString, getHandlerPath, getIncludeFileAsString, mustache, path, qs, renderPage, sendLoginOrPage, _this = this; db = require('./persistence'); - mm = require('./module-manager'); - fs = require('fs'); path = require('path'); @@ -28,22 +26,17 @@ Request Handler crypto = require('crypto-js'); - objUserCmds = { - 'store_action': mm.storeActionModule, - 'get_actionmodules': mm.getAllActionModules, - 'store_event': mm.storeEventModule, - 'get_eventmodules': mm.getAllEventModules, - 'store_rule': mm.storeRule - }; - - objAdminCmds = {}; - exports = module.exports = function(args) { var log, user, users, _i, _len; log = args.logger; + _this.userRequestHandler = args['request-service']; + _this.objAdminCmds = { + shutdown: function(args, answerHandler) { + answerHandler.answerSuccess('Shutting down... BYE!'); + return setTimeout(args['shutdown-function'], 500); + } + }; db(args); - mm(args); - mm.addDBLink(db); users = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'config', 'users.json'))); for (_i = 0, _len = users.length; _i < _len; _i++) { user = users[_i]; @@ -52,22 +45,6 @@ Request Handler return module.exports; }; - /* - This allows the parent to add the shutdown handler. - The shutdown function will be called if the admin command shutdown is issued. - - @public addShutdownHandler( *fShutdown* ) - @param {function} fShutdown - */ - - - exports.addShutdownHandler = function(fShutdown) { - return objAdminCmds.shutdown = function(args, answerHandler) { - answerHandler.answerSuccess('Shutting down... BYE!'); - return setTimeout(fShutdown, 500); - }; - }; - /* Handles possible events that were posted to this server and pushes them into the event queue. @@ -315,11 +292,15 @@ Request Handler var obj; obj = qs.parse(body); console.log(obj); - if (typeof objUserCmds[obj.command] === 'function') { - return objUserCmds[obj.command](req.session.user, obj, answerHandler(req, resp)); - } else { - return resp.send(404, 'Command unknown!'); - } + return this.userRequestHandler(req.session.user, obj, function(err, obj) { + console.log('user request handler sent answer!'); + console.log(obj); + if (!err) { + return resp.send('yay!'); + } else { + return resp.send(404, 'Command unknown!'); + } + }); }); } }; diff --git a/js-coffee/webapi-eca.js b/js-coffee/webapi-eca.js index d5366c5..a3defba 100644 --- a/js-coffee/webapi-eca.js +++ b/js-coffee/webapi-eca.js @@ -13,7 +13,7 @@ WebAPI-ECA Engine (function() { - var argv, conf, cp, db, engine, fs, http, init, logger, opt, optimist, path, procCmds, shutDown, usage, + var argv, cm, conf, cp, db, engine, fs, http, init, logger, nameEP, opt, optimist, path, procCmds, shutDown, usage, _this = this; logger = require('./logging'); @@ -22,10 +22,14 @@ WebAPI-ECA Engine db = require('./persistence'); + cm = require('./components-manager'); + engine = require('./engine'); http = require('./http-listener'); + nameEP = 'event-poller'; + fs = require('fs'); path = require('path'); @@ -79,10 +83,6 @@ WebAPI-ECA Engine 'n': { alias: 'nolog', describe: 'Set this if no output shall be generated' - }, - 'u': { - alias: 'unit-test-flag', - describe: "Set this if you are running the unit tests. This will cause the\nsystem to not call process.exit() at the end of the shutDown routine\nin order to get rid of the express server that would keep running" } }; @@ -107,7 +107,6 @@ WebAPI-ECA Engine console.error('FAIL: Config file not ready! Shutting down...'); process.exit(); } - _this.isUnitTest = argv.u || false; logconf = conf.getLogConf(); if (argv.m) { logconf['mode'] = argv.m; @@ -145,15 +144,21 @@ WebAPI-ECA Engine } else { _this.log.info('RS | Initialzing engine'); engine(args); - _this.log.info('RS | Initialzing http listener'); - http.addShutdownHandler(shutDown); - http(args); - _this.log.info('RS | Passing handlers to engine'); - engine.addPersistence(db); - _this.log.info('RS | Passing handlers to http listener'); - _this.log.info('RS | Forking child process for the event poller'); + _this.log.info('RS | Forking a child process for the event poller'); cliArgs = [args.logconf['mode'], args.logconf['io-level'], args.logconf['file-level'], args.logconf['file-path'], args.logconf['nolog']]; - return poller = cp.fork(path.resolve(__dirname, 'event-poller'), cliArgs); + poller = cp.fork(path.resolve(__dirname, nameEP), cliArgs); + _this.log.info('RS | Initialzing module manager'); + cm(args); + cm.addListener('newRule', function(evt) { + return poller.send(evt); + }); + cm.addListener('newRule', function(evt) { + return engine.internalEvent(evt); + }); + _this.log.info('RS | Initialzing http listener'); + args['request-service'] = cm.processRequest; + args['shutdown-function'] = shutDown; + return http(args); } }); }; diff --git a/testing/test_logging.coffee b/testing/test_logging.coffee index a8ce7cf..d88b8f3 100644 --- a/testing/test_logging.coffee +++ b/testing/test_logging.coffee @@ -82,9 +82,12 @@ exports.testCustomPath = ( test ) => exports.testWrongPath = ( test ) => empty = [ + 'trace' + 'debug' 'info' 'warn' 'error' + 'fatal' ] test.expect empty.length diff --git a/testing/test_request-handler.coffee b/testing/test_request-handler.coffee new file mode 100644 index 0000000..1816cbd --- /dev/null +++ b/testing/test_request-handler.coffee @@ -0,0 +1,118 @@ +http = require 'http' +path = require 'path' +events = require 'events' +cp = require 'child_process' +logger = require path.join '..', 'js-coffee', 'logging' +log = logger.getLogger + nolog: true +i = 0 + +createRequest = ( query, origUrl ) -> + req = new events.EventEmitter() + req.query = query + req.originalUrl = origUrl + req.session = {} + req + +createLoggedInRequest = ( query, origUrl ) -> + req = createRequest() + req.session = + user: + name: 'unittester' + req + +createAdminRequest = ( query, origUrl ) -> + req = createRequest() + req.session = + user: + name: 'adminunittester' + isAdmin: true + req + +postRequestData = ( req, data ) -> + req.emit 'data', data + req.emit 'end' + +# cb want's to get a response like { code, msg } +createResponse = ( cb ) -> + resp = + send: ( code, msg ) -> + if msg + code = parseInt code + else + msg = code + code = 200 + cb code, msg + +exports.setUp = ( cb ) => + @rh = require path.join '..', 'js-coffee', 'request-handler' + cb() + +# test: ( test ) -> +# test.expect 1 + +# args = +# logger: log +# args[ 'request-service' ] = ( usr, obj, cb ) -> +# console.log 'got a request to answer!' +# args[ 'shutdown-function' ] = () -> +# console.log 'request handler wants us to shutdown!' +# @rh args + +# test.ok true, 'yay' +# test.done() + +exports.events = + setUp: ( cb ) => + @db = require path.join '..', 'js-coffee', 'persistence' + args = + logger: log + args[ 'db-port' ] = 6379 + @db args + cb() + + tearDown: ( cb ) => + @db.shutDown() + cb() + + testEvent: ( test ) => + test.expect 1 + + doPolling = true + fPollEventQueue = ( cb ) => + console.log 'polling' + if doPolling + @db.popEvent cb + setTimeout fPollEventQueue, 5 + + fPollEventQueue ( err, obj ) -> + console.log 'got something? :' + console.log err + console.log obj + + args = + logger: log + args[ 'request-service' ] = ( usr, obj, cb ) -> + test.ok false, 'testEvent should not cause a service request call' + args[ 'shutdown-function' ] = () -> + test.ok false, 'testEvent should not cause a system shutdown' + @rh args + + evt = 'event=unittest&eventid=unittest0' + req = createRequest() + console.log req + resp = createResponse ( code, msg ) -> + console.log 'got response: ' + msg + doPolling = false + @rh.handleEvent req, resp + fWait = () -> + postRequestData req, evt + + setTimeout fWait, 50 + # req.emit 'data', evt + # req.emit 'end' + + + test.ok true, 'yay' + test.done() +