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