| 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> this.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( |
| 36 (value, ignoreReplyTo) { |
| 37 assert(ignoreReplyTo === null); |
| 38 value = _filterException(value); |
| 39 if (adjustReply != null) { |
| 40 // give derived proxy class a chance to transate SendPort to |
| 41 // RpcProxy |
| 42 value = adjustReply(value); |
| 43 } |
| 44 if (value is Exception) { |
| 45 completer.completeException(value); |
| 46 } else { |
| 47 completer.complete(value); |
| 48 } |
| 49 } |
| 50 ); |
| 51 }); |
| 52 return completer.future; |
| 53 } |
| 54 |
| 55 /** Convert RpcProxy objects to SendPorts. */ |
| 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 // TODO - need to figure out if/how to wait for proxy's |
| 65 // port to be ready |
| 66 filtered.add(proxy.futurePort.value); |
| 67 } else { |
| 68 filtered.add(arg); |
| 69 } |
| 70 } |
| 71 return filtered; |
| 72 } |
| 73 |
| 74 // TODO (mattsh) hack, remove once we have serializable exceptions |
| 75 Object _filterException(Object value) { |
| 76 // Check if value is a serialized exception. |
| 77 Exception e = RpcException.parse(value); |
| 78 if (e != null) { |
| 79 return e; |
| 80 } else { |
| 81 return value; |
| 82 } |
| 83 } |
| 84 } |
| 85 |
| 86 |
| 87 |
| 88 /** |
| 89 * Base class for all Receivers |
| 90 * |
| 91 * RpcReceiver objects have a ReceivePort, where they receive commands (from |
| 92 * RpcProxy objects) that they interpret and translate into method |
| 93 * calls on a "target" object. |
| 94 * |
| 95 * All RpcReceiver derived classes must implement the [receiveCommand] abstract |
| 96 * method (where they actually command messages and call |
| 97 * appropriate methods on the target object). |
| 98 * |
| 99 * type parameters: |
| 100 * T - the type of the target object that this a receiver for |
| 101 */ |
| 102 class RpcReceiver<T> { |
| 103 |
| 104 // static map of containing all receivers in this isolate. This is used |
| 105 // to be able to find a receiver and target, given a SendPort. |
| 106 static Map<SendPort, RpcReceiver> _receivers; |
| 107 static _register(RpcReceiver receiver) { |
| 108 if (_receivers == null) { |
| 109 _receivers = new Map<SendPort, RpcReceiver>(); |
| 110 } |
| 111 _receivers[receiver._receivePort.toSendPort()] = receiver; |
| 112 } |
| 113 |
| 114 static void closeAll() { |
| 115 for (RpcReceiver receiver in _receivers.getValues()) { |
| 116 receiver._receivePort.close(); |
| 117 } |
| 118 } |
| 119 |
| 120 /** the port that this receiver will listen on */ |
| 121 final ReceivePort _receivePort; |
| 122 |
| 123 /** the "target" object that this RpcReceiver will be calling |
| 124 * to actually do some work. |
| 125 */ |
| 126 final T target; |
| 127 |
| 128 RpcReceiver(T this.target, ReceivePort this._receivePort) { |
| 129 // place this receiver in the receiver registry |
| 130 _register(this); |
| 131 |
| 132 // start listening on the receive port for command messages |
| 133 _receivePort.receive((var message, SendPort replyTo) { |
| 134 String command = message["command"]; |
| 135 |
| 136 // filter incoming arguments (looking for SendPorts |
| 137 // that we need to translate to RpcReceiver objects) |
| 138 List args = _filterIncomingArgs(message["args"]); |
| 139 |
| 140 // Call the derived RpcReceiver to execute the command |
| 141 // (if the command throws an exception, then catch the |
| 142 // exception, serialize it, and send as the reply) |
| 143 Object reply; |
| 144 try { |
| 145 reply = receiveCommand(message["command"], args); |
| 146 } catch (Object e) { |
| 147 reply = RpcException.format(e); |
| 148 } |
| 149 |
| 150 reply = _filterReply(reply); |
| 151 |
| 152 // send reply back to the proxy |
| 153 replyTo.send(reply, null); |
| 154 }); |
| 155 } |
| 156 |
| 157 /** |
| 158 * Translate any ReceivePort objects in the arguments to |
| 159 * the corresponding target object. |
| 160 */ |
| 161 static List _filterIncomingArgs(List originalArgs) { |
| 162 List args = new List(); |
| 163 var i = 0; |
| 164 if (originalArgs != null) { |
| 165 for (var arg in originalArgs) { |
| 166 if (arg is SendPort) { |
| 167 if (_receivers[arg] == null) { |
| 168 throw "can't find receiver for SendPort"; |
| 169 } |
| 170 arg = _receivers[arg].target; |
| 171 if (arg == null) { |
| 172 throw "receiver is missing target"; |
| 173 } |
| 174 } |
| 175 args.add(arg); |
| 176 i++; |
| 177 } |
| 178 } |
| 179 return args; |
| 180 } |
| 181 |
| 182 /** |
| 183 * Walk over the reply that this receiver is about to send |
| 184 * back, and translate RpcReceiver objects in the reply to the |
| 185 * corresponding ReceivePort. |
| 186 */ |
| 187 // TODO(mattsh) need to walk deeply |
| 188 static _filterReply(Object reply) { |
| 189 if (reply is RpcReceiver) { |
| 190 RpcReceiver receiver = reply; |
| 191 reply = receiver._receivePort.toSendPort(); |
| 192 } |
| 193 return reply; |
| 194 } |
| 195 |
| 196 /** |
| 197 * (implemented by derived classes). |
| 198 * |
| 199 * parameters - |
| 200 * command - String identifying what command to execute |
| 201 * on the target object |
| 202 * args - list of arguments to the command (if any arguments |
| 203 * were ReceivePorts, these have been translated to the |
| 204 * corresponding target objects, so this List79 will not |
| 205 * contain any ReceivePorts) |
| 206 */ |
| 207 abstract Object receiveCommand(String command, List args); |
| 208 } |
| 209 |
| 210 // TODO - need better way to serialize exceptions. For now |
| 211 // we take the message, and prefix with a recognizable string. |
| 212 class RpcException implements Exception { |
| 213 |
| 214 static final String prefix = "RpcException:"; |
| 215 |
| 216 final String message; |
| 217 const RpcException(String this.message); |
| 218 |
| 219 String toString() { |
| 220 return message; |
| 221 } |
| 222 |
| 223 static String format(Object e) { |
| 224 return prefix + e.toString(); |
| 225 } |
| 226 |
| 227 static RpcException parse(Object object) { |
| 228 if (object === null || !(object is String)) { |
| 229 return null; |
| 230 } |
| 231 String s = object; |
| 232 if (!s.startsWith(prefix)) { |
| 233 return null; |
| 234 } |
| 235 return new RpcException(s.substring(prefix.length, s.length)); |
| 236 } |
| 237 } |
| OLD | NEW |