acts_as_executor 1.0.0.beta2 → 1.0.0.rc1
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/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
|