Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / Agents / SnapshotDifferencer.cs @ 1cc1e8c5

History | View | Annotate | Download (8.5 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
        /// <summary>
157
        /// Deleted objects are those that existed in the Previous listing
158
        /// but are not found in the Current listing
159
        /// </summary>
160
        public IEnumerable<ObjectInfo> Deleted
161
        {
162
            get { return _state.Previous
163
                .Where(info=>!_state.CurrentDict.ContainsKey(info.UUID)); }
164
        }
165

    
166
        /// <summary>
167
        /// Created objects are those that exist in the Current listing
168
        /// but are not found in the Previous listing 
169
        /// </summary>
170
        public IEnumerable<ObjectInfo> Created
171
        {
172
            get { return _state.Current
173
                .Where(info=>!_state.PreviousDict.ContainsKey(info.UUID)); }
174
        }
175
                
176
        public IEnumerable<ObjectInfo> Common
177
        {
178
            get { return _state.Common; }
179
        }
180

    
181
        public IEnumerable<ObjectInfo> Changed
182
        {
183
            get
184
            {
185
                return Common.Where(info => 
186
                    //The hash is different
187
                    info.PreviousHash != info.X_Object_Hash 
188
                    //And the Uri is unchanged or there is no previous version
189
                    && (info.Previous == null ||  info.Uri == info.Previous.Uri));
190
            }
191
        }
192

    
193
        /// <summary>
194
        /// Unchanged objects have the same current and previous hash
195
        /// </summary>
196
        public IEnumerable<ObjectInfo> Unchanged
197
        {
198
            get { return Common.Where(i => i.PreviousHash == i.X_Object_Hash); }
199
        }
200

    
201
        /// <summary>
202
        /// Moved objects have a previous version with a different name
203
        /// </summary>
204
        public IEnumerable<ObjectInfo>  Moved
205
        {
206
            get
207
            {                                
208
                return Common.Where(info=>
209
                    //A previous version exists
210
                    info.Previous!= null 
211
                    //and the Uri is different
212
                    && info.Uri!=info.Previous.Uri);                    
213
            }
214
        }
215
    }
216

    
217
    public class AccountsDifferencer
218
    {
219
        readonly ConcurrentDictionary<Uri, SnapshotDifferencer> _differencers = new ConcurrentDictionary<Uri, SnapshotDifferencer>();
220

    
221
        public ConcurrentDictionary<Uri, SnapshotDifferencer> Differencers { get { return _differencers; } }
222

    
223
        public SnapshotDifferencer PostSnapshot(AccountInfo accountInfo, List<ObjectInfo> cleanRemotes)
224
        {
225
            SnapshotDifferencer differencer;
226
            if (!_differencers.TryGetValue(accountInfo.AccountKey, out differencer))
227
            {
228
                differencer = new SnapshotDifferencer();
229
                _differencers[accountInfo.AccountKey] = differencer;
230
            }
231
            differencer.Post(cleanRemotes);
232
            return differencer;
233
        }
234

    
235
    }
236
}