1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sync/notifier/unacked_invalidation_set.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "sync/internal_api/public/base/ack_handle.h"
9 #include "sync/notifier/object_id_invalidation_map.h"
10 #include "sync/notifier/sync_invalidation_listener.h"
14 const char kSourceKey
[] = "source";
15 const char kNameKey
[] = "name";
16 const char kInvalidationListKey
[] = "invalidation-list";
22 const size_t UnackedInvalidationSet::kMaxBufferedInvalidations
= 5;
25 UnackedInvalidationSet::UnackedInvalidationSet(
26 invalidation::ObjectId id
)
30 UnackedInvalidationSet::~UnackedInvalidationSet() {}
32 const invalidation::ObjectId
& UnackedInvalidationSet::object_id() const {
36 void UnackedInvalidationSet::Add(
37 const Invalidation
& invalidation
) {
38 SingleObjectInvalidationSet set
;
39 set
.Insert(invalidation
);
42 Truncate(kMaxBufferedInvalidations
);
45 void UnackedInvalidationSet::AddSet(
46 const SingleObjectInvalidationSet
& invalidations
) {
47 invalidations_
.insert(invalidations
.begin(), invalidations
.end());
49 Truncate(kMaxBufferedInvalidations
);
52 void UnackedInvalidationSet::ExportInvalidations(
53 WeakHandle
<AckHandler
> ack_handler
,
54 ObjectIdInvalidationMap
* out
) const {
55 for (SingleObjectInvalidationSet::const_iterator it
= invalidations_
.begin();
56 it
!= invalidations_
.end(); ++it
) {
57 // Copy the invalidation and set the copy's ack_handler.
58 Invalidation
inv(*it
);
59 inv
.set_ack_handler(ack_handler
);
64 void UnackedInvalidationSet::Clear() {
65 invalidations_
.clear();
68 void UnackedInvalidationSet::SetHandlerIsRegistered() {
72 void UnackedInvalidationSet::SetHandlerIsUnregistered() {
74 Truncate(kMaxBufferedInvalidations
);
77 // Removes the matching ack handle from the list.
78 void UnackedInvalidationSet::Acknowledge(const AckHandle
& handle
) {
79 bool handle_found
= false;
80 for (SingleObjectInvalidationSet::const_iterator it
= invalidations_
.begin();
81 it
!= invalidations_
.end(); ++it
) {
82 if (it
->ack_handle().Equals(handle
)) {
83 invalidations_
.erase(*it
);
88 DLOG_IF(WARNING
, !handle_found
)
89 << "Unrecognized to ack for object " << ObjectIdToString(object_id_
);
90 (void)handle_found
; // Silence unused variable warning in release builds.
93 // Erase the invalidation with matching ack handle from the list. Also creates
94 // an 'UnknownVersion' invalidation with the same ack handle and places it at
95 // the beginning of the list. If an unknown version invalidation currently
96 // exists, it is replaced.
97 void UnackedInvalidationSet::Drop(const AckHandle
& handle
) {
98 SingleObjectInvalidationSet::const_iterator it
;
99 for (it
= invalidations_
.begin(); it
!= invalidations_
.end(); ++it
) {
100 if (it
->ack_handle().Equals(handle
)) {
104 if (it
== invalidations_
.end()) {
105 DLOG(WARNING
) << "Unrecognized drop request for object "
106 << ObjectIdToString(object_id_
);
110 Invalidation unknown_version
= Invalidation::InitFromDroppedInvalidation(*it
);
111 invalidations_
.erase(*it
);
113 // If an unknown version is in the list, we remove it so we can replace it.
114 if (!invalidations_
.empty() && invalidations_
.begin()->is_unknown_version()) {
115 invalidations_
.erase(*invalidations_
.begin());
118 invalidations_
.insert(unknown_version
);
121 scoped_ptr
<base::DictionaryValue
> UnackedInvalidationSet::ToValue() const {
122 scoped_ptr
<base::DictionaryValue
> value(new base::DictionaryValue
);
123 value
->SetString(kSourceKey
, base::IntToString(object_id_
.source()));
124 value
->SetString(kNameKey
, object_id_
.name());
126 scoped_ptr
<base::ListValue
> list_value(new base::ListValue
);
127 for (InvalidationsSet::const_iterator it
= invalidations_
.begin();
128 it
!= invalidations_
.end(); ++it
) {
129 list_value
->Append(it
->ToValue().release());
131 value
->Set(kInvalidationListKey
, list_value
.release());
136 bool UnackedInvalidationSet::ResetFromValue(
137 const base::DictionaryValue
& value
) {
138 std::string source_str
;
139 if (!value
.GetString(kSourceKey
, &source_str
)) {
140 DLOG(WARNING
) << "Unable to deserialize source";
144 if (!base::StringToInt(source_str
, &source
)) {
145 DLOG(WARNING
) << "Invalid source: " << source_str
;
149 if (!value
.GetString(kNameKey
, &name
)) {
150 DLOG(WARNING
) << "Unable to deserialize name";
153 object_id_
= invalidation::ObjectId(source
, name
);
154 const base::ListValue
* invalidation_list
= NULL
;
155 if (!value
.GetList(kInvalidationListKey
, &invalidation_list
)
156 || !ResetListFromValue(*invalidation_list
)) {
157 // Earlier versions of this class did not set this field, so we don't treat
158 // parsing errors here as a fatal failure.
159 DLOG(WARNING
) << "Unable to deserialize invalidation list.";
164 bool UnackedInvalidationSet::ResetListFromValue(
165 const base::ListValue
& list
) {
166 for (size_t i
= 0; i
< list
.GetSize(); ++i
) {
167 const base::DictionaryValue
* dict
;
168 if (!list
.GetDictionary(i
, &dict
)) {
169 DLOG(WARNING
) << "Failed to get invalidation dictionary at index " << i
;
172 scoped_ptr
<Invalidation
> invalidation
= Invalidation::InitFromValue(*dict
);
174 DLOG(WARNING
) << "Failed to parse invalidation at index " << i
;
177 invalidations_
.insert(*invalidation
.get());
182 void UnackedInvalidationSet::Truncate(size_t max_size
) {
183 DCHECK_GT(max_size
, 0U);
185 if (invalidations_
.size() <= max_size
) {
189 while (invalidations_
.size() > max_size
) {
190 invalidations_
.erase(*invalidations_
.begin());
193 // We dropped some invalidations. We remember the fact that an unknown
194 // amount of information has been lost by ensuring this list begins with
195 // an UnknownVersion invalidation. We remove the oldest remaining
196 // invalidation to make room for it.
197 invalidation::ObjectId id
= invalidations_
.begin()->object_id();
198 invalidations_
.erase(*invalidations_
.begin());
200 Invalidation unknown_version
= Invalidation::InitUnknownVersion(id
);
201 invalidations_
.insert(unknown_version
);
204 } // namespace syncer