Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / PithosMonitor.cs @ c28a075a

History | View | Annotate | Download (12.2 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 Network.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

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

    
134
            StartNetworkAgent();
135

    
136
            StartWorkflowAgent();
137
            WorkflowAgent.RestartInterruptedFiles(_accountInfo);
138
            _isInitialized = true;
139
        }
140

    
141
        private void EnsurePithosContainers()
142
        {
143

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

    
161
        public string AuthenticationUrl { get; set; }
162

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

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

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

    
211
        
212
  
213

    
214

    
215
        private void StartWorkflowAgent()
216
        {
217

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

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

    
239
        public bool UsePithos { get; set; }
240

    
241

    
242

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

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

    
277
        private Timer timer;
278

    
279
        private void StartNetworkAgent()
280
        {
281

    
282
            NetworkAgent.AddAccount(_accountInfo);
283

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

    
288
            NetworkAgent.ProcessRemoteFiles();
289
        }
290

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

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

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

    
313
       
314

    
315

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

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

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

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

    
338

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

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

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

    
358

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

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

    
375
    public interface IStatusNotification
376
    {        
377
        void NotifyChange(string status,TraceLevel level=TraceLevel.Info);
378
        void NotifyChangedFile(string filePath);
379
    }
380
}