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