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