Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / Agents / SnapshotDifferencer.cs @ 537c5f20

History | View | Annotate | Download (9 kB)

1
#region
2
/* -----------------------------------------------------------------------
3
 * <copyright file="SnapshotDifferencer.cs" company="GRNet">
4
 * 
5
 * Copyright 2011-2012 GRNET S.A. All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or
8
 * without modification, are permitted provided that the following
9
 * conditions are met:
10
 *
11
 *   1. Redistributions of source code must retain the above
12
 *      copyright notice, this list of conditions and the following
13
 *      disclaimer.
14
 *
15
 *   2. Redistributions in binary form must reproduce the above
16
 *      copyright notice, this list of conditions and the following
17
 *      disclaimer in the documentation and/or other materials
18
 *      provided with the distribution.
19
 *
20
 *
21
 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
22
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
25
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
 * POSSIBILITY OF SUCH DAMAGE.
33
 *
34
 * The views and conclusions contained in the software and
35
 * documentation are those of the authors and should not be
36
 * interpreted as representing official policies, either expressed
37
 * or implied, of GRNET S.A.
38
 * </copyright>
39
 * -----------------------------------------------------------------------
40
 */
41
#endregion
42

    
43
using System;
44
using System.Collections.Concurrent;
45
using System.Diagnostics.Contracts;
46
using Pithos.Interfaces;
47
using Pithos.Network;
48
using System.Collections.Generic;
49
using System.Linq;
50

    
51
namespace Pithos.Core.Agents
52
{
53

    
54
    /// <summary>
55
    /// Calculates the differences between two ObjectInfo snapshots.
56
    /// </summary>
57
    public class SnapshotDifferencer
58
    {
59
        /// <summary>
60
        /// Contains the previous and current listings.
61
        /// Using a separate class for the state simplifies the Post method
62
        /// which has to modify the listings
63
        /// </summary>
64
        private class State
65
        {
66
            /// <summary>
67
            /// The previous snapshot listing
68
            /// </summary>
69
            public readonly ObjectInfo[] Previous;
70

    
71
            /// <summary>
72
            /// The current snapshot listing
73
            /// </summary>
74
            public readonly ObjectInfo[] Current;
75

    
76
            public readonly Dictionary<string, ObjectInfo> CurrentDict;
77

    
78
            public readonly Dictionary<string, ObjectInfo> PreviousDict;
79

    
80

    
81
            /// <summary>
82
            /// Common objects, lazily evalueated. 
83
            /// The common objects are used to calculate both the Changed and Unchanged objects
84
            /// </summary>
85
            public readonly IEnumerable<ObjectInfo> Common;
86

    
87
            private readonly static ObjectInfo[] Empty = new ObjectInfo[0];
88

    
89
            [ContractInvariantMethod]
90
            private void StatInvariant()
91
            {
92
// ReSharper disable InvocationIsSkipped
93
                Contract.Invariant(Current!=null);
94
                Contract.Invariant(Previous!=null);
95
                Contract.Invariant(CurrentDict!=null);
96
                Contract.Invariant(PreviousDict!=null);
97
                Contract.Invariant(Common!=null);
98
// ReSharper restore InvocationIsSkipped
99
            }
100

    
101
            public State(ObjectInfo[] previous, ObjectInfo[] current)
102
            {
103
                Previous = previous ?? Empty;
104
                Current = current ?? Empty;
105
                CurrentDict = Current.ToDictionary(info => info.UUID);
106
                PreviousDict = Previous.ToDictionary(info => info.UUID);
107

    
108
                //Attach the previous version to the current listings
109
                foreach (var info in Current)
110
                {
111
                    ObjectInfo prev;
112
                    if (PreviousDict.TryGetValue(info.UUID, out prev))
113
                        info.SetPrevious(prev);
114
                }
115

    
116
                Common=Current.Where(c=>c.Previous !=null);
117
            }
118
        }
119

    
120

    
121
        private State _state;
122

    
123
        /// <summary>
124
        /// Default constructor. Initializes the Current and Previous listings to empty lists
125
        /// </summary>
126
        public SnapshotDifferencer()
127
        {                        
128
            _state = new State(null, null);
129
        }
130

    
131
        /// <summary>
132
        /// Posts a new listing
133
        /// </summary>
134
        /// <param name="list"></param>
135
        /// <returns></returns>
136
        public SnapshotDifferencer Post(IEnumerable<ObjectInfo> list)
137
        {
138
            ObjectInfo[] newCurrent=null;
139
            if (list != null)
140
            {
141
                //The state field holds the old state
142
                var oldState = _state;
143
                //Replace any NoModification entries with previous values that have
144
                //the same account, container and possibly, folder
145
                newCurrent = list.Replace(
146
                    info => info is NoModificationInfo,
147
                    noMod => oldState.Current.Where(noMod.CorrespondsTo))
148
                    .ToArray();
149
            }
150
            //Set the new state
151
            _state = new State(_state.Current, newCurrent);
152

    
153
            return this;
154
        }
155

    
156
        public ObjectInfo[] Previous
157
        {
158
            get
159
            {
160
                if (_state==null)
161
                    return new ObjectInfo[0];
162
                return _state.Previous;
163
            }
164
        }
165

    
166
        public ObjectInfo[] Current
167
        {
168
            get
169
            {
170
                if (_state == null)
171
                    return new ObjectInfo[0];
172
                return _state.Current;                
173
            }
174
        }
175

    
176
        
177
        /// <summary>
178
        /// Deleted objects are those that existed in the Previous listing
179
        /// but are not found in the Current listing
180
        /// </summary>
181
        public IEnumerable<ObjectInfo> Deleted
182
        {
183
            get { return _state.Previous
184
                .Where(info=>!_state.CurrentDict.ContainsKey(info.UUID)); }
185
        }
186

    
187
        /// <summary>
188
        /// Created objects are those that exist in the Current listing
189
        /// but are not found in the Previous listing 
190
        /// </summary>
191
        public IEnumerable<ObjectInfo> Created
192
        {
193
            get { return _state.Current
194
                .Where(info=>!_state.PreviousDict.ContainsKey(info.UUID)); }
195
        }
196
                
197
        public IEnumerable<ObjectInfo> Common
198
        {
199
            get { return _state.Common; }
200
        }
201

    
202
        public IEnumerable<ObjectInfo> Changed
203
        {
204
            get
205
            {
206
                return Common.Where(info => 
207
                    //The hash is different
208
                    info.PreviousHash != info.X_Object_Hash 
209
                    //And the Uri is unchanged or there is no previous version
210
                    && (info.Previous == null ||  info.Uri == info.Previous.Uri));
211
            }
212
        }
213

    
214
        /// <summary>
215
        /// Unchanged objects have the same current and previous hash
216
        /// </summary>
217
        public IEnumerable<ObjectInfo> Unchanged
218
        {
219
            get { return Common.Where(i => i.PreviousHash == i.X_Object_Hash); }
220
        }
221

    
222
        /// <summary>
223
        /// Moved objects have a previous version with a different name
224
        /// </summary>
225
        public IEnumerable<ObjectInfo>  Moved
226
        {
227
            get
228
            {                                
229
                return Common.Where(info=>
230
                    //A previous version exists
231
                    info.Previous!= null 
232
                    //and the Uri is different
233
                    && info.Uri!=info.Previous.Uri);                    
234
            }
235
        }
236
    }
237

    
238
    public class AccountsDifferencer
239
    {
240
        readonly ConcurrentDictionary<Uri, SnapshotDifferencer> _differencers = new ConcurrentDictionary<Uri, SnapshotDifferencer>();
241

    
242
        public ConcurrentDictionary<Uri, SnapshotDifferencer> Differencers { get { return _differencers; } }
243

    
244
        public SnapshotDifferencer PostSnapshot(AccountInfo accountInfo, List<ObjectInfo> cleanRemotes)
245
        {
246
            SnapshotDifferencer differencer;
247
            if (!_differencers.TryGetValue(accountInfo.AccountKey, out differencer))
248
            {
249
                differencer = new SnapshotDifferencer();
250
                _differencers[accountInfo.AccountKey] = differencer;
251
            }
252
            differencer.Post(cleanRemotes);
253
            return differencer;
254
        }
255

    
256
    }
257
}