multichannel.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. // Copyright 2010 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 Definition of goog.messaging.MultiChannel, which uses a
  16. * single underlying MessageChannel to carry several independent virtual message
  17. * channels.
  18. *
  19. */
  20. goog.provide('goog.messaging.MultiChannel');
  21. goog.provide('goog.messaging.MultiChannel.VirtualChannel');
  22. goog.require('goog.Disposable');
  23. goog.require('goog.log');
  24. goog.require('goog.messaging.MessageChannel'); // interface
  25. goog.require('goog.object');
  26. /**
  27. * Creates a new MultiChannel wrapping a single MessageChannel. The
  28. * underlying channel shouldn't have any other listeners registered, but it
  29. * should be connected.
  30. *
  31. * Note that the other side of the channel should also be connected to a
  32. * MultiChannel with the same number of virtual channels.
  33. *
  34. * @param {goog.messaging.MessageChannel} underlyingChannel The underlying
  35. * channel to use as transport for the virtual channels.
  36. * @constructor
  37. * @extends {goog.Disposable}
  38. * @final
  39. */
  40. goog.messaging.MultiChannel = function(underlyingChannel) {
  41. goog.messaging.MultiChannel.base(this, 'constructor');
  42. /**
  43. * The underlying channel across which all requests are sent.
  44. * @type {goog.messaging.MessageChannel}
  45. * @private
  46. */
  47. this.underlyingChannel_ = underlyingChannel;
  48. /**
  49. * All the virtual channels that are registered for this MultiChannel.
  50. * These are null if they've been disposed.
  51. * @type {Object<?goog.messaging.MultiChannel.VirtualChannel>}
  52. * @private
  53. */
  54. this.virtualChannels_ = {};
  55. this.underlyingChannel_.registerDefaultService(
  56. goog.bind(this.handleDefault_, this));
  57. };
  58. goog.inherits(goog.messaging.MultiChannel, goog.Disposable);
  59. /**
  60. * Logger object for goog.messaging.MultiChannel.
  61. * @type {goog.log.Logger}
  62. * @private
  63. */
  64. goog.messaging.MultiChannel.prototype.logger_ =
  65. goog.log.getLogger('goog.messaging.MultiChannel');
  66. /**
  67. * Creates a new virtual channel that will communicate across the underlying
  68. * channel.
  69. * @param {string} name The name of the virtual channel. Must be unique for this
  70. * MultiChannel. Cannot contain colons.
  71. * @return {!goog.messaging.MultiChannel.VirtualChannel} The new virtual
  72. * channel.
  73. */
  74. goog.messaging.MultiChannel.prototype.createVirtualChannel = function(name) {
  75. if (name.indexOf(':') != -1) {
  76. throw Error(
  77. 'Virtual channel name "' + name + '" should not contain colons');
  78. }
  79. if (name in this.virtualChannels_) {
  80. throw Error(
  81. 'Virtual channel "' + name + '" was already created for ' +
  82. 'this multichannel.');
  83. }
  84. var channel = new goog.messaging.MultiChannel.VirtualChannel(this, name);
  85. this.virtualChannels_[name] = channel;
  86. return channel;
  87. };
  88. /**
  89. * Handles the default service for the underlying channel. This dispatches any
  90. * unrecognized services to the appropriate virtual channel.
  91. *
  92. * @param {string} serviceName The name of the service being called.
  93. * @param {string|!Object} payload The message payload.
  94. * @private
  95. */
  96. goog.messaging.MultiChannel.prototype.handleDefault_ = function(
  97. serviceName, payload) {
  98. var match = serviceName.match(/^([^:]*):(.*)/);
  99. if (!match) {
  100. goog.log.warning(
  101. this.logger_, 'Invalid service name "' + serviceName + '": no ' +
  102. 'virtual channel specified');
  103. return;
  104. }
  105. var channelName = match[1];
  106. serviceName = match[2];
  107. if (!(channelName in this.virtualChannels_)) {
  108. goog.log.warning(
  109. this.logger_, 'Virtual channel "' + channelName + ' does not ' +
  110. 'exist, but a message was received for it: "' + serviceName + '"');
  111. return;
  112. }
  113. var virtualChannel = this.virtualChannels_[channelName];
  114. if (!virtualChannel) {
  115. goog.log.warning(
  116. this.logger_, 'Virtual channel "' + channelName + ' has been ' +
  117. 'disposed, but a message was received for it: "' + serviceName +
  118. '"');
  119. return;
  120. }
  121. if (!virtualChannel.defaultService_) {
  122. goog.log.warning(
  123. this.logger_, 'Service "' + serviceName + '" is not registered ' +
  124. 'on virtual channel "' + channelName + '"');
  125. return;
  126. }
  127. virtualChannel.defaultService_(serviceName, payload);
  128. };
  129. /** @override */
  130. goog.messaging.MultiChannel.prototype.disposeInternal = function() {
  131. goog.object.forEach(
  132. this.virtualChannels_, function(channel) { goog.dispose(channel); });
  133. goog.dispose(this.underlyingChannel_);
  134. delete this.virtualChannels_;
  135. delete this.underlyingChannel_;
  136. };
  137. /**
  138. * A message channel that proxies its messages over another underlying channel.
  139. *
  140. * @param {goog.messaging.MultiChannel} parent The MultiChannel
  141. * which created this channel, and which contains the underlying
  142. * MessageChannel that's used as the transport.
  143. * @param {string} name The name of this virtual channel. Unique among the
  144. * virtual channels in parent.
  145. * @constructor
  146. * @implements {goog.messaging.MessageChannel}
  147. * @extends {goog.Disposable}
  148. * @final
  149. */
  150. goog.messaging.MultiChannel.VirtualChannel = function(parent, name) {
  151. goog.messaging.MultiChannel.VirtualChannel.base(this, 'constructor');
  152. /**
  153. * The MultiChannel containing the underlying transport channel.
  154. * @type {goog.messaging.MultiChannel}
  155. * @private
  156. */
  157. this.parent_ = parent;
  158. /**
  159. * The name of this virtual channel.
  160. * @type {string}
  161. * @private
  162. */
  163. this.name_ = name;
  164. };
  165. goog.inherits(goog.messaging.MultiChannel.VirtualChannel, goog.Disposable);
  166. /**
  167. * The default service to run if no other services match.
  168. * @type {?function(string, (string|!Object))}
  169. * @private
  170. */
  171. goog.messaging.MultiChannel.VirtualChannel.prototype.defaultService_;
  172. /**
  173. * Logger object for goog.messaging.MultiChannel.VirtualChannel.
  174. * @type {goog.log.Logger}
  175. * @private
  176. */
  177. goog.messaging.MultiChannel.VirtualChannel.prototype.logger_ =
  178. goog.log.getLogger('goog.messaging.MultiChannel.VirtualChannel');
  179. /**
  180. * This is a no-op, since the underlying channel is expected to already be
  181. * initialized when it's passed in.
  182. *
  183. * @override
  184. */
  185. goog.messaging.MultiChannel.VirtualChannel.prototype.connect = function(
  186. opt_connectCb) {
  187. if (opt_connectCb) {
  188. opt_connectCb();
  189. }
  190. };
  191. /**
  192. * This always returns true, since the underlying channel is expected to already
  193. * be initialized when it's passed in.
  194. *
  195. * @override
  196. */
  197. goog.messaging.MultiChannel.VirtualChannel.prototype.isConnected = function() {
  198. return true;
  199. };
  200. /**
  201. * @override
  202. */
  203. goog.messaging.MultiChannel.VirtualChannel.prototype.registerService = function(
  204. serviceName, callback, opt_objectPayload) {
  205. this.parent_.underlyingChannel_.registerService(
  206. this.name_ + ':' + serviceName,
  207. goog.bind(this.doCallback_, this, callback), opt_objectPayload);
  208. };
  209. /**
  210. * @override
  211. */
  212. goog.messaging.MultiChannel.VirtualChannel.prototype.registerDefaultService =
  213. function(callback) {
  214. this.defaultService_ = goog.bind(this.doCallback_, this, callback);
  215. };
  216. /**
  217. * @override
  218. */
  219. goog.messaging.MultiChannel.VirtualChannel.prototype.send = function(
  220. serviceName, payload) {
  221. if (this.isDisposed()) {
  222. throw Error('#send called for disposed VirtualChannel.');
  223. }
  224. this.parent_.underlyingChannel_.send(this.name_ + ':' + serviceName, payload);
  225. };
  226. /**
  227. * Wraps a callback with a function that will log a warning and abort if it's
  228. * called when this channel is disposed.
  229. *
  230. * @param {function()} callback The callback to wrap.
  231. * @param {...*} var_args Other arguments, passed to the callback.
  232. * @private
  233. */
  234. goog.messaging.MultiChannel.VirtualChannel.prototype.doCallback_ = function(
  235. callback, var_args) {
  236. if (this.isDisposed()) {
  237. goog.log.warning(
  238. this.logger_, 'Virtual channel "' + this.name_ + '" received ' +
  239. ' a message after being disposed.');
  240. return;
  241. }
  242. callback.apply({}, Array.prototype.slice.call(arguments, 1));
  243. };
  244. /** @override */
  245. goog.messaging.MultiChannel.VirtualChannel.prototype.disposeInternal =
  246. function() {
  247. this.parent_.virtualChannels_[this.name_] = null;
  248. this.parent_ = null;
  249. };