| 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 |