2019-03-28 15:56:35 +00:00
package org.thoughtcrime.securesms.jobmanager ;
import android.app.Application ;
2020-09-29 14:00:46 +00:00
2019-06-05 19:47:14 +00:00
import androidx.annotation.NonNull ;
import androidx.annotation.Nullable ;
import androidx.annotation.WorkerThread ;
2019-03-28 15:56:35 +00:00
2020-06-06 22:49:19 +00:00
import com.annimon.stream.Collectors ;
2019-03-28 15:56:35 +00:00
import com.annimon.stream.Stream ;
2020-12-04 23:31:58 +00:00
import org.signal.core.util.logging.Log ;
2019-03-28 15:56:35 +00:00
import org.thoughtcrime.securesms.jobmanager.persistence.ConstraintSpec ;
import org.thoughtcrime.securesms.jobmanager.persistence.DependencySpec ;
import org.thoughtcrime.securesms.jobmanager.persistence.FullSpec ;
import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec ;
import org.thoughtcrime.securesms.jobmanager.persistence.JobStorage ;
import org.thoughtcrime.securesms.util.Debouncer ;
import java.util.ArrayList ;
2020-01-08 02:00:32 +00:00
import java.util.Collection ;
2019-03-28 15:56:35 +00:00
import java.util.Collections ;
2020-01-03 19:10:16 +00:00
import java.util.HashMap ;
2021-02-10 17:08:50 +00:00
import java.util.HashSet ;
2019-03-28 15:56:35 +00:00
import java.util.LinkedList ;
import java.util.List ;
2020-01-03 19:10:16 +00:00
import java.util.Map ;
2020-06-06 22:49:19 +00:00
import java.util.Set ;
2022-03-21 22:40:17 +00:00
import java.util.function.Predicate ;
2019-03-28 15:56:35 +00:00
/ * *
* Manages the queue of jobs . This is the only class that should write to { @link JobStorage } to
* ensure consistency .
* /
class JobController {
2021-03-29 22:37:22 +00:00
private static final String TAG = Log . tag ( JobController . class ) ;
2019-03-28 15:56:35 +00:00
private final Application application ;
private final JobStorage jobStorage ;
private final JobInstantiator jobInstantiator ;
private final ConstraintInstantiator constraintInstantiator ;
private final Data . Serializer dataSerializer ;
2019-10-15 20:46:29 +00:00
private final JobTracker jobTracker ;
2019-03-28 15:56:35 +00:00
private final Scheduler scheduler ;
private final Debouncer debouncer ;
private final Callback callback ;
2020-01-03 19:10:16 +00:00
private final Map < String , Job > runningJobs ;
2019-03-28 15:56:35 +00:00
JobController ( @NonNull Application application ,
@NonNull JobStorage jobStorage ,
@NonNull JobInstantiator jobInstantiator ,
@NonNull ConstraintInstantiator constraintInstantiator ,
@NonNull Data . Serializer dataSerializer ,
2019-10-15 20:46:29 +00:00
@NonNull JobTracker jobTracker ,
2019-03-28 15:56:35 +00:00
@NonNull Scheduler scheduler ,
@NonNull Debouncer debouncer ,
@NonNull Callback callback )
{
this . application = application ;
this . jobStorage = jobStorage ;
this . jobInstantiator = jobInstantiator ;
this . constraintInstantiator = constraintInstantiator ;
this . dataSerializer = dataSerializer ;
2019-10-15 20:46:29 +00:00
this . jobTracker = jobTracker ;
2019-03-28 15:56:35 +00:00
this . scheduler = scheduler ;
this . debouncer = debouncer ;
this . callback = callback ;
2020-01-03 19:10:16 +00:00
this . runningJobs = new HashMap < > ( ) ;
2019-03-28 15:56:35 +00:00
}
@WorkerThread
synchronized void init ( ) {
jobStorage . updateAllJobsToBePending ( ) ;
notifyAll ( ) ;
}
synchronized void wakeUp ( ) {
notifyAll ( ) ;
}
2020-06-23 01:34:31 +00:00
@WorkerThread
2019-03-28 15:56:35 +00:00
synchronized void submitNewJobChain ( @NonNull List < List < Job > > chain ) {
chain = Stream . of ( chain ) . filterNot ( List : : isEmpty ) . toList ( ) ;
if ( chain . isEmpty ( ) ) {
Log . w ( TAG , "Tried to submit an empty job chain. Skipping." ) ;
return ;
}
if ( chainExceedsMaximumInstances ( chain ) ) {
Job solo = chain . get ( 0 ) . get ( 0 ) ;
2020-03-06 21:56:01 +00:00
jobTracker . onStateChange ( solo , JobTracker . JobState . IGNORED ) ;
2020-12-07 22:30:05 +00:00
Log . w ( TAG , JobLogger . format ( solo , "Already at the max instance count. Factory limit: " + solo . getParameters ( ) . getMaxInstancesForFactory ( ) + ", Queue limit: " + solo . getParameters ( ) . getMaxInstancesForQueue ( ) + ". Skipping." ) ) ;
2019-03-28 15:56:35 +00:00
return ;
}
insertJobChain ( chain ) ;
triggerOnSubmit ( chain ) ;
notifyAll ( ) ;
2021-08-02 21:46:56 +00:00
scheduleJobs ( chain . get ( 0 ) ) ;
2019-03-28 15:56:35 +00:00
}
2020-01-08 02:00:32 +00:00
@WorkerThread
2020-06-06 22:49:19 +00:00
synchronized void submitJobWithExistingDependencies ( @NonNull Job job , @NonNull Collection < String > dependsOn , @Nullable String dependsOnQueue ) {
2020-01-08 02:00:32 +00:00
List < List < Job > > chain = Collections . singletonList ( Collections . singletonList ( job ) ) ;
if ( chainExceedsMaximumInstances ( chain ) ) {
2020-03-06 21:56:01 +00:00
jobTracker . onStateChange ( job , JobTracker . JobState . IGNORED ) ;
2020-12-07 22:30:05 +00:00
Log . w ( TAG , JobLogger . format ( job , "Already at the max instance count. Factory limit: " + job . getParameters ( ) . getMaxInstancesForFactory ( ) + ", Queue limit: " + job . getParameters ( ) . getMaxInstancesForQueue ( ) + ". Skipping." ) ) ;
2020-01-08 02:00:32 +00:00
return ;
}
2021-02-10 17:08:50 +00:00
Set < String > allDependsOn = new HashSet < > ( dependsOn ) ;
Set < String > aliveDependsOn = Stream . of ( dependsOn )
. filter ( id - > jobStorage . getJobSpec ( id ) ! = null )
. collect ( Collectors . toSet ( ) ) ;
2020-01-08 02:00:32 +00:00
2020-06-06 22:49:19 +00:00
if ( dependsOnQueue ! = null ) {
2021-02-10 17:08:50 +00:00
List < String > inQueue = Stream . of ( jobStorage . getJobsInQueue ( dependsOnQueue ) )
. map ( JobSpec : : getId )
. toList ( ) ;
allDependsOn . addAll ( inQueue ) ;
aliveDependsOn . addAll ( inQueue ) ;
}
if ( jobTracker . haveAnyFailed ( allDependsOn ) ) {
Log . w ( TAG , "This job depends on a job that failed! Failing this job immediately." ) ;
List < Job > dependents = onFailure ( job ) ;
2021-02-11 16:43:21 +00:00
job . setContext ( application ) ;
2021-02-10 17:08:50 +00:00
job . onFailure ( ) ;
Stream . of ( dependents ) . forEach ( Job : : onFailure ) ;
return ;
2020-06-06 22:49:19 +00:00
}
2021-02-10 17:08:50 +00:00
FullSpec fullSpec = buildFullSpec ( job , aliveDependsOn ) ;
2020-01-08 02:00:32 +00:00
jobStorage . insertJobs ( Collections . singletonList ( fullSpec ) ) ;
scheduleJobs ( Collections . singletonList ( job ) ) ;
triggerOnSubmit ( chain ) ;
notifyAll ( ) ;
}
2020-01-03 19:10:16 +00:00
@WorkerThread
synchronized void cancelJob ( @NonNull String id ) {
Job runningJob = runningJobs . get ( id ) ;
if ( runningJob ! = null ) {
Log . w ( TAG , JobLogger . format ( runningJob , "Canceling while running." ) ) ;
runningJob . cancel ( ) ;
} else {
JobSpec jobSpec = jobStorage . getJobSpec ( id ) ;
if ( jobSpec ! = null ) {
Job job = createJob ( jobSpec , jobStorage . getConstraintSpecs ( id ) ) ;
Log . w ( TAG , JobLogger . format ( job , "Canceling while inactive." ) ) ;
Log . w ( TAG , JobLogger . format ( job , "Job failed." ) ) ;
2020-01-08 20:56:51 +00:00
job . cancel ( ) ;
2021-02-10 17:08:50 +00:00
List < Job > dependents = onFailure ( job ) ;
2020-01-03 19:10:16 +00:00
job . onFailure ( ) ;
2021-02-10 17:08:50 +00:00
Stream . of ( dependents ) . forEach ( Job : : onFailure ) ;
2020-01-03 19:10:16 +00:00
} else {
Log . w ( TAG , "Tried to cancel JOB::" + id + ", but it could not be found." ) ;
}
}
}
2020-06-12 14:06:20 +00:00
@WorkerThread
synchronized void cancelAllInQueue ( @NonNull String queue ) {
2020-09-29 14:00:46 +00:00
Stream . of ( jobStorage . getJobsInQueue ( queue ) )
. map ( JobSpec : : getId )
2020-06-12 14:06:20 +00:00
. forEach ( this : : cancelJob ) ;
}
2021-05-05 16:49:18 +00:00
@WorkerThread
synchronized void update ( @NonNull JobUpdater updater ) {
List < JobSpec > allJobs = jobStorage . getAllJobSpecs ( ) ;
List < JobSpec > updatedJobs = new LinkedList < > ( ) ;
for ( JobSpec job : allJobs ) {
JobSpec updated = updater . update ( job , dataSerializer ) ;
if ( updated ! = job ) {
updatedJobs . add ( updated ) ;
}
}
jobStorage . updateJobs ( updatedJobs ) ;
notifyAll ( ) ;
}
2022-03-21 22:40:17 +00:00
@WorkerThread
synchronized List < JobSpec > findJobs ( @NonNull Predicate < JobSpec > predicate ) {
return Stream . of ( jobStorage . getAllJobSpecs ( ) )
. filter ( predicate : : test )
. toList ( ) ;
}
2019-03-28 15:56:35 +00:00
@WorkerThread
2021-01-28 14:48:09 +00:00
synchronized void onRetry ( @NonNull Job job , long backoffInterval ) {
if ( backoffInterval < = 0 ) {
throw new IllegalArgumentException ( "Invalid backoff interval! " + backoffInterval ) ;
}
2019-11-14 04:58:59 +00:00
int nextRunAttempt = job . getRunAttempt ( ) + 1 ;
2021-01-28 14:48:09 +00:00
long nextRunAttemptTime = System . currentTimeMillis ( ) + backoffInterval ;
2019-11-14 04:58:59 +00:00
String serializedData = dataSerializer . serialize ( job . serialize ( ) ) ;
2019-03-28 15:56:35 +00:00
2019-11-14 04:58:59 +00:00
jobStorage . updateJobAfterRetry ( job . getId ( ) , false , nextRunAttempt , nextRunAttemptTime , serializedData ) ;
2020-03-06 21:56:01 +00:00
jobTracker . onStateChange ( job , JobTracker . JobState . PENDING ) ;
2019-03-28 15:56:35 +00:00
List < Constraint > constraints = Stream . of ( jobStorage . getConstraintSpecs ( job . getId ( ) ) )
. map ( ConstraintSpec : : getFactoryKey )
. map ( constraintInstantiator : : instantiate )
. toList ( ) ;
long delay = Math . max ( 0 , nextRunAttemptTime - System . currentTimeMillis ( ) ) ;
Log . i ( TAG , JobLogger . format ( job , "Scheduling a retry in " + delay + " ms." ) ) ;
scheduler . schedule ( delay , constraints ) ;
notifyAll ( ) ;
}
synchronized void onJobFinished ( @NonNull Job job ) {
runningJobs . remove ( job . getId ( ) ) ;
}
@WorkerThread
2020-04-10 17:13:27 +00:00
synchronized void onSuccess ( @NonNull Job job , @Nullable Data outputData ) {
if ( outputData ! = null ) {
List < JobSpec > updates = Stream . of ( jobStorage . getDependencySpecsThatDependOnJob ( job . getId ( ) ) )
. map ( DependencySpec : : getJobId )
. map ( jobStorage : : getJobSpec )
. map ( jobSpec - > mapToJobWithInputData ( jobSpec , outputData ) )
. toList ( ) ;
jobStorage . updateJobs ( updates ) ;
}
2019-03-28 15:56:35 +00:00
jobStorage . deleteJob ( job . getId ( ) ) ;
2020-03-06 21:56:01 +00:00
jobTracker . onStateChange ( job , JobTracker . JobState . SUCCESS ) ;
2019-03-28 15:56:35 +00:00
notifyAll ( ) ;
}
/ * *
* @return The list of all dependent jobs that should also be failed .
* /
@WorkerThread
synchronized @NonNull List < Job > onFailure ( @NonNull Job job ) {
List < Job > dependents = Stream . of ( jobStorage . getDependencySpecsThatDependOnJob ( job . getId ( ) ) )
. map ( DependencySpec : : getJobId )
. map ( jobStorage : : getJobSpec )
. withoutNulls ( )
. map ( jobSpec - > {
List < ConstraintSpec > constraintSpecs = jobStorage . getConstraintSpecs ( jobSpec . getId ( ) ) ;
return createJob ( jobSpec , constraintSpecs ) ;
} )
. toList ( ) ;
List < Job > all = new ArrayList < > ( dependents . size ( ) + 1 ) ;
all . add ( job ) ;
all . addAll ( dependents ) ;
jobStorage . deleteJobs ( Stream . of ( all ) . map ( Job : : getId ) . toList ( ) ) ;
2020-03-06 21:56:01 +00:00
Stream . of ( all ) . forEach ( j - > jobTracker . onStateChange ( j , JobTracker . JobState . FAILURE ) ) ;
2019-03-28 15:56:35 +00:00
return dependents ;
}
/ * *
* Retrieves the next job that is eligible for execution . To be ' eligible ' means that the job :
* - Has no dependencies
* - Has no unmet constraints
*
* This method will block until a job is available .
* When the job returned from this method has been run , you must call { @link # onJobFinished ( Job ) } .
* /
@WorkerThread
2020-06-25 15:48:23 +00:00
synchronized @NonNull Job pullNextEligibleJobForExecution ( @NonNull JobPredicate predicate ) {
2019-03-28 15:56:35 +00:00
try {
Job job ;
2020-06-25 15:48:23 +00:00
while ( ( job = getNextEligibleJobForExecution ( predicate ) ) = = null ) {
2019-03-28 15:56:35 +00:00
if ( runningJobs . isEmpty ( ) ) {
debouncer . publish ( callback : : onEmpty ) ;
}
wait ( ) ;
}
jobStorage . updateJobRunningState ( job . getId ( ) , true ) ;
2020-01-03 19:10:16 +00:00
runningJobs . put ( job . getId ( ) , job ) ;
2020-03-06 21:56:01 +00:00
jobTracker . onStateChange ( job , JobTracker . JobState . RUNNING ) ;
2019-03-28 15:56:35 +00:00
return job ;
} catch ( InterruptedException e ) {
Log . e ( TAG , "Interrupted." ) ;
throw new AssertionError ( e ) ;
}
}
/ * *
* Retrieves a string representing the state of the job queue . Intended for debugging .
* /
@WorkerThread
synchronized @NonNull String getDebugInfo ( ) {
List < JobSpec > jobs = jobStorage . getAllJobSpecs ( ) ;
List < ConstraintSpec > constraints = jobStorage . getAllConstraintSpecs ( ) ;
List < DependencySpec > dependencies = jobStorage . getAllDependencySpecs ( ) ;
StringBuilder info = new StringBuilder ( ) ;
info . append ( "-- Jobs\n" ) ;
if ( ! jobs . isEmpty ( ) ) {
Stream . of ( jobs ) . forEach ( j - > info . append ( j . toString ( ) ) . append ( '\n' ) ) ;
} else {
info . append ( "None\n" ) ;
}
info . append ( "\n-- Constraints\n" ) ;
if ( ! constraints . isEmpty ( ) ) {
Stream . of ( constraints ) . forEach ( c - > info . append ( c . toString ( ) ) . append ( '\n' ) ) ;
} else {
info . append ( "None\n" ) ;
}
info . append ( "\n-- Dependencies\n" ) ;
if ( ! dependencies . isEmpty ( ) ) {
Stream . of ( dependencies ) . forEach ( d - > info . append ( d . toString ( ) ) . append ( '\n' ) ) ;
} else {
info . append ( "None\n" ) ;
}
return info . toString ( ) ;
}
2021-02-23 23:34:18 +00:00
synchronized boolean areQueuesEmpty ( @NonNull Set < String > queueKeys ) {
return jobStorage . areQueuesEmpty ( queueKeys ) ;
}
2019-03-28 15:56:35 +00:00
@WorkerThread
private boolean chainExceedsMaximumInstances ( @NonNull List < List < Job > > chain ) {
if ( chain . size ( ) = = 1 & & chain . get ( 0 ) . size ( ) = = 1 ) {
Job solo = chain . get ( 0 ) . get ( 0 ) ;
2020-12-07 22:30:05 +00:00
boolean exceedsFactory = solo . getParameters ( ) . getMaxInstancesForFactory ( ) ! = Job . Parameters . UNLIMITED & &
jobStorage . getJobCountForFactory ( solo . getFactoryKey ( ) ) > = solo . getParameters ( ) . getMaxInstancesForFactory ( ) ;
if ( exceedsFactory ) {
return true ;
}
boolean exceedsQueue = solo . getParameters ( ) . getQueue ( ) ! = null & &
solo . getParameters ( ) . getMaxInstancesForQueue ( ) ! = Job . Parameters . UNLIMITED & &
2021-02-06 15:54:52 +00:00
jobStorage . getJobCountForFactoryAndQueue ( solo . getFactoryKey ( ) , solo . getParameters ( ) . getQueue ( ) ) > = solo . getParameters ( ) . getMaxInstancesForQueue ( ) ;
2020-12-07 22:30:05 +00:00
if ( exceedsQueue ) {
2019-03-28 15:56:35 +00:00
return true ;
}
}
2020-12-07 22:30:05 +00:00
2019-03-28 15:56:35 +00:00
return false ;
}
@WorkerThread
private void triggerOnSubmit ( @NonNull List < List < Job > > chain ) {
Stream . of ( chain )
. forEach ( list - > Stream . of ( list ) . forEach ( job - > {
job . setContext ( application ) ;
job . onSubmit ( ) ;
} ) ) ;
}
@WorkerThread
private void insertJobChain ( @NonNull List < List < Job > > chain ) {
List < FullSpec > fullSpecs = new LinkedList < > ( ) ;
2020-01-08 02:00:32 +00:00
List < String > dependsOn = Collections . emptyList ( ) ;
2019-03-28 15:56:35 +00:00
for ( List < Job > jobList : chain ) {
for ( Job job : jobList ) {
fullSpecs . add ( buildFullSpec ( job , dependsOn ) ) ;
}
2020-01-08 02:00:32 +00:00
dependsOn = Stream . of ( jobList ) . map ( Job : : getId ) . toList ( ) ;
2019-03-28 15:56:35 +00:00
}
jobStorage . insertJobs ( fullSpecs ) ;
}
@WorkerThread
2020-01-08 02:00:32 +00:00
private @NonNull FullSpec buildFullSpec ( @NonNull Job job , @NonNull Collection < String > dependsOn ) {
2019-03-28 15:56:35 +00:00
job . setRunAttempt ( 0 ) ;
JobSpec jobSpec = new JobSpec ( job . getId ( ) ,
job . getFactoryKey ( ) ,
job . getParameters ( ) . getQueue ( ) ,
2019-09-05 18:39:18 +00:00
System . currentTimeMillis ( ) ,
2019-03-28 15:56:35 +00:00
job . getNextRunAttemptTime ( ) ,
job . getRunAttempt ( ) ,
job . getParameters ( ) . getMaxAttempts ( ) ,
job . getParameters ( ) . getLifespan ( ) ,
dataSerializer . serialize ( job . serialize ( ) ) ,
2020-04-10 17:13:27 +00:00
null ,
2020-06-25 12:32:36 +00:00
false ,
job . getParameters ( ) . isMemoryOnly ( ) ) ;
2019-03-28 15:56:35 +00:00
List < ConstraintSpec > constraintSpecs = Stream . of ( job . getParameters ( ) . getConstraintKeys ( ) )
2020-06-25 12:32:36 +00:00
. map ( key - > new ConstraintSpec ( jobSpec . getId ( ) , key , jobSpec . isMemoryOnly ( ) ) )
2019-03-28 15:56:35 +00:00
. toList ( ) ;
List < DependencySpec > dependencySpecs = Stream . of ( dependsOn )
2020-06-25 12:32:36 +00:00
. map ( depends - > {
JobSpec dependsOnJobSpec = jobStorage . getJobSpec ( depends ) ;
boolean memoryOnly = job . getParameters ( ) . isMemoryOnly ( ) | | ( dependsOnJobSpec ! = null & & dependsOnJobSpec . isMemoryOnly ( ) ) ;
return new DependencySpec ( job . getId ( ) , depends , memoryOnly ) ;
} )
2019-03-28 15:56:35 +00:00
. toList ( ) ;
return new FullSpec ( jobSpec , constraintSpecs , dependencySpecs ) ;
}
@WorkerThread
private void scheduleJobs ( @NonNull List < Job > jobs ) {
for ( Job job : jobs ) {
2021-08-02 21:46:56 +00:00
List < String > constraintKeys = job . getParameters ( ) . getConstraintKeys ( ) ;
List < Constraint > constraints = new ArrayList < > ( constraintKeys . size ( ) ) ;
for ( String key : constraintKeys ) {
constraints . add ( constraintInstantiator . instantiate ( key ) ) ;
}
2019-03-28 15:56:35 +00:00
scheduler . schedule ( 0 , constraints ) ;
}
}
@WorkerThread
2020-06-25 15:48:23 +00:00
private @Nullable Job getNextEligibleJobForExecution ( @NonNull JobPredicate predicate ) {
List < JobSpec > jobSpecs = Stream . of ( jobStorage . getPendingJobsWithNoDependenciesInCreatedOrder ( System . currentTimeMillis ( ) ) )
. filter ( predicate : : shouldRun )
. toList ( ) ;
2019-03-28 15:56:35 +00:00
for ( JobSpec jobSpec : jobSpecs ) {
List < ConstraintSpec > constraintSpecs = jobStorage . getConstraintSpecs ( jobSpec . getId ( ) ) ;
List < Constraint > constraints = Stream . of ( constraintSpecs )
. map ( ConstraintSpec : : getFactoryKey )
. map ( constraintInstantiator : : instantiate )
. toList ( ) ;
if ( Stream . of ( constraints ) . allMatch ( Constraint : : isMet ) ) {
return createJob ( jobSpec , constraintSpecs ) ;
}
}
return null ;
}
private @NonNull Job createJob ( @NonNull JobSpec jobSpec , @NonNull List < ConstraintSpec > constraintSpecs ) {
Job . Parameters parameters = buildJobParameters ( jobSpec , constraintSpecs ) ;
2019-11-13 16:39:58 +00:00
try {
Data data = dataSerializer . deserialize ( jobSpec . getSerializedData ( ) ) ;
Job job = jobInstantiator . instantiate ( jobSpec . getFactoryKey ( ) , parameters , data ) ;
job . setRunAttempt ( jobSpec . getRunAttempt ( ) ) ;
job . setNextRunAttemptTime ( jobSpec . getNextRunAttemptTime ( ) ) ;
job . setContext ( application ) ;
return job ;
} catch ( RuntimeException e ) {
2020-01-03 19:10:16 +00:00
Log . e ( TAG , "Failed to instantiate job! Failing it and its dependencies without calling Job#onFailure. Crash imminent." ) ;
2019-03-28 15:56:35 +00:00
2019-11-13 16:39:58 +00:00
List < String > failIds = Stream . of ( jobStorage . getDependencySpecsThatDependOnJob ( jobSpec . getId ( ) ) )
. map ( DependencySpec : : getJobId )
. toList ( ) ;
jobStorage . deleteJob ( jobSpec . getId ( ) ) ;
jobStorage . deleteJobs ( failIds ) ;
Log . e ( TAG , "Failed " + failIds . size ( ) + " dependent jobs." ) ;
throw e ;
}
2019-03-28 15:56:35 +00:00
}
private @NonNull Job . Parameters buildJobParameters ( @NonNull JobSpec jobSpec , @NonNull List < ConstraintSpec > constraintSpecs ) {
2019-10-15 20:46:29 +00:00
return new Job . Parameters . Builder ( jobSpec . getId ( ) )
2019-03-28 15:56:35 +00:00
. setCreateTime ( jobSpec . getCreateTime ( ) )
. setLifespan ( jobSpec . getLifespan ( ) )
. setMaxAttempts ( jobSpec . getMaxAttempts ( ) )
. setQueue ( jobSpec . getQueueKey ( ) )
. setConstraints ( Stream . of ( constraintSpecs ) . map ( ConstraintSpec : : getFactoryKey ) . toList ( ) )
2020-04-10 17:13:27 +00:00
. setInputData ( jobSpec . getSerializedInputData ( ) ! = null ? dataSerializer . deserialize ( jobSpec . getSerializedInputData ( ) ) : null )
2019-03-28 15:56:35 +00:00
. build ( ) ;
}
2020-04-10 17:13:27 +00:00
private @NonNull JobSpec mapToJobWithInputData ( @NonNull JobSpec jobSpec , @NonNull Data inputData ) {
return new JobSpec ( jobSpec . getId ( ) ,
jobSpec . getFactoryKey ( ) ,
jobSpec . getQueueKey ( ) ,
jobSpec . getCreateTime ( ) ,
jobSpec . getNextRunAttemptTime ( ) ,
jobSpec . getRunAttempt ( ) ,
jobSpec . getMaxAttempts ( ) ,
jobSpec . getLifespan ( ) ,
jobSpec . getSerializedData ( ) ,
dataSerializer . serialize ( inputData ) ,
2020-06-25 12:32:36 +00:00
jobSpec . isRunning ( ) ,
jobSpec . isMemoryOnly ( ) ) ;
2020-04-10 17:13:27 +00:00
}
2019-03-28 15:56:35 +00:00
interface Callback {
void onEmpty ( ) ;
}
}