Index: chrome/browser/chromeos/gdata/gdata_files.cc |
=================================================================== |
--- chrome/browser/chromeos/gdata/gdata_files.cc (revision 149486) |
+++ chrome/browser/chromeos/gdata/gdata_files.cc (working copy) |
@@ -4,19 +4,25 @@ |
#include "chrome/browser/chromeos/gdata/gdata_files.h" |
+#include <leveldb/db.h> |
#include <vector> |
#include "base/message_loop_proxy.h" |
#include "base/platform_file.h" |
+#include "base/string_number_conversions.h" |
#include "base/string_util.h" |
#include "base/stringprintf.h" |
+#include "base/sequenced_task_runner.h" |
#include "base/tracked_objects.h" |
#include "base/utf_string_conversions.h" |
#include "chrome/browser/chromeos/gdata/gdata.pb.h" |
#include "chrome/browser/chromeos/gdata/gdata_util.h" |
#include "chrome/browser/chromeos/gdata/gdata_wapi_parser.h" |
+#include "content/public/browser/browser_thread.h" |
#include "net/base/escape.h" |
+using content::BrowserThread; |
+ |
namespace gdata { |
namespace { |
@@ -423,12 +429,130 @@ |
child_directories_.clear(); |
} |
+// GDataDirectoryServiceDB implementation. |
+ |
+// Params for GDatadirectoryServiceDB::Create. |
+struct CreateDBParams { |
+ CreateDBParams(const FilePath& db_path, |
+ base::SequencedTaskRunner* blocking_task_runner) |
+ : db_path(db_path), |
+ blocking_task_runner(blocking_task_runner) { |
+ } |
+ |
+ FilePath db_path; |
+ base::SequencedTaskRunner* blocking_task_runner; |
+ scoped_ptr<GDataDirectoryServiceDB> db; |
+ GDataDirectoryService::SerializedMap serialized_resources; |
+}; |
+ |
+// Wrapper for level db. All methods must be called on blocking thread. |
+class GDataDirectoryServiceDB { |
+ public: |
+ // Creates, initializes and reads from the database. |
+ static void Create(CreateDBParams* params); |
+ |
+ // Save |serialized_resources| to the database. |
satorux1
2012/08/02 17:24:03
Saves
achuithb
2012/08/02 20:26:25
Done.
|
+ void Save(const GDataDirectoryService::SerializedMap& serialized_resources); |
+ |
+ private: |
+ // Private ctor accessed through Create. |
+ GDataDirectoryServiceDB(const FilePath& db_path, |
+ base::SequencedTaskRunner* blocking_task_runner); |
+ |
+ // Initialize the database. |
satorux1
2012/08/02 17:24:03
Initializes
achuithb
2012/08/02 20:26:25
Done.
|
+ void Init(); |
+ |
+ // Read the database into |serialized_resources|. |
satorux1
2012/08/02 17:24:03
ditto
achuithb
2012/08/02 20:26:25
Done.
|
+ void Read(GDataDirectoryService::SerializedMap* serialized_resources); |
+ |
+ // Truncate the database. |
satorux1
2012/08/02 17:24:03
ditto
achuithb
2012/08/02 20:26:25
Done.
|
+ void Truncate(); |
satorux1
2012/08/02 17:24:03
Truncating a DB sounds rather awkward, as Truncate
achuithb
2012/08/02 20:26:25
Done.
|
+ |
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; |
+ scoped_ptr<leveldb::DB> level_db_; |
+ FilePath db_path_; |
+}; |
+ |
+GDataDirectoryServiceDB::GDataDirectoryServiceDB(const FilePath& db_path, |
+ base::SequencedTaskRunner* blocking_task_runner) |
+ : blocking_task_runner_(blocking_task_runner), |
+ db_path_(db_path) { |
+ DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); |
+} |
+ |
+// static |
+void GDataDirectoryServiceDB::Create(CreateDBParams* params) { |
satorux1
2012/08/02 17:24:03
I was confused about this function because of the
achuithb
2012/08/02 20:26:25
I renamed the function, but it needs to be defined
|
+ DCHECK(params->blocking_task_runner->RunsTasksOnCurrentThread()); |
+ DCHECK(!params->db_path.empty()); |
+ |
+ params->db.reset(new GDataDirectoryServiceDB(params->db_path, |
+ params->blocking_task_runner)); |
+ params->db->Init(); |
+ params->db->Read(¶ms->serialized_resources); |
+} |
+ |
+ |
+void GDataDirectoryServiceDB::Init() { |
+ DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); |
+ DCHECK(!db_path_.empty()); |
+ |
+ DVLOG(1) << "Init " << db_path_.value(); |
+ |
+ leveldb::DB* level_db = NULL; |
+ leveldb::Options options; |
+ options.create_if_missing = true; |
+ leveldb::Status db_status = leveldb::DB::Open(options, db_path_.value(), |
+ &level_db); |
+ DCHECK(level_db); |
+ DCHECK(db_status.ok()); |
+ level_db_.reset(level_db); |
+} |
+ |
+void GDataDirectoryServiceDB::Read( |
+ GDataDirectoryService::SerializedMap* serialized_resources) { |
+ DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); |
+ DVLOG(1) << "Read " << db_path_.value(); |
+ |
+ if (serialized_resources) { |
satorux1
2012/08/02 17:24:03
can this be null?
DCHECK(serialized_resources); i
achuithb
2012/08/02 20:26:25
Done.
|
+ scoped_ptr<leveldb::Iterator> iter(level_db_->NewIterator( |
+ leveldb::ReadOptions())); |
+ for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { |
+ DVLOG(1) << "Read, resource " << iter->key().ToString(); |
+ serialized_resources->insert(std::make_pair(iter->key().ToString(), |
+ iter->value().ToString())); |
+ } |
+ } |
+} |
+ |
+void GDataDirectoryServiceDB::Save( |
+ const GDataDirectoryService::SerializedMap& serialized_resources) { |
+ DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); |
+ |
+ Truncate(); |
+ for (GDataDirectoryService::SerializedMap::const_iterator iter = |
+ serialized_resources.begin(); |
+ iter != serialized_resources.end(); ++iter) { |
+ DVLOG(1) << "Saving resource " << iter->first << " to db"; |
+ level_db_->Put(leveldb::WriteOptions(), |
+ leveldb::Slice(iter->first), |
+ leveldb::Slice(iter->second)); |
+ } |
+} |
+ |
+void GDataDirectoryServiceDB::Truncate() { |
+ level_db_.reset(); |
+ leveldb::DestroyDB(db_path_.value(), leveldb::Options()); |
+ Init(); |
+} |
+ |
// GDataDirectoryService class implementation. |
GDataDirectoryService::GDataDirectoryService() |
- : serialized_size_(0), |
+ : blocking_task_runner_(NULL), |
+ serialized_size_(0), |
largest_changestamp_(0), |
- origin_(UNINITIALIZED) { |
+ origin_(UNINITIALIZED), |
+ ui_weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
root_.reset(new GDataDirectory(NULL, this)); |
root_->set_title(kGDataRootDirectory); |
root_->SetBaseNameFromTitle(); |
@@ -437,12 +561,23 @@ |
} |
GDataDirectoryService::~GDataDirectoryService() { |
+ ClearRoot(); |
+ |
+ // Ensure db is closed on the blocking pool. |
+ if (blocking_task_runner_ && directory_service_db_.get()) |
+ blocking_task_runner_->PostTask(FROM_HERE, |
+ base::Bind(&base::DeletePointer<GDataDirectoryServiceDB>, |
+ directory_service_db_.release())); |
+} |
+ |
+void GDataDirectoryService::ClearRoot() { |
// Note that children have a reference to root_, |
// so we need to delete them here. |
root_->RemoveChildren(); |
RemoveEntryFromResourceMap(root_.get()); |
DCHECK(resource_map_.empty()); |
resource_map_.clear(); |
+ root_.reset(); |
} |
void GDataDirectoryService::AddEntryToDirectory( |
@@ -554,6 +689,166 @@ |
} |
} |
+void GDataDirectoryService::InitFromDB( |
+ const FilePath& db_path, |
+ base::SequencedTaskRunner* blocking_task_runner, |
+ LoadRootFeedParams* load_params, |
+ const base::Closure& callback) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ DCHECK(!db_path.empty()); |
+ DCHECK(blocking_task_runner); |
+ |
+ if (directory_service_db_.get()) { |
+ NOTREACHED(); |
+ if (!callback.is_null()) |
+ callback.Run(); |
+ } |
+ |
+ blocking_task_runner_ = blocking_task_runner; |
+ |
+ DVLOG(1) << "InitFromDB " << db_path.value(); |
+ |
+ CreateDBParams* create_params = |
+ new CreateDBParams(db_path, blocking_task_runner); |
+ blocking_task_runner_->PostTaskAndReply( |
+ FROM_HERE, |
+ base::Bind(&GDataDirectoryServiceDB::Create, |
+ create_params), |
+ base::Bind(&GDataDirectoryService::InitResourceMap, |
+ ui_weak_ptr_factory_.GetWeakPtr(), |
+ base::Owned(create_params), |
+ load_params, |
+ callback)); |
+} |
+ |
+void GDataDirectoryService::InitResourceMap( |
+ CreateDBParams* create_params, |
+ LoadRootFeedParams* load_params, |
+ const base::Closure& callback) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ DCHECK(create_params); |
+ |
+ directory_service_db_ = create_params->db.Pass(); |
+ if (create_params->serialized_resources.empty()) { |
+ origin_ = INITIALIZING; |
+ if (load_params) |
+ load_params->load_error = GDATA_FILE_ERROR_NOT_FOUND; |
+ if (!callback.is_null()) |
+ callback.Run(); |
+ return; |
+ } |
+ |
+ ClearRoot(); |
+ |
+ int32 version = 0; |
+ ResourceMap resource_map; |
+ for (SerializedMap::const_iterator iter = |
+ create_params->serialized_resources.begin(); |
+ iter != create_params->serialized_resources.end(); ++iter) { |
+ if (iter->first == "version") { |
+ if (!base::StringToInt(iter->second, &version) || |
+ version != kProtoVersion) { |
+ if (!callback.is_null()) |
+ callback.Run(); |
+ return; |
+ } |
+ continue; |
+ } |
+ |
+ if (iter->first == "largest_changestamp") { |
+ base::StringToInt(iter->second, &largest_changestamp_); |
+ DVLOG(1) << "InitResourceMap largest_changestamp_" |
+ << largest_changestamp_; |
+ continue; |
+ } |
+ |
+ scoped_ptr<GDataEntry> entry = FromProtoString(iter->second); |
+ if (entry.get()) { |
+ DVLOG(1) << "Inserting resource " << iter->first |
+ << " into resource_map"; |
+ resource_map.insert(std::make_pair(iter->first, entry.release())); |
+ } else { |
+ NOTREACHED() << "Failed to parse GDataEntry for resource " |
+ << iter->first; |
+ } |
+ } |
+ |
+ // Fix up parent-child relations. |
+ for (ResourceMap::iterator iter = resource_map.begin(); |
+ iter != resource_map.end(); ++iter) { |
+ GDataEntry* entry = iter->second; |
+ ResourceMap::iterator parent_it = |
+ resource_map.find(entry->parent_resource_id()); |
+ if (parent_it != resource_map.end()) { |
+ GDataDirectory* parent = parent_it->second->AsGDataDirectory(); |
+ if (parent) { |
+ DVLOG(1) << "Adding " << entry->resource_id() |
+ << " as a child of " << parent->resource_id(); |
+ parent->AddEntry(entry); |
+ } else { |
+ NOTREACHED() << "Parent is not a directory " << parent->resource_id(); |
+ } |
+ } else if (entry->resource_id() == kGDataRootDirectoryResourceId) { |
+ root_.reset(entry->AsGDataDirectory()); |
+ DCHECK(root_.get()); |
+ AddEntryToResourceMap(root_.get()); |
+ } else { |
+ NOTREACHED() << "Missing parent id " << entry->parent_resource_id() |
+ << " for resource " << entry->resource_id(); |
+ } |
+ } |
+ |
+ DCHECK(root_.get()); |
+ DCHECK_EQ(resource_map.size(), resource_map_.size()); |
+ DCHECK_EQ(resource_map.size(), |
+ create_params->serialized_resources.size() - 2); |
+ |
+ origin_ = FROM_CACHE; |
+ |
+ if (!callback.is_null()) |
+ callback.Run(); |
+} |
+ |
+void GDataDirectoryService::SaveToDB() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ if (!blocking_task_runner_ || !directory_service_db_.get()) { |
+ NOTREACHED(); |
+ return; |
+ } |
+ |
+ size_t serialized_size = 0; |
+ SerializedMap serialized_resources; |
+ for (ResourceMap::const_iterator iter = resource_map_.begin(); |
+ iter != resource_map_.end(); ++iter) { |
+ GDataEntryProto proto; |
+ iter->second->ToProtoFull(&proto); |
+ std::string serialized_string; |
+ const bool ok = proto.SerializeToString(&serialized_string); |
+ DCHECK(ok); |
+ if (ok) { |
+ serialized_resources.insert( |
+ std::make_pair(iter->first, serialized_string)); |
+ serialized_size += serialized_string.size(); |
+ } |
+ } |
+ |
+ serialized_resources.insert( |
+ std::make_pair("version", |
satorux1
2012/08/02 17:24:03
the indentation is awkard. 4 spaces from the previ
achuithb
2012/08/02 20:26:25
Done.
|
+ base::IntToString(kProtoVersion))); |
+ serialized_resources.insert( |
+ std::make_pair("largest_changestamp", |
satorux1
2012/08/02 17:24:03
ditto.
achuithb
2012/08/02 20:26:25
Done.
|
+ base::IntToString(largest_changestamp_))); |
+ set_last_serialized(base::Time::Now()); |
+ set_serialized_size(serialized_size); |
+ |
+ blocking_task_runner_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&GDataDirectoryServiceDB::Save, |
+ base::Unretained(directory_service_db_.get()), |
+ serialized_resources)); |
+} |
+ |
// Convert to/from proto. |
// static |
@@ -788,4 +1083,31 @@ |
return true; |
} |
+scoped_ptr<GDataEntry> GDataDirectoryService::FromProtoString( |
+ const std::string& serialized_proto) { |
+ scoped_ptr<GDataEntry> entry; |
+ |
+ GDataEntryProto entry_proto; |
+ if (!entry_proto.ParseFromString(serialized_proto)) |
+ return entry.Pass(); |
+ |
+ if (entry_proto.file_info().is_directory()) { |
+ entry.reset(new GDataDirectory(NULL, this)); |
+ // Call GDataEntry::FromProto. |
+ if (!entry->FromProto(entry_proto)) { |
+ NOTREACHED() << "FromProto (directory) failed"; |
+ entry.reset(); |
+ } |
+ } else { |
+ scoped_ptr<GDataFile> file(new GDataFile(NULL, this)); |
+ // Call GDataFile::FromProto. |
+ if (file->FromProto(entry_proto)) { |
+ entry.reset(file.release()); |
+ } else { |
+ NOTREACHED() << "FromProto (file) failed"; |
+ } |
+ } |
+ return entry.Pass(); |
+} |
+ |
} // namespace gdata |