B4J Library Quartz Scheduler (Cron) - For industry grade task scheduling software

Hello everyone,
I wrapped this last year whilst trying to learn Java, I then had to learn about cron expressions o_O.
Yesterday I cleaned up the code (a lot) and added the IDE tooltip for the community, I also created an extensive B4J example for developers to learn from, so read it carefully.

Quartz is a lightweight, embeddable job scheduler. Its purpose is simple, it lets your program run tasks automatically at specific times or intervals, without relying on the operating system’s scheduler.

Here is the essence of Quartz in plain terms.

What Quartz does

  • Runs jobs on a schedule (every minute, every hour, every day, cron expressions, etc.)
  • Manages recurring tasks reliably inside your application
  • Supports multiple jobs, triggers, calendars, and priorities
  • Can persist schedules in a database so they survive restarts (You have to add the database code of your choice)
  • Works in desktop apps, servers, and background services

Why developers around the world use Quartz

  • It's the industry standard
  • It is far more flexible than OS‑level schedulers
  • It handles complex timing rules (cron, exclusions, holidays)
  • It is stable and battle tested

In one short paragraph

Quartz is a powerful, programmable timer system that lets your application run tasks automatically and reliably according to any schedule you define. It can handle everything from writing to and reading from databases and files, to launching external applications using the Shell command, and much more. What Quartz actually does is simple. It fires your code at the exact times or intervals you specify, without you needing to manage timers, threads, or OS‑level schedulers.

How you use that power is entirely up to you. As a B4J developer, you can combine Quartz with your existing knowledge of file I/O, networking, SQL, APIs, and system automation to build anything from simple reminders to full industrial grade task automation. With a bit of imagination and the help of the B4X search box when you need specific techniques, you can easily manipulate this library to its full potential.

Quartz Scheduler is not just usable in industry, it is for industry grade software.
Quartz Scheduler has been used for more than 20 years in production systems across finance, telecoms, logistics, healthcare, and enterprise Java platforms.

Hint: Personally, I would create an SQLite database to store all jobs and schedules, and then connect it to the Quartz Scheduler.


B4J library tab:
1770821109745.png


Example B4J application:
1770819773702.png


SS_QuartzScheduler

Author:
Peter Simpson
Version: 1.0
  • QuartzScheduler
    • Events:
      • JobDeleted (JobName As String)
      • JobError (JobName As String, Error As String)
      • JobExecute (JobName As String, Data As Map)
      • JobFinished (JobName As String)
      • JobScheduled (JobName As String)
      • JobStarted (JobName As String)
      • SchedulerError (Error As String)
      • SchedulerShutdown
      • SchedulerStarted
    • Functions:
      • CancelJob (jobName As String)
        Alias for DeleteJob.
        jobName is the Job name.
      • ClearLog
        Clears internal log entries.
      • DeleteGroup (groupName As String)
        Deletes all jobs in a given group.
        groupName is the Group name.
      • DeleteJob (jobName As String)
        Deletes a job by name in any group.
        jobName is the Job name.
      • GetJobData (jobName As String) As Map
        Retrieves the Map of data associated with a job.
        jobName is the Job name.
        Returns a Map of job data (empty if job not found).
      • GetJobDetailInfo (jobName As String) As Map
        Returns basic job detail metadata as Map: Name, Group, Description.
        jobName is the Job name.
        Returns a Map with keys "Name", "Group", "Description" (empty if job not found).
      • GetJobLog (jobName As String) As List
        Returns logs related to a specific job name.
        jobName is the Job name.
        Returns a List of strings.
      • GetJobState (jobName As String) As String
        Returns the current state of a job.
        Possible values:
        SCHEDULED
        PAUSED
        RUNNING
        COMPLETE
        NOT_FOUND
        jobName The job name.
        Returns State string.
      • GetLog As List
        Returns the full internal log of the scheduler.
        Returns a List of strings.
      • GetNextRunTime (jobName As String) As Long
        Returns the next scheduled execution time for a job.
        jobName is the Job name.
        Returns Next run time in milliseconds (0 if not found or no upcoming run).
      • GetTriggerInfo (jobName As String) As List
        Returns detailed trigger information for a specific job.
        Each item in the returned list is a Map containing trigger metadata.
        jobName is the job name.
        Returns A List of Maps describing the job's triggers.
      • Initialize (eventName As String)
        Initialise synchronously.
        Uses RAMJobStore.
        eventName is the Event name prefix (e.g., "qs").
      • InitializeAsync (eventName As String)
        Initialise asynchronously (wrapper entry point).
        Uses RAMJobStore.
        eventName is the Event name prefix (e.g., "qs").
      • InterruptJob (jobName As String)
        Requests interruption of a running job (if it implements InterruptableJob).
        jobName is the Job name.
      • JobExists (jobName As String) As Boolean
        Checks whether a job with the given name exists in any group.
        jobName is the Job name.
        Returns true if the job exists, false otherwise.
      • ListGroups As List
        Returns all job groups currently registered in the scheduler.
        Returns A List of group names.
      • ListJobs As List
        Returns a List of job names currently known to the scheduler.
      • PauseGroup (groupName As String)
        Pauses all jobs in a given group.
        groupName is the Group name.
      • PauseJob (jobName As String)
        Pauses a job by name in any group.
        jobName is the Job name.
      • ResumeGroup (groupName As String)
        Resumes all jobs in a given group.
        groupName is the Group name.
      • ResumeJob (jobName As String)
        Resumes a previously paused job by name in any group.
        jobName is the Job name.
      • RunJobNow (jobName As String)
        Triggers a job to run immediately, searching all groups for the job name.
        jobName is the Job name.
      • ScheduleCronJob (jobName As String, cron As String, data As Map)
        Schedule a cron job synchronously (default group).
        jobName is the Job name (unique).
        cron is the Cron expression.
        data is the Map of data to pass to job (can be null).
      • ScheduleCronJob2 (jobName As String, groupName As String, cron As String, data As Map)
        Schedule a cron job synchronously with group.
        jobName is the Job name.
        groupName is the Group name.
        cron is the Cron expression.
        data is the Map data.
      • ScheduleJobAsync (jobName As String, cronOrNull As String, intervalSeconds As Long, repeatCount As Long, data As Map)
        Schedule job asynchronously on background thread (default group).
        jobName is the Job name.
        cronOrNull is the Cron expression or null.
        intervalSeconds is the Interval seconds for repeating (ignored if cron provided).
        repeatCount is the Repeat count for repeating (ignored if cron provided).
        data is the Map data.
      • ScheduleJobAsync2 (jobName As String, groupName As String, cronOrNull As String, intervalSeconds As Long, repeatCount As Long, data As Map)
        Schedule job asynchronously on background thread with group.
        jobName is the Job name.
        groupName is the Group name.
        cronOrNull is the Cron expression or null.
        intervalSeconds is the Interval seconds for repeating (ignored if cron provided).
        repeatCount is the Repeat count for repeating (ignored if cron provided).
        data is the Map data.
      • ScheduleRepeatingJob (jobName As String, intervalSeconds As Long, repeatCount As Long, data As Map)
        Schedule a repeating job synchronously (default group).
        jobName is the Job name.
        intervalSeconds is the Interval in seconds.
        repeatCount Number of repeats (<= 0 => forever).
        data is the Map data.
      • ScheduleRepeatingJob2 (jobName As String, groupName As String, intervalSeconds As Long, repeatCount As Long, data As Map)
        Schedule a repeating job synchronously with group.
        jobName is the Job name.
        groupName is the Group name.
        intervalSeconds is the Interval in seconds.
        repeatCount is the Number of repeats (<= 0 => forever).
        data is the Map data.
      • SetJobChain (jobNames As List)
        Define a simple chain of jobs: JobNames(0) -> JobNames(1) -> JobNames(2) ...
        When one finishes successfully, the next is triggered.
        jobNames is the List of job names in execution order.
      • Shutdown
        Shutdown scheduler synchronously. Waits for running jobs to complete.
      • ShutdownAsync
        Shutdown scheduler on background thread. Returns immediately.
      • Start
        Start scheduler synchronously.
        Blocks until scheduler.start() returns.
      • StartAsync
        Start scheduler on background thread.
        Returns immediately.
Important:
Always use the following methods to Initialize, Start, and Shutdown Quartz Scheduler. See the attached B4J example and also the examples in post #3 below:

InitializeAsync
StartAsync
ShutdownAsync


I will be making the non-async methods private in the next update.

PLEASE NOTE:
TO RUN THE ATTACHED EXAMPLE, YOU NEED TO DOWNLOAD THE THIRD-PARTY JAVA DEPENDENCIES LINKED BELOW, AS WELL AS USING THE ATTACHED POST LIBRARY.

CLICK HERE - Download Extra Libraries <<<<<<<<<<<<<<<<<<<<<<<<


Enjoy...
 

Attachments

  • QuartzScheduler.zip
    12 KB · Views: 7
  • QuartzScheduler_Lib.zip
    14.4 KB · Views: 7
Last edited:

Peter Simpson

Expert
Licensed User
Longtime User
More...

Quartz Scheduler is used in industry

Quartz is a mature, battle tested, enterprise‑level scheduling engine. It is part of the Java ecosystem and is widely deployed in the following categories
  • Banking systems
  • Insurance platforms
  • Telecom billing systems
  • Manufacturing automation
  • Logistics and fleet management
  • Large scale web applications
  • Microservices
  • Cloud based backend systems
It is stable, predictable, and designed for long running mission critical workloads.

Why industry uses Quartz

1. Reliability

Quartz is designed to run for months or years without stopping. It handles the following with ease.
  • Missed triggers
  • Retries
  • Persistent job stores
  • Clustering
  • Failover

2. Flexibility

This library supports the following.

3. Persistence

Quartz can store schedules in:
  • RAM (fast, simple) - Default
  • You can manually add your own SQLite dayabase, or JDBC databases (PostgreSQL, MySQL, SQL Server, Oracle)
This means schedules survive application restarts, essential for production.

4. Clustering

Multiple servers can share the same job store and coordinate scheduling (Store jobs and schedules in JDBC databases). This is a major enterprise feature.

5. Embeddable

Quartz runs inside your application, not as a separate service. This gives developers full control.

Why Quartz works perfectly in B4J

Quartz is:
  • Safe
  • Easy to use
  • Fully asynchronous
  • Can be used with any database. You have to add the connector yourself and manipulate the events
This is exactly how industrial systems integrate Quartz:
  • JobExecute
  • JobStarted
  • JobFinished
  • SchedulerError

In short

Quartz is one of the most trusted scheduling engines in the Java world.


Enjoy...
 
Last edited:

Peter Simpson

Expert
Licensed User
Longtime User
Hello Everyone,
Here are two simple examples of writing to files, but the scheduled action could be anything from starting services, sending emails, backing up data, shutting down a machine. Your imagination can run wild.

Examples below are not fully tested, but I can't see any reason why they should not work.

Everyday at 16:30

Daily at 16:30:
Sub Class_Globals
    Private Root As B4XView
    Private XUI As XUI

    Private QS As QuartzScheduler
End Sub

Public Sub Initialize
End Sub

Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")

    XUI.SetDataFolder("Simply Software\Quartz Scheduler")

    QS.InitializeAsync("qs")
    QS.StartAsync
End Sub

Private Sub QS_schedulerstarted
    Log("[Quartz] SchedulerStarted")

    ' Daily at 16:30
    Dim cron As String = "0 30 16 * * ?"

    Dim Data As Map
        Data.Initialize
        Data.Put("filename", "daily1630_output.txt")

    QS.ScheduleCronJob("Daily1630Writer", cron, Data)
End Sub

Private Sub QS_jobstarted(JobName As String)
    Log($"[Quartz] JobStarted: ${JobName}"$)
End Sub

Private Sub QS_jobfinished(JobName As String)
    Log($"[Quartz] JobFinished: ${JobName}"$)
End Sub

Private Sub QS_joberror(JobName As String, Error As String)
    Log($"[Quartz] JobError: ${JobName} | ${Error}"$)
End Sub

Private Sub QS_jobscheduled(JobName As String)
    Log($"[Quartz] JobScheduled: ${JobName}"$)
End Sub

Private Sub QS_jobdeleted(JobName As String)
    Log($"[Quartz] JobDeleted: ${JobName}"$)
End Sub

Private Sub QS_jobexecute(JobName As String, Data As Map)
    Log($"[Quartz] JobExecute: ${JobName}, Data=${Data}"$)

    If JobName = "Daily1630Writer" Then
        Dim filename As String = Data.Get("filename")

        ' *** B4J writes to the file at 16:30 every day ***
        File.WriteString(XUI.DefaultFolder, File.GetName(filename), "Peter Simpson writes dailt at 16:30..." & CRLF)
    End If
End Sub

Private Sub QS_schedulererror(Error As String)
    If Error = Null Or Error = "" Then Return
    Log($"[Quartz] SchedulerError: ${Error}"$)
End Sub

Private Sub QS_schedulershutdown
    Log("[Quartz] SchedulerShutdown")
End Sub

Everyday Saturday at 00:00 (Midnight is Sunday)
Every Saturday at 00:00:
Sub Class_Globals
    Private Root As B4XView
    Private XUI As XUI

    Private QS As QuartzScheduler
End Sub

Public Sub Initialize
End Sub

Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")

    XUI.SetDataFolder("Simply Software\Quartz Scheduler")

    QS.InitializeAsync("qs")
    QS.StartAsync
End Sub

Private Sub QS_schedulerstarted
    Log("[Quartz] SchedulerStarted")

    ' Midnight at the start of Saturday
    Dim cron As String = "0 0 0 ? * SUN"

    Dim Data As Map
        Data.Initialize
    Data.Put("filename", "midnight_output.txt")

    QS.ScheduleCronJob("MidnightWriter", cron, Data)
End Sub

Private Sub QS_jobstarted(JobName As String)
    Log($"[Quartz] JobStarted: ${JobName}"$)
End Sub

Private Sub QS_jobfinished(JobName As String)
    Log($"[Quartz] JobFinished: ${JobName}"$)
End Sub

Private Sub QS_joberror(JobName As String, Error As String)
    Log($"[Quartz] JobError: ${JobName} | ${Error}"$)
End Sub

Private Sub QS_jobscheduled(JobName As String)
    Log($"[Quartz] JobScheduled: ${JobName}"$)
End Sub

Private Sub QS_jobdeleted(JobName As String)
    Log($"[Quartz] JobDeleted: ${JobName}"$)
End Sub

Private Sub QS_jobexecute(JobName As String, Data As Map)
    Log($"[Quartz] JobExecute: ${JobName}, Data=${Data}"$)

    If JobName = "MidnightWriter" Then
        Dim filename As String = Data.Get("filename")

        ' *** B4J writes to the file at midnight ***
        File.WriteString(XUI.DefaultFolder, File.GetName(filename), "Peter Simpson uses B4X..." & CRLF)     
    End If
End Sub

Private Sub QS_schedulererror(Error As String)
    If Error = Null Or Error = "" Then Return
    Log($"[Quartz] SchedulerError: ${Error}"$)
End Sub

Private Sub QS_schedulershutdown
    Log("[Quartz] SchedulerShutdown")
End Sub


Enjoy...
 
Last edited:

Peter Simpson

Expert
Licensed User
Longtime User
Even more...

Beginner Tutorial:
How to write cron commands


Cron expressions are short text strings that describe when a job should run. They are used by Quartz, Linux cron, and many schedulers. Your wrapper accepts standard Quartz cron expressions, which use six fields.

This tutorial teaches a complete beginner how to read and write cron expressions correctly.


1. The Six Field Quartz Cron Format

Quartz uses the following structure:

Seconds Minutes Hours DayOfMonth Month DayOfWeek

Each field is separated by a space.

Allowed ranges

FieldRange
Seconds0 to 59
Minutes0 to 59
Hours0 to 23
Day of month1 to 31
Month1 to 12
Day of week1 to 7 (1 is Sunday)


2. Meaning of Special Characters

Asterisk *

Means every possible value.

Example:
means every second of every minute of every hour.

Slash /
Means step values.

Example:
0/10 * means every 10 seconds.

Comma ,
Means multiple specific values.

Example:
0 0 9,17 * means at 9am and 5pm.

Hyphen -
Means a range.

Example:
0 0 9-17 * means every hour from 9am to 5pm.

Question mark ?
Used in DayOfMonth or DayOfWeek when you do not want to specify a value.

Example:
0 0 12 ? * 2 means every Monday at noon.


3. Common Cron Examples

Every minute
0 0/1 * ?

Every 10 seconds
0/10 ?

Every day at 6.30am
0 30 6 ?

Every Sunday at 11pm
0 0 23 ? * 1

First day of every month at midnight
0 0 0 1 * ?

Every weekday at 9am
0 0 9 ? * 2-6

Every 15 minutes
0 0/15 * ?


4. How to think about cron expressions

A good way to understand cron is to read it from left to right:

Seconds: When in the minute
Minutes: Which minute
Hours: Which hour
Day of month: Which date
Month: Which month
Day of week: Which weekday

Example:

0 45 14 ?

Read as:

At second 0
At minute 45
At hour 14 (2.45pm)
Every day
Every month
Any weekday


5. Testing cron expressions

Quartz cron expressions can be tested using online tools.
  • CronMaker
  • FreeFormatter Quartz Cron Tester
These tools show the next run times so you can confirm your expression is correct.


6. Tips for writing reliable cron expressions

Always include the seconds field because Quartz requires it.
Use ? in either DayOfMonth or DayOfWeek when you do not care about that field.
Use step values like 0/5 for regular intervals.
Keep expressions simple until you are confident.
Test your cron before using it in your application.


Enjoy...
 
Last edited:

Peter Simpson

Expert
Licensed User
Longtime User
Even more, again...

Below is a quick cheat sheet to help B4J developers learn how to use cron.

Cron expression cheat sheet - Quartz Format

This cheat sheet gives developers quick, ready to use cron expressions for the most common scheduling patterns.

All examples use the Quartz six field format:

Seconds Minutes Hours DayOfMonth Month DayOfWeek


1. Quick reference table

PurposeCron Expression
Every second
Every 5 seconds0/5 ?
Every 10 seconds0/10 ?
Every 30 seconds0/30 ?
Every minute0 0/1 * ?
Every 5 minutes0 0/5 * ?
Every 15 minutes0 0/15 * ?
Every 30 minutes0 0/30 * ?
Every hour0 0 * ?
Every 2 hours0 0 0/2 ?
Every 6 hours0 0 0/6 ?
Every day at midnight0 0 0 ?
Every day at 6am0 0 6 ?
Every day at 6.30am0 30 6 ?
Every Monday at noon0 0 12 ? * 2
Every Friday at 5pm0 0 17 ? * 6
Every weekend (Sat and Sun) at 9am0 0 9 ? * 1,7
First day of every month at midnight0 0 0 1 * ?
Last day of every month at midnight0 0 0 L * ?
Every weekday at 9am0 0 9 ? * 2-6
Every weekend at 9am0 0 9 ? * 1,7
Every year on 1 January at midnight0 0 0 1 1 ?


2. Human Friendly Examples

Run something every 10 seconds

0/10 ?

Run something every 15 minutes
0 0/15 * ?

Run at 2.45pm every day
0 45 14 ?

Run at 11pm every Sunday
0 0 23 ? * 1

Run at 3am on the first day of every month
0 0 3 1 * ?

Run at 9am Monday to Friday
0 0 9 ? * 2-6


3. Special characters explained

SymbolMeaning
*Every possible value
/Step values (for example 0/5 means every 5 units)
,List of values
-Range of values
?No specific value (used in DayOfMonth or DayOfWeek)
LLast day of the month


4. Tips for writing reliable cron expressions

Always include the seconds field because Quartz requires it.
Use ? in either DayOfMonth or DayOfWeek when you do not care about that field.
Use step values like 0/5 for regular intervals.
Keep expressions simple until you are confident.
Test your cron expressions using an online Quartz tester.


Enjoy...
 
Last edited:
Top