1 package body WF_ENGINE_UTIL as
2 /* $Header: wfengb.pls 120.38.12020000.3 2012/11/13 19:42:45 alsosa ship $ */
3
4 type InstanceArrayTyp is table of pls_integer
5 index by binary_integer;
6 type TypeArrayTyp is table of varchar2(8)
7 index by binary_integer;
8 type NameArrayTyp is table of varchar2(30)
9 index by binary_integer;
10
11 --
12 -- Exception
13 --
14 no_savepoint exception;
15 bad_format exception; --</rwunderl:2307104>
16
17 pragma EXCEPTION_INIT(no_savepoint, -1086);
18 pragma EXCEPTION_INIT(bad_format, -6502); --<rwunderl:2307104/>
19
20 --
21 -- Activity_Parent_Process globals
22 -- Globals used to cache values retrieved in activity_parent_process
23 -- for performance, to avoid fetching the same value many times.
24 -- NOTE: In SYNCHMODE, this stack must be the complete call stack
25 -- of subprocesses at all times. In normal mode, the stack may or may
26 -- not be accurate, because
27 -- 1. calls for different items can be interwoven
28 -- 2. calls can jump anywhere on the process tree in some situations
29 -- (HandleError, etc).
30 -- ALWAYS check the key values before using values on the stack.
31 --
32
33 app_itemtype varchar2(8) := '';
34 app_itemkey varchar2(240) := '';
35
36 app_level pls_integer := '';
37 app_parent_itemtype TypeArrayTyp;
38 app_parent_name NameArrayTyp;
39 app_parent_id InstanceArrayTyp;
40
41 -- Bug 3824367
42 -- Optimizing the code using a single cursor with binds
43 cursor curs_activityattr (c_actid NUMBER, c_aname VARCHAR2) is
44 select WAAV.PROCESS_ACTIVITY_ID, WAAV.NAME, WAAV.VALUE_TYPE,
45 WAAV.TEXT_VALUE, WAAV.NUMBER_VALUE, WAAV.DATE_VALUE
46 from WF_ACTIVITY_ATTR_VALUES WAAV
47 where WAAV.PROCESS_ACTIVITY_ID = c_actid
48 and WAAV.NAME = c_aname;
49
50 --
51 -- ClearCache
52 -- Clear runtime cache
53 procedure ClearCache
54 is
55 begin
56 wf_engine_util.app_itemtype := '';
57 wf_engine_util.app_itemkey := '';
58 wf_engine_util.app_level := '';
59 exception
60 when others then
61 Wf_Core.Context('Wf_Engine_Util', 'ClearCache');
62 raise;
63 end ClearCache;
64
65 --
66 -- AddProcessStack
67 -- Add a new subprocess to activity_parent_process call stack.
68 -- Called when a new (sub)process is entered.
69 -- IN
70 -- itemtype - item itemtype
71 -- itemkey - item itemkey
72 -- act_itemtype - activity itemtype of process
73 -- act_name - activity name of process
74 -- actid - instance id of process
75 -- rootflag - TRUE if this is the root process
76 --
77 procedure AddProcessStack(
78 itemtype in varchar2,
79 itemkey in varchar2,
80 act_itemtype in varchar2,
81 act_name in varchar2,
82 actid in number,
83 rootflag in boolean)
84 is
85 begin
86 -- SYNCHMODE: Error if item doesn't match the cache, unless
87 -- starting a new process.
88 -- NOTE: Chance for an error here if you try to initiate a new process
89 -- while a synch process is still running. In that case you will
90 -- should eventually get an error from app for the process 1
91 -- because process 2 has trashed the stack. Can't think of a way to
92 -- detect the error directly here.
93 if (itemkey = wf_engine.eng_synch) then
94 if ((not rootflag) and
95 ((nvl(wf_engine_util.app_itemtype, 'x') <> itemtype) or
96 (nvl(wf_engine_util.app_itemkey, 'x') <> itemkey))) then
97 Wf_Core.Token('ITEMTYPE', itemtype);
98 Wf_Core.Token('ITEMKEY', itemkey);
99 Wf_Core.Raise('WFENG_SYNCH_ITEM');
100 end if;
101 end if;
102
103 -- If this is the root process, OR this is a different item,
104 -- then re-initialize the stack.
105 if ((rootflag) or
106 (nvl(wf_engine_util.app_itemtype, 'x') <> itemtype) or
107 (nvl(wf_engine_util.app_itemkey, 'x') <> itemkey)) then
108 wf_engine_util.app_itemtype := itemtype;
109 wf_engine_util.app_itemkey := itemkey;
110 wf_engine_util.app_level := 0;
111 end if;
112
113 -- Add the process to the stack
114 wf_engine_util.app_level := wf_engine_util.app_level + 1;
115 wf_engine_util.app_parent_itemtype(wf_engine_util.app_level) := act_itemtype;
116 wf_engine_util.app_parent_name(wf_engine_util.app_level) := act_name;
117 wf_engine_util.app_parent_id(wf_engine_util.app_level) := actid;
118 exception
119 when others then
120 Wf_Core.Context('Wf_Engine_Util', 'AddProcessStack',
121 itemtype, itemkey, act_itemtype, act_name, to_char(actid));
122 raise;
123 end AddProcessStack;
124
125 --
126 -- RemoveProcessStack
127 -- Remove a process from the process stack.
128 -- Called when a (sub)process exits.
129 -- IN
130 -- itemtype - item type
131 -- itemkey - itemkey
132 -- actid - instance id of process just completed
133 --
134 procedure RemoveProcessStack(
135 itemtype in varchar2,
136 itemkey in varchar2,
137 actid in number)
138 is
139 begin
140 -- If this is the top process on the stack, pop it off.
141 -- Must check if type/key/actid match, in case items and processes
142 -- are being interwoven and this is not the correct stack.
143 if (nvl(wf_engine_util.app_level, 0) > 0) then
144 if ((wf_engine_util.app_itemtype = itemtype) and
145 (wf_engine_util.app_itemkey = itemkey) and
146 (wf_engine_util.app_parent_id(wf_engine_util.app_level) = actid)) then
147 wf_engine_util.app_level := wf_engine_util.app_level - 1;
148 end if;
149 end if;
150 exception
151 when others then
152 Wf_Core.Context('Wf_Engine_Util', 'RemoveProcessStack', itemtype,
153 itemkey, to_char(actid));
154 raise;
155 end RemoveProcessStack;
156
157 --
158 -- Activity_Parent_Process (PRIVATE)
159 -- Get the activity's direct parent process.
160 -- IN
161 -- itemtype - Item type
162 -- itemkey - Item key
163 -- actid - The activity instance id.
164 --
165 function activity_parent_process(
166 itemtype in varchar2,
167 itemkey in varchar2,
168 actid in number)
169 return number
170 is
171 parentid pls_integer;
172 status PLS_INTEGER;
173
174 begin
175 -- Retrieve parent activity name
176 WF_CACHE.GetProcessActivity(activity_parent_process.actid, status);
177
178 if (status <> WF_CACHE.task_SUCCESS) then
179
180 select WPA.PROCESS_ITEM_TYPE, WPA.PROCESS_NAME, WPA.PROCESS_VERSION,
181 WPA.ACTIVITY_ITEM_TYPE, WPA.ACTIVITY_NAME, WPA.INSTANCE_ID,
182 WPA.INSTANCE_LABEL, WPA.PERFORM_ROLE, WPA.PERFORM_ROLE_TYPE,
183 WPA.START_END, WPA.DEFAULT_RESULT
184 into WF_CACHE.ProcessActivities(activity_parent_process.actid)
185 from WF_PROCESS_ACTIVITIES WPA
186 where WPA.INSTANCE_ID = activity_parent_process.actid;
187
188 end if;
189
190 -- Check the cached values in the call stack for a match, starting
191 -- at the bottom. If
192 -- 1. Itemtype and key
193 -- 2. Parent type and name
194 -- are the same, then the parent id must be the same.
195 -- Return it directly.
196 if ((nvl(wf_engine_util.app_level, 0) > 0) and
197 (itemtype = wf_engine_util.app_itemtype) and
198 (itemkey = wf_engine_util.app_itemkey)) then
199 for i in reverse 1 .. wf_engine_util.app_level loop
200 if ((WF_CACHE.ProcessActivities(actid).PROCESS_ITEM_TYPE =
201 wf_engine_util.app_parent_itemtype(i)) and
202 (WF_CACHE.ProcessActivities(actid).PROCESS_NAME =
203 wf_engine_util.app_parent_name(i))) then
204 -- Found a match.
205 return(wf_engine_util.app_parent_id(i));
206 end if;
207 end loop;
208 end if;
209
210 -- SYNCHMODE: If we don't have a match in the cache, then some restricted
211 -- activity must have happened. Raise an error.
212 if (itemkey = wf_engine.eng_synch) then
213 Wf_Core.Token('ITEMTYPE', itemtype);
214 Wf_Core.Token('ITEMKEY', itemkey);
215 Wf_Core.Raise('WFENG_SYNCH_ITEM');
216 end if;
217
218 -- If no match was found, then either
219 -- 1. Activity has a different parent process name
220 -- 2. This is a new item
221 -- 3. This is the first call to app
222 -- In any case, join to WIAS to find an active instance for the
223 -- parent process name. Note there will be more than one instance
224 -- matching the parent activity name because of:
225 -- 1. The same activity may be used in multiple processes,
226 -- (even though the activity is used only once any particular
227 -- process tree).
228 -- 2. Versions
229 -- The join to active rows in WAIS for this item should choose
230 -- exactly one of these.
231
232 -- bug 1663684 - Added hint to choose a different driving table
233 SELECT /*+ leading(wias) index(wias,WF_ITEM_ACTIVITY_STATUSES_PK) */
234 WPA.INSTANCE_ID
235 INTO parentid
236 FROM WF_ITEM_ACTIVITY_STATUSES WIAS,
237 WF_PROCESS_ACTIVITIES WPA
238 WHERE WPA.ACTIVITY_ITEM_TYPE =
239 WF_CACHE.ProcessActivities(actid).PROCESS_ITEM_TYPE
240 AND WPA.ACTIVITY_NAME = WF_CACHE.ProcessActivities(actid).PROCESS_NAME
241 AND WPA.INSTANCE_ID = WIAS.PROCESS_ACTIVITY
242 AND WIAS.ITEM_TYPE = activity_parent_process.itemtype
243 AND WIAS.ITEM_KEY = activity_parent_process.itemkey;
244
245 -- Re-initialize process stack, starting with the new value
246 Wf_Engine_Util.AddProcessStack(itemtype, itemkey,
247 WF_CACHE.ProcessActivities(actid).PROCESS_ITEM_TYPE,
248 WF_CACHE.ProcessActivities(actid).PROCESS_NAME, parentid, TRUE);
249
250 return parentid;
251 exception
252 when no_data_found then
253 Wf_Core.Context('Wf_Engine_Util', 'Activity_Parent_Process',
254 to_char(actid));
255 Wf_Core.Token('ITEMTYPE', itemtype);
256 Wf_Core.Token('ITEMKEY', itemkey);
257 Wf_Core.Token('CHILDPROCESS', to_char(actid));
258 Wf_Core.Token('FUNCTION', 'Activity_Parent_Process');
259 Wf_Core.Raise('WFSQL_INTERNAL');
260 when others then
261 Wf_Core.Context('Wf_Engine_Util', 'Activity_Parent_Process',
262 to_char(actid));
263 raise;
264
265 end activity_parent_process;
266
267 --
268 -- Complete_Activity (PRIVATE)
269 -- Mark an activity complete (after checking post-notification function
270 -- if requested), then clean up and prepare to continue process:
271 -- - Kill any outstanding child activities
272 -- - Complete the parent process if this is an END activity
273 -- - Follow any transitions to further activities in process
274 -- IN
275 -- itemtype - A valid item type
276 -- itemkey - A string generated from the application object's primary key.
277 -- actid - The activity instance id.
278 -- result - The activity result.
279 -- runpntf - if TRUE then check the post-notification function before
280 -- completion
281 --
282 procedure complete_activity(itemtype in varchar2,
283 itemkey in varchar2,
284 actid in number,
285 result in varchar2,
286 runpntf in boolean)
287 is
288 -- Select all the transition activities for a given from activity
289 -- and result
290 cursor children (fromact pls_integer, fromact_result varchar2) is
291 SELECT WAT1.FROM_PROCESS_ACTIVITY, WAT1.RESULT_CODE,
292 WAT1.TO_PROCESS_ACTIVITY
293 FROM WF_ACTIVITY_TRANSITIONS WAT1
294 WHERE WAT1.FROM_PROCESS_ACTIVITY = fromact
295 AND (WAT1.RESULT_CODE in (fromact_result, wf_engine.eng_trans_any)
296 OR (WAT1.RESULT_CODE = wf_engine.eng_trans_default
297 AND NOT EXISTS
298 (SELECT NULL
299 FROM WF_ACTIVITY_TRANSITIONS WAT2
300 WHERE WAT2.FROM_PROCESS_ACTIVITY = fromact
301 AND WAT2.RESULT_CODE = fromact_result)
302 )
303 );
304 childarr InstanceArrayTyp;
305 i pls_integer := 0;
306
307 pntfstatus varchar2(8); -- Status of post-notification function
308 pntfresult varchar2(30); -- Result of post-notification function
309 lresult varchar2(30); -- Local result buffer
310
311 parent_status varchar2(8); -- Status of parent activity
312
313 actdate date; -- Active date
314 acttype varchar2(8); -- Activity type
315
316 notid pls_integer; -- Notification id
317 user varchar2(320); -- Not. assigned user
318 msgtype varchar2(8); -- Not. message type
319 msgname varchar2(30); -- Not. messaage name
320 priority number; -- Not. priority
321 duedate date; -- Not. duedate
322 not_status varchar2(8); -- Not. status
323
324 root varchar2(30); -- Root process of activity
325 version pls_integer; -- Root process version
326 rootid pls_integer; -- Id of root process
327
328 status PLS_INTEGER;
329
330 --<rwunderl:2412971>
331 TransitionCount pls_integer := 0;
332 l_baseLnk NUMBER;
333 l_prevLnk NUMBER;
334 watIND NUMBER;
335 l_LinkCollision BOOLEAN;
336
337 begin
338 actdate := Wf_Item.Active_Date(itemtype, itemkey);
339 acttype := Wf_Activity.Instance_Type(actid, actdate);
340
341 if (runpntf and (acttype = wf_engine.eng_notification)) then
342 -- First execute possible post-notification function to see if activity
343 -- should really complete.
344 Wf_Engine_Util.Execute_Post_NTF_Function(itemtype, itemkey, actid,
345 wf_engine.eng_run, pntfstatus, pntfresult);
346
347 if (pntfstatus = wf_engine.eng_waiting) then
348 -- Either post-notification function is not complete, or error occurred.
349 -- In either case exit immediately without changing status.
350
351 -- Bug 2078211
352 -- if the status is waiting and the input parameter result is
353 -- wf_engine.eng_timedout, continue executing as the activity
354 -- needs to be timedout as determined by the procedure
355 -- Wf_Engine_Util.processtimeout
356
357 if (result = wf_engine.eng_timedout) then
358 lresult := result;
359 else
360 return;
361 end if;
362 elsif (pntfstatus = wf_engine.eng_completed) then
363 -- Post-notification activity is complete.
364 -- Replace result with result of post-notification function.
365 lresult := pntfresult;
366 else
367 -- Any pntfstatus other than waiting or complete means this is not
368 -- a post-notification activity, so use original result.
369 lresult := result;
370 end if;
371 else
372 lresult := result;
373 end if;
374
375 -- Update the item activity status
376 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
377 wf_engine.eng_completed, lresult, '', SYSDATE);
378
379 if (acttype = wf_engine.eng_process) then
380 -- If this activity is a process, kill any deferred children.
381 Wf_Engine_Util.Process_Kill_Children(itemtype, itemkey, actid);
382
383 -- Remove myself from the process call stack
384 Wf_Engine_Util.RemoveProcessStack(itemtype, itemkey, actid);
385 elsif (acttype = wf_engine.eng_notification) then
386 -- Cancel any outstanding notifications for this activity if
387 -- a response is expected.
388 -- (Response expected is signalled by a non-null result)
389 Wf_Item_Activity_Status.Notification_Status(itemtype, itemkey, actid,
390 notid, user);
391 if ((notid is not null) and (lresult <> wf_engine.eng_null)) then
392 begin
393 Wf_Notification.CancelGroup(gid=>notid, timeout=>TRUE);
394 exception
395 when others then
396 -- Ignore any errors from cancelling notifications
397 null;
398 end;
399 end if;
400 end if;
401
402 -- If this is the root process of the item, then exit immediately.
403 Wf_Item.Root_Process(itemtype, itemkey, root, version);
404 rootid := Wf_Process_Activity.RootInstanceId(itemtype, itemkey, root);
405 if (actid = rootid) then
406 return;
407 end if;
408
409 -- Also exit immediately if parent process no longer active.
410 -- This is to:
411 -- 1. avoid re-completing the parent if this happens to be
412 -- an end activity (immediately below).
413 -- 2. avoid creating confusing COMPLETE/#FORCE rows in process_activity
414 -- for activities following this one.
415 -- SYNCHMODE: No need to check, parent must always be active.
416 if (itemkey <> wf_engine.eng_synch) then
417 Wf_Item_Activity_Status.Status(itemtype, itemkey,
418 Wf_Engine_Util.Activity_Parent_Process(itemtype, itemkey, actid),
419 parent_status);
420 if (parent_status in (wf_engine.eng_completed, wf_engine.eng_error)) then
421 return;
422 end if;
423 end if;
424
425 -- Check if this is an ending activity.
426 -- If so, then also complete the parent process.
427 -- You can also exit immediately, because,
428 -- 1. If this is a process activity, then completing the parent process
429 -- will complete all of its children recursively, so there is no
430 -- need for this process to kill it's children. Instead, the
431 -- complete_activity is allowed to filter up to the top-level
432 -- ending process, which kills all the children in its tree in one shot.
433 -- 2. There are no transitions out of an ending process.
434 if (Wf_Activity.Ending(actid, actdate)) then
435
436 -- SS: Get the result code to complete the parent process with.
437 -- The result for the parent process will always be the default_result
438 -- of the ending activity, regardless of the result of the activity
439 -- itself.
440
441 WF_CACHE.GetProcessActivity(complete_activity.actid, status);
442
443 if (status <> WF_CACHE.task_SUCCESS) then
444 select WPA.PROCESS_ITEM_TYPE, WPA.PROCESS_NAME, WPA.PROCESS_VERSION,
445 WPA.ACTIVITY_ITEM_TYPE, WPA.ACTIVITY_NAME, WPA.INSTANCE_ID,
446 WPA.INSTANCE_LABEL, WPA.PERFORM_ROLE, WPA.PERFORM_ROLE_TYPE,
447 WPA.START_END, WPA.DEFAULT_RESULT
448 into WF_CACHE.ProcessActivities(complete_activity.actid)
449 from WF_PROCESS_ACTIVITIES WPA
450 where WPA.INSTANCE_ID = complete_activity.actid;
451
452 end if;
453
454 -- Complete the parent process and return immediately.
455 Wf_Engine_Util.Complete_Activity(itemtype, itemkey,
456 Wf_Engine_Util.Activity_Parent_Process(itemtype, itemkey, actid),
457 WF_CACHE.ProcessActivities(complete_activity.actid).DEFAULT_RESULT);
458 return;
459 end if;
460
461 --<rwunderl:2412971>
462 -- Check WF_CACHE
463 WF_CACHE.GetActivityTransitions(FromActID=>actid,
464 result=>lresult,
465 status=>status,
466 watIND=>watIND);
467
468
469 if (status <> WF_CACHE.task_SUCCESS) then
470 -- The transitions for this activity/result is not in cache, so we will
471 -- store them using a for loop to get all the next transition activities.
472 -- Then we will access the list from cache to avoid maximum open cursor
473 -- problem. First we need to retain the base index to be used later.
474 l_baseLnk := watIND;
475 l_linkCollision := FALSE;
476 for child in children(actid, lresult) loop
477 if (TransitionCount > 0) then --Second and succeeding iterations
478 --We will locally store the record index from the last loop iteration.
479 l_prevLnk := watIND;
480 --We will now generate an index for the next transition from the
481 --actid, lresult, and the current TO_PROCESS_ACTIVITY.
482 watIND := WF_CACHE.HashKey(actid||':'||lresult||':'||
483 WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY);
484 --Check to make sure a record is not already here.
485 if (WF_CACHE.ActivityTransitions.EXISTS(watIND)) then
486 if ((WF_CACHE.ActivityTransitions(watIND).FROM_PROCESS_ACTIVITY <>
487 child.FROM_PROCESS_ACTIVITY) or
488 (WF_CACHE.ActivityTransitions(watIND).RESULT_CODE <>
489 child.RESULT_CODE) or
490 (WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY <>
491 child.TO_PROCESS_ACTIVITY)) then
492 l_linkCollision := TRUE; --We will continue
493 --populating this linked list, but after
494 --we use it, we will clear the pl/sql table.
495 end if;
496 end if;
497
498 --Now the PL/SQL table index has moved to the next link, so we will
499 --populate the prev_lnk with our locally stored index. This feature,
500 --not yet used, allows us to traverse backwards through the link list
501 --if needed. Since it is not yet used, it is commented out.
502 --WF_CACHE.ActivityTransitions(watIND).PREV_LNK := l_prevLnk;
503
504 --l_prevLnk represents the index of the previous record, and we need
505 --to update its NEXT_LNK field with the current index.
506 WF_CACHE.ActivityTransitions(l_prevLnk).NEXT_LNK := watIND;
507 -- else
508 -- WF_CACHE.ActivityTransitions(watIND).PREV_LNK := -1;
509
510 end if;
511
512 WF_CACHE.ActivityTransitions(watIND).FROM_PROCESS_ACTIVITY :=
513 child.FROM_PROCESS_ACTIVITY;
514
515 WF_CACHE.ActivityTransitions(watIND).RESULT_CODE := child.RESULT_CODE;
516
517 WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY :=
518 child.TO_PROCESS_ACTIVITY;
519
520 TransitionCount := TransitionCount+1;
521 end loop;
522 WF_CACHE.ActivityTransitions(watIND).NEXT_LNK := -1;
523 watIND := l_baseLnk; --Reset the index back to the beginning.
524 status := WF_CACHE.task_SUCCESS; --We now have the records successfully
525 --in cache.
526
527 end if;
528
529 -- Load a local InstanceArrayTyp, we do this because of the recursion that
530 -- occurs. Since the ActivityTransitions Cache is global, any hashCollision
531 -- would clear the cache and could cause problems as we process activities.
532 while (watIND <> -1) loop
533 childarr(i) := WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY;
534 i := i+1;
535 watIND := WF_CACHE.ActivityTransitions(watIND).NEXT_LNK;
536 end loop;
537 childarr(i) := '';
538
539 if (l_linkCollision) then
540 --When populating the linked list, we discovered that a hash collision
541 --caused us to overwrite a link belonging to another list. This would
542 --cause the other list to be incorrect. We will clear the table so the
543 --lists will be rebuilt after this transaction.
544 WF_CACHE.ActivityTransitions.DELETE;
545
546 end if;
547 --</rwunderl:2412971>
548
549 -- SYNCHMODE: Check for branching.
550 -- If more than one transition out, this is an illegal branch point.
551 if ((itemkey = wf_engine.eng_synch) and (i > 1)) then
552 Wf_Core.Token('ACTID', to_char(actid));
553 Wf_Core.Token('RESULT', lresult);
554 Wf_Core.Raise('WFENG_SYNCH_BRANCH');
555 end if;
556
557 i := 0;
558 -- While loop to hande the next transition activities.
559 while (childarr(i) is not NULL) loop
560 Wf_Engine_Util.Process_Activity(itemtype, itemkey,
561 childarr(i),
562 WF_ENGINE.THRESHOLD);
563 i := i+1;
564 end loop;
565
566 exception
567 when others then
568 Wf_Core.Context('Wf_Engine_Util', 'Complete_Activity', itemtype, itemkey,
569 actid, result);
570 raise;
571 end complete_activity;
572
573 ------------------------------------------------------------------
574 --Bug 2259039
575 --The start process code is consolidated into the new API
576 --start_process_internal.
577 ------------------------------------------------------------------
578 --
579 -- Start_Process_Internal
580 -- Begins execution of the process. The process will be identified by the
581 -- itemtype and itemkey. The engine locates the starting activities
582 -- of the root process and executes them.
583 -- IN
584 -- itemtype - A valid item type
585 -- itemkey - Item Key
586 -- runmode - Start mode. Valid values are:
587 -- START : a valid startprocess
588 -- ACTIVITY : called in complete_activity
589 -- EVENT : when process is started from a receive event.
590 --
591 procedure Start_Process_Internal(
592 itemtype in varchar2,
593 itemkey in varchar2,
594 runmode in varchar2)
595 is
596 -- Select all the start activities in this parent process with
597 -- no in-transitions.
598 cursor starter_children (itemtype in varchar2,
599 process in varchar2,
600 version in number) is
601 SELECT PROCESS_ITEM_TYPE, PROCESS_NAME, PROCESS_VERSION,
602 ACTIVITY_ITEM_TYPE, ACTIVITY_NAME, INSTANCE_ID,
603 INSTANCE_LABEL, PERFORM_ROLE, PERFORM_ROLE_TYPE,
604 START_END, DEFAULT_RESULT
605 FROM WF_PROCESS_ACTIVITIES WPA
606 WHERE WPA.PROCESS_ITEM_TYPE = itemtype
607 AND WPA.PROCESS_NAME = process
608 AND WPA.PROCESS_VERSION = version
609 AND WPA.START_END = wf_engine.eng_start
610 AND NOT EXISTS (
611 SELECT NULL
612 FROM WF_ACTIVITY_TRANSITIONS WAT
613 WHERE WAT.TO_PROCESS_ACTIVITY = WPA.INSTANCE_ID);
614
615 childarr InstanceArrayTyp; -- Place holder for all the instance id
616 -- selected from starter_children cursor
617 i pls_integer := 0; -- Counter for the for loop
618 process varchar2(30) := ''; -- root process activity name
619 version pls_integer; -- root process activity version
620 processid pls_integer;
621 actdate date;
622 rerun varchar2(8); -- Activity rerun flag
623 acttype varchar2(8); -- Activity type
624 cost number; -- Activity cost
625 ftype varchar2(30); -- Activity function type
626 defer_mode boolean := FALSE;
627
628 TransitionCount pls_integer := 0;
629 l_baseLnk NUMBER;
630 l_prevLnk NUMBER;
631 psaIND NUMBER;
632 l_linkCollision BOOLEAN;
633 status PLS_INTEGER;
634
635 trig_savepoint exception;
636 pragma exception_init(trig_savepoint, -04092);
637 dist_savepoint exception;
638 pragma exception_init(dist_savepoint, -02074);
639 begin
640 -- Check if the item exists and also get back the root process name
641 -- and version
642 Wf_Item.Root_Process(itemtype, itemkey, process, version);
643 if (process is null) then
644 Wf_Core.Token('TYPE', itemtype);
645 Wf_Core.Token('KEY', itemkey);
646 Wf_Core.Raise('WFENG_ITEM');
647 end if;
648
649 -- Insert a row for the process into WIAS table.
650 -- Get the id of the process root.
651 processid := Wf_Process_Activity.RootInstanceId(itemtype, itemkey,
652 process);
653 if (processid is null) then
654 Wf_Core.Token('TYPE', itemtype);
655 Wf_Core.Token('NAME', process);
656 Wf_Core.Raise('WFENG_PROCESS_RUNNABLE');
657 end if;
658
659 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, processid,
660 wf_engine.eng_active, wf_engine.eng_null, SYSDATE, null,
661 newStatus=>TRUE);
662
663 -- Initialize process call stack with the root process.
664 Wf_Engine_Util.AddProcessStack(itemtype, itemkey, itemtype, process,
665 processid, TRUE);
666
667 -- Get the cost of the parent process.
668 -- If the cost is over the threshold, then set a flag to immediately
669 -- defer child activities.
670 -- NOTE: Ordinarily it would be ok to let process_activity do the
671 -- job and defer activities if needed, but the savepoint in the loop
672 -- below causes failures if StartProcess is called from a db trigger.
673 -- This is a workaround to avoid the savepoints altogether if
674 -- the process is to be immediately deferred.
675 --
676 --
677 -- SYNCHMODE: Synch processes cannot be deferred.
678 actdate := Wf_Item.Active_Date(itemtype, itemkey);
679 Wf_Activity.Info(processid, actdate, rerun, acttype, cost, ftype);
680 if ((itemkey <> wf_engine.eng_synch) and
681 (cost > wf_engine.threshold)) then
682 defer_mode := TRUE;
683 end if;
684
685 --<rwunderl:2412971>
686 -- Retrieve the starting activities from cache.
687 WF_CACHE.GetProcessStartActivities(itemType=>itemtype,
688 name=>process,
689 version=>version,
690 status=>status,
691 psaIND=>psaIND);
692
693 if (status <> WF_CACHE.task_SUCCESS) then
694 -- Starting activities are not in cache, so we will store them using a for
695 -- loop to get all the next transition activities.
696 -- Then we will access the list from cache to avoid maximum open cursor
697 -- problem. First we need to retain the base index to be used later.
698 l_baseLnk := psaIND;
699 l_linkCollision := FALSE;
700 for child in starter_children(itemtype, process, version) loop
701 if (TransitionCount > 0) then --Second and succeeding iterations
702 --We will locally store the record index from the last loop iteration.
703 l_prevLnk := psaIND;
704 --We will now generate an index for the start activity from the
705 --itemType, name, version, and the current INSTANCE_ID
706 psaIND := WF_CACHE.HashKey(itemType||':'||process||':'||version||
707 ':'||WF_CACHE.ProcessStartActivities(psaIND).INSTANCE_ID);
708
709 --Check to make sure a record is not already here.
710 if (WF_CACHE.ProcessStartActivities.EXISTS(psaIND)) then
711 l_linkCollision := TRUE; --There should be no record here, so this
712 --is a hash collision. We will continue
713 --populating this linked list, but after
714 --we use it, we will clear the pl/sql table
715 end if;
716
717 --Now the PL/SQL table index has moved to the next link, so we will
718 --populate the prev_lnk with our locally stored index. This feature,
719 --not yet used, allows us to traverse backwards through the link list
720 --if needed. Since it is not yet used, it is commented out.
721 --WF_CACHE.ProcessStartActivities(psaIND).PREV_LNK := l_prevLnk;
722
723 --l_prevLnk represents the index of the previous record, and we need
724 --to update its NEXT_LNK field with the current index.
725 WF_CACHE.ProcessStartActivities(l_prevLnk).NEXT_LNK := psaIND;
726 --else
727 -- WF_CACHE.ProcessStartActivities(psaIND).PREV_LNK := -1;
728
729 end if;
730
731 WF_CACHE.ProcessStartActivities(psaIND).PROCESS_ITEM_TYPE :=
732 child.PROCESS_ITEM_TYPE;
733
734 WF_CACHE.ProcessStartActivities(psaIND).PROCESS_NAME :=
735 child.PROCESS_NAME;
736
737 WF_CACHE.ProcessStartActivities(psaIND).PROCESS_VERSION :=
738 child.PROCESS_VERSION;
739
740 WF_CACHE.ProcessStartActivities(psaIND).INSTANCE_ID := child.INSTANCE_ID;
741
742 --While we are here, we can populate the ProcessActivities cache hoping
743 --that a later request of any of these process activities will save us
744 --another trip to the DB.
745 WF_CACHE.ProcessActivities(child.INSTANCE_ID).PROCESS_ITEM_TYPE :=
746 child.PROCESS_ITEM_TYPE;
747 WF_CACHE.ProcessActivities(child.INSTANCE_ID).PROCESS_NAME :=
748 child.PROCESS_NAME;
749 WF_CACHE.ProcessActivities(child.INSTANCE_ID).PROCESS_VERSION :=
750 child.PROCESS_VERSION;
751 WF_CACHE.ProcessActivities(child.INSTANCE_ID).ACTIVITY_ITEM_TYPE :=
752 child.ACTIVITY_ITEM_TYPE;
753 WF_CACHE.ProcessActivities(child.INSTANCE_ID).ACTIVITY_NAME :=
754 child.ACTIVITY_NAME;
755 WF_CACHE.ProcessActivities(child.INSTANCE_ID).INSTANCE_ID :=
756 child.INSTANCE_ID;
757 WF_CACHE.ProcessActivities(child.INSTANCE_ID).INSTANCE_LABEL :=
758 child.INSTANCE_LABEL;
759 WF_CACHE.ProcessActivities(child.INSTANCE_ID).PERFORM_ROLE :=
760 child.PERFORM_ROLE;
761 WF_CACHE.ProcessActivities(child.INSTANCE_ID).PERFORM_ROLE_TYPE :=
762 child.PERFORM_ROLE_TYPE;
763 WF_CACHE.ProcessActivities(child.INSTANCE_ID).START_END :=
764 child.START_END;
765 WF_CACHE.ProcessActivities(child.INSTANCE_ID).DEFAULT_RESULT :=
766 child.DEFAULT_RESULT;
767
768 TransitionCount := TransitionCount+1;
769 end loop;
770 WF_CACHE.ProcessStartActivities(psaIND).NEXT_LNK := -1;
771 psaIND := l_baseLnk; --Reset the index back to the beginning.
772 status := WF_CACHE.task_SUCCESS; --We now have the records successfully
773 --in cache.
774
775 end if;
776
777 -- Load a local InstanceArrayTyp, we do this because of the recursion that
778 -- occurs. Since the ProcessStartActivities Cache is global, any
779 -- hashCollision would clear the cache and could cause problems as we
780 -- process activities in recursive calls.
781 while (psaIND <> -1) loop
782 childarr(i) := WF_CACHE.ProcessStartActivities(psaIND).INSTANCE_ID;
783 i := i+1;
784 psaIND := WF_CACHE.ProcessStartActivities(psaIND).NEXT_LNK;
785 end loop;
786 childarr(i) := '';
787
788 if (l_linkCollision) then
789 --When populating the linked list, we discovered that a hash collision
790 --caused us to overwrite a link belonging to another list. This would
791 --cause the other list to be incorrect. We will clear the table so the
792 --lists will be rebuilt after this transaction.
793 WF_CACHE.ProcessStartActivities.DELETE;
794
795 end if;
796 --</rwunderl:2412971>
797
798 -- SYNCHMODE: Only 1 starter allowed in synch processes
799 if ((itemkey = wf_engine.eng_synch) and
800 (i > 1)) then
801 Wf_Core.Token('ACTID', process);
802 Wf_Core.Token('RESULT', 'START');
803 Wf_Core.Raise('WFENG_SYNCH_BRANCH');
804 end if;
805
806 -- SS: Process all 'true' start activities of this process.
807 -- 'True' start activities are those which are marked as starts,
808 -- and also have no in-transitions.
809 -- Activities with in-transitions may be marked as starters if it
810 -- is possible to jump into the middle of a process with a
811 -- completeactivity call. If this is the case, we don't want to
812 -- separately start these activities when starting the process as
813 -- a whole, because they will presumably already have been executed
814 -- in the flow starting from the 'true' starts.
815 i := 0;
816 while(childarr(i) is not null) loop
817 if (runmode in ('EVENT','ACTIVITY')) then
818 -- For runmode modes, just mark starters as NOTIFIED,
819 -- and thus ready for external input, but don't actually run
820 -- them. Only the start activities matching the specific
821 -- activity/event will be run (below).
822 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, childarr(i),
823 wf_engine.eng_notified, wf_engine.eng_null, SYSDATE, null,
824 newStatus=>TRUE);
825 elsif (defer_mode) then
826 -- Insert child rows as deferred with no further processing
827 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, childarr(i),
828 wf_engine.eng_deferred, wf_engine.eng_null, SYSDATE, null,
829 newStatus=>TRUE);
830 else -- Must be START mode, and not deferred
831 -- Process start activity normally
832 if (itemkey = wf_engine.eng_synch) then
833 -- SYNCHMODE: No fancy error processing!
834 Wf_Engine_Util.Process_Activity(itemtype, itemkey, childarr(i),
835 WF_ENGINE.THRESHOLD);
836 else
837 begin
838 savepoint wf_savepoint;
839 Wf_Engine_Util.Process_Activity(itemtype, itemkey, childarr(i),
840 WF_ENGINE.THRESHOLD);
841 exception
842 when trig_savepoint or dist_savepoint then
843 -- Oops, you forgot to defer your trigger or distributed
844 -- transaction initiated process! I'll do it for you.
845 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey,
846 childarr(i), wf_engine.eng_deferred, wf_engine.eng_null,
847 SYSDATE, null, newStatus=>TRUE);
848 when others then
849 -- If anything in this process raises an exception:
850 -- 1. rollback any work in this process thread
851 -- 2. set this activity to error status
852 -- 3. execute the error process (if any)
853 -- 4. clear the error to continue with next activity
854 rollback to wf_savepoint;
855 Wf_Core.Context('Wf_Engine', 'Start_Process_Internal', itemtype, itemkey);
856 Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, processid,
857 wf_engine.eng_exception, FALSE);
858 Wf_Engine_Util.Execute_Error_Process(itemtype, itemkey, processid,
859 wf_engine.eng_exception);
860 Wf_Core.Clear;
861 return;
862 end;
863 end if;
864 end if;
865 i := i+1;
866 end loop;
867
868 -- Report an error if no start activities can be found.
869 if (i = 0) then
870 Wf_Core.Token('PROCESS', process);
871 Wf_Core.Raise('WFENG_NO_START');
872 end if;
873
874 exception
875 when others then
876 -- Bug 4117740
877 -- Call clearcache() when #SYNCH flow is in error
878 if ((itemkey = WF_ENGINE.eng_synch) and
879 (wf_core.error_name is null or wf_core.error_name <> 'WFENG_SYNCH_ITEM') and
880 (not WF_ENGINE.debug)) then
881 Wf_Item.ClearCache;
882 end if;
883
884 Wf_Core.Context('Wf_Engine_Util', 'Start_Process_Internal',
885 itemtype, itemkey);
886 raise;
887 end Start_Process_Internal;
888
889
890 --
891 -- Process_Activity (PRIVATE)
892 -- Execute a single activity (function, notification, or sub-process),
893 -- after checking parent and activity statuses and conditions.
894 -- IN
895 -- itemtype - A valid item type
896 -- itemkey - A string generated from the application object's primary key.
897 -- actid - The activity instance id.
898 -- threshold - Max cost to process without deferring
899 -- activate - A flag to indicate that if the assigned activity is currently
900 -- active, should process_activity() still process it?
901 --
902 procedure process_activity(
903 itemtype in varchar2,
904 itemkey in varchar2,
905 actid in number,
906 threshold in number,
907 activate in boolean)
908 is
909 actdate date;
910
911 -- Select all the start activities in a process with no in-transitions.
912 cursor starter_children(parent in pls_integer) is
913 SELECT C.INSTANCE_ID
914 FROM WF_PROCESS_ACTIVITIES P, WF_PROCESS_ACTIVITIES C,
915 WF_ACTIVITIES A
916 WHERE P.INSTANCE_ID = parent
917 AND P.ACTIVITY_ITEM_TYPE = C.PROCESS_ITEM_TYPE
918 AND P.ACTIVITY_NAME = C.PROCESS_NAME
919 AND C.PROCESS_VERSION = A.VERSION
920 AND A.NAME = C.PROCESS_NAME
921 AND A.ITEM_TYPE = C.PROCESS_ITEM_TYPE
922 AND actdate >= A.BEGIN_DATE
923 AND actdate < NVL(A.END_DATE, actdate+1)
924 AND C.START_END = wf_engine.eng_start
925 AND NOT EXISTS (
926 SELECT NULL
927 FROM WF_ACTIVITY_TRANSITIONS WAT
928 WHERE WAT.TO_PROCESS_ACTIVITY = C.INSTANCE_ID);
929
930 rerun varchar2(8); -- Activity rerun flag
931 cost number; -- Activity cost
932 status varchar2(8); -- Activity status
933 result varchar2(30); -- Activity result
934 acttype varchar2(8); -- Activity type
935 act_itemtype varchar2(8); -- Activity itemtype
936 act_name varchar2(30); -- Activity name
937 act_functype varchar2(30); -- Activity function type
938 childarr InstanceArrayTyp; -- Place holder for all the instance id
939 -- selected from starter_children cursor
940 i pls_integer := 0;
941
942 trig_savepoint exception;
943 pragma exception_init(trig_savepoint, -04092);
944 dist_savepoint exception;
945 pragma exception_init(dist_savepoint, -02074);
946 begin
947
948 -- Check this activity's parent process
949 -- SYNCHMODE: No need to check parent, will always be active.
950 if (itemkey <> wf_engine.eng_synch) then
951 Wf_Item_Activity_Status.Status(itemtype, itemkey,
952 Wf_Engine_Util.Activity_Parent_Process(itemtype, itemkey, actid),
953 status);
954
955 if (status is null) then
956 -- return WF_PARENT_PROCESS_NOT_RUNNING;
957 -- TO BE UPDATED
958 -- Actually this case should not happen
959 return;
960 elsif ((status = wf_engine.eng_completed) or
961 (status = wf_engine.eng_error)) then
962 -- Mark it as completed cause the parent process is completed/errored
963 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
964 wf_engine.eng_completed, wf_engine.eng_force, sysdate, sysdate);
965 return;
966
967 elsif (status = wf_engine.eng_suspended) then
968 -- Insert this activity as deferred
969 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
970 wf_engine.eng_deferred, null,
971 sysdate, null, suspended=>TRUE);
972 return;
973 elsif (status in (wf_engine.eng_notified, wf_engine.eng_waiting,
974 wf_engine.eng_deferred)) then
975 -- NOTE: This should never happened because the engine will never
976 -- set the status of a process to be 'WAITING' or 'NOTIFIED'
977 -- return;
978 Wf_Core.Token('ITEM_TYPE', itemtype);
979 Wf_Core.Token('ITEM_KEY', itemkey);
980 Wf_Core.Token('PROCESS', to_char(actid));
981 Wf_Core.Token('STATUS', status);
982 Wf_Core.Raise('WFSQL_INTERNAL');
983 end if;
984 end if;
985
986 -- If we came here, that means the parent process is ACTIVE
987
988 -- Get the information of this activity
989 -- Out of these three return variables, cost is the only one could be null
990 actdate := Wf_Item.Active_Date(itemtype, itemkey);
991 Wf_Activity.Info(actid, actdate, rerun, acttype,
992 cost, act_functype);
993
994 -- If this activity is currently active, do nothing
995 -- If this activity has already been completed, check the rerun flag
996 --
997 -- SYNCHMODE: Ignore the current status of the activity. No loop
998 -- reset or other processing is allowed.
999 if (itemkey = wf_engine.eng_synch) then
1000 status := '';
1001 result := '';
1002 else
1003 Wf_Item_Activity_Status.Result(itemtype, itemkey, actid, status, result);
1004 if (status is not null) then
1005 if ( (status = wf_engine.eng_active) AND (activate = FALSE) )then
1006 -- Maybe don't have to do anything because it is running already
1007 return;
1008
1009 -- Bug 2111183
1010 -- resetting activity with status eng_notified prevents a orphaned
1011 -- notification in WF_NOTIFICATIONS if the notification activity
1012 -- is revisited in a loop simultaneously by two incoming transitions
1013
1014 elsif (status in (wf_engine.eng_completed, wf_engine.eng_error,
1015 wf_engine.eng_notified)) then
1016 -- Check the rerun flag to see what should be done
1017 if (rerun = wf_engine.eng_ignore) then
1018 -- No loop - do nothing
1019 return;
1020 elsif (rerun = wf_engine.eng_reset) then
1021 -- Reset activities, cancel mode
1022 Wf_Engine_Util.Reset_Activities(itemtype, itemkey, actid, TRUE);
1023 elsif (rerun = wf_engine.eng_loop) then
1024 -- Reset activities, no-cancel mode
1025 Wf_Engine_Util.Reset_Activities(itemtype, itemkey, actid, FALSE);
1026 end if;
1027 elsif ((status = wf_engine.eng_suspended) AND
1028 (acttype <> wf_engine.eng_process))then
1029 -- Only the process type of activity can have a 'SUSPENDED' status
1030 -- If this is not a process type activity, then THIS IS A PROBLEM
1031 -- CAN NOT DO ANYTHING
1032 Wf_Core.Token('ITEM_TYPE', itemtype);
1033 Wf_Core.Token('ITEM_KEY', itemkey);
1034 Wf_Core.Token('ACTIVITY_TYPE', acttype);
1035 Wf_Core.Token('STATUS', status);
1036 Wf_Core.Raise('WFSQL_INTERNAL');
1037 end if;
1038 end if;
1039 end if;
1040
1041 -- If we came here, we have
1042 -- (1) not yet run this activity before
1043 -- (2) this is a deferred activity
1044 -- (3) this is a waiting activity (including logical_and)
1045 -- (4) this is re-runnable activity and we did a reset already
1046 --
1047 -- SYNCHMODE: Ignore cost, always run process immediately
1048 if ((itemkey = wf_engine.eng_synch) or
1049 (cost is null and act_functype = 'PL/SQL') or
1050 (cost <= nvl(threshold, cost) and act_functype = 'PL/SQL')) then
1051 -- If status is null, we want to create the status
1052 -- If status is not null, we want to update the status back to active
1053 -- except for a suspended process
1054
1055 if (status is null ) then
1056 -- Insert this activity as active into the WIAS table
1057 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
1058 wf_engine.eng_active, null,
1059 sysdate, null, newStatus=>TRUE);
1060
1061 elsif (status <> wf_engine.eng_suspended) then
1062 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
1063 wf_engine.eng_active, null,
1064 sysdate, null, newStatus=>FALSE);
1065 end if;
1066
1067 if (acttype = wf_engine.eng_process) then
1068 -- PROCESS activity
1069 -- Add this subprocess to the call stack
1070 Wf_Process_Activity.ActivityName(actid, act_itemtype, act_name);
1071 Wf_Engine_Util.AddProcessStack(itemtype, itemkey, act_itemtype,
1072 act_name, actid, FALSE);
1073
1074 -- For loop to get all the start activities first.
1075 -- This is to avoid the maximum open cursor problem
1076 for child in starter_children(actid) loop
1077 childarr(i) := child.instance_id;
1078 i := i+1;
1079 end loop;
1080 childarr(i) := '';
1081
1082 -- SYNCHMODE: Only one starter allowed in synch process
1083 if ((itemkey = wf_engine.eng_synch) and (i > 1)) then
1084 Wf_Core.Token('ACTID', act_name);
1085 Wf_Core.Token('RESULT', 'START');
1086 Wf_Core.Raise('WFENG_SYNCH_BRANCH');
1087 end if;
1088
1089 -- While loop to handle all the start activities
1090 i := 0;
1091 while(childarr(i) is not null) loop
1092 Wf_Engine_Util.Process_Activity(itemtype, itemkey, childarr(i),
1093 threshold);
1094 i := i+1;
1095 end loop;
1096 else
1097 -- Function/Notification/Event type activities
1098 begin
1099 Wf_Engine_Util.Execute_Activity(itemtype, itemkey, actid,
1100 wf_engine.eng_run);
1101 exception
1102 when trig_savepoint or dist_savepoint then
1103 -- Oops, you forgot to defer your trigger or distributed
1104 -- transaction initiated process! I'll do it for you.
1105 -- (Note this is only needed here for restarting a
1106 -- process using CompleteActivity, all other will be caught
1107 -- by error handling savepoints before this.)
1108 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey,
1109 actid, wf_engine.eng_deferred, null,
1110 SYSDATE, null, newStatus=>TRUE);
1111 end;
1112 end if;
1113 else
1114 -- Cost is over the threshold or this is a callout function
1115 -- Insert this activity into the WIAS table and mark it as deferred
1116 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
1117 wf_engine.eng_deferred, null,
1118 sysdate, null, newStatus=>TRUE);
1119 end if; -- end if deferred
1120
1121 return;
1122 exception
1123 when others then
1124 Wf_Core.Context('Wf_Engine_Util', 'Process_Activity', itemtype, itemkey,
1125 to_char(actid), to_char(threshold));
1126 raise;
1127 end process_activity;
1128
1129 --
1130 -- Reset_Activities (PRIVATE)
1131 -- Reset completed activities to redo a loop
1132 -- IN
1133 -- itemtype - A valid item type
1134 -- itemkey - A string generated from the application object's primary key.
1135 -- itemactid - The activity instance id.
1136 -- cancel - Cancel the activities before resetting or not
1137 --
1138 procedure reset_activities(itemtype in varchar2,
1139 itemkey in varchar2,
1140 actid in number,
1141 cancel in boolean)
1142 is
1143 actdate date;
1144
1145 -- Select all the start activities for this parent process
1146 cursor starter_children(parent in pls_integer) is
1147 SELECT C.INSTANCE_ID
1148 FROM WF_PROCESS_ACTIVITIES P, WF_PROCESS_ACTIVITIES C,
1149 WF_ACTIVITIES A
1150 WHERE P.INSTANCE_ID = parent
1151 AND P.ACTIVITY_ITEM_TYPE = C.PROCESS_ITEM_TYPE
1152 AND P.ACTIVITY_NAME = C.PROCESS_NAME
1153 AND C.PROCESS_VERSION = A.VERSION
1154 AND A.NAME = C.PROCESS_NAME
1155 AND A.ITEM_TYPE = C.PROCESS_ITEM_TYPE
1156 AND actdate >= A.BEGIN_DATE
1157 AND actdate < NVL(A.END_DATE, actdate+1)
1158 AND C.START_END = wf_engine.eng_start;
1159
1160 -- Select the to activity(ies) by given the from activity and result
1161 cursor to_activities(fromact in pls_integer, fromact_result varchar2) is
1162 SELECT WAT1.FROM_PROCESS_ACTIVITY, WAT1.RESULT_CODE,
1163 WAT1.TO_PROCESS_ACTIVITY
1164 FROM WF_ACTIVITY_TRANSITIONS WAT1
1165 WHERE WAT1.FROM_PROCESS_ACTIVITY = fromact
1166 AND (WAT1.RESULT_CODE in (fromact_result, wf_engine.eng_trans_any)
1167 OR (WAT1.RESULT_CODE = wf_engine.eng_trans_default
1168 AND NOT EXISTS
1169 (SELECT NULL
1170 FROM WF_ACTIVITY_TRANSITIONS WAT2
1171 WHERE WAT2.FROM_PROCESS_ACTIVITY = fromact
1172 AND WAT2.RESULT_CODE = fromact_result)
1173 )
1174 );
1175
1176 childarr InstanceArrayTyp;
1177 i pls_integer := 0; -- counter for the childarr
1178 savearr InstanceArrayTyp; -- Save all the children and then process them
1179 -- at the reversed order
1180 result varchar2(30);
1181 status varchar2(8);
1182 typ varchar2(8);
1183 notid pls_integer;
1184 user varchar2(320);
1185 pntfstatus varchar2(8);
1186 pntfresult varchar2(30);
1187
1188 --<rwunderl:2412971>
1189 TransitionCount pls_integer := 0;
1190 l_baseLnk NUMBER;
1191 l_prevLnk NUMBER;
1192 watIND NUMBER;
1193 l_LinkCollision BOOLEAN;
1194
1195 begin
1196 Wf_Item_Activity_Status.Result(itemtype, itemkey, actid, status, result);
1197
1198 if (status is null) then
1199 return; -- This means the end of a path
1200 end if;
1201
1202 -- Undo the current activity, depending on type
1203 actdate := Wf_Item.Active_Date(itemtype, itemkey);
1204 typ := Wf_Activity.Instance_Type(actid, actdate);
1205 if (typ = wf_engine.eng_process) then
1206 -- For loop to get the starting activities of this process
1207 i := 0;
1208 for child in starter_children(actid) loop
1209 childarr(i) := child.instance_id;
1210 i := i + 1;
1211 end loop;
1212 childarr(i) := '';
1213
1214 -- Reset all starting activities of child process.
1215 i := 0;
1216 while (childarr(i) is not null) loop
1217 Wf_Engine_Util.Reset_Activities(itemtype, itemkey, childarr(i), cancel);
1218 i := i + 1;
1219 end loop;
1220 elsif (typ = wf_engine.eng_notification) then
1221 if (cancel) then
1222 -- Run post-notification function in cancel mode if there is one.
1223 Wf_Engine_Util.Execute_Post_NTF_Function(itemtype, itemkey, actid,
1224 wf_engine.eng_cancel, pntfstatus, pntfresult);
1225
1226 -- Cancel any open notifications sent by this activity
1227 Wf_Item_Activity_Status.Notification_Status(itemtype, itemkey, actid,
1228 notid, user);
1229 if (notid is not null) then
1230 begin
1231 Wf_Notification.CancelGroup(notid);
1232 exception
1233 when others then
1234 null; -- Ignore errors in cancelling
1235 end;
1236 end if;
1237 end if;
1238 elsif (typ in (wf_engine.eng_function, wf_engine.eng_event)) then
1239 if (cancel) then
1240 -- Call function in cancel mode
1241 Wf_Engine_Util.Execute_Activity(itemtype, itemkey, actid,
1242 wf_engine.eng_cancel);
1243 end if;
1244 end if;
1245
1246 -- Move the WIAS record to the history table.
1247 -- Note: Do NOT move this call. The move_to_history() must be before any
1248 -- recursive calls to reset_activities() in the current process,
1249 -- or infinite recursion will result.
1250 Wf_Engine_Util.Move_To_History(itemtype, itemkey, actid);
1251
1252 -- Reset all activities following this one in current process,
1253 -- but only if this activity really completed.
1254 if (status = wf_engine.eng_completed) then
1255 --<rwunderl:2412971>
1256 -- Check WF_CACHE
1257 WF_CACHE.GetActivityTransitions(FromActID=>actid,
1258 result=>result,
1259 status=>status,
1260 watIND=>watIND);
1261
1262
1263 if (status <> WF_CACHE.task_SUCCESS) then
1264 -- The transitions for this activity/result is not in cache, so we will
1265 -- store them using a for loop to get all the next transition activities.
1266 -- Then we will access the list from cache to avoid maximum open cursor
1267 -- problem. First we need to retain the base index to be used later.
1268 l_baseLnk := watIND;
1269 l_linkCollision := FALSE;
1270
1271 for to_activity in to_activities(actid, result) loop
1272 if (TransitionCount > 0) then --Second and succeeding iterations
1273 --We will locally store the record index from the last loop iteration.
1274 l_prevLnk := watIND;
1275 --We will now generate an index for the next transition from the
1276 --actid, result, and the current TO_PROCESS_ACTIVITY.
1277 watIND := WF_CACHE.HashKey(actid||':'||result||':'||
1278 WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY);
1279
1280 --Check to make sure a record is not already here.
1281 if (WF_CACHE.ActivityTransitions.EXISTS(watIND)) then
1282 if ((WF_CACHE.ActivityTransitions(watIND).FROM_PROCESS_ACTIVITY <>
1283 to_activity.FROM_PROCESS_ACTIVITY) or
1284 (WF_CACHE.ActivityTransitions(watIND).RESULT_CODE <>
1285 to_activity.RESULT_CODE) or
1286 (WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY <>
1287 to_activity.TO_PROCESS_ACTIVITY)) then
1288 l_linkCollision := TRUE; --We will continue
1289 --populating this linked list, but after
1290 --we use it, we will clear the pl/sql table.
1291 end if;
1292 end if;
1293
1294 --Now the PL/SQL table index has moved to the next link, so we will
1295 --populate the prev_lnk with our locally stored index. This feature,
1296 --not yet used, allows us to traverse backwards through the link list
1297 --if needed. Since it is not yet used, it is commented out.
1298 -- WF_CACHE.ActivityTransitions(watIND).PREV_LNK := l_prevLnk;
1299
1300 --l_prevLnk represents the index of the previous record, and we need
1301 --to update its NEXT_LNK field with the current index.
1302 WF_CACHE.ActivityTransitions(l_prevLnk).NEXT_LNK := watIND;
1303 -- else
1304 -- WF_CACHE.ActivityTransitions(watIND).PREV_LNK := -1;
1305
1306 end if;
1307
1308 WF_CACHE.ActivityTransitions(watIND).FROM_PROCESS_ACTIVITY :=
1309 to_activity.FROM_PROCESS_ACTIVITY;
1310
1311 WF_CACHE.ActivityTransitions(watIND).RESULT_CODE :=
1312 to_activity.RESULT_CODE;
1313
1314 WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY :=
1315 to_activity.TO_PROCESS_ACTIVITY;
1316
1317 TransitionCount := TransitionCount+1;
1318 end loop;
1319 WF_CACHE.ActivityTransitions(watIND).NEXT_LNK := -1;
1320 watIND := l_baseLnk; --Reset the index back to the beginning.
1321 status := WF_CACHE.task_SUCCESS; --We now have the records successfully
1322 --in cache.
1323
1324 end if;
1325
1326 -- Load a local InstanceArrayTyp, we do this because of the recursion that
1327 -- occurs. Since the ActivityTransitions Cache is global, any hashCollision
1328 -- would clear the cache and could cause problems as we process activities.
1329 while (watIND <> -1) loop
1330 childarr(i) := WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY;
1331 i := i+1;
1332 watIND := WF_CACHE.ActivityTransitions(watIND).NEXT_LNK;
1333 end loop;
1334 childarr(i) := '';
1335
1336 i := 0;
1337 while (childarr(i) is not null) loop
1338 Wf_Engine_Util.Reset_Activities(itemtype, itemkey, childarr(i), cancel);
1339 i := i + 1;
1340 end loop;
1341 end if;
1342
1343 if (l_linkCollision) then
1344 --When populating the linked list, we discovered that a hash collision
1345 --caused us to overwrite a link belonging to another list. This would
1346 --cause the other list to be incorrect. We will clear the table so the
1347 --lists will be rebuilt after this transaction.
1348 WF_CACHE.ActivityTransitions.DELETE;
1349
1350 end if;
1351 --</rwunderl:2412971>
1352 exception
1353 when others then
1354 Wf_Core.Context('Wf_Engine_Util', 'Reset_Activities', itemtype, itemkey,
1355 to_char(actid));
1356 raise;
1357 end reset_activities;
1358
1359 --
1360 -- Reset_Tree (PRIVATE)
1361 -- Reset an activity and all parent activities above it in a tree
1362 -- and prepare for re-execution. Used to reset the process to an
1363 -- arbitrary point in HandleError.
1364 -- IN
1365 -- itemtype - Item type
1366 -- itemkey - Item key
1367 -- rootid - Instance id of process root
1368 -- goalid - Instance id of activity to reset
1369 -- RETURNS
1370 -- TRUE if goalid found
1371 --
1372 function Reset_Tree(
1373 itemtype in varchar2,
1374 itemkey in varchar2,
1375 rootid in number,
1376 goalid in number,
1377 actdate in date)
1378 return boolean is
1379
1380 -- Cursor to select children of activity
1381 cursor children(parentid in pls_integer, actdate in date) is
1382 select WPA2.INSTANCE_ID
1383 from WF_PROCESS_ACTIVITIES WPA1,
1384 WF_ACTIVITIES WA,
1385 WF_PROCESS_ACTIVITIES WPA2
1386 where WPA1.INSTANCE_ID = parentid
1387 and WPA2.PROCESS_ITEM_TYPE = WA.ITEM_TYPE
1388 and WPA2.PROCESS_NAME = WA.NAME
1389 and WA.ITEM_TYPE = WPA1.ACTIVITY_ITEM_TYPE
1390 and WA.NAME = WPA1.ACTIVITY_NAME
1391 and actdate >= WA.BEGIN_DATE
1392 and actdate < NVL(WA.END_DATE, actdate+1)
1393 and WPA2.PROCESS_VERSION = WA.VERSION;
1394
1395 childarr InstanceArrayTyp;
1396 i number := 0;
1397
1398 -- Cursor to select following activities
1399 cursor to_activities(fromact in pls_integer, fromact_result in varchar2) is
1400 SELECT WAT1.FROM_PROCESS_ACTIVITY, WAT1.RESULT_CODE,
1401 WAT1.TO_PROCESS_ACTIVITY
1402 FROM WF_ACTIVITY_TRANSITIONS WAT1
1403 WHERE WAT1.FROM_PROCESS_ACTIVITY = fromact
1404 AND (WAT1.RESULT_CODE in (fromact_result, wf_engine.eng_trans_any)
1405 OR (WAT1.RESULT_CODE = wf_engine.eng_trans_default
1406 AND NOT EXISTS
1407 (SELECT NULL
1408 FROM WF_ACTIVITY_TRANSITIONS WAT2
1409 WHERE WAT2.FROM_PROCESS_ACTIVITY = fromact
1410 AND WAT2.RESULT_CODE = fromact_result)
1411 )
1412 );
1413
1414 actarr InstanceArrayTyp;
1415 j pls_integer := 0;
1416
1417 status varchar2(8);
1418 result varchar2(30);
1419
1420 --<rwunderl:2412971>
1421 TransitionCount pls_integer := 0;
1422 l_baseLnk NUMBER;
1423 l_prevLnk NUMBER;
1424 watIND NUMBER;
1425 l_LinkCollision BOOLEAN;
1426
1427 begin
1428 -- Goal has been found. Reset the activity and all following it on this
1429 -- level, then set status to waiting for possible re-execution.
1430 if (rootid = goalid) then
1431 Wf_Engine_Util.Reset_Activities(itemtype, itemkey, goalid, TRUE);
1432 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, goalid,
1433 wf_engine.eng_active, wf_engine.eng_null, sysdate, null);
1434 return(TRUE);
1435 end if;
1436
1437 -- Otherwise, loop through children of rootid.
1438 for child in children(rootid, actdate) loop
1439 childarr(i) := child.instance_id;
1440 i := i + 1;
1441 end loop;
1442 childarr(i) := '';
1443
1444 i := 0;
1445 while (childarr(i) is not null) loop
1446 -- Check if goal is in the subtree rooted at this child
1447 if (Wf_Engine_Util.Reset_Tree(itemtype, itemkey, childarr(i), goalid,
1448 actdate)) then
1449
1450 -- Goal has been found in a child of this activity.
1451 Wf_Item_Activity_Status.Result(itemtype, itemkey, rootid,
1452 status, result);
1453
1454 -- Reset any activities FOLLOWING the root.
1455 -- Do not reset the root itself - it is a process and its children
1456 -- were already reset in the recursive call.
1457 -- Likewise, do not reset actual child - it has already been reset.
1458 if (status = wf_engine.eng_completed) then
1459 --<rwunderl:2412971>
1460 -- Check WF_CACHE
1461 WF_CACHE.GetActivityTransitions(FromActID=>rootid,
1462 result=>result,
1463 status=>status,
1464 watIND=>watIND);
1465
1466 if (status <> WF_CACHE.task_SUCCESS) then
1467 -- The transitions for this activity/result is not in cache, so we will
1468 -- store them using a for loop to get all the next transition
1469 -- activities. Then we will access the list from cache to avoid
1470 -- maximum open cursor problem. First we need to retain the base index
1471 -- to be used later.
1472 l_baseLnk := watIND;
1473 l_linkCollision := FALSE;
1474 for to_activity in to_activities(rootid, result) loop
1475 if (TransitionCount > 0) then --Second and succeeding iterations
1476 --We will locally store the record index from the last loop
1477 --iteration.
1478 l_prevLnk := watIND;
1479
1480 --We will now generate an index for the next transition from the
1481 --actid, result, and the current TO_PROCESS_ACTIVITY.
1482 watIND := WF_CACHE.HashKey(rootid||':'||result||':'||
1483 WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY);
1484 --Check to make sure a record is not already here.
1485 if (WF_CACHE.ActivityTransitions.EXISTS(watIND)) then
1486 if ((WF_CACHE.ActivityTransitions(watIND).FROM_PROCESS_ACTIVITY <>
1487 to_activity.FROM_PROCESS_ACTIVITY) or
1488 (WF_CACHE.ActivityTransitions(watIND).RESULT_CODE <>
1489 to_activity.RESULT_CODE) or
1490 (WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY <>
1491 to_activity.TO_PROCESS_ACTIVITY)) then
1492 l_linkCollision := TRUE; --We will continue
1493 --populating this linked list, but after
1494 --we use it, we will clear the pl/sql
1495 --table.
1496 end if;
1497 end if;
1498
1499 --Now the PL/SQL table index has moved to the next link, so we
1500 --will populate the prev_lnk with our locally stored index.
1501 --This feature, not yet used, allows us to traverse backwards
1502 --through the link list if needed. Since it is not yet used,
1503 --it is commented out.
1504 -- WF_CACHE.ActivityTransitions(watIND).PREV_LNK := l_prevLnk;
1505
1506 --l_prevLnk represents the index of the previous record, and we
1507 --need to update its NEXT_LNK field with the current index.
1508 WF_CACHE.ActivityTransitions(l_prevLnk).NEXT_LNK := watIND;
1509 -- else
1510 -- WF_CACHE.ActivityTransitions(watIND).PREV_LNK := -1;
1511
1512 end if;
1513
1514 WF_CACHE.ActivityTransitions(watIND).FROM_PROCESS_ACTIVITY :=
1515 to_activity.FROM_PROCESS_ACTIVITY;
1516
1517 WF_CACHE.ActivityTransitions(watIND).RESULT_CODE :=
1518 to_activity.RESULT_CODE;
1519
1520 WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY :=
1521 to_activity.TO_PROCESS_ACTIVITY;
1522
1523 TransitionCount := TransitionCount+1;
1524 end loop;
1525 WF_CACHE.ActivityTransitions(watIND).NEXT_LNK := -1;
1526 watIND := l_baseLnk; --Reset the index back to the beginning.
1527 status := WF_CACHE.task_SUCCESS; --We now have the records
1528 --successfully in cache.
1529 end if;
1530
1531 j := 0;
1532 -- Load a local InstanceArrayTyp, we do this because of the recursion
1533 -- that occurs. Since the ActivityTransitions Cache is global, any
1534 -- hashCollision would clear the cache and could cause problems as we
1535 -- process activities.
1536 while (watIND <> -1) loop
1537 actarr(j) := WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY;
1538 j := j+1;
1539 watIND := WF_CACHE.ActivityTransitions(watIND).NEXT_LNK;
1540 end loop;
1541 actarr(j) := '';
1542
1543 j := 0;
1544 while (actarr(j) is not null) loop
1545 Wf_Engine_Util.Reset_Activities(itemtype, itemkey, actarr(j), TRUE);
1546 j := j + 1;
1547 end loop;
1548 end if;
1549
1550 if (l_linkCollision) then
1551 --When populating the linked list, we discovered that a hash collision
1552 --caused us to overwrite a link belonging to another list. This would
1553 --cause the other list to be incorrect. We will clear the table so the
1554 --lists will be rebuilt after this transaction.
1555 WF_CACHE.ActivityTransitions.DELETE;
1556
1557 end if;
1558 --</rwunderl:2412971>
1559
1560 -- Set the root activity status to active if not already
1561 if (nvl(status, 'x') <> wf_engine.eng_active) then
1562 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, rootid,
1563 wf_engine.eng_active, wf_engine.eng_null, sysdate, null);
1564 end if;
1565
1566 -- Goal has been found, so exit now
1567 return(TRUE);
1568 end if;
1569
1570 i := i + 1;
1571 end loop;
1572
1573 -- Goal not found anywhere.
1574 return(FALSE);
1575 exception
1576 when others then
1577 Wf_Core.Context('Wf_Engine_Util', 'Reset_Tree', itemtype, itemkey,
1578 to_char(rootid), to_char(goalid), to_char(actdate));
1579 raise;
1580 end Reset_Tree;
1581
1582 --
1583 -- Move_To_History (PRIVATE)
1584 -- Move the item activity status row from WF_ITEM_ACTIVITY_STATUSES to
1585 -- WF_ITEM_ACTIVITY_STATUSES_H table.
1586 -- IN
1587 -- itemtype - A valid item type from (WF_ITEM_TYPES table).
1588 -- itemkey - A string generated from the application object's primary key.
1589 -- actid - The activity instance id.
1590 --
1591 procedure move_to_history(itemtype in varchar2,
1592 itemkey in varchar2,
1593 actid in number) is
1594 begin
1595
1596 -- Copy row to history table, changing status to COMPLETE/#FORCE
1597 -- if status is not already complete.
1598 INSERT INTO WF_ITEM_ACTIVITY_STATUSES_H (
1599 ITEM_TYPE,
1600 ITEM_KEY,
1601 PROCESS_ACTIVITY,
1602 ACTIVITY_STATUS,
1603 ACTIVITY_RESULT_CODE,
1604 ASSIGNED_USER,
1605 NOTIFICATION_ID,
1606 OUTBOUND_QUEUE_ID,
1607 BEGIN_DATE,
1608 END_DATE,
1609 DUE_DATE,
1610 EXECUTION_TIME,
1611 ERROR_NAME,
1612 ERROR_MESSAGE,
1613 ERROR_STACK,
1614 ACTION,
1615 PERFORMED_BY
1616 ) SELECT
1617 ITEM_TYPE,
1618 ITEM_KEY,
1619 PROCESS_ACTIVITY,
1620 wf_engine.eng_completed,
1621 decode(ACTIVITY_STATUS,
1622 wf_engine.eng_completed, ACTIVITY_RESULT_CODE,
1623 wf_engine.eng_force),
1624 ASSIGNED_USER,
1625 NOTIFICATION_ID,
1626 OUTBOUND_QUEUE_ID,
1627 nvl(BEGIN_DATE, sysdate),
1628 nvl(END_DATE, sysdate),
1629 DUE_DATE,
1630 EXECUTION_TIME,
1631 ERROR_NAME,
1632 ERROR_MESSAGE,
1633 ERROR_STACK,
1634 ACTION,
1635 PERFORMED_BY
1636 FROM WF_ITEM_ACTIVITY_STATUSES
1637 WHERE ITEM_TYPE = itemtype
1638 AND ITEM_KEY = itemkey
1639 AND PROCESS_ACTIVITY = actid;
1640
1641 -- 3966635 Workflow Provisioning Project
1642 -- Insert added so as not to loose the change. This insert
1643 -- should replace the one above.
1644 -- INSERT INTO WF_ITEM_ACTIVITY_STATUSES_H (
1645 -- ITEM_TYPE,
1646 -- ITEM_KEY,
1647 -- PROCESS_ACTIVITY,
1648 -- ACTIVITY_STATUS,
1649 -- ACTIVITY_RESULT_CODE,
1650 -- ASSIGNED_USER,
1651 -- NOTIFICATION_ID,
1652 -- OUTBOUND_QUEUE_ID,
1653 -- BEGIN_DATE,
1654 -- END_DATE,
1655 -- DUE_DATE,
1656 -- EXECUTION_TIME,
1657 -- ERROR_NAME,
1658 -- ERROR_MESSAGE,
1659 -- ERROR_STACK,
1660 -- ACTION,
1661 -- PERFORMED_BY,
1662 -- PROV_REQUEST_ID
1663 --) SELECT
1664 -- ITEM_TYPE,
1665 -- ITEM_KEY,
1666 -- PROCESS_ACTIVITY,
1667 -- wf_engine.eng_completed,
1668 -- decode(ACTIVITY_STATUS,
1669 -- wf_engine.eng_completed, ACTIVITY_RESULT_CODE,
1670 -- wf_engine.eng_force),
1671 -- ASSIGNED_USER,
1672 -- NOTIFICATION_ID,
1673 -- OUTBOUND_QUEUE_ID,
1674 -- nvl(BEGIN_DATE, sysdate),
1675 -- nvl(END_DATE, sysdate),
1676 -- DUE_DATE,
1677 -- EXECUTION_TIME,
1678 -- ERROR_NAME,
1679 -- ERROR_MESSAGE,
1680 -- ERROR_STACK,
1681 -- ACTION,
1682 -- PERFORMED_BY,
1683 -- PROV_REQUEST_ID
1684 --FROM WF_ITEM_ACTIVITY_STATUSES
1685 --WHERE ITEM_TYPE = itemtype
1686 --AND ITEM_KEY = itemkey
1687 --AND PROCESS_ACTIVITY = actid;
1688
1689 if (Wf_Engine.Debug) then
1690 commit;
1691 end if;
1692
1693 Wf_Item_Activity_Status.Delete_Status(itemtype, itemkey, actid);
1694
1695 EXCEPTION
1696 when OTHERS then
1697 Wf_Core.Context('Wf_Engine_Util', 'Move_To_History', itemtype, itemkey,
1698 to_char(actid));
1699 raise;
1700
1701 END move_to_history;
1702
1703 --
1704 -- Execute_Activity (PRIVATE)
1705 -- Execute a notification or function activity and process the result.
1706 -- IN
1707 -- itemtype - A valid item type from (WF_ITEM_TYPES table).
1708 -- itemkey - A string generated from the application object's primary key.
1709 -- actid - The activity instance id.
1710 -- funmode - function mode (RUN/CANCEL/TIMEOUT)
1711 --
1712 procedure execute_activity(itemtype in varchar2,
1713 itemkey in varchar2,
1714 actid in number,
1715 funmode in varchar2)
1716 is
1717 funcname varchar2(240); -- Name of activity function
1718 result varchar2(370); -- Function result
1719 id varchar2(30); -- Id for error code or notification id
1720 notuser varchar2(320); -- Notification user
1721 col1 pls_integer;
1722 col2 pls_integer;
1723 actdate date;
1724 acttype varchar2(8);
1725 resume_date date;
1726 begin
1727 actdate := Wf_Item.Active_Date(itemtype, itemkey);
1728 acttype := Wf_Activity.Instance_Type(actid, actdate);
1729
1730 if (acttype = wf_engine.eng_function) then
1731 funcname := Wf_Activity.Activity_Function(itemtype, itemkey, actid);
1732 if (funcname is null) then
1733 Wf_Core.Token('TYPE', itemtype);
1734 Wf_Core.Token('NAME', to_char(actid));
1735 Wf_Core.Raise('WFENG_ACTIVITY_FUNCTION');
1736 end if;
1737 end if;
1738
1739 -- Execute the activity function
1740 Wf_Core.Clear;
1741 begin
1742 if (acttype = wf_engine.eng_notification) then
1743 Wf_Engine_Util.Notification(itemtype, itemkey, actid, funmode, result);
1744 elsif (acttype = wf_engine.eng_function) then
1745 Wf_Engine_Util.Function_Call(funcname, itemtype, itemkey, actid,
1746 funmode, result);
1747 elsif (acttype = wf_engine.eng_event) then
1748 Wf_Engine_Util.Event_Activity(itemtype, itemkey, actid, funmode, result);
1749 else
1750 -- Bad activity type, don't know how to execute.
1751 Wf_Core.Token('ITEM_TYPE', itemtype);
1752 Wf_Core.Token('ITEM_KEY', itemkey);
1753 Wf_Core.Token('ACTIVITY_ID', to_char(actid));
1754 Wf_Core.Token('ACTIVITY_TYPE', acttype);
1755 Wf_Core.Raise('WFSQL_INTERNAL');
1756 end if;
1757 exception
1758 when others then
1759 if (itemkey = wf_engine.eng_synch) then
1760 -- SYNCHMODE: No saved errors allowed.
1761 -- Raise exception directly to calling process.
1762 raise;
1763 elsif (funmode <> wf_engine.eng_cancel) then
1764 -- Set error info columns if activity function raised exception,
1765 -- unless running in cancel mode.
1766 Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, actid,
1767 wf_engine.eng_exception, FALSE);
1768 result := wf_engine.eng_error||':'||wf_engine.eng_exception;
1769 end if;
1770 end;
1771
1772 -- The engine does not care about the result when undoing a function
1773 if (funmode = wf_engine.eng_cancel) then
1774 return;
1775 end if;
1776
1777 -- Possible results :
1778 -- ERROR[:errcode]
1779 -- WAITING
1780 -- DEFERRED[:resume_date]
1781 -- NOTIFIED[:notid:user]
1782 -- COMPLETE[:result]
1783 -- result -> this implies COMPLETE:result
1784
1785 -- Handle different results
1786 if (substr(result, 1, length(wf_engine.eng_error)) =
1787 wf_engine.eng_error) then
1788 -- Get the error code
1789 id := substr(result, length(wf_engine.eng_error)+2, 30);
1790 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
1791 wf_engine.eng_error, id);
1792
1793 -- Call error_process to execute any error processes.
1794 Wf_Engine_Util.Execute_Error_Process(itemtype, itemkey, actid, id);
1795
1796 elsif (result = wf_engine.eng_waiting) then
1797 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
1798 wf_engine.eng_waiting, '', to_date(NULL), to_date(NULL));
1799
1800 elsif (substr(result, 1, length(wf_engine.eng_deferred)) =
1801 wf_engine.eng_deferred) then
1802 -- Extract the resume_date if one was returned
1803 col1 := instr(result, ':', 1, 1);
1804 if (col1 <> 0) then
1805 resume_date := to_date(substr(result, col1+1), wf_engine.date_format);
1806 else
1807 resume_date := to_date(NULL);
1808 end if;
1809
1810 -- Set the status to 'DEFERRED', and reset the begin_date to the
1811 -- extracted resume_date if there is one.
1812 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
1813 wf_engine.eng_deferred, wf_engine.eng_null, resume_date,
1814 to_date(NULL));
1815
1816 elsif (substr(result, 1, length(wf_engine.eng_notified)) =
1817 wf_engine.eng_notified) then
1818 -- Get the notification id and user
1819 col1 := instr(result, ':', 1, 1);
1820 col2 := instr(result, ':', 1, 2);
1821 if ((col1 <> 0) and (col2 <> 0)) then
1822 id := substr(result, col1+1, col2-col1-1);
1823 notuser := substr(result, col2+1, 320);
1824
1825 -- Set notification id and user, but only if not null.
1826 -- This is to allow for pseudo-notifications that are only blocking
1827 -- waiting for external completion.
1828 if (nvl(id, wf_engine.eng_null) <> wf_engine.eng_null) then
1829 Wf_Item_Activity_Status.Update_Notification(itemtype, itemkey, actid,
1830 to_number(id), notuser);
1831 end if;
1832 end if;
1833
1834 if ((nvl(id, wf_engine.eng_null) <> wf_engine.eng_null) and
1835 not Wf_Notification.OpenNotificationsExist(id)) then
1836 -- Notification has already been closed, presumably by an
1837 -- auto-routing rule that has already submitted the response.
1838 -- If this is the case, the notification has been responded to
1839 -- and is closed, but CB did NOT continue execution following
1840 -- completion (see comments in 'complete' processing in CB).
1841 -- Call complete_activity here to continue processing immediately
1842 -- instead of just marking activity as notified.
1843
1844 result := Wf_Engine.GetItemAttrText(itemtype, itemkey, 'RESULT');
1845 Wf_Engine_Util.Complete_Activity(itemtype, itemkey, actid, result);
1846
1847 else
1848 -- Notification not auto-routed, or pseudo-notification.
1849 -- In either case, mark status NOTIFIED to block execution.
1850 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
1851 wf_engine.eng_notified, '', to_date(NULL),to_date(NULL));
1852 end if;
1853
1854 else -- Assume COMPLETE
1855 -- Strip off optional 'COMPLETE:' tag
1856 if (substr(result, 1, length(wf_engine.eng_completed)+1) =
1857 wf_engine.eng_completed||':') then
1858 result := substr(result, length(wf_engine.eng_completed)+2, 30);
1859 else
1860 result := substr(result, 1, 30);
1861 end if;
1862
1863 Wf_Engine_Util.Complete_Activity(itemtype, itemkey, actid, result);
1864 end if;
1865
1866 exception
1867 when others then
1868 Wf_Core.Context('Wf_Engine_Util', 'Execute_Activity', itemtype, itemkey,
1869 to_char(actid), funmode);
1870 raise;
1871 end execute_activity;
1872
1873 --
1874 -- Function_Call (PRIVATE)
1875 -- Call an arbitrary function using dynamic sql.
1876 -- The function must conform to Workflow interface standard.
1877 -- IN
1878 -- funname - The name of the function that is going to be executed.
1879 -- itemtype - A valid item type from (WF_ITEM_TYPES table).
1880 -- itemkey - A string generated from the application object's primary key.
1881 -- actid - The activity instance id.
1882 -- funmode - Function mode (RUN/CANCEL/TIMEOUT)
1883 -- OUT
1884 -- result - The result of executing this function.
1885 --
1886 procedure function_call(funname in varchar2,
1887 itemtype in varchar2,
1888 itemkey in varchar2,
1889 actid in number,
1890 funmode in varchar2,
1891 result out NOCOPY varchar2)
1892 is
1893 sqlbuf varchar2(2000);
1894 temp varchar2(120);
1895 defer_mode boolean := false;
1896 setctx_mode boolean := false;
1897 acttype varchar2(8);
1898 actdate date;
1899 executed boolean;
1900 begin
1901 --<6133861:rwunderl> If this is a no-op, we do not need to perform any
1902 --processing including the selector function.
1903 if ( upper(funname) = 'WF_STANDARD.NOOP') then
1904 result := wf_engine.eng_completed||':'||wf_engine.eng_null;
1905 return;
1906 end if;
1907
1908 begin
1909 begin
1910 savepoint do_execute;
1911 -- bug 4307516
1912 -- do not run set context via selector function within a post-
1913 -- notification function
1914 if (Wf_Engine.context_nid is null) then
1915 -- First initialize context if not already done in this session
1916 if (wf_engine.setctx_itemtype is null or
1917 wf_engine.setctx_itemtype <> itemtype or
1918 wf_engine.setctx_itemkey is null or
1919 wf_engine.setctx_itemkey <> itemkey) then
1920 -- Context is not set, call selector.
1921 -- NOTE: Be sure to set setctx globals BEFORE calling
1922 -- execute_selector_function or recursive loop will develop.
1923 wf_engine.setctx_itemtype := itemtype;
1924 wf_engine.setctx_itemkey := itemkey;
1925
1926 -- check TEST_CTX
1927 temp := Wf_Engine_Util.Execute_Selector_Function(itemtype, itemkey,
1928 wf_engine.eng_testctx);
1929 if (nvl(temp, 'TRUE') = 'TRUE' ) then
1930 -- it does not care about the context (null)
1931 -- or the context is already correct ('TRUE')
1932 -- do nothing in either case
1933 null;
1934 elsif (temp = 'FALSE') then
1935 if (wf_engine.preserved_context) then
1936 defer_mode := true;
1937 --<rwunderl:5971238> Unset the itemType/itemKey context since we
1938 --are deferring this item to the background engine and the
1939 --selector has not really been called in set_ctx mode.
1940 wf_engine.setctx_itemtype := null;
1941 wf_engine.setctx_itemkey := null;
1942
1943 else
1944 setctx_mode := true;
1945 end if;
1946 elsif (temp = 'NOTSET') then
1947 setctx_mode := true;
1948 end if;
1949 end if;
1950
1951 if (defer_mode) then
1952 -- defer to background engine means return a result of 'DEFERRED'
1953 -- do not run the actual function, return right away.
1954 result := wf_engine.eng_deferred;
1955 return;
1956 end if;
1957
1958 if (setctx_mode) then
1959 temp := Wf_Engine_Util.Execute_Selector_Function(itemtype, itemkey,
1960 wf_engine.eng_setctx);
1961 end if;
1962 end if;
1963
1964 Wf_Core.Clear;
1965
1966 temp := '012345678901234567890123456789012345678901234567890123456789'||
1967 '012345678901234567890123456789012345678901234567890123456789';
1968
1969 if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
1970 wf_log_pkg.string2(wf_log_pkg.level_statement,
1971 'wf.plsql.wf_engine_util.function_call.actfunc_callout',
1972 'Start executing PLSQL procedure - '||funname, true);
1973 end if;
1974
1975 Wf_Function_Call.Execute(funname, itemtype, itemkey, actid, funmode,
1976 temp, executed);
1977
1978 if (not executed) then
1979 -- Funname came from info entered through builder
1980 -- We may further check if there are ilegal characters like ';'
1981 -- However, this may cause performance impact. Maybe better
1982 -- verify somewhere else first.
1983 -- BINDVAR_SCAN_IGNORE
1984 sqlbuf := 'begin ' || funname || ' (:v1, :v2, :v3, :v4, :v5); end;';
1985 execute immediate sqlbuf using
1986 in itemtype,
1987 in itemkey,
1988 in actid,
1989 in funmode,
1990 in out temp;
1991 end if;
1992
1993 if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
1994 wf_log_pkg.string2(wf_log_pkg.level_statement,
1995 'wf.plsql.wf_engine_util.function_call.actfunc_callout',
1996 'End executing PLSQL procedure - '||funname, false);
1997 end if;
1998
1999 -- Check for no return value error.
2000 -- No value was returned if temp is still the placeholder
2001 -- value sent in.
2002 if (temp =
2003 '012345678901234567890123456789012345678901234567890123456789'||
2004 '012345678901234567890123456789012345678901234567890123456789')
2005 then
2006 if (actid is null) then
2007 -- This is a selector function call so we expect null result
2008 -- Set result to null so the calling function will ignore it.
2009 temp := '';
2010 else
2011 --Check if the acitvity is of type Notification.
2012 /* Bug 1341139
2013 Check the activity type and incase of a post-notification function
2014 make Resultout optional */
2015 -- actdate := Wf_Item.Active_Date(itemtype, itemkey);
2016 -- acttype := Wf_Activity.Instance_Type(actid, actdate);
2017
2018 -- if (acttype = wf_engine.eng_notification) then
2019 if (Wf_Engine.context_nid is not null) then
2020 temp := null;
2021 else
2022 -- This is a real function. Set an error.
2023 Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, actid,
2024 wf_engine.eng_noresult, FALSE);
2025 temp := wf_engine.eng_error||':'||wf_engine.eng_noresult;
2026 end if;
2027
2028 end if;
2029 end if;
2030
2031 if (substr(temp, 1, 5) = wf_engine.eng_error) then
2032 rollback to do_execute;
2033 end if;
2034
2035 result := temp;
2036
2037 exception
2038 when OTHERS then
2039 rollback to do_execute;
2040 raise;
2041 end;
2042 exception
2043 when NO_SAVEPOINT then
2044 Wf_Core.Token('FUNCTION', funname);
2045 Wf_Core.Token('ACTIVITY', Wf_Engine.GetActivityLabel(actid));
2046 Wf_Core.Raise('WFENG_COMMIT_INSIDE');
2047 end;
2048
2049 exception
2050 when OTHERS then
2051 wf_engine.setctx_itemtype := '';
2052 wf_engine.setctx_itemkey := '';
2053 result := wf_engine.eng_error;
2054 Wf_Core.Context('Wf_Engine_Util', 'Function_Call', funname, itemtype,
2055 itemkey, to_char(actid), funmode);
2056 raise;
2057 end function_call;
2058
2059 --
2060 -- Execute_Selector_Function (PRIVATE)
2061 -- Execute the selector function in the requested mode
2062 -- IN
2063 -- itemtype - itemtype
2064 -- itemkey - itemkey
2065 -- runmode - mode to run selector process with
2066 -- RETURNS
2067 -- Result of selector function, if any
2068 --
2069 function Execute_Selector_Function(
2070 itemtype in varchar2,
2071 itemkey in varchar2,
2072 runmode in varchar2)
2073 return varchar2
2074 is
2075
2076 result varchar2(30);
2077
2078 status PLS_INTEGER;
2079 witIND NUMBER;
2080
2081 begin
2082 -- Look for selector function.
2083 begin
2084 WF_CACHE.GetItemType(itemtype, status, witIND);
2085
2086 if (status <> WF_CACHE.task_SUCCESS) then
2087
2088 SELECT NAME, WF_SELECTOR
2089 INTO WF_CACHE.ItemTypes(witIND)
2090 FROM WF_ITEM_TYPES
2091 WHERE NAME = itemtype;
2092
2093 end if;
2094
2095 exception
2096 when no_data_found then
2097 Wf_Core.Token('TYPE', itemtype);
2098 Wf_Core.Raise('WFENG_ITEM_TYPE');
2099 end;
2100
2101 -- If no selector found, then nothing to do
2102 if (WF_CACHE.ItemTypes(witIND).WF_SELECTOR is null) then
2103 return(null);
2104 end if;
2105
2106 -- Call selector function
2107 begin
2108 Wf_Engine_Util.Function_Call(WF_CACHE.ItemTypes(witIND).WF_SELECTOR,
2109 itemtype, itemkey, null, runmode, result);
2110 exception
2111 when others then
2112 -- If this is setctx call and the function failed, unset the setctx
2113 -- globals so that subsequent calls will attempt the function again.
2114 -- This is so repeated calls can be made in the same session when
2115 -- debugging selector functions.
2116 -- NOTE: Do NOT unset the flag inside function_call itself or
2117 -- recursive loop might develop.
2118 if (runmode = wf_engine.eng_setctx) then
2119 wf_engine.setctx_itemtype := '';
2120 wf_engine.setctx_itemkey := '';
2121 end if;
2122 raise;
2123 end;
2124
2125 -- Return result unless set mode
2126 if (runmode <> wf_engine.eng_setctx) then
2127 return(result);
2128 else
2129 return(null);
2130 end if;
2131 exception
2132 when others then
2133 Wf_Core.Context('Wf_Engine_Util', 'Execute_Selector_Function',
2134 itemtype, itemkey, runmode);
2135 raise;
2136 end Execute_Selector_Function;
2137
2138 --
2139 -- Get_Root_Process (PRIVATE)
2140 -- Get the root process name by calling the workflow selector.
2141 -- If there is no workflow selector available for this item type,
2142 -- attempt to pick a default one based on starting activity.
2143 -- IN
2144 -- itemtype - itemtype
2145 -- itemkey - itemkey
2146 -- activity - starting activity instance to search for, if any
2147 -- RETURNS
2148 -- Root process name, or null if can't be found.
2149 --
2150 function Get_Root_Process(itemtype in varchar2,
2151 itemkey in varchar2,
2152 activity in varchar2 default '')
2153 return varchar2
2154 is
2155 selector varchar2(240);
2156 root varchar2(30); -- The root process for this item key
2157 actdate date;
2158 colon pls_integer;
2159 process varchar2(30); -- Start activity parent process
2160 label varchar2(30); -- Start activity instance label
2161 begin
2162 -- Look for selector function to execute
2163 root := Wf_Engine_Util.Execute_Selector_Function(itemtype, itemkey,
2164 wf_engine.eng_run);
2165
2166 -- Return root function if one found
2167 if (root is not null) then
2168 return(root);
2169 end if;
2170
2171 -- If no selector and no start activity, return null so calling proc
2172 -- can raise error.
2173 if (activity is null) then
2174 return(null);
2175 end if;
2176
2177 -- Parse activity arg into <process_name> and <instance_label> components.
2178 colon := instr(activity, ':');
2179 if (colon <> 0) then
2180 -- Activity arg is <process name>:<instance label>
2181 process := substr(activity, 1, colon-1);
2182 label := substr(activity, colon+1);
2183 else
2184 -- Activity arg is just instance label
2185 process := '';
2186 label := activity;
2187 end if;
2188
2189 -- If no selector function is defined, then query if there is one and only
2190 -- one root process for this itemtype with the given activity instance as a
2191 -- starting activity.
2192 -- If there is not one and only one such process return null so
2193 -- calling function will raise error.
2194
2195 -- SS: Sysdate is not totally correct for the active date, but is the best
2196 -- we can do. The item can't be created yet, because it doesn't have a
2197 -- root process, and you can't find the root process until the item is
2198 -- created - chicken or egg syndrome. Anyway, sysdate should be close
2199 -- enough for almost every case since the assumption is the item will
2200 -- be created almost immediately after finding the root process.
2201 actdate := sysdate;
2202 begin
2203 select WPAP.ACTIVITY_NAME
2204 into root
2205 from WF_PROCESS_ACTIVITIES WPAP, WF_ACTIVITIES WAP,
2206 WF_PROCESS_ACTIVITIES WPAC, WF_ACTIVITIES WAC
2207 where WAP.ITEM_TYPE = get_root_process.itemtype
2208 and WAP.NAME = 'ROOT'
2209 and actdate >= WAP.BEGIN_DATE
2210 and actdate < nvl(WAP.END_DATE, get_root_process.actdate+1)
2211 and WPAP.PROCESS_ITEM_TYPE = WAP.ITEM_TYPE
2212 and WPAP.PROCESS_NAME = WAP.NAME
2213 and WPAP.PROCESS_VERSION = WAP.VERSION
2214 and WAC.ITEM_TYPE = WPAP.ACTIVITY_ITEM_TYPE
2215 and WAC.NAME = WPAP.ACTIVITY_NAME
2216 and get_root_process.actdate >= WAC.BEGIN_DATE
2217 and get_root_process.actdate <
2218 nvl(WAC.END_DATE, get_root_process.actdate+1)
2219 and WPAC.PROCESS_ITEM_TYPE = WAC.ITEM_TYPE
2220 and WPAC.PROCESS_NAME = WAC.NAME
2221 and WPAC.PROCESS_VERSION = WAC.VERSION
2222 and WPAC.PROCESS_NAME = nvl(get_root_process.process, WPAC.PROCESS_NAME)
2223 and WPAC.INSTANCE_LABEL = get_root_process.label
2224 and WPAC.START_END = wf_engine.eng_start;
2225 exception
2226 when too_many_rows then
2227 -- Multiple processes use this start activity.
2228 -- No way to distinguish which one to use - error.
2229 return(null);
2230 when no_data_found then
2231 -- No processes use this start activity. Error.
2232 return(null);
2233 end;
2234
2235 return(root);
2236 exception
2237 when others then
2238 Wf_Core.Context('Wf_Engine_Util', 'Get_Root_Process', itemtype, itemkey,
2239 activity);
2240 raise;
2241 end Get_Root_Process;
2242
2243 --
2244 -- Process_Kill_ChildProcess (PRIVATE)
2245 -- Completes all incomplete child processes with the 'FORCE' result
2246 -- under this process.
2247 -- IN
2248 -- itemtype - A valid item type
2249 -- itemkey - A string generated from the application object's primary key.
2250 --
2251 procedure process_kill_childprocess(itemtype in varchar2,
2252 itemkey in varchar2)
2253 is
2254 --Cursor get all children and/or master for this process
2255 --and abort in cascade
2256
2257 --First select all Child Processes to be aborted
2258 CURSOR child_proc (p_itemtype varchar2, p_itemkey varchar2) is
2259 SELECT wi.item_type, wi.item_key
2260 FROM WF_ITEMS WI
2261 WHERE END_DATE IS NULL
2262 AND (WI.ITEM_TYPE <> p_itemtype
2263 or WI.ITEM_KEY <> p_itemkey)
2264 START WITH WI.ITEM_TYPE = p_itemtype
2265 AND WI.ITEM_KEY = p_itemkey
2266 CONNECT BY PRIOR WI.ITEM_TYPE = WI.PARENT_ITEM_TYPE
2267 AND PRIOR WI.ITEM_KEY = WI.PARENT_ITEM_KEY;
2268
2269 /*
2270 --We only kill the child process
2271 --Select all masters
2272 CURSOR master_proc (p_itemtype varchar2, p_itemkey varchar2) is
2273 SELECT wi.item_type, wi.item_key
2274 FROM WF_ITEMS WI
2275 WHERE END_DATE IS NULL
2276 AND WI.ITEM_TYPE <> p_itemtype
2277 AND WI.ITEM_KEY <> p_itemkey
2278 START WITH WI.ITEM_TYPE = p_itemtype
2279 AND WI.ITEM_KEY = p_itemkey
2280 CONNECT BY PRIOR WI.PARENT_ITEM_TYPE = WI.ITEM_TYPE
2281 AND PRIOR WI.PARENT_ITEM_KEY = WI.ITEM_KEY;
2282 */
2283
2284 begin
2285 --Now open each cursor and call abort for each
2286 for child_curs in child_proc(itemtype , itemkey) loop
2287 wf_engine.abortprocess(child_curs.item_type,child_curs.item_key);
2288 end loop;
2289
2290 --We only kill child process for this as master
2291 --Now master
2292 /*
2293 for master_curs in master_proc(itemtype , itemkey) loop
2294 wf_engine.abortprocess(master_curs.item_type,master_curs.item_key);
2295 end loop;
2296 */
2297 exception
2298 when others then
2299 Wf_Core.Context('Wf_Engine_Util', 'Process_kill_childprocess', itemtype, itemkey);
2300 raise;
2301 end Process_kill_childprocess;
2302
2303 --
2304 -- Process_Kill_Children (PRIVATE)
2305 -- Completes all incomplete children activities with the 'FORCE' result
2306 -- under this process.
2307 -- IN
2308 -- itemtype - A valid item type
2309 -- itemkey - A string generated from the application object's primary key.
2310 -- processid - The process instance id.
2311 --
2312 procedure process_kill_children(itemtype in varchar2,
2313 itemkey in varchar2,
2314 processid in number) is
2315
2316 childid pls_integer;
2317 actdate date;
2318
2319 cursor children_to_kill (pid in pls_integer) is
2320 SELECT
2321 WIAS.PROCESS_ACTIVITY, WIAS.ACTIVITY_STATUS
2322 FROM WF_PROCESS_ACTIVITIES PA, WF_PROCESS_ACTIVITIES PA1,
2323 WF_ACTIVITIES A1, WF_ITEM_ACTIVITY_STATUSES WIAS
2324 WHERE PA.INSTANCE_ID = pid
2325 AND PA.ACTIVITY_ITEM_TYPE = PA1.PROCESS_ITEM_TYPE
2326 AND PA.ACTIVITY_NAME = PA1.PROCESS_NAME
2327 AND PA1.PROCESS_VERSION = A1.VERSION
2328 AND PA1.PROCESS_ITEM_TYPE = A1.ITEM_TYPE
2329 AND PA1.PROCESS_NAME = A1.NAME
2330 AND actdate >= A1.BEGIN_DATE
2331 AND actdate < NVL(A1.END_DATE, actdate+1)
2332 AND PA1.INSTANCE_ID = WIAS.PROCESS_ACTIVITY
2333 AND WIAS.ITEM_TYPE = itemtype
2334 AND WIAS.ITEM_KEY = itemkey
2335 AND WIAS.ACTIVITY_STATUS <> 'COMPLETE';
2336
2337 childarr InstanceArrayTyp; -- Place holder for all the instance id
2338 -- selected from children_be_suspended cursor
2339 type StatusArrayTyp is table of varchar2(8)
2340 index by binary_integer;
2341
2342 statusarr StatusArrayTyp;
2343 i pls_integer := 0;
2344
2345 status varchar2(8);
2346 notid pls_integer;
2347 user varchar2(320);
2348 begin
2349 -- SYNCHMODE: Do nothing here.
2350 -- Synchmode processes must be straight-line, no branching, no
2351 -- blocking, so there can't be anything left to kill (and if there
2352 -- was, we couldn't know about it anyway because nothing is saved).
2353 if (itemkey = wf_engine.eng_synch) then
2354 return;
2355 end if;
2356
2357 -- Get the active date of the item to use for process versions.
2358 actdate := Wf_Item.Active_Date(itemtype, itemkey);
2359
2360 -- For loop to get all the children processes ids
2361 for child in children_to_kill(processid) loop
2362 childarr(i) := child.process_activity;
2363 statusarr(i) := child.activity_status;
2364 i := i + 1;
2365 end loop;
2366 childarr(i) := '';
2367
2368 -- While loop to handle all the children processes
2369 i := 0;
2370 while (childarr(i) is not null) loop
2371 childid := childarr(i);
2372
2373 if (Wf_Activity.Instance_Type(childid, actdate) =
2374 wf_engine.eng_process) then
2375 -- If child is a process then recursively kill its children
2376 Wf_Engine_Util.Process_Kill_Children(itemtype, itemkey, childid);
2377 else
2378 -- Cancel any open notifications sent by this activity
2379 Wf_Item_Activity_Status.Notification_Status(itemtype, itemkey,
2380 childid, notid, user);
2381 if (notid is not null) then
2382 begin
2383 Wf_Notification.CancelGroup(notid,'');
2384 exception
2385 when others then
2386 null; -- Ignore errors in cancelling
2387 end;
2388 end if;
2389 end if;
2390
2391 -- If activity is defered then remove it from the deferred queue
2392 -- if you dont remove it then the background engine will remove
2393 -- it when it processes and finds it doesnt correspond.
2394 if statusarr(i) = wf_engine.eng_deferred then
2395 wf_queue.PurgeEvent(wf_queue.DeferredQueue,
2396 wf_queue.GetMessageHandle(wf_queue.DeferredQueue,
2397 itemtype , itemkey, childid));
2398 end if;
2399
2400 -- Complete the activity with the force result
2401 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, childid,
2402 wf_engine.eng_completed, wf_engine.eng_force, to_date(NULL),
2403 SYSDATE);
2404
2405 -- No needs to check null type because this is internal function
2406 i := i + 1;
2407 end loop;
2408
2409 exception
2410 when others then
2411 Wf_Core.Context('Wf_Engine_Util', 'Process_Kill_Children', itemtype,
2412 itemkey, to_char(processid));
2413 raise;
2414
2415 end process_kill_children;
2416
2417 --
2418 -- Suspend_Child_Processes (PRIVATE)
2419 -- Suspends all the immediate children process activities.
2420 -- IN
2421 -- itemtype - A valid item type
2422 -- itemkey - A string generated from the application object's primary key.
2423 -- processid - The process instance id.
2424 --
2425 procedure suspend_child_processes(itemtype in varchar2,
2426 itemkey in varchar2,
2427 processid in number) is
2428
2429 actdate date;
2430
2431 -- Select all the active children process(es) under this parent process
2432 cursor children_be_suspended(parent in pls_integer) is
2433 SELECT
2434 WIAS.PROCESS_ACTIVITY
2435 FROM WF_ITEM_ACTIVITY_STATUSES WIAS, WF_PROCESS_ACTIVITIES WPA1,
2436 WF_ACTIVITIES WA1, WF_PROCESS_ACTIVITIES WPA2, WF_ACTIVITIES WA2
2437 WHERE WPA1.INSTANCE_ID = processid
2438 AND WPA1.ACTIVITY_ITEM_TYPE = WA1.ITEM_TYPE
2439 AND WPA1.ACTIVITY_NAME = WA1.NAME
2440 AND actdate >= WA1.BEGIN_DATE
2441 AND actdate < NVL(WA1.END_DATE, actdate+1)
2442 AND WA1.ITEM_TYPE = WPA2.PROCESS_ITEM_TYPE
2443 AND WA1.NAME = WPA2.PROCESS_NAME
2444 AND WA1.VERSION = WPA2.PROCESS_VERSION
2445 AND WPA2.ACTIVITY_ITEM_TYPE = WA2.ITEM_TYPE
2446 AND WPA2.ACTIVITY_NAME = WA2.NAME
2447 AND actdate >= WA2.BEGIN_DATE
2448 AND actdate < NVL(WA2.END_DATE, actdate+1)
2449 AND WA2.TYPE = wf_engine.eng_process
2450 AND WPA2.INSTANCE_ID = WIAS.PROCESS_ACTIVITY
2451 AND WIAS.ITEM_TYPE = itemtype
2452 AND WIAS.ITEM_KEY = itemkey
2453 AND WIAS.ACTIVITY_STATUS = 'ACTIVE'; --use literal to force index
2454
2455 childarr InstanceArrayTyp; -- Place holder for all the instance id
2456 -- selected from children_be_suspended cursor
2457 i pls_integer := 0;
2458 begin
2459 -- Get the active date of the item to use for process versions.
2460 actdate := Wf_Item.Active_Date(itemtype, itemkey);
2461
2462 -- For loop to get all the children processes ids
2463 for child in children_be_suspended(processid) loop
2464 childarr(i) := child.process_activity;
2465 i := i + 1;
2466 end loop;
2467 childarr(i) := '';
2468
2469 -- While loop to handle all the children processes
2470 i := 0;
2471 while (childarr(i) is not null) loop
2472 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, childarr(i),
2473 wf_engine.eng_suspended, null, to_date(NULL), SYSDATE);
2474 suspend_child_processes(itemtype, itemkey, childarr(i));
2475 i := i + 1;
2476 end loop;
2477
2478 exception
2479 when others then
2480 Wf_Core.Context('Wf_Engine_Util', 'Suspend_Child_Processes', itemtype,
2481 itemkey, to_char(processid));
2482 raise;
2483 end suspend_child_processes;
2484
2485 --
2486 -- Resume_Child_Processes (PRIVATE)
2487 -- Resumes all the children process activities.
2488 -- IN
2489 -- itemtype - A valid item type
2490 -- itemkey - A string generated from the application object's primary key.
2491 -- processid - The process instance id.
2492 --
2493 procedure resume_child_processes(itemtype in varchar2,
2494 itemkey in varchar2,
2495 processid in number) is
2496
2497 actdate date;
2498
2499 -- Select all the suspended children processes under this parent process
2500 cursor children_be_resumed(parent in pls_integer) is
2501 SELECT
2502 WIAS.PROCESS_ACTIVITY
2503 FROM WF_ITEM_ACTIVITY_STATUSES WIAS, WF_PROCESS_ACTIVITIES WPA1,
2504 WF_PROCESS_ACTIVITIES WPA2, WF_ACTIVITIES WA
2505 WHERE WPA1.INSTANCE_ID = processid
2506 AND WPA1.ACTIVITY_ITEM_TYPE = WA.ITEM_TYPE
2507 AND WPA1.ACTIVITY_NAME = WA.NAME
2508 AND actdate >= WA.BEGIN_DATE
2509 AND actdate < NVL(WA.END_DATE, actdate+1)
2510 AND WA.ITEM_TYPE = WPA2.PROCESS_ITEM_TYPE
2511 AND WA.NAME = WPA2.PROCESS_NAME
2512 AND WA.VERSION = WPA2.PROCESS_VERSION
2513 AND WPA2.INSTANCE_ID = WIAS.PROCESS_ACTIVITY
2514 AND WIAS.ITEM_TYPE = itemtype
2515 AND WIAS.ITEM_KEY = itemkey
2516 AND WIAS.ACTIVITY_STATUS = 'SUSPEND'; -- use literal to force index
2517
2518 childarr InstanceArrayTyp; -- Place holder for all the instance id
2519 -- selected from children_be_resumed cursor
2520 i pls_integer := 0;
2521
2522 begin
2523 -- Get the active date of the item to use for process versions.
2524 actdate := Wf_Item.Active_Date(itemtype, itemkey);
2525
2526 -- For loop to get all the children processes id
2527 for child in children_be_resumed(processid) loop
2528 childarr(i) := child.process_activity;
2529 i := i + 1;
2530 end loop;
2531 childarr(i) := '';
2532
2533 -- While loop to handle all the children processes
2534 i := 0;
2535 while (childarr(i) is not null) loop
2536 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, childarr(i),
2537 wf_engine.eng_active, null, null, SYSDATE);
2538 resume_child_processes(itemtype, itemkey, childarr(i));
2539 i := i + 1;
2540 end loop;
2541
2542 exception
2543 when others then
2544 Wf_Core.Context('Wf_Engine_Util', 'Resume_Child_Processes', itemtype,
2545 itemkey, to_char(processid));
2546 raise;
2547 end resume_child_processes;
2548
2549 --
2550 -- Notification (PRIVATE)
2551 -- This is the default notification activity function.
2552 -- It looks up the notification info and then sends it.
2553 -- IN
2554 -- itemtype - A valid item type
2555 -- itemkey - A string generated from the application object's primary key.
2556 -- actid - The notification process activity(instance id).
2557 -- funcmode - Function mode (RUN/CANCEL)
2558 -- OUT
2559 -- result - NOTIFIED:notificationid:user
2560 --
2561 procedure Notification(
2562 itemtype in varchar2,
2563 itemkey in varchar2,
2564 actid in number,
2565 funcmode in varchar2,
2566 result out NOCOPY varchar2)
2567 is
2568 msg varchar2(30);
2569 msgtype varchar2(8);
2570 prole varchar2(320);
2571 expand_role varchar2(1);
2572 begin
2573 -- SYNCHMODE: Not allowed
2574 if (itemkey = wf_engine.eng_synch) then
2575 Wf_Core.Token('OPERATION', 'Wf_Engine.Notification');
2576 Wf_Core.Raise('WFENG_SYNCH_DISABLED');
2577 end if;
2578
2579 -- Get the message, perform role, and timeout
2580 -- msg and prole could came back as null since they are nullable columns
2581 Wf_Activity.Notification_Info(itemtype, itemkey, actid, msg, msgtype,
2582 expand_role);
2583 prole := Wf_Activity.Perform_Role(itemtype, itemkey, actid);
2584
2585 Wf_Engine_Util.Notification_Send(itemtype, itemkey, actid, msg, msgtype,
2586 prole, expand_role, result);
2587
2588 exception
2589 when others then
2590 wf_core.context('Wf_Engine_Util', 'Notification', itemtype, itemkey,
2591 to_char(actid), funcmode);
2592 raise;
2593 end Notification;
2594
2595 --
2596 -- Notification_Send (PRIVATE)
2597 -- Actually sends the notification
2598 -- IN
2599 -- itemtype - a valid item type
2600 -- itemkey - a string generated from the application object's primary key.
2601 -- actid - the notification process activity(instance id).
2602 -- msg - name of msg to send
2603 -- msgtype - its message type
2604 -- prole - performer role
2605 -- expand_role the expand role arg for notifications
2606 -- OUT
2607 -- result - notified:notificationid:user
2608 --
2609 procedure Notification_Send(
2610 itemtype in varchar2,
2611 itemkey in varchar2,
2612 actid in number,
2613 msg in varchar2,
2614 msgtype in varchar2,
2615 prole in varchar2,
2616 expand_role in varchar2,
2617 result out NOCOPY varchar2)
2618 is
2619 priority number;
2620 notid pls_integer;
2621 ctx varchar2(2000);
2622 duedate date;
2623 dummy pls_integer;
2624 performer varchar2(320);
2625 begin
2626 if (msg is null) then
2627 Wf_Core.Token('TYPE', itemtype);
2628 Wf_Core.Token('ACTID', to_char(actid));
2629 Wf_Core.Raise('WFENG_NOTIFICATION_MESSAGE');
2630 end if;
2631
2632 if (prole is null) then
2633 Wf_Core.Token('TYPE', itemtype);
2634 Wf_Core.Token('ACTID', to_char(actid));
2635 Wf_Core.Raise('WFENG_NOTIFICATION_PERFORMER');
2636 end if;
2637
2638 /* Bug 2156047 */
2639 -- clear global variables to store context info
2640 wf_engine.g_nid := '';
2641 wf_engine.g_text := '';
2642
2643 -- Construct context as itemtype:key:actid
2644 ctx := itemtype||':'||itemkey||':'||to_char(actid);
2645
2646 -- Mark duedate of notification as timeout date of activity
2647 duedate := Wf_Item_Activity_Status.Due_Date(itemtype, itemkey, actid);
2648
2649 -- Check for #PRIORITY activity attribute to override default
2650 -- priority of this notification
2651 begin
2652 priority := Wf_Engine.GetActivityAttrNumber(itemtype, itemkey, actid,
2653 wf_engine.eng_priority, ignore_notfound=>TRUE);
2654 exception
2655 when others then
2656 if (wf_core.error_name = 'WFENG_ACTIVITY_ATTR') then
2657 -- If no priority attr default to null
2658 priority := '';
2659 Wf_Core.Clear;
2660 else
2661 raise;
2662 end if;
2663 end;
2664
2665 -- Send notification, either to expanded role or singly
2666 -- depending on expand_role flag.
2667 if (expand_role = 'Y') then
2668 notid := Wf_Notification.SendGroup(prole, msgtype, msg, duedate,
2669 'WF_ENGINE.CB', ctx, '', priority);
2670 else
2671 notid := Wf_Notification.Send(prole, msgtype, msg, duedate,
2672 'WF_ENGINE.CB', ctx, '', priority);
2673 end if;
2674
2675 -- Check for a change in the performer. If the notification
2676 -- was automatically routed by Send, the assigned_user might have
2677 -- been updated. If so, reset the performer to the new role to
2678 -- avoid over-writing with the old value.
2679 performer := nvl(Wf_Activity.Perform_Role(itemtype, itemkey, actid),
2680 prole);
2681
2682 -- If there are no respond-type attributes to this message,
2683 -- then no response is expected. Instead of returning a 'NOTIFIED'
2684 -- response, return a response of '' so that the activity will
2685 -- complete immediately instead of waiting for the notification
2686 -- to be responded to.
2687 begin
2688 select 1 into dummy from sys.dual where exists
2689 (select null
2690 from WF_MESSAGE_ATTRIBUTES
2691 where MESSAGE_TYPE = msgtype
2692 and MESSAGE_NAME = msg
2693 AND SUBTYPE = 'RESPOND');
2694
2695 -- Response is expected.
2696 -- Return result of 'NOTIFIED:notid:role'.
2697 result := Wf_Engine.Eng_Notified||':'||to_char(notid)||':'||performer;
2698
2699 exception
2700 when no_data_found then
2701 -- No respond attributes.
2702 -- Set notification id, then complete immediately.
2703 Wf_Item_Activity_Status.Update_Notification(itemtype, itemkey, actid,
2704 notid, performer);
2705 result := Wf_Engine.Eng_Null;
2706 end;
2707
2708 /* Bug 2156047 */
2709 -- Need to cache the Notification id and Performer role for
2710 -- executing Post Notification function
2711
2712 Wf_Engine.g_nid := notid;
2713 Wf_Engine.g_text := performer;
2714
2715 exception
2716 when others then
2717 Wf_Core.Context('Wf_Engine_Util', 'Notification_Send', itemtype, itemkey,
2718 to_char(actid), msgtype||':'||msg);
2719 raise;
2720 end Notification_Send;
2721
2722 --Notification_Copy (PRIVATE)
2723 -- Copies a notification by creating a new one with same attributes.
2724 -- IN
2725 -- copy_nid - the notifiation id to copy
2726 -- itemtype -
2727 -- itemkey -
2728 -- OUT
2729 -- nid - tyhe new notification id that was created
2730 --
2731 procedure Notification_Copy (
2732 copy_nid in number,
2733 old_itemkey in varchar2,
2734 new_itemkey in varchar2,
2735 nid in out NOCOPY number) is
2736
2737 gid pls_integer:=0;
2738
2739 cursor ntf_details is
2740 select
2741 notification_id,
2742 group_id,
2743 MESSAGE_TYPE, MESSAGE_NAME,
2744 RECIPIENT_ROLE, ORIGINAL_RECIPIENT,
2745 STATUS,
2746 wf_core.random,
2747 MAIL_STATUS, PRIORITY,
2748 BEGIN_DATE, END_DATE, DUE_DATE,
2749 USER_COMMENT,CALLBACK,
2750 CONTEXT
2751 from wf_notifications
2752 where group_id = copy_nid;
2753
2754 begin
2755 for ntf_row in ntf_details loop
2756
2757 -- create a new notification
2758 select WF_NOTIFICATIONS_S.NEXTVAL
2759 into nid
2760 from SYS.DUAL;
2761
2762 -- Use nid of the first notification as group id for the rest
2763 -- but only if notification is expand_roles
2764 if (gid =0) then
2765 gid := nid;
2766 end if;
2767
2768 insert into WF_NOTIFICATIONS (
2769 NOTIFICATION_ID, GROUP_ID,
2770 MESSAGE_TYPE, MESSAGE_NAME,
2771 RECIPIENT_ROLE, ORIGINAL_RECIPIENT,
2772 STATUS,
2773 ACCESS_KEY,
2774 MAIL_STATUS, PRIORITY,
2775 BEGIN_DATE, END_DATE, DUE_DATE,
2776 USER_COMMENT,CALLBACK,
2777 CONTEXT)
2778 values (
2779 nid, gid,
2780 ntf_row.MESSAGE_TYPE, ntf_row.MESSAGE_NAME,
2781 ntf_row.RECIPIENT_ROLE, ntf_row.ORIGINAL_RECIPIENT,
2782 ntf_row.STATUS,
2783 wf_core.random,
2784 ntf_row.MAIL_STATUS, ntf_row.PRIORITY,
2785 ntf_row.BEGIN_DATE, ntf_row.END_DATE, ntf_row.DUE_DATE,
2786 ntf_row.USER_COMMENT,ntf_row.CALLBACK,
2787 replace(ntf_row.CONTEXT,':'||old_itemkey||':',':'||new_itemkey||':'));
2788
2789
2790 -- create notification attributes
2791 insert into WF_NOTIFICATION_ATTRIBUTES (
2792 NOTIFICATION_ID,
2793 NAME,
2794 TEXT_VALUE,
2795 NUMBER_VALUE,
2796 DATE_VALUE)
2797 select
2798 nid,
2799 NAME,
2800 TEXT_VALUE,
2801 NUMBER_VALUE,
2802 DATE_VALUE
2803 from WF_NOTIFICATION_ATTRIBUTES
2804 where notification_id = ntf_row.notification_id
2805 union all
2806 select nid,
2807 NAME,
2808 TEXT_DEFAULT,
2809 NUMBER_DEFAULT,
2810 DATE_DEFAULT
2811 from WF_MESSAGE_ATTRIBUTES
2812 where MESSAGE_TYPE = ntf_row.MESSAGE_TYPE
2813 and MESSAGE_NAME = ntf_row.MESSAGE_NAME
2814 and name not in
2815 (select name
2816 from WF_NOTIFICATION_ATTRIBUTES
2817 where notification_id = ntf_row.notification_id);
2818
2819 -- Copy associated Notification Comments
2820 INSERT INTO wf_comments
2821 (notification_id,
2822 from_role,
2823 from_user,
2824 to_role,
2825 to_user,
2826 comment_date,
2827 action,
2828 action_type,
2829 user_comment,
2830 proxy_role)
2831 SELECT nid,
2832 from_role,
2833 from_user,
2834 to_role,
2835 to_user,
2836 comment_date,
2837 action,
2838 action_type,
2839 user_comment,
2840 proxy_role
2841 FROM wf_comments
2842 WHERE notification_id = ntf_row.notification_id;
2843
2844 end loop;
2845
2846 nid:=gid;
2847
2848 exception
2849 when others then
2850 Wf_Core.Context('Wf_Engine_Util', 'Notification_Copy');
2851 raise;
2852 end notification_copy;
2853
2854 --Notification_refresh (PRIVATE)
2855 -- Refreshes all itemtype message attribute
2856 -- for all sent messages in this itemtype/itmekey
2857 -- IN
2858 -- itemtype - a valid item type
2859 -- itemkey - a string generated from the application object's primary key.
2860 --
2861 procedure notification_refresh
2862 (itemtype in varchar2,
2863 itemkey in varchar2) is
2864
2865 --attr_name varchar2(30);
2866 --attr_type varchar2(8);
2867 attr_tvalue varchar2(4000);
2868 attr_nvalue number;
2869 attr_dvalue date;
2870
2871 cursor message_attrs_cursor(itemtype varchar2, itemkey varchar2) is
2872 select ma.NAME, ma.TYPE, ma.SUBTYPE,
2873 ma.TEXT_DEFAULT, ma.NUMBER_DEFAULT, ma.DATE_DEFAULT,
2874 n.notification_id
2875 from wf_item_activity_statuses_h ias,
2876 wf_notifications n,
2877 wf_message_attributes ma
2878 where ias.item_type = itemtype
2879 and ias.item_key = itemkey
2880 and ias.notification_id = n.notification_id
2881 and ma.message_type = n.message_type
2882 and ma.message_name = n.message_name
2883 and ma.value_type = 'ITEMATTR';
2884
2885 begin
2886
2887 --
2888 -- Look up all notification attributes and reset them
2889 --
2890 for message_attr_row in message_attrs_cursor(itemtype, itemkey) loop
2891
2892 --dont call the notification callback function because this will
2893 --only ever be called from the engine.
2894
2895 --attr_name := message_attr_row.name;
2896 --attr_type := message_attr_row.type;
2897 attr_tvalue := '';
2898 attr_nvalue := '';
2899 attr_dvalue := '';
2900
2901 if (message_attr_row.type = 'NUMBER') then
2902 attr_nvalue := wf_engine.GetItemAttrNumber(itemtype, itemkey, message_attr_row.TEXT_DEFAULT);
2903 elsif (message_attr_row.type = 'DATE') then
2904 attr_dvalue := wf_engine.GetItemAttrDate(itemtype, itemkey, message_attr_row.TEXT_DEFAULT);
2905 else
2906 attr_tvalue := wf_engine.GetItemAttrText(itemtype, itemkey, message_attr_row.TEXT_DEFAULT);
2907 end if;
2908
2909 --
2910 -- Update the notification attribute
2911 --
2912 update WF_NOTIFICATION_ATTRIBUTES
2913 set TEXT_VALUE = attr_tvalue,
2914 NUMBER_VALUE = attr_nvalue,
2915 DATE_VALUE = attr_dvalue
2916 where notification_id = message_attr_row.notification_id
2917 and name = message_attr_row.name;
2918 end loop;
2919
2920
2921 exception
2922 when others then
2923 Wf_Core.Context('Wf_Engine_Util', 'Notification_refresh');
2924 raise;
2925 end notification_refresh;
2926
2927
2928 --
2929 -- Execute_Error_Process (Private)
2930 -- Attempts to run an error process for an activity that has error'ed out.
2931 -- IN
2932 -- itemtype - a valid item type
2933 -- itemkey - a string generated from the application object's primary key.
2934 -- actid - the notification process activity(instance id).
2935 -- result - activity result code
2936 --
2937 procedure execute_error_process (
2938 itemtype in varchar2,
2939 itemkey in varchar2,
2940 actid in number,
2941 result in varchar2)
2942 is
2943 errortype varchar2(8) := '';
2944 errorprocess varchar2(30) := '';
2945 erractid pls_integer;
2946 actdate date;
2947 root varchar2(30);
2948 version pls_integer;
2949 rootid pls_integer;
2950 errkey varchar2(240);
2951 notid pls_integer;
2952 user varchar2(320);
2953 label varchar2(62);
2954 errname varchar2(30);
2955 errmsg varchar2(2000);
2956 errstack varchar2(4000);
2957 err_url varchar2(2000);
2958 err_userkey varchar2(240);
2959 newstatus varchar2(8);
2960 newresult varchar2(30);
2961
2962 begin
2963 actdate := Wf_Item.Active_Date(itemtype, itemkey);
2964
2965 --
2966 -- Look for an error process to execute.
2967 -- If this activity does not have an error process, look for the
2968 -- nearest parent process activity that does have one.
2969 --
2970 Wf_Item.Root_Process(itemtype, itemkey, root, version);
2971 rootid := Wf_Process_Activity.RootInstanceId(itemtype, itemkey, root);
2972 if (rootid is null) then
2973 Wf_Core.Token('TYPE', itemtype);
2974 Wf_Core.Token('KEY', itemkey);
2975 Wf_Core.Token('NAME', root);
2976 Wf_Core.Raise('WFENG_ITEM_ROOT');
2977 end if;
2978
2979 erractid := actid;
2980
2981 Wf_Activity.Error_Process(erractid, actdate, errortype, errorprocess);
2982
2983 while ((errorprocess is null) and (erractid <> rootid)) loop
2984 erractid := Wf_Engine_Util.Activity_Parent_Process(itemtype, itemkey,
2985 erractid);
2986 Wf_Activity.Error_Process(erractid, actdate, errortype, errorprocess);
2987 end loop;
2988
2989 -- If no error process, then nothing to do.
2990 -- Provided WF_HANDLEERRORS is set to 'N'
2991 if (errorprocess is null) then
2992 --Bug 2769454
2993 --We need to be able to launch atleast the deafult error
2994 --process if no process has been defined on process or
2995 --the parent process.
2996 --To maintain the old functionality we would create a token
2997 --WF_HANDLEERRORS which if set to 'Y' we would run the default_error
2998 --process and incase of 'N' (anything other than N is traeted as Y)
2999 -- would have the old behaviour.
3000
3001 --For eliminating potential infinite loop now if
3002 --there is a scenario of the default error processing
3003 --erroring out due to some reason
3004 if (( wf_core.translate('WF_HANDLEERRORS') <> 'N')
3005 AND (itemtype <> 'WFERROR')) then
3006 --Set the error process and type to DEFAULT_ERROR
3007 errortype := 'WFERROR';
3008 errorprocess := 'DEFAULT_ERROR';
3009 else
3010 return;
3011 end if;
3012 end if;
3013
3014 -- Get key for errorprocess: concatenate WF to ensure unique
3015 select 'WF'||to_char(WF_ERROR_PROCESSES_S.NEXTVAL)
3016 into errkey
3017 from SYS.DUAL;
3018
3019 -- Create process and set item parent columns with ids of
3020 -- activity initiating error.
3021 Wf_Engine.CreateProcess(errortype, errkey, errorprocess);
3022 wf_engine.SetItemParent(errortype, errkey, itemtype, itemkey,
3023 to_char(actid));
3024
3025 -- Select and set pre-defined error attributes.
3026 wf_item_activity_status.notification_status(itemtype, itemkey, actid,
3027 notid, user);
3028 label := Wf_Engine.GetActivityLabel(actid);
3029
3030 wf_item_activity_status.error_info(itemtype, itemkey, actid,
3031 errname, errmsg, errstack);
3032
3033 -- look up the monitor URL
3034 err_url := WF_MONITOR.GetEnvelopeURL
3035 ( x_agent => wf_core.translate('WF_WEB_AGENT'),
3036 x_item_type => itemtype,
3037 x_item_key => itemkey,
3038 x_admin_mode => 'YES');
3039 -- look up the user key
3040 err_userkey := Wf_Engine.GetItemUserKey(itemtype, itemkey);
3041
3042 Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
3043 'ERROR_ITEM_TYPE', itemtype);
3044 Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
3045 'ERROR_ITEM_KEY', itemkey);
3046 Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
3047 'ERROR_ACTIVITY_LABEL', label);
3048 Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'NUMBER',
3049 'ERROR_ACTIVITY_ID', actid);
3050 Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
3051 'ERROR_RESULT_CODE', result);
3052 Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'NUMBER',
3053 'ERROR_NOTIFICATION_ID', to_char(notid));
3054 Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
3055 'ERROR_ASSIGNED_USER', user);
3056 Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
3057 'ERROR_NAME', errname);
3058 Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
3059 'ERROR_MESSAGE', errmsg);
3060 Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
3061 'ERROR_STACK', errstack);
3062 Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
3063 'ERROR_MONITOR_URL', err_url);
3064 Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
3065 'ERROR_USER_KEY', err_userkey);
3066
3067 -- Run the error process.
3068 Wf_Engine.StartProcess(errortype, errkey);
3069
3070 exception
3071 when others then
3072 -- If an error is raised in error process, do NOT raise another exception.
3073 -- Append the new error to the original error in WIAS error columns,
3074 -- then clear and ignore the exception.
3075 Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, actid, '', TRUE);
3076 Wf_Core.Clear;
3077 end Execute_Error_Process;
3078
3079 --
3080 -- SetErrorItemAttr (PRIVATE)
3081 -- Called by execute_error_process to set error item attributes.
3082 -- IN
3083 -- error_type - error process itemtype
3084 -- error_key - error process itemkey
3085 -- attrtype - attribute type
3086 -- item_attr - attribute name
3087 -- avalue - attribute value
3088 --
3089 procedure SetErrorItemAttr (
3090 error_type in varchar2,
3091 error_key in varchar2,
3092 attrtype in varchar2,
3093 item_attr in varchar2,
3094 avalue in varchar2)
3095 is
3096 begin
3097 if (attrtype = 'TEXT') then
3098 Wf_Engine.SetItemAttrText(error_type, error_key, item_attr, avalue);
3099 else
3100 Wf_Engine.SetItemAttrNumber(error_type, error_key, item_attr, to_number(avalue));
3101 end if;
3102 exception
3103 when others then
3104 if (wf_core.error_name = 'WFENG_ITEM_ATTR') then
3105 if (attrtype = 'TEXT') then
3106 Wf_Engine.AddItemAttr(error_type, error_key, item_attr, avalue);
3107 WF_CORE.Clear;
3108 else
3109 Wf_Engine.AddItemAttr(error_type, error_key, item_attr, '',
3110 to_number(avalue));
3111 WF_CORE.Clear;
3112 end if;
3113 else
3114 raise;
3115 end if;
3116 end SetErrorItemAttr;
3117
3118
3119 --
3120 -- Execute_Post_NTF_Function (PRIVATE)
3121 -- Execute the post-notification function to see if activity should complete.
3122 -- IN
3123 -- itemtype - A valid item type
3124 -- itemkey - A string generated from the application object's primary key.
3125 -- actid - The notification process activity(instance id).
3126 -- funmode - Run post-notification function in run or cancel mode
3127 -- OUT
3128 -- pntfstatus - Flag to indicate post-notification results. Values are
3129 -- 'WAITING' - post-notification function for activity is not yet complete
3130 -- 'COMPLETE' - post-notification function for activity is complete
3131 -- null - this is not a post-notification activity
3132 -- pntfresult - Result of post-notification function if pntfstatus='COMPLETE'
3133 --
3134 procedure Execute_Post_NTF_Function (itemtype in varchar2,
3135 itemkey in varchar2,
3136 actid in number,
3137 funmode in varchar2,
3138 pntfstatus out NOCOPY varchar2,
3139 pntfresult out NOCOPY varchar2)
3140 is
3141 message varchar2(30);
3142 msgtype varchar2(8);
3143 expand_role varchar2(1);
3144 funcname varchar2(240);
3145 result varchar2(240);
3146 errcode varchar2(30);
3147 l_notid number;
3148 l_responder varchar2(320);
3149
3150 begin
3151 -- See if a post-notification function was attached
3152 funcname := Wf_Activity.Activity_Function(itemtype, itemkey, actid);
3153
3154 -- If there is no post-notification function,
3155 -- then no action is required so exit immediately.
3156 if (funcname is null) then
3157 pntfstatus := null;
3158 pntfresult := null;
3159 return;
3160 end if;
3161
3162 /* Bug 2156047 */
3163 -- Set global context areas.
3164 -- This is context information for use by post ntf function
3165 -- when executing in modes RUN, RESPOND, TRANSFER etc.
3166 Wf_Item_Activity_Status.Notification_Status(itemtype, itemkey, actid,
3167 l_notid, l_responder);
3168 Wf_Engine.context_nid := l_notid;
3169 Wf_Engine.context_text := l_responder;
3170
3171
3172 -- There is a post-notification function. Execute it.
3173 begin
3174 Wf_Engine_Util.Function_Call(funcname, itemtype, itemkey, actid, funmode,
3175 result);
3176 exception
3177 -- Set error info columns if post-notification function raised exception,
3178 -- unless running in cancel mode.
3179 when others then
3180 if (funmode <> wf_engine.eng_cancel) then
3181 Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, actid,
3182 wf_engine.eng_exception, FALSE);
3183 result := wf_engine.eng_error||':'||wf_engine.eng_exception;
3184 end if;
3185 end;
3186
3187 /* Bug 2156047 */
3188 -- clear context values
3189 Wf_Engine.context_nid := '';
3190 Wf_Engine.context_text := '';
3191
3192 -- The engine does not care about the result when undoing a function
3193 if (funmode = wf_engine.eng_cancel) then
3194 return;
3195 end if;
3196
3197
3198 -- Handle different results
3199 if ((result is null) or (result = wf_engine.eng_null)) then
3200 -- Assume a null result means post-notification function is not
3201 -- implemented.
3202 pntfstatus := null;
3203 pntfresult := null;
3204 elsif (substr(result, 1, length(wf_engine.eng_error)) =
3205 wf_engine.eng_error) then
3206 -- Get the error code
3207 errcode := substr(result, length(wf_engine.eng_error)+2, 30);
3208 Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
3209 wf_engine.eng_error, errcode);
3210
3211 -- Call error_process to execute any error processes.
3212 Wf_Engine_Util.Execute_Error_Process(itemtype, itemkey, actid, errcode);
3213
3214 -- Return status waiting to prevent activity from completing.
3215 pntfstatus := wf_engine.eng_waiting;
3216 pntfresult := null;
3217 elsif (result = wf_engine.eng_waiting) then
3218 -- Post-notification function is not yet completed.
3219 -- Return status waiting to prevent activity from completing.
3220 pntfstatus := wf_engine.eng_waiting;
3221 pntfresult := null;
3222 else
3223 -- Result must be COMPLETE. Other statuses are not allowed for
3224 -- post-notification functions.
3225
3226 -- Strip off optional 'COMPLETE:' tag from result
3227 if (substr(result, 1, length(wf_engine.eng_completed)+1) =
3228 wf_engine.eng_completed||':') then
3229 result := substr(result, length(wf_engine.eng_completed)+2, 30);
3230 end if;
3231
3232 -- Return complete status and result.
3233 pntfstatus := wf_engine.eng_completed;
3234 pntfresult := result;
3235 end if;
3236 return;
3237 exception
3238 when others then
3239 Wf_Core.Context('Wf_Engine_Util', 'Execute_Post_NTF_Function', itemtype,
3240 itemkey, to_char(actid), funmode);
3241 raise;
3242 end Execute_Post_NTF_Function;
3243
3244 --
3245 -- Execute_Notification_Callback (PRIVATE)
3246 -- Look for a function on a notification activity and execute in
3247 -- appropriate mode.
3248 -- Called from CB when a notification is acted on.
3249 -- IN
3250 -- funcmode - callback mode (FORWARD, TRANSFER, RESPOND)
3251 -- itemtype - item type of notification context
3252 -- itemkey - item key of notification context
3253 -- actid - activity of notification context
3254 -- ctx_nid - notification id
3255 -- ctx_text - new recipient role (FORWARD or TRANSFER)
3256 --
3257 procedure Execute_Notification_Callback(
3258 funcmode in varchar2,
3259 itemtype in varchar2,
3260 itemkey in varchar2,
3261 actid in number,
3262 ctx_nid in number,
3263 ctx_text in varchar2)
3264 is
3265 funcname varchar2(240);
3266 result varchar2(2000);
3267 errcode varchar2(2000);
3268
3269 begin
3270 funcname := Wf_Activity.Activity_Function(itemtype, itemkey, actid);
3271
3272 -- No callback function, nothing to do.
3273 if (funcname is null) then
3274 return;
3275 end if;
3276
3277 -- Set global context areas.
3278 -- This is context information for use by callback function while
3279 -- running.
3280 Wf_Engine.context_nid := ctx_nid;
3281 Wf_Engine.context_text := ctx_text;
3282
3283 -- Bug 3065814
3284 -- Set all context information for the post-notification function
3285 wf_engine.context_user := wf_notification.g_context_user;
3286 wf_engine.context_user_comment := wf_notification.g_context_user_comment ;
3287 wf_engine.context_recipient_role := wf_notification.g_context_recipient_role ;
3288 wf_engine.context_original_recipient:= wf_notification.g_context_original_recipient;
3289 wf_engine.context_from_role := wf_notification.g_context_from_role ;
3290 wf_engine.context_new_role := wf_notification.g_context_new_role ;
3291 wf_engine.context_more_info_role := wf_notification.g_context_more_info_role ;
3292 wf_engine.context_proxy := wf_notification.g_context_proxy;
3293
3294 wf_engine.context_user_key := wf_engine.GetItemUserKey(itemtype, itemkey);
3295
3296 -- Call function in requested mode
3297 Wf_Engine_Util.Function_Call(funcname, itemtype, itemkey, actid,
3298 funcmode, result);
3299
3300 -- Error handling...
3301 -- 1. If function raises its own exception, let it trickle up.
3302 -- 2. If function returned a result of 'ERROR:...', convert it
3303 -- to a generic exception and let that trickle up so that
3304 -- the originating function will fail.
3305 if (substr(result, 1, length(wf_engine.eng_error)) =
3306 wf_engine.eng_error) then
3307 errcode := substr(result, length(wf_engine.eng_error)+2, 2000);
3308 Wf_Core.Token('ERRCODE', errcode);
3309 Wf_Core.Raise('WFENG_NOTIFICATION_FUNCTION');
3310 end if;
3311
3312 -- Clear global context areas
3313 Wf_Engine.context_nid := '';
3314 Wf_Engine.context_text := '';
3315
3316 --Bug 3065814
3317 wf_engine.context_user := '';
3318 wf_engine.context_user_comment := '';
3319 wf_engine.context_recipient_role := '';
3320 wf_engine.context_original_recipient:='';
3321 wf_engine.context_from_role :='';
3322 wf_engine.context_new_role :='';
3323 wf_engine.context_more_info_role := '';
3324 wf_engine.context_user_key := '';
3325 wf_engine.context_proxy := '';
3326
3327 exception
3328 when others then
3329 -- Clear global context, just in case
3330 Wf_Engine.context_nid := '';
3331 Wf_Engine.context_text := '';
3332 -- Bug 3065814
3333 wf_engine.context_user := '';
3334 wf_engine.context_user_comment := '';
3335 wf_engine.context_recipient_role := '';
3336 wf_engine.context_original_recipient:='';
3337 wf_engine.context_from_role :='';
3338 wf_engine.context_new_role :='';
3339 wf_engine.context_more_info_role := '';
3340 wf_engine.context_user_key := '';
3341 wf_engine.context_proxy := '';
3342
3343 Wf_Core.Context('Wf_Engine_Util', 'Execute_Notification_Callback',
3344 funcmode, itemtype, itemkey, to_char(actid),
3345 to_char(ctx_nid)||':'||ctx_text);
3346 raise;
3347 end Execute_Notification_Callback;
3348
3349 --
3350 -- Activity_Timeout (PUBLIC)
3351 -- IN
3352 -- actid - Process activity (instance id).
3353 function Activity_Timeout(actid in number) return varchar2
3354 is
3355 waavIND NUMBER;
3356 status PLS_INTEGER;
3357
3358 begin
3359 -- Check Arguments
3360 if (actid is null) then
3361 Wf_Core.Token('ACTID', nvl(actid, 'NULL'));
3362 Wf_Core.Raise('WFSQL_ARGS');
3363 end if;
3364 -- Check value_type flag for possible item_attribute ref.
3365 WF_CACHE.GetActivityAttrValue(actid, '#TIMEOUT', status, waavIND);
3366
3367 if (status <> WF_CACHE.task_SUCCESS) then
3368 open curs_activityattr (actid, '#TIMEOUT');
3369 fetch curs_activityattr into WF_CACHE.ActivityAttrValues(waavIND);
3370 close curs_activityattr;
3371 end if;
3372
3373 if (WF_CACHE.ActivityAttrValues(waavIND).NUMBER_VALUE is not null) then
3374 return(to_char(WF_CACHE.ActivityAttrValues(waavIND).NUMBER_VALUE));
3375 elsif (WF_CACHE.ActivityAttrValues(waavIND).DATE_VALUE is not null) then
3376 return(to_char(WF_CACHE.ActivityAttrValues(waavIND).DATE_VALUE)||' '||
3377 to_char(WF_CACHE.ActivityAttrValues(waavIND).DATE_VALUE,
3378 'HH24:MI:SS'));
3379
3380 elsif (WF_CACHE.ActivityAttrValues(waavIND).VALUE_TYPE = 'ITEMATTR') then
3381 return(substrb(WF_CACHE.ActivityAttrValues(waavIND).TEXT_VALUE, 1, 30));
3382
3383 else
3384 return(null);
3385
3386 end if;
3387
3388 exception
3389 when no_data_found then
3390 --Check to ensure that cursor is not open
3391 if (curs_activityattr%ISOPEN) then
3392 CLOSE curs_activityattr;
3393 end if;
3394
3395 WF_CACHE.ActivityAttrValues(waavIND).PROCESS_ACTIVITY_ID := actid;
3396 WF_CACHE.ActivityAttrValues(waavIND).NAME := '#TIMEOUT';
3397 WF_CACHE.ActivityAttrValues(waavIND).VALUE_TYPE := 'CONSTANT';
3398 WF_CACHE.ActivityAttrValues(waavIND).TEXT_VALUE := '';
3399 WF_CACHE.ActivityAttrValues(waavIND).NUMBER_VALUE := '';
3400 WF_CACHE.ActivityAttrValues(waavIND).DATE_VALUE := to_date(NULL);
3401 return(null);
3402
3403 when others then
3404 --Check to ensure that cursor is not open
3405 if (curs_activityattr%ISOPEN) then
3406 CLOSE curs_activityattr;
3407 end if;
3408
3409 return(null);
3410
3411 end Activity_Timeout;
3412
3413 --
3414 -- Event_Activity (PRIVATE)
3415 -- Execute an event activity.
3416 -- IN
3417 -- itemtype - A valid item type
3418 -- itemkey - A string generated from the application object's primary key.
3419 -- actid - The event process activity(instance id).
3420 -- funcmode - Function mode (RUN/CANCEL)
3421 -- OUT
3422 -- result - event activity reslt
3423 --
3424 procedure Event_Activity(
3425 itemtype in varchar2,
3426 itemkey in varchar2,
3427 actid in number,
3428 funcmode in varchar2,
3429 result out NOCOPY varchar2)
3430 is
3431 event_name varchar2(240); -- Event name filter
3432 direction varchar2(8); -- Event direction (receive/raise/send)
3433 evtname varchar2(240); -- Event name (for raise)
3434 evtkey varchar2(2000); -- Event key (for raise)
3435 msgdata clob; -- Message contents as clob (for raise)
3436 evtmsg wf_event_t; -- Event message (for send)
3437 attr varchar2(4000); -- Attrs for event override (for send)
3438 priority number; -- Event priority (for send)
3439 atsign pls_integer; -- Used to parse agent@system
3440 outagent wf_agent_t; -- Out agent override (send)
3441 toagent wf_agent_t; -- To agent override (send)
3442 atype varchar2(8);
3443 asubtype varchar2(8);
3444 aformat varchar2(240);
3445 avalue varchar2(4000);
3446 parameterlist wf_parameter_list_t;
3447 parametert wf_parameter_t;
3448 counter pls_integer;
3449 block_mode varchar2(1);
3450 cb_event_name varchar2(240);
3451 cb_event_key varchar2(2000);
3452
3453 -- BUG 2452470 CTILLEY
3454 -- Updated the cursor to select value_type and text_value to pass based
3455 -- on the type since the item attribute may not be the same name as the
3456 -- activity attribute
3457
3458 CURSOR CURS_ACTATTRS IS
3459 SELECT NAME, VALUE_TYPE, TEXT_VALUE
3460 FROM WF_ACTIVITY_ATTR_VALUES
3461 WHERE PROCESS_ACTIVITY_ID = EVENT_ACTIVITY.ACTID
3462 AND substrb(NAME,1,1) <> '#';
3463
3464 --Bug 2761887
3465 l_length integer;
3466
3467 -- Bug 3908657
3468 l_fresh_parameterlist boolean;
3469
3470 begin
3471 -- Do nothing in cancel or timeout mode
3472 if (funcmode <> wf_engine.eng_run) then
3473 result := wf_engine.eng_null;
3474 return;
3475 end if;
3476
3477 -- Get event name and direction
3478 Wf_Activity.Event_Info(itemtype, itemkey, actid, event_name,
3479 direction);
3480
3481 if (direction = wf_engine.eng_receive) then
3482 -- RECEIVE event
3483 -- Block and wait for event to be received.
3484 result := wf_engine.eng_notified||':'||wf_engine.eng_null||
3485 ':'||wf_engine.eng_null;
3486 return;
3487
3488 elsif (direction = wf_engine.eng_raise) then
3489 -- RAISE event
3490 -- Retrieve applicable attrs
3491 -- #EVENTNAME
3492 evtname := Wf_Engine.GetActivityAttrText(itemtype, itemkey, actid,
3493 wf_engine.eng_eventname);
3494 if (evtname is null) then
3495 Wf_Core.Token('#EVENTNAME', '');
3496 Wf_Core.Raise('WFSQL_ARGS');
3497 end if;
3498 -- #EVENTKEY
3499 evtkey := Wf_Engine.GetActivityAttrText(itemtype, itemkey, actid,
3500 wf_engine.eng_eventkey);
3501 if (evtkey is null) then
3502 Wf_Core.Token('#EVENTKEY', '');
3503 Wf_Core.Raise('WFSQL_ARGS');
3504 end if;
3505
3506 -- #EVENTMESSAGE (may be null)
3507 msgdata := Wf_Engine.GetActivityAttrClob(itemtype, itemkey, actid,
3508 Wf_Engine.eng_eventmessage);
3509
3510 --Bug #2761887
3511 --Now verify if we have the reserved activity attribute
3512 --#EVENTMESSAGE2 is set
3513 begin
3514 evtmsg := Wf_Engine.getActivityAttrEvent(itemtype, itemkey,
3515 actid, wf_engine.eng_defaultevent);
3516
3517 exception
3518 when others then
3519 --If this atrribute does not exist the exception is
3520 --raised we just ignore it.
3521 if (wf_core.error_name ='WFENG_ACTIVITY_ATTR') then
3522 --we will initialise the event here so that the
3523 --parameterlist is usable down the line
3524 wf_event_t.initialize(evtmsg);
3525 --clear the error stack
3526 wf_core.clear;
3527 else
3528 --Any other error raise it
3529 raise;
3530 end if;
3531
3532 end ;
3533
3534 --If the clob and parameterlist exist we will just set them
3535 l_length := dbms_lob.getlength(msgdata);
3536 IF l_length IS NULL THEN
3537 --Set the event message from the default #RAISEEVENT
3538 msgdata := evtmsg.event_data ;
3539 --we will add the parameterlist at the end
3540 end if;
3541
3542 -- Check if any Activity Attributes set, these will
3543 -- be set in the parameter list
3544
3545 for attr in curs_actattrs loop
3546
3547 -- Reset Attribute Value
3548 avalue :=null;
3549
3550 -- Bug2452470 CTILLEY: Need to take into account that the activity
3551 -- attribute may not be an item attribute and even if it is it may
3552 -- not be the same name as the activity attribute.
3553
3554 -- Get the activity attribute type
3555 if attr.value_type = 'ITEMATTR' then
3556 wf_engine.GetItemAttrInfo(itemtype, attr.text_value, atype,
3557 asubtype, aformat);
3558 else
3559 wf_engine.GetActivityAttrInfo(itemtype, itemkey, actid, attr.name,
3560 atype, asubtype, aformat);
3561 end if;
3562
3563 -- NUMBER Value
3564 if (atype = 'NUMBER') then
3565 avalue:= to_char(wf_engine.GetActivityAttrNumber(itemtype,itemkey,
3566 actid, attr.name),wf_core.canonical_number_mask);
3567
3568 -- DATE Value
3569 elsif (atype = 'DATE') then
3570 avalue:=to_char(wf_engine.GetActivityAttrDate(itemtype,itemkey,
3571 actid, attr.name),nvl(aformat,wf_core.canonical_date_mask));
3572
3573 -- TEXT/LOOKUP/ROLE/ATTR etc Value
3574 else
3575 avalue:=substr(wf_engine.GetActivityAttrText(itemtype,itemkey,
3576 actid, attr.name),1,2000);
3577 end if;
3578
3579 --Set the Value into the Parameter List
3580 --Bug 2761887
3581 --Lets use the addparametertolist for the event parameterlist
3582 --so that any attribute of the same name overwrites
3583 --existing ones in the default events parameter list.
3584
3585 evtmsg.AddParameterToList(attr.name,avalue);
3586
3587 end loop;
3588
3589 -- We also need to set the current work item as the master
3590 -- work item or context into the parameter list, this will be picked
3591 -- up during the Receive Event processing
3592
3593 --Bug 2761887
3594 evtmsg.addparametertolist('#CONTEXT',itemtype||':'||itemkey);
3595 --Now set the event parameterlistr into the parameterlist
3596 parameterlist := evtmsg.GETPARAMETERLIST;
3597
3598
3599
3600 -- Raise event
3601 Wf_Event.Raise(
3602 p_event_name => evtname,
3603 p_event_key => evtkey,
3604 p_event_data => msgdata,
3605 p_parameters => parameterlist);
3606
3607 elsif (direction = wf_engine.eng_send) then
3608 -- SEND event
3609
3610 -- Get base event struct to send
3611 -- #EVENTMESSAGE
3612 evtmsg := Wf_Engine.GetActivityAttrEvent(itemtype, itemkey, actid,
3613 Wf_Engine.eng_eventmessage);
3614 if (evtmsg is null) then
3615 Wf_Core.Token('#EVENTMESSAGE', '');
3616 Wf_Core.Raise('WFSQL_ARGS');
3617 end if;
3618
3619 -- Initialize the variables here
3620 outagent := wf_agent_t(NULL, NULL); -- Out agent override (send)
3621 toagent := wf_agent_t(NULL, NULL); -- To agent override (send)
3622 parametert := wf_parameter_t(null, null);
3623 counter := 0;
3624
3625
3626 -- Other attributes are treated as over-rides to values in the
3627 -- event message struct retrieved above.
3628 -- Use them to reset values if present.
3629 -- #EVENTNAME
3630 attr := Wf_Engine.GetActivityAttrText(itemtype, itemkey, actid,
3631 wf_engine.eng_eventname);
3632 if (attr is not null) then
3633 evtmsg.SetEventName(attr);
3634 end if;
3635
3636 -- #EVENTKEY
3637 attr := Wf_Engine.GetActivityAttrText(itemtype, itemkey, actid,
3638 wf_engine.eng_eventkey);
3639 if (attr is not null) then
3640 evtmsg.SetEventKey(attr);
3641 end if;
3642
3643 -- #EVENTOUTAGENT
3644 attr := Wf_Engine.GetActivityAttrText(itemtype, itemkey, actid,
3645 Wf_Engine.eng_eventoutagent);
3646 if (attr is not null) then
3647 -- Value must be in format <agent>@<system>
3648 atsign := instr(attr, '@');
3649 if (atsign is null) then
3650 Wf_Core.Token('#EVENTOUTAGENT', attr);
3651 Wf_Core.Raise('WFSQL_ARGS');
3652 end if;
3653 outagent.setname(substr(attr, 1, atsign-1));
3654 outagent.setsystem(substr(attr, atsign+1));
3655 evtmsg.SetFromAgent(outagent);
3656 end if;
3657
3658 -- #EVENTTOAGENT
3659 attr := Wf_Engine.GetActivityAttrText(itemtype, itemkey, actid,
3660 Wf_Engine.eng_eventtoagent);
3661 if (attr is not null) then
3662 -- Value must be in format <agent>@<system>
3663 atsign := instr(attr, '@');
3664 if (atsign is null) then
3665 Wf_Core.Token('#EVENTTOAGENT', attr);
3666 Wf_Core.Raise('WFSQL_ARGS');
3667 end if;
3668 toagent.setname(substr(attr, 1, atsign-1));
3669 toagent.setsystem(substr(attr, atsign+1));
3670 evtmsg.SetToAgent(toagent);
3671 end if;
3672
3673 -- #PRIORITY
3674 begin
3675 priority := Wf_Engine.GetActivityAttrNumber(itemtype, itemkey, actid,
3676 wf_engine.eng_priority);
3677 if (priority is not null) then
3678 evtmsg.SetPriority(priority);
3679 end if;
3680 exception
3681 when others then
3682 if (wf_core.error_name = 'WFENG_ACTIVITY_ATTR') then
3683 -- Ignore if priority attr not found
3684 Wf_Core.Clear;
3685 else
3686 raise;
3687 end if;
3688 end;
3689
3690 -- Correlation ID
3691 -- Set message correlation id to itemkey if not already set
3692 if (evtmsg.GetCorrelationId is null) then
3693 evtmsg.SetCorrelationId(itemkey);
3694 end if;
3695
3696 -- Bug 2065730
3697 -- Initialize the parameterlist with the existing parameterlist
3698 -- of the event.
3699 -- Obtain the activity attributes through the cursor
3700 -- Build the attribute name and value in the parameter list
3701 -- parameterlist
3702 -- Call setParameterList and set the parameter list
3703 -- This is done before passing the event to the SEND.
3704
3705 parameterlist := evtmsg.getParameterList();
3706
3707 -- Bug 3908657
3708 -- Avoid duplicate attribute by calling AddParamToList()
3709 -- Keep the optimization if we start with a fresh list,
3710 -- since addParameterToList loop through all the attributes
3711 -- to avoid duplicate, it could be expansive.
3712
3713 if (parameterlist is null) then
3714 l_fresh_parameterlist := true;
3715 else
3716 l_fresh_parameterlist := false;
3717 end if;
3718
3719
3720 if (l_fresh_parameterlist) then
3721 parameterlist := wf_parameter_list_t(null);
3722 parametert.SetName('#CONTEXT');
3723 parametert.SetValue(itemtype||':'||itemkey);
3724 parameterlist(1) := parametert;
3725 counter := 1;
3726 else
3727 evtmsg.addParameterToList('#CONTEXT',itemtype||':'||itemkey);
3728 end if;
3729
3730 for attr in curs_actattrs loop
3731 avalue :=null;
3732
3733 wf_engine.GetActivityAttrInfo(itemtype, itemkey, actid,
3734 attr.name, atype, asubtype, aformat);
3735
3736 if (atype = 'NUMBER') then
3737 avalue := to_char(wf_engine.GetActivityAttrNumber(
3738 itemtype,itemkey,actid, attr.name),
3739 wf_core.canonical_number_mask);
3740
3741 elsif (atype = 'DATE') then
3742 avalue := to_char(wf_engine.GetActivityAttrDate(
3743 itemtype,itemkey,actid, attr.name),
3744 nvl(aformat,wf_core.canonical_date_mask));
3745
3746 else
3747 avalue := substr(wf_engine.GetActivityAttrText(
3748 itemtype,itemkey,actid, attr.name),1,2000);
3749 end if;
3750
3751 if (l_fresh_parameterlist) then
3752 -- Set the Value into the Parameter List
3753 parameterlist.extend;
3754 counter := counter + 1;
3755 parametert.SetName(attr.name);
3756 parametert.SetValue(avalue);
3757 parameterlist(counter) := parametert;
3758 else
3759 evtmsg.addParameterToList(attr.name,avalue);
3760 end if;
3761 end loop;
3762
3763 if (l_fresh_parameterlist) then
3764 evtmsg.setParameterList(parameterlist);
3765 end if;
3766 -- End 2065730
3767
3768 -- Bug 2294745 - Enh for OTA callback to workflow
3769 -- add activity id to event parameter list
3770 evtmsg.addParameterToList('ACTIVITY_ID',actid);
3771
3772 -- get block mode and add to event parameterlist
3773 block_mode := wf_engine.GetActivityAttrText(itemtype, itemkey, actid,
3774 wf_engine.eng_block_mode, true);
3775 if (block_mode is not null) then
3776 evtmsg.addParameterToList(wf_engine.eng_block_mode, block_mode);
3777 end if;
3778
3779 -- get cb event name and add to event parameterlist
3780 cb_event_name := wf_engine.GetActivityAttrText(itemtype, itemkey, actid,
3781 wf_engine.eng_cb_event_name, true);
3782 if (cb_event_name is not null) then
3783 evtmsg.addParameterToList(wf_engine.eng_cb_event_name, cb_event_name);
3784 end if;
3785
3786 -- get cb event key and add to event parameterlist
3787 cb_event_key := wf_engine.GetActivityAttrText(itemtype, itemkey, actid,
3788 wf_engine.eng_cb_event_key, true);
3789 if (cb_event_key is not null) then
3790 evtmsg.addParameterToList(wf_engine.eng_cb_event_key, cb_event_key);
3791 end if;
3792 -- End 2294745
3793
3794 -- Send event
3795 Wf_Event.Send(p_event => evtmsg);
3796
3797 if (wf_core.Translate('WF_INSTALL')='EMBEDDED') then
3798 ECX_ERRORLOG.Outbound_Log (p_event => evtmsg);
3799 end if;
3800
3801 --<rwunderl:2699059> Checking for reserved attribute to store msgid.
3802 begin
3803 WF_ENGINE.SetItemAttrText(itemtype, itemkey,
3804 WF_ENGINE.GetActivityAttrText(itemtype, itemkey, actid,
3805 '#WF_EVT_MSGID'),
3806 rawtohex(WF_EVENT.g_msgID));
3807
3808 exception
3809 when others then
3810 if (WF_CORE.error_name = 'WFENG_ACTIVITY_ATTR') then
3811 null; --If the activity attribute is not setup by the ItemType
3812 --developer, we will do nothing.
3813 else
3814 raise; --Some other problem has occured.
3815
3816 end if;
3817 end;
3818
3819 else
3820 -- Unrecognized event direction, raise error
3821 Wf_Core.Token('ITEMTYPE', itemtype);
3822 Wf_Core.Token('ITEMKEY', itemkey);
3823 Wf_Core.Token('ACTID', to_char(actid));
3824 Wf_Core.Token('DIRECTION', direction);
3825 Wf_Core.Raise('WFSQL_INTERNAL');
3826 end if;
3827
3828
3829 --If block mode is set to 'Y'then the activity status is
3830 --set to NOTIFIED.
3831 if (block_mode = 'Y') then
3832 result := wf_engine.eng_notified||':'||wf_engine.eng_null||':'
3833 ||wf_engine.eng_null;
3834 else
3835 -- when block_mode is null or is not 'Y'
3836 result := 'COMPLETE:#NULL';
3837 end if;
3838
3839 exception
3840 when others then
3841 Wf_Core.Context('Wf_Engine_Util', 'Event_Activity', itemtype,
3842 itemkey, to_char(actid), funcmode);
3843 raise;
3844 end Event_Activity;
3845
3846 end Wf_Engine_Util;