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.
5 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.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/logger.h"
17 #include "google_apis/drive/drive_api_parser.h"
18 #include "google_apis/drive/gdata_wapi_parser.h"
20 namespace sync_file_system {
21 namespace drive_backend {
25 ////////////////////////////////////////////////////////////////////////////////
26 // Functions below are for wrapping the access to legacy GData WAPI classes.
28 bool IsDeleted(const google_apis::ResourceEntry& entry) {
29 return entry.deleted();
32 bool HasNoParents(const google_apis::ResourceEntry& entry) {
33 return !entry.GetLinkByType(google_apis::Link::LINK_PARENT);
36 bool HasFolderAsParent(const google_apis::ResourceEntry& entry,
37 const std::string& parent_id) {
38 const ScopedVector<google_apis::Link>& links = entry.links();
39 for (ScopedVector<google_apis::Link>::const_iterator itr = links.begin();
40 itr != links.end(); ++itr) {
41 const google_apis::Link& link = **itr;
42 if (link.type() != google_apis::Link::LINK_PARENT)
44 if (drive::util::ExtractResourceIdFromUrl(link.href()) == parent_id)
50 bool LessOnCreationTime(const google_apis::ResourceEntry& left,
51 const google_apis::ResourceEntry& right) {
52 return left.published_time() < right.published_time();
55 // Posts a request to continue listing. Returns false if the list doesn't need
57 bool GetRemainingFileList(
58 google_apis::CancelCallback* cancel_callback,
59 drive::DriveServiceInterface* api_service,
60 const google_apis::ResourceList& resource_list,
61 const google_apis::GetResourceListCallback& callback) {
63 if (!resource_list.GetNextFeedURL(&next_url))
66 *cancel_callback = api_service->GetRemainingFileList(next_url, callback);
70 std::string GetID(const google_apis::ResourceEntry& entry) {
71 return entry.resource_id();
74 ScopedVector<google_apis::FileResource> ConvertResourceEntriesToFileResources(
75 const ScopedVector<google_apis::ResourceEntry>& entries) {
76 ScopedVector<google_apis::FileResource> resources;
77 for (ScopedVector<google_apis::ResourceEntry>::const_iterator itr =
82 drive::util::ConvertResourceEntryToFileResource(
85 return resources.Pass();
88 // Functions above are for wrapping the access to legacy GData WAPI classes.
89 ////////////////////////////////////////////////////////////////////////////////
93 SyncEngineInitializer::SyncEngineInitializer(
94 SyncEngineContext* sync_context,
95 base::SequencedTaskRunner* task_runner,
96 drive::DriveServiceInterface* drive_service,
97 const base::FilePath& database_path,
98 leveldb::Env* env_override)
99 : sync_context_(sync_context),
100 env_override_(env_override),
101 task_runner_(task_runner),
102 drive_service_(drive_service),
103 database_path_(database_path),
104 find_sync_root_retry_count_(0),
105 largest_change_id_(0),
106 weak_ptr_factory_(this) {
108 DCHECK(drive_service_);
111 SyncEngineInitializer::~SyncEngineInitializer() {
112 if (!cancel_callback_.is_null())
113 cancel_callback_.Run();
116 void SyncEngineInitializer::Run(const SyncStatusCallback& callback) {
117 util::Log(logging::LOG_VERBOSE, FROM_HERE, "[Initialize] Start.");
119 // The metadata seems to have been already initialized. Just return with OK.
120 if (sync_context_ && sync_context_->GetMetadataDatabase()) {
121 util::Log(logging::LOG_VERBOSE, FROM_HERE,
122 "[Initialize] Already initialized.");
123 callback.Run(SYNC_STATUS_OK);
127 MetadataDatabase::Create(
128 task_runner_.get(), database_path_, env_override_,
129 base::Bind(&SyncEngineInitializer::DidCreateMetadataDatabase,
130 weak_ptr_factory_.GetWeakPtr(), callback));
133 scoped_ptr<MetadataDatabase> SyncEngineInitializer::PassMetadataDatabase() {
134 return metadata_database_.Pass();
137 void SyncEngineInitializer::DidCreateMetadataDatabase(
138 const SyncStatusCallback& callback,
139 SyncStatusCode status,
140 scoped_ptr<MetadataDatabase> instance) {
141 if (status != SYNC_STATUS_OK) {
142 util::Log(logging::LOG_VERBOSE, FROM_HERE,
143 "[Initialize] Failed to initialize MetadataDatabase.");
144 callback.Run(status);
149 metadata_database_ = instance.Pass();
150 if (metadata_database_->HasSyncRoot()) {
151 util::Log(logging::LOG_VERBOSE, FROM_HERE,
152 "[Initialize] Found local cache of sync-root.");
153 callback.Run(SYNC_STATUS_OK);
157 GetAboutResource(callback);
160 void SyncEngineInitializer::GetAboutResource(
161 const SyncStatusCallback& callback) {
162 set_used_network(true);
163 drive_service_->GetAboutResource(
164 base::Bind(&SyncEngineInitializer::DidGetAboutResource,
165 weak_ptr_factory_.GetWeakPtr(), callback));
168 void SyncEngineInitializer::DidGetAboutResource(
169 const SyncStatusCallback& callback,
170 google_apis::GDataErrorCode error,
171 scoped_ptr<google_apis::AboutResource> about_resource) {
172 cancel_callback_.Reset();
174 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
175 if (status != SYNC_STATUS_OK) {
176 util::Log(logging::LOG_VERBOSE, FROM_HERE,
177 "[Initialize] Failed to get AboutResource.");
178 callback.Run(status);
182 DCHECK(about_resource);
183 root_folder_id_ = about_resource->root_folder_id();
184 largest_change_id_ = about_resource->largest_change_id();
186 DCHECK(!root_folder_id_.empty());
187 FindSyncRoot(callback);
190 void SyncEngineInitializer::FindSyncRoot(const SyncStatusCallback& callback) {
191 if (find_sync_root_retry_count_++ >= kMaxRetry) {
192 util::Log(logging::LOG_VERBOSE, FROM_HERE,
193 "[Initialize] Reached max retry count.");
194 callback.Run(SYNC_STATUS_FAILED);
198 set_used_network(true);
199 cancel_callback_ = drive_service_->SearchByTitle(
200 kSyncRootFolderTitle,
201 std::string(), // parent_folder_id
202 base::Bind(&SyncEngineInitializer::DidFindSyncRoot,
203 weak_ptr_factory_.GetWeakPtr(),
207 void SyncEngineInitializer::DidFindSyncRoot(
208 const SyncStatusCallback& callback,
209 google_apis::GDataErrorCode error,
210 scoped_ptr<google_apis::ResourceList> resource_list) {
211 cancel_callback_.Reset();
213 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
214 if (status != SYNC_STATUS_OK) {
215 util::Log(logging::LOG_VERBOSE, FROM_HERE,
216 "[Initialize] Failed to find sync root.");
217 callback.Run(status);
221 if (!resource_list) {
223 util::Log(logging::LOG_VERBOSE, FROM_HERE,
224 "[Initialize] Got invalid resource list.");
225 callback.Run(SYNC_STATUS_FAILED);
229 ScopedVector<google_apis::ResourceEntry>* entries =
230 resource_list->mutable_entries();
231 for (ScopedVector<google_apis::ResourceEntry>::iterator itr =
233 itr != entries->end(); ++itr) {
234 google_apis::ResourceEntry* entry = *itr;
236 // Ignore deleted folder.
237 if (IsDeleted(*entry))
240 // Pick an orphaned folder or a direct child of the root folder and
242 DCHECK(!root_folder_id_.empty());
243 if (!HasNoParents(*entry) && !HasFolderAsParent(*entry, root_folder_id_))
246 if (!sync_root_folder_ || LessOnCreationTime(*entry, *sync_root_folder_)) {
247 sync_root_folder_.reset(entry);
252 set_used_network(true);
253 // If there are more results, retrieve them.
254 if (GetRemainingFileList(
256 drive_service_, *resource_list,
257 base::Bind(&SyncEngineInitializer::DidFindSyncRoot,
258 weak_ptr_factory_.GetWeakPtr(),
262 if (!sync_root_folder_) {
263 CreateSyncRoot(callback);
267 if (!HasNoParents(*sync_root_folder_)) {
268 DetachSyncRoot(callback);
272 ListAppRootFolders(callback);
275 void SyncEngineInitializer::CreateSyncRoot(const SyncStatusCallback& callback) {
276 DCHECK(!sync_root_folder_);
277 set_used_network(true);
278 cancel_callback_ = drive_service_->AddNewDirectory(
279 root_folder_id_, kSyncRootFolderTitle,
280 drive::DriveServiceInterface::AddNewDirectoryOptions(),
281 base::Bind(&SyncEngineInitializer::DidCreateSyncRoot,
282 weak_ptr_factory_.GetWeakPtr(),
286 void SyncEngineInitializer::DidCreateSyncRoot(
287 const SyncStatusCallback& callback,
288 google_apis::GDataErrorCode error,
289 scoped_ptr<google_apis::ResourceEntry> entry) {
290 DCHECK(!sync_root_folder_);
291 cancel_callback_.Reset();
293 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
294 if (status != SYNC_STATUS_OK) {
295 util::Log(logging::LOG_VERBOSE, FROM_HERE,
296 "[Initialize] Failed to create sync root.");
297 callback.Run(status);
301 FindSyncRoot(callback);
304 void SyncEngineInitializer::DetachSyncRoot(const SyncStatusCallback& callback) {
305 DCHECK(sync_root_folder_);
306 set_used_network(true);
307 cancel_callback_ = drive_service_->RemoveResourceFromDirectory(
308 root_folder_id_, GetID(*sync_root_folder_),
309 base::Bind(&SyncEngineInitializer::DidDetachSyncRoot,
310 weak_ptr_factory_.GetWeakPtr(),
314 void SyncEngineInitializer::DidDetachSyncRoot(
315 const SyncStatusCallback& callback,
316 google_apis::GDataErrorCode error) {
317 cancel_callback_.Reset();
319 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
320 if (status != SYNC_STATUS_OK) {
321 util::Log(logging::LOG_VERBOSE, FROM_HERE,
322 "[Initialize] Failed to detach sync root.");
323 callback.Run(status);
327 ListAppRootFolders(callback);
330 void SyncEngineInitializer::ListAppRootFolders(
331 const SyncStatusCallback& callback) {
332 DCHECK(sync_root_folder_);
333 set_used_network(true);
334 cancel_callback_ = drive_service_->GetResourceListInDirectory(
335 GetID(*sync_root_folder_),
336 base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
337 weak_ptr_factory_.GetWeakPtr(),
341 void SyncEngineInitializer::DidListAppRootFolders(
342 const SyncStatusCallback& callback,
343 google_apis::GDataErrorCode error,
344 scoped_ptr<google_apis::ResourceList> resource_list) {
345 cancel_callback_.Reset();
347 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
348 if (status != SYNC_STATUS_OK) {
349 util::Log(logging::LOG_VERBOSE, FROM_HERE,
350 "[Initialize] Failed to get initial app-root folders.");
351 callback.Run(status);
355 if (!resource_list) {
357 util::Log(logging::LOG_VERBOSE, FROM_HERE,
358 "[Initialize] Got invalid initial app-root list.");
359 callback.Run(SYNC_STATUS_FAILED);
363 ScopedVector<google_apis::ResourceEntry>* new_entries =
364 resource_list->mutable_entries();
365 app_root_folders_.insert(app_root_folders_.end(),
366 new_entries->begin(), new_entries->end());
367 new_entries->weak_clear();
369 set_used_network(true);
370 if (GetRemainingFileList(
374 base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
375 weak_ptr_factory_.GetWeakPtr(), callback)))
378 PopulateDatabase(callback);
381 void SyncEngineInitializer::PopulateDatabase(
382 const SyncStatusCallback& callback) {
383 DCHECK(sync_root_folder_);
384 metadata_database_->PopulateInitialData(
386 *drive::util::ConvertResourceEntryToFileResource(
388 ConvertResourceEntriesToFileResources(app_root_folders_),
389 base::Bind(&SyncEngineInitializer::DidPopulateDatabase,
390 weak_ptr_factory_.GetWeakPtr(),
394 void SyncEngineInitializer::DidPopulateDatabase(
395 const SyncStatusCallback& callback,
396 SyncStatusCode status) {
397 if (status != SYNC_STATUS_OK) {
398 util::Log(logging::LOG_VERBOSE, FROM_HERE,
399 "[Initialize] Failed to populate initial data"
400 " to MetadataDatabase.");
401 callback.Run(status);
405 util::Log(logging::LOG_VERBOSE, FROM_HERE,
406 "[Initialize] Completed successfully.");
407 callback.Run(SYNC_STATUS_OK);
410 } // namespace drive_backend
411 } // namespace sync_file_system