| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| === Scheduler |
| |
| Apache Karaf provides an optional Scheduler which provides a Service Listener which listens for Runnable Services and schedules their execution, based on the service properties. |
| |
| This Scheduler implementation uses the Quartz Scheduler library to understand cron-like expressions. |
| |
| ==== Installation |
| |
| To enable the Apache Karaf Scheduler, you just have to install the `scheduler` feature: |
| |
| ---- |
| karaf@root()> feature:install scheduler |
| ---- |
| |
| The `scheduler` feature automatically installs the `scheduler` command group, too: |
| |
| ---- |
| scheduler:list |
| scheduler:schedule-script |
| scheduler:schedule-command |
| scheduler:unschedule |
| scheduler:reschedule |
| ---- |
| |
| ==== Configuration |
| |
| All jobs allow configuration using service properties: |
| |
| .Scheduler properties |
| [width="100%",cols="3,2,10",options="header"] |
| |========================================================= |
| |Property |Default |Description |
| |
| |Scheduler.PROPERTY_SCHEDULER_PERIOD | - | |
| Defines the period for a job. The period is expressed in seconds. This property needs to be of type Long. |
| |
| |Scheduler.PROPERTY_SCHEDULER_TIMES | -1 | |
| Defines the number of times the job is executed. -1 means infinite. |
| |
| |Scheduler.PROPERTY_SCHEDULER_IMMEDIATE |false | |
| Define if a periodically job should be scheduled immediate. |
| Default is to not startup immediate, the job is started the first time after the period has expired. |
| This property needs to be of type Boolean. |
| |
| |Scheduler.PROPERTY_SCHEDULER_EXPRESSION | - | |
| Define the cron expression for a job. Must be a Quartz compatible expression. |
| |
| |Scheduler.PROPERTY_SCHEDULER_CONCURRENT | - | |
| Define if the job can be run concurrently. |
| |
| |Scheduler.PROPERTY_SCHEDULER_NAME |- | |
| Define the job name. |
| |
| |========================================================= |
| |
| |
| ==== Schedule a new Job using the Whitebox-Service paradigm (recommended) |
| This example uses Declarative Services to register a Service of Type "org.apache.karaf.scheduler.Job" so that it is recognized by the Scheduler Service. |
| |
| Alternatively, jobs can be registered as type "Runnable" in a more API neutral way. In this case you won't get the "JobContext" information though. |
| ---- |
| @Component(immediate = true, property = { |
| Scheduler.PROPERTY_SCHEDULER_EXPRESSION + "=0 0/10 * * * ?", |
| } ) |
| public class SchedulerPing implements Job { |
| |
| @Override |
| public void execute(JobContext context) { |
| // .. |
| } |
| } |
| ---- |
| |
| This will register a Job with the WhiteboxHandler. You can verify that the job is registered: |
| |
| ---- |
| karaf@root()> scheduler:list |
| Name │ Schedule |
| ───────────────────────┼───────────────────── |
| Registered Service.185 │ cron(0 0/10 * * * ?) |
| ---- |
| |
| The Karaf scheduler can also schedule `Runnable` service. |
| |
| For instance, if you have the following bean: |
| |
| ``` |
| @Component(immediate = true, property = { |
| "scheduler.period:Long=60", |
| "scheduler.concurrent:Boolean=false", |
| "scheduler.name=PingJob" |
| } |
| ) |
| public class PingThread implements Runnable { |
| |
| @Override |
| public void run() { |
| // .. |
| } |
| |
| } |
| ``` |
| |
| This will register a job for the thread (runnable): |
| |
| ---- |
| karaf@root()> scheduler:list |
| Name │ Schedule |
| ───────────────────────────┼────────────────────────────────────────── |
| PingJob.126 │ at(2017-11-22T15:37:17.103+01:00, -1, 10) |
| ---- |
| |
| It's also possible to pass the number of execution of the scheduler job using the `scheduler.times` service property: |
| |
| ``` |
| @Component(immediate = true, property = { |
| "scheduler.period:Long=60", |
| "scheduler.times:Integer=5", |
| "scheduler.concurrent:Boolean=false", |
| "scheduler.name=PingJob" |
| } |
| ) |
| public class PingThread implements Runnable { |
| |
| @Override |
| public void run() { |
| // .. |
| } |
| |
| } |
| ``` |
| |
| ==== Schedule a new Job using the Gogo Shell |
| |
| ---- |
| karaf@root()> scheduler:schedule-script --help |
| DESCRIPTION |
| scheduler:schedule |
| |
| Schedule a script execution |
| |
| SYNTAX |
| scheduler:schedule [options] script |
| |
| ARGUMENTS |
| script |
| The script to schedule |
| |
| OPTIONS |
| --at |
| Absolute date in ISO format (ex: 2014-05-13T13:56:45) |
| --concurrent |
| Should jobs run concurrently or not (defaults to false) |
| --period |
| Time during executions (in seconds) |
| --times |
| Number of times this job should be executed |
| (defaults to -1) |
| --cron |
| The cron expression |
| --help |
| Display this help message |
| --name |
| Name of this job |
| |
| ---- |
| |
| ==== Schedule a command |
| |
| ---- |
| karaf@root()> scheduler:schedule-command --help |
| DESCRIPTION |
| scheduler:schedule-command |
| |
| Schedule a command execution |
| |
| SYNTAX |
| scheduler:schedule-command [options] command |
| |
| ARGUMENTS |
| command |
| The command to schedule |
| (required) |
| |
| OPTIONS |
| --period |
| Time during executions (in seconds) |
| --at |
| Absolute date in ISO format (ex: 2014-05-13T13:56:45) |
| --help |
| Display this help message |
| --times |
| Number of times this job should be executed |
| (defaults to -1) |
| --concurrent |
| Should jobs run concurrently or not (defaults to false) |
| --cron |
| The cron expression |
| |
| ---- |
| |
| ==== Schedule a new Job using the Scheduler Service |
| |
| Recommendation: Before using this low level api for registering jobs, consider using the whitebox approach instead. |
| |
| ---- |
| .. |
| import org.apache.karaf.scheduler.Scheduler; |
| |
| @Component |
| public class Demo { |
| |
| @Reference Scheduler scheduler; |
| |
| public void useScheduler() |
| { |
| schedule(new MyJob(), scheduler.EXPR("0 0/10 * * * ?")); |
| } |
| |
| class MyJob implements Job { |
| .. |
| } |
| |
| } |
| |
| ---- |
| |
| ==== Update scheduling of an existing job |
| |
| You can change the scheduling of an existing job using `scheduler:reschedule` command. |
| |
| This command works as the schedule command (using the same `at`, `period`, `cron`, ... options) but taking the job name |
| as argument (as given by the `scheduler:list` command). |
| |
| ==== Using shared jobs store |
| |
| By the default, the Apache Karaf scheduler uses a memory storage for jobs. It's local to a single Karaf instance. |
| |
| You can setup several Karaf instances scheduler to use a shared job storage. It's especially interesting when you have |
| a farm or cluster of Karaf instances. |
| |
| For instance, you can use a database as storage. |
| |
| You can create the datasource to this database, using the regular Karaf `jdbc` commands. For instance, to setup a `DataSource` |
| for a remote Derby database, you can do: |
| |
| ---- |
| karaf@root()> feature:install jdbc |
| karaf@root()> feature:install pax-jdbc-derbyclient |
| karaf@root()> jdbc:ds-create -dn derbyclient -url jdbc:derby://host:1527/karaf_scheduler scheduler |
| karaf@root()> feature:install jndi |
| ---- |
| |
| You have to init the DataBase tables with the SQL scripts provided in Quartz distribution (in the `docs/dbTables` directory). |
| |
| Then you can configure the Karaf Scheduler to use this job storage, updating the `etc/org.apache.karaf.scheduler.cfg` file like this: |
| |
| ---- |
| #============================================================================ |
| # Configure Karaf Scheduler Properties |
| #============================================================================ |
| org.quartz.scheduler.instanceName=Karaf |
| org.quartz.scheduler.instanceId=AUTO |
| |
| #============================================================================ |
| # Configure ThreadPool |
| #============================================================================ |
| org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool |
| org.quartz.threadPool.threadCount=30 |
| org.quartz.threadPool.threadPriority=5 |
| |
| |
| #============================================================================ |
| # Configure JDBC DataSource |
| #============================================================================ |
| org.quartz.dataSource.scheduler.jndiURL=osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=scheduler) |
| |
| #============================================================================ |
| # Configure JDBC JobStore |
| #============================================================================ |
| org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX |
| org.quartz.jobStore.dataSource=scheduler |
| org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate |
| ---- |
| |
| Then several Karaf instances scheduler will share the same JDBC job store and can work in a "clustered" way. |