You need to sign in to do that
Don't have an account?
![jhartfield jhartfield](https://dfc-org-production.my.site.com/img/userprofile/default_profile_45_v2.png)
Need a way to programatically change or delete a Scheduled Job
Is there any way to change or delete a Scheduled Job ? I am able to create one easily enough, but once it is made I can only look at the values in the CronTrigger object, but not update them. Also, it seems I cannot delete them. I also looked at the System.AbortJob method, but that seems to only stop a job that is already running - I get an error if I try it on a Queued job.
The reason I am doing this is to try to create a 'sliding' job. So, when a trigger is fired the job will run in 5 minutes. But if the trigger is fired again before that 5 minutes is up, it will find the old job and either delete it and make a new one for 5 minutes out, or update the old one so the next scheduled time is in 5 minutes.
Any ideas are welcome!
Jason
I was surprised at the lack of a system.unschedule
I tried system.abortJob but got an error message.
It appears that the name isn't even in CronTrigger so I don't believe you can even see if the scheduled apex is already scheduled unless you use an oddball CronExpression and assume no one ever will use that same oddball CronExpression. In my case, I need to schedule a job, but only if it isn't already scheduled.
I tried just inserting it each time inside a try catch, and apparently I couldn't even catch the system.exception thrown when scheduling apex with the same name? Sorta weird....
There doesn't seem to be any way at all to tell what class is being executes. I'd love to be able to even just tell the namespace so I could make sure I'm not deleting jobs I didn't create from the CronTrigger table.
Salesforce, help, let me see the job name OR the id of the class being executed OR the namespace. Please?
To abort a job you need to use the job ID from the CronTrigger table. After I schedule my job, I store this ID in a multi-purpose custom setting object I call Settings.
My abort method:
// Unschedule all existing jobs
public static void unscheduleApexJobs(){ Settings__c s = Settings__c.getOrgDefaults(); if (s != null){ try{ if (s.Schedule_ID__c != '' && s.Schedule_ID__c != null){ system.abortJob(s.Schedule_ID__c); } } catch (Exception e){ // let it go b/c the job isn't scheduled } } }
Hope that helps.
So Does it mean that we can schedule an apex job through SFDC UI but we CANNOT unschedule it through SFDC UI?
Do we need to depend on the Apex code for deleting the scheduled job?
Please let me know.
Thanks,
KP
I do something really close of your solution, storing the id in a table and I retrieve the ID at the next job.
The problem is that my system.abortjob(myId) runs without any problem, no exception, ... but the job is not removed !
Am I doing something wrong ??
It definitely removes it for me. I just use that one system.abort line
There is a Monitoring | Scheduled Jobs screen where you can manually abort a schedule after you create one. There is a UI to do scheduling, but Apex allows for much greater flexibility. For example, in the UI, I an only run something daily, whereas via Apex I could run something every minute if I wanted to.
Well I got it working. It's not really nice but it works, i did the following:
list<CronTrigger> cron = new list<CronTrigger>([select Id, State from CronTrigger]);
for(CronTrigger CT:cron){ System.abortjob(CT.Id); }
The problem I think was I tried to remove Cron job with a State = "Waiting". But even now, when I see my queue list, I still have always the oldest job which has a state of "waiting" although he is finished a long time ago and I have a second job which is the latest one. For this one, that makes sense but for the oldest one, I don't really understand... anyway
Just out of curiosity, why are you unscheduling something inside the schedulable class? Are you scheduling a job to unschedule jobs? If so, you probably cannot unschedule the job that you are running at that moment and that's why one is left over. Seems a little odd, but let me know if I am just understanding.
The content of the schedulable class should only be to execute what you want executed on a schedule. e.g. close old opportunities that were never closed, but should have been.
Another piece of code sets the schedule. Once you run this, your schedule is setup and you leave it alone. It will kick off when it is supposed to. Then you just sit back, relax and watch it work for you.
If you ever want to unschedule it, then you can call system.abort() in a normal method. I wouldn't call the system.abort() method inside of the scheduleable class. I've never tried, but I could imagine a case where the system is unable to abort a schedule when it's job is currently running.
>>>
Just out of curiosity, why are you unscheduling something inside the schedulable class?
<<<
He may be trying to implement this code:
http://community.salesforce.com/t5/Apex-Code-Development/How-to-create-a-Schedule-Job-which-runs-after-specific-interval/m-p/178565
Which looks promising, but (as it exists) will fail after the 10th self-scheduled run. It successfully creates new schedule entries, but does not delete the old ones.
I am still not getting it. It's too recursive. You don't need to call system.schedule inside the class you are scheduling. The class you are scheduling should contain the logic you want run (e.g. kick off a batch job, update some data, etc.)
To schedule a job to run every minute, outside of the class you'd run this:
The expresstion '0 * * * * ? *' should schedule the job to run every minute. So you only have 1 schedule in the crontrigger table. The expression works like this:
Actually, the example you gave won't work as you cannot use the '*' wildcard in schedule timings for seconds and minutes on Force.com. All values must be integers. They have done this to prevent this you scheduling more frequently than every hour.
Got it. My most often occurring job is every hour, so I never had to deal with trying to go more often. I wasn't aware of that nuance. Regardless, I still don't think it makes sense to schedule something inside the class you are scheduling.
Not sure if it would work, but another approach I can think of would be to use timed workflow to send an email to an email service 5 minutes after the record is saved. The email service would have the stuff you need to process. If timed workflow can send this email and it can be caught by an email services, then this would replace your need for an Apex trigger or a schedule. Kind of a hack and it's just a theory right now, but worth some time investigating.
Our approach with clients to date is that, if they need to schedule Apex more frequently than the SFDC approach allows, we propose either calling from an external scheduler (so calling an execute method exposed as a web-service from SFDC), or using something such as CronKit (AppExchange app).
The other option is that you could set up a Force.com Site to allow any request to a specific page to call the Apex required. The only thing with that is that you'll need to give the various permissions required on each object to the public guest account... not ideal really.
CronKit uses workflow and triggers to bypass the 10 schedule limit. The only thing is that you'll then be running your code in the context of a trigger, so much smaller Governor limits than normal Apex execution.
Also, there's no guarantee of when a job actually runs - can be up to 15 minutes later than you requested, depending on available platform resources.
Nice! I forgot about CronKit. Your approach seems the most robust.
To delete a scheduled job, which was scheduled by the code itself you need abort the job at the end of the execute method.
like this :
global class testSchedulable implements Schedulable{
global void execute(SchedulableContext sc) {
Database.executebatch(new batchApex_class());
system.abortJob(sc.getTriggerId());
}
}
This will remove the entry of the scheduled job from 'All scheduled jobs'.
Hope this helps!
Thanks,
Rajit
Hello,
I am having a nearly similar requirement to delete all the completed scheduled jobs (becoz of 10 limit in org) and then kick off a process every n minutes.
Somehow I am not getting any job in completed state although some of the jobs have run in past. All the jobs show in Waiting state.
I am stuck so as to how to identify completed jobs and then delete them. Can anybody let me know if I am missing something.
Here is the sample code (where i am deleting all jobs which is not right) :
List<CronTrigger> completedJobs =[Select c.State, c.EndTime, c.OwnerId, c.Id,c.TimesTriggered, c.NextFireTime,
c.CronExpression From CronTrigger c ];
//where c.State = 'Completed'
//where c.EndTime < :currentTime
try{
System.debug('Scheduled Job Size is ::'+completedJobs.size());
for(CronTrigger cronJob:completedJobs){
System.debug(':: Job to Abort::'+String.valueOf(cronJob) + ' c.TimesTriggered ' + cronJob.TimesTriggered
+ ' c.NextFireTime ' +cronJob.NextFireTime);
// to exclude current job to be deleted
if(cronJob.Id !=triggerId ) {
System.abortJob(cronJob.id);
System.debug(':: Job aborted ::'+String.valueOf(cronJob) );
}
else
{
System.debug(':: Cannot abort this Job ::'+String.valueOf(cronJob) );
}
}
Its urgent so if anybody has any clue please help me out!
thanks
Anjali
An approach we are looking at is to use the system.abortjob method to removed completed scheduled jobs that have no next run and status = deleted in the CronTrigger sObject. Adding code like the following in the Schedulable class before other code:
List<CronTrigger> listCronTrigger = [select Id from CronTrigger where State = 'DELETED' and nextfiretime = null];
If (listCronTrigger.size() > 0)
{
For (Integer i = 0; i < listCronTrigger.size(); i++)
{ System.abortJob(listCronTrigger[i].Id); }
}
Thanks. I spent about 2 hours trying to figure this out. I put System.Abort in the execute method and it worked!! Was about to kick my computer!!
What about from a trigger?
System.abortJob doesn't kill the job but works from anonymous... why is that?
I'm trying to update a scheduler based on user input (so that they don't have to figure out the cron):
State__c configuration = [Select s.Send_Reports__c, s.Run_every_days__c, s.Name, s.Last_Time_Report_Collected__c, s.Id From State__c s limit 1]; List<CronTrigger> ct = [SELECT id, CronExpression, TimesTriggered, NextFireTime FROM CronTrigger limit 1];
Integer period = Integer.valueOf(configuration.Run_every_days__c);
String cron = '0 00 00 */' + period + ' * ?';
if (!ct.isEmpty()){ System.abortJob(ct.get(0).id); }
if (configuration.Send_Reports__c)
System.schedule('Email Schedule', cron, new EmailScheduler());
I am facing the same. problem. Were you able to solve this issue?