Annotation Interface Scheduled
Marks a method for automatic scheduled execution by the framework.
The annotated method must be void and take no parameters.
It must be inside a @Service or other managed bean.
Tasks are automatically cancelled when the owning plugin is unloaded.
TaskManager walks the class hierarchy when scanning for
@Scheduled methods, so an annotated method is still found on a ByteBuddy AOP proxy of
the declaring bean.
Usage example:
@Service
public class InterestService {
@Scheduled(period = 36000, async = true) // Every 30 minutes, async
public void distributeInterest() {
// Framework calls this automatically
}
}
async = true is for literal timings only: a @Scheduled bound to a config key
(below) must be sync, and async = true together with a binding refuses the module at load
(#535 tracks async bindings).
Binding the timing to a config key (6.3.0)
Instead of a literal,periodKey() and delayKey() can name a
@ConfigEntry path on the module's own config entity, given by config(). The
value is read in seconds and multiplied by 20 for ticks, so an operator can tune the
interval without a second, hand-written scheduler in the module:
@Scheduled(config = EconomyConfig.class, periodKey = "interest.interval", delayKey = "interest.interval")
public void distributeInterest() { ... }
- The default lives only in the config field (
private int interestInterval = 1800;). Leave the literal it replaces unset:periodwithperiodKey,delaywithdelayKey. Setting both refuses the module at load. - Checked at load. The module alone is refused, naming the key and the value, when the
config class is not registered exactly once for the module (a directory
@ConfigEntitycannot be bound), the key matches no@ConfigEntrypath, the field is not anint,long,IntegerorLong, or the value is below 1 second,nullor aboveInteger.MAX_VALUE / 20seconds (about 3.4 years).0does not mean "off". - Declared methods only. The binding is found on the bean class's own declared
methods (after unwrapping an AOP proxy), exactly like an unbound
@Scheduled; a method inherited from a superclass is neither scheduled nor checked. Declare the bound method on the bean class itself (see issue #532). - Applied at
/ul reload, keeping the task's place in its cycle: the next run is the last run plus the new period (before the first run: the arm time plus the new delay), or the next tick if that moment has already passed. A reload never runs the task early and never postpones it by restarting its clock; a task whose value did not change is not touched. An invalid value on reload is not applied -- the running value is kept and a WARNING names the key. A panel edit takes effect at the next/ul reload. The reload step runs on the main thread only. - Panel edits are range-checked. A panel write that sets a bound key outside this
range (for example
0) is refused like a@Rangeviolation, and nothing is written. - No
@Rangeon a bound field. The binding's range above is the field's range. A module@Rangeon the same field would make an out-of-range reload throw from the config reload itself, which aborts the rest of that module's reload (issue #509) instead of keeping the running value. - Sync only. A bound method cannot be
async = true; that combination is refused at load. Bind a sync task and hand the heavy work toBukkit.getScheduler().runTaskAsynchronously(...)from its body. A sync task's runs happen on the main thread, where the reload also runs, which is what lets a reload keep the task's place in its cycle exactly. Literal (unbound)asynctasks are unaffected. Issue #535 tracks allowing async bindings. - Modules only. A binding on an External Plugin API bean is refused.
- Declare
api-version: 630in the module'splugin.yml. An older framework does not know these elements and silently ignores them -- the method would then run once at load instead of on the configured interval -- so the floor is what makes it refuse the module instead. 6.3.0 itself refuses a module that uses a binding while declaring a lowerapi-version, so the mistake shows up where it is made.
- Since:
- 6.2.0
-
Optional Element Summary
Optional ElementsModifier and TypeOptional ElementDescriptionbooleanRun on async thread instead of main server thread.Class<? extends AbstractConfigEntity> longInitial delay in ticks before first execution.@ConfigEntrypath whose value, in seconds (at least 1), is the initial delay.longRepeat interval in ticks.@ConfigEntrypath whose value, in seconds (at least 1), is the repeat interval.
-
Element Details
-
delay
long delayInitial delay in ticks before first execution. Default: 0. Leave unset whendelayKey()is set.- Returns:
- delay in ticks
- Default:
0L
-
period
long periodRepeat interval in ticks. -1 = run once after delay. Default: -1. Leave unset whenperiodKey()is set.- Returns:
- period in ticks
- Default:
-1L
-
async
boolean asyncRun on async thread instead of main server thread. Default: false- Returns:
- true if async
- Default:
false
-
config
Class<? extends AbstractConfigEntity> configConfig entity class whose@ConfigEntrykeysperiodKey()anddelayKey()name. It must be registered exactly once for the module, and the value is read from that same instance, which/ul reloadreloads in place. Default:AbstractConfigEntityitself, meaning unbound. See "Binding the timing to a config key" above.- Returns:
- the bound config entity class
- Since:
- 6.3.0
- Default:
com.ultikits.ultitools.abstracts.AbstractConfigEntity.class
-
periodKey
String periodKey@ConfigEntrypath whose value, in seconds (at least 1), is the repeat interval. The path is matched as@ConfigEntry(path = ...)declares it, or the field name when the path is empty. Requiresconfig(); excludesperiod(). Default: unbound.- Returns:
- the bound period key
- Since:
- 6.3.0
- Default:
""
-
delayKey
String delayKey@ConfigEntrypath whose value, in seconds (at least 1), is the initial delay. May name the same key asperiodKey()-- a task that must not run at load, such as a payout, waits one full interval first. Requiresconfig(); excludesdelay(). Default: unbound.- Returns:
- the bound delay key
- Since:
- 6.3.0
- Default:
""
-