github.com/Axway/agent-sdk@v1.1.101/pkg/jobs/scheduledjob.go (about) 1 package jobs 2 3 import ( 4 "sync" 5 "time" 6 7 "github.com/gorhill/cronexpr" 8 9 "github.com/Axway/agent-sdk/pkg/util/errors" 10 ) 11 12 type scheduleJobProps struct { 13 schedule string 14 cronExp *cronexpr.Expression 15 stopChan chan bool 16 } 17 18 type scheduleJob struct { 19 baseJob 20 scheduleJobProps 21 cronLock *sync.Mutex 22 } 23 24 // newScheduledJob - creates a job that is ran at a specific time (@hourly,@daily,@weekly,min hour dow dom) 25 func newScheduledJob(newJob Job, schedule, name string, failJobChan chan string, opts ...jobOpt) (JobExecution, error) { 26 exp, err := cronexpr.Parse(schedule) 27 if err != nil { 28 return nil, errors.Wrap(ErrRegisteringJob, err.Error()).FormatError("scheduled") 29 } 30 31 thisJob := scheduleJob{ 32 createBaseJob(newJob, failJobChan, name, JobTypeScheduled), 33 scheduleJobProps{ 34 cronExp: exp, 35 schedule: schedule, 36 stopChan: make(chan bool, 1), 37 }, 38 &sync.Mutex{}, 39 } 40 41 for _, o := range opts { 42 o(&thisJob.baseJob) 43 } 44 45 go thisJob.start() 46 return &thisJob, nil 47 } 48 49 func (b *scheduleJob) getNextExecution() time.Duration { 50 b.cronLock.Lock() 51 defer b.cronLock.Unlock() 52 nextTime := b.cronExp.Next(time.Now()) 53 return time.Until(nextTime) 54 } 55 56 // start - calls the Execute function from the Job definition 57 func (b *scheduleJob) start() { 58 b.startLog() 59 b.waitForReady() 60 61 // This could happen while rescheduling the job, pool tries to start 62 // and one of the job fails which triggers stop setting the flag to not ready 63 // Return in this case to allow pool to reschedule the job 64 if !b.IsReady() { 65 return 66 } 67 b.setIsStopped(false) 68 ticker := time.NewTicker(b.getNextExecution()) 69 defer ticker.Stop() 70 b.SetStatus(JobStatusRunning) 71 for { 72 // Non-blocking channel read, if stopped then exit 73 select { 74 case <-b.stopChan: 75 b.SetStatus(JobStatusStopped) 76 return 77 case <-ticker.C: 78 b.executeCronJob() 79 if b.getError() != nil { 80 b.setExecutionError() 81 } 82 ticker.Stop() 83 ticker = time.NewTicker(b.getNextExecution()) 84 } 85 } 86 } 87 88 // stop - write to the stop channel to stop the execution loop 89 func (b *scheduleJob) stop() { 90 if b.getIsStopped() { 91 b.logger.Tracef("job has already been stopped") 92 return 93 } 94 95 b.stopLog() 96 if b.IsReady() { 97 b.logger.Tracef("writing to %s stop channel", b.GetName()) 98 b.stopChan <- true 99 b.logger.Tracef("wrote to %s stop channel", b.GetName()) 100 b.UnsetIsReady() 101 } else { 102 b.stopReadyIfWaiting(0) 103 } 104 b.setIsStopped(true) 105 }