    /**
     * 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");
      }

    });