123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- // Copyright 2010 The Closure Library Authors. All Rights Reserved.
- //
- // 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.
- /**
- * @fileoverview Definition of goog.messaging.MultiChannel, which uses a
- * single underlying MessageChannel to carry several independent virtual message
- * channels.
- *
- */
- goog.provide('goog.messaging.MultiChannel');
- goog.provide('goog.messaging.MultiChannel.VirtualChannel');
- goog.require('goog.Disposable');
- goog.require('goog.log');
- goog.require('goog.messaging.MessageChannel'); // interface
- goog.require('goog.object');
- /**
- * Creates a new MultiChannel wrapping a single MessageChannel. The
- * underlying channel shouldn't have any other listeners registered, but it
- * should be connected.
- *
- * Note that the other side of the channel should also be connected to a
- * MultiChannel with the same number of virtual channels.
- *
- * @param {goog.messaging.MessageChannel} underlyingChannel The underlying
- * channel to use as transport for the virtual channels.
- * @constructor
- * @extends {goog.Disposable}
- * @final
- */
- goog.messaging.MultiChannel = function(underlyingChannel) {
- goog.messaging.MultiChannel.base(this, 'constructor');
- /**
- * The underlying channel across which all requests are sent.
- * @type {goog.messaging.MessageChannel}
- * @private
- */
- this.underlyingChannel_ = underlyingChannel;
- /**
- * All the virtual channels that are registered for this MultiChannel.
- * These are null if they've been disposed.
- * @type {Object<?goog.messaging.MultiChannel.VirtualChannel>}
- * @private
- */
- this.virtualChannels_ = {};
- this.underlyingChannel_.registerDefaultService(
- goog.bind(this.handleDefault_, this));
- };
- goog.inherits(goog.messaging.MultiChannel, goog.Disposable);
- /**
- * Logger object for goog.messaging.MultiChannel.
- * @type {goog.log.Logger}
- * @private
- */
- goog.messaging.MultiChannel.prototype.logger_ =
- goog.log.getLogger('goog.messaging.MultiChannel');
- /**
- * Creates a new virtual channel that will communicate across the underlying
- * channel.
- * @param {string} name The name of the virtual channel. Must be unique for this
- * MultiChannel. Cannot contain colons.
- * @return {!goog.messaging.MultiChannel.VirtualChannel} The new virtual
- * channel.
- */
- goog.messaging.MultiChannel.prototype.createVirtualChannel = function(name) {
- if (name.indexOf(':') != -1) {
- throw Error(
- 'Virtual channel name "' + name + '" should not contain colons');
- }
- if (name in this.virtualChannels_) {
- throw Error(
- 'Virtual channel "' + name + '" was already created for ' +
- 'this multichannel.');
- }
- var channel = new goog.messaging.MultiChannel.VirtualChannel(this, name);
- this.virtualChannels_[name] = channel;
- return channel;
- };
- /**
- * Handles the default service for the underlying channel. This dispatches any
- * unrecognized services to the appropriate virtual channel.
- *
- * @param {string} serviceName The name of the service being called.
- * @param {string|!Object} payload The message payload.
- * @private
- */
- goog.messaging.MultiChannel.prototype.handleDefault_ = function(
- serviceName, payload) {
- var match = serviceName.match(/^([^:]*):(.*)/);
- if (!match) {
- goog.log.warning(
- this.logger_, 'Invalid service name "' + serviceName + '": no ' +
- 'virtual channel specified');
- return;
- }
- var channelName = match[1];
- serviceName = match[2];
- if (!(channelName in this.virtualChannels_)) {
- goog.log.warning(
- this.logger_, 'Virtual channel "' + channelName + ' does not ' +
- 'exist, but a message was received for it: "' + serviceName + '"');
- return;
- }
- var virtualChannel = this.virtualChannels_[channelName];
- if (!virtualChannel) {
- goog.log.warning(
- this.logger_, 'Virtual channel "' + channelName + ' has been ' +
- 'disposed, but a message was received for it: "' + serviceName +
- '"');
- return;
- }
- if (!virtualChannel.defaultService_) {
- goog.log.warning(
- this.logger_, 'Service "' + serviceName + '" is not registered ' +
- 'on virtual channel "' + channelName + '"');
- return;
- }
- virtualChannel.defaultService_(serviceName, payload);
- };
- /** @override */
- goog.messaging.MultiChannel.prototype.disposeInternal = function() {
- goog.object.forEach(
- this.virtualChannels_, function(channel) { goog.dispose(channel); });
- goog.dispose(this.underlyingChannel_);
- delete this.virtualChannels_;
- delete this.underlyingChannel_;
- };
- /**
- * A message channel that proxies its messages over another underlying channel.
- *
- * @param {goog.messaging.MultiChannel} parent The MultiChannel
- * which created this channel, and which contains the underlying
- * MessageChannel that's used as the transport.
- * @param {string} name The name of this virtual channel. Unique among the
- * virtual channels in parent.
- * @constructor
- * @implements {goog.messaging.MessageChannel}
- * @extends {goog.Disposable}
- * @final
- */
- goog.messaging.MultiChannel.VirtualChannel = function(parent, name) {
- goog.messaging.MultiChannel.VirtualChannel.base(this, 'constructor');
- /**
- * The MultiChannel containing the underlying transport channel.
- * @type {goog.messaging.MultiChannel}
- * @private
- */
- this.parent_ = parent;
- /**
- * The name of this virtual channel.
- * @type {string}
- * @private
- */
- this.name_ = name;
- };
- goog.inherits(goog.messaging.MultiChannel.VirtualChannel, goog.Disposable);
- /**
- * The default service to run if no other services match.
- * @type {?function(string, (string|!Object))}
- * @private
- */
- goog.messaging.MultiChannel.VirtualChannel.prototype.defaultService_;
- /**
- * Logger object for goog.messaging.MultiChannel.VirtualChannel.
- * @type {goog.log.Logger}
- * @private
- */
- goog.messaging.MultiChannel.VirtualChannel.prototype.logger_ =
- goog.log.getLogger('goog.messaging.MultiChannel.VirtualChannel');
- /**
- * This is a no-op, since the underlying channel is expected to already be
- * initialized when it's passed in.
- *
- * @override
- */
- goog.messaging.MultiChannel.VirtualChannel.prototype.connect = function(
- opt_connectCb) {
- if (opt_connectCb) {
- opt_connectCb();
- }
- };
- /**
- * This always returns true, since the underlying channel is expected to already
- * be initialized when it's passed in.
- *
- * @override
- */
- goog.messaging.MultiChannel.VirtualChannel.prototype.isConnected = function() {
- return true;
- };
- /**
- * @override
- */
- goog.messaging.MultiChannel.VirtualChannel.prototype.registerService = function(
- serviceName, callback, opt_objectPayload) {
- this.parent_.underlyingChannel_.registerService(
- this.name_ + ':' + serviceName,
- goog.bind(this.doCallback_, this, callback), opt_objectPayload);
- };
- /**
- * @override
- */
- goog.messaging.MultiChannel.VirtualChannel.prototype.registerDefaultService =
- function(callback) {
- this.defaultService_ = goog.bind(this.doCallback_, this, callback);
- };
- /**
- * @override
- */
- goog.messaging.MultiChannel.VirtualChannel.prototype.send = function(
- serviceName, payload) {
- if (this.isDisposed()) {
- throw Error('#send called for disposed VirtualChannel.');
- }
- this.parent_.underlyingChannel_.send(this.name_ + ':' + serviceName, payload);
- };
- /**
- * Wraps a callback with a function that will log a warning and abort if it's
- * called when this channel is disposed.
- *
- * @param {function()} callback The callback to wrap.
- * @param {...*} var_args Other arguments, passed to the callback.
- * @private
- */
- goog.messaging.MultiChannel.VirtualChannel.prototype.doCallback_ = function(
- callback, var_args) {
- if (this.isDisposed()) {
- goog.log.warning(
- this.logger_, 'Virtual channel "' + this.name_ + '" received ' +
- ' a message after being disposed.');
- return;
- }
- callback.apply({}, Array.prototype.slice.call(arguments, 1));
- };
- /** @override */
- goog.messaging.MultiChannel.VirtualChannel.prototype.disposeInternal =
- function() {
- this.parent_.virtualChannels_[this.name_] = null;
- this.parent_ = null;
- };
|