Chromium Code Reviews| Index: corelib/src/proxy.dart |
| diff --git a/corelib/src/proxy.dart b/corelib/src/proxy.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8683af4b69a96b79a71ea28d9dc64798fd58ac68 |
| --- /dev/null |
| +++ b/corelib/src/proxy.dart |
| @@ -0,0 +1,242 @@ |
| +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +/** |
| + * Base class for all RpcProxy's |
| + * |
| + * RpcProxy objects run in the "client" isolate and have a SendPort which |
| + * they use to send messages to RpcReceiver objects running in a "service" |
| + * isolate. |
| + */ |
| + |
| +class RpcProxy { |
| + final Future<SendPort> futurePort; |
| + RpcProxy(Future<SendPort> this.futurePort) {} |
| + |
| + /** |
| + * Called by derived classes to send a command through a SendPort to |
| + * an RpcReceiver. |
| + * |
| + * parameters: |
| + * command - String identifying what command to execute |
| + * args - optional list of arguments to the command (this may contain other |
| + * RpcProxy objects to refer to other target objects in the service |
| + * isolate). |
| + * |
| + * returns: |
| + * a Future object that will be set to the value that was |
| + * received as a reply to the SendPort call. |
| + */ |
| + Future sendCommand(String command, List args, adjustReply(Object value)) { |
| + Completer completer = new Completer(); |
| + futurePort.then((SendPort port) { |
| + args = _filterArgs(args); |
| + port.call({"command" : command, "args" : args}).receive( |
| + (value, ignoreReplyTo) { |
| + assert(ignoreReplyTo === null); |
| + value = _filterException(value); |
| + if (adjustReply != null) { |
| + // give derived proxy class a chance to transate SendPort to |
| + // RpcProxy |
| + value = adjustReply(value); |
| + } |
| + if (value is Exception) { |
| + completer.completeException(value); |
| + } else { |
| + completer.complete(value); |
| + } |
| + } |
| + ); |
| + }); |
| + return completer.future; |
| + } |
| + |
| + /** |
| + * Convert RpcProxy objects to SendPorts |
|
Siggi Cherem (dart-lang)
2011/11/30 01:44:38
style nits:
- use single line /** ... */ for comme
mattsh
2011/11/30 05:03:33
Done.
|
| + */ |
| + static List _filterArgs(List args) { |
| + if (args == null) { |
| + return null; |
| + } |
| + List filtered = new List(); |
| + for (Object arg in args) { |
| + if (arg is RpcProxy) { |
| + RpcProxy proxy = arg; |
| + // TODO - need to figure out if/how to wait for proxy's |
| + // port to be ready |
| + filtered.add(proxy.futurePort.value); |
| + } else { |
| + filtered.add(arg); |
| + } |
| + } |
| + return filtered; |
| + } |
| + |
| + // TODO (mattsh) hack, remove once we have serializable exceptions |
| + Object _filterException(Object value) { |
| + // Check if value is a serialized exception. |
| + Exception e = RpcException.parse(value); |
| + if (e != null) { |
| + return e; |
| + } else { |
| + return value; |
| + } |
| + } |
| +} |
| + |
| + |
| + |
| +/** |
| + * Base class for all Receivers |
| + * |
| + * RpcReceiver objects have a ReceivePort, where they receive commands (from |
| + * RpcProxy objects) that they interpret and translate into method |
| + * calls on a "target" object. |
| + * |
| + * All RpcReceiver derived classes must implement the [receiveCommand] abstract |
| + * method (where they actually command messages and call |
| + * appropriate methods on the target object). |
| + * |
| + * type parameters: |
| + * T - the type of the target object that this a receiver for |
| + */ |
| +class RpcReceiver<T> { |
| + |
| + // static map of containing all receivers in this isolate. This is used |
| + // to be able to find a receiver and target, given a SendPort. |
| + static Map<SendPort, RpcReceiver> _receivers; |
| + static _register(RpcReceiver receiver) { |
| + if (_receivers == null) { |
| + _receivers = new Map<SendPort, RpcReceiver>(); |
| + } |
| + _receivers[receiver._receivePort.toSendPort()] = receiver; |
| + } |
| + |
| + static void closeAll() { |
| + for (RpcReceiver receiver in _receivers.getValues()) { |
| + receiver._receivePort.close(); |
| + } |
| + } |
| + |
| + // the port that this receiver will listen on |
|
Siggi Cherem (dart-lang)
2011/11/30 01:44:38
(nit): to be consistent with all of our libraries,
Siggi Cherem (dart-lang)
2011/11/30 01:46:45
(here and elsewhere in this code)
mattsh
2011/11/30 05:03:33
Done.
mattsh
2011/11/30 05:03:33
Done.
|
| + final ReceivePort _receivePort; |
| + |
| + // the "target" object that this RpcReceiver will be calling |
| + // to actually do some work. |
| + final T target; |
| + |
| + RpcReceiver(T this.target, ReceivePort this._receivePort) { |
| + // place this receiver in the receiver registry |
| + _register(this); |
| + |
| + // start listening on the receive port for command messages |
| + _receivePort.receive((var message, SendPort replyTo) { |
| + String command = message["command"]; |
| + |
| + // filter incoming arguments (looking for SendPorts |
| + // that we need to translate to RpcReceiver objects) |
| + List args = _filterIncomingArgs(message["args"]); |
| + |
| + // Call the derived RpcReceiver to execute the command |
| + // (if the command throws an exception, then catch the |
| + // exception, serialize it, and send as the reply) |
| + Object reply; |
| + try { |
| + reply = receiveCommand(message["command"], args); |
| + } catch (var e) { |
| + // TODO(mattsh) - would really prefer to catch Object, but currently |
| + // bug http://code.google.com/p/dart/issues/detail?id=469 |
| + reply = RpcException.format(e); |
| + } |
| + |
| + reply = _filterReply(reply); |
| + |
| + // send reply back to the proxy |
| + replyTo.send(reply, null); |
| + }); |
| + } |
| + |
| + /** |
| + * Translate any ReceivePort objects in the arguments to |
| + * the corresponding target object. |
| + */ |
| + static List _filterIncomingArgs(List originalArgs) { |
| + List args = new List(); |
| + var i = 0; |
| + if (originalArgs != null) { |
| + for (var arg in originalArgs) { |
| + if (arg is SendPort) { |
| + if (_receivers[arg] == null) { |
| + throw "can't find receiver for SendPort"; |
| + } |
| + arg = _receivers[arg].target; |
| + if (arg == null) { |
| + throw "receiver is missing target"; |
| + } |
| + } |
| + args.add(arg); |
| + i++; |
| + } |
| + } |
| + return args; |
| + } |
| + |
| + /** |
| + * Walk over the reply that this receiver is about to send |
| + * back, and: |
| + * 1. translate RpcReceiver objects in the reply to the corresponding |
| + * ReceivePort. |
|
Siggi Cherem (dart-lang)
2011/11/30 01:44:38
nit: is there a 2. ?, remove the bullet list?
mattsh
2011/11/30 05:03:33
Done.
|
| + * |
| + * TODO(mattsh) need to walk deeply |
|
Siggi Cherem (dart-lang)
2011/11/30 01:46:45
TODO's shouldn't be inside the /** */ docs, so tha
mattsh
2011/11/30 05:03:33
Done.
|
| + */ |
| + static _filterReply(Object reply) { |
| + if (reply is RpcReceiver) { |
| + RpcReceiver receiver = reply; |
| + reply = receiver._receivePort.toSendPort(); |
| + } |
| + return reply; |
| + } |
| + |
| + /** |
| + * (implemented by derived classes). |
| + * |
| + * parameters - |
| + * command - String identifying what command to execute |
| + * on the target object |
| + * args - list of arguments to the command (if any arguments |
| + * were ReceivePorts, these have been translated to the |
| + * corresponding target objects, so this List79 will not |
| + * contain any ReceivePorts) |
| + */ |
| + abstract Object receiveCommand(String command, List args); |
| +} |
| + |
| +// TODO - hack - need better way to serialize exceptions. For now |
|
Siggi Cherem (dart-lang)
2011/11/30 01:44:38
TODO(mattsh)?
no need to say 'hack'
mattsh
2011/11/30 05:03:33
Done.
|
| +// we take the message, and prefix with a recognizable string. |
| +class RpcException implements Exception { |
| + |
| + static final String prefix = "RpcException:"; |
| + |
| + final String message; |
| + const RpcException(String this.message); |
| + |
| + String toString() { |
| + return message; |
| + } |
| + |
| + static String format(Exception e) { |
| + return prefix + e.toString(); |
| + } |
| + |
| + static RpcException parse(Object object) { |
| + if (object === null || !(object is String)) { |
| + return null; |
| + } |
| + String s = object; |
| + if (!s.startsWith(prefix)) { |
| + return null; |
| + } |
| + return new RpcException(s.substring(prefix.length, s.length)); |
| + } |
| +} |