acts_as_executor 1.0.0.beta2 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/acts_as_executor.gemspec +8 -1
- data/lib/acts_as_executor.rb +18 -31
- data/lib/acts_as_executor/common/future_task.rb +2 -2
- data/lib/acts_as_executor/common/instance_support_methods.rb +15 -0
- data/lib/acts_as_executor/executor/factory.rb +18 -22
- data/lib/acts_as_executor/executor/model/class_methods.rb +33 -0
- data/lib/acts_as_executor/executor/model/instance_methods.rb +87 -0
- data/lib/acts_as_executor/executor/model/instance_support_methods.rb +36 -0
- data/lib/acts_as_executor/task/clazz.rb +26 -0
- data/lib/acts_as_executor/task/model/class_methods.rb +13 -0
- data/lib/acts_as_executor/task/model/instance_methods.rb +64 -0
- data/lib/acts_as_executor/task/model/instance_support_methods.rb +32 -0
- data/lib/acts_as_executor/validators/class_exists_validator.rb +1 -1
- data/lib/acts_as_executor/validators/class_includes_validator.rb +10 -0
- data/lib/acts_as_executor/version.rb +1 -1
- data/lib/generators/{executor_generator.rb → acts_as_executor_generator.rb} +1 -5
- data/lib/generators/templates/migration/create_executors.rb +2 -2
- data/spec/acts_as_executor_spec.rb +22 -0
- data/spec/common/future_task_spec.rb +21 -0
- data/spec/common/instance_support_methods_spec.rb +25 -0
- data/spec/common/units_spec.rb +19 -0
- data/spec/executor/factory_spec.rb +31 -0
- data/spec/executor/model/class_methods_spec.rb +49 -0
- data/spec/executor/model/instance_methods_spec.rb +150 -0
- data/spec/executor/model/instance_support_methods_spec.rb +70 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/blueprints.rb +20 -0
- data/spec/support/classes.rb +55 -0
- data/spec/support/custom_matchers.rb +26 -0
- data/spec/support/database_connection.rb +4 -0
- data/spec/task/clazz_spec.rb +42 -0
- data/spec/task/model/class_methods_spec.rb +13 -0
- data/spec/task/model/instance_methods_spec.rb +80 -0
- data/spec/task/model/instance_support_methods_spec.rb +56 -0
- data/spec/task/schedules_spec.rb +11 -0
- data/spec/validators/class_exists_validator_spec.rb +21 -0
- data/spec/validators/class_includes_validator_spec.rb +21 -0
- metadata +88 -21
- data/README.rdoc +0 -67
- data/lib/acts_as_executor/executor/kinds.rb +0 -16
- data/lib/acts_as_executor/executor/model/actions.rb +0 -65
- data/lib/acts_as_executor/executor/model/associations.rb +0 -11
- data/lib/acts_as_executor/executor/model/attributes.rb +0 -16
- data/lib/acts_as_executor/executor/model/base.rb +0 -24
- data/lib/acts_as_executor/executor/model/helpers.rb +0 -25
- data/lib/acts_as_executor/executor/model/validations.rb +0 -13
- data/lib/acts_as_executor/task/model/actions.rb +0 -34
- data/lib/acts_as_executor/task/model/associations.rb +0 -11
- data/lib/acts_as_executor/task/model/attributes.rb +0 -16
- data/lib/acts_as_executor/task/model/base.rb +0 -15
- data/lib/acts_as_executor/task/model/helpers.rb +0 -14
- data/lib/acts_as_executor/task/model/validations.rb +0 -16
- data/lib/generators/templates/initializer/acts_as_executor.rb +0 -4
data/acts_as_executor.gemspec
CHANGED
@@ -10,8 +10,15 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.email = ["philostler@gmail.com"]
|
11
11
|
s.homepage = "https://github.com/philostler/acts_as_executor"
|
12
12
|
s.summary = %q{Java Executor framework integration for Rails}
|
13
|
-
s.description = %q{
|
13
|
+
s.description = %q{Seamlessly integrates Java's Executor framework with JRuby on Rails}
|
14
14
|
|
15
15
|
s.files = Dir["**/*.rb"] + Dir["*.rdoc"] + Dir["LICENSE"] + Dir["*.gemspec"]
|
16
16
|
s.require_paths = ["lib"]
|
17
|
+
|
18
|
+
s.add_dependency "activemodel", ">= 3.0"
|
19
|
+
s.add_dependency "activerecord", ">= 3.0"
|
20
|
+
|
21
|
+
s.add_development_dependency "activerecord-jdbcsqlite3-adapter", "~> 1.1"
|
22
|
+
s.add_development_dependency "machinist", "2.0.0.beta2"
|
23
|
+
s.add_development_dependency "rspec", "~> 2.6"
|
17
24
|
end
|
data/lib/acts_as_executor.rb
CHANGED
@@ -1,46 +1,33 @@
|
|
1
|
+
require "active_model"
|
2
|
+
require "active_record"
|
3
|
+
|
4
|
+
require "acts_as_executor/version"
|
5
|
+
|
1
6
|
require "acts_as_executor/common/future_task"
|
7
|
+
require "acts_as_executor/common/instance_support_methods"
|
2
8
|
require "acts_as_executor/common/units"
|
3
9
|
|
4
10
|
require "acts_as_executor/executor/factory"
|
5
|
-
require "acts_as_executor/executor/kinds"
|
6
11
|
|
7
|
-
require "acts_as_executor/executor/model/
|
8
|
-
require "acts_as_executor/executor/model/
|
9
|
-
require "acts_as_executor/executor/model/
|
10
|
-
require "acts_as_executor/executor/model/base"
|
11
|
-
require "acts_as_executor/executor/model/helpers"
|
12
|
-
require "acts_as_executor/executor/model/validations"
|
12
|
+
require "acts_as_executor/executor/model/class_methods"
|
13
|
+
require "acts_as_executor/executor/model/instance_methods"
|
14
|
+
require "acts_as_executor/executor/model/instance_support_methods"
|
13
15
|
|
16
|
+
require "acts_as_executor/task/clazz"
|
14
17
|
require "acts_as_executor/task/schedules"
|
15
18
|
|
16
|
-
require "acts_as_executor/task/model/
|
17
|
-
require "acts_as_executor/task/model/
|
18
|
-
require "acts_as_executor/task/model/
|
19
|
-
require "acts_as_executor/task/model/base"
|
20
|
-
require "acts_as_executor/task/model/helpers"
|
21
|
-
require "acts_as_executor/task/model/validations"
|
19
|
+
require "acts_as_executor/task/model/class_methods"
|
20
|
+
require "acts_as_executor/task/model/instance_methods"
|
21
|
+
require "acts_as_executor/task/model/instance_support_methods"
|
22
22
|
|
23
23
|
require "acts_as_executor/validators/class_exists_validator"
|
24
|
+
require "acts_as_executor/validators/class_includes_validator"
|
24
25
|
|
25
|
-
ActiveRecord::Base.send :extend, ActsAsExecutor::Executor::Model::
|
26
|
-
ActiveRecord::Base.send :extend, ActsAsExecutor::Task::Model::
|
26
|
+
ActiveRecord::Base.send :extend, ActsAsExecutor::Executor::Model::ClassMethods
|
27
|
+
ActiveRecord::Base.send :extend, ActsAsExecutor::Task::Model::ClassMethods
|
27
28
|
|
28
29
|
module ActsAsExecutor
|
29
|
-
def self.
|
30
|
-
|
31
|
-
end
|
32
|
-
def self.logger
|
33
|
-
@@logger ||= Rails.logger
|
34
|
-
end
|
35
|
-
def self.logger= logger
|
36
|
-
@@logger = logger
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.configure
|
40
|
-
yield self
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.rails_startup?
|
44
|
-
File.basename($0) == "rails"
|
30
|
+
def self.rails_initialized?
|
31
|
+
File.basename($0) != "rake"
|
45
32
|
end
|
46
33
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ActsAsExecutor
|
2
|
+
module Common
|
3
|
+
module InstanceSupportMethods
|
4
|
+
private
|
5
|
+
def log_statement executor_name, statement
|
6
|
+
"\"" + executor_name + "\" " + statement
|
7
|
+
end
|
8
|
+
|
9
|
+
def log_message executor_name, doing, task_id, clazz_name, message = ""
|
10
|
+
if message.kind_of? Hash then message = "with \"" + message.inspect + "\"" end
|
11
|
+
"\"" + executor_name + "\" " + doing + " \"" + task_id + "#" + clazz_name + "\" " + message
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,30 +1,26 @@
|
|
1
1
|
module ActsAsExecutor
|
2
2
|
module Executor
|
3
3
|
class Factory
|
4
|
-
def self.create
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
raise ArgumentError, "size must be larger than 0" unless size.to_i > 0
|
12
|
-
return Java::java.util.concurrent.Executors.new_fixed_thread_pool size.to_i
|
13
|
-
|
14
|
-
when ActsAsExecutor::Executor::Kinds::SINGLE
|
15
|
-
return Java::java.util.concurrent.Executors.new_single_thread_executor
|
16
|
-
|
17
|
-
when ActsAsExecutor::Executor::Kinds::SCHEDULED
|
18
|
-
raise TypeError, "size cannot be nil" unless size
|
19
|
-
raise ArgumentError, "size must be larger than 0" unless size.to_i > 0
|
20
|
-
return Java::java.util.concurrent.Executors.new_scheduled_thread_pool size.to_i
|
21
|
-
|
22
|
-
when ActsAsExecutor::Executor::Kinds::SINGLE_SCHEDULED
|
23
|
-
return Java::java.util.concurrent.Executors.new_single_thread_scheduled_executor
|
24
|
-
|
4
|
+
def self.create max_tasks, schedulable
|
5
|
+
executor = nil
|
6
|
+
if schedulable
|
7
|
+
if max_tasks == nil
|
8
|
+
# No Cached Scheduled Implementation
|
9
|
+
elsif max_tasks > 1
|
10
|
+
executor = Java::java.util.concurrent.Executors.new_scheduled_thread_pool max_tasks.to_i # Scheduled
|
25
11
|
else
|
26
|
-
|
12
|
+
executor = Java::java.util.concurrent.Executors.new_single_thread_scheduled_executor # Single Scheduled
|
13
|
+
end
|
14
|
+
else
|
15
|
+
if max_tasks == nil
|
16
|
+
executor = Java::java.util.concurrent.Executors.new_cached_thread_pool # Cached
|
17
|
+
elsif max_tasks > 1
|
18
|
+
executor = Java::java.util.concurrent.Executors.new_fixed_thread_pool max_tasks.to_i # Fixed
|
19
|
+
else
|
20
|
+
executor = Java::java.util.concurrent.Executors.new_single_thread_executor # Single
|
21
|
+
end
|
27
22
|
end
|
23
|
+
executor
|
28
24
|
end
|
29
25
|
end
|
30
26
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ActsAsExecutor
|
2
|
+
module Executor
|
3
|
+
module Model
|
4
|
+
module ClassMethods
|
5
|
+
def acts_as_executor *arguments
|
6
|
+
send :include, ActsAsExecutor::Common::InstanceSupportMethods
|
7
|
+
send :include, ActsAsExecutor::Executor::Model::InstanceMethods
|
8
|
+
send :include, ActsAsExecutor::Executor::Model::InstanceSupportMethods
|
9
|
+
|
10
|
+
hash = arguments.last.is_a?(Hash) ? arguments.pop : {}
|
11
|
+
self.log = hash[:log]
|
12
|
+
|
13
|
+
if ActsAsExecutor.rails_initialized?
|
14
|
+
all
|
15
|
+
at_exit do
|
16
|
+
all.each do |e|
|
17
|
+
e.send :shutdown
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def log
|
24
|
+
@@log ? @@log : Rails.logger
|
25
|
+
end
|
26
|
+
|
27
|
+
def log= log
|
28
|
+
@@log = log
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module ActsAsExecutor
|
2
|
+
module Executor
|
3
|
+
module Model
|
4
|
+
module InstanceMethods
|
5
|
+
def self.included base
|
6
|
+
# Associations
|
7
|
+
base.has_many :tasks, :class_name => base.table_name.singularize.camelize + "Task", :foreign_key => "executor_id"
|
8
|
+
|
9
|
+
# Callbacks
|
10
|
+
base.after_find :startup, :if => :startupable?
|
11
|
+
base.after_save :startup, :if => :startupable?
|
12
|
+
base.after_destroy :shutdown, :if => :shutdownable?
|
13
|
+
|
14
|
+
# Validations
|
15
|
+
base.validates :name, :presence => true, :uniqueness => true
|
16
|
+
base.validates :max_tasks, :numericality => { :only_integer => true, :greater_than_or_equal_to => 1 }, :allow_nil => true
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def startup
|
21
|
+
log.debug log_statement name, "startup triggered"
|
22
|
+
self.executor = ActsAsExecutor::Executor::Factory.create max_tasks, schedulable
|
23
|
+
log.info log_statement name, "started"
|
24
|
+
|
25
|
+
tasks.all
|
26
|
+
end
|
27
|
+
|
28
|
+
def execute clazz, task_id, schedule = nil, start = nil, every = nil, units = nil
|
29
|
+
begin
|
30
|
+
humanized_schedule = schedule ? schedule.gsub("_", " ") : "one time"
|
31
|
+
log.debug log_message name, "preparing", task_id, clazz.class.name, "for execution (" + humanized_schedule + ")"
|
32
|
+
|
33
|
+
if schedulable?
|
34
|
+
units = Java::java.util.concurrent.TimeUnit.value_of(units.upcase)
|
35
|
+
case schedule
|
36
|
+
when ActsAsExecutor::Task::Schedules::ONE_SHOT
|
37
|
+
future = executor.schedule clazz, start, units
|
38
|
+
when ActsAsExecutor::Task::Schedules::FIXED_DELAY
|
39
|
+
future = executor.schedule_with_fixed_delay clazz, start, every, units
|
40
|
+
when ActsAsExecutor::Task::Schedules::FIXED_RATE
|
41
|
+
future = executor.schedule_at_fixed_rate clazz, start, every, units
|
42
|
+
end
|
43
|
+
else
|
44
|
+
future = ActsAsExecutor::Common::FutureTask.new clazz, nil
|
45
|
+
executor.execute future
|
46
|
+
end
|
47
|
+
|
48
|
+
log.info log_message name, "enqueued", task_id, clazz.class.name
|
49
|
+
future
|
50
|
+
rescue Java::java.util.concurrent.RejectedExecutionException
|
51
|
+
log.warn log_message name, "preparing", task_id, clazz.class.name, "encountered a rejected execution exception"
|
52
|
+
rescue Exception => exception
|
53
|
+
log.error log_message name, "preparing", task_id, clazz.class.name, "encountered an unexpected exception. " + exception
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def shutdown
|
58
|
+
log.debug log_statement name, "shutdown triggered"
|
59
|
+
begin
|
60
|
+
executor.shutdown
|
61
|
+
rescue Java::java.lang.SecurityException
|
62
|
+
log.warn log_statement name, "shutdown encountered a security exception"
|
63
|
+
forced_shutdown
|
64
|
+
else
|
65
|
+
log.info log_statement name, "shutdown"
|
66
|
+
ensure
|
67
|
+
self.executor = nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def forced_shutdown
|
72
|
+
log.debug log_statement name, "forced shutdown triggered"
|
73
|
+
begin
|
74
|
+
executor.shutdown_now
|
75
|
+
rescue Java::java.lang.SecurityException
|
76
|
+
log.error log_statement name, "forced shutdown encountered a security exception"
|
77
|
+
log.fatal log_statement name, "forced shutdown failure"
|
78
|
+
else
|
79
|
+
log.info log_statement name, "forced shutdown"
|
80
|
+
ensure
|
81
|
+
self.executor = nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module ActsAsExecutor
|
2
|
+
module Executor
|
3
|
+
module Model
|
4
|
+
module InstanceSupportMethods
|
5
|
+
private
|
6
|
+
@@executors = Hash.new
|
7
|
+
def executor
|
8
|
+
@@executors[id]
|
9
|
+
end
|
10
|
+
|
11
|
+
def executor= executor
|
12
|
+
raise ArgumentError, "cannot reference executor against nil id" unless id
|
13
|
+
@@executors[id] = executor
|
14
|
+
end
|
15
|
+
|
16
|
+
def startupable?
|
17
|
+
if executor == nil && ActsAsExecutor.rails_initialized?
|
18
|
+
return true
|
19
|
+
end
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
|
23
|
+
def shutdownable?
|
24
|
+
if executor != nil && ActsAsExecutor.rails_initialized?
|
25
|
+
return true
|
26
|
+
end
|
27
|
+
return false
|
28
|
+
end
|
29
|
+
|
30
|
+
def log
|
31
|
+
self.class.log
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ActsAsExecutor
|
2
|
+
module Task
|
3
|
+
module Clazz
|
4
|
+
include Java::java.lang.Runnable
|
5
|
+
|
6
|
+
attr_accessor :arguments
|
7
|
+
attr_writer :uncaught_exception_handler
|
8
|
+
|
9
|
+
private
|
10
|
+
def run
|
11
|
+
begin
|
12
|
+
if @arguments
|
13
|
+
@arguments.each_pair do |key, value|
|
14
|
+
class_eval { attr_accessor key } unless respond_to? key
|
15
|
+
send "#{key}=", value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
execute
|
20
|
+
rescue Exception => exception
|
21
|
+
@uncaught_exception_handler.call exception if @uncaught_exception_handler
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ActsAsExecutor
|
2
|
+
module Task
|
3
|
+
module Model
|
4
|
+
module ClassMethods
|
5
|
+
def acts_as_executor_task
|
6
|
+
send :include, ActsAsExecutor::Common::InstanceSupportMethods
|
7
|
+
send :include, ActsAsExecutor::Task::Model::InstanceMethods
|
8
|
+
send :include, ActsAsExecutor::Task::Model::InstanceSupportMethods
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module ActsAsExecutor
|
2
|
+
module Task
|
3
|
+
module Model
|
4
|
+
module InstanceMethods
|
5
|
+
def self.included base
|
6
|
+
# Associations
|
7
|
+
base.belongs_to :executor, :class_name => base.table_name.sub(/_tasks/, "").camelize
|
8
|
+
|
9
|
+
# Callbacks
|
10
|
+
base.after_find :enqueue, :if => :enqueueable?
|
11
|
+
base.after_save :enqueue, :if => :enqueueable?
|
12
|
+
base.after_destroy :cancel, :if => :cancelable?
|
13
|
+
|
14
|
+
# Serialization
|
15
|
+
base.serialize :arguments, Hash
|
16
|
+
|
17
|
+
# Validations
|
18
|
+
base.validates :executor_id, :presence => true
|
19
|
+
base.validates :clazz, :presence => true, :class_exists => true, :class_includes => { :includes => ActsAsExecutor::Task::Clazz }
|
20
|
+
base.validates :schedule, :inclusion => { :in => ActsAsExecutor::Task::Schedules::ALL }, :if => "executor and executor.schedulable?"
|
21
|
+
base.validates :start, :numericality => { :only_integer => true, :greater_than_or_equal_to => 0 }, :if => "executor and executor.schedulable?"
|
22
|
+
base.validates :every, :numericality => { :only_integer => true, :greater_than_or_equal_to => 1 }, :if => "executor and executor.schedulable? and schedule != ActsAsExecutor::Task::Schedules::ONE_SHOT"
|
23
|
+
base.validates :units, :inclusion => { :in => ActsAsExecutor::Common::Units::ALL }, :if => "executor and executor.schedulable?"
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def enqueue
|
28
|
+
begin
|
29
|
+
executor.send(:log).debug log_message executor.name, "creating", id.to_s, clazz, arguments
|
30
|
+
|
31
|
+
instance = instantiate clazz, arguments
|
32
|
+
|
33
|
+
self.future = executor.send(:execute, instance, id.to_s, schedule, start, every, units)
|
34
|
+
future.done_handler = method :done_handler
|
35
|
+
rescue Exception => exception
|
36
|
+
executor.send(:log).error log_message executor.name, "creating", id.to_s, clazz, "encountered an unexpected exception. " + exception
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def instantiate class_name, arguments
|
41
|
+
instance = Object.const_get(class_name).new
|
42
|
+
instance.arguments = arguments
|
43
|
+
instance.uncaught_exception_handler = method :uncaught_exception_handler
|
44
|
+
instance
|
45
|
+
end
|
46
|
+
|
47
|
+
def done_handler
|
48
|
+
executor.send(:log).debug log_message executor.name, "completed", id.to_s, clazz
|
49
|
+
destroy
|
50
|
+
end
|
51
|
+
|
52
|
+
def uncaught_exception_handler exception
|
53
|
+
executor.send(:log).error log_message executor.name, "executing", id.to_s, clazz, "encountered an uncaught exception. " + exception
|
54
|
+
end
|
55
|
+
|
56
|
+
def cancel
|
57
|
+
future.cancel true
|
58
|
+
|
59
|
+
self.future = nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ActsAsExecutor
|
2
|
+
module Task
|
3
|
+
module Model
|
4
|
+
module InstanceSupportMethods
|
5
|
+
private
|
6
|
+
@@futures = Hash.new
|
7
|
+
def future
|
8
|
+
@@futures[id]
|
9
|
+
end
|
10
|
+
|
11
|
+
def future= future
|
12
|
+
raise ArgumentError, "cannot reference future against nil id" unless id
|
13
|
+
@@futures[id] = future
|
14
|
+
end
|
15
|
+
|
16
|
+
def enqueueable?
|
17
|
+
if future == nil
|
18
|
+
return true
|
19
|
+
end
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
|
23
|
+
def cancelable?
|
24
|
+
if future != nil
|
25
|
+
return true
|
26
|
+
end
|
27
|
+
return false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|