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