portoperator.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // Copyright 2011 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview The central node of a {@link goog.messaging.PortNetwork}. The
  16. * operator is responsible for providing the two-way communication channels (via
  17. * {@link MessageChannel}s) between each pair of nodes in the network that need
  18. * to communicate with one another. Each network should have one and only one
  19. * operator.
  20. *
  21. */
  22. goog.provide('goog.messaging.PortOperator');
  23. goog.require('goog.Disposable');
  24. goog.require('goog.asserts');
  25. goog.require('goog.log');
  26. goog.require('goog.messaging.PortChannel');
  27. goog.require('goog.messaging.PortNetwork'); // interface
  28. goog.require('goog.object');
  29. /**
  30. * The central node of a PortNetwork.
  31. *
  32. * @param {string} name The name of this node.
  33. * @constructor
  34. * @extends {goog.Disposable}
  35. * @implements {goog.messaging.PortNetwork}
  36. * @final
  37. */
  38. goog.messaging.PortOperator = function(name) {
  39. goog.messaging.PortOperator.base(this, 'constructor');
  40. /**
  41. * The collection of channels for communicating with other contexts in the
  42. * network. These are the channels that are returned to the user, as opposed
  43. * to the channels used for internal network communication. This is lazily
  44. * populated as the user requests communication with other contexts, or other
  45. * contexts request communication with the operator.
  46. *
  47. * @type {!Object<!goog.messaging.PortChannel>}
  48. * @private
  49. */
  50. this.connections_ = {};
  51. /**
  52. * The collection of channels for internal network communication with other
  53. * contexts. This is not lazily populated, and always contains entries for
  54. * each member of the network.
  55. *
  56. * @type {!Object<!goog.messaging.MessageChannel>}
  57. * @private
  58. */
  59. this.switchboard_ = {};
  60. /**
  61. * The name of the operator context.
  62. *
  63. * @type {string}
  64. * @private
  65. */
  66. this.name_ = name;
  67. };
  68. goog.inherits(goog.messaging.PortOperator, goog.Disposable);
  69. /**
  70. * The logger for PortOperator.
  71. * @type {goog.log.Logger}
  72. * @private
  73. */
  74. goog.messaging.PortOperator.prototype.logger_ =
  75. goog.log.getLogger('goog.messaging.PortOperator');
  76. /** @override */
  77. goog.messaging.PortOperator.prototype.dial = function(name) {
  78. this.connectSelfToPort_(name);
  79. return this.connections_[name];
  80. };
  81. /**
  82. * Adds a caller to the network with the given name. This port should have no
  83. * services registered on it. It will be disposed along with the PortOperator.
  84. *
  85. * @param {string} name The name of the port to add.
  86. * @param {!goog.messaging.MessageChannel} port The port to add. Must be either
  87. * a {@link goog.messaging.PortChannel} or a decorator wrapping a
  88. * PortChannel; in particular, it must be able to send and receive
  89. * {@link MessagePort}s.
  90. */
  91. goog.messaging.PortOperator.prototype.addPort = function(name, port) {
  92. this.switchboard_[name] = port;
  93. port.registerService(
  94. goog.messaging.PortNetwork.REQUEST_CONNECTION_SERVICE,
  95. goog.bind(this.requestConnection_, this, name));
  96. };
  97. /**
  98. * Connects two contexts by creating a {@link MessageChannel} and sending one
  99. * end to one context and the other end to the other. Called when we receive a
  100. * request from a caller to connect it to another context (including potentially
  101. * the operator).
  102. *
  103. * @param {string} sourceName The name of the context requesting the connection.
  104. * @param {!Object|string} message The name of the context to which
  105. * the connection is requested.
  106. * @private
  107. */
  108. goog.messaging.PortOperator.prototype.requestConnection_ = function(
  109. sourceName, message) {
  110. var requestedName = /** @type {string} */ (message);
  111. if (requestedName == this.name_) {
  112. this.connectSelfToPort_(sourceName);
  113. return;
  114. }
  115. var sourceChannel = this.switchboard_[sourceName];
  116. var requestedChannel = this.switchboard_[requestedName];
  117. goog.asserts.assert(goog.isDefAndNotNull(sourceChannel));
  118. if (!requestedChannel) {
  119. var err = 'Port "' + sourceName + '" requested a connection to port "' +
  120. requestedName + '", which doesn\'t exist';
  121. goog.log.warning(this.logger_, err);
  122. sourceChannel.send(
  123. goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE,
  124. {'success': false, 'message': err});
  125. return;
  126. }
  127. var messageChannel = new MessageChannel();
  128. sourceChannel.send(
  129. goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE,
  130. {'success': true, 'name': requestedName, 'port': messageChannel.port1});
  131. requestedChannel.send(
  132. goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE,
  133. {'success': true, 'name': sourceName, 'port': messageChannel.port2});
  134. };
  135. /**
  136. * Connects together the operator and a caller by creating a
  137. * {@link MessageChannel} and sending one end to the remote context.
  138. *
  139. * @param {string} contextName The name of the context to which to connect the
  140. * operator.
  141. * @private
  142. */
  143. goog.messaging.PortOperator.prototype.connectSelfToPort_ = function(
  144. contextName) {
  145. if (contextName in this.connections_) {
  146. // We've already established a connection with this port.
  147. return;
  148. }
  149. var contextChannel = this.switchboard_[contextName];
  150. if (!contextChannel) {
  151. throw Error('Port "' + contextName + '" doesn\'t exist');
  152. }
  153. var messageChannel = new MessageChannel();
  154. contextChannel.send(
  155. goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE,
  156. {'success': true, 'name': this.name_, 'port': messageChannel.port1});
  157. messageChannel.port2.start();
  158. this.connections_[contextName] =
  159. new goog.messaging.PortChannel(messageChannel.port2);
  160. };
  161. /** @override */
  162. goog.messaging.PortOperator.prototype.disposeInternal = function() {
  163. goog.object.forEach(this.switchboard_, goog.dispose);
  164. goog.object.forEach(this.connections_, goog.dispose);
  165. delete this.switchboard_;
  166. delete this.connections_;
  167. goog.messaging.PortOperator.base(this, 'disposeInternal');
  168. };