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

Unified 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: rebase + satorux feedback 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/chromeos/gdata/gdata_files.cc
===================================================================
--- chrome/browser/chromeos/gdata/gdata_files.cc (revision 149888)
+++ chrome/browser/chromeos/gdata/gdata_files.cc (working copy)
@@ -4,25 +4,37 @@
#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 {
const char kSlash[] = "/";
const char kEscapedSlash[] = "\xE2\x88\x95";
+// m: prefix for filesystem metadata db keys, version and largest_changestamp.
+// r: prefix for resource id db keys.
+const char kDBKeyLargestChangestamp[] = "m:largest_changestamp";
+const char kDBKeyVersion[] = "m:version";
+const char kDBKeyResourceIdPrefix[] = "r:";
+
// Extracts resource_id out of edit url.
std::string ExtractResourceId(const GURL& url) {
return net::UnescapeURLComponent(url.ExtractFileName(),
@@ -423,12 +435,131 @@
child_directories_.clear();
}
+// ResourceMetadataDB 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;
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
+ scoped_ptr<ResourceMetadataDB> db;
+ GDataDirectoryService::SerializedMap serialized_resources;
+};
+
+// Wrapper for level db. All methods must be called on blocking thread.
+class ResourceMetadataDB {
+ public:
+ ResourceMetadataDB(const FilePath& db_path,
+ base::SequencedTaskRunner* blocking_task_runner);
+
+ // Initializes the database.
+ void Init();
+
+ // Reads the database into |serialized_resources|.
+ void Read(GDataDirectoryService::SerializedMap* serialized_resources);
+
+ // Saves |serialized_resources| to the database.
+ void Save(const GDataDirectoryService::SerializedMap& serialized_resources);
+
+ private:
+ // Clears the database.
+ void Clear();
+
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
+ scoped_ptr<leveldb::DB> level_db_;
+ FilePath db_path_;
+};
+
+ResourceMetadataDB::ResourceMetadataDB(const FilePath& db_path,
+ base::SequencedTaskRunner* blocking_task_runner)
+ : blocking_task_runner_(blocking_task_runner),
+ db_path_(db_path) {
+ DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
+}
+
+// Creates, initializes and reads from the database.
+// This must be defined after ResourceMetadataDB and CreateDBParams.
+static void CreateResourceMetadataDBOnBlockingPool(
+ CreateDBParams* params) {
+ DCHECK(params->blocking_task_runner->RunsTasksOnCurrentThread());
+ DCHECK(!params->db_path.empty());
+
+ params->db.reset(new ResourceMetadataDB(params->db_path,
+ params->blocking_task_runner));
+ params->db->Init();
+ params->db->Read(&params->serialized_resources);
+}
+
+void ResourceMetadataDB::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 ResourceMetadataDB::Read(
+ GDataDirectoryService::SerializedMap* serialized_resources) {
+ DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(serialized_resources);
+ DVLOG(1) << "Read " << db_path_.value();
+
+ 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 ResourceMetadataDB::Save(
+ const GDataDirectoryService::SerializedMap& serialized_resources) {
+ DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
+
+ Clear();
+ for (GDataDirectoryService::SerializedMap::const_iterator iter =
+ serialized_resources.begin();
+ iter != serialized_resources.end(); ++iter) {
+ DVLOG(1) << "Saving resource " << iter->first << " to db";
+ leveldb::Status status = level_db_->Put(leveldb::WriteOptions(),
+ leveldb::Slice(iter->first),
+ leveldb::Slice(iter->second));
+ if (!status.ok()) {
+ LOG(ERROR) << "leveldb Put failed of " << iter->first
+ << ", with " << status.ToString();
+ NOTREACHED();
+ }
+ }
+}
+
+void ResourceMetadataDB::Clear() {
+ 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),
+ weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
root_.reset(new GDataDirectory(NULL, this));
root_->set_title(kGDataRootDirectory);
root_->SetBaseNameFromTitle();
@@ -437,12 +568,22 @@
}
GDataDirectoryService::~GDataDirectoryService() {
+ ClearRoot();
+
+ // Ensure db is closed on the blocking pool.
+ if (blocking_task_runner_ && directory_service_db_.get())
+ blocking_task_runner_->DeleteSoon(FROM_HERE,
+ 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(
@@ -561,6 +702,173 @@
}
}
+void GDataDirectoryService::InitFromDB(
+ const FilePath& db_path,
+ base::SequencedTaskRunner* blocking_task_runner,
+ GDataFileError* error,
+ const base::Closure& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!db_path.empty());
+ DCHECK(blocking_task_runner);
+ DCHECK(error);
+
+ 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(&CreateResourceMetadataDBOnBlockingPool,
+ create_params),
+ base::Bind(&GDataDirectoryService::InitResourceMap,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Owned(create_params),
+ error,
+ callback));
+}
+
+void GDataDirectoryService::InitResourceMap(
+ CreateDBParams* create_params,
+ GDataFileError* error,
+ const base::Closure& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(create_params);
+ DCHECK(error);
+ // Ensure this callback is run before we return.
+ base::ScopedClosureRunner callback_runner(callback);
+
+ SerializedMap* serialized_resources = &create_params->serialized_resources;
+ directory_service_db_ = create_params->db.Pass();
+ if (serialized_resources->empty()) {
+ origin_ = INITIALIZING;
+ *error = GDATA_FILE_ERROR_NOT_FOUND;
+ return;
+ }
+
+ ClearRoot();
+
+ // Version check.
+ int32 version = 0;
+ SerializedMap::iterator iter = serialized_resources->find(kDBKeyVersion);
+ if (iter == serialized_resources->end() ||
+ !base::StringToInt(iter->second, &version) ||
+ version != kProtoVersion) {
+ *error = GDATA_FILE_ERROR_FAILED;
+ return;
+ }
+ serialized_resources->erase(iter);
+
+ // Get the largest changestamp.
+ iter = serialized_resources->find(kDBKeyLargestChangestamp);
+ if (iter == serialized_resources->end() ||
+ !base::StringToInt(iter->second, &largest_changestamp_)) {
+ NOTREACHED() << "Could not find/parse largest_changestamp";
+ *error = GDATA_FILE_ERROR_FAILED;
+ return;
+ } else {
+ DVLOG(1) << "InitResourceMap largest_changestamp_" << largest_changestamp_;
+ serialized_resources->erase(iter);
+ }
+
+ ResourceMap resource_map;
+ for (SerializedMap::const_iterator iter = serialized_resources->begin();
+ iter != serialized_resources->end(); ++iter) {
+ if (iter->first.find(kDBKeyResourceIdPrefix) != 0) {
+ NOTREACHED() << "Incorrect prefix for db key " << iter->first;
+ continue;
+ }
+
+ const std::string resource_id =
+ iter->first.substr(strlen(kDBKeyResourceIdPrefix));
+ scoped_ptr<GDataEntry> entry = FromProtoString(iter->second);
+ if (entry.get()) {
+ DVLOG(1) << "Inserting resource " << resource_id
+ << " into resource_map";
+ resource_map.insert(std::make_pair(resource_id, entry.release()));
+ } else {
+ NOTREACHED() << "Failed to parse GDataEntry for resource " << resource_id;
+ }
+ }
+
+ // 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(), serialized_resources->size());
+
+ origin_ = FROM_CACHE;
+}
+
+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(std::string(kDBKeyResourceIdPrefix) + iter->first,
+ serialized_string));
+ serialized_size += serialized_string.size();
+ }
+ }
+
+ serialized_resources.insert(std::make_pair(kDBKeyVersion,
+ base::IntToString(kProtoVersion)));
+ serialized_resources.insert(std::make_pair(kDBKeyLargestChangestamp,
+ base::IntToString(largest_changestamp_)));
+ set_last_serialized(base::Time::Now());
+ set_serialized_size(serialized_size);
+
+ blocking_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&ResourceMetadataDB::Save,
+ base::Unretained(directory_service_db_.get()),
+ serialized_resources));
+}
+
// Convert to/from proto.
// static
@@ -795,4 +1103,31 @@
return true;
}
+scoped_ptr<GDataEntry> GDataDirectoryService::FromProtoString(
+ const std::string& serialized_proto) {
+ GDataEntryProto entry_proto;
+ if (!entry_proto.ParseFromString(serialized_proto))
+ return scoped_ptr<GDataEntry>();
+
+ scoped_ptr<GDataEntry> entry;
+ if (entry_proto.file_info().is_directory()) {
+ entry.reset(new GDataDirectory(NULL, this));
+ // Call GDataEntry::FromProto instead of GDataDirectory::FromProto because
+ // the proto does not include children.
+ 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

Powered by Google App Engine
This is Rietveld 408576698