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..873b3297c0d2003df2ee41d728950d0d3fbd3315 |
| --- /dev/null |
| +++ b/corelib/src/proxy.dart |
| @@ -0,0 +1,160 @@ |
| +// 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 |
|
Siggi Cherem (dart-lang)
2011/11/11 23:41:51
generally use the dart-doc style through-out (e.g.
|
| + * |
| + * 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; |
|
Ben Laurie (Google)
2011/11/11 21:51:43
_futurePort?
|
| + RpcProxy(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). |
|
Ben Laurie (Google)
2011/11/11 21:51:43
How does that work? I don't see code to send RpcPr
mattsh
2011/11/11 22:16:02
Right. That's not in this CL yet, but plan to add
Siggi Cherem (dart-lang)
2011/11/11 23:41:51
is the purpose of this to establish a bi-direction
|
| + * |
| + * 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) { |
| + Completer completer = new Completer(); |
| + futurePort.then((SendPort port) { |
| + port.call({"command" : command, "args" : args}).receive((value, ignoreReplyTo) { |
|
Siggi Cherem (dart-lang)
2011/11/11 23:41:51
80
|
| + assert(ignoreReplyTo === null); |
| + value = _filterException(value); |
| + if (value is Exception) { |
| + completer.completeException(value); |
| + } else { |
| + completer.complete(value); |
| + } |
| + } |
| + ); |
| + }); |
| + return completer.future; |
| + } |
| + |
| + // 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). |
| + * |
| + */ |
| +class RpcReceiver { |
| + |
| + // 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 |
| + final ReceivePort _receivePort; |
| + |
| + RpcReceiver(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"]; |
| + List<Object> args = 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 (Object e) { |
| + reply = RpcException.format(e); |
| + } |
| + |
| + // send reply back to the proxy |
| + replyTo.send(reply, null); |
| + }); |
| + } |
| + |
| + /** |
| + * (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 |
| +// we take the message, and prefix with a recognizable string. |
| +class RpcException implements Exception { |
| + |
| + static final String prefix = "RpcException:"; |
| + |
| + final String message; |
| + const RpcException(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)); |
| + } |
| +} |