trinidad_scheduler_extension 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|