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.
5 #ifndef COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_
6 #define COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_
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 "base/strings/string_split.h"
20 #include "components/leveldb_proto/leveldb_database.h"
21 #include "components/leveldb_proto/proto_database.h"
23 namespace leveldb_proto
{
25 typedef base::StringPairs KeyValueVector
;
26 typedef std::vector
<std::string
> KeyVector
;
28 // When the ProtoDatabaseImpl instance is deleted, in-progress asynchronous
29 // operations will be completed and the corresponding callbacks will be called.
30 // Construction/calls/destruction should all happen on the same thread.
32 class ProtoDatabaseImpl
: public ProtoDatabase
<T
> {
34 // All blocking calls/disk access will happen on the provided |task_runner|.
35 explicit ProtoDatabaseImpl(
36 scoped_refptr
<base::SequencedTaskRunner
> task_runner
);
38 virtual ~ProtoDatabaseImpl();
40 // ProtoDatabase implementation.
41 // TODO(cjhopman): Perhaps Init() shouldn't be exposed to users and not just
42 // part of the constructor
43 virtual void Init(const base::FilePath
& database_dir
,
44 typename ProtoDatabase
<T
>::InitCallback callback
) OVERRIDE
;
45 virtual void UpdateEntries(
46 scoped_ptr
<typename ProtoDatabase
<T
>::KeyEntryVector
> entries_to_save
,
47 scoped_ptr
<KeyVector
> keys_to_remove
,
48 typename ProtoDatabase
<T
>::UpdateCallback callback
) OVERRIDE
;
49 virtual void LoadEntries(
50 typename ProtoDatabase
<T
>::LoadCallback callback
) OVERRIDE
;
52 // Allow callers to provide their own Database implementation.
53 void InitWithDatabase(scoped_ptr
<LevelDB
> database
,
54 const base::FilePath
& database_dir
,
55 typename ProtoDatabase
<T
>::InitCallback callback
);
58 base::ThreadChecker thread_checker_
;
60 // Used to run blocking tasks in-order.
61 scoped_refptr
<base::SequencedTaskRunner
> task_runner_
;
63 scoped_ptr
<LevelDB
> db_
;
65 DISALLOW_COPY_AND_ASSIGN(ProtoDatabaseImpl
);
71 void RunInitCallback(typename ProtoDatabase
<T
>::InitCallback callback
,
72 const bool* success
) {
73 callback
.Run(*success
);
77 void RunUpdateCallback(typename ProtoDatabase
<T
>::UpdateCallback callback
,
78 const bool* success
) {
79 callback
.Run(*success
);
83 void RunLoadCallback(typename ProtoDatabase
<T
>::LoadCallback callback
,
84 const bool* success
, scoped_ptr
<std::vector
<T
> > entries
) {
85 callback
.Run(*success
, entries
.Pass());
88 void InitFromTaskRunner(LevelDB
* database
, const base::FilePath
& database_dir
,
92 // TODO(cjhopman): Histogram for database size.
93 *success
= database
->Init(database_dir
);
97 void UpdateEntriesFromTaskRunner(
99 scoped_ptr
<typename ProtoDatabase
<T
>::KeyEntryVector
> entries_to_save
,
100 scoped_ptr
<KeyVector
> keys_to_remove
, bool* success
) {
102 // Serialize the values from Proto to string before passing on to database.
103 KeyValueVector pairs_to_save
;
104 for (typename ProtoDatabase
<T
>::KeyEntryVector::iterator it
=
105 entries_to_save
->begin();
106 it
!= entries_to_save
->end(); ++it
) {
107 pairs_to_save
.push_back(
108 std::make_pair(it
->first
, it
->second
.SerializeAsString()));
110 *success
= database
->Save(pairs_to_save
, *keys_to_remove
);
113 template <typename T
>
114 void LoadEntriesFromTaskRunner(LevelDB
* database
, std::vector
<T
>* entries
,
120 std::vector
<std::string
> loaded_entries
;
121 *success
= database
->Load(&loaded_entries
);
122 for (std::vector
<std::string
>::iterator it
= loaded_entries
.begin();
123 it
!= loaded_entries
.end(); ++it
) {
125 if (!entry
.ParseFromString(*it
)) {
126 DLOG(WARNING
) << "Unable to parse leveldb_proto entry " << *it
;
127 // TODO(cjhopman): Decide what to do about un-parseable entries.
129 entries
->push_back(entry
);
135 template <typename T
>
136 ProtoDatabaseImpl
<T
>::ProtoDatabaseImpl(
137 scoped_refptr
<base::SequencedTaskRunner
> task_runner
)
138 : task_runner_(task_runner
) {}
140 template <typename T
>
141 ProtoDatabaseImpl
<T
>::~ProtoDatabaseImpl() {
142 DCHECK(thread_checker_
.CalledOnValidThread());
143 if (!task_runner_
->DeleteSoon(FROM_HERE
, db_
.release())) {
144 DLOG(WARNING
) << "DOM distiller database will not be deleted.";
148 template <typename T
>
149 void ProtoDatabaseImpl
<T
>::Init(
150 const base::FilePath
& database_dir
,
151 typename ProtoDatabase
<T
>::InitCallback callback
) {
152 DCHECK(thread_checker_
.CalledOnValidThread());
153 InitWithDatabase(scoped_ptr
<LevelDB
>(new LevelDB()), database_dir
, callback
);
156 template <typename T
>
157 void ProtoDatabaseImpl
<T
>::InitWithDatabase(
158 scoped_ptr
<LevelDB
> database
, const base::FilePath
& database_dir
,
159 typename ProtoDatabase
<T
>::InitCallback callback
) {
160 DCHECK(thread_checker_
.CalledOnValidThread());
163 db_
.reset(database
.release());
164 bool* success
= new bool(false);
165 task_runner_
->PostTaskAndReply(
166 FROM_HERE
, base::Bind(InitFromTaskRunner
, base::Unretained(db_
.get()),
167 database_dir
, success
),
168 base::Bind(RunInitCallback
<T
>, callback
, base::Owned(success
)));
171 template <typename T
>
172 void ProtoDatabaseImpl
<T
>::UpdateEntries(
173 scoped_ptr
<typename ProtoDatabase
<T
>::KeyEntryVector
> entries_to_save
,
174 scoped_ptr
<KeyVector
> keys_to_remove
,
175 typename ProtoDatabase
<T
>::UpdateCallback callback
) {
176 DCHECK(thread_checker_
.CalledOnValidThread());
177 bool* success
= new bool(false);
178 task_runner_
->PostTaskAndReply(
180 base::Bind(UpdateEntriesFromTaskRunner
<T
>, base::Unretained(db_
.get()),
181 base::Passed(&entries_to_save
), base::Passed(&keys_to_remove
),
183 base::Bind(RunUpdateCallback
<T
>, callback
, base::Owned(success
)));
186 template <typename T
>
187 void ProtoDatabaseImpl
<T
>::LoadEntries(
188 typename ProtoDatabase
<T
>::LoadCallback callback
) {
189 DCHECK(thread_checker_
.CalledOnValidThread());
190 bool* success
= new bool(false);
192 scoped_ptr
<std::vector
<T
> > entries(new std::vector
<T
>());
193 // Get this pointer before entries is base::Passed() so we can use it below.
194 std::vector
<T
>* entries_ptr
= entries
.get();
196 task_runner_
->PostTaskAndReply(
197 FROM_HERE
, base::Bind(LoadEntriesFromTaskRunner
<T
>,
198 base::Unretained(db_
.get()), entries_ptr
, success
),
199 base::Bind(RunLoadCallback
<T
>, callback
, base::Owned(success
),
200 base::Passed(&entries
)));
203 } // namespace leveldb_proto
205 #endif // COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_