batch-kit 0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +165 -0
- data/lib/batch-kit.rb +9 -0
- data/lib/batch-kit/arguments.rb +57 -0
- data/lib/batch-kit/config.rb +517 -0
- data/lib/batch-kit/configurable.rb +68 -0
- data/lib/batch-kit/core_ext/enumerable.rb +97 -0
- data/lib/batch-kit/core_ext/file.rb +69 -0
- data/lib/batch-kit/core_ext/file_utils.rb +103 -0
- data/lib/batch-kit/core_ext/hash.rb +17 -0
- data/lib/batch-kit/core_ext/numeric.rb +17 -0
- data/lib/batch-kit/core_ext/string.rb +88 -0
- data/lib/batch-kit/database.rb +133 -0
- data/lib/batch-kit/database/java_util_log_handler.rb +65 -0
- data/lib/batch-kit/database/log4r_outputter.rb +57 -0
- data/lib/batch-kit/database/models.rb +548 -0
- data/lib/batch-kit/database/schema.rb +229 -0
- data/lib/batch-kit/encryption.rb +7 -0
- data/lib/batch-kit/encryption/java_encryption.rb +178 -0
- data/lib/batch-kit/encryption/ruby_encryption.rb +175 -0
- data/lib/batch-kit/events.rb +157 -0
- data/lib/batch-kit/framework/acts_as_job.rb +197 -0
- data/lib/batch-kit/framework/acts_as_sequence.rb +123 -0
- data/lib/batch-kit/framework/definable.rb +169 -0
- data/lib/batch-kit/framework/job.rb +121 -0
- data/lib/batch-kit/framework/job_definition.rb +105 -0
- data/lib/batch-kit/framework/job_run.rb +145 -0
- data/lib/batch-kit/framework/runnable.rb +235 -0
- data/lib/batch-kit/framework/sequence.rb +87 -0
- data/lib/batch-kit/framework/sequence_definition.rb +38 -0
- data/lib/batch-kit/framework/sequence_run.rb +48 -0
- data/lib/batch-kit/framework/task_definition.rb +89 -0
- data/lib/batch-kit/framework/task_run.rb +53 -0
- data/lib/batch-kit/helpers/date_time.rb +54 -0
- data/lib/batch-kit/helpers/email.rb +198 -0
- data/lib/batch-kit/helpers/html.rb +175 -0
- data/lib/batch-kit/helpers/process.rb +101 -0
- data/lib/batch-kit/helpers/zip.rb +30 -0
- data/lib/batch-kit/job.rb +11 -0
- data/lib/batch-kit/lockable.rb +138 -0
- data/lib/batch-kit/loggable.rb +78 -0
- data/lib/batch-kit/logging.rb +169 -0
- data/lib/batch-kit/logging/java_util_logger.rb +87 -0
- data/lib/batch-kit/logging/log4r_logger.rb +71 -0
- data/lib/batch-kit/logging/null_logger.rb +35 -0
- data/lib/batch-kit/logging/stdout_logger.rb +96 -0
- data/lib/batch-kit/resources.rb +191 -0
- data/lib/batch-kit/sequence.rb +7 -0
- metadata +122 -0
@@ -0,0 +1,169 @@
|
|
1
|
+
class BatchKit
|
2
|
+
|
3
|
+
# Captures details of a definable batch process, e.g. a Task or Job.
|
4
|
+
#
|
5
|
+
# @abstract
|
6
|
+
class Definable
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# Register additional properties to be recorded on this definable.
|
11
|
+
#
|
12
|
+
# @param props [Array<Symbol>] The names of properties to be added
|
13
|
+
# to the definition. Used by sub-classes to add to the available
|
14
|
+
# properties. This provides a mechanism by which associated
|
15
|
+
# Run objects can obtain a list of process properties that they
|
16
|
+
# can delegate.
|
17
|
+
def add_properties(*props)
|
18
|
+
attr_accessor(*props)
|
19
|
+
properties.concat(props)
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# @return [Array<Symbol>] the names of properties available on this
|
24
|
+
# definition.
|
25
|
+
def properties
|
26
|
+
@properties ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
# When this class is inherited, we need to copy the common property
|
31
|
+
# names into the sub-class, since each sub-class needs the common
|
32
|
+
# property names defined on this base class, as well as any sub-
|
33
|
+
# class specific properties.
|
34
|
+
def inherited(subclass)
|
35
|
+
subclass.instance_variable_set(:@properties, @properties.clone)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# @!attribute :name [String] A user-friendly name for this process.
|
42
|
+
# @!attribute :description [String] A short description of what this
|
43
|
+
# process does.
|
44
|
+
# @!attribute :instance [String] An optional expression used to assign
|
45
|
+
# an instance identifier to a process.
|
46
|
+
# An instance identifier allows a process that has different execution
|
47
|
+
# profiles (typically depending on the arguments it is run with)
|
48
|
+
# to identify which of those profiles it is executing. This expression
|
49
|
+
# will be evaluated at the time this process is invoked, and the
|
50
|
+
# result will become the instance identifier for the Runnable.
|
51
|
+
# @!attribute :runs [Array<Runnable>] Array of runs of this process.
|
52
|
+
# @!attribute :lock_name [String] The name of any lock that this
|
53
|
+
# process requires before it can proceed. If nil, no lock is
|
54
|
+
# required and the process can commence without any co-ordination
|
55
|
+
# with other processes.
|
56
|
+
# @!attribute :lock_timeout [Fixnum] Number of seconds after which a
|
57
|
+
# lock obtained by this process will expire. This is to ensure that
|
58
|
+
# locks don't remain indefinitely if a process fails to release the
|
59
|
+
# lock properly. As such, it should be longer than any reasonable
|
60
|
+
# run of this process is likely to take, but no longer.
|
61
|
+
# @!attribute :lock_wait_timeout [Fixnum] Number of seconds before
|
62
|
+
# this process will give up waiting for a lock to become available.
|
63
|
+
add_properties(:name, :description, :instance, :runs,
|
64
|
+
:lock_name, :lock_timeout, :lock_wait_timeout
|
65
|
+
)
|
66
|
+
|
67
|
+
|
68
|
+
# Create a new instance of this definition.
|
69
|
+
def initialize
|
70
|
+
@runs = []
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
# Returns an event name for publication, based on the sub-class of Runnable
|
75
|
+
# that is triggering the event.
|
76
|
+
def event_name(event)
|
77
|
+
"#{self.class.name.split('::')[1].downcase}.#{event}"
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
# Sets properties from an options hash.
|
82
|
+
#
|
83
|
+
# @param opts [Hash] A hash containing properties to be set on this
|
84
|
+
# definable.
|
85
|
+
def set_from_options(opts)
|
86
|
+
unknown = opts.keys - self.class.properties
|
87
|
+
if unknown.size > 0
|
88
|
+
raise ArgumentError, "The following option(s) are invalid for #{
|
89
|
+
self.class.name}: #{unknown.join(', ')}. Valid options are: #{
|
90
|
+
self.class.properties.join(', ')}"
|
91
|
+
end
|
92
|
+
self.class.properties.each do |prop|
|
93
|
+
if opts.has_key?(prop)
|
94
|
+
self.send("#{prop}=", opts[prop])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
# Adds an aspect (as in aspect-oriented programming, or AOP) around the
|
101
|
+
# existing instance method +mthd_name+ on +tgt_class+. The aspect does
|
102
|
+
# the following:
|
103
|
+
# - Calls the #pre_execute method with the object instance on which the
|
104
|
+
# aspect method is being invoked. If the #pre_execute method returns
|
105
|
+
# false, the method call is skipped; otherwise, proceeds to the next
|
106
|
+
# step.
|
107
|
+
# - Calls the #around_execute method, which must yield back at the point
|
108
|
+
# at which the wrapped method body should be invoked.
|
109
|
+
# - Calls the #post_execute method with a boolean OK indicator, and the
|
110
|
+
# result of the method (if OK) or the exception it threw (if not OK).
|
111
|
+
#
|
112
|
+
# @param tgt_class [Class] The class on which the method to be wrapped
|
113
|
+
# is defined.
|
114
|
+
# @param mthd_name [Symbol] The name of the instance method to be
|
115
|
+
# wrapped.
|
116
|
+
def add_aspect(tgt_class, mthd_name)
|
117
|
+
defn = self
|
118
|
+
mthd = tgt_class.instance_method(mthd_name)
|
119
|
+
tgt_class.class_eval do
|
120
|
+
define_method mthd_name do |*args, &block|
|
121
|
+
run = defn.create_run(self, *args)
|
122
|
+
if run.pre_execute(self, *args)
|
123
|
+
ok = false
|
124
|
+
result = nil
|
125
|
+
begin
|
126
|
+
run.around_execute(self, *args) do
|
127
|
+
result = mthd.bind(self).call(*args, &block)
|
128
|
+
end
|
129
|
+
ok = true
|
130
|
+
run.success(self, result)
|
131
|
+
result
|
132
|
+
rescue Exception => ex
|
133
|
+
run.failure(self, ex) unless ok
|
134
|
+
raise
|
135
|
+
rescue Interrupt
|
136
|
+
run.abort(self) unless ok
|
137
|
+
raise
|
138
|
+
ensure
|
139
|
+
run.post_execute(self, ok)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
Events.publish(tgt_class, event_name('defined'), mthd_name)
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
# Creates an associated Runnable object for this definition. This method
|
149
|
+
# must be overridden in sub-classes.
|
150
|
+
#
|
151
|
+
# @param process_obj [Object] The process object instance on which the
|
152
|
+
# process method will be invoked.
|
153
|
+
# @param args [Array<Object>] The arguments to be passed to the process
|
154
|
+
# method.
|
155
|
+
def create_run(process_obj, *args)
|
156
|
+
raise "Not implemented in #{self.class.name}"
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
# Add a handler for interrupt (i.e. Ctrl-C etc) signals; this simply
|
161
|
+
# raises an Interrupt exception on the main thread
|
162
|
+
trap 'INT' do
|
163
|
+
Thread.main.raise Interrupt
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require_relative '../arguments'
|
2
|
+
require_relative '../configurable'
|
3
|
+
require_relative '../loggable'
|
4
|
+
|
5
|
+
|
6
|
+
# Default log level is :detail
|
7
|
+
BatchKit::LogManager.configure(log_level: :detail)
|
8
|
+
|
9
|
+
|
10
|
+
class BatchKit
|
11
|
+
|
12
|
+
class Job
|
13
|
+
|
14
|
+
include Arguments
|
15
|
+
include Configurable
|
16
|
+
include Loggable
|
17
|
+
|
18
|
+
|
19
|
+
# Include ActsAsJob into any inheriting class
|
20
|
+
def self.inherited(sub_class)
|
21
|
+
sub_class.class_eval do
|
22
|
+
include ActsAsJob
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
# A class variable for controlling whether jobs run; defaults to true.
|
28
|
+
# Provides a means for orchestration programs to prevent the running
|
29
|
+
# of jobs on require when jobs need to be runnable as standalone progs.
|
30
|
+
@@enabled = true
|
31
|
+
def self.enabled=(val)
|
32
|
+
@@enabled = val
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# A method that instantiates an instance of this job, parses
|
37
|
+
# arguments from the command-line, and then executes the job.
|
38
|
+
def self.run(args = ARGV)
|
39
|
+
if @@enabled
|
40
|
+
if args.length == 0 && self.args_def.keys.length > 0
|
41
|
+
shell
|
42
|
+
else
|
43
|
+
run_once(args)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# Instantiates and executes a job, using the supplied arguments +args+.
|
50
|
+
#
|
51
|
+
# @param args [Array<String>] an array containin§g the command-line to
|
52
|
+
# be processed by the job.
|
53
|
+
def self.run_once(args, show_usage_on_error = true)
|
54
|
+
job = self.new
|
55
|
+
job.parse_arguments(args, show_usage_on_error)
|
56
|
+
unless self.job.method_name
|
57
|
+
raise "No job entry method has been defined; use job :<method_name> or job do ... end in your class"
|
58
|
+
end
|
59
|
+
job.send(self.job.method_name)
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# Starts an interactive shell for this job. Each command line entered is
|
64
|
+
# passed to a new instance of the job for execution.
|
65
|
+
def self.shell(prompt = '> ')
|
66
|
+
require 'readline'
|
67
|
+
require 'csv'
|
68
|
+
puts "Starting interactive shell... enter 'exit' to quit"
|
69
|
+
while true do
|
70
|
+
args = Readline.readline(prompt, true)
|
71
|
+
break if args == 'exit' || args == 'quit'
|
72
|
+
begin
|
73
|
+
run_once(CSV.parse_line(args, col_sep: ' '), false)
|
74
|
+
rescue Exception
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
# Convenience method for using a lock within a job method
|
81
|
+
#
|
82
|
+
# @param lock_name [String] The name of the lock to obtain during
|
83
|
+
# execution of the block.
|
84
|
+
# @param lock_timeout [Fixnum] The maximum time (in seconds) until the
|
85
|
+
# lock should expire.
|
86
|
+
# @param wait_timeout [Fixnum] An optional time (in seconds) to wait for
|
87
|
+
# the lock to become available if it is already in use.
|
88
|
+
def with_lock(lock_name, lock_timeout, wait_timeout = nil, &blk)
|
89
|
+
self.job_run.with_lock(lock_name, lock_timeout, wait_timeout, &blk)
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
Events.subscribe(self, 'job_run.execute') do |obj, run, *args|
|
94
|
+
Console.title = run.label
|
95
|
+
end
|
96
|
+
|
97
|
+
Events.subscribe(self, 'task_run.execute') do |obj, run, *args|
|
98
|
+
Console.title = "#{run.job_run.label} : #{run.label}"
|
99
|
+
end
|
100
|
+
|
101
|
+
Events.subscribe(self, 'task_run.post-execute') do |obj, run, *args|
|
102
|
+
Console.title = run.job_run.label
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
# Add unhandled exception logging
|
107
|
+
Events.subscribe(self, ['sequence_run.failure',
|
108
|
+
'job_run.failure',
|
109
|
+
'task_run.failure']) do |obj, run, ex|
|
110
|
+
unless (oid = ex.object_id) == @last_id
|
111
|
+
@last_id = oid
|
112
|
+
# Strip out framework methods from backtrace
|
113
|
+
ex.backtrace.reject!{ |f| f =~ /lib.batch.framework/ }
|
114
|
+
obj.log.error "#{ex} at #{ex.backtrace.first}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
|
4
|
+
class BatchKit
|
5
|
+
|
6
|
+
class Job
|
7
|
+
|
8
|
+
# Captures details about a job definition - the class of the job, the server
|
9
|
+
# it runs on, the file it is defined in, etc.
|
10
|
+
class Definition < Definable
|
11
|
+
|
12
|
+
# @!attribute :job_class [Class] The class that defines the job.
|
13
|
+
# @!attribute :method_name [Symbol] The method that is run to execute
|
14
|
+
# the job.
|
15
|
+
# @!attribute :computer [String] The name of the machine on which the
|
16
|
+
# job was instantiated.
|
17
|
+
# @!attribute :file [String] The name of the file containing the job
|
18
|
+
# code.
|
19
|
+
# @!attribute :do_not_track [Boolean] By default, job executions may be
|
20
|
+
# recorded (if a persistence layer is available). This attribute can be
|
21
|
+
# used by jobs to indicate that runs of this job should not be recorded.
|
22
|
+
# @!attribute :tasks [Hash<Task::Definition>] A hash of task method names to
|
23
|
+
# Task::Definition objects capturing details of each task that is defined
|
24
|
+
# for this Job::Definition.
|
25
|
+
# @!attribute :job_id [Fixnum] A unique id for this Job::Definition, as
|
26
|
+
# assigned by the persistence layer.
|
27
|
+
# @!attribute :job_version [Fixnum] A version number for the job.
|
28
|
+
add_properties(
|
29
|
+
# Properties from job/task declarations
|
30
|
+
:job_class, :method_name, :computer, :file, :do_not_track, :tasks,
|
31
|
+
# Properties provided by persistence layer
|
32
|
+
:job_id, :job_version
|
33
|
+
)
|
34
|
+
|
35
|
+
|
36
|
+
# Create a new job Definition object for the job defined in +job_class+
|
37
|
+
# in +job_file+.
|
38
|
+
def initialize(job_class, job_file, job_name = nil)
|
39
|
+
raise ArgumentError, "job_class must be a Class" unless job_class.is_a?(Class)
|
40
|
+
@job_class = job_class
|
41
|
+
@file = job_file
|
42
|
+
@name = job_name || job_class.name.gsub(/([^A-Z ])([A-Z])/, '\1 \2').
|
43
|
+
gsub(/_/, ' ').gsub('::', ':').gsub(/\b([a-z])/) { $1.upcase }
|
44
|
+
@computer = Socket.gethostname
|
45
|
+
@method_name = nil
|
46
|
+
@tasks = {}
|
47
|
+
super()
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
# Define a job method - the method to be run to trigger the execution
|
52
|
+
# of the job.
|
53
|
+
#
|
54
|
+
# @param mthd_name [Symbol] The name of a method on the job class
|
55
|
+
# that is executed to begin the job processing. Note: This method
|
56
|
+
# must already exist on the job class when this setter is called, so
|
57
|
+
# that it can be wrapped in an aspect with before/after processing.
|
58
|
+
def method_name=(mthd_name)
|
59
|
+
unless job_class.instance_methods.include?(mthd_name)
|
60
|
+
raise ArgumentError, "Job class #{job_class.name} does not define a ##{mthd_name} method"
|
61
|
+
end
|
62
|
+
if @method_name
|
63
|
+
raise "Job class #{job_class.name} already has a job method defined (##{@method_name})"
|
64
|
+
end
|
65
|
+
@method_name = mthd_name
|
66
|
+
|
67
|
+
# Add an aspect for executing job
|
68
|
+
add_aspect(job_class, mthd_name)
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
# Add a record of a run of the job, or details about a task that the job
|
73
|
+
# performs.
|
74
|
+
def <<(task)
|
75
|
+
unless task.is_a?(Task::Definition)
|
76
|
+
raise ArgumentError, "Only a Task::Definition can be added to a Job::Definition"
|
77
|
+
end
|
78
|
+
key = task.method_name
|
79
|
+
if @tasks.has_key?(key)
|
80
|
+
raise ArgumentError, "#{self} already has a task for ##{key}"
|
81
|
+
end
|
82
|
+
@tasks[key] = task
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
# Create a new Job::Run object for a run of thie job.
|
87
|
+
#
|
88
|
+
# @param job_obj [Object] The job object that is running this job.
|
89
|
+
# @param args [Array<Object>] The arguments passed to the job method.
|
90
|
+
def create_run(job_obj, *args)
|
91
|
+
job_run = Job::Run.new(self, job_obj, *args)
|
92
|
+
@runs << job_run
|
93
|
+
job_run
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def to_s
|
98
|
+
"<BatchKit::Job::Definition #{@job_class.name}##{@method_name}>"
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'etc'
|
2
|
+
|
3
|
+
|
4
|
+
class BatchKit
|
5
|
+
|
6
|
+
class Job
|
7
|
+
|
8
|
+
# Captures details of an execution of a job.
|
9
|
+
class Run < Runnable
|
10
|
+
|
11
|
+
# @!attribute :run_by [String] The name of the user that ran this job
|
12
|
+
# instance.
|
13
|
+
# @!attribute :cmd_line [String] The command-line used to invoke the job.
|
14
|
+
# @!attribute :job_args [ArgParser::Arguments] A structure holding the
|
15
|
+
# parsed job arguments.
|
16
|
+
#
|
17
|
+
# @!attribute :job_run_id [Fixnum] An integer identifier that uniquely
|
18
|
+
# identifies this job run.
|
19
|
+
# @!attribute :pid [Fixnum] A process identifier (PID) for the process
|
20
|
+
# that is running the job.
|
21
|
+
# @!attribute :request_id [Fixnum] An integer identifier that links this
|
22
|
+
# job run to a job run request (if job is run on-demand).
|
23
|
+
# @!attribute :requestors [Array<String>] A list of the requestor(s) that
|
24
|
+
# requested for this job to be run. May be more than one if the request
|
25
|
+
# has been in a queue.
|
26
|
+
# @!attribute :start_time [Time] Time at which the job started
|
27
|
+
# executing.
|
28
|
+
# @!attribute :end_time [Time] Time at which the job ended execution.
|
29
|
+
# @!attribute :task_runs [Array<TaskRun>] An array containing details of
|
30
|
+
# the tasks that were executed by this job.
|
31
|
+
# @!attribute :exit_code [Fixnum] An exit status code for the job, where
|
32
|
+
# 0 signifies success, and non-zero failure. A value of -1 indicates
|
33
|
+
# the job was aborted (killed).
|
34
|
+
# @!attribute :exception [Exception] Any uncaught exception that
|
35
|
+
# occurred during job execution (and which was not caught by a task
|
36
|
+
# run).
|
37
|
+
PROPERTIES = [
|
38
|
+
:run_by, :pid, :job_run_id,
|
39
|
+
:cmd_line, :job_args,
|
40
|
+
:request_id, :requestors, :task_runs
|
41
|
+
]
|
42
|
+
# Define accessors for each property
|
43
|
+
PROPERTIES.each do |attr|
|
44
|
+
attr_accessor attr
|
45
|
+
end
|
46
|
+
|
47
|
+
# Make Job::Definition properties accessible off this Job::Run.
|
48
|
+
add_delegated_properties(*Job::Definition.properties)
|
49
|
+
|
50
|
+
|
51
|
+
# Instantiate a new JobRun representing a run of a job.
|
52
|
+
#
|
53
|
+
# @param job_def [Job::Definition] The Job::Definition to which this
|
54
|
+
# run relates.
|
55
|
+
# @param job_object [Object] The job object instance from which the
|
56
|
+
# job is being executed.
|
57
|
+
# @param run_args [Array<Object>] An array of the argument values
|
58
|
+
# passed to the job method.
|
59
|
+
def initialize(job_def, job_object, *run_args)
|
60
|
+
raise ArgumentError unless job_def.is_a?(Job::Definition)
|
61
|
+
@run_by = Etc.getlogin
|
62
|
+
@cmd_line = "#{$0} #{ARGV.map{ |s| s =~ / |^\*$/ ? %Q{"#{s}"} : s }.join(' ')}".strip
|
63
|
+
@pid = ::Process.pid
|
64
|
+
@task_runs = []
|
65
|
+
@job_args = job_object.arguments if job_object.respond_to?(:arguments)
|
66
|
+
super(job_def, job_object, run_args)
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# Adds a Task::Run to this Job::Run.
|
71
|
+
def <<(task_run)
|
72
|
+
unless task_run.is_a?(Task::Run)
|
73
|
+
raise ArgumentError, "Only Task::Run objects can be added to this Job::Run"
|
74
|
+
end
|
75
|
+
@task_runs << task_run
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
# Called as the process is executing.
|
80
|
+
#
|
81
|
+
# @param process_obj [Object] Object that is executing the batch
|
82
|
+
# process.
|
83
|
+
# @param args [*Object] Any arguments passed to the method that is
|
84
|
+
# executing the process.
|
85
|
+
# @yield at the point when the process should execute.
|
86
|
+
def around_execute(process_obj, *args)
|
87
|
+
if process_obj.job_run && process_obj.job_run.status == :executing
|
88
|
+
raise "There is already a job run active (#{process_obj.job_run}) for #{process_obj}"
|
89
|
+
end
|
90
|
+
process_obj.instance_variable_set(:@__job_run__, self)
|
91
|
+
super
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# Called after the process executes and completes successfully.
|
96
|
+
#
|
97
|
+
# @param process_obj [Object] Object that is executing the batch
|
98
|
+
# process.
|
99
|
+
# @param result [Object] The return value of the process.
|
100
|
+
def success(process_obj, result)
|
101
|
+
super
|
102
|
+
process_obj.on_success if process_obj.respond_to?(:on_success)
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
# Called after the process executes and fails.
|
107
|
+
#
|
108
|
+
# @param process_obj [Object] Object that is executing the batch
|
109
|
+
# process.
|
110
|
+
# @param exception [Exception] The exception that caused the job to
|
111
|
+
# fail.
|
112
|
+
def failure(process_obj, exception)
|
113
|
+
super
|
114
|
+
process_obj.on_failure(exception) if process_obj.respond_to?(:on_failure)
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
# Called if a batch process is aborted.
|
119
|
+
#
|
120
|
+
# @param process_obj [Object] Object that is executing the batch
|
121
|
+
# process.
|
122
|
+
def abort(process_obj)
|
123
|
+
super
|
124
|
+
process_obj.on_abort if process_obj.respond_to?(:on_abort)
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
# @return [Boolean] True if the job run should be recorded via any
|
129
|
+
# persistence layer.
|
130
|
+
def persist?
|
131
|
+
!definition.do_not_track
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
# @return [String] a short representation of this job run.
|
136
|
+
def to_s
|
137
|
+
"<BatchKit::Job::Run label='#{label}'>"
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|