sync_engine_initializer.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
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    drive::DriveServiceInterface* drive_service,
81    const base::FilePath& database_path,
82    leveldb::Env* env_override)
83    : sync_context_(sync_context),
84      env_override_(env_override),
85      task_runner_(task_runner),
86      drive_service_(drive_service),
87      database_path_(database_path),
88      find_sync_root_retry_count_(0),
89      largest_change_id_(0),
90      weak_ptr_factory_(this) {
91  DCHECK(task_runner);
92  DCHECK(drive_service_);
93}
94
95SyncEngineInitializer::~SyncEngineInitializer() {
96  if (!cancel_callback_.is_null())
97    cancel_callback_.Run();
98}
99
100void SyncEngineInitializer::Run(scoped_ptr<SyncTaskToken> token) {
101  util::Log(logging::LOG_VERBOSE, FROM_HERE, "[Initialize] Start.");
102
103  // The metadata seems to have been already initialized. Just return with OK.
104  if (sync_context_ && 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  drive_service_->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_ = drive_service_->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_ = drive_service_->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_ = drive_service_->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_ = drive_service_->RemoveResourceFromDirectory(
294      root_folder_id_, sync_root_folder_->resource_id(),
295      base::Bind(&SyncEngineInitializer::DidDetachSyncRoot,
296                 weak_ptr_factory_.GetWeakPtr(),
297                 base::Passed(&token)));
298}
299
300void SyncEngineInitializer::DidDetachSyncRoot(
301    scoped_ptr<SyncTaskToken> token,
302    google_apis::GDataErrorCode error) {
303  cancel_callback_.Reset();
304
305  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
306  if (status != SYNC_STATUS_OK) {
307    util::Log(logging::LOG_VERBOSE, FROM_HERE,
308              "[Initialize] Failed to detach sync root.");
309    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
310    return;
311  }
312
313  ListAppRootFolders(token.Pass());
314}
315
316void SyncEngineInitializer::ListAppRootFolders(
317    scoped_ptr<SyncTaskToken> token) {
318  DCHECK(sync_root_folder_);
319  set_used_network(true);
320  cancel_callback_ = drive_service_->GetResourceListInDirectory(
321      sync_root_folder_->resource_id(),
322      base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
323                 weak_ptr_factory_.GetWeakPtr(),
324                 base::Passed(&token)));
325}
326
327void SyncEngineInitializer::DidListAppRootFolders(
328    scoped_ptr<SyncTaskToken> token,
329    google_apis::GDataErrorCode error,
330    scoped_ptr<google_apis::ResourceList> resource_list) {
331  cancel_callback_.Reset();
332
333  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
334  if (status != SYNC_STATUS_OK) {
335    util::Log(logging::LOG_VERBOSE, FROM_HERE,
336              "[Initialize] Failed to get initial app-root folders.");
337    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
338    return;
339  }
340
341  if (!resource_list) {
342    NOTREACHED();
343    util::Log(logging::LOG_VERBOSE, FROM_HERE,
344              "[Initialize] Got invalid initial app-root list.");
345    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
346    return;
347  }
348
349  ScopedVector<google_apis::ResourceEntry>* new_entries =
350      resource_list->mutable_entries();
351  app_root_folders_.insert(app_root_folders_.end(),
352                           new_entries->begin(), new_entries->end());
353  new_entries->weak_clear();
354
355  set_used_network(true);
356  GURL next_url;
357  if (resource_list->GetNextFeedURL(&next_url)) {
358    cancel_callback_ = drive_service_->GetRemainingFileList(
359        next_url,
360        base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
361                   weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
362    return;
363  }
364
365  PopulateDatabase(token.Pass());
366}
367
368void SyncEngineInitializer::PopulateDatabase(
369    scoped_ptr<SyncTaskToken> token) {
370  DCHECK(sync_root_folder_);
371  metadata_database_->PopulateInitialData(
372      largest_change_id_,
373      *drive::util::ConvertResourceEntryToFileResource(
374          *sync_root_folder_),
375      ConvertResourceEntriesToFileResources(app_root_folders_),
376      base::Bind(&SyncEngineInitializer::DidPopulateDatabase,
377                 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
378}
379
380void SyncEngineInitializer::DidPopulateDatabase(
381    scoped_ptr<SyncTaskToken> token,
382    SyncStatusCode status) {
383  if (status != SYNC_STATUS_OK) {
384    util::Log(logging::LOG_VERBOSE, FROM_HERE,
385              "[Initialize] Failed to populate initial data"
386              " to MetadataDatabase.");
387    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
388    return;
389  }
390
391  util::Log(logging::LOG_VERBOSE, FROM_HERE,
392            "[Initialize] Completed successfully.");
393  SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
394}
395
396}  // namespace drive_backend
397}  // namespace sync_file_system
398