Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(392)

Side by Side Diff: chrome/browser/chromeos/gdata/gdata_files.cc

Issue 10800092: Database support for GDataDirectoryService. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: nits Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/chromeos/gdata/gdata_files.h" 5 #include "chrome/browser/chromeos/gdata/gdata_files.h"
6 6
7 #include <leveldb/db.h>
7 #include <vector> 8 #include <vector>
8 9
9 #include "base/message_loop_proxy.h" 10 #include "base/message_loop_proxy.h"
10 #include "base/platform_file.h" 11 #include "base/platform_file.h"
12 #include "base/string_number_conversions.h"
11 #include "base/string_util.h" 13 #include "base/string_util.h"
12 #include "base/stringprintf.h" 14 #include "base/stringprintf.h"
15 #include "base/sequenced_task_runner.h"
13 #include "base/tracked_objects.h" 16 #include "base/tracked_objects.h"
14 #include "base/utf_string_conversions.h" 17 #include "base/utf_string_conversions.h"
15 #include "chrome/browser/chromeos/gdata/gdata.pb.h" 18 #include "chrome/browser/chromeos/gdata/gdata.pb.h"
16 #include "chrome/browser/chromeos/gdata/gdata_util.h" 19 #include "chrome/browser/chromeos/gdata/gdata_util.h"
17 #include "chrome/browser/chromeos/gdata/gdata_wapi_parser.h" 20 #include "chrome/browser/chromeos/gdata/gdata_wapi_parser.h"
21 #include "content/public/browser/browser_thread.h"
18 #include "net/base/escape.h" 22 #include "net/base/escape.h"
19 23
24 using content::BrowserThread;
25
20 namespace gdata { 26 namespace gdata {
21 namespace { 27 namespace {
22 28
23 const char kSlash[] = "/"; 29 const char kSlash[] = "/";
24 const char kEscapedSlash[] = "\xE2\x88\x95"; 30 const char kEscapedSlash[] = "\xE2\x88\x95";
31 const char kDBKeyLargestChangestamp[] = "m:largest_changestamp";
satorux1 2012/08/03 20:19:16 Maybe add // m: prefix is used for global metadat
achuithb 2012/08/03 21:41:04 Done.
32 const char kDBKeyVersion[] = "m:version";
33 const char kDBKeyResourceIdPrefix[] = "r:";
25 34
26 // Extracts resource_id out of edit url. 35 // Extracts resource_id out of edit url.
27 std::string ExtractResourceId(const GURL& url) { 36 std::string ExtractResourceId(const GURL& url) {
28 return net::UnescapeURLComponent(url.ExtractFileName(), 37 return net::UnescapeURLComponent(url.ExtractFileName(),
29 net::UnescapeRule::URL_SPECIAL_CHARS); 38 net::UnescapeRule::URL_SPECIAL_CHARS);
30 } 39 }
31 40
32 // Returns true if |proto| is a valid proto as the root directory. 41 // Returns true if |proto| is a valid proto as the root directory.
33 // Used to reject incompatible proto. 42 // Used to reject incompatible proto.
34 bool IsValidRootDirectoryProto(const GDataDirectoryProto& proto) { 43 bool IsValidRootDirectoryProto(const GDataDirectoryProto& proto) {
(...skipping 381 matching lines...) Expand 10 before | Expand all | Expand 10 after
416 GDataDirectory* dir = iter->second; 425 GDataDirectory* dir = iter->second;
417 // Remove directories recursively. 426 // Remove directories recursively.
418 dir->RemoveChildren(); 427 dir->RemoveChildren();
419 if (directory_service_) 428 if (directory_service_)
420 directory_service_->RemoveEntryFromResourceMap(dir); 429 directory_service_->RemoveEntryFromResourceMap(dir);
421 } 430 }
422 STLDeleteValues(&child_directories_); 431 STLDeleteValues(&child_directories_);
423 child_directories_.clear(); 432 child_directories_.clear();
424 } 433 }
425 434
435 // ResourceMetadataDB implementation.
436
437 // Params for GDatadirectoryServiceDB::Create.
438 struct CreateDBParams {
439 CreateDBParams(const FilePath& db_path,
440 base::SequencedTaskRunner* blocking_task_runner)
441 : db_path(db_path),
442 blocking_task_runner(blocking_task_runner) {
443 }
444
445 FilePath db_path;
446 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
447 scoped_ptr<ResourceMetadataDB> db;
448 GDataDirectoryService::SerializedMap serialized_resources;
449 };
450
451 // Wrapper for level db. All methods must be called on blocking thread.
452 class ResourceMetadataDB {
453 public:
454 ResourceMetadataDB(const FilePath& db_path,
455 base::SequencedTaskRunner* blocking_task_runner);
456
457 // Initializes the database.
458 void Init();
459
460 // Reads the database into |serialized_resources|.
461 void Read(GDataDirectoryService::SerializedMap* serialized_resources);
462
463 // Saves |serialized_resources| to the database.
464 void Save(const GDataDirectoryService::SerializedMap& serialized_resources);
465
466 private:
467 // Clears the database.
468 void Clear();
469
470 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
471 scoped_ptr<leveldb::DB> level_db_;
472 FilePath db_path_;
473 };
474
475 ResourceMetadataDB::ResourceMetadataDB(const FilePath& db_path,
476 base::SequencedTaskRunner* blocking_task_runner)
477 : blocking_task_runner_(blocking_task_runner),
478 db_path_(db_path) {
479 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
480 }
481
482 // Creates, initializes and reads from the database.
483 // This must be defined after ResourceMetadataDB and CreateDBParams.
484 static void CreateResourceMetadataDBOnBlockingPool(
485 CreateDBParams* params) {
486 DCHECK(params->blocking_task_runner->RunsTasksOnCurrentThread());
487 DCHECK(!params->db_path.empty());
488
489 params->db.reset(new ResourceMetadataDB(params->db_path,
490 params->blocking_task_runner));
491 params->db->Init();
492 params->db->Read(&params->serialized_resources);
493 }
494
495 void ResourceMetadataDB::Init() {
496 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
497 DCHECK(!db_path_.empty());
498
499 DVLOG(1) << "Init " << db_path_.value();
500
501 leveldb::DB* level_db = NULL;
502 leveldb::Options options;
503 options.create_if_missing = true;
504 leveldb::Status db_status = leveldb::DB::Open(options, db_path_.value(),
505 &level_db);
506 DCHECK(level_db);
507 DCHECK(db_status.ok());
508 level_db_.reset(level_db);
509 }
510
511 void ResourceMetadataDB::Read(
512 GDataDirectoryService::SerializedMap* serialized_resources) {
513 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
514 DCHECK(serialized_resources);
515 DVLOG(1) << "Read " << db_path_.value();
516
517 scoped_ptr<leveldb::Iterator> iter(level_db_->NewIterator(
518 leveldb::ReadOptions()));
519 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
520 DVLOG(1) << "Read, resource " << iter->key().ToString();
521 serialized_resources->insert(std::make_pair(iter->key().ToString(),
522 iter->value().ToString()));
523 }
524 }
525
526 void ResourceMetadataDB::Save(
527 const GDataDirectoryService::SerializedMap& serialized_resources) {
528 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
529
530 Clear();
531 for (GDataDirectoryService::SerializedMap::const_iterator iter =
532 serialized_resources.begin();
533 iter != serialized_resources.end(); ++iter) {
534 DVLOG(1) << "Saving resource " << iter->first << " to db";
535 leveldb::Status status = level_db_->Put(leveldb::WriteOptions(),
536 leveldb::Slice(iter->first),
537 leveldb::Slice(iter->second));
538 if (!status.ok()) {
539 LOG(ERROR) << "leveldb Put failed of " << iter->first
540 << ", with " << status.ToString();
541 NOTREACHED();
542 }
543 }
544 }
545
546 void ResourceMetadataDB::Clear() {
547 level_db_.reset();
548 leveldb::DestroyDB(db_path_.value(), leveldb::Options());
549 Init();
550 }
551
426 // GDataDirectoryService class implementation. 552 // GDataDirectoryService class implementation.
427 553
428 GDataDirectoryService::GDataDirectoryService() 554 GDataDirectoryService::GDataDirectoryService()
429 : serialized_size_(0), 555 : blocking_task_runner_(NULL),
556 serialized_size_(0),
430 largest_changestamp_(0), 557 largest_changestamp_(0),
431 origin_(UNINITIALIZED) { 558 origin_(UNINITIALIZED),
559 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
432 root_.reset(new GDataDirectory(NULL, this)); 560 root_.reset(new GDataDirectory(NULL, this));
433 root_->set_title(kGDataRootDirectory); 561 root_->set_title(kGDataRootDirectory);
434 root_->SetBaseNameFromTitle(); 562 root_->SetBaseNameFromTitle();
435 root_->set_resource_id(kGDataRootDirectoryResourceId); 563 root_->set_resource_id(kGDataRootDirectoryResourceId);
436 AddEntryToResourceMap(root_.get()); 564 AddEntryToResourceMap(root_.get());
437 } 565 }
438 566
439 GDataDirectoryService::~GDataDirectoryService() { 567 GDataDirectoryService::~GDataDirectoryService() {
568 ClearRoot();
569
570 // Ensure db is closed on the blocking pool.
571 if (blocking_task_runner_ && directory_service_db_.get())
572 blocking_task_runner_->DeleteSoon(FROM_HERE,
573 directory_service_db_.release());
574 }
575
576 void GDataDirectoryService::ClearRoot() {
440 // Note that children have a reference to root_, 577 // Note that children have a reference to root_,
441 // so we need to delete them here. 578 // so we need to delete them here.
442 root_->RemoveChildren(); 579 root_->RemoveChildren();
443 RemoveEntryFromResourceMap(root_.get()); 580 RemoveEntryFromResourceMap(root_.get());
444 DCHECK(resource_map_.empty()); 581 DCHECK(resource_map_.empty());
445 resource_map_.clear(); 582 resource_map_.clear();
583 root_.reset();
446 } 584 }
447 585
448 void GDataDirectoryService::AddEntryToDirectory( 586 void GDataDirectoryService::AddEntryToDirectory(
449 const FilePath& directory_path, 587 const FilePath& directory_path,
450 GDataEntry* entry, 588 GDataEntry* entry,
451 const FileOperationCallback& callback) { 589 const FileOperationCallback& callback) {
452 GDataEntry* destination = FindEntryByPathSync(directory_path); 590 GDataEntry* destination = FindEntryByPathSync(directory_path);
453 GDataFileError error = GDATA_FILE_ERROR_FAILED; 591 GDataFileError error = GDATA_FILE_ERROR_FAILED;
454 if (!destination) { 592 if (!destination) {
455 error = GDATA_FILE_ERROR_NOT_FOUND; 593 error = GDATA_FILE_ERROR_NOT_FOUND;
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
554 GDataDirectory* entry_parent = old_entry ? old_entry->parent() : NULL; 692 GDataDirectory* entry_parent = old_entry ? old_entry->parent() : NULL;
555 if (entry_parent) { 693 if (entry_parent) {
556 DCHECK_EQ(fresh_file->resource_id(), old_entry->resource_id()); 694 DCHECK_EQ(fresh_file->resource_id(), old_entry->resource_id());
557 DCHECK(old_entry->AsGDataFile()); 695 DCHECK(old_entry->AsGDataFile());
558 696
559 entry_parent->RemoveEntry(old_entry); 697 entry_parent->RemoveEntry(old_entry);
560 entry_parent->AddEntry(fresh_file.release()); 698 entry_parent->AddEntry(fresh_file.release());
561 } 699 }
562 } 700 }
563 701
702 void GDataDirectoryService::InitFromDB(
703 const FilePath& db_path,
704 base::SequencedTaskRunner* blocking_task_runner,
705 LoadRootFeedParams* load_params,
706 const base::Closure& callback) {
707 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
708 DCHECK(!db_path.empty());
709 DCHECK(blocking_task_runner);
710
711 if (directory_service_db_.get()) {
712 NOTREACHED();
713 if (!callback.is_null())
714 callback.Run();
715 }
716
717 blocking_task_runner_ = blocking_task_runner;
718
719 DVLOG(1) << "InitFromDB " << db_path.value();
720
721 CreateDBParams* create_params =
722 new CreateDBParams(db_path, blocking_task_runner);
723 blocking_task_runner_->PostTaskAndReply(
724 FROM_HERE,
725 base::Bind(&CreateResourceMetadataDBOnBlockingPool,
726 create_params),
727 base::Bind(&GDataDirectoryService::InitResourceMap,
728 weak_ptr_factory_.GetWeakPtr(),
729 base::Owned(create_params),
730 load_params,
731 callback));
732 }
733
734 void GDataDirectoryService::InitResourceMap(
735 CreateDBParams* create_params,
736 LoadRootFeedParams* load_params,
737 const base::Closure& callback) {
738 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
739 DCHECK(create_params);
740 // Ensure this callback is run before we return.
741 base::ScopedClosureRunner callback_runner(callback);
742
743 SerializedMap* serialized_resources = &create_params->serialized_resources;
744 directory_service_db_ = create_params->db.Pass();
745 if (serialized_resources->empty()) {
746 origin_ = INITIALIZING;
747 if (load_params)
748 load_params->load_error = GDATA_FILE_ERROR_NOT_FOUND;
749 return;
750 }
751
752 ClearRoot();
753
754 // Version check.
755 int32 version = 0;
756 SerializedMap::iterator iter = serialized_resources->find(kDBKeyVersion);
757 if (iter == serialized_resources->end() ||
758 !base::StringToInt(iter->second, &version) ||
759 version != kProtoVersion) {
760 if (load_params)
satorux1 2012/08/03 20:19:16 can it be NULL? Can we add DCHECK(load_params) at
achuithb 2012/08/03 21:41:04 It can be NULL for tests. I'm going to change th
761 load_params->load_error = GDATA_FILE_ERROR_NOT_FOUND;
satorux1 2012/08/03 20:19:16 maybe GDATA_FILE_ERROR_FAILED?
achuithb 2012/08/03 21:41:04 Done.
762 return;
763 }
764 serialized_resources->erase(iter);
765
766 // Get the largest changestamp.
767 iter = serialized_resources->find(kDBKeyLargestChangestamp);
768 if (iter != serialized_resources->end()) {
769 base::StringToInt(iter->second, &largest_changestamp_);
770 DVLOG(1) << "InitResourceMap largest_changestamp_"
771 << largest_changestamp_;
772 serialized_resources->erase(iter);
773 } else {
774 NOTREACHED() << "Could not find largest_changestamp";
satorux1 2012/08/03 20:19:16 Add this? load_params->load_error = GDATA_FILE_ER
achuithb 2012/08/03 21:41:04 Done.
775 }
776
777 ResourceMap resource_map;
778 for (SerializedMap::const_iterator iter = serialized_resources->begin();
779 iter != serialized_resources->end(); ++iter) {
780 if (iter->first.find(kDBKeyResourceIdPrefix) != 0) {
781 NOTREACHED() << "Incorrect prefix for db key " << iter->first;
782 continue;
783 }
784
785 const std::string resource_id =
786 iter->first.substr(strlen(kDBKeyResourceIdPrefix));
787 scoped_ptr<GDataEntry> entry = FromProtoString(iter->second);
788 if (entry.get()) {
789 DVLOG(1) << "Inserting resource " << resource_id
790 << " into resource_map";
791 resource_map.insert(std::make_pair(resource_id, entry.release()));
792 } else {
793 NOTREACHED() << "Failed to parse GDataEntry for resource " << resource_id;
794 }
795 }
796
797 // Fix up parent-child relations.
798 for (ResourceMap::iterator iter = resource_map.begin();
799 iter != resource_map.end(); ++iter) {
800 GDataEntry* entry = iter->second;
801 ResourceMap::iterator parent_it =
802 resource_map.find(entry->parent_resource_id());
803 if (parent_it != resource_map.end()) {
804 GDataDirectory* parent = parent_it->second->AsGDataDirectory();
805 if (parent) {
806 DVLOG(1) << "Adding " << entry->resource_id()
807 << " as a child of " << parent->resource_id();
808 parent->AddEntry(entry);
809 } else {
810 NOTREACHED() << "Parent is not a directory " << parent->resource_id();
811 }
812 } else if (entry->resource_id() == kGDataRootDirectoryResourceId) {
813 root_.reset(entry->AsGDataDirectory());
814 DCHECK(root_.get());
815 AddEntryToResourceMap(root_.get());
816 } else {
817 NOTREACHED() << "Missing parent id " << entry->parent_resource_id()
818 << " for resource " << entry->resource_id();
819 }
820 }
821
822 DCHECK(root_.get());
823 DCHECK_EQ(resource_map.size(), resource_map_.size());
824 DCHECK_EQ(resource_map.size(), serialized_resources->size());
825
826 origin_ = FROM_CACHE;
827 }
828
829 void GDataDirectoryService::SaveToDB() {
830 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
831
832 if (!blocking_task_runner_ || !directory_service_db_.get()) {
833 NOTREACHED();
834 return;
835 }
836
837 size_t serialized_size = 0;
838 SerializedMap serialized_resources;
839 for (ResourceMap::const_iterator iter = resource_map_.begin();
840 iter != resource_map_.end(); ++iter) {
841 GDataEntryProto proto;
842 iter->second->ToProtoFull(&proto);
843 std::string serialized_string;
844 const bool ok = proto.SerializeToString(&serialized_string);
845 DCHECK(ok);
846 if (ok) {
847 serialized_resources.insert(
848 std::make_pair(std::string(kDBKeyResourceIdPrefix) + iter->first,
849 serialized_string));
850 serialized_size += serialized_string.size();
851 }
852 }
853
854 serialized_resources.insert(std::make_pair(kDBKeyVersion,
855 base::IntToString(kProtoVersion)));
856 serialized_resources.insert(std::make_pair(kDBKeyLargestChangestamp,
857 base::IntToString(largest_changestamp_)));
858 set_last_serialized(base::Time::Now());
859 set_serialized_size(serialized_size);
860
861 blocking_task_runner_->PostTask(
862 FROM_HERE,
863 base::Bind(&ResourceMetadataDB::Save,
864 base::Unretained(directory_service_db_.get()),
865 serialized_resources));
866 }
867
564 // Convert to/from proto. 868 // Convert to/from proto.
565 869
566 // static 870 // static
567 void GDataEntry::ConvertProtoToPlatformFileInfo( 871 void GDataEntry::ConvertProtoToPlatformFileInfo(
568 const PlatformFileInfoProto& proto, 872 const PlatformFileInfoProto& proto,
569 base::PlatformFileInfo* file_info) { 873 base::PlatformFileInfo* file_info) {
570 file_info->size = proto.size(); 874 file_info->size = proto.size();
571 file_info->is_directory = proto.is_directory(); 875 file_info->is_directory = proto.is_directory();
572 file_info->is_symbolic_link = proto.is_symbolic_link(); 876 file_info->is_symbolic_link = proto.is_symbolic_link();
573 file_info->last_modified = base::Time::FromInternalValue( 877 file_info->last_modified = base::Time::FromInternalValue(
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after
788 1092
789 if (!root_->FromProto(proto.gdata_directory())) 1093 if (!root_->FromProto(proto.gdata_directory()))
790 return false; 1094 return false;
791 1095
792 origin_ = FROM_CACHE; 1096 origin_ = FROM_CACHE;
793 largest_changestamp_ = proto.largest_changestamp(); 1097 largest_changestamp_ = proto.largest_changestamp();
794 1098
795 return true; 1099 return true;
796 } 1100 }
797 1101
1102 scoped_ptr<GDataEntry> GDataDirectoryService::FromProtoString(
1103 const std::string& serialized_proto) {
1104 GDataEntryProto entry_proto;
1105 if (!entry_proto.ParseFromString(serialized_proto))
1106 return scoped_ptr<GDataEntry>();
1107
1108 scoped_ptr<GDataEntry> entry;
1109 if (entry_proto.file_info().is_directory()) {
1110 entry.reset(new GDataDirectory(NULL, this));
1111 // Call GDataEntry::FromProto instead of GDataDirectory::FromProto because
1112 // the proto does not include children.
1113 if (!entry->FromProto(entry_proto)) {
1114 NOTREACHED() << "FromProto (directory) failed";
1115 entry.reset();
1116 }
1117 } else {
1118 scoped_ptr<GDataFile> file(new GDataFile(NULL, this));
1119 // Call GDataFile::FromProto.
1120 if (file->FromProto(entry_proto)) {
1121 entry.reset(file.release());
1122 } else {
1123 NOTREACHED() << "FromProto (file) failed";
1124 }
1125 }
1126 return entry.Pass();
1127 }
1128
798 } // namespace gdata 1129 } // namespace gdata
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/gdata/gdata_files.h ('k') | chrome/browser/chromeos/gdata/gdata_files_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698