sync_engine_initializer.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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.
4
5#include "chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/logging.h"
10#include "chrome/browser/drive/drive_api_service.h"
11#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
12#include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
13#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
14#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
15#include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
16#include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
17#include "chrome/browser/sync_file_system/logger.h"
18#include "google_apis/drive/drive_api_parser.h"
19#include "google_apis/drive/gdata_wapi_parser.h"
20
21namespace sync_file_system {
22namespace drive_backend {
23
24namespace {
25
26////////////////////////////////////////////////////////////////////////////////
27// Functions below are for wrapping the access to legacy GData WAPI classes.
28
29bool HasNoParents(const google_apis::FileResource& entry) {
30  return entry.parents().empty();
31}
32
33bool HasFolderAsParent(const google_apis::FileResource& entry,
34                       const std::string& parent_id) {
35  for (size_t i = 0; i < entry.parents().size(); ++i) {
36    if (entry.parents()[i].file_id() == parent_id)
37      return true;
38  }
39  return false;
40}
41
42bool LessOnCreationTime(const google_apis::FileResource& left,
43                        const google_apis::FileResource& right) {
44  return left.created_date() < right.created_date();
45}
46
47typedef base::Callback<void(scoped_ptr<SyncTaskToken> token,
48                            google_apis::GDataErrorCode error,
49                            scoped_ptr<google_apis::ResourceList> resources)>
50    TokenAndResourceListCallback;
51
52// Functions above are for wrapping the access to legacy GData WAPI classes.
53////////////////////////////////////////////////////////////////////////////////
54
55}  // namespace
56
57SyncEngineInitializer::SyncEngineInitializer(
58    SyncEngineContext* sync_context,
59    const base::FilePath& database_path,
60    leveldb::Env* env_override)
61    : sync_context_(sync_context),
62      env_override_(env_override),
63      database_path_(database_path),
64      find_sync_root_retry_count_(0),
65      largest_change_id_(0),
66      weak_ptr_factory_(this) {
67  DCHECK(sync_context);
68}
69
70SyncEngineInitializer::~SyncEngineInitializer() {
71  if (!cancel_callback_.is_null())
72    cancel_callback_.Run();
73}
74
75void SyncEngineInitializer::RunPreflight(scoped_ptr<SyncTaskToken> token) {
76  util::Log(logging::LOG_VERBOSE, FROM_HERE, "[Initialize] Start.");
77  DCHECK(sync_context_);
78  DCHECK(sync_context_->GetDriveService());
79
80  // The metadata seems to have been already initialized. Just return with OK.
81  if (sync_context_->GetMetadataDatabase()) {
82    util::Log(logging::LOG_VERBOSE, FROM_HERE,
83              "[Initialize] Already initialized.");
84    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
85    return;
86  }
87
88  MetadataDatabase::Create(
89      sync_context_->GetWorkerTaskRunner(),
90      database_path_,
91      env_override_,
92      base::Bind(&SyncEngineInitializer::DidCreateMetadataDatabase,
93                 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
94}
95
96scoped_ptr<MetadataDatabase> SyncEngineInitializer::PassMetadataDatabase() {
97  return metadata_database_.Pass();
98}
99
100void SyncEngineInitializer::DidCreateMetadataDatabase(
101    scoped_ptr<SyncTaskToken> token,
102    SyncStatusCode status,
103    scoped_ptr<MetadataDatabase> instance) {
104  if (status != SYNC_STATUS_OK) {
105    util::Log(logging::LOG_VERBOSE, FROM_HERE,
106              "[Initialize] Failed to initialize MetadataDatabase.");
107    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
108    return;
109  }
110
111  DCHECK(instance);
112  metadata_database_ = instance.Pass();
113  if (metadata_database_->HasSyncRoot()) {
114    util::Log(logging::LOG_VERBOSE, FROM_HERE,
115              "[Initialize] Found local cache of sync-root.");
116    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
117    return;
118  }
119
120  GetAboutResource(token.Pass());
121}
122
123void SyncEngineInitializer::GetAboutResource(
124    scoped_ptr<SyncTaskToken> token) {
125  set_used_network(true);
126  sync_context_->GetDriveService()->GetAboutResource(
127      base::Bind(&SyncEngineInitializer::DidGetAboutResource,
128                 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
129}
130
131void SyncEngineInitializer::DidGetAboutResource(
132    scoped_ptr<SyncTaskToken> token,
133    google_apis::GDataErrorCode error,
134    scoped_ptr<google_apis::AboutResource> about_resource) {
135  cancel_callback_.Reset();
136
137  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
138  if (status != SYNC_STATUS_OK) {
139    util::Log(logging::LOG_VERBOSE, FROM_HERE,
140              "[Initialize] Failed to get AboutResource.");
141    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
142    return;
143  }
144
145  DCHECK(about_resource);
146  root_folder_id_ = about_resource->root_folder_id();
147  largest_change_id_ = about_resource->largest_change_id();
148
149  DCHECK(!root_folder_id_.empty());
150  FindSyncRoot(token.Pass());
151}
152
153void SyncEngineInitializer::FindSyncRoot(scoped_ptr<SyncTaskToken> token) {
154  if (find_sync_root_retry_count_++ >= kMaxRetry) {
155    util::Log(logging::LOG_VERBOSE, FROM_HERE,
156              "[Initialize] Reached max retry count.");
157    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
158    return;
159  }
160
161  set_used_network(true);
162  cancel_callback_ = sync_context_->GetDriveService()->SearchByTitle(
163      kSyncRootFolderTitle,
164      std::string(),  // parent_folder_id
165      base::Bind(&SyncEngineInitializer::DidFindSyncRoot,
166                 weak_ptr_factory_.GetWeakPtr(),
167                 base::Passed(&token)));
168}
169
170void SyncEngineInitializer::DidFindSyncRoot(
171    scoped_ptr<SyncTaskToken> token,
172    google_apis::GDataErrorCode error,
173    scoped_ptr<google_apis::FileList> file_list) {
174  cancel_callback_.Reset();
175
176  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
177  if (status != SYNC_STATUS_OK) {
178    util::Log(logging::LOG_VERBOSE, FROM_HERE,
179              "[Initialize] Failed to find sync root.");
180    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
181    return;
182  }
183
184  if (!file_list) {
185    NOTREACHED();
186    util::Log(logging::LOG_VERBOSE, FROM_HERE,
187              "[Initialize] Got invalid resource list.");
188    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
189    return;
190  }
191
192  ScopedVector<google_apis::FileResource>* items = file_list->mutable_items();
193  for (ScopedVector<google_apis::FileResource>::iterator itr = items->begin();
194       itr != items->end(); ++itr) {
195    google_apis::FileResource* entry = *itr;
196
197    // Ignore deleted folder.
198    if (entry->labels().is_trashed())
199      continue;
200
201    // Pick an orphaned folder or a direct child of the root folder and
202    // ignore others.
203    DCHECK(!root_folder_id_.empty());
204    if (!HasNoParents(*entry) && !HasFolderAsParent(*entry, root_folder_id_))
205      continue;
206
207    if (!sync_root_folder_ || LessOnCreationTime(*entry, *sync_root_folder_)) {
208      sync_root_folder_.reset(entry);
209      *itr = NULL;
210    }
211  }
212
213  set_used_network(true);
214  // If there are more results, retrieve them.
215  if (!file_list->next_link().is_empty()) {
216    cancel_callback_ = sync_context_->GetDriveService()->GetRemainingFileList(
217        file_list->next_link(),
218        base::Bind(&SyncEngineInitializer::DidFindSyncRoot,
219                   weak_ptr_factory_.GetWeakPtr(),
220                   base::Passed(&token)));
221    return;
222  }
223
224  if (!sync_root_folder_) {
225    CreateSyncRoot(token.Pass());
226    return;
227  }
228
229  if (!HasNoParents(*sync_root_folder_)) {
230    DetachSyncRoot(token.Pass());
231    return;
232  }
233
234  ListAppRootFolders(token.Pass());
235}
236
237void SyncEngineInitializer::CreateSyncRoot(scoped_ptr<SyncTaskToken> token) {
238  DCHECK(!sync_root_folder_);
239  set_used_network(true);
240  cancel_callback_ = sync_context_->GetDriveService()->AddNewDirectory(
241      root_folder_id_, kSyncRootFolderTitle,
242      drive::DriveServiceInterface::AddNewDirectoryOptions(),
243      base::Bind(&SyncEngineInitializer::DidCreateSyncRoot,
244                 weak_ptr_factory_.GetWeakPtr(),
245                 base::Passed(&token)));
246}
247
248void SyncEngineInitializer::DidCreateSyncRoot(
249    scoped_ptr<SyncTaskToken> token,
250    google_apis::GDataErrorCode error,
251    scoped_ptr<google_apis::FileResource> entry) {
252  DCHECK(!sync_root_folder_);
253  cancel_callback_.Reset();
254
255  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
256  if (status != SYNC_STATUS_OK) {
257    util::Log(logging::LOG_VERBOSE, FROM_HERE,
258              "[Initialize] Failed to create sync root.");
259    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
260    return;
261  }
262
263  FindSyncRoot(token.Pass());
264}
265
266void SyncEngineInitializer::DetachSyncRoot(scoped_ptr<SyncTaskToken> token) {
267  DCHECK(sync_root_folder_);
268  set_used_network(true);
269  cancel_callback_ =
270      sync_context_->GetDriveService()->RemoveResourceFromDirectory(
271          root_folder_id_,
272          sync_root_folder_->file_id(),
273          base::Bind(&SyncEngineInitializer::DidDetachSyncRoot,
274                     weak_ptr_factory_.GetWeakPtr(),
275                     base::Passed(&token)));
276}
277
278void SyncEngineInitializer::DidDetachSyncRoot(
279    scoped_ptr<SyncTaskToken> token,
280    google_apis::GDataErrorCode error) {
281  cancel_callback_.Reset();
282
283  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
284  if (status != SYNC_STATUS_OK) {
285    util::Log(logging::LOG_VERBOSE, FROM_HERE,
286              "[Initialize] Failed to detach sync root.");
287    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
288    return;
289  }
290
291  ListAppRootFolders(token.Pass());
292}
293
294void SyncEngineInitializer::ListAppRootFolders(
295    scoped_ptr<SyncTaskToken> token) {
296  DCHECK(sync_root_folder_);
297  set_used_network(true);
298  cancel_callback_ =
299      sync_context_->GetDriveService()->GetFileListInDirectory(
300          sync_root_folder_->file_id(),
301          base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
302                     weak_ptr_factory_.GetWeakPtr(),
303                     base::Passed(&token)));
304}
305
306void SyncEngineInitializer::DidListAppRootFolders(
307    scoped_ptr<SyncTaskToken> token,
308    google_apis::GDataErrorCode error,
309    scoped_ptr<google_apis::FileList> file_list) {
310  cancel_callback_.Reset();
311
312  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
313  if (status != SYNC_STATUS_OK) {
314    util::Log(logging::LOG_VERBOSE, FROM_HERE,
315              "[Initialize] Failed to get initial app-root folders.");
316    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
317    return;
318  }
319
320  if (!file_list) {
321    NOTREACHED();
322    util::Log(logging::LOG_VERBOSE, FROM_HERE,
323              "[Initialize] Got invalid initial app-root list.");
324    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
325    return;
326  }
327
328  ScopedVector<google_apis::FileResource>* new_entries =
329      file_list->mutable_items();
330  app_root_folders_.insert(app_root_folders_.end(),
331                           new_entries->begin(), new_entries->end());
332  new_entries->weak_clear();
333
334  set_used_network(true);
335  if (!file_list->next_link().is_empty()) {
336    cancel_callback_ =
337        sync_context_->GetDriveService()->GetRemainingFileList(
338            file_list->next_link(),
339            base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
340                       weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
341    return;
342  }
343
344  PopulateDatabase(token.Pass());
345}
346
347void SyncEngineInitializer::PopulateDatabase(
348    scoped_ptr<SyncTaskToken> token) {
349  DCHECK(sync_root_folder_);
350  metadata_database_->PopulateInitialData(
351      largest_change_id_,
352      *sync_root_folder_,
353      app_root_folders_,
354      base::Bind(&SyncEngineInitializer::DidPopulateDatabase,
355                 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
356}
357
358void SyncEngineInitializer::DidPopulateDatabase(
359    scoped_ptr<SyncTaskToken> token,
360    SyncStatusCode status) {
361  if (status != SYNC_STATUS_OK) {
362    util::Log(logging::LOG_VERBOSE, FROM_HERE,
363              "[Initialize] Failed to populate initial data"
364              " to MetadataDatabase.");
365    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
366    return;
367  }
368
369  util::Log(logging::LOG_VERBOSE, FROM_HERE,
370            "[Initialize] Completed successfully.");
371  SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
372}
373
374}  // namespace drive_backend
375}  // namespace sync_file_system
376