Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / Agents / SnapshotDifferencer.cs @ 64171b56

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

    
49
namespace Pithos.Core.Agents
50
{
51

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

    
69
            /// <summary>
70
            /// The current snapshot listing
71
            /// </summary>
72
            public readonly IEnumerable<ObjectInfo> Current;
73

    
74
            /// <summary>
75
            /// Common objects, lazily evalueated. 
76
            /// The common objects are used to calculate both the Changed and Unchanged objects
77
            /// </summary>
78
            public readonly Lazy<IEnumerable<ObjectInfo>> Common;
79

    
80
            public State(IEnumerable<ObjectInfo> previous, IEnumerable<ObjectInfo> current)
81
            {
82
                Previous = previous ?? new List<ObjectInfo>();
83
                Current = current ?? new List<ObjectInfo>();
84

    
85
                Common=new Lazy<IEnumerable<ObjectInfo>>(() =>
86
                    Current.Join(Previous,
87
                                outKey => new { outKey.Account, outKey.Container, outKey.Name },
88
                                inKey => new { inKey.Account, inKey.Container, inKey.Name },
89
                                (outer, inner) =>
90
                                {
91
                                    outer.PreviousHash = inner.Hash;
92
                                    return outer;
93
                                }));            
94
            }
95
        }
96

    
97

    
98
        private State _state;
99

    
100
        /// <summary>
101
        /// The comparer used to identify common objects.
102
        /// Objects are considered common when they have the same Account, Container and Name
103
        /// </summary>
104
        private readonly ObjectInfoComparer _comparer = new ObjectInfoComparer();
105

    
106
        /// <summary>
107
        /// Default constructor. Initializes the Current and Previous listings to empty lists
108
        /// </summary>
109
        public SnapshotDifferencer()
110
            :this(new List<ObjectInfo>(),new List<ObjectInfo>())
111
        {
112
        }
113

    
114
        /// <summary>
115
        /// Creates a new differencer using the specified previous and current list. Null lists
116
        /// are replaced with empty lists
117
        /// </summary>
118
        /// <param name="previous"></param>
119
        /// <param name="current"></param>
120
        public SnapshotDifferencer(IEnumerable<ObjectInfo> previous,IEnumerable<ObjectInfo> current  )
121
        {
122
            _state=new State(previous,current);
123
        }
124

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

    
147
            return this;
148
        }
149
        
150
        public IEnumerable<ObjectInfo> Deleted
151
        {
152
            get { return _state.Previous.Except(_state.Current,_comparer); }
153
        }
154
        public IEnumerable<ObjectInfo> Created
155
        {
156
            get { return _state.Current.Except(_state.Previous,_comparer); }
157
        }
158
                
159
        public IEnumerable<ObjectInfo> Common
160
        {
161
            get { return _state.Common.Value; }
162
        }
163

    
164
        public IEnumerable<ObjectInfo> Changed
165
        {
166
            get{return Common.Where(i=>i.PreviousHash!=i.Hash);}
167
        }
168
        public IEnumerable<ObjectInfo> Unchanged
169
        {
170
            get{ return Common.Where(i => i.PreviousHash == i.Hash);}
171
        }
172
    }
173

    
174
    public class AccountsDifferencer
175
    {
176
        readonly ConcurrentDictionary<string, SnapshotDifferencer> _differencers = new ConcurrentDictionary<string, SnapshotDifferencer>();
177

    
178
        public ConcurrentDictionary<string, SnapshotDifferencer> Differencers { get { return _differencers; } }
179

    
180
        public SnapshotDifferencer PostSnapshot(AccountInfo accountInfo, List<ObjectInfo> cleanRemotes)
181
        {
182
            SnapshotDifferencer differencer;
183
            if (!_differencers.TryGetValue(accountInfo.UserName, out differencer))
184
            {
185
                differencer = new SnapshotDifferencer();
186
                _differencers[accountInfo.UserName] = differencer;
187
            }
188
            differencer.Post(cleanRemotes);
189
            return differencer;
190
        }
191

    
192
    }
193
}