1 // Copyright (c) 2013 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 CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_DATABASE_H_
6 #define CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_DATABASE_H_
11 #include "base/basictypes.h"
12 #include "base/files/file_path.h"
13 #include "base/gtest_prod_util.h"
14 #include "base/memory/ref_counted_memory.h"
15 #include "base/synchronization/lock.h"
16 #include "base/timer/timer.h"
17 #include "chrome/browser/extensions/activity_log/activity_actions.h"
18 #include "chrome/common/extensions/extension.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "sql/connection.h"
21 #include "sql/init_status.h"
28 namespace extensions
{
30 // Encapsulates the SQL connection for the activity log database. This class
31 // holds the database connection and has methods for writing. All of the
32 // methods except the constructor need to be called on the DB thread.
34 // Object ownership and lifetime is a bit complicated for ActivityLog,
35 // ActivityLogPolicy, and ActivityDatabase:
37 // ActivityLog ----> ActivityLogPolicy ----> ActivityDatabase
40 // \--(ActivityDatabase::Delegate)-/
42 // The ActivityLogPolicy object contains a pointer to the ActivityDatabase, and
43 // the ActivityDatabase contains a pointer back to the ActivityLogPolicy object
44 // (as an instance of ActivityDatabase::Delegate).
46 // Since some cleanup must happen on the database thread, deletion should occur
48 // 1. ActivityLog calls ActivityLogPolicy::Close()
49 // 2. ActivityLogPolicy should call ActivityDatabase::Close() on the database
51 // 3. ActivityDatabase::Close() shuts down the database, then calls
52 // ActivityDatabase::Delegate::OnDatabaseClose().
53 // 4. ActivityDatabase::Delegate::OnDatabaseClose() should delete the
54 // ActivityLogPolicy object.
55 // 5. ActivityDatabase::Close() finishes running by deleting the
56 // ActivityDatabase object.
58 // (This assumes the common case that the ActivityLogPolicy uses an
59 // ActivityDatabase and implements the ActivityDatabase::Delegate interface.
60 // It is also possible for an ActivityLogPolicy to not use a database at all,
61 // in which case ActivityLogPolicy::Close() should directly delete itself.)
62 class ActivityDatabase
{
64 // Interface defining calls that the ActivityDatabase can make into a
65 // ActivityLogPolicy instance to implement policy-specific behavior. Methods
66 // are always invoked on the database thread. Classes other than
67 // ActivityDatabase should not call these methods.
70 friend class ActivityDatabase
;
72 // A Delegate is never directly deleted; it should instead delete itself
73 // after any final cleanup when OnDatabaseClose() is invoked.
74 virtual ~Delegate() {}
76 // Initializes the database schema; this gives a policy a chance to create
77 // or update database tables as needed. Should return true on success.
78 // Will be called from within a database transaction.
79 virtual bool InitDatabase(sql::Connection
* db
) = 0;
81 // Requests that the policy flush any pending actions to the database.
82 // Should return true on success or false on a database error. Will not be
83 // called from a transaction (the implementation may wish to use a
84 // transaction for the flush).
85 virtual bool FlushDatabase(sql::Connection
* db
) = 0;
87 // Called if the database encounters a permanent error; the policy should
88 // not expect to make any future writes to the database and may want to
89 // discard any queued data.
90 virtual void OnDatabaseFailure() = 0;
92 // Called by ActivityDatabase just before the ActivityDatabase object is
93 // deleted. The database will make no further callbacks after invoking
94 // this method, so it is an appropriate time for the policy to delete
96 virtual void OnDatabaseClose() = 0;
99 // Value to be passed to AdviseFlush below to force a database flush.
100 static const int kFlushImmediately
= -1;
102 // Need to call Init to actually use the ActivityDatabase. The Delegate
103 // provides hooks for an ActivityLogPolicy to control the database schema and
105 explicit ActivityDatabase(Delegate
* delegate
);
107 // Opens the DB. This invokes OnDatabaseInit in the delegate to create or
108 // update the database schema if needed.
109 void Init(const base::FilePath
& db_name
);
111 // An ActivityLogPolicy should call this to kill the ActivityDatabase.
114 // Inform the database that there may be additional data which could be
115 // written out. The size parameter should indicate (approximately) how many
116 // records are queued to be written; the database may use this information to
117 // schedule a flush early if too much data is queueing up. A value of
118 // kFlushImmediately will force an immediate call into
119 // Delegate::FlushDatabase(); otherwise, it is up to the database to
120 // determine when to flush.
121 void AdviseFlush(int size
);
123 // Turns off batch I/O writing mode. This should only be used in unit tests,
124 // browser tests, or in our special --enable-extension-activity-log-testing
126 void SetBatchModeForTesting(bool batch_mode
);
128 bool is_db_valid() const { return valid_db_
; }
130 // A helper method for initializing or upgrading a database table. The
131 // content_fields array should list the names of all of the columns in the
132 // database. The field_types should specify the types of the corresponding
133 // columns (e.g., INTEGER or LONGVARCHAR). There should be the same number of
134 // field_types as content_fields, since the two arrays should correspond.
135 static bool InitializeTable(sql::Connection
* db
,
136 const char* table_name
,
137 const char* content_fields
[],
138 const char* field_types
[],
139 const int num_content_fields
);
141 // Runs the given callback, passing it a handle to the database connection.
142 // If the database is not valid, the callback is run (to allow it to do any
143 // needed cleanup) but passed a NULL value.
144 void RunOnDatabase(const base::Callback
<void(sql::Connection
*)>& callback
);
147 // This should never be invoked by another class. Use Close() to order a
149 virtual ~ActivityDatabase();
151 // Used by the Init() method as a convenience for handling a failed database
152 // initialization attempt. Prints an error and puts us in the soft failure
154 void LogInitFailure();
156 // When we're in batched mode (which is on by default), we write to the db
157 // every X minutes instead of on every API call. This prevents the annoyance
158 // of writing to disk multiple times a second.
159 void RecordBatchedActions();
161 // If an error is unrecoverable or occurred while we were trying to close
162 // the database properly, we take "emergency" actions: break any outstanding
163 // transactions, raze the database, and close. When next opened, the
164 // database will be empty.
165 void HardFailureClose();
167 // Doesn't actually close the DB, but changes bools to prevent further writes
169 void SoftFailureClose();
171 // Handle errors in database writes. For a serious & permanent error, it
172 // invokes HardFailureClose(); for a less serious/permanent error, it invokes
173 // SoftFailureClose().
174 void DatabaseErrorCallback(int error
, sql::Statement
* stmt
);
176 // For unit testing only.
177 void RecordBatchedActionsWhileTesting();
178 void SetTimerForTesting(int milliseconds
);
180 // Retrieve a handle to the raw SQL database. This is only intended to be
181 // used by ActivityLogDatabasePolicy::GetDatabaseConnection(), and should
182 // only be called on the database thread.
183 sql::Connection
* GetSqlConnection();
185 // A reference a Delegate for policy-specific database behavior. See the
186 // top-level comment for ActivityDatabase for comments on cleanup.
192 base::TimeDelta batching_period_
;
193 base::RepeatingTimer
<ActivityDatabase
> timer_
;
194 bool already_closed_
;
197 friend class ActivityLogDatabasePolicy
;
198 FRIEND_TEST_ALL_PREFIXES(ActivityDatabaseTest
, BatchModeOff
);
199 FRIEND_TEST_ALL_PREFIXES(ActivityDatabaseTest
, BatchModeOn
);
200 FRIEND_TEST_ALL_PREFIXES(ActivityDatabaseTest
, BatchModeFlush
);
201 DISALLOW_COPY_AND_ASSIGN(ActivityDatabase
);
204 } // namespace extensions
205 #endif // CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_ACTIVITY_DATABASE_H_