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.
5 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.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"
23 namespace sync_file_system {
24 namespace drive_backend {
28 ////////////////////////////////////////////////////////////////////////////////
29 // Functions below are for wrapping the access to legacy GData WAPI classes.
31 bool HasNoParents(const google_apis::ResourceEntry& entry) {
32 return !entry.GetLinkByType(google_apis::Link::LINK_PARENT);
35 bool 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)
43 if (drive::util::ExtractResourceIdFromUrl(link.href()) == parent_id)
49 bool LessOnCreationTime(const google_apis::ResourceEntry& left,
50 const google_apis::ResourceEntry& right) {
51 return left.published_time() < right.published_time();
54 typedef base::Callback<void(scoped_ptr<SyncTaskToken> token,
55 google_apis::GDataErrorCode error,
56 scoped_ptr<google_apis::ResourceList> resources)>
57 TokenAndResourceListCallback;
59 ScopedVector<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 =
67 drive::util::ConvertResourceEntryToFileResource(
70 return resources.Pass();
73 // Functions above are for wrapping the access to legacy GData WAPI classes.
74 ////////////////////////////////////////////////////////////////////////////////
78 SyncEngineInitializer::SyncEngineInitializer(
79 SyncEngineContext* sync_context,
80 base::SequencedTaskRunner* task_runner,
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 database_path_(database_path),
87 find_sync_root_retry_count_(0),
88 largest_change_id_(0),
89 weak_ptr_factory_(this) {
94 SyncEngineInitializer::~SyncEngineInitializer() {
95 if (!cancel_callback_.is_null())
96 cancel_callback_.Run();
99 void SyncEngineInitializer::RunPreflight(scoped_ptr<SyncTaskToken> token) {
100 util::Log(logging::LOG_VERBOSE, FROM_HERE, "[Initialize] Start.");
101 DCHECK(sync_context_);
102 DCHECK(sync_context_->GetDriveService());
104 // The metadata seems to have been already initialized. Just return with OK.
105 if (sync_context_->GetMetadataDatabase()) {
106 util::Log(logging::LOG_VERBOSE, FROM_HERE,
107 "[Initialize] Already initialized.");
108 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
112 // TODO(tzik): Stop using MessageLoopProxy before moving out from UI thread.
113 scoped_refptr<base::SequencedTaskRunner> worker_task_runner(
114 base::MessageLoopProxy::current());
116 MetadataDatabase::Create(
117 worker_task_runner.get(),
118 task_runner_.get(), database_path_, env_override_,
119 base::Bind(&SyncEngineInitializer::DidCreateMetadataDatabase,
120 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
123 scoped_ptr<MetadataDatabase> SyncEngineInitializer::PassMetadataDatabase() {
124 return metadata_database_.Pass();
127 void SyncEngineInitializer::DidCreateMetadataDatabase(
128 scoped_ptr<SyncTaskToken> token,
129 SyncStatusCode status,
130 scoped_ptr<MetadataDatabase> instance) {
131 if (status != SYNC_STATUS_OK) {
132 util::Log(logging::LOG_VERBOSE, FROM_HERE,
133 "[Initialize] Failed to initialize MetadataDatabase.");
134 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
139 metadata_database_ = instance.Pass();
140 if (metadata_database_->HasSyncRoot()) {
141 util::Log(logging::LOG_VERBOSE, FROM_HERE,
142 "[Initialize] Found local cache of sync-root.");
143 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
147 GetAboutResource(token.Pass());
150 void SyncEngineInitializer::GetAboutResource(
151 scoped_ptr<SyncTaskToken> token) {
152 set_used_network(true);
153 sync_context_->GetDriveService()->GetAboutResource(
154 base::Bind(&SyncEngineInitializer::DidGetAboutResource,
155 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
158 void SyncEngineInitializer::DidGetAboutResource(
159 scoped_ptr<SyncTaskToken> token,
160 google_apis::GDataErrorCode error,
161 scoped_ptr<google_apis::AboutResource> about_resource) {
162 cancel_callback_.Reset();
164 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
165 if (status != SYNC_STATUS_OK) {
166 util::Log(logging::LOG_VERBOSE, FROM_HERE,
167 "[Initialize] Failed to get AboutResource.");
168 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
172 DCHECK(about_resource);
173 root_folder_id_ = about_resource->root_folder_id();
174 largest_change_id_ = about_resource->largest_change_id();
176 DCHECK(!root_folder_id_.empty());
177 FindSyncRoot(token.Pass());
180 void SyncEngineInitializer::FindSyncRoot(scoped_ptr<SyncTaskToken> token) {
181 if (find_sync_root_retry_count_++ >= kMaxRetry) {
182 util::Log(logging::LOG_VERBOSE, FROM_HERE,
183 "[Initialize] Reached max retry count.");
184 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
188 set_used_network(true);
189 cancel_callback_ = sync_context_->GetDriveService()->SearchByTitle(
190 kSyncRootFolderTitle,
191 std::string(), // parent_folder_id
192 base::Bind(&SyncEngineInitializer::DidFindSyncRoot,
193 weak_ptr_factory_.GetWeakPtr(),
194 base::Passed(&token)));
197 void SyncEngineInitializer::DidFindSyncRoot(
198 scoped_ptr<SyncTaskToken> token,
199 google_apis::GDataErrorCode error,
200 scoped_ptr<google_apis::ResourceList> resource_list) {
201 cancel_callback_.Reset();
203 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
204 if (status != SYNC_STATUS_OK) {
205 util::Log(logging::LOG_VERBOSE, FROM_HERE,
206 "[Initialize] Failed to find sync root.");
207 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
211 if (!resource_list) {
213 util::Log(logging::LOG_VERBOSE, FROM_HERE,
214 "[Initialize] Got invalid resource list.");
215 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
219 ScopedVector<google_apis::ResourceEntry>* entries =
220 resource_list->mutable_entries();
221 for (ScopedVector<google_apis::ResourceEntry>::iterator itr =
223 itr != entries->end(); ++itr) {
224 google_apis::ResourceEntry* entry = *itr;
226 // Ignore deleted folder.
227 if (entry->deleted())
230 // Pick an orphaned folder or a direct child of the root folder and
232 DCHECK(!root_folder_id_.empty());
233 if (!HasNoParents(*entry) && !HasFolderAsParent(*entry, root_folder_id_))
236 if (!sync_root_folder_ || LessOnCreationTime(*entry, *sync_root_folder_)) {
237 sync_root_folder_.reset(entry);
242 set_used_network(true);
243 // If there are more results, retrieve them.
245 if (resource_list->GetNextFeedURL(&next_url)) {
246 cancel_callback_ = sync_context_->GetDriveService()->GetRemainingFileList(
248 base::Bind(&SyncEngineInitializer::DidFindSyncRoot,
249 weak_ptr_factory_.GetWeakPtr(),
250 base::Passed(&token)));
254 if (!sync_root_folder_) {
255 CreateSyncRoot(token.Pass());
259 if (!HasNoParents(*sync_root_folder_)) {
260 DetachSyncRoot(token.Pass());
264 ListAppRootFolders(token.Pass());
267 void SyncEngineInitializer::CreateSyncRoot(scoped_ptr<SyncTaskToken> token) {
268 DCHECK(!sync_root_folder_);
269 set_used_network(true);
270 cancel_callback_ = sync_context_->GetDriveService()->AddNewDirectory(
271 root_folder_id_, kSyncRootFolderTitle,
272 drive::DriveServiceInterface::AddNewDirectoryOptions(),
273 base::Bind(&SyncEngineInitializer::DidCreateSyncRoot,
274 weak_ptr_factory_.GetWeakPtr(),
275 base::Passed(&token)));
278 void SyncEngineInitializer::DidCreateSyncRoot(
279 scoped_ptr<SyncTaskToken> token,
280 google_apis::GDataErrorCode error,
281 scoped_ptr<google_apis::ResourceEntry> entry) {
282 DCHECK(!sync_root_folder_);
283 cancel_callback_.Reset();
285 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
286 if (status != SYNC_STATUS_OK) {
287 util::Log(logging::LOG_VERBOSE, FROM_HERE,
288 "[Initialize] Failed to create sync root.");
289 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
293 FindSyncRoot(token.Pass());
296 void SyncEngineInitializer::DetachSyncRoot(scoped_ptr<SyncTaskToken> token) {
297 DCHECK(sync_root_folder_);
298 set_used_network(true);
300 sync_context_->GetDriveService()->RemoveResourceFromDirectory(
302 sync_root_folder_->resource_id(),
303 base::Bind(&SyncEngineInitializer::DidDetachSyncRoot,
304 weak_ptr_factory_.GetWeakPtr(),
305 base::Passed(&token)));
308 void SyncEngineInitializer::DidDetachSyncRoot(
309 scoped_ptr<SyncTaskToken> token,
310 google_apis::GDataErrorCode error) {
311 cancel_callback_.Reset();
313 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
314 if (status != SYNC_STATUS_OK) {
315 util::Log(logging::LOG_VERBOSE, FROM_HERE,
316 "[Initialize] Failed to detach sync root.");
317 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
321 ListAppRootFolders(token.Pass());
324 void SyncEngineInitializer::ListAppRootFolders(
325 scoped_ptr<SyncTaskToken> token) {
326 DCHECK(sync_root_folder_);
327 set_used_network(true);
329 sync_context_->GetDriveService()->GetResourceListInDirectory(
330 sync_root_folder_->resource_id(),
331 base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
332 weak_ptr_factory_.GetWeakPtr(),
333 base::Passed(&token)));
336 void SyncEngineInitializer::DidListAppRootFolders(
337 scoped_ptr<SyncTaskToken> token,
338 google_apis::GDataErrorCode error,
339 scoped_ptr<google_apis::ResourceList> resource_list) {
340 cancel_callback_.Reset();
342 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
343 if (status != SYNC_STATUS_OK) {
344 util::Log(logging::LOG_VERBOSE, FROM_HERE,
345 "[Initialize] Failed to get initial app-root folders.");
346 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
350 if (!resource_list) {
352 util::Log(logging::LOG_VERBOSE, FROM_HERE,
353 "[Initialize] Got invalid initial app-root list.");
354 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
358 ScopedVector<google_apis::ResourceEntry>* new_entries =
359 resource_list->mutable_entries();
360 app_root_folders_.insert(app_root_folders_.end(),
361 new_entries->begin(), new_entries->end());
362 new_entries->weak_clear();
364 set_used_network(true);
366 if (resource_list->GetNextFeedURL(&next_url)) {
368 sync_context_->GetDriveService()->GetRemainingFileList(
370 base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
371 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
375 PopulateDatabase(token.Pass());
378 void SyncEngineInitializer::PopulateDatabase(
379 scoped_ptr<SyncTaskToken> token) {
380 DCHECK(sync_root_folder_);
381 metadata_database_->PopulateInitialData(
383 *drive::util::ConvertResourceEntryToFileResource(
385 ConvertResourceEntriesToFileResources(app_root_folders_),
386 base::Bind(&SyncEngineInitializer::DidPopulateDatabase,
387 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
390 void SyncEngineInitializer::DidPopulateDatabase(
391 scoped_ptr<SyncTaskToken> token,
392 SyncStatusCode status) {
393 if (status != SYNC_STATUS_OK) {
394 util::Log(logging::LOG_VERBOSE, FROM_HERE,
395 "[Initialize] Failed to populate initial data"
396 " to MetadataDatabase.");
397 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
401 util::Log(logging::LOG_VERBOSE, FROM_HERE,
402 "[Initialize] Completed successfully.");
403 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
406 } // namespace drive_backend
407 } // namespace sync_file_system