/** * Main class of the monitoring module. * * A monitor instance can be reused, but should be disposed via `close` method after last use. * * The monitor needs three components to perform the different tasks. * All components can define an optional 'dispose' function (with no parameter and no return value), which will be used by the monitor to clean up after the run. * * # Collector * Responsible for collecting the working set * ## Interface * * - hasMoreResults () : Boolean * - getResults () : de.elo.ix.client.Sord[] * - postProcess (de.elo.ix.client.Sord sord, Object[] results, Object ctx) * - getContext () : Object (optional, can return a prefilled context, if not implemented, the analyzers start with an empty context object) * * # Analyzer * * ## Interface * Responsible for performing the analysis and determining the necessary actions. * * - analyze (de.elo.ix.client.Sord sord, Object ctx) : Object[] * * # Executor * Responsible for executing the actions. * * ## Interface * * - execute (de.elo.ix.client.Sord sord, Object[] results, Object ctx) * * # Inter module dependencies * The object array returned by the analyzer modules `analyze` method will be handed over to the executor modules `execute` method and should therefor contain the needed properties. * The same objects will after execution be handed over to the collector modules `postProcess` method. The executor can enhance the result data further if needed. * * Each module is responsible for ensuring the consistency of the data it gets, because the monitor has no way of knowing, which modules play well together. * * # Module orchestration * * 1. Use `collector.getResults` to collect the first batch of Sord objects * 2. Iterate over batch * 1. Use `analyzer.analyze()` to check each Sord * 2. Use `executor.execute()` to execute potential actions * 3. Use `collector.postProcess()` to perform potential post processing * 3. Repeat from beginning until there are no results left * 4. Cleanup modules via `dispose()` * * # Usage: * * var nextRunCollector = sol.create("sol.common_monitoring.as.collectors.NextRunCollector", { ... }); * var retentionAnalyzer = sol.create("sol.common_monitoring.as.analyzers.RetentionAnalyzer", { ... }); * var simpleExecutor = sol.create("sol.common_monitoring.as.executors.SimpleExecutor", { ... }); * * var monitor = sol.create("sol.common_monitoring.as.Monitor", { * collector: nextRunCollector, * analyzer: retentionAnalyzer, * executor: simpleExecutor * }); * monitor.run(); * monitor.close(); * * @author PZ, ELO Digital Office GmbH * @version 1.02.000 * * @eloas * @requires sol.commom.DateUtils * @requires sol.common.ObjectUtils */ sol.define("sol.common_monitoring.as.Monitor", { requiredConfig: ["collector", "analyzer", "executor"], /** * @cfg {Object} collector The component responsible for collecting the working set */ /** * @cfg {Object} analyzer The component responsible for performing the analysis and determining the necessary actions */ /** * @cfg {String} executor The component responsible for executing the actions */ initialize: function (config) { var me = this; me.$super("sol.Base", "initialize", [config]); me.logger.info("initialize monitor"); me.todayIso = sol.common.DateUtils.dateToIso(new Date()); me.checkComponents(); }, /** * @private * Checks all components if they implement the correct interface. * @throws InitializationException */ checkComponents: function () { var me = this; me.logger.debug("initialize monitor: check component interfaces"); me.checkInterface(me.collector, "collector", ["hasMoreResults", "getResults", "postProcess"]); me.checkInterface(me.analyzer, "analyzer", ["analyze"]); me.checkInterface(me.executor, "executor", ["execute"]); }, /** * @private * Checks a component if it implements the correct interface. * @param {Array} component Component * @param {String} name Name * @param {Array} functions Functions * @throws InitializationException */ checkInterface: function (component, name, functions) { if (!component) { throw "InitializationException: no '" + name + "' defined"; } if (functions && (functions.length > 0)) { functions.forEach(function (f) { if (!component[f] || !sol.common.ObjectUtils.isFunction(component[f])) { throw "InitializationException: '" + name + "' has to define a function '" + f + "'"; } }); } }, /** * @private * Cleans up a component if it defines a `dispose` function. * @param {Object} component The component * @param {String} name The name of the component */ disposeComponent: function (component, name) { var me = this; me.logger.debug("cleanup " + name); if (!component) { me.logger.debug(["component '{0}' is not defined and cannot be disposed", name]); return; } if (!component.dispose || !sol.common.ObjectUtils.isFunction(component.dispose)) { me.logger.debug(["component '{0}' does not define a 'dispose' function", name]); return; } try { component.dispose(); } catch (ex) { me.logger.error(["error cleaning up '{0}' module", name], ex); } }, /** * Main method execute the monitor. */ run: function () { var me = this, ctx, workingSet, i, max, currentSord, results; me.logger.enter("run"); me.logger.info(["processing start: {0}", me.todayIso]); while (me.collector.hasMoreResults()) { workingSet = me.collector.getResults(); me.logger.info(["processing batch: found {0} entries", workingSet.length]); for (i = 0, max = workingSet.length; i < max; i++) { try { ctx = (sol.common.ObjectUtils.isFunction(me.collector.getContext)) ? me.collector.getContext() : {}; currentSord = workingSet[i]; me.logger.info(["process sord: objId={0}; name={1}", currentSord.id, currentSord.name]); results = me.analyzer.analyze(currentSord, ctx); me.executor.execute(currentSord, results, ctx); me.collector.postProcess(currentSord, results, ctx); } catch (ex) { me.logger.error(["an error occured while processing sord: objId={0}; name={1}", currentSord.id, currentSord.name], ex); } } } me.logger.exit("run"); }, /** * This function should be used, to cleanup after using the monitor. */ close: function () { var me = this; me.disposeComponent(me.collector, "collector"); me.disposeComponent(me.analyzer, "analyzer"); me.disposeComponent(me.executor, "executor"); } });