acts_as_executor 1.0.0.beta1 → 1.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,13 +2,15 @@
2
2
  acts_as_executor seamlessly integrates Java's Executor framework with JRuby on Rails, giving an application the ability to interact with executors in a familiar Rails'eque way.
3
3
 
4
4
  ==Features
5
- * Database persisted, scheduled executor and task configurations
5
+ * Database persisted, executor and task configurations
6
+ * Create and delete both executors and tasks dynamically at runtime
7
+ * Automatic management of completed tasks
6
8
 
7
9
  ==Getting Started
8
10
  Rails 3 Gemfile
9
11
  gem "acts_as_executor"
10
12
  Install via
11
- gem install acts_as_executor
13
+ gem install acts_as_executor --pre
12
14
  # or
13
15
  bundle install
14
16
 
@@ -17,21 +19,31 @@ Install via
17
19
 
18
20
  e.g.
19
21
  rails generate executor Executor
22
+ create config/initializers/acts_as_executor.rb
20
23
  create app/models/executor.rb
21
24
  create app/models/executor_task.rb
22
- create db/migrate/20110523205511_create_executors.rb
25
+ create db/migrate/20110601230311_create_executors.rb
23
26
  rake db:migrate
24
27
 
25
- ====Seed
26
- e = Executor.create(:name => "background_processing",
27
- :kind => ActsAsExecutor::Executor::Kinds::SCHEDULED,
28
- :size => 4)
28
+ ====Create Executor
29
+ Executor.create(:name => "cached_executor",
30
+ :kind => ActsAsExecutor::Executor::Kinds::CACHED)
31
+ ====Create Scheduled Executor
32
+ Executor.create(:name => "scheduled_executor",
33
+ :kind => ActsAsExecutor::Executor::Kinds::SCHEDULED,
34
+ :size => 3)
35
+ ====Create Task
36
+ e = Executor.find_by_name("cached_executor")
29
37
  e.tasks.create(:clazz => "HelloWorldTask",
30
- :arguments => { :message => "This is my argument message" },
38
+ :arguments => { :message => "This is my message")
39
+ ====Create Scheduled Task
40
+ e = Executor.find_by_name("scheduled_executor")
41
+ e.tasks.create(:clazz => "HelloWorldTask",
42
+ :arguments => { :message => "This is my scheduled message" },
31
43
  :schedule => ActsAsExecutor::Task::Schedules::FIXED_DELAY,
32
44
  :start => 1,
33
45
  :every => 5,
34
- :units => ActsAsExecutor::Common::Units::SECONDS)
46
+ :units => ActsAsExecutor::Common::Units::MINUTES)
35
47
 
36
48
  ====Example Task
37
49
  class HelloWorldTask
@@ -44,7 +56,12 @@ Install via
44
56
  end
45
57
  end
46
58
 
47
- ==What's New in Release 1.0.0.beta1
48
- * Automatic startup only on Rails start. e.g. rake tasks don't trigger executor startup
49
- * Arguments can now be passed to tasks
50
- * Logging to <tt>Rails.logger</tt>
59
+ ==Notes
60
+ Upon a task completing its execution on a non-scheduled executor, the task will be delete from the database. Due to the possiblity that multiple threads may be attempting to delete records simultaneously, it is recommended NOT to use SQLite as a database provider. In this scenario, database locking may occur causing records to fail deletion and thus exist for tasks that have actually completed thier execution.
61
+
62
+ ==What's New in Release 1.0.0.beta2
63
+ * Support for all executor types, scheduled and non-scheduled
64
+ * Executors can be created and deleted at runtime
65
+ * Tasks can be created and added to executors at runtime
66
+ * Automatic management of completed tasks on non-scheduled executors
67
+ * Custom logger can now be set via initializer
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "acts_as_executor/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "acts_as_executor"
7
+ s.version = ActsAsExecutor::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Phil Ostler"]
10
+ s.email = ["philostler@gmail.com"]
11
+ s.homepage = "https://github.com/philostler/acts_as_executor"
12
+ s.summary = %q{Java Executor framework integration for Rails}
13
+ s.description = %q{Seamless integration of Java's Executor framework with JRuby on Rails}
14
+
15
+ s.files = Dir["**/*.rb"] + Dir["*.rdoc"] + Dir["LICENSE"] + Dir["*.gemspec"]
16
+ s.require_paths = ["lib"]
17
+ end
@@ -1,3 +1,4 @@
1
+ require "acts_as_executor/common/future_task"
1
2
  require "acts_as_executor/common/units"
2
3
 
3
4
  require "acts_as_executor/executor/factory"
@@ -14,7 +15,9 @@ require "acts_as_executor/task/schedules"
14
15
 
15
16
  require "acts_as_executor/task/model/actions"
16
17
  require "acts_as_executor/task/model/associations"
18
+ require "acts_as_executor/task/model/attributes"
17
19
  require "acts_as_executor/task/model/base"
20
+ require "acts_as_executor/task/model/helpers"
18
21
  require "acts_as_executor/task/model/validations"
19
22
 
20
23
  require "acts_as_executor/validators/class_exists_validator"
@@ -29,6 +32,14 @@ module ActsAsExecutor
29
32
  def self.logger
30
33
  @@logger ||= Rails.logger
31
34
  end
35
+ def self.logger= logger
36
+ @@logger = logger
37
+ end
38
+
39
+ def self.configure
40
+ yield self
41
+ end
42
+
32
43
  def self.rails_startup?
33
44
  File.basename($0) == "rails"
34
45
  end
@@ -0,0 +1,11 @@
1
+ module ActsAsExecutor
2
+ module Common
3
+ class FutureTask < Java::java.util.concurrent.FutureTask
4
+ attr_accessor :task
5
+
6
+ def done
7
+ task.destroy
8
+ end
9
+ end
10
+ end
11
+ end
@@ -3,12 +3,25 @@ module ActsAsExecutor
3
3
  class Factory
4
4
  def self.create kind, size = nil
5
5
  case kind
6
+ when ActsAsExecutor::Executor::Kinds::CACHED
7
+ return Java::java.util.concurrent.Executors.new_cached_thread_pool
8
+
9
+ when ActsAsExecutor::Executor::Kinds::FIXED
10
+ raise TypeError, "size cannot be nil" unless size
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
+
6
17
  when ActsAsExecutor::Executor::Kinds::SCHEDULED
7
18
  raise TypeError, "size cannot be nil" unless size
8
19
  raise ArgumentError, "size must be larger than 0" unless size.to_i > 0
9
20
  return Java::java.util.concurrent.Executors.new_scheduled_thread_pool size.to_i
21
+
10
22
  when ActsAsExecutor::Executor::Kinds::SINGLE_SCHEDULED
11
23
  return Java::java.util.concurrent.Executors.new_single_thread_scheduled_executor
24
+
12
25
  else
13
26
  return nil
14
27
  end
@@ -1,12 +1,16 @@
1
1
  module ActsAsExecutor
2
2
  module Executor
3
3
  class Kinds
4
+ CACHED = "cached"
5
+ FIXED = "fixed"
6
+ SINGLE = "single"
7
+
4
8
  SCHEDULED = "scheduled"
5
9
  SINGLE_SCHEDULED = "single_scheduled"
6
10
 
7
- ALL = [ SCHEDULED, SINGLE_SCHEDULED ]
11
+ ALL = [ CACHED, FIXED, SINGLE, SCHEDULED, SINGLE_SCHEDULED ]
8
12
  ALL_SCHEDULED = [ SCHEDULED, SINGLE_SCHEDULED ]
9
- REQUIRING_SIZE = [ SCHEDULED ]
13
+ REQUIRING_SIZE = [ FIXED, SCHEDULED ]
10
14
  end
11
15
  end
12
16
  end
@@ -3,68 +3,60 @@ module ActsAsExecutor
3
3
  module Model
4
4
  module Actions
5
5
  def self.included base
6
- base.after_find :startup, :if => "ActsAsExecutor.rails_startup?"
7
- base.after_destroy :shutdown
6
+ base.after_find :startup, :if => :startup_now?
7
+ base.after_save :startup, :if => :startup_now?
8
+
9
+ base.after_destroy :shutdown, :if => :shutdown_now?
8
10
  end
9
-
11
+
10
12
  def startup
11
- unless self.executor
12
- ActsAsExecutor.log.debug "Executor \"" + name + "\" starting up..."
13
- self.executor = ActsAsExecutor::Executor::Factory.create kind, size
14
- ActsAsExecutor.log.info "Executor \"" + name + "\" has completed startup"
15
-
16
- ActsAsExecutor.log.debug "Executor \"" + name + "\" attaching tasks..."
17
- tasks.each do |t|
18
- submit t
19
- end
20
- ActsAsExecutor.log.info "Executor \"" + name + "\" has completed attaching tasks"
21
- end
22
- end
13
+ ActsAsExecutor.log.debug "Executor \"" + name + "\" starting up..."
14
+ self.executor = ActsAsExecutor::Executor::Factory.create kind, size
15
+ ActsAsExecutor.log.info "Executor \"" + name + "\" has completed startup"
23
16
 
24
- def shutdown
25
- if self.executor
26
- ActsAsExecutor.log.debug "Executor \"" + name + "\" shutting down..."
27
- begin
28
- self.executor.shutdown
29
- rescue Java::java.lang.RuntimePermission
30
- ActsAsExecutor.log.error "Executor \"" + name + "\" has experienced a RuntimePermission error"
31
- rescue Java::java.lang.SecurityException
32
- ActsAsExecutor.log.error "Executor \"" + name + "\" has experienced a SecurityException error"
33
- end
34
- ActsAsExecutor.log.info "Executor \"" + name + "\" has completed shutdown"
35
- end
17
+ tasks.all
36
18
  end
37
19
 
38
- def submit task
20
+ def shutdown
21
+ ActsAsExecutor.log.debug "Executor \"" + name + "\" shutting down..."
39
22
  begin
40
- clazz = task.clazz.constantize.new
41
- rescue NameError
42
- ActsAsExecutor.log.error "Executor \"" + name + "\" attaching task \"" + task.clazz + "\" could not create class"
43
- end
44
-
45
- if task.arguments
46
- task.arguments.each_pair do |k, v|
47
- attribute = "#{k}="
48
- clazz.send attribute, v if clazz.respond_to? attribute
49
- end
23
+ self.executor.shutdown
24
+ rescue Java::java.lang.RuntimePermission
25
+ ActsAsExecutor.log.error "Executor \"" + name + "\" has experienced a RuntimePermission error"
26
+ rescue Java::java.lang.SecurityException
27
+ ActsAsExecutor.log.error "Executor \"" + name + "\" has experienced a SecurityException error"
50
28
  end
29
+ ActsAsExecutor.log.info "Executor \"" + name + "\" has completed shutdown"
30
+ end
51
31
 
32
+ def execute clazz, schedule, start, every, units
52
33
  begin
53
- units = Java::java.util.concurrent.TimeUnit.value_of(task.units.upcase)
54
- case task.schedule
55
- when ActsAsExecutor::Task::Schedules::ONE_SHOT
56
- self.executor.schedule clazz, task.start, units
57
- when ActsAsExecutor::Task::Schedules::FIXED_DELAY
58
- self.executor.schedule_with_fixed_delay clazz, task.start, task.every, units
59
- when ActsAsExecutor::Task::Schedules::FIXED_RATE
60
- self.executor.schedule_at_fixed_rate clazz, task.start, task.every, units
34
+ if schedulable?
35
+ units = Java::java.util.concurrent.TimeUnit.value_of(units.upcase)
36
+ case schedule
37
+ when ActsAsExecutor::Task::Schedules::ONE_SHOT
38
+ future = self.executor.schedule clazz, start, units
39
+ when ActsAsExecutor::Task::Schedules::FIXED_DELAY
40
+ future = self.executor.schedule_with_fixed_delay clazz, start, every, units
41
+ when ActsAsExecutor::Task::Schedules::FIXED_RATE
42
+ future = self.executor.schedule_at_fixed_rate clazz, start, every, units
43
+ end
44
+ else
45
+ case clazz
46
+ when Java::java.util.concurrent.Callable
47
+ future = ActsAsExecutor::Common::FutureTask.new clazz
48
+ when Java::java.lang.Runnable
49
+ future = ActsAsExecutor::Common::FutureTask.new clazz, nil
50
+ end
51
+ self.executor.execute future
61
52
  end
62
- rescue Java::java.lang.NullPointerException
63
- ActsAsExecutor.log.error "Executor \"" + name + "\" attaching task \"" + task.clazz + "\" threw a NullPointerException"
64
- rescue Java::java.lang.IllegalArgumentException
65
- ActsAsExecutor.log.error "Executor \"" + name + "\" attaching task \"" + task.clazz + "\" threw a IllegalArgumentException"
66
- rescue Java::java.util.concurrent.RejectedExecutionException
67
- ActsAsExecutor.log.error "Executor \"" + name + "\" attaching task \"" + task.clazz + "\" threw a RejectedExecutionException"
53
+ return future
54
+ rescue Java::java.lang.NullPointerException => e
55
+ ActsAsExecutor.log.error "Executor \"" + name + "\" attaching task threw a NullPointerException. " + e.to_s
56
+ rescue Java::java.lang.IllegalArgumentException => e
57
+ ActsAsExecutor.log.error "Executor \"" + name + "\" attaching task threw a IllegalArgumentException. " + e.to_s
58
+ rescue Java::java.util.concurrent.RejectedExecutionException => e
59
+ ActsAsExecutor.log.error "Executor \"" + name + "\" attaching task threw a RejectedExecutionException. " + e.to_s
68
60
  end
69
61
  end
70
62
  end
@@ -2,6 +2,20 @@ module ActsAsExecutor
2
2
  module Executor
3
3
  module Model
4
4
  module Helpers
5
+ def startup_now?
6
+ if self.executor == nil && ActsAsExecutor.rails_startup?
7
+ return true
8
+ end
9
+ return false
10
+ end
11
+
12
+ def shutdown_now?
13
+ if self.executor != nill && ActsAsExecutor.rails_startup?
14
+ return true
15
+ end
16
+ return false
17
+ end
18
+
5
19
  def schedulable?
6
20
  ActsAsExecutor::Executor::Kinds::ALL_SCHEDULED.include? kind
7
21
  end
@@ -4,6 +4,29 @@ module ActsAsExecutor
4
4
  module Actions
5
5
  def self.included base
6
6
  base.serialize :arguments, Hash
7
+
8
+ base.after_find :execute, :if => :execute_now?
9
+ base.after_save :execute, :if => :execute_now?
10
+ end
11
+
12
+ def execute
13
+ begin
14
+ instance = clazz.constantize.new
15
+ rescue NameError
16
+ ActsAsExecutor.log.error "Task creating task could not create class"
17
+ end
18
+
19
+ if arguments
20
+ arguments.each_pair do |k, v|
21
+ attribute = "#{k}="
22
+ instance.send attribute, v if instance.respond_to? attribute
23
+ end
24
+ end
25
+
26
+ self.future = executor.execute instance, schedule, start, every, units
27
+ if !executor.schedulable?
28
+ self.future.task = self
29
+ end
7
30
  end
8
31
  end
9
32
  end
@@ -0,0 +1,16 @@
1
+ module ActsAsExecutor
2
+ module Task
3
+ module Model
4
+ module Attributes
5
+ @@futures = Hash.new
6
+
7
+ def future
8
+ @@futures[id]
9
+ end
10
+ def future= future
11
+ @@futures[id] = future
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -3,8 +3,10 @@ module ActsAsExecutor
3
3
  module Model
4
4
  module Base
5
5
  def acts_as_executor_task
6
- send :include, ActsAsExecutor::Task::Model::Associations
7
6
  send :include, ActsAsExecutor::Task::Model::Actions
7
+ send :include, ActsAsExecutor::Task::Model::Associations
8
+ send :include, ActsAsExecutor::Task::Model::Attributes
9
+ send :include, ActsAsExecutor::Task::Model::Helpers
8
10
  send :include, ActsAsExecutor::Task::Model::Validations
9
11
  end
10
12
  end
@@ -0,0 +1,14 @@
1
+ module ActsAsExecutor
2
+ module Task
3
+ module Model
4
+ module Helpers
5
+ def execute_now?
6
+ if self.future == nil
7
+ return true
8
+ end
9
+ return false
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,3 +1,3 @@
1
1
  module ActsAsExecutor
2
- VERSION = "1.0.0.beta1"
2
+ VERSION = "1.0.0.beta2"
3
3
  end
@@ -3,6 +3,10 @@ class ExecutorGenerator < Rails::Generators::NamedBase
3
3
 
4
4
  source_root File.expand_path("../templates", __FILE__)
5
5
 
6
+ def initializer
7
+ template File.join("initializer", "acts_as_executor.rb"), File.join("config", "initializers", "acts_as_executor.rb")
8
+ end
9
+
6
10
  def models
7
11
  template File.join("models", "executor.rb"), File.join("app", "models", file_name + ".rb")
8
12
  template File.join("models", "executor_task.rb"), File.join("app", "models", file_name + "_task.rb")
@@ -0,0 +1,4 @@
1
+ ActsAsExecutor.configure do |config|
2
+ # Default = Rails.logger
3
+ # config.logger = Log4r::Logger["ActsAsExecutor"]
4
+ end
File without changes
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: acts_as_executor
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease: 6
5
- version: 1.0.0.beta1
5
+ version: 1.0.0.beta2
6
6
  platform: ruby
7
7
  authors:
8
8
  - Phil Ostler
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-05-24 00:00:00 +01:00
13
+ date: 2011-06-05 00:00:00 +01:00
14
14
  default_executable:
15
15
  dependencies: []
16
16
 
@@ -26,6 +26,7 @@ extra_rdoc_files: []
26
26
  files:
27
27
  - lib/acts_as_executor.rb
28
28
  - lib/acts_as_executor/version.rb
29
+ - lib/acts_as_executor/common/future_task.rb
29
30
  - lib/acts_as_executor/common/units.rb
30
31
  - lib/acts_as_executor/executor/factory.rb
31
32
  - lib/acts_as_executor/executor/kinds.rb
@@ -38,15 +39,20 @@ files:
38
39
  - lib/acts_as_executor/task/schedules.rb
39
40
  - lib/acts_as_executor/task/model/actions.rb
40
41
  - lib/acts_as_executor/task/model/associations.rb
42
+ - lib/acts_as_executor/task/model/attributes.rb
41
43
  - lib/acts_as_executor/task/model/base.rb
44
+ - lib/acts_as_executor/task/model/helpers.rb
42
45
  - lib/acts_as_executor/task/model/validations.rb
43
46
  - lib/acts_as_executor/validators/class_exists_validator.rb
44
47
  - lib/generators/executor_generator.rb
48
+ - lib/generators/templates/initializer/acts_as_executor.rb
45
49
  - lib/generators/templates/migration/create_executors.rb
46
50
  - lib/generators/templates/models/executor.rb
47
51
  - lib/generators/templates/models/executor_task.rb
52
+ - spec/spec_helper.rb
48
53
  - README.rdoc
49
54
  - LICENSE
55
+ - acts_as_executor.gemspec
50
56
  has_rdoc: true
51
57
  homepage: https://github.com/philostler/acts_as_executor
52
58
  licenses: []