Path: blob/trunk/third_party/closure/goog/messaging/multichannel.js
2868 views
// Copyright 2010 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Definition of goog.messaging.MultiChannel, which uses a16* single underlying MessageChannel to carry several independent virtual message17* channels.18*19*/202122goog.provide('goog.messaging.MultiChannel');23goog.provide('goog.messaging.MultiChannel.VirtualChannel');2425goog.require('goog.Disposable');26goog.require('goog.log');27goog.require('goog.messaging.MessageChannel'); // interface28goog.require('goog.object');29303132/**33* Creates a new MultiChannel wrapping a single MessageChannel. The34* underlying channel shouldn't have any other listeners registered, but it35* should be connected.36*37* Note that the other side of the channel should also be connected to a38* MultiChannel with the same number of virtual channels.39*40* @param {goog.messaging.MessageChannel} underlyingChannel The underlying41* channel to use as transport for the virtual channels.42* @constructor43* @extends {goog.Disposable}44* @final45*/46goog.messaging.MultiChannel = function(underlyingChannel) {47goog.messaging.MultiChannel.base(this, 'constructor');4849/**50* The underlying channel across which all requests are sent.51* @type {goog.messaging.MessageChannel}52* @private53*/54this.underlyingChannel_ = underlyingChannel;5556/**57* All the virtual channels that are registered for this MultiChannel.58* These are null if they've been disposed.59* @type {Object<?goog.messaging.MultiChannel.VirtualChannel>}60* @private61*/62this.virtualChannels_ = {};6364this.underlyingChannel_.registerDefaultService(65goog.bind(this.handleDefault_, this));66};67goog.inherits(goog.messaging.MultiChannel, goog.Disposable);686970/**71* Logger object for goog.messaging.MultiChannel.72* @type {goog.log.Logger}73* @private74*/75goog.messaging.MultiChannel.prototype.logger_ =76goog.log.getLogger('goog.messaging.MultiChannel');777879/**80* Creates a new virtual channel that will communicate across the underlying81* channel.82* @param {string} name The name of the virtual channel. Must be unique for this83* MultiChannel. Cannot contain colons.84* @return {!goog.messaging.MultiChannel.VirtualChannel} The new virtual85* channel.86*/87goog.messaging.MultiChannel.prototype.createVirtualChannel = function(name) {88if (name.indexOf(':') != -1) {89throw Error(90'Virtual channel name "' + name + '" should not contain colons');91}9293if (name in this.virtualChannels_) {94throw Error(95'Virtual channel "' + name + '" was already created for ' +96'this multichannel.');97}9899var channel = new goog.messaging.MultiChannel.VirtualChannel(this, name);100this.virtualChannels_[name] = channel;101return channel;102};103104105/**106* Handles the default service for the underlying channel. This dispatches any107* unrecognized services to the appropriate virtual channel.108*109* @param {string} serviceName The name of the service being called.110* @param {string|!Object} payload The message payload.111* @private112*/113goog.messaging.MultiChannel.prototype.handleDefault_ = function(114serviceName, payload) {115var match = serviceName.match(/^([^:]*):(.*)/);116if (!match) {117goog.log.warning(118this.logger_, 'Invalid service name "' + serviceName + '": no ' +119'virtual channel specified');120return;121}122123var channelName = match[1];124serviceName = match[2];125if (!(channelName in this.virtualChannels_)) {126goog.log.warning(127this.logger_, 'Virtual channel "' + channelName + ' does not ' +128'exist, but a message was received for it: "' + serviceName + '"');129return;130}131132var virtualChannel = this.virtualChannels_[channelName];133if (!virtualChannel) {134goog.log.warning(135this.logger_, 'Virtual channel "' + channelName + ' has been ' +136'disposed, but a message was received for it: "' + serviceName +137'"');138return;139}140141if (!virtualChannel.defaultService_) {142goog.log.warning(143this.logger_, 'Service "' + serviceName + '" is not registered ' +144'on virtual channel "' + channelName + '"');145return;146}147148virtualChannel.defaultService_(serviceName, payload);149};150151152/** @override */153goog.messaging.MultiChannel.prototype.disposeInternal = function() {154goog.object.forEach(155this.virtualChannels_, function(channel) { goog.dispose(channel); });156goog.dispose(this.underlyingChannel_);157delete this.virtualChannels_;158delete this.underlyingChannel_;159};160161162163/**164* A message channel that proxies its messages over another underlying channel.165*166* @param {goog.messaging.MultiChannel} parent The MultiChannel167* which created this channel, and which contains the underlying168* MessageChannel that's used as the transport.169* @param {string} name The name of this virtual channel. Unique among the170* virtual channels in parent.171* @constructor172* @implements {goog.messaging.MessageChannel}173* @extends {goog.Disposable}174* @final175*/176goog.messaging.MultiChannel.VirtualChannel = function(parent, name) {177goog.messaging.MultiChannel.VirtualChannel.base(this, 'constructor');178179/**180* The MultiChannel containing the underlying transport channel.181* @type {goog.messaging.MultiChannel}182* @private183*/184this.parent_ = parent;185186/**187* The name of this virtual channel.188* @type {string}189* @private190*/191this.name_ = name;192};193goog.inherits(goog.messaging.MultiChannel.VirtualChannel, goog.Disposable);194195196/**197* The default service to run if no other services match.198* @type {?function(string, (string|!Object))}199* @private200*/201goog.messaging.MultiChannel.VirtualChannel.prototype.defaultService_;202203204/**205* Logger object for goog.messaging.MultiChannel.VirtualChannel.206* @type {goog.log.Logger}207* @private208*/209goog.messaging.MultiChannel.VirtualChannel.prototype.logger_ =210goog.log.getLogger('goog.messaging.MultiChannel.VirtualChannel');211212213/**214* This is a no-op, since the underlying channel is expected to already be215* initialized when it's passed in.216*217* @override218*/219goog.messaging.MultiChannel.VirtualChannel.prototype.connect = function(220opt_connectCb) {221if (opt_connectCb) {222opt_connectCb();223}224};225226227/**228* This always returns true, since the underlying channel is expected to already229* be initialized when it's passed in.230*231* @override232*/233goog.messaging.MultiChannel.VirtualChannel.prototype.isConnected = function() {234return true;235};236237238/**239* @override240*/241goog.messaging.MultiChannel.VirtualChannel.prototype.registerService = function(242serviceName, callback, opt_objectPayload) {243this.parent_.underlyingChannel_.registerService(244this.name_ + ':' + serviceName,245goog.bind(this.doCallback_, this, callback), opt_objectPayload);246};247248249/**250* @override251*/252goog.messaging.MultiChannel.VirtualChannel.prototype.registerDefaultService =253function(callback) {254this.defaultService_ = goog.bind(this.doCallback_, this, callback);255};256257258/**259* @override260*/261goog.messaging.MultiChannel.VirtualChannel.prototype.send = function(262serviceName, payload) {263if (this.isDisposed()) {264throw Error('#send called for disposed VirtualChannel.');265}266267this.parent_.underlyingChannel_.send(this.name_ + ':' + serviceName, payload);268};269270271/**272* Wraps a callback with a function that will log a warning and abort if it's273* called when this channel is disposed.274*275* @param {function()} callback The callback to wrap.276* @param {...*} var_args Other arguments, passed to the callback.277* @private278*/279goog.messaging.MultiChannel.VirtualChannel.prototype.doCallback_ = function(280callback, var_args) {281if (this.isDisposed()) {282goog.log.warning(283this.logger_, 'Virtual channel "' + this.name_ + '" received ' +284' a message after being disposed.');285return;286}287288callback.apply({}, Array.prototype.slice.call(arguments, 1));289};290291292/** @override */293goog.messaging.MultiChannel.VirtualChannel.prototype.disposeInternal =294function() {295this.parent_.virtualChannels_[this.name_] = null;296this.parent_ = null;297};298299300