<commit_before>var path = require('path')
var http = require('http')
var util = require('util')
var argv = require('cmdenv')('micromono')
var async = require('async')
var debug = require('debug')('micromono:service:manager')
var Service = require('./index')
var discovery = require('../discovery/udp')
var Farcaster = require('./farcaster')
var spawnSync = require('child_process').spawnSync
var singletonify = require('../helper').singletonify


/**
 * Service manager manages services, local and/or remote.
 *
 * @param  {Object} options Options for the service manager:
 * {
 *   serviceDir: '/path/to/services', // Local path where we can find services.
 *   allowPending: false // Allow services pending for probing.
 * }
 * @return {ServiceManager}           Instance of ServiceManager.
 */
var ServiceManager = module.exports = function(options) {
  this.services = {}
  this.serviceDir = options.serviceDir
  this.remoteServices = []

  if (options.allowPending) {
    var pendingServices = this.pendingServices = {}
    var id = setInterval(function() {
      var services = Object.keys(pendingServices)
      if (services.length > 0) {
        debug('Pending service(s): %s', services.join(', '))
      } else {
        clearInterval(id)
      }
    }, 2000)
  }
}

/**
 * Require and get the class of service.
 *
 * @param  {String} name Name of the service to require.
 * @return {Service}     The service constructor.
 */
ServiceManager.prototype.require = function(serviceName) {
  var ServiceClass
  var ServiceFactory
  var _name = serviceName

  try {
    if (this.serviceDir) {

      serviceName = path.resolve(this.serviceDir, serviceName)
      debug('[%s] resolved path "%s"', _name, serviceName)
    }
    if (this.services[_name]) {
      debug('[%s] service already required', _name)
      return this.services[_name]
    } else {
      debug('[%s] require service from path "%s"', _name, serviceName)
      ServiceClass = require(serviceName)
    }
  } catch (e) {
    var expectedMessage = 'Cannot find module \'' + serviceName + '\''
    if (e.code !== 'MODULE_NOT_FOUND' || e.message !== expectedMessage) {
      // throw error if we found the module which contains error.
      throw e
    }

    debug('[%s] Failed to locate service locally, try to probe from network.', serviceName)

    var probedResult = spawnSync('node', [require.resolve('../discovery/prober'), serviceName])
    if (probedResult.status !== 0) {
      if (this.pendingServices && probedResult.status === 100) {
        // probe timeout, save the name to pending services and continue
        ServiceClass = this.pendingServices[serviceName] = Service
      } else {
        throw new Error(probedResult.stderr.toString())
      }
    } else {
      var announcement = JSON.parse(probedResult.stdout)
      var ann = util.inspect(announcement, {
        colors: true,
        depth: 4
      })
      debug('[%s] service probed from network:', announcement.name, '\n', ann)
      ServiceClass = Farcaster.build(announcement)
    }
  }

  if (ServiceClass !== Service) {
    ServiceFactory = this.register(ServiceClass, _name)
  }

  return ServiceFactory
}

/**
 * Start the service with a standalone internal server.
 *
 * @param  {Service} ServiceClass Service class.
 * @return {Promise}              Instance of promise.
 */
ServiceManager.prototype.startService = function(ServiceClass) {
  if ('function' === typeof ServiceClass) {
    this.mainService = this.register(ServiceClass)
  }

  return this.runServices()
}

/**
 * Run required services with optional server.
 *
 * @param  {MicroMonoServer} [server] Optional instance of MicroMonoServer.
 * @return {Promise}                  Instance of promise.
 */
ServiceManager.prototype.runServices = function(server) {
  var self = this

  return new Promise(function(resolve, reject) {
    async.eachSeries(Object.keys(self.services), function(name, callback) {
      var ServiceFactory = self.services[name]
      self.runService(server, ServiceFactory)
        .then(function(serviceInstance) {
          if (serviceInstance.isRemote()) {
            self.remoteServices.push(serviceInstance.name)
          } else if (!server) {
            // only announce when we are running as a service in standalone process
            debug('[%s] start announcing service', serviceInstance.name)
            discovery.announce(serviceInstance.announcement)
          }
          callback()
        })
        .catch(callback)
    }, function bootServicesFinished(err) {
      if (err) {
        debug('run services error', err && err.stack || err)
        reject(err)
      } else {
        resolve()
      }
    })
  })
}

/**
 * Run a single service with optional server.
 *
 * @param  {MicroMonoServer} [server] Optional instance of MicroMonoServer.
 * @param  {Service} ServiceFactory   Singletonified service factory class.
 * @return {Promise}                  Instance of promise.
 */
ServiceManager.prototype.runService = function(server, ServiceFactory) {
  var serviceInstance = new ServiceFactory()
  var ann = serviceInstance.announcement

  debug('[%s] runService()', serviceInstance.name)

  var framework
  if (server) {
    debug('[%s] runService() with server', serviceInstance.name)
    framework = server.framework
  }

  if (ann.use && framework) {
    Object.keys(ann.use).forEach(function(middlewareName) {
      var url = ann.use[middlewareName]
      debug('[%s] use middleware "%s" with url "%s"', serviceInstance.name, middlewareName, url)
      var _middlewareName = framework.type + '-' + middlewareName

      var middleware
      try {
        debug('[%s] try to load internal middleware "%s"', serviceInstance.name, _middlewareName)
        middleware = require('../web/middleware/' + _middlewareName)
      } catch (e) {
        middleware = require(middlewareName)
      }

      framework.useMiddleware(url, middleware, serviceInstance)
    })
  }

  var serviceOptions = {
    port: 0
  }<commit_msg>Update debug info.<commit_after>var path = require('path')
var http = require('http')
var util = require('util')
var argv = require('cmdenv')('micromono')
var async = require('async')
var debug = require('debug')('micromono:service:manager')
var Service = require('./index')
var discovery = require('../discovery/udp')
var Farcaster = require('./farcaster')
var spawnSync = require('child_process').spawnSync
var singletonify = require('../helper').singletonify


/**
 * Service manager manages services, local and/or remote.
 *
 * @param  {Object} options Options for the service manager:
 * {
 *   serviceDir: '/path/to/services', // Local path where we can find services.
 *   allowPending: false // Allow services pending for probing.
 * }
 * @return {ServiceManager}           Instance of ServiceManager.
 */
var ServiceManager = module.exports = function(options) {
  this.services = {}
  this.serviceDir = options.serviceDir
  this.remoteServices = []

  if (options.allowPending) {
    var pendingServices = this.pendingServices = {}
    var id = setInterval(function() {
      var services = Object.keys(pendingServices)
      if (services.length > 0) {
        debug('Pending service(s): %s', services.join(', '))
      } else {
        clearInterval(id)
      }
    }, 2000)
  }
}

/**
 * Require and get the class of service.
 *
 * @param  {String} name Name of the service to require.
 * @return {Service}     The service constructor.
 */
ServiceManager.prototype.require = function(serviceName) {
  var ServiceClass
  var ServiceFactory
  var _name = serviceName

  try {
    if (this.serviceDir) {

      serviceName = path.resolve(this.serviceDir, serviceName)
      debug('[%s] resolved path "%s"', _name, serviceName)
    }
    if (this.services[_name]) {
      debug('[%s] service already required', _name)
      return this.services[_name]
    } else {
      debug('[%s] require service from path "%s"', _name, serviceName)
      ServiceClass = require(serviceName)
    }
  } catch (e) {
    var expectedMessage = 'Cannot find module \'' + serviceName + '\''
    if (e.code !== 'MODULE_NOT_FOUND' || e.message !== expectedMessage) {
      // throw error if we found the module which contains error.
      throw e
    }

    debug('[%s] Failed to locate service locally, try to probe from network.', serviceName)

    var probedResult = spawnSync('node', [require.resolve('../discovery/prober'), serviceName])
    if (probedResult.status !== 0) {
      if (this.pendingServices && probedResult.status === 100) {
        // probe timeout, save the name to pending services and continue
        ServiceClass = this.pendingServices[serviceName] = Service
      } else {
        throw new Error(probedResult.stderr.toString())
      }
    } else {
      var announcement = JSON.parse(probedResult.stdout)
      var ann = util.inspect(announcement, {
        colors: true,
        depth: 4
      })
      debug('[%s] service probed from network:', announcement.name, '\n', ann)
      ServiceClass = Farcaster.build(announcement)
    }
  }

  if (ServiceClass !== Service) {
    ServiceFactory = this.register(ServiceClass, _name)
  }

  return ServiceFactory
}

/**
 * Start the service with a standalone internal server.
 *
 * @param  {Service} ServiceClass Service class.
 * @return {Promise}              Instance of promise.
 */
ServiceManager.prototype.startService = function(ServiceClass) {
  if ('function' === typeof ServiceClass) {
    this.mainService = this.register(ServiceClass)
  }

  return this.runServices()
}

/**
 * Run required services with optional server.
 *
 * @param  {MicroMonoServer} [server] Optional instance of MicroMonoServer.
 * @return {Promise}                  Instance of promise.
 */
ServiceManager.prototype.runServices = function(server) {
  var self = this

  return new Promise(function(resolve, reject) {
    async.eachSeries(Object.keys(self.services), function(name, callback) {
      var ServiceFactory = self.services[name];
      self.runService(server, ServiceFactory)
        .then(function(serviceInstance) {
          if (serviceInstance.isRemote()) {
            self.remoteServices.push(serviceInstance.name)
          } else if (!server) {
            // only announce when we are running as a service in standalone process
            debug('[%s] start announcing service', serviceInstance.name)
            discovery.announce(serviceInstance.announcement)
          }
          callback()
        })
        .catch(callback)
    }, function bootServicesFinished(err) {
      if (err) {
        debug('run services error', err && err.stack || err)
        reject(err)
      } else {
        resolve()
      }
    })
  })
}

/**
 * Run a single service with optional server.
 *
 * @param  {MicroMonoServer} [server] Optional instance of MicroMonoServer.
 * @param  {Service} ServiceFactory   Singletonified service factory class.
 * @return {Promise}                  Instance of promise.
 */
ServiceManager.prototype.runService = function(server, ServiceFactory) {
  var serviceInstance = new ServiceFactory()
  var ann = serviceInstance.announcement

  debug('[%s] runService()', serviceInstance.name)

  var framework
  if (server) {
    debug('[%s] runService() with server', serviceInstance.name)
    framework = server.framework
  }

  if (ann.use && framework) {
    Object.keys(ann.use).forEach(function(middlewareName) {
      var url = ann.use[middlewareName];
      debug('[%s] use middleware "%s"', serviceInstance.name, middlewareName)
      var _middlewareName = framework.type + '-' + middlewareName

      var middleware
      try {
        debug('[%s] try to load internal middleware "%s"', serviceInstance.name, _middlewareName)
        middleware = require('../web/middleware/' + _middlewareName)
      } catch (e) {
        middleware = require(middlewareName)
      }

      framework.useMiddleware(url, middleware, serviceInstance)
    })
  }

  var serviceOptions = {
    port: 0
  }
{'fraction_non_alphanumeric': 0.12313994315331885, 'fraction_numerical': 0.001839157331549908, 'mean_word_length': 3.0497630331753554, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1269303', 'n_tokens_mistral': 3406, 'n_tokens_neox': 3322, 'n_words': 910}