mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-24 00:37:47 +00:00
Move JobManager to external project and dependency.
// FREEBIE
This commit is contained in:
parent
0d01feebc3
commit
de75a98093
@ -45,12 +45,13 @@ dependencies {
|
||||
compile 'com.squareup.dagger:dagger:1.2.2'
|
||||
provided 'com.squareup.dagger:dagger-compiler:1.2.2'
|
||||
|
||||
compile 'org.whispersystems:jobmanager:0.9.0'
|
||||
|
||||
androidTestCompile 'com.squareup:fest-android:1.0.8'
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker:1.1'
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.1'
|
||||
|
||||
compile project(':libtextsecure')
|
||||
compile project(':jobqueue')
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
@ -72,6 +73,7 @@ dependencyVerification {
|
||||
'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74',
|
||||
'com.googlecode.libphonenumber:libphonenumber:eba17eae81dd622ea89a00a3a8c025b2f25d342e0d9644c5b62e16f15687c3ab',
|
||||
'org.whispersystems:gson:08f4f7498455d1539c9233e5aac18e9b1805815ef29221572996508eb512fe51',
|
||||
'org.whispersystems:jobmanager:adb4329b69035053a7b6a48e22a3280235ac405b225ed8679b994612a8e6f5b6',
|
||||
'com.android.support:support-annotations:1aa96ef0cc4a445bfc2f93ccf762305bc57fa107b12afe9d11f3863ae8a11036',
|
||||
]
|
||||
}
|
||||
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
#Fri Nov 14 10:44:11 MSK 2014
|
||||
#Fri Nov 28 10:03:17 PST 2014
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
|
||||
|
1
jobqueue/.gitignore
vendored
1
jobqueue/.gitignore
vendored
@ -1 +0,0 @@
|
||||
/build
|
@ -1,238 +0,0 @@
|
||||
# JobManager
|
||||
|
||||
An Android library that facilitates scheduling persistent jobs which are executed when their
|
||||
prerequisites have been met. Similar to Path's android-priority-queue.
|
||||
|
||||
## The JobManager Way
|
||||
|
||||
Android apps often need to perform blocking operations. A messaging app might need to make REST
|
||||
API calls over a network, send SMS messages, download attachments, and interact with a database.
|
||||
|
||||
The standard Android way to do these things are with Services, AsyncTasks, or a dedicated Thread.
|
||||
However, some of an app's operations might need to wait until certain dependencies are available
|
||||
(such as a network connection), and some of the operations might need to be durable (complete even if the
|
||||
app restarts before they have a chance to run). The standard Android way can result in
|
||||
a lot of retry logic, timers for monitoring dependencies, and one-off code for making operations
|
||||
durable.
|
||||
|
||||
By contrast, the JobManager way allows operations to be broken up into Jobs. A Job represents a
|
||||
unit of work to be done, the prerequisites that need to be met (such as network access) before the
|
||||
work can execute, and the characteristics of the job (such as durable persistence).
|
||||
|
||||
Applications construct a `JobManager` at initialization time:
|
||||
|
||||
```
|
||||
public class ApplicationContext extends Application {
|
||||
|
||||
private JobManager jobManager;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
initializeJobManager();
|
||||
}
|
||||
|
||||
private void initializeJobManager() {
|
||||
this.jobManager = JobManager.newBuilder(this)
|
||||
.withName("SampleJobManager")
|
||||
.withConsumerThreads(5)
|
||||
.build();
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
This constructs a new `JobManager` with 5 consumer threads dedicated to executing Jobs. A
|
||||
Job looks like this:
|
||||
|
||||
```
|
||||
public class SampleJob extends Job {
|
||||
|
||||
public SampleJob() {
|
||||
super(JobParameters.newBuilder().create());
|
||||
}
|
||||
|
||||
@Override
|
||||
public onAdded() {
|
||||
// Called after the Job has been added to the queue.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() {
|
||||
// Here's where we execute our work.
|
||||
Log.w("SampleJob", "Hello, world!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled() {
|
||||
// This would be called if the job had failed.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(Exception exception) {
|
||||
// Called if onRun() had thrown an exception to determine whether
|
||||
// onRun() should be called again.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A Job is scheduled simply by adding it to the JobManager:
|
||||
|
||||
```
|
||||
this.jobManager.add(new SampleJob());
|
||||
```
|
||||
|
||||
## Persistence
|
||||
|
||||
To create durable Jobs, the JobManager needs to be given an interface responsible for serializing
|
||||
and deserializing Job objects. A `JavaJobSerializer` is included with JobManager that uses Java
|
||||
Serialization, but you can specify your own serializer if you wish:
|
||||
|
||||
```
|
||||
public class ApplicationContext extends Application {
|
||||
|
||||
private JobManager jobManager;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
initializeJobManager();
|
||||
}
|
||||
|
||||
private void initializeJobManager() {
|
||||
this.jobManager = JobManager.newBuilder(this)
|
||||
.withName("SampleJobManager")
|
||||
.withConsumerThreads(5)
|
||||
.withJobSerializer(new JavaJobSerializer())
|
||||
.build();
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The Job simply needs to declare itself as durable when constructed:
|
||||
|
||||
```
|
||||
public class SampleJob extends Job {
|
||||
|
||||
public SampleJob() {
|
||||
super(JobParameters.newBuilder()
|
||||
.withPersistence()
|
||||
.create());
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
Persistent jobs that are enqueued will be serialized to disk to ensure that they run even if
|
||||
the App restarts first. A Job's onAdded() method is called after the commit to disk is complete.
|
||||
|
||||
## Requirements
|
||||
|
||||
A Job might have certain requirements that need to be met before it can run. A requirement is
|
||||
represented by the `Requirement` interface. Each `Requirement` must also have a corresponding
|
||||
`RequirementProvider` that is registered with the JobManager.
|
||||
|
||||
A `Requirement` tells you whether it is present when queried, while a `RequirementProvider`
|
||||
broadcasts to a listener when a Requirement's status might have changed. `Requirement` is attached
|
||||
to Job, while `RequirementProvider` is attached to JobManager.
|
||||
|
||||
|
||||
One common `Requirement` a `Job` might depend on is the presence of network connectivity.
|
||||
A `NetworkRequirement` is bundled with JobManager:
|
||||
|
||||
```
|
||||
public class ApplicationContext extends Application {
|
||||
|
||||
private JobManager jobManager;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
initializeJobManager();
|
||||
}
|
||||
|
||||
private void initializeJobManager() {
|
||||
this.jobManager = JobManager.newBuilder(this)
|
||||
.withName("SampleJobManager")
|
||||
.withConsumerThreads(5)
|
||||
.withJobSerializer(new JavaJobSerializer())
|
||||
.withRequirementProviders(new NetworkRequirementProvider(this))
|
||||
.build();
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
The Job declares itself as having a `Requirement` when constructed:
|
||||
|
||||
```
|
||||
public class SampleJob extends Job {
|
||||
|
||||
public SampleJob(Context context) {
|
||||
super(JobParameters.newBuilder()
|
||||
.withPersistence()
|
||||
.withRequirement(new NetworkRequirement(context))
|
||||
.create());
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
It is possible that Jobs (and Requirements) might require dependency injection. A simple example
|
||||
is `Context`, which many Jobs might require, but can't be persisted to disk for durable Jobs. Or
|
||||
maybe Jobs require more complex DI through libraries such as Dagger.
|
||||
|
||||
JobManager has an extremely primitive DI mechanism strictly for injecting `Context` objects into
|
||||
Jobs and Requirements after they're deserialized, and includes support for plugging in more complex
|
||||
DI systems such as Dagger.
|
||||
|
||||
The JobManager `Context` injection works by having your `Job` and/or `Requirement` implement the
|
||||
`ContextDependent` interface. `Job`s and `Requirement`s implementing that interface will get a
|
||||
`setContext(Context context)` call immediately after the persistent `Job` or `Requirement` is
|
||||
deserialized.
|
||||
|
||||
To plugin a more complex DI mechanism, simply pass an instance of the `DependencyInjector` interface
|
||||
to the `JobManager`:
|
||||
|
||||
```
|
||||
public class ApplicationContext extends Application implements DependencyInjector {
|
||||
|
||||
private JobManager jobManager;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
initializeJobManager();
|
||||
}
|
||||
|
||||
private void initializeJobManager() {
|
||||
this.jobManager = JobManager.newBuilder(this)
|
||||
.withName("SampleJobManager")
|
||||
.withConsumerThreads(5)
|
||||
.withJobSerializer(new JavaJobSerializer())
|
||||
.withRequirementProviders(new NetworkRequirementProvider(this))
|
||||
.withDependencyInjector(this)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectDependencies(Object object) {
|
||||
// And here we do our DI magic.
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
`injectDependencies(Object object)` will be called for a `Job` before the job's `onAdded()` method
|
||||
is called, or after a persistent job is deserialized.
|
@ -1,19 +0,0 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 21
|
||||
buildToolsVersion '21.1.1'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "org.whispersystems.jobqueue"
|
||||
minSdkVersion 9
|
||||
targetSdkVersion 19
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
}
|
@ -1,225 +0,0 @@
|
||||
package org.whispersystems.jobqueue;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import org.whispersystems.jobqueue.jobs.PersistentTestJob;
|
||||
import org.whispersystems.jobqueue.jobs.RequirementDeferringTestJob;
|
||||
import org.whispersystems.jobqueue.jobs.RequirementTestJob;
|
||||
import org.whispersystems.jobqueue.jobs.TestJob;
|
||||
import org.whispersystems.jobqueue.persistence.JavaJobSerializer;
|
||||
import org.whispersystems.jobqueue.requirements.RequirementProvider;
|
||||
import org.whispersystems.jobqueue.util.MockRequirement;
|
||||
import org.whispersystems.jobqueue.util.MockRequirementProvider;
|
||||
import org.whispersystems.jobqueue.util.PersistentMockRequirement;
|
||||
import org.whispersystems.jobqueue.util.PersistentRequirement;
|
||||
import org.whispersystems.jobqueue.util.PersistentResult;
|
||||
import org.whispersystems.jobqueue.util.RunnableThrowable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class JobManagerTest extends AndroidTestCase {
|
||||
|
||||
public void testTransientJobExecution() throws InterruptedException {
|
||||
TestJob testJob = new TestJob();
|
||||
JobManager jobManager = JobManager.newBuilder(getContext())
|
||||
.withName("transient-test")
|
||||
.withConsumerThreads(1)
|
||||
.build();
|
||||
|
||||
jobManager.add(testJob);
|
||||
|
||||
assertTrue(testJob.isAdded());
|
||||
assertTrue(testJob.isRan());
|
||||
}
|
||||
|
||||
public void testTransientRequirementJobExecution() throws InterruptedException {
|
||||
MockRequirementProvider provider = new MockRequirementProvider();
|
||||
MockRequirement requirement = new MockRequirement(false);
|
||||
TestJob testJob = new RequirementTestJob(requirement);
|
||||
|
||||
JobManager jobManager = JobManager.newBuilder(getContext())
|
||||
.withName("transient-requirement-test")
|
||||
.withRequirementProviders(provider)
|
||||
.withConsumerThreads(1)
|
||||
.build();
|
||||
|
||||
jobManager.add(testJob);
|
||||
|
||||
assertTrue(testJob.isAdded());
|
||||
assertTrue(!testJob.isRan());
|
||||
|
||||
requirement.setPresent(true);
|
||||
provider.fireChange();
|
||||
|
||||
assertTrue(testJob.isRan());
|
||||
}
|
||||
|
||||
public void testTransientRequirementDeferringJobExecution() throws InterruptedException {
|
||||
final Object lock = new Object();
|
||||
|
||||
RunnableThrowable waitRunnable = new RunnableThrowable() {
|
||||
public Boolean shouldThrow = false;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
try {
|
||||
synchronized (lock) {
|
||||
lock.wait();
|
||||
|
||||
if (shouldThrow) {
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void shouldThrow(Boolean value) {
|
||||
shouldThrow = value;
|
||||
}
|
||||
};
|
||||
|
||||
MockRequirementProvider provider = new MockRequirementProvider();
|
||||
MockRequirement requirement = new MockRequirement(false);
|
||||
RequirementDeferringTestJob testJob = new RequirementDeferringTestJob(requirement, 5, waitRunnable);
|
||||
|
||||
JobManager jobManager = JobManager.newBuilder(getContext())
|
||||
.withName("transient-requirement-test")
|
||||
.withRequirementProviders(provider)
|
||||
.withConsumerThreads(1)
|
||||
.build();
|
||||
|
||||
jobManager.add(testJob);
|
||||
|
||||
waitRunnable.shouldThrow(true);
|
||||
requirement.setPresent(true);
|
||||
provider.fireChange();
|
||||
|
||||
assertTrue(testJob.isRan());
|
||||
assertTrue(!testJob.isFinished());
|
||||
synchronized (lock) { lock.notifyAll(); }
|
||||
assertTrue(!testJob.isFinished());
|
||||
|
||||
requirement.setPresent(false);
|
||||
provider.fireChange();
|
||||
assertTrue(!testJob.isFinished());
|
||||
synchronized (lock) { lock.notifyAll(); }
|
||||
assertTrue(!testJob.isFinished());
|
||||
|
||||
waitRunnable.shouldThrow(false);
|
||||
requirement.setPresent(true);
|
||||
provider.fireChange();
|
||||
assertTrue(!testJob.isFinished());
|
||||
synchronized (lock) { lock.notifyAll(); }
|
||||
assertTrue(testJob.isFinished());
|
||||
}
|
||||
|
||||
public void testPersistentJobExecuton() throws InterruptedException {
|
||||
PersistentMockRequirement requirement = new PersistentMockRequirement();
|
||||
PersistentTestJob testJob = new PersistentTestJob(requirement);
|
||||
JobManager jobManager = JobManager.newBuilder(getContext())
|
||||
.withName("persistent-requirement-test3")
|
||||
.withJobSerializer(new JavaJobSerializer())
|
||||
.withConsumerThreads(1)
|
||||
.build();
|
||||
|
||||
PersistentResult.getInstance().reset();
|
||||
PersistentRequirement.getInstance().setPresent(false);
|
||||
|
||||
jobManager.add(testJob);
|
||||
|
||||
assertTrue(PersistentResult.getInstance().isAdded());
|
||||
assertTrue(!PersistentResult.getInstance().isRan());
|
||||
|
||||
PersistentRequirement.getInstance().setPresent(true);
|
||||
|
||||
jobManager = JobManager.newBuilder(getContext())
|
||||
.withName("persistent-requirement-test3")
|
||||
.withJobSerializer(new JavaJobSerializer())
|
||||
.withConsumerThreads(1)
|
||||
.build();
|
||||
|
||||
assertTrue(PersistentResult.getInstance().isRan());
|
||||
}
|
||||
|
||||
public void testEncryptedJobExecuton() throws InterruptedException {
|
||||
EncryptionKeys keys = new EncryptionKeys(new byte[30]);
|
||||
PersistentMockRequirement requirement = new PersistentMockRequirement();
|
||||
PersistentTestJob testJob = new PersistentTestJob(requirement, keys);
|
||||
JobManager jobManager = JobManager.newBuilder(getContext())
|
||||
.withName("persistent-requirement-test4")
|
||||
.withJobSerializer(new JavaJobSerializer())
|
||||
.withConsumerThreads(1)
|
||||
.build();
|
||||
|
||||
jobManager.setEncryptionKeys(keys);
|
||||
|
||||
PersistentResult.getInstance().reset();
|
||||
PersistentRequirement.getInstance().setPresent(false);
|
||||
|
||||
jobManager.add(testJob);
|
||||
|
||||
assertTrue(PersistentResult.getInstance().isAdded());
|
||||
assertTrue(!PersistentResult.getInstance().isRan());
|
||||
|
||||
PersistentRequirement.getInstance().setPresent(true);
|
||||
jobManager = JobManager.newBuilder(getContext())
|
||||
.withName("persistent-requirement-test4")
|
||||
.withJobSerializer(new JavaJobSerializer())
|
||||
.withConsumerThreads(1)
|
||||
.build();
|
||||
|
||||
assertTrue(!PersistentResult.getInstance().isRan());
|
||||
|
||||
jobManager.setEncryptionKeys(keys);
|
||||
|
||||
assertTrue(PersistentResult.getInstance().isRan());
|
||||
}
|
||||
|
||||
public void testGroupIdExecution() throws InterruptedException {
|
||||
final Object lock = new Object();
|
||||
|
||||
Runnable waitRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
synchronized (lock) {
|
||||
lock.wait();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TestJob testJobOne = new TestJob(JobParameters.newBuilder().withGroupId("foo").create(), waitRunnable);
|
||||
TestJob testJobTwo = new TestJob(JobParameters.newBuilder().withGroupId("foo").create());
|
||||
TestJob testJobThree = new TestJob(JobParameters.newBuilder().withGroupId("bar").create());
|
||||
JobManager jobManager = JobManager.newBuilder(getContext())
|
||||
.withName("transient-test")
|
||||
.withConsumerThreads(3)
|
||||
.build();
|
||||
|
||||
jobManager.add(testJobOne);
|
||||
jobManager.add(testJobTwo);
|
||||
jobManager.add(testJobThree);
|
||||
|
||||
assertTrue(testJobOne.isAdded());
|
||||
assertTrue(testJobTwo.isAdded());
|
||||
assertTrue(testJobThree.isAdded());
|
||||
|
||||
assertTrue(testJobOne.isRan());
|
||||
assertTrue(!testJobTwo.isRan());
|
||||
assertTrue(testJobThree.isRan());
|
||||
|
||||
synchronized (lock) {
|
||||
lock.notifyAll();
|
||||
}
|
||||
|
||||
assertTrue(testJobTwo.isRan());
|
||||
}
|
||||
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package org.whispersystems.jobqueue.jobs;
|
||||
|
||||
import org.whispersystems.jobqueue.EncryptionKeys;
|
||||
import org.whispersystems.jobqueue.Job;
|
||||
import org.whispersystems.jobqueue.JobParameters;
|
||||
import org.whispersystems.jobqueue.requirements.Requirement;
|
||||
import org.whispersystems.jobqueue.util.PersistentResult;
|
||||
|
||||
public class PersistentTestJob extends Job {
|
||||
|
||||
public PersistentTestJob(Requirement requirement) {
|
||||
super(JobParameters.newBuilder().withRequirement(requirement).withPersistence().create());
|
||||
}
|
||||
|
||||
public PersistentTestJob(Requirement requirement, EncryptionKeys keys) {
|
||||
super(JobParameters.newBuilder().withRequirement(requirement).withPersistence().withEncryption(keys).create());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onAdded() {
|
||||
PersistentResult.getInstance().onAdded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws Exception {
|
||||
PersistentResult.getInstance().onRun();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled() {
|
||||
PersistentResult.getInstance().onCanceled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(Exception exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package org.whispersystems.jobqueue.jobs;
|
||||
|
||||
import org.whispersystems.jobqueue.JobParameters;
|
||||
import org.whispersystems.jobqueue.requirements.Requirement;
|
||||
import org.whispersystems.jobqueue.util.RunnableThrowable;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class RequirementDeferringTestJob extends TestJob {
|
||||
|
||||
private final Object FINISHED_LOCK = new Object();
|
||||
|
||||
private boolean finished = false;
|
||||
|
||||
private RunnableThrowable runnable;
|
||||
|
||||
public RequirementDeferringTestJob(Requirement requirement, int retryCount, RunnableThrowable runnable) {
|
||||
super(JobParameters.newBuilder().withRequirement(requirement).withRetryCount(retryCount).create());
|
||||
this.runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws Exception {
|
||||
synchronized (RAN_LOCK) {
|
||||
this.ran = true;
|
||||
}
|
||||
|
||||
if (runnable != null)
|
||||
runnable.run();
|
||||
|
||||
synchronized (FINISHED_LOCK) {
|
||||
this.finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(Exception exception) {
|
||||
if (exception instanceof Exception) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isFinished() throws InterruptedException {
|
||||
synchronized (FINISHED_LOCK) {
|
||||
if (!finished) FINISHED_LOCK.wait(1000);
|
||||
return finished;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package org.whispersystems.jobqueue.jobs;
|
||||
|
||||
import org.whispersystems.jobqueue.JobParameters;
|
||||
import org.whispersystems.jobqueue.requirements.Requirement;
|
||||
|
||||
public class RequirementTestJob extends TestJob {
|
||||
|
||||
public RequirementTestJob(Requirement requirement) {
|
||||
super(JobParameters.newBuilder().withRequirement(requirement).create());
|
||||
}
|
||||
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
package org.whispersystems.jobqueue.jobs;
|
||||
|
||||
import org.whispersystems.jobqueue.Job;
|
||||
import org.whispersystems.jobqueue.JobParameters;
|
||||
|
||||
public class TestJob extends Job {
|
||||
|
||||
private final Object ADDED_LOCK = new Object();
|
||||
protected final Object RAN_LOCK = new Object();
|
||||
private final Object CANCELED_LOCK = new Object();
|
||||
|
||||
private boolean added = false;
|
||||
protected boolean ran = false;
|
||||
private boolean canceled = false;
|
||||
|
||||
private Runnable runnable;
|
||||
|
||||
public TestJob() {
|
||||
this(JobParameters.newBuilder().create());
|
||||
}
|
||||
|
||||
public TestJob(JobParameters parameters) {
|
||||
super(parameters);
|
||||
}
|
||||
|
||||
public TestJob(JobParameters parameters, Runnable runnable) {
|
||||
super(parameters);
|
||||
this.runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdded() {
|
||||
synchronized (ADDED_LOCK) {
|
||||
this.added = true;
|
||||
this.ADDED_LOCK.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws Exception {
|
||||
synchronized (RAN_LOCK) {
|
||||
this.ran = true;
|
||||
}
|
||||
|
||||
if (runnable != null)
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled() {
|
||||
synchronized (CANCELED_LOCK) {
|
||||
this.canceled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(Exception exception) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isAdded() throws InterruptedException {
|
||||
synchronized (ADDED_LOCK) {
|
||||
if (!added) ADDED_LOCK.wait(1000);
|
||||
return added;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRan() throws InterruptedException {
|
||||
synchronized (RAN_LOCK) {
|
||||
if (!ran) RAN_LOCK.wait(1000);
|
||||
return ran;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCanceled() throws InterruptedException {
|
||||
synchronized (CANCELED_LOCK) {
|
||||
if (!canceled) CANCELED_LOCK.wait(1000);
|
||||
return canceled;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package org.whispersystems.jobqueue.util;
|
||||
|
||||
import org.whispersystems.jobqueue.requirements.Requirement;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class MockRequirement implements Requirement {
|
||||
|
||||
private AtomicBoolean present;
|
||||
|
||||
public MockRequirement(boolean present) {
|
||||
this.present = new AtomicBoolean(present);
|
||||
}
|
||||
|
||||
public void setPresent(boolean present) {
|
||||
this.present.set(present);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPresent() {
|
||||
return present.get();
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package org.whispersystems.jobqueue.util;
|
||||
|
||||
import org.whispersystems.jobqueue.requirements.RequirementListener;
|
||||
import org.whispersystems.jobqueue.requirements.RequirementProvider;
|
||||
|
||||
public class MockRequirementProvider implements RequirementProvider {
|
||||
|
||||
private RequirementListener listener;
|
||||
|
||||
public void fireChange() {
|
||||
listener.onRequirementStatusChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListener(RequirementListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "mock-requirement-provider";
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package org.whispersystems.jobqueue.util;
|
||||
|
||||
import org.whispersystems.jobqueue.requirements.Requirement;
|
||||
|
||||
public class PersistentMockRequirement implements Requirement {
|
||||
@Override
|
||||
public boolean isPresent() {
|
||||
return PersistentRequirement.getInstance().isPresent();
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package org.whispersystems.jobqueue.util;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class PersistentRequirement {
|
||||
|
||||
private AtomicBoolean present = new AtomicBoolean(false);
|
||||
|
||||
private static final PersistentRequirement instance = new PersistentRequirement();
|
||||
|
||||
public static PersistentRequirement getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void setPresent(boolean present) {
|
||||
this.present.set(present);
|
||||
}
|
||||
|
||||
public boolean isPresent() {
|
||||
return present.get();
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
package org.whispersystems.jobqueue.util;
|
||||
|
||||
public class PersistentResult {
|
||||
|
||||
private final Object ADDED_LOCK = new Object();
|
||||
private final Object RAN_LOCK = new Object();
|
||||
private final Object CANCELED_LOCK = new Object();
|
||||
|
||||
private boolean added = false;
|
||||
private boolean ran = false;
|
||||
private boolean canceled = false;
|
||||
|
||||
private static final PersistentResult instance = new PersistentResult();
|
||||
|
||||
public static PersistentResult getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void onAdded() {
|
||||
synchronized (ADDED_LOCK) {
|
||||
this.added = true;
|
||||
this.ADDED_LOCK.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void onRun() throws Exception {
|
||||
synchronized (RAN_LOCK) {
|
||||
this.ran = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void onCanceled() {
|
||||
synchronized (CANCELED_LOCK) {
|
||||
this.canceled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAdded() throws InterruptedException {
|
||||
synchronized (ADDED_LOCK) {
|
||||
if (!added) ADDED_LOCK.wait(1000);
|
||||
return added;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRan() throws InterruptedException {
|
||||
synchronized (RAN_LOCK) {
|
||||
if (!ran) RAN_LOCK.wait(1000);
|
||||
return ran;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCanceled() throws InterruptedException {
|
||||
synchronized (CANCELED_LOCK) {
|
||||
if (!canceled) CANCELED_LOCK.wait(1000);
|
||||
return canceled;
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
synchronized (ADDED_LOCK) {
|
||||
this.added = false;
|
||||
}
|
||||
|
||||
synchronized (RAN_LOCK) {
|
||||
this.ran = false;
|
||||
}
|
||||
|
||||
synchronized (CANCELED_LOCK) {
|
||||
this.canceled = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package org.whispersystems.jobqueue.util;
|
||||
|
||||
public interface RunnableThrowable {
|
||||
|
||||
public void run() throws Exception;
|
||||
|
||||
public void shouldThrow(Boolean value);
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.whispersystems.jobqueue">
|
||||
|
||||
<application />
|
||||
|
||||
</manifest>
|
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue;
|
||||
|
||||
public class EncryptionKeys {
|
||||
|
||||
private transient final byte[] encoded;
|
||||
|
||||
public EncryptionKeys(byte[] encoded) {
|
||||
this.encoded = encoded;
|
||||
}
|
||||
|
||||
public byte[] getEncoded() {
|
||||
return encoded;
|
||||
}
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue;
|
||||
|
||||
import org.whispersystems.jobqueue.requirements.Requirement;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An abstract class representing a unit of work that can be scheduled with
|
||||
* the JobManager. This should be extended to implement tasks.
|
||||
*/
|
||||
public abstract class Job implements Serializable {
|
||||
|
||||
private final JobParameters parameters;
|
||||
|
||||
private transient long persistentId;
|
||||
private transient int runIteration;
|
||||
|
||||
public Job(JobParameters parameters) {
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
public List<Requirement> getRequirements() {
|
||||
return parameters.getRequirements();
|
||||
}
|
||||
|
||||
public boolean isRequirementsMet() {
|
||||
for (Requirement requirement : parameters.getRequirements()) {
|
||||
if (!requirement.isPresent()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getGroupId() {
|
||||
return parameters.getGroupId();
|
||||
}
|
||||
|
||||
public boolean isPersistent() {
|
||||
return parameters.isPersistent();
|
||||
}
|
||||
|
||||
public EncryptionKeys getEncryptionKeys() {
|
||||
return parameters.getEncryptionKeys();
|
||||
}
|
||||
|
||||
public void setEncryptionKeys(EncryptionKeys keys) {
|
||||
parameters.setEncryptionKeys(keys);
|
||||
}
|
||||
|
||||
public int getRetryCount() {
|
||||
return parameters.getRetryCount();
|
||||
}
|
||||
|
||||
public void setPersistentId(long persistentId) {
|
||||
this.persistentId = persistentId;
|
||||
}
|
||||
|
||||
public long getPersistentId() {
|
||||
return persistentId;
|
||||
}
|
||||
|
||||
public int getRunIteration() {
|
||||
return runIteration;
|
||||
}
|
||||
|
||||
public void setRunIteration(int runIteration) {
|
||||
this.runIteration = runIteration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a job has been added to the JobManager queue. If it's a persistent job,
|
||||
* the state has been persisted to disk before this method is called.
|
||||
*/
|
||||
public abstract void onAdded();
|
||||
|
||||
/**
|
||||
* Called to actually execute the job.
|
||||
* @throws Exception
|
||||
*/
|
||||
public abstract void onRun() throws Exception;
|
||||
|
||||
/**
|
||||
* If onRun() throws an exception, this method will be called to determine whether the
|
||||
* job should be retried.
|
||||
*
|
||||
* @param exception The exception onRun() threw.
|
||||
* @return true if onRun() should be called again, false otherwise.
|
||||
*/
|
||||
public abstract boolean onShouldRetry(Exception exception);
|
||||
|
||||
/**
|
||||
* Called if a job fails to run (onShouldRetry returned false, or the number of retries exceeded
|
||||
* the job's configured retry count.
|
||||
*/
|
||||
public abstract void onCanceled();
|
||||
|
||||
|
||||
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.whispersystems.jobqueue.persistence.PersistentStorage;
|
||||
|
||||
class JobConsumer extends Thread {
|
||||
|
||||
private static final String TAG = JobConsumer.class.getSimpleName();
|
||||
|
||||
enum JobResult {
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
DEFERRED
|
||||
}
|
||||
|
||||
private final JobQueue jobQueue;
|
||||
private final PersistentStorage persistentStorage;
|
||||
|
||||
public JobConsumer(String name, JobQueue jobQueue, PersistentStorage persistentStorage) {
|
||||
super(name);
|
||||
this.jobQueue = jobQueue;
|
||||
this.persistentStorage = persistentStorage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
Job job = jobQueue.getNext();
|
||||
JobResult result = runJob(job);
|
||||
|
||||
if (result == JobResult.DEFERRED) {
|
||||
jobQueue.push(job);
|
||||
} else {
|
||||
if (result == JobResult.FAILURE) {
|
||||
job.onCanceled();
|
||||
}
|
||||
|
||||
if (job.isPersistent()) {
|
||||
persistentStorage.remove(job.getPersistentId());
|
||||
}
|
||||
}
|
||||
|
||||
if (job.getGroupId() != null) {
|
||||
jobQueue.setGroupIdAvailable(job.getGroupId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private JobResult runJob(Job job) {
|
||||
int retryCount = job.getRetryCount();
|
||||
int runIteration = job.getRunIteration();
|
||||
|
||||
for (;runIteration<retryCount;runIteration++) {
|
||||
try {
|
||||
job.onRun();
|
||||
return JobResult.SUCCESS;
|
||||
} catch (Exception exception) {
|
||||
Log.w(TAG, exception);
|
||||
if (exception instanceof RuntimeException) {
|
||||
throw (RuntimeException)exception;
|
||||
} else if (!job.onShouldRetry(exception)) {
|
||||
return JobResult.FAILURE;
|
||||
} else if (!job.isRequirementsMet()) {
|
||||
job.setRunIteration(runIteration+1);
|
||||
return JobResult.DEFERRED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return JobResult.FAILURE;
|
||||
}
|
||||
|
||||
}
|
@ -1,253 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import org.whispersystems.jobqueue.dependencies.DependencyInjector;
|
||||
import org.whispersystems.jobqueue.persistence.JobSerializer;
|
||||
import org.whispersystems.jobqueue.persistence.PersistentStorage;
|
||||
import org.whispersystems.jobqueue.requirements.RequirementListener;
|
||||
import org.whispersystems.jobqueue.requirements.RequirementProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* A JobManager allows you to enqueue {@link org.whispersystems.jobqueue.Job} tasks
|
||||
* that are executed once a Job's {@link org.whispersystems.jobqueue.requirements.Requirement}s
|
||||
* are met.
|
||||
*/
|
||||
public class JobManager implements RequirementListener {
|
||||
|
||||
private final JobQueue jobQueue = new JobQueue();
|
||||
private final Executor eventExecutor = Executors.newSingleThreadExecutor();
|
||||
private final AtomicBoolean hasLoadedEncrypted = new AtomicBoolean(false);
|
||||
|
||||
private final PersistentStorage persistentStorage;
|
||||
private final List<RequirementProvider> requirementProviders;
|
||||
private final DependencyInjector dependencyInjector;
|
||||
|
||||
private JobManager(Context context, String name,
|
||||
List<RequirementProvider> requirementProviders,
|
||||
DependencyInjector dependencyInjector,
|
||||
JobSerializer jobSerializer, int consumers)
|
||||
{
|
||||
this.persistentStorage = new PersistentStorage(context, name, jobSerializer, dependencyInjector);
|
||||
this.requirementProviders = requirementProviders;
|
||||
this.dependencyInjector = dependencyInjector;
|
||||
|
||||
eventExecutor.execute(new LoadTask(null));
|
||||
|
||||
if (requirementProviders != null && !requirementProviders.isEmpty()) {
|
||||
for (RequirementProvider provider : requirementProviders) {
|
||||
provider.setListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0;i<consumers;i++) {
|
||||
new JobConsumer("JobConsumer-" + i, jobQueue, persistentStorage).start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context An Android {@link android.content.Context}.
|
||||
* @return a {@link org.whispersystems.jobqueue.JobManager.Builder} used to construct a JobManager.
|
||||
*/
|
||||
public static Builder newBuilder(Context context) {
|
||||
return new Builder(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link org.whispersystems.jobqueue.requirements.RequirementProvider} registered with
|
||||
* the JobManager by name.
|
||||
*
|
||||
* @param name The name of the registered {@link org.whispersystems.jobqueue.requirements.RequirementProvider}
|
||||
* @return The RequirementProvider, or null if no provider is registered with that name.
|
||||
*/
|
||||
public RequirementProvider getRequirementProvider(String name) {
|
||||
for (RequirementProvider provider : requirementProviders) {
|
||||
if (provider.getName().equals(name)) {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setEncryptionKeys(EncryptionKeys keys) {
|
||||
if (hasLoadedEncrypted.compareAndSet(false, true)) {
|
||||
eventExecutor.execute(new LoadTask(keys));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a {@link org.whispersystems.jobqueue.Job} to be executed.
|
||||
*
|
||||
* @param job The Job to be executed.
|
||||
*/
|
||||
public void add(final Job job) {
|
||||
eventExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (job.isPersistent()) {
|
||||
persistentStorage.store(job);
|
||||
}
|
||||
|
||||
if (dependencyInjector != null) {
|
||||
dependencyInjector.injectDependencies(job);
|
||||
}
|
||||
|
||||
job.onAdded();
|
||||
jobQueue.add(job);
|
||||
} catch (IOException e) {
|
||||
Log.w("JobManager", e);
|
||||
job.onCanceled();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequirementStatusChanged() {
|
||||
eventExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
jobQueue.onRequirementStatusChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class LoadTask implements Runnable {
|
||||
|
||||
private final EncryptionKeys keys;
|
||||
|
||||
public LoadTask(EncryptionKeys keys) {
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
List<Job> pendingJobs;
|
||||
|
||||
if (keys == null) pendingJobs = persistentStorage.getAllUnencrypted();
|
||||
else pendingJobs = persistentStorage.getAllEncrypted(keys);
|
||||
|
||||
jobQueue.addAll(pendingJobs);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final Context context;
|
||||
private String name;
|
||||
private List<RequirementProvider> requirementProviders;
|
||||
private DependencyInjector dependencyInjector;
|
||||
private JobSerializer jobSerializer;
|
||||
private int consumerThreads;
|
||||
|
||||
Builder(Context context) {
|
||||
this.context = context;
|
||||
this.consumerThreads = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* A name for the {@link org.whispersystems.jobqueue.JobManager}. This is a required parameter,
|
||||
* and is linked to the durable queue used by persistent jobs.
|
||||
*
|
||||
* @param name The name for the JobManager to build.
|
||||
* @return The builder.
|
||||
*/
|
||||
public Builder withName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link org.whispersystems.jobqueue.requirements.RequirementProvider}s to register with this
|
||||
* JobManager. Optional. Each {@link org.whispersystems.jobqueue.requirements.Requirement} an
|
||||
* enqueued Job depends on should have a matching RequirementProvider registered here.
|
||||
*
|
||||
* @param requirementProviders The RequirementProviders
|
||||
* @return The builder.
|
||||
*/
|
||||
public Builder withRequirementProviders(RequirementProvider... requirementProviders) {
|
||||
this.requirementProviders = Arrays.asList(requirementProviders);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link org.whispersystems.jobqueue.dependencies.DependencyInjector} to use for injecting
|
||||
* dependencies into {@link Job}s. Optional. Injection occurs just before a Job's onAdded() callback, or
|
||||
* after deserializing a persistent job.
|
||||
*
|
||||
* @param dependencyInjector The injector to use.
|
||||
* @return The builder.
|
||||
*/
|
||||
public Builder withDependencyInjector(DependencyInjector dependencyInjector) {
|
||||
this.dependencyInjector = dependencyInjector;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link org.whispersystems.jobqueue.persistence.JobSerializer} to use for persistent Jobs.
|
||||
* Required if persistent Jobs are used.
|
||||
*
|
||||
* @param jobSerializer The serializer to use.
|
||||
* @return The builder.
|
||||
*/
|
||||
public Builder withJobSerializer(JobSerializer jobSerializer) {
|
||||
this.jobSerializer = jobSerializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of threads dedicated to consuming Jobs from the queue and executing them.
|
||||
*
|
||||
* @param consumerThreads The number of threads.
|
||||
* @return The builder.
|
||||
*/
|
||||
public Builder withConsumerThreads(int consumerThreads) {
|
||||
this.consumerThreads = consumerThreads;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A constructed JobManager.
|
||||
*/
|
||||
public JobManager build() {
|
||||
if (name == null) {
|
||||
throw new IllegalArgumentException("You must specify a name!");
|
||||
}
|
||||
|
||||
if (requirementProviders == null) {
|
||||
requirementProviders = new LinkedList<>();
|
||||
}
|
||||
|
||||
return new JobManager(context, name, requirementProviders,
|
||||
dependencyInjector, jobSerializer,
|
||||
consumerThreads);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue;
|
||||
|
||||
import org.whispersystems.jobqueue.requirements.Requirement;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The set of parameters that describe a {@link org.whispersystems.jobqueue.Job}.
|
||||
*/
|
||||
public class JobParameters implements Serializable {
|
||||
|
||||
private transient EncryptionKeys encryptionKeys;
|
||||
|
||||
private final List<Requirement> requirements;
|
||||
private final boolean isPersistent;
|
||||
private final int retryCount;
|
||||
private final String groupId;
|
||||
|
||||
private JobParameters(List<Requirement> requirements,
|
||||
boolean isPersistent, String groupId,
|
||||
EncryptionKeys encryptionKeys,
|
||||
int retryCount)
|
||||
{
|
||||
this.requirements = requirements;
|
||||
this.isPersistent = isPersistent;
|
||||
this.groupId = groupId;
|
||||
this.encryptionKeys = encryptionKeys;
|
||||
this.retryCount = retryCount;
|
||||
}
|
||||
|
||||
public List<Requirement> getRequirements() {
|
||||
return requirements;
|
||||
}
|
||||
|
||||
public boolean isPersistent() {
|
||||
return isPersistent;
|
||||
}
|
||||
|
||||
public EncryptionKeys getEncryptionKeys() {
|
||||
return encryptionKeys;
|
||||
}
|
||||
|
||||
public void setEncryptionKeys(EncryptionKeys encryptionKeys) {
|
||||
this.encryptionKeys = encryptionKeys;
|
||||
}
|
||||
|
||||
public int getRetryCount() {
|
||||
return retryCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a builder used to construct JobParameters.
|
||||
*/
|
||||
public static Builder newBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private List<Requirement> requirements = new LinkedList<>();
|
||||
private boolean isPersistent = false;
|
||||
private EncryptionKeys encryptionKeys = null;
|
||||
private int retryCount = 100;
|
||||
private String groupId = null;
|
||||
|
||||
/**
|
||||
* Specify a {@link org.whispersystems.jobqueue.requirements.Requirement }that must be met
|
||||
* before the Job is executed. May be called multiple times to register multiple requirements.
|
||||
* @param requirement The Requirement that must be met.
|
||||
* @return the builder.
|
||||
*/
|
||||
public Builder withRequirement(Requirement requirement) {
|
||||
this.requirements.add(requirement);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that the Job should be durably persisted to disk, so that it remains in the queue
|
||||
* across application restarts.
|
||||
* @return The builder.
|
||||
*/
|
||||
public Builder withPersistence() {
|
||||
this.isPersistent = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that the job should use encryption when durably persisted to disk.
|
||||
* @param encryptionKeys The keys to encrypt the serialized job with before persisting.
|
||||
* @return the builder.
|
||||
*/
|
||||
public Builder withEncryption(EncryptionKeys encryptionKeys) {
|
||||
this.encryptionKeys = encryptionKeys;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify how many times the job should be retried if execution fails but onShouldRetry() returns
|
||||
* true.
|
||||
*
|
||||
* @param retryCount The number of times the job should be retried.
|
||||
* @return the builder.
|
||||
*/
|
||||
public Builder withRetryCount(int retryCount) {
|
||||
this.retryCount = retryCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a groupId the job should belong to. Jobs with the same groupId are guaranteed to be
|
||||
* executed serially.
|
||||
*
|
||||
* @param groupId The job's groupId.
|
||||
* @return the builder.
|
||||
*/
|
||||
public Builder withGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the JobParameters instance that describes a Job.
|
||||
*/
|
||||
public JobParameters create() {
|
||||
return new JobParameters(requirements, isPersistent, groupId, encryptionKeys, retryCount);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
|
||||
class JobQueue {
|
||||
|
||||
private final Set<String> activeGroupIds = new HashSet<>();
|
||||
private final LinkedList<Job> jobQueue = new LinkedList<>();
|
||||
|
||||
synchronized void onRequirementStatusChanged() {
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
synchronized void add(Job job) {
|
||||
jobQueue.add(job);
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
synchronized void addAll(List<Job> jobs) {
|
||||
jobQueue.addAll(jobs);
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
synchronized void push(Job job) {
|
||||
jobQueue.push(job);
|
||||
}
|
||||
|
||||
synchronized Job getNext() {
|
||||
try {
|
||||
Job nextAvailableJob;
|
||||
|
||||
while ((nextAvailableJob = getNextAvailableJob()) == null) {
|
||||
wait();
|
||||
}
|
||||
|
||||
return nextAvailableJob;
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void setGroupIdAvailable(String groupId) {
|
||||
if (groupId != null) {
|
||||
activeGroupIds.remove(groupId);
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
private Job getNextAvailableJob() {
|
||||
if (jobQueue.isEmpty()) return null;
|
||||
|
||||
ListIterator<Job> iterator = jobQueue.listIterator();
|
||||
while (iterator.hasNext()) {
|
||||
Job job = iterator.next();
|
||||
|
||||
if (job.isRequirementsMet() && isGroupIdAvailable(job.getGroupId())) {
|
||||
iterator.remove();
|
||||
setGroupIdUnavailable(job.getGroupId());
|
||||
return job;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isGroupIdAvailable(String groupId) {
|
||||
return groupId == null || !activeGroupIds.contains(groupId);
|
||||
}
|
||||
|
||||
private void setGroupIdUnavailable(String groupId) {
|
||||
if (groupId != null) {
|
||||
activeGroupIds.add(groupId);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue.dependencies;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Any Job or Requirement that depends on {@link android.content.Context} can implement this
|
||||
* interface to receive a Context after being deserialized.
|
||||
*/
|
||||
public interface ContextDependent {
|
||||
public void setContext(Context context);
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue.dependencies;
|
||||
|
||||
/**
|
||||
* Interface responsible for injecting dependencies into Jobs.
|
||||
*/
|
||||
public interface DependencyInjector {
|
||||
public void injectDependencies(Object object);
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue.persistence;
|
||||
|
||||
import android.util.Base64;
|
||||
|
||||
import org.whispersystems.jobqueue.EncryptionKeys;
|
||||
import org.whispersystems.jobqueue.Job;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
/**
|
||||
* An implementation of {@link org.whispersystems.jobqueue.persistence.JobSerializer} that uses
|
||||
* Java Serialization.
|
||||
*/
|
||||
public class JavaJobSerializer implements JobSerializer {
|
||||
|
||||
public JavaJobSerializer() {}
|
||||
|
||||
@Override
|
||||
public String serialize(Job job) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||
oos.writeObject(job);
|
||||
|
||||
return Base64.encodeToString(baos.toByteArray(), Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Job deserialize(EncryptionKeys keys, boolean encrypted, String serialized) throws IOException {
|
||||
try {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(serialized, Base64.NO_WRAP));
|
||||
ObjectInputStream ois = new ObjectInputStream(bais);
|
||||
|
||||
return (Job)ois.readObject();
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue.persistence;
|
||||
|
||||
import org.whispersystems.jobqueue.EncryptionKeys;
|
||||
import org.whispersystems.jobqueue.Job;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A JobSerializer is responsible for serializing and deserializing persistent jobs.
|
||||
*/
|
||||
public interface JobSerializer {
|
||||
|
||||
/**
|
||||
* Serialize a job object into a string.
|
||||
* @param job The Job to serialize.
|
||||
* @return The serialized Job.
|
||||
* @throws IOException if serialization fails.
|
||||
*/
|
||||
public String serialize(Job job) throws IOException;
|
||||
|
||||
/**
|
||||
* Deserialize a String into a Job.
|
||||
* @param keys Optional encryption keys that could have been used.
|
||||
* @param encrypted True if the job was encrypted using the encryption keys.
|
||||
* @param serialized The serialized Job.
|
||||
* @return The deserialized Job.
|
||||
* @throws IOException If the Job deserialization fails.
|
||||
*/
|
||||
public Job deserialize(EncryptionKeys keys, boolean encrypted, String serialized) throws IOException;
|
||||
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue.persistence;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
||||
import org.whispersystems.jobqueue.EncryptionKeys;
|
||||
import org.whispersystems.jobqueue.Job;
|
||||
import org.whispersystems.jobqueue.dependencies.ContextDependent;
|
||||
import org.whispersystems.jobqueue.dependencies.DependencyInjector;
|
||||
import org.whispersystems.jobqueue.requirements.Requirement;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class PersistentStorage {
|
||||
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
|
||||
private static final String TABLE_NAME = "queue";
|
||||
private static final String ID = "_id";
|
||||
private static final String ITEM = "item";
|
||||
private static final String ENCRYPTED = "encrypted";
|
||||
|
||||
private static final String DATABASE_CREATE = String.format("CREATE TABLE %s (%s INTEGER PRIMARY KEY, %s TEXT NOT NULL, %s INTEGER DEFAULT 0);",
|
||||
TABLE_NAME, ID, ITEM, ENCRYPTED);
|
||||
|
||||
private final Context context;
|
||||
private final DatabaseHelper databaseHelper;
|
||||
private final JobSerializer jobSerializer;
|
||||
private final DependencyInjector dependencyInjector;
|
||||
|
||||
public PersistentStorage(Context context, String name,
|
||||
JobSerializer serializer,
|
||||
DependencyInjector dependencyInjector)
|
||||
{
|
||||
this.databaseHelper = new DatabaseHelper(context, "_jobqueue-" + name);
|
||||
this.context = context;
|
||||
this.jobSerializer = serializer;
|
||||
this.dependencyInjector = dependencyInjector;
|
||||
}
|
||||
|
||||
public void store(Job job) throws IOException {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(ITEM, jobSerializer.serialize(job));
|
||||
contentValues.put(ENCRYPTED, job.getEncryptionKeys() != null);
|
||||
|
||||
long id = databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues);
|
||||
job.setPersistentId(id);
|
||||
}
|
||||
|
||||
public List<Job> getAllUnencrypted() {
|
||||
return getJobs(null, ENCRYPTED + " = 0");
|
||||
}
|
||||
|
||||
public List<Job> getAllEncrypted(EncryptionKeys keys) {
|
||||
return getJobs(keys, ENCRYPTED + " = 1");
|
||||
}
|
||||
|
||||
private List<Job> getJobs(EncryptionKeys keys, String where) {
|
||||
List<Job> results = new LinkedList<>();
|
||||
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
||||
Cursor cursor = null;
|
||||
|
||||
try {
|
||||
cursor = database.query(TABLE_NAME, null, where, null, null, null, ID + " ASC", null);
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
||||
String item = cursor.getString(cursor.getColumnIndexOrThrow(ITEM));
|
||||
boolean encrypted = cursor.getInt(cursor.getColumnIndexOrThrow(ENCRYPTED)) == 1;
|
||||
|
||||
try{
|
||||
Job job = jobSerializer.deserialize(keys, encrypted, item);
|
||||
|
||||
job.setPersistentId(id);
|
||||
job.setEncryptionKeys(keys);
|
||||
injectDependencies(job);
|
||||
|
||||
results.add(job);
|
||||
} catch (IOException e) {
|
||||
Log.w("PersistentStore", e);
|
||||
remove(id);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public void remove(long id) {
|
||||
databaseHelper.getWritableDatabase()
|
||||
.delete(TABLE_NAME, ID + " = ?", new String[] {String.valueOf(id)});
|
||||
}
|
||||
|
||||
private void injectDependencies(Job job) {
|
||||
if (job instanceof ContextDependent) {
|
||||
((ContextDependent)job).setContext(context);
|
||||
}
|
||||
|
||||
for (Requirement requirement : job.getRequirements()) {
|
||||
if (requirement instanceof ContextDependent) {
|
||||
((ContextDependent)requirement).setContext(context);
|
||||
}
|
||||
}
|
||||
|
||||
if (dependencyInjector != null) {
|
||||
dependencyInjector.injectDependencies(job);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DatabaseHelper extends SQLiteOpenHelper {
|
||||
|
||||
public DatabaseHelper(Context context, String name) {
|
||||
super(context, name, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(DATABASE_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue.requirements;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
|
||||
import org.whispersystems.jobqueue.dependencies.ContextDependent;
|
||||
|
||||
/**
|
||||
* A requirement that is satisfied when a network connection is present.
|
||||
*/
|
||||
public class NetworkRequirement implements Requirement, ContextDependent {
|
||||
|
||||
private transient Context context;
|
||||
|
||||
public NetworkRequirement(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public NetworkRequirement() {}
|
||||
|
||||
@Override
|
||||
public boolean isPresent() {
|
||||
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo netInfo = cm.getActiveNetworkInfo();
|
||||
|
||||
return netInfo != null && netInfo.isConnectedOrConnecting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContext(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue.requirements;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
|
||||
public class NetworkRequirementProvider implements RequirementProvider {
|
||||
|
||||
private RequirementListener listener;
|
||||
|
||||
private final NetworkRequirement requirement;
|
||||
|
||||
public NetworkRequirementProvider(Context context) {
|
||||
this.requirement = new NetworkRequirement(context);
|
||||
|
||||
context.getApplicationContext().registerReceiver(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (requirement.isPresent()) {
|
||||
listener.onRequirementStatusChanged();
|
||||
}
|
||||
}
|
||||
}, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "network";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListener(RequirementListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue.requirements;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* A Requirement that must be satisfied before a Job can run.
|
||||
*/
|
||||
public interface Requirement extends Serializable {
|
||||
/**
|
||||
* @return true if the requirement is satisfied, false otherwise.
|
||||
*/
|
||||
public boolean isPresent();
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue.requirements;
|
||||
|
||||
public interface RequirementListener {
|
||||
public void onRequirementStatusChanged();
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.jobqueue.requirements;
|
||||
|
||||
/**
|
||||
* Notifies listeners when a {@link org.whispersystems.jobqueue.requirements.Requirement}'s
|
||||
* state is likely to have changed.
|
||||
*/
|
||||
public interface RequirementProvider {
|
||||
/**
|
||||
* @return The name of the provider.
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* The {@link org.whispersystems.jobqueue.requirements.RequirementListener} to call when
|
||||
* a {@link org.whispersystems.jobqueue.requirements.Requirement}'s status is likely to
|
||||
* have changed.
|
||||
*
|
||||
* @param listener The listener to call.
|
||||
*/
|
||||
public void setListener(RequirementListener listener);
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
<resources>
|
||||
</resources>
|
@ -1 +1 @@
|
||||
include ':libtextsecure', ':libaxolotl', ':jobqueue'
|
||||
include ':libtextsecure', ':libaxolotl'
|
||||
|
Loading…
x
Reference in New Issue
Block a user