Path: blob/trunk/third_party/closure/goog/messaging/portoperator.js
2868 views
// Copyright 2011 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 The central node of a {@link goog.messaging.PortNetwork}. The16* operator is responsible for providing the two-way communication channels (via17* {@link MessageChannel}s) between each pair of nodes in the network that need18* to communicate with one another. Each network should have one and only one19* operator.20*21*/2223goog.provide('goog.messaging.PortOperator');2425goog.require('goog.Disposable');26goog.require('goog.asserts');27goog.require('goog.log');28goog.require('goog.messaging.PortChannel');29goog.require('goog.messaging.PortNetwork'); // interface30goog.require('goog.object');31323334/**35* The central node of a PortNetwork.36*37* @param {string} name The name of this node.38* @constructor39* @extends {goog.Disposable}40* @implements {goog.messaging.PortNetwork}41* @final42*/43goog.messaging.PortOperator = function(name) {44goog.messaging.PortOperator.base(this, 'constructor');4546/**47* The collection of channels for communicating with other contexts in the48* network. These are the channels that are returned to the user, as opposed49* to the channels used for internal network communication. This is lazily50* populated as the user requests communication with other contexts, or other51* contexts request communication with the operator.52*53* @type {!Object<!goog.messaging.PortChannel>}54* @private55*/56this.connections_ = {};5758/**59* The collection of channels for internal network communication with other60* contexts. This is not lazily populated, and always contains entries for61* each member of the network.62*63* @type {!Object<!goog.messaging.MessageChannel>}64* @private65*/66this.switchboard_ = {};6768/**69* The name of the operator context.70*71* @type {string}72* @private73*/74this.name_ = name;75};76goog.inherits(goog.messaging.PortOperator, goog.Disposable);777879/**80* The logger for PortOperator.81* @type {goog.log.Logger}82* @private83*/84goog.messaging.PortOperator.prototype.logger_ =85goog.log.getLogger('goog.messaging.PortOperator');868788/** @override */89goog.messaging.PortOperator.prototype.dial = function(name) {90this.connectSelfToPort_(name);91return this.connections_[name];92};939495/**96* Adds a caller to the network with the given name. This port should have no97* services registered on it. It will be disposed along with the PortOperator.98*99* @param {string} name The name of the port to add.100* @param {!goog.messaging.MessageChannel} port The port to add. Must be either101* a {@link goog.messaging.PortChannel} or a decorator wrapping a102* PortChannel; in particular, it must be able to send and receive103* {@link MessagePort}s.104*/105goog.messaging.PortOperator.prototype.addPort = function(name, port) {106this.switchboard_[name] = port;107port.registerService(108goog.messaging.PortNetwork.REQUEST_CONNECTION_SERVICE,109goog.bind(this.requestConnection_, this, name));110};111112113/**114* Connects two contexts by creating a {@link MessageChannel} and sending one115* end to one context and the other end to the other. Called when we receive a116* request from a caller to connect it to another context (including potentially117* the operator).118*119* @param {string} sourceName The name of the context requesting the connection.120* @param {!Object|string} message The name of the context to which121* the connection is requested.122* @private123*/124goog.messaging.PortOperator.prototype.requestConnection_ = function(125sourceName, message) {126var requestedName = /** @type {string} */ (message);127if (requestedName == this.name_) {128this.connectSelfToPort_(sourceName);129return;130}131132var sourceChannel = this.switchboard_[sourceName];133var requestedChannel = this.switchboard_[requestedName];134135goog.asserts.assert(goog.isDefAndNotNull(sourceChannel));136if (!requestedChannel) {137var err = 'Port "' + sourceName + '" requested a connection to port "' +138requestedName + '", which doesn\'t exist';139goog.log.warning(this.logger_, err);140sourceChannel.send(141goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE,142{'success': false, 'message': err});143return;144}145146var messageChannel = new MessageChannel();147sourceChannel.send(148goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE,149{'success': true, 'name': requestedName, 'port': messageChannel.port1});150requestedChannel.send(151goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE,152{'success': true, 'name': sourceName, 'port': messageChannel.port2});153};154155156/**157* Connects together the operator and a caller by creating a158* {@link MessageChannel} and sending one end to the remote context.159*160* @param {string} contextName The name of the context to which to connect the161* operator.162* @private163*/164goog.messaging.PortOperator.prototype.connectSelfToPort_ = function(165contextName) {166if (contextName in this.connections_) {167// We've already established a connection with this port.168return;169}170171var contextChannel = this.switchboard_[contextName];172if (!contextChannel) {173throw Error('Port "' + contextName + '" doesn\'t exist');174}175176var messageChannel = new MessageChannel();177contextChannel.send(178goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE,179{'success': true, 'name': this.name_, 'port': messageChannel.port1});180messageChannel.port2.start();181this.connections_[contextName] =182new goog.messaging.PortChannel(messageChannel.port2);183};184185186/** @override */187goog.messaging.PortOperator.prototype.disposeInternal = function() {188goog.object.forEach(this.switchboard_, goog.dispose);189goog.object.forEach(this.connections_, goog.dispose);190delete this.switchboard_;191delete this.connections_;192goog.messaging.PortOperator.base(this, 'disposeInternal');193};194195196