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