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