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