DBA Data[Home] [Help]

PACKAGE BODY: APPS.WF_ENGINE_UTIL

Source


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;