ruote 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.
- data/README.txt +24 -0
- data/bin/validate-workflow.rb +89 -0
- data/examples/about_state.rb +81 -0
- data/examples/bigflow.rb +19 -0
- data/examples/csv_weather.rb +23 -0
- data/examples/engine_template.rb +247 -0
- data/examples/flowtracing.rb +24 -0
- data/examples/homeworkreview.rb +68 -0
- data/examples/kotoba.rb +22 -0
- data/examples/mano_tracker.rb +172 -0
- data/examples/openwferu.rb +58 -0
- data/examples/quotereporter.rb +157 -0
- data/examples/scheduler_cron_usage.rb +48 -0
- data/examples/scheduler_usage.rb +56 -0
- data/lib/openwfe.rb +41 -0
- data/lib/openwfe/contextual.rb +111 -0
- data/lib/openwfe/def.rb +46 -0
- data/lib/openwfe/engine.rb +37 -0
- data/lib/openwfe/engine/engine.rb +756 -0
- data/lib/openwfe/engine/expool_methods.rb +172 -0
- data/lib/openwfe/engine/file_persisted_engine.rb +105 -0
- data/lib/openwfe/engine/participant_methods.rb +133 -0
- data/lib/openwfe/engine/status_methods.rb +353 -0
- data/lib/openwfe/engine/update_exp_methods.rb +112 -0
- data/lib/openwfe/exceptions.rb +51 -0
- data/lib/openwfe/expool/errorjournal.rb +476 -0
- data/lib/openwfe/expool/expressionpool.rb +1144 -0
- data/lib/openwfe/expool/expstorage.rb +403 -0
- data/lib/openwfe/expool/history.rb +174 -0
- data/lib/openwfe/expool/journal.rb +224 -0
- data/lib/openwfe/expool/journal_replay.rb +321 -0
- data/lib/openwfe/expool/parser.rb +242 -0
- data/lib/openwfe/expool/representation.rb +121 -0
- data/lib/openwfe/expool/threadedexpstorage.rb +188 -0
- data/lib/openwfe/expool/wfidgen.rb +388 -0
- data/lib/openwfe/expool/yamlexpstorage.rb +224 -0
- data/lib/openwfe/expressions/condition.rb +244 -0
- data/lib/openwfe/expressions/environment.rb +246 -0
- data/lib/openwfe/expressions/expressionmap.rb +258 -0
- data/lib/openwfe/expressions/fe_cancel.rb +109 -0
- data/lib/openwfe/expressions/fe_command.rb +241 -0
- data/lib/openwfe/expressions/fe_concurrence.rb +662 -0
- data/lib/openwfe/expressions/fe_cron.rb +259 -0
- data/lib/openwfe/expressions/fe_cursor.rb +259 -0
- data/lib/openwfe/expressions/fe_define.rb +192 -0
- data/lib/openwfe/expressions/fe_do.rb +168 -0
- data/lib/openwfe/expressions/fe_equals.rb +291 -0
- data/lib/openwfe/expressions/fe_filter.rb +129 -0
- data/lib/openwfe/expressions/fe_filter_definition.rb +168 -0
- data/lib/openwfe/expressions/fe_fqv.rb +250 -0
- data/lib/openwfe/expressions/fe_if.rb +303 -0
- data/lib/openwfe/expressions/fe_iterator.rb +145 -0
- data/lib/openwfe/expressions/fe_listen.rb +371 -0
- data/lib/openwfe/expressions/fe_losfor.rb +111 -0
- data/lib/openwfe/expressions/fe_misc.rb +421 -0
- data/lib/openwfe/expressions/fe_participant.rb +269 -0
- data/lib/openwfe/expressions/fe_reserve.rb +212 -0
- data/lib/openwfe/expressions/fe_save.rb +274 -0
- data/lib/openwfe/expressions/fe_sequence.rb +117 -0
- data/lib/openwfe/expressions/fe_set.rb +139 -0
- data/lib/openwfe/expressions/fe_sleep.rb +166 -0
- data/lib/openwfe/expressions/fe_step.rb +159 -0
- data/lib/openwfe/expressions/fe_subprocess.rb +168 -0
- data/lib/openwfe/expressions/fe_timeout.rb +127 -0
- data/lib/openwfe/expressions/fe_wait.rb +78 -0
- data/lib/openwfe/expressions/fe_when.rb +142 -0
- data/lib/openwfe/expressions/filter.rb +104 -0
- data/lib/openwfe/expressions/flowexpression.rb +847 -0
- data/lib/openwfe/expressions/iterator.rb +221 -0
- data/lib/openwfe/expressions/merge.rb +84 -0
- data/lib/openwfe/expressions/raw.rb +547 -0
- data/lib/openwfe/expressions/rprocdef.rb +375 -0
- data/lib/openwfe/expressions/time.rb +333 -0
- data/lib/openwfe/expressions/timeout.rb +178 -0
- data/lib/openwfe/expressions/value.rb +126 -0
- data/lib/openwfe/filterdef.rb +259 -0
- data/lib/openwfe/flowexpressionid.rb +357 -0
- data/lib/openwfe/listeners/listener.rb +97 -0
- data/lib/openwfe/listeners/listeners.rb +139 -0
- data/lib/openwfe/listeners/socketlisteners.rb +272 -0
- data/lib/openwfe/logging.rb +122 -0
- data/lib/openwfe/omixins.rb +95 -0
- data/lib/openwfe/orest/controlclient.rb +119 -0
- data/lib/openwfe/orest/definitions.rb +113 -0
- data/lib/openwfe/orest/exception.rb +60 -0
- data/lib/openwfe/orest/oldrestservlet.rb +279 -0
- data/lib/openwfe/orest/osocket.rb +148 -0
- data/lib/openwfe/orest/restclient.rb +176 -0
- data/lib/openwfe/orest/workitem.rb +206 -0
- data/lib/openwfe/orest/worklistclient.rb +272 -0
- data/lib/openwfe/orest/xmlcodec.rb +670 -0
- data/lib/openwfe/participants.rb +38 -0
- data/lib/openwfe/participants/enoparticipants.rb +230 -0
- data/lib/openwfe/participants/participant.rb +141 -0
- data/lib/openwfe/participants/participantmap.rb +249 -0
- data/lib/openwfe/participants/participants.rb +407 -0
- data/lib/openwfe/participants/soapparticipants.rb +135 -0
- data/lib/openwfe/participants/socketparticipants.rb +202 -0
- data/lib/openwfe/participants/storeparticipants.rb +254 -0
- data/lib/openwfe/rudefinitions.rb +130 -0
- data/lib/openwfe/service.rb +103 -0
- data/lib/openwfe/storage/yamlcustom.rb +106 -0
- data/lib/openwfe/storage/yamlfilestorage.rb +245 -0
- data/lib/openwfe/tools/flowtracer.rb +81 -0
- data/lib/openwfe/util/dollar.rb +217 -0
- data/lib/openwfe/util/irb.rb +86 -0
- data/lib/openwfe/util/observable.rb +144 -0
- data/lib/openwfe/util/ometa.rb +62 -0
- data/lib/openwfe/util/workqueue.rb +124 -0
- data/lib/openwfe/util/xml.rb +418 -0
- data/lib/openwfe/utils.rb +554 -0
- data/lib/openwfe/version.rb +37 -0
- data/lib/openwfe/workitem.rb +499 -0
- data/lib/openwfe/worklist/oldrest.rb +244 -0
- data/lib/openwfe/worklist/storelocks.rb +293 -0
- data/lib/openwfe/worklist/storeparticipant.rb +44 -0
- data/lib/openwfe/worklist/worklist.rb +297 -0
- data/test/README.txt +27 -0
- data/test/back_0916_test.rb +111 -0
- data/test/bm/bm_1_xml_vs_prog.rb +56 -0
- data/test/bm/bm_2_step.rb +109 -0
- data/test/bm/ft_0f_5ms.rb +35 -0
- data/test/bm/ft_26_load.rb +210 -0
- data/test/bm/ft_26b_load.rb +86 -0
- data/test/bm/ft_26c_load.rb +97 -0
- data/test/bm/ft_26d_load.rb +97 -0
- data/test/bm/ft_recu.rb +71 -0
- data/test/clone_test.rb +122 -0
- data/test/concurrence_test.rb +77 -0
- data/test/condition_test.rb +155 -0
- data/test/console_test.rb +12 -0
- data/test/cron_ltest.rb +15 -0
- data/test/description_test.rb +87 -0
- data/test/eno_test.rb +76 -0
- data/test/expmap_test.rb +54 -0
- data/test/expool_20031219_0916.tgz +0 -0
- data/test/fe_lookup_att_test.rb +62 -0
- data/test/fei_test.rb +181 -0
- data/test/file_persisted_engine_test.rb +64 -0
- data/test/file_persistence_test.rb +134 -0
- data/test/filep_cancel_test.rb +123 -0
- data/test/filter_test.rb +109 -0
- data/test/flowtestbase.rb +351 -0
- data/test/ft_0.rb +68 -0
- data/test/ft_0b_sequence.rb +36 -0
- data/test/ft_0c_testname.rb +33 -0
- data/test/ft_0d_participant.rb +30 -0
- data/test/ft_0e_multibody.rb +34 -0
- data/test/ft_10_loop.rb +134 -0
- data/test/ft_11_ppd.rb +415 -0
- data/test/ft_11b_ppd.rb +54 -0
- data/test/ft_12_blockparticipant.rb +97 -0
- data/test/ft_13_eno.rb +52 -0
- data/test/ft_14_subprocess.rb +88 -0
- data/test/ft_14b_subprocess.rb +192 -0
- data/test/ft_14c_subprocess.rb +68 -0
- data/test/ft_15_iterator.rb +216 -0
- data/test/ft_15b_iterator.rb +74 -0
- data/test/ft_16_fqv.rb +73 -0
- data/test/ft_17_condition.rb +84 -0
- data/test/ft_18_pname.rb +56 -0
- data/test/ft_1_unset.rb +175 -0
- data/test/ft_1b_unset.rb +39 -0
- data/test/ft_20_cron.rb +53 -0
- data/test/ft_21_cron.rb +87 -0
- data/test/ft_21b_cron_pause.rb +82 -0
- data/test/ft_22_history.rb +74 -0
- data/test/ft_23_when.rb +77 -0
- data/test/ft_23b_when.rb +70 -0
- data/test/ft_23c_wait.rb +80 -0
- data/test/ft_23d_cww.rb +58 -0
- data/test/ft_24_def.rb +44 -0
- data/test/ft_25_cancel.rb +89 -0
- data/test/ft_27_getflowpos.rb +147 -0
- data/test/ft_28_fileparticipant.rb +63 -0
- data/test/ft_29_httprb.rb +106 -0
- data/test/ft_2_concurrence.rb +135 -0
- data/test/ft_2b_concurrence.rb +188 -0
- data/test/ft_2c_concurrence.rb +64 -0
- data/test/ft_30_socketlistener.rb +203 -0
- data/test/ft_31_flowname.rb +40 -0
- data/test/ft_32_journal.rb +91 -0
- data/test/ft_32c_journal.rb +102 -0
- data/test/ft_32d_journal.rb +84 -0
- data/test/ft_33_description.rb +107 -0
- data/test/ft_34_cancelwfid.rb +80 -0
- data/test/ft_35_localdefs.rb +75 -0
- data/test/ft_36_subprocids.rb +97 -0
- data/test/ft_37_pnames.rb +70 -0
- data/test/ft_38_tag.rb +127 -0
- data/test/ft_38b_tag.rb +161 -0
- data/test/ft_38c_tag.rb +100 -0
- data/test/ft_39_reserve.rb +63 -0
- data/test/ft_39b_reserve.rb +84 -0
- data/test/ft_3_equals.rb +170 -0
- data/test/ft_3b_lookup_vf.rb +83 -0
- data/test/ft_40_defined.rb +61 -0
- data/test/ft_41_case.rb +110 -0
- data/test/ft_42_environments.rb +75 -0
- data/test/ft_43_pat10.rb +85 -0
- data/test/ft_44_save.rb +70 -0
- data/test/ft_44b_restore.rb +212 -0
- data/test/ft_45_citerator.rb +214 -0
- data/test/ft_46_pparams.rb +62 -0
- data/test/ft_47_filter.rb +160 -0
- data/test/ft_48_fe_filter.rb +88 -0
- data/test/ft_49_condition.rb +126 -0
- data/test/ft_4_misc.rb +237 -0
- data/test/ft_50_xml_attribute.rb +155 -0
- data/test/ft_51_stack.rb +55 -0
- data/test/ft_52_obs_participant.rb +123 -0
- data/test/ft_53_null_noop_participant.rb +62 -0
- data/test/ft_54_listen.rb +288 -0
- data/test/ft_54b_listen.rb +66 -0
- data/test/ft_54c_listen.rb +99 -0
- data/test/ft_55_ptimeout.rb +59 -0
- data/test/ft_56_timeout.rb +59 -0
- data/test/ft_57_a.rb +145 -0
- data/test/ft_58_ejournal.rb +151 -0
- data/test/ft_59_ps.rb +150 -0
- data/test/ft_59b_ps_for_pat.rb +58 -0
- data/test/ft_5_time.rb +118 -0
- data/test/ft_60_ecancel.rb +161 -0
- data/test/ft_61_elsub.rb +51 -0
- data/test/ft_62_procparticipant.rb +71 -0
- data/test/ft_63_pause.rb +124 -0
- data/test/ft_64_alias.rb +102 -0
- data/test/ft_64_clone.rb +69 -0
- data/test/ft_65_stringlaunch.rb +59 -0
- data/test/ft_66_subforget.rb +70 -0
- data/test/ft_67_schedlaunch.rb +116 -0
- data/test/ft_68_ifparticipant.rb +70 -0
- data/test/ft_69_cancelmissing.rb +51 -0
- data/test/ft_6_lambda.rb +64 -0
- data/test/ft_70_lookupvar.rb +55 -0
- data/test/ft_71_log.rb +60 -0
- data/test/ft_72_lookup_processes.rb +76 -0
- data/test/ft_73_cancel_sub.rb +139 -0
- data/test/ft_74_block_and_workitem_dup.rb +63 -0
- data/test/ft_75_ruby_attributes.rb +87 -0
- data/test/ft_76_merge_isolate.rb +88 -0
- data/test/ft_77_segments.rb +35 -0
- data/test/ft_78_eval.rb +150 -0
- data/test/ft_79_tticket.rb +187 -0
- data/test/ft_79b_tticket.rb +172 -0
- data/test/ft_79c_outcome.rb +56 -0
- data/test/ft_7_lose.rb +104 -0
- data/test/ft_7b_lose.rb +78 -0
- data/test/ft_80_spname.rb +91 -0
- data/test/ft_81_exp.rb +60 -0
- data/test/ft_82_trecu.rb +46 -0
- data/test/ft_83_badpause.rb +58 -0
- data/test/ft_84_updateexp.rb +198 -0
- data/test/ft_85_dolhash.rb +43 -0
- data/test/ft_86_dollar_fv.rb +68 -0
- data/test/ft_87_define.rb +74 -0
- data/test/ft_8_forget.rb +44 -0
- data/test/ft_9_cursor.rb +145 -0
- data/test/ft_9b_cursor.rb +105 -0
- data/test/ft_tests.rb +124 -0
- data/test/hash_test.rb +75 -0
- data/test/hparticipant_test.rb +164 -0
- data/test/lookup_att_test.rb +90 -0
- data/test/lookup_vf_test.rb +94 -0
- data/test/misc_test.rb +90 -0
- data/test/nut_0_irb.rb +20 -0
- data/test/obs_test.rb +142 -0
- data/test/orest_test.rb +251 -0
- data/test/param_test.rb +290 -0
- data/test/participant_test.rb +101 -0
- data/test/pending.rb +23 -0
- data/test/ps_representation.rb +133 -0
- data/test/rake_ltest.rb +38 -0
- data/test/rake_qtest.rb +68 -0
- data/test/raw_prog_test.rb +412 -0
- data/test/restart_cron_test.rb +136 -0
- data/test/restart_paused_test.rb +98 -0
- data/test/restart_sleep_test.rb +140 -0
- data/test/restart_tests.rb +18 -0
- data/test/restart_when_test.rb +112 -0
- data/test/ruby_procdef_test.rb +132 -0
- data/test/rutest_utils.rb +63 -0
- data/test/sec_test.rb +205 -0
- data/test/slock_test.rb +80 -0
- data/test/storage_test.rb +44 -0
- data/test/test.rb +3 -0
- data/test/timeout_test.rb +105 -0
- data/test/util_xml_test.rb +112 -0
- data/test/wfid_test.rb +175 -0
- data/test/wi_test.rb +75 -0
- metadata +433 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
|
|
2
|
+
#
|
|
3
|
+
# showing how to use the scheduler
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
require 'rubygems'
|
|
7
|
+
|
|
8
|
+
require 'time'
|
|
9
|
+
|
|
10
|
+
require 'openwfe/util/scheduler'
|
|
11
|
+
include OpenWFE
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def p (msg)
|
|
15
|
+
t = Time.new
|
|
16
|
+
puts "#{t.iso8601} -- #{msg}"
|
|
17
|
+
end
|
|
18
|
+
#
|
|
19
|
+
# a small method for displaying the time at the beginning
|
|
20
|
+
# of each output line
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
scheduler = Scheduler.new
|
|
24
|
+
scheduler.start
|
|
25
|
+
#
|
|
26
|
+
# create a scheduler instance and start it
|
|
27
|
+
|
|
28
|
+
p "started scheduler"
|
|
29
|
+
|
|
30
|
+
i = 0
|
|
31
|
+
|
|
32
|
+
scheduler.schedule("1-60 * * * *") do
|
|
33
|
+
p "minute ##{i}"
|
|
34
|
+
i = i + 1
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
scheduler.schedule_in("2m10s") do
|
|
38
|
+
p "after 2 minutes and 10 seconds stopping the scheduler and exiting..."
|
|
39
|
+
scheduler.stop
|
|
40
|
+
end
|
|
41
|
+
#
|
|
42
|
+
# using a regular "at" job to stop the scheduler after 4 minutes
|
|
43
|
+
|
|
44
|
+
scheduler.join
|
|
45
|
+
#
|
|
46
|
+
# align the thread of this program to the scheduler thread
|
|
47
|
+
# i.e. exit program only when scheduler terminates
|
|
48
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
|
|
2
|
+
#
|
|
3
|
+
# showing how to use the scheduler
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
require 'rubygems'
|
|
7
|
+
|
|
8
|
+
require 'time'
|
|
9
|
+
|
|
10
|
+
require 'openwfe/util/scheduler'
|
|
11
|
+
include OpenWFE
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def p (msg)
|
|
15
|
+
t = Time.new
|
|
16
|
+
puts "#{t.iso8601} -- #{msg}"
|
|
17
|
+
end
|
|
18
|
+
#
|
|
19
|
+
# a small method for displaying the time at the beginning
|
|
20
|
+
# of each output line
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
scheduler = Scheduler.new
|
|
24
|
+
scheduler.start
|
|
25
|
+
#
|
|
26
|
+
# create a scheduler instance and start it
|
|
27
|
+
|
|
28
|
+
p "started scheduler"
|
|
29
|
+
|
|
30
|
+
scheduler.schedule_in("3s") do
|
|
31
|
+
p "after 3 seconds"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
scheduler.schedule_in("2s") do
|
|
35
|
+
p "after 2 seconds"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
scheduler.schedule_in("5500") do
|
|
39
|
+
p "after 5500 ms stopping the scheduler and exiting..."
|
|
40
|
+
scheduler.stop
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
#scheduler.schedule_at("x" do
|
|
44
|
+
#end
|
|
45
|
+
|
|
46
|
+
#scheduler.schedule_in("3M4h27m") do
|
|
47
|
+
# p "3 months, 4 hours and 27 minutes... A bit too much"
|
|
48
|
+
#end
|
|
49
|
+
#
|
|
50
|
+
# showing what the time strings are capable of
|
|
51
|
+
|
|
52
|
+
scheduler.join
|
|
53
|
+
#
|
|
54
|
+
# align the thread of this program to the scheduler thread
|
|
55
|
+
# i.e. exit program only when scheduler terminates
|
|
56
|
+
|
data/lib/openwfe.rb
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#
|
|
2
|
+
#--
|
|
3
|
+
# Copyright (c) 2005-2007, John Mettraux and Alain Hoang, 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
|
+
# = openwferu -- Open Workflow Engine in Ruby
|
|
34
|
+
#
|
|
35
|
+
# This is the main file for the openwferu engine. It is normally
|
|
36
|
+
# referenced as a library via a require statement.
|
|
37
|
+
#
|
|
38
|
+
|
|
39
|
+
require 'openwfe/engine/engine'
|
|
40
|
+
#require 'openwfe/engine/file_persisted_engine'
|
|
41
|
+
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#
|
|
2
|
+
#--
|
|
3
|
+
# Copyright (c) 2006-2008, John Mettraux, Nicolas Modrzyk 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
|
+
# Nicolas Modrzyk at openwfe.org
|
|
39
|
+
#
|
|
40
|
+
|
|
41
|
+
module OpenWFE
|
|
42
|
+
|
|
43
|
+
#
|
|
44
|
+
# This mixin helds an application_context field and provides a
|
|
45
|
+
# lookup method for digging into that context.
|
|
46
|
+
#
|
|
47
|
+
module Contextual
|
|
48
|
+
|
|
49
|
+
attr_accessor :application_context
|
|
50
|
+
|
|
51
|
+
alias :ac :application_context
|
|
52
|
+
|
|
53
|
+
#
|
|
54
|
+
# Looks up something in the application context, if the given
|
|
55
|
+
# key is a class, then the first value in the context of that
|
|
56
|
+
# class is returned.
|
|
57
|
+
#
|
|
58
|
+
def lookup (key)
|
|
59
|
+
|
|
60
|
+
if key.kind_of? Class
|
|
61
|
+
@application_context.each do |k, value|
|
|
62
|
+
return value if value.class == key
|
|
63
|
+
end
|
|
64
|
+
return nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
@application_context[key]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
#
|
|
71
|
+
# Use reflection to instantiate the new service,and
|
|
72
|
+
# add it to the application context
|
|
73
|
+
# The service_name can be a String or a Symbol (which will be
|
|
74
|
+
# turned into a String).
|
|
75
|
+
#
|
|
76
|
+
def init_service (service_name, service_class)
|
|
77
|
+
|
|
78
|
+
s = service_class.new service_name, @application_context
|
|
79
|
+
|
|
80
|
+
unless service_name
|
|
81
|
+
s.service_name = "#{service_class.name}::#{s.object_id}"
|
|
82
|
+
@application_context[s.service_name.to_s] = s
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
s
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
#
|
|
89
|
+
# Returns the work directory for the OpenWFE[ru] application context
|
|
90
|
+
# (if any).
|
|
91
|
+
#
|
|
92
|
+
def get_work_directory (context_or_dir=nil)
|
|
93
|
+
|
|
94
|
+
dir = if context_or_dir.is_a?(String)
|
|
95
|
+
context_or_dir
|
|
96
|
+
elsif context_or_dir.respond_to?(:[])
|
|
97
|
+
context_or_dir[:work_directory]
|
|
98
|
+
else
|
|
99
|
+
@application_context[:work_directory] if @application_context
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
dir = DEFAULT_WORK_DIRECTORY unless dir
|
|
103
|
+
|
|
104
|
+
FileUtils.makedirs(dir) unless File.exist?(dir)
|
|
105
|
+
|
|
106
|
+
dir
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
end
|
data/lib/openwfe/def.rb
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
# just a redirection
|
|
36
|
+
#
|
|
37
|
+
# require 'openwfe/def'
|
|
38
|
+
#
|
|
39
|
+
# being shorter and easier to remember than
|
|
40
|
+
#
|
|
41
|
+
# require 'openwfe/expressions/raw_prog'
|
|
42
|
+
#
|
|
43
|
+
|
|
44
|
+
require 'openwfe/expressions/rprocdef'
|
|
45
|
+
require 'openwfe/expressions/raw'
|
|
46
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#
|
|
2
|
+
#--
|
|
3
|
+
# Copyright (c) 2006-2008, John Mettraux, Nicolas Modrzyk 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
|
+
# just for nicer looking examples
|
|
35
|
+
|
|
36
|
+
require 'openwfe/engine/engine'
|
|
37
|
+
|
|
@@ -0,0 +1,756 @@
|
|
|
1
|
+
#
|
|
2
|
+
#--
|
|
3
|
+
# Copyright (c) 2006-2008, John Mettraux, Nicolas Modrzyk 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
|
+
# Nicolas Modrzyk at openwfe.org
|
|
39
|
+
#
|
|
40
|
+
|
|
41
|
+
require 'logger'
|
|
42
|
+
require 'fileutils'
|
|
43
|
+
|
|
44
|
+
require 'rufus/scheduler' # gem 'rufus-scheduler'
|
|
45
|
+
|
|
46
|
+
require 'openwfe/omixins'
|
|
47
|
+
require 'openwfe/rudefinitions'
|
|
48
|
+
require 'openwfe/service'
|
|
49
|
+
require 'openwfe/workitem'
|
|
50
|
+
require 'openwfe/util/irb'
|
|
51
|
+
require 'openwfe/util/workqueue'
|
|
52
|
+
require 'openwfe/expool/wfidgen'
|
|
53
|
+
require 'openwfe/expool/expressionpool'
|
|
54
|
+
require 'openwfe/expool/expstorage'
|
|
55
|
+
require 'openwfe/expool/errorjournal'
|
|
56
|
+
require 'openwfe/engine/expool_methods'
|
|
57
|
+
require 'openwfe/engine/status_methods'
|
|
58
|
+
require 'openwfe/engine/participant_methods'
|
|
59
|
+
require 'openwfe/engine/update_exp_methods'
|
|
60
|
+
require 'openwfe/expressions/environment'
|
|
61
|
+
require 'openwfe/expressions/expressionmap'
|
|
62
|
+
require 'openwfe/participants/participantmap'
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
module OpenWFE
|
|
66
|
+
|
|
67
|
+
#
|
|
68
|
+
# The simplest implementation of the OpenWFE workflow engine.
|
|
69
|
+
# No persistence is used, everything is stored in memory.
|
|
70
|
+
#
|
|
71
|
+
class Engine < Service
|
|
72
|
+
|
|
73
|
+
include OwfeServiceLocator
|
|
74
|
+
include FeiMixin
|
|
75
|
+
|
|
76
|
+
include ExpoolMethods
|
|
77
|
+
include StatusMethods
|
|
78
|
+
include ParticipantMethods
|
|
79
|
+
include UpdateExpMethods
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
#
|
|
83
|
+
# The name of the engine, will be used to 'stamp' each expression
|
|
84
|
+
# active in the engine (and thus indirectrly, each workitem)
|
|
85
|
+
#
|
|
86
|
+
attr_reader :engine_name
|
|
87
|
+
|
|
88
|
+
#
|
|
89
|
+
# Builds an OpenWFEru engine.
|
|
90
|
+
#
|
|
91
|
+
# Accepts an optional initial application_context (containing
|
|
92
|
+
# initialization params for services for example).
|
|
93
|
+
#
|
|
94
|
+
# The engine itself uses one param :logger, used to define
|
|
95
|
+
# where all the log output for OpenWFEru should go.
|
|
96
|
+
# By default, this output goes to logs/openwferu.log
|
|
97
|
+
#
|
|
98
|
+
def initialize (application_context={})
|
|
99
|
+
|
|
100
|
+
super S_ENGINE, application_context
|
|
101
|
+
|
|
102
|
+
@engine_name = application_context[:engine_name] || 'engine'
|
|
103
|
+
|
|
104
|
+
$OWFE_LOG = application_context[:logger]
|
|
105
|
+
|
|
106
|
+
unless $OWFE_LOG
|
|
107
|
+
#puts "Creating logs in " + FileUtils.pwd
|
|
108
|
+
FileUtils.mkdir("logs") unless File.exist?("logs")
|
|
109
|
+
$OWFE_LOG = Logger.new "logs/openwferu.log", 10, 1024000
|
|
110
|
+
$OWFE_LOG.level = Logger::INFO
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# build order matters.
|
|
114
|
+
#
|
|
115
|
+
# especially for the expstorage which 'observes' the expression
|
|
116
|
+
# pool and thus needs to be instantiated after it.
|
|
117
|
+
|
|
118
|
+
build_scheduler
|
|
119
|
+
#
|
|
120
|
+
# for delayed or repetitive executions (it's the engine's clock)
|
|
121
|
+
# see http://openwferu.rubyforge.org/scheduler.html
|
|
122
|
+
|
|
123
|
+
build_expression_map
|
|
124
|
+
#
|
|
125
|
+
# mapping expression names ('sequence', 'if', 'concurrence',
|
|
126
|
+
# 'when'...) to their implementations (SequenceExpression,
|
|
127
|
+
# IfExpression, ConcurrenceExpression, ...)
|
|
128
|
+
|
|
129
|
+
build_wfid_generator
|
|
130
|
+
#
|
|
131
|
+
# the workflow instance (process instance) id generator
|
|
132
|
+
# making sure each process instance has a unique identifier
|
|
133
|
+
|
|
134
|
+
build_workqueue
|
|
135
|
+
#
|
|
136
|
+
# where apply/reply get queued and processed asynchronously
|
|
137
|
+
# by a single thread
|
|
138
|
+
|
|
139
|
+
build_expression_pool
|
|
140
|
+
#
|
|
141
|
+
# the core (hairy ball) of the engine
|
|
142
|
+
|
|
143
|
+
build_expression_storage
|
|
144
|
+
#
|
|
145
|
+
# the engine persistence (persisting the expression instances
|
|
146
|
+
# that make up process instances)
|
|
147
|
+
|
|
148
|
+
build_participant_map
|
|
149
|
+
#
|
|
150
|
+
# building the services that maps participant names to
|
|
151
|
+
# participant implementations / instances.
|
|
152
|
+
|
|
153
|
+
build_error_journal
|
|
154
|
+
#
|
|
155
|
+
# builds the error journal (keeping track of failures
|
|
156
|
+
# in business process executions, and an opportunity to
|
|
157
|
+
# fix and replay)
|
|
158
|
+
|
|
159
|
+
linfo { "new() --- engine started --- #{self.object_id}" }
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
#
|
|
163
|
+
# Call this method once the participants for a persisted engine
|
|
164
|
+
# have been [re]added.
|
|
165
|
+
#
|
|
166
|
+
# If this method is called too soon, missing participants will
|
|
167
|
+
# cause trouble... Call this method after all the participants
|
|
168
|
+
# have been added.
|
|
169
|
+
#
|
|
170
|
+
def reschedule
|
|
171
|
+
|
|
172
|
+
get_expression_pool.reschedule()
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
alias :reload :reschedule
|
|
176
|
+
|
|
177
|
+
#
|
|
178
|
+
# When 'parameters' are used at the top of a process definition, this
|
|
179
|
+
# method can be used to assert a launchitem before launch.
|
|
180
|
+
# An expression will be raised if the parameters do not match the
|
|
181
|
+
# requirements.
|
|
182
|
+
#
|
|
183
|
+
# Note that the launch method will raise those exceptions as well.
|
|
184
|
+
# This method can be useful in some scenarii though.
|
|
185
|
+
#
|
|
186
|
+
def pre_launch_check (launchitem)
|
|
187
|
+
|
|
188
|
+
get_expression_pool.prepare_raw_expression(launchitem)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
#
|
|
192
|
+
# Launches a [business] process.
|
|
193
|
+
# The 'launch_object' param may contain either a LaunchItem instance,
|
|
194
|
+
# either a String containing the URL of the process definition
|
|
195
|
+
# to launch (with an empty LaunchItem created on the fly).
|
|
196
|
+
#
|
|
197
|
+
# The launch object can also be a String containing the XML process
|
|
198
|
+
# definition or directly a class extending OpenWFE::ProcessDefinition
|
|
199
|
+
# (Ruby process definition).
|
|
200
|
+
#
|
|
201
|
+
# Returns the FlowExpressionId instance of the expression at the
|
|
202
|
+
# root of the newly launched process.
|
|
203
|
+
#
|
|
204
|
+
# Options for scheduled launches like :at, :in and :cron are accepted
|
|
205
|
+
# via the 'options' optional parameter.
|
|
206
|
+
# For example :
|
|
207
|
+
#
|
|
208
|
+
# engine.launch(launch_item)
|
|
209
|
+
# # will launch immediately
|
|
210
|
+
#
|
|
211
|
+
# engine.launch(launch_item, :in => "1d20m")
|
|
212
|
+
# # will launch in one day and twenty minutes
|
|
213
|
+
#
|
|
214
|
+
# engine.launch(launch_item, :at => "Tue Sep 11 20:23:02 +0900 2007")
|
|
215
|
+
# # will launch at that point in time
|
|
216
|
+
#
|
|
217
|
+
# engine.launch(launch_item, :cron => "0 5 * * *")
|
|
218
|
+
# # will launch that same process every day,
|
|
219
|
+
# # five minutes after midnight (see "man 5 crontab")
|
|
220
|
+
#
|
|
221
|
+
def launch (launch_object, options={})
|
|
222
|
+
|
|
223
|
+
launchitem = extract_launchitem launch_object
|
|
224
|
+
|
|
225
|
+
fei = get_expression_pool.launch launchitem, options
|
|
226
|
+
|
|
227
|
+
#linfo { "launch() #{fei.wfid} : #{fei.wfname} #{fei.wfrevision}" }
|
|
228
|
+
|
|
229
|
+
fei.dup
|
|
230
|
+
#
|
|
231
|
+
# so that users of this launch() method can play with their
|
|
232
|
+
# fei without breaking things
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
#
|
|
236
|
+
# This method is used to feed a workitem back to the engine (after
|
|
237
|
+
# it got sent to a worklist or wherever by a participant).
|
|
238
|
+
# Participant implementations themselves do call this method usually.
|
|
239
|
+
#
|
|
240
|
+
# This method also accepts LaunchItem instances.
|
|
241
|
+
#
|
|
242
|
+
# Since OpenWFEru 0.9.16, this reply method accepts InFlowWorkitem
|
|
243
|
+
# that don't belong to a process instance (ie whose flow_expression_id
|
|
244
|
+
# is nil). It will simply notify the participant_map of the reply
|
|
245
|
+
# for the given participant_name. If there is no participant_name
|
|
246
|
+
# specified for this orphan workitem, an exception will be raised.
|
|
247
|
+
#
|
|
248
|
+
def reply (workitem)
|
|
249
|
+
|
|
250
|
+
if workitem.is_a?(InFlowWorkItem)
|
|
251
|
+
|
|
252
|
+
if workitem.flow_expression_id
|
|
253
|
+
#
|
|
254
|
+
# vanilla case, workitem coming back
|
|
255
|
+
# (from listener probably)
|
|
256
|
+
|
|
257
|
+
return get_expression_pool.reply(
|
|
258
|
+
workitem.flow_expression_id, workitem)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
if workitem.participant_name
|
|
262
|
+
#
|
|
263
|
+
# a workitem that doesn't belong to a process instance
|
|
264
|
+
# but bears a participant name.
|
|
265
|
+
# Notify, there may be something listening on
|
|
266
|
+
# this channel (see the 'listen' expression).
|
|
267
|
+
|
|
268
|
+
return get_participant_map.onotify(
|
|
269
|
+
workitem.participant_name, :reply, workitem)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
raise \
|
|
273
|
+
"InFlowWorkitem doesn't belong to a process instance" +
|
|
274
|
+
" nor to a participant"
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
return get_expression_pool.launch(workitem) \
|
|
278
|
+
if workitem.is_a?(LaunchItem)
|
|
279
|
+
#
|
|
280
|
+
# launchitem coming from listener
|
|
281
|
+
# let's attempt to launch a new process instance
|
|
282
|
+
|
|
283
|
+
raise \
|
|
284
|
+
"engine.reply() " +
|
|
285
|
+
"cannot handle instances of #{workitem.class}"
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
alias :forward :reply
|
|
289
|
+
alias :proceed :reply
|
|
290
|
+
|
|
291
|
+
#
|
|
292
|
+
# Adds a workitem listener to this engine.
|
|
293
|
+
#
|
|
294
|
+
# The 'freq' parameters if present might indicate how frequently
|
|
295
|
+
# the resource should be polled for incoming workitems.
|
|
296
|
+
#
|
|
297
|
+
# engine.add_workitem_listener(listener, "3m10s")
|
|
298
|
+
# # every 3 minutes and 10 seconds
|
|
299
|
+
#
|
|
300
|
+
# engine.add_workitem_listener(listener, "0 22 * * 1-5")
|
|
301
|
+
# # every weekday at 10pm
|
|
302
|
+
#
|
|
303
|
+
# TODO : block handling...
|
|
304
|
+
#
|
|
305
|
+
def add_workitem_listener (listener, freq=nil)
|
|
306
|
+
|
|
307
|
+
name = nil
|
|
308
|
+
|
|
309
|
+
if listener.kind_of? Class
|
|
310
|
+
|
|
311
|
+
listener = init_service nil, listener
|
|
312
|
+
|
|
313
|
+
name = listener.service_name
|
|
314
|
+
else
|
|
315
|
+
|
|
316
|
+
name = listener.name if listener.respond_to? :name
|
|
317
|
+
name = "#{listener.class}::#{listener.object_id}" unless name
|
|
318
|
+
|
|
319
|
+
@application_context[name] = listener
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
result = nil
|
|
323
|
+
|
|
324
|
+
if freq
|
|
325
|
+
|
|
326
|
+
freq = freq.to_s.strip
|
|
327
|
+
|
|
328
|
+
result = if Rufus::Scheduler.is_cron_string(freq)
|
|
329
|
+
|
|
330
|
+
get_scheduler.schedule(freq, listener)
|
|
331
|
+
else
|
|
332
|
+
|
|
333
|
+
get_scheduler.schedule_every(freq, listener)
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
linfo { "add_workitem_listener() added '#{name}'" }
|
|
338
|
+
|
|
339
|
+
result
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
#
|
|
343
|
+
# Makes the current thread join the engine's scheduler thread
|
|
344
|
+
#
|
|
345
|
+
# You can thus make an engine standalone with something like :
|
|
346
|
+
#
|
|
347
|
+
# require 'openwfe/engine/engine'
|
|
348
|
+
#
|
|
349
|
+
# the_engine = OpenWFE::Engine.new
|
|
350
|
+
# the_engine.join
|
|
351
|
+
#
|
|
352
|
+
# And you'll have to hit CTRL-C to make it stop.
|
|
353
|
+
#
|
|
354
|
+
def join
|
|
355
|
+
|
|
356
|
+
get_scheduler.join
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
#
|
|
360
|
+
# Calling this method makes the control flow block until the
|
|
361
|
+
# workflow engine is inactive.
|
|
362
|
+
#
|
|
363
|
+
# TODO : implement idle_for
|
|
364
|
+
#
|
|
365
|
+
def join_until_idle
|
|
366
|
+
|
|
367
|
+
storage = get_expression_storage
|
|
368
|
+
|
|
369
|
+
while storage.size > 1
|
|
370
|
+
sleep 1
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
#
|
|
375
|
+
# Enabling the console means that hitting CTRL-C on the window /
|
|
376
|
+
# term / dos box / whatever does run the OpenWFEru engine will
|
|
377
|
+
# open an IRB interactive console for directly manipulating the
|
|
378
|
+
# engine instance.
|
|
379
|
+
#
|
|
380
|
+
# Hit CTRL-D to get out of the console.
|
|
381
|
+
#
|
|
382
|
+
def enable_irb_console
|
|
383
|
+
|
|
384
|
+
OpenWFE::trap_int_irb(binding)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
#--
|
|
388
|
+
# Makes sure that hitting CTRL-C will actually kill the engine VM and
|
|
389
|
+
# not open an IRB console.
|
|
390
|
+
#
|
|
391
|
+
#def disable_irb_console
|
|
392
|
+
# $openwfe_irb = nil
|
|
393
|
+
# trap 'INT' do
|
|
394
|
+
# exit 0
|
|
395
|
+
# end
|
|
396
|
+
#end
|
|
397
|
+
#++
|
|
398
|
+
|
|
399
|
+
#
|
|
400
|
+
# Stopping the engine will stop all the services in the
|
|
401
|
+
# application context.
|
|
402
|
+
#
|
|
403
|
+
def stop
|
|
404
|
+
|
|
405
|
+
linfo { "stop() stopping engine '#{@service_name}'" }
|
|
406
|
+
|
|
407
|
+
@application_context.each do |sname, service|
|
|
408
|
+
|
|
409
|
+
next if sname == self.service_name
|
|
410
|
+
|
|
411
|
+
#if service.kind_of?(ServiceMixin)
|
|
412
|
+
if service.respond_to?(:stop)
|
|
413
|
+
|
|
414
|
+
service.stop
|
|
415
|
+
|
|
416
|
+
linfo do
|
|
417
|
+
"stop() stopped service '#{sname}' (#{service.class})"
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
linfo { "stop() stopped engine '#{@service_name}'" }
|
|
423
|
+
|
|
424
|
+
nil
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
#
|
|
428
|
+
# Waits for a given process instance to terminate.
|
|
429
|
+
# The method only exits when the flow terminates, but beware : if
|
|
430
|
+
# the process already terminated, the method will never exit.
|
|
431
|
+
#
|
|
432
|
+
# The parameter can be a FlowExpressionId instance, for example the
|
|
433
|
+
# one given back by a launch(), or directly a workflow instance id
|
|
434
|
+
# (String).
|
|
435
|
+
#
|
|
436
|
+
# This method is mainly used in utests.
|
|
437
|
+
#
|
|
438
|
+
def wait_for (fei_or_wfid)
|
|
439
|
+
|
|
440
|
+
wfid = if fei_or_wfid.kind_of?(FlowExpressionId)
|
|
441
|
+
fei_or_wfid.workflow_instance_id
|
|
442
|
+
else
|
|
443
|
+
fei_or_wfid
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
t = Thread.new { Thread.stop }
|
|
447
|
+
|
|
448
|
+
to = get_expression_pool.add_observer(:terminate) do |c, fe, wi|
|
|
449
|
+
t.wakeup if (fe.fei.workflow_instance_id == wfid and t.alive?)
|
|
450
|
+
end
|
|
451
|
+
te = get_expression_pool.add_observer(:error) do |c, fei, m, i, e|
|
|
452
|
+
t.wakeup if (fei.parent_wfid == wfid and t.alive?)
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
t.join
|
|
456
|
+
|
|
457
|
+
#tc = get_expression_pool.add_observer(:cancel) do |c, fe|
|
|
458
|
+
# if (fe.fei.wfid == wfid and fe.fei.expid == "0" and t.alive?)
|
|
459
|
+
# sleep 0.500
|
|
460
|
+
# t.wakeup
|
|
461
|
+
# end
|
|
462
|
+
#end
|
|
463
|
+
|
|
464
|
+
linfo { "wait_for() #{wfid}" }
|
|
465
|
+
|
|
466
|
+
get_expression_pool.remove_observer to, :terminate
|
|
467
|
+
get_expression_pool.remove_observer te, :error
|
|
468
|
+
#get_expression_pool.remove_observer tc, :cancel
|
|
469
|
+
#
|
|
470
|
+
# it would work as well without specifying the channel,
|
|
471
|
+
# but it's thus a little bit faster
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
#
|
|
475
|
+
# Pauses a process (sets its /__paused__ variable to true).
|
|
476
|
+
#
|
|
477
|
+
def pause_process (wfid)
|
|
478
|
+
|
|
479
|
+
wfid = extract_wfid wfid
|
|
480
|
+
|
|
481
|
+
root_expression = get_expression_pool.fetch_root wfid
|
|
482
|
+
|
|
483
|
+
get_expression_pool.paused_instances[wfid] = true
|
|
484
|
+
root_expression.set_variable VAR_PAUSED, true
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
#
|
|
488
|
+
# Restarts a process : removes its 'paused' flag (variable) and makes
|
|
489
|
+
# sure to 'replay' events (replies) that came for it while it was
|
|
490
|
+
# in pause.
|
|
491
|
+
#
|
|
492
|
+
def resume_process (wfid)
|
|
493
|
+
|
|
494
|
+
wfid = extract_wfid wfid
|
|
495
|
+
|
|
496
|
+
root_expression = get_expression_pool.fetch_root wfid
|
|
497
|
+
|
|
498
|
+
#
|
|
499
|
+
# remove 'paused' flag
|
|
500
|
+
|
|
501
|
+
get_expression_pool.paused_instances.delete wfid
|
|
502
|
+
root_expression.unset_variable VAR_PAUSED
|
|
503
|
+
|
|
504
|
+
#
|
|
505
|
+
# replay
|
|
506
|
+
#
|
|
507
|
+
# select PausedError instances in separate list
|
|
508
|
+
|
|
509
|
+
errors = get_error_journal.get_error_log wfid
|
|
510
|
+
error_class = PausedError.name
|
|
511
|
+
paused_errors = errors.select { |e| e.error_class == error_class }
|
|
512
|
+
|
|
513
|
+
return if paused_errors.size < 1
|
|
514
|
+
|
|
515
|
+
# replay select PausedError instances
|
|
516
|
+
|
|
517
|
+
paused_errors.each do |e|
|
|
518
|
+
replay_at_error e
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
#
|
|
523
|
+
# Takes care of removing an error from the error journal and
|
|
524
|
+
# they replays its process at that point.
|
|
525
|
+
#
|
|
526
|
+
def replay_at_error (error)
|
|
527
|
+
|
|
528
|
+
get_error_journal.remove_errors(
|
|
529
|
+
error.fei.parent_wfid,
|
|
530
|
+
error)
|
|
531
|
+
|
|
532
|
+
get_workqueue.push(
|
|
533
|
+
get_expression_pool,
|
|
534
|
+
:do_apply_reply,
|
|
535
|
+
error.message,
|
|
536
|
+
error.fei,
|
|
537
|
+
error.workitem)
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
#
|
|
541
|
+
# Looks up a process variable in a process.
|
|
542
|
+
# If fei_or_wfid is not given, will simply look in the
|
|
543
|
+
# 'engine environment' (where the top level variables '//' do reside).
|
|
544
|
+
#
|
|
545
|
+
def lookup_variable (var_name, fei_or_wfid=nil)
|
|
546
|
+
|
|
547
|
+
return get_expression_pool.fetch_engine_environment[var_name] \
|
|
548
|
+
unless fei_or_wfid
|
|
549
|
+
|
|
550
|
+
fetch_exp(fei_or_wfid).lookup_variable var_name
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
#
|
|
554
|
+
# Returns the variables set for a process or an expression.
|
|
555
|
+
#
|
|
556
|
+
# If a process (wfid) is given, variables of the process environment
|
|
557
|
+
# will be returned, else variables in the environment valid for the
|
|
558
|
+
# expression (fei) will be returned.
|
|
559
|
+
#
|
|
560
|
+
# If nothing (or nil) is given, the variables set in the engine
|
|
561
|
+
# environment will be returned.
|
|
562
|
+
#
|
|
563
|
+
def get_variables (fei_or_wfid=nil)
|
|
564
|
+
|
|
565
|
+
return get_expression_pool.fetch_engine_environment.variables \
|
|
566
|
+
unless fei_or_wfid
|
|
567
|
+
|
|
568
|
+
fetch_exp(fei_or_wfid).get_environment.variables
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
#
|
|
572
|
+
# Returns an array of wfid (workflow instance ids) whose root
|
|
573
|
+
# environment containes the given variable
|
|
574
|
+
#
|
|
575
|
+
# If there are no matches, an empty array will be returned.
|
|
576
|
+
#
|
|
577
|
+
# Regular expressions are accepted as values.
|
|
578
|
+
#
|
|
579
|
+
# If no value is given, all processes with the given variable name
|
|
580
|
+
# set will be returned.
|
|
581
|
+
#
|
|
582
|
+
def lookup_processes (var_name, value=nil)
|
|
583
|
+
|
|
584
|
+
# TODO : maybe this would be better in the ExpressionPool
|
|
585
|
+
|
|
586
|
+
regexp = if value
|
|
587
|
+
if value.is_a?(Regexp)
|
|
588
|
+
value
|
|
589
|
+
else
|
|
590
|
+
Regexp.compile(value.to_s)
|
|
591
|
+
end
|
|
592
|
+
else
|
|
593
|
+
nil
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
envs = get_expression_storage.find_expressions(
|
|
597
|
+
:include_classes => Environment)
|
|
598
|
+
|
|
599
|
+
envs = envs.find_all do |env|
|
|
600
|
+
val = env.variables[var_name]
|
|
601
|
+
(val and ((not regexp) or (regexp.match(val))))
|
|
602
|
+
end
|
|
603
|
+
envs.collect do |env|
|
|
604
|
+
env.fei.wfid
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
#envs.inject([]) do |r, env|
|
|
608
|
+
# val = env.variables[var_name]
|
|
609
|
+
# r << env.fei.wfid \
|
|
610
|
+
# if (val and ((not regexp) or (regexp.match(val))))
|
|
611
|
+
# r
|
|
612
|
+
#end
|
|
613
|
+
#
|
|
614
|
+
# seems slower...
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
protected
|
|
618
|
+
|
|
619
|
+
#--
|
|
620
|
+
# the following methods may get overridden upon extension
|
|
621
|
+
# see for example file_persisted_engine.rb
|
|
622
|
+
#++
|
|
623
|
+
|
|
624
|
+
#
|
|
625
|
+
# Builds the ExpressionMap (the mapping between expression names
|
|
626
|
+
# and expression implementations).
|
|
627
|
+
#
|
|
628
|
+
def build_expression_map
|
|
629
|
+
|
|
630
|
+
@application_context[S_EXPRESSION_MAP] = ExpressionMap.new
|
|
631
|
+
#
|
|
632
|
+
# the expression map is not a Service anymore,
|
|
633
|
+
# it's a simple instance (that will be reused in other
|
|
634
|
+
# OpenWFEru components)
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
#
|
|
638
|
+
# This implementation builds a KotobaWfidGenerator instance and
|
|
639
|
+
# binds it in the engine context.
|
|
640
|
+
# There are other WfidGeneration implementations available, like
|
|
641
|
+
# UuidWfidGenerator or FieldWfidGenerator.
|
|
642
|
+
#
|
|
643
|
+
def build_wfid_generator
|
|
644
|
+
|
|
645
|
+
#init_service S_WFID_GENERATOR, DefaultWfidGenerator
|
|
646
|
+
#init_service S_WFID_GENERATOR, UuidWfidGenerator
|
|
647
|
+
init_service S_WFID_GENERATOR, KotobaWfidGenerator
|
|
648
|
+
|
|
649
|
+
#g = FieldWfidGenerator.new(
|
|
650
|
+
# S_WFID_GENERATOR, @application_context, "wfid")
|
|
651
|
+
#
|
|
652
|
+
# showing how to initialize a FieldWfidGenerator that
|
|
653
|
+
# will take as workflow instance id the value found in
|
|
654
|
+
# the field "wfid" of the LaunchItem.
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
#
|
|
658
|
+
# Builds the workqueue where apply/reply work is queued
|
|
659
|
+
# and processed.
|
|
660
|
+
#
|
|
661
|
+
def build_workqueue
|
|
662
|
+
|
|
663
|
+
init_service S_WORKQUEUE, WorkQueue
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
#
|
|
667
|
+
# Builds the OpenWFEru expression pool (the core of the engine)
|
|
668
|
+
# and binds it in the engine context.
|
|
669
|
+
# There is only one implementation of the expression pool, so
|
|
670
|
+
# this method is usually never overriden.
|
|
671
|
+
#
|
|
672
|
+
def build_expression_pool
|
|
673
|
+
|
|
674
|
+
init_service S_EXPRESSION_POOL, ExpressionPool
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
#
|
|
678
|
+
# The implementation here builds an InMemoryExpressionStorage
|
|
679
|
+
# instance.
|
|
680
|
+
#
|
|
681
|
+
# See FilePersistedEngine or CachedFilePersistedEngine for
|
|
682
|
+
# overrides of this method.
|
|
683
|
+
#
|
|
684
|
+
def build_expression_storage
|
|
685
|
+
|
|
686
|
+
init_service S_EXPRESSION_STORAGE, InMemoryExpressionStorage
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
#
|
|
690
|
+
# The ParticipantMap is a mapping between participant names
|
|
691
|
+
# (well rather regular expressions) and participant implementations
|
|
692
|
+
# (see http://openwferu.rubyforge.org/participants.html)
|
|
693
|
+
#
|
|
694
|
+
def build_participant_map
|
|
695
|
+
|
|
696
|
+
init_service S_PARTICIPANT_MAP, ParticipantMap
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
#
|
|
700
|
+
# There is only one Scheduler implementation, that's the one
|
|
701
|
+
# built and bound here.
|
|
702
|
+
#
|
|
703
|
+
def build_scheduler
|
|
704
|
+
|
|
705
|
+
s = Rufus::Scheduler.new(
|
|
706
|
+
:thread_name =>
|
|
707
|
+
"rufus scheduler for Ruote (engine #{self.object_id})")
|
|
708
|
+
|
|
709
|
+
@application_context[S_SCHEDULER] = s
|
|
710
|
+
|
|
711
|
+
s.start
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
#
|
|
715
|
+
# The default implementation of this method uses an
|
|
716
|
+
# InMemoryErrorJournal (do not use in production).
|
|
717
|
+
#
|
|
718
|
+
def build_error_journal
|
|
719
|
+
|
|
720
|
+
init_service S_ERROR_JOURNAL, InMemoryErrorJournal
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
#
|
|
724
|
+
# Turns the raw launch request info into a LaunchItem instance.
|
|
725
|
+
#
|
|
726
|
+
def extract_launchitem (launch_object)
|
|
727
|
+
|
|
728
|
+
if launch_object.kind_of?(OpenWFE::LaunchItem)
|
|
729
|
+
|
|
730
|
+
launch_object
|
|
731
|
+
|
|
732
|
+
elsif launch_object.kind_of?(Class)
|
|
733
|
+
|
|
734
|
+
LaunchItem.new launch_object
|
|
735
|
+
|
|
736
|
+
elsif launch_object.kind_of?(String)
|
|
737
|
+
|
|
738
|
+
li = OpenWFE::LaunchItem.new
|
|
739
|
+
|
|
740
|
+
if launch_object[0, 1] == '<' or launch_object.index("\n")
|
|
741
|
+
|
|
742
|
+
li.workflow_definition_url = "field:__definition"
|
|
743
|
+
li['__definition'] = launch_object
|
|
744
|
+
|
|
745
|
+
else
|
|
746
|
+
|
|
747
|
+
li.workflow_definition_url = launch_object
|
|
748
|
+
end
|
|
749
|
+
|
|
750
|
+
li
|
|
751
|
+
end
|
|
752
|
+
end
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
end
|
|
756
|
+
|