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