Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / Agents / SnapshotDifferencer.cs @ b666b39a

History | View | Annotate | Download (8.6 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
using System.Collections.Concurrent;
43
using System.Diagnostics.Contracts;
44
using Pithos.Interfaces;
45
using Pithos.Network;
46
using System;
47
using System.Collections.Generic;
48
using System.Linq;
49

    
50
namespace Pithos.Core.Agents
51
{
52

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

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

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

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

    
79

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

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

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

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

    
107
                Common=new Lazy<IEnumerable<ObjectInfo>>(() =>
108
                    Current.Join(Previous,
109
                                outKey => new { outKey.Account, outKey.Container, outKey.Name },
110
                                inKey => new { inKey.Account, inKey.Container, inKey.Name },
111
                                (outer, inner) =>outer.SetPrevious(inner)));            
112
            }
113
        }
114

    
115

    
116
        private State _state;
117

    
118
        /// <summary>
119
        /// The comparer used to identify common objects.
120
        /// Objects are considered common when they have the same Account, Container and Name
121
        /// </summary>
122
        private readonly ObjectInfoComparer _comparer = new ObjectInfoComparer();
123

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

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

    
154
            return this;
155
        }
156
        
157
        /// <summary>
158
        /// Deleted objects are those that existed in the Previous listing
159
        /// but are not found in the Current listing, and their UUIDs do not
160
        /// appear in the Current listing (ie they were not renamed/moved)
161
        /// </summary>
162
        public IEnumerable<ObjectInfo> Deleted
163
        {
164
            get { return _state.Previous.Except(_state.Current,_comparer)
165
                .Where(info=>!_state.CurrentDict.ContainsKey(info.UUID)); }
166
        }
167

    
168
        /// <summary>
169
        /// Created objects are those that exist in the Current listing
170
        /// but are not found in the Previous listing, and their UUIDs do not
171
        /// appear in the Previous listing (ie they were not renamed/moved)
172
        /// </summary>
173
        public IEnumerable<ObjectInfo> Created
174
        {
175
            get { return _state.Current.Except(_state.Previous,_comparer)
176
                .Where(info=>!_state.PreviousDict.ContainsKey(info.UUID)); }
177
        }
178
                
179
        public IEnumerable<ObjectInfo> Common
180
        {
181
            get { return _state.Common.Value; }
182
        }
183

    
184
        public IEnumerable<ObjectInfo> Changed
185
        {
186
            get{return Common.Where(i=>i.PreviousHash!=i.Hash);}
187
        }
188
        public IEnumerable<ObjectInfo> Unchanged
189
        {
190
            get{ return Common.Where(i => i.PreviousHash == i.Hash);}
191
        }
192

    
193
        public IEnumerable<ObjectInfo>  Moved
194
        {
195
            get
196
            {
197
                                
198
                return _state.Current.Join(_state.Previous,
199
                                    outer => outer.UUID,
200
                                    inner => inner.UUID,
201
                                    (outer, inner) => (outer.Name == inner.Name ? null : outer.SetPrevious(inner)))
202
                    .Where(t => t != null);                
203
            }
204
        }
205
    }
206

    
207
    public class AccountsDifferencer
208
    {
209
        readonly ConcurrentDictionary<string, SnapshotDifferencer> _differencers = new ConcurrentDictionary<string, SnapshotDifferencer>();
210

    
211
        public ConcurrentDictionary<string, SnapshotDifferencer> Differencers { get { return _differencers; } }
212

    
213
        public SnapshotDifferencer PostSnapshot(AccountInfo accountInfo, List<ObjectInfo> cleanRemotes)
214
        {
215
            SnapshotDifferencer differencer;
216
            if (!_differencers.TryGetValue(accountInfo.UserName, out differencer))
217
            {
218
                differencer = new SnapshotDifferencer();
219
                _differencers[accountInfo.UserName] = differencer;
220
            }
221
            differencer.Post(cleanRemotes);
222
            return differencer;
223
        }
224

    
225
    }
226
}