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

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: minor 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";
25 31
26 // Extracts resource_id out of edit url. 32 // Extracts resource_id out of edit url.
27 std::string ExtractResourceId(const GURL& url) { 33 std::string ExtractResourceId(const GURL& url) {
28 return net::UnescapeURLComponent(url.ExtractFileName(), 34 return net::UnescapeURLComponent(url.ExtractFileName(),
29 net::UnescapeRule::URL_SPECIAL_CHARS); 35 net::UnescapeRule::URL_SPECIAL_CHARS);
(...skipping 386 matching lines...) Expand 10 before | Expand all | Expand 10 after
416 GDataDirectory* dir = iter->second; 422 GDataDirectory* dir = iter->second;
417 // Remove directories recursively. 423 // Remove directories recursively.
418 dir->RemoveChildren(); 424 dir->RemoveChildren();
419 if (directory_service_) 425 if (directory_service_)
420 directory_service_->RemoveEntryFromResourceMap(dir); 426 directory_service_->RemoveEntryFromResourceMap(dir);
421 } 427 }
422 STLDeleteValues(&child_directories_); 428 STLDeleteValues(&child_directories_);
423 child_directories_.clear(); 429 child_directories_.clear();
424 } 430 }
425 431
432 // GDataDirectoryServiceDB implementation.
433
434 // Params for GDatadirectoryServiceDB::Create.
435 struct CreateDBParams {
436 CreateDBParams(const FilePath& db_path,
437 base::SequencedTaskRunner* blocking_task_runner)
438 : db_path(db_path),
439 blocking_task_runner(blocking_task_runner) {
440 }
441
442 FilePath db_path;
443 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
444 scoped_ptr<GDataDirectoryServiceDB> db;
445 GDataDirectoryService::SerializedMap serialized_resources;
446 };
447
448 // Wrapper for level db. All methods must be called on blocking thread.
449 class GDataDirectoryServiceDB {
satorux1 2012/08/03 06:32:50 From this name, I originally thought this is a sub
achuithb 2012/08/03 19:15:59 I like ResourceMetadataDB. I've done the rename.
450 public:
451 GDataDirectoryServiceDB(const FilePath& db_path,
452 base::SequencedTaskRunner* blocking_task_runner);
453
454 // Initializes the database.
455 void Init();
456
457 // Reads the database into |serialized_resources|.
458 void Read(GDataDirectoryService::SerializedMap* serialized_resources);
459
460 // Saves |serialized_resources| to the database.
461 void Save(const GDataDirectoryService::SerializedMap& serialized_resources);
462
463 private:
464 // Clears the database.
465 void Clear();
466
467 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
468 scoped_ptr<leveldb::DB> level_db_;
469 FilePath db_path_;
470 };
471
472 GDataDirectoryServiceDB::GDataDirectoryServiceDB(const FilePath& db_path,
473 base::SequencedTaskRunner* blocking_task_runner)
474 : blocking_task_runner_(blocking_task_runner),
475 db_path_(db_path) {
476 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
477 }
478
479 // Creates, initializes and reads from the database.
480 // This must be defined after GDataDirectoryServiceDB and CreateDBParams.
481 static void CreateGDataDirectoryServiceDBOnBlockingPool(
482 CreateDBParams* params) {
483 DCHECK(params->blocking_task_runner->RunsTasksOnCurrentThread());
484 DCHECK(!params->db_path.empty());
485
486 params->db.reset(new GDataDirectoryServiceDB(params->db_path,
487 params->blocking_task_runner));
488 params->db->Init();
489 params->db->Read(&params->serialized_resources);
490 }
491
492 void GDataDirectoryServiceDB::Init() {
493 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
494 DCHECK(!db_path_.empty());
495
496 DVLOG(1) << "Init " << db_path_.value();
497
498 leveldb::DB* level_db = NULL;
499 leveldb::Options options;
500 options.create_if_missing = true;
501 leveldb::Status db_status = leveldb::DB::Open(options, db_path_.value(),
502 &level_db);
503 DCHECK(level_db);
504 DCHECK(db_status.ok());
505 level_db_.reset(level_db);
506 }
507
508 void GDataDirectoryServiceDB::Read(
509 GDataDirectoryService::SerializedMap* serialized_resources) {
510 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
511 DCHECK(serialized_resources);
512 DVLOG(1) << "Read " << db_path_.value();
513
514 scoped_ptr<leveldb::Iterator> iter(level_db_->NewIterator(
515 leveldb::ReadOptions()));
516 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
517 DVLOG(1) << "Read, resource " << iter->key().ToString();
518 serialized_resources->insert(std::make_pair(iter->key().ToString(),
519 iter->value().ToString()));
520 }
521 }
522
523 void GDataDirectoryServiceDB::Save(
524 const GDataDirectoryService::SerializedMap& serialized_resources) {
525 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
526
527 Clear();
528 for (GDataDirectoryService::SerializedMap::const_iterator iter =
529 serialized_resources.begin();
530 iter != serialized_resources.end(); ++iter) {
531 DVLOG(1) << "Saving resource " << iter->first << " to db";
532 level_db_->Put(leveldb::WriteOptions(),
satorux1 2012/08/03 06:32:50 What if Put fails? Shouldn't we check the return v
achuithb 2012/08/03 19:15:59 I've added a LOG(error), but I'm not sure what els
533 leveldb::Slice(iter->first),
534 leveldb::Slice(iter->second));
535 }
536 }
537
538 void GDataDirectoryServiceDB::Clear() {
539 level_db_.reset();
540 leveldb::DestroyDB(db_path_.value(), leveldb::Options());
541 Init();
542 }
543
426 // GDataDirectoryService class implementation. 544 // GDataDirectoryService class implementation.
427 545
428 GDataDirectoryService::GDataDirectoryService() 546 GDataDirectoryService::GDataDirectoryService()
429 : serialized_size_(0), 547 : blocking_task_runner_(NULL),
548 serialized_size_(0),
430 largest_changestamp_(0), 549 largest_changestamp_(0),
431 origin_(UNINITIALIZED) { 550 origin_(UNINITIALIZED),
551 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
432 root_.reset(new GDataDirectory(NULL, this)); 552 root_.reset(new GDataDirectory(NULL, this));
433 root_->set_title(kGDataRootDirectory); 553 root_->set_title(kGDataRootDirectory);
434 root_->SetBaseNameFromTitle(); 554 root_->SetBaseNameFromTitle();
435 root_->set_resource_id(kGDataRootDirectoryResourceId); 555 root_->set_resource_id(kGDataRootDirectoryResourceId);
436 AddEntryToResourceMap(root_.get()); 556 AddEntryToResourceMap(root_.get());
437 } 557 }
438 558
439 GDataDirectoryService::~GDataDirectoryService() { 559 GDataDirectoryService::~GDataDirectoryService() {
560 ClearRoot();
561
562 // Ensure db is closed on the blocking pool.
563 if (blocking_task_runner_ && directory_service_db_.get())
564 blocking_task_runner_->PostTask(FROM_HERE,
satorux1 2012/08/03 06:32:50 I think you can use DelteSoon() instead
achuithb 2012/08/03 19:15:59 Done.
565 base::Bind(&base::DeletePointer<GDataDirectoryServiceDB>,
566 directory_service_db_.release()));
567 }
568
569 void GDataDirectoryService::ClearRoot() {
440 // Note that children have a reference to root_, 570 // Note that children have a reference to root_,
441 // so we need to delete them here. 571 // so we need to delete them here.
442 root_->RemoveChildren(); 572 root_->RemoveChildren();
443 RemoveEntryFromResourceMap(root_.get()); 573 RemoveEntryFromResourceMap(root_.get());
444 DCHECK(resource_map_.empty()); 574 DCHECK(resource_map_.empty());
445 resource_map_.clear(); 575 resource_map_.clear();
576 root_.reset();
446 } 577 }
447 578
448 void GDataDirectoryService::AddEntryToDirectory( 579 void GDataDirectoryService::AddEntryToDirectory(
449 const FilePath& directory_path, 580 const FilePath& directory_path,
450 GDataEntry* entry, 581 GDataEntry* entry,
451 const FileOperationCallback& callback) { 582 const FileOperationCallback& callback) {
452 GDataEntry* destination = FindEntryByPathSync(directory_path); 583 GDataEntry* destination = FindEntryByPathSync(directory_path);
453 GDataFileError error = GDATA_FILE_ERROR_FAILED; 584 GDataFileError error = GDATA_FILE_ERROR_FAILED;
454 if (!destination) { 585 if (!destination) {
455 error = GDATA_FILE_ERROR_NOT_FOUND; 586 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; 685 GDataDirectory* entry_parent = old_entry ? old_entry->parent() : NULL;
555 if (entry_parent) { 686 if (entry_parent) {
556 DCHECK_EQ(fresh_file->resource_id(), old_entry->resource_id()); 687 DCHECK_EQ(fresh_file->resource_id(), old_entry->resource_id());
557 DCHECK(old_entry->AsGDataFile()); 688 DCHECK(old_entry->AsGDataFile());
558 689
559 entry_parent->RemoveEntry(old_entry); 690 entry_parent->RemoveEntry(old_entry);
560 entry_parent->AddEntry(fresh_file.release()); 691 entry_parent->AddEntry(fresh_file.release());
561 } 692 }
562 } 693 }
563 694
695 void GDataDirectoryService::InitFromDB(
696 const FilePath& db_path,
697 base::SequencedTaskRunner* blocking_task_runner,
698 LoadRootFeedParams* load_params,
699 const base::Closure& callback) {
700 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
701 DCHECK(!db_path.empty());
702 DCHECK(blocking_task_runner);
703
704 if (directory_service_db_.get()) {
705 NOTREACHED();
706 if (!callback.is_null())
707 callback.Run();
708 }
709
710 blocking_task_runner_ = blocking_task_runner;
711
712 DVLOG(1) << "InitFromDB " << db_path.value();
713
714 CreateDBParams* create_params =
715 new CreateDBParams(db_path, blocking_task_runner);
716 blocking_task_runner_->PostTaskAndReply(
717 FROM_HERE,
718 base::Bind(&CreateGDataDirectoryServiceDBOnBlockingPool,
719 create_params),
720 base::Bind(&GDataDirectoryService::InitResourceMap,
721 weak_ptr_factory_.GetWeakPtr(),
722 base::Owned(create_params),
723 load_params,
724 callback));
725 }
726
727 void GDataDirectoryService::InitResourceMap(
728 CreateDBParams* create_params,
729 LoadRootFeedParams* load_params,
730 const base::Closure& callback) {
731 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
732 DCHECK(create_params);
733 // Ensure this callback is run before we return.
734 base::ScopedClosureRunner callback_runner(callback);
735
736 directory_service_db_ = create_params->db.Pass();
737 if (create_params->serialized_resources.empty()) {
738 origin_ = INITIALIZING;
739 if (load_params)
740 load_params->load_error = GDATA_FILE_ERROR_NOT_FOUND;
741 return;
742 }
743
744 ClearRoot();
745
746 int32 version = 0;
747 ResourceMap resource_map;
748 for (SerializedMap::const_iterator iter =
749 create_params->serialized_resources.begin();
satorux1 2012/08/03 06:32:50 indentation
achuithb 2012/08/03 19:15:59 Done.
750 iter != create_params->serialized_resources.end(); ++iter) {
751 if (iter->first == "version") {
752 if (!base::StringToInt(iter->second, &version) ||
753 version != kProtoVersion) {
satorux1 2012/08/03 06:32:50 shouldn't we set the load_error to GDATA_FILE_ERRO
achuithb 2012/08/03 19:15:59 Done.
754 return;
755 }
756 continue;
757 }
758
759 if (iter->first == "largest_changestamp") {
760 base::StringToInt(iter->second, &largest_changestamp_);
761 DVLOG(1) << "InitResourceMap largest_changestamp_"
762 << largest_changestamp_;
satorux1 2012/08/03 06:32:50 ditto. load_error?
achuithb 2012/08/03 19:15:59 I think this is an error but it's not fatal, right
763 continue;
764 }
765
766 scoped_ptr<GDataEntry> entry = FromProtoString(iter->second);
767 if (entry.get()) {
768 DVLOG(1) << "Inserting resource " << iter->first
769 << " into resource_map";
770 resource_map.insert(std::make_pair(iter->first, entry.release()));
771 } else {
772 NOTREACHED() << "Failed to parse GDataEntry for resource "
773 << iter->first;
774 }
775 }
776
777 // Fix up parent-child relations.
778 for (ResourceMap::iterator iter = resource_map.begin();
779 iter != resource_map.end(); ++iter) {
780 GDataEntry* entry = iter->second;
781 ResourceMap::iterator parent_it =
782 resource_map.find(entry->parent_resource_id());
783 if (parent_it != resource_map.end()) {
784 GDataDirectory* parent = parent_it->second->AsGDataDirectory();
785 if (parent) {
786 DVLOG(1) << "Adding " << entry->resource_id()
787 << " as a child of " << parent->resource_id();
788 parent->AddEntry(entry);
789 } else {
790 NOTREACHED() << "Parent is not a directory " << parent->resource_id();
791 }
792 } else if (entry->resource_id() == kGDataRootDirectoryResourceId) {
793 root_.reset(entry->AsGDataDirectory());
794 DCHECK(root_.get());
795 AddEntryToResourceMap(root_.get());
796 } else {
797 NOTREACHED() << "Missing parent id " << entry->parent_resource_id()
798 << " for resource " << entry->resource_id();
799 }
800 }
801
802 DCHECK(root_.get());
803 DCHECK_EQ(resource_map.size(), resource_map_.size());
804 DCHECK_EQ(resource_map.size(),
805 create_params->serialized_resources.size() - 2);
806
807 origin_ = FROM_CACHE;
808 }
809
810 void GDataDirectoryService::SaveToDB() {
811 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
812
813 if (!blocking_task_runner_ || !directory_service_db_.get()) {
814 NOTREACHED();
815 return;
816 }
817
818 size_t serialized_size = 0;
819 SerializedMap serialized_resources;
820 for (ResourceMap::const_iterator iter = resource_map_.begin();
821 iter != resource_map_.end(); ++iter) {
822 GDataEntryProto proto;
823 iter->second->ToProtoFull(&proto);
824 std::string serialized_string;
825 const bool ok = proto.SerializeToString(&serialized_string);
826 DCHECK(ok);
827 if (ok) {
828 serialized_resources.insert(
829 std::make_pair(iter->first, serialized_string));
830 serialized_size += serialized_string.size();
831 }
832 }
833
834 serialized_resources.insert(std::make_pair("version",
835 base::IntToString(kProtoVersion)));
836 serialized_resources.insert(std::make_pair("largest_changestamp",
satorux1 2012/08/03 06:32:50 Let's define constants for "version" and "largest_
achuithb 2012/08/03 19:15:59 Done.
837 base::IntToString(largest_changestamp_)));
838 set_last_serialized(base::Time::Now());
839 set_serialized_size(serialized_size);
840
841 blocking_task_runner_->PostTask(
842 FROM_HERE,
843 base::Bind(&GDataDirectoryServiceDB::Save,
844 base::Unretained(directory_service_db_.get()),
845 serialized_resources));
846 }
847
564 // Convert to/from proto. 848 // Convert to/from proto.
565 849
566 // static 850 // static
567 void GDataEntry::ConvertProtoToPlatformFileInfo( 851 void GDataEntry::ConvertProtoToPlatformFileInfo(
568 const PlatformFileInfoProto& proto, 852 const PlatformFileInfoProto& proto,
569 base::PlatformFileInfo* file_info) { 853 base::PlatformFileInfo* file_info) {
570 file_info->size = proto.size(); 854 file_info->size = proto.size();
571 file_info->is_directory = proto.is_directory(); 855 file_info->is_directory = proto.is_directory();
572 file_info->is_symbolic_link = proto.is_symbolic_link(); 856 file_info->is_symbolic_link = proto.is_symbolic_link();
573 file_info->last_modified = base::Time::FromInternalValue( 857 file_info->last_modified = base::Time::FromInternalValue(
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after
788 1072
789 if (!root_->FromProto(proto.gdata_directory())) 1073 if (!root_->FromProto(proto.gdata_directory()))
790 return false; 1074 return false;
791 1075
792 origin_ = FROM_CACHE; 1076 origin_ = FROM_CACHE;
793 largest_changestamp_ = proto.largest_changestamp(); 1077 largest_changestamp_ = proto.largest_changestamp();
794 1078
795 return true; 1079 return true;
796 } 1080 }
797 1081
1082 scoped_ptr<GDataEntry> GDataDirectoryService::FromProtoString(
1083 const std::string& serialized_proto) {
1084 scoped_ptr<GDataEntry> entry;
1085
1086 GDataEntryProto entry_proto;
1087 if (!entry_proto.ParseFromString(serialized_proto))
1088 return entry.Pass();
hashimoto 2012/08/03 05:17:07 Why don't you simply pass scoped_ptr<GDataEntry>()
satorux1 2012/08/03 06:32:50 Agreed with hashimoto. That'll be clearer
achuithb 2012/08/03 19:15:59 Done.
1089
1090 if (entry_proto.file_info().is_directory()) {
satorux1 2012/08/03 06:32:50 move GDataEntryProto entry_proto; right before 'if
achuithb 2012/08/03 19:15:59 I don't understand? It's needed for ParseFromStrin
satorux1 2012/08/03 20:19:16 my bad. i was wrong.
1091 entry.reset(new GDataDirectory(NULL, this));
1092 // Call GDataEntry::FromProto.
satorux1 2012/08/03 06:32:50 Please add some more comment that why GDtaDirector
achuithb 2012/08/03 19:15:59 Correct. Comment added.
1093 if (!entry->FromProto(entry_proto)) {
1094 NOTREACHED() << "FromProto (directory) failed";
1095 entry.reset();
1096 }
1097 } else {
1098 scoped_ptr<GDataFile> file(new GDataFile(NULL, this));
1099 // Call GDataFile::FromProto.
1100 if (file->FromProto(entry_proto)) {
1101 entry.reset(file.release());
1102 } else {
1103 NOTREACHED() << "FromProto (file) failed";
1104 }
1105 }
1106 return entry.Pass();
1107 }
1108
798 } // namespace gdata 1109 } // namespace gdata
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698