1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
// Copyright 2011 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 The leaf node of a {@link goog.messaging.PortNetwork}. Callers
* connect to the operator, and request connections with other contexts from it.
*
*/
goog.provide('goog.messaging.PortCaller');
goog.require('goog.Disposable');
goog.require('goog.async.Deferred');
goog.require('goog.messaging.DeferredChannel');
goog.require('goog.messaging.PortChannel');
goog.require('goog.messaging.PortNetwork'); // interface
goog.require('goog.object');
/**
* The leaf node of a network.
*
* @param {!goog.messaging.MessageChannel} operatorPort The channel for
* communicating with the operator. The other side of this channel should be
* passed to {@link goog.messaging.PortOperator#addPort}. Must be either a
* {@link goog.messaging.PortChannel} or a decorator wrapping a PortChannel;
* in particular, it must be able to send and receive {@link MessagePort}s.
* @constructor
* @extends {goog.Disposable}
* @implements {goog.messaging.PortNetwork}
* @final
*/
goog.messaging.PortCaller = function(operatorPort) {
goog.messaging.PortCaller.base(this, 'constructor');
/**
* The channel to the {@link goog.messaging.PortOperator} for this network.
*
* @type {!goog.messaging.MessageChannel}
* @private
*/
this.operatorPort_ = operatorPort;
/**
* The collection of channels for communicating with other contexts in the
* network. Each value can contain a {@link goog.aync.Deferred} and/or a
* {@link goog.messaging.MessageChannel}.
*
* If the value contains a Deferred, then the channel is a
* {@link goog.messaging.DeferredChannel} wrapping that Deferred. The Deferred
* will be resolved with a {@link goog.messaging.PortChannel} once we receive
* the appropriate port from the operator. This is the situation when this
* caller requests a connection to another context; the DeferredChannel is
* used to queue up messages until we receive the port from the operator.
*
* If the value does not contain a Deferred, then the channel is simply a
* {@link goog.messaging.PortChannel} communicating with the given context.
* This is the situation when this context received a port for the other
* context before it was requested.
*
* If a value exists for a given key, it must contain a channel, but it
* doesn't necessarily contain a Deferred.
*
* @type {!Object<{deferred: goog.async.Deferred,
* channel: !goog.messaging.MessageChannel}>}
* @private
*/
this.connections_ = {};
this.operatorPort_.registerService(
goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE,
goog.bind(this.connectionGranted_, this), true /* opt_json */);
};
goog.inherits(goog.messaging.PortCaller, goog.Disposable);
/** @override */
goog.messaging.PortCaller.prototype.dial = function(name) {
if (name in this.connections_) {
return this.connections_[name].channel;
}
this.operatorPort_.send(
goog.messaging.PortNetwork.REQUEST_CONNECTION_SERVICE, name);
var deferred = new goog.async.Deferred();
var channel = new goog.messaging.DeferredChannel(deferred);
this.connections_[name] = {deferred: deferred, channel: channel};
return channel;
};
/**
* Registers a connection to another context in the network. This is called when
* the operator sends us one end of a {@link MessageChannel}, either because
* this caller requested a connection with another context, or because that
* context requested a connection with this caller.
*
* It's possible that the remote context and this one request each other roughly
* concurrently. The operator doesn't keep track of which contexts have been
* connected, so it will create two separate {@link MessageChannel}s in this
* case. However, the first channel created will reach both contexts first, so
* we simply ignore all connections with a given context after the first.
*
* @param {!Object|string} message The name of the context
* being connected and the port connecting the context.
* @private
*/
goog.messaging.PortCaller.prototype.connectionGranted_ = function(message) {
var args = /** @type {{name: string, port: MessagePort}} */ (message);
var port = args['port'];
var entry = this.connections_[args['name']];
if (entry && (!entry.deferred || entry.deferred.hasFired())) {
// If two PortCallers request one another at the same time, the operator may
// send out a channel for connecting them multiple times. Since both callers
// will receive the first channel's ports first, we can safely ignore and
// close any future ports.
port.close();
} else if (!args['success']) {
throw new Error(args['message']);
} else {
port.start();
var channel = new goog.messaging.PortChannel(port);
if (entry) {
entry.deferred.callback(channel);
} else {
this.connections_[args['name']] = {channel: channel, deferred: null};
}
}
};
/** @override */
goog.messaging.PortCaller.prototype.disposeInternal = function() {
goog.dispose(this.operatorPort_);
goog.object.forEach(this.connections_, goog.dispose);
delete this.operatorPort_;
delete this.connections_;
goog.messaging.PortCaller.base(this, 'disposeInternal');
};
|