sync_engine_initializer.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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/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_v1/drive_file_sync_util.h" 16#include "google_apis/drive/drive_api_parser.h" 17#include "google_apis/drive/gdata_wapi_parser.h" 18 19namespace sync_file_system { 20namespace drive_backend { 21 22namespace { 23 24//////////////////////////////////////////////////////////////////////////////// 25// Functions below are for wrapping the access to legacy GData WAPI classes. 26 27bool IsDeleted(const google_apis::ResourceEntry& entry) { 28 return entry.deleted(); 29} 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 54// Posts a request to continue listing. Returns false if the list doesn't need 55// listing anymore. 56bool GetRemainingFileList( 57 google_apis::CancelCallback* cancel_callback, 58 drive::DriveServiceInterface* api_service, 59 const google_apis::ResourceList& resource_list, 60 const google_apis::GetResourceListCallback& callback) { 61 GURL next_url; 62 if (!resource_list.GetNextFeedURL(&next_url)) 63 return false; 64 65 *cancel_callback = api_service->GetRemainingFileList(next_url, callback); 66 return true; 67} 68 69std::string GetID(const google_apis::ResourceEntry& entry) { 70 return entry.resource_id(); 71} 72 73ScopedVector<google_apis::FileResource> ConvertResourceEntriesToFileResources( 74 const ScopedVector<google_apis::ResourceEntry>& entries) { 75 ScopedVector<google_apis::FileResource> resources; 76 for (ScopedVector<google_apis::ResourceEntry>::const_iterator itr = 77 entries.begin(); 78 itr != entries.end(); 79 ++itr) { 80 resources.push_back( 81 drive::util::ConvertResourceEntryToFileResource( 82 **itr).release()); 83 } 84 return resources.Pass(); 85} 86 87// Functions above are for wrapping the access to legacy GData WAPI classes. 88//////////////////////////////////////////////////////////////////////////////// 89 90} // namespace 91 92SyncEngineInitializer::SyncEngineInitializer( 93 SyncEngineContext* sync_context, 94 base::SequencedTaskRunner* task_runner, 95 drive::DriveServiceInterface* drive_service, 96 const base::FilePath& database_path) 97 : sync_context_(sync_context), 98 task_runner_(task_runner), 99 drive_service_(drive_service), 100 database_path_(database_path), 101 find_sync_root_retry_count_(0), 102 largest_change_id_(0), 103 weak_ptr_factory_(this) { 104 DCHECK(task_runner); 105 DCHECK(drive_service_); 106} 107 108SyncEngineInitializer::~SyncEngineInitializer() { 109 if (!cancel_callback_.is_null()) 110 cancel_callback_.Run(); 111} 112 113void SyncEngineInitializer::Run(const SyncStatusCallback& callback) { 114 // The metadata seems to have been already initialized. Just return with OK. 115 if (sync_context_ && sync_context_->GetMetadataDatabase()) { 116 callback.Run(SYNC_STATUS_OK); 117 return; 118 } 119 120 MetadataDatabase::Create( 121 task_runner_.get(), database_path_, 122 base::Bind(&SyncEngineInitializer::DidCreateMetadataDatabase, 123 weak_ptr_factory_.GetWeakPtr(), callback)); 124} 125 126scoped_ptr<MetadataDatabase> SyncEngineInitializer::PassMetadataDatabase() { 127 return metadata_database_.Pass(); 128} 129 130void SyncEngineInitializer::DidCreateMetadataDatabase( 131 const SyncStatusCallback& callback, 132 SyncStatusCode status, 133 scoped_ptr<MetadataDatabase> instance) { 134 if (status != SYNC_STATUS_OK) { 135 callback.Run(status); 136 return; 137 } 138 139 DCHECK(instance); 140 metadata_database_ = instance.Pass(); 141 if (metadata_database_->HasSyncRoot()) { 142 callback.Run(SYNC_STATUS_OK); 143 return; 144 } 145 146 GetAboutResource(callback); 147} 148 149void SyncEngineInitializer::GetAboutResource( 150 const SyncStatusCallback& callback) { 151 set_used_network(true); 152 drive_service_->GetAboutResource( 153 base::Bind(&SyncEngineInitializer::DidGetAboutResource, 154 weak_ptr_factory_.GetWeakPtr(), callback)); 155} 156 157void SyncEngineInitializer::DidGetAboutResource( 158 const SyncStatusCallback& callback, 159 google_apis::GDataErrorCode error, 160 scoped_ptr<google_apis::AboutResource> about_resource) { 161 cancel_callback_.Reset(); 162 163 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); 164 if (status != SYNC_STATUS_OK) { 165 callback.Run(status); 166 return; 167 } 168 169 DCHECK(about_resource); 170 root_folder_id_ = about_resource->root_folder_id(); 171 largest_change_id_ = about_resource->largest_change_id(); 172 173 DCHECK(!root_folder_id_.empty()); 174 FindSyncRoot(callback); 175} 176 177void SyncEngineInitializer::FindSyncRoot(const SyncStatusCallback& callback) { 178 if (find_sync_root_retry_count_++ >= kMaxRetry) { 179 callback.Run(SYNC_STATUS_FAILED); 180 return; 181 } 182 183 set_used_network(true); 184 cancel_callback_ = drive_service_->SearchByTitle( 185 kSyncRootFolderTitle, 186 std::string(), // parent_folder_id 187 base::Bind(&SyncEngineInitializer::DidFindSyncRoot, 188 weak_ptr_factory_.GetWeakPtr(), 189 callback)); 190} 191 192void SyncEngineInitializer::DidFindSyncRoot( 193 const SyncStatusCallback& callback, 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 callback.Run(status); 201 return; 202 } 203 204 ScopedVector<google_apis::ResourceEntry>* entries = 205 resource_list->mutable_entries(); 206 for (ScopedVector<google_apis::ResourceEntry>::iterator itr = 207 entries->begin(); 208 itr != entries->end(); ++itr) { 209 google_apis::ResourceEntry* entry = *itr; 210 211 // Ignore deleted folder. 212 if (IsDeleted(*entry)) 213 continue; 214 215 // Pick an orphaned folder or a direct child of the root folder and 216 // ignore others. 217 DCHECK(!root_folder_id_.empty()); 218 if (!HasNoParents(*entry) && !HasFolderAsParent(*entry, root_folder_id_)) 219 continue; 220 221 if (!sync_root_folder_ || LessOnCreationTime(*entry, *sync_root_folder_)) { 222 sync_root_folder_.reset(entry); 223 *itr = NULL; 224 } 225 } 226 227 set_used_network(true); 228 // If there are more results, retrieve them. 229 if (GetRemainingFileList( 230 &cancel_callback_, 231 drive_service_, *resource_list, 232 base::Bind(&SyncEngineInitializer::DidFindSyncRoot, 233 weak_ptr_factory_.GetWeakPtr(), 234 callback))) 235 return; 236 237 if (!sync_root_folder_) { 238 CreateSyncRoot(callback); 239 return; 240 } 241 242 if (!HasNoParents(*sync_root_folder_)) { 243 DetachSyncRoot(callback); 244 return; 245 } 246 247 ListAppRootFolders(callback); 248} 249 250void SyncEngineInitializer::CreateSyncRoot(const SyncStatusCallback& callback) { 251 DCHECK(!sync_root_folder_); 252 set_used_network(true); 253 cancel_callback_ = drive_service_->AddNewDirectory( 254 root_folder_id_, kSyncRootFolderTitle, 255 base::Bind(&SyncEngineInitializer::DidCreateSyncRoot, 256 weak_ptr_factory_.GetWeakPtr(), 257 callback)); 258} 259 260void SyncEngineInitializer::DidCreateSyncRoot( 261 const SyncStatusCallback& callback, 262 google_apis::GDataErrorCode error, 263 scoped_ptr<google_apis::ResourceEntry> entry) { 264 DCHECK(!sync_root_folder_); 265 cancel_callback_.Reset(); 266 267 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); 268 if (status != SYNC_STATUS_OK) { 269 callback.Run(status); 270 return; 271 } 272 273 FindSyncRoot(callback); 274} 275 276void SyncEngineInitializer::DetachSyncRoot(const SyncStatusCallback& callback) { 277 DCHECK(sync_root_folder_); 278 set_used_network(true); 279 cancel_callback_ = drive_service_->RemoveResourceFromDirectory( 280 root_folder_id_, GetID(*sync_root_folder_), 281 base::Bind(&SyncEngineInitializer::DidDetachSyncRoot, 282 weak_ptr_factory_.GetWeakPtr(), 283 callback)); 284} 285 286void SyncEngineInitializer::DidDetachSyncRoot( 287 const SyncStatusCallback& callback, 288 google_apis::GDataErrorCode error) { 289 cancel_callback_.Reset(); 290 291 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); 292 if (status != SYNC_STATUS_OK) { 293 callback.Run(status); 294 return; 295 } 296 297 ListAppRootFolders(callback); 298} 299 300void SyncEngineInitializer::ListAppRootFolders( 301 const SyncStatusCallback& callback) { 302 DCHECK(sync_root_folder_); 303 set_used_network(true); 304 cancel_callback_ = drive_service_->GetResourceListInDirectory( 305 GetID(*sync_root_folder_), 306 base::Bind(&SyncEngineInitializer::DidListAppRootFolders, 307 weak_ptr_factory_.GetWeakPtr(), 308 callback)); 309} 310 311void SyncEngineInitializer::DidListAppRootFolders( 312 const SyncStatusCallback& callback, 313 google_apis::GDataErrorCode error, 314 scoped_ptr<google_apis::ResourceList> resource_list) { 315 cancel_callback_.Reset(); 316 317 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); 318 if (status != SYNC_STATUS_OK) { 319 callback.Run(status); 320 return; 321 } 322 323 ScopedVector<google_apis::ResourceEntry>* new_entries = 324 resource_list->mutable_entries(); 325 app_root_folders_.insert(app_root_folders_.end(), 326 new_entries->begin(), new_entries->end()); 327 new_entries->weak_clear(); 328 329 set_used_network(true); 330 if (GetRemainingFileList( 331 &cancel_callback_, 332 drive_service_, 333 *resource_list, 334 base::Bind(&SyncEngineInitializer::DidListAppRootFolders, 335 weak_ptr_factory_.GetWeakPtr(), callback))) 336 return; 337 338 PopulateDatabase(callback); 339} 340 341void SyncEngineInitializer::PopulateDatabase( 342 const SyncStatusCallback& callback) { 343 DCHECK(sync_root_folder_); 344 metadata_database_->PopulateInitialData( 345 largest_change_id_, 346 *drive::util::ConvertResourceEntryToFileResource( 347 *sync_root_folder_), 348 ConvertResourceEntriesToFileResources(app_root_folders_), 349 base::Bind(&SyncEngineInitializer::DidPopulateDatabase, 350 weak_ptr_factory_.GetWeakPtr(), 351 callback)); 352} 353 354void SyncEngineInitializer::DidPopulateDatabase( 355 const SyncStatusCallback& callback, 356 SyncStatusCode status) { 357 if (status != SYNC_STATUS_OK) { 358 callback.Run(status); 359 return; 360 } 361 362 callback.Run(SYNC_STATUS_OK); 363} 364 365} // namespace drive_backend 366} // namespace sync_file_system 367