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