Just a heads up: On March 24, 2025, starting at 4:30pm CDT / 19:30 UTC, the site will be undergoing scheduled maintenance for a few hours. During this time, the site might be unavailable for a short while. Thanks for your patience.
×Hi,
I am developing a bitbucket plugin which needs to run some automated tasks. So I first tried SAL scheduler. But then I found out that bitbucket server does not support SAL scheduler because of a known bug mentioned in here.
https://jira.atlassian.com/browse/BSERV-8556
and in that bug, it has recommended to use atlassian-scheduler instead of SAL scheduler. But the problem is I could not find a simple example which used this atlassian-scheduler ( https://bitbucket.org/atlassian/atlassian-scheduler ) other than from here
https://bitbucket.org/cfuller/atlassian-scheduler-jira-example
this example is an jira one. The problem is this example is a bit complex to me and I would really apprciate if anyone can give me a simple sample code to automate a simple task.
Thanks
Hi. I'm the original author of the atlassian-scheduler API and the scheduler example you cited in your question. That example grew to be a bit more than I had originally intended, as we also ended up adding a few things to it for use in testing and proving that it could work with things like AO that needed to access other components after JIRA was initialized.
As far as the Atlassian Scheduler API itself is concerned, however, there are really only two things that you absolutely must deal with:
I'm unlikely to have any spare time to simplify the example in the immediate future, but you can probably see the earlier versions of it just by looking back in the repo's history to before all the Active Objects, debugging, and SAL extras were added. For example, try checking it out at 4d2b84d.
I don't work on Bitbucket, so I'm not as familiar with their code base, but there shouldn't be much about the example that is JIRA-specific. If you have more specific questions, feel free to ask.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Chris,
Thank you for the quick reply. I managed to create an small exmple from your help.
I need to clarify following 2 qs and would apprciate if you can help me on this.
1. Why there is 2 identifiers called JobRunnerKey and JobId? What is the difference?
2. I need 2 schedulers where 1st scheduler print "Job A" for each 6000ms and 2nd scheduler print "Job B" foreach 12000ms.
My original code was this. (1 scheduler, 1 job)
public class PluginJobRunnerImpl implements PluginJobRunner, InitializingBean, DisposableBean { private static final Random RANDOM = new Random(); private static final long SCHEDULE_BASE_INTERVAL_MILLIS = 6000L; private static final int SCHEDULE_MAX_JITTER = 10000; private static final Logger LOG = LoggerFactory.getLogger(PluginJobRunnerImpl.class); private static final JobRunnerKey JOB = JobRunnerKey.of(PluginJobRunnerImpl.class.getName()); private static final String JOB_ID_PREFIX = "Job ID ="; private final SchedulerService schedulerService; public PluginJobRunnerImpl(SchedulerService schedulerService) { this.schedulerService = schedulerService; } @Override public void afterPropertiesSet() throws Exception { schedulerService.registerJobRunner(JOB, this); createSchedule(1, SCHEDULE_BASE_INTERVAL_MILLIS); } @Override public void destroy() { schedulerService.unregisterJobRunner(JOB); } @Override public JobRunnerResponse runJob(JobRunnerRequest request) { try { System.out.println("Job A"); return JobRunnerResponse.success(); } catch (Exception e) { return JobRunnerResponse.failed(e); } } @Override public void createSchedule(int jobID, long intervalInMillis) throws Exception { final int jitter = RANDOM.nextInt(SCHEDULE_MAX_JITTER); final Date firstRun = new Date(System.currentTimeMillis() + jitter); final JobConfig jobConfig = JobConfig.forJobRunnerKey(JOB) .withSchedule(Schedule.forInterval(intervalInMillis, firstRun)) .withRunMode(RunMode.RUN_ONCE_PER_CLUSTER); try { final JobId jobId = toJobId(jobID); final JobDetails existing = schedulerService.getJobDetails(jobId); if (existing != null) { schedulerService.unscheduleJob(jobId); } schedulerService.scheduleJob(jobId, jobConfig); } catch (SchedulerServiceException sse) { throw new Exception("Unable to create schedule for job ID '" + jobID + '\'', sse); } } @Override public void deleteSchedule(@Nonnull String jobId) throws Exception { final JobId id = JobId.of(jobId); final JobDetails jobDetails = schedulerService.getJobDetails(id); if (jobDetails != null) { if (!JOB.equals(jobDetails.getJobRunnerKey())) { throw new Exception("JobId '" + jobId + "' does not belong to me!"); } schedulerService.unscheduleJob(id); } } private static JobId toJobId(int jobID) { return JobId.of(JOB_ID_PREFIX + jobID); } }
And I did some modifications to it in oder to create 2 schedulers. Is there any other way than this?
public class PluginJobRunnerImpl2 implements InitializingBean, DisposableBean { private static final Random RANDOM = new Random(); private static final long SCHEDULE_BASE_INTERVAL_MILLIS_FOR_JOB_A = 6000L; private static final long SCHEDULE_BASE_INTERVAL_MILLIS_FOR_JOB_B = 12000L; private static final int SCHEDULE_MAX_JITTER = 10000; private static final Logger LOG = LoggerFactory.getLogger(PluginJobRunnerImpl2.class); private static final JobRunnerKey JOB_A = JobRunnerKey.of(JobRunnerA.class.getName()); private static final JobRunnerKey JOB_B = JobRunnerKey.of(JobRunnerB.class.getName()); private static final String JOB_ID_PREFIX = "Job ID ="; private final SchedulerService schedulerService; public PluginJobRunnerImpl2(SchedulerService schedulerService) { this.schedulerService = schedulerService; } @Override public void afterPropertiesSet() throws Exception { schedulerService.registerJobRunner(JOB_A, new JobRunnerA()); schedulerService.registerJobRunner(JOB_B, new JobRunnerB()); createSchedule(JOB_A, 1, SCHEDULE_BASE_INTERVAL_MILLIS_FOR_JOB_A); //1 is just an unique ID for job id createSchedule(JOB_B, 2, SCHEDULE_BASE_INTERVAL_MILLIS_FOR_JOB_B); //2 is just an unique ID for job id } @Override public void destroy() { schedulerService.unregisterJobRunner(JOB_A); schedulerService.unregisterJobRunner(JOB_B); } public void createSchedule(JobRunnerKey JOB, int jobID, long intervalInMillis) throws Exception { final int jitter = RANDOM.nextInt(SCHEDULE_MAX_JITTER); final Date firstRun = new Date(System.currentTimeMillis() + jitter); final JobConfig jobConfig = JobConfig.forJobRunnerKey(JOB) .withSchedule(Schedule.forInterval(intervalInMillis, firstRun)) .withRunMode(RunMode.RUN_ONCE_PER_CLUSTER); try { final JobId jobId = toJobId(jobID); final JobDetails existing = schedulerService.getJobDetails(jobId); if (existing != null) { LOG.error("We will be replacing an existing job with jobId=" + jobId + ": " + existing); schedulerService.unscheduleJob(jobId); } schedulerService.scheduleJob(jobId, jobConfig); LOG.error("Successfully scheduled jobId=" + jobId); } catch (SchedulerServiceException sse) { throw new Exception("Unable to create schedule for job ID '" + jobID + '\'', sse); } } private static JobId toJobId(int jobID) { return JobId.of(JOB_ID_PREFIX + jobID); } } public class JobRunnerA implements JobRunner { private static final Logger LOG = LoggerFactory.getLogger(JobRunnerA.class); @Nullable @Override public JobRunnerResponse runJob(JobRunnerRequest jobRunnerRequest) { LOG.info("------------------JOB A----------------"); return JobRunnerResponse.success(); } } public class JobRunnerB implements JobRunner { private static final Logger LOG = LoggerFactory.getLogger(JobRunnerB.class); @Nullable @Override public JobRunnerResponse runJob(JobRunnerRequest jobRunnerRequest) { LOG.info("------------------JOB B----------------"); return JobRunnerResponse.success(); } }
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Chris,
Can you please have a look at this question?
Thanks in advance.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You refer here, I have done and done
https://developer.atlassian.com/server/jira/platform/developing-for-high-availability-and-clustering/#PluginGuidetoJIRAHighAvailabilityandClustering-Scheduledtasksandbackgroundprocesses
https://support.atlassian.com/jira-service-desk-cloud/docs/construct-cron-expressions-for-a-filter-subscription/
https://developer.atlassian.com/server/framework/atlassian-sdk/sal-3-0-upgrade-guide/
https://developer.atlassian.com/server/framework/atlassian-sdk/scheduling-events-via-sal-tutorial/
Example:
https://bitbucket.org/cfuller/atlassian-scheduler-jira-example
https://github.com/blackducksoftware/blackduck-jira
https://bitbucket.org/atlassian/application-links
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.