ruote-extras 0.9.18

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+