Class: Pione::RuleEngine::BasicHandler

Inherits:
Object
  • Object
show all
Includes:
Log::MessageLog, TupleSpace::TupleSpaceInterface
Defined in:
lib/pione/rule-engine/basic-handler.rb

Overview

BasicHandler is a base class for rule handlers.

Constant Summary

Constants included from Log::MessageLog

Log::MessageLog::MESSAGE_QUEUE

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Log::MessageLog

#debug_message, #debug_message_begin, #debug_message_end, debug_mode, debug_mode=, debug_mode?, message, quiet_mode, quiet_mode=, quiet_mode?, #show, #user_message, #user_message_begin, #user_message_end

Methods included from TupleSpace::TupleSpaceInterface

#process_log, #processing_error, #read!, #set_tuple_space, #take!, #take_all!, tuple_space_operation, #tuple_space_server, #with_process_log

Constructor Details

#initialize(space, env, package_id, rule_name, rule_definition, inputs, param_set, domain_id, caller_id) ⇒ BasicHandler

Create a new handler for rule.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/pione/rule-engine/basic-handler.rb', line 25

def initialize(space, env, package_id, rule_name, rule_definition, inputs, param_set, domain_id, caller_id)
  ### set tuple space server
  set_tuple_space(space)

  ### set informations
  @plain_env = env
  @env = setup_env(env, param_set)
  @package_id = package_id
  @rule_name = rule_name
  @rule_definition = rule_definition
  @rule_condition = rule_definition.rule_condition_context.eval(@env)
  @inputs = inputs
  @outputs = []
  @param_set = param_set
  @digest = Util::TaskDigest.generate(package_id, rule_name, inputs, param_set)
  @base_location = read!(TupleSpace::BaseLocationTuple.any).location
  @dry_run = begin read!(TupleSpace::DryRunTuple.any).availability rescue false end
  @domain_id = domain_id
  @domain_location = make_location("", @domain_id)
  @caller_id = caller_id
end

Instance Attribute Details

#base_locationObject (readonly)

base location



18
19
20
# File 'lib/pione/rule-engine/basic-handler.rb', line 18

def base_location
  @base_location
end

#caller_idObject (readonly)

from domain



22
23
24
# File 'lib/pione/rule-engine/basic-handler.rb', line 22

def caller_id
  @caller_id
end

#digestObject (readonly)

handler’s digest string



17
18
19
# File 'lib/pione/rule-engine/basic-handler.rb', line 17

def digest
  @digest
end

#domain_idObject (readonly)

domain id



20
21
22
# File 'lib/pione/rule-engine/basic-handler.rb', line 20

def domain_id
  @domain_id
end

#domain_locationObject (readonly)

domain location



21
22
23
# File 'lib/pione/rule-engine/basic-handler.rb', line 21

def domain_location
  @domain_location
end

#dry_runObject (readonly)

flag of dry run mode



19
20
21
# File 'lib/pione/rule-engine/basic-handler.rb', line 19

def dry_run
  @dry_run
end

#envObject (readonly)

handler’s environement



9
10
11
# File 'lib/pione/rule-engine/basic-handler.rb', line 9

def env
  @env
end

#inputsObject (readonly)

input tuples



14
15
16
# File 'lib/pione/rule-engine/basic-handler.rb', line 14

def inputs
  @inputs
end

#outputsObject (readonly)

output tuples



15
16
17
# File 'lib/pione/rule-engine/basic-handler.rb', line 15

def outputs
  @outputs
end

#package_idObject (readonly)

package id



10
11
12
# File 'lib/pione/rule-engine/basic-handler.rb', line 10

def package_id
  @package_id
end

#param_setObject (readonly)

parameter set



16
17
18
# File 'lib/pione/rule-engine/basic-handler.rb', line 16

def param_set
  @param_set
end

#plain_envObject (readonly)

plain environment



8
9
10
# File 'lib/pione/rule-engine/basic-handler.rb', line 8

def plain_env
  @plain_env
end

#rule_conditionObject (readonly)

rule condtions



13
14
15
# File 'lib/pione/rule-engine/basic-handler.rb', line 13

def rule_condition
  @rule_condition
end

#rule_definitionObject (readonly)

definition of the handling rule



12
13
14
# File 'lib/pione/rule-engine/basic-handler.rb', line 12

def rule_definition
  @rule_definition
end

#rule_nameObject (readonly)

rule name



11
12
13
# File 'lib/pione/rule-engine/basic-handler.rb', line 11

def rule_name
  @rule_name
end

Instance Method Details

#apply_touch_operation(condition, tuples) ⇒ Object

Apply touch operation.



173
174
175
176
177
178
179
180
181
182
# File 'lib/pione/rule-engine/basic-handler.rb', line 173

def apply_touch_operation(condition, tuples)
  _condition = condition.eval(@env)
  if _condition.operation == :touch
    if tuples.empty?
      create_data_by_touch_operation(_condition)
    else
      update_time_by_touch_operation(tuples)
    end
  end
end

#create_data_by_touch_operation(condition) ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/pione/rule-engine/basic-handler.rb', line 184

def create_data_by_touch_operation(condition)
  # NOTE: touch operation applies first piece of data sequence now
  name = condition.pieces.first.pattern
  location = @domain_location + name
  # create a empty file
  location.create("") unless location.exist?
  # FIXME: write a touch tuple
  time = Time.now
  write(TupleSpace::TouchTuple.new(name: name, domain: @domain_id, time: time))
  # FIXME: create an output data tuple
  data_tuple = TupleSpace::DataTuple.new(name: name, domain: @domain_id, location: location, time: time)
  write(data_tuple)
  [data_tuple]
end

#executeObject

Executes the rule.

Raises:

  • (NotImplementError)


85
86
87
# File 'lib/pione/rule-engine/basic-handler.rb', line 85

def execute
  raise NotImplementError
end

#find_outputs_from_spacevoid

This method returns an undefined value.

Find outputs from the domain space.



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/pione/rule-engine/basic-handler.rb', line 146

def find_outputs_from_space
  tuples = read_all(TupleSpace::DataTuple.new(domain: @domain_id))
  outputs = []

  @rule_condition.outputs.each_with_index do |condition, i|
    _condition = condition.eval(@env)
    case _condition.distribution
    when :all
      outputs[i] = tuples.find_all {|t| _condition.match(t.name)}
    when :each
      # FIXME
      outputs[i] = tuples.find_all {|t| _condition.match(t.name)}
    end

    # apply touch operation and push the result
    if new_tuples = apply_touch_operation(_condition, outputs[i])
      outputs[i] = new_tuples
    end

    # write data null if needed
    write_data_null(_condition, outputs[i], i)
  end

  return outputs
end

#handleObject

Handle the rule and return the outputs.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/pione/rule-engine/basic-handler.rb', line 48

def handle
  # make rule and task process log
  process_log(make_task_process_record.merge(transition: "start"))
  process_log(make_rule_process_record.merge(transition: "start"))

  # show begin messages
  user_message(@digest, 0, "==>")
  debug_message("caller: %s" % @caller_id)

  # save domain log
  Log::DomainLog.new(self).save

  # save domain info
  domain_info_location = @working_directory ? @working_directory :@domain_location
  System::DomainInfo.new(env.dumpable).write(domain_info_location)

  # execute the rule
  outputs = execute

  # publish outputs and finished
  begin
    outputs.flatten.compact.each {|output| write(output)}
    write(TupleSpace::FinishedTuple.new(@domain_id, :succeeded, outputs))
  rescue Rinda::RedundantTupleError
    write(TupleSpace::FinishedTuple.new(@domain_id, :error, outputs))
  end

  # show end message
  show_outputs(outputs)
  user_message(@digest, 0, "<==")

  # put rule and task process log
  process_log(make_rule_process_record.merge(transition: "complete"))
  process_log(make_task_process_record.merge(transition: "complete"))
end

#make_location(name, domain_id) ⇒ BasicLocation

Make location by data name and the domain.

Parameters:

  • name (String)

    data name

  • domain (String)

    domain of the data

Returns:

  • (BasicLocation)

    the location



97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/pione/rule-engine/basic-handler.rb', line 97

def make_location(name, domain_id)
  if domain_id == "root"
    return @base_location + "./%s" % name
  else
    # make relative path
    pakcage_id, rule_name, task_id = domain_id.split(":")
    path = "./.%s/%s/%s/%s" % [package_id, rule_name, task_id, name]

    # make location
    return @base_location + path
  end
end

#make_output_location(name) ⇒ Object

Make output data location by the name.



111
112
113
114
115
116
117
# File 'lib/pione/rule-engine/basic-handler.rb', line 111

def make_output_location(name)
  # FIXME: maybe we should not lift output here
  return if @caller_id.nil?

  # get parent domain or root domain
  make_location(name, @caller_id)
end

#make_output_tuple(data_expr) ⇒ Object

Make output tuple by the name.



120
121
122
123
124
# File 'lib/pione/rule-engine/basic-handler.rb', line 120

def make_output_tuple(data_expr)
  name = data_expr.first.name
  location = make_output_location(name)
  TupleSpace::DataTuple.new(name: name, domain: @domain_id, location: location, time: nil)
end

#make_rule_process_recordObject

Build rule process record.



220
221
222
223
224
225
226
227
228
229
# File 'lib/pione/rule-engine/basic-handler.rb', line 220

def make_rule_process_record
  Log::RuleProcessRecord.new.tap do |record|
    record.name = "&%s:%s" % [@package_id, @rule_name]
    record.rule_type = @rule_definition.rule_type
    if @caller
      caller_package_id, caller_rule_name, caller_task_id = @caller.split(":")
      record.caller = "&%s:%s" % [caller_package_id, caller_rule_name]
    end
  end
end

#make_task_process_recordObject



231
232
233
234
235
236
237
238
239
240
# File 'lib/pione/rule-engine/basic-handler.rb', line 231

def make_task_process_record
  Log::TaskProcessRecord.new.tap do |record|
    record.name = @digest
    record.package_id = @package_id
    record.rule_name = @rule_name
    record.rule_type = @rule_definition.rule_type
    record.inputs = @inputs.flatten.map{|input| input.name}.join(",")
    record.parameters = @param_set.textize
  end
end

#publish_outputs(outputs) ⇒ Object

Publish output data tuples.



243
244
245
246
247
# File 'lib/pione/rule-engine/basic-handler.rb', line 243

def publish_outputs(outputs)
  # output data
rescue Rinda::RedundantTupleError
  write("finished")
end

#setup_env(env, param_set) ⇒ Object

Setup handler’s environment. We make a new environment that is introduced a new layer in top of the plain package environment, so we can do any operations safety.



129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/pione/rule-engine/basic-handler.rb', line 129

def setup_env(env, param_set)
  # put new layer
  _env = env.layer
  # set current package id
  _env.set(current_package_id: package_id)
  # merge parameter set
  _env.merge_param_set(param_set)

  ### system environment
  # ENV.each do |key, value|
  #   @variable_table.set(Variable.new("ENV_" + key), PioneString.new(value))
  # end
end

#show_outputs(outputs) ⇒ Object

Show output tuples as message. This method is used for debugging only.



250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/pione/rule-engine/basic-handler.rb', line 250

def show_outputs(outputs)
  debug_message("Result of %s:" % @digest)
  if outputs
    outputs.each_with_index do |output, i|
      output.each_with_index do |t, ii|
        debug_message("[%s,%s] %s" % [i, ii, t.name], 1)
      end
    end
  else
    debug_message("no outputs", 1)
  end
end

#update_time_by_touch_operation(tuples) ⇒ Object



199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/pione/rule-engine/basic-handler.rb', line 199

def update_time_by_touch_operation(tuples)
  fun = lambda do |tuple|
    time = Time.now
    new_data = TupleSpace::DataTuple.new(name: tuple.name, domain: @domain_id, location: tuple.location, time: time)
    write(TupleSpace::TouchTuple.new(name: tuple.name, domain: @domain_id, time: time))
    write(new_data)
    new_data
  end
  tuples.map do |tuple|
    take!(TupleSpace::DataTuple.new(name: tuple.name, domain: @domain_id)) ? fun.call(tuple) : tuple
  end
end

#write_data_null(output, tuples, i) ⇒ Object

Write a data null tuple if the output condition accepts nonexistence.



213
214
215
216
217
# File 'lib/pione/rule-engine/basic-handler.rb', line 213

def write_data_null(output, tuples, i)
  if output.accept_nonexistence? and tuples.nil?
    write(TupleSpace::DataNullTuple.new(domain: @domain_id, position: i))
  end
end