1// Copyright 2012 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.
4
5#include "sync/engine/commit.h"
6
7#include "base/debug/trace_event.h"
8#include "sync/engine/commit_contribution.h"
9#include "sync/engine/commit_processor.h"
10#include "sync/engine/commit_util.h"
11#include "sync/engine/syncer.h"
12#include "sync/engine/syncer_proto_util.h"
13#include "sync/internal_api/public/events/commit_request_event.h"
14#include "sync/internal_api/public/events/commit_response_event.h"
15#include "sync/sessions/sync_session.h"
16
17namespace syncer {
18
19Commit::Commit(
20    const std::map<ModelType, CommitContribution*>& contributions,
21    const sync_pb::ClientToServerMessage& message,
22    ExtensionsActivity::Records extensions_activity_buffer)
23  : contributions_(contributions),
24    deleter_(&contributions_),
25    message_(message),
26    extensions_activity_buffer_(extensions_activity_buffer),
27    cleaned_up_(false) {
28}
29
30Commit::~Commit() {
31  DCHECK(cleaned_up_);
32}
33
34Commit* Commit::Init(
35    ModelTypeSet requested_types,
36    ModelTypeSet enabled_types,
37    size_t max_entries,
38    const std::string& account_name,
39    const std::string& cache_guid,
40    CommitProcessor* commit_processor,
41    ExtensionsActivity* extensions_activity) {
42  // Gather per-type contributions.
43  ContributionMap contributions;
44  commit_processor->GatherCommitContributions(
45      requested_types,
46      max_entries,
47      &contributions);
48
49  // Give up if no one had anything to commit.
50  if (contributions.empty())
51    return NULL;
52
53  sync_pb::ClientToServerMessage message;
54  message.set_message_contents(sync_pb::ClientToServerMessage::COMMIT);
55  message.set_share(account_name);
56
57  sync_pb::CommitMessage* commit_message = message.mutable_commit();
58  commit_message->set_cache_guid(cache_guid);
59
60  // Set extensions activity if bookmark commits are present.
61  ExtensionsActivity::Records extensions_activity_buffer;
62  ContributionMap::iterator it = contributions.find(syncer::BOOKMARKS);
63  if (it != contributions.end() && it->second->GetNumEntries() != 0) {
64    commit_util::AddExtensionsActivityToMessage(
65        extensions_activity,
66        &extensions_activity_buffer,
67        commit_message);
68  }
69
70  // Set the client config params.
71  commit_util::AddClientConfigParamsToMessage(
72      enabled_types,
73      commit_message);
74
75  // Finally, serialize all our contributions.
76  for (std::map<ModelType, CommitContribution*>::iterator it =
77           contributions.begin(); it != contributions.end(); ++it) {
78    it->second->AddToCommitMessage(&message);
79  }
80
81  // If we made it this far, then we've successfully prepared a commit message.
82  return new Commit(contributions, message, extensions_activity_buffer);
83}
84
85SyncerError Commit::PostAndProcessResponse(
86    sessions::SyncSession* session,
87    sessions::StatusController* status,
88    ExtensionsActivity* extensions_activity) {
89  ModelTypeSet request_types;
90  for (ContributionMap::const_iterator it = contributions_.begin();
91       it != contributions_.end(); ++it) {
92    request_types.Put(it->first);
93  }
94  session->mutable_status_controller()->set_commit_request_types(request_types);
95
96  if (session->context()->debug_info_getter()) {
97    sync_pb::DebugInfo* debug_info = message_.mutable_debug_info();
98    session->context()->debug_info_getter()->GetDebugInfo(debug_info);
99  }
100
101  DVLOG(1) << "Sending commit message.";
102
103  CommitRequestEvent request_event(
104      base::Time::Now(),
105      message_.commit().entries_size(),
106      request_types,
107      message_);
108  session->SendProtocolEvent(request_event);
109
110  TRACE_EVENT_BEGIN0("sync", "PostCommit");
111  const SyncerError post_result = SyncerProtoUtil::PostClientToServerMessage(
112      &message_, &response_, session);
113  TRACE_EVENT_END0("sync", "PostCommit");
114
115  // TODO(rlarocque): Use result that includes errors captured later?
116  CommitResponseEvent response_event(
117      base::Time::Now(),
118      post_result,
119      response_);
120  session->SendProtocolEvent(response_event);
121
122  if (post_result != SYNCER_OK) {
123    LOG(WARNING) << "Post commit failed";
124    return post_result;
125  }
126
127  if (!response_.has_commit()) {
128    LOG(WARNING) << "Commit response has no commit body!";
129    return SERVER_RESPONSE_VALIDATION_FAILED;
130  }
131
132  size_t message_entries = message_.commit().entries_size();
133  size_t response_entries = response_.commit().entryresponse_size();
134  if (message_entries != response_entries) {
135    LOG(ERROR)
136       << "Commit response has wrong number of entries! "
137       << "Expected: " << message_entries << ", "
138       << "Got: " << response_entries;
139    return SERVER_RESPONSE_VALIDATION_FAILED;
140  }
141
142  if (session->context()->debug_info_getter()) {
143    // Clear debug info now that we have successfully sent it to the server.
144    DVLOG(1) << "Clearing client debug info.";
145    session->context()->debug_info_getter()->ClearDebugInfo();
146  }
147
148  // Let the contributors process the responses to each of their requests.
149  SyncerError processing_result = SYNCER_OK;
150  for (std::map<ModelType, CommitContribution*>::iterator it =
151       contributions_.begin(); it != contributions_.end(); ++it) {
152    TRACE_EVENT1("sync", "ProcessCommitResponse",
153                 "type", ModelTypeToString(it->first));
154    SyncerError type_result =
155        it->second->ProcessCommitResponse(response_, status);
156    if (processing_result == SYNCER_OK && type_result != SYNCER_OK) {
157      processing_result = type_result;
158    }
159  }
160
161  // Handle bookmarks' special extensions activity stats.
162  if (session->status_controller().
163          model_neutral_state().num_successful_bookmark_commits == 0) {
164    extensions_activity->PutRecords(extensions_activity_buffer_);
165  }
166
167  return processing_result;
168}
169
170void Commit::CleanUp() {
171  for (ContributionMap::iterator it = contributions_.begin();
172       it != contributions_.end(); ++it) {
173    it->second->CleanUp();
174  }
175  cleaned_up_ = true;
176}
177
178}  // namespace syncer
179