openwferu-extras 0.9.12.863

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,151 @@
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
+ require 'base64'
44
+ require 'monitor'
45
+
46
+ require 'openwfe/service'
47
+ require 'openwfe/util/scheduler'
48
+ require 'openwfe/listeners/listener'
49
+
50
+ require 'openwfe/extras/util/sqs'
51
+
52
+
53
+ #
54
+ # some base listener implementations
55
+ #
56
+ module OpenWFE
57
+ module Extras
58
+
59
+ #
60
+ # Polls an Amazon SQS queue for workitems
61
+ #
62
+ # Workitems can be instances of InFlowWorkItem or LaunchItem.
63
+ #
64
+ # require 'openwfe/extras/listeners/sqslisteners'
65
+ #
66
+ # ql = OpenWFE::SqsListener("workqueue1", engine.application_context)
67
+ #
68
+ # engine.add_workitem_listener(ql, "2m30s")
69
+ # #
70
+ # # thus, the engine will poll our "workqueue1" SQS queue
71
+ # # every 2 minutes and 30 seconds
72
+ #
73
+ class SqsListener < Service
74
+ include MonitorMixin
75
+ include WorkItemListener
76
+ include Schedulable
77
+
78
+ #
79
+ # The name of the Amazon SQS whom this listener cares for
80
+ #
81
+ attr_reader :queue_name
82
+
83
+ def initialize (queue_name, application_context)
84
+
85
+ @queue_name = queue_name.to_s
86
+
87
+ service_name = "#{self.class}::#{@queue_name}"
88
+
89
+ super(service_name, application_context)
90
+
91
+ linfo { "new() queue is '#{@queue_name}'" }
92
+ end
93
+
94
+ #
95
+ # Will 'find' files in the work directory (by default ./work/in/),
96
+ # extract the workitem in them and feed it back to the engine.
97
+ #
98
+ def trigger (params)
99
+ synchronize do
100
+
101
+ ldebug { "trigger()" }
102
+
103
+ qs = SQS::QueueService.new
104
+
105
+ qs.create_queue(@queue_name)
106
+ # just to be sure it is there
107
+
108
+ while true
109
+
110
+ l = qs.get_messages(
111
+ @queue_name, :timeout => 0, :count => 255)
112
+
113
+ break if l.length < 1
114
+
115
+ l.each do |msg|
116
+
117
+ o = decode_object(msg)
118
+
119
+ handle_object(o)
120
+
121
+ msg.delete
122
+
123
+ ldebug do
124
+ "trigger() " +
125
+ "handled successfully msg #{msg.message_id}"
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ #
133
+ # Extracts a workitem from the message's body.
134
+ #
135
+ # By default, this listeners assumes the workitem is stored in
136
+ # its "hash form" (not directly as a Ruby InFlowWorkItem instance).
137
+ #
138
+ # LaunchItem instances (as hash as well) are also accepted.
139
+ #
140
+ def decode_object (message)
141
+
142
+ o = Base64.decode64(message.message_body)
143
+ o = YAML.load(o)
144
+ o = OpenWFE::workitem_from_h(o)
145
+ o
146
+ end
147
+ end
148
+
149
+ end
150
+ end
151
+
@@ -0,0 +1,334 @@
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
+
34
+ #
35
+ # "made in Japan"
36
+ #
37
+ # John Mettraux at openwfe.org
38
+ #
39
+
40
+ require 'rubygems'
41
+ require_gem 'activerecord'
42
+
43
+
44
+ require 'openwfe/workitem'
45
+ require 'openwfe/flowexpressionid'
46
+ require 'openwfe/participants/participant'
47
+
48
+
49
+ module OpenWFE
50
+ module Extras
51
+
52
+ #
53
+ # The migration for ActiveParticipant and associated classes.
54
+ #
55
+ # There are two tables 'workitems' and 'fields'. As its name implies,
56
+ # the latter table stores the fields (also called attributes in OpenWFE
57
+ # speak) of the workitems.
58
+ #
59
+ # See Workitem and Field for more details.
60
+ #
61
+ # For centralization purposes, the migration and the model are located
62
+ # in the same source file. It should be quite easy for the Rails hackers
63
+ # among you to sort that out for a Rails based usage.
64
+ #
65
+ class OwfeTables < ActiveRecord::Migration
66
+ def self.up
67
+ create_table :workitems do |t|
68
+ t.column :fei, :string
69
+ t.column :wfid, :string
70
+ t.column :wf_name, :string
71
+ t.column :wf_revision, :string
72
+ t.column :participant_name, :string
73
+ t.column :store, :string
74
+ end
75
+ create_table :fields do |t|
76
+ t.column :key, :string, :null => false
77
+ t.column :value, :text
78
+ t.column :workitem_id, :integer, :null => false
79
+ end
80
+ add_index :fields, [ :workitem_id, :key], :unique => true
81
+ end
82
+ def self.down
83
+ drop_table :workitems
84
+ drop_table :fields
85
+ end
86
+ end
87
+
88
+ #
89
+ # The ActiveRecord version of an OpenWFEru workitem (InFlowWorkItem).
90
+ #
91
+ # One can very easily build a worklist based on a participant name via :
92
+ #
93
+ # wl = OpenWFE::Extras::Workitem.find_all_by_participant_name("toto")
94
+ # puts "found #{wl.size} workitems for participant 'toto'"
95
+ #
96
+ # These workitems are not OpenWFEru workitems directly. But the conversion
97
+ # is pretty easy.
98
+ # Note that you probaly won't need to do the conversion by yourself,
99
+ # except for certain advanced scenarii.
100
+ #
101
+ # awi = OpenWFE::Extras::Workitem.find_by_participant_name("toto")
102
+ # #
103
+ # # returns the first workitem in the database whose participant
104
+ # # name is 'toto'.
105
+ #
106
+ # owi = awi.as_owfe_workitem
107
+ # #
108
+ # # Now we have a copy of the reference as a OpenWFEru
109
+ # # InFlowWorkItem instance.
110
+ #
111
+ # awi = OpenWFE::Extras::Workitem.from_owfe_workitem(owi)
112
+ # #
113
+ # # turns an OpenWFEru InFlowWorkItem instance into an
114
+ # # 'active workitem'.
115
+ #
116
+ class Workitem < ActiveRecord::Base
117
+
118
+ has_many :fields, :dependent => :destroy
119
+
120
+ #
121
+ # Returns the flow expression id of this work (its unique OpenWFEru
122
+ # identifier) as a FlowExpressionId instance.
123
+ # (within the Workitem it's just stored as a String).
124
+ #
125
+ def full_fei
126
+
127
+ OpenWFE::FlowExpressionId.from_s(fei)
128
+ end
129
+
130
+ #
131
+ # Generates a (new) Workitem from an OpenWFEru InFlowWorkItem instance.
132
+ #
133
+ # This is a 'static' method :
134
+ #
135
+ # awi = OpenWFE::Densha::Workitem.from_owfe_workitem(wi)
136
+ #
137
+ # (This method will not save the 'ActiveWorkitem').
138
+ #
139
+ def self.from_owfe_workitem (wi)
140
+
141
+ i = Workitem.new
142
+ i.fei = wi.fei.to_s
143
+ i.wfid = wi.fei.wfid
144
+ i.wf_name = wi.fei.workflow_definition_name
145
+ i.wf_revision = wi.fei.workflow_definition_revision
146
+ i.participant_name = wi.participant_name
147
+
148
+ wi.attributes.each do |k, v|
149
+ f = Field.new
150
+ f.key = k
151
+ f.value = v
152
+ i.fields << f
153
+ end
154
+ i
155
+ end
156
+
157
+ #
158
+ # Turns the densha Workitem into an OpenWFEru InFlowWorkItem.
159
+ #
160
+ def as_owfe_workitem
161
+
162
+ wi = OpenWFE::InFlowWorkItem.new
163
+ wi.fei = full_fei
164
+ wi.participant_name = participant_name
165
+ wi.attributes = fields_hash
166
+ wi
167
+ end
168
+
169
+ #
170
+ # Returns a hash version of the 'fields' of this workitem.
171
+ #
172
+ # (Each time this method is called, it returns a new hash).
173
+ #
174
+ def fields_hash
175
+
176
+ h = {}
177
+ fields.each do |f|
178
+ h[f.key] = f.value
179
+ end
180
+ h
181
+ end
182
+
183
+ #
184
+ # Returns the Field instance with the given key. This method accept
185
+ # symbols as well as strings as its parameter.
186
+ #
187
+ # wi.field("customer_name")
188
+ # wi.field :customer_name
189
+ #
190
+ def field (key)
191
+
192
+ fields.find_by_key(key.to_s)
193
+ end
194
+
195
+ #
196
+ # A shortcut method, replies to the workflow engine and removes self
197
+ # from the database.
198
+ # Handy for people who don't want to play with an ActiveParticipant
199
+ # instance when just consuming workitems (that an active participant
200
+ # pushed in the database).
201
+ #
202
+ def reply (engine)
203
+
204
+ engine.reply self.as_owfe_workitem
205
+ self.destroy
206
+ end
207
+
208
+ alias :forward :reply
209
+ alias :proceed :reply
210
+ end
211
+
212
+ #
213
+ # A Field (Attribute) of a Workitem.
214
+ #
215
+ class Field < ActiveRecord::Base
216
+
217
+ belongs_to :workitem
218
+ serialize :value
219
+
220
+ #
221
+ # A quick method for doing
222
+ #
223
+ # f = Field.new
224
+ # f.key = key
225
+ # f.value = value
226
+ #
227
+ # One can then quickly add fields to an [active] workitem via :
228
+ #
229
+ # wi.fields << Field.new_field("toto", "b")
230
+ #
231
+ def self.new_field (key, value)
232
+ f = Field.new
233
+ f.key = key
234
+ f.value = value
235
+ f
236
+ end
237
+ end
238
+
239
+
240
+ #
241
+ # A basic 'ActiveParticipant'.
242
+ # A store participant whose store is a set of ActiveRecord tables.
243
+ #
244
+ # Sample usage :
245
+ #
246
+ # class MyDefinition < OpenWFE::ProcessDefinition
247
+ # sequence do
248
+ # active0
249
+ # active1
250
+ # end
251
+ # end
252
+ #
253
+ # def play_with_the_engine
254
+ #
255
+ # engine = OpenWFE::Engine.new
256
+ #
257
+ # engine.register_participant(
258
+ # :active0, OpenWFE::Extras::ActiveParticipant)
259
+ # engine.register_participant(
260
+ # :active1, OpenWFE::Extras::ActiveParticipant)
261
+ #
262
+ # li = OpenWFE::LaunchItem.new(MyDefinition)
263
+ # li.customer_name = 'toto'
264
+ # engine.launch li
265
+ #
266
+ # sleep 0.500
267
+ # # give some slack to the engine, it's asynchronous after all
268
+ #
269
+ # wi = OpenWFE::Extras::Workitem.find_by_participant_name("active0")
270
+ #
271
+ # # ...
272
+ # end
273
+ #
274
+ class ActiveParticipant
275
+ include OpenWFE::LocalParticipant
276
+
277
+ #
278
+ # This is the method called by the OpenWFEru engine to hand a
279
+ # workitem to this participant.
280
+ #
281
+ def consume (workitem)
282
+
283
+ awi = Workitem.from_owfe_workitem(workitem)
284
+ #
285
+ # turns the workitem into an 'active' one
286
+
287
+ awi.save
288
+ #
289
+ # and saves it in the db.
290
+ end
291
+
292
+ #
293
+ # Called by the engine when the whole process instance (or just the
294
+ # segment of it that sports this participant) is cancelled.
295
+ # Will removed the workitem with the same fei as the cancelitem
296
+ # from the database.
297
+ #
298
+ # No expression will be raised if there is no corresponding workitem.
299
+ #
300
+ def cancel (cancelitem)
301
+
302
+ Workitem.delete_all([ "fei = ?", cancelitem.fei.to_s ])
303
+ end
304
+
305
+ #
306
+ # When the activity/work/operation whatever is over and the flow
307
+ # should resume, this is the method to use to hand back the [modified]
308
+ # workitem to the [local] engine.
309
+ #
310
+ def reply_to_engine (workitem)
311
+
312
+ super workitem.as_owfe_workitem
313
+ #
314
+ # replies to the workflow engine
315
+
316
+ workitem.destroy
317
+ #
318
+ # removes the workitem from the database
319
+ end
320
+ end
321
+
322
+ #
323
+ # TODO : please document me
324
+ #
325
+ class ActiveStoreParticipant < ActiveParticipant
326
+ include Enumerable
327
+
328
+ def each (&block)
329
+ end
330
+ end
331
+
332
+ end
333
+ end
334
+