Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / PithosMonitor.cs @ 5ce54458

History | View | Annotate | Download (11.9 kB)

1
using System;
2
using System.Collections.Concurrent;
3
using System.Collections.Generic;
4
using System.ComponentModel.Composition;
5
using System.Diagnostics;
6
using System.Diagnostics.Contracts;
7
using System.IO;
8
using System.Linq;
9
using System.Net.NetworkInformation;
10
using System.Security.Cryptography;
11
using System.ServiceModel.Description;
12
using System.Text;
13
using System.Threading;
14
using System.Threading.Tasks;
15
using Castle.ActiveRecord.Queries;
16
using Microsoft.WindowsAPICodePack.Net;
17
using Pithos.Core.Agents;
18
using Pithos.Interfaces;
19
using System.ServiceModel;
20
using Pithos.Network;
21

    
22
namespace Pithos.Core
23
{
24
    [Export(typeof(PithosMonitor))]
25
    public class PithosMonitor:IDisposable
26
    {
27
        private const string PithosContainer = "pithos";
28
        private const string TrashContainer = "trash";
29

    
30
        private const string FragmentsFolder = "fragments";
31

    
32
        private int _blockSize;
33
        private string _blockHash;
34

    
35
        [Import]
36
        public IPithosSettings Settings{get;set;}
37

    
38
        [Import]
39
        public IStatusKeeper StatusKeeper { get; set; }
40

    
41
        [Import]
42
        public IPithosWorkflow Workflow { get; set; }
43

    
44
        [Import]
45
        public ICloudClient CloudClient { get; set; }
46

    
47
        [Import]
48
        public ICloudClient CloudListeningClient { get; set; }
49

    
50
        public IStatusNotification StatusNotification { get; set; }
51

    
52
        [Import]
53
        public FileAgent FileAgent { get; set; }
54
        
55
        [Import]
56
        public WorkflowAgent WorkflowAgent { get; set; }
57
        
58
        [Import]
59
        public NetworkAgent NetworkAgent { get; set; }
60

    
61

    
62
        public string UserName { get; set; }
63
        public string ApiKey { get; set; }
64

    
65
        private ServiceHost _statusService { get; set; }
66

    
67

    
68
        public bool Pause
69
        {
70
            get { return FileAgent.Pause; }
71
            set
72
            {
73
                FileAgent.Pause = value;
74
                if (value)
75
                {
76
                    StatusKeeper.SetPithosStatus(PithosStatus.SyncPaused);
77
                    StatusNotification.NotifyChange("Paused");
78
                }
79
                else
80
                {
81
                    StatusKeeper.SetPithosStatus(PithosStatus.InSynch);
82
                    StatusNotification.NotifyChange("Synchronizing");
83
                }
84
            }
85
        }
86

    
87
        public string RootPath { get; set; }
88

    
89

    
90
        CancellationTokenSource _cancellationSource;
91

    
92

    
93

    
94
        public void Start()
95
        {
96
            StatusNotification.NotifyChange("Starting");
97
            if (_cancellationSource != null)
98
            {
99
                if (!_cancellationSource.IsCancellationRequested)
100
                    return;
101
            }
102
            _cancellationSource = new CancellationTokenSource();
103

    
104
            var proxyUri = ProxyFromSettings();            
105
            CloudClient.Proxy = proxyUri;
106
            CloudClient.UsePithos = this.UsePithos;
107
            
108
            EnsurePithosContainers();
109
            
110
            StatusKeeper.BlockHash = _blockHash;
111
            StatusKeeper.BlockSize = _blockSize;
112
            
113
            StatusKeeper.StartProcessing(_cancellationSource.Token);
114
            IndexLocalFiles(RootPath);
115
            StartWatcherAgent(RootPath);
116
            StartStatusService();
117
            StartWorkflowAgent();
118
            WorkflowAgent.RestartInterruptedFiles();
119
        }
120

    
121
        private void EnsurePithosContainers()
122
        {
123
            CloudClient.UsePithos = this.UsePithos;
124
            CloudClient.AuthenticationUrl = this.AuthenticationUrl;
125
            CloudClient.Authenticate(UserName, ApiKey);
126

    
127
            var pithosContainers = new[] { TrashContainer,PithosContainer};
128
            foreach (var container in pithosContainers)
129
            {
130
                var info=CloudClient.GetContainerInfo(container);
131
                if (info == ContainerInfo.Empty)
132
                {
133
                    CloudClient.CreateContainer(container);
134
                    info = CloudClient.GetContainerInfo(container);
135
                }
136
                _blockSize = info.BlockSize;
137
                _blockHash = info.BlockHash;
138
            }
139

    
140
            var allContainers= CloudClient.ListContainers();
141
            var extraContainers = from container in allContainers
142
                                  where !pithosContainers.Contains(container.Name.ToLower())
143
                                      select container;
144

    
145

    
146

    
147
        }
148

    
149
        public string AuthenticationUrl { get; set; }
150

    
151
        private Uri ProxyFromSettings()
152
        {            
153
            if (Settings.UseManualProxy)
154
            {
155
                var proxyUri = new UriBuilder
156
                                   {
157
                                       Host = Settings.ProxyServer, 
158
                                       Port = Settings.ProxyPort
159
                                   };
160
                if (Settings.ProxyAuthentication)
161
                {
162
                    proxyUri.UserName = Settings.ProxyUsername;
163
                    proxyUri.Password = Settings.ProxyPassword;
164
                }
165
                return proxyUri.Uri;
166
            }
167
            return null;
168
        }
169

    
170
        private void IndexLocalFiles(string path)
171
        {
172
            StatusNotification.NotifyChange("Indexing Local Files",TraceLevel.Info);
173
            Trace.TraceInformation("[START] Index Local");
174
            try
175
            {
176
                var fragmentsPath=Path.Combine(RootPath, FragmentsFolder);
177
                var files =
178
                    from filePath in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories).AsParallel()
179
                    where !filePath.StartsWith(fragmentsPath,StringComparison.InvariantCultureIgnoreCase) &&
180
                            !filePath.EndsWith(".ignore",StringComparison.InvariantCultureIgnoreCase)
181
                    select filePath.ToLower();
182
                StatusKeeper.StoreUnversionedFiles(files);
183
                
184
            }
185
            catch (Exception exc)
186
            {
187
                Trace.TraceError("[ERROR] Index Local - {0}", exc);
188
            }
189
            finally
190
            {
191
                Trace.TraceInformation("[END] Inxed Local");
192
            }
193
        }
194

    
195
        
196
        private void StartStatusService()
197
        {
198
            // Create a ServiceHost for the CalculatorService type and provide the base address.
199
            var baseAddress = new Uri("net.pipe://localhost/pithos");
200
            _statusService = new ServiceHost(typeof(StatusService), baseAddress);
201
            
202
            var binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
203
            
204
            _statusService.AddServiceEndpoint(typeof(IStatusService), binding, "net.pipe://localhost/pithos/statuscache");
205
            _statusService.AddServiceEndpoint(typeof (ISettingsService), binding, "net.pipe://localhost/pithos/settings");
206

    
207

    
208
            //// Add a mex endpoint
209
            var smb = new ServiceMetadataBehavior
210
                          {
211
                              HttpGetEnabled = true, 
212
                              HttpGetUrl = new Uri("http://localhost:30000/pithos/mex")
213
                          };
214
            _statusService.Description.Behaviors.Add(smb);
215

    
216

    
217
            _statusService.Open();
218
        }
219

    
220
        private void StopStatusService()
221
        {
222
            if (_statusService == null)
223
                return;
224

    
225
            if (_statusService.State == CommunicationState.Faulted)
226
                _statusService.Abort();
227
            else if (_statusService.State != CommunicationState.Closed)
228
                _statusService.Close();
229
            _statusService = null;
230

    
231
        }
232

    
233

    
234
        private void StartWorkflowAgent()
235
        {
236

    
237
            bool connected = NetworkListManager.IsConnectedToInternet;
238
            //If we are not connected retry later
239
            if (!connected)
240
            {
241
                Task.Factory.StartNewDelayed(10000, StartWorkflowAgent);
242
                return;
243
            }
244

    
245
            try
246
            {
247
                CloudClient.UsePithos = this.UsePithos;
248
                CloudClient.AuthenticationUrl = this.AuthenticationUrl;
249
                CloudClient.Authenticate(UserName, ApiKey);
250

    
251
                StartNetworkAgent(RootPath);
252

    
253
                WorkflowAgent.StatusNotification = StatusNotification;
254
                WorkflowAgent.FragmentsPath = Path.Combine(RootPath, FragmentsFolder);
255
                WorkflowAgent.Start();                
256
            }
257
            catch (Exception)
258
            {
259
                //Faild to authenticate due to network or account error
260
                //Retry after a while
261
                Task.Factory.StartNewDelayed(10000, StartWorkflowAgent);
262
            }
263
        }
264

    
265
        public bool UsePithos { get; set; }
266

    
267

    
268

    
269
        internal class LocalFileComparer:EqualityComparer<CloudAction>
270
        {
271
            public override bool Equals(CloudAction x, CloudAction y)
272
            {
273
                if (x.Action != y.Action)
274
                    return false;
275
                if (x.LocalFile != null && y.LocalFile != null && !x.LocalFile.FullName.Equals(y.LocalFile.FullName))
276
                    return false;
277
                if (x.CloudFile != null && y.CloudFile != null && !x.CloudFile.Hash.Equals(y.CloudFile.Hash))
278
                    return false;
279
                if (x.CloudFile == null ^ y.CloudFile == null ||
280
                    x.LocalFile == null ^ y.LocalFile == null)
281
                    return false;
282
                return true;
283
            }
284

    
285
            public override int GetHashCode(CloudAction obj)
286
            {
287
                var hash1 = (obj.LocalFile == null) ? int.MaxValue : obj.LocalFile.FullName.GetHashCode();
288
                var hash2 = (obj.CloudFile == null) ? int.MaxValue : obj.CloudFile.Hash.GetHashCode();
289
                var hash3 = obj.Action.GetHashCode();
290
                return hash1 ^ hash2 & hash3;
291
            }
292
        }        
293

    
294
        private Timer timer;
295

    
296
        private void StartNetworkAgent(string accountPath)
297
        {
298
            NetworkAgent.StatusNotification = StatusNotification;
299

    
300
            NetworkAgent.Start(PithosContainer, TrashContainer,_blockSize,_blockHash);
301

    
302
            NetworkAgent.ProcessRemoteFiles(accountPath);
303
        }
304

    
305
        //Make sure a hidden fragments folder exists to store partial downloads
306
        private static string CreateHiddenFolder(string rootPath, string folderName)
307
        {
308
            if (String.IsNullOrWhiteSpace(rootPath))
309
                throw new ArgumentNullException("rootPath");
310
            if (!Path.IsPathRooted(rootPath))
311
                throw new ArgumentException("rootPath");
312
            if (String.IsNullOrWhiteSpace(folderName))
313
                throw new ArgumentNullException("folderName");
314
            Contract.EndContractBlock();
315

    
316
            var folder = Path.Combine(rootPath, folderName);
317
            if (!Directory.Exists(folder))
318
            {
319
                var info = Directory.CreateDirectory(folder);
320
                info.Attributes |= FileAttributes.Hidden;
321

    
322
                Trace.TraceInformation("Created Fragments Folder: {0}", folder);
323
            }
324
            return folder;
325
        }
326

    
327
       
328

    
329

    
330
        private void StartWatcherAgent(string path)
331
        {
332
            FileAgent.StatusKeeper = StatusKeeper;
333
            FileAgent.Workflow = Workflow;
334
            FileAgent.FragmentsPath = Path.Combine(RootPath, FragmentsFolder);
335
            FileAgent.Start(path);
336
        }
337

    
338
        public void Stop()
339
        {            
340
            FileAgent.Stop();
341
            if (timer != null)
342
                timer.Dispose();
343
            timer = null;
344
            StopStatusService();
345
        }
346

    
347

    
348
        ~PithosMonitor()
349
        {
350
            Dispose(false);
351
        }
352

    
353
        public void Dispose()
354
        {
355
            Dispose(true);
356
            GC.SuppressFinalize(this);
357
        }
358

    
359
        protected virtual void Dispose(bool disposing)
360
        {
361
            if (disposing)
362
            {
363
                Stop();
364
            }
365
        }
366

    
367

    
368
    }
369

    
370
    public interface IStatusNotification
371
    {        
372
        void NotifyChange(string status,TraceLevel level=TraceLevel.Info);
373
    }
374
}