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 = @rule_definition.rule_condition_context.eval(@env)
  @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.



181
182
183
184
185
186
187
188
189
190
# File 'lib/pione/rule-engine/basic-handler.rb', line 181

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



192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/pione/rule-engine/basic-handler.rb', line 192

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)


93
94
95
# File 'lib/pione/rule-engine/basic-handler.rb', line 93

def execute
  raise NotImplementError
end

#find_outputs_from_spacevoid

This method returns an undefined value.

Find outputs from the domain space.



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/pione/rule-engine/basic-handler.rb', line 154

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.



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
83
84
85
86
87
88
89
90
# File 'lib/pione/rule-engine/basic-handler.rb', line 54

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 :@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"))
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



105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/pione/rule-engine/basic-handler.rb', line 105

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.



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

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.



128
129
130
131
132
# File 'lib/pione/rule-engine/basic-handler.rb', line 128

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.



228
229
230
231
232
233
234
235
236
237
# File 'lib/pione/rule-engine/basic-handler.rb', line 228

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



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

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.



251
252
253
254
255
# File 'lib/pione/rule-engine/basic-handler.rb', line 251

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.



137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/pione/rule-engine/basic-handler.rb', line 137

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.



258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/pione/rule-engine/basic-handler.rb', line 258

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



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

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.



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

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