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

Proposed by Manuel de la Peña
Status: Merged
Approved by: Vincenzo Di Somma
Approved revision: 49
Merged at revision: 66
Proposed branch: lp:~mandel/ubuntuone-windows-installer/implement_db_port_manager
Merge into: lp:ubuntuone-windows-installer/beta
Prerequisite: lp:~mandel/ubuntuone-windows-installer/implement_dotnet_ipc_events
Diff against target: 469 lines (+303/-4)
8 files modified
.bzrignore (+1/-0)
src/Canonical.UbuntuOne.ProcessDispatcher.Tests/App.config (+50/-0)
src/Canonical.UbuntuOne.ProcessDispatcher.Tests/Canonical.UbuntuOne.ProcessDispatcher.Tests.csproj (+20/-0)
src/Canonical.UbuntuOne.ProcessDispatcher.Tests/Net/PortManagerFixture.cs (+15/-0)
src/Canonical.UbuntuOne.ProcessDispatcher.Tests/SetUpClass.cs (+43/-0)
src/Canonical.UbuntuOne.ProcessDispatcher/App.config (+9/-2)
src/Canonical.UbuntuOne.ProcessDispatcher/Canonical.UbuntuOne.ProcessDispatcher.csproj (+8/-0)
src/Canonical.UbuntuOne.ProcessDispatcher/Net/PortManager.cs (+157/-2)
To merge this branch: bzr merge lp:~mandel/ubuntuone-windows-installer/implement_db_port_manager
Reviewer Review Type Date Requested Status
Vincenzo Di Somma (community) Approve
Rick McBride (community) Approve
Review via email: mp+32972@code.launchpad.net

Description of the change

Provides an implementation of the port manager that stores the relation between a user and a port in a SQLite db. This allows to ensure that the same port is always used when assigned so that the system admin can allow exceptions in the Windows firewall (either the OS one or a 3 party one)

To post a comment you must log in.
Revision history for this message
Rick McBride (rmcbride) wrote :

Looks good, tests pass. +1

review: Approve
Revision history for this message
Vincenzo Di Somma (vds) wrote :

Same for me!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2010-08-10 12:42:54 +0000
3+++ .bzrignore 2010-08-18 10:05:56 +0000
4@@ -27,3 +27,4 @@
5 src/UbuntuOneClient.Tests/bin
6 src/UbuntuOneClient.Tests/obj
7 *.ReSharper
8+test-results
9
10=== added directory 'lib/SQLite'
11=== added file 'lib/SQLite/System.Data.SQLite.DLL'
12Binary files lib/SQLite/System.Data.SQLite.DLL 1970-01-01 00:00:00 +0000 and lib/SQLite/System.Data.SQLite.DLL 2010-08-18 10:05:56 +0000 differ
13=== added file 'src/Canonical.UbuntuOne.ProcessDispatcher.Tests/App.config'
14--- src/Canonical.UbuntuOne.ProcessDispatcher.Tests/App.config 1970-01-01 00:00:00 +0000
15+++ src/Canonical.UbuntuOne.ProcessDispatcher.Tests/App.config 2010-08-18 10:05:56 +0000
16@@ -0,0 +1,50 @@
17+<?xml version="1.0"?>
18+<configuration>
19+ <configSections>
20+ <sectionGroup name="spring">
21+ <section name="parsers"
22+ type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />
23+ <section name="context"
24+ type="Spring.Context.Support.ContextHandler, Spring.Core"/>
25+ <section name="objects"
26+ type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
27+ </sectionGroup>
28+ <sectionGroup name="common">
29+ <section name="logging"
30+ type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
31+ </sectionGroup>
32+ </configSections>
33+
34+ <!-- logging configuration -->
35+ <common>
36+ <logging>
37+ <factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4Net">
38+ <!-- choices are INLINE, FILE, FILE-WATCH, EXTERNAL-->
39+ <!-- otherwise BasicConfigurer.Configure is used -->
40+ <!-- log4net configuration file is specified with key configFile-->
41+ <arg key="configType"
42+ value="FILE" />
43+ <arg key="configFile"
44+ value="~/Config/log4net.config" />
45+ </factoryAdapter>
46+ </logging>
47+ </common>
48+
49+ <!-- Spring configuration -->
50+ <spring>
51+ <parsers>
52+ <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
53+ </parsers>
54+ <context>
55+ <resource uri="config://spring/objects"/>
56+ </context>
57+ <objects xmlns="http://www.springframework.net"
58+ xmlns:db="http://www.springframework.net/database">
59+ <!-- most internal objects definition is in the embeded objects.xml file here you will just find the db configuration -->
60+ <db:provider id="DbProvider"
61+ provider="SQLite-1.0.65"
62+ connectionString="Data Source=UbuntuOne.db;Version=3;FailIfMissing=False;Compress=True"/>
63+ </objects>
64+ </spring>
65+ <startup/>
66+</configuration>
67
68=== modified file 'src/Canonical.UbuntuOne.ProcessDispatcher.Tests/Canonical.UbuntuOne.ProcessDispatcher.Tests.csproj'
69--- src/Canonical.UbuntuOne.ProcessDispatcher.Tests/Canonical.UbuntuOne.ProcessDispatcher.Tests.csproj 2010-08-18 10:05:56 +0000
70+++ src/Canonical.UbuntuOne.ProcessDispatcher.Tests/Canonical.UbuntuOne.ProcessDispatcher.Tests.csproj 2010-08-18 10:05:56 +0000
71@@ -65,6 +65,7 @@
72 <Compile Include="Protobuf\SyncDaemonMessageFactoryFixture.cs" />
73 <Compile Include="Protobuf\SyncFolderMessageFactoryFixture.cs" />
74 <Compile Include="Protobuf\SyncShareMessageFactoryFixture.cs" />
75+ <Compile Include="SetUpClass.cs" />
76 <Compile Include="SyncConfigurationServiceFixture.cs" />
77 <Compile Include="SyncDaemonServiceFixture.cs" />
78 <Compile Include="SyncFoldersServiceFixture.cs" />
79@@ -81,6 +82,10 @@
80 </ProjectReference>
81 </ItemGroup>
82 <ItemGroup>
83+ <Reference Include="Common.Logging.Log4Net, Version=1.2.0.2, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
84+ <SpecificVersion>False</SpecificVersion>
85+ <HintPath>..\..\lib\Spring.Net\Common.Logging.Log4Net.dll</HintPath>
86+ </Reference>
87 <Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
88 <SpecificVersion>False</SpecificVersion>
89 <HintPath>..\..\lib\log4net\log4net.dll</HintPath>
90@@ -97,11 +102,23 @@
91 <SpecificVersion>False</SpecificVersion>
92 <HintPath>..\..\lib\RhinoMocks\Rhino.Mocks.dll</HintPath>
93 </Reference>
94+ <Reference Include="Spring.Core, Version=1.3.0.20349, Culture=neutral, PublicKeyToken=65e474d141e25e07, processorArchitecture=MSIL">
95+ <SpecificVersion>False</SpecificVersion>
96+ <HintPath>..\..\lib\Spring.Net\Spring.Core.dll</HintPath>
97+ </Reference>
98+ <Reference Include="Spring.Data, Version=1.3.0.20349, Culture=neutral, PublicKeyToken=65e474d141e25e07, processorArchitecture=MSIL">
99+ <SpecificVersion>False</SpecificVersion>
100+ <HintPath>..\..\lib\Spring.Net\Spring.Data.dll</HintPath>
101+ </Reference>
102 <Reference Include="System" />
103 <Reference Include="System.Core">
104 <RequiredTargetFramework>3.5</RequiredTargetFramework>
105 </Reference>
106 <Reference Include="System.Data" />
107+ <Reference Include="System.Data.SQLite, Version=1.0.65.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86">
108+ <SpecificVersion>False</SpecificVersion>
109+ <HintPath>..\..\lib\SQLite\System.Data.SQLite.DLL</HintPath>
110+ </Reference>
111 <Reference Include="System.Xml" />
112 </ItemGroup>
113 <ItemGroup>
114@@ -121,6 +138,9 @@
115 <Install>false</Install>
116 </BootstrapperPackage>
117 </ItemGroup>
118+ <ItemGroup>
119+ <None Include="App.config" />
120+ </ItemGroup>
121 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
122 <ProjectExtensions>
123 <MonoDevelop>
124
125=== modified file 'src/Canonical.UbuntuOne.ProcessDispatcher.Tests/Net/PortManagerFixture.cs'
126--- src/Canonical.UbuntuOne.ProcessDispatcher.Tests/Net/PortManagerFixture.cs 2010-08-12 07:59:15 +0000
127+++ src/Canonical.UbuntuOne.ProcessDispatcher.Tests/Net/PortManagerFixture.cs 2010-08-18 10:05:56 +0000
128@@ -18,9 +18,12 @@
129 * Authors: Manuel de la Peña <manuel.delapena@canonical.com>
130 */
131 using Canonical.UbuntuOne.Common;
132+using Canonical.UbuntuOne.Common.Container;
133 using Canonical.UbuntuOne.ProcessDispatcher.Net;
134 using NUnit.Framework;
135 using Rhino.Mocks;
136+using Spring.Data.Common;
137+using Spring.Data.Core;
138
139 namespace Canonical.UbuntuOne.ProcessDispatcher.Tests.Net
140 {
141@@ -33,6 +36,7 @@
142 private IAuthentication _auth;
143 private PortManager _portManager;
144 private MockRepository _mocks;
145+ private IDbProvider _dbProvider;
146
147 #endregion
148
149@@ -44,15 +48,26 @@
150 _mocks = new MockRepository();
151 _portFinder = _mocks.StrictMock<IPortFinder>();
152 _auth = _mocks.StrictMock<IAuthentication>();
153+ _dbProvider = ObjectsContainer.GetImplementationOf<IDbProvider>();
154 _portManager = PortManager.Instance;
155 _portManager.Authentication = _auth;
156 _portManager.PortFinder = _portFinder;
157+ _portManager.DbProvider = _dbProvider;
158 }
159
160 [TearDown]
161 public void TearDown()
162 {
163 _portManager.Clear();
164+ // Clear the db
165+ var template = new AdoTemplate(_dbProvider);
166+ template.Execute(
167+ command =>
168+ {
169+ command.CommandText = string.Format("DELETE FROM {0};", PortManager.UserPortsTableName);
170+ return command.ExecuteScalar();
171+ }
172+ );
173 }
174
175 #endregion
176
177=== added file 'src/Canonical.UbuntuOne.ProcessDispatcher.Tests/SetUpClass.cs'
178--- src/Canonical.UbuntuOne.ProcessDispatcher.Tests/SetUpClass.cs 1970-01-01 00:00:00 +0000
179+++ src/Canonical.UbuntuOne.ProcessDispatcher.Tests/SetUpClass.cs 2010-08-18 10:05:56 +0000
180@@ -0,0 +1,43 @@
181+/*
182+ * Copyright 2010 Canonical Ltd.
183+ *
184+ * This file is part of UbuntuOne on Windows.
185+ *
186+ * UbuntuOne on Windows is free software: you can redistribute it and/or modify
187+ * it under the terms of the GNU Lesser General Public License version
188+ * as published by the Free Software Foundation.
189+ *
190+ * Ubuntu One on Windows is distributed in the hope that it will be useful,
191+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
192+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
193+ * GNU Lesser General Public License for more details.
194+ *
195+ * You should have received a copy of the GNU Lesser General Public License
196+ * along with UbuntuOne for Windows. If not, see <http://www.gnu.org/licenses/>.
197+ *
198+ * Authors: Manuel de la Peña <manuel.delapena@canonical.com>
199+ */
200+using Canonical.UbuntuOne.Common.Container;
201+using NUnit.Framework;
202+
203+namespace Canonical.UbuntuOne.ProcessDispatcher.Tests
204+{
205+ /// <summary>
206+ /// Set up class ran before any test to be able to use the IoC from xml
207+ /// </summary>
208+ [SetUpFixture]
209+ public class SetUpClass
210+ {
211+ [SetUp]
212+ public void Setup()
213+ {
214+ ObjectsContainer.Initialize(new SpringContainer());
215+ }
216+
217+ [TearDown]
218+ public void TearDown()
219+ {
220+ ObjectsContainer.Clear();
221+ }
222+ }
223+}
224
225=== modified file 'src/Canonical.UbuntuOne.ProcessDispatcher/App.config'
226--- src/Canonical.UbuntuOne.ProcessDispatcher/App.config 2010-07-26 15:55:00 +0000
227+++ src/Canonical.UbuntuOne.ProcessDispatcher/App.config 2010-08-18 10:05:56 +0000
228@@ -32,11 +32,18 @@
229
230 <!-- Spring configuration -->
231 <spring>
232+ <parsers>
233+ <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
234+ </parsers>
235 <context>
236 <resource uri="assembly://Canonical.UbuntuOne.ProcessDispatcher/Canonical.UbuntuOne.ProcessDispatcher/objects.xml" />
237 </context>
238- <objects xmlns="http://www.springframework.net">
239- <!-- most internal objects definition is in the embeded objects.xml file -->
240+ <objects xmlns="http://www.springframework.net"
241+ xmlns:db="http://www.springframework.net/database">
242+ <!-- most internal objects definition is in the embeded objects.xml file here you will just find the db configuration -->
243+ <db:provider id="DbProvider"
244+ provider="SQLite-1.0.65"
245+ connectionString="Data Source=UbuntuOne.db;Version=3;FailIfMissing=False;Compress=True"/>
246 </objects>
247 </spring>
248
249
250=== modified file 'src/Canonical.UbuntuOne.ProcessDispatcher/Canonical.UbuntuOne.ProcessDispatcher.csproj'
251--- src/Canonical.UbuntuOne.ProcessDispatcher/Canonical.UbuntuOne.ProcessDispatcher.csproj 2010-08-18 10:05:56 +0000
252+++ src/Canonical.UbuntuOne.ProcessDispatcher/Canonical.UbuntuOne.ProcessDispatcher.csproj 2010-08-18 10:05:56 +0000
253@@ -127,6 +127,14 @@
254 <SpecificVersion>False</SpecificVersion>
255 <HintPath>..\..\lib\log4net\log4net.dll</HintPath>
256 </Reference>
257+ <Reference Include="Spring.Core, Version=1.3.0.20349, Culture=neutral, PublicKeyToken=65e474d141e25e07, processorArchitecture=MSIL">
258+ <SpecificVersion>False</SpecificVersion>
259+ <HintPath>..\..\lib\Spring.Net\Spring.Core.dll</HintPath>
260+ </Reference>
261+ <Reference Include="Spring.Data, Version=1.3.0.20349, Culture=neutral, PublicKeyToken=65e474d141e25e07, processorArchitecture=MSIL">
262+ <SpecificVersion>False</SpecificVersion>
263+ <HintPath>..\..\lib\Spring.Net\Spring.Data.dll</HintPath>
264+ </Reference>
265 <Reference Include="Spring.Services, Version=1.3.0.20214, Culture=neutral, PublicKeyToken=65e474d141e25e07, processorArchitecture=MSIL">
266 <SpecificVersion>False</SpecificVersion>
267 <HintPath>..\..\lib\Spring.Net\Spring.Services.dll</HintPath>
268
269=== modified file 'src/Canonical.UbuntuOne.ProcessDispatcher/Net/PortManager.cs'
270--- src/Canonical.UbuntuOne.ProcessDispatcher/Net/PortManager.cs 2010-08-12 07:59:15 +0000
271+++ src/Canonical.UbuntuOne.ProcessDispatcher/Net/PortManager.cs 2010-08-18 10:05:56 +0000
272@@ -17,14 +17,19 @@
273 *
274 * Authors: Manuel de la Peña <manuel.delapena@canonical.com>
275 */
276+using System;
277 using System.Collections.Generic;
278+using System.Data;
279 using Canonical.UbuntuOne.Common;
280+using Spring.Data.Common;
281+using Spring.Data.Core;
282
283 namespace Canonical.UbuntuOne.ProcessDispatcher.Net
284 {
285 /// <summary>
286 /// Implementation of the IPortManager that keeps track of the ports assigned to the different users and ensures that
287- /// there are correctly managed.
288+ /// there are correctly managed. The port manager create a sqlite db that will be used to ensure that a user
289+ /// allw
290 /// </summary>
291 internal class PortManager : IPortManager
292 {
293@@ -67,6 +72,8 @@
294
295 private readonly object _dictionaryLock = new object();
296 private readonly Dictionary<string, UserPorts> _ports;
297+ internal const string UserPortsTableName = "UserPorts";
298+ private bool _usetTableExists;
299
300 #endregion
301
302@@ -82,6 +89,12 @@
303 /// </summary>
304 internal IPortFinder PortFinder { get; set; }
305
306+ /// <summary>
307+ /// Gets and sets the provider that the port manager uses to store the relation between a user and the
308+ /// ports he use in the application.
309+ /// </summary>
310+ internal IDbProvider DbProvider { get; set; }
311+
312 #endregion
313
314 #region Helpers
315@@ -93,11 +106,153 @@
316 {
317 lock (_dictionaryLock)
318 {
319- var userPorts = new UserPorts(PortFinder.GetFreePort(), PortFinder.GetFreePort());
320+ // we check if the table exists, this can happe either the first time we start in the system
321+ // and the table does not exists or when we reboot and we lost the info of the flag. Ofcourse the
322+ // the table could have been drop at some point
323+ if(!_usetTableExists)
324+ {
325+ var reallyExists = UserPortsTableExists();
326+ if(!reallyExists)
327+ {
328+ CreateUserPortsTable();
329+ }
330+ _usetTableExists = true;
331+ }
332+ int dotNetPort;
333+ int pythonPort;
334+ // we try to get the data from the db
335+ if(!TryGetUserPortFromDb(Authentication.Username, out pythonPort, out dotNetPort))
336+ {
337+ // we cannot get the ports, therefore we generate them.
338+ pythonPort = PortFinder.GetFreePort();
339+ dotNetPort = PortFinder.GetFreePort();
340+ // store the user ports in the db for the next time
341+ InserUserPortsInDb(Authentication.Username, pythonPort, dotNetPort);
342+ }
343+ var userPorts = new UserPorts(pythonPort, dotNetPort);
344 _ports.Add(Authentication.Username, userPorts);
345 }
346 }
347
348+
349+ /// <summary>
350+ /// Helper method that returns if the table that stores the user name and ports relation is
351+ /// present in the db.
352+ /// </summary>
353+ /// <returns>A boolean value stating if the UserPorts table is present in the db.</returns>
354+ private bool UserPortsTableExists()
355+ {
356+ // query the sqlite schema using and ado template
357+ var template = new AdoTemplate(DbProvider);
358+ var result = template.Execute(
359+ command =>
360+ {
361+ command.CommandText = "SELECT count(*) FROM sqlite_master WHERE type='table' AND name=:tableName;";
362+ var parameter = command.CreateParameter();
363+ parameter.ParameterName = ":tableName";
364+ parameter.Value = UserPortsTableName;
365+ command.Parameters.Add(parameter);
366+ var count = command.ExecuteScalar();
367+ return (long)count != 0;
368+ }
369+ );
370+
371+ return (bool)result;
372+ }
373+
374+ /// <summary>
375+ /// Helper method that will create the user porst table in the db when needed.
376+ /// </summary>
377+ private void CreateUserPortsTable()
378+ {
379+ // create the table using an ado template.
380+ var template = new AdoTemplate(DbProvider);
381+ template.ExecuteNonQuery( CommandType.Text,
382+ String.Format("CREATE TABLE IF NOT EXISTS {0}('Username' TEXT PRIMARY KEY, 'PythonPort' INT, 'DotNetPort' INT);",
383+ UserPortsTableName));
384+ }
385+
386+ /// <summary>
387+ /// Helper method that adds a new pair of ports for a user in the db.
388+ /// </summary>
389+ /// <param name="username">The name of the user whose ports we are going to insert.</param>
390+ /// <param name="pythonPort">The port to be used to talk to python.</param>
391+ /// <param name="dotNetPort">The port to be used to talk to dotNet.</param>
392+ private void InserUserPortsInDb(string username, int pythonPort, int dotNetPort)
393+ {
394+ var template = new AdoTemplate(DbProvider);
395+ template.Execute(
396+ command =>
397+ {
398+ command.CommandText = String.Format(
399+ "INSERT INTO {0}(Username, PythonPort, DotNetPort) VALUES (:username, :pythonPort, :dotNetPort)",
400+ UserPortsTableName);
401+ var usernameParam = command.CreateParameter();
402+ usernameParam.ParameterName = ":username";
403+ usernameParam.Value = username;
404+ command.Parameters.Add(usernameParam);
405+
406+ var pythonParam = command.CreateParameter();
407+ pythonParam.ParameterName = ":pythonPort";
408+ pythonParam.Value = pythonPort;
409+ command.Parameters.Add(pythonParam);
410+
411+ var dotNetParam = command.CreateParameter();
412+ dotNetParam.ParameterName = ":dotNetPort";
413+ dotNetParam.Value = dotNetPort;
414+ command.Parameters.Add(dotNetParam);
415+
416+ return command.ExecuteScalar();
417+ }
418+ );
419+ }
420+
421+ /// <summary>
422+ /// Helper method that allows to retrieve the ports of a user from the db. If the data cannot be retrieved from
423+ /// the db.
424+ /// </summary>
425+ /// <param name="username">The username whose ports we want to retrieve.</param>
426+ /// <param name="pythonPort">Out parameter used to return the port used to talk with python.</param>
427+ /// <param name="dotNetPort">Out parameter used to return the port used to talk with dot net.</param>
428+ /// <returns>A boolean flag that indicates if the data could have been retrieved from the db.</returns>
429+ private bool TryGetUserPortFromDb(string username, out int pythonPort, out int dotNetPort)
430+ {
431+ // try to get the ports from the db, if it is not possible we return false
432+ var template = new AdoTemplate(DbProvider);
433+ try
434+ {
435+ var userPorts = (UserPorts)template.Execute(
436+ command =>
437+ {
438+ command.CommandText =
439+ String.Format("SELECT PythonPort, DotNetPort FROM {0} WHERE Username=:username;",
440+ UserPortsTableName);
441+ var parameter = command.CreateParameter();
442+ parameter.ParameterName = ":username";
443+ parameter.Value = username;
444+ command.Parameters.Add(parameter);
445+ var reader = command.ExecuteReader();
446+ while (reader.Read())
447+ {
448+ return new UserPorts(System.Convert.ToInt32((long) reader["PythonPort"]),
449+ System.Convert.ToInt32((long) reader["DotNetPort"]));
450+ }
451+ return new UserPorts(-1, -1);
452+ }
453+ );
454+ pythonPort = userPorts.PythonPort;
455+ dotNetPort = userPorts.DotNetPort;
456+ return false;
457+ }
458+ catch(Exception)
459+ {
460+ // an exception will be thrown if we cannot get the data from the db, therefore we return false
461+ pythonPort = -1;
462+ dotNetPort = -1;
463+ return false;
464+ }
465+ }
466+
467 #endregion
468
469 #region Constructors

Subscribers

People subscribed via source and target branches

to all changes: