All Downloads are FREE. Search and download functionalities are using the official Maven repository.

features.container.service.js Maven / Gradle / Ivy

Go to download

Packages all the features that shindig provides into a single jar file to allow loading from the classpath

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */


/**
 * @fileoverview This represents the service layer that talks to OSAPI
 * endpoints. All RPC requests should go into this class.
 */


/**
 * @param {Object=} opt_config Configuration JSON.
 * @constructor
 */
osapi.container.Service = function(opt_config) {
  var config = this.config_ = opt_config || {};

  var injectedEndpoint = ((gadgets.config.get('osapi') || {}).endPoints ||
          [window.__API_URI.getOrigin() + '/rpc'])[0];
  var matches = /^([^\/]*\/\/[^\/]+)(.*)$/.exec(injectedEndpoint);
  /**
   * @type {string}
   * @private
   */
  this.apiHost_ = String(osapi.container.util.getSafeJsonValue(config,
      osapi.container.ServiceConfig.API_HOST, matches[1]));

  /**
   * @type {string}
   * @private
   */
  this.apiPath_ = String(osapi.container.util.getSafeJsonValue(config,
      osapi.container.ServiceConfig.API_PATH, matches[2]));

  /**
   * Map of gadget URLs to cached gadgetInfo response.
   * @type {Object}
   * @private
   */
  this.cachedMetadatas_ = {};

  /**
   * Map of gadget URLs to cached tokenInfo response.
   * @type {Object}
   * @private
   */
  this.cachedTokens_ = {};

  /**
   * @see osapi.container.Container.prototype.getLanguage
   */
  if (config.GET_LANGUAGE) {
    this.getLanguage = config.GET_LANGUAGE;
  }

  /**
   * @see osapi.container.Container.prototype.getCountry
   */
  if (config.GET_COUNTRY) {
    this.getCountry = config.GET_COUNTRY;
  }

  this.registerOsapiServices();

  this.onConstructed(config);
};


/**
 * Callback that occurs after instantiation/construction of this. Override to
 * provide your specific functionalities.
 * @param {Object=} opt_config Configuration JSON.
 */
osapi.container.Service.prototype.onConstructed = function(opt_config) {};


/**
 * Return a possibly-cached gadgets metadata for gadgets in request.ids, for
 * container request.container. If metadata is not cache, fetch from server
 * only for the uncached gadget URLs. The optional callback opt_callback will be
 * called, after a response is received.
 * @param {Object} request JSON object representing the request.
 * @param {function(Object)=} opt_callback function to call upon data receive.
 */
osapi.container.Service.prototype.getGadgetMetadata = function(request, opt_callback) {
  // TODO: come up with an expiration mechanism to evict cached gadgets.
  // Can be based on renderParam['nocache']. Be careful with preloaded and
  // arbitrarily-navigated gadgets. The former should be indefinite, unless
  // unloaded. The later can done without user knowing.
  var callback = opt_callback || function() {};

  var uncachedUrls = osapi.container.util.toArrayOfJsonKeys(
      this.getUncachedDataByRequest_(this.cachedMetadatas_, request));
  var finalResponse = this.getCachedDataByRequest_(this.cachedMetadatas_, request);

  // If fully cached, return from cache.
  if (uncachedUrls.length == 0) {
    callback(finalResponse);

  // Otherwise, request for uncached metadatas.
  } else {
    var self = this;
    request['ids'] = uncachedUrls;
    request['language'] = this.getLanguage();
    request['country'] = this.getCountry();
    osapi['gadgets']['metadata'](request).execute(function(response) {

      // If response entirely fails, augment individual errors.
      if (response['error']) {
        for (var i = 0; i < request['ids'].length; i++) {
          finalResponse[id] = { 'error' : response['error'] };
        }

      // Otherwise, cache response. Augment final response with server response.
      } else {
        var currentTimeMs = osapi.container.util.getCurrentTimeMs();
        for (var id in response) {
          var resp = response[id];
          self.updateResponse_(resp, id, currentTimeMs);
          self.cachedMetadatas_[id] = resp;
          finalResponse[id] = resp;
        }
      }

      callback(finalResponse);
    });
  }
};


/**
 * Add preloaded gadgets to cache
 * @param {Object} response hash of gadgets metadata.
 * @param {Object} refTime time to override responseTime (in order to support external caching).
 */
osapi.container.Service.prototype.addGadgetMetadatas = function(response, refTime) {
  this.addToCache_(response, refTime, this.cachedMetadatas_);
};


/**
 * Add preloaded tokens to cache
 * @param {Object} response hash of gadgets metadata.
 * @param {Object} refTime data time to override responseTime
 *     (in order to support external caching).
 */
osapi.container.Service.prototype.addGadgetTokens = function(response, refTime) {
  this.addToCache_(response, refTime, this.cachedTokens_);
};


/**
 * Utility function to add data to cache
 * @param {Object} response hash of gadgets metadata.
 * @param {Object} refTime data time to override responseTime (in order to support external caching).
 * @param {Object} cache the cache to update.
 * @private
 */
osapi.container.Service.prototype.addToCache_ = function(response, refTime, cache) {
  var currentTimeMs = osapi.container.util.getCurrentTimeMs();
  for (var id in response) {
    var resp = response[id];
    this.updateResponse_(resp, id, currentTimeMs, refTime);
    cache[id] = resp;
  }
};


/**
 * Update gadget data, set gadget id and calculate expiration time
 * @param {Object} resp gadget metadata item.
 * @param {string} id gadget id.
 * @param {Object} currentTimeMs current time.
 * @param {Object} opt_refTime data time to override responseTime (support external caching).
 * @private
 */
osapi.container.Service.prototype.updateResponse_ = function(
    resp, id, currentTimeMs, opt_refTime) {
  resp[osapi.container.MetadataParam.URL] = id;
  // This ignores time to fetch metadata. Okay, expect to be < 2s.
  resp[osapi.container.MetadataParam.LOCAL_EXPIRE_TIME] =
      resp[osapi.container.MetadataResponse.EXPIRE_TIME_MS] -
      (opt_refTime == null ?
          resp[osapi.container.MetadataResponse.RESPONSE_TIME_MS] : opt_refTime) +
      currentTimeMs;
};


/**
 * @param {Object} request JSON object representing the request.
 * @param {function(Object)=} opt_callback function to call upon data receive.
 */
osapi.container.Service.prototype.getGadgetToken = function(request, opt_callback) {
  var callback = opt_callback || function() {};

  // Do not check against cache. Always do a server fetch.
  var self = this;
  var tokenResponseCallback = function(response) {
    var finalResponse = {};

    // If response entirely fails, augment individual errors.
    if (response['error']) {
      for (var i = 0; i < request['ids'].length; i++) {
        finalResponse[request['ids'][i]] = { 'error' : response['error'] };
      }

    // Otherwise, cache response. Augment final response with server response.
    } else {
      for (var id in response) {
        response[id]['url'] = id; // make sure url is set
        self.cachedTokens_[id] = response[id];
        finalResponse[id] = response[id];
      }
    }

    callback(finalResponse);
  };

  // If we have a custom token fetch function, call it -- otherwise use the default
  if (this.config_[osapi.container.ContainerConfig.GET_GADGET_TOKEN]) {
    this.config_[osapi.container.ContainerConfig.GET_GADGET_TOKEN](request, tokenResponseCallback);
  } else {
    osapi['gadgets']['token'](request).execute(tokenResponseCallback);
  }
};


/**
 * @param {string} url gadget URL to use as key to get cached metadata.
 * @return {string} the gadgetInfo referenced by this URL.
 */
osapi.container.Service.prototype.getCachedGadgetMetadata = function(url) {
  return this.cachedMetadatas_[url];
};


/**
 * @param {string} url gadget URL to use as key to get cached token.
 * @return {string} the tokenInfo referenced by this URL.
 */
osapi.container.Service.prototype.getCachedGadgetToken = function(url) {
  return this.cachedTokens_[url];
};


/**
 * @param {Object} urls JSON containing gadget URLs to avoid removing.
 */
osapi.container.Service.prototype.uncacheStaleGadgetMetadataExcept =
    function(urls) {
  for (var url in this.cachedMetadatas_) {
    if (typeof urls[url] === 'undefined') {
      var gadgetInfo = this.cachedMetadatas_[url];
      if (gadgetInfo[osapi.container.MetadataParam.LOCAL_EXPIRE_TIME] <
          osapi.container.util.getCurrentTimeMs()) {
        delete this.cachedMetadatas_[url];
      }
    }
  }
};


/**
 * Initialize OSAPI endpoint methods/interfaces.
 */
osapi.container.Service.prototype.registerOsapiServices = function() {
  var endPoint = this.apiHost_ + this.apiPath_;

  var osapiServicesConfig = {};
  osapiServicesConfig['gadgets.rpc'] = ['container.listMethods'];
  osapiServicesConfig[endPoint] = [
    'gadgets.metadata',
    'gadgets.token'
  ];

  gadgets.config.init({
    'osapi': { 'endPoints': [endPoint] },
    'osapi.services': osapiServicesConfig
  });
};


/**
 * Get cached data by ids listed in request.
 * @param {Object} cache JSON containing cached data.
 * @param {Object} request containing ids.
 * @return {Object} JSON containing requested and cached entries.
 * @private
 */
osapi.container.Service.prototype.getCachedDataByRequest_ = function(
    cache, request) {
  return this.filterCachedDataByRequest_(cache, request,
      function(data) { return (typeof data !== 'undefined') });
};


/**
 * Get uncached data by ids listed in request.
 * @param {Object} cache JSON containing cached data.
 * @param {Object} request containing ids.
 * @return {Object} JSON containing requested and uncached entries.
 * @private
 */
osapi.container.Service.prototype.getUncachedDataByRequest_ = function(
    cache, request) {
  return this.filterCachedDataByRequest_(cache, request,
      function(data) { return (typeof data === 'undefined') });
};


/**
 * Helper to filter out cached data
 * @param {Object} data JSON containing cached data.
 * @param {Object} request containing ids.
 * @param {Function} filterFunc function to filter result.
 * @return {Object} JSON containing requested and filtered entries.
 * @private
 */
osapi.container.Service.prototype.filterCachedDataByRequest_ = function(
    data, request, filterFunc) {
  var result = {};
  for (var i = 0; i < request['ids'].length; i++) {
    var id = request['ids'][i];
    var cachedData = data[id];
    if (filterFunc(cachedData)) {
      result[id] = cachedData;
    }
  }
  return result;
};


/**
 * @return {string} Best-guess locale for current browser.
 * @private
 */
osapi.container.Service.prototype.getLocale_ = function() {
  var nav = window.navigator;
  return nav.userLanguage || nav.systemLanguage || nav.language;
};


/**
 * A callback function that will return the correct language locale part to use when
 * asking the server to render a gadget or when asking the server for 1 or more
 * gadget's metadata.
 * 
* May be overridden by passing in a config parameter during container construction. * * @return {string} Language locale part. */ osapi.container.Service.prototype.getLanguage = function() { try { return this.getLocale_().split('-')[0] || 'ALL'; } catch (e) { return 'ALL'; } }; /** * A callback function that will return the correct country locale part to use when * asking the server to render a gadget or when asking the server for 1 or more * gadget's metadata. *
* May be overridden by passing in a config parameter during container construction. * @return {string} Country locale part. */ osapi.container.Service.prototype.getCountry = function() { try { return this.getLocale_().split('-')[1] || 'ALL'; } catch (e) { return 'ALL'; } }; // ----------------------------------------------------------------------------- // Configuration // ----------------------------------------------------------------------------- /** * Enumeration of configuration keys for this service. This is specified in * JSON to provide extensible configuration. * @enum {string} */ osapi.container.ServiceConfig = {}; /** * Host to fetch gadget information, via XHR. * @type {string} * @const */ osapi.container.ServiceConfig.API_HOST = 'apiHost'; /** * Path to fetch gadget information, via XHR. * @type {string} * @const */ osapi.container.ServiceConfig.API_PATH = 'apiPath';




© 2015 - 2025 Weber Informatics LLC | Privacy Policy