respondingchannel.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // Copyright 2012 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.RespondingChannel, which wraps a
  16. * MessageChannel and allows the user to get the response from the services.
  17. *
  18. */
  19. goog.provide('goog.messaging.RespondingChannel');
  20. goog.require('goog.Disposable');
  21. goog.require('goog.log');
  22. goog.require('goog.messaging.MultiChannel');
  23. /**
  24. * Creates a new RespondingChannel wrapping a single MessageChannel.
  25. * @param {goog.messaging.MessageChannel} messageChannel The messageChannel to
  26. * to wrap and allow for responses. This channel must not have any existing
  27. * services registered. All service registration must be done through the
  28. * {@link RespondingChannel#registerService} api instead. The other end of
  29. * channel must also be a RespondingChannel.
  30. * @constructor
  31. * @extends {goog.Disposable}
  32. */
  33. goog.messaging.RespondingChannel = function(messageChannel) {
  34. goog.messaging.RespondingChannel.base(this, 'constructor');
  35. /**
  36. * The message channel wrapped in a MultiChannel so we can send private and
  37. * public messages on it.
  38. * @type {goog.messaging.MultiChannel}
  39. * @private
  40. */
  41. this.messageChannel_ = new goog.messaging.MultiChannel(messageChannel);
  42. /**
  43. * Map of invocation signatures to function callbacks. These are used to keep
  44. * track of the asyncronous service invocations so the result of a service
  45. * call can be passed back to a callback in the calling frame.
  46. * @type {Object<number, function(Object)>}
  47. * @private
  48. */
  49. this.sigCallbackMap_ = {};
  50. /**
  51. * The virtual channel to send private messages on.
  52. * @type {goog.messaging.MultiChannel.VirtualChannel}
  53. * @private
  54. */
  55. this.privateChannel_ = this.messageChannel_.createVirtualChannel(
  56. goog.messaging.RespondingChannel.PRIVATE_CHANNEL_);
  57. /**
  58. * The virtual channel to send public messages on.
  59. * @type {goog.messaging.MultiChannel.VirtualChannel}
  60. * @private
  61. */
  62. this.publicChannel_ = this.messageChannel_.createVirtualChannel(
  63. goog.messaging.RespondingChannel.PUBLIC_CHANNEL_);
  64. this.privateChannel_.registerService(
  65. goog.messaging.RespondingChannel.CALLBACK_SERVICE_,
  66. goog.bind(this.callbackServiceHandler_, this), true);
  67. };
  68. goog.inherits(goog.messaging.RespondingChannel, goog.Disposable);
  69. /**
  70. * The name of the method invocation callback service (used internally).
  71. * @type {string}
  72. * @const
  73. * @private
  74. */
  75. goog.messaging.RespondingChannel.CALLBACK_SERVICE_ = 'mics';
  76. /**
  77. * The name of the channel to send private control messages on.
  78. * @type {string}
  79. * @const
  80. * @private
  81. */
  82. goog.messaging.RespondingChannel.PRIVATE_CHANNEL_ = 'private';
  83. /**
  84. * The name of the channel to send public messages on.
  85. * @type {string}
  86. * @const
  87. * @private
  88. */
  89. goog.messaging.RespondingChannel.PUBLIC_CHANNEL_ = 'public';
  90. /**
  91. * The next signature index to save the callback against.
  92. * @type {number}
  93. * @private
  94. */
  95. goog.messaging.RespondingChannel.prototype.nextSignatureIndex_ = 0;
  96. /**
  97. * Logger object for goog.messaging.RespondingChannel.
  98. * @type {goog.log.Logger}
  99. * @private
  100. */
  101. goog.messaging.RespondingChannel.prototype.logger_ =
  102. goog.log.getLogger('goog.messaging.RespondingChannel');
  103. /**
  104. * Gets a random number to use for method invocation results.
  105. * @return {number} A unique random signature.
  106. * @private
  107. */
  108. goog.messaging.RespondingChannel.prototype.getNextSignature_ = function() {
  109. return this.nextSignatureIndex_++;
  110. };
  111. /** @override */
  112. goog.messaging.RespondingChannel.prototype.disposeInternal = function() {
  113. goog.dispose(this.messageChannel_);
  114. delete this.messageChannel_;
  115. // Note: this.publicChannel_ and this.privateChannel_ get disposed by
  116. // this.messageChannel_
  117. delete this.publicChannel_;
  118. delete this.privateChannel_;
  119. };
  120. /**
  121. * Sends a message over the channel.
  122. * @param {string} serviceName The name of the service this message should be
  123. * delivered to.
  124. * @param {string|!Object} payload The value of the message. If this is an
  125. * Object, it is serialized to a string before sending if necessary.
  126. * @param {function(?Object)} callback The callback invoked with
  127. * the result of the service call.
  128. */
  129. goog.messaging.RespondingChannel.prototype.send = function(
  130. serviceName, payload, callback) {
  131. var signature = this.getNextSignature_();
  132. this.sigCallbackMap_[signature] = callback;
  133. var message = {};
  134. message['signature'] = signature;
  135. message['data'] = payload;
  136. this.publicChannel_.send(serviceName, message);
  137. };
  138. /**
  139. * Receives the results of the peer's service results.
  140. * @param {!Object|string} message The results from the remote service
  141. * invocation.
  142. * @private
  143. */
  144. goog.messaging.RespondingChannel.prototype.callbackServiceHandler_ = function(
  145. message) {
  146. var signature = message['signature'];
  147. var result = message['data'];
  148. if (signature in this.sigCallbackMap_) {
  149. var callback =
  150. /** @type {function(Object)} */ (this.sigCallbackMap_[signature]);
  151. callback(result);
  152. delete this.sigCallbackMap_[signature];
  153. } else {
  154. goog.log.warning(this.logger_, 'Received signature is invalid');
  155. }
  156. };
  157. /**
  158. * Registers a service to be called when a message is received.
  159. * @param {string} serviceName The name of the service.
  160. * @param {function(!Object)} callback The callback to process the
  161. * incoming messages. Passed the payload.
  162. */
  163. goog.messaging.RespondingChannel.prototype.registerService = function(
  164. serviceName, callback) {
  165. this.publicChannel_.registerService(
  166. serviceName, goog.bind(this.callbackProxy_, this, callback), true);
  167. };
  168. /**
  169. * A intermediary proxy for service callbacks to be invoked and return their
  170. * their results to the remote caller's callback.
  171. * @param {function((string|!Object))} callback The callback to process the
  172. * incoming messages. Passed the payload.
  173. * @param {!Object|string} message The message containing the signature and
  174. * the data to invoke the service callback with.
  175. * @private
  176. */
  177. goog.messaging.RespondingChannel.prototype.callbackProxy_ = function(
  178. callback, message) {
  179. var resultMessage = {};
  180. resultMessage['data'] = callback(message['data']);
  181. resultMessage['signature'] = message['signature'];
  182. // The callback invoked above may have disposed the channel so check if it
  183. // exists.
  184. if (this.privateChannel_) {
  185. this.privateChannel_.send(
  186. goog.messaging.RespondingChannel.CALLBACK_SERVICE_, resultMessage);
  187. }
  188. };