/* * Copyright 2013 Jive Software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var q = require('q'); var jive = require('../../api'); function Scheduler() { return this; } /** * An in-memory implementation of scheduler. * @class memoryScheduler */ module.exports = Scheduler; var tasks = {}; var running = {}; var lastRunTs = {}; var eventHandlerMap = {}; /** * @memberof memoryScheduler * @param _eventHandlerMap */ function init( _eventHandlerMap ) { eventHandlerMap = _eventHandlerMap || jive.events.eventHandlerMap; // setup listeners jive.events.globalEvents.forEach( function(event) { var handlers = eventHandlerMap[event]; if ( handlers ) { if ( typeof handlers === 'function' ) { // normalize single handler into an array handlers = [ handlers ]; } handlers.forEach( function( handler ) { jive.events.addLocalEventListener( event, handler ); }); } }); return this; } Scheduler.prototype.init = init; /** * Schedule a task. * @memberof memoryScheduler * @param eventID which event to fire * @param context what to pass to the event * @param interval The interval to invoke the callback * @param delay The number of milliseconds after which the event will be fired for the first time. * @param exclusive If true, then will not execute if another event named with the same eventID is already executing. * @param timeout The number of milliseconds, after which the schedule will declare the event has timed out, and will fire the reject on any promise that was returned. * @returns {Object} Promise */ function schedule(eventID, context, interval, delay, exclusive, timeout) { eventID = eventID || jive.util.guid(); context = context || {}; var deferred = q.defer(); var handlers; if (context['eventListener']) { if ( eventHandlerMap[context['eventListener']] ) { handlers = eventHandlerMap[context['eventListener']][eventID]; } } else { handlers = eventHandlerMap[eventID]; } handlers = handlers || []; var next = function(timer, eventID) { var promises = []; running[eventID] = true; lastRunTs[eventID] = new Date().getTime(); handlers.forEach( function(handler) { var p = handler(context); if ( p && p['then'] ) { promises.push(p); } }); q.all( promises).then( // success function(result ) { result = result['forEach'] && result.length == 1 ? result[0] : result; // nuke self, if no longer scheduled if ( timer && eventID && !tasks[eventID] ) { clearInterval(timer); } delete running[eventID]; deferred.resolve(result); }, // fail function(e) { if ( timer && eventID && !tasks[eventID] ) { clearInterval(timer); } delete running[eventID]; deferred.reject(e); } ); }; if (interval) { if ( !running[eventID] ) { var d = delay || 1; setTimeout( function() { var timer = tasks[eventID] = setInterval(function() { var hasTimedOut = timeout ? new Date().getTime() - (lastRunTs[eventID] || 0) > timeout : false; if ( !running[eventID] || hasTimedOut ) { if (hasTimedOut ) { jive.logger.debug(eventID,'timed out!'); } next(timer, eventID); } }, interval); }, delay - interval > 0 ? (delay - interval) : delay ); } else { jive.logger.debug("Skipping", eventID, "already running."); } } else { setTimeout( function() { var hasTimedOut = timeout ? new Date().getTime() - (lastRunTs[eventID] || 0) > timeout : false; if ( !exclusive || !running[eventID] || hasTimedOut ) { if (hasTimedOut ) { jive.logger.debug(eventID,'timed out!'); } next(undefined, eventID); } }, delay || 1); } jive.logger.debug("Scheduled task: " + eventID, interval || "immediate"); return deferred.promise; } Scheduler.prototype.schedule = schedule; /** * @memberof memoryScheduler * @param eventID * @returns {Object} Promise */ function unschedule(eventID){ clearInterval(tasks[eventID]); delete tasks[eventID]; return q.resolve(); } Scheduler.prototype.unschedule = unschedule; /** * @memberof memoryScheduler * @returns {Object} Promise */ function getTasks(){ return Object.keys(tasks); } Scheduler.prototype.getTasks = getTasks; /** * @memberof memoryScheduler * @param eventID * @returns {Object} Promise */ function isScheduled( eventID ) { var deferred = q.defer(); if (tasks[eventID]) { deferred.resolve(true); } else { deferred.resolve(false); } return deferred.promise; } Scheduler.prototype.isScheduled = isScheduled; /** * @memberof memoryScheduler * @returns {Object} Promise */ function shutdown(){ var scheduler = this; eventHandlerMap = {}; running = {}; lastRunTs = {}; this.getTasks().forEach(function(taskKey){ scheduler.unschedule(taskKey); }); return q.resolve(); } Scheduler.prototype.shutdown = shutdown;