| 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 * Background: |
| 7 * |
| 8 * There are two "target" classes here that are the actual implementation |
| 9 * of some service that performs some function. These "target" classes |
| 10 * do not know about ports, isolates or rpc. They expose a local (usually |
| 11 * synchronous) interface to perform some task. In this example, the "target" |
| 12 * classes are very simple: |
| 13 * |
| 14 * Mint - creates new Purse objects with an initial amount of money |
| 15 * Purse - holds an amount of money, and supports making deposits |
| 16 * by taking money out of another purse. |
| 17 * |
| 18 * The point of this example is to show how to create a new Dart isolate |
| 19 * and run Mint and Purse objects in that isolate, and communicate with |
| 20 * these objects with a SendPort/ReceivePort message/reply conversation, |
| 21 * where command arguments are marshalled and unmarshalled by RpcProxy and |
| 22 * RpcReceiver objects. |
| 23 * |
| 24 * This example also shows: |
| 25 * 1. how the messages in the conversation can refer to target |
| 26 * objects besides the direct recipient of the message |
| 27 * 2. how target objects can throw exceptions and these are propagated back |
| 28 * through the SendPort/ReceivePort channel and back to the Future |
| 29 * object that is waiting for the reply. |
| 30 * |
| 31 * Classes: |
| 32 * |
| 33 * Mint - used to create Purse objects |
| 34 * Purse - holds a bank balance, supports depositing money from another Purse |
| 35 * MintReceiver - listens on a ReceivePort and forwards commands to Mint |
| 36 * PurseReceiver - listens on a ReceivePort and forwards commands to Purse |
| 37 * MintProxy - sends commands to a SendPort (which is connected to a |
| 38 * MintReceiver) |
| 39 * PurseProxy - sends commands to a SendPort (which is connected to a |
| 40 * PurseReceiver) |
| 41 * MintIsolate - used to spawn a new Isolate and return the initial MintProxy |
| 42 * |
| 43 * Base classes used: (these are defined in proxy.dart) |
| 44 * RpcReceiver - MintReceiver and PurseReceiver derive from RpcReceiver to |
| 45 * get behavior common to all Receivers. |
| 46 * RpcProxy - MintProxy and PurseProxy derive from RpcProxy to get behavior |
| 47 * common to all proxies |
| 48 * |
| 49 * Explanation: |
| 50 * |
| 51 * We make a "receiver" object (MintReceiver and PurseReceiver) for each |
| 52 * target object that is being exposed. Each "receiver" object gets |
| 53 * its own ReceivePort, where it listen for messages that it interprets |
| 54 * as commands for its target object. |
| 55 * |
| 56 * When a command is received by a receiver, the receiver calls the appropriate |
| 57 * method on the the target object. The receiver object runs in the same |
| 58 * isolate as its target object. This isolate is called the "service" isolate. |
| 59 * |
| 60 * RpcProxy objects run in the "client" isolate. Each proxy object has a |
| 61 * SendPort which is connected to the ReceivePort of a corresponding receiver |
| 62 * object. |
| 63 * |
| 64 * So, a MintProxy object (running in the "client" isolate) is connected |
| 65 * to a MintReceiver object (running in the "service" isolate) which is |
| 66 * connected to a Mint target object (also running in the "service" isolate). |
| 67 * |
| 68 * Startup/Shutdown: |
| 69 * |
| 70 * The MintIsolate has a method open that spawns a new isolate and returns |
| 71 * a MintProxy object. |
| 72 * |
| 73 * To shut down the MintIsolate, call 'close' on the MintProxy object. This |
| 74 * will close all receive ports of all receivers in the isolate, which will |
| 75 * cause the mint isolate to shutdown. |
| 76 */ |
| 77 |
| 78 class Mint { |
| 79 Mint(); |
| 80 Purse createPurse(int initialBalance) { |
| 81 return new Purse(initialBalance); |
| 82 } |
| 83 } |
| 84 |
| 85 class Purse { |
| 86 int _balance; |
| 87 |
| 88 Purse(this._balance) {} |
| 89 |
| 90 int queryBalance() { |
| 91 return _balance; |
| 92 } |
| 93 |
| 94 int deposit(int amount, Purse source) { |
| 95 if (source._balance < amount) { |
| 96 throw "OverdraftException"; |
| 97 } |
| 98 source._balance -= amount; |
| 99 _balance += amount; |
| 100 return _balance; |
| 101 } |
| 102 } |
| 103 |
| 104 |
| 105 class MintProxy extends RpcProxy { |
| 106 MintProxy(Future<SendPort> sendPort) : super(sendPort) { } |
| 107 Future<PurseProxy> createPurse(int initialBalance) { |
| 108 return sendCommand("createPurse", [initialBalance], (sendPort) { |
| 109 Completer<SendPort> completer = new Completer(); |
| 110 completer.complete(sendPort); |
| 111 return new PurseProxy(completer.future); |
| 112 }); |
| 113 } |
| 114 Future<String> close() { |
| 115 return sendCommand("close", null, null); |
| 116 } |
| 117 } |
| 118 |
| 119 class MintReceiver extends RpcReceiver<Mint> { |
| 120 MintReceiver(ReceivePort receivePort) : super(new Mint(), receivePort) {} |
| 121 Object receiveCommand(String command, List args) { |
| 122 switch(command) { |
| 123 case "createPurse": |
| 124 int balance = args[0]; |
| 125 Purse purse = target.createPurse(balance); |
| 126 return new PurseReceiver(purse, new ReceivePort()); |
| 127 case "close": |
| 128 RpcReceiver.closeAll(); |
| 129 return "close command processed"; |
| 130 default: |
| 131 throw "MintReceiver unrecognized command"; |
| 132 } |
| 133 } |
| 134 } |
| 135 |
| 136 class PurseProxy extends RpcProxy { |
| 137 PurseProxy(Future<SendPort> sendPort) : super(sendPort) { } |
| 138 Future<int> queryBalance() { |
| 139 return sendCommand("queryBalance", null, null); |
| 140 } |
| 141 Future<int> deposit(int amount, PurseProxy from) { |
| 142 return sendCommand("deposit", [amount, from], null); |
| 143 } |
| 144 } |
| 145 |
| 146 class PurseReceiver extends RpcReceiver<Purse> { |
| 147 |
| 148 PurseReceiver(Purse purse, ReceivePort receivePort) : super(purse, receivePort
) {} |
| 149 |
| 150 Object receiveCommand(String command, List args) { |
| 151 switch(command) { |
| 152 case "queryBalance": |
| 153 return target.queryBalance(); |
| 154 case "deposit": |
| 155 int amount = args[0]; |
| 156 Purse fromPurse = args[1]; |
| 157 return target.deposit(amount, fromPurse); |
| 158 default: |
| 159 throw "PurseReceiver unrecognized command"; |
| 160 } |
| 161 } |
| 162 } |
| 163 |
| 164 class MintIsolate extends Isolate { |
| 165 MintIsolate() : super.light() {} |
| 166 |
| 167 MintProxy open() { |
| 168 return new MintProxy(spawn()); |
| 169 } |
| 170 |
| 171 void main() { |
| 172 ReceivePort receivePort = port; |
| 173 new MintReceiver(receivePort); |
| 174 } |
| 175 } |
| 176 |
| 177 class MintTest { |
| 178 static void testMain() { |
| 179 print("starting test"); |
| 180 MintProxy mint = new MintIsolate().open(); |
| 181 mint.createPurse(100).then((PurseProxy purse1) { |
| 182 purse1.queryBalance().then((int balance) { |
| 183 Expect.equals(100, balance); |
| 184 mint.createPurse(0).then((PurseProxy purse2) { |
| 185 purse2.queryBalance().then((int balance) { |
| 186 Expect.equals(0, balance); |
| 187 purse2.deposit(5, purse1).then((int newBalance) { |
| 188 Expect.equals(0 + 5, newBalance); |
| 189 purse1.queryBalance().then((int balance) { |
| 190 Expect.equals(100 - 5, balance); |
| 191 }); |
| 192 purse2.deposit(42, purse1).then((int newBalance) { |
| 193 int balance2 = newBalance; |
| 194 Expect.equals(0 + 5 + 42, balance2); |
| 195 purse1.queryBalance().then((int balance) { |
| 196 int balance1 = balance; |
| 197 Expect.equals(100 - 5 - 42, balance); |
| 198 // Now try to deposit more money into purse1 than |
| 199 // is currently in purse2. Make sure we get an exception |
| 200 // doing this. |
| 201 Future<int> badBalance = purse1.deposit(1000, purse2); |
| 202 badBalance.then((int newBalance) { |
| 203 // Should never arrive here because there are |
| 204 // insufficient funds to actually do the deposit |
| 205 Expect.fail("did not detect overdraft"); |
| 206 }); |
| 207 badBalance.handleException((Object exception) { |
| 208 if (exception.toString().contains( |
| 209 "OverdraftException", 0)) { |
| 210 print("Correctly detected overdraft."); |
| 211 // Check that the balance in each purse is unchanged from |
| 212 // before we attempted to do the overdraft. |
| 213 purse1.queryBalance().then((int balance) { |
| 214 Expect.equals(balance, balance1); |
| 215 purse2.queryBalance().then((int balance) { |
| 216 Expect.equals(balance, balance2); |
| 217 // OK, we're all done now, close down the mint isolate |
| 218 mint.close().then((reply) { |
| 219 print(reply); |
| 220 }); |
| 221 }); |
| 222 }); |
| 223 return true; |
| 224 } else { |
| 225 return false; |
| 226 } |
| 227 }); |
| 228 }); |
| 229 }); |
| 230 }); |
| 231 }); |
| 232 }); |
| 233 }); |
| 234 }); |
| 235 print ("exit main"); |
| 236 } |
| 237 } |
| 238 |
| 239 main() { |
| 240 MintTest.testMain(); |
| 241 } |
| OLD | NEW |