Merge lp:~mandel/ubuntuone-windows-installer/implement_dotnet_ipc_events into lp:ubuntuone-windows-installer/beta
- implement_dotnet_ipc_events
- Merge into beta
Status: | Merged |
---|---|
Approved by: | Rick McBride |
Approved revision: | 48 |
Merged at revision: | 64 |
Proposed branch: | lp:~mandel/ubuntuone-windows-installer/implement_dotnet_ipc_events |
Merge into: | lp:ubuntuone-windows-installer/beta |
Prerequisite: | lp:~mandel/ubuntuone-windows-installer/implement_port_manager |
Diff against target: |
981 lines (+790/-32) 16 files modified
main.build (+1/-1) src/Canonical.UbuntuOne.Common/Canonical.UbuntuOne.Common.csproj (+1/-0) src/Canonical.UbuntuOne.Common/OperationContracts/IEventNotifier.cs (+59/-0) src/Canonical.UbuntuOne.Common/OperationContracts/ISyncConfiguration.cs (+2/-6) src/Canonical.UbuntuOne.Common/OperationContracts/ISyncDaemon.cs (+1/-5) src/Canonical.UbuntuOne.Common/OperationContracts/ISyncDaemonClient.cs (+2/-2) src/Canonical.UbuntuOne.Common/OperationContracts/ISyncFileManager.cs (+2/-6) src/Canonical.UbuntuOne.Common/OperationContracts/ISyncFolders.cs (+2/-6) src/Canonical.UbuntuOne.Common/OperationContracts/ISyncShares.cs (+2/-6) src/Canonical.UbuntuOne.ProcessDispatcher.Tests/Canonical.UbuntuOne.ProcessDispatcher.Tests.csproj (+1/-0) src/Canonical.UbuntuOne.ProcessDispatcher.Tests/EventNotifierFixture.cs (+239/-0) src/Canonical.UbuntuOne.ProcessDispatcher/CallerContext.cs (+43/-0) src/Canonical.UbuntuOne.ProcessDispatcher/Canonical.UbuntuOne.ProcessDispatcher.csproj (+4/-0) src/Canonical.UbuntuOne.ProcessDispatcher/EventNotifier.cs (+357/-0) src/Canonical.UbuntuOne.ProcessDispatcher/ICallerContext.cs (+34/-0) src/Canonical.UbuntuOne.ProcessDispatcher/IEventDispatcher.cs (+40/-0) |
To merge this branch: | bzr merge lp:~mandel/ubuntuone-windows-installer/implement_dotnet_ipc_events |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Rick McBride (community) | Approve | ||
Stuart Colville (community) | Approve | ||
Review via email: mp+32759@code.launchpad.net |
Commit message
Description of the change
Provides the technique used to communicate the different windows client the firing of events from the python code.
Rick McBride (rmcbride) wrote : | # |
Looks good, however 'NAnt.exe tests' hangs after
'Executng Canonical.
At that point nunit-console.exe spikes at 50% processor utilization and about 30,048K. The first time I tried this I left it running like this for two hours and it never came back.
Once the script is broken w/ CTRL-C, nunit-console.exe has to be killed at the task manager before anything further can be run in this context.
- 47. By Manuel de la Peña
-
Modify the tests to beignored until RhinoMocks has multithreading support.
- 48. By Manuel de la Peña
-
Fix echo typo.
Manuel de la Peña (mandel) wrote : | # |
Rick,
This issue is again due to RhinoMocks not having support with multithreading (major PITA in this area of the code).
As we did with the other test that had this same issue I have added an ignore message. Right now I do not have the time to look at RhinoMocks to add the support or to use a completely diff lib in the project for mocking (I doubt there is any better than Rhino and open sourced).
Rick McBride (rmcbride) wrote : | # |
Works now with that test disabled.
Preview Diff
1 | === modified file 'main.build' |
2 | --- main.build 2010-08-10 10:04:31 +0000 |
3 | +++ main.build 2010-08-18 09:45:53 +0000 |
4 | @@ -176,7 +176,7 @@ |
5 | program="nunit-console.exe" |
6 | commandline="Canonical.UbuntuOne.Common.Tests.dll /xml=../../../../test-results/Canonical.UbuntuOne.Common.Tests-Result.xml" /> |
7 | |
8 | - <echo>Executng Canonical.UbuntuOne.ProcessDispatcher.Tests</echo> |
9 | + <echo>Executing Canonical.UbuntuOne.ProcessDispatcher.Tests</echo> |
10 | <exec basedir="tools/NUnit" |
11 | managed="true" |
12 | workingdir="src/Canonical.UbuntuOne.ProcessDispatcher.Tests/bin/${enviroment}" |
13 | |
14 | === modified file 'src/Canonical.UbuntuOne.Common/Canonical.UbuntuOne.Common.csproj' |
15 | --- src/Canonical.UbuntuOne.Common/Canonical.UbuntuOne.Common.csproj 2010-08-04 14:36:09 +0000 |
16 | +++ src/Canonical.UbuntuOne.Common/Canonical.UbuntuOne.Common.csproj 2010-08-18 09:45:53 +0000 |
17 | @@ -58,6 +58,7 @@ |
18 | <Compile Include="Container\SpringContainer.cs" /> |
19 | <Compile Include="Container\UnsatisfiedDependencyException.cs" /> |
20 | <Compile Include="IAuthentication.cs" /> |
21 | + <Compile Include="OperationContracts\IEventNotifier.cs" /> |
22 | <Compile Include="Utils\ApplicationWrapper.cs" /> |
23 | <Compile Include="Utils\Explorer.cs" /> |
24 | <Compile Include="Utils\ExplorerException.cs" /> |
25 | |
26 | === added file 'src/Canonical.UbuntuOne.Common/OperationContracts/IEventNotifier.cs' |
27 | --- src/Canonical.UbuntuOne.Common/OperationContracts/IEventNotifier.cs 1970-01-01 00:00:00 +0000 |
28 | +++ src/Canonical.UbuntuOne.Common/OperationContracts/IEventNotifier.cs 2010-08-18 09:45:53 +0000 |
29 | @@ -0,0 +1,59 @@ |
30 | +/* |
31 | + * Copyright 2010 Canonical Ltd. |
32 | + * |
33 | + * This file is part of UbuntuOne on Windows. |
34 | + * |
35 | + * UbuntuOne on Windows is free software: you can redistribute it and/or modify |
36 | + * it under the terms of the GNU Lesser General Public License version |
37 | + * as published by the Free Software Foundation. |
38 | + * |
39 | + * Ubuntu One on Windows is distributed in the hope that it will be useful, |
40 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
41 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
42 | + * GNU Lesser General Public License for more details. |
43 | + * |
44 | + * You should have received a copy of the GNU Lesser General Public License |
45 | + * along with UbuntuOne for Windows. If not, see <http://www.gnu.org/licenses/>. |
46 | + * |
47 | + * Authors: Manuel de la Peña <manuel.delapena@canonical.com> |
48 | + */ |
49 | +using System.ServiceModel; |
50 | + |
51 | +namespace Canonical.UbuntuOne.Common.OperationContracts |
52 | +{ |
53 | + /// <summary> |
54 | + /// Interface to be implemented by those objects that will allow to |
55 | + /// </summary> |
56 | + [ServiceContract( CallbackContract = typeof(ISyncDaemonClient), |
57 | + SessionMode = SessionMode.Required)] |
58 | + public interface IEventNotifier |
59 | + { |
60 | + /// <summary> |
61 | + /// Method that allows to register a client that will be reciving notitications |
62 | + /// from than point until it unregisters |
63 | + /// </summary> |
64 | + [OperationContract(IsOneWay = true)] |
65 | + void RegisterClient(); |
66 | + |
67 | + /// <summary> |
68 | + /// Method that allows to unregister a client from receiving notification. |
69 | + /// </summary> |
70 | + [OperationContract(IsOneWay = true)] |
71 | + void UnregisterClient(); |
72 | + |
73 | + /// <summary> |
74 | + /// Method that tells the notifier to start listening for other events using a separate thread. |
75 | + /// </summary> |
76 | + void StartListening(); |
77 | + |
78 | + /// <summary> |
79 | + /// Method that tells the notifier to stop listning for events. |
80 | + /// </summary> |
81 | + void StopListening(); |
82 | + |
83 | + /// <summary> |
84 | + /// Gets if the notifier is listening. |
85 | + /// </summary> |
86 | + bool IsListening { get; } |
87 | + } |
88 | +} |
89 | |
90 | === modified file 'src/Canonical.UbuntuOne.Common/OperationContracts/ISyncConfiguration.cs' |
91 | --- src/Canonical.UbuntuOne.Common/OperationContracts/ISyncConfiguration.cs 2010-08-10 08:50:26 +0000 |
92 | +++ src/Canonical.UbuntuOne.Common/OperationContracts/ISyncConfiguration.cs 2010-08-18 09:45:53 +0000 |
93 | @@ -1,4 +1,4 @@ |
94 | -/** |
95 | +/* |
96 | * Copyright 2010 Canonical Ltd. |
97 | * |
98 | * This file is part of UbuntuOne on Windows. |
99 | @@ -25,11 +25,7 @@ |
100 | /// Interface to be implemented by those object that allows to interact with the |
101 | /// different setting of the sync daemon. |
102 | /// </summary> |
103 | - [ServiceContract( |
104 | - Name = "SyncConfiguration", |
105 | - Namespace = "http://one.ubuntu.com/syncconfiguration", |
106 | - CallbackContract = typeof(ISyncDaemonClient), |
107 | - SessionMode = SessionMode.Required)] |
108 | + [ServiceContract] |
109 | public interface ISyncConfiguration |
110 | { |
111 | /// <summary> |
112 | |
113 | === modified file 'src/Canonical.UbuntuOne.Common/OperationContracts/ISyncDaemon.cs' |
114 | --- src/Canonical.UbuntuOne.Common/OperationContracts/ISyncDaemon.cs 2010-08-10 08:50:26 +0000 |
115 | +++ src/Canonical.UbuntuOne.Common/OperationContracts/ISyncDaemon.cs 2010-08-18 09:45:53 +0000 |
116 | @@ -26,11 +26,7 @@ |
117 | /// The implementators of this interface should allow the callng clients to perform a number of applications on |
118 | /// the sync daemon from windows. This will allow users to interact with the daemon through .Net. |
119 | /// </summary> |
120 | - [ServiceContract( |
121 | - Name = "SyncDaemon", |
122 | - Namespace = "http://one.ubuntu.com/syncdaemon", |
123 | - CallbackContract = typeof(ISyncDaemonClient), |
124 | - SessionMode = SessionMode.Required)] |
125 | + [ServiceContract] |
126 | public interface ISyncDaemon |
127 | { |
128 | |
129 | |
130 | === modified file 'src/Canonical.UbuntuOne.Common/OperationContracts/ISyncDaemonClient.cs' |
131 | --- src/Canonical.UbuntuOne.Common/OperationContracts/ISyncDaemonClient.cs 2010-08-05 07:13:28 +0000 |
132 | +++ src/Canonical.UbuntuOne.Common/OperationContracts/ISyncDaemonClient.cs 2010-08-18 09:45:53 +0000 |
133 | @@ -1,4 +1,4 @@ |
134 | -/** |
135 | +/* |
136 | * Copyright 2010 Canonical Ltd. |
137 | * |
138 | * This file is part of UbuntuOne on Windows. |
139 | @@ -25,7 +25,7 @@ |
140 | /// Interface to be implementedby a sync daemon client that wants to interact with the |
141 | /// daemon that manages the Ubuntu One storage in a machine. |
142 | /// </summary> |
143 | - [ServiceContract(Namespace = "http://one.ubuntu.com/client")] |
144 | + [ServiceContract] |
145 | public interface ISyncDaemonClient |
146 | { |
147 | /// <summary> |
148 | |
149 | === modified file 'src/Canonical.UbuntuOne.Common/OperationContracts/ISyncFileManager.cs' |
150 | --- src/Canonical.UbuntuOne.Common/OperationContracts/ISyncFileManager.cs 2010-08-10 08:50:26 +0000 |
151 | +++ src/Canonical.UbuntuOne.Common/OperationContracts/ISyncFileManager.cs 2010-08-18 09:45:53 +0000 |
152 | @@ -1,4 +1,4 @@ |
153 | -/** |
154 | +/* |
155 | * Copyright 2010 Canonical Ltd. |
156 | * |
157 | * This file is part of UbuntuOne on Windows. |
158 | @@ -25,11 +25,7 @@ |
159 | /// <summary> |
160 | /// Service that provides method to interact with the FileManager used by the sync daemon. |
161 | /// </summary> |
162 | - [ServiceContract( |
163 | - Name = "SyncFileManager", |
164 | - Namespace = "http://one.ubuntu.com/syncfilemanager", |
165 | - CallbackContract = typeof(ISyncDaemonClient), |
166 | - SessionMode = SessionMode.Required)] |
167 | + [ServiceContract] |
168 | public interface ISyncFileManager |
169 | { |
170 | |
171 | |
172 | === modified file 'src/Canonical.UbuntuOne.Common/OperationContracts/ISyncFolders.cs' |
173 | --- src/Canonical.UbuntuOne.Common/OperationContracts/ISyncFolders.cs 2010-08-10 08:50:26 +0000 |
174 | +++ src/Canonical.UbuntuOne.Common/OperationContracts/ISyncFolders.cs 2010-08-18 09:45:53 +0000 |
175 | @@ -1,4 +1,4 @@ |
176 | -/** |
177 | +/* |
178 | * Copyright 2010 Canonical Ltd. |
179 | * |
180 | * This file is part of UbuntuOne on Windows. |
181 | @@ -26,11 +26,7 @@ |
182 | /// Interface to be implemented by those objects that allow to interact with the data related to |
183 | /// the sync folders by the Ubuntu One sync daemon. |
184 | /// </summary> |
185 | - [ServiceContract( |
186 | - Name = "SyncFolders", |
187 | - Namespace = "http://one.ubuntu.com/syncfolders", |
188 | - CallbackContract = typeof(ISyncDaemonClient), |
189 | - SessionMode = SessionMode.Required)] |
190 | + [ServiceContract] |
191 | public interface ISyncFolders |
192 | { |
193 | |
194 | |
195 | === modified file 'src/Canonical.UbuntuOne.Common/OperationContracts/ISyncShares.cs' |
196 | --- src/Canonical.UbuntuOne.Common/OperationContracts/ISyncShares.cs 2010-08-10 08:50:26 +0000 |
197 | +++ src/Canonical.UbuntuOne.Common/OperationContracts/ISyncShares.cs 2010-08-18 09:45:53 +0000 |
198 | @@ -1,4 +1,4 @@ |
199 | -/** |
200 | +/* |
201 | * Copyright 2010 Canonical Ltd. |
202 | * |
203 | * This file is part of UbuntuOne on Windows. |
204 | @@ -26,11 +26,7 @@ |
205 | /// Contract to be implemented by a object that allows to interact with the different shares |
206 | /// that a user has in the Ubuntu One storage infrastructure. |
207 | /// </summary> |
208 | - [ServiceContract( |
209 | - Name = "SyncShares", |
210 | - Namespace = "http://one.ubuntu.com/shares", |
211 | - CallbackContract = typeof(ISyncDaemonClient), |
212 | - SessionMode = SessionMode.Required)] |
213 | + [ServiceContract] |
214 | public interface ISyncShares |
215 | { |
216 | |
217 | |
218 | === modified file 'src/Canonical.UbuntuOne.ProcessDispatcher.Tests/Canonical.UbuntuOne.ProcessDispatcher.Tests.csproj' |
219 | --- src/Canonical.UbuntuOne.ProcessDispatcher.Tests/Canonical.UbuntuOne.ProcessDispatcher.Tests.csproj 2010-08-12 07:59:15 +0000 |
220 | +++ src/Canonical.UbuntuOne.ProcessDispatcher.Tests/Canonical.UbuntuOne.ProcessDispatcher.Tests.csproj 2010-08-18 09:45:53 +0000 |
221 | @@ -55,6 +55,7 @@ |
222 | <Compile Include="..\Version.cs"> |
223 | <Link>Properties\Version.cs</Link> |
224 | </Compile> |
225 | + <Compile Include="EventNotifierFixture.cs" /> |
226 | <Compile Include="Net\PortManagerFixture.cs" /> |
227 | <Compile Include="Net\TcpClientCommunicatorFixture.cs" /> |
228 | <Compile Include="Net\TcpClientFactoryFixture.cs" /> |
229 | |
230 | === added file 'src/Canonical.UbuntuOne.ProcessDispatcher.Tests/EventNotifierFixture.cs' |
231 | --- src/Canonical.UbuntuOne.ProcessDispatcher.Tests/EventNotifierFixture.cs 1970-01-01 00:00:00 +0000 |
232 | +++ src/Canonical.UbuntuOne.ProcessDispatcher.Tests/EventNotifierFixture.cs 2010-08-18 09:45:53 +0000 |
233 | @@ -0,0 +1,239 @@ |
234 | +/* |
235 | + * Copyright 2010 Canonical Ltd. |
236 | + * |
237 | + * This file is part of UbuntuOne on Windows. |
238 | + * |
239 | + * UbuntuOne on Windows is free software: you can redistribute it and/or modify |
240 | + * it under the terms of the GNU Lesser General Public License version |
241 | + * as published by the Free Software Foundation. |
242 | + * |
243 | + * Ubuntu One on Windows is distributed in the hope that it will be useful, |
244 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
245 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
246 | + * GNU Lesser General Public License for more details. |
247 | + * |
248 | + * You should have received a copy of the GNU Lesser General Public License |
249 | + * along with UbuntuOne for Windows. If not, see <http://www.gnu.org/licenses/>. |
250 | + * |
251 | + * Authors: Manuel de la Peña <manuel.delapena@canonical.com> |
252 | + */ |
253 | +using System; |
254 | +using System.Net.Sockets; |
255 | +using Canonical.UbuntuOne.Common; |
256 | +using Canonical.UbuntuOne.ProcessDispatcher.Net; |
257 | +using NUnit.Framework; |
258 | +using Rhino.Mocks; |
259 | + |
260 | +namespace Canonical.UbuntuOne.ProcessDispatcher.Tests |
261 | +{ |
262 | + [TestFixture] |
263 | + public class EventNotifierFixture |
264 | + { |
265 | + #region Vars |
266 | + |
267 | + private MockRepository _mocks; |
268 | + private IEventDispatcher _eventDispatcher; |
269 | + private ISyncDaemonClient _notifyIconClient; |
270 | + private ICallerContext<ISyncDaemonClient> _callerContext; |
271 | + private IPortManager _portManager; |
272 | + private IProtobufSerializer _serilizer; |
273 | + private TcpClient _client; |
274 | + private EventNotifier _notifier; |
275 | + private int _freePort; |
276 | + |
277 | + #endregion |
278 | + |
279 | + #region Setup |
280 | + |
281 | + [SetUp] |
282 | + public void Setup() |
283 | + { |
284 | + _mocks = new MockRepository(); |
285 | + _eventDispatcher = _mocks.DynamicMock<IEventDispatcher>(); |
286 | + _notifyIconClient = _mocks.StrictMock<ISyncDaemonClient>(); |
287 | + _mocks.StrictMock<ISyncDaemonClient>(); |
288 | + _callerContext = _mocks.StrictMock<ICallerContext<ISyncDaemonClient>>(); |
289 | + _portManager = _mocks.StrictMock<IPortManager>(); |
290 | + _serilizer = _mocks.DynamicMock<IProtobufSerializer>(); |
291 | + _freePort = 50128; |
292 | + _notifier = new EventNotifier |
293 | + { |
294 | + CallerContext = _callerContext, |
295 | + EventDispatcher = _eventDispatcher, |
296 | + PortManager = _portManager, |
297 | + ProtobufSerializer = _serilizer |
298 | + }; |
299 | + } |
300 | + |
301 | + [TearDown] |
302 | + public void TearDown() |
303 | + { |
304 | + _notifier.KillListeningThread(); |
305 | + } |
306 | + |
307 | + #endregion |
308 | + |
309 | + #region Tests |
310 | + |
311 | + [Test] |
312 | + public void RegisterClientTest() |
313 | + { |
314 | + using (_mocks.Record()) |
315 | + { |
316 | + Expect.Call(_callerContext.GetCaller()) |
317 | + .Return(_notifyIconClient) |
318 | + .Repeat.Once(); |
319 | + } |
320 | + using(_mocks.Playback()) |
321 | + { |
322 | + _notifier.RegisterClient(); |
323 | + } |
324 | + } |
325 | + |
326 | + [Test] |
327 | + public void UnregisterClientRegisteredTest() |
328 | + { |
329 | + using (_mocks.Record()) |
330 | + { |
331 | + Expect.Call(_callerContext.GetCaller()) |
332 | + .Return(_notifyIconClient) |
333 | + .Repeat.Once(); |
334 | + } |
335 | + using (_mocks.Playback()) |
336 | + { |
337 | + _notifier.UnregisterClient(); |
338 | + } |
339 | + } |
340 | + |
341 | + [Test] |
342 | + public void UnregisterClientNotRegisteredTest() |
343 | + { |
344 | + using (_mocks.Record()) |
345 | + { |
346 | + Expect.Call(_callerContext.GetCaller()) |
347 | + .Return(_notifyIconClient) |
348 | + .Repeat.Once(); |
349 | + } |
350 | + using (_mocks.Playback()) |
351 | + { |
352 | + _notifier.UnregisterClient(); |
353 | + } |
354 | + } |
355 | + |
356 | + [Test] |
357 | + public void StartListeningTest() |
358 | + { |
359 | + try |
360 | + { |
361 | + Assert.Ignore("This test is not reliable until RhinoMocks has multithread support."); |
362 | + //using (_mocks.Record()) |
363 | + //{ |
364 | + // Expect.Call(_portManager.GetDotNetClientPort()) |
365 | + // .Return(_freePort) |
366 | + // .Repeat.Any(); |
367 | + //} |
368 | + //using (_mocks.Playback()) |
369 | + //{ |
370 | + // _notifier.StartListening(); |
371 | + // // create a client that will connect to the port |
372 | + // while (!_notifier.IsListening) |
373 | + // { |
374 | + // // wait doing nothing :( |
375 | + // } |
376 | + // _client = new TcpClient("localhost", _freePort); |
377 | + // // write data in the buffer to tell the the event notifier |
378 | + // var stream = _client.GetStream(); |
379 | + // var data = System.Text.Encoding.ASCII.GetBytes("Test"); |
380 | + // stream.Write(data, 0, data.Length); |
381 | + // stream.Flush(); |
382 | + // stream.Close(); |
383 | + //} |
384 | + } |
385 | + catch (SocketException) |
386 | + { |
387 | + Assert.Inconclusive( |
388 | + "The test could not be performed due to a socket exception. You firewall is probably blocking connections to {0}", |
389 | + _freePort); |
390 | + } |
391 | + } |
392 | + |
393 | + [Test] |
394 | + [ExpectedException(typeof(InvalidOperationException))] |
395 | + public void StartListeningAlreadyListeningTest() |
396 | + { |
397 | + try |
398 | + { |
399 | + using (_mocks.Record()) |
400 | + { |
401 | + Expect.Call(_portManager.GetDotNetClientPort()) |
402 | + .Return(_freePort) |
403 | + .Repeat.Once(); |
404 | + } |
405 | + using (_mocks.Playback()) |
406 | + { |
407 | + _notifier.StartListening(); |
408 | + _notifier.StartListening(); |
409 | + } |
410 | + } |
411 | + catch (SocketException) |
412 | + { |
413 | + Assert.Inconclusive( |
414 | + "The test could not be performed due to a socket exception. You firewall is probably blocking connections to {0}", |
415 | + _freePort); |
416 | + } |
417 | + } |
418 | + |
419 | + [Test] |
420 | + public void StopListeningTest() |
421 | + { |
422 | + try |
423 | + { |
424 | + using (_mocks.Record()) |
425 | + { |
426 | + Expect.Call(_portManager.GetDotNetClientPort()) |
427 | + .Return(_freePort) |
428 | + .Repeat.Any(); |
429 | + } |
430 | + using (_mocks.Playback()) |
431 | + { |
432 | + _notifier.StartListening(); |
433 | + _notifier.StopListening(); |
434 | + } |
435 | + } |
436 | + catch (SocketException) |
437 | + { |
438 | + Assert.Inconclusive( |
439 | + "The test could not be performed due to a socket exception. You firewall is probably blocking connections to {0}", |
440 | + _freePort); |
441 | + } |
442 | + } |
443 | + |
444 | + [Test] |
445 | + [ExpectedException(typeof(InvalidOperationException))] |
446 | + public void StopListeningExceptionTest() |
447 | + { |
448 | + try |
449 | + { |
450 | + using (_mocks.Record()) |
451 | + { |
452 | + Expect.Call(_portManager.GetDotNetClientPort()) |
453 | + .Return(_freePort) |
454 | + .Repeat.Once(); |
455 | + } |
456 | + using (_mocks.Playback()) |
457 | + { |
458 | + _notifier.StopListening(); |
459 | + } |
460 | + } |
461 | + catch(SocketException) |
462 | + { |
463 | + Assert.Inconclusive( |
464 | + "The test could not be performed due to a socket exception. You firewall is probably blocking connections to {0}", |
465 | + _freePort); |
466 | + } |
467 | + } |
468 | + |
469 | + #endregion |
470 | + |
471 | + } |
472 | +} |
473 | |
474 | === added file 'src/Canonical.UbuntuOne.ProcessDispatcher/CallerContext.cs' |
475 | --- src/Canonical.UbuntuOne.ProcessDispatcher/CallerContext.cs 1970-01-01 00:00:00 +0000 |
476 | +++ src/Canonical.UbuntuOne.ProcessDispatcher/CallerContext.cs 2010-08-18 09:45:53 +0000 |
477 | @@ -0,0 +1,43 @@ |
478 | +/* |
479 | + * Copyright 2010 Canonical Ltd. |
480 | + * |
481 | + * This file is part of UbuntuOne on Windows. |
482 | + * |
483 | + * UbuntuOne on Windows is free software: you can redistribute it and/or modify |
484 | + * it under the terms of the GNU Lesser General Public License version |
485 | + * as published by the Free Software Foundation. |
486 | + * |
487 | + * Ubuntu One on Windows is distributed in the hope that it will be useful, |
488 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
489 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
490 | + * GNU Lesser General Public License for more details. |
491 | + * |
492 | + * You should have received a copy of the GNU Lesser General Public License |
493 | + * along with UbuntuOne for Windows. If not, see <http://www.gnu.org/licenses/>. |
494 | + * |
495 | + * Authors: Manuel de la Peña <manuel.delapena@canonical.com> |
496 | + */ |
497 | +using System.ServiceModel; |
498 | + |
499 | +namespace Canonical.UbuntuOne.ProcessDispatcher |
500 | +{ |
501 | + /// <summary> |
502 | + /// Implementation of the ICallerContext that realies in WCF the retrive the caller of a |
503 | + /// WCF Operation contract. |
504 | + /// </summary> |
505 | + internal class CallerContext<TCaller> : ICallerContext<TCaller> |
506 | + { |
507 | + #region Implementation of ICallerContext<TCaller> |
508 | + |
509 | + /// <summary> |
510 | + /// Returns the object that has called the method, |
511 | + /// </summary> |
512 | + /// <returns>The object that performed the call.</returns> |
513 | + public TCaller GetCaller() |
514 | + { |
515 | + return OperationContext.Current.GetCallbackChannel<TCaller>(); |
516 | + } |
517 | + |
518 | + #endregion |
519 | + } |
520 | +} |
521 | |
522 | === modified file 'src/Canonical.UbuntuOne.ProcessDispatcher/Canonical.UbuntuOne.ProcessDispatcher.csproj' |
523 | --- src/Canonical.UbuntuOne.ProcessDispatcher/Canonical.UbuntuOne.ProcessDispatcher.csproj 2010-08-12 07:59:15 +0000 |
524 | +++ src/Canonical.UbuntuOne.ProcessDispatcher/Canonical.UbuntuOne.ProcessDispatcher.csproj 2010-08-18 09:45:53 +0000 |
525 | @@ -71,6 +71,10 @@ |
526 | <Compile Include="..\Version.cs"> |
527 | <Link>Properties\Version.cs</Link> |
528 | </Compile> |
529 | + <Compile Include="CallerContext.cs" /> |
530 | + <Compile Include="EventNotifier.cs" /> |
531 | + <Compile Include="ICallerContext.cs" /> |
532 | + <Compile Include="IEventDispatcher.cs" /> |
533 | <Compile Include="Net\IPortFinder.cs" /> |
534 | <Compile Include="Net\IPortManager.cs" /> |
535 | <Compile Include="Net\ITcpClient.cs" /> |
536 | |
537 | === added file 'src/Canonical.UbuntuOne.ProcessDispatcher/EventNotifier.cs' |
538 | --- src/Canonical.UbuntuOne.ProcessDispatcher/EventNotifier.cs 1970-01-01 00:00:00 +0000 |
539 | +++ src/Canonical.UbuntuOne.ProcessDispatcher/EventNotifier.cs 2010-08-18 09:45:53 +0000 |
540 | @@ -0,0 +1,357 @@ |
541 | +/* |
542 | + * Copyright 2010 Canonical Ltd. |
543 | + * |
544 | + * This file is part of UbuntuOne on Windows. |
545 | + * |
546 | + * UbuntuOne on Windows is free software: you can redistribute it and/or modify |
547 | + * it under the terms of the GNU Lesser General Public License version |
548 | + * as published by the Free Software Foundation. |
549 | + * |
550 | + * Ubuntu One on Windows is distributed in the hope that it will be useful, |
551 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
552 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
553 | + * GNU Lesser General Public License for more details. |
554 | + * |
555 | + * You should have received a copy of the GNU Lesser General Public License |
556 | + * along with UbuntuOne for Windows. If not, see <http://www.gnu.org/licenses/>. |
557 | + * |
558 | + * Authors: Manuel de la Peña <manuel.delapena@canonical.com> |
559 | + */ |
560 | +using System; |
561 | +using System.Collections.Generic; |
562 | +using System.IO; |
563 | +using System.Net; |
564 | +using System.Net.Sockets; |
565 | +using System.Threading; |
566 | +using Canonical.UbuntuOne.Common; |
567 | +using Canonical.UbuntuOne.Common.OperationContracts; |
568 | +using Canonical.UbuntuOne.ProcessDispatcher.Net; |
569 | +using log4net; |
570 | +using PortManagerImpl = Canonical.UbuntuOne.ProcessDispatcher.Net.PortManager; |
571 | + |
572 | +namespace Canonical.UbuntuOne.ProcessDispatcher |
573 | +{ |
574 | + /// <summary> |
575 | + /// Implementation of the IEventNotifier that receives messages from the python code through sockets and |
576 | + /// broadcasts those messages to the .net clients that have registered to receive them. |
577 | + /// </summary> |
578 | + internal class EventNotifier : IEventNotifier |
579 | + { |
580 | + #region Helper class |
581 | + |
582 | + /// <summary> |
583 | + /// This class is used to allows to keep the state of the async reading operation of the |
584 | + /// socket. |
585 | + /// </summary> |
586 | + private class StateObject |
587 | + { |
588 | + /// <summary> |
589 | + /// The socket we are working with. |
590 | + /// </summary> |
591 | + public Socket WorkSocket; |
592 | + |
593 | + /// <summary> |
594 | + /// The size of the buffer that will be used to read. |
595 | + /// </summary> |
596 | + public const int BufferSize = 1024; |
597 | + |
598 | + /// <summary> |
599 | + /// The buffer wich will be used to place the read information. |
600 | + /// </summary> |
601 | + public readonly byte[] Buffer = new byte[BufferSize]; |
602 | + |
603 | + /// <summary> |
604 | + /// Stream that is used to concatenated the data that has been processed |
605 | + /// so far. |
606 | + /// </summary> |
607 | + public readonly MemoryStream ResultBuilder = new MemoryStream(); |
608 | + } |
609 | + |
610 | + #endregion |
611 | + |
612 | + #region Vars |
613 | + |
614 | + private IPortManager _portManager; |
615 | + private ILog _logger; |
616 | + private readonly object _loggerLock = new object(); |
617 | + private readonly object _clientsLock = new object(); |
618 | + private readonly object _portManagerLock = new object(); |
619 | + private readonly object _isListeningLock = new object(); |
620 | + private static readonly ManualResetEvent _allDone = new ManualResetEvent(false); |
621 | + private static readonly HashSet<ISyncDaemonClient> _registeredClients = new HashSet<ISyncDaemonClient>(); |
622 | + private bool _hasToListen; |
623 | + private bool _isListening; |
624 | + private Thread _listeningThread; |
625 | + |
626 | + #endregion |
627 | + |
628 | + #region DI properties |
629 | + |
630 | + /// <summary> |
631 | + /// Gets and sets the protobuf serializer that is used to deserialize the messages comming from python. |
632 | + /// </summary> |
633 | + internal IProtobufSerializer ProtobufSerializer { get; set; } |
634 | + |
635 | + /// <summary> |
636 | + /// Gets and sets the event dispatcher that is used to notify the different clients about |
637 | + /// an event. |
638 | + /// </summary> |
639 | + internal IEventDispatcher EventDispatcher { get; set; } |
640 | + |
641 | + /// <summary> |
642 | + /// Gets and sets the object that is used to retrieve the information of the object |
643 | + /// that performs the different WCF calls. |
644 | + /// </summary> |
645 | + internal ICallerContext<ISyncDaemonClient> CallerContext { get; set; } |
646 | + |
647 | + /// <summary> |
648 | + /// Gets and sets the logger that is used to log the different activities. |
649 | + /// </summary> |
650 | + internal ILog Logger |
651 | + { |
652 | + get |
653 | + { |
654 | + if(_logger == null) |
655 | + { |
656 | + lock (_loggerLock) |
657 | + { |
658 | + _logger = LogManager.GetLogger(typeof (EventNotifier)); |
659 | + } |
660 | + } |
661 | + return _logger; |
662 | + } |
663 | + set { _logger = value; } |
664 | + } |
665 | + /// <summary> |
666 | + /// Gets and sets the port manager that is used to ensure that the ports are correctly used |
667 | + /// for the application per user. |
668 | + /// </summary> |
669 | + internal IPortManager PortManager |
670 | + { |
671 | + get |
672 | + { |
673 | + if(_portManager == null) |
674 | + { |
675 | + lock (_portManagerLock) |
676 | + { |
677 | + _portManager = PortManagerImpl.Instance; |
678 | + } |
679 | + } |
680 | + return _portManager; |
681 | + } |
682 | + set { _portManager = value; } |
683 | + } |
684 | + |
685 | + #endregion |
686 | + |
687 | + #region HelperMethods |
688 | + |
689 | + /// <summary> |
690 | + /// This method listens to the port that has been set for this service to be listening for |
691 | + /// </summary> |
692 | + private void Listen() |
693 | + { |
694 | + |
695 | + var localEndPoint = new IPEndPoint(IPAddress.Any, PortManager.GetDotNetClientPort()); |
696 | + var listener = new Socket(localEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); |
697 | + try |
698 | + { |
699 | + listener.Bind(localEndPoint); |
700 | + listener.Listen(1000); |
701 | + lock (_isListeningLock) |
702 | + { |
703 | + _isListening = true; |
704 | + } |
705 | + while (_hasToListen) |
706 | + { |
707 | + _allDone.Reset(); |
708 | + |
709 | + Logger.Info("Waiting for a connection..."); |
710 | + listener.BeginAccept( |
711 | + new AsyncCallback(AcceptCallback), |
712 | + listener); |
713 | + |
714 | + _allDone.WaitOne(); |
715 | + } |
716 | + } |
717 | + catch (Exception e) |
718 | + { |
719 | + var message = string.Format("An exception was thrown when listening to port {0}", |
720 | + PortManager.GetDotNetClientPort()); |
721 | + Logger.Error(message, e); |
722 | + } |
723 | + } |
724 | + |
725 | + /// <summary> |
726 | + /// Callback that will be used to accept connections in an async manner. The method |
727 | + /// will be ran in a separate thread and will take care of dispathinch the work to other threads. |
728 | + /// </summary> |
729 | + /// <param name="asyncResult">The async result that will be passed to the call back with the |
730 | + /// connect information.</param> |
731 | + private void AcceptCallback(IAsyncResult asyncResult) |
732 | + { |
733 | + var listener = (Socket)asyncResult.AsyncState; |
734 | + var handler = listener.EndAccept(asyncResult); |
735 | + // Signal the main thread to continue. |
736 | + _allDone.Set(); |
737 | + |
738 | + // Create the state object. |
739 | + var state = new StateObject |
740 | + { |
741 | + WorkSocket = handler |
742 | + }; |
743 | + handler.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, |
744 | + new AsyncCallback(ReadCallback), state); |
745 | + } |
746 | + |
747 | + /// <summary> |
748 | + /// Callback that will be used after a read operation has been performed once the data from |
749 | + /// the python client has arrived. |
750 | + /// </summary> |
751 | + /// <param name="ar">The async result that will be passed to the callback with the read information |
752 | + /// that has been collected so far.</param> |
753 | + private void ReadCallback(IAsyncResult ar) |
754 | + { |
755 | + var state = (StateObject)ar.AsyncState; |
756 | + var handler = state.WorkSocket; |
757 | + |
758 | + // Read data from the client socket. |
759 | + var read = handler.EndReceive(ar); |
760 | + |
761 | + // Data was read from the client socket. |
762 | + if (read > 0) |
763 | + { |
764 | + // create a binary writer that will be used to append the data to the stream. |
765 | + var writer = new BinaryWriter(state.ResultBuilder); |
766 | + writer.Write(state.Buffer, 0, read); |
767 | + writer.Flush(); |
768 | + // start another callback to see if we have more info to read. |
769 | + handler.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, |
770 | + new AsyncCallback(ReadCallback), state); |
771 | + } |
772 | + else |
773 | + { |
774 | + if (state.ResultBuilder.Length > 1) |
775 | + { |
776 | + // All the data has been read from the client; |
777 | + // display it on the console. |
778 | + var workThread = new Thread(ProcessMessage); |
779 | + workThread.Start(state.ResultBuilder); |
780 | + |
781 | + } |
782 | + handler.Close(); |
783 | + } |
784 | + } |
785 | + |
786 | + /// <summary> |
787 | + /// Method that will be executed in a thread that will process the message comming from python and |
788 | + /// will notify the different clients. |
789 | + /// </summary> |
790 | + /// <param name="o">An object that should be castable to a MemoryStream that contains the data of the |
791 | + /// protobuf message.</param> |
792 | + private void ProcessMessage(object o) |
793 | + { |
794 | + // cast the object to the data that we actually have, |
795 | + var data = (MemoryStream) o; |
796 | + var protobufMessage = ProtobufSerializer.Deserialize<SyncDaemonClientMessage>(data); |
797 | + EventDispatcher.Dispatch(protobufMessage, _registeredClients); |
798 | + } |
799 | + |
800 | + #endregion |
801 | + |
802 | + /// <summary> |
803 | + /// Helper method that can be used internally to kill the listening thread to allow the event listener to stop. This |
804 | + /// method is very useful when running tests to be sure that not too many threads are kept alive. |
805 | + /// </summary> |
806 | + internal void KillListeningThread() |
807 | + { |
808 | + if (_listeningThread != null) |
809 | + { |
810 | + _hasToListen = false; |
811 | + _listeningThread.Abort(); |
812 | + } |
813 | + } |
814 | + |
815 | + #region Implementation of IEventNotifier |
816 | + |
817 | + /// <summary> |
818 | + /// Method that allows to register a client that will be reciving notitications |
819 | + /// from than point until it unregisters |
820 | + /// </summary> |
821 | + public void RegisterClient() |
822 | + { |
823 | + lock (_clientsLock) |
824 | + { |
825 | + // we get the client that will be used for the callbacks |
826 | + var client = CallerContext.GetCaller(); |
827 | + if(!_registeredClients.Contains(client)) |
828 | + { |
829 | + _registeredClients.Add(client); |
830 | + } |
831 | + } |
832 | + } |
833 | + |
834 | + /// <summary> |
835 | + /// Method that allows to unregister a client from receiving notification. |
836 | + /// </summary> |
837 | + public void UnregisterClient() |
838 | + { |
839 | + lock (_clientsLock) |
840 | + { |
841 | + var client = CallerContext.GetCaller(); |
842 | + if(_registeredClients.Contains(client)) |
843 | + { |
844 | + _registeredClients.Remove(client); |
845 | + } |
846 | + } |
847 | + } |
848 | + |
849 | + /// <summary> |
850 | + /// Method that tells the notifier to start listening for other events using a separate thread. |
851 | + /// </summary> |
852 | + public void StartListening() |
853 | + { |
854 | + if (!_hasToListen) |
855 | + { |
856 | + _hasToListen = true; |
857 | + // Once created, start listening to the port used by python to talk with us |
858 | + _listeningThread = new Thread(Listen) {Name = "EventNotifierSocketServer"}; |
859 | + _listeningThread.Start(); |
860 | + } |
861 | + else |
862 | + { |
863 | + throw new InvalidOperationException("EventNotifier cannot start listning because it already is."); |
864 | + } |
865 | + } |
866 | + |
867 | + /// <summary> |
868 | + /// Method that tells the notifier to stop listning for events. |
869 | + /// </summary> |
870 | + public void StopListening() |
871 | + { |
872 | + if (_hasToListen) |
873 | + { |
874 | + _hasToListen = false; |
875 | + lock (_isListeningLock) |
876 | + { |
877 | + _isListening = false; |
878 | + } |
879 | + _listeningThread.Join(); |
880 | + } |
881 | + else |
882 | + { |
883 | + throw new InvalidOperationException("Cannot stop listening because EventNotifier never started to listen."); |
884 | + } |
885 | + } |
886 | + |
887 | + /// <summary> |
888 | + /// Gets if the notifier is listening. |
889 | + /// </summary> |
890 | + public bool IsListening |
891 | + { |
892 | + get { return _isListening; } |
893 | + } |
894 | + |
895 | + #endregion |
896 | + } |
897 | +} |
898 | |
899 | === added file 'src/Canonical.UbuntuOne.ProcessDispatcher/ICallerContext.cs' |
900 | --- src/Canonical.UbuntuOne.ProcessDispatcher/ICallerContext.cs 1970-01-01 00:00:00 +0000 |
901 | +++ src/Canonical.UbuntuOne.ProcessDispatcher/ICallerContext.cs 2010-08-18 09:45:53 +0000 |
902 | @@ -0,0 +1,34 @@ |
903 | +/* |
904 | + * Copyright 2010 Canonical Ltd. |
905 | + * |
906 | + * This file is part of UbuntuOne on Windows. |
907 | + * |
908 | + * UbuntuOne on Windows is free software: you can redistribute it and/or modify |
909 | + * it under the terms of the GNU Lesser General Public License version |
910 | + * as published by the Free Software Foundation. |
911 | + * |
912 | + * Ubuntu One on Windows is distributed in the hope that it will be useful, |
913 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
914 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
915 | + * GNU Lesser General Public License for more details. |
916 | + * |
917 | + * You should have received a copy of the GNU Lesser General Public License |
918 | + * along with UbuntuOne for Windows. If not, see <http://www.gnu.org/licenses/>. |
919 | + * |
920 | + * Authors: Manuel de la Peña <manuel.delapena@canonical.com> |
921 | + */ |
922 | +namespace Canonical.UbuntuOne.ProcessDispatcher |
923 | +{ |
924 | + /// <summary> |
925 | + /// Interface to be implemented by those objects that can retrieve information regarding the |
926 | + /// context of a method call. |
927 | + /// </summary> |
928 | + internal interface ICallerContext<TCaller> |
929 | + { |
930 | + /// <summary> |
931 | + /// Returns the object that has called the method, |
932 | + /// </summary> |
933 | + /// <returns>The object that performed the call.</returns> |
934 | + TCaller GetCaller(); |
935 | + } |
936 | +} |
937 | |
938 | === added file 'src/Canonical.UbuntuOne.ProcessDispatcher/IEventDispatcher.cs' |
939 | --- src/Canonical.UbuntuOne.ProcessDispatcher/IEventDispatcher.cs 1970-01-01 00:00:00 +0000 |
940 | +++ src/Canonical.UbuntuOne.ProcessDispatcher/IEventDispatcher.cs 2010-08-18 09:45:53 +0000 |
941 | @@ -0,0 +1,40 @@ |
942 | +/* |
943 | + * Copyright 2010 Canonical Ltd. |
944 | + * |
945 | + * This file is part of UbuntuOne on Windows. |
946 | + * |
947 | + * UbuntuOne on Windows is free software: you can redistribute it and/or modify |
948 | + * it under the terms of the GNU Lesser General Public License version |
949 | + * as published by the Free Software Foundation. |
950 | + * |
951 | + * Ubuntu One on Windows is distributed in the hope that it will be useful, |
952 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
953 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
954 | + * GNU Lesser General Public License for more details. |
955 | + * |
956 | + * You should have received a copy of the GNU Lesser General Public License |
957 | + * along with UbuntuOne for Windows. If not, see <http://www.gnu.org/licenses/>. |
958 | + * |
959 | + * Authors: Manuel de la Peña <manuel.delapena@canonical.com> |
960 | + */ |
961 | +using System.Collections.Generic; |
962 | +using Canonical.UbuntuOne.Common; |
963 | + |
964 | +namespace Canonical.UbuntuOne.ProcessDispatcher |
965 | +{ |
966 | + /// <summary> |
967 | + /// Interface to be implemented by an object that knows how to dispatch events froma protobuf message |
968 | + /// to a collection of ISyncDaemonClients. |
969 | + /// </summary> |
970 | + internal interface IEventDispatcher |
971 | + { |
972 | + /// <summary> |
973 | + /// Method that ensures that an event that is given in a particular protobuf message |
974 | + /// is notified to all the different clients. |
975 | + /// </summary> |
976 | + /// <param name="clientMessage">The client message that was sent by the python code.</param> |
977 | + /// <param name="clients">An enumerable that contains all those clients that have to be notified about the |
978 | + /// particular event.</param> |
979 | + void Dispatch(SyncDaemonClientMessage clientMessage, IEnumerable<ISyncDaemonClient> clients); |
980 | + } |
981 | +} |
tests pass, looks good.