openwferu 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/bin/validate-workflow.rb +1 -0
  2. data/examples/openwferu.rb +57 -0
  3. data/lib/openwfe/contextual.rb +2 -0
  4. data/lib/openwfe/engine/engine.rb +109 -17
  5. data/lib/openwfe/engine/file_persisted_engine.rb +11 -1
  6. data/lib/openwfe/exceptions.rb +53 -0
  7. data/lib/openwfe/expool/expressionpool.rb +195 -137
  8. data/lib/openwfe/expool/journal.rb +175 -0
  9. data/lib/openwfe/expool/journal_replay.rb +121 -0
  10. data/lib/openwfe/expool/wfidgen.rb +318 -0
  11. data/lib/openwfe/expool/yamlexpstorage.rb +23 -54
  12. data/lib/openwfe/expressions/condition.rb +14 -2
  13. data/lib/openwfe/expressions/environment.rb +47 -37
  14. data/lib/openwfe/expressions/expressionmap.rb +73 -67
  15. data/lib/openwfe/expressions/fe_cancel.rb +2 -0
  16. data/lib/openwfe/expressions/fe_concurrence.rb +14 -1
  17. data/lib/openwfe/expressions/fe_cron.rb +186 -0
  18. data/lib/openwfe/expressions/fe_cursor.rb +6 -0
  19. data/lib/openwfe/expressions/fe_define.rb +2 -0
  20. data/lib/openwfe/expressions/fe_fqv.rb +2 -0
  21. data/lib/openwfe/expressions/fe_iterator.rb +2 -0
  22. data/lib/openwfe/expressions/fe_losfor.rb +4 -0
  23. data/lib/openwfe/expressions/fe_misc.rb +16 -1
  24. data/lib/openwfe/expressions/fe_participant.rb +7 -0
  25. data/lib/openwfe/expressions/fe_raw.rb +125 -8
  26. data/lib/openwfe/expressions/fe_sequence.rb +2 -0
  27. data/lib/openwfe/expressions/fe_sleep.rb +213 -0
  28. data/lib/openwfe/expressions/fe_subprocess.rb +2 -0
  29. data/lib/openwfe/expressions/fe_value.rb +8 -0
  30. data/lib/openwfe/expressions/fe_when.rb +205 -0
  31. data/lib/openwfe/expressions/flowexpression.rb +62 -9
  32. data/lib/openwfe/expressions/raw_prog.rb +147 -45
  33. data/lib/openwfe/expressions/raw_xml.rb +50 -5
  34. data/lib/openwfe/expressions/timeout.rb +32 -5
  35. data/lib/openwfe/flowexpressionid.rb +6 -4
  36. data/lib/openwfe/listeners/listeners.rb +4 -3
  37. data/lib/openwfe/listeners/socketlisteners.rb +30 -4
  38. data/lib/openwfe/logging.rb +9 -1
  39. data/lib/openwfe/participants/participants.rb +9 -9
  40. data/lib/openwfe/participants/socketparticipants.rb +41 -8
  41. data/lib/openwfe/rudefinitions.rb +21 -0
  42. data/lib/openwfe/storage/yamlextras.rb +115 -0
  43. data/lib/openwfe/storage/yamlfilestorage.rb +23 -4
  44. data/lib/openwfe/util/csvtable.rb +76 -6
  45. data/lib/openwfe/util/dollar.rb +5 -0
  46. data/lib/openwfe/util/kotoba.rb +236 -0
  47. data/lib/openwfe/util/ometa.rb +64 -0
  48. data/lib/openwfe/util/safe.rb +2 -0
  49. data/lib/openwfe/util/scheduler.rb +58 -11
  50. data/lib/openwfe/utils.rb +27 -0
  51. data/lib/openwfe/version.rb +1 -1
  52. data/lib/openwfe/worklist/storeparticipant.rb +2 -3
  53. data/test/csv_test.rb +57 -0
  54. data/test/expmap_test.rb +54 -0
  55. data/test/file_persistence_test.rb +9 -9
  56. data/test/flowtestbase.rb +19 -1
  57. data/test/ft_11_ppd.rb +18 -0
  58. data/test/ft_15_iterator.rb +27 -0
  59. data/test/ft_23b_when.rb +2 -2
  60. data/test/ft_27_getflowpos.rb +19 -9
  61. data/test/ft_29_httprb.rb +31 -2
  62. data/test/ft_30_socketlistener.rb +3 -5
  63. data/test/ft_31_flowname.rb +40 -0
  64. data/test/ft_32_journal.rb +60 -0
  65. data/test/ft_33_description.rb +100 -0
  66. data/test/ft_34_cancelwfid.rb +69 -0
  67. data/test/ft_35_localdefs.rb +63 -0
  68. data/test/ft_7_lose.rb +0 -61
  69. data/test/ft_7b_lose.rb +85 -0
  70. data/test/ft_tests.rb +49 -0
  71. data/test/kotoba_test.rb +72 -0
  72. data/test/misc_test.rb +4 -12
  73. data/test/param_test.rb +284 -0
  74. data/test/rake_qtest.rb +13 -35
  75. data/test/rake_test.rb +2 -3
  76. data/test/raw_prog_test.rb +9 -7
  77. data/test/restart_cron_test.rb +9 -5
  78. data/test/restart_sleep_test.rb +104 -0
  79. data/test/restart_tests.rb +15 -0
  80. data/test/restart_when_test.rb +105 -0
  81. data/test/scheduler_test.rb +2 -2
  82. data/test/sec_test.rb +163 -0
  83. data/test/wfid_test.rb +69 -13
  84. metadata +31 -16
  85. data/lib/openwfe/expressions/fe_time.rb +0 -454
  86. data/test/restart_test.rb +0 -79
@@ -141,9 +141,10 @@ module OpenWFE
141
141
  def reply_to_socket (socket, result)
142
142
 
143
143
  socket.puts result.to_s
144
+ socket.puts
144
145
  socket.close_write
145
146
 
146
- ldebug { "reply_to_socket() result is >>>#{result}<<<" }
147
+ #ldebug { "reply_to_socket() result is >#{result}<" }
147
148
  end
148
149
 
149
150
  #
@@ -165,9 +166,20 @@ module OpenWFE
165
166
 
166
167
  linfo { "listen() listening on #{@server.addr.join(' ')}" }
167
168
 
168
- while socket = @server.accept
169
+ while true
170
+
171
+ socket = nil
172
+
173
+ begin
174
+ socket = @server.accept
175
+ rescue Exception => e
176
+ linfo { "listen() shut down '#{e}'" }
177
+ end
178
+
179
+ return unless socket
180
+
169
181
  OpenWFE.call_in_thread(@service_name, self) do
170
- handle_socket(socket) if is_allowed? socket
182
+ handle_socket(socket) if socket and is_allowed? socket
171
183
  end
172
184
  end
173
185
  end
@@ -221,7 +233,21 @@ module OpenWFE
221
233
  end
222
234
  end
223
235
 
224
- result = get_engine.reply(wi)
236
+ result = nil
237
+
238
+ begin
239
+
240
+ result = get_engine.reply(wi)
241
+
242
+ ldebug { "handle_socket() result is >>#{result}<<" }
243
+
244
+ rescue Exception => e
245
+
246
+ result = "ERROR\n\n"
247
+ result << OpenWFE::exception_to_s(e)
248
+
249
+ ldebug { "handle_socket() error reply :\n" + result }
250
+ end
225
251
 
226
252
  reply_to_socket(socket, result)
227
253
 
@@ -40,7 +40,8 @@
40
40
  #
41
41
 
42
42
  require 'logger'
43
- require 'openwfe/rudefinitions'
43
+ require 'openwfe/utils'
44
+ #require 'openwfe/rudefinitions'
44
45
 
45
46
 
46
47
  module OpenWFE
@@ -74,6 +75,13 @@ module OpenWFE
74
75
  do_log(:unknown, message, &block)
75
76
  end
76
77
 
78
+ #
79
+ # A simplification of caller_to_s for direct usage when debugging
80
+ #
81
+ def ldebug_callstack (msg, max_lines=nil)
82
+ ldebug { "#{msg}\n" + OpenWFE::caller_to_s(9, max_lines) }
83
+ end
84
+
77
85
  private
78
86
 
79
87
  def do_log (level, message, &block)
@@ -68,15 +68,15 @@ module OpenWFE
68
68
  #
69
69
  def initialize (context_or_dir=nil)
70
70
 
71
- if context_or_dir.kind_of? Hash
72
- @application_context = context_or_dir
73
- @workdir = @application_context[:work_directory]
74
- else
75
- @workdir = context_or_dir
76
- end
77
-
78
- @workdir = OpenWFE::DEFAULT_WORK_DIRECTORY unless @workdir
79
- @workdir = "#{@workdir}/out/"
71
+ #if context_or_dir.kind_of? Hash
72
+ # @application_context = context_or_dir
73
+ # @workdir = @application_context[:work_directory]
74
+ #else
75
+ # @workdir = context_or_dir
76
+ #end
77
+ #@workdir = OpenWFE::DEFAULT_WORK_DIRECTORY unless @workdir
78
+ #@workdir = "#{@workdir}/out/"
79
+ @workdir = OpenWFE::get_work_directory(context_or_dir) + "/out/"
80
80
 
81
81
  @reply_anyway = false
82
82
  end
@@ -41,8 +41,8 @@
41
41
 
42
42
  require 'yaml'
43
43
  require 'socket'
44
+ require 'timeout'
44
45
 
45
- #require 'openwfe/utils'
46
46
  require 'openwfe/rest/xmlcodec'
47
47
  require 'openwfe/participants/participant'
48
48
 
@@ -80,19 +80,22 @@ module OpenWFE
80
80
 
81
81
  #
82
82
  # The method called by the engine for each incoming workitem.
83
+ # Will dispatch the workitem over a TCP connection.
83
84
  #
84
85
  def consume (workitem)
85
86
 
87
+ dispatch(workitem)
88
+ end
89
+
90
+ def dispatch (workitem)
91
+
86
92
  socket = TCPSocket.new(@host, @port)
87
93
  socket.puts encode_workitem(workitem)
94
+ socket.puts
88
95
  socket.close_write
89
96
 
90
- reply = ""
91
- while true
92
- r = socket.gets
93
- break unless r
94
- reply << r
95
- end
97
+ reply = fetch_reply(socket)
98
+
96
99
  socket.close
97
100
 
98
101
  decode_reply(reply)
@@ -108,7 +111,7 @@ module OpenWFE
108
111
  #
109
112
  def SocketParticipant.dispatch (host, port, workitem)
110
113
 
111
- SocketParticipant.new(host, port).consume(workitem)
114
+ SocketParticipant.new(host, port).dispatch(workitem)
112
115
  end
113
116
 
114
117
  protected
@@ -127,6 +130,32 @@ module OpenWFE
127
130
  def decode_reply (r)
128
131
  r
129
132
  end
133
+
134
+ #
135
+ # The code that waits for the reply from the server, nicely
136
+ # wrapped inside a timeout and a rescue block.
137
+ #
138
+ def fetch_reply (socket)
139
+
140
+ reply = ""
141
+
142
+ begin
143
+
144
+ timeout(7) do
145
+ while true
146
+ r = socket.gets
147
+ break unless r
148
+ reply << r
149
+ end
150
+ end
151
+
152
+ rescue Exception => e
153
+ puts e
154
+ raise "timeout while waiting for reply"
155
+ end
156
+
157
+ reply
158
+ end
130
159
  end
131
160
 
132
161
  #
@@ -155,6 +184,10 @@ module OpenWFE
155
184
 
156
185
  protected
157
186
 
187
+ #
188
+ # This implementation encodes the workitem as an XML document.
189
+ # This is compatible with OpenWFEja.
190
+ #
158
191
  def encode_workitem (wi)
159
192
 
160
193
  sxml = OpenWFE::xml_encode(wi)
@@ -37,6 +37,8 @@
37
37
  # "made in Japan"
38
38
  #
39
39
 
40
+ require 'fileutils'
41
+
40
42
  require 'openwfe/utils'
41
43
  require 'openwfe/version'
42
44
 
@@ -50,6 +52,7 @@ module OpenWFE
50
52
 
51
53
  S_ENGINE = 'engine'
52
54
  S_EXPRESSION_MAP = 'expressionMap'
55
+ S_WFID_GENERATOR = 'wfidGenerator'
53
56
  S_EXPRESSION_POOL = 'expressionPool'
54
57
  S_EXPRESSION_STORAGE = 'expressionStorage'
55
58
  S_PARTICIPANT_MAP = 'participantMap'
@@ -70,6 +73,21 @@ module OpenWFE
70
73
 
71
74
  DEFAULT_WORK_DIRECTORY = './work'
72
75
 
76
+ #
77
+ # Returns the work directory for the OpenWFE[ru] application context
78
+ # (if any).
79
+ #
80
+ def OpenWFE.get_work_directory (dir=nil)
81
+
82
+ dir = DEFAULT_WORK_DIRECTORY unless @application_context
83
+ dir = @application_context[:work_directory] unless dir
84
+ dir = DEFAULT_WORK_DIRECTORY unless dir
85
+
86
+ FileUtils.makedirs(dir) unless File.exist?(dir)
87
+
88
+ dir
89
+ end
90
+
73
91
  #
74
92
  # A mixin for easy OpenWFE service lookup
75
93
  # (assumes the presence of an application context instance var)
@@ -85,6 +103,9 @@ module OpenWFE
85
103
  def get_expression_map
86
104
  return @application_context[S_EXPRESSION_MAP]
87
105
  end
106
+ def get_wfid_generator
107
+ return @application_context[S_WFID_GENERATOR]
108
+ end
88
109
  def get_expression_pool
89
110
  return @application_context[S_EXPRESSION_POOL]
90
111
  end
@@ -0,0 +1,115 @@
1
+ #
2
+ #--
3
+ # Copyright (c) 2007, John Mettraux, OpenWFE.org
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions are met:
8
+ #
9
+ # . Redistributions of source code must retain the above copyright notice, this
10
+ # list of conditions and the following disclaimer.
11
+ #
12
+ # . Redistributions in binary form must reproduce the above copyright notice,
13
+ # this list of conditions and the following disclaimer in the documentation
14
+ # and/or other materials provided with the distribution.
15
+ #
16
+ # . Neither the name of the "OpenWFE" nor the names of its contributors may be
17
+ # used to endorse or promote products derived from this software without
18
+ # specific prior written permission.
19
+ #
20
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ # POSSIBILITY OF SUCH DAMAGE.
31
+ #++
32
+ #
33
+ # $Id: definitions.rb 2725 2006-06-02 13:26:32Z jmettraux $
34
+ #
35
+
36
+ #
37
+ # "made in Japan"
38
+ #
39
+ # John Mettraux at openwfe.org
40
+ #
41
+
42
+ require 'yaml'
43
+
44
+
45
+ module OpenWFE
46
+
47
+ #
48
+ # reopening some classes in order to facilitate their
49
+ # yaml serialization
50
+ #
51
+
52
+ #
53
+ # opening for tuning yaml persistence
54
+ #
55
+ class FlowExpression
56
+
57
+ def to_yaml_properties
58
+
59
+ l = super()
60
+
61
+ l.delete("@application_context")
62
+
63
+ l.delete("@timeout_job_id")
64
+ #l.delete("@scheduler_job_id")
65
+ # scheduler ids should not get persisted
66
+
67
+ return l
68
+ end
69
+ end
70
+
71
+ #
72
+ # making sure that the FlowExpressionId is serialized as a unique String
73
+ #
74
+ class FlowExpressionId
75
+
76
+ yaml_as "tag:ruby.yaml.org,2002:#{self}"
77
+
78
+ #def to_yaml (opts={})
79
+ # @s = to_s
80
+ # super
81
+ #end
82
+ #def to_yaml_properties
83
+ # [ "@s" ]
84
+ #end
85
+
86
+ def to_yaml (opts={})
87
+ YAML::quick_emit(self.object_id, opts) do |out|
88
+ out.map(taguri) do |map|
89
+ map.add("s", to_s)
90
+ end
91
+ end
92
+ end
93
+
94
+ def FlowExpressionId.yaml_new (klass, tag, val)
95
+ s = val["s"]
96
+ begin
97
+ FlowExpressionId.to_fei(s)
98
+ rescue Exception => e
99
+ raise "failed to decode FlowExpressionId out of '#{s}'"
100
+ end
101
+ end
102
+ end
103
+
104
+ #
105
+ # opening for tuning yaml persistence
106
+ #
107
+ class XmlRawExpression
108
+ def to_yaml_properties
109
+ l = super()
110
+ l.delete("@raw_representation")
111
+ return l
112
+ end
113
+ end
114
+ end
115
+
@@ -142,7 +142,10 @@ module OpenWFE
142
142
 
143
143
  fei_path = compute_file_path(fei)
144
144
 
145
- return nil if not File.exist?(fei_path)
145
+ if not File.exist?(fei_path)
146
+ ldebug { "[] didn't find file at #{fei_path}" }
147
+ return nil
148
+ end
146
149
 
147
150
  load_object(fei_path)
148
151
  end
@@ -151,7 +154,7 @@ module OpenWFE
151
154
  # Returns the count of objects currently stored in this instance.
152
155
  #
153
156
  def length
154
- #return count_objects(0, @basepath)
157
+
155
158
  count_objects()
156
159
  end
157
160
 
@@ -161,8 +164,6 @@ module OpenWFE
161
164
 
162
165
  def load_object (path)
163
166
 
164
- #data = IO.read(path)
165
- #object = YAML.load(data)
166
167
  object = YAML.load_file(path)
167
168
 
168
169
  object.application_context = @application_context \
@@ -171,10 +172,18 @@ module OpenWFE
171
172
  object
172
173
  end
173
174
 
175
+ #
176
+ # Returns the number of 'objects' currently in this storage.
177
+ #
174
178
  def count_objects
179
+
175
180
  count = 0
181
+
176
182
  Find.find(@basepath) do |path|
183
+
184
+ next unless File.exist? path
177
185
  next if File.stat(path).directory?
186
+
178
187
  count += 1 if OpenWFE::ends_with(path, ".yaml")
179
188
  end
180
189
  return count
@@ -184,11 +193,16 @@ module OpenWFE
184
193
  # Passes each object path to the given block
185
194
  #
186
195
  def each_object_path (&block)
196
+
187
197
  return unless block
198
+
188
199
  synchronize do
189
200
  Find.find(@basepath) do |path|
201
+
202
+ next unless File.exist? path
190
203
  next if File.stat(path).directory?
191
204
  next unless OpenWFE::ends_with(path, ".yaml")
205
+
192
206
  ldebug { "each_object_path() considering #{path}" }
193
207
  block.call path
194
208
  end
@@ -212,6 +226,11 @@ module OpenWFE
212
226
 
213
227
  protected
214
228
 
229
+ #
230
+ # Each object is meant to have a unique file path,
231
+ # this method wraps the determination of that path. It has to
232
+ # be provided by extending classes.
233
+ #
215
234
  def compute_file_path (object)
216
235
  raise NotImplementedError.new
217
236
  end
@@ -107,6 +107,27 @@ module OpenWFE
107
107
  # # will yield "Henry" who takes care of all the politics stuff,
108
108
  # # except for Asia and America
109
109
  #
110
+ # '>', '>=', '<' and '<=' can be put in front of individual cell values :
111
+ #
112
+ # table = CsvTable.new("""
113
+ # ,
114
+ # in:fx, out:fy
115
+ # ,
116
+ # >100,a
117
+ # >=10,b
118
+ # ,c
119
+ # """)
120
+ #
121
+ # h = { 'fx' => '10' }
122
+ # table.transform(h)
123
+ #
124
+ # require 'pp'; pp h
125
+ # # will yield { 'fx' => '10', 'fy' => 'b' }
126
+ #
127
+ # Such comparisons are done after the elements are transformed to float
128
+ # numbers. By default, non-numeric arguments will get compared as Strings.
129
+ #
130
+ #
110
131
  # Disclaimer : the decision / CSV table system is no replacement for
111
132
  # full rule engines with forward and backward chaining, RETE implementation
112
133
  # and the like...
@@ -229,13 +250,15 @@ module OpenWFE
229
250
 
230
251
  cell = OpenWFE::dosub(cell, fexp, wi)
231
252
 
232
- modifiers = 0
233
- modifiers += Regexp::IGNORECASE if @ignore_case
234
-
235
253
  #puts "__does '#{value}' match '#{cell}' ?"
236
254
 
237
- rcell = Regexp.new(cell, modifiers)
238
- return false unless rcell.match(value)
255
+ b = if cell[0, 1] == '<' or cell[0, 1] == '>'
256
+ numeric_compare(value, cell)
257
+ else
258
+ regex_compare(value, cell)
259
+ end
260
+
261
+ return false unless b
239
262
  end
240
263
 
241
264
  #puts "__row matches"
@@ -243,6 +266,52 @@ module OpenWFE
243
266
  return true
244
267
  end
245
268
 
269
+ def regex_compare (value, cell)
270
+
271
+ modifiers = 0
272
+ modifiers += Regexp::IGNORECASE if @ignore_case
273
+
274
+ rcell = Regexp.new(cell, modifiers)
275
+
276
+ rcell.match(value)
277
+ end
278
+
279
+ def numeric_compare (value, cell)
280
+
281
+ comparator = cell[0, 1]
282
+ comparator += "=" if cell[1, 1] == "="
283
+ cell = cell[comparator.length..-1]
284
+
285
+ nvalue = narrow(value)
286
+ ncell = narrow(cell)
287
+
288
+ if nvalue.is_a? String or ncell.is_a? String
289
+ value = '"' + value + '"'
290
+ cell = '"' + cell + '"'
291
+ else
292
+ value = nvalue
293
+ cell = ncell
294
+ end
295
+
296
+ s = "#{value} #{comparator} #{cell}"
297
+
298
+ #puts "...>>>#{s}<<<"
299
+
300
+ begin
301
+ return OpenWFE::eval_safely(s, 4)
302
+ rescue Exception => e
303
+ return false
304
+ end
305
+ end
306
+
307
+ def narrow (s)
308
+ begin
309
+ return Float(s)
310
+ rescue Exception => e
311
+ return s
312
+ end
313
+ end
314
+
246
315
  def resolve_in_header (in_header)
247
316
 
248
317
  in_header = "f:#{in_header}" \
@@ -279,7 +348,8 @@ module OpenWFE
279
348
  elsif type == "f"
280
349
  wi.set_attribute(target, value)
281
350
  elsif type == "r"
282
- instance_eval(value)
351
+ #instance_eval(value)
352
+ OpenWFE::instance_eval_safely(self, value, 3)
283
353
  end
284
354
  end
285
355
  end
@@ -188,6 +188,11 @@ module OpenWFE
188
188
 
189
189
  def call_ruby (ruby_code)
190
190
 
191
+ if @flow_expression
192
+ return "" \
193
+ if @flow_expression.ac[:ruby_eval_allowed] != true
194
+ end
195
+
191
196
  #binding = nil
192
197
  #binding = @flow_expression.get_binding if @flow_expression
193
198
  #eval(ruby_code, binding).to_s