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 have a JIRA service that I need to run every other Wednesday at 11:59 PM...I have been searching and apparently is not as easy as it sounds, since I have to create some kind of expression within the cron expression. The following cron expression: 0 59 23 ? * 4 will run every Wednesday at 11:59 PM and I need it to run every other Wednesday at 11:59 PM. Somehow I have to "divide" the nth week of the year by 2 and if the remaining is 1 then that means the script should run that Wednesday. For instance, the script should run the first Wednesday of the year, then the third Wednesday, then the fifth Wednesday and so on. How do I write an expression like that in the cron expression field in the Service section in JIRA?
Thanks
The short answer is that you can't. The cron expression syntax just isn't powerful enough to support that.
Longer answer:
Both Quartz (the scheduler that JIRA used to use) and Caesium (what I wrote to replace it) evaluate the cron expression by parsing it to a set of matching conditions and then tweaking what the current time is to figure out when it will next match. Quartz does this using a TreeSet
of accepted integer values (pretty silly for the year field!) and Caesium has a more flexible rule-based implementation, but the expression language is identical (except that Caesium's implementation has a lot fewer bugs in it).
But other than the fact that the day-of-week value depends on the year, month, and day values, these fields are all evaluated more or less independently. When what you want involves an interaction between multiple fields that involves an OR condition or on knowledge of some flip-flopping state, there just isn't any way to say it.
For example, there is no way to say "run this job every 90 minutes". I've seen people attempt to do this by writing something like "0 0/90 * * * ?" and Quartz actually accepts this as valid, but it does not do what you wanted. Due to how the "0/90" is actually interpreted, it ends up exactly the same as just saying "0", and the job runs every hour instead of every 90 minutes.
For that specialized case, you can awkwardly work around the problem by creating multiple rules that fill in the gaps. For example, by combining:
But since there can occasionally be an odd number of Wednesdays in a month, there isn't any way to use this same strategy. You could get, say, the first and third Wednesday, but it would take 3 weeks instead of 2 when one of the months has an odd number of them.
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 your detailed answer...so the question is, how do I run my JIRA service every other Wednesday? This is an script done in ScriptRunner so should I put the condition in the script itself? and If so, should I set the cron expression in the Interval field in JIRA to
0 59 23 ? * 4 (like it will execute every Wednesday at 11:59 PM) and then the condition in the script would decide whether that Wednesday the script should run or not?
Thank you for your assistance
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
That sounds like a sensible plan, yes. The only problem is figuring out whether this is a time to run or a time to ignore it. Here are a few ideas:
If I had to solve this problem, I think I would take the second path, and the logic would look something like this:
long now = System.currentTimeMillis(); long stored = Optional.ofNullable(getStoredTime()).orElse(0L); if (now < stored) { // too soon; wait for the next one return; } long later = Instant.now().plus(13, ChronoUnit.DAYS).toEpochMilli(); setStoredTime(later); // run the job
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Chris...I really appreciate your help...do I enter that piece of code at the beginning of the script, correct? As I mentioned the script is an script runner script which uses groovy so I am not really sure where to add that code and most likely I need to import some libraries that I dont know of. This is the script
import com.atlassian.greenhopper.service.sprint.Sprint
import com.atlassian.greenhopper.service.sprint.SprintManager
import com.onresolve.scriptrunner.runner.customisers.PluginModuleCompilationCustomiser
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
@WithPlugin("com.pyxis.greenhopper.jira")
def sprintServiceOutcome = PluginModuleCompilationCustomiser.getGreenHopperBean(SprintManager).getAllSprints()
if (sprintServiceOutcome.valid) {
sprintServiceOutcome.getValue().findAll { it.state == Sprint.State.ACTIVE }?.each { updateSprintState(it, Sprint.State.CLOSED) }
}
else {
log.error "Invalid sprint service outcome, ${sprintServiceOutcome.errors}"
}
def updateSprintState(Sprint sprint, Sprint.State state) {
def sprintManager = PluginModuleCompilationCustomiser.getGreenHopperBean(SprintManager)
def newSprint = sprint.builder(sprint).state(state).build()
def outcome = sprintManager.updateSprint(newSprint)
if (outcome.isInvalid()) {
log.warn "Could not update sprint with name ${sprint.name}, ${outcome.getErrors()}"
} else {
log.warn "${sprint.name} updated."
}
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
The example I wrote was in Java, which should probably translate easily into groovy, but isn't something I have much experience with, myself. The check was indeed meant to be at the start of the script (just after the imports). There are also some bits of supporting code that I did not implement because you would need to work out for yourself how you want to do them.
For example, I did not show an implementation for getStoredTime()
or setStoredTime(later)
, but an obvious way to implement them might be with a PropertySet
using the ps.getLong(String)
and ps.setLong(String, Long)
calls. There are other options too, but picking exactly how to do that and implementing it is a bit beyond the amount of help that I can really offer you. I would suggest looking for other questions on here that talk about PropertySet
, PluginSettings
, and Active Objects for more information about what options are available for this.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thank you for all your help Chris!...I really appreciate it.
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.