Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / PithosMonitor.cs @ 0bd56b7c

History | View | Annotate | Download (12.8 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
using log4net;
22

    
23
namespace Pithos.Core
24
{
25
    [Export(typeof(PithosMonitor))]
26
    public class PithosMonitor:IDisposable
27
    {
28
        private int _blockSize;
29
        private string _blockHash;
30

    
31
        [Import]
32
        public IPithosSettings Settings{get;set;}
33

    
34
        [Import]
35
        public IStatusKeeper StatusKeeper { get; set; }
36

    
37
        [Import]
38
        public IPithosWorkflow Workflow { get; set; }
39

    
40
        public ICloudClient CloudClient { get; set; }
41

    
42
        public IStatusNotification StatusNotification { get; set; }
43

    
44
        [Import]
45
        public FileAgent FileAgent { get; set; }
46
        
47
        [Import]
48
        public WorkflowAgent WorkflowAgent { get; set; }
49
        
50
        [Import]
51
        public NetworkAgent NetworkAgent { get; set; }        
52

    
53
        public string UserName { get; set; }
54
        public string ApiKey { get; set; }
55

    
56
        private AccountInfo _accountInfo;
57

    
58

    
59
        private static readonly ILog Log = LogManager.GetLogger(typeof(PithosMonitor));
60

    
61

    
62
        public bool Pause
63
        {
64
            get { return FileAgent.Pause; }
65
            set
66
            {
67
                FileAgent.Pause = value;
68
                if (value)
69
                {
70
                    StatusKeeper.SetPithosStatus(PithosStatus.SyncPaused);
71
                    StatusNotification.NotifyChange("Paused");
72
                }
73
                else
74
                {
75
                    StatusKeeper.SetPithosStatus(PithosStatus.InSynch);
76
                    StatusNotification.NotifyChange("Synchronizing");
77
                }
78
            }
79
        }
80

    
81
        private string _rootPath;
82
        public string RootPath
83
        {
84
            get { return _rootPath; }
85
            set
86
            {
87
                _rootPath = value.ToLower();
88
            }
89
        }
90

    
91

    
92
        CancellationTokenSource _cancellationSource;
93

    
94

    
95
        private bool _isInitialized;
96

    
97
        public void Start()
98
        {            
99
            if (String.IsNullOrWhiteSpace(ApiKey))
100
                throw new InvalidOperationException("The ApiKey is empty");
101
            if (String.IsNullOrWhiteSpace(UserName))
102
                throw new InvalidOperationException("The UserName is empty");
103
            if (String.IsNullOrWhiteSpace(AuthenticationUrl))
104
                throw new InvalidOperationException("The Authentication url is empty");
105
            Contract.EndContractBlock();
106

    
107
            StatusNotification.NotifyChange("Starting");
108
            if (_isInitialized)
109
            {
110
                if (!_cancellationSource.IsCancellationRequested)
111
                    return;
112
            }
113
            _cancellationSource = new CancellationTokenSource();
114

    
115
            CloudClient=new CloudFilesClient(UserName,ApiKey);
116
            var proxyUri = ProxyFromSettings();            
117
            CloudClient.Proxy = proxyUri;
118
            CloudClient.UsePithos = this.UsePithos;
119
            CloudClient.AuthenticationUrl = this.AuthenticationUrl;            
120

    
121
            _accountInfo = CloudClient.Authenticate();
122
            _accountInfo.AccountPath = RootPath;
123

    
124
            var policy=CloudClient.GetAccountPolicies(_accountInfo);
125

    
126
            StatusNotification.NotifyAccount(policy);
127
            EnsurePithosContainers();
128
            
129
            StatusKeeper.BlockHash = _blockHash;
130
            StatusKeeper.BlockSize = _blockSize;
131
            
132
            StatusKeeper.StartProcessing(_cancellationSource.Token);
133
            IndexLocalFiles();
134
            StartWatcherAgent();
135

    
136
            StartNetworkAgent();
137

    
138
            StartWorkflowAgent();
139
            WorkflowAgent.RestartInterruptedFiles(_accountInfo);
140
            _isInitialized = true;
141
        }
142

    
143
        private void EnsurePithosContainers()
144
        {
145

    
146
            //Create the two default containers if they are missing
147
            var pithosContainers = new List<string>{ FolderConstants.TrashContainer,FolderConstants.PithosContainer};
148
            foreach (var container in pithosContainers)
149
            {                
150
                var info=CloudClient.GetContainerInfo(this.UserName, container);
151
                if (info == ContainerInfo.Empty)
152
                {
153
                    CloudClient.CreateContainer(this.UserName, container);
154
                    info = CloudClient.GetContainerInfo(this.UserName, container);
155
                }
156
                _blockSize = info.BlockSize;
157
                _blockHash = info.BlockHash;
158
                _accountInfo.BlockSize = _blockSize;
159
                _accountInfo.BlockHash = _blockHash;
160
            }
161
        }
162

    
163
        public string AuthenticationUrl { get; set; }
164

    
165
        private Uri ProxyFromSettings()
166
        {            
167
            if (Settings.UseManualProxy)
168
            {
169
                var proxyUri = new UriBuilder
170
                                   {
171
                                       Host = Settings.ProxyServer, 
172
                                       Port = Settings.ProxyPort
173
                                   };
174
                if (Settings.ProxyAuthentication)
175
                {
176
                    proxyUri.UserName = Settings.ProxyUsername;
177
                    proxyUri.Password = Settings.ProxyPassword;
178
                }
179
                return proxyUri.Uri;
180
            }
181
            return null;
182
        }
183

    
184
        private void IndexLocalFiles()
185
        {
186
            StatusNotification.NotifyChange("Indexing Local Files",TraceLevel.Info);
187
            using (log4net.ThreadContext.Stacks["Monitor"].Push("Indexing local files"))
188
            {
189
                Log.Info("START");
190
                try
191
                {
192
                    var fragmentsPath = Path.Combine(RootPath, FolderConstants.FragmentsFolder);
193
                    var directory = new DirectoryInfo(RootPath);
194
                    var files =
195
                        from file in directory.EnumerateFiles("*", SearchOption.AllDirectories)
196
                        where !file.FullName.StartsWith(fragmentsPath, StringComparison.InvariantCultureIgnoreCase) &&
197
                              !file.Extension.Equals("ignore", StringComparison.InvariantCultureIgnoreCase)
198
                        select file;
199
                    StatusKeeper.ProcessExistingFiles(files);
200

    
201
                }
202
                catch (Exception exc)
203
                {
204
                    Log.Error("[ERROR]", exc);
205
                }
206
                finally
207
                {
208
                    Log.Info("[END]");
209
                }
210
            }
211
        }
212

    
213
        
214
  
215

    
216

    
217
        private void StartWorkflowAgent()
218
        {
219

    
220
            bool connected = NetworkListManager.IsConnectedToInternet;
221
            //If we are not connected retry later
222
            if (!connected)
223
            {
224
                Task.Factory.StartNewDelayed(10000, StartWorkflowAgent);
225
                return;
226
            }
227

    
228
            try
229
            {
230
                WorkflowAgent.StatusNotification = StatusNotification;
231
                WorkflowAgent.Start();                
232
            }
233
            catch (Exception)
234
            {
235
                //Faild to authenticate due to network or account error
236
                //Retry after a while
237
                Task.Factory.StartNewDelayed(10000, StartWorkflowAgent);
238
            }
239
        }
240

    
241
        public bool UsePithos { get; set; }
242

    
243

    
244
        internal class LocalFileComparer:EqualityComparer<CloudAction>
245
        {
246
            public override bool Equals(CloudAction x, CloudAction y)
247
            {
248
                if (x.Action != y.Action)
249
                    return false;
250
                if (x.LocalFile != null && y.LocalFile != null && !x.LocalFile.FullName.Equals(y.LocalFile.FullName))
251
                    return false;
252
                if (x.CloudFile != null && y.CloudFile != null )
253
                {
254
                    if (x.CloudFile.Hash == null & y.CloudFile.Hash != null)
255
                        return false;
256
                    if (x.CloudFile.Hash != null & y.CloudFile.Hash == null)
257
                        return false;
258
                    if (x.CloudFile.Hash == null & y.CloudFile.Hash == null)
259
                        return (x.CloudFile.Name == y.CloudFile.Name);
260
                    if (!x.CloudFile.Hash.Equals(y.CloudFile.Hash))
261
                        return false;
262
                }
263
                if (x.CloudFile == null ^ y.CloudFile == null ||
264
                    x.LocalFile == null ^ y.LocalFile == null)
265
                    return false;
266
                return true;
267
            }
268

    
269
            public override int GetHashCode(CloudAction obj)
270
            {
271
                var hash1 = (obj.LocalFile == null) ? int.MaxValue : obj.LocalFile.FullName.GetHashCode();
272
                var hash2 = (obj.CloudFile == null) ? int.MaxValue : (obj.CloudFile.Hash ?? obj.CloudFile.Name??"").GetHashCode();
273
                var hash3 = obj.Action.GetHashCode();
274
                return hash1 ^ hash2 & hash3;
275
            }
276
        }        
277

    
278
        private Timer timer;
279

    
280
        private void StartNetworkAgent()
281
        {
282

    
283
            NetworkAgent.AddAccount(_accountInfo);
284

    
285
            NetworkAgent.StatusNotification = StatusNotification;
286
                        
287
            NetworkAgent.Start();
288

    
289
            NetworkAgent.ProcessRemoteFiles();
290
        }
291

    
292
        //Make sure a hidden fragments folder exists to store partial downloads
293
        private static string CreateHiddenFolder(string rootPath, string folderName)
294
        {
295
            if (String.IsNullOrWhiteSpace(rootPath))
296
                throw new ArgumentNullException("rootPath");
297
            if (!Path.IsPathRooted(rootPath))
298
                throw new ArgumentException("rootPath");
299
            if (String.IsNullOrWhiteSpace(folderName))
300
                throw new ArgumentNullException("folderName");
301
            Contract.EndContractBlock();
302

    
303
            var folder = Path.Combine(rootPath, folderName);
304
            if (!Directory.Exists(folder))
305
            {
306
                var info = Directory.CreateDirectory(folder);
307
                info.Attributes |= FileAttributes.Hidden;
308

    
309
                Log.InfoFormat("Created Fragments Folder: {0}", folder);
310
            }
311
            return folder;
312
        }
313

    
314
       
315

    
316

    
317
        private void StartWatcherAgent()
318
        {
319
            AgentLocator<FileAgent>.Register(FileAgent,RootPath);
320

    
321
            FileAgent.StatusKeeper = StatusKeeper;
322
            FileAgent.Workflow = Workflow;
323
            FileAgent.FragmentsPath = Path.Combine(RootPath, FolderConstants.FragmentsFolder);
324
            FileAgent.Start(_accountInfo, RootPath);
325
        }
326

    
327
        public void Stop()
328
        {
329
            AgentLocator<FileAgent>.Remove(RootPath);
330

    
331
            if (FileAgent!=null)
332
                FileAgent.Stop();
333
            FileAgent = null;
334
            if (timer != null)
335
                timer.Dispose();
336
            timer = null;            
337
        }
338

    
339

    
340
        ~PithosMonitor()
341
        {
342
            Dispose(false);
343
        }
344

    
345
        public void Dispose()
346
        {
347
            Dispose(true);
348
            GC.SuppressFinalize(this);
349
        }
350

    
351
        protected virtual void Dispose(bool disposing)
352
        {
353
            if (disposing)
354
            {
355
                Stop();
356
            }
357
        }
358

    
359

    
360
        public void MoveFileStates(string oldPath, string newPath)
361
        {
362
            if (String.IsNullOrWhiteSpace(oldPath))
363
                throw new ArgumentNullException("oldPath");
364
            if (!Path.IsPathRooted(oldPath))
365
                throw new ArgumentException("oldPath must be an absolute path","oldPath");
366
            if (string.IsNullOrWhiteSpace(newPath))
367
                throw new ArgumentNullException("newPath");
368
            if (!Path.IsPathRooted(newPath))
369
                throw new ArgumentException("newPath must be an absolute path","newPath");
370
            Contract.EndContractBlock();
371

    
372
            StatusKeeper.ChangeRoots(oldPath, newPath);
373
        }
374

    
375
        public void AddSelectivePaths(string[] added)
376
        {
377
           /* FileAgent.SelectivePaths.AddRange(added);
378
            NetworkAgent.SyncPaths(added);*/
379
        }
380

    
381
        public void RemoveSelectivePaths(string[] removed)
382
        {
383
            FileAgent.SelectivePaths.RemoveAll(removed.Contains);
384
            foreach (var removedPath in removed.Where(Directory.Exists))
385
            {
386
                Directory.Delete(removedPath,true);
387
            }
388
        }
389
    }
390

    
391

    
392
    public interface IStatusNotification
393
    {        
394
        void NotifyChange(string status,TraceLevel level=TraceLevel.Info);
395
        void NotifyChangedFile(string filePath);
396
        void NotifyAccount(AccountInfo policy);
397
    }
398
}