trinidad_scheduler_extension 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +26 -0
- data/README +127 -0
- data/lib/trinidad_scheduler_extension.rb +22 -0
- data/lib/trinidad_scheduler_extension/app_job.rb +152 -0
- data/lib/trinidad_scheduler_extension/config/log4j.properties +11 -0
- data/lib/trinidad_scheduler_extension/extensions/object.rb +12 -0
- data/lib/trinidad_scheduler_extension/jars/log4j-1.2.16.jar +0 -0
- data/lib/trinidad_scheduler_extension/jars/quartz-1.8.4.jar +0 -0
- data/lib/trinidad_scheduler_extension/jars/slf4j-api-1.6.1.jar +0 -0
- data/lib/trinidad_scheduler_extension/jars/slf4j-log4j12-1.6.1.jar +0 -0
- data/lib/trinidad_scheduler_extension/job_detail.rb +20 -0
- data/lib/trinidad_scheduler_extension/job_factory.rb +9 -0
- data/lib/trinidad_scheduler_extension/scheduled_job.rb +22 -0
- data/lib/trinidad_scheduler_extension/scheduler_extension.rb +18 -0
- data/lib/trinidad_scheduler_extension/scheduler_listener.rb +42 -0
- data/lib/trinidad_scheduler_extension/trinidad_scheduler.rb +131 -0
- data/lib/trinidad_scheduler_extension/version.rb +3 -0
- metadata +83 -0
data/LICENSE
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
== TrinidadScheduler Extension
|
2
|
+
|
3
|
+
Copyright (c) 2011 Brandon Dewitt
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
== Additional Bundled Software
|
25
|
+
|
26
|
+
Quartz Scheduler is licensed according to the terms of Apache License, Version 2.0 (current). See http://www.apache.org/licenses/LICENSE-2.0 for details.
|
data/README
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
Trinidad Scheduler Extension
|
2
|
+
===
|
3
|
+
Trinidad Scheduler uses Quartz to schedule processes for execution. It can be run as a server extension to Trinidad and/or a Web Application extension
|
4
|
+
for Trinidad. If run as a Server extension all schedulers will get the server configuration options each option that is defined at the Web Application
|
5
|
+
level will override the Server option.
|
6
|
+
|
7
|
+
Trinidad Scheduler creates a unique scheduler for each web application.
|
8
|
+
|
9
|
+
Most processes we schedule are scheduled using the *Cron* Trigger and *run_later*
|
10
|
+
|
11
|
+
Install Gem
|
12
|
+
---
|
13
|
+
gem install trinidad_scheduler_extension
|
14
|
+
|
15
|
+
Configure Trinidad
|
16
|
+
---
|
17
|
+
In either the Server *extensions* block or the Web Application *extentions* block add "scheduler"
|
18
|
+
|
19
|
+
extensions:
|
20
|
+
scheduler:
|
21
|
+
|
22
|
+
Example Usage
|
23
|
+
---
|
24
|
+
It is valid to use the top level scheduling methods and run_later together
|
25
|
+
|
26
|
+
class ScheduledLog < TrinidadScheduler.Cron "0/5 * * * * ?"
|
27
|
+
def run
|
28
|
+
_logger.info "Executed every 5 seconds"
|
29
|
+
|
30
|
+
TrinidadScheduler.run_later do
|
31
|
+
_logger.info "Executed after a 3 second delay"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Laziness
|
37
|
+
---
|
38
|
+
Trinidad Scheduler is very lazy. Schedulers will only be instantiated when they are needed to execute a job or to setup a schedule for execution.
|
39
|
+
This laziness extends to even runtime definition of classes and use of run_later in conditional statements. When a run_later block is encountered or
|
40
|
+
a class is defined at runtime that inherits from a TrinidadScheduler base method the scheduler will be created and started (if it does not exist)
|
41
|
+
|
42
|
+
If schedules are defined during application initialization then the scheduler will not be started until after the application is started by Tomcat.
|
43
|
+
|
44
|
+
(The lazy nature of TrinidadScheduler also gives the user time to define a logger outstide of the default configured log4j StdOut logger that
|
45
|
+
is included with TrinidadScheduler)
|
46
|
+
|
47
|
+
Usage
|
48
|
+
===
|
49
|
+
The extension defines several methods that return classes based on the configuration options provided. These methods map to the scheduler trigger type
|
50
|
+
that Quartz provides. The implemented triggers are CronTrigger, SimpleTrigger, and DateIntervalTrigger.
|
51
|
+
|
52
|
+
Cron Trigger
|
53
|
+
---
|
54
|
+
To define a process to be run based on a [cron expression] (http://en.wikipedia.org/wiki/CRON_expression#CRON_expression)
|
55
|
+
|
56
|
+
class ScheduledClass < TrinidadScheduler.Cron "0/5 * * * * ?"
|
57
|
+
def run
|
58
|
+
_logger.info "I am printed every 5 seconds"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
The method *TrinidadScheduler.Cron* takes a cron expression as it's only argument and returns a class. This anonymous class is the parent of
|
63
|
+
ScheduledClass and does the work to wrap ScheduledClass for use as a CronTrigger in Quartz.
|
64
|
+
|
65
|
+
The instance method "run" must be defined because it is called when the scheduled process is triggered. *_logger* is an instance variable available
|
66
|
+
in ScheduledClass which gives the class access to the Quartz logger that is configured.
|
67
|
+
|
68
|
+
Simple Trigger
|
69
|
+
---
|
70
|
+
Schedule an INFO log message every 5 seconds starting now, setting the end is not necessary in this context, but is done
|
71
|
+
|
72
|
+
class TestJob < TrinidadScheduler.Simple :start => Time.now, :end => Time.now + 240, :repeat 3, :interval => 5000
|
73
|
+
def run
|
74
|
+
_logger.info "I am inside this block" #=> prints "I am inside this block" every 5 seconds
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
The Simple Trigger will execute based on options passed to the method *TrinidadScheduler.Simple*, the options available are outlined
|
79
|
+
above in the example, none of them are necessary if you only want to trigger the process once. You can define a start and end time as well as how many
|
80
|
+
times to fire the trigger along with an interval to be observed between trigger execution.
|
81
|
+
|
82
|
+
DateInterval Trigger
|
83
|
+
---
|
84
|
+
Schedule an INFO log message every 5 seconds starting now and ending after 4 minutes
|
85
|
+
|
86
|
+
class TestJob < TrinidadScheduler.DateInterval :start => Time.now, :end => Time.now + 240, :unit => :second, :interval => 5
|
87
|
+
def run
|
88
|
+
_logger.info "I am inside this block" #=> prints "I am inside this block" every 5 seconds
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
The DateInterval Trigger will execute a triggered process based on the configuration options passed. For more information on using the DateInterval
|
93
|
+
trigger consult the source.
|
94
|
+
|
95
|
+
run_later
|
96
|
+
---
|
97
|
+
Schedules a block of code to run in another Thread after execution proceeds in the current Thread
|
98
|
+
*after the job runs it removes itself from the job scheduler
|
99
|
+
|
100
|
+
Using run_later with default 3 second delay
|
101
|
+
|
102
|
+
TrinidadScheduler.run_later do
|
103
|
+
_logger.info "I am inside this block" #=> prints "I am inside this block"
|
104
|
+
end
|
105
|
+
|
106
|
+
Using run_later with 20 second delay
|
107
|
+
|
108
|
+
TrinidadScheduler.run_later(:delay => 20) do
|
109
|
+
_logger.info "I am inside this block" #=> prints "I am inside this block"
|
110
|
+
end
|
111
|
+
|
112
|
+
Behind the scemes *run_later* is actually implemented using an anonymous class that inherits from TrinidadScheduler.Simple to schedule the run.
|
113
|
+
|
114
|
+
|
115
|
+
Inspiration
|
116
|
+
---
|
117
|
+
Open Source software is a community effort - thanks to all, but the following were instrumental in the inspiration for TrinidadScheduler.
|
118
|
+
|
119
|
+
[techwhizbang] (https://github.com/techwhizbang/jruby-quartz) for handling of Quartz JobFactory
|
120
|
+
[why_metaid] (https://github.com/evaryont/why_metaid) for metaid extension
|
121
|
+
[TERRACOTTA] (http://www.terracotta.org/) for continued support Quartz Scheduler
|
122
|
+
[calavera] (https://github.com/calavera/trinidad) for Trinidad Server
|
123
|
+
|
124
|
+
|
125
|
+
Copyright
|
126
|
+
---
|
127
|
+
Copyright (c) 2011 Brandon Dewitt<brandon+trinidad_scheduler@myjibe.com>. See LICENSE for details.
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
require 'trinidad'
|
4
|
+
require 'trinidad/jars'
|
5
|
+
|
6
|
+
# Jar files that are needed
|
7
|
+
require 'trinidad_scheduler_extension/jars/log4j-1.2.16'
|
8
|
+
require "trinidad_scheduler_extension/jars/slf4j-api-1.6.1"
|
9
|
+
require "trinidad_scheduler_extension/jars/slf4j-log4j12-1.6.1"
|
10
|
+
require "trinidad_scheduler_extension/jars/quartz-1.8.4"
|
11
|
+
|
12
|
+
# Trinidad Scheduler Extension files
|
13
|
+
require 'trinidad_scheduler_extension/version.rb'
|
14
|
+
require 'trinidad_scheduler_extension/trinidad_scheduler'
|
15
|
+
require 'trinidad_scheduler_extension/extensions/object'
|
16
|
+
require 'trinidad_scheduler_extension/scheduler_listener'
|
17
|
+
require 'trinidad_scheduler_extension/job_detail'
|
18
|
+
require 'trinidad_scheduler_extension/job_factory'
|
19
|
+
require 'trinidad_scheduler_extension/scheduled_job'
|
20
|
+
require 'trinidad_scheduler_extension/app_job'
|
21
|
+
require 'trinidad_scheduler_extension/scheduler_extension'
|
22
|
+
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module TrinidadScheduler
|
2
|
+
module AppJob
|
3
|
+
include org.quartz.Job
|
4
|
+
include TrinidadScheduler::ScheduledJob
|
5
|
+
|
6
|
+
def self.included(other_obj)
|
7
|
+
new_job = TrinidadScheduler::JobDetail.new("#{ other_obj.job_detail_name rescue other_obj }", "#{other_obj}", other_obj)
|
8
|
+
begin
|
9
|
+
TrinidadScheduler[$servlet_context].schedule_job(new_job, other_obj.trigger)
|
10
|
+
rescue Exception => ex
|
11
|
+
raise JobError.new(ex)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Method to schedule a block of code to run in another Thread after execution proceeds in the current Thread
|
17
|
+
# after the job runs it removes itself from the job scheduler
|
18
|
+
#
|
19
|
+
# @example Running run_later with default 3 second delay
|
20
|
+
# TrinidadScheduler.run_later do
|
21
|
+
# _logger.info "I am inside this block" #=> prints "I am inside this block"
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# @example Running run_later with 20 second delay
|
25
|
+
# TrinidadScheduler.run_later(:delay => 20) do
|
26
|
+
# _logger.info "I am inside this block" #=> prints "I am inside this block"
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# @param [Hash] opts the options for the process to be run
|
30
|
+
# @option opts [Integer] :delay the number of seconds delay before the block is triggered
|
31
|
+
# @param [Block] the block that will be run in a separate Thread after the delay
|
32
|
+
def self.run_later(opts={:delay=>3}, &blk)
|
33
|
+
Class.new(TrinidadScheduler.Simple :start => (Time.now + opts[:delay])) do
|
34
|
+
meta_def(:job_detail_name){ Time.now.to_i.to_s << Time.now.usec.to_s }
|
35
|
+
meta_def(:run_proc){ blk }
|
36
|
+
|
37
|
+
def run
|
38
|
+
self.class.run_proc.call
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Method to return an inheritable class for scheduling a CronTrigger Job
|
44
|
+
# the class that inherits from this method will have it's instance run method executed based on the cron_expression
|
45
|
+
#
|
46
|
+
# @example Schedule an INFO log message every 5 seconds
|
47
|
+
# class TestJob < TrinidadScheduler.Cron "0/5 * * * * ?"
|
48
|
+
# def run
|
49
|
+
# _logger.info "I am inside this block" #=> prints "I am inside this block" every 5 seconds
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# @param [String] cron_expression the Cron Expression that defines the CronTrigger for the job class
|
54
|
+
# @return [Class] a new Class that is run by the CronTrigger that is defined
|
55
|
+
def self.Cron(cron_expression)
|
56
|
+
Class.new do
|
57
|
+
meta_def(:cron){ cron_expression }
|
58
|
+
|
59
|
+
def self.inherited(subclass)
|
60
|
+
meta_def :trigger do
|
61
|
+
org.quartz.CronTrigger.new("#{subclass}" + ".trigger", "#{subclass}", self.cron)
|
62
|
+
end
|
63
|
+
|
64
|
+
subclass.send(:include, TrinidadScheduler::AppJob)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Method to return an inheritable class for scheduling a SimpleTrigger Job
|
70
|
+
# the class that inherits from this method will have it's instance run method executed based on the options passed
|
71
|
+
#
|
72
|
+
# @example Schedule an INFO log message every 5 seconds starting now, setting the end is not necessary in this context, but it done
|
73
|
+
# class TestJob < TrinidadScheduler.Simple :start => Time.now, :end => Time.now + 240, :repeat 3, :interval => 5000
|
74
|
+
# def run
|
75
|
+
# _logger.info "I am inside this block" #=> prints "I am inside this block" every 5 seconds
|
76
|
+
# end
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# @param [Hash] opts the options for the SimpleTrigger
|
80
|
+
# @option opts [java.util.Date, Time] :start the starting time of the trigger
|
81
|
+
# @option opts [java.util.Date, Time] :end the ending time of the trigger
|
82
|
+
# @option opts [Integer] :repeat the number of times to repeat the job (defaults to 0)
|
83
|
+
# @option opts [Integer] :interval the number of milliseconds between runs
|
84
|
+
# @return [Class] a new anonymous Class that is the parent of the Class run by the SimpleTrigger that is defined
|
85
|
+
def self.Simple(opts={})
|
86
|
+
opts[:start] ||= java.util.Date.new(Time.now.to_i*1000)
|
87
|
+
opts[:start] = java.util.Date.new(opts[:start].to_i*1000) if opts[:start].class == Time
|
88
|
+
|
89
|
+
opts[:end] ||= java.util.Date.new((Time.now + 10.years).to_i*1000)
|
90
|
+
opts[:end] = java.util.Date.new(opts[:end].to_i*1000) if opts[:end].class == Time
|
91
|
+
|
92
|
+
opts[:repeat] ||= 0
|
93
|
+
opts[:interval] ||= 0
|
94
|
+
|
95
|
+
Class.new do
|
96
|
+
meta_def(:opts){ opts }
|
97
|
+
|
98
|
+
def self.inherited(subclass)
|
99
|
+
meta_def :trigger do
|
100
|
+
org.quartz.SimpleTrigger.new("#{subclass}" + ".trigger", "#{subclass}",
|
101
|
+
self.opts[:start], self.opts[:end],
|
102
|
+
self.opts[:repeat], self.opts[:interval])
|
103
|
+
end
|
104
|
+
|
105
|
+
subclass.send(:include, TrinidadScheduler::AppJob)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Method to return an inheritable class for scheduling a DateIntervalTrigger Job
|
111
|
+
# the class that inherits from this method will have it's instance run method executed based on the options passed
|
112
|
+
#
|
113
|
+
# @example Schedule an INFO log message every 5 seconds starting now and ending after 4 minutes
|
114
|
+
# class TestJob < TrinidadScheduler.DateInterval :start => Time.now, :end => Time.now + 240, :unit => :second, :interval => 5
|
115
|
+
# def run
|
116
|
+
# _logger.info "I am inside this block" #=> prints "I am inside this block" every 5 seconds
|
117
|
+
# end
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# @param [Hash] opts the options for the DateIntervalTrigger
|
121
|
+
# @option opts [java.util.Date, Time] :start the starting time of the trigger
|
122
|
+
# @option opts [java.util.Date, Time] :end the ending time of the trigger
|
123
|
+
# @option opts [Symbol, String] :unit the defined unit (:day, :second, :year, :month, :week)
|
124
|
+
# @option opts [Integer] :interval the number of units between runs
|
125
|
+
# @return [Class] a new anonymous Class that is the parent of the Class run by the SimpleTrigger that is defined
|
126
|
+
def self.DateInterval(opts={})
|
127
|
+
opts[:start] ||= java.util.Date.new(Time.now.to_i*1000)
|
128
|
+
opts[:start] = java.util.Date.new(opts[:start].to_i*1000) if opts[:start].class == Time
|
129
|
+
|
130
|
+
opts[:end] ||= java.util.Date.new((Time.now + 10.years).to_i*1000)
|
131
|
+
opts[:end] = java.util.Date.new(opts[:end].to_i*1000) if opts[:end].class == Time
|
132
|
+
|
133
|
+
opts[:unit] ||= :day
|
134
|
+
opts[:unit] = org.quartz.DateIntervalTrigger::IntervalUnit.value_of(opts[:unit].to_s.upcase)
|
135
|
+
|
136
|
+
opts[:interval] ||= 1
|
137
|
+
|
138
|
+
Class.new do
|
139
|
+
meta_def(:opts){ opts }
|
140
|
+
|
141
|
+
def self.inherited(subclass)
|
142
|
+
meta_def :trigger do
|
143
|
+
org.quartz.DateIntervalTrigger.new("#{subclass}" + ".trigger", "#{subclass}",
|
144
|
+
self.opts[:start], self.opts[:end],
|
145
|
+
self.opts[:unit], self.opts[:interval])
|
146
|
+
end
|
147
|
+
|
148
|
+
subclass.send(:include, TrinidadScheduler::AppJob)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#
|
2
|
+
# our log4j properties / configuration file
|
3
|
+
#
|
4
|
+
# STDOUT appender
|
5
|
+
log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender
|
6
|
+
log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout
|
7
|
+
log4j.appender.STDOUT.layout.ConversionPattern=%d %p [%t] %C{1} - %m\n
|
8
|
+
|
9
|
+
# use the STDOUT appender. set the level to INFO.
|
10
|
+
log4j.rootLogger=INFO, STDOUT
|
11
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Metaid == a few simple metaclass helper
|
2
|
+
# (See http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html.)
|
3
|
+
class Object
|
4
|
+
# The hidden singleton lurks behind everyone
|
5
|
+
def metaclass; class << self; self; end; end
|
6
|
+
def meta_eval &blk; metaclass.instance_eval &blk; end
|
7
|
+
|
8
|
+
# Adds methods to a metaclass
|
9
|
+
def meta_def name, &blk
|
10
|
+
meta_eval { define_method name, &blk }
|
11
|
+
end
|
12
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module TrinidadScheduler
|
2
|
+
class JobDetail < org.quartz.JobDetail
|
3
|
+
|
4
|
+
attr_accessor :job
|
5
|
+
|
6
|
+
def initialize(name, group, job_class)
|
7
|
+
super()
|
8
|
+
set_name name
|
9
|
+
set_group group
|
10
|
+
@job = job_class.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def validate()
|
14
|
+
raise org.quartz.SchedulerException.new("Job's name cannot be null",
|
15
|
+
org.quartz.SchedulerException.ERR_CLIENT_ERROR) if get_name == nil
|
16
|
+
raise org.quartz.SchedulerException.new("Job's group cannot be null",
|
17
|
+
org.quartz.SchedulerException.ERR_CLIENT_ERROR) if get_group == nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module TrinidadScheduler
|
2
|
+
module ScheduledJob
|
3
|
+
class JobError < StandardError; end
|
4
|
+
|
5
|
+
attr_accessor :_context
|
6
|
+
attr_accessor :_logger
|
7
|
+
|
8
|
+
def run
|
9
|
+
raise "Implement a [run] method if you are going to use #{self.class} as a job class"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(context)
|
13
|
+
begin
|
14
|
+
@_context = context
|
15
|
+
@_logger = org.apache.log4j.Logger.getLogger("#{self.class}")
|
16
|
+
run()
|
17
|
+
rescue Exception => ex
|
18
|
+
raise JobError.new(ex)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Trinidad
|
2
|
+
module Extensions
|
3
|
+
|
4
|
+
class SchedulerWebAppExtension < WebAppExtension
|
5
|
+
|
6
|
+
def configure(tomcat, app_context)
|
7
|
+
app_context.add_lifecycle_listener(TrinidadScheduler::WebAppListener.new(app_context.servlet_context, @options))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class SchedulerServerExtension < ServerExtension
|
12
|
+
|
13
|
+
def configure(tomcat)
|
14
|
+
tomcat.get_host.add_container_listener(TrinidadScheduler::GlobalListener.new(@options))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module TrinidadScheduler
|
2
|
+
class WebAppListener
|
3
|
+
include org.apache.catalina.LifecycleListener
|
4
|
+
|
5
|
+
def initialize(servlet_context, options)
|
6
|
+
@servlet_context = servlet_context
|
7
|
+
@options = options
|
8
|
+
TrinidadScheduler.store_scheduler_options(@servlet_context, @options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def needs_started?
|
12
|
+
TrinidadScheduler.scheduler_exists?(@servlet_context) && !TrinidadScheduler[@servlet_context].is_started
|
13
|
+
end
|
14
|
+
|
15
|
+
def lifecycle_event(event)
|
16
|
+
case event.type
|
17
|
+
when org.apache.catalina.Lifecycle::START_EVENT then
|
18
|
+
if needs_started?
|
19
|
+
TrinidadScheduler[@servlet_context].start
|
20
|
+
TrinidadScheduler[@servlet_context].resume_all
|
21
|
+
end
|
22
|
+
|
23
|
+
TrinidadScheduler.set_servlet_started(@servlet_context)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class GlobalListener
|
29
|
+
include org.apache.catalina.ContainerListener
|
30
|
+
|
31
|
+
def initialize(options)
|
32
|
+
@options = options
|
33
|
+
end
|
34
|
+
|
35
|
+
def container_event(event)
|
36
|
+
case event.type
|
37
|
+
when org.apache.catalina.Container::ADD_CHILD_EVENT then
|
38
|
+
event.data.add_lifecycle_listener(TrinidadScheduler::WebAppListener.new(event.data.servlet_context, @options))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module TrinidadScheduler
|
2
|
+
CONFIG_HOME = File.expand_path(File.dirname(__FILE__) + "/trinidad_scheduler_extension/config")
|
3
|
+
JAR_HOME = File.expand_path(File.dirname(__FILE__) + "/trinidad_scheduler_extension/jars")
|
4
|
+
|
5
|
+
# Sets log4j properties if not established by Application Servers
|
6
|
+
# TrinidadScheduler is really lazy so this is only set when a scheduler is needed
|
7
|
+
def self.trinidad_scheduler_init_log4j
|
8
|
+
if java.lang.System.get_property('log4j.configuration').nil?
|
9
|
+
java.lang.System.set_property('log4j.configuration', java.io.File.new("#{CONFIG_HOME}/log4j.properties").to_url.to_s)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Standardizing the naming of the variables that are stored on the context
|
14
|
+
def self.context_path(path)
|
15
|
+
path.gsub("/", "") == "" ? "Default" : path.gsub("/", "").capitalize
|
16
|
+
end
|
17
|
+
|
18
|
+
# Assists in lazily evaluating if a scheduler is needed for a context
|
19
|
+
#
|
20
|
+
# @param [ServletContext] context
|
21
|
+
# @return [Boolean]
|
22
|
+
def self.scheduler_exists?(context)
|
23
|
+
!!context.get_attribute(scheduler_name(context))
|
24
|
+
end
|
25
|
+
|
26
|
+
# Tomcat event callbacks are good for static systems but JRuby allows dynamic definition of classes and function
|
27
|
+
# so I am storing a variable on the servlet context that allow the extension to check if the servlet has been started
|
28
|
+
# during lazy evaluation of the need for a scheduler and/or starting the scheduler
|
29
|
+
#
|
30
|
+
# @param [ServletContext] context
|
31
|
+
# @return [Boolean]
|
32
|
+
def self.servlet_started?(context)
|
33
|
+
!!context.get_attribute(started_name(context))
|
34
|
+
end
|
35
|
+
|
36
|
+
# Helper to centralize the operations on the servlet contexts, sets the servlet started variable when the context is started, reguardless of whether
|
37
|
+
# a scheduler exists or not
|
38
|
+
#
|
39
|
+
# @param [ServletContext] context
|
40
|
+
def self.set_servlet_started(context)
|
41
|
+
context.set_attribute(started_name(context), true)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Helper method that attaches the configuration options from the Trinidad config file to the ServletContext
|
45
|
+
#
|
46
|
+
# @param [ServletContext] context
|
47
|
+
# @param [Hash] options
|
48
|
+
def self.store_scheduler_options(context, options)
|
49
|
+
context.set_attribute(options_name(context), options)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Centralized definition of where variables will be stored on the ServletContext
|
53
|
+
def self.started_name(context)
|
54
|
+
"TrinidadScheduler::#{context_path(context.context_path)}::ServletStarted"
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.options_name(context)
|
58
|
+
"TrinidadScheduler::#{context_path(context.context_path)}::SchedulerOptions"
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.scheduler_name(context)
|
62
|
+
"TrinidadScheduler::#{context_path(context.context_path)}::Scheduler"
|
63
|
+
end
|
64
|
+
|
65
|
+
# Bracket accessor defined to retreive the scheduler for a context
|
66
|
+
# if no scheduler is attached to the context then one is created and attached at time of access and returned
|
67
|
+
#
|
68
|
+
# @param [ServletContext] context
|
69
|
+
# @return [org.quartz.impl.StdScheduler]
|
70
|
+
def self.[](context)
|
71
|
+
if !scheduler_exists?(context)
|
72
|
+
self.trinidad_scheduler_init_log4j
|
73
|
+
self[context] = self.quartz_scheduler(context, context.get_attribute(options_name(context)))
|
74
|
+
end
|
75
|
+
|
76
|
+
scheduler = context.get_attribute(scheduler_name(context))
|
77
|
+
|
78
|
+
if !scheduler.is_started && servlet_started?(context)
|
79
|
+
scheduler.start
|
80
|
+
scheduler.resume_all
|
81
|
+
end
|
82
|
+
|
83
|
+
return scheduler
|
84
|
+
end
|
85
|
+
|
86
|
+
# Bracket assignment operator, will attach the scheduler passed to the context in the brackets
|
87
|
+
#
|
88
|
+
# @param [ServletContext] context
|
89
|
+
# @param [org.quartz.impl.StdScheduler] scheduler
|
90
|
+
def self.[]=(context, scheduler)
|
91
|
+
context.set_attribute(scheduler_name(context), scheduler)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Method to build and return Quartz schedulers
|
95
|
+
#
|
96
|
+
# @param [ServletContext] context
|
97
|
+
# @param [Hash] opts, the options to configure the scheduler with
|
98
|
+
def self.quartz_scheduler(context, opts={})
|
99
|
+
options = {:wrapped => false, :thread_count => 10, :thread_priority => 5}
|
100
|
+
options.merge!(opts)
|
101
|
+
options[:name] = context_path(context.context_path)
|
102
|
+
|
103
|
+
scheduler_factory = org.quartz.impl.StdSchedulerFactory.new
|
104
|
+
scheduler_factory.initialize(quartz_properties(options))
|
105
|
+
scheduler = scheduler_factory.get_scheduler
|
106
|
+
scheduler.set_job_factory(TrinidadScheduler::JobFactory.new)
|
107
|
+
scheduler.pause_all
|
108
|
+
return scheduler
|
109
|
+
end
|
110
|
+
|
111
|
+
# Properties stream for initializing a scheduler
|
112
|
+
# Currently restricts schedulers to RAMJobStore and SimpleThreadPool
|
113
|
+
def self.quartz_properties(opts={})
|
114
|
+
prop_string = java.lang.String.new("
|
115
|
+
org.quartz.scheduler.rmi.export = false
|
116
|
+
org.quartz.scheduler.rmi.proxy = false
|
117
|
+
org.quartz.scheduler.wrapJobExecutionInUserTransaction = #{opts[:wrapped]}
|
118
|
+
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
|
119
|
+
org.quartz.threadPool.threadCount = #{opts[:thread_count]}
|
120
|
+
org.quartz.threadPool.threadPriority = #{opts[:thread_priority]}
|
121
|
+
org.quartz.threadPool.threadNamePrefix = WorkerThread::#{opts[:name]}
|
122
|
+
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
|
123
|
+
org.quartz.jobStore.misfireThreshold = 60000
|
124
|
+
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore")
|
125
|
+
|
126
|
+
qp = java.util.Properties.new
|
127
|
+
qp.load(java.io.ByteArrayInputStream.new(prop_string.getBytes()))
|
128
|
+
qp.set_property("org.quartz.scheduler.instanceName", "Quartz::#{opts[:name]}::Application")
|
129
|
+
return qp
|
130
|
+
end
|
131
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: trinidad_scheduler_extension
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brandon Dewitt
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-03-06 00:00:00 -05:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: trinidad_jars
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
description: Extension to support scheduled jobs in Trinidad
|
28
|
+
email: brandon+trinidad_scheduler@myjibe.com
|
29
|
+
executables: []
|
30
|
+
|
31
|
+
extensions: []
|
32
|
+
|
33
|
+
extra_rdoc_files:
|
34
|
+
- LICENSE
|
35
|
+
- README
|
36
|
+
files:
|
37
|
+
- LICENSE
|
38
|
+
- README
|
39
|
+
- lib/trinidad_scheduler_extension.rb
|
40
|
+
- lib/trinidad_scheduler_extension/app_job.rb
|
41
|
+
- lib/trinidad_scheduler_extension/config/log4j.properties
|
42
|
+
- lib/trinidad_scheduler_extension/extensions/object.rb
|
43
|
+
- lib/trinidad_scheduler_extension/jars/log4j-1.2.16.jar
|
44
|
+
- lib/trinidad_scheduler_extension/jars/quartz-1.8.4.jar
|
45
|
+
- lib/trinidad_scheduler_extension/jars/slf4j-api-1.6.1.jar
|
46
|
+
- lib/trinidad_scheduler_extension/jars/slf4j-log4j12-1.6.1.jar
|
47
|
+
- lib/trinidad_scheduler_extension/job_detail.rb
|
48
|
+
- lib/trinidad_scheduler_extension/job_factory.rb
|
49
|
+
- lib/trinidad_scheduler_extension/scheduled_job.rb
|
50
|
+
- lib/trinidad_scheduler_extension/scheduler_extension.rb
|
51
|
+
- lib/trinidad_scheduler_extension/scheduler_listener.rb
|
52
|
+
- lib/trinidad_scheduler_extension/trinidad_scheduler.rb
|
53
|
+
- lib/trinidad_scheduler_extension/version.rb
|
54
|
+
has_rdoc: true
|
55
|
+
homepage: https://github.com/bdewitt/trinidad_scheduler_extension
|
56
|
+
licenses: []
|
57
|
+
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.6.1
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: "Extension to support scheduled jobs in Trinidad: Extension"
|
82
|
+
test_files: []
|
83
|
+
|