Statistics
| Branch: | Revision:

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

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
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

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

    
57
        private Network.AccountInfo _accountInfo;
58

    
59

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

    
62

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

    
82
        public string RootPath { get; set; }
83

    
84

    
85
        CancellationTokenSource _cancellationSource;
86

    
87

    
88
        private bool _isInitialized;
89

    
90
        public void Start()
91
        {            
92
            if (String.IsNullOrWhiteSpace(ApiKey))
93
                throw new InvalidOperationException("The ApiKey is empty");
94
            if (String.IsNullOrWhiteSpace(UserName))
95
                throw new InvalidOperationException("The UserName is empty");
96
            if (String.IsNullOrWhiteSpace(AuthenticationUrl))
97
                throw new InvalidOperationException("The Authentication url is empty");
98
            Contract.EndContractBlock();
99

    
100
            StatusNotification.NotifyChange("Starting");
101
            if (_isInitialized)
102
            {
103
                if (!_cancellationSource.IsCancellationRequested)
104
                    return;
105
            }
106
            _cancellationSource = new CancellationTokenSource();
107

    
108
            CloudClient=new CloudFilesClient(UserName,ApiKey);
109
            var proxyUri = ProxyFromSettings();            
110
            CloudClient.Proxy = proxyUri;
111
            CloudClient.UsePithos = this.UsePithos;
112
            CloudClient.AuthenticationUrl = this.AuthenticationUrl;            
113

    
114
            _accountInfo = CloudClient.Authenticate();
115
            _accountInfo.AccountPath = RootPath;
116

    
117

    
118
            EnsurePithosContainers();
119
            
120
            StatusKeeper.BlockHash = _blockHash;
121
            StatusKeeper.BlockSize = _blockSize;
122
            
123
            StatusKeeper.StartProcessing(_cancellationSource.Token);
124
            IndexLocalFiles(RootPath);
125
            StartWatcherAgent(RootPath);
126

    
127
            StartNetworkAgent();
128

    
129
            StartWorkflowAgent();
130
            WorkflowAgent.RestartInterruptedFiles(_accountInfo);
131
            _isInitialized = true;
132
        }
133

    
134
        private void EnsurePithosContainers()
135
        {
136

    
137
            //Create the two default containers if they are missing
138
            var pithosContainers = new List<string>{ FolderConstants.TrashContainer,FolderConstants.PithosContainer};
139
            foreach (var container in pithosContainers)
140
            {                
141
                var info=CloudClient.GetContainerInfo(this.UserName, container);
142
                if (info == ContainerInfo.Empty)
143
                {
144
                    CloudClient.CreateContainer(this.UserName, container);
145
                    info = CloudClient.GetContainerInfo(this.UserName, container);
146
                }
147
                _blockSize = info.BlockSize;
148
                _blockHash = info.BlockHash;
149
                _accountInfo.BlockSize = _blockSize;
150
                _accountInfo.BlockHash = _blockHash;
151
            }
152
        }
153

    
154
        public string AuthenticationUrl { get; set; }
155

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

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

    
192
                }
193
                catch (Exception exc)
194
                {
195
                    Log.Error("[ERROR]", exc);
196
                }
197
                finally
198
                {
199
                    Log.Info("[END]");
200
                }
201
            }
202
        }
203

    
204
        
205
  
206

    
207

    
208
        private void StartWorkflowAgent()
209
        {
210

    
211
            bool connected = NetworkListManager.IsConnectedToInternet;
212
            //If we are not connected retry later
213
            if (!connected)
214
            {
215
                Task.Factory.StartNewDelayed(10000, StartWorkflowAgent);
216
                return;
217
            }
218

    
219
            try
220
            {
221
                WorkflowAgent.StatusNotification = StatusNotification;
222
                WorkflowAgent.Start();                
223
            }
224
            catch (Exception)
225
            {
226
                //Faild to authenticate due to network or account error
227
                //Retry after a while
228
                Task.Factory.StartNewDelayed(10000, StartWorkflowAgent);
229
            }
230
        }
231

    
232
        public bool UsePithos { get; set; }
233

    
234

    
235

    
236
        internal class LocalFileComparer:EqualityComparer<CloudAction>
237
        {
238
            public override bool Equals(CloudAction x, CloudAction y)
239
            {
240
                if (x.Action != y.Action)
241
                    return false;
242
                if (x.LocalFile != null && y.LocalFile != null && !x.LocalFile.FullName.Equals(y.LocalFile.FullName))
243
                    return false;
244
                if (x.CloudFile != null && y.CloudFile != null )
245
                {
246
                    if (x.CloudFile.Hash == null & y.CloudFile.Hash != null)
247
                        return false;
248
                    if (x.CloudFile.Hash != null & y.CloudFile.Hash == null)
249
                        return false;
250
                    if (x.CloudFile.Hash == null & y.CloudFile.Hash == null)
251
                        return (x.CloudFile.Name == y.CloudFile.Name);
252
                    if (!x.CloudFile.Hash.Equals(y.CloudFile.Hash))
253
                        return false;
254
                }
255
                if (x.CloudFile == null ^ y.CloudFile == null ||
256
                    x.LocalFile == null ^ y.LocalFile == null)
257
                    return false;
258
                return true;
259
            }
260

    
261
            public override int GetHashCode(CloudAction obj)
262
            {
263
                var hash1 = (obj.LocalFile == null) ? int.MaxValue : obj.LocalFile.FullName.GetHashCode();
264
                var hash2 = (obj.CloudFile == null) ? int.MaxValue : (obj.CloudFile.Hash ?? obj.CloudFile.Name??"").GetHashCode();
265
                var hash3 = obj.Action.GetHashCode();
266
                return hash1 ^ hash2 & hash3;
267
            }
268
        }        
269

    
270
        private Timer timer;
271

    
272
        private void StartNetworkAgent()
273
        {
274

    
275
            NetworkAgent.AddAccount(_accountInfo);
276

    
277
            NetworkAgent.StatusNotification = StatusNotification;
278
            
279

    
280
            NetworkAgent.Start();
281

    
282
            NetworkAgent.ProcessRemoteFiles();
283
        }
284

    
285
        //Make sure a hidden fragments folder exists to store partial downloads
286
        private static string CreateHiddenFolder(string rootPath, string folderName)
287
        {
288
            if (String.IsNullOrWhiteSpace(rootPath))
289
                throw new ArgumentNullException("rootPath");
290
            if (!Path.IsPathRooted(rootPath))
291
                throw new ArgumentException("rootPath");
292
            if (String.IsNullOrWhiteSpace(folderName))
293
                throw new ArgumentNullException("folderName");
294
            Contract.EndContractBlock();
295

    
296
            var folder = Path.Combine(rootPath, folderName);
297
            if (!Directory.Exists(folder))
298
            {
299
                var info = Directory.CreateDirectory(folder);
300
                info.Attributes |= FileAttributes.Hidden;
301

    
302
                Log.InfoFormat("Created Fragments Folder: {0}", folder);
303
            }
304
            return folder;
305
        }
306

    
307
       
308

    
309

    
310
        private void StartWatcherAgent(string path)
311
        {
312
            FileAgent.StatusKeeper = StatusKeeper;
313
            FileAgent.Workflow = Workflow;
314
            FileAgent.FragmentsPath = Path.Combine(RootPath, FolderConstants.FragmentsFolder);
315
            FileAgent.Start(_accountInfo,path);
316
        }
317

    
318
        public void Stop()
319
        {          
320
            if (FileAgent!=null)
321
                FileAgent.Stop();
322
            FileAgent = null;
323
            if (timer != null)
324
                timer.Dispose();
325
            timer = null;            
326
        }
327

    
328

    
329
        ~PithosMonitor()
330
        {
331
            Dispose(false);
332
        }
333

    
334
        public void Dispose()
335
        {
336
            Dispose(true);
337
            GC.SuppressFinalize(this);
338
        }
339

    
340
        protected virtual void Dispose(bool disposing)
341
        {
342
            if (disposing)
343
            {
344
                Stop();
345
            }
346
        }
347

    
348

    
349
        public void MoveFileStates(string oldPath, string newPath)
350
        {
351
            if (String.IsNullOrWhiteSpace(oldPath))
352
                throw new ArgumentNullException("oldPath");
353
            if (!Path.IsPathRooted(oldPath))
354
                throw new ArgumentException("oldPath must be an absolute path","oldPath");
355
            if (string.IsNullOrWhiteSpace(newPath))
356
                throw new ArgumentNullException("newPath");
357
            if (!Path.IsPathRooted(newPath))
358
                throw new ArgumentException("newPath must be an absolute path","newPath");
359
            Contract.EndContractBlock();
360

    
361
            StatusKeeper.ChangeRoots(oldPath, newPath);
362
        }
363
    }
364

    
365
    public interface IStatusNotification
366
    {        
367
        void NotifyChange(string status,TraceLevel level=TraceLevel.Info);
368
        void NotifyChangedFile(string filePath);
369
    }
370
}