Merge lp:~mandel/ubuntuone-windows-installer/implement_dotnet_ipc_events into lp:ubuntuone-windows-installer/beta

Proposed by Manuel de la Peña
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
Reviewer Review Type Date Requested Status
Rick McBride (community) Approve
Stuart Colville (community) Approve
Review via email: mp+32759@code.launchpad.net

Description of the change

Provides the technique used to communicate the different windows client the firing of events from the python code.

To post a comment you must log in.
Revision history for this message
Stuart Colville (muffinresearch) wrote :

tests pass, looks good.

review: Approve
Revision history for this message
Rick McBride (rmcbride) wrote :

Looks good, however 'NAnt.exe tests' hangs after

'Executng Canonical.UbuntuOne.ProcessDispatcher.Tests' and the exec block after it. (Note that Executing is misspelled in the echo string).

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.

review: Needs Fixing
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.

Revision history for this message
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).

Revision history for this message
Rick McBride (rmcbride) wrote :

Works now with that test disabled.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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+}

Subscribers

People subscribed via source and target branches

to all changes: