2018-06-18 12:27:04 -07:00
|
|
|
package org.thoughtcrime.securesms.jobmanager;
|
|
|
|
|
2018-08-09 10:15:43 -04:00
|
|
|
import android.content.Context;
|
|
|
|
import android.support.annotation.NonNull;
|
|
|
|
import android.support.annotation.Nullable;
|
2019-03-28 08:56:35 -07:00
|
|
|
import android.support.annotation.WorkerThread;
|
2018-06-18 12:27:04 -07:00
|
|
|
|
2018-08-09 10:15:43 -04:00
|
|
|
import org.thoughtcrime.securesms.logging.Log;
|
2018-06-18 12:27:04 -07:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.concurrent.TimeUnit;
|
2018-06-18 12:27:04 -07:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
/**
|
|
|
|
* A durable unit of work.
|
|
|
|
*
|
|
|
|
* Jobs have {@link Parameters} that describe the conditions upon when you'd like them to run, how
|
|
|
|
* often they should be retried, and how long they should be retried for.
|
|
|
|
*
|
|
|
|
* Never rely on a specific instance of this class being run. It can be created and destroyed as the
|
|
|
|
* job is retried. State that you want to save is persisted to a {@link Data} object in
|
|
|
|
* {@link #serialize()}. Your job is then recreated using a {@link Factory} that you register in
|
|
|
|
* {@link JobManager.Configuration.Builder#setJobFactories(Map)}, which is given the saved
|
|
|
|
* {@link Data} bundle.
|
|
|
|
*/
|
|
|
|
public abstract class Job {
|
2018-06-18 12:27:04 -07:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
private static final String TAG = Log.tag(Job.class);
|
2018-06-18 12:27:04 -07:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
private final Parameters parameters;
|
2018-06-18 12:27:04 -07:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
private String id;
|
|
|
|
private int runAttempt;
|
|
|
|
private long nextRunAttemptTime;
|
2018-06-18 12:27:04 -07:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
protected Context context;
|
2018-10-28 22:13:51 -07:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public Job(@NonNull Parameters parameters) {
|
2018-08-09 10:15:43 -04:00
|
|
|
this.parameters = parameters;
|
2018-06-19 19:22:39 -07:00
|
|
|
}
|
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public final String getId() {
|
|
|
|
return id;
|
2018-10-28 22:13:51 -07:00
|
|
|
}
|
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public final @NonNull Parameters getParameters() {
|
|
|
|
return parameters;
|
|
|
|
}
|
2018-12-07 12:16:37 -08:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public final int getRunAttempt() {
|
|
|
|
return runAttempt;
|
|
|
|
}
|
2018-12-07 12:16:37 -08:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public final long getNextRunAttemptTime() {
|
|
|
|
return nextRunAttemptTime;
|
|
|
|
}
|
2018-12-07 12:16:37 -08:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
/**
|
|
|
|
* This is already called by {@link JobController} during job submission, but if you ever run a
|
|
|
|
* job without submitting it to the {@link JobManager}, then you'll need to invoke this yourself.
|
|
|
|
*/
|
2019-04-17 10:21:30 -04:00
|
|
|
public final void setContext(@NonNull Context context) {
|
2019-03-28 08:56:35 -07:00
|
|
|
this.context = context;
|
2018-06-18 12:27:04 -07:00
|
|
|
}
|
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
/** Should only be invoked by {@link JobController} */
|
|
|
|
final void setId(@NonNull String id) {
|
|
|
|
this.id = id;
|
2018-06-18 12:27:04 -07:00
|
|
|
}
|
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
/** Should only be invoked by {@link JobController} */
|
|
|
|
final void setRunAttempt(int runAttempt) {
|
|
|
|
this.runAttempt = runAttempt;
|
|
|
|
}
|
2018-10-20 22:52:14 -07:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
/** Should only be invoked by {@link JobController} */
|
|
|
|
final void setNextRunAttemptTime(long nextRunAttemptTime) {
|
|
|
|
this.nextRunAttemptTime = nextRunAttemptTime;
|
|
|
|
}
|
2018-10-20 22:52:14 -07:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
@WorkerThread
|
|
|
|
final void onSubmit() {
|
|
|
|
Log.i(TAG, JobLogger.format(this, "onSubmit()"));
|
2018-08-09 10:15:43 -04:00
|
|
|
onAdded();
|
2018-06-18 12:27:04 -07:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:15:43 -04:00
|
|
|
/**
|
2019-03-28 08:56:35 -07:00
|
|
|
* Called when the job is first submitted to the {@link JobManager}.
|
2018-08-09 10:15:43 -04:00
|
|
|
*/
|
2019-03-28 08:56:35 -07:00
|
|
|
@WorkerThread
|
|
|
|
public void onAdded() {
|
|
|
|
}
|
2018-06-19 19:22:39 -07:00
|
|
|
|
2018-08-09 10:15:43 -04:00
|
|
|
/**
|
2019-03-28 08:56:35 -07:00
|
|
|
* Called after a job has run and its determined that a retry is required.
|
2018-08-09 10:15:43 -04:00
|
|
|
*/
|
2019-03-28 08:56:35 -07:00
|
|
|
@WorkerThread
|
|
|
|
public void onRetry() {
|
|
|
|
}
|
2018-06-19 19:22:39 -07:00
|
|
|
|
2018-06-18 12:27:04 -07:00
|
|
|
/**
|
2019-03-28 08:56:35 -07:00
|
|
|
* Serialize your job state so that it can be recreated in the future.
|
2018-06-18 12:27:04 -07:00
|
|
|
*/
|
2019-03-28 08:56:35 -07:00
|
|
|
public abstract @NonNull Data serialize();
|
2018-06-18 12:27:04 -07:00
|
|
|
|
|
|
|
/**
|
2019-03-28 08:56:35 -07:00
|
|
|
* Returns the key that can be used to find the relevant factory needed to create your job.
|
2018-06-18 12:27:04 -07:00
|
|
|
*/
|
2019-03-28 08:56:35 -07:00
|
|
|
public abstract @NonNull String getFactoryKey();
|
2018-08-09 10:15:43 -04:00
|
|
|
|
|
|
|
/**
|
2019-03-28 08:56:35 -07:00
|
|
|
* Called to do your actual work.
|
2018-08-09 10:15:43 -04:00
|
|
|
*/
|
2019-03-28 08:56:35 -07:00
|
|
|
@WorkerThread
|
|
|
|
public abstract @NonNull Result run();
|
2018-06-18 12:27:04 -07:00
|
|
|
|
|
|
|
/**
|
2019-03-28 08:56:35 -07:00
|
|
|
* Called when your job has completely failed.
|
2018-06-18 12:27:04 -07:00
|
|
|
*/
|
2019-03-28 08:56:35 -07:00
|
|
|
@WorkerThread
|
|
|
|
public abstract void onCanceled();
|
2018-06-18 12:27:04 -07:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public interface Factory<T extends Job> {
|
|
|
|
@NonNull T create(@NonNull Parameters parameters, @NonNull Data data);
|
2018-08-09 10:15:43 -04:00
|
|
|
}
|
2018-06-18 12:27:04 -07:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public enum Result {
|
|
|
|
SUCCESS, FAILURE, RETRY
|
2018-12-07 12:16:37 -08:00
|
|
|
}
|
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public static final class Parameters {
|
|
|
|
|
|
|
|
public static final int IMMORTAL = -1;
|
|
|
|
public static final int UNLIMITED = -1;
|
|
|
|
|
|
|
|
private final long createTime;
|
|
|
|
private final long lifespan;
|
|
|
|
private final int maxAttempts;
|
|
|
|
private final long maxBackoff;
|
|
|
|
private final int maxInstances;
|
|
|
|
private final String queue;
|
|
|
|
private final List<String> constraintKeys;
|
|
|
|
|
|
|
|
private Parameters(long createTime,
|
|
|
|
long lifespan,
|
|
|
|
int maxAttempts,
|
|
|
|
long maxBackoff,
|
|
|
|
int maxInstances,
|
|
|
|
@Nullable String queue,
|
|
|
|
@NonNull List<String> constraintKeys)
|
|
|
|
{
|
|
|
|
this.createTime = createTime;
|
|
|
|
this.lifespan = lifespan;
|
|
|
|
this.maxAttempts = maxAttempts;
|
|
|
|
this.maxBackoff = maxBackoff;
|
|
|
|
this.maxInstances = maxInstances;
|
|
|
|
this.queue = queue;
|
|
|
|
this.constraintKeys = constraintKeys;
|
|
|
|
}
|
2018-06-18 12:27:04 -07:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public long getCreateTime() {
|
|
|
|
return createTime;
|
|
|
|
}
|
2018-06-18 12:27:04 -07:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public long getLifespan() {
|
|
|
|
return lifespan;
|
|
|
|
}
|
2018-08-09 10:15:43 -04:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public int getMaxAttempts() {
|
|
|
|
return maxAttempts;
|
2018-08-09 10:15:43 -04:00
|
|
|
}
|
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public long getMaxBackoff() {
|
|
|
|
return maxBackoff;
|
|
|
|
}
|
2018-08-09 10:15:43 -04:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public int getMaxInstances() {
|
|
|
|
return maxInstances;
|
|
|
|
}
|
2018-08-09 10:15:43 -04:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public @Nullable String getQueue() {
|
|
|
|
return queue;
|
2018-08-09 10:15:43 -04:00
|
|
|
}
|
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public List<String> getConstraintKeys() {
|
|
|
|
return constraintKeys;
|
|
|
|
}
|
2018-08-09 10:15:43 -04:00
|
|
|
|
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
public static final class Builder {
|
2018-08-09 10:15:43 -04:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
private long createTime = System.currentTimeMillis();
|
|
|
|
private long maxBackoff = TimeUnit.SECONDS.toMillis(30);
|
|
|
|
private long lifespan = IMMORTAL;
|
|
|
|
private int maxAttempts = 1;
|
|
|
|
private int maxInstances = UNLIMITED;
|
|
|
|
private String queue = null;
|
|
|
|
private List<String> constraintKeys = new LinkedList<>();
|
2018-08-09 10:15:43 -04:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
/** Should only be invoked by {@link JobController} */
|
|
|
|
Builder setCreateTime(long createTime) {
|
|
|
|
this.createTime = createTime;
|
|
|
|
return this;
|
|
|
|
}
|
2018-08-09 10:15:43 -04:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
/**
|
|
|
|
* Specify the amount of time this job is allowed to be retried. Defaults to {@link #IMMORTAL}.
|
|
|
|
*/
|
|
|
|
public @NonNull Builder setLifespan(long lifespan) {
|
|
|
|
this.lifespan = lifespan;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Specify the maximum number of times you want to attempt this job. Defaults to 1.
|
|
|
|
*/
|
|
|
|
public @NonNull Builder setMaxAttempts(int maxAttempts) {
|
|
|
|
this.maxAttempts = maxAttempts;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Specify the longest amount of time to wait between retries. No guarantees that this will
|
|
|
|
* be respected on API >= 26.
|
|
|
|
*/
|
|
|
|
public @NonNull Builder setMaxBackoff(long maxBackoff) {
|
|
|
|
this.maxBackoff = maxBackoff;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Specify the maximum number of instances you'd want of this job at any given time. If
|
|
|
|
* enqueueing this job would put it over that limit, it will be ignored.
|
|
|
|
*
|
|
|
|
* Duplicates are determined by two jobs having the same {@link Job#getFactoryKey()}.
|
|
|
|
*
|
|
|
|
* This property is ignored if the job is submitted as part of a {@link JobManager.Chain}.
|
|
|
|
*
|
|
|
|
* Defaults to {@link #UNLIMITED}.
|
|
|
|
*/
|
|
|
|
public @NonNull Builder setMaxInstances(int maxInstances) {
|
|
|
|
this.maxInstances = maxInstances;
|
|
|
|
return this;
|
|
|
|
}
|
2018-08-09 10:15:43 -04:00
|
|
|
|
2019-03-28 08:56:35 -07:00
|
|
|
/**
|
|
|
|
* Specify a string representing a queue. All jobs within the same queue are run in a
|
|
|
|
* serialized fashion -- one after the other, in order of insertion. Failure of a job earlier
|
|
|
|
* in the queue has no impact on the execution of jobs later in the queue.
|
|
|
|
*/
|
|
|
|
public @NonNull Builder setQueue(@Nullable String queue) {
|
|
|
|
this.queue = queue;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a constraint via the key that was used to register its factory in
|
|
|
|
* {@link JobManager.Configuration)};
|
|
|
|
*/
|
|
|
|
public @NonNull Builder addConstraint(@NonNull String constraintKey) {
|
|
|
|
constraintKeys.add(constraintKey);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set constraints via the key that was used to register its factory in
|
|
|
|
* {@link JobManager.Configuration)};
|
|
|
|
*/
|
|
|
|
public @NonNull Builder setConstraints(@NonNull List<String> constraintKeys) {
|
|
|
|
this.constraintKeys.clear();
|
|
|
|
this.constraintKeys.addAll(constraintKeys);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public @NonNull Parameters build() {
|
|
|
|
return new Parameters(createTime, lifespan, maxAttempts, maxBackoff, maxInstances, queue, constraintKeys);
|
|
|
|
}
|
|
|
|
}
|
2018-08-09 10:15:43 -04:00
|
|
|
}
|
2018-06-18 12:27:04 -07:00
|
|
|
}
|