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