diff options
Diffstat (limited to 'mobile/android/base/java/org/mozilla/gecko/db/AbstractTransactionalProvider.java')
-rw-r--r-- | mobile/android/base/java/org/mozilla/gecko/db/AbstractTransactionalProvider.java | 328 |
1 files changed, 0 insertions, 328 deletions
diff --git a/mobile/android/base/java/org/mozilla/gecko/db/AbstractTransactionalProvider.java b/mobile/android/base/java/org/mozilla/gecko/db/AbstractTransactionalProvider.java deleted file mode 100644 index 7e289b76f..000000000 --- a/mobile/android/base/java/org/mozilla/gecko/db/AbstractTransactionalProvider.java +++ /dev/null @@ -1,328 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.db; - -import org.mozilla.gecko.AppConstants.Versions; - -import android.content.ContentProvider; -import android.content.ContentValues; -import android.database.SQLException; -import android.database.sqlite.SQLiteDatabase; -import android.net.Uri; -import android.text.TextUtils; -import android.util.Log; - -/** - * This abstract class exists to capture some of the transaction-handling - * commonalities in Fennec's DB layer. - * - * In particular, this abstracts DB access, batching, and a particular - * transaction approach. - * - * That approach is: subclasses implement the abstract methods - * {@link #insertInTransaction(android.net.Uri, android.content.ContentValues)}, - * {@link #deleteInTransaction(android.net.Uri, String, String[])}, and - * {@link #updateInTransaction(android.net.Uri, android.content.ContentValues, String, String[])}. - * - * These are all called expecting a transaction to be established, so failed - * modifications can be rolled-back, and work batched. - * - * If no transaction is established, that's not a problem. Transaction nesting - * can be avoided by using {@link #beginWrite(SQLiteDatabase)}. - * - * The decision of when to begin a transaction is left to the subclasses, - * primarily to avoid the pattern of a transaction being begun, a read occurring, - * and then a write being necessary. This lock upgrade can result in SQLITE_BUSY, - * which we don't handle well. Better to avoid starting a transaction too soon! - * - * You are probably interested in some subclasses: - * - * * {@link AbstractPerProfileDatabaseProvider} provides a simple abstraction for - * querying databases that are stored in the user's profile directory. - * * {@link PerProfileDatabaseProvider} is a simple version that only allows a - * single ContentProvider to access each per-profile database. - * * {@link SharedBrowserDatabaseProvider} is an example of a per-profile provider - * that allows for multiple providers to safely work with the same databases. - */ -@SuppressWarnings("javadoc") -public abstract class AbstractTransactionalProvider extends ContentProvider { - private static final String LOGTAG = "GeckoTransProvider"; - - private static final boolean logDebug = Log.isLoggable(LOGTAG, Log.DEBUG); - private static final boolean logVerbose = Log.isLoggable(LOGTAG, Log.VERBOSE); - - protected abstract SQLiteDatabase getReadableDatabase(Uri uri); - protected abstract SQLiteDatabase getWritableDatabase(Uri uri); - - public abstract SQLiteDatabase getWritableDatabaseForTesting(Uri uri); - - protected abstract Uri insertInTransaction(Uri uri, ContentValues values); - protected abstract int deleteInTransaction(Uri uri, String selection, String[] selectionArgs); - protected abstract int updateInTransaction(Uri uri, ContentValues values, String selection, String[] selectionArgs); - - /** - * Track whether we're in a batch operation. - * - * When we're in a batch operation, individual write steps won't even try - * to start a transaction... and neither will they attempt to finish one. - * - * Set this to <code>Boolean.TRUE</code> when you're entering a batch -- - * a section of code in which {@link ContentProvider} methods will be - * called, but nested transactions should not be started. Callers are - * responsible for beginning and ending the enclosing transaction, and - * for setting this to <code>Boolean.FALSE</code> when done. - * - * This is a ThreadLocal separate from `db.inTransaction` because batched - * operations start transactions independent of individual ContentProvider - * operations. This doesn't work well with the entire concept of this - * abstract class -- that is, automatically beginning and ending transactions - * for each insert/delete/update operation -- and doing so without - * causing arbitrary nesting requires external tracking. - * - * Note that beginWrite takes a DB argument, but we don't differentiate - * between databases in this tracking flag. If your ContentProvider manages - * multiple database transactions within the same thread, you'll need to - * amend this scheme -- but then, you're already doing some serious wizardry, - * so rock on. - */ - final ThreadLocal<Boolean> isInBatchOperation = new ThreadLocal<Boolean>(); - - private boolean isInBatch() { - final Boolean isInBatch = isInBatchOperation.get(); - if (isInBatch == null) { - return false; - } - - return isInBatch; - } - - /** - * If we're not currently in a transaction, and we should be, start one. - */ - protected void beginWrite(final SQLiteDatabase db) { - if (isInBatch()) { - trace("Not bothering with an intermediate write transaction: inside batch operation."); - return; - } - - if (!db.inTransaction()) { - trace("beginWrite: beginning transaction."); - db.beginTransaction(); - } - } - - /** - * If we're not in a batch, but we are in a write transaction, mark it as - * successful. - */ - protected void markWriteSuccessful(final SQLiteDatabase db) { - if (isInBatch()) { - trace("Not marking write successful: inside batch operation."); - return; - } - - if (db.inTransaction()) { - trace("Marking write transaction successful."); - db.setTransactionSuccessful(); - } - } - - /** - * If we're not in a batch, but we are in a write transaction, - * end it. - * - * @see PerProfileDatabaseProvider#markWriteSuccessful(SQLiteDatabase) - */ - protected void endWrite(final SQLiteDatabase db) { - if (isInBatch()) { - trace("Not ending write: inside batch operation."); - return; - } - - if (db.inTransaction()) { - trace("endWrite: ending transaction."); - db.endTransaction(); - } - } - - protected void beginBatch(final SQLiteDatabase db) { - trace("Beginning batch."); - isInBatchOperation.set(Boolean.TRUE); - db.beginTransaction(); - } - - protected void markBatchSuccessful(final SQLiteDatabase db) { - if (isInBatch()) { - trace("Marking batch successful."); - db.setTransactionSuccessful(); - return; - } - Log.w(LOGTAG, "Unexpectedly asked to mark batch successful, but not in batch!"); - throw new IllegalStateException("Not in batch."); - } - - protected void endBatch(final SQLiteDatabase db) { - trace("Ending batch."); - db.endTransaction(); - isInBatchOperation.set(Boolean.FALSE); - } - - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - trace("Calling delete on URI: " + uri + ", " + selection + ", " + selectionArgs); - - final SQLiteDatabase db = getWritableDatabase(uri); - int deleted = 0; - - try { - deleted = deleteInTransaction(uri, selection, selectionArgs); - markWriteSuccessful(db); - } finally { - endWrite(db); - } - - if (deleted > 0) { - final boolean shouldSyncToNetwork = !isCallerSync(uri); - getContext().getContentResolver().notifyChange(uri, null, shouldSyncToNetwork); - } - - return deleted; - } - - @Override - public Uri insert(Uri uri, ContentValues values) { - trace("Calling insert on URI: " + uri); - - final SQLiteDatabase db = getWritableDatabase(uri); - Uri result = null; - try { - result = insertInTransaction(uri, values); - markWriteSuccessful(db); - } catch (SQLException sqle) { - Log.e(LOGTAG, "exception in DB operation", sqle); - } catch (UnsupportedOperationException uoe) { - Log.e(LOGTAG, "don't know how to perform that insert", uoe); - } finally { - endWrite(db); - } - - if (result != null) { - final boolean shouldSyncToNetwork = !isCallerSync(uri); - getContext().getContentResolver().notifyChange(uri, null, shouldSyncToNetwork); - } - - return result; - } - - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - trace("Calling update on URI: " + uri + ", " + selection + ", " + selectionArgs); - - final SQLiteDatabase db = getWritableDatabase(uri); - int updated = 0; - - try { - updated = updateInTransaction(uri, values, selection, - selectionArgs); - markWriteSuccessful(db); - } finally { - endWrite(db); - } - - if (updated > 0) { - final boolean shouldSyncToNetwork = !isCallerSync(uri); - getContext().getContentResolver().notifyChange(uri, null, shouldSyncToNetwork); - } - - return updated; - } - - @Override - public int bulkInsert(Uri uri, ContentValues[] values) { - if (values == null) { - return 0; - } - - int numValues = values.length; - int successes = 0; - - final SQLiteDatabase db = getWritableDatabase(uri); - - debug("bulkInsert: explicitly starting transaction."); - beginBatch(db); - - try { - for (int i = 0; i < numValues; i++) { - insertInTransaction(uri, values[i]); - successes++; - } - trace("Flushing DB bulkinsert..."); - markBatchSuccessful(db); - } finally { - debug("bulkInsert: explicitly ending transaction."); - endBatch(db); - } - - if (successes > 0) { - final boolean shouldSyncToNetwork = !isCallerSync(uri); - getContext().getContentResolver().notifyChange(uri, null, shouldSyncToNetwork); - } - - return successes; - } - - /** - * Indicates whether a query should include deleted fields - * based on the URI. - * @param uri query URI - */ - protected static boolean shouldShowDeleted(Uri uri) { - String showDeleted = uri.getQueryParameter(BrowserContract.PARAM_SHOW_DELETED); - return !TextUtils.isEmpty(showDeleted); - } - - /** - * Indicates whether an insertion should be made if a record doesn't - * exist, based on the URI. - * @param uri query URI - */ - protected static boolean shouldUpdateOrInsert(Uri uri) { - String insertIfNeeded = uri.getQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED); - return Boolean.parseBoolean(insertIfNeeded); - } - - /** - * Indicates whether query is a test based on the URI. - * @param uri query URI - */ - protected static boolean isTest(Uri uri) { - if (uri == null) { - return false; - } - String isTest = uri.getQueryParameter(BrowserContract.PARAM_IS_TEST); - return !TextUtils.isEmpty(isTest); - } - - /** - * Return true of the query is from Firefox Sync. - * @param uri query URI - */ - protected static boolean isCallerSync(Uri uri) { - String isSync = uri.getQueryParameter(BrowserContract.PARAM_IS_SYNC); - return !TextUtils.isEmpty(isSync); - } - - protected static void trace(String message) { - if (logVerbose) { - Log.v(LOGTAG, message); - } - } - - protected static void debug(String message) { - if (logDebug) { - Log.d(LOGTAG, message); - } - } -}
\ No newline at end of file |