ryoakg
6/7/2016 - 6:39 AM

cron4j-schedule-string-wrapper.clj

(set-env! :dependencies '[[it.sauronsoftware.cron4j/cron4j "2.2.5"]])
(import (it.sauronsoftware.cron4j Scheduler))

(defonce scheduler (atom (Scheduler.)))
(defonce task-id (atom nil))

;;; Scheduler
(.isStarted @scheduler) ; => false
(.start @scheduler)
;; (.stop @scheduler)
(.isStarted @scheduler) ; => true

(.getGuid @scheduler) ; => "f758caa855270c5a10a6b6ab0000015533418c0158ad410c"
(.getID (.getTimeZone @scheduler))      ; => "Asia/Tokyo"
;; (.setTimeZone @scheduler x)



;;; simple usage
(reset! task-id
        (.schedule @scheduler "* * * * *"
                   #(println (java.util.Date.))))
;; prints:
;; #inst "2016-06-09T03:15:00.009-00:00"
;; #inst "2016-06-09T03:16:00.015-00:00"
;; #inst "2016-06-09T03:17:00.004-00:00"

(str (.getSchedulingPattern @scheduler @task-id)) ; => "* * * * *"

(.getTask @scheduler @task-id)
;; => #object[it.sauronsoftware.cron4j.RunnableTask 0x2e6399e1 "Task[runnable=boot.user$eval9938$fn__9939@3b7e62f]"]

(comment
  (class (.getExecutingTasks @scheduler))
  (seq (.getExecutingTasks @scheduler)))

(.deschedule @scheduler @task-id)
;; (.stop @scheduler)



;;; a task takes time then specified interval.
(reset! task-id
        (.schedule @scheduler "* * * * *"
                   #(do
                      (println (java.util.Date.))
                      (Thread/sleep (* 1000 60 3))))) ;3min
;; prints:
;; #inst "2016-06-09T03:20:00.004-00:00"
;; #inst "2016-06-09T03:21:00.002-00:00"
;; #inst "2016-06-09T03:22:00.001-00:00"
;; #inst "2016-06-09T03:23:00.002-00:00"
;; #inst "2016-06-09T03:24:00.001-00:00"
(.deschedule @scheduler @task-id)

(def lock (Object.))
(reset! task-id
        (.schedule @scheduler "* * * * *"
                   #(locking lock
                      (println (java.util.Date.))
                      (Thread/sleep (* 1000 60 3)))))
;; #inst "2016-06-10T06:28:00.006-00:00"
;; #inst "2016-06-10T06:31:00.008-00:00"
;; #inst "2016-06-10T06:34:00.009-00:00"
;; #inst "2016-06-10T06:37:00.010-00:00"
(.deschedule @scheduler @task-id)


;;; reschedule
(do
  (reset! task-id
          (.schedule @scheduler "* * * * *"
                     #(println (java.util.Date.))))
  ;; prints:
  ;; #inst "2016-06-09T03:44:00.007-00:00"
  ;; #inst "2016-06-09T03:45:00.001-00:00"
  ;; #inst "2016-06-09T03:46:00.002-00:00"
  (Thread/sleep (* 3 60 1000))
  (.reschedule @scheduler @task-id "*/2 * * * *")
  ;; prints:
  ;; #inst "2016-06-09T03:48:00.002-00:00"
  ;; #inst "2016-06-09T03:50:00.002-00:00"
  ;; #inst "2016-06-09T03:52:00.001-00:00"
  )
(.deschedule @scheduler @task-id)



;;; Pattern
(it.sauronsoftware.cron4j.SchedulingPattern/validate "*/2 * * * *") ; => true
(it.sauronsoftware.cron4j.SchedulingPattern/validate "a * * * *") ; => false

(defonce task-ids (atom []))
(swap! task-ids conj
       (.schedule @scheduler "*/2 * * * *"
                  #(println "a: " (java.util.Date.))))
(swap! task-ids conj
       (.schedule @scheduler "1-59/2 * * * *"
                  #(println "b: " (java.util.Date.))))
;; prints:
;; a:  #inst "2016-06-14T01:52:00.001-00:00"
;; b:  #inst "2016-06-14T01:53:00.001-00:00"
;; a:  #inst "2016-06-14T01:54:00.001-00:00"
;; b:  #inst "2016-06-14T01:55:00.000-00:00"
;; a:  #inst "2016-06-14T01:56:00.001-00:00"
;; b:  #inst "2016-06-14T01:57:00.001-00:00"
;; a:  #inst "2016-06-14T01:58:00.001-00:00"
(dorun (map #(.deschedule @scheduler %) @task-ids))


;;; Task
(reset! task-id
        (.schedule @scheduler "* * * * *"
                   (proxy [it.sauronsoftware.cron4j.Task] []
                     (execute [^it.sauronsoftware.cron4j.TaskExecutionContext ctx]
                       (println (java.util.Date.))))))

(.deschedule @scheduler @task-id)

(.launch @scheduler
         (proxy [it.sauronsoftware.cron4j.Task] []
           (execute [^it.sauronsoftware.cron4j.TaskExecutionContext ctx]
             #(println (java.util.Date.)))))

  (let [task (proxy [it.sauronsoftware.cron4j.Task] []
               (execute [^it.sauronsoftware.cron4j.TaskExecutionContext ctx]
                 (println (java.util.Date.))))]
    [(.canBePaused task)
     (.canBeStopped task)
     (.supportsCompletenessTracking task)
     (.supportsStatusTracking task)])

(comment
  (do
    (.launch @scheduler
             (proxy [it.sauronsoftware.cron4j.Task] []
               (execute [^it.sauronsoftware.cron4j.TaskExecutionContext ctx]
                 #(do
                    (println (java.util.Date.)) (flush)
                    (Thread/sleep (* 10 1000))))))
    (seq (.getExecutingTasks @scheduler)))

  (reset! task-id
          (.schedule @scheduler "* * * * *"
                     #(throw (ex-info "error!!" {}))))

  ;; public it.sauronsoftware.cron4j.Task Scheduler.getTask(String)
  )



;;; SchedulerListeners
(comment
  (class (.getSchedulerListeners @scheduler))
  ;; public void Scheduler.addSchedulerListener(SchedulerListener)
  ;; public void Scheduler.removeSchedulerListener(SchedulerListener)
  (reify
    it.sauronsoftware.cron4j.SchedulerListener
    (taskFailed [^TaskExecutor executor ^Throwable ex])
    (taskLaunching [^TaskExecutor executor])
    (taskSucceeded [^TaskExecutor executor])
    )
  )



;;; TaskCollector
(comment
  (class (.getTaskCollectors @scheduler))
  ;; public void Scheduler.addTaskCollector(it.sauronsoftware.cron4j.TaskCollector)
  ;; public void Scheduler.removeTaskCollector(it.sauronsoftware.cron4j.TaskCollector)
  )



;;; Daemon
(comment
  (.isDaemon @scheduler)
  ;; public void Scheduler.setDaemon(boolean) throws IllegalStateException
  ;; public boolean Scheduler.isDaemon()
  )
(set-env! :dependencies '[[it.sauronsoftware.cron4j/cron4j "2.2.5"]
                          [prismatic/schema "1.1.0"]])

(require '[schema.core :as s]
         '[clojure.string :as str])
(import (it.sauronsoftware.cron4j Scheduler))

(def ^:private
  pattern-schema
  "http://www.sauronsoftware.it/projects/cron4j/manual.php#p02"
  {:month         (s/conditional integer? (-> s/Int (s/constrained #(<= 1 % 12) 'month))
                                 :else (s/enum :* :jan :feb :mar :apr :may :jun :jul :aug :sep :oct :nov :dec))
   :days-of-month (s/conditional integer? (-> s/Int (s/constrained #(<= 1 % 31) 'days-of-month))
                                 :else (s/pred #(#{:L ; the last day of month.
                                                   :*} %)))
   :hour          (s/conditional integer? (-> s/Int (s/constrained #(<= 0 % 23) 'hour))
                                 :else (s/pred #(= :* %)))
   :minute        (s/conditional integer? (-> s/Int (s/constrained #(<= 0 % 59) 'minute))
                                 :else (s/pred #(= :* %)))
   :days-of-week  (s/conditional integer? (-> s/Int (s/constrained #(<= 0 % 7)  'days-of-week))
                                 :else (s/enum :* :sun :mon :tue :wed :thu :fri :sat))})

(defn- schedule-string [schedule]
  (let [extract ^const (juxt :minute :hour :days-of-month :month :days-of-week)]
    (->> (s/validate pattern-schema schedule)
         extract
         (map #(cond (keyword? %) (name %)
                     :else (str %)))
         (str/join \space))))

(defonce scheduler (atom (Scheduler.)))
(defonce task-id (atom nil))

(reset! task-id
        (.schedule @scheduler
                   (schedule-string {:month :*
                                     :days-of-month :*
                                     :hour :*
                                     :minute :*
                                     :days-of-week :*})
                   #(println (java.util.Date.))))
(.start @scheduler)