package org.edx.mobile.module.db.impl; import android.content.Context; import android.database.sqlite.SQLiteException; import org.edx.mobile.logger.Logger; import java.util.LinkedList; import java.util.Queue; class IDatabaseBaseImpl implements Runnable { protected static final Logger logger = new Logger(IDatabaseBaseImpl.class.getName()); protected Context context; private DbHelper helper; private Queue<IDbOperation<?>> opQueue = new LinkedList<IDbOperation<?>>(); private boolean isQueueProcessing = false; public IDatabaseBaseImpl(Context context) { this.context = context; helper = new DbHelper(context); } @Override public void run() { if (isQueueProcessing) { // queue is already being processed, so return // this will NOT allow multiple threads to process operation queue return; } do { // mark queue being processed isQueueProcessing = true; IDbOperation<?> op = getNextQueuedOperation(); if (op == null) { break; } // perform the database operation execute(op); } while (true); // mark queue not being processed isQueueProcessing = false; // logger.debug("All database operations completed, queue is empty"); } /** * Executes given database operation. This is a blocking call. * Returns result of the operation. * * @param op * @return */ private synchronized <T> T execute(IDbOperation<T> op) { // perform this database operation synchronized (helper) { T result; try { result = op.requestExecute(helper.getDatabase()); } catch (SQLiteException e) { /* Catch any SQLite exceptions thrown by the operation, or by the database creation * or upgrade process invoked by the helper, deliver the exception to the callback, * log it in Crashlytics, and return the default value of the operation. */ op.getCallback().sendException(e); logger.error(e, true); result = op.getDefaultValue(); } return result; } } /** * Enqueues given database operation to the operation queue and starts processing the queue, * if not already started. * Operation is executed in a queue in background thread if callback is provided for the * operation and this method returns null. Otherwise this is a blocking call and returns * result object. * * @param operation */ public synchronized <T> T enqueue(IDbOperation<T> operation) { // execute right away if this operation doesn't have a callback to send back the result if (operation.getCallback() == null) { return execute(operation); } // add non-blocking operations to the queue and process in sequence synchronized (opQueue) { opQueue.add(operation); } // start processing the queue as we have a database operation to be processed new Thread(this).start(); return null; } /** * Returns and removes the next operation from the operation queue. * * @return */ private IDbOperation<?> getNextQueuedOperation() { synchronized (opQueue) { if (opQueue.isEmpty()) { return null; } return opQueue.remove(); } } /** * Closes this database object. */ public void release() { helper.close(); } }