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 : sync_context_(sync_context
),
99 task_runner_(task_runner
),
100 drive_service_(drive_service
),
101 database_path_(database_path
),
102 find_sync_root_retry_count_(0),
103 largest_change_id_(0),
104 weak_ptr_factory_(this) {
106 DCHECK(drive_service_
);
109 SyncEngineInitializer::~SyncEngineInitializer() {
110 if (!cancel_callback_
.is_null())
111 cancel_callback_
.Run();
114 void SyncEngineInitializer::Run(const SyncStatusCallback
& callback
) {
115 util::Log(logging::LOG_VERBOSE
, FROM_HERE
, "[Initialize] Start.");
117 // The metadata seems to have been already initialized. Just return with OK.
118 if (sync_context_
&& sync_context_
->GetMetadataDatabase()) {
119 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
120 "[Initialize] Already initialized.");
121 callback
.Run(SYNC_STATUS_OK
);
125 MetadataDatabase::Create(
126 task_runner_
.get(), database_path_
,
127 base::Bind(&SyncEngineInitializer::DidCreateMetadataDatabase
,
128 weak_ptr_factory_
.GetWeakPtr(), callback
));
131 scoped_ptr
<MetadataDatabase
> SyncEngineInitializer::PassMetadataDatabase() {
132 return metadata_database_
.Pass();
135 void SyncEngineInitializer::DidCreateMetadataDatabase(
136 const SyncStatusCallback
& callback
,
137 SyncStatusCode status
,
138 scoped_ptr
<MetadataDatabase
> instance
) {
139 if (status
!= SYNC_STATUS_OK
) {
140 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
141 "[Initialize] Failed to initialize MetadataDatabase.");
142 callback
.Run(status
);
147 metadata_database_
= instance
.Pass();
148 if (metadata_database_
->HasSyncRoot()) {
149 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
150 "[Initialize] Found local cache of sync-root.");
151 callback
.Run(SYNC_STATUS_OK
);
155 GetAboutResource(callback
);
158 void SyncEngineInitializer::GetAboutResource(
159 const SyncStatusCallback
& callback
) {
160 set_used_network(true);
161 drive_service_
->GetAboutResource(
162 base::Bind(&SyncEngineInitializer::DidGetAboutResource
,
163 weak_ptr_factory_
.GetWeakPtr(), callback
));
166 void SyncEngineInitializer::DidGetAboutResource(
167 const SyncStatusCallback
& callback
,
168 google_apis::GDataErrorCode error
,
169 scoped_ptr
<google_apis::AboutResource
> about_resource
) {
170 cancel_callback_
.Reset();
172 SyncStatusCode status
= GDataErrorCodeToSyncStatusCode(error
);
173 if (status
!= SYNC_STATUS_OK
) {
174 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
175 "[Initialize] Failed to get AboutResource.");
176 callback
.Run(status
);
180 DCHECK(about_resource
);
181 root_folder_id_
= about_resource
->root_folder_id();
182 largest_change_id_
= about_resource
->largest_change_id();
184 DCHECK(!root_folder_id_
.empty());
185 FindSyncRoot(callback
);
188 void SyncEngineInitializer::FindSyncRoot(const SyncStatusCallback
& callback
) {
189 if (find_sync_root_retry_count_
++ >= kMaxRetry
) {
190 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
191 "[Initialize] Reached max retry count.");
192 callback
.Run(SYNC_STATUS_FAILED
);
196 set_used_network(true);
197 cancel_callback_
= drive_service_
->SearchByTitle(
198 kSyncRootFolderTitle
,
199 std::string(), // parent_folder_id
200 base::Bind(&SyncEngineInitializer::DidFindSyncRoot
,
201 weak_ptr_factory_
.GetWeakPtr(),
205 void SyncEngineInitializer::DidFindSyncRoot(
206 const SyncStatusCallback
& callback
,
207 google_apis::GDataErrorCode error
,
208 scoped_ptr
<google_apis::ResourceList
> resource_list
) {
209 cancel_callback_
.Reset();
211 SyncStatusCode status
= GDataErrorCodeToSyncStatusCode(error
);
212 if (status
!= SYNC_STATUS_OK
) {
213 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
214 "[Initialize] Failed to find sync root.");
215 callback
.Run(status
);
219 if (!resource_list
) {
221 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
222 "[Initialize] Got invalid resource list.");
223 callback
.Run(SYNC_STATUS_FAILED
);
227 ScopedVector
<google_apis::ResourceEntry
>* entries
=
228 resource_list
->mutable_entries();
229 for (ScopedVector
<google_apis::ResourceEntry
>::iterator itr
=
231 itr
!= entries
->end(); ++itr
) {
232 google_apis::ResourceEntry
* entry
= *itr
;
234 // Ignore deleted folder.
235 if (IsDeleted(*entry
))
238 // Pick an orphaned folder or a direct child of the root folder and
240 DCHECK(!root_folder_id_
.empty());
241 if (!HasNoParents(*entry
) && !HasFolderAsParent(*entry
, root_folder_id_
))
244 if (!sync_root_folder_
|| LessOnCreationTime(*entry
, *sync_root_folder_
)) {
245 sync_root_folder_
.reset(entry
);
250 set_used_network(true);
251 // If there are more results, retrieve them.
252 if (GetRemainingFileList(
254 drive_service_
, *resource_list
,
255 base::Bind(&SyncEngineInitializer::DidFindSyncRoot
,
256 weak_ptr_factory_
.GetWeakPtr(),
260 if (!sync_root_folder_
) {
261 CreateSyncRoot(callback
);
265 if (!HasNoParents(*sync_root_folder_
)) {
266 DetachSyncRoot(callback
);
270 ListAppRootFolders(callback
);
273 void SyncEngineInitializer::CreateSyncRoot(const SyncStatusCallback
& callback
) {
274 DCHECK(!sync_root_folder_
);
275 set_used_network(true);
276 cancel_callback_
= drive_service_
->AddNewDirectory(
277 root_folder_id_
, kSyncRootFolderTitle
,
278 base::Bind(&SyncEngineInitializer::DidCreateSyncRoot
,
279 weak_ptr_factory_
.GetWeakPtr(),
283 void SyncEngineInitializer::DidCreateSyncRoot(
284 const SyncStatusCallback
& callback
,
285 google_apis::GDataErrorCode error
,
286 scoped_ptr
<google_apis::ResourceEntry
> entry
) {
287 DCHECK(!sync_root_folder_
);
288 cancel_callback_
.Reset();
290 SyncStatusCode status
= GDataErrorCodeToSyncStatusCode(error
);
291 if (status
!= SYNC_STATUS_OK
) {
292 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
293 "[Initialize] Failed to create sync root.");
294 callback
.Run(status
);
298 FindSyncRoot(callback
);
301 void SyncEngineInitializer::DetachSyncRoot(const SyncStatusCallback
& callback
) {
302 DCHECK(sync_root_folder_
);
303 set_used_network(true);
304 cancel_callback_
= drive_service_
->RemoveResourceFromDirectory(
305 root_folder_id_
, GetID(*sync_root_folder_
),
306 base::Bind(&SyncEngineInitializer::DidDetachSyncRoot
,
307 weak_ptr_factory_
.GetWeakPtr(),
311 void SyncEngineInitializer::DidDetachSyncRoot(
312 const SyncStatusCallback
& callback
,
313 google_apis::GDataErrorCode error
) {
314 cancel_callback_
.Reset();
316 SyncStatusCode status
= GDataErrorCodeToSyncStatusCode(error
);
317 if (status
!= SYNC_STATUS_OK
) {
318 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
319 "[Initialize] Failed to detach sync root.");
320 callback
.Run(status
);
324 ListAppRootFolders(callback
);
327 void SyncEngineInitializer::ListAppRootFolders(
328 const SyncStatusCallback
& callback
) {
329 DCHECK(sync_root_folder_
);
330 set_used_network(true);
331 cancel_callback_
= drive_service_
->GetResourceListInDirectory(
332 GetID(*sync_root_folder_
),
333 base::Bind(&SyncEngineInitializer::DidListAppRootFolders
,
334 weak_ptr_factory_
.GetWeakPtr(),
338 void SyncEngineInitializer::DidListAppRootFolders(
339 const SyncStatusCallback
& callback
,
340 google_apis::GDataErrorCode error
,
341 scoped_ptr
<google_apis::ResourceList
> resource_list
) {
342 cancel_callback_
.Reset();
344 SyncStatusCode status
= GDataErrorCodeToSyncStatusCode(error
);
345 if (status
!= SYNC_STATUS_OK
) {
346 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
347 "[Initialize] Failed to get initial app-root folders.");
348 callback
.Run(status
);
352 if (!resource_list
) {
354 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
355 "[Initialize] Got invalid initial app-root list.");
356 callback
.Run(SYNC_STATUS_FAILED
);
360 ScopedVector
<google_apis::ResourceEntry
>* new_entries
=
361 resource_list
->mutable_entries();
362 app_root_folders_
.insert(app_root_folders_
.end(),
363 new_entries
->begin(), new_entries
->end());
364 new_entries
->weak_clear();
366 set_used_network(true);
367 if (GetRemainingFileList(
371 base::Bind(&SyncEngineInitializer::DidListAppRootFolders
,
372 weak_ptr_factory_
.GetWeakPtr(), callback
)))
375 PopulateDatabase(callback
);
378 void SyncEngineInitializer::PopulateDatabase(
379 const SyncStatusCallback
& callback
) {
380 DCHECK(sync_root_folder_
);
381 metadata_database_
->PopulateInitialData(
383 *drive::util::ConvertResourceEntryToFileResource(
385 ConvertResourceEntriesToFileResources(app_root_folders_
),
386 base::Bind(&SyncEngineInitializer::DidPopulateDatabase
,
387 weak_ptr_factory_
.GetWeakPtr(),
391 void SyncEngineInitializer::DidPopulateDatabase(
392 const SyncStatusCallback
& callback
,
393 SyncStatusCode status
) {
394 if (status
!= SYNC_STATUS_OK
) {
395 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
396 "[Initialize] Failed to populate initial data"
397 " to MetadataDatabase.");
398 callback
.Run(status
);
402 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
403 "[Initialize] Completed successfully.");
404 callback
.Run(SYNC_STATUS_OK
);
407 } // namespace drive_backend
408 } // namespace sync_file_system