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, #set_tuple_space, tuple_space_operation, #tuple_space_server, #with_process_log

Constructor Details

#initialize(param) ⇒ BasicHandler

Create a new handler for rule.

Parameters:

  • param (Hash)

    see RuleEngine.make



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/pione/rule-engine/basic-handler.rb', line 28

def initialize(param)
  ### set tuple space server
  set_tuple_space(param[:tuple_space])

  ### set informations
  @plain_env = param[:env]
  @env = setup_env(param[:env], param[:param_set])
  @package_id = param[:package_id]
  @rule_name = param[:rule_name]
  @rule_definition = param[:rule_definition]
  @rule_condition = eval_rule_condition()
  @inputs = param[:inputs]
  @outputs = []
  @param_set = param[: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 = param[:domain_id]
  @domain_location = make_location("", @domain_id)
  @caller_id = param[:caller_id]
  @request_from = param[:request_from]
  @session_id = param[:session_id]
  @client_ui = param[:client_ui]
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.



207
208
209
210
211
212
213
214
215
216
# File 'lib/pione/rule-engine/basic-handler.rb', line 207

def apply_touch_operation(condition, tuples)
  _condition = condition.eval(@env)
  if _condition.operation == :touch or (self.kind_of?(EmptyHandler) and _condition.operation == :write)
    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



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

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

#eval_rule_conditionObject



108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/pione/rule-engine/basic-handler.rb', line 108

def eval_rule_condition()
  rule_condition = @rule_definition.rule_condition_context.eval(@env)

  # change to touch operation if the definition is empty rule
  # if @rule_definition.kind_of?(Lang::EmptyRuleDefinition)
  #   rule_condition.outputs.each do |output|
  #     if output.operation == :write
  #       output.set(operation: :touch)
  #     end
  #   end
  # end

  return rule_condition
end

#executeObject

Executes the rule.

Raises:

  • (NotImplementError)


104
105
106
# File 'lib/pione/rule-engine/basic-handler.rb', line 104

def execute
  raise NotImplementError
end

#find_outputs_from_spacevoid

This method returns an undefined value.

Find outputs from the domain space.



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/pione/rule-engine/basic-handler.rb', line 180

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

#handleBoolean

Handle the rule and return the outputs.

Returns:

  • (Boolean)

    true if rule execution has succeeded, or false



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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/pione/rule-engine/basic-handler.rb', line 57

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)

  unless @domain_id == "root"
    # save domain log
    Log::DomainLog.new(self).save

    # save a domain dump file
    domain_dump_location = @working_directory ? @working_directory.location : @domain_location
    System::DomainDump.new(env.dumpable).write(domain_dump_location)
  end

  # execute the rule
  outputs = execute

  # publish outputs and finished
  begin
    outputs.flatten.compact.each {|output| write(output)}
    write(TupleSpace::FinishedTuple.new(@domain_id, Util::UUID.generate, :succeeded, outputs))
  rescue Rinda::RedundantTupleError
    write(TupleSpace::FinishedTuple.new(@domain_id, Util::UUID.generate, :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"))

  return true

rescue Object => e
  user_message("ERROR: " + e.message, 0, "info", :red)
  status = System::Status.error(message: e.message, exception: e)
  write(TupleSpace::CommandTuple.new(name: "terminate", args: [status]))
  return false
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



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

def make_location(name, domain_id)
  if domain_id == "root"
    return @base_location + "./output/%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.



145
146
147
148
149
150
151
# File 'lib/pione/rule-engine/basic-handler.rb', line 145

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.



154
155
156
157
158
# File 'lib/pione/rule-engine/basic-handler.rb', line 154

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.



254
255
256
257
258
259
260
261
262
263
# File 'lib/pione/rule-engine/basic-handler.rb', line 254

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



265
266
267
268
269
270
271
272
273
274
# File 'lib/pione/rule-engine/basic-handler.rb', line 265

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.



277
278
279
280
281
# File 'lib/pione/rule-engine/basic-handler.rb', line 277

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.



163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/pione/rule-engine/basic-handler.rb', line 163

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.



284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/pione/rule-engine/basic-handler.rb', line 284

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



233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/pione/rule-engine/basic-handler.rb', line 233

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.



247
248
249
250
251
# File 'lib/pione/rule-engine/basic-handler.rb', line 247

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