ruote-extras 0.9.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,100 @@
1
+ #
2
+ #--
3
+ # Copyright (c) 2007, Tomaso Tosolini and 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
+
34
+ #
35
+ # "made in Italy"
36
+ #
37
+ # Tomaso Tosolini
38
+ # John Mettraux at openwfe.org
39
+ #
40
+
41
+ require 'openwfe/engine/engine'
42
+ require 'openwfe/extras/expool/dbexpstorage'
43
+ require 'openwfe/extras/expool/dberrorjournal'
44
+
45
+
46
+ module OpenWFE::Extras
47
+
48
+ #
49
+ # A simple DbPersistedEngine, pure storage, no caching, no optimization.
50
+ # For tests only.
51
+ #
52
+ class DbPersistedEngine < OpenWFE::Engine
53
+
54
+ protected
55
+
56
+ #
57
+ # Overrides the method already found in Engine with a persisted
58
+ # expression storage
59
+ #
60
+ def build_expression_storage
61
+
62
+ init_service OpenWFE::S_EXPRESSION_STORAGE, DbExpressionStorage
63
+ end
64
+
65
+ #
66
+ # Uses a file persisted error journal.
67
+ #
68
+ def build_error_journal
69
+
70
+ init_service OpenWFE::S_ERROR_JOURNAL, DbErrorJournal
71
+ end
72
+ end
73
+
74
+ #
75
+ # This OpenWFEru engine features database persistence (thanks to
76
+ # ActiveRecord), with a cache (for faster read operations) and a
77
+ # threaded wrapper (for buffering out unecessary write operations),
78
+ # hence it's fast (of course its's slower than in-memory storage.
79
+ #
80
+ class CachedDbPersistedEngine < DbPersistedEngine
81
+
82
+ protected
83
+
84
+ def build_expression_storage ()
85
+
86
+ @application_context[:expression_cache_size] ||= 1000
87
+
88
+ init_service(
89
+ OpenWFE::S_EXPRESSION_STORAGE,
90
+ OpenWFE::CacheExpressionStorage)
91
+
92
+ #init_service(
93
+ # S_EXPRESSION_STORAGE + ".1",
94
+ # DbExpressionStorage)
95
+ init_service(
96
+ OpenWFE::S_EXPRESSION_STORAGE + ".1",
97
+ ThreadedDbExpressionStorage)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,189 @@
1
+ #
2
+ #--
3
+ # Copyright (c) 2007-2008, Tomaso Tosolini, 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
+
34
+ #
35
+ # "made in Italy"
36
+ #
37
+ # Tomaso Tosolini
38
+ # John Mettraux
39
+ #
40
+
41
+ #require 'rubygems'
42
+
43
+ #require_gem 'activerecord'
44
+ gem 'activerecord'; require 'active_record'
45
+
46
+ require 'openwfe/omixins'
47
+ require 'openwfe/expool/errorjournal'
48
+
49
+
50
+ module OpenWFE::Extras
51
+
52
+ #
53
+ # A migration for creating/dropping the "process errors" table, the
54
+ # content of this table makes up for an error journal.
55
+ #
56
+ # There is one record per process error, the log journal can be
57
+ # easily rebuilt by doing find_all_by_wfid().
58
+ #
59
+ class ProcessErrorTables < ActiveRecord::Migration
60
+
61
+ def self.up
62
+
63
+ create_table :process_errors do |t|
64
+
65
+ t.column :wfid, :string, :null => false
66
+ t.column :svalue, :text, :null => false
67
+ # 'value' could be reserved, using 'svalue' instead
68
+ # It stands for 'serialized value'.
69
+ end
70
+ add_index :process_errors, :wfid
71
+ end
72
+
73
+ def self.down
74
+
75
+ drop_table :process_errors
76
+ end
77
+ end
78
+
79
+ #
80
+ # The active record for process errors. Not much to say.
81
+ #
82
+ class ProcessError < ActiveRecord::Base
83
+
84
+ serialize :svalue
85
+
86
+ #
87
+ # Returns the OpenWFE process error, as serialized
88
+ # (but takes care of setting its db_id)
89
+ #
90
+ def owfe_error
91
+
92
+ result = svalue
93
+ class << result
94
+ attr_accessor :db_id
95
+ end
96
+ result.db_id = id
97
+ result
98
+ end
99
+ end
100
+
101
+ #
102
+ # A database backed error journal.
103
+ #
104
+ # (no synchronization needed it seems)
105
+ #
106
+ class DbErrorJournal < OpenWFE::ErrorJournal
107
+ include OpenWFE::FeiMixin
108
+
109
+ def initialize (service_name, application_context)
110
+
111
+ require 'openwfe/storage/yamlcustom'
112
+ # making sure this file has been required at this point
113
+ # this yamlcustom thing prevents the whole OpenWFE ecosystem
114
+ # to get serialized :)
115
+
116
+ super
117
+ end
118
+
119
+ #
120
+ # Returns the error log for a given workflow/process instance,
121
+ # the older error first.
122
+ #
123
+ def get_error_log (wfid)
124
+
125
+ wfid = extract_wfid wfid, true
126
+ errors = ProcessError.find_all_by_wfid wfid, :order => "id asc"
127
+ errors.collect { |e| e.owfe_error }
128
+ end
129
+
130
+ #
131
+ # Erases all the errors for one given workflow/process instance.
132
+ #
133
+ def remove_error_log (wfid)
134
+
135
+ ProcessError.destroy_all ["wfid = ?", wfid]
136
+ end
137
+
138
+ #
139
+ # Returns a map wfid => error log, ie returns 1 error log for
140
+ # each workflow/process instance that encountered an error.
141
+ #
142
+ def get_error_logs
143
+
144
+ errors = ProcessError.find :all
145
+
146
+ result = {}
147
+
148
+ errors.each do |e|
149
+ (result[e.wfid] ||= []) << e.owfe_error
150
+ end
151
+
152
+ result
153
+ end
154
+
155
+ #
156
+ # Removes a set of errors. This is used by the expool when
157
+ # resuming a previously broken process instance.
158
+ #
159
+ def remove_errors (wfid, errors)
160
+
161
+ errors = Array(errors)
162
+
163
+ errors.each do |e|
164
+ ProcessError.delete e.db_id
165
+ end
166
+ end
167
+
168
+ protected
169
+
170
+ #
171
+ # This is the inner method used by the error journal to
172
+ # record a process error (instance of OpenWFE::ProcessError)
173
+ # that it observed in the expression pool.
174
+ #
175
+ # This method will throw an exception in case of trouble with
176
+ # the database.
177
+ #
178
+ def record_error (process_error)
179
+
180
+ e = ProcessError.new
181
+
182
+ e.wfid = process_error.wfid
183
+ e.svalue = process_error
184
+
185
+ e.save!
186
+ end
187
+ end
188
+ end
189
+
@@ -0,0 +1,353 @@
1
+ #
2
+ #--
3
+ # Copyright (c) 2007-2008, Tomaso Tosolini, 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
+
34
+ #
35
+ # "made in Italy"
36
+ #
37
+ # Tomaso Tosolini
38
+ # John Mettraux
39
+ #
40
+
41
+ #require 'rubygems'
42
+
43
+ #require_gem 'activerecord'
44
+ gem 'activerecord'; require 'active_record'
45
+
46
+ require 'monitor'
47
+
48
+ require 'openwfe/service'
49
+ require 'openwfe/rudefinitions'
50
+ require 'openwfe/expool/expstorage'
51
+ require 'openwfe/expool/threadedexpstorage'
52
+
53
+
54
+ module OpenWFE::Extras
55
+
56
+ #
57
+ # A migration for creating/dropping the "expressions" table.
58
+ # 'expressions' are atomic pieces of running process instances.
59
+ #
60
+ class ExpressionTables < ActiveRecord::Migration
61
+
62
+ def self.up
63
+
64
+ create_table :expressions do |t|
65
+
66
+ t.column :fei, :string, :null => false
67
+ t.column :wfid, :string, :null => false
68
+ #t.column :wfname, :string, :null => false
69
+ t.column :exp_class, :string, :null => false
70
+
71
+ #t.column :svalue, :text, :null => false
72
+ t.column :svalue, :text, :null => false, :limit => 1024 * 1024
73
+ #
74
+ # 'value' could be reserved, using 'svalue' instead
75
+ #
76
+ # :limit patch by Maarten Oelering (a greater value
77
+ # could be required in some cases)
78
+ end
79
+ add_index :expressions, :fei
80
+ add_index :expressions, :wfid
81
+ #add_index :expressions, :wfname
82
+ add_index :expressions, :exp_class
83
+ end
84
+
85
+ def self.down
86
+
87
+ drop_table :expressions
88
+ end
89
+ end
90
+
91
+ #
92
+ # The ActiveRecord wrapper for an OpenWFEru FlowExpression instance.
93
+ #
94
+ class Expression < ActiveRecord::Base
95
+
96
+ serialize :svalue
97
+ end
98
+
99
+ #
100
+ # Storing OpenWFE flow expressions in a database.
101
+ #
102
+ class DbExpressionStorage
103
+ include MonitorMixin
104
+ include OpenWFE::ServiceMixin
105
+ include OpenWFE::OwfeServiceLocator
106
+ include OpenWFE::ExpressionStorageBase
107
+
108
+ #
109
+ # Constructor.
110
+ #
111
+ def initialize (service_name, application_context)
112
+
113
+ require 'openwfe/storage/yamlcustom'
114
+ # making sure this file has been required at this point
115
+ # this yamlcustom thing prevents the whole OpenWFE ecosystem
116
+ # to get serialized :)
117
+
118
+ super() # absolutely necessary as we include MonitorMixin
119
+ service_init service_name, application_context
120
+
121
+ observe_expool
122
+ end
123
+
124
+ #
125
+ # Stores an expression.
126
+ #
127
+ def []= (fei, flow_expression)
128
+
129
+ ldebug { "[]= storing #{fei.to_s}" }
130
+
131
+ synchronize do
132
+
133
+ e = Expression.find_by_fei fei.to_s
134
+
135
+ unless e
136
+ e = Expression.new
137
+ e.fei = fei.to_s
138
+ e.wfid = fei.wfid
139
+ #e.wfname = fei.wfname
140
+ end
141
+
142
+ e.exp_class = flow_expression.class.name
143
+ e.svalue = flow_expression
144
+
145
+ e.save!
146
+ end
147
+ end
148
+
149
+ #
150
+ # Retrieves a flow expression.
151
+ #
152
+ def [] (fei)
153
+
154
+ e = Expression.find_by_fei fei.to_s
155
+ return nil unless e
156
+
157
+ as_owfe_expression e
158
+ end
159
+
160
+ #
161
+ # Returns true if there is a FlowExpression stored with the given id.
162
+ #
163
+ def has_key? (fei)
164
+
165
+ (Expression.find_by_fei(fei.to_s) != nil)
166
+ end
167
+
168
+ #
169
+ # Deletes a flow expression.
170
+ #
171
+ def delete (fei)
172
+
173
+ synchronize do
174
+ Expression.delete_all ["fei = ?", fei.to_s]
175
+ end
176
+ end
177
+
178
+ #
179
+ # Returns the count of expressions currently stored.
180
+ #
181
+ def size
182
+
183
+ Expression.count
184
+ end
185
+
186
+ alias :length :size
187
+
188
+ #
189
+ # Danger ! Will remove all the expressions in the database.
190
+ #
191
+ def purge
192
+
193
+ Expression.delete_all
194
+ end
195
+
196
+ #
197
+ # Gather expressions matching certain parameters.
198
+ #
199
+ def find_expressions (options={})
200
+
201
+ conditions = determine_conditions options
202
+ # note : this call modifies the options hash...
203
+
204
+ #
205
+ # maximize usage of SQL querying
206
+
207
+ exps = Expression.find :all, :conditions => conditions
208
+
209
+ #
210
+ # do the rest of the filtering
211
+
212
+ exps = exps.collect do |exp|
213
+ as_owfe_expression exp
214
+ end
215
+
216
+ exps.find_all do |fexp|
217
+ does_match? options, fexp
218
+ end
219
+ end
220
+
221
+ #
222
+ # Fetches the root of a process instance.
223
+ #
224
+ def fetch_root (wfid)
225
+
226
+ params = {}
227
+
228
+ params[:conditions] = [
229
+ "wfid = ? AND exp_class = ?",
230
+ wfid,
231
+ OpenWFE::DefineExpression.to_s
232
+ ]
233
+
234
+ exps = Expression.find(:all, params)
235
+
236
+ e = exps.sort { |fe1, fe2| fe1.fei.expid <=> fe2.fei.expid }[0]
237
+ #
238
+ # find the one with the smallest expid
239
+
240
+ as_owfe_expression e
241
+ end
242
+
243
+ protected
244
+
245
+ #
246
+ # Grabs the options to build a conditions array for use by
247
+ # find().
248
+ #
249
+ # Note : this method, modifies the options hash (it removes
250
+ # the args it needs).
251
+ #
252
+ def determine_conditions (options)
253
+
254
+ wfid = options.delete :wfid
255
+ wfid_prefix = options.delete :wfid_prefix
256
+ #parent_wfid = options.delete :parent_wfid
257
+
258
+ query = []
259
+ conditions = []
260
+
261
+ if wfid
262
+ query << "wfid = ?"
263
+ conditions << wfid
264
+ elsif wfid_prefix
265
+ query << "wfid LIKE ?"
266
+ conditions << "#{wfid_prefix}%"
267
+ end
268
+
269
+ add_class_conditions options, query, conditions
270
+
271
+ conditions = conditions.flatten
272
+
273
+ if conditions.size < 1
274
+ nil
275
+ else
276
+ conditions.insert 0, query.join(" AND ")
277
+ end
278
+ end
279
+
280
+ #
281
+ # Used by determine_conditions().
282
+ #
283
+ def add_class_conditions (options, query, conditions)
284
+
285
+ ic = options.delete :include_classes
286
+ ic = Array(ic)
287
+
288
+ ec = options.delete :exclude_classes
289
+ ec = Array(ec)
290
+
291
+ acc ic, query, conditions, "OR"
292
+ acc ec, query, conditions, "AND"
293
+ end
294
+
295
+ def acc (classes, query, conditions, join)
296
+
297
+ return if classes.size < 1
298
+
299
+ classes = classes.collect do |kind|
300
+ get_expression_map.get_expression_classes kind
301
+ end
302
+ classes = classes.flatten
303
+
304
+ quer = []
305
+ cond = []
306
+ classes.each do |cl|
307
+
308
+ quer << if join == "AND"
309
+ "exp_class != ?"
310
+ else
311
+ "exp_class = ?"
312
+ end
313
+
314
+ cond << cl.to_s
315
+ end
316
+ quer = quer.join " #{join} "
317
+
318
+ query << "(#{quer})"
319
+ conditions << cond
320
+ end
321
+
322
+ #
323
+ # Extracts the OpenWFE FlowExpression instance from the
324
+ # active record and makes sure its application_context is set.
325
+ #
326
+ def as_owfe_expression (record)
327
+
328
+ return nil unless record
329
+
330
+ fe = record.svalue
331
+ fe.application_context = @application_context
332
+ fe
333
+ end
334
+ end
335
+
336
+ #
337
+ # A DbExpressionStorage that does less work, for more performance,
338
+ # thanks to the ThreadedStorageMixin.
339
+ #
340
+ class ThreadedDbExpressionStorage < DbExpressionStorage
341
+ include OpenWFE::ThreadedStorageMixin
342
+
343
+ def initialize (service_name, application_context)
344
+
345
+ super
346
+
347
+ start_queue
348
+ #
349
+ # which sets @thread_id
350
+ end
351
+ end
352
+ end
353
+