Revision 0c02aa65
b/trunk/Pithos.Client.WPF/App.xaml.cs | ||
---|---|---|
18 | 18 |
{ |
19 | 19 |
public App() |
20 | 20 |
{ |
21 |
var extensionController = new ShellExtensionController(); |
|
21 |
//var extensionController = new ShellExtensionController();
|
|
22 | 22 |
//extensionController.RegisterExtensions(); |
23 | 23 |
|
24 | 24 |
|
b/trunk/Pithos.Client.WPF/Configuration/PithosSettings.cs | ||
---|---|---|
18 | 18 |
using System.Text; |
19 | 19 |
|
20 | 20 |
[Export(typeof(IPithosSettings))] |
21 |
[Export] |
|
21 | 22 |
public class PithosSettings : IPithosSettings |
22 | 23 |
{ |
23 | 24 |
public bool UseDefaultProxy |
... | ... | |
112 | 113 |
set { Settings.Default.ExtensionsActivated = value; } |
113 | 114 |
} |
114 | 115 |
|
116 |
public bool ShowDesktopNotifications |
|
117 |
{ |
|
118 |
get { return Settings.Default.ShowDesktopNotifications; } |
|
119 |
set { Settings.Default.ShowDesktopNotifications = value; } |
|
120 |
} |
|
115 | 121 |
/* |
116 | 122 |
public override IEnumerable<string> GetDynamicMemberNames() |
117 | 123 |
{ |
b/trunk/Pithos.Client.WPF/Pithos.Client.WPF.csproj | ||
---|---|---|
117 | 117 |
<DependentUpon>FilePropertiesView.xaml</DependentUpon> |
118 | 118 |
</Compile> |
119 | 119 |
<Compile Include="FilePropertiesViewModel.cs" /> |
120 |
<Compile Include="Notification.cs" /> |
|
121 |
<Compile Include="PithosAccount.cs" /> |
|
120 | 122 |
<Compile Include="PithosCommand.cs" /> |
121 | 123 |
<Compile Include="PreferencesView.xaml.cs"> |
122 | 124 |
<DependentUpon>PreferencesView.xaml</DependentUpon> |
123 | 125 |
</Compile> |
124 | 126 |
<Compile Include="PreferencesViewModel.cs" /> |
125 |
<Compile Include="ShellExtensionController.cs" /> |
|
126 | 127 |
<Compile Include="TaskbarViewModel.cs" /> |
127 | 128 |
<Page Include="FilePropertiesView.xaml"> |
128 | 129 |
<SubType>Designer</SubType> |
... | ... | |
188 | 189 |
<Resource Include="Images\TrayInSynch.ico" /> |
189 | 190 |
</ItemGroup> |
190 | 191 |
<ItemGroup> |
192 |
<ProjectReference Include="..\Libraries\ParallelExtensionsExtras\ParallelExtensionsExtras.csproj"> |
|
193 |
<Project>{C45218F8-09E7-4F57-85BC-5D8D2AC736A3}</Project> |
|
194 |
<Name>ParallelExtensionsExtras</Name> |
|
195 |
</ProjectReference> |
|
191 | 196 |
<ProjectReference Include="..\NotifyIconWpf\NotifyIconWpf.csproj"> |
192 | 197 |
<Project>{7AC63864-7638-41C4-969C-D3197EF2BED9}</Project> |
193 | 198 |
<Name>NotifyIconWpf</Name> |
... | ... | |
204 | 209 |
<Project>{C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD}</Project> |
205 | 210 |
<Name>Pithos.Network</Name> |
206 | 211 |
</ProjectReference> |
207 |
<ProjectReference Include="..\Pithos.ShellExtensions\Pithos.ShellExtensions.csproj"> |
|
208 |
<Project>{240B432F-1030-4623-BCC3-FF351D6C1B63}</Project> |
|
209 |
<Name>Pithos.ShellExtensions</Name> |
|
210 |
</ProjectReference> |
|
211 | 212 |
</ItemGroup> |
212 | 213 |
<ItemGroup> |
213 | 214 |
<Resource Include="Images\Accounts.png" /> |
b/trunk/Pithos.Client.WPF/PreferencesView.xaml | ||
---|---|---|
102 | 102 |
</ListBox.ItemTemplate> |
103 | 103 |
</ListBox> |
104 | 104 |
<StackPanel Orientation="Horizontal" Grid.Row="1"> |
105 |
<Button Name="AddAccount" Content="Add" Style="{StaticResource ButtonStyle}" Width="50"/> |
|
106 |
<Button Name="RemoveAccount" Content="Remove" Style="{StaticResource ButtonStyle}" Width="50"/> |
|
105 |
<Button Name="AddPithosAccount" Content="Add" Style="{StaticResource ButtonStyle}" Width="50"/>
|
|
106 |
<Button Name="RemoveAccount" Content="Remove" Style="{StaticResource ButtonStyle}" Width="50"/>
|
|
107 | 107 |
</StackPanel> |
108 | 108 |
</Grid> |
109 |
<GroupBox Header="Account" Padding="5" Margin="5" Height="190" HorizontalAlignment="Stretch" VerticalAlignment="Top" Width="272">
|
|
110 |
<Grid> |
|
109 |
<GroupBox Header="Account" Padding="5" Margin="5" Height="190" HorizontalAlignment="Stretch" VerticalAlignment="Top" Width="272"> |
|
110 |
<Grid>
|
|
111 | 111 |
<Grid.ColumnDefinitions> |
112 | 112 |
<ColumnDefinition Width="Auto"/> |
113 | 113 |
<ColumnDefinition Width="91*"/> |
... | ... | |
129 | 129 |
<Button Name="SelectSyncFolders" Content="Selective Sync" Width="Auto" HorizontalAlignment="Left" Style="{StaticResource ButtonStyle}" Grid.Column="1" Grid.Row="4"/> |
130 | 130 |
|
131 | 131 |
</Grid> |
132 |
|
|
132 | 133 |
</GroupBox> |
134 |
|
|
133 | 135 |
</StackPanel> |
134 | 136 |
</TabItem> |
135 | 137 |
<TabItem VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch"> |
b/trunk/Pithos.Client.WPF/PreferencesView.xaml.cs | ||
---|---|---|
10 | 10 |
using System.Windows.Media; |
11 | 11 |
using System.Windows.Media.Imaging; |
12 | 12 |
using System.Windows.Shapes; |
13 |
using Caliburn.Micro; |
|
14 |
using Hardcodet.Wpf.TaskbarNotification; |
|
13 | 15 |
|
14 | 16 |
namespace Pithos.Client.WPF |
15 | 17 |
{ |
... | ... | |
37 | 39 |
{ |
38 | 40 |
this.Show(); |
39 | 41 |
this.Topmost = true; |
42 |
|
|
43 |
|
|
40 | 44 |
} |
45 |
|
|
41 | 46 |
} |
42 | 47 |
} |
b/trunk/Pithos.Client.WPF/PreferencesViewModel.cs | ||
---|---|---|
16 | 16 |
using System.Windows; |
17 | 17 |
using System.Windows.Forms; |
18 | 18 |
using Caliburn.Micro; |
19 |
using Hardcodet.Wpf.TaskbarNotification; |
|
19 | 20 |
using Pithos.Client.WPF.Configuration; |
20 | 21 |
using Pithos.Core; |
21 | 22 |
using Pithos.Interfaces; |
22 |
using Pithos.ShellExtensions; |
|
23 |
|
|
23 | 24 |
using Screen = Caliburn.Micro.Screen; |
24 | 25 |
|
25 | 26 |
namespace Pithos.Client.WPF |
... | ... | |
28 | 29 |
using System.Collections.Generic; |
29 | 30 |
using System.Linq; |
30 | 31 |
using System.Text; |
32 |
using System.Threading.Tasks; |
|
31 | 33 |
|
32 | 34 |
/// <summary> |
33 | 35 |
/// TODO: Update summary. |
34 | 36 |
/// </summary> |
35 | 37 |
[Export(typeof(IShell))] |
36 |
public class PreferencesViewModel : Screen, IShell |
|
38 |
public class PreferencesViewModel : Screen, IShell, IHandle<Notification>
|
|
37 | 39 |
{ |
38 |
|
|
40 |
private IEventAggregator _events; |
|
39 | 41 |
|
40 | 42 |
|
41 |
public IPithosSettings Settings { get; set; }
|
|
43 |
public PithosSettings Settings { get; set; } |
|
42 | 44 |
|
43 | 45 |
|
44 | 46 |
public PithosMonitor Monitor { get; private set; } |
45 | 47 |
|
46 | 48 |
public TaskbarViewModel Taskbar { get; set; } |
47 | 49 |
|
48 |
ShellExtensionController _extensionController=new ShellExtensionController(); |
|
50 |
//ShellExtensionController _extensionController=new ShellExtensionController();
|
|
49 | 51 |
|
50 | 52 |
[ImportingConstructor] |
51 |
public PreferencesViewModel(TaskbarViewModel taskbar, IPithosSettings settings, PithosMonitor monitor)
|
|
53 |
public PreferencesViewModel(IEventAggregator events, TaskbarViewModel taskbar, PithosSettings settings, PithosMonitor monitor)
|
|
52 | 54 |
{ |
55 |
_events = events; |
|
56 |
_events.Subscribe(this); |
|
57 |
|
|
53 | 58 |
DisplayName = "Pithos Preferences"; |
54 | 59 |
|
55 | 60 |
Taskbar=taskbar; |
... | ... | |
65 | 70 |
new FileEntry{FileName="Moo",FullPath=@"e:\Pithos\moo"} , |
66 | 71 |
new FileEntry{FileName="Mee",FullPath=@"e:\Pithos\mee"} |
67 | 72 |
}); |
68 |
Taskbar.StatusMessage = "In Synch"; |
|
69 |
Taskbar.UpdateStatus(); |
|
73 |
Taskbar.StatusMessage = "In Synch"; |
|
70 | 74 |
} |
71 | 75 |
|
72 | 76 |
protected override void OnViewAttached(object view, object context) |
... | ... | |
81 | 85 |
{ |
82 | 86 |
var window = (Window)view; |
83 | 87 |
window.Hide(); |
84 |
|
|
88 |
Taskbar.UpdateStatus(); |
|
85 | 89 |
base.OnViewLoaded(view); |
86 | 90 |
} |
87 | 91 |
|
... | ... | |
189 | 193 |
NotifyOfPropertyChange(()=>Settings); |
190 | 194 |
} |
191 | 195 |
|
196 |
public void AddPithosAccount() |
|
197 |
{ |
|
198 |
var task=PithosAccount.RetrieveCredentialsAsync(Settings.PithosSite) |
|
199 |
.ContinueWith(t=> |
|
200 |
{ |
|
201 |
var credentials=t.Result; |
|
202 |
var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName); |
|
203 |
if (account == null) |
|
204 |
{ |
|
205 |
account=new AccountSettings{ |
|
206 |
AccountName=credentials.UserName, |
|
207 |
ApiKey=credentials.Password, |
|
208 |
UsePithos=true |
|
209 |
}; |
|
210 |
Settings.Accounts.Add(account); |
|
211 |
} |
|
212 |
else |
|
213 |
{ |
|
214 |
account.ApiKey=credentials.Password; |
|
215 |
} |
|
216 |
SelectedAccountIndex= Settings.Accounts.IndexOf(account); |
|
217 |
NotifyOfPropertyChange(()=>Settings); |
|
218 |
}); |
|
219 |
((Task)task).WaitWithPumping(); |
|
220 |
} |
|
221 |
|
|
192 | 222 |
public void RemoveAccount() |
193 | 223 |
{ |
194 | 224 |
Settings.Accounts.RemoveAll(account => account.AccountName == CurrentAccount.AccountName); |
... | ... | |
215 | 245 |
|
216 | 246 |
Settings.ExtensionsActivated = value; |
217 | 247 |
|
218 |
if (value) |
|
219 |
_extensionController.RegisterExtensions(); |
|
220 |
else |
|
221 |
{ |
|
222 |
_extensionController.UnregisterExtensions(); |
|
223 |
} |
|
248 |
//if (value)
|
|
249 |
// _extensionController.RegisterExtensions();
|
|
250 |
//else
|
|
251 |
//{
|
|
252 |
// _extensionController.UnregisterExtensions();
|
|
253 |
//}
|
|
224 | 254 |
NotifyOfPropertyChange(() => ExtensionsActivated); |
225 | 255 |
} |
226 | 256 |
} |
... | ... | |
244 | 274 |
// _selectedAccountIndex = value; |
245 | 275 |
//else |
246 | 276 |
// _selectedAccountIndex = 0; |
277 |
_selectedAccountIndex = value; |
|
247 | 278 |
NotifyOfPropertyChange(() => CurrentAccount); |
248 | 279 |
NotifyOfPropertyChange(() => CanRemoveAccount); |
249 | 280 |
NotifyOfPropertyChange(()=>SelectedAccountIndex); |
... | ... | |
267 | 298 |
} |
268 | 299 |
|
269 | 300 |
|
301 |
public void Handle(Notification notification) |
|
302 |
{ |
|
303 |
if (!Settings.ShowDesktopNotifications) |
|
304 |
return; |
|
305 |
BalloonIcon icon = BalloonIcon.None; |
|
306 |
switch (notification.Level) |
|
307 |
{ |
|
308 |
case TraceLevel.Error: |
|
309 |
icon = BalloonIcon.Error; |
|
310 |
break; |
|
311 |
case TraceLevel.Info: |
|
312 |
case TraceLevel.Verbose: |
|
313 |
icon = BalloonIcon.Info; |
|
314 |
break; |
|
315 |
case TraceLevel.Warning: |
|
316 |
icon = BalloonIcon.Warning; |
|
317 |
break; |
|
318 |
default: |
|
319 |
icon = BalloonIcon.None; |
|
320 |
break; |
|
321 |
} |
|
322 |
|
|
323 |
var tv = (PreferencesView)this.GetView(); |
|
324 |
tv.TaskbarView.ShowBalloonTip(notification.Title, notification.Message, icon); |
|
325 |
} |
|
326 |
|
|
270 | 327 |
} |
271 | 328 |
} |
b/trunk/Pithos.Client.WPF/ShellExtensionController.cs | ||
---|---|---|
8 | 8 |
using System.IO; |
9 | 9 |
using System.IO.IsolatedStorage; |
10 | 10 |
using System.Runtime.Serialization; |
11 |
using Pithos.ShellExtensions; |
|
11 |
|
|
12 | 12 |
|
13 | 13 |
namespace Pithos.Client.WPF |
14 | 14 |
{ |
b/trunk/Pithos.Client.WPF/TaskbarViewModel.cs | ||
---|---|---|
7 | 7 |
using System.ComponentModel.Composition; |
8 | 8 |
using System.Diagnostics; |
9 | 9 |
using System.IO; |
10 |
using System.Threading.Tasks; |
|
10 | 11 |
using System.Windows; |
11 | 12 |
using Caliburn.Micro; |
12 | 13 |
using Pithos.Client.WPF.Properties; |
... | ... | |
24 | 25 |
/// TODO: Update summary. |
25 | 26 |
/// </summary> |
26 | 27 |
[Export] |
27 |
public class TaskbarViewModel:ViewAware |
|
28 |
public class TaskbarViewModel:ViewAware,IStatusNotification
|
|
28 | 29 |
{ |
29 | 30 |
private IStatusChecker _statusChecker; |
31 |
private IEventAggregator _events; |
|
30 | 32 |
|
31 | 33 |
public PithosMonitor Monitor { get; private set; } |
32 | 34 |
|
... | ... | |
35 | 37 |
public IScreen Parent { get; set; } |
36 | 38 |
|
37 | 39 |
[ImportingConstructor] |
38 |
public TaskbarViewModel(IStatusChecker statusChecker,PithosMonitor monitor,IPithosSettings settings) |
|
40 |
public TaskbarViewModel(IEventAggregator events, IStatusChecker statusChecker,PithosMonitor monitor,IPithosSettings settings)
|
|
39 | 41 |
{ |
40 | 42 |
OpenPithosFolderCommand = new PithosCommand(OpenPithosFolder); |
41 | 43 |
_statusChecker = statusChecker; |
44 |
_events = events; |
|
42 | 45 |
Settings = settings; |
43 | 46 |
Monitor = monitor; |
47 |
Monitor.StatusNotification = this; |
|
48 |
|
|
49 |
|
|
44 | 50 |
|
45 | 51 |
var account=settings.Accounts.FirstOrDefault(act => act.IsActive); |
46 | 52 |
if (account != null) |
... | ... | |
167 | 173 |
StatusIcon = String.Format(@"Images/{0}.ico", info.IconName); |
168 | 174 |
StatusMessage = String.Format("Pithos 1.0\r\n{0}", info.StatusText); |
169 | 175 |
} |
176 |
|
|
177 |
var tv=this.GetView(); |
|
178 |
_events.Publish(new Notification { Title = "Start", Message = "Start Monitoring", Level = TraceLevel.Info}); |
|
170 | 179 |
if (!String.IsNullOrWhiteSpace(Monitor.UserName) && |
171 | 180 |
!String.IsNullOrWhiteSpace(Monitor.ApiKey)) |
172 |
try |
|
173 |
{ |
|
174 |
Monitor.Start(); |
|
175 |
} |
|
176 |
catch (Exception exc) |
|
177 |
{ |
|
178 |
MessageBox.Show("An exception occured. Can't start monitoring"); |
|
179 |
} |
|
181 |
Task.Factory.StartNew(() => |
|
182 |
Monitor.Start()) |
|
183 |
.ContinueWith(t =>{ |
|
184 |
if (t.IsFaulted) |
|
185 |
{ |
|
186 |
|
|
187 |
var message= String.Format("An exception occured. Can't start monitoring\n{0}",t.Exception); |
|
188 |
_events.Publish(new Notification { Title = "Error",Message=message,Level= TraceLevel.Error }); |
|
189 |
MessageBox.Show(message); |
|
190 |
} |
|
191 |
}); |
|
192 |
} |
|
193 |
|
|
194 |
|
|
195 |
|
|
196 |
public void NotifyChange(string status, TraceLevel level=TraceLevel.Info) |
|
197 |
{ |
|
198 |
this.StatusMessage = status; |
|
199 |
|
|
200 |
_events.Publish(new Notification { Title = "Pithos", Message = status, Level = level }); |
|
180 | 201 |
} |
181 | 202 |
} |
182 | 203 |
} |
b/trunk/Pithos.Core/FileHashMap.cs | ||
---|---|---|
1 |
using System.Collections.Generic; |
|
2 |
|
|
3 |
public class FileHashMap |
|
4 |
{ |
|
5 |
public byte[] Hash { get; set; } |
|
6 |
public IEnumerable<byte[]> Hashes { get; set; } |
|
7 |
} |
b/trunk/Pithos.Core/FileState.cs | ||
---|---|---|
4 | 4 |
// </copyright> |
5 | 5 |
// ----------------------------------------------------------------------- |
6 | 6 |
|
7 |
using System.Threading.Tasks; |
|
7 | 8 |
using Castle.ActiveRecord; |
8 | 9 |
using Castle.ActiveRecord.Framework; |
9 | 10 |
using Pithos.Interfaces; |
... | ... | |
47 | 48 |
get { return _tags; } |
48 | 49 |
set { _tags=value;} |
49 | 50 |
} |
50 |
|
|
51 |
|
|
52 |
[HasMany(Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Lazy = true)] |
|
53 |
public IList<FileHash> Hashes { get; set; } |
|
54 |
|
|
55 |
public FileState() |
|
56 |
{ |
|
57 |
|
|
58 |
} |
|
59 |
|
|
60 |
private static int BLOCK_SIZE = 4194304; |
|
61 |
|
|
62 |
public static Task<FileState> CreateForAsync(string filePath) |
|
63 |
{ |
|
64 |
var fileState = new FileState |
|
65 |
{ |
|
66 |
FilePath = filePath, |
|
67 |
OverlayStatus = FileOverlayStatus.Unversioned, |
|
68 |
FileStatus = FileStatus.Created |
|
69 |
}; |
|
70 |
|
|
71 |
|
|
72 |
return Task.Factory.StartNew(()=> { |
|
73 |
fileState.Checksum = Signature.CalculateHash(fileState.FilePath); |
|
74 |
}) |
|
75 |
.ContinueWith(_=> Signature.CalculateBlockHashesAsync(filePath, BLOCK_SIZE)).Unwrap() |
|
76 |
.ContinueWith(t => |
|
77 |
{ |
|
78 |
fileState.Hashes = t.Result; |
|
79 |
}).ContinueWith(t => |
|
80 |
{ |
|
81 |
fileState.HashmapHash = Signature.CalculateHashmapHash(fileState.Hashes); |
|
82 |
}).ContinueWith(t=> fileState); |
|
83 |
//return fileState; |
|
84 |
} |
|
85 |
|
|
86 |
public byte[] HashmapHash { get; set; } |
|
51 | 87 |
} |
52 | 88 |
|
53 | 89 |
[ActiveRecord] |
... | ... | |
63 | 99 |
public FileState FileState { get; set; } |
64 | 100 |
|
65 | 101 |
} |
102 |
|
|
103 |
[ActiveRecord] |
|
104 |
public class FileHash : ActiveRecordLinqBase<FileHash> |
|
105 |
{ |
|
106 |
[PrimaryKey] |
|
107 |
public string FilePath { get; set; } |
|
108 |
|
|
109 |
[Property] |
|
110 |
public byte[] Value { get; set; } |
|
111 |
|
|
112 |
[BelongsTo("FilePath")] |
|
113 |
public FileState FileState { get; set; } |
|
114 |
|
|
115 |
} |
|
66 | 116 |
|
67 | 117 |
|
68 | 118 |
} |
b/trunk/Pithos.Core/Pithos.Core.csproj | ||
---|---|---|
127 | 127 |
<Reference Include="System.Xml" /> |
128 | 128 |
</ItemGroup> |
129 | 129 |
<ItemGroup> |
130 |
<Compile Include="FileHashMap.cs" /> |
|
130 | 131 |
<Compile Include="FileState.cs" /> |
131 | 132 |
<Compile Include="IStatusService.cs" /> |
132 | 133 |
<Compile Include="JobQueue.cs" /> |
b/trunk/Pithos.Core/PithosMonitor.cs | ||
---|---|---|
40 | 40 |
[Import] |
41 | 41 |
public ICloudClient CloudListeningClient { get; set; } |
42 | 42 |
|
43 |
public IStatusNotification StatusNotification { get; set; } |
|
44 |
|
|
43 | 45 |
public string UserName { get; set; } |
44 | 46 |
public string ApiKey { get; set; } |
45 | 47 |
|
... | ... | |
57 | 59 |
if (value) |
58 | 60 |
{ |
59 | 61 |
StatusKeeper.SetPithosStatus(PithosStatus.SyncPaused); |
62 |
StatusNotification.NotifyChange("Paused"); |
|
60 | 63 |
} |
61 | 64 |
else |
62 | 65 |
{ |
63 | 66 |
StatusKeeper.SetPithosStatus(PithosStatus.InSynch); |
67 |
StatusNotification.NotifyChange("In Synch"); |
|
64 | 68 |
} |
65 | 69 |
} |
66 | 70 |
} |
... | ... | |
77 | 81 |
|
78 | 82 |
public void Start() |
79 | 83 |
{ |
80 |
|
|
84 |
StatusNotification.NotifyChange("Starting"); |
|
81 | 85 |
if (_cancellationSource != null) |
82 | 86 |
{ |
83 | 87 |
if (!_cancellationSource.IsCancellationRequested) |
... | ... | |
135 | 139 |
|
136 | 140 |
private void IndexLocalFiles(string path) |
137 | 141 |
{ |
138 |
Trace.TraceInformation("[START] Inxed Local"); |
|
142 |
StatusNotification.NotifyChange("Indexing Local Files",TraceLevel.Info); |
|
143 |
Trace.TraceInformation("[START] Index Local"); |
|
139 | 144 |
try |
140 | 145 |
{ |
141 | 146 |
var files = |
... | ... | |
157 | 162 |
|
158 | 163 |
private void RestartInterruptedFiles() |
159 | 164 |
{ |
165 |
StatusNotification.NotifyChange("Restart processing interrupted files", TraceLevel.Verbose); |
|
160 | 166 |
var interruptedStates = new[] { FileOverlayStatus.Unversioned, FileOverlayStatus.Modified }; |
161 | 167 |
var filesQuery = from state in FileState.Queryable |
162 | 168 |
where interruptedStates.Contains(state.OverlayStatus) |
... | ... | |
350 | 356 |
|
351 | 357 |
var uniques = |
352 | 358 |
onlyLocal.Union(onlyRemote).Union(commonObjects) |
353 |
.Except(_networkActions,new LocalFileComparer()); |
|
354 |
|
|
359 |
.Except(_networkActions,new LocalFileComparer());
|
|
360 |
|
|
355 | 361 |
_networkActions.AddFromEnumerable(uniques, false); |
356 | 362 |
|
363 |
StatusNotification.NotifyChange(String.Format("Processing {0} files", _networkActions.Count)); |
|
364 |
|
|
357 | 365 |
Trace.TraceInformation("[LISTENER] End Processing"); |
358 | 366 |
|
359 | 367 |
} |
... | ... | |
734 | 742 |
|
735 | 743 |
|
736 | 744 |
} |
745 |
|
|
746 |
public interface IStatusNotification |
|
747 |
{ |
|
748 |
void NotifyChange(string status,TraceLevel level=TraceLevel.Info); |
|
749 |
} |
|
737 | 750 |
} |
b/trunk/Pithos.Core/Signature.cs | ||
---|---|---|
1 |
using System; |
|
2 |
using System.Collections; |
|
3 |
using System.Collections.Concurrent; |
|
4 |
using System.Collections.Generic; |
|
1 | 5 |
using System.IO; |
2 | 6 |
using System.Security.Cryptography; |
3 | 7 |
using System.Text; |
8 |
using System.Threading.Tasks; |
|
9 |
using System.Linq; |
|
10 |
using Pithos.Core; |
|
4 | 11 |
|
5 | 12 |
public static class Signature |
6 | 13 |
{ |
... | ... | |
18 | 25 |
} |
19 | 26 |
return hash; |
20 | 27 |
} |
28 |
|
|
29 |
/*public static FileHashMap CalculateHashMap(string path, int blockSize) |
|
30 |
{ |
|
31 |
CalculateBlockHashes(path, blockSize) |
|
32 |
.ContinueWith(blockTask => |
|
33 |
{ |
|
34 |
var hashes = blockTask.Result; |
|
35 |
var hashMapHash = CalculateHashmapHash(hashes); |
|
36 |
return new FileHashMap {Hash = hashMapHash, Hashes = hashes}; |
|
37 |
}); |
|
38 |
}*/ |
|
39 |
|
|
40 |
public static Task<IList<FileHash>> CalculateBlockHashesAsync(string path, int blockSize) |
|
41 |
{ |
|
42 |
var hashMap = new ConcurrentQueue<FileHash>(); |
|
43 |
var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, true); |
|
44 |
|
|
45 |
return stream.ReadBuffersAsync(blockSize, (buffer, read) => |
|
46 |
{ |
|
47 |
using (var hasher = SHA256.Create()) |
|
48 |
{ |
|
49 |
var hash = hasher.ComputeHash(buffer, 0, read); |
|
50 |
hashMap.Enqueue(new FileHash{FilePath=path,Value=hash}); |
|
51 |
} |
|
52 |
}).ContinueWith(t=> |
|
53 |
{ |
|
54 |
stream.Close(); |
|
55 |
return (IList<FileHash>)hashMap.ToList(); |
|
56 |
}); |
|
57 |
} |
|
58 |
//I'd rather return an enumerable of hash calculation tasks, each of which can |
|
59 |
//be use to further store the hashes to storage without first creating the list of hashes |
|
60 |
|
|
61 |
/* //Or I could pass an action object, just like ReadBuffers does |
|
62 |
Task.Factory.Iterate(HashMapIterator(stream, blockSize,hashMap.Enqueue)) |
|
63 |
.ContinueWith(t=> |
|
64 |
{ |
|
65 |
SHA256.Create(); |
|
66 |
});*/ |
|
67 |
|
|
68 |
|
|
69 |
|
|
70 |
public static byte[] CalculateHashmapHash(IList<FileHash> hashMap ) |
|
71 |
{ |
|
72 |
using (var stream = new MemoryStream()) |
|
73 |
using(var hasher=SHA256.Create()) |
|
74 |
{ |
|
75 |
foreach (var hash in hashMap) |
|
76 |
{ |
|
77 |
stream.Write(hash.Value, 0, hash.Value.Length); |
|
78 |
} |
|
79 |
return hasher.ComputeHash(stream); |
|
80 |
} |
|
81 |
} |
|
82 |
|
|
83 |
public static IEnumerable<byte[]> HashMapLinq(string path, int blockSize) |
|
84 |
{ |
|
85 |
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, true)) |
|
86 |
{ |
|
87 |
|
|
88 |
var query = from block in stream.GetIterator(blockSize).AsParallel().AsOrdered() |
|
89 |
select block; |
|
90 |
|
|
91 |
return query; |
|
92 |
} |
|
93 |
} |
|
94 |
|
|
95 |
public class Block |
|
96 |
{ |
|
97 |
public int Offset { get; set; } |
|
98 |
public byte[] Data { get; set; } |
|
99 |
} |
|
100 |
|
|
101 |
public static IEnumerable<byte[]> GetIterator(this Stream stream,int blockSize) |
|
102 |
{ |
|
103 |
var buffer = new byte[blockSize]; |
|
104 |
while(true) |
|
105 |
{ |
|
106 |
stream.Read(buffer, 0, buffer.Length); |
|
107 |
yield return buffer; |
|
108 |
} |
|
109 |
} |
|
110 |
|
|
111 |
|
|
112 |
|
|
113 |
private static IEnumerable<Task> HashMapIterator(Stream input, int bufferSize,Action<byte[]> hashAvailable ) |
|
114 |
{ |
|
115 |
// Create a buffer that will be used over and over |
|
116 |
var buffer = new byte[bufferSize]; |
|
117 |
|
|
118 |
using (var hasher = SHA256.Create()) |
|
119 |
{ |
|
120 |
// Until there's no more data |
|
121 |
while (true) |
|
122 |
{ |
|
123 |
// Asynchronously read a buffer and yield until the operation completes |
|
124 |
var readTask = input.ReadAsync(buffer, 0, buffer.Length); |
|
125 |
yield return readTask; |
|
126 |
|
|
127 |
// If there's no more data in the stream, we're done. |
|
128 |
if (readTask.Result <= 0) break; |
|
129 |
|
|
130 |
// Otherwise, hand the data off to the delegate |
|
131 |
var hashTask = Task.Factory.StartNew(() => hasher.ComputeHash(buffer, 0, readTask.Result)); |
|
132 |
yield return hashTask; |
|
133 |
hashAvailable(hashTask.Result); |
|
134 |
} |
|
135 |
} |
|
136 |
} |
|
137 |
|
|
21 | 138 |
} |
b/trunk/Pithos.Core/StatusKeeper.cs | ||
---|---|---|
79 | 79 |
|
80 | 80 |
public IEnumerable<string> StoreUnversionedFiles(ParallelQuery<string> paths) |
81 | 81 |
{ |
82 |
var existingFiles = from state in FileState.Queryable |
|
83 |
select state.FilePath; |
|
84 |
|
|
85 |
var newFiles = (from file in paths.Except(existingFiles.AsParallel()) |
|
86 |
select new FileState |
|
87 |
{ |
|
88 |
FilePath = file, |
|
89 |
OverlayStatus = FileOverlayStatus.Unversioned, |
|
90 |
FileStatus=FileStatus.Created, |
|
91 |
Checksum=Signature.CalculateHash(file) |
|
92 |
} |
|
93 |
); |
|
82 |
var existingFiles = (from state in FileState.Queryable |
|
83 |
select state.FilePath).ToList(); |
|
84 |
|
|
85 |
var newFiles = paths.Except(existingFiles.AsParallel());//(from file in paths.Except(existingFiles.AsParallel()) |
|
86 |
//select FileState.FromPathAsync(file) |
|
87 |
//); |
|
94 | 88 |
|
95 |
//var files=new ConcurrentBag<string>(); |
|
96 |
newFiles.ForAll(state=> _statusUpdateQueue.Add(state.Save)); |
|
89 |
//var files=new ConcurrentBag<string>(); |
|
90 |
newFiles.ForAll(file=> |
|
91 |
FileState.CreateForAsync(file) |
|
92 |
.ContinueWith(state=>_statusUpdateQueue.Add(state.Result.Save))); |
|
97 | 93 |
|
98 |
return newFiles.Select(state => state.FilePath);// files.GetConsumingEnumerable();
|
|
94 |
return newFiles;//.Select(state => state);// files.GetConsumingEnumerable();
|
|
99 | 95 |
|
100 | 96 |
} |
101 | 97 |
|
... | ... | |
147 | 143 |
|
148 | 144 |
|
149 | 145 |
ConcurrentDictionary<string,NetworkState> _networkState=new ConcurrentDictionary<string, NetworkState>(); |
146 |
private int BLOCK_SIZE = 4194304; |
|
150 | 147 |
|
151 | 148 |
public void SetNetworkState(string path,NetworkState state) |
152 | 149 |
{ |
b/trunk/Pithos.Network/PithosClient.cs | ||
---|---|---|
66 | 66 |
protected override WebRequest GetWebRequest(Uri address) |
67 | 67 |
{ |
68 | 68 |
TimedOut = false; |
69 |
HttpWebRequest request = base.GetWebRequest(address) as HttpWebRequest; |
|
69 |
var webRequest = base.GetWebRequest(address); |
|
70 |
var request = webRequest as HttpWebRequest; |
|
70 | 71 |
request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip; |
71 | 72 |
if(Timeout>0) |
72 | 73 |
request.Timeout = Timeout; |
... | ... | |
278 | 279 |
throw new WebException(String.Format("{0} with code {1} - {2}", message, StatusCode, StatusDescription)); |
279 | 280 |
} |
280 | 281 |
|
281 |
/*private Func<T> Retry<T>(Func< T> original, int retryCount) |
|
282 |
{ |
|
283 |
return () => |
|
284 |
{ |
|
285 |
while (true) |
|
286 |
{ |
|
287 |
try |
|
288 |
{ |
|
289 |
return original(); |
|
290 |
} |
|
291 |
catch (WebException e) |
|
292 |
{ |
|
293 |
var statusCode = ((HttpWebResponse)e.Response).StatusCode; |
|
294 |
this.StatusCode = statusCode; |
|
295 |
|
|
296 |
switch (e.Status) |
|
297 |
{ |
|
298 |
case WebExceptionStatus.Timeout: |
|
299 | 282 |
|
300 |
TimedOut = true; |
|
301 |
if (retryCount == 0) |
|
302 |
{ |
|
303 |
Trace.TraceError("[ERROR] Timed out too many times. {0}\n", e); |
|
304 |
throw new RetryException("Timed out too many times.", e); |
|
305 |
} |
|
306 |
retryCount--; |
|
307 |
Trace.TraceError( |
|
308 |
"[RETRY] Timed out after {0} ms. Will retry {1} more times\n{2}", Timeout, |
|
309 |
retryCount, e); |
|
310 |
|
|
311 |
break; |
|
312 |
case WebExceptionStatus.ProtocolError: |
|
313 |
switch (statusCode) |
|
314 |
{ |
|
315 |
case HttpStatusCode.NotFound: |
|
316 |
{ |
|
317 |
return default(T); |
|
318 |
} |
|
319 |
case HttpStatusCode.ServiceUnavailable: |
|
320 |
{ |
|
321 |
|
|
322 |
TimedOut = false; |
|
323 |
if (retryCount == 0) |
|
324 |
{ |
|
325 |
Trace.TraceError("[ERROR] Failed too many times. {0}\n", e); |
|
326 |
throw new RetryException("Failed too many times.", e); |
|
327 |
} |
|
328 |
retryCount--; |
|
329 |
Trace.TraceError( |
|
330 |
"[RETRY] Failed due to 503. Will retry {0} more times\n{1}", retryCount, e); |
|
331 |
break; |
|
332 |
} |
|
333 |
default: |
|
334 |
throw; |
|
335 |
} |
|
336 |
break; |
|
337 |
default: |
|
338 |
throw; |
|
339 |
} |
|
340 |
} |
|
341 |
} |
|
342 |
}; |
|
343 |
}*/ |
|
344 |
|
|
345 |
private Task<T> Retry<T>(Func< T> original, int retryCount) |
|
283 |
private Task<T> Retry<T>(Func<T> original, int retryCount, TaskCompletionSource<T> tcs = null) |
|
346 | 284 |
{ |
347 |
return Task.Factory.StartNew(() => original()).ContinueWith(_original => |
|
285 |
if (tcs == null) |
|
286 |
tcs = new TaskCompletionSource<T>(); |
|
287 |
Task.Factory.StartNew(original).ContinueWith(_original => |
|
348 | 288 |
{ |
349 |
if(_original.IsFaulted ) |
|
289 |
if (!_original.IsFaulted) |
|
290 |
tcs.SetFromTask(_original); |
|
291 |
else |
|
350 | 292 |
{ |
351 | 293 |
var e = _original.Exception.InnerException; |
352 |
if (e is WebException) |
|
294 |
var we = (e as WebException); |
|
295 |
if (we==null) |
|
296 |
tcs.SetException(e); |
|
297 |
else |
|
353 | 298 |
{ |
354 |
var we = (e as WebException); |
|
355 |
|
|
356 |
var statusCode = HttpStatusCode.RequestTimeout; |
|
357 |
if (we.Response != null) |
|
358 |
{ |
|
359 |
statusCode = ((HttpWebResponse) we.Response).StatusCode; |
|
360 |
this.StatusCode = statusCode; |
|
361 |
} |
|
362 |
|
|
363 |
if (we.Status==WebExceptionStatus.Timeout || |
|
364 |
(we.Status==WebExceptionStatus.ProtocolError && statusCode==HttpStatusCode.ServiceUnavailable)) |
|
299 |
var statusCode = GetStatusCode(we); |
|
300 |
|
|
301 |
//Return null for 404 |
|
302 |
if (statusCode == HttpStatusCode.NotFound) |
|
303 |
tcs.SetResult(default(T)); |
|
304 |
//Retry for timeouts and service unavailable |
|
305 |
else if (we.Status == WebExceptionStatus.Timeout || |
|
306 |
(we.Status == WebExceptionStatus.ProtocolError && statusCode == HttpStatusCode.ServiceUnavailable)) |
|
365 | 307 |
{ |
366 | 308 |
TimedOut = true; |
367 | 309 |
if (retryCount == 0) |
368 | 310 |
{ |
369 | 311 |
Trace.TraceError("[ERROR] Timed out too many times. {0}\n", e); |
370 |
throw new RetryException("Timed out too many times.", e); |
|
312 |
tcs.SetException(new RetryException("Timed out too many times.", e)); |
|
313 |
} |
|
314 |
else |
|
315 |
{ |
|
316 |
Trace.TraceError( |
|
317 |
"[RETRY] Timed out after {0} ms. Will retry {1} more times\n{2}", Timeout, |
|
318 |
retryCount, e); |
|
319 |
Retry(original, retryCount - 1, tcs); |
|
371 | 320 |
} |
372 |
Trace.TraceError( |
|
373 |
"[RETRY] Timed out after {0} ms. Will retry {1} more times\n{2}", Timeout, |
|
374 |
retryCount, e); |
|
375 |
return Retry(original, retryCount - 1); |
|
376 | 321 |
} |
377 |
|
|
378 |
if (statusCode==HttpStatusCode.NotFound) |
|
379 |
return Task<T>.Factory.StartNew(() => default(T)); |
|
322 |
else |
|
323 |
tcs.SetException(e); |
|
380 | 324 |
} |
381 |
throw e; |
|
382 |
} |
|
383 |
else |
|
384 |
return Task<T>.Factory.StartNew(() => _original.Result); |
|
385 |
}).Unwrap(); |
|
325 |
}; |
|
326 |
}); |
|
327 |
return tcs.Task; |
|
386 | 328 |
} |
387 | 329 |
|
388 |
|
|
330 |
private HttpStatusCode GetStatusCode(WebException we) |
|
331 |
{ |
|
332 |
var statusCode = HttpStatusCode.RequestTimeout; |
|
333 |
if (we.Response != null) |
|
334 |
{ |
|
335 |
statusCode = ((HttpWebResponse) we.Response).StatusCode; |
|
336 |
this.StatusCode = statusCode; |
|
337 |
} |
|
338 |
return statusCode; |
|
339 |
} |
|
389 | 340 |
} |
390 | 341 |
|
391 | 342 |
public class RetryException:Exception |
b/trunk/Pithos.sln | ||
---|---|---|
49 | 49 |
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
50 | 50 |
{240B432F-1030-4623-BCC3-FF351D6C1B63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
51 | 51 |
{240B432F-1030-4623-BCC3-FF351D6C1B63}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU |
52 |
{240B432F-1030-4623-BCC3-FF351D6C1B63}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU |
|
53 | 52 |
{240B432F-1030-4623-BCC3-FF351D6C1B63}.Debug|x86.ActiveCfg = Debug|Any CPU |
54 | 53 |
{240B432F-1030-4623-BCC3-FF351D6C1B63}.Release|Any CPU.ActiveCfg = Release|Any CPU |
55 | 54 |
{240B432F-1030-4623-BCC3-FF351D6C1B63}.Release|Any CPU.Build.0 = Release|Any CPU |
Also available in: Unified diff