sync_engine_initializer.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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  SyncStatusCode status = SYNC_STATUS_FAILED;
89  scoped_ptr<MetadataDatabase> metadata_database =
90      MetadataDatabase::Create(database_path_, env_override_, &status);
91
92  if (status != SYNC_STATUS_OK) {
93    util::Log(logging::LOG_VERBOSE, FROM_HERE,
94              "[Initialize] Failed to initialize MetadataDatabase.");
95    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
96    return;
97  }
98
99  DCHECK(metadata_database);
100  metadata_database_ = metadata_database.Pass();
101  if (metadata_database_->HasSyncRoot()) {
102    util::Log(logging::LOG_VERBOSE, FROM_HERE,
103              "[Initialize] Found local cache of sync-root.");
104    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
105    return;
106  }
107
108  GetAboutResource(token.Pass());
109}
110
111scoped_ptr<MetadataDatabase> SyncEngineInitializer::PassMetadataDatabase() {
112  return metadata_database_.Pass();
113}
114
115void SyncEngineInitializer::GetAboutResource(
116    scoped_ptr<SyncTaskToken> token) {
117  set_used_network(true);
118  sync_context_->GetDriveService()->GetAboutResource(
119      base::Bind(&SyncEngineInitializer::DidGetAboutResource,
120                 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
121}
122
123void SyncEngineInitializer::DidGetAboutResource(
124    scoped_ptr<SyncTaskToken> token,
125    google_apis::GDataErrorCode error,
126    scoped_ptr<google_apis::AboutResource> about_resource) {
127  cancel_callback_.Reset();
128
129  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
130  if (status != SYNC_STATUS_OK) {
131    util::Log(logging::LOG_VERBOSE, FROM_HERE,
132              "[Initialize] Failed to get AboutResource.");
133    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
134    return;
135  }
136
137  DCHECK(about_resource);
138  root_folder_id_ = about_resource->root_folder_id();
139  largest_change_id_ = about_resource->largest_change_id();
140
141  DCHECK(!root_folder_id_.empty());
142  FindSyncRoot(token.Pass());
143}
144
145void SyncEngineInitializer::FindSyncRoot(scoped_ptr<SyncTaskToken> token) {
146  if (find_sync_root_retry_count_++ >= kMaxRetry) {
147    util::Log(logging::LOG_VERBOSE, FROM_HERE,
148              "[Initialize] Reached max retry count.");
149    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
150    return;
151  }
152
153  set_used_network(true);
154  cancel_callback_ = sync_context_->GetDriveService()->SearchByTitle(
155      kSyncRootFolderTitle,
156      std::string(),  // parent_folder_id
157      base::Bind(&SyncEngineInitializer::DidFindSyncRoot,
158                 weak_ptr_factory_.GetWeakPtr(),
159                 base::Passed(&token)));
160}
161
162void SyncEngineInitializer::DidFindSyncRoot(
163    scoped_ptr<SyncTaskToken> token,
164    google_apis::GDataErrorCode error,
165    scoped_ptr<google_apis::FileList> file_list) {
166  cancel_callback_.Reset();
167
168  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
169  if (status != SYNC_STATUS_OK) {
170    util::Log(logging::LOG_VERBOSE, FROM_HERE,
171              "[Initialize] Failed to find sync root.");
172    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
173    return;
174  }
175
176  if (!file_list) {
177    NOTREACHED();
178    util::Log(logging::LOG_VERBOSE, FROM_HERE,
179              "[Initialize] Got invalid resource list.");
180    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
181    return;
182  }
183
184  ScopedVector<google_apis::FileResource>* items = file_list->mutable_items();
185  for (ScopedVector<google_apis::FileResource>::iterator itr = items->begin();
186       itr != items->end(); ++itr) {
187    google_apis::FileResource* entry = *itr;
188
189    // Ignore deleted folder.
190    if (entry->labels().is_trashed())
191      continue;
192
193    // Pick an orphaned folder or a direct child of the root folder and
194    // ignore others.
195    DCHECK(!root_folder_id_.empty());
196    if (!HasNoParents(*entry) && !HasFolderAsParent(*entry, root_folder_id_))
197      continue;
198
199    if (!sync_root_folder_ || LessOnCreationTime(*entry, *sync_root_folder_)) {
200      sync_root_folder_.reset(entry);
201      *itr = NULL;
202    }
203  }
204
205  set_used_network(true);
206  // If there are more results, retrieve them.
207  if (!file_list->next_link().is_empty()) {
208    cancel_callback_ = sync_context_->GetDriveService()->GetRemainingFileList(
209        file_list->next_link(),
210        base::Bind(&SyncEngineInitializer::DidFindSyncRoot,
211                   weak_ptr_factory_.GetWeakPtr(),
212                   base::Passed(&token)));
213    return;
214  }
215
216  if (!sync_root_folder_) {
217    CreateSyncRoot(token.Pass());
218    return;
219  }
220
221  if (!HasNoParents(*sync_root_folder_)) {
222    DetachSyncRoot(token.Pass());
223    return;
224  }
225
226  ListAppRootFolders(token.Pass());
227}
228
229void SyncEngineInitializer::CreateSyncRoot(scoped_ptr<SyncTaskToken> token) {
230  DCHECK(!sync_root_folder_);
231  set_used_network(true);
232  cancel_callback_ = sync_context_->GetDriveService()->AddNewDirectory(
233      root_folder_id_, kSyncRootFolderTitle,
234      drive::DriveServiceInterface::AddNewDirectoryOptions(),
235      base::Bind(&SyncEngineInitializer::DidCreateSyncRoot,
236                 weak_ptr_factory_.GetWeakPtr(),
237                 base::Passed(&token)));
238}
239
240void SyncEngineInitializer::DidCreateSyncRoot(
241    scoped_ptr<SyncTaskToken> token,
242    google_apis::GDataErrorCode error,
243    scoped_ptr<google_apis::FileResource> entry) {
244  DCHECK(!sync_root_folder_);
245  cancel_callback_.Reset();
246
247  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
248  if (status != SYNC_STATUS_OK) {
249    util::Log(logging::LOG_VERBOSE, FROM_HERE,
250              "[Initialize] Failed to create sync root.");
251    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
252    return;
253  }
254
255  FindSyncRoot(token.Pass());
256}
257
258void SyncEngineInitializer::DetachSyncRoot(scoped_ptr<SyncTaskToken> token) {
259  DCHECK(sync_root_folder_);
260  set_used_network(true);
261  cancel_callback_ =
262      sync_context_->GetDriveService()->RemoveResourceFromDirectory(
263          root_folder_id_,
264          sync_root_folder_->file_id(),
265          base::Bind(&SyncEngineInitializer::DidDetachSyncRoot,
266                     weak_ptr_factory_.GetWeakPtr(),
267                     base::Passed(&token)));
268}
269
270void SyncEngineInitializer::DidDetachSyncRoot(
271    scoped_ptr<SyncTaskToken> token,
272    google_apis::GDataErrorCode error) {
273  cancel_callback_.Reset();
274
275  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
276  if (status != SYNC_STATUS_OK) {
277    util::Log(logging::LOG_VERBOSE, FROM_HERE,
278              "[Initialize] Failed to detach sync root.");
279    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
280    return;
281  }
282
283  ListAppRootFolders(token.Pass());
284}
285
286void SyncEngineInitializer::ListAppRootFolders(
287    scoped_ptr<SyncTaskToken> token) {
288  DCHECK(sync_root_folder_);
289  set_used_network(true);
290  cancel_callback_ =
291      sync_context_->GetDriveService()->GetFileListInDirectory(
292          sync_root_folder_->file_id(),
293          base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
294                     weak_ptr_factory_.GetWeakPtr(),
295                     base::Passed(&token)));
296}
297
298void SyncEngineInitializer::DidListAppRootFolders(
299    scoped_ptr<SyncTaskToken> token,
300    google_apis::GDataErrorCode error,
301    scoped_ptr<google_apis::FileList> file_list) {
302  cancel_callback_.Reset();
303
304  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
305  if (status != SYNC_STATUS_OK) {
306    util::Log(logging::LOG_VERBOSE, FROM_HERE,
307              "[Initialize] Failed to get initial app-root folders.");
308    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
309    return;
310  }
311
312  if (!file_list) {
313    NOTREACHED();
314    util::Log(logging::LOG_VERBOSE, FROM_HERE,
315              "[Initialize] Got invalid initial app-root list.");
316    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
317    return;
318  }
319
320  ScopedVector<google_apis::FileResource>* new_entries =
321      file_list->mutable_items();
322  app_root_folders_.insert(app_root_folders_.end(),
323                           new_entries->begin(), new_entries->end());
324  new_entries->weak_clear();
325
326  set_used_network(true);
327  if (!file_list->next_link().is_empty()) {
328    cancel_callback_ =
329        sync_context_->GetDriveService()->GetRemainingFileList(
330            file_list->next_link(),
331            base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
332                       weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
333    return;
334  }
335
336  PopulateDatabase(token.Pass());
337}
338
339void SyncEngineInitializer::PopulateDatabase(
340    scoped_ptr<SyncTaskToken> token) {
341  DCHECK(sync_root_folder_);
342  SyncStatusCode status = metadata_database_->PopulateInitialData(
343      largest_change_id_, *sync_root_folder_, app_root_folders_);
344  if (status != SYNC_STATUS_OK) {
345    util::Log(logging::LOG_VERBOSE, FROM_HERE,
346              "[Initialize] Failed to populate initial data"
347              " to MetadataDatabase.");
348    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
349    return;
350  }
351
352  util::Log(logging::LOG_VERBOSE, FROM_HERE,
353            "[Initialize] Completed successfully.");
354  SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
355}
356
357}  // namespace drive_backend
358}  // namespace sync_file_system
359