Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * Base class for all RpcProxy's | |
| 7 * | |
| 8 * RpcProxy objects run in the "client" isolate and have a SendPort which | |
| 9 * they use to send messages to RpcReceiver objects running in a "service" | |
| 10 * isolate. | |
| 11 */ | |
| 12 | |
| 13 class RpcProxy { | |
| 14 final Future<SendPort> futurePort; | |
| 15 RpcProxy(Future<SendPort> futurePort) : futurePort = futurePort {} | |
| 16 | |
| 17 /** | |
| 18 * Called by derived classes to send a command through a SendPort to | |
| 19 * an RpcReceiver. | |
| 20 * | |
| 21 * parameters: | |
| 22 * command - String identifying what command to execute | |
| 23 * args - optional list of arguments to the command (this may contain other | |
| 24 * RpcProxy objects to refer to other target objects in the service | |
| 25 * isolate). | |
| 26 * | |
| 27 * returns: | |
| 28 * a Future object that will be set to the value that was | |
| 29 * received as a reply to the SendPort call. | |
| 30 */ | |
| 31 Future sendCommand(String command, List args, adjustReply(Object value)) { | |
| 32 Completer completer = new Completer(); | |
| 33 futurePort.then((SendPort port) { | |
| 34 args = _filterArgs(args); | |
| 35 port.call({"command" : command, "args" : args}).receive((value, ignoreRe plyTo) { | |
|
Ben Laurie (Google)
2011/11/29 18:59:28
width
mattsh
2011/11/29 21:29:46
Done.
| |
| 36 assert(ignoreReplyTo === null); | |
| 37 value = _filterException(value); | |
| 38 if (adjustReply != null) { | |
| 39 // give derived proxy class a chance to transate SendPort to RpcProx y | |
|
Ben Laurie (Google)
2011/11/29 18:59:28
width
mattsh
2011/11/29 21:29:46
Done.
| |
| 40 value = adjustReply(value); | |
| 41 } | |
| 42 if (value is Exception) { | |
| 43 completer.completeException(value); | |
| 44 } else { | |
| 45 completer.complete(value); | |
| 46 } | |
| 47 } | |
| 48 ); | |
| 49 }); | |
| 50 return completer.future; | |
| 51 } | |
| 52 | |
| 53 /** | |
| 54 * Convert RpcProxy objects to SendPorts | |
| 55 */ | |
| 56 static List _filterArgs(List args) { | |
| 57 if (args == null) { | |
| 58 return null; | |
| 59 } | |
| 60 List filtered = new List(); | |
| 61 for (Object arg in args) { | |
| 62 if (arg is RpcProxy) { | |
| 63 RpcProxy proxy = arg; | |
| 64 filtered.add(proxy.futurePort.value); | |
|
Ben Laurie (Google)
2011/11/29 18:59:28
How do you know this has completed?
mattsh
2011/11/29 21:29:46
Because it is called from inside futurePort.then
Ben Laurie (Google)
2011/11/29 21:36:45
So? This is a different proxy's value...
mattsh
2011/11/29 22:32:06
Good point. Let me add a TODO about this.
| |
| 65 } else { | |
| 66 filtered.add(arg); | |
| 67 } | |
| 68 } | |
| 69 return filtered; | |
| 70 } | |
| 71 | |
| 72 // TODO (mattsh) hack, remove once we have serializable exceptions | |
| 73 Object _filterException(Object value) { | |
| 74 // Check if value is a serialized exception. | |
| 75 Exception e = RpcException.parse(value); | |
| 76 if (e != null) { | |
| 77 return e; | |
| 78 } else { | |
| 79 return value; | |
| 80 } | |
| 81 } | |
| 82 } | |
| 83 | |
| 84 | |
| 85 | |
| 86 /** | |
| 87 * Base class for all Receivers | |
| 88 * | |
| 89 * RpcReceiver objects have a ReceivePort, where they receive commands (from | |
| 90 * RpcProxy objects) that they interpret and translate into method | |
| 91 * calls on a "target" object. | |
| 92 * | |
| 93 * All RpcReceiver derived classes must implement the [receiveCommand] abstract | |
| 94 * method (where they actually command messages and call | |
| 95 * appropriate methods on the target object). | |
| 96 * | |
| 97 * type parameters: | |
| 98 * T - the type of the target object that this a receiver for | |
| 99 */ | |
| 100 class RpcReceiver<T> { | |
| 101 | |
| 102 // static map of containing all receivers in this isolate. This is used | |
| 103 // to be able to find a receiver and target, given a SendPort. | |
| 104 static Map<SendPort, RpcReceiver> _receivers; | |
| 105 static _register(RpcReceiver receiver) { | |
| 106 if (_receivers == null) { | |
| 107 _receivers = new Map<SendPort, RpcReceiver>(); | |
| 108 } | |
| 109 _receivers[receiver._receivePort.toSendPort()] = receiver; | |
| 110 } | |
| 111 | |
| 112 static void closeAll() { | |
| 113 for (RpcReceiver receiver in _receivers.getValues()) { | |
| 114 receiver._receivePort.close(); | |
| 115 } | |
| 116 } | |
| 117 | |
| 118 // the port that this receiver will listen on | |
| 119 final ReceivePort _receivePort; | |
| 120 | |
| 121 // the "target" object that this RpcReceiver will be calling | |
| 122 // to actually do some work. | |
| 123 final T target; | |
| 124 | |
| 125 RpcReceiver(this.target, this._receivePort) { | |
| 126 // place this receiver in the receiver registry | |
| 127 _register(this); | |
| 128 | |
| 129 // start listening on the receive port for command messages | |
| 130 _receivePort.receive((var message, SendPort replyTo) { | |
| 131 String command = message["command"]; | |
| 132 | |
| 133 // filter incoming arguments (looking for SendPorts | |
| 134 // that we need to translate to RpcReceiver objects) | |
| 135 List args = _filterIncomingArgs(message["args"]); | |
| 136 | |
| 137 // Call the derived RpcReceiver to execute the command | |
| 138 // (if the command throws an exception, then catch the | |
| 139 // exception, serialize it, and send as the reply) | |
| 140 Object reply; | |
| 141 try { | |
| 142 reply = receiveCommand(message["command"], args); | |
| 143 } catch (var e) { | |
| 144 // TODO(mattsh) - would really prefer to catch Object, but currently | |
| 145 // bug http://code.google.com/p/dart/issues/detail?id=469 | |
| 146 reply = RpcException.format(e); | |
| 147 } | |
| 148 | |
| 149 reply = _filterReply(reply); | |
| 150 | |
| 151 // send reply back to the proxy | |
| 152 replyTo.send(reply, null); | |
| 153 }); | |
| 154 } | |
| 155 | |
| 156 /** | |
| 157 * Translate any ReceivePort objects in the arguments to | |
| 158 * the corresponding target object. | |
| 159 */ | |
| 160 static List _filterIncomingArgs(List originalArgs) { | |
| 161 List args = new List(); | |
| 162 var i = 0; | |
| 163 if (originalArgs != null) { | |
| 164 for (var arg in originalArgs) { | |
| 165 if (arg is SendPort) { | |
| 166 if (_receivers[arg] == null) { | |
| 167 throw "can't find receiver for SendPort"; | |
| 168 } | |
| 169 arg = _receivers[arg].target; | |
| 170 if (arg == null) { | |
| 171 throw "receiver is missing target"; | |
| 172 } | |
| 173 } | |
| 174 args.add(arg); | |
| 175 i++; | |
| 176 } | |
| 177 } | |
| 178 return args; | |
| 179 } | |
| 180 | |
| 181 /** | |
| 182 * Walk over the reply that this receiver is about to send | |
| 183 * back, and: | |
| 184 * 1. translate RpcReceiver objects in the reply to the corresponding | |
| 185 * ReceivePort. | |
| 186 * | |
| 187 * TODO(mattsh) need to walk deeply | |
| 188 */ | |
| 189 static _filterReply(Object reply) { | |
| 190 if (reply is RpcReceiver) { | |
| 191 RpcReceiver receiver = reply; | |
| 192 reply = receiver._receivePort.toSendPort(); | |
| 193 } | |
| 194 return reply; | |
| 195 } | |
| 196 | |
| 197 /** | |
| 198 * (implemented by derived classes). | |
| 199 * | |
| 200 * parameters - | |
| 201 * command - String identifying what command to execute | |
| 202 * on the target object | |
| 203 * args - list of arguments to the command (if any arguments | |
| 204 * were ReceivePorts, these have been translated to the | |
| 205 * corresponding target objects, so this List79 will not | |
| 206 * contain any ReceivePorts) | |
| 207 */ | |
| 208 abstract Object receiveCommand(String command, List args); | |
| 209 } | |
| 210 | |
| 211 // TODO - hack - need better way to serialize exceptions. For now | |
| 212 // we take the message, and prefix with a recognizable string. | |
| 213 class RpcException implements Exception { | |
| 214 | |
| 215 static final String prefix = "RpcException:"; | |
| 216 | |
| 217 final String message; | |
| 218 const RpcException(this.message); | |
| 219 | |
| 220 String toString() { | |
| 221 return message; | |
| 222 } | |
| 223 | |
| 224 static String format(Exception e) { | |
| 225 return prefix + e.toString(); | |
| 226 } | |
| 227 | |
| 228 static RpcException parse(Object object) { | |
| 229 if (object === null || !(object is String)) { | |
| 230 return null; | |
| 231 } | |
| 232 String s = object; | |
| 233 if (!s.startsWith(prefix)) { | |
| 234 return null; | |
| 235 } | |
| 236 return new RpcException(s.substring(prefix.length, s.length)); | |
| 237 } | |
| 238 } | |
| OLD | NEW |