/*
* Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.greenrobot.dao.async;
import net.sqlcipher.database.SQLiteDatabase;
import de.greenrobot.dao.AbstractDao;
import de.greenrobot.dao.DaoException;
/**
* An operation that will be enqueued for asynchronous execution.
*
* @author Markus
*
* @see AsyncSession
*/
// TODO Implement Future<V>
public class AsyncOperation {
public static enum OperationType {
Insert, InsertInTxIterable, InsertInTxArray, //
InsertOrReplace, InsertOrReplaceInTxIterable, InsertOrReplaceInTxArray, //
Update, UpdateInTxIterable, UpdateInTxArray, //
Delete, DeleteInTxIterable, DeleteInTxArray, //
DeleteByKey, DeleteAll, //
TransactionRunnable, TransactionCallable, //
QueryList, QueryUnique, //
Load, LoadAll, //
Count, Refresh
}
public static final int FLAG_MERGE_TX = 1;
/** TODO unused, just an idea */
public static final int FLAG_STOP_QUEUE_ON_EXCEPTION = 1 << 1;
final OperationType type;
final AbstractDao<Object, Object> dao;
private final SQLiteDatabase database;
/** Entity, Iterable<Entity>, Entity[], or Runnable. */
final Object parameter;
final int flags;
volatile long timeStarted;
volatile long timeCompleted;
private volatile boolean completed;
volatile Throwable throwable;
volatile Object result;
volatile int mergedOperationsCount;
int sequenceNumber;
@SuppressWarnings("unchecked")
AsyncOperation(OperationType type, AbstractDao<?, ?> dao, Object parameter, int flags) {
this.type = type;
this.flags = flags;
this.dao = (AbstractDao<Object, Object>) dao;
this.database = null;
this.parameter = parameter;
}
AsyncOperation(OperationType type, SQLiteDatabase database, Object parameter, int flags) {
this.type = type;
this.database = database;
this.flags = flags;
this.dao = null;
this.parameter = parameter;
}
public Throwable getThrowable() {
return throwable;
}
public void setThrowable(Throwable throwable) {
this.throwable = throwable;
}
public OperationType getType() {
return type;
}
public Object getParameter() {
return parameter;
}
/**
* The operation's result after it has completed. Waits until a result is available.
*
* @return The operation's result or null if the operation type does not produce any result.
* @throws {@link AsyncDaoException} if the operation produced an exception
* @see #waitForCompletion()
*/
public synchronized Object getResult() {
if (!completed) {
waitForCompletion();
}
if (throwable != null) {
throw new AsyncDaoException(this, throwable);
}
return result;
}
/** @return true if this operation may be merged with others into a single database transaction. */
public boolean isMergeTx() {
return (flags & FLAG_MERGE_TX) != 0;
}
SQLiteDatabase getDatabase() {
return database != null ? database : dao.getDatabase();
}
/**
* @return true if this operation is mergeable with the given operation. Checks for null, {@link #FLAG_MERGE_TX},
* and if the database instances match.
*/
boolean isMergeableWith(AsyncOperation other) {
return other != null && isMergeTx() && other.isMergeTx() && getDatabase() == other.getDatabase();
}
public long getTimeStarted() {
return timeStarted;
}
public long getTimeCompleted() {
return timeCompleted;
}
public long getDuration() {
if (timeCompleted == 0) {
throw new DaoException("This operation did not yet complete");
} else {
return timeCompleted - timeStarted;
}
}
public boolean isFailed() {
return throwable != null;
}
public boolean isCompleted() {
return completed;
}
/**
* Waits until the operation is complete. If the thread gets interrupted, any {@link InterruptedException} will be
* rethrown as a {@link DaoException}.
*
* @return Result if any, see {@link #getResult()}
*/
public synchronized Object waitForCompletion() {
while (!completed) {
try {
wait();
} catch (InterruptedException e) {
throw new DaoException("Interrupted while waiting for operation to complete", e);
}
}
return result;
}
/**
* Waits until the operation is complete, but at most the given amount of milliseconds.If the thread gets
* interrupted, any {@link InterruptedException} will be rethrown as a {@link DaoException}.
*
* @return true if the operation completed in the given time frame.
*/
public synchronized boolean waitForCompletion(int maxMillis) {
if (!completed) {
try {
wait(maxMillis);
} catch (InterruptedException e) {
throw new DaoException("Interrupted while waiting for operation to complete", e);
}
}
return completed;
}
/** Called when the operation is done. Notifies any threads waiting for this operation's completion. */
synchronized void setCompleted() {
completed = true;
notifyAll();
}
public boolean isCompletedSucessfully() {
return completed && throwable == null;
}
/**
* If this operation was successfully merged with other operation into a single TX, this will give the count of
* merged operations. If the operation was not merged, it will be 0.
*/
public int getMergedOperationsCount() {
return mergedOperationsCount;
}
/**
* Each operation get a unique sequence number when the operation is enqueued. Can be used for efficiently
* identifying/mapping operations.
*/
public int getSequenceNumber() {
return sequenceNumber;
}
/** Reset to prepare another execution run. */
void reset() {
timeStarted = 0;
timeCompleted = 0;
completed = false;
throwable = null;
result = null;
mergedOperationsCount = 0;
}
}