1// Copyright 2014 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#ifndef COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_
6#define COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_
7
8#include <string>
9#include <vector>
10
11#include "base/bind.h"
12#include "base/files/file_path.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/message_loop/message_loop.h"
15#include "base/sequenced_task_runner.h"
16#include "base/strings/string_util.h"
17#include "base/threading/sequenced_worker_pool.h"
18#include "base/threading/thread_checker.h"
19#include "components/leveldb_proto/leveldb_database.h"
20#include "components/leveldb_proto/proto_database.h"
21
22namespace leveldb_proto {
23
24typedef std::vector<std::pair<std::string, std::string> > KeyValueVector;
25typedef std::vector<std::string> KeyVector;
26
27// When the ProtoDatabaseImpl instance is deleted, in-progress asynchronous
28// operations will be completed and the corresponding callbacks will be called.
29// Construction/calls/destruction should all happen on the same thread.
30template <typename T>
31class ProtoDatabaseImpl : public ProtoDatabase<T> {
32 public:
33  // All blocking calls/disk access will happen on the provided |task_runner|.
34  explicit ProtoDatabaseImpl(
35      scoped_refptr<base::SequencedTaskRunner> task_runner);
36
37  virtual ~ProtoDatabaseImpl();
38
39  // ProtoDatabase implementation.
40  // TODO(cjhopman): Perhaps Init() shouldn't be exposed to users and not just
41  //     part of the constructor
42  virtual void Init(const base::FilePath& database_dir,
43                    typename ProtoDatabase<T>::InitCallback callback) OVERRIDE;
44  virtual void UpdateEntries(
45      scoped_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
46      scoped_ptr<KeyVector> keys_to_remove,
47      typename ProtoDatabase<T>::UpdateCallback callback) OVERRIDE;
48  virtual void LoadEntries(
49      typename ProtoDatabase<T>::LoadCallback callback) OVERRIDE;
50
51  // Allow callers to provide their own Database implementation.
52  void InitWithDatabase(scoped_ptr<LevelDB> database,
53                        const base::FilePath& database_dir,
54                        typename ProtoDatabase<T>::InitCallback callback);
55
56 private:
57  base::ThreadChecker thread_checker_;
58
59  // Used to run blocking tasks in-order.
60  scoped_refptr<base::SequencedTaskRunner> task_runner_;
61
62  scoped_ptr<LevelDB> db_;
63
64  DISALLOW_COPY_AND_ASSIGN(ProtoDatabaseImpl);
65};
66
67namespace {
68
69template <typename T>
70void RunInitCallback(typename ProtoDatabase<T>::InitCallback callback,
71                     const bool* success) {
72  callback.Run(*success);
73}
74
75template <typename T>
76void RunUpdateCallback(typename ProtoDatabase<T>::UpdateCallback callback,
77                       const bool* success) {
78  callback.Run(*success);
79}
80
81template <typename T>
82void RunLoadCallback(typename ProtoDatabase<T>::LoadCallback callback,
83                     const bool* success, scoped_ptr<std::vector<T> > entries) {
84  callback.Run(*success, entries.Pass());
85}
86
87void InitFromTaskRunner(LevelDB* database, const base::FilePath& database_dir,
88                        bool* success) {
89  DCHECK(success);
90
91  // TODO(cjhopman): Histogram for database size.
92  *success = database->Init(database_dir);
93}
94
95template <typename T>
96void UpdateEntriesFromTaskRunner(
97    LevelDB* database,
98    scoped_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
99    scoped_ptr<KeyVector> keys_to_remove, bool* success) {
100  DCHECK(success);
101  // Serialize the values from Proto to string before passing on to database.
102  KeyValueVector pairs_to_save;
103  for (typename ProtoDatabase<T>::KeyEntryVector::iterator it =
104           entries_to_save->begin();
105       it != entries_to_save->end(); ++it) {
106    pairs_to_save.push_back(
107        std::make_pair(it->first, it->second.SerializeAsString()));
108  }
109  *success = database->Save(pairs_to_save, *keys_to_remove);
110}
111
112template <typename T>
113void LoadEntriesFromTaskRunner(LevelDB* database, std::vector<T>* entries,
114                               bool* success) {
115  DCHECK(success);
116  DCHECK(entries);
117
118  entries->clear();
119  std::vector<std::string> loaded_entries;
120  *success = database->Load(&loaded_entries);
121  for (std::vector<std::string>::iterator it = loaded_entries.begin();
122       it != loaded_entries.end(); ++it) {
123    T entry;
124    if (!entry.ParseFromString(*it)) {
125      DLOG(WARNING) << "Unable to parse leveldb_proto entry " << *it;
126      // TODO(cjhopman): Decide what to do about un-parseable entries.
127    }
128    entries->push_back(entry);
129  }
130}
131
132}  // namespace
133
134template <typename T>
135ProtoDatabaseImpl<T>::ProtoDatabaseImpl(
136    scoped_refptr<base::SequencedTaskRunner> task_runner)
137    : task_runner_(task_runner) {}
138
139template <typename T>
140ProtoDatabaseImpl<T>::~ProtoDatabaseImpl() {
141  DCHECK(thread_checker_.CalledOnValidThread());
142  if (!task_runner_->DeleteSoon(FROM_HERE, db_.release())) {
143    DLOG(WARNING) << "DOM distiller database will not be deleted.";
144  }
145}
146
147template <typename T>
148void ProtoDatabaseImpl<T>::Init(
149    const base::FilePath& database_dir,
150    typename ProtoDatabase<T>::InitCallback callback) {
151  DCHECK(thread_checker_.CalledOnValidThread());
152  InitWithDatabase(scoped_ptr<LevelDB>(new LevelDB()), database_dir, callback);
153}
154
155template <typename T>
156void ProtoDatabaseImpl<T>::InitWithDatabase(
157    scoped_ptr<LevelDB> database, const base::FilePath& database_dir,
158    typename ProtoDatabase<T>::InitCallback callback) {
159  DCHECK(thread_checker_.CalledOnValidThread());
160  DCHECK(!db_);
161  DCHECK(database);
162  db_.reset(database.release());
163  bool* success = new bool(false);
164  task_runner_->PostTaskAndReply(
165      FROM_HERE, base::Bind(InitFromTaskRunner, base::Unretained(db_.get()),
166                            database_dir, success),
167      base::Bind(RunInitCallback<T>, callback, base::Owned(success)));
168}
169
170template <typename T>
171void ProtoDatabaseImpl<T>::UpdateEntries(
172    scoped_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
173    scoped_ptr<KeyVector> keys_to_remove,
174    typename ProtoDatabase<T>::UpdateCallback callback) {
175  DCHECK(thread_checker_.CalledOnValidThread());
176  bool* success = new bool(false);
177  task_runner_->PostTaskAndReply(
178      FROM_HERE,
179      base::Bind(UpdateEntriesFromTaskRunner<T>, base::Unretained(db_.get()),
180                 base::Passed(&entries_to_save), base::Passed(&keys_to_remove),
181                 success),
182      base::Bind(RunUpdateCallback<T>, callback, base::Owned(success)));
183}
184
185template <typename T>
186void ProtoDatabaseImpl<T>::LoadEntries(
187    typename ProtoDatabase<T>::LoadCallback callback) {
188  DCHECK(thread_checker_.CalledOnValidThread());
189  bool* success = new bool(false);
190
191  scoped_ptr<std::vector<T> > entries(new std::vector<T>());
192  // Get this pointer before entries is base::Passed() so we can use it below.
193  std::vector<T>* entries_ptr = entries.get();
194
195  task_runner_->PostTaskAndReply(
196      FROM_HERE, base::Bind(LoadEntriesFromTaskRunner<T>,
197                            base::Unretained(db_.get()), entries_ptr, success),
198      base::Bind(RunLoadCallback<T>, callback, base::Owned(success),
199                 base::Passed(&entries)));
200}
201
202}  // namespace leveldb_proto
203
204#endif  // COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_
205