01d8129283c0e6915022f2b7a0e9643dc5d509ad
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / drive_backend / sync_engine_initializer.cc
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/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"
19
20 namespace sync_file_system {
21 namespace drive_backend {
22
23 namespace {
24
25 ////////////////////////////////////////////////////////////////////////////////
26 // Functions below are for wrapping the access to legacy GData WAPI classes.
27
28 bool IsDeleted(const google_apis::ResourceEntry& entry) {
29   return entry.deleted();
30 }
31
32 bool HasNoParents(const google_apis::ResourceEntry& entry) {
33   return !entry.GetLinkByType(google_apis::Link::LINK_PARENT);
34 }
35
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)
43       continue;
44     if (drive::util::ExtractResourceIdFromUrl(link.href()) == parent_id)
45       return true;
46   }
47   return false;
48 }
49
50 bool LessOnCreationTime(const google_apis::ResourceEntry& left,
51                         const google_apis::ResourceEntry& right) {
52   return left.published_time() < right.published_time();
53 }
54
55 // Posts a request to continue listing.  Returns false if the list doesn't need
56 // listing anymore.
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) {
62   GURL next_url;
63   if (!resource_list.GetNextFeedURL(&next_url))
64     return false;
65
66   *cancel_callback = api_service->GetRemainingFileList(next_url, callback);
67   return true;
68 }
69
70 std::string GetID(const google_apis::ResourceEntry& entry) {
71   return entry.resource_id();
72 }
73
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 =
78            entries.begin();
79        itr != entries.end();
80        ++itr) {
81     resources.push_back(
82         drive::util::ConvertResourceEntryToFileResource(
83             **itr).release());
84   }
85   return resources.Pass();
86 }
87
88 // Functions above are for wrapping the access to legacy GData WAPI classes.
89 ////////////////////////////////////////////////////////////////////////////////
90
91 }  // namespace
92
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) {
107   DCHECK(task_runner);
108   DCHECK(drive_service_);
109 }
110
111 SyncEngineInitializer::~SyncEngineInitializer() {
112   if (!cancel_callback_.is_null())
113     cancel_callback_.Run();
114 }
115
116 void SyncEngineInitializer::Run(const SyncStatusCallback& callback) {
117   util::Log(logging::LOG_VERBOSE, FROM_HERE, "[Initialize] Start.");
118
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);
124     return;
125   }
126
127   MetadataDatabase::Create(
128       task_runner_.get(), database_path_, env_override_,
129       base::Bind(&SyncEngineInitializer::DidCreateMetadataDatabase,
130                  weak_ptr_factory_.GetWeakPtr(), callback));
131 }
132
133 scoped_ptr<MetadataDatabase> SyncEngineInitializer::PassMetadataDatabase() {
134   return metadata_database_.Pass();
135 }
136
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);
145     return;
146   }
147
148   DCHECK(instance);
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);
154     return;
155   }
156
157   GetAboutResource(callback);
158 }
159
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));
166 }
167
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();
173
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);
179     return;
180   }
181
182   DCHECK(about_resource);
183   root_folder_id_ = about_resource->root_folder_id();
184   largest_change_id_ = about_resource->largest_change_id();
185
186   DCHECK(!root_folder_id_.empty());
187   FindSyncRoot(callback);
188 }
189
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);
195     return;
196   }
197
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(),
204                  callback));
205 }
206
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();
212
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);
218     return;
219   }
220
221   if (!resource_list) {
222     NOTREACHED();
223     util::Log(logging::LOG_VERBOSE, FROM_HERE,
224               "[Initialize] Got invalid resource list.");
225     callback.Run(SYNC_STATUS_FAILED);
226     return;
227   }
228
229   ScopedVector<google_apis::ResourceEntry>* entries =
230       resource_list->mutable_entries();
231   for (ScopedVector<google_apis::ResourceEntry>::iterator itr =
232            entries->begin();
233        itr != entries->end(); ++itr) {
234     google_apis::ResourceEntry* entry = *itr;
235
236     // Ignore deleted folder.
237     if (IsDeleted(*entry))
238       continue;
239
240     // Pick an orphaned folder or a direct child of the root folder and
241     // ignore others.
242     DCHECK(!root_folder_id_.empty());
243     if (!HasNoParents(*entry) && !HasFolderAsParent(*entry, root_folder_id_))
244       continue;
245
246     if (!sync_root_folder_ || LessOnCreationTime(*entry, *sync_root_folder_)) {
247       sync_root_folder_.reset(entry);
248       *itr = NULL;
249     }
250   }
251
252   set_used_network(true);
253   // If there are more results, retrieve them.
254   if (GetRemainingFileList(
255           &cancel_callback_,
256           drive_service_, *resource_list,
257           base::Bind(&SyncEngineInitializer::DidFindSyncRoot,
258                      weak_ptr_factory_.GetWeakPtr(),
259                      callback)))
260     return;
261
262   if (!sync_root_folder_) {
263     CreateSyncRoot(callback);
264     return;
265   }
266
267   if (!HasNoParents(*sync_root_folder_)) {
268     DetachSyncRoot(callback);
269     return;
270   }
271
272   ListAppRootFolders(callback);
273 }
274
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(),
283                  callback));
284 }
285
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();
292
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);
298     return;
299   }
300
301   FindSyncRoot(callback);
302 }
303
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(),
311                  callback));
312 }
313
314 void SyncEngineInitializer::DidDetachSyncRoot(
315     const SyncStatusCallback& callback,
316     google_apis::GDataErrorCode error) {
317   cancel_callback_.Reset();
318
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);
324     return;
325   }
326
327   ListAppRootFolders(callback);
328 }
329
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(),
338                  callback));
339 }
340
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();
346
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);
352     return;
353   }
354
355   if (!resource_list) {
356     NOTREACHED();
357     util::Log(logging::LOG_VERBOSE, FROM_HERE,
358               "[Initialize] Got invalid initial app-root list.");
359     callback.Run(SYNC_STATUS_FAILED);
360     return;
361   }
362
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();
368
369   set_used_network(true);
370   if (GetRemainingFileList(
371           &cancel_callback_,
372           drive_service_,
373           *resource_list,
374           base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
375                      weak_ptr_factory_.GetWeakPtr(), callback)))
376     return;
377
378   PopulateDatabase(callback);
379 }
380
381 void SyncEngineInitializer::PopulateDatabase(
382     const SyncStatusCallback& callback) {
383   DCHECK(sync_root_folder_);
384   metadata_database_->PopulateInitialData(
385       largest_change_id_,
386       *drive::util::ConvertResourceEntryToFileResource(
387           *sync_root_folder_),
388       ConvertResourceEntriesToFileResources(app_root_folders_),
389       base::Bind(&SyncEngineInitializer::DidPopulateDatabase,
390                  weak_ptr_factory_.GetWeakPtr(),
391                  callback));
392 }
393
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);
402     return;
403   }
404
405   util::Log(logging::LOG_VERBOSE, FROM_HERE,
406             "[Initialize] Completed successfully.");
407   callback.Run(SYNC_STATUS_OK);
408 }
409
410 }  // namespace drive_backend
411 }  // namespace sync_file_system