REM dbdrv: sql ~PROD ~PATH ~FILE none none none package &phase=plb \
REM dbdrv: checkfile:~PROD:~PATH:~FILE
/*=======================================================================+
| Copyright (C) 1995 Oracle Corporation Redwood Shores, California, Usa|
| All Rights Reserved. |
+=======================================================================+
| FILENAME
| wfengb.pls
| DESCRIPTION
| PL/SQL body for package: WF_ENGINE
| MODIFICATION LOG
| 9/2001 JWSMITH BUG 1893606 - Updated AbortProcess.
| 2/2002 JWSMITH BUG 2001012 - Increased user, prole, performer,
| c_prole, c_assigned_user, assuser, performrole,
| c_assigned_user to varchar2(320)
| 7/2002 CTILLEY BUG 2452470 - Updated Event_Activity to be able to
| handle attributes that are not item attributes
| 2/2003 CTILLEY BUG 2811737 - Updated CompleteActivity so that when
| the notification is closed the end_date is set to sysdate
| 12/2003 SHANJGIK BUG 2722369 AssignActivity new parameters added
| 12/2004 HTAY BUG 3824367 - Incorporated cursor sharing among API
| GetActivityAttrText,GetActivityAttrNumber,GetActivityAttrDate,
| GetActivityAttrClob,GetActivityAttrEvent and Activity_Timeout
| 02/2005 HTAY BUG 4117740 - Called wf_item.clearcache() when #SYNCH
failed in CreateProcess and Start_Process_internal
*=======================================================================*/
set verify off;
WHENEVER SQLERROR EXIT FAILURE ROLLBACK;
WHENEVER OSERROR EXIT FAILURE ROLLBACK;
set scan off
create or replace package body WF_ENGINE as
/* $Header: wfengb.pls 26.108 2005/03/02 05:14:50 rwunderl ship $ */
type InstanceArrayTyp is table of pls_integer
index by binary_integer;
type RowidArrayTyp is table of rowid
index by binary_integer;
--
-- Exception
--
no_savepoint exception;
bad_format exception; --
pragma EXCEPTION_INIT(no_savepoint, -1086);
pragma EXCEPTION_INIT(bad_format, -6502); --
-- Bug 2607770
resource_busy exception;
pragma exception_init(resource_busy, -00054);
-- private variable
schema varchar2(30);
-- Bug 3824367
-- Optimizing the code using a single cursor with binds
cursor curs_activityattr (c_actid NUMBER, c_aname VARCHAR2) is
select WAAV.PROCESS_ACTIVITY_ID, WAAV.NAME, WAAV.VALUE_TYPE,
WAAV.TEXT_VALUE, WAAV.NUMBER_VALUE, WAAV.DATE_VALUE
from WF_ACTIVITY_ATTR_VALUES WAAV
where WAAV.PROCESS_ACTIVITY_ID = c_actid
and WAAV.NAME = c_aname;
--
-- Current_Schema (PRIVATE)
-- Return the current schema
--
function Current_Schema
return varchar2
is
begin
if (wf_engine.schema is null) then
select sys_context('USERENV','CURRENT_SCHEMA')
into wf_engine.schema
from sys.dual;
end if;
return wf_engine.schema;
exception
when OTHERS then
Wf_Core.Context('Wf_Engine', 'Current_Schema');
raise;
end Current_Schema;
--
-- AddItemAttr (PUBLIC)
-- Add a new unvalidated run-time item attribute.
-- IN:
-- itemtype - item type
-- itemkey - item key
-- aname - attribute name
-- text_value - add text value to it if provided.
-- number_value - add number value to it if provided.
-- date_value - add date value to it if provided.
-- NOTE:
-- The new attribute has no type associated. Get/set usages of the
-- attribute must insure type consistency.
--
procedure AddItemAttr(itemtype in varchar2,
itemkey in varchar2,
aname in varchar2,
text_value in varchar2,
number_value in number,
date_value in date)
is
wiavIND NUMBER;
iStatus PLS_INTEGER;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
-- Insure this is a valid item
elsif (not Wf_Item.Item_Exist(itemtype, itemkey)) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
end if;
if (itemkey = wf_engine.eng_synch) then
--ItemAttrValues are indexed on the hash value of the name.
--ItemKey is not used here because we are in #SYNCH mode.
WF_CACHE.GetItemAttrValue(itemtype, itemKey, aname, iStatus, wiavIND);
WF_CACHE.ItemAttrValues(wiavIND).ITEM_TYPE := itemtype;
WF_CACHE.ItemAttrValues(wiavIND).ITEM_KEY := itemKey;
WF_CACHE.ItemAttrValues(wiavIND).NAME := aname;
WF_CACHE.ItemAttrValues(wiavIND).TEXT_VALUE := text_value;
WF_CACHE.ItemAttrValues(wiavIND).NUMBER_VALUE := number_value;
WF_CACHE.ItemAttrValues(wiavIND).DATE_VALUE := date_value;
else
insert into WF_ITEM_ATTRIBUTE_VALUES (
ITEM_TYPE,
ITEM_KEY,
NAME,
TEXT_VALUE,
NUMBER_VALUE,
DATE_VALUE
) values (
itemtype,
itemkey,
aname,
AddItemAttr.text_value,
AddItemAttr.number_value,
AddItemAttr.date_value
);
end if;
exception
when dup_val_on_index then
Wf_Core.Context('Wf_Engine', 'AddItemAttr', itemtype, itemkey, aname);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ATTRIBUTE', aname);
Wf_Core.Raise('WFENG_ITEM_ATTR_UNIQUE');
when others then
Wf_Core.Context('Wf_Engine', 'AddItemAttr', itemtype, itemkey, aname);
raise;
end AddItemAttr;
--
-- AddItemAttrTextArray (PUBLIC)
-- Add an array of new unvalidated run-time item attributes of type text.
-- IN:
-- itemtype - item type
-- itemkey - item key
-- aname - Array of Names
-- avalue - Array of New values for attribute
-- NOTE:
-- The new attributes have no type associated. Get/set usages of these
-- attributes must insure type consistency.
--
procedure AddItemAttrTextArray(
itemtype in varchar2,
itemkey in varchar2,
aname in Wf_Engine.NameTabTyp,
avalue in Wf_Engine.TextTabTyp)
is
iStatus pls_integer;
wiavIND pls_integer;
arrayIndex pls_integer;
success_cnt pls_integer;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
-- Insure this is a valid item and validate that the text array
-- tables passed in are in proper order.
elsif (not Wf_Item.Item_Exist(itemtype, itemkey)) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
elsif (aname.COUNT = 0 or avalue.COUNT = 0) then
-- Do not do anything if index table is empty.
return;
elsif (aname.LAST <> avalue.LAST or aname.COUNT <> avalue.COUNT) then
-- Raise an error if the two index tables do not end at the same index
-- or do not have the same number of elements.
Wf_Core.Raise('WFENG_ITEM_ATTR_ARRAY_MISMATCH');
end if;
-- Check to see if we are in synch mode and use WF_CACHE.
success_cnt := 0;
if (itemkey = wf_engine.eng_synch) then
-- Use WF_CACHE.ItemAttrValues for #SYNCH mode.
for arrayIndex in aname.FIRST..aname.LAST loop
-- first check duplicate attribute name
WF_CACHE.GetItemAttrValue( itemType, itemKey, aname(arrayIndex), iStatus,
wiavIND);
if (iStatus = WF_CACHE.task_SUCCESS) then
null; --There is already an attribute in cache, so we will try to
--load the rest, then raise a dup_val_on_index after we
--complete.
else
WF_CACHE.ItemAttrValues(wiavIND).ITEM_TYPE := itemtype;
WF_CACHE.ItemAttrValues(wiavIND).ITEM_KEY := itemKey;
WF_CACHE.ItemAttrValues(wiavIND).NAME := aname(arrayIndex);
WF_CACHE.ItemAttrValues(wiavIND).TEXT_VALUE := avalue(arrayIndex);
success_cnt := success_cnt + 1;
end if;
end loop;
else
forall arrayIndex in aname.FIRST..aname.LAST
insert into WF_ITEM_ATTRIBUTE_VALUES (
ITEM_TYPE,
ITEM_KEY,
NAME,
TEXT_VALUE
) values (
itemtype,
itemkey,
aname(arrayIndex),
avalue(arrayIndex)
);
success_cnt := SQL%ROWCOUNT;
if (success_cnt <> aname.COUNT) then
raise dup_val_on_index;
end if;
end if;
exception
when dup_val_on_index then
Wf_Core.Context('Wf_Engine', 'AddItemAttrTextArray', itemtype, itemkey);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('TOTAL', to_char(aname.COUNT));
Wf_Core.Token('SUCCESS', to_char(success_cnt));
Wf_Core.Raise('WFENG_ITEM_ATTR_ARRAY');
when others then
Wf_Core.Context('Wf_Engine', 'AddItemAttrTextArray', itemtype, itemkey);
raise;
end AddItemAttrTextArray;
--
-- AddItemAttrNumberArray (PUBLIC)
-- Add an array of new unvalidated run-time item attributes of type number.
-- IN:
-- itemtype - item type
-- itemkey - item key
-- aname - Array of Names
-- avalue - Array of New values for attribute
-- NOTE:
-- The new attributes have no type associated. Get/set usages of these
-- attributes must insure type consistency.
--
procedure AddItemAttrNumberArray(
itemtype in varchar2,
itemkey in varchar2,
aname in Wf_Engine.NameTabTyp,
avalue in Wf_Engine.NumTabTyp)
is
arrayIndex pls_integer;
iStatus pls_integer;
wiavIND NUMBER;
success_cnt number;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
-- Insure this is a valid item, and that the attribute arrays are
-- matching and in the proper form.
elsif (not Wf_Item.Item_Exist(itemtype, itemkey)) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
elsif (aname.COUNT = 0 or avalue.COUNT = 0) then
-- Do not do anything if index table is empty.
return;
elsif (aname.LAST <> avalue.LAST or aname.COUNT <> avalue.COUNT) then
-- Raise an error if the two index tables do not end at the same index
-- or do not have the same number of elements.
Wf_Core.Raise('WFENG_ITEM_ATTR_ARRAY_MISMATCH');
end if;
success_cnt := 0;
--If we are in #SYNCH mode, we will go ahead and use WF_CACHE to
--Store the attributes.
if (itemkey = wf_engine.eng_synch) then
for arrayIndex in aname.FIRST..aname.LAST loop
-- first check duplicate attribute name
WF_CACHE.GetItemAttrValue(itemtype, itemKey, aname(arrayIndex), iStatus,
wiavIND);
if (iStatus = WF_CACHE.task_SUCCESS) then
null; --Proceed and attempt to add the rest before raising
--dup_val_on_index.
else
WF_CACHE.ItemAttrValues(wiavIND).ITEM_TYPE := itemtype;
WF_CACHE.ItemAttrValues(wiavIND).ITEM_KEY := itemKey;
WF_CACHE.ItemAttrValues(wiavIND).NAME := aname(arrayIndex);
WF_CACHE.ItemAttrValues(wiavIND).NUMBER_VALUE := avalue(arrayIndex);
success_cnt := success_cnt + 1;
end if;
end loop;
else
forall arrayIndex in aname.FIRST..aname.LAST
insert into WF_ITEM_ATTRIBUTE_VALUES (
ITEM_TYPE,
ITEM_KEY,
NAME,
NUMBER_VALUE
) values (
itemtype,
itemkey,
aname(arrayIndex),
avalue(arrayIndex)
);
success_cnt := SQL%ROWCOUNT;
if (success_cnt <> aname.COUNT) then
raise dup_val_on_index;
end if;
end if;
exception
when dup_val_on_index then
Wf_Core.Context('Wf_Engine', 'AddItemAttrNumberArray', itemtype, itemkey);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('TOTAL', to_char(aname.COUNT));
Wf_Core.Token('SUCCESS', to_char(success_cnt));
Wf_Core.Raise('WFENG_ITEM_ATTR_ARRAY');
when others then
Wf_Core.Context('Wf_Engine', 'AddItemAttrNumberArray', itemtype, itemkey);
raise;
end AddItemAttrNumberArray;
--
-- AddItemAttrDateArray (PUBLIC)
-- Add an array of new unvalidated run-time item attributes of type date.
-- IN:
-- itemtype - item type
-- itemkey - item key
-- aname - Array of Names
-- avalue - Array of New values for attribute
-- NOTE:
-- The new attributes have no type associated. Get/set usages of these
-- attributes must insure type consistency.
--
procedure AddItemAttrDateArray(
itemtype in varchar2,
itemkey in varchar2,
aname in Wf_Engine.NameTabTyp,
avalue in Wf_Engine.DateTabTyp)
is
iStatus pls_integer;
wiavIND number;
success_cnt number;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Insure this is a valid item, and the array tables match and are
-- in proper form.
if (not Wf_Item.Item_Exist(itemtype, itemkey)) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
elsif (aname.COUNT = 0 or avalue.COUNT = 0) then
-- Do not do anything if index table is empty.
return;
elsif (aname.LAST <> avalue.LAST or aname.COUNT <> avalue.COUNT) then
-- Raise an error if the two index tables do not end at the same index
-- or do not have the same number of elements.
Wf_Core.Raise('WFENG_ITEM_ATTR_ARRAY_MISMATCH');
end if;
success_cnt := 0;
-- If in #SYNCH mode, we will use WF_CACHE to store the attributes.
if (itemkey = wf_engine.eng_synch) then
for arrayIndex in aname.FIRST..aname.LAST loop
WF_CACHE.GetItemAttrValue(itemtype, itemKey, aname(arrayIndex), iStatus,
wiavIND);
if (iStatus = WF_CACHE.task_SUCCESS) then
null; --Attempt to add the rest before raising the dup_val_on_index
else
WF_CACHE.ItemAttrValues(wiavIND).ITEM_TYPE := itemtype;
WF_CACHE.ItemAttrValues(wiavIND).ITEM_KEY := itemKey;
WF_CACHE.ItemAttrValues(wiavIND).NAME := aname(arrayIndex);
WF_CACHE.ItemAttrValues(wiavIND).DATE_VALUE := avalue(arrayIndex);
success_cnt := success_cnt + 1;
end if;
end loop;
else
forall arrayIndex in aname.FIRST..aname.LAST
insert into WF_ITEM_ATTRIBUTE_VALUES (
ITEM_TYPE,
ITEM_KEY,
NAME,
DATE_VALUE
) values (
itemtype,
itemkey,
aname(arrayIndex),
avalue(arrayIndex)
);
success_cnt := SQL%ROWCOUNT;
if (success_cnt <> aname.COUNT) then
raise dup_val_on_index;
end if;
end if;
exception
when dup_val_on_index then
Wf_Core.Context('Wf_Engine', 'AddItemAttrDateArray', itemtype, itemkey);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('TOTAL', to_char(aname.COUNT));
Wf_Core.Token('SUCCESS', to_char(success_cnt));
Wf_Core.Raise('WFENG_ITEM_ATTR_ARRAY');
when others then
Wf_Core.Context('Wf_Engine', 'AddItemAttrDateArray', itemtype, itemkey);
raise;
end AddItemAttrDateArray;
--
-- SetItemAttrText (PUBLIC)
-- Set the value of a text item attribute.
-- If the attribute is a NUMBER or DATE type, then translate the
-- text-string value to a number/date using attribute format.
-- For all other types, store the value directly.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- aname - Attribute Name
-- avalue - New value for attribute
--
procedure SetItemAttrText(itemtype in varchar2,
itemkey in varchar2,
aname in varchar2,
avalue in varchar2)
is
tvalue varchar2(4000);
nvalue number;
dvalue date;
i pls_integer;
status PLS_INTEGER;
wiaIND NUMBER;
wiavIND NUMBER;
role_info_tbl wf_directory.wf_local_roles_tbl_type;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Get type and format of attr.
-- This is used for translating number/date strings.
WF_CACHE.GetItemAttribute(itemtype, aname, status, wiaIND);
if (status <> WF_CACHE.task_SUCCESS) then
begin
select WIA.ITEM_TYPE, WIA.NAME, WIA.TYPE, WIA.SUBTYPE, WIA.FORMAT,
WIA.TEXT_DEFAULT, WIA.NUMBER_DEFAULT, WIA.DATE_DEFAULT
into WF_CACHE.ItemAttributes(wiaIND)
from WF_ITEM_ATTRIBUTES WIA
where WIA.ITEM_TYPE = itemtype
and WIA.NAME = aname;
exception
when no_data_found then
-- This is an unvalidated runtime attr.
-- Treat it as a varchar2.
WF_CACHE.ItemAttributes(wiaIND).ITEM_TYPE := itemtype;
WF_CACHE.ItemAttributes(wiaIND).NAME := aname;
WF_CACHE.ItemAttributes(wiaIND).TYPE := 'VARCHAR2';
WF_CACHE.ItemAttributes(wiaIND).SUBTYPE := '';
WF_CACHE.ItemAttributes(wiaIND).FORMAT := '';
end;
end if;
-- Update attribute value in appropriate type column.
if (WF_CACHE.ItemAttributes(wiaIND).TYPE = 'NUMBER') then
if (WF_CACHE.ItemAttributes(wiaIND).FORMAT is null) then
nvalue := to_number(avalue);
else
nvalue := to_number(avalue, WF_CACHE.ItemAttributes(wiaIND).FORMAT);
end if;
Wf_Engine.SetItemAttrNumber(itemtype, itemkey, aname, nvalue);
elsif (WF_CACHE.ItemAttributes(wiaIND).TYPE = 'DATE') then
if (WF_CACHE.ItemAttributes(wiaIND).FORMAT is null) then
dvalue := to_date(avalue);
else
dvalue := to_date(avalue, WF_CACHE.ItemAttributes(wiaIND).FORMAT);
end if;
Wf_Engine.SetItemAttrDate(itemtype, itemkey, aname, dvalue);
else -- One of the text values
if (WF_CACHE.ItemAttributes(wiaIND).TYPE = 'VARCHAR2') then
-- VARCHAR2 type. Truncate value as necessary
if (WF_CACHE.ItemAttributes(wiaIND).FORMAT is null) then
tvalue := avalue;
else
tvalue := substr(avalue, 1,
to_number(WF_CACHE.ItemAttributes(wiaIND).FORMAT));
end if;
elsif (WF_CACHE.ItemAttributes(wiaIND).TYPE = 'ROLE') then
-- ROLE type. Decode to internal name.
if (avalue is null) then
-- Null role values are ok
tvalue := '';
else
-- First check if value is internal name
Wf_Directory.GetRoleInfo2(avalue,role_info_tbl);
tvalue := role_info_tbl(1).name;
-- If not internal name, check for display_name
if (tvalue is null) then
begin
SELECT name
INTO tvalue
FROM wf_role_lov_vl
WHERE upper(display_name) = upper(avalue)
AND rownum = 1;
exception
when no_data_found then
-- Not displayed or internal role name, error
wf_core.token('ROLE', avalue);
wf_core.raise('WFNTF_ROLE');
end;
end if;
end if;
else
-- LOOKUP, FORM, URL, DOCUMENT, misc type.
-- Use value directly.
tvalue := avalue;
end if;
-- Set the text value.
if (itemkey = wf_engine.eng_synch) then
-- Use WF_CACHE in synch mode
WF_CACHE.GetItemAttrValue(itemtype, itemKey, aname, status, wiavIND);
if (status <> WF_CACHE.task_SUCCESS) then
raise no_data_found;
else
WF_CACHE.ItemAttrValues(wiavIND).TEXT_VALUE := tvalue;
end if;
else
update WF_ITEM_ATTRIBUTE_VALUES set
TEXT_VALUE = tvalue
where ITEM_TYPE = itemtype
and ITEM_KEY = itemkey
and NAME = aname;
if (SQL%NOTFOUND) then
raise no_data_found;
end if;
end if;
end if;
exception
when no_data_found then
Wf_Core.Context('Wf_Engine', 'SetItemAttrText', itemtype, itemkey,
aname, avalue);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ATTRIBUTE', aname);
Wf_Core.Raise('WFENG_ITEM_ATTR');
when bad_format then --
Wf_Core.Context('Wf_Engine', 'SetItemAttrText', itemtype, itemkey,
aname, avalue);
Wf_Core.Token('VALUE', avalue);
Wf_Core.Token('TYPE', WF_CACHE.ItemAttributes(wiaIND).TYPE);
if (WF_CACHE.ItemAttributes(wiaIND).FORMAT is not null) then
WF_CORE.Token('FORMAT', '('||WF_CACHE.ItemAttributes(wiaIND).FORMAT||')');
else
WF_CORE.Token('FORMAT', '');
end if;
Wf_Core.Raise('WFENG_BAD_FORMAT');
when others then
Wf_Core.Context('Wf_Engine', 'SetItemAttrText', itemtype, itemkey,
aname, avalue);
raise;
end SetItemAttrText;
--
-- SetItemAttrText2 (PRIVATE)
-- Set the value of a text item attribute.
-- USE ONLY WITH VARCHAR2 VALUES.
--
-- IN:
-- p_itemtype - Item type
-- p_itemkey - Item key
-- p_aname - Attribute Name
-- p_avalue - New value for attribute
-- RETURNS:
-- boolean
--
function SetItemAttrText2(p_itemtype in varchar2,
p_itemkey in varchar2,
p_aname in varchar2,
p_avalue in varchar2) return boolean
is
status PLS_INTEGER;
wiavIND NUMBER;
begin
-- Check Arguments
if ((p_itemtype is null) or
(p_itemkey is null) or
(p_aname is null)) then
Wf_Core.Token('p_itemtype', nvl(p_itemtype, 'NULL'));
Wf_Core.Token('p_itemkey', nvl(p_itemkey, 'NULL'));
Wf_Core.Token('p_aname', nvl(p_aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Set the text value.
if (p_itemkey = wf_engine.eng_synch) then
-- Use WF_CACHE in synch mode
WF_CACHE.GetItemAttrValue(p_itemtype, p_itemkey, p_aname, status, wiavIND);
if (status <> WF_CACHE.task_SUCCESS) then
return FALSE;
else
WF_CACHE.ItemAttrValues(wiavIND).TEXT_VALUE := p_avalue;
return TRUE;
end if;
else
update WF_ITEM_ATTRIBUTE_VALUES set
TEXT_VALUE = p_avalue
where ITEM_TYPE = p_itemtype
and ITEM_KEY = p_itemkey
and NAME = p_aname;
if (SQL%NOTFOUND) then
return FALSE;
else
return TRUE;
end if;
end if;
exception
when no_data_found then
return FALSE;
when others then
Wf_Core.Context('Wf_Engine', 'SetItemAttrText2', p_itemtype, p_itemkey,
p_aname, p_avalue);
raise;
end SetItemAttrText2;
--
-- SetEventItemAttr (PRIVATE)
-- Set the value of an event item attribute.
-- If the attribute is a NUMBER or DATE type, then translate the
-- text-string value to a number/date using attribute format.
-- For all other types, store the value directly.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- aname - Attribute Name
-- avalue - New value for attribute
--
procedure SetEventItemAttr(itemtype in varchar2,
itemkey in varchar2,
aname in varchar2,
avalue in varchar2)
is
nvalue number;
dvalue date;
wiaIND NUMBER;
status PLS_INTEGER;
begin
-- Get type and format of attr.
-- This is used for translating number/date strings.
WF_CACHE.GetItemAttribute(itemtype, aname, status, wiaIND);
if (status <> WF_CACHE.task_SUCCESS) then
begin
select WIA.ITEM_TYPE, WIA.NAME, WIA.TYPE, WIA.SUBTYPE, WIA.FORMAT,
WIA.TEXT_DEFAULT, WIA.NUMBER_DEFAULT, WIA.DATE_DEFAULT
into WF_CACHE.ItemAttributes(wiaIND)
from WF_ITEM_ATTRIBUTES WIA
where WIA.ITEM_TYPE = itemtype
and WIA.NAME = aname;
exception
when no_data_found then
-- This is an unvalidated runtime attr.
-- Treat it as a varchar2.
WF_CACHE.ItemAttributes(wiaIND).ITEM_TYPE := itemtype;
WF_CACHE.ItemAttributes(wiaIND).NAME := aname;
WF_CACHE.ItemAttributes(wiaIND).TYPE := 'VARCHAR2';
WF_CACHE.ItemAttributes(wiaIND).SUBTYPE := '';
WF_CACHE.ItemAttributes(wiaIND).FORMAT := '';
end;
end if;
-- Update attribute value in appropriate type column.
if (WF_CACHE.ItemAttributes(wiaIND).TYPE = 'NUMBER') then
if (WF_CACHE.ItemAttributes(wiaIND).FORMAT is null) then
nvalue := to_number(avalue, wf_core.canonical_number_mask);
else
nvalue := to_number(avalue, WF_CACHE.ItemAttributes(wiaIND).FORMAT);
end if;
Wf_Engine.SetItemAttrNumber(itemtype, itemkey, aname, nvalue);
elsif (WF_CACHE.ItemAttributes(wiaIND).TYPE = 'DATE') then
if (WF_CACHE.ItemAttributes(wiaIND).FORMAT is null) then
dvalue := to_date(avalue, wf_core.canonical_date_mask);
else
dvalue := to_date(avalue, WF_CACHE.ItemAttributes(wiaIND).FORMAT);
end if;
Wf_Engine.SetItemAttrDate(itemtype, itemkey, aname, dvalue);
else -- One of the text values
Wf_Engine.SetItemAttrText(itemtype, itemkey, aname, avalue);
end if;
exception
when no_data_found then
Wf_Core.Context('Wf_Engine', 'SetEventItemAttr', itemtype, itemkey,
aname, avalue);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ATTRIBUTE', aname);
Wf_Core.Raise('WFENG_ITEM_ATTR');
when others then
Wf_Core.Context('Wf_Engine', 'SetEventItemAttr', itemtype, itemkey,
aname, avalue);
raise;
end SetEventItemAttr;
--
-- SetItemAttrNumber (PUBLIC)
-- Set the value of a number item attribute.
-- Attribute must be a NUMBER-type attribute.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- aname - Attribute Name
-- avalue - New value for attribute
--
procedure SetItemAttrNumber(itemtype in varchar2,
itemkey in varchar2,
aname in varchar2,
avalue in number)
is
iStatus PLS_INTEGER;
wiavIND NUMBER;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
if (itemkey = wf_engine.eng_synch) then
WF_CACHE.GetItemAttrValue(itemtype, itemKey, aname, iStatus, wiavIND);
if (iStatus <> WF_CACHE.task_SUCCESS) then
raise no_data_found;
else
WF_CACHE.ItemAttrValues(wiavIND).NUMBER_VALUE := avalue;
end if;
else
update WF_ITEM_ATTRIBUTE_VALUES set
NUMBER_VALUE = avalue
where ITEM_TYPE = itemtype
and ITEM_KEY = itemkey
and NAME = aname;
if (SQL%NOTFOUND) then
raise no_data_found;
end if;
end if;
exception
when no_data_found then
Wf_Core.Context('Wf_Engine', 'SetItemAttrNumber', itemtype, itemkey,
aname, to_char(avalue));
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ATTRIBUTE', aname);
Wf_Core.Raise('WFENG_ITEM_ATTR');
when others then
Wf_Core.Context('Wf_Engine', 'SetItemAttrNumber', itemtype, itemkey,
aname, to_char(avalue));
raise;
end SetItemAttrNumber;
--
-- SetItemAttrDate (PUBLIC)
-- Set the value of a date item attribute.
-- Attribute must be a DATE-type attribute.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- aname - Attribute Name
-- avalue - New value for attribute
--
procedure SetItemAttrDate(itemtype in varchar2,
itemkey in varchar2,
aname in varchar2,
avalue in date)
is
iStatus PLS_INTEGER;
wiavIND NUMBER;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
if (itemkey = wf_engine.eng_synch) then
WF_CACHE.GetItemAttrValue(itemtype, itemKey, aname, iStatus, wiavIND);
if (iStatus <> WF_CACHE.task_SUCCESS) then
raise no_data_found;
else
WF_CACHE.ItemAttrValues(wiavIND).DATE_VALUE := avalue;
end if;
else
update WF_ITEM_ATTRIBUTE_VALUES set
DATE_VALUE = avalue
where ITEM_TYPE = itemtype
and ITEM_KEY = itemkey
and NAME = aname;
if (SQL%NOTFOUND) then
raise no_data_found;
end if;
end if;
exception
when no_data_found then
Wf_Core.Context('Wf_Engine', 'SetItemAttrDate', itemtype, itemkey,
aname, to_char(avalue));
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ATTRIBUTE', aname);
Wf_Core.Raise('WFENG_ITEM_ATTR');
when bad_format then --
Wf_Core.Context('Wf_Engine', 'SetItemAttr', itemtype, itemkey,
aname, avalue);
Wf_Core.Token('VALUE', avalue);
Wf_Core.Token('FORMAT', 'DATE');
Wf_Core.Raise('WFENG_BAD_FORMAT');
when others then
Wf_Core.Context('Wf_Engine', 'SetItemAttrDate', itemtype, itemkey,
aname, to_char(avalue));
raise;
end SetItemAttrDate;
--
-- SetItemAttrDocument (PUBLIC)
-- Set the value of a document item attribute.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- aname - Attribute Name
-- documentid - Document Identifier - full concatenated document attribute
-- strings:
-- nodeid:libraryid:documentid:version:document_name
--
--
procedure SetItemAttrDocument(itemtype in varchar2,
itemkey in varchar2,
aname in varchar2,
documentid in varchar2)
is
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
Wf_Engine.SetItemAttrText(itemtype, itemkey, aname, documentid);
exception
when others then
Wf_Core.Context('Wf_Engine', 'SetItemAttrDocument', itemtype, itemkey,
aname);
raise;
end SetItemAttrDocument;
-- SetItemAttrEvent
-- Set event-type item attribute
-- IN
-- itemtype - process item type
-- itemkey - process item key
-- name - attribute name
-- event - attribute value
--
procedure SetItemAttrEvent(
itemtype in varchar2,
itemkey in varchar2,
name in varchar2,
event in wf_event_t)
is
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(name is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('NAME', nvl(name, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Not allowed in synch mode
if (itemkey = wf_engine.eng_synch) then
wf_core.token('OPERATION', 'Wf_Engine.SetItemAttrEvent');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
update WF_ITEM_ATTRIBUTE_VALUES set
EVENT_VALUE = SetItemAttrEvent.event
where ITEM_TYPE = SetItemAttrEvent.itemtype
and ITEM_KEY = SetItemAttrEvent.itemkey
and NAME = SetItemAttrEvent.name;
if (SQL%NOTFOUND) then
raise no_data_found;
end if;
exception
when no_data_found then
Wf_Core.Context('Wf_Engine', 'SetItemAttrEvent', itemtype, itemkey,
name);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ATTRIBUTE', name);
Wf_Core.Raise('WFENG_ITEM_ATTR');
when others then
Wf_Core.Context('Wf_Engine', 'SetItemAttrEvent', itemtype, itemkey,
name);
raise;
end SetItemAttrEvent;
--
-- SetItemAttrTextArray (PUBLIC)
-- Set the values of an array of text item attribute.
-- Unlike SetItemAttrText(), it stores the values directly.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- aname - Array of Names
-- avalue - Array of New values for attribute
--
procedure SetItemAttrTextArray(
itemtype in varchar2,
itemkey in varchar2,
aname in Wf_Engine.NameTabTyp,
avalue in Wf_Engine.TextTabTyp)
is
status pls_integer;
arrayIndex pls_integer;
wiavIND number;
success_cnt number;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
if (aname.COUNT = 0 or avalue.COUNT = 0) then
-- Do not do anything if index table is empty.
return;
elsif (aname.LAST <> avalue.LAST or aname.COUNT <> avalue.COUNT) then
-- Raise an error if the two index tables do not end at the same index
-- or do not have the same number of elements.
Wf_Core.Raise('WFENG_ITEM_ATTR_ARRAY_MISMATCH');
end if;
-- Set the text value.
if (itemkey = wf_engine.eng_synch) then
-- Use WF_CACHE in synch mode
success_cnt := 0;
for arrayIndex in aname.FIRST..aname.LAST loop
WF_CACHE.GetItemAttrValue(itemtype, itemKey, aname(arrayIndex), status,
wiavIND);
if (status <> WF_CACHE.task_SUCCESS) then
null; --The attribute is not in cache to be set. We will proceed to
--try to set the remainder and then raise a no_data_found after
--we complete
else
WF_CACHE.ItemAttrValues(wiavIND).TEXT_VALUE := avalue(arrayIndex);
success_cnt := success_cnt + 1;
end if;
end loop;
else
forall arrayIndex in aname.FIRST..aname.LAST
update WF_ITEM_ATTRIBUTE_VALUES set
TEXT_VALUE = avalue(arrayIndex)
where ITEM_TYPE = itemtype
and ITEM_KEY = itemkey
and NAME = aname(arrayIndex);
success_cnt := SQL%ROWCOUNT;
if (success_cnt <> aname.COUNT) then
raise no_data_found;
end if;
end if;
exception
when no_data_found then
Wf_Core.Context('Wf_Engine', 'SetItemAttrTextArray', itemtype, itemkey);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('TOTAL', to_char(aname.COUNT));
Wf_Core.Token('SUCCESS', to_char(success_cnt));
Wf_Core.Raise('WFENG_ITEM_ATTR_ARRAY');
when others then
Wf_Core.Context('Wf_Engine', 'SetItemAttrTextArray', itemtype, itemkey);
raise;
end SetItemAttrTextArray;
--
-- SetItemAttrNumberArray (PUBLIC)
-- Set the value of an array of number item attribute.
-- Attribute must be a NUMBER-type attribute.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- aname - Array of Names
-- avalue - Array of new value for attribute
--
procedure SetItemAttrNumberArray(
itemtype in varchar2,
itemkey in varchar2,
aname in Wf_Engine.NameTabTyp,
avalue in Wf_Engine.NumTabTyp)
is
arrayIndex pls_integer;
status pls_integer;
wiavIND number;
success_cnt number;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
elsif (aname.COUNT = 0 or avalue.COUNT = 0) then
-- Do not do anything if index table is empty.
return;
elsif (aname.LAST <> avalue.LAST or aname.COUNT <> avalue.COUNT) then
-- Raise an error if the two index tables do not end at the same index
-- or do not have the same number of elements.
Wf_Core.Raise('WFENG_ITEM_ATTR_ARRAY_MISMATCH');
end if;
-- Set the number value.
if (itemkey = wf_engine.eng_synch) then
-- Use WF_CACHE in synch mode
success_cnt := 0;
for arrayIndex in aname.FIRST..aname.LAST loop
WF_CACHE.GetItemAttrValue(itemtype, itemKey, aname(arrayIndex), status,
wiavIND);
if (status <> WF_CACHE.task_SUCCESS) then
null; --The attribute is not in cache to be set. We will proceed to
--try to set the remainder and then raise a no_data_found after
--we complete.
else
WF_CACHE.ItemAttrValues(wiavIND).NUMBER_VALUE := avalue(arrayIndex);
success_cnt := success_cnt + 1;
end if;
end loop;
else
forall arrayIndex in aname.FIRST..aname.LAST
update WF_ITEM_ATTRIBUTE_VALUES set
NUMBER_VALUE = avalue(arrayIndex)
where ITEM_TYPE = itemtype
and ITEM_KEY = itemkey
and NAME = aname(arrayIndex);
success_cnt := SQL%ROWCOUNT;
if (success_cnt <> aname.COUNT) then
raise no_data_found;
end if;
end if;
exception
when no_data_found then
Wf_Core.Context('Wf_Engine', 'SetItemAttrNumberArray', itemtype, itemkey);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('TOTAL', to_char(aname.COUNT));
Wf_Core.Token('SUCCESS', to_char(success_cnt));
Wf_Core.Raise('WFENG_ITEM_ATTR_ARRAY');
when others then
Wf_Core.Context('Wf_Engine', 'SetItemAttrNumberArray', itemtype, itemkey);
raise;
end SetItemAttrNumberArray;
--
-- SetItemAttrDateArray (PUBLIC)
-- Set the value of an array of date item attribute.
-- Attribute must be a DATE-type attribute.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- aname - Array of Name
-- avalue - Array of new value for attribute
--
procedure SetItemAttrDateArray(
itemtype in varchar2,
itemkey in varchar2,
aname in Wf_Engine.NameTabTyp,
avalue in Wf_Engine.DateTabTyp)
is
status pls_integer;
arrayIndex pls_integer;
wiavIND number;
success_cnt number;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
elsif (aname.COUNT = 0 or avalue.COUNT = 0) then
-- Do not do anything if index table is empty.
return;
elsif (aname.LAST <> avalue.LAST or aname.COUNT <> avalue.COUNT) then
-- Raise an error if the two index tables do not end at the same index
-- or do not have the same number of elements.
Wf_Core.Raise('WFENG_ITEM_ATTR_ARRAY_MISMATCH');
end if;
success_cnt := 0;
-- Set the date value.
if (itemkey = wf_engine.eng_synch) then
-- Use WF_CACHE in synch mode
for arrayIndex in aname.FIRST..aname.LAST loop
WF_CACHE.GetItemAttrValue(itemtype, itemKey, aname(arrayIndex), status,
wiavIND);
if (status <> WF_CACHE.task_SUCCESS) then
null; --The attribute is not in cache to be set. We will proceed to
--try to set the remainder and then raise a no_data_found after
--we complete.
else
WF_CACHE.ItemAttrValues(wiavIND).DATE_VALUE := avalue(arrayIndex);
success_cnt := success_cnt + 1;
end if;
end loop;
else
forall arrayIndex in aname.FIRST..aname.LAST
update WF_ITEM_ATTRIBUTE_VALUES set
DATE_VALUE = avalue(arrayIndex)
where ITEM_TYPE = itemtype
and ITEM_KEY = itemkey
and NAME = aname(arrayIndex);
success_cnt := SQL%ROWCOUNT;
if (success_cnt <> aname.COUNT) then
raise no_data_found;
end if;
end if;
exception
when no_data_found then
Wf_Core.Context('Wf_Engine', 'SetItemAttrDateArray', itemtype, itemkey);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('TOTAL', to_char(aname.COUNT));
Wf_Core.Token('SUCCESS', to_char(success_cnt));
Wf_Core.Raise('WFENG_ITEM_ATTR_ARRAY');
when others then
Wf_Core.Context('Wf_Engine', 'SetItemAttrDateArray', itemtype, itemkey);
raise;
end SetItemAttrDateArray;
--
-- GetItemAttrInfo (PUBLIC)
-- Get type information about a item attribute.
-- IN:
-- itemtype - Item type
-- aname - Attribute name
-- OUT:
-- atype - Attribute type
-- subtype - 'SEND' or 'RESPOND'
-- format - Attribute format
--
procedure GetItemAttrInfo(itemtype in varchar2,
aname in varchar2,
atype out NOCOPY varchar2,
subtype out NOCOPY varchar2,
format out NOCOPY varchar2)
is
wiaIND NUMBER;
status PLS_INTEGER;
begin
-- Check Arguments
if ((itemtype is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
WF_CACHE.GetItemAttribute(itemtype, aname, status, wiaIND);
if (status <> WF_CACHE.task_SUCCESS) then
select WIA.ITEM_TYPE, WIA.NAME, WIA.TYPE, WIA.SUBTYPE, WIA.FORMAT,
WIA.TEXT_DEFAULT, WIA.NUMBER_DEFAULT, WIA.DATE_DEFAULT
into WF_CACHE.ItemAttributes(wiaIND)
from WF_ITEM_ATTRIBUTES WIA
where WIA.ITEM_TYPE = itemtype
and WIA.NAME = aname;
end if;
atype := WF_CACHE.ItemAttributes(wiaIND).TYPE;
subtype := WF_CACHE.ItemAttributes(wiaIND).SUBTYPE;
format := WF_CACHE.ItemAttributes(wiaIND).FORMAT;
exception
when no_data_found then
Wf_Core.Context('Wf_Engine', 'GetItemAttrInfo', itemtype, aname);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', NULL);
Wf_Core.Token('ATTRIBUTE', aname);
Wf_Core.Raise('WFENG_ITEM_ATTR');
when others then
Wf_Core.Context('Wf_Engine', 'GetItemAttrInfo', itemtype, aname);
raise;
end GetItemAttrInfo;
--
-- GetItemAttrText (PUBLIC)
-- Get the value of a text item attribute.
-- If the attribute is a NUMBER or DATE type, then translate the
-- number/date value to a text-string representation using attrbute format.
-- For all other types, get the value directly.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- aname - Attribute Name
-- RETURNS:
-- Attribute value
--
function GetItemAttrText(itemtype in varchar2,
itemkey in varchar2,
aname in varchar2,
ignore_notfound in boolean)
return varchar2 is
lvalue varchar2(4000);
nvalue number;
dvalue date;
i pls_integer;
wiaIND NUMBER;
wiavIND NUMBER;
status PLS_INTEGER;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Get type and format of attr.
-- This is used for translating number/date strings.
WF_CACHE.GetItemAttribute(itemtype, aname, status, wiaIND);
if (status <> WF_CACHE.task_SUCCESS) then
begin
select WIA.ITEM_TYPE, WIA.NAME, WIA.TYPE, WIA.SUBTYPE, WIA.FORMAT,
WIA.TEXT_DEFAULT, WIA.NUMBER_DEFAULT, WIA.DATE_DEFAULT
into WF_CACHE.ItemAttributes(wiaIND)
from WF_ITEM_ATTRIBUTES WIA
where WIA.ITEM_TYPE = itemtype
and WIA.NAME = aname;
exception
when no_data_found then
-- This is an unvalidated runtime attr.
-- Treat it as a varchar2.
WF_CACHE.ItemAttributes(wiaIND).ITEM_TYPE := itemtype;
WF_CACHE.ItemAttributes(wiaIND).NAME := aname;
WF_CACHE.ItemAttributes(wiaIND).TYPE := 'VARCHAR2';
WF_CACHE.ItemAttributes(wiaIND).SUBTYPE := '';
WF_CACHE.ItemAttributes(wiaIND).FORMAT := '';
end;
end if;
-- Select value from appropriate type column.
if (WF_CACHE.ItemAttributes(wiaIND).TYPE = 'NUMBER') then
nvalue := Wf_Engine.GetItemAttrNumber(itemtype, itemkey, aname);
if (WF_CACHE.ItemAttributes(wiaIND).FORMAT is null) then
lvalue := to_char(nvalue);
else
lvalue := to_char(nvalue, WF_CACHE.ItemAttributes(wiaIND).FORMAT);
end if;
elsif (WF_CACHE.ItemAttributes(wiaIND).TYPE = 'DATE') then
dvalue := Wf_Engine.GetItemAttrDate(itemtype, itemkey, aname);
if (WF_CACHE.ItemAttributes(wiaIND).FORMAT is null) then
lvalue := to_char(dvalue);
else
lvalue := to_char(dvalue, WF_CACHE.ItemAttributes(wiaIND).FORMAT);
end if;
else
-- VARCHAR2, LOOKUP, FORM, URL, DOCUMENT.
-- Get the text value directly with no translation.
if (itemkey = wf_engine.eng_synch) then
-- Use WF_CACHE in synch mode
WF_CACHE.GetItemAttrValue(itemtype, itemKey, aname, status, wiavIND);
if (status <> WF_CACHE.task_SUCCESS) then
raise no_data_found;
else
return(WF_CACHE.ItemAttrValues(wiavIND).TEXT_VALUE);
end if;
else
select TEXT_VALUE
into lvalue
from WF_ITEM_ATTRIBUTE_VALUES
where ITEM_TYPE = itemtype
and ITEM_KEY = itemkey
and NAME = aname;
end if;
end if;
return(lvalue);
exception
when no_data_found then
if (ignore_notfound) then
return(null);
else
Wf_Core.Context('Wf_Engine', 'GetItemAttrText', itemtype, itemkey,
aname);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ATTRIBUTE', aname);
Wf_Core.Raise('WFENG_ITEM_ATTR');
end if;
when others then
Wf_Core.Context('Wf_Engine', 'GetItemAttrText', itemtype, itemkey,
aname);
raise;
end GetItemAttrText;
--
-- GetItemAttrNumber (PUBLIC)
-- Get the value of a number item attribute.
-- Attribute must be a NUMBER-type attribute.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- aname - Attribute Name
-- RETURNS:
-- Attribute value
--
function GetItemAttrNumber(itemtype in varchar2,
itemkey in varchar2,
aname in varchar2,
ignore_notfound in boolean)
return number is
wiavIND number;
status pls_integer;
lvalue number;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
if (itemkey = wf_engine.eng_synch) then
-- Use WF_CACHE in synch mode
WF_CACHE.GetItemAttrValue(itemtype, itemKey, aname, status, wiavIND);
if (status <> WF_CACHE.task_SUCCESS) then
raise no_data_found;
else
return(WF_CACHE.ItemAttrValues(wiavIND).NUMBER_VALUE);
end if;
else
select NUMBER_VALUE
into lvalue
from WF_ITEM_ATTRIBUTE_VALUES
where ITEM_TYPE = itemtype
and ITEM_KEY = itemkey
and NAME = aname;
end if;
return(lvalue);
exception
when no_data_found then
if (ignore_notfound) then
return(null);
else
Wf_Core.Context('Wf_Engine', 'GetItemAttrNumber', itemtype, itemkey,
aname);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ATTRIBUTE', aname);
Wf_Core.Raise('WFENG_ITEM_ATTR');
end if;
when others then
Wf_Core.Context('Wf_Engine', 'GetItemAttrNumber', itemtype, itemkey,
aname);
raise;
end GetItemAttrNumber;
--
-- GetItemAttrDate (PUBLIC)
-- Get the value of a date item attribute.
-- Attribute must be a DATE-type attribute.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- aname - Attribute Name
-- RETURNS:
-- Attribute value
--
function GetItemAttrDate (itemtype in varchar2,
itemkey in varchar2,
aname in varchar2,
ignore_notfound in boolean)
return date is
lvalue date;
wiavIND number;
status pls_integer;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
if (itemkey = wf_engine.eng_synch) then
-- Use WF_CACHE in synch mode
WF_CACHE.GetItemAttrValue(itemtype, itemKey, aname, status, wiavIND);
if (status <> WF_CACHE.task_SUCCESS) then
raise no_data_found;
else
return(WF_CACHE.ItemAttrValues(wiavIND).DATE_VALUE);
end if;
else
select DATE_VALUE
into lvalue
from WF_ITEM_ATTRIBUTE_VALUES
where ITEM_TYPE = itemtype
and ITEM_KEY = itemkey
and NAME = aname;
end if;
return(lvalue);
exception
when no_data_found then
if (ignore_notfound) then
return(null);
else
Wf_Core.Context('Wf_Engine', 'GetItemAttrDate', itemtype, itemkey,
aname);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ATTRIBUTE', aname);
Wf_Core.Raise('WFENG_ITEM_ATTR');
end if;
when others then
Wf_Core.Context('Wf_Engine', 'GetItemAttrDate', itemtype, itemkey,
aname);
raise;
end GetItemAttrDate;
--
-- GetItemAttrDocument (PUBLIC)
-- Get the value of a document item attribute.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- aname - Attribute Name
-- RETURNS:
-- documentid - Document Identifier - full concatenated document attribute
-- strings:
-- nodeid:libraryid:documentid:version:document_name
--
function GetItemAttrDocument(itemtype in varchar2,
itemkey in varchar2,
aname in varchar2,
ignore_notfound in boolean)
return varchar2
is
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
return(Wf_Engine.GetItemAttrText(itemtype, itemkey, aname, ignore_notfound));
exception
when others then
Wf_Core.Context('Wf_Engine', 'GetItemAttrDocument', itemtype, itemkey,
aname);
raise;
end GetItemAttrDocument;
--
-- GetItemAttrClob (PUBLIC)
-- Get display contents of item attribute as a clob
-- NOTE
-- Returns expanded content of attribute.
-- For DOCUMENT-type attributes, this will be the actual document
-- generated. For all other types, this will be the displayed
-- value of the attribute.
-- Use GetItemAttrText to retrieve internal key.
-- IN
-- itemtype - item type
-- itemkey - item key
-- aname - item attribute name
-- RETURNS
-- Expanded content of item attribute as a clob
--
function GetItemAttrClob(
itemtype in varchar2,
itemkey in varchar2,
aname in varchar2)
return clob
is
tempclob clob;
value varchar2(32000);
wiaIND NUMBER;
status PLS_INTEGER;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- ### Needs to be integrated with document support in wf_notifications!
-- Get attribute type info
WF_CACHE.GetItemAttribute(itemtype, aname, status, wiaIND);
if (status <> WF_CACHE.task_SUCCESS) then
begin
select WIA.ITEM_TYPE, WIA.NAME, WIA.TYPE, WIA.SUBTYPE, WIA.FORMAT,
WIA.TEXT_DEFAULT, WIA.NUMBER_DEFAULT, WIA.DATE_DEFAULT
into WF_CACHE.ItemAttributes(wiaIND)
from WF_ITEM_ATTRIBUTES WIA
where WIA.ITEM_TYPE = GetItemAttrClob.itemtype
and WIA.NAME = GetItemAttrClob.aname;
exception
when no_data_found then
-- This is an unvalidated runtime attr.
-- Treat it as a varchar2.
WF_CACHE.ItemAttributes(wiaIND).ITEM_TYPE := itemtype;
WF_CACHE.ItemAttributes(wiaIND).NAME := aname;
WF_CACHE.ItemAttributes(wiaIND).TYPE := 'VARCHAR2';
WF_CACHE.ItemAttributes(wiaIND).SUBTYPE := '';
WF_CACHE.ItemAttributes(wiaIND).FORMAT := '';
end;
end if;
-- Build clob with contents based on attr type
if (WF_CACHE.ItemAttributes(wiaIND).TYPE = '###NOTDONE') then
-- Parse out document subtypes
null;
else
-- All others just use text value
value := WF_Engine.GetItemAttrText(itemtype, itemkey, aname);
end if;
-- Write value to fake clob and return
if (value is null) then
-- Dbms_lob raises error if value is null...
return(null);
else
dbms_lob.createtemporary(tempclob, TRUE, dbms_lob.session);
dbms_lob.write(tempclob, lengthb(value), 1, value);
return(tempclob);
end if;
exception
when others then
Wf_Core.Context('Wf_Engine', 'GetItemAttrClob', itemtype,
itemkey, aname);
raise;
end GetItemAttrClob;
--
-- GetItemAttrEvent
-- Get event-type item attribute
-- IN
-- itemtype - process item type
-- itemkey - process item key
-- name - attribute name
-- RETURNS
-- Attribute value
--
function GetItemAttrEvent(
itemtype in varchar2,
itemkey in varchar2,
name in varchar2)
return wf_event_t
is
lvalue wf_event_t;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(name is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('NAME', nvl(name, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Not allowed in synch mode
if (itemkey = wf_engine.eng_synch) then
wf_core.token('OPERATION', 'Wf_Engine.GetItemAttrEvent');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
select EVENT_VALUE
into lvalue
from WF_ITEM_ATTRIBUTE_VALUES
where ITEM_TYPE = GetItemAttrEvent.itemtype
and ITEM_KEY = GetItemAttrEvent.itemkey
and NAME = GetItemAttrEvent.name;
--Initialization done only if event_value is null
if lvalue is null then
Wf_Event_T.Initialize(lvalue);
end if;
return(lvalue);
exception
when no_data_found then
Wf_Core.Context('Wf_Engine', 'GetItemAttrEvent', itemtype, itemkey,
name);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ATTRIBUTE', name);
Wf_Core.Raise('WFENG_ITEM_ATTR');
when others then
Wf_Core.Context('Wf_Engine', 'GetItemAttrEvent', itemtype,
itemkey, name);
raise;
end GetItemAttrEvent;
--
-- GetActivityAttrInfo (PUBLIC)
-- Get type information about an activity attribute.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- actid - Process activity id
-- aname - Attribute name
-- OUT:
-- atype - Attribute type
-- subtype - 'SEND' or 'RESPOND'
-- format - Attribute format
--
procedure GetActivityAttrInfo(itemtype in varchar2,
itemkey in varchar2,
actid in number,
aname in varchar2,
atype out NOCOPY varchar2,
subtype out NOCOPY varchar2,
format out NOCOPY varchar2)
is
actdate date;
waIND NUMBER;
waaIND NUMBER;
status PLS_INTEGER;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(actid is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ACTID', nvl(actid, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
actdate := Wf_Item.Active_Date(itemtype, itemkey);
WF_CACHE.GetActivityAttr( itemtype, aname, actid, actdate, status, waIND,
waaIND );
if (status <> WF_CACHE.task_SUCCESS) then
waIND := 0; --If the Get failed, we presume we did not get proper
waaIND := 0; --hash values for the indexes. So we default to 0.
select WA.ITEM_TYPE, WA.NAME, WA.VERSION, WA.TYPE, WA.RERUN,
WA.EXPAND_ROLE, WA.COST, WA.ERROR_ITEM_TYPE, WA.ERROR_PROCESS,
WA.FUNCTION, WA.FUNCTION_TYPE,
WA.EVENT_NAME, WA.MESSAGE, WA.BEGIN_DATE,
WA.END_DATE, WA.DIRECTION, WAA.ACTIVITY_ITEM_TYPE,
WAA.ACTIVITY_NAME, WAA.ACTIVITY_VERSION, WAA.NAME, WAA.TYPE,
WAA.SUBTYPE, WAA.FORMAT, WPA.PROCESS_ITEM_TYPE, WPA.PROCESS_NAME,
WPA.PROCESS_VERSION, WPA.ACTIVITY_ITEM_TYPE, WPA.ACTIVITY_NAME,
WPA.INSTANCE_ID, WPA.INSTANCE_LABEL, WPA.PERFORM_ROLE,
WPA.PERFORM_ROLE_TYPE, WPA.START_END, WPA.DEFAULT_RESULT
into WF_CACHE.Activities(waIND).ITEM_TYPE,
WF_CACHE.Activities(waIND).NAME,
WF_CACHE.Activities(waIND).VERSION,
WF_CACHE.Activities(waIND).TYPE,
WF_CACHE.Activities(waIND).RERUN,
WF_CACHE.Activities(waIND).EXPAND_ROLE,
WF_CACHE.Activities(waIND).COST,
WF_CACHE.Activities(waIND).ERROR_ITEM_TYPE,
WF_CACHE.Activities(waIND).ERROR_PROCESS,
WF_CACHE.Activities(waIND).FUNCTION,
WF_CACHE.Activities(waIND).FUNCTION_TYPE,
WF_CACHE.Activities(waIND).EVENT_NAME,
WF_CACHE.Activities(waIND).MESSAGE,
WF_CACHE.Activities(waIND).BEGIN_DATE,
WF_CACHE.Activities(waIND).END_DATE,
WF_CACHE.Activities(waIND).DIRECTION,
WF_CACHE.ActivityAttributes(waaIND).ACTIVITY_ITEM_TYPE,
WF_CACHE.ActivityAttributes(waaIND).ACTIVITY_NAME,
WF_CACHE.ActivityAttributes(waaIND).ACTIVITY_VERSION,
WF_CACHE.ActivityAttributes(waaIND).NAME,
WF_CACHE.ActivityAttributes(waaIND).TYPE,
WF_CACHE.ActivityAttributes(waaIND).SUBTYPE,
WF_CACHE.ActivityAttributes(waaIND).FORMAT,
WF_CACHE.ProcessActivities(actid).PROCESS_ITEM_TYPE,
WF_CACHE.ProcessActivities(actid).PROCESS_NAME,
WF_CACHE.ProcessActivities(actid).PROCESS_VERSION,
WF_CACHE.ProcessActivities(actid).ACTIVITY_ITEM_TYPE,
WF_CACHE.ProcessActivities(actid).ACTIVITY_NAME,
WF_CACHE.ProcessActivities(actid).INSTANCE_ID,
WF_CACHE.ProcessActivities(actid).INSTANCE_LABEL,
WF_CACHE.ProcessActivities(actid).PERFORM_ROLE,
WF_CACHE.ProcessActivities(actid).PERFORM_ROLE_TYPE,
WF_CACHE.ProcessActivities(actid).START_END,
WF_CACHE.ProcessActivities(actid).DEFAULT_RESULT
from WF_ACTIVITY_ATTRIBUTES WAA, WF_PROCESS_ACTIVITIES WPA,
WF_ACTIVITIES WA
where WPA.INSTANCE_ID = actid
and WA.ITEM_TYPE = WPA.ACTIVITY_ITEM_TYPE
and WA.NAME = WPA.ACTIVITY_NAME
and actdate >= WA.BEGIN_DATE
and actdate < NVL(WA.END_DATE, actdate+1)
and WAA.ACTIVITY_ITEM_TYPE = WA.ITEM_TYPE
and WAA.ACTIVITY_NAME = WA.NAME
and WAA.ACTIVITY_VERSION = WA.VERSION
and WAA.NAME = aname;
--Get the proper hash key and copy the temporary records into the
--proper locations.
waIND := WF_CACHE.HashKey(itemType ||
WF_CACHE.ProcessActivities(actid).ACTIVITY_NAME);
WF_CACHE.Activities(waIND) := WF_CACHE.Activities(0);
waaIND := WF_CACHE.HashKey(itemType || aname ||
WF_CACHE.ProcessActivities(actid).ACTIVITY_NAME);
WF_CACHE.ActivityAttributes(waaIND) :=
WF_CACHE.ActivityAttributes(0);
end if;
atype := WF_CACHE.ActivityAttributes(waaIND).TYPE;
subtype := WF_CACHE.ActivityAttributes(waaIND).SUBTYPE;
format := WF_CACHE.ActivityAttributes(waaIND).FORMAT;
exception
when no_data_found then
Wf_Core.Context('Wf_Engine', 'GetActivityAttrInfo', itemtype, itemkey,
to_char(actid), aname);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ACTIVITY', to_char(actid));
Wf_Core.Token('ATTRIBUTE', aname);
Wf_Core.Raise('WFENG_ACTIVITY_ATTR');
when others then
Wf_Core.Context('Wf_Engine', 'GetActivityAttrInfo', itemtype, itemkey,
to_char(actid), aname);
raise;
end GetActivityAttrInfo;
--
-- GetActivityAttrText (PUBLIC)
-- Get the value of a text item attribute.
-- If the attribute is a NUMBER or DATE type, then translate the
-- number/date value to a text-string representation using attrbute format.
-- For all other types, get the value directly.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- actid - Process activity id
-- aname - Attribute Name
-- RETURNS:
-- Attribute value
--
function GetActivityAttrText(itemtype in varchar2,
itemkey in varchar2,
actid in number,
aname in varchar2,
ignore_notfound in boolean)
return varchar2 is
actdate date;
status PLS_INTEGER;
waavIND NUMBER;
waaIND NUMBER;
waIND NUMBER;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(actid is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ACTID', nvl(actid, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- First check value_type flag for possible item_attribute ref.
-- Checking to see if the Attribute Value is in cache.
WF_CACHE.GetActivityAttrValue(actid, aname, status, waavIND);
if (status <> WF_CACHE.task_SUCCESS) then
open curs_activityattr (actid, aname);
fetch curs_activityattr into WF_CACHE.ActivityAttrValues(waavIND);
close curs_activityattr;
end if;
-- If it is a reference, return value of item_attr instead of
-- contents of WAAV.
if (WF_CACHE.ActivityAttrValues(waavIND).VALUE_TYPE = 'ITEMATTR') then
if (WF_CACHE.ActivityAttrValues(waavIND).TEXT_VALUE is null) then
return(null); -- Null itemattr means null value, not an error
end if;
return(GetItemAttrText(itemtype, itemkey,
substrb(WF_CACHE.ActivityAttrValues(waavIND).TEXT_VALUE, 1, 30)));
end if;
-- This is NOT an itemattr reference, get value directly from WAAV.
-- Get type and format of attr for translating number/date strings.
begin
actdate := Wf_Item.Active_Date(itemtype, itemkey);
WF_CACHE.GetActivityAttr( itemtype, aname, actid, actdate, status, waIND,
waaIND );
if (status <> WF_CACHE.task_SUCCESS) then
waIND := 0;
waaIND := 0;
select WA.ITEM_TYPE, WA.NAME, WA.VERSION, WA.TYPE, WA.RERUN,
WA.EXPAND_ROLE, WA.COST, WA.ERROR_ITEM_TYPE, WA.ERROR_PROCESS,
WA.FUNCTION, WA.FUNCTION_TYPE,
WA.EVENT_NAME, WA.MESSAGE, WA.BEGIN_DATE,
WA.END_DATE, WA.DIRECTION, WAA.ACTIVITY_ITEM_TYPE,
WAA.ACTIVITY_NAME, WAA.ACTIVITY_VERSION, WAA.NAME, WAA.TYPE,
WAA.SUBTYPE, WAA.FORMAT, WPA.PROCESS_ITEM_TYPE, WPA.PROCESS_NAME,
WPA.PROCESS_VERSION, WPA.ACTIVITY_ITEM_TYPE, WPA.ACTIVITY_NAME,
WPA.INSTANCE_ID, WPA.INSTANCE_LABEL, WPA.PERFORM_ROLE,
WPA.PERFORM_ROLE_TYPE, WPA.START_END, WPA.DEFAULT_RESULT
into WF_CACHE.Activities(waIND).ITEM_TYPE,
WF_CACHE.Activities(waIND).NAME,
WF_CACHE.Activities(waIND).VERSION,
WF_CACHE.Activities(waIND).TYPE,
WF_CACHE.Activities(waIND).RERUN,
WF_CACHE.Activities(waIND).EXPAND_ROLE,
WF_CACHE.Activities(waIND).COST,
WF_CACHE.Activities(waIND).ERROR_ITEM_TYPE,
WF_CACHE.Activities(waIND).ERROR_PROCESS,
WF_CACHE.Activities(waIND).FUNCTION,
WF_CACHE.Activities(waIND).FUNCTION_TYPE,
WF_CACHE.Activities(waIND).EVENT_NAME,
WF_CACHE.Activities(waIND).MESSAGE,
WF_CACHE.Activities(waIND).BEGIN_DATE,
WF_CACHE.Activities(waIND).END_DATE,
WF_CACHE.Activities(waIND).DIRECTION,
WF_CACHE.ActivityAttributes(waaIND).ACTIVITY_ITEM_TYPE,
WF_CACHE.ActivityAttributes(waaIND).ACTIVITY_NAME,
WF_CACHE.ActivityAttributes(waaIND).ACTIVITY_VERSION,
WF_CACHE.ActivityAttributes(waaIND).NAME,
WF_CACHE.ActivityAttributes(waaIND).TYPE,
WF_CACHE.ActivityAttributes(waaIND).SUBTYPE,
WF_CACHE.ActivityAttributes(waaIND).FORMAT,
WF_CACHE.ProcessActivities(actid).PROCESS_ITEM_TYPE,
WF_CACHE.ProcessActivities(actid).PROCESS_NAME,
WF_CACHE.ProcessActivities(actid).PROCESS_VERSION,
WF_CACHE.ProcessActivities(actid).ACTIVITY_ITEM_TYPE,
WF_CACHE.ProcessActivities(actid).ACTIVITY_NAME,
WF_CACHE.ProcessActivities(actid).INSTANCE_ID,
WF_CACHE.ProcessActivities(actid).INSTANCE_LABEL,
WF_CACHE.ProcessActivities(actid).PERFORM_ROLE,
WF_CACHE.ProcessActivities(actid).PERFORM_ROLE_TYPE,
WF_CACHE.ProcessActivities(actid).START_END,
WF_CACHE.ProcessActivities(actid).DEFAULT_RESULT
from WF_ACTIVITY_ATTRIBUTES WAA, WF_PROCESS_ACTIVITIES WPA,
WF_ACTIVITIES WA
where WPA.INSTANCE_ID = actid
and WA.ITEM_TYPE = WPA.ACTIVITY_ITEM_TYPE
and WA.NAME = WPA.ACTIVITY_NAME
and actdate >= WA.BEGIN_DATE
and actdate < NVL(WA.END_DATE, actdate+1)
and WAA.ACTIVITY_ITEM_TYPE = WA.ITEM_TYPE
and WAA.ACTIVITY_NAME = WA.NAME
and WAA.ACTIVITY_VERSION = WA.VERSION
and WAA.NAME = aname;
--Get the proper hash key and copy the temporary records into the
--proper locations.
waIND := WF_CACHE.HashKey(itemType ||
WF_CACHE.ProcessActivities(actid).ACTIVITY_NAME);
WF_CACHE.Activities(waIND) := WF_CACHE.Activities(0);
waaIND := WF_CACHE.HashKey(itemType || aname ||
WF_CACHE.ProcessActivities(actid).ACTIVITY_NAME);
WF_CACHE.ActivityAttributes(waaIND) :=
WF_CACHE.ActivityAttributes(0);
end if;
exception
when no_data_found then
-- This is an unvalidated runtime attr.
-- Treat it as a varchar2.
-- We know that the activity and process activity should be retrievable.
-- We will build a unvalidated runtime attr in cache. First we need to
-- validate that we have the correct activity and process activity cached
WF_CACHE.GetProcessActivityInfo(actid, actdate, status, waIND);
if (status <> WF_CACHE.task_SUCCESS) then
waIND := 0;
select WA.ITEM_TYPE, WA.NAME, WA.VERSION, WA.TYPE, WA.RERUN,
WA.EXPAND_ROLE, WA.COST, WA.ERROR_ITEM_TYPE, WA.ERROR_PROCESS,
WA.FUNCTION, WA.FUNCTION_TYPE, WA.MESSAGE, WA.BEGIN_DATE,
WA.END_DATE, WA.DIRECTION, WPA.PROCESS_ITEM_TYPE,
WPA.PROCESS_NAME, WPA.PROCESS_VERSION, WPA.ACTIVITY_ITEM_TYPE,
WPA.ACTIVITY_NAME, WPA.INSTANCE_ID, WPA.INSTANCE_LABEL,
WPA.PERFORM_ROLE, WPA.PERFORM_ROLE_TYPE, WPA.START_END,
WPA.DEFAULT_RESULT
into WF_CACHE.Activities(waIND).ITEM_TYPE,
WF_CACHE.Activities(waIND).NAME,
WF_CACHE.Activities(waIND).VERSION,
WF_CACHE.Activities(waIND).TYPE,
WF_CACHE.Activities(waIND).RERUN,
WF_CACHE.Activities(waIND).EXPAND_ROLE,
WF_CACHE.Activities(waIND).COST,
WF_CACHE.Activities(waIND).ERROR_ITEM_TYPE,
WF_CACHE.Activities(waIND).ERROR_PROCESS,
WF_CACHE.Activities(waIND).FUNCTION,
WF_CACHE.Activities(waIND).FUNCTION_TYPE,
WF_CACHE.Activities(waIND).MESSAGE,
WF_CACHE.Activities(waIND).BEGIN_DATE,
WF_CACHE.Activities(waIND).END_DATE,
WF_CACHE.Activities(waIND).DIRECTION,
WF_CACHE.ProcessActivities(actid).PROCESS_ITEM_TYPE,
WF_CACHE.ProcessActivities(actid).PROCESS_NAME,
WF_CACHE.ProcessActivities(actid).PROCESS_VERSION,
WF_CACHE.ProcessActivities(actid).ACTIVITY_ITEM_TYPE,
WF_CACHE.ProcessActivities(actid).ACTIVITY_NAME,
WF_CACHE.ProcessActivities(actid).INSTANCE_ID,
WF_CACHE.ProcessActivities(actid).INSTANCE_LABEL,
WF_CACHE.ProcessActivities(actid).PERFORM_ROLE,
WF_CACHE.ProcessActivities(actid).PERFORM_ROLE_TYPE,
WF_CACHE.ProcessActivities(actid).START_END,
WF_CACHE.ProcessActivities(actid).DEFAULT_RESULT
from WF_PROCESS_ACTIVITIES WPA, WF_ACTIVITIES WA
where WPA.INSTANCE_ID = actid
and WA.ITEM_TYPE = WPA.ACTIVITY_ITEM_TYPE
and WA.NAME = WPA.ACTIVITY_NAME
and actdate >= WA.BEGIN_DATE
and actdate < NVL(WA.END_DATE, actdate+1);
end if;
waIND := WF_CACHE.HashKey(itemType ||
WF_CACHE.ProcessActivities(actid).ACTIVITY_NAME);
waaIND := WF_CACHE.HashKey(itemType || aname ||
WF_CACHE.ProcessActivities(actid).ACTIVITY_NAME);
WF_CACHE.Activities(waIND) := WF_CACHE.Activities(0);
WF_CACHE.ActivityAttributes(waaIND).ACTIVITY_ITEM_TYPE :=
WF_CACHE.Activities(waIND).ITEM_TYPE;
WF_CACHE.ActivityAttributes(waaIND).ACTIVITY_NAME :=
WF_CACHE.Activities(waIND).NAME;
WF_CACHE.ActivityAttributes(waaIND).ACTIVITY_VERSION :=
WF_CACHE.Activities(waIND).VERSION;
WF_CACHE.ActivityAttributes(waaIND).NAME := aname;
WF_CACHE.ActivityAttributes(waaIND).TYPE := 'VARCHAR2';
WF_CACHE.ActivityAttributes(waaIND).SUBTYPE := '';
WF_CACHE.ActivityAttributes(waaIND).FORMAT := '';
end;
-- Format return value as needed for text/number/date type.
if (WF_CACHE.ActivityAttributes(waaIND).TYPE = 'NUMBER') then
if (WF_CACHE.ActivityAttributes(waaIND).FORMAT <> '') then
return(to_char(WF_CACHE.ActivityAttrValues(waavIND).NUMBER_VALUE,
WF_CACHE.ActivityAttributes(waaIND).FORMAT));
else
return(to_char(WF_CACHE.ActivityAttrValues(waavIND).NUMBER_VALUE));
end if;
elsif (WF_CACHE.ActivityAttributes(waaIND).TYPE = 'DATE') then
if (WF_CACHE.ActivityAttributes(waaIND).FORMAT <> '') then
return(to_char(WF_CACHE.ActivityAttrValues(waavIND).DATE_VALUE,
WF_CACHE.ActivityAttributes(waaIND).FORMAT));
else
return(to_char(WF_CACHE.ActivityAttrValues(waavIND).DATE_VALUE));
end if;
else
-- VARCHAR2, LOOKUP, FORM, URL, DOCUMENT.
-- Set the text value directly with no translation.
return(WF_CACHE.ActivityAttrValues(waavIND).TEXT_VALUE);
end if;
exception
when no_data_found then
--Check to ensure that cursor is not open
if (curs_activityattr%ISOPEN) then
CLOSE curs_activityattr;
end if;
if (ignore_notfound) then
return(null);
else
Wf_Core.Context('Wf_Engine', 'GetActivityAttrText', itemtype, itemkey,
to_char(actid), aname);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ACTIVITY', to_char(actid));
Wf_Core.Token('ATTRIBUTE', aname);
Wf_Core.Raise('WFENG_ACTIVITY_ATTR');
end if;
when others then
--Check to ensure that cursor is not open
if (curs_activityattr%ISOPEN) then
CLOSE curs_activityattr;
end if;
Wf_Core.Context('Wf_Engine', 'GetActivityAttrText', itemtype, itemkey,
to_char(actid), aname);
raise;
end GetActivityAttrText;
--
-- GetActivityAttrNumber (PUBLIC)
-- Get the value of a number item attribute.
-- Attribute must be a NUMBER-type attribute.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- actid - Process activity id
-- aname - Attribute Name
-- RETURNS:
-- Attribute value
--
function GetActivityAttrNumber(itemtype in varchar2,
itemkey in varchar2,
actid in number,
aname in varchar2,
ignore_notfound in boolean)
return number is
waavIND NUMBER;
status PLS_INTEGER;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(actid is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ACTID', nvl(actid, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
WF_CACHE.GetActivityAttrValue(actid, aname, status, waavIND);
if (status <> WF_CACHE.task_SUCCESS) then
open curs_activityattr (actid, aname);
fetch curs_activityattr into WF_CACHE.ActivityAttrValues(waavIND);
close curs_activityattr;
end if;
-- If it is a reference, replace lvalue with value of itemattr.
if (WF_CACHE.ActivityAttrValues(waavIND).VALUE_TYPE = 'ITEMATTR') then
return(GetItemAttrNumber(itemtype, itemkey,
substrb(WF_CACHE.ActivityAttrValues(waavIND).TEXT_VALUE, 1, 30)));
else
return(WF_CACHE.ActivityAttrValues(waavIND).NUMBER_VALUE);
end if;
exception
when no_data_found then
--Check to ensure that cursor is not open
if (curs_activityattr%ISOPEN) then
CLOSE curs_activityattr;
end if;
if (ignore_notfound) then
WF_CACHE.ActivityAttrValues(waavIND).PROCESS_ACTIVITY_ID := actid;
WF_CACHE.ActivityAttrValues(waavIND).NAME := aname;
WF_CACHE.ActivityAttrValues(waavIND).VALUE_TYPE := 'CONSTANT';
WF_CACHE.ActivityAttrValues(waavIND).TEXT_VALUE := '';
WF_CACHE.ActivityAttrValues(waavIND).NUMBER_VALUE := '';
WF_CACHE.ActivityAttrValues(waavIND).DATE_VALUE := to_date(NULL);
return null;
else
Wf_Core.Context('Wf_Engine', 'GetActivityAttrNumber', itemtype, itemkey,
to_char(actid), aname);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ACTIVITY', to_char(actid));
Wf_Core.Token('ATTRIBUTE', aname);
Wf_Core.Raise('WFENG_ACTIVITY_ATTR');
end if;
when others then
--Check to ensure that cursor is not open
if (curs_activityattr%ISOPEN) then
CLOSE curs_activityattr;
end if;
Wf_Core.Context('Wf_Engine', 'GetActivityAttrNumber', itemtype, itemkey,
to_char(actid), aname);
raise;
end GetActivityAttrNumber;
--
-- GetActivityAttrDate (PUBLIC)
-- Get the value of a date item attribute.
-- Attribute must be a DATE-type attribute.
-- IN:
-- itemtype - Item type
-- itemkey - Item key
-- actid - Process activity id
-- aname - Attribute Name
-- RETURNS:
-- Attribute value
--
function GetActivityAttrDate(itemtype in varchar2,
itemkey in varchar2,
actid in number,
aname in varchar2,
ignore_notfound in boolean)
return date is
waavIND NUMBER;
status PLS_INTEGER;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(actid is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ACTID', nvl(actid, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- First check value_type flag for possible item_attribute ref.
WF_CACHE.GetActivityAttrValue(actid, aname, status, waavIND);
if (status <> WF_CACHE.task_SUCCESS) then
open curs_activityattr (actid, aname);
fetch curs_activityattr into WF_CACHE.ActivityAttrValues(waavIND);
close curs_activityattr;
end if;
-- If it is a reference, get the item attribute and return it.
if (WF_CACHE.ActivityAttrValues(waavIND).VALUE_TYPE = 'ITEMATTR') then
return(GetItemAttrDate(itemtype, itemkey,
substrb(WF_CACHE.ActivityAttrValues(waavIND).TEXT_VALUE, 1, 30)));
else
return(WF_CACHE.ActivityAttrValues(waavIND).DATE_VALUE);
end if;
exception
when no_data_found then
--Check to ensure that cursor is not open
if (curs_activityattr%ISOPEN) then
CLOSE curs_activityattr;
end if;
if (ignore_notfound) then
WF_CACHE.ActivityAttrValues(waavIND).PROCESS_ACTIVITY_ID := actid;
WF_CACHE.ActivityAttrValues(waavIND).NAME := aname;
WF_CACHE.ActivityAttrValues(waavIND).VALUE_TYPE := 'CONSTANT';
WF_CACHE.ActivityAttrValues(waavIND).TEXT_VALUE := '';
WF_CACHE.ActivityAttrValues(waavIND).NUMBER_VALUE := '';
WF_CACHE.ActivityAttrValues(waavIND).DATE_VALUE := to_date(NULL);
return(null);
else
Wf_Core.Context('Wf_Engine', 'GetActivityAttrDate', itemtype, itemkey,
to_char(actid), aname);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ACTIVITY', to_char(actid));
Wf_Core.Token('ATTRIBUTE', aname);
Wf_Core.Raise('WFENG_ACTIVITY_ATTR');
end if;
when others then
--Check to ensure that cursor is not open
if (curs_activityattr%ISOPEN) then
CLOSE curs_activityattr;
end if;
Wf_Core.Context('Wf_Engine', 'GetActivityAttrDate', itemtype, itemkey,
to_char(actid), aname);
raise;
end GetActivityAttrDate;
--
-- GetActivityAttrClob (PUBLIC)
-- Get display contents of activity attribute as a clob
-- NOTE
-- Returns expanded content of attribute.
-- For DOCUMENT-type attributes, this will be the actual document
-- generated. For all other types, this will be the displayed
-- value of the attribute.
-- Use GetActivityAttrText to retrieve internal key.
-- IN
-- itemtype - item type
-- itemkey - item key
-- aname - activity attribute name
-- RETURNS
-- Expanded content of activity attribute as a clob
--
function GetActivityAttrClob(
itemtype in varchar2,
itemkey in varchar2,
actid in number,
aname in varchar2)
return clob
is
atype varchar2(8);
format varchar2(240);
value varchar2(32000);
actdate date;
tempclob clob;
waavIND NUMBER;
status PLS_INTEGER;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(actid is null) or
(aname is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ACTID', nvl(actid, 'NULL'));
Wf_Core.Token('ANAME', nvl(aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- First check value_type flag for possible item_attribute ref.
WF_CACHE.GetActivityAttrValue(actid, aname, status, waavIND);
if (status <> WF_CACHE.task_SUCCESS) then
open curs_activityattr (actid, aname);
fetch curs_activityattr into WF_CACHE.ActivityAttrValues(waavIND);
close curs_activityattr;
end if;
-- If it is a reference, return value of item_attr instead of
-- contents of WAAV.
if (WF_CACHE.ActivityAttrValues(waavIND).VALUE_TYPE = 'ITEMATTR') then
if (WF_CACHE.ActivityAttrValues(waavIND).TEXT_VALUE is null) then
return(null); -- Null itemattr means null value, not an error
else
return(GetItemAttrClob(itemtype, itemkey,
substrb(WF_CACHE.ActivityAttrValues(waavIND).TEXT_VALUE, 1, 30)));
end if;
end if;
-- Make fake clob to hold result
dbms_lob.createtemporary(tempclob, TRUE, dbms_lob.session);
-- Build clob with contents based on attr type
if (atype = '###NOTDONE') then
-- Parse out document subtypes
null;
else
-- All others just use text value
value := WF_Engine.GetActivityAttrText(itemtype, itemkey, actid, aname);
end if;
-- Write value to fake clob and return
dbms_lob.write(tempclob, lengthb(value), 1, value);
return(tempclob);
exception
when others then
--Check to ensure that cursor is not open
if (curs_activityattr%ISOPEN) then
CLOSE curs_activityattr;
end if;
Wf_Core.Context('Wf_Engine', 'GetActivityAttrClob', itemtype,
itemkey, to_char(actid), aname);
raise;
end GetActivityAttrClob;
--
-- GetActivityAttrEvent
-- Get event-type activity attribute
-- IN
-- itemtype - process item type
-- itemkey - process item key
-- actid - current activity id
-- name - attribute name
-- RETURNS
-- Attribute value
--
function GetActivityAttrEvent(
itemtype in varchar2,
itemkey in varchar2,
actid in number,
name in varchar2)
return wf_event_t
is
waavIND NUMBER;
status PLS_INTEGER;
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(actid is null) or
(name is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('ACTID', nvl(actid, 'NULL'));
Wf_Core.Token('NAME', nvl(name, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- First check value_type flag for possible item_attribute ref.
-- First check value_type flag for possible item_attribute ref.
WF_CACHE.GetActivityAttrValue(actid, name, status, waavIND);
if (status <> WF_CACHE.task_SUCCESS) then
open curs_activityattr (actid, GetActivityAttrEvent.name);
fetch curs_activityattr into WF_CACHE.ActivityAttrValues(waavIND);
close curs_activityattr;
end if;
-- If it is a reference, replace lvalue with value of itemattr.
if (WF_CACHE.ActivityAttrValues(waavIND).VALUE_TYPE = 'ITEMATTR') then
return(GetItemAttrEvent(itemtype, itemkey,
substrb(WF_CACHE.ActivityAttrValues(waavIND).TEXT_VALUE, 1, 30)));
else
-- Only itemattr-type activity event attrs are supported
return NULL;
end if;
exception
when no_data_found then
--Check to ensure that cursor is not open
if (curs_activityattr%ISOPEN) then
CLOSE curs_activityattr;
end if;
Wf_Core.Context('Wf_Engine', 'GetActivityAttrEvent', itemtype, itemkey,
to_char(actid), name);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ACTIVITY', to_char(actid));
Wf_Core.Token('ATTRIBUTE', name);
Wf_Core.Raise('WFENG_ACTIVITY_ATTR');
when others then
--Check to ensure that cursor is not open
if (curs_activityattr%ISOPEN) then
CLOSE curs_activityattr;
end if;
Wf_Core.Context('Wf_Engine', 'GetActivityAttrEvent', itemtype,
itemkey, to_char(actid), name);
raise;
end GetActivityAttrEvent;
--
-- Set_Item_Parent (PUBLIC)
-- *** OBSOLETE - Use SetItemParent instead ***
--
procedure Set_Item_Parent(itemtype in varchar2,
itemkey in varchar2,
parent_itemtype in varchar2,
parent_itemkey in varchar2,
parent_context in varchar2)
is
begin
-- Not allowed in synch mode
if (itemkey = wf_engine.eng_synch) then
wf_core.token('OPERATION', 'Wf_Engine.Set_Item_Parent');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
Wf_Item.Set_Item_Parent(itemtype, itemkey, parent_itemtype, parent_itemkey,
parent_context);
exception
when others then
Wf_Core.Context('Wf_Engine', 'Set_Item_Parent', itemtype, itemkey,
parent_itemtype, parent_itemkey, parent_context);
raise;
end Set_Item_Parent;
--
-- SetItemParent (PUBLIC)
-- Set the parent info of an item
-- IN
-- itemtype - Item type
-- itemkey - Item key
-- parent_itemtype - Itemtype of parent
-- parent_itemkey - Itemkey of parent
-- parent_context - Context info about parent
-- masterdetail - Signal if the two flows are coordinated.
--
procedure SetItemParent(itemtype in varchar2,
itemkey in varchar2,
parent_itemtype in varchar2,
parent_itemkey in varchar2,
parent_context in varchar2,
masterdetail in boolean)
is
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null) or
(parent_itemtype is null) or
(parent_itemkey is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Token('PARENT_ITEMTYPE', nvl(parent_itemtype, 'NULL'));
Wf_Core.Token('PARENT_ITEMKEY', nvl(parent_itemkey, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Not allowed in synch mode
if (itemkey = wf_engine.eng_synch) then
wf_core.token('OPERATION', 'Wf_Engine.SetItemParent');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
Wf_Item.Set_Item_Parent(itemtype, itemkey, parent_itemtype,
parent_itemkey, parent_context, masterdetail);
exception
when others then
Wf_Core.Context('Wf_Engine', 'SetItemParent', itemtype, itemkey,
parent_itemtype, parent_itemkey, parent_context);
raise;
end SetItemParent;
--
-- SetItemOwner (PUBLIC)
-- Set the owner of an item
-- IN
-- itemtype - Item type
-- itemkey - Item key
-- owner - Role designated as owner of the item
--
procedure SetItemOwner(
itemtype in varchar2,
itemkey in varchar2,
owner in varchar2)
is
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Not allowed in synch mode
if (itemkey = wf_engine.eng_synch) then
wf_core.token('OPERATION', 'Wf_Engine.SetItemOwner');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
Wf_Item.SetItemOwner(itemtype, itemkey, owner);
exception
when others then
Wf_Core.Context('Wf_Engine', 'SetItemOwner', itemtype, itemkey,
owner);
raise;
end SetItemOwner;
--
-- GetItemUserKey (PUBLIC)
-- Get the user key of an item
-- IN
-- itemtype - Item type
-- itemkey - Item key
-- RETURNS
-- User key of the item
--
function GetItemUserKey(
itemtype in varchar2,
itemkey in varchar2)
return varchar2
is
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
return(Wf_Item.GetItemUserKey(itemtype, itemkey));
exception
when others then
Wf_Core.Context('Wf_Engine', 'GetItemUserKey', itemtype, itemkey);
raise;
end GetItemUserKey;
--
-- SetItemUserKey (PUBLIC)
-- Set the user key of an item
-- IN
-- itemtype - Item type
-- itemkey - Item key
-- userkey - User key to be set
--
procedure SetItemUserKey(
itemtype in varchar2,
itemkey in varchar2,
userkey in varchar2)
is
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Not allowed in synch mode
if (itemkey = wf_engine.eng_synch) then
wf_core.token('OPERATION', 'Wf_Engine.SetItemUserKey');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
Wf_Item.SetItemUserKey(itemtype, itemkey, userkey);
exception
when others then
Wf_Core.Context('Wf_Engine', 'SetItemUserKey', itemtype, itemkey,
userkey);
raise;
end SetItemUserKey;
--
-- GetActivityLabel (PUBLIC)
-- Get activity instance label given id, in a format
-- suitable for passing to other wf_engine apis.
-- IN
-- actid - activity instance id
-- RETURNS
-- ||':'||
--
function GetActivityLabel(
actid in number)
return varchar2
is
status PLS_INTEGER;
begin
-- Check Arguments
if (actid is null) then
Wf_Core.Token('ACTID', nvl(actid, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
WF_CACHE.GetProcessActivity(actid, status);
if (status <> WF_CACHE.task_SUCCESS) then
select WPA.PROCESS_ITEM_TYPE, WPA.PROCESS_NAME, WPA.PROCESS_VERSION,
WPA.ACTIVITY_ITEM_TYPE, WPA.ACTIVITY_NAME, WPA.INSTANCE_ID,
WPA.INSTANCE_LABEL, WPA.PERFORM_ROLE, WPA.PERFORM_ROLE_TYPE,
WPA.START_END, WPA.DEFAULT_RESULT
into WF_CACHE.ProcessActivities(actid)
from WF_PROCESS_ACTIVITIES WPA
where WPA.INSTANCE_ID = GetActivityLabel.actid;
end if;
return(WF_CACHE.ProcessActivities(actid).PROCESS_NAME || ':' ||
WF_CACHE.ProcessActivities(actid).INSTANCE_LABEL);
exception
when no_data_found then
Wf_Core.Context('Wf_Engine', 'GetActivityLabel', to_char(actid));
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Raise('WFENG_ACTID');
when others then
Wf_Core.Context('Wf_Engine', 'GetActivityLabel', to_char(actid));
raise;
end GetActivityLabel;
-- Bug 2376033
-- Overloads the previous API with an additional event type parmeter
--
-- CB (PUBLIC)
-- This is the callback function used by the notification system to
-- get and set process attributes, and mark a process complete.
--
-- The command may be one of:
-- GET - Get the value of an attribute
-- SET - Set the value of an attribute
-- COMPLETE - Mark the activity as complete
-- ERROR - Mark the activity as error status
-- TESTCTX - Test current context via selector function
-- FORWARD - Execute notification function for FORWARD
-- TRANSFER - Execute notification function for TRANSFER
-- RESPOND - Execute notification function for RESPOND
--
-- The context is in the format ::.
--
-- The text_value/number_value/date_value fields are mutually exclusive.
-- It is assumed that only one will be used, depending on the value of
-- the attr_type argument ('VARCHAR2', 'NUMBER', or 'DATE').
--
-- IN:
-- command - Action requested. Must be one of 'GET', 'SET', or 'COMPLETE'.
-- context - Context data in the form '::'
-- attr_name - Attribute name to set/get for 'GET' or 'SET'
-- attr_type - Attribute type for 'SET'
-- text_value - Text Attribute value for 'SET'
-- number_value - Number Attribute value for 'SET'
-- date_value - Date Attribute value for 'SET'
-- OUT:
-- text_value - Text Attribute value for 'GET'
-- number_value - Number Attribute value for 'GET'
-- date_value - Date Attribute value for 'GET'
-- event_value - Event Attribute value for 'GET'
--
--No locking logic right now
--Locking at the item level is implemented in all cases
--where there is chances that status o fthe activity is being
--changed and there may be simultaneous access.
procedure CB(command in varchar2,
context in varchar2,
attr_name in varchar2,
attr_type in varchar2,
text_value in out NOCOPY varchar2,
number_value in out NOCOPY number,
date_value in out NOCOPY date,
event_value in out nocopy wf_event_t)
is
firstcolon pls_integer;
secondcolon pls_integer;
itemtype varchar2(8);
itemkey varchar2(240);
actid pls_integer;
status varchar2(8);
result varchar2(2000);
message varchar2(30);
msgtype varchar2(8);
expand_role varchar2(1);
wf_invalid_command exception;
wf_invalid_argument exception;
trig_savepoint exception;
pragma exception_init(trig_savepoint, -04092);
dist_savepoint exception;
pragma exception_init(dist_savepoint, -02074);
begin
--
-- Argument validation
--
if (command is null) then
raise wf_invalid_command;
end if;
if (context is null) then
raise wf_invalid_argument;
end if;
--
-- Take the context apart and extract item_type and
-- item_key from it.
--
firstcolon := instr(context, ':', 1,1);
secondcolon := instr(context, ':', -1,1);
if (firstcolon = 0 or secondcolon = 0) then
raise wf_invalid_argument;
end if;
itemtype := substr(context, 1, firstcolon - 1);
itemkey := substr(context, firstcolon + 1, secondcolon - firstcolon - 1);
actid := to_number(substr(context, secondcolon+1,
length(context) - secondcolon));
-- Not allowed in synch mode
if (itemkey = wf_engine.eng_synch) then
wf_core.token('OPERATION', 'Wf_Engine.CB');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
--
-- Handle the command now ... Get value and type Return Null
-- If the specified item not found ...
-- Bug 2376033 Get event attribute value for Event attribute type
--
if (upper(command) = 'GET') then
if (attr_type = 'NUMBER') then
number_value := GetItemAttrNumber(itemtype, itemkey, attr_name);
elsif (attr_type = 'DATE') then
date_value := GetItemAttrDate(itemtype, itemkey, attr_name);
elsif (attr_type = 'EVENT') then
event_value := GetItemAttrEvent(itemtype, itemkey, attr_name);
else
text_value := GetItemAttrText(itemtype, itemkey, attr_name);
end if;
elsif (upper(command) = 'SET') then
begin
if (attr_type = 'NUMBER') then
SetItemAttrNumber(itemtype, itemkey, attr_name, number_value);
elsif (attr_type = 'DATE') then
SetItemAttrDate(itemtype, itemkey, attr_name, date_value);
elsif (attr_type = 'EVENT') then
SetItemAttrEvent(itemtype, itemkey, attr_name, event_value);
else
SetItemAttrText(itemtype, itemkey, attr_name, text_value);
end if;
exception
when OTHERS then
-- If attr is not already defined, add a runtime attribute
-- with this name, then try the set again.
if (wf_core.error_name = 'WFENG_ITEM_ATTR') then
if (attr_type = 'EVENT') then
raise;
end if;
wf_core.clear;
if (attr_type = 'NUMBER') then
AddItemAttr(itemtype=>itemtype,
itemkey=>itemkey,
aname=>attr_name,
number_value=>number_value);
elsif (attr_type = 'DATE') then
AddItemAttr(itemtype=>itemtype,
itemkey=>itemkey,
aname=>attr_name,
date_value=>date_value);
else
AddItemAttr(itemtype=>itemtype,
itemkey=>itemkey,
aname=>attr_name,
text_value=>text_value);
end if;
else
raise;
end if;
end;
elsif (upper(command) = wf_engine.eng_completed) then
-- CB is signalling that a notification has completed.
-- If the activity originating this notification still has ACTIVE
-- status, then a routing rule (or some other kind of automatic
-- processing) has completed the notification before the activity
-- itself has finished. In this case, do NOT actually complete
-- the activity and continue processing. Exit silently and let
-- execute_activity() pick up the execution when the activity
-- owning this notification is actually completed.
Wf_Item_Activity_Status.Status(itemtype, itemkey,
actid, status);
if (status = wf_engine.eng_active) then
-- Do nothing!!!
return;
end if;
-- ### DL: Trap rollback error for savepoint
-- ### We do not trap the cases where we have trigger or distributed
-- ### savepoint at this time, but we can. More testing is needed.
-- ### Mainly we do not want to initiate error processing when those
-- ### exceptions are caught.
-- Use the text_value passed in as the result code for the activity.
result := text_value;
begin
savepoint wf_savepoint;
if (WF_CACHE.MetaRefreshed) then
NULL;
end if;
Wf_Engine_Util.Complete_Activity(itemtype, itemkey, actid, result);
exception
when trig_savepoint or dist_savepoint then
-- Savepoint violation.
-- Try without fancy error processing.
Wf_Engine_Util.Complete_Activity(itemtype, itemkey, actid, result);
when others then
-- If anything in this process raises an exception:
-- 1. rollback any work in this process thread
-- 2. set this activity to error status
-- 3. execute the error process (if any)
-- 4. clear the error to continue with next activity
rollback to wf_savepoint;
--The rollback will be done in the when others block
Wf_Core.Context('Wf_Engine', 'CB', command, context, attr_name,
attr_type, ':'||text_value||':'||to_char(number_value)||':'||
to_char(date_value)||':');
Wf_Item_Activity_Status.Set_Error(itemtype,
itemkey, actid, wf_engine.eng_exception, FALSE);
Wf_Engine_Util.Execute_Error_Process(itemtype,
itemkey, actid, wf_engine.eng_exception);
Wf_Core.Clear;
end;
elsif (upper(command) = wf_engine.eng_error) then
-- Set the error status
Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, actid,
wf_engine.eng_mail, FALSE);
-- Run any error process for the activity
Wf_Engine_Util.Execute_Error_Process(itemtype, itemkey, actid,
wf_engine.eng_mail);
elsif (upper(command) = 'TESTCTX') then
-- Call selector function in test mode
-- Return true if result is either true or null (means context
-- test not implemented)
result := Wf_Engine_Util.Execute_Selector_Function(itemtype,
itemkey, wf_engine.eng_testctx);
text_value := nvl(result, 'TRUE');
elsif (upper(command) = 'SETCTX') then
-- Call selector function in set mode
result := Wf_Engine_Util.Execute_Selector_Function(itemtype,
itemkey, wf_engine.eng_setctx);
elsif (upper(command) in ('FORWARD', 'TRANSFER', 'RESPOND',
'ANSWER', 'QUESTION', 'VALIDATE')) then
-- FORWARD/TRANSFER/RESPOND/ANSWER/QUESTION/VALIDATE
-- Look for a notification callback function to execute.
-- NOTES:
-- 1. For these modes, the value buffers must pass in the expected
-- expected values:
-- text_value = recipient_role (null for RESPOND)
-- number_value = notification_id
-- 2. The callback function will raise an exception if the
-- operation isn't allowed. If so, allow the exception to raise
-- up to the calling function.
Wf_Engine_Util.Execute_Notification_Callback(command, itemtype,
itemkey, actid, number_value, text_value);
-- For TRANSFER mode only, reset the assigned user, but only
-- if not a voting activity
if (command = 'TRANSFER') then
Wf_Activity.Notification_Info(itemtype, itemkey, actid,
message, msgtype, expand_role);
if (expand_role = 'N') then
Wf_Item_Activity_Status.Update_Notification(itemtype, itemkey, actid,
number_value, text_value);
end if;
end if;
else
raise wf_invalid_command;
end if;
exception
when wf_invalid_command then
Wf_Core.Context('Wf_Engine', 'CB', command, context, attr_name, attr_type,
':'||text_value||':'||to_char(number_value)||':'||
to_char(date_value)||':');
Wf_Core.Token('COMMAND', command);
Wf_Core.Raise('WFSQL_COMMAND');
when wf_invalid_argument then
Wf_Core.Context('Wf_Engine', 'CB', command, context, attr_name, attr_type,
':'||text_value||':'||to_char(number_value)||':'||
to_char(date_value)||':');
Wf_Core.Token('CONTEXT', context);
Wf_Core.Raise('WFSQL_ARGS');
when OTHERS then
Wf_Core.Context('Wf_Engine', 'CB', command, context, attr_name, attr_type,
':'||text_value||':'||to_char(number_value)||':'||
to_char(date_value)||':');
raise;
end CB;
-- Bug 2376033
-- Transferred the logic to the overloaded CB with additional event attribute
-- parameter. This calls the new CB with event paramter as null.
-- CB (PUBLIC)
-- This is the callback function used by the notification system to
-- get and set process attributes, and mark a process complete.
--
-- The command may be one of:
-- GET - Get the value of an attribute
-- SET - Set the value of an attribute
-- COMPLETE - Mark the activity as complete
-- ERROR - Mark the activity as error status
-- TESTCTX - Test current context via selector function
-- FORWARD - Execute notification function for FORWARD
-- TRANSFER - Execute notification function for TRANSFER
-- RESPOND - Execute notification function for RESPOND
--
-- The context is in the format ::.
--
-- The text_value/number_value/date_value fields are mutually exclusive.
-- It is assumed that only one will be used, depending on the value of
-- the attr_type argument ('VARCHAR2', 'NUMBER', or 'DATE').
--
-- IN:
-- command - Action requested. Must be one of 'GET', 'SET', or 'COMPLETE'.
-- context - Context data in the form '::'
-- attr_name - Attribute name to set/get for 'GET' or 'SET'
-- attr_type - Attribute type for 'SET'
-- text_value - Text Attribute value for 'SET'
-- number_value - Number Attribute value for 'SET'
-- date_value - Date Attribute value for 'SET'
-- OUT:
-- text_value - Text Attribute value for 'GET'
-- number_value - Number Attribute value for 'GET'
-- date_value - Date Attribute value for 'GET'
--
procedure CB(command in varchar2,
context in varchar2,
attr_name in varchar2,
attr_type in varchar2,
text_value in out NOCOPY varchar2,
number_value in out NOCOPY number,
date_value in out NOCOPY date)
is
event_value wf_event_t;
begin
Wf_Engine.CB(command, context, attr_name, attr_type, text_value, number_value, date_value, event_value);
exception
when OTHERS then
Wf_Core.Context('Wf_Engine', 'oldCB', command, context, attr_name, attr_type,
':'||text_value||':'||to_char(number_value)||':'||
to_char(date_value)||':');
raise;
end CB;
--
-- ProcessDeferred (PUBLIC)
-- Process all deferred activities
-- IN
-- itemtype - Item type to process. If null process all item types.
-- minthreshold - Minimum cost activity to process. No minimum if null.
-- maxthreshold - Maximum cost activity to process. No maximum if null.
--
procedure ProcessDeferred(itemtype in varchar2,
minthreshold in number,
maxthreshold in number) is
begin
wf_queue.ProcessDeferredQueue(itemtype, minthreshold, maxthreshold);
exception
when others then
Wf_Core.Context('Wf_Engine', 'ProcessDeferred',itemtype,
to_char(minthreshold), to_char(maxthreshold));
raise;
end ProcessDeferred;
--
-- ProcessTimeout (PUBLIC)
-- Pick up one timed out activity and execute timeout transition.
-- IN
-- itemtype - Item type to process. If null process all item types.
--
procedure ProcessTimeOut(itemtype in varchar2)
is
resource_busy exception;
pragma exception_init(resource_busy, -00054);
l_itemtype varchar2(8);
l_itemkey varchar2(240);
l_actid pls_integer;
pntfstatus varchar2(8);
pntfresult varchar2(30);
-- Select one timeout activity that matches itemtype
-- NOTE: Two separate cursors are used for itemtype and no-itemtype
-- cases to get better execution plans.
-- select everything but completed and error.
-- avoid "not in" which disables index in RBO
cursor curs_itype is
select
S.ROWID ROW_ID
from WF_ITEM_ACTIVITY_STATUSES S
where S.DUE_DATE < SYSDATE
and S.ACTIVITY_STATUS in ('ACTIVE','WAITING','NOTIFIED',
'SUSPEND','DEFERRED')
and S.ITEM_TYPE = itemtype;
cursor curs_noitype is
select
S.ROWID ROW_ID
from WF_ITEM_ACTIVITY_STATUSES S
where S.DUE_DATE < SYSDATE
and S.ACTIVITY_STATUS in ('ACTIVE','WAITING','NOTIFIED',
'SUSPEND','DEFERRED');
idarr RowidArrayTyp;
arrsize pls_integer;
eligible boolean;
schema varchar2(30);
begin
-- Fetch eligible rows into array
arrsize := 0;
if (itemtype is not null) then
-- Fetch by itemtype
for id in curs_itype loop
arrsize := arrsize + 1;
idarr(arrsize) := id.row_id;
end loop;
else
-- Fetch all itemtypes
for id in curs_noitype loop
arrsize := arrsize + 1;
idarr(arrsize) := id.row_id;
end loop;
end if;
-- Process all eligible rows found
for i in 1 .. arrsize loop
-- Lock row, and check if still eligible for execution
-- To check eligibility, do original select only add rowid condition.
-- Note ok to use no-itemtype variant since itemtype can't change
-- and was already filtered for in original select.
-- select everything but completed and error. avoid "not in" which
-- disables index in RBO.
begin
select
S.ITEM_TYPE, S.ITEM_KEY, S.PROCESS_ACTIVITY
into l_itemtype, l_itemkey, l_actid
from WF_ITEM_ACTIVITY_STATUSES S , WF_ITEMS WI
where S.DUE_DATE < SYSDATE
and S.ACTIVITY_STATUS in ('WAITING','NOTIFIED','SUSPEND',
'DEFERRED','ACTIVE')
and S.ROWID = idarr(i)
and WI.item_type = S.ITEM_TYPE
and WI.item_key = S.ITEM_KEY
for update of S.ACTIVITY_STATUS, WI.item_type , wi.item_key NOWAIT;
-- check if schema matched
schema := Wf_Engine.GetItemAttrText(l_itemtype,l_itemkey,
wf_engine.eng_schema, ignore_notfound=>TRUE);
if (schema is null or
schema = Wf_Engine.Current_Schema) then
eligible := TRUE;
else
eligible := FALSE;
end if;
exception
when resource_busy or no_data_found then
-- If row already locked, or no longer eligible to run,
-- continue on to next item in list.
eligible := FALSE;
end;
if (eligible) then
-- Set the status to COMPLETE:#TIMEOUT.
Wf_Item_Activity_Status.Create_Status(l_itemtype, l_itemkey, l_actid,
wf_engine.eng_completed, wf_engine.eng_timedout);
begin
begin
begin
savepoint wf_savepoint;
-- If there is a function attached, call it in timeout mode to
-- give the function one last chance to complete and override
-- the timeout.
Wf_Engine_Util.Execute_Post_NTF_Function(l_itemtype, l_itemkey,
l_actid, wf_engine.eng_timeout, pntfstatus, pntfresult);
if (pntfstatus = wf_engine.eng_completed) then
-- Post-notification function found and returned a completed
-- status.
-- Complete activity with result of post-notification function.
Wf_Engine_Util.Complete_Activity(l_itemtype, l_itemkey, l_actid,
pntfresult, FALSE);
else
-- Either had no post-notification function, or result was still
-- not complete.
-- In either case, complete activity with #TIMEOUT.
Wf_Engine_Util.Complete_Activity(l_itemtype, l_itemkey, l_actid,
wf_engine.eng_timedout);
end if;
exception
when others then
-- If anything in this process raises an exception:
-- 1. rollback any work in this process thread
-- Raise an exception for the next exception handler to finish
-- remaining steps.
rollback to wf_savepoint;
raise;
end;
exception
when NO_SAVEPOINT then
-- Catch any savepoint error in case of a commit happened.
Wf_Core.Token('ACTIVITY', Wf_Engine.GetActivityLabel(l_actid));
Wf_Core.Raise('WFENG_COMMIT_IN_COMPLETE');
end;
exception
when OTHERS then
-- Remaining steps for completing activity raises an exception:
-- 2. set this activity to error status
-- 3. execute the error process (if any)
-- 4. clear the error to continue with next activity
Wf_Core.Context('Wf_Engine', 'ProcessTimeout', l_itemkey, l_itemtype,
to_char(l_actid));
Wf_Item_Activity_Status.Set_Error(l_itemtype, l_itemkey, l_actid,
wf_engine.eng_exception, FALSE);
Wf_Engine_Util.Execute_Error_Process(l_itemtype, l_itemkey,
l_actid, wf_engine.eng_exception);
Wf_Core.Clear;
end;
end if;
-- For eligible row: Commit work to insure this activity
-- thread doesn't interfere with others.
-- For non-eligible row: Commit to release the lock.
commit;
Fnd_Concurrent.Set_Preferred_RBS;
end loop;
exception
when others then
Wf_Core.Context('Wf_Engine', 'ProcessTimeout', l_itemkey, l_itemtype,
to_char(l_actid));
raise;
end ProcessTimeOut;
--
-- ProcessStuckProcess (PUBLIC)
-- Pick up one stuck process, mark error status, and execute error process.
-- IN
-- itemtype - Item type to process. If null process all item types.
--
procedure ProcessStuckProcess(itemtype in varchar2)
is
resource_busy exception;
pragma exception_init(resource_busy, -00054);
l_itemtype varchar2(8);
l_itemkey varchar2(240);
l_actid pls_integer;
-- Select all activities from WIAS where:
-- 1. Activity is a PROCESS activity
-- 2. Activity has ACTIVE status
-- 3. Activity has no direct child activities which have a status of:
-- (ACTIVE, NOTIFIED, DEFERRED, SUSPENDED, ERROR)
-- 4. Item has requested itemtype (first curs only)
-- NOTE: Two separate cursors are used for itemtype and no-itemtype
-- cases to get better execution plans.
cursor curs_itype is
select /*+ ORDERED USE_NL (WIASP WI WPAP WAP)
INDEX (WIASP WF_ITEM_ACTIVITY_STATUSES_N1) */
WIASP.ROWID ROW_ID
from WF_ITEM_ACTIVITY_STATUSES WIASP,
WF_ITEMS WI,
WF_PROCESS_ACTIVITIES WPAP,
WF_ACTIVITIES WAP
where WIASP.ITEM_TYPE = itemtype
and WIASP.PROCESS_ACTIVITY = WPAP.INSTANCE_ID
and WPAP.ACTIVITY_ITEM_TYPE = WAP.ITEM_TYPE
and WPAP.ACTIVITY_NAME = WAP.NAME
and WIASP.ITEM_TYPE = WI.ITEM_TYPE
and WIASP.ITEM_KEY = WI.ITEM_KEY
and WI.BEGIN_DATE >= WAP.BEGIN_DATE
and WI.BEGIN_DATE < nvl(WAP.END_DATE, WI.BEGIN_DATE+1)
and WAP.TYPE = wf_engine.eng_process
and WIASP.ACTIVITY_STATUS = 'ACTIVE' --use literal to force index
and not exists
(select null
from WF_ITEM_ACTIVITY_STATUSES WIASC,
WF_PROCESS_ACTIVITIES WPAC
where WAP.ITEM_TYPE = WPAC.PROCESS_ITEM_TYPE
and WAP.NAME = WPAC.PROCESS_NAME
and WAP.VERSION = WPAC.PROCESS_VERSION
and WPAC.INSTANCE_ID = WIASC.PROCESS_ACTIVITY
and WIASC.ITEM_TYPE = WI.ITEM_TYPE
and WIASC.ITEM_KEY = WI.ITEM_KEY
and WIASC.ACTIVITY_STATUS in ('ACTIVE','NOTIFIED','SUSPEND',
'DEFERRED','ERROR'));
cursor curs_noitype is
select /*+ ORDERED USE_NL (WIASP WI WPAP WAP)
INDEX (WIASP WF_ITEM_ACTIVITY_STATUSES_N1) */
WIASP.ROWID ROW_ID
from WF_ITEM_ACTIVITY_STATUSES WIASP,
WF_ITEMS WI,
WF_PROCESS_ACTIVITIES WPAP,
WF_ACTIVITIES WAP
where WIASP.PROCESS_ACTIVITY = WPAP.INSTANCE_ID
and WPAP.ACTIVITY_ITEM_TYPE = WAP.ITEM_TYPE
and WPAP.ACTIVITY_NAME = WAP.NAME
and WIASP.ITEM_TYPE = WI.ITEM_TYPE
and WIASP.ITEM_KEY = WI.ITEM_KEY
and WI.BEGIN_DATE >= WAP.BEGIN_DATE
and WI.BEGIN_DATE < nvl(WAP.END_DATE, WI.BEGIN_DATE+1)
and WAP.TYPE = 'PROCESS'
and WIASP.ACTIVITY_STATUS = 'ACTIVE' --use literal to force index
and not exists
(select null
from WF_ITEM_ACTIVITY_STATUSES WIASC,
WF_PROCESS_ACTIVITIES WPAC
where WAP.ITEM_TYPE = WPAC.PROCESS_ITEM_TYPE
and WAP.NAME = WPAC.PROCESS_NAME
and WAP.VERSION = WPAC.PROCESS_VERSION
and WPAC.INSTANCE_ID = WIASC.PROCESS_ACTIVITY
and WIASC.ITEM_TYPE = decode(wap.direction,
wap.direction, WI.ITEM_TYPE,
wi.item_type)
and WIASC.ITEM_KEY = WI.ITEM_KEY
and WIASC.ACTIVITY_STATUS in ('ACTIVE', 'NOTIFIED', 'SUSPEND',
'DEFERRED', 'ERROR'));
idarr RowidArrayTyp;
arrsize pls_integer;
eligible boolean;
begin
-- Fetch eligible rows into array
arrsize := 0;
if (itemtype is not null) then
-- Fetch by itemtype
for id in curs_itype loop
arrsize := arrsize + 1;
idarr(arrsize) := id.row_id;
end loop;
else
-- Fetch all itemtypes
for id in curs_noitype loop
arrsize := arrsize + 1;
idarr(arrsize) := id.row_id;
end loop;
end if;
-- Process all eligible rows found
for i in 1 .. arrsize loop
-- Lock row, and check if still eligible for execution
-- To check for eligibility, check that:
-- 1. Activity is a PROCESS activity
-- 2. Activity has ACTIVE status
-- 3. Activity has no direct child activities which have a status of:
-- (ACTIVE, NOTIFIED, DEFERRED, SUSPENDED, ERROR)
-- 4. Item has requested itemtype (first curs only)
begin
select
WIASP.ITEM_TYPE, WIASP.ITEM_KEY, WIASP.PROCESS_ACTIVITY
into l_itemtype, l_itemkey, l_actid
from WF_ITEM_ACTIVITY_STATUSES WIASP,
WF_PROCESS_ACTIVITIES WPAP,
WF_ACTIVITIES WAP,
WF_ITEMS WI
where WIASP.PROCESS_ACTIVITY = WPAP.INSTANCE_ID
and WPAP.ACTIVITY_ITEM_TYPE = WAP.ITEM_TYPE
and WPAP.ACTIVITY_NAME = WAP.NAME
and WIASP.ITEM_TYPE = WI.ITEM_TYPE
and WIASP.ITEM_KEY = WI.ITEM_KEY
and WI.BEGIN_DATE >= WAP.BEGIN_DATE
and WI.BEGIN_DATE < nvl(WAP.END_DATE, WI.BEGIN_DATE+1)
and WAP.TYPE = wf_engine.eng_process
and WIASP.ACTIVITY_STATUS = 'ACTIVE' --use literal to force index
and not exists
(select null
from WF_ITEM_ACTIVITY_STATUSES WIASC,
WF_PROCESS_ACTIVITIES WPAC
where WAP.ITEM_TYPE = WPAC.PROCESS_ITEM_TYPE
and WAP.NAME = WPAC.PROCESS_NAME
and WAP.VERSION = WPAC.PROCESS_VERSION
and WPAC.INSTANCE_ID = WIASC.PROCESS_ACTIVITY
and WIASC.ITEM_TYPE = WI.ITEM_TYPE
and WIASC.ITEM_KEY = WI.ITEM_KEY
and WIASC.ACTIVITY_STATUS in ('ACTIVE','NOTIFIED','SUSPEND',
'DEFERRED','ERROR'))
and WIASP.ROWID = idarr(i)
for update of WIASP.ACTIVITY_STATUS, WI.ITEM_TYPE ,WI.ITEM_KEY NOWAIT;
eligible := TRUE;
exception
when resource_busy or no_data_found then
-- If row already locked, or no longer eligible to run,
-- continue on to next item in list.
eligible := FALSE;
end;
if (eligible) then
-- Set the status to ERROR:#STUCK
Wf_Item_Activity_Status.Create_Status(l_itemtype, l_itemkey, l_actid,
wf_engine.eng_error, wf_engine.eng_stuck);
-- Execute the error process for stuck process
begin
begin
begin
savepoint wf_savepoint;
Wf_Engine_Util.Execute_Error_Process(l_itemtype, l_itemkey, l_actid,
wf_engine.eng_stuck);
exception
when others then
-- If anything in this process raises an exception:
-- 1. rollback any work in this process thread
-- Raise an exception for the next exception handler to finish
-- remaining steps.
rollback to wf_savepoint;
raise;
end;
exception
when NO_SAVEPOINT then
-- Catch any savepoint error in case of a commit happened.
Wf_Core.Token('ACTIVITY', Wf_Engine.GetActivityLabel(l_actid));
Wf_Core.Raise('WFENG_COMMIT_IN_ERRPROC');
end;
exception
when OTHERS then
-- Remaining steps for completing activity raises an exception:
-- 2. set this activity to error status
-- 3. execute the error process (if any)
-- 4. clear the error to continue with next activity
Wf_Core.Context('Wf_Engine', 'ProcessStuckProcess', l_itemkey,
l_itemtype, to_char(l_actid));
Wf_Item_Activity_Status.Set_Error(l_itemtype, l_itemkey, l_actid,
wf_engine.eng_exception, FALSE);
Wf_Engine_Util.Execute_Error_Process(l_itemtype, l_itemkey,
l_actid, wf_engine.eng_exception);
Wf_Core.Clear;
end;
-- Commit work to insure this activity thread doesn't interfere
-- with others.
commit;
Fnd_Concurrent.Set_Preferred_RBS;
end if;
end loop;
exception
when others then
Wf_Core.Context('Wf_Engine', 'ProcessStuckProcess', l_itemkey, l_itemtype,
to_char(l_actid));
raise;
end ProcessStuckProcess;
--
-- Background (PUBLIC)
-- Process all current deferred and/or timeout activities within
-- threshold limits.
-- IN
-- itemtype - Item type to process. If null process all item types.
-- minthreshold - Minimum cost activity to process. No minimum if null.
-- maxthreshold - Maximum cost activity to process. No maximum if null.
-- process_deferred - Run deferred or waiting processes
-- process_timeout - Handle timeout errors
-- process_stuck - Handle stuck process errors
--
procedure Background (itemtype in varchar2,
minthreshold in number,
maxthreshold in number,
process_deferred in boolean,
process_timeout in boolean,
process_stuck in boolean)
is
l_aq_tm_processes varchar2(512);
begin
if (WF_CACHE.MetaRefreshed) then
null;
end if;
--Bug 2307442
--Select the value of aq_tm_processes
SELECT value
INTO l_aq_tm_processes
FROM v$parameter
WHERE name = 'aq_tm_processes';
--Check the value of aq_tm_processes
if (l_aq_tm_processes ='0') then
--If the value aq_tm_processes is 0 then raise error
wf_core.raise('WFENG_AQ_TM_PROCESSES_ERROR');
end if;
--Bug 2307428
--Enable the deferred and inbound queues.
wf_queue.Enablebackgroundqueues;
-- Do not need to preserve context
wf_engine.preserved_context := FALSE;
-- Process deferred activities
if (process_deferred) then
-- process the inbound queue first - it may place events on the deferred Q
wf_queue.ProcessInboundQueue(itemtype);
wf_engine.ProcessDeferred(itemtype, minthreshold, maxthreshold);
end if;
-- Process timeout activities
if (process_timeout) then
wf_engine.ProcessTimeout(itemtype);
end if;
-- Process stuck activities
if (process_stuck) then
wf_engine.ProcessStuckProcess(itemtype);
end if;
exception
when others then
Wf_Core.Context('Wf_Engine', 'Background', itemtype,
to_char(minthreshold), to_char(maxthreshold));
raise;
end Background;
--
-- BackgroundConcurrent (PUBLIC)
-- Run background process for deferred and/or timeout activities
-- from Concurrent Manager.
-- This is a cover of Background() with different argument types to
-- be used by the Concurrent Manager.
-- IN
-- errbuf - CPM error message
-- retcode - CPM return code (0 = success, 1 = warning, 2 = error)
-- itemtype - Item type to process. If null process all item types.
-- minthreshold - Minimum cost activity to process. No minimum if null.
-- maxthreshold - Maximum cost activity to process. No maximum if null.
-- process_deferred - Run deferred or waiting processes
-- process_timeout - Handle timeout errors
-- process_stuck - Handle stuck process errors
--
procedure BackgroundConcurrent (
errbuf out NOCOPY varchar2,
retcode out NOCOPY varchar2,
itemtype in varchar2,
minthreshold in varchar2,
maxthreshold in varchar2,
process_deferred in varchar2,
process_timeout in varchar2,
process_stuck in varchar2)
is
minthreshold_num number;
maxthreshold_num number;
process_deferred_bool boolean;
process_timeout_bool boolean;
process_stuck_bool boolean;
errname varchar2(30);
errmsg varchar2(2000);
errstack varchar2(4000);
begin
-- Convert arguments from varchar2 to real type.
minthreshold_num := to_number(minthreshold);
maxthreshold_num := to_number(maxthreshold);
if (upper(substr(process_deferred, 1, 1)) = 'Y') then
process_deferred_bool := TRUE;
else
process_deferred_bool := FALSE;
end if;
if (upper(substr(process_timeout, 1, 1)) = 'Y') then
process_timeout_bool := TRUE;
else
process_timeout_bool := FALSE;
end if;
if (upper(substr(process_stuck, 1, 1)) = 'Y') then
process_stuck_bool := TRUE;
else
process_stuck_bool := FALSE;
end if;
-- Call background engine with new args
Wf_Engine.Background(
itemtype,
minthreshold_num,
maxthreshold_num,
process_deferred_bool,
process_timeout_bool,
process_stuck_bool);
-- Return 0 for successful completion.
errbuf := '';
retcode := '0';
exception
when others then
-- Retrieve error message into errbuf
wf_core.get_error(errname, errmsg, errstack);
if (errmsg is not null) then
errbuf := errmsg;
else
errbuf := sqlerrm;
end if;
-- Return 2 for error.
retcode := '2';
end BackgroundConcurrent;
--
-- CreateProcess (PUBLIC)
-- Create a new runtime process (for an application item).
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- process - A valid root process for this item type
-- (or null to use the item's selector function)
--
procedure CreateProcess(itemtype in varchar2,
itemkey in varchar2,
process in varchar2,
user_key in varchar2,
owner_role in varchar2)
is
root varchar2(30);
version number;
actdate date;
typ varchar2(8);
rootid pls_integer;
status varchar2(8);
l_event wf_event_t; -- Buffer for initing event itemattrs
-- All event item attrs to be initialized
-- Initialization is now deferred until GetItemAttrEvent
/* cursor evtcurs is
select WIA.NAME
from WF_ITEM_ATTRIBUTES WIA
where WIA.ITEM_TYPE = CreateProcess.itemtype
and WIA.TYPE = 'EVENT';*/
begin
-- Argument validation
if ((itemtype is null) or (itemkey is null)) then
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', itemkey);
Wf_Core.Token('PROCESS', process);
Wf_Core.Raise('WFSQL_ARGS');
end if;
--
if (WF_CACHE.MetaRefreshed) then
null;
end if;
-- Check for duplicate item
if (itemkey = wf_engine.eng_synch) then
if (Wf_Item.Item_Exist(itemtype, itemkey)) then
-- SYNCHMODE: If duplicate is a synch process, check the status
-- of the root process of the existing item.
-- If the cached item is already complete, then it is ok to
-- toss out the old item and create a new one.
begin
Wf_Item.Root_Process(itemtype, itemkey, root, version);
rootid := Wf_Process_Activity.RootInstanceId(itemtype,
itemkey, root);
Wf_Item_Activity_Status.Status(itemtype, itemkey, rootid, status);
exception
when others then
status := 'x'; -- Treat errors like incomplete process
end;
if (nvl(status, 'x') <> wf_engine.eng_completed) then
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', itemkey);
Wf_Core.Raise('WFENG_SYNCH_ITEM');
end if;
end if;
else
-- Not synchmode. Clear plsql cache first, just in case previous
-- item was purged/rolled back, then check for duplicate.
Wf_Item.ClearCache;
if (Wf_Item.Item_Exist(itemtype, itemkey)) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM_UNIQUE');
end if;
end if;
if (process is null) then
-- Call the selector function to get the process
root := Wf_Engine_Util.Get_Root_Process(itemtype, itemkey);
if (root is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM_ROOT_SELECTOR');
end if;
else
root := process;
end if;
-- Check that the root argument is a valid process.
-- NOTE: The check that the process exists must be done BEFORE
-- calling create_item to avoid foreign key problems during the insert.
-- The check that the process is runnable can't be done until AFTER
-- create_item so the date has been established.
actdate := sysdate;
typ := Wf_Activity.Type(itemtype, root, actdate);
if ((typ is null) or (typ <> wf_engine.eng_process)) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('NAME', root);
Wf_Core.Raise('WFENG_PROCESS_NAME');
end if;
-- Insert row in items table
Wf_Item.Create_Item(itemtype, itemkey, root, actdate, createprocess.user_key,
createprocess.owner_role);
-- Validate the root argument is runnable
rootid := Wf_Process_Activity.RootInstanceId(itemtype, itemkey,
root);
if (rootid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('NAME', root);
Wf_Core.Raise('WFENG_PROCESS_RUNNABLE');
end if;
if (itemkey <> WF_ENGINE.eng_synch) then
-- Create monitor access key attributes
Wf_Engine.AddItemAttr(itemtype, itemkey, wf_engine.wfmon_mon_key,
Wf_Core.Random);
Wf_Engine.AddItemAttr(itemtype, itemkey, wf_engine.wfmon_acc_key,
Wf_Core.Random);
end if;
-- Create a schema attribute
Wf_Engine.AddItemAttr(itemtype, itemkey, wf_engine.eng_schema,
Wf_Engine.Current_Schema);
-- Initialize all EVENT-type item attributes
-- Not done here, it is deferred until GetItemAttrEvent
/* for evtattr in evtcurs loop
Wf_Event_T.Initialize(l_event);
Wf_Engine.SetItemAttrEvent(
itemtype => itemtype,
itemkey => itemkey,
name => evtattr.name,
event => l_event);
end loop;*/
exception
when others then
-- Bug 4117740
-- Call clearcache() when #SYNCH flow is in error
if ((itemkey = WF_ENGINE.eng_synch) and
(wf_core.error_name is null or wf_core.error_name <> 'WFENG_SYNCH_ITEM') and
(not WF_ENGINE.debug)) then
Wf_Item.ClearCache;
end if;
Wf_Core.Context('Wf_Engine', 'CreateProcess', itemtype, itemkey, process);
raise;
end CreateProcess;
--
-- StartProcess (PUBLIC)
-- Begins execution of the process. The process will be identified by the
-- itemtype and itemkey. The engine locates the starting activities
-- of the root process and executes them.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
--
procedure StartProcess(itemtype in varchar2,
itemkey in varchar2)
is
begin
if (WF_CACHE.MetaRefreshed) then
null;
end if;
--Bug 2259039
Wf_Engine_Util.Start_Process_Internal(
itemtype=> itemtype,
itemkey => itemkey,
runmode => 'START');
exception
when others then
Wf_Core.Context('Wf_Engine', 'StartProcess', itemtype, itemkey);
raise;
end StartProcess;
--
-- LaunchProcess (PUBLIC)
-- Launch a process both creates and starts it.
-- This is a wrapper for friendlier UI
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- process - A valid root process for this item type
-- (or null to use the item's selector function)
-- userkey - User key to be set
-- owner - Role designated as owner of the item
--
procedure LaunchProcess(itemtype in varchar2,
itemkey in varchar2,
process in varchar2,
userkey in varchar2,
owner in varchar2) is
begin
-- Check Arguments
if ((itemtype is null) or
(itemkey is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
wf_engine.CreateProcess (itemtype,itemkey,process);
if userkey is not null then
wf_engine.SetItemUserKey(itemtype,itemkey,userkey);
end if;
if owner is not null then
wf_engine.SetItemOwner(itemtype,itemkey,owner);
end if;
wf_engine.StartProcess (itemtype,itemkey);
exception
when others then
Wf_Core.Context('Wf_Engine', 'LaunchProcess', itemtype, itemkey,
process, userkey, owner);
raise;
end LaunchProcess;
--
-- SuspendProcess (PUBLIC)
-- Suspends process execution, meaning no new transitions will occur.
-- Outstanding notifications will be allowed to complete, but they will not
-- cause activity transitions. If the process argument is null, the root
-- process for the item is suspended, otherwise the named process is
-- suspended.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- process - Process to suspend, specified in the form
-- [:]
-- If null suspend the root process.
--
procedure SuspendProcess(itemtype in varchar2,
itemkey in varchar2,
process in varchar2) is
root varchar2(30); -- The root process for this item key
version pls_integer; -- Root process version
rootid pls_integer; -- Instance id of root process
actdate date; -- Active date of item
proc varchar2(61); -- The process name that is going to be suspended
procid pls_integer; -- The process id that is going to be suspended
status varchar2(8); -- The status of the process
-- Cursor to select deferred activities to remove from background queue
cursor defact is
select PROCESS_ACTIVITY, BEGIN_DATE
from WF_ITEM_ACTIVITY_STATUSES
where ITEM_TYPE = itemtype
and ITEM_KEY = itemkey
and ACTIVITY_STATUS = wf_engine.eng_deferred;
begin
-- Check Arguments
if (itemtype is null) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
-- Not allowed in synch mode
elsif (itemkey = wf_engine.eng_synch) then
wf_core.token('OPERATION', 'Wf_Engine.SuspendProcess');
wf_core.raise('WFENG_SYNCH_DISABLED');
elsif (itemkey is null) then
WF_ENGINE.SuspendAll(itemtype, process); --
return;
end if;
-- Get the root process for this key and also validate the item
Wf_Item.Root_Process(itemtype, itemkey, root, version);
if (root is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
end if;
-- Get the process instance id.
-- Search the process beginnning at the root process of the item for the
-- activity matching process.
actdate := Wf_Item.Active_Date(itemtype, itemkey);
rootid := Wf_Process_Activity.RootInstanceId(itemtype, itemkey, root);
if (rootid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', root);
Wf_Core.Raise('WFENG_ITEM_ROOT');
end if;
if (process is null) then
-- Suspend the root process
proc := root;
procid := rootid;
else
-- Suspend the given process
proc := process;
procid := Wf_Process_Activity.FindActivity(rootid, proc, actdate);
if (procid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', proc);
Wf_Core.Token('VERSION', to_char(version));
Wf_Core.Raise('WFENG_ITEM_PROCESS');
end if;
-- Check that activity is a PROCESS-type.
-- Only PROCESS activities may be suspended.
if (Wf_Activity.Instance_Type(procid, actdate) <>
wf_engine.eng_process) then
Wf_Core.Token('NAME', proc);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Raise('WFENG_PROCESS_NAME');
end if;
end if;
-- Always clear the cache first
-- AbortProcess, SuspendProcess and ResumeProcess should be rarely called
-- from the background engine, so it should be safe to force reading from
-- the database.
Wf_Item_Activity_Status.ClearCache;
-- Check if the process is active
Wf_Item_Activity_Status.Status(itemtype, itemkey, procid, status);
if (status is null) then
-- This process has not been run yet. Create a pre-suspended
-- status row so engine does not run process later
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, procid,
wf_engine.eng_suspended, wf_engine.eng_null, null, null,
newStatus=>TRUE);
elsif (status = wf_engine.eng_deferred) then
-- Change status from 'deferred' to 'suspended'
-- Doing this prevents the background processor from picking it up.
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, procid,
wf_engine.eng_suspended, null,
null, null);
elsif (status = wf_engine.eng_active) then
-- Mark process as 'suspended', 'null' in WIAS table
-- Doing this stops the engine from going through the rest of the flow
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, procid,
wf_engine.eng_suspended, null,
null, null);
-- Suspend all the children processes
Wf_Engine_Util.Suspend_Child_Processes(itemtype, itemkey, procid);
else
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', proc);
Wf_Core.Raise('WFENG_ITEM_PROCESS_ACTIVE');
end if;
exception
when others then
Wf_Core.Context('Wf_Engine', 'SuspendProcess', itemtype, itemkey, process);
raise;
end SuspendProcess;
--
-- AbortProcess (PUBLIC)
-- Abort process execution. Outstanding notifications are canceled. The
-- process is then considered complete, with a status specified by the
-- result argument.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- process - Process to abort, specified in the form
-- [:]
-- If null abort the root process.
-- result - Result to complete process with
-- verify_lock - This boolean param determines whether we should lock
-- the item before processing or not . This would control
-- concurrent execution contention.
-- cascade - This boolean param determines if the process should be
-- aborted in cascade or not, ie kill all child processes
-- to this process.
--
procedure AbortProcess(itemtype in varchar2,
itemkey in varchar2,
process in varchar2,
result in varchar2,
verify_lock in boolean,
cascade in boolean) is
root varchar2(30); -- The root process for this item key
version pls_integer; -- Root process version
rootid pls_integer; -- Instance id of root process
actdate date; -- Active date of item
proc varchar2(61); -- Process name
procid pls_integer; -- The process id that is going to be suspended
status varchar2(8); -- The status of the process
dummy pls_integer; -- Added for bug 1893606 - JWSMITH
--Bug 1166527
l_parameterlist wf_parameter_list_t := wf_parameter_list_t();
l_lock boolean;
begin
-- Check Arguments
if (itemtype is null) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
elsif (itemkey = wf_engine.eng_synch) then -- Not allowed in synch mode
wf_core.token('OPERATION', 'Wf_Engine.AbortProcess');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
--Do the check for lock ONLY if there is an explicit
--request for the same.
if verify_lock then
--Check if we can acquire lock for the
--the item type/key here
l_lock := wf_item.acquire_lock(itemtype,itemkey,true) ;
end if;
-- Get the root process for this key and also validate the item
Wf_Item.Root_Process(itemtype, itemkey, root, version);
if (root is null) then
Wf_Core.Context('Wf_Engine', 'AbortProcess', itemtype, itemkey, process);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
end if;
-- Get the process instance id.
-- Search the process beginnning at the root process of the item for the
-- activity matching process.
actdate := Wf_Item.Active_Date(itemtype, itemkey);
rootid := Wf_Process_Activity.RootInstanceId(itemtype, itemkey, root);
if (rootid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', root);
Wf_Core.Raise('WFENG_ITEM_ROOT');
end if;
if (process is null) then
-- Abort the root process
proc := root;
procid := rootid;
else
-- Abort the given process
proc := process;
procid := Wf_Process_Activity.FindActivity(rootid, process, actdate);
if (procid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', process);
Wf_Core.Token('VERSION', to_char(version));
Wf_Core.Raise('WFENG_ITEM_PROCESS');
end if;
-- Check that activity is a PROCESS-type.
-- Only PROCESS activities may be aborted.
if (Wf_Activity.Instance_Type(procid, actdate) <>
wf_engine.eng_process) then
Wf_Core.Token('NAME', proc);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Raise('WFENG_PROCESS_NAME');
end if;
end if;
-- Always clear the cache first
Wf_Item_Activity_Status.ClearCache;
-- Check the process is not already complete
Wf_Item_Activity_Status.Status(itemtype, itemkey, procid, status);
if (status is null) then
if (WF_ITEM.SetEndDate(itemtype, itemkey) = 1) then
Wf_Core.Token('TYPE', itemtype);
Wf_core.Token('KEY', itemkey);
Wf_core.Token('NAME', proc);
Wf_Core.Raise('WFENG_ITEM_PROCESS_RUNNING');
end if;
elsif (status = wf_engine.eng_completed) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', proc);
Wf_Core.Raise('WFENG_ITEM_PROCESS_ACTIVE');
else
-- Mark process as 'COMPLETE', 'result' in WIAS table
-- Doing this stops the engine from going through the rest of the flow
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, procid,
wf_engine.eng_completed, result,
null, SYSDATE);
-- Kill child activities recursively
Wf_Engine_Util.Process_Kill_Children(itemtype, itemkey, procid);
--If cascade option is set to true abort all child
--processes aswell
if cascade then
Wf_Engine_Util.Process_Kill_ChildProcess(itemtype, itemkey);
end if;
end if;
--Include the information of the aborted process in the events
--parameter list.
wf_event.AddParameterToList('ITMETYPE', itemtype, l_parameterlist);
wf_event.AddParameterToList('ITEMKEY', itemkey, l_parameterlist);
wf_event.AddParameterToList('PROCESS', process, l_parameterlist);
wf_event.AddParameterToList('RESULT', result, l_parameterlist);
-- Raise the event
wf_event.Raise(p_event_name => 'oracle.apps.wf.engine.abort',
p_event_key => itemkey,
p_parameters => l_parameterlist);
exception
when resource_busy then
wf_core.token('TYPE',itemtype);
wf_core.token('KEY',itemkey);
wf_core.raise('WFENG_RESOURCE_BUSY');
when others then
Wf_Core.Context('Wf_Engine', 'AbortProcess', itemtype, itemkey,
process, result);
raise;
end AbortProcess;
--
-- ResumeProcess (PUBLIC)
-- Returns a process to normal execution status. Any transitions which
-- were deferred by SuspendProcess() will now be processed.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- process - Process to resume, specified in the form
-- [:]
-- If null resume the root process.
--
procedure ResumeProcess(itemtype in varchar2,
itemkey in varchar2,
process in varchar2)
is
root varchar2(30); -- The root process for this item key
version pls_integer; -- Root process version
rootid pls_integer; -- Instance id of root process
actdate date; -- Active date of item
proc varchar2(61); -- The process name that is going to be suspended
procid pls_integer; -- The process id that is going to be suspended
status varchar2(8); -- The status of the process
-- Cursor to select deferred activities to restart.
cursor defact is
select
PROCESS_ACTIVITY, BEGIN_DATE
from WF_ITEM_ACTIVITY_STATUSES
where ITEM_TYPE = itemtype
and ITEM_KEY = itemkey
and ACTIVITY_STATUS = wf_engine.eng_deferred;
actidarr InstanceArrayTyp; -- Deferred activities array
i pls_integer := 0; -- Counter for the for loop
trig_savepoint exception;
pragma exception_init(trig_savepoint, -04092);
dist_savepoint exception;
pragma exception_init(dist_savepoint, -02074);
--Bug 2484201
--Array to select the begin_date for the deferred activities
type InstanceDateArray is table of date index by binary_integer;
act_begin_date InstanceDateArray;
begin
-- Check Arguments
if (itemtype is null) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
elsif (itemkey = wf_engine.eng_synch) then -- Not allowed in synch mode
wf_core.token('OPERATION', 'Wf_Engine.ResumeProcess');
wf_core.raise('WFENG_SYNCH_DISABLED');
elsif (itemkey is NULL) then
WF_ENGINE.ResumeAll(itemtype, process); --
return;
end if;
-- Get the root process for this key
Wf_Item.Root_Process(itemtype, itemkey, root, version);
if (root is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
end if;
-- Get the process instance id.
-- Search the process beginnning at the root process of the item for the
-- activity matching process.
actdate := Wf_Item.Active_Date(itemtype, itemkey);
rootid := Wf_Process_Activity.RootInstanceId(itemtype, itemkey, root);
if (rootid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', root);
Wf_Core.Raise('WFENG_ITEM_ROOT');
end if;
if (process is null) then
-- Resume the root process
proc := root;
procid := rootid;
else
-- Resume the given process
proc := process;
procid := Wf_Process_Activity.FindActivity(rootid, process, actdate);
if (procid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', process);
Wf_Core.Token('VERSION', to_char(version));
Wf_Core.Raise('WFENG_ITEM_PROCESS');
end if;
-- Check that activity is a PROCESS-type.
-- Only PROCESS activities may be resumed.
if (Wf_Activity.Instance_Type(procid, actdate) <>
wf_engine.eng_process) then
Wf_Core.Token('NAME', proc);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Raise('WFENG_PROCESS_NAME');
end if;
end if;
-- Always clear the cache first
Wf_Item_Activity_Status.ClearCache;
-- Check if the process is suspended
Wf_Item_Activity_Status.Status(itemtype, itemkey, procid, status);
if (status is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', proc);
Wf_Core.Raise('WFENG_ITEM_PROCESS_RUNNING');
elsif (status <> wf_engine.eng_suspended) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', proc);
Wf_Core.Raise('WFENG_ITEM_PROCESS_SUSPENDED');
else
-- If we came here, that means the process is currently suspended.
-- Mark process as eng_active 'active', 'null' in WIAS table
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, procid,
wf_engine.eng_active, null, null, null);
-- Mark any sub-processes as active again
Wf_Engine_Util.Resume_Child_Processes(itemtype, itemkey, procid);
-- Restart any activities that were deferred because completion
-- came in while process was suspended.
--
-- Note that cursor will select all deferred activities, even if they
-- were deferred for other reasons than suspended process, but this is
-- OK because:
-- 1. Activities deferred because cost is higher than threshold will
-- be immediately re-deferred by process_activity()
-- 2. Deferred activities that are not in the sub-process just resumed
-- will still have a suspended parent, and will also be immediately
-- re-deferred by process_activity().
-- This causes a little extra processing in rare cases, but is easier
-- than figuring out the cause for each deferral here.
for actid in defact loop
actidarr(i) := actid.process_activity;
act_begin_date(i) := actid.begin_date;
i := i + 1;
end loop;
actidarr(i) := '';
i := 0;
while (actidarr(i) is not null) loop
--Bug 2484201
--Set the begin date in call to Create_status as the begin_date
--of the activity or to sysdate if begin_date is null
--Also set the status to active only if begin_date <= sysdate
if (nvl(act_begin_date(i),sysdate) <= sysdate) then
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actidarr(i),
wf_engine.eng_active, null, sysdate, null);
begin
savepoint wf_savepoint;
Wf_Engine_Util.Process_Activity(itemtype, itemkey, actidarr(i),
Wf_Engine.Threshold, TRUE);
exception
when trig_savepoint or dist_savepoint then
-- Can't restart process here, re-defer for the
-- background process to pick up.
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey,
actidarr(i),wf_engine.eng_deferred, null, sysdate, null);
when others then
-- If anything in this process raises an exception:
-- 1. rollback any work in this process thread
-- 2. set this activity to error status
-- 3. execute the error process (if any)
-- 4. clear the error to continue with next activity
rollback to wf_savepoint;
Wf_Core.Context('Wf_Engine', 'ResumeProcess', itemtype, itemkey,
process);
Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, actidarr(i),
wf_engine.eng_exception, FALSE);
Wf_Engine_Util.Execute_Error_Process(itemtype, itemkey, actidarr(i),
wf_engine.eng_exception);
Wf_Core.Clear;
end;
--else case status is same as right now that is deferred.
end if;
i := i + 1;
end loop;
end if;
exception
when others then
Wf_Core.Context('Wf_Engine', 'ResumeProcess', itemtype, itemkey, process);
raise;
end ResumeProcess;
--
-- SuspendAll (PUBLIC)) --
-- Suspends all processes for a given itemType.
-- IN
-- itemtype - A valid itemType
--
Procedure SuspendAll (p_itemType in varchar2,
p_process in varchar2) is
cursor Open_Items(p_itemType in varchar2) is
SELECT item_key
FROM wf_items
WHERE item_type = p_itemType
AND end_date is NULL;
cursor All_Open_Items is
SELECT item_type, item_key
FROM wf_items
WHERE end_date is NULL;
begin
if (p_itemType is NULL) then
for c in All_Open_items loop
begin
WF_ENGINE.SuspendProcess(c.item_type, c.item_key, p_process);
exception
when others then
if ( wf_core.error_name = 'WFENG_ITEM_PROCESS_ACTIVE' ) then
wf_core.clear;
else
raise;
end if;
end;
end loop;
else
for c in Open_Items(p_itemType) loop
begin
WF_ENGINE.SuspendProcess(p_itemType, c.item_key, p_process);
exception
when others then
if ( wf_core.error_name = 'WFENG_ITEM_PROCESS_ACTIVE' ) then
wf_core.clear;
else
raise;
end if;
end;
end loop;
end if;
exception
when others then
Wf_Core.Context('Wf_Engine', 'SuspendAll', p_itemType, p_process);
raise;
end SuspendAll;
--
-- ResumeAll (PUBLIC) --
-- Resumes all processes for a given itemType.
-- IN
-- itemtype - A valid itemType
--
Procedure ResumeAll (p_itemType in varchar2,
p_process in varchar2) is
cursor suspended_items(p_itemType in varchar2) is
SELECT distinct wias.item_key
FROM wf_item_activity_statuses wias
WHERE wias.item_type = p_itemType
AND wias.activity_status = wf_engine.eng_suspended;
cursor all_suspended_items is
SELECT distinct wias.item_type, wias.item_key
FROM wf_item_activity_statuses wias
WHERE wias.activity_status = wf_engine.eng_suspended;
begin
if (p_itemType is NULL) then
for c in all_suspended_items loop
begin
WF_ENGINE.ResumeProcess(c.item_type, c.item_key, p_process);
exception
when others then
null;
end;
end loop;
else
for c in suspended_items(p_itemType) loop
begin
WF_ENGINE.ResumeProcess(p_itemType, c.item_key, p_process);
exception
when others then
null;
end;
end loop;
end if;
end ResumeAll;
Procedure CreateForkProcess (
copy_itemtype in varchar2,
copy_itemkey in varchar2,
new_itemkey in varchar2,
same_version in boolean,
masterdetail in boolean) is
root_process varchar2(30);
root_process_version number;
dummy varchar2(30);
dummyNum number;
status varchar2(50);
result varchar2(50);
l_parent_itemType varchar2(8);
l_parent_itemKey varchar2(240);
l_parent_context varchar2(2000);
ValTooLarge EXCEPTION;
pragma exception_init(ValTooLarge, -01401);
begin
-- Argument validation
if (copy_itemtype is null)
or (copy_itemkey is null)
or (new_itemkey is null) then
Wf_Core.Token('COPY_ITEMTYPE', copy_itemtype);
Wf_Core.Token('COPY_ITEMKEY', copy_itemkey);
Wf_Core.Token('NEW_ITEMKEY', new_itemkey);
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Not allowed in synch mode
if (new_itemkey = wf_engine.eng_synch)
or (copy_itemkey = wf_engine.eng_synch) then
wf_core.token('OPERATION', 'Wf_Engine.SuspendProcess');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
-- Check status
Wf_engine.ItemStatus(copy_itemtype, copy_itemkey, status, result);
if (status = wf_engine.eng_error) then
Wf_Core.Raise('WFENG_NOFORK_ONERROR');
end if;
-- Check for duplicate item
if (Wf_Item.Item_Exist(copy_itemtype, new_itemkey)) then
Wf_Core.Token('TYPE', copy_itemtype);
Wf_Core.Token('KEY', new_itemkey);
Wf_Core.Raise('WFENG_ITEM_UNIQUE');
end if;
--Place row-lock on this item and retrieve parent process info:
select parent_item_type, parent_item_key, parent_context
into l_parent_itemType, l_parent_itemKey, l_parent_context
from wf_items
where item_type = copy_itemtype
and item_key = copy_itemkey
for update of item_type;
--Create the process
if same_version then
insert into wf_items(
ITEM_TYPE, ITEM_KEY,
ROOT_ACTIVITY, ROOT_ACTIVITY_VERSION,
OWNER_ROLE, USER_KEY,
PARENT_ITEM_TYPE, PARENT_ITEM_KEY, PARENT_CONTEXT,
BEGIN_DATE, END_DATE)
select
ITEM_TYPE, NEW_ITEMKEY,
ROOT_ACTIVITY, ROOT_ACTIVITY_VERSION,
OWNER_ROLE, USER_KEY,
PARENT_ITEM_TYPE, PARENT_ITEM_KEY, PARENT_CONTEXT,
BEGIN_DATE, null
from wf_items
where item_type = copy_itemtype
and item_key = copy_itemkey;
else
--lookup the root process
wf_item.Root_Process(itemtype => copy_itemtype,
itemkey => copy_itemkey,
wflow => root_process,
version =>root_process_version);
wf_engine.CreateProcess(copy_itemtype,new_itemkey,root_process);
--delete any defaulted attributes because we will copy the existing ones.
delete from wf_item_attribute_values
where item_type = copy_itemtype
and item_key = new_itemkey;
end if;
-- copy all item attributes including runtime attributes. Also, copy
-- those item attributes that were added after the item was forked
insert into wf_item_attribute_values
(ITEM_TYPE, ITEM_KEY, NAME,
TEXT_VALUE, NUMBER_VALUE, DATE_VALUE)
select ITEM_TYPE, NEW_ITEMKEY, NAME,
TEXT_VALUE, NUMBER_VALUE, DATE_VALUE
from wf_item_attribute_values
where item_type = copy_itemtype
and item_key = copy_itemkey
and name not like '#LBL_'
and name not like '#CNT_'
union all
select ITEM_TYPE, new_itemkey, NAME,
TEXT_DEFAULT, NUMBER_DEFAULT, DATE_DEFAULT
from WF_ITEM_ATTRIBUTES
where ITEM_TYPE = copy_itemtype
and NAME not in
(select name
from wf_item_attribute_values
where item_type = copy_itemtype
and item_key = copy_itemkey
and name not like '#LBL_'
and name not like '#CNT_');
-- reset the access_keys to make them unique
Wf_Engine.SetItemAttrText(copy_itemtype, new_itemkey,
wf_engine.wfmon_mon_key, Wf_Core.Random);
Wf_Engine.SetItemAttrText(copy_itemtype, new_itemkey,
wf_engine.wfmon_acc_key, Wf_Core.Random);
-- reset the schema, just in case, if the #SCHEMA attribute does not exist
-- it will be added. The CreateProcess api now adds the #SCHEMA.
-- Only items created before WF_ENGINE was upgraded will encounter the
-- exception to be handled, so this is for backward compatibility.
begin
Wf_Engine.SetItemAttrText(copy_itemtype, new_itemkey,
wf_engine.eng_schema, Wf_Engine.Current_Schema);
exception
when others then
if (wf_core.error_name = 'WFENG_ITEM_ATTR') then
wf_core.clear;
WF_ENGINE.AddItemAttr(copy_itemtype, new_itemkey,
wf_engine.eng_schema,
Wf_Engine.Current_Schema);
else
raise;
end if;
end;
-- Finally set an itemkey to record what this originated from
begin
Wf_Engine.AddItemAttr(copy_itemtype, new_itemkey, '#FORKED_FROM',
copy_itemkey);
exception
when others then
--
-- If item attribute already exists then ignore the error
--
if ( wf_core.error_name = 'WFENG_ITEM_ATTR_UNIQUE' ) then
wf_core.clear;
Wf_Engine.SetItemAttrText(copy_itemtype, new_itemkey,
'#FORKED_FROM', copy_itemkey);
else
raise;
end if;
end;
if (masterdetail) then
--The caller has signaled that this is a master/detail process
--We first will attempt to zero out any #WAITFORDETAIL attribute that may be
--on this forked process (it is a master itself).
dummyNum := WF_ENGINE.AddToItemAttrNumber(copy_itemType, new_itemKey,
'#WAITFORDETAIL',
to_number(NULL));
if ((l_parent_itemType is NOT null) and (l_parent_itemKey is NOT null)) then
--There is a parent item to this forked item, so we will validate and
--increment the parent's #WAITFORDETAIL counter.
if (WF_ENGINE.AddToItemAttrNumber(l_parent_itemType, l_parent_itemKey,
'#WAITFORDETAIL', 1) is NOT null) then
--The parent has a #WAITFORDETAIL, so we can proceed on to check for
--parent context.
if (l_parent_context is NOT null) then
--There is a parent context, so we will add the #LBL_ attribute to
--the child flow, and will increment the corresponding #CNT_ attribute
--in the parent flow.
begin
WF_ENGINE.AddItemAttr(itemtype=>copy_itemType, itemkey=>new_itemkey,
aname=>'#LBL_'||l_parent_context,
text_value=>l_parent_context);
--Since there was a parent context in the forked_from flow, we know
--The parent has a counter for this label, so we can just increment.
dummyNum := WF_ENGINE.AddToItemAttrNumber(l_parent_itemType,
l_parent_itemKey,
'#CNT_'||l_parent_context,
1);
exception
when ValTooLarge then
Wf_Core.Context('WF_ENGINE', 'CreateForkProcess', copy_itemtype,
copy_itemkey, new_itemkey, l_parent_itemtype,
l_parent_itemkey, l_parent_context, 'TRUE');
WF_CORE.Token('LABEL', l_parent_context);
WF_CORE.Token('LENGTH', 25);
WF_CORE.Raise('WFENG_LABEL_TOO_LARGE');
end;
else
-- PARENT_CONTEXT is null
-- increase all known #CNT counter by 1
update WF_ITEM_ATTRIBUTE_VALUES
set NUMBER_VALUE = NUMBER_VALUE + 1
where NAME like '#CNT_%'
and NUMBER_VALUE is not null
and ITEM_TYPE = l_parent_itemType
and ITEM_KEY = l_parent_itemKey;
end if; --PARENT_CONTEXT is not null
end if; --#WAITFORDETAIL exists in the parent item.
end if; --There is a parent item to this forked process.
end if; --The caller signalled that this is a master/detail process.
exception
when others then
Wf_Core.Context('Wf_Engine', 'CreateForkProcess');
raise;
end CreateForkProcess;
--
-- StartForkProcess (PUBLIC)
-- Start a process that has been forked. Depending on the way this was
-- forked, this will execute startprocess if its to start with the latest
-- version or it copies the forked process activty by activity.
-- IN
-- itemtype - Item type
-- itemkey - item key to start
--
procedure StartForkProcess(
itemtype in varchar2,
itemkey in varchar2) as
copy_itemkey varchar2(30);
cursor all_activities is
select ITEM_TYPE, ITEM_KEY, PROCESS_ACTIVITY,
ACTIVITY_STATUS, ACTIVITY_RESULT_CODE,
ASSIGNED_USER, NOTIFICATION_ID,
BEGIN_DATE, END_DATE, EXECUTION_TIME,
ERROR_NAME, ERROR_MESSAGE, ERROR_STACK,
OUTBOUND_QUEUE_ID, DUE_DATE
from wf_item_activity_statuses
where item_type = itemtype
and item_key = copy_itemkey;
cursor all_activities_hist is
select ITEM_TYPE, ITEM_KEY, PROCESS_ACTIVITY,
ACTIVITY_STATUS, ACTIVITY_RESULT_CODE,
ASSIGNED_USER, NOTIFICATION_ID,
BEGIN_DATE, END_DATE, EXECUTION_TIME,
ERROR_NAME, ERROR_MESSAGE, ERROR_STACK,
OUTBOUND_QUEUE_ID, DUE_DATE
from wf_item_activity_statuses_h
where item_type = itemtype
and item_key = copy_itemkey;
-- order by nid so that we re-execute in chronological order
cursor ntf_open is
select ITEM_TYPE, ITEM_KEY, PROCESS_ACTIVITY,
ACTIVITY_STATUS, ACTIVITY_RESULT_CODE,
ASSIGNED_USER, NOTIFICATION_ID,
BEGIN_DATE, END_DATE, EXECUTION_TIME,
ERROR_NAME, ERROR_MESSAGE, ERROR_STACK,
OUTBOUND_QUEUE_ID, DUE_DATE
from wf_item_activity_statuses
where item_type = itemtype
and item_key = copy_itemkey
and notification_id is not null
and activity_status = 'NOTIFIED'
order by notification_id;
nid number;
act_fname varchar2(240);
act_ftype varchar2(30);
delay number; -- dont use pls_integer or numeric overflow can occur.
msg_id raw(16):=null;
copy_root_process varchar2(30);
copy_process_version pls_integer;
copy_active_date date;
new_root_process varchar2(30);
new_process_version pls_integer;
new_active_date date;
begin
-- Argument validation
if (itemtype is null)
or (itemkey is null) then
Wf_Core.Token('COPY_ITEMTYPE', itemtype);
Wf_Core.Token('COPY_ITEMKEY', copy_itemkey);
Wf_Core.Token('NEW_ITEMKEY', itemkey);
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- get the forked_from attribute: if it doesnt exist then this cannot be
-- a forked item
begin
copy_itemkey := Wf_Engine.GetItemAttrText(itemtype, itemkey,'#FORKED_FROM');
exception when others then
Wf_Core.Raise('WF_NOFORK');
end;
-- Not allowed in synch mode
if (itemkey = wf_engine.eng_synch)
or (copy_itemkey = wf_engine.eng_synch) then
wf_core.token('OPERATION', 'Wf_Engine.SuspendProcess');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
-- Check item exists and store attributes while cached
if not (Wf_Item.Item_Exist(itemtype, copy_itemkey)) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', copy_itemkey);
Wf_Core.Raise('WFENG_ITEM');
end if;
wf_item.Root_Process(itemtype => itemtype,
itemkey => copy_itemkey,
wflow => copy_root_process,
version =>copy_process_version);
copy_active_date:= wf_item.Active_Date(itemtype => itemtype,
itemkey => copy_itemkey);
--check status of item to copy is active or complete
--
-- Check item exists
if not (Wf_Item.Item_Exist(itemtype, itemkey)) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
end if;
wf_item.Root_Process(itemtype => itemtype,
itemkey => itemkey,
wflow => new_root_process,
version =>new_process_version);
new_active_date:= wf_item.Active_Date(itemtype => itemtype,
itemkey => itemkey);
-- validate both copy and new items have same process and start dates.
-- if not, this isnt a true fork: we are simply starting a process that
-- uses the latest version so use startprocess
if copy_root_process <> new_root_process
or copy_process_version <> new_process_version
or copy_active_date <> new_active_date then
begin
wf_engine.startprocess(itemtype,itemkey);
exception when others then
Wf_Core.raise('WF_CANNOT_FORK');
end;
return;
end if;
-- copy all activities except open notifications
-- leave these to last because routing rule may complete the thread
for act in all_activities loop
msg_id :=null;
nid := null;
if act.notification_id is not null then
--if complete then copy else ignore (we re-execute later)
if act.activity_status = wf_engine.eng_completed then
wf_engine_util.notification_copy (act.notification_id,
act.item_key, itemkey, nid);
end if;
elsif act.activity_status = wf_engine.eng_deferred then
--process defered activity
act_fname:= Wf_Activity.activity_function
(act.item_type,act.item_key,act.process_activity);
act_ftype:= Wf_Activity.activity_function_type
(act.item_type,act.item_key,act.process_activity);
if act_ftype = 'PL/SQL' then
if act.begin_date <= sysdate then
delay :=0;
else
delay := round((act.begin_date - sysdate)*24*60*60 + 0.5);
end if;
wf_queue.enqueue_event
(queuename=>wf_queue.DeferredQueue,
itemtype=> act.item_type,
itemkey=>itemkey,
actid=>act.process_activity,
delay=>delay,
message_handle=>msg_id);
--even if internal, keep message handle for easy access.
--msg_id :=null;
elsif act_ftype = 'EXTERNAL' then
-- this is a callout so write to OUTBOUND queue
-- do not set the correlation here for compatibility reason
wf_queue.enqueue_event
(queuename=>wf_queue.OutboundQueue,
itemtype=> act.item_type,
itemkey=>itemkey,
actid=>act.process_activity,
funcname=>act_fname,
paramlist=>wf_queue.get_param_list(act.item_type,itemkey,
act.process_activity),
message_handle=>msg_id);
else
-- this is a callout so write to OUTBOUND queue for other type
wf_queue.enqueue_event
(queuename=>wf_queue.OutboundQueue,
itemtype=> act.item_type,
itemkey=>itemkey,
actid=>act.process_activity,
correlation=>act_ftype,
funcname=>act_fname,
paramlist=>wf_queue.get_param_list(act.item_type,itemkey,
act.process_activity),
message_handle=>msg_id);
end if;
--else
--must be a function activity
--in this case we dont have to set any values, but just copy
end if;
-- now insert the status
insert into wf_item_activity_statuses
(ITEM_TYPE, ITEM_KEY, PROCESS_ACTIVITY,
ACTIVITY_STATUS, ACTIVITY_RESULT_CODE,
ASSIGNED_USER, NOTIFICATION_ID,
BEGIN_DATE, END_DATE, EXECUTION_TIME,
ERROR_NAME, ERROR_MESSAGE, ERROR_STACK,
OUTBOUND_QUEUE_ID, DUE_DATE)
values(act.item_type, itemkey, act.process_activity,
act.activity_status, act.activity_result_code,
act.assigned_user, nid,
act.begin_date, act.end_date, act.execution_time,
act.error_name, act.error_message, act.error_stack,
msg_id, act.due_date);
end loop; --end defered status
-- repeat for all history
for hist in all_activities_hist loop
nid := null;
if hist.notification_id is not null then
wf_engine_util.notification_copy (hist.notification_id,
hist.item_key, itemkey, nid);
end if;
-- now insert the status
insert into wf_item_activity_statuses_h
(ITEM_TYPE, ITEM_KEY, PROCESS_ACTIVITY,
ACTIVITY_STATUS, ACTIVITY_RESULT_CODE,
ASSIGNED_USER, NOTIFICATION_ID,
BEGIN_DATE, END_DATE, EXECUTION_TIME,
ERROR_NAME, ERROR_MESSAGE, ERROR_STACK,
OUTBOUND_QUEUE_ID, DUE_DATE)
values(hist.item_type, itemkey, hist.process_activity,
hist.activity_status, hist.activity_result_code,
hist.assigned_user, nid,
hist.begin_date, hist.end_date, hist.execution_time,
hist.error_name, hist.error_message, hist.error_stack,
null, hist.due_date);
end loop;
-- update any active functions to notified state
begin
update wf_item_activity_statuses ias
set activity_status = wf_engine.eng_notified
where item_type = itemtype
and item_key = itemkey
and activity_status = 'ACTIVE'
and activity_status = wf_engine.eng_active
and exists (select 'its a function, not subprocess'
from wf_process_activities pa,
wf_activities ac
where pa.activity_name = ac.name
and pa.activity_item_type = ac.item_type
and pa.activity_item_type = ias.item_type
and pa.instance_id = ias.process_activity
and type='FUNCTION');
end;
-- update item attributes on all copied notifications
wf_engine_util.notification_refresh(itemtype,itemkey);
-- as last step, launch all notifications still open
-- keep this as last step because routing rules may allow
-- continuation of thread.
for ntf in ntf_open loop
Wf_Engine_Util.Process_Activity(itemtype, itemkey,
ntf.process_activity,wf_engine.threshold);
end loop;
exception
when others then
Wf_Core.Context('Wf_Engine', 'StartForkProcess');
raise;
end StartForkProcess;
--
--
-- BeginActivity (PUBLIC)
-- Determines if the specified activity may currently be performed on the
-- work item. This is a test that the performer may proactively determine
-- that their intent to perform an activity on an item is, in fact, allowed.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- activity - Completed activity, specified in the form
-- [:]
--
procedure BeginActivity(itemtype in varchar2,
itemkey in varchar2,
activity in varchar2)
is
root varchar2(30); -- The name of the root process for this key
version pls_integer; -- Root process version
actdate date; -- Active date of item
actid pls_integer; -- activity instance id
begin
-- Not allowed in synch mode
if (itemkey = wf_engine.eng_synch) then
wf_core.token('OPERATION', 'Wf_Engine.BeginActivity');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
-- Argument validation
if ((itemtype is null) or (itemkey is null) or (activity is null)) then
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', itemkey);
Wf_Core.Token('ACTIVITY', activity);
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Validate the activity and get the actid.
-- One of these conditions must hold:
-- 1. The item does not exist
-- --> The process is being implicitly started for the first time
-- by completing a START activity.
-- 2. The item and root process exist, and activity is NOTIFIED
-- --> Activity just completed in a running process.
-- Check if item exists and get root process
Wf_Item.Root_Process(itemtype, itemkey, root, version);
if (root is null) then
-- Item does not exist. Must be case (1).
-- Use selector to get the root process
-- Note must do this here, instead of relying on CreateProcess
-- to call the selector, because CreateProcess can't take the
-- start activity as an argument to implicitly choose a root
-- process when no selector function is defined.
root := Wf_Engine_Util.Get_Root_Process(itemtype, itemkey, activity);
if (root is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM_ROOT_SELECTOR');
end if;
else
-- Item exists. Must be case (2).
-- Check that the activity is currently notified.
actid := Wf_Process_Activity.ActiveInstanceId(itemtype, itemkey,
activity, wf_engine.eng_notified);
-- Any other status, or no status at all, is an error.
if (actid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', activity);
Wf_Core.Raise('WFENG_NOT_NOTIFIED');
end if;
end if;
exception
when others then
Wf_Core.Context('Wf_Engine', 'BeginActivity', itemtype, itemkey, activity);
raise;
end BeginActivity;
--
-- CompleteActivity (PUBLIC)
-- Notifies the workflow engine that an activity has been completed for a
-- particular process(item). This procedure can have one or more of the
-- following effects:
-- o Creates a new item. If the completed activity is the start of a process,
-- then a new item can be created by this call. If the completed activity
-- is not the start of a process, it would be an invalid activity error.
-- o Complete an activity with an optional result. This signals the
-- workflow engine that an asynchronous activity has been completed.
-- An optional activity completion result can also be passed.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- activity - Completed activity, specified in the form
-- [:]
-- - An optional result.
--
procedure CompleteActivity(itemtype in varchar2,
itemkey in varchar2,
activity in varchar2,
result in varchar2)
is
root varchar2(30); -- The name of the root process for this key
version pls_integer; -- Root process version
rootid pls_integer; -- Root process actid
actid pls_integer; -- activity instance id
notid pls_integer; -- Notification group id
user varchar2(320); -- Notification assigned user
trig_savepoint exception;
pragma exception_init(trig_savepoint, -04092);
dist_savepoint exception;
pragma exception_init(dist_savepoint, -02074);
--Bug 2607770
l_lock boolean;
begin
-- Argument validation
if ((itemtype is null) or (itemkey is null) or (activity is null)) then
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', itemkey);
Wf_Core.Token('ACTIVITY', activity);
Wf_Core.Raise('WFSQL_ARGS');
end if;
if (WF_CACHE.MetaRefreshed) then
null;
end if;
-- Validate the activity and get the actid.
-- One of these conditions must hold:
-- 1. The item does not exist
-- --> The process is being implicitly started for the first time
-- by completing a START activity.
-- 2. The item and root process exist, and activity is NOTIFIED
-- --> Activity just completed in a running process.
-- Check if item exists and get root process
Wf_Item.Root_Process(itemtype, itemkey, root, version);
if (root is null) then
-- Item does not exist. Must be case (1).
-- Use selector to get the root process
-- Note must do this here, instead of relying on CreateProcess
-- to call the selector, because CreateProcess can't take the
-- start activity as an argument to implicitly choose a root
-- process when no selector function is defined.
root := Wf_Engine_Util.Get_Root_Process(itemtype, itemkey, activity);
if (root is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM_ROOT_SELECTOR');
end if;
-- Create new process
Wf_Engine.CreateProcess(itemtype, itemkey, root);
--Bug 2259039
-- Start the process for this activity.
-- The activity to be completed will be left in NOTIFIED status
-- as a side-effect of this call.
Wf_Engine_Util.Start_Process_Internal(
itemtype => itemtype,
itemkey => itemkey,
runmode => 'ACTIVITY');
-- Get root process for the item
Wf_Item.Root_Process(itemtype, itemkey, root, version);
-- Look for the starting activity in the root process.
actid := Wf_Process_Activity.StartInstanceId(itemtype, root, version,
activity);
-- Create a status row for new activity
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
wf_engine.eng_active, wf_engine.eng_null, sysdate, null, newStatus=>TRUE);
else
--Bug 2607770
--Its only in the else condition that you need to get
--a lock over the existing item to make sure noone else is
--processing it.
-- Item exists. Must be case (2).
-- Check that the activity is currently notified.
actid := Wf_Process_Activity.ActiveInstanceId(itemtype, itemkey,
activity, wf_engine.eng_notified);
-- Any other status, or no status at all, is an error.
if (actid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', activity);
Wf_Core.Raise('WFENG_NOT_NOTIFIED');
end if;
--If acquire lock returns true we will continue
--If it returns false we raise exception to the user
--Any other exception we let the caller decide what to do
if (itemkey <> wf_engine.eng_synch) then
--If its an async process and you cannot acquire a lock
--raise the exception to the user
l_lock := wf_item.acquire_lock(itemtype,itemkey,true);
end if;
-- Get notification id
Wf_Item_Activity_Status.Notification_Status(itemtype, itemkey, actid,
notid, user);
-- Close any open notifications associated with this activity.
-- Note: if notifications are not closed here, they will be cancelled
-- anyway by complete_activity. They are only closed here so that the
-- status is closed and not cancelled when going through the external
-- CompleteActivity interface.
-- Bug2811737 CTILLEY - added update to end_date
if (notid is not null) then
update WF_NOTIFICATIONS WN set
status = 'CLOSED',
end_date = sysdate
where WN.GROUP_ID = CompleteActivity.notid
and WN.STATUS = 'OPEN';
end if;
end if;
-- Finally, complete our lovely new activity.
if (itemkey = wf_engine.eng_synch) then
-- SYNCHMODE: No error trapping in synchmode.
Wf_Engine_Util.Complete_Activity(itemtype, itemkey, actid, result);
else
begin
savepoint wf_savepoint;
Wf_Engine_Util.Complete_Activity(itemtype, itemkey, actid, result);
exception
when trig_savepoint or dist_savepoint then
-- You must be in a restricted environment,
-- no fancy error processing for you!
-- NOTE: Must go ahead and complete the activity instead of
-- deferring directly, because the activity must be marked as
-- complete. Any following activities started by completing
-- this activity will be caught and deferred in another
-- savepoint trap in process_activity.
Wf_Engine_Util.Complete_Activity(itemtype, itemkey, actid, result);
when others then
-- If anything in this process raises an exception:
-- 1. rollback any work in this process thread
-- 2. set this activity to error status
-- 3. execute the error process (if any)
-- 4. clear the error to continue with next activity
rollback to wf_savepoint;
Wf_Core.Context('Wf_Engine', 'CompleteActivity', itemtype, itemkey,
activity, result);
Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, actid,
wf_engine.eng_exception, FALSE);
Wf_Engine_Util.Execute_Error_Process(itemtype, itemkey, actid,
wf_engine.eng_exception);
Wf_Core.Clear;
end;
end if;
exception
when resource_busy then
wf_core.token('TYPE',itemtype);
wf_core.token('KEY',itemkey);
wf_core.raise('WFENG_RESOURCE_BUSY');
when others then
Wf_Core.Context('Wf_Engine', 'CompleteActivity', itemtype, itemkey,
activity, result);
raise;
end CompleteActivity;
--
-- CompleteActivityInternalName (PUBLIC)
-- Identical to CompleteActivity, except that the internal name of
-- completed activity is passed instead of the activity instance label.
-- NOTES:
-- 1. There must be exactly ONE instance of this activity with NOTIFIED
-- status.
-- 2. Using this api to start a new process is not supported.
-- 3. Synchronous processes are not supported in this api.
-- 4. This should only be used if for some reason the instance label is
-- not known. CompleteActivity should be used if the instance
-- label is known.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- activity - Internal name of completed activity, in the format
-- [:]
-- - An optional result.
--
procedure CompleteActivityInternalName(
itemtype in varchar2,
itemkey in varchar2,
activity in varchar2,
result in varchar2)
is
colon pls_integer;
process varchar2(30);
actname varchar2(30);
label varchar2(30);
begin
-- Not allowed in synch mode
if (itemkey = wf_engine.eng_synch) then
wf_core.token('OPERATION', 'Wf_Engine.CompleteActivityInternalName');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
-- Argument validation
if ((itemtype is null) or (itemkey is null) or (activity is null)) then
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', itemkey);
Wf_Core.Token('ACTIVITY', activity);
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Parse activity arg into and components.
colon := instr(activity, ':');
if (colon <> 0) then
-- Activity arg is :
process := substr(activity, 1, colon-1);
actname := substr(activity, colon+1);
else
-- Activity arg is just activity name
process := '';
actname := activity;
end if;
-- Look up activity instance label
begin
select WPA.PROCESS_NAME, WPA.INSTANCE_LABEL
into process, label
from WF_ITEM_ACTIVITY_STATUSES WIAS, WF_PROCESS_ACTIVITIES WPA
where WIAS.ITEM_TYPE = itemtype
and WIAS.ITEM_KEY = itemkey
and WIAS.ACTIVITY_STATUS = wf_engine.eng_notified
and WIAS.PROCESS_ACTIVITY = WPA.INSTANCE_ID
and WPA.ACTIVITY_NAME = actname
and WPA.PROCESS_NAME = nvl(process, WPA.PROCESS_NAME);
exception
when no_data_found then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', activity);
Wf_Core.Raise('WFENG_NOT_NOTIFIED');
end;
-- Complete activity with the correct arguments
Wf_Engine.CompleteActivity(itemtype, itemkey, process||':'||label,
result);
exception
when others then
Wf_Core.Context('Wf_Engine', 'CompleteActivityInternalName',
itemtype, itemkey, activity, result);
raise;
end CompleteActivityInternalName;
--
-- AssignActivity (PUBLIC)
-- Assigns or re-assigns the user who will perform an activity. It may be
-- called before the activity has been enabled(transitioned to). If a user
-- is assigned to an activity that already has an outstanding notification,
-- that notification will be forwarded to the new user.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- activity - Activity to assign, specified in the form
-- [:]
-- performer - User who will perform this activity.
-- reassignType - DELEGATE, TRANSFER or null
-- ntfComments - Comments while reassigning
-- 16-DEC-03 shanjgik bug 2722369 new parameters added
procedure AssignActivity(itemtype in varchar2,
itemkey in varchar2,
activity in varchar2,
performer in varchar2,
reassignType in varchar2,
ntfComments in varchar2) is
root varchar2(30);
version pls_integer;
rootid pls_integer;
actid pls_integer;
status varchar2(8);
notid pls_integer;
user varchar2(320);
acttype varchar2(8);
actdate date;
msg varchar2(30);
msgtype varchar2(8);
expand_role varchar2(1);
begin
-- Not allowed in synch mode
if (itemkey = wf_engine.eng_synch) then
wf_core.token('OPERATION', 'Wf_Engine.AssignActivity');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
-- Argument validation
if ((itemtype is null) or (itemkey is null) or (activity is null) or
(performer is null)) then
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', itemkey);
Wf_Core.Token('ACTIVITY', activity);
Wf_Core.Token('PERFORMER', performer);
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Get the root process for this key, and check that the item
-- has been created.
Wf_Item.Root_Process(itemtype, itemkey, root, version);
if (root is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
end if;
-- Get the root process actid.
rootid := Wf_Process_Activity.RootInstanceId(itemtype, itemkey, root);
if (rootid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', root);
Wf_Core.Raise('WFENG_ITEM_ROOT');
end if;
-- Get the actid and check that this is a valid activity in the
-- root process
actdate := Wf_Item.Active_Date(itemtype, itemkey);
actid := Wf_Process_Activity.FindActivity(rootid, activity, actdate);
if (actid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', activity);
Wf_Core.Raise('WFENG_ITEM_ACTIVITY');
end if;
-- Check if this activity is a notification type of activity
acttype := Wf_Activity.Type(itemtype, activity, actdate);
if (acttype <> wf_engine.eng_notification) then
Wf_Core.Token('NAME', activity);
Wf_Core.Raise('WFENG_NOTIFICATION_NAME');
end if;
-- Check if the activity is active
Wf_Item_Activity_Status.Status(itemtype, itemkey, actid, status);
if (status is null) then
-- Insert one row with the performer
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
wf_engine.eng_waiting, '', null, null, newStatus=>TRUE);
Wf_Item_Activity_Status.Update_Notification(itemtype, itemkey, actid,
'', performer);
elsif (status = wf_engine.eng_waiting) then
Wf_Item_Activity_Status.Update_Notification(itemtype, itemkey, actid,
'', performer);
elsif (status in (wf_engine.eng_notified, wf_engine.eng_error)) then
-- Check this is not a voting activity.
-- Voting activities cannot be re-assigned.
Wf_Activity.Notification_Info(itemtype, itemkey, actid, msg, msgtype,
expand_role);
if (expand_role = 'Y') then
Wf_Core.Token('NAME', activity);
Wf_Core.Raise('WFENG_VOTE_REASSIGN');
end if;
-- Get notification id
Wf_Item_Activity_Status.Notification_Status(itemtype, itemkey, actid,
notid, user);
-- Update the assigned user column in WIAS
Wf_Item_Activity_Status.Update_Notification(itemtype, itemkey, actid,
notid, performer);
if (notid is not null) then
-- 16-DEC-03 shanjgik bug fix 2722369 check for reassignType added
if (reassignType = Wf_Engine.eng_delegate) then
-- delegate the notification
Wf_Notification.Forward(notid, performer, ntfComments);
else -- case reassignType is TRANSFER or null
-- Call Wf_Notification.Transfer(notid, performer) to transfer
-- ownership of the notification to the new performer.
Wf_Notification.Transfer(notid, performer, ntfComments);
end if;
end if;
else
-- Activity must be complete (all other statuses are not valid
-- for a notification).
Wf_Core.Token('ACTIVITY', activity);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM_ACTIVITY_COMPLETE');
end if;
EXCEPTION
when OTHERS then
Wf_Core.Context('Wf_Engine', 'AssignActivity', itemtype, itemkey,
activity, performer);
raise;
end AssignActivity;
--
-- HandleErrorInternal (PRIVATE)
-- Reset the process thread to the given activity and begin execution
-- again from that point. If command is:
-- SKIP - mark the activity complete with given result and continue
-- RETRY - re-execute the activity before continuing
-- IN
-- itemtype - A valid item type.
-- itemkey - The item key of the process.
-- root - Root acitivity label
-- rootid - Root acitivty id
-- activity - Activity label
-- actid - Activity id to reset
-- actdate - Active Date
-- command - SKIP or RETRY.
-- - Activity result for the 'SKIP' command.
--
procedure HandleErrorInternal(itemtype in varchar2,
itemkey in varchar2,
root in varchar2,
rootid in number,
activity in varchar2,
actid in number,
actdate in date,
command in varchar2,
result in varchar2 default '')
is
version pls_integer;
funcname varchar2(240);
resultout varchar2(240);
trig_savepoint exception;
pragma exception_init(trig_savepoint, -04092);
dist_savepoint exception;
pragma exception_init(dist_savepoint, -02074);
--Bug 1166527
event_name VARCHAR2(240);
l_parameterlist wf_parameter_list_t := wf_parameter_list_t();
begin
-- Not allowed in synch mode
-- Validate this before calling this function
-- No Argument validation
-- Validate this before calling this function
-- Make sure item is valid
-- Validate this before calling this function
-- Reset the process starting from the goal activity.
-- This reset behaves similar to loop reset, cancelling activities,
-- moving rows to history, etc. It then resets the activity status
-- to active, AND resets or creates status rows for any parent process
-- to active if necessary.
if (not Wf_Engine_Util.Reset_Tree(itemtype, itemkey, rootid,
actid, actdate)) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', activity);
Wf_Core.Raise('WFENG_ITEM_ACTIVITY');
end if;
if (command = wf_engine.eng_skip) then
-- *** SKIP ***
-- Mark activity complete with given result
begin
savepoint wf_savepoint;
-- execute the activity function with SKIP command (bug 2425229)
funcname := Wf_Activity.Activity_Function(itemtype, itemkey, actid);
Wf_Engine_Util.Function_Call(funcname, itemtype, itemkey, actid, wf_engine.eng_skip,
resultout);
-- Check if skip is allowed on this activity
if (resultout = wf_engine.eng_noskip) then
Wf_Core.Token('LABEL', Wf_Engine.GetActivityLabel(actid));
Wf_Core.Raise('WFENG_NOSKIP');
end if;
Wf_Engine_Util.Complete_Activity(itemtype, itemkey, actid, result, FALSE);
exception
when trig_savepoint or dist_savepoint then
-- You must be in a restricted environment,
-- no fancy error processing for you! Try running directly.
Wf_Engine_Util.Complete_Activity(itemtype, itemkey, actid,
result, FALSE);
when others then
if (Wf_Core.Error_Name = 'WFENG_NOSKIP') then
-- No processing. Raise to the caller that the activity cannot be skipped.
raise;
else
-- If anything in this process raises an exception:
-- 1. rollback any work in this process thread
-- 2. set this activity to error status
-- 3. execute the error process (if any)
-- 4. clear the error to continue with next activity
rollback to wf_savepoint;
Wf_Core.Context('Wf_Engine', 'HandleErrorInternal', itemtype, itemkey,
activity, command, result);
Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, actid,
wf_engine.eng_exception, FALSE);
Wf_Engine_Util.Execute_Error_Process(itemtype, itemkey, actid,
wf_engine.eng_exception);
Wf_Core.Clear;
end if;
end;
--We will raise the skip event here .
event_name := 'oracle.apps.wf.engine.skip';
else
-- *** RETRY ***
if (actid = rootid) then
-- Restart root process from beginnning
Wf_Engine.StartProcess(itemtype, itemkey);
else
-- Start at given activity
begin
savepoint wf_savepoint;
Wf_Engine_Util.Process_Activity(itemtype, itemkey, actid,
Wf_Engine.Threshold, TRUE);
exception
when trig_savepoint or dist_savepoint then
-- You must be in a restricted environment,
-- no fancy error processing for you!
-- Immediately defer activity to background engine.
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey,
actid, wf_engine.eng_deferred, wf_engine.eng_null,
SYSDATE, null);
when others then
-- If anything in this process raises an exception:
-- 1. rollback any work in this process thread
-- 2. set this activity to error status
-- 3. execute the error process (if any)
-- 4. clear the error to continue with next activity
rollback to wf_savepoint;
Wf_Core.Context('Wf_Engine', 'HandleErrorInternal',itemtype,itemkey,
activity, command, result);
Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, actid,
wf_engine.eng_exception, FALSE);
Wf_Engine_Util.Execute_Error_Process(itemtype, itemkey, actid,
wf_engine.eng_exception);
Wf_Core.Clear;
end;
end if;
event_name := 'oracle.apps.wf.engine.retry';
end if;
--Pass the signature of the handle error API in the
--parameter list
wf_event.AddParameterToList('ITMETYPE', itemtype, l_parameterlist);
wf_event.AddParameterToList('ITEMKEY', itemkey, l_parameterlist);
wf_event.AddParameterToList('ACTIVITY', activity, l_parameterlist);
if (result is NOT NULL) then
wf_event.AddParameterToList('RESULT', result, l_parameterlist);
end if;
-- Raise the event
wf_event.Raise(p_event_name => event_name,
p_event_key => itemkey,
p_parameters => l_parameterlist);
exception
when others then
Wf_Core.Context('Wf_Engine', 'HandleErrorInternal', itemtype, itemkey,
activity, command, result);
raise;
end HandleErrorInternal;
--
-- HandleError (PUBLIC)
-- Reset the process thread to the given activity and begin execution
-- again from that point. If command is:
-- SKIP - mark the activity complete with given result and continue
-- RETRY - re-execute the activity before continuing
-- IN
-- itemtype - A valid item type.
-- itemkey - The item key of the process.
-- activity - Activity to reset, specified in the form
-- [:]
-- command - SKIP or RETRY.
-- - Activity result for the 'SKIP' command.
--
procedure HandleError(itemtype in varchar2,
itemkey in varchar2,
activity in varchar2,
command in varchar2,
result in varchar2)
is
root varchar2(30);
version pls_integer;
rootid pls_integer;
actid pls_integer;
actdate date;
trig_savepoint exception;
pragma exception_init(trig_savepoint, -04092);
dist_savepoint exception;
pragma exception_init(dist_savepoint, -02074);
begin
-- Not allowed in synch mode
if (itemkey = wf_engine.eng_synch) then
wf_core.token('OPERATION', 'Wf_Engine.HandleError');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
-- Argument validation
if ((itemtype is null) or (itemkey is null) or (activity is null) or
(upper(command) not in (wf_engine.eng_skip, wf_engine.eng_retry))) then
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', itemkey);
Wf_Core.Token('ACTIVITY', activity);
Wf_Core.Token('COMMAND', command);
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- If we are in a different Fwk session, need to clear Workflow PLSQL state
if (not Wfa_Sec.CheckSession) then
Wf_Global.Init;
end if;
-- Make sure item is valid
Wf_Item.Root_Process(itemtype, itemkey, root, version);
if (root is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
end if;
rootid := Wf_Process_Activity.RootInstanceId(itemtype, itemkey, root);
if (rootid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', root);
Wf_Core.Raise('WFENG_ITEM_ROOT');
end if;
-- Look for the activity instance for this item
actdate := Wf_Item.Active_Date(itemtype, itemkey);
actid := Wf_Process_Activity.FindActivity(rootid, activity, actdate);
if (actid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('PROCESS', root);
Wf_Core.Token('NAME', activity);
Wf_Core.Raise('WFENG_ACTIVITY_EXIST');
end if;
if (WF_CACHE.MetaRefreshed) then
null;
end if;
-- Store the info for Audit
Wf_Item_Activity_Status.Audit(itemtype, itemkey, actid, upper(command), null);
-- Call the internal function to do the real job
HandleErrorInternal(itemtype, itemkey, root, rootid, activity, actid,
actdate, upper(command), result);
exception
when others then
Wf_Core.Context('Wf_Engine', 'HandleError', itemtype, itemkey, activity,
command, result);
raise;
end HandleError;
--
-- HandleErrorAll (PUBLIC)
-- Reset the process thread to the given item type and/or item key and/or
-- activity.
-- IN
-- itemtype - A valid item type.
-- itemkey - The item key of the process.
-- activity - Activity to reset, specified in the form
-- [:]
-- command - SKIP or RETRY.
-- - Activity result for the "SKIP" command.
-- docommit - True if you want a commit for every n iterations.
-- n is defined as wf_engine.commit_frequency
--
procedure HandleErrorAll(itemtype in varchar2,
itemkey in varchar2,
activity in varchar2,
command in varchar2,
result in varchar2,
docommit in boolean)
is
root varchar2(30);
version number;
rootid number;
actdate date;
c_item_key varchar2(240);
c_activity varchar2(30);
c_actid number;
cursor actc(x_itemtype varchar2, x_itemkey varchar2, x_activity varchar2) is
select ias.ITEM_KEY,
pa.INSTANCE_LABEL activity,
pa.INSTANCE_ID actid
from WF_ITEM_ACTIVITY_STATUSES ias,
WF_PROCESS_ACTIVITIES pa
where ias.ITEM_TYPE = x_itemtype
and (x_itemkey is null or ias.ITEM_KEY = x_itemkey)
and (x_activity is null or pa.INSTANCE_LABEL = x_activity)
and ias.PROCESS_ACTIVITY = pa.INSTANCE_ID
and ias.ACTIVITY_STATUS = 'ERROR';
begin
--Check arguments.
if (itemtype is null) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
if (WF_CACHE.MetaRefreshed) then
null;
end if;
-- outer loop
<>
loop
open actc(itemtype, itemkey, activity);
-- inner loop
<>
loop
fetch actc into c_item_key, c_activity, c_actid;
if (actc%notfound) then
exit outer_handle;
end if;
-- Not allowed in synch mode
if (c_item_key = wf_engine.eng_synch) then
wf_core.token('OPERATION', 'Wf_Engine.HandleErrorAll');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
-- Argument validation
if ((itemtype is null) or (c_item_key is null) or (c_activity is null) or
(upper(command) not in (wf_engine.eng_skip, wf_engine.eng_retry)))
then
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', c_item_key);
Wf_Core.Token('ACTIVITY', c_activity);
Wf_Core.Token('COMMAND', command);
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Make sure item is valid
Wf_Item.Root_Process(itemtype, c_item_key, root, version);
if (root is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', c_item_key);
Wf_Core.Raise('WFENG_ITEM');
end if;
rootid := Wf_Process_Activity.RootInstanceId(itemtype, c_item_key, root);
if (rootid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', c_item_key);
Wf_Core.Token('NAME', root);
Wf_Core.Raise('WFENG_ITEM_ROOT');
end if;
-- Look for the activity instance for this item
actdate := Wf_Item.Active_Date(itemtype, c_item_key);
-- Call the internal function to do the real job
HandleErrorInternal(itemtype, c_item_key, root, rootid, c_activity,
c_actid, actdate, upper(command), result);
exit handle_loop when
(docommit and (actc%rowcount = wf_engine.commit_frequency));
end loop handle_loop;
if (actc%ISOPEN) then
close actc;
end if;
if (docommit) then
commit;
Fnd_Concurrent.Set_Preferred_RBS;
end if;
end loop outer_handle;
if (docommit) then
commit;
Fnd_Concurrent.Set_Preferred_RBS;
end if;
if (actc%ISOPEN) then
close actc;
end if;
exception
when others then
Wf_Core.Context('Wf_Engine', 'HandleErrorAll', itemtype, itemkey);
raise;
end HandleErrorAll;
--
-- ItemStatus (Public)
-- This is a public cover for WF_ITEM_ACTIVITY_STATUS.ROOT_STATUS
-- Returns the status and result for the root process of this item.
-- If the item does not exist an exception will be raised.
-- IN
-- itemtype - Activity item type.
-- itemkey - The item key.
-- OUT
-- status - Activity status for root process of this item
-- result - Result code for root process of this item
--
procedure ItemStatus(itemtype in varchar2,
itemkey in varchar2,
status out NOCOPY varchar2,
result out NOCOPY varchar2) is
begin
--Check arguments.
if ((itemtype is null) or
(itemkey is null)) then
Wf_Core.Token('ITEMTYPE', nvl(itemtype, 'NULL'));
Wf_Core.Token('ITEMKEY', nvl(itemkey, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
wf_item_activity_status.root_status(itemtype,itemkey,status,result);
exception
when others then
Wf_Core.Context('Wf_Engine', 'ItemStatus', itemtype, itemkey);
raise;
end ItemStatus;
-- API to reterive more granular information from the
-- item
-- If the item is active and
-- If there is an errored activity then status is set to ERROR
-- the errname , errmsg and errstack info is given
-- activity , error stack etc are provided
-- If the first activity is deferred then the actid of the same
-- is provided and the item status is given as 'DEFERRED'
-- If an activity is in notified status then we get the
-- actid of the same.
procedure ItemInfo(itemtype in varchar2,
itemkey in varchar2,
status out NOCOPY varchar2,
result out NOCOPY varchar2,
actid out NOCOPY number,
errname out NOCOPY varchar2,
errmsg out NOCOPY varchar2,
errstack out NOCOPY varchar2)
is
l_status varchar2(8);
l_result varchar2(30);
l_instance_id number;
--Order all activities for this itemtype ,key
--giving priority to ERROR , NOTIFIED , DEFERRED (--> in that order)
--and execution time
/*
Lets do a single select for rownum < 1 this
should suffice
cursor act_curs (p_itemtype varchar2, p_itemkey varchar2) is
select pa.instance_label,pa.instance_id
ias.activity_status,
ias.activity_result_code ,
ias.assigned_user,
ias.notification_id NID,
ntf.status,
ias.performed_by
from wf_item_activity_statuses ias,
wf_process_activities pa,
wf_activities ac,
wf_activities ap,
wf_items i,
wf_notifications ntf
where ias.item_type = p_itemtype
and ias.item_key = p_itemkey
and ias.activity_status = wf_engine.eng_completed
and ias.process_activity = pa.instance_id
and pa.activity_name = ac.name
and pa.activity_item_type = ac.item_type
and pa.process_name = ap.name
and pa.process_item_type = ap.item_type
and pa.process_version = ap.version
and i.item_type = '&item_type'
and i.item_key = ias.item_key
and i.begin_date >= ac.begin_date
and i.begin_date < nvl(ac.end_date, i.begin_date+1)
and ntf.notification_id(+) = ias.notification_id
order by decode(ias.activity_status,'ERROR',1,'NOTIFIED',2,'DEFERRED',3,'SUSPEND',4,'WAITING',5,'ACTIVE',6,'COMPLETE',7) asc , ias.execution_time desc
*/
begin
--Get the item status
--Use the API above for the same
wf_engine.ItemStatus(itemtype ,itemkey ,l_status,l_result);
--Now check the status if root has completed
--we do not want to go further lower
--Else if the root is still active , lets find
--where the execution is stuck at.
if l_status= 'ACTIVE' then
--Get last executed activities result and status
select process_activity,
activity_status,
activity_result_code
into l_instance_id,
l_status,
l_result
from
(
select process_activity,
activity_status,
activity_result_code
from wf_item_activity_statuses
where item_type = itemtype
and item_key = itemkey
and activity_status <> wf_engine.eng_completed
order by decode(activity_status, 'ERROR',1, 'NOTIFIED',2, 'DEFERRED',3,
'SUSPEND',4, 'WAITING',5, 'ACTIVE',6, 7) asc,
begin_date desc, execution_time desc
)
where rownum < 2;
--Now lets start getting all details out of the last activity
if l_status = 'ERROR' then
--Populate the error stack
wf_item_activity_status.Error_Info(itemtype,itemkey,l_instance_id,errname,errmsg,errstack);
end if;
status := l_status;
result := l_result;
actid := l_instance_id;
--U can get it using the actid using Notification_Status API
--nid := l_notification_id;
else
--If the root is not active return whatever is its status
--and result
status := l_status ;
result := l_result ;
end if;
exception
when others then
Wf_Core.Context('Wf_Engine', 'ItemInfo', itemtype, itemkey);
raise;
end ItemInfo;
--
-- Activity_Exist_In_Process (Public)
-- Check if an activity exist in a process
-- ### OBSOLETE - Use FindActivity instead ###
-- ### DO NOT REMOVE, refer to bug 1869241 ###
-- IN
-- p_item_type
-- p_item_key
-- p_activity_item_type
-- p_activity_name
-- RET
-- TRUE if activity exist, FALSE otherwise
--
function Activity_Exist_In_Process (
p_item_type in varchar2,
p_item_key in varchar2,
p_activity_item_type in varchar2,
p_activity_name in varchar2)
return boolean
is
rootactivity varchar2(30);
active_date date;
begin
begin
select ROOT_ACTIVITY, BEGIN_DATE
into rootactivity, active_date
from WF_ITEMS
where ITEM_TYPE = p_item_type
and ITEM_KEY = p_item_key;
exception
-- if itemtype/itemkey combination not exists, treats it as not exists
when NO_DATA_FOUND then
return FALSE;
when OTHERS then
raise;
end;
return(Wf_Engine.Activity_Exist(
p_process_item_type=>p_item_type,
p_process_name=>rootactivity,
p_activity_item_type=>p_activity_item_type,
p_activity_name=>p_activity_name,
active_date=>active_date));
exception
when others then
Wf_Core.Context('Wf_Engine', 'Activity_Exist_In_Process',
p_item_type, p_item_key,
nvl(p_activity_item_type, p_item_type),
p_activity_name);
raise;
end Activity_Exist_In_Process;
--
-- Activity_Exist
-- Check if an activity exist in a process
-- ### OBSOLETE - Use FindActivity instead. ###
-- ### DO NOT REMOVE, refer to bug 1869241 ###
-- IN
-- p_process_item_type
-- p_process_name
-- p_activity_item_type
-- p_anctivity_name
-- active_date
-- iteration - maximum 8 level deep (0-7)
-- RET
-- TRUE if activity exist, FALSE otherwise
--
function Activity_Exist (
p_process_item_type in varchar2,
p_process_name in varchar2,
p_activity_item_type in varchar2 default null,
p_activity_name in varchar2,
active_date in date default sysdate,
iteration in number default 0)
return boolean
is
m_version number;
n number;
cursor actcur(ver number) is
select WPA.ACTIVITY_ITEM_TYPE, WPA.ACTIVITY_NAME
from WF_PROCESS_ACTIVITIES WPA,
WF_ACTIVITIES WA
where WPA.PROCESS_ITEM_TYPE = p_process_item_type
and WPA.PROCESS_NAME = p_process_name
and WPA.PROCESS_VERSION = ver
and WPA.ACTIVITY_ITEM_TYPE = WA.ITEM_TYPE
and WPA.ACTIVITY_NAME = WA.NAME
and WA.TYPE = 'PROCESS'
and active_date >= WA.BEGIN_DATE
and active_date < nvl(WA.END_DATE, active_date+1);
begin
-- first check the iteration to avoid infinite loop
if (iteration > 7) then
return FALSE;
end if;
-- then get the active version
begin
select VERSION into m_version
from WF_ACTIVITIES
where ITEM_TYPE = p_process_item_type
and NAME = p_process_name
and active_date >= BEGIN_DATE
and active_date < nvl(END_DATE, active_date + 1);
exception
-- no active version exist
when NO_DATA_FOUND then
return FALSE;
when OTHERS then
raise;
end;
-- then check to see if such activity exist
select count(1) into n
from WF_PROCESS_ACTIVITIES
where PROCESS_ITEM_TYPE = p_process_item_type
and PROCESS_NAME = p_process_name
and PROCESS_VERSION = m_version
and ACTIVITY_ITEM_TYPE = nvl(p_activity_item_type, p_process_item_type)
and ACTIVITY_NAME = p_activity_name;
if (n = 0) then
-- recursively check subprocesses
for actr in actcur(m_version) loop
if (Wf_Engine.Activity_Exist(
actr.activity_item_type,
actr.activity_name,
nvl(p_activity_item_type, p_process_item_type),
p_activity_name,
active_date,
iteration+1)
) then
return TRUE;
end if;
end loop;
return FALSE;
else
return TRUE;
end if;
exception
when OTHERS then
Wf_Core.Context('Wf_Engine', 'Activity_Exist',
p_process_item_type, p_process_name,
nvl(p_activity_item_type, p_process_item_type),
p_activity_name);
raise;
end Activity_Exist;
--
-- Event
-- Signal event to workflow process
-- IN
-- itemtype - Item type of process
-- itemkey - Item key of process
-- process_name - Process to start (only if process not already running)
-- event_message - Event message payload
--
procedure Event(
itemtype in varchar2,
itemkey in varchar2,
process_name in varchar2,
event_message in wf_event_t)
is
event_name varchar2(240);
actdate date; -- Active date of item
root varchar2(30); -- Root process name
version pls_integer; -- Root process version
rootid pls_integer; -- Root process instance id
aname varchar2(30); -- Item attr name
avalue varchar2(2000); -- Item attr value
plist wf_parameter_list_t; -- Event message parameter list
-- Bug 2255002
parent_itemtype varchar2(8); -- parent item type
parent_itemkey varchar2(240); -- parent item key
-- Blocked activities waiting for event (if existing process)
cursor evtacts is
SELECT WIAS.PROCESS_ACTIVITY actid
FROM WF_ITEM_ACTIVITY_STATUSES WIAS, WF_PROCESS_ACTIVITIES WPA,
WF_ACTIVITIES WA
WHERE WIAS.ITEM_TYPE = event.itemtype
AND WIAS.ITEM_KEY = event.itemkey
AND WIAS.ACTIVITY_STATUS = 'NOTIFIED'
AND WIAS.PROCESS_ACTIVITY = WPA.INSTANCE_ID
AND WPA.ACTIVITY_ITEM_TYPE = WA.ITEM_TYPE
AND WPA.ACTIVITY_NAME = WA.NAME
AND actdate >= WA.BEGIN_DATE
AND actdate < NVL(WA.END_DATE, actdate+1)
AND WA.TYPE = 'EVENT'
AND WA.DIRECTION = 'RECEIVE'
AND (WA.EVENT_NAME is null
OR WA.EVENT_NAME in
(SELECT WE.NAME -- Single events
FROM WF_EVENTS WE
WHERE WE.TYPE = 'EVENT'
AND WE.NAME = event.event_name
UNION ALL
SELECT GRP.NAME -- Groups containing event
FROM WF_EVENTS GRP, WF_EVENT_GROUPS WEG, WF_EVENTS MBR
WHERE GRP.TYPE = 'GROUP'
AND GRP.GUID = WEG.GROUP_GUID
AND WEG.MEMBER_GUID = MBR.GUID
AND MBR.NAME = event.event_name));
actarr InstanceArrayTyp; -- Event activities to execute
i pls_integer := 0; -- Loop counter
l_lock boolean;
begin
-- Check args
if ((itemtype is null) or
(itemkey is null) or
(event_message is null)) then
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', itemkey);
Wf_Core.Token('EVENT_MESSAGE', '');
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Not allowed in synch mode
if (itemkey = wf_engine.eng_synch) then
Wf_Core.Token('OPERATION', 'Wf_Engine.Set_Item_Parent');
Wf_Core.Raise('WFENG_SYNCH_DISABLED');
end if;
-- Retrieve event name from message
event_name := event_message.GetEventName;
if (event_name is null) then
Wf_Core.Token('EVENT_MESSAGE.EVENT_NAME', '');
Wf_Core.Raise('WFSQL_ARGS');
end if;
if (WF_CACHE.MetaRefreshed) then
null;
end if;
-- Check if item exists
if (Wf_Item.Item_Exist(itemtype, itemkey)) then
-- Process is already running.
--Acquire lock here so that no other session
--will work on it.
--Acquire lock here by opening the cursor
l_lock := wf_item.acquire_lock(itemtype, itemkey,true);
-- Find all activities waiting for this event.
actdate := WF_Item.Active_Date(itemtype, itemkey);
for act in evtacts loop
actarr(i) := act.actid;
i := i + 1;
end loop;
actarr(i) := '';
else
-- Process not running yet, create it.
-- If process_name is null then will use selector function.
Wf_Engine.CreateProcess(itemtype, itemkey, process_name);
actdate := WF_Item.Active_Date(itemtype, itemkey);
-- Bug 2259039
-- Start the new process
Wf_Engine_Util.Start_Process_Internal(
itemtype => itemtype,
itemkey => itemkey,
runmode => 'EVENT');
--Select the activities waiting to receive this event
actdate := WF_Item.Active_Date(itemtype, itemkey);
for act in evtacts loop
actarr(i) := act.actid;
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, act.actid,
wf_engine.eng_notified, wf_engine.eng_null, sysdate, null);
i := i + 1;
end loop;
actarr(i) := '';
end if;
-- Check at least one matching event activity found
if (i = 0) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('EVENT', event_name);
Wf_Core.Raise('WFENG_EVENT_NOTFOUND');
end if;
-- Set item attributes for all parameters contained in the event
-- message body.
-- NOTE: Must be done here AFTER the process has been created
-- and BEFORE any activities are executed.
plist := event_message.GetParameterList;
if (plist is not null) then
for i in plist.first .. plist.last loop
aname := plist(i).GetName;
avalue := plist(i).GetValue;
begin
if aname = '#CONTEXT' then
-- Bug 2255002 - if the parent item type and parent item key
-- already exist do nothing
SELECT parent_item_type, parent_item_key
INTO parent_itemtype, parent_itemkey
FROM wf_items
WHERE item_type = itemtype
AND item_key = itemkey;
if (parent_itemtype is null and parent_itemkey is null ) then
Wf_Engine.SetItemParent(itemtype => itemtype,
itemkey => itemkey,
parent_itemtype =>
substr(avalue,1,
instr(avalue,':')-1),
parent_itemkey =>
substr(avalue,
instr(avalue,':')+1),
parent_context => null);
end if;
elsif aname = '#OWNER_ROLE' then
--Bug 2388634
--This is for the applications to set their item owner
--by including a #OWNER_ROLE parameter for the event
wf_engine.SetItemowner(itemtype,itemkey,avalue);
else
-- event item attributes may use canonical masks.
Wf_Engine.SetEventItemAttr(itemtype, itemkey, aname, avalue);
end if;
exception
when others then
if (wf_core.error_name = 'WFENG_ITEM_ATTR') then
-- If attr doesn't exist create runtime itemattr
Wf_Core.Clear;
Wf_Engine.AddItemAttr(itemtype, itemkey, aname, avalue);
else
raise; -- All other errors are raised up.
end if;
end;
end loop;
end if;
-- Complete matching event activities
i := 0;
while (actarr(i) is not null) loop
begin
savepoint wf_savepoint;
-- Save event data to itemattrs requested by this activity.
-- #EVENTNAME
aname := Wf_Engine.GetActivityAttrText(itemtype, itemkey, actarr(i),
wf_engine.eng_eventname);
if (aname is not null) then
Wf_Engine.SetItemAttrText(itemtype, itemkey, aname, event_name);
end if;
-- #EVENTKEY
aname := Wf_Engine.GetActivityAttrText(itemtype, itemkey, actarr(i),
wf_engine.eng_eventkey);
if (aname is not null) then
Wf_Engine.SetItemAttrText(itemtype, itemkey, aname,
event_message.GetEventKey);
end if;
-- #EVENTMESSAGE
aname := Wf_Engine.GetActivityAttrText(itemtype, itemkey, actarr(i),
wf_engine.eng_eventmessage);
if (aname is not null) then
Wf_Engine.SetItemAttrEvent(itemtype, itemkey, aname, event_message);
end if;
-- Execute our lovely event activity (result is always null).
Wf_Engine_Util.Complete_Activity(itemtype, itemkey, actarr(i),
wf_engine.eng_null);
exception
when others then
-- If anything in this process raises an exception:
-- 1. rollback any work in this process thread
-- 2. set this activity to error status
-- 3. execute the error process (if any)
-- 4. clear the error to continue with next activity
rollback to wf_savepoint;
Wf_Core.Context('Wf_Engine', 'Event', itemtype, itemkey,
process_name, event_name);
Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, actarr(i),
wf_engine.eng_exception, FALSE);
Wf_Engine_Util.Execute_Error_Process(itemtype, itemkey, actarr(i),
wf_engine.eng_exception);
Wf_Core.Clear;
end;
i := i + 1;
end loop;
exception
when others then
Wf_Core.Context('Wf_Engine', 'Event', itemtype, itemkey,
process_name, event_name);
raise;
end Event;
--
-- Event2
-- Signal event to workflow process
-- IN
-- event_message - Event message payload
--
procedure Event2(
event_message in wf_event_t)
is
event_name varchar2(240);
actdate date; -- Active date of item
root varchar2(30); -- Root process name
version pls_integer; -- Root process version
rootid pls_integer; -- Root process instance id
aname varchar2(30); -- Item attr name
avalue varchar2(2000); -- Item attr value
plist wf_parameter_list_t; -- Event message parameter list
businesskey varchar2(240);
-- Blocked activities waiting for event (if existing process)
cursor evtacts is
SELECT WIAS.ITEM_TYPE, WIAS.ITEM_KEY, WIAS.PROCESS_ACTIVITY actid
FROM WF_ITEM_ACTIVITY_STATUSES WIAS, WF_PROCESS_ACTIVITIES WPA,
WF_ACTIVITIES WA , WF_ITEMS WI
WHERE WIAS.ACTIVITY_STATUS = 'NOTIFIED'
AND WIAS.PROCESS_ACTIVITY = WPA.INSTANCE_ID
AND WPA.ACTIVITY_ITEM_TYPE = WA.ITEM_TYPE
AND WPA.ACTIVITY_NAME = WA.NAME
AND WA.TYPE = 'EVENT'
AND WA.DIRECTION = 'RECEIVE'
AND (WA.EVENT_NAME is null
OR WA.EVENT_NAME in
(SELECT WE.NAME -- Single events
FROM WF_EVENTS WE
WHERE WE.TYPE = 'EVENT'
AND WE.NAME = event2.event_name
UNION ALL
SELECT GRP.NAME -- Groups containing event
FROM WF_EVENTS GRP, WF_EVENT_GROUPS WEG, WF_EVENTS MBR
WHERE GRP.TYPE = 'GROUP'
AND GRP.GUID = WEG.GROUP_GUID
AND WEG.MEMBER_GUID = MBR.GUID
AND MBR.NAME = event2.event_name))
AND EXISTS
( SELECT 1 FROM WF_ACTIVITY_ATTR_VALUES WAAV,
WF_ITEM_ATTRIBUTE_VALUES WIAV
WHERE WAAV.PROCESS_ACTIVITY_ID = WIAS.PROCESS_ACTIVITY
AND WAAV.NAME = '#BUSINESS_KEY'
AND WAAV.VALUE_TYPE = 'ITEMATTR'
AND WIAV.ITEM_TYPE = WIAS.ITEM_TYPE
AND WIAV.ITEM_KEY = WIAS.ITEM_KEY
AND WAAV.TEXT_VALUE = WIAV.NAME
AND WIAV.TEXT_VALUE = event2.businesskey)
AND WI.item_type = WIAS.ITEM_TYPE
AND WI.item_key = WIAS.ITEM_KEY
for update of WI.ITEM_TYPE,WI.item_key NOWAIT;
ectacts_rec evtacts%ROWTYPE;
litemtype varchar2(8);
litemkey varchar2(240);
lactid number;
i pls_integer := 0; -- Loop counter
begin
-- Check args
if ((event_message is null)) then
Wf_Core.Token('EVENT_MESSAGE', '');
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Retrieve event name from message
event_name := event_message.GetEventName;
businesskey := event_message.GetEventKey;
if (event_name is null) then
Wf_Core.Token('EVENT_MESSAGE.EVENT_NAME', '');
Wf_Core.Raise('WFSQL_ARGS');
end if;
--Here before opening the cursor we will set the savepoint
--This is so that we do not have to depend on the cursor behaviour itself
--but once the cursor fails to acquire lock we expliciltly rollback
--But having the for update statement in the cursor eliminates the need
--for explicitly locking the workitems .
savepoint wf_savepoint_event2;
-- Find all activities waiting for this event.
for evtacts_rec in evtacts loop
-- Set item attributes for all parameters contained in the event
-- message body.
-- NOTE: Must be done here AFTER the process has been created
-- and BEFORE any activities are executed.
plist := event_message.GetParameterList;
if ((plist is not null) and (plist.count > 0)) then
for i in plist.first .. plist.last loop
aname := plist(i).GetName;
avalue := plist(i).GetValue;
begin
if aname = '#CONTEXT' then
Wf_Engine.SetItemParent(itemtype => evtacts_rec.item_type,
itemkey => evtacts_rec.item_key,
parent_itemtype =>substr(avalue,1,instr(avalue,':')-1),
parent_itemkey =>substr(avalue,instr(avalue,':')+1),
parent_context => null);
else
-- event item attributes may use canonical masks.
Wf_Engine.SetEventItemAttr(evtacts_rec.item_type,
evtacts_rec.item_key, aname, avalue);
end if;
exception
when others then
if (wf_core.error_name = 'WFENG_ITEM_ATTR') then
-- If attr doesn't exist create runtime itemattr
Wf_Core.Clear;
Wf_Engine.AddItemAttr(evtacts_rec.item_type,
evtacts_rec.item_key,
aname, avalue);
else
raise; -- All other errors are raised up.
end if;
end;
end loop;
end if;
begin
savepoint wf_savepoint;
-- Save event data to itemattrs requested by this activity.
-- #EVENTNAME
aname := Wf_Engine.GetActivityAttrText(evtacts_rec.item_type,
evtacts_rec.item_key,
evtacts_rec.actid,
wf_engine.eng_eventname);
if (aname is not null) then
Wf_Engine.SetItemAttrText(evtacts_rec.item_type,
evtacts_rec.item_key,
aname,
event_name);
end if;
-- #EVENTKEY
aname := Wf_Engine.GetActivityAttrText(evtacts_rec.item_type,
evtacts_rec.item_key,
evtacts_rec.actid,
wf_engine.eng_eventkey);
if (aname is not null) then
Wf_Engine.SetItemAttrText(evtacts_rec.item_type,
evtacts_rec.item_key, aname,
event_message.GetEventKey);
end if;
-- #EVENTMESSAGE
aname := Wf_Engine.GetActivityAttrText(evtacts_rec.item_type,
evtacts_rec.item_key,
evtacts_rec.actid,
wf_engine.eng_eventmessage);
if (aname is not null) then
Wf_Engine.SetItemAttrEvent(evtacts_rec.item_type,
evtacts_rec.item_key,
aname,
event_message);
end if;
-- Execute our lovely event activity (result is always null).
Wf_Engine_Util.Complete_Activity(evtacts_rec.item_type,
evtacts_rec.item_key, evtacts_rec.actid,
wf_engine.eng_null);
exception
when others then
-- If anything in this process raises an exception:
-- 1. rollback any work in this process thread
-- 2. set this activity to error status
-- 3. execute the error process (if any)
-- 4. clear the error to continue with next activity
rollback to wf_savepoint;
Wf_Core.Context('Wf_Engine', 'Event2', evtacts_rec.item_type,
evtacts_rec.item_key, event_name);
Wf_Item_Activity_Status.Set_Error(evtacts_rec.item_type,
evtacts_rec.item_key,
evtacts_rec.actid,
wf_engine.eng_exception, FALSE);
Wf_Engine_Util.Execute_Error_Process(evtacts_rec.item_type,
evtacts_rec.item_key,
evtacts_rec.actid,
wf_engine.eng_exception);
Wf_Core.Clear;
end;
i := i + 1;
end loop;
-- Check at least one matching event activity found
if (i = 0) then
Wf_Core.Token('EVENT2', event_name);
Wf_Core.Raise('WFENG_EVENT_NOTFOUND');
end if;
exception
when resource_busy then
--Rollback to ensure that we aren't locking anything here
rollback to wf_savepoint_event2;
raise;
when others then
Wf_Core.Context('Wf_Engine', 'Event2', businesskey, event_name);
raise;
end Event2;
--
-- AddToItemAttrNumber
-- Increments (or decrements) an numeric item attribute and returns the
-- new value. If the item attribute does not exist, it returns null.
-- IN
-- p_itemtype - process item type
-- p_itemkey - process item key
-- p_aname - Item Attribute Name
-- p_name - attribute name
-- p_addend - Numeric value to be added to the item attribute. If p_addend
-- is set to null, it will set the ItemAttrNumber to 0.
--
-- RETURNS
-- Attribute value (NUMBER) or NULL if attribute does not exist.
--
function AddToItemAttrNumber(
p_itemtype in varchar2,
p_itemkey in varchar2,
p_aname in varchar2,
p_addend in number)
return number is
iStatus PLS_INTEGER;
wiavIND NUMBER;
l_avalue NUMBER;
begin
-- Check Arguments
if ((p_itemtype is null) or
(p_itemkey is null) or
(p_aname is null)) then
Wf_Core.Token('P_ITEMTYPE', nvl(p_itemtype, 'NULL'));
Wf_Core.Token('P_ITEMKEY', nvl(p_itemkey, 'NULL'));
Wf_Core.Token('P_ANAME', nvl(p_aname, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
if (p_itemkey = wf_engine.eng_synch) then
WF_CACHE.GetItemAttrValue(p_itemtype, p_itemKey, p_aname, iStatus,
wiavIND);
if (iStatus <> WF_CACHE.task_SUCCESS) then
return null;
else
if (p_addend is NOT null) then
WF_CACHE.ItemAttrValues(wiavIND).NUMBER_VALUE :=
(WF_CACHE.ItemAttrValues(wiavIND).NUMBER_VALUE + p_addend);
else
WF_CACHE.ItemAttrValues(wiavIND).NUMBER_VALUE := 0;
end if;
return WF_CACHE.ItemAttrValues(wiavIND).NUMBER_VALUE;
end if;
else
if (p_addend is NOT null) then
update WF_ITEM_ATTRIBUTE_VALUES wiav
set wiav.NUMBER_VALUE = (wiav.NUMBER_VALUE+p_addend)
where wiav.ITEM_TYPE = p_itemtype
and wiav.ITEM_KEY = p_itemkey
and wiav.NAME = p_aname
returning wiav.NUMBER_VALUE into l_avalue;
else
update WF_ITEM_ATTRIBUTE_VALUES wiav
set wiav.NUMBER_VALUE = 0
where wiav.ITEM_TYPE = p_itemtype
and wiav.ITEM_KEY = p_itemkey
and wiav.NAME = p_aname
returning wiav.NUMBER_VALUE into l_avalue;
end if;
if (SQL%NOTFOUND) then
return null;
end if;
return l_avalue;
end if;
exception
when no_data_found then
return NULL;
when others then
Wf_Core.Context('Wf_Engine', 'AddToItemAttrNumber', p_itemtype, p_itemkey,
p_aname, to_char(p_addend));
raise;
end AddToItemAttrNumber;
end Wf_Engine;
/
--show errors package body WF_ENGINE
--select to_date( 'SQLERROR') from user_errors
--where type = 'PACKAGE BODY'
--and name = 'WF_ENGINE'
--/
commit;
REM ================================================================
/*=======================================================================+
| Copyright (C) 1995 Oracle Corporation Redwood Shores, California, Usa|
| All Rights Reserved. |
+=======================================================================+
| DESCRIPTION
| PL/SQL body for package: WF_ENGINE_UTIL
| NOTES
| This package contains utilities used internally by the Workflow
| Engine. It is not for public use and may be changed without notice.
*=======================================================================*/
create or replace package body WF_ENGINE_UTIL as
/* $Header: wfengb.pls 26.108 2005/03/02 05:14:50 rwunderl ship $ */
type InstanceArrayTyp is table of pls_integer
index by binary_integer;
type TypeArrayTyp is table of varchar2(8)
index by binary_integer;
type NameArrayTyp is table of varchar2(30)
index by binary_integer;
--
-- Exception
--
no_savepoint exception;
bad_format exception; --
pragma EXCEPTION_INIT(no_savepoint, -1086);
pragma EXCEPTION_INIT(bad_format, -6502); --
--
-- Activity_Parent_Process globals
-- Globals used to cache values retrieved in activity_parent_process
-- for performance, to avoid fetching the same value many times.
-- NOTE: In SYNCHMODE, this stack must be the complete call stack
-- of subprocesses at all times. In normal mode, the stack may or may
-- not be accurate, because
-- 1. calls for different items can be interwoven
-- 2. calls can jump anywhere on the process tree in some situations
-- (HandleError, etc).
-- ALWAYS check the key values before using values on the stack.
--
app_itemtype varchar2(8) := '';
app_itemkey varchar2(240) := '';
app_level pls_integer := '';
app_parent_itemtype TypeArrayTyp;
app_parent_name NameArrayTyp;
app_parent_id InstanceArrayTyp;
-- Bug 3824367
-- Optimizing the code using a single cursor with binds
cursor curs_activityattr (c_actid NUMBER, c_aname VARCHAR2) is
select WAAV.PROCESS_ACTIVITY_ID, WAAV.NAME, WAAV.VALUE_TYPE,
WAAV.TEXT_VALUE, WAAV.NUMBER_VALUE, WAAV.DATE_VALUE
from WF_ACTIVITY_ATTR_VALUES WAAV
where WAAV.PROCESS_ACTIVITY_ID = c_actid
and WAAV.NAME = c_aname;
--
-- ClearCache
-- Clear runtime cache
procedure ClearCache
is
begin
wf_engine_util.app_itemtype := '';
wf_engine_util.app_itemkey := '';
wf_engine_util.app_level := '';
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'ClearCache');
raise;
end ClearCache;
--
-- AddProcessStack
-- Add a new subprocess to activity_parent_process call stack.
-- Called when a new (sub)process is entered.
-- IN
-- itemtype - item itemtype
-- itemkey - item itemkey
-- act_itemtype - activity itemtype of process
-- act_name - activity name of process
-- actid - instance id of process
-- rootflag - TRUE if this is the root process
--
procedure AddProcessStack(
itemtype in varchar2,
itemkey in varchar2,
act_itemtype in varchar2,
act_name in varchar2,
actid in number,
rootflag in boolean)
is
begin
-- SYNCHMODE: Error if item doesn't match the cache, unless
-- starting a new process.
-- NOTE: Chance for an error here if you try to initiate a new process
-- while a synch process is still running. In that case you will
-- should eventually get an error from app for the process 1
-- because process 2 has trashed the stack. Can't think of a way to
-- detect the error directly here.
if (itemkey = wf_engine.eng_synch) then
if ((not rootflag) and
((nvl(wf_engine_util.app_itemtype, 'x') <> itemtype) or
(nvl(wf_engine_util.app_itemkey, 'x') <> itemkey))) then
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', itemkey);
Wf_Core.Raise('WFENG_SYNCH_ITEM');
end if;
end if;
-- If this is the root process, OR this is a different item,
-- then re-initialize the stack.
if ((rootflag) or
(nvl(wf_engine_util.app_itemtype, 'x') <> itemtype) or
(nvl(wf_engine_util.app_itemkey, 'x') <> itemkey)) then
wf_engine_util.app_itemtype := itemtype;
wf_engine_util.app_itemkey := itemkey;
wf_engine_util.app_level := 0;
end if;
-- Add the process to the stack
wf_engine_util.app_level := wf_engine_util.app_level + 1;
wf_engine_util.app_parent_itemtype(wf_engine_util.app_level) := act_itemtype;
wf_engine_util.app_parent_name(wf_engine_util.app_level) := act_name;
wf_engine_util.app_parent_id(wf_engine_util.app_level) := actid;
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'AddProcessStack',
itemtype, itemkey, act_itemtype, act_name, to_char(actid));
raise;
end AddProcessStack;
--
-- RemoveProcessStack
-- Remove a process from the process stack.
-- Called when a (sub)process exits.
-- IN
-- itemtype - item type
-- itemkey - itemkey
-- actid - instance id of process just completed
--
procedure RemoveProcessStack(
itemtype in varchar2,
itemkey in varchar2,
actid in number)
is
begin
-- If this is the top process on the stack, pop it off.
-- Must check if type/key/actid match, in case items and processes
-- are being interwoven and this is not the correct stack.
if (nvl(wf_engine_util.app_level, 0) > 0) then
if ((wf_engine_util.app_itemtype = itemtype) and
(wf_engine_util.app_itemkey = itemkey) and
(wf_engine_util.app_parent_id(wf_engine_util.app_level) = actid)) then
wf_engine_util.app_level := wf_engine_util.app_level - 1;
end if;
end if;
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'RemoveProcessStack', itemtype,
itemkey, to_char(actid));
raise;
end RemoveProcessStack;
--
-- Activity_Parent_Process (PRIVATE)
-- Get the activity's direct parent process.
-- IN
-- itemtype - Item type
-- itemkey - Item key
-- actid - The activity instance id.
--
function activity_parent_process(
itemtype in varchar2,
itemkey in varchar2,
actid in number)
return number
is
parentid pls_integer;
status PLS_INTEGER;
begin
-- Retrieve parent activity name
WF_CACHE.GetProcessActivity(activity_parent_process.actid, status);
if (status <> WF_CACHE.task_SUCCESS) then
select WPA.PROCESS_ITEM_TYPE, WPA.PROCESS_NAME, WPA.PROCESS_VERSION,
WPA.ACTIVITY_ITEM_TYPE, WPA.ACTIVITY_NAME, WPA.INSTANCE_ID,
WPA.INSTANCE_LABEL, WPA.PERFORM_ROLE, WPA.PERFORM_ROLE_TYPE,
WPA.START_END, WPA.DEFAULT_RESULT
into WF_CACHE.ProcessActivities(activity_parent_process.actid)
from WF_PROCESS_ACTIVITIES WPA
where WPA.INSTANCE_ID = activity_parent_process.actid;
end if;
-- Check the cached values in the call stack for a match, starting
-- at the bottom. If
-- 1. Itemtype and key
-- 2. Parent type and name
-- are the same, then the parent id must be the same.
-- Return it directly.
if ((nvl(wf_engine_util.app_level, 0) > 0) and
(itemtype = wf_engine_util.app_itemtype) and
(itemkey = wf_engine_util.app_itemkey)) then
for i in reverse 1 .. wf_engine_util.app_level loop
if ((WF_CACHE.ProcessActivities(actid).PROCESS_ITEM_TYPE =
wf_engine_util.app_parent_itemtype(i)) and
(WF_CACHE.ProcessActivities(actid).PROCESS_NAME =
wf_engine_util.app_parent_name(i))) then
-- Found a match.
return(wf_engine_util.app_parent_id(i));
end if;
end loop;
end if;
-- SYNCHMODE: If we don't have a match in the cache, then some restricted
-- activity must have happened. Raise an error.
if (itemkey = wf_engine.eng_synch) then
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', itemkey);
Wf_Core.Raise('WFENG_SYNCH_ITEM');
end if;
-- If no match was found, then either
-- 1. Activity has a different parent process name
-- 2. This is a new item
-- 3. This is the first call to app
-- In any case, join to WIAS to find an active instance for the
-- parent process name. Note there will be more than one instance
-- matching the parent activity name because of:
-- 1. The same activity may be used in multiple processes,
-- (even though the activity is used only once any particular
-- process tree).
-- 2. Versions
-- The join to active rows in WAIS for this item should choose
-- exactly one of these.
-- bug 1663684 - Added hint to choose a different driving table
SELECT /*+ leading(wias) index(wias,WF_ITEM_ACTIVITY_STATUSES_PK) */
WPA.INSTANCE_ID
INTO parentid
FROM WF_ITEM_ACTIVITY_STATUSES WIAS,
WF_PROCESS_ACTIVITIES WPA
WHERE WPA.ACTIVITY_ITEM_TYPE =
WF_CACHE.ProcessActivities(actid).PROCESS_ITEM_TYPE
AND WPA.ACTIVITY_NAME = WF_CACHE.ProcessActivities(actid).PROCESS_NAME
AND WPA.INSTANCE_ID = WIAS.PROCESS_ACTIVITY
AND WIAS.ITEM_TYPE = activity_parent_process.itemtype
AND WIAS.ITEM_KEY = activity_parent_process.itemkey;
-- Re-initialize process stack, starting with the new value
Wf_Engine_Util.AddProcessStack(itemtype, itemkey,
WF_CACHE.ProcessActivities(actid).PROCESS_ITEM_TYPE,
WF_CACHE.ProcessActivities(actid).PROCESS_NAME, parentid, TRUE);
return parentid;
exception
when no_data_found then
Wf_Core.Context('Wf_Engine_Util', 'Activity_Parent_Process',
to_char(actid));
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', itemkey);
Wf_Core.Token('CHILDPROCESS', to_char(actid));
Wf_Core.Token('FUNCTION', 'Activity_Parent_Process');
Wf_Core.Raise('WFSQL_INTERNAL');
when others then
Wf_Core.Context('Wf_Engine_Util', 'Activity_Parent_Process',
to_char(actid));
raise;
end activity_parent_process;
--
-- Complete_Activity (PRIVATE)
-- Mark an activity complete (after checking post-notification function
-- if requested), then clean up and prepare to continue process:
-- - Kill any outstanding child activities
-- - Complete the parent process if this is an END activity
-- - Follow any transitions to further activities in process
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- actid - The activity instance id.
-- result - The activity result.
-- runpntf - if TRUE then check the post-notification function before
-- completion
--
procedure complete_activity(itemtype in varchar2,
itemkey in varchar2,
actid in number,
result in varchar2,
runpntf in boolean)
is
-- Select all the transition activities for a given from activity
-- and result
cursor children (fromact pls_integer, fromact_result varchar2) is
SELECT WAT1.FROM_PROCESS_ACTIVITY, WAT1.RESULT_CODE,
WAT1.TO_PROCESS_ACTIVITY
FROM WF_ACTIVITY_TRANSITIONS WAT1
WHERE WAT1.FROM_PROCESS_ACTIVITY = fromact
AND (WAT1.RESULT_CODE in (fromact_result, wf_engine.eng_trans_any)
OR (WAT1.RESULT_CODE = wf_engine.eng_trans_default
AND NOT EXISTS
(SELECT NULL
FROM WF_ACTIVITY_TRANSITIONS WAT2
WHERE WAT2.FROM_PROCESS_ACTIVITY = fromact
AND WAT2.RESULT_CODE = fromact_result)
)
);
childarr InstanceArrayTyp;
i pls_integer := 0;
pntfstatus varchar2(8); -- Status of post-notification function
pntfresult varchar2(30); -- Result of post-notification function
lresult varchar2(30); -- Local result buffer
parent_status varchar2(8); -- Status of parent activity
actdate date; -- Active date
acttype varchar2(8); -- Activity type
notid pls_integer; -- Notification id
user varchar2(320); -- Not. assigned user
msgtype varchar2(8); -- Not. message type
msgname varchar2(30); -- Not. messaage name
priority number; -- Not. priority
duedate date; -- Not. duedate
not_status varchar2(8); -- Not. status
root varchar2(30); -- Root process of activity
version pls_integer; -- Root process version
rootid pls_integer; -- Id of root process
status PLS_INTEGER;
--
TransitionCount pls_integer := 0;
l_baseLnk NUMBER;
l_prevLnk NUMBER;
watIND NUMBER;
l_LinkCollision BOOLEAN;
begin
actdate := Wf_Item.Active_Date(itemtype, itemkey);
acttype := Wf_Activity.Instance_Type(actid, actdate);
if (runpntf and (acttype = wf_engine.eng_notification)) then
-- First execute possible post-notification function to see if activity
-- should really complete.
Wf_Engine_Util.Execute_Post_NTF_Function(itemtype, itemkey, actid,
wf_engine.eng_run, pntfstatus, pntfresult);
if (pntfstatus = wf_engine.eng_waiting) then
-- Either post-notification function is not complete, or error occurred.
-- In either case exit immediately without changing status.
-- Bug 2078211
-- if the status is waiting and the input parameter result is
-- wf_engine.eng_timedout, continue executing as the activity
-- needs to be timedout as determined by the procedure
-- Wf_Engine_Util.processtimeout
if (result = wf_engine.eng_timedout) then
lresult := result;
else
return;
end if;
elsif (pntfstatus = wf_engine.eng_completed) then
-- Post-notification activity is complete.
-- Replace result with result of post-notification function.
lresult := pntfresult;
else
-- Any pntfstatus other than waiting or complete means this is not
-- a post-notification activity, so use original result.
lresult := result;
end if;
else
lresult := result;
end if;
-- Update the item activity status
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
wf_engine.eng_completed, lresult, '', SYSDATE);
if (acttype = wf_engine.eng_process) then
-- If this activity is a process, kill any deferred children.
Wf_Engine_Util.Process_Kill_Children(itemtype, itemkey, actid);
-- Remove myself from the process call stack
Wf_Engine_Util.RemoveProcessStack(itemtype, itemkey, actid);
elsif (acttype = wf_engine.eng_notification) then
-- Cancel any outstanding notifications for this activity if
-- a response is expected.
-- (Response expected is signalled by a non-null result)
Wf_Item_Activity_Status.Notification_Status(itemtype, itemkey, actid,
notid, user);
if ((notid is not null) and (lresult <> wf_engine.eng_null)) then
begin
Wf_Notification.CancelGroup(notid);
exception
when others then
-- Ignore any errors from cancelling notifications
null;
end;
end if;
end if;
-- If this is the root process of the item, then exit immediately.
Wf_Item.Root_Process(itemtype, itemkey, root, version);
rootid := Wf_Process_Activity.RootInstanceId(itemtype, itemkey, root);
if (actid = rootid) then
return;
end if;
-- Also exit immediately if parent process no longer active.
-- This is to:
-- 1. avoid re-completing the parent if this happens to be
-- an end activity (immediately below).
-- 2. avoid creating confusing COMPLETE/#FORCE rows in process_activity
-- for activities following this one.
-- SYNCHMODE: No need to check, parent must always be active.
if (itemkey <> wf_engine.eng_synch) then
Wf_Item_Activity_Status.Status(itemtype, itemkey,
Wf_Engine_Util.Activity_Parent_Process(itemtype, itemkey, actid),
parent_status);
if (parent_status in (wf_engine.eng_completed, wf_engine.eng_error)) then
return;
end if;
end if;
-- Check if this is an ending activity.
-- If so, then also complete the parent process.
-- You can also exit immediately, because,
-- 1. If this is a process activity, then completing the parent process
-- will complete all of its children recursively, so there is no
-- need for this process to kill it's children. Instead, the
-- complete_activity is allowed to filter up to the top-level
-- ending process, which kills all the children in its tree in one shot.
-- 2. There are no transitions out of an ending process.
if (Wf_Activity.Ending(actid, actdate)) then
-- SS: Get the result code to complete the parent process with.
-- The result for the parent process will always be the default_result
-- of the ending activity, regardless of the result of the activity
-- itself.
WF_CACHE.GetProcessActivity(complete_activity.actid, status);
if (status <> WF_CACHE.task_SUCCESS) then
select WPA.PROCESS_ITEM_TYPE, WPA.PROCESS_NAME, WPA.PROCESS_VERSION,
WPA.ACTIVITY_ITEM_TYPE, WPA.ACTIVITY_NAME, WPA.INSTANCE_ID,
WPA.INSTANCE_LABEL, WPA.PERFORM_ROLE, WPA.PERFORM_ROLE_TYPE,
WPA.START_END, WPA.DEFAULT_RESULT
into WF_CACHE.ProcessActivities(complete_activity.actid)
from WF_PROCESS_ACTIVITIES WPA
where WPA.INSTANCE_ID = complete_activity.actid;
end if;
-- Complete the parent process and return immediately.
Wf_Engine_Util.Complete_Activity(itemtype, itemkey,
Wf_Engine_Util.Activity_Parent_Process(itemtype, itemkey, actid),
WF_CACHE.ProcessActivities(complete_activity.actid).DEFAULT_RESULT);
return;
end if;
--
-- Check WF_CACHE
WF_CACHE.GetActivityTransitions(FromActID=>actid,
result=>lresult,
status=>status,
watIND=>watIND);
if (status <> WF_CACHE.task_SUCCESS) then
-- The transitions for this activity/result is not in cache, so we will
-- store them using a for loop to get all the next transition activities.
-- Then we will access the list from cache to avoid maximum open cursor
-- problem. First we need to retain the base index to be used later.
l_baseLnk := watIND;
l_linkCollision := FALSE;
for child in children(actid, lresult) loop
if (TransitionCount > 0) then --Second and succeeding iterations
--We will locally store the record index from the last loop iteration.
l_prevLnk := watIND;
--We will now generate an index for the next transition from the
--actid, lresult, and the current TO_PROCESS_ACTIVITY.
watIND := WF_CACHE.HashKey(actid||':'||lresult||':'||
WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY);
--Check to make sure a record is not already here.
if (WF_CACHE.ActivityTransitions.EXISTS(watIND)) then
if ((WF_CACHE.ActivityTransitions(watIND).FROM_PROCESS_ACTIVITY <>
child.FROM_PROCESS_ACTIVITY) or
(WF_CACHE.ActivityTransitions(watIND).RESULT_CODE <>
child.RESULT_CODE) or
(WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY <>
child.TO_PROCESS_ACTIVITY)) then
l_linkCollision := TRUE; --We will continue
--populating this linked list, but after
--we use it, we will clear the pl/sql table.
end if;
end if;
--Now the PL/SQL table index has moved to the next link, so we will
--populate the prev_lnk with our locally stored index. This feature,
--not yet used, allows us to traverse backwards through the link list
--if needed. Since it is not yet used, it is commented out.
--WF_CACHE.ActivityTransitions(watIND).PREV_LNK := l_prevLnk;
--l_prevLnk represents the index of the previous record, and we need
--to update its NEXT_LNK field with the current index.
WF_CACHE.ActivityTransitions(l_prevLnk).NEXT_LNK := watIND;
-- else
-- WF_CACHE.ActivityTransitions(watIND).PREV_LNK := -1;
end if;
WF_CACHE.ActivityTransitions(watIND).FROM_PROCESS_ACTIVITY :=
child.FROM_PROCESS_ACTIVITY;
WF_CACHE.ActivityTransitions(watIND).RESULT_CODE := child.RESULT_CODE;
WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY :=
child.TO_PROCESS_ACTIVITY;
TransitionCount := TransitionCount+1;
end loop;
WF_CACHE.ActivityTransitions(watIND).NEXT_LNK := -1;
watIND := l_baseLnk; --Reset the index back to the beginning.
status := WF_CACHE.task_SUCCESS; --We now have the records successfully
--in cache.
end if;
-- Load a local InstanceArrayTyp, we do this because of the recursion that
-- occurs. Since the ActivityTransitions Cache is global, any hashCollision
-- would clear the cache and could cause problems as we process activities.
while (watIND <> -1) loop
childarr(i) := WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY;
i := i+1;
watIND := WF_CACHE.ActivityTransitions(watIND).NEXT_LNK;
end loop;
childarr(i) := '';
if (l_linkCollision) then
--When populating the linked list, we discovered that a hash collision
--caused us to overwrite a link belonging to another list. This would
--cause the other list to be incorrect. We will clear the table so the
--lists will be rebuilt after this transaction.
WF_CACHE.ActivityTransitions.DELETE;
end if;
--
-- SYNCHMODE: Check for branching.
-- If more than one transition out, this is an illegal branch point.
if ((itemkey = wf_engine.eng_synch) and (i > 1)) then
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Token('RESULT', lresult);
Wf_Core.Raise('WFENG_SYNCH_BRANCH');
end if;
i := 0;
-- While loop to hande the next transition activities.
while (childarr(i) is not NULL) loop
Wf_Engine_Util.Process_Activity(itemtype, itemkey,
childarr(i),
WF_ENGINE.THRESHOLD);
i := i+1;
end loop;
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Complete_Activity', itemtype, itemkey,
actid, result);
raise;
end complete_activity;
------------------------------------------------------------------
--Bug 2259039
--The start process code is consolidated into the new API
--start_process_internal.
------------------------------------------------------------------
--
-- Start_Process_Internal
-- Begins execution of the process. The process will be identified by the
-- itemtype and itemkey. The engine locates the starting activities
-- of the root process and executes them.
-- IN
-- itemtype - A valid item type
-- itemkey - Item Key
-- runmode - Start mode. Valid values are:
-- START : a valid startprocess
-- ACTIVITY : called in complete_activity
-- EVENT : when process is started from a receive event.
--
procedure Start_Process_Internal(
itemtype in varchar2,
itemkey in varchar2,
runmode in varchar2)
is
-- Select all the start activities in this parent process with
-- no in-transitions.
cursor starter_children (itemtype in varchar2,
process in varchar2,
version in number) is
SELECT PROCESS_ITEM_TYPE, PROCESS_NAME, PROCESS_VERSION,
ACTIVITY_ITEM_TYPE, ACTIVITY_NAME, INSTANCE_ID,
INSTANCE_LABEL, PERFORM_ROLE, PERFORM_ROLE_TYPE,
START_END, DEFAULT_RESULT
FROM WF_PROCESS_ACTIVITIES WPA
WHERE WPA.PROCESS_ITEM_TYPE = itemtype
AND WPA.PROCESS_NAME = process
AND WPA.PROCESS_VERSION = version
AND WPA.START_END = wf_engine.eng_start
AND NOT EXISTS (
SELECT NULL
FROM WF_ACTIVITY_TRANSITIONS WAT
WHERE WAT.TO_PROCESS_ACTIVITY = WPA.INSTANCE_ID);
childarr InstanceArrayTyp; -- Place holder for all the instance id
-- selected from starter_children cursor
i pls_integer := 0; -- Counter for the for loop
process varchar2(30) := ''; -- root process activity name
version pls_integer; -- root process activity version
processid pls_integer;
actdate date;
rerun varchar2(8); -- Activity rerun flag
acttype varchar2(8); -- Activity type
cost number; -- Activity cost
ftype varchar2(30); -- Activity function type
defer_mode boolean := FALSE;
TransitionCount pls_integer := 0;
l_baseLnk NUMBER;
l_prevLnk NUMBER;
psaIND NUMBER;
l_linkCollision BOOLEAN;
status PLS_INTEGER;
trig_savepoint exception;
pragma exception_init(trig_savepoint, -04092);
dist_savepoint exception;
pragma exception_init(dist_savepoint, -02074);
begin
-- Check if the item exists and also get back the root process name
-- and version
Wf_Item.Root_Process(itemtype, itemkey, process, version);
if (process is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
end if;
-- Insert a row for the process into WIAS table.
-- Get the id of the process root.
processid := Wf_Process_Activity.RootInstanceId(itemtype, itemkey,
process);
if (processid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('NAME', process);
Wf_Core.Raise('WFENG_PROCESS_RUNNABLE');
end if;
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, processid,
wf_engine.eng_active, wf_engine.eng_null, SYSDATE, null,
newStatus=>TRUE);
-- Initialize process call stack with the root process.
Wf_Engine_Util.AddProcessStack(itemtype, itemkey, itemtype, process,
processid, TRUE);
-- Get the cost of the parent process.
-- If the cost is over the threshold, then set a flag to immediately
-- defer child activities.
-- NOTE: Ordinarily it would be ok to let process_activity do the
-- job and defer activities if needed, but the savepoint in the loop
-- below causes failures if StartProcess is called from a db trigger.
-- This is a workaround to avoid the savepoints altogether if
-- the process is to be immediately deferred.
--
--
-- SYNCHMODE: Synch processes cannot be deferred.
actdate := Wf_Item.Active_Date(itemtype, itemkey);
Wf_Activity.Info(processid, actdate, rerun, acttype, cost, ftype);
if ((itemkey <> wf_engine.eng_synch) and
(cost > wf_engine.threshold)) then
defer_mode := TRUE;
end if;
--
-- Retrieve the starting activities from cache.
WF_CACHE.GetProcessStartActivities(itemType=>itemtype,
name=>process,
version=>version,
status=>status,
psaIND=>psaIND);
if (status <> WF_CACHE.task_SUCCESS) then
-- Starting activities are not in cache, so we will store them using a for
-- loop to get all the next transition activities.
-- Then we will access the list from cache to avoid maximum open cursor
-- problem. First we need to retain the base index to be used later.
l_baseLnk := psaIND;
l_linkCollision := FALSE;
for child in starter_children(itemtype, process, version) loop
if (TransitionCount > 0) then --Second and succeeding iterations
--We will locally store the record index from the last loop iteration.
l_prevLnk := psaIND;
--We will now generate an index for the start activity from the
--itemType, name, version, and the current INSTANCE_ID
psaIND := WF_CACHE.HashKey(itemType||':'||process||':'||version||
':'||WF_CACHE.ProcessStartActivities(psaIND).INSTANCE_ID);
--Check to make sure a record is not already here.
if (WF_CACHE.ProcessStartActivities.EXISTS(psaIND)) then
l_linkCollision := TRUE; --There should be no record here, so this
--is a hash collision. We will continue
--populating this linked list, but after
--we use it, we will clear the pl/sql table
end if;
--Now the PL/SQL table index has moved to the next link, so we will
--populate the prev_lnk with our locally stored index. This feature,
--not yet used, allows us to traverse backwards through the link list
--if needed. Since it is not yet used, it is commented out.
--WF_CACHE.ProcessStartActivities(psaIND).PREV_LNK := l_prevLnk;
--l_prevLnk represents the index of the previous record, and we need
--to update its NEXT_LNK field with the current index.
WF_CACHE.ProcessStartActivities(l_prevLnk).NEXT_LNK := psaIND;
--else
-- WF_CACHE.ProcessStartActivities(psaIND).PREV_LNK := -1;
end if;
WF_CACHE.ProcessStartActivities(psaIND).PROCESS_ITEM_TYPE :=
child.PROCESS_ITEM_TYPE;
WF_CACHE.ProcessStartActivities(psaIND).PROCESS_NAME :=
child.PROCESS_NAME;
WF_CACHE.ProcessStartActivities(psaIND).PROCESS_VERSION :=
child.PROCESS_VERSION;
WF_CACHE.ProcessStartActivities(psaIND).INSTANCE_ID := child.INSTANCE_ID;
--While we are here, we can populate the ProcessActivities cache hoping
--that a later request of any of these process activities will save us
--another trip to the DB.
WF_CACHE.ProcessActivities(child.INSTANCE_ID).PROCESS_ITEM_TYPE :=
child.PROCESS_ITEM_TYPE;
WF_CACHE.ProcessActivities(child.INSTANCE_ID).PROCESS_NAME :=
child.PROCESS_NAME;
WF_CACHE.ProcessActivities(child.INSTANCE_ID).PROCESS_VERSION :=
child.PROCESS_VERSION;
WF_CACHE.ProcessActivities(child.INSTANCE_ID).ACTIVITY_ITEM_TYPE :=
child.ACTIVITY_ITEM_TYPE;
WF_CACHE.ProcessActivities(child.INSTANCE_ID).ACTIVITY_NAME :=
child.ACTIVITY_NAME;
WF_CACHE.ProcessActivities(child.INSTANCE_ID).INSTANCE_ID :=
child.INSTANCE_ID;
WF_CACHE.ProcessActivities(child.INSTANCE_ID).INSTANCE_LABEL :=
child.INSTANCE_LABEL;
WF_CACHE.ProcessActivities(child.INSTANCE_ID).PERFORM_ROLE :=
child.PERFORM_ROLE;
WF_CACHE.ProcessActivities(child.INSTANCE_ID).PERFORM_ROLE_TYPE :=
child.PERFORM_ROLE_TYPE;
WF_CACHE.ProcessActivities(child.INSTANCE_ID).START_END :=
child.START_END;
WF_CACHE.ProcessActivities(child.INSTANCE_ID).DEFAULT_RESULT :=
child.DEFAULT_RESULT;
TransitionCount := TransitionCount+1;
end loop;
WF_CACHE.ProcessStartActivities(psaIND).NEXT_LNK := -1;
psaIND := l_baseLnk; --Reset the index back to the beginning.
status := WF_CACHE.task_SUCCESS; --We now have the records successfully
--in cache.
end if;
-- Load a local InstanceArrayTyp, we do this because of the recursion that
-- occurs. Since the ProcessStartActivities Cache is global, any
-- hashCollision would clear the cache and could cause problems as we
-- process activities in recursive calls.
while (psaIND <> -1) loop
childarr(i) := WF_CACHE.ProcessStartActivities(psaIND).INSTANCE_ID;
i := i+1;
psaIND := WF_CACHE.ProcessStartActivities(psaIND).NEXT_LNK;
end loop;
childarr(i) := '';
if (l_linkCollision) then
--When populating the linked list, we discovered that a hash collision
--caused us to overwrite a link belonging to another list. This would
--cause the other list to be incorrect. We will clear the table so the
--lists will be rebuilt after this transaction.
WF_CACHE.ProcessStartActivities.DELETE;
end if;
--
-- SYNCHMODE: Only 1 starter allowed in synch processes
if ((itemkey = wf_engine.eng_synch) and
(i > 1)) then
Wf_Core.Token('ACTID', process);
Wf_Core.Token('RESULT', 'START');
Wf_Core.Raise('WFENG_SYNCH_BRANCH');
end if;
-- SS: Process all 'true' start activities of this process.
-- 'True' start activities are those which are marked as starts,
-- and also have no in-transitions.
-- Activities with in-transitions may be marked as starters if it
-- is possible to jump into the middle of a process with a
-- completeactivity call. If this is the case, we don't want to
-- separately start these activities when starting the process as
-- a whole, because they will presumably already have been executed
-- in the flow starting from the 'true' starts.
i := 0;
while(childarr(i) is not null) loop
if (runmode in ('EVENT','ACTIVITY')) then
-- For runmode modes, just mark starters as NOTIFIED,
-- and thus ready for external input, but don't actually run
-- them. Only the start activities matching the specific
-- activity/event will be run (below).
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, childarr(i),
wf_engine.eng_notified, wf_engine.eng_null, SYSDATE, null,
newStatus=>TRUE);
elsif (defer_mode) then
-- Insert child rows as deferred with no further processing
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, childarr(i),
wf_engine.eng_deferred, wf_engine.eng_null, SYSDATE, null,
newStatus=>TRUE);
else -- Must be START mode, and not deferred
-- Process start activity normally
if (itemkey = wf_engine.eng_synch) then
-- SYNCHMODE: No fancy error processing!
Wf_Engine_Util.Process_Activity(itemtype, itemkey, childarr(i),
WF_ENGINE.THRESHOLD);
else
begin
savepoint wf_savepoint;
Wf_Engine_Util.Process_Activity(itemtype, itemkey, childarr(i),
WF_ENGINE.THRESHOLD);
exception
when trig_savepoint or dist_savepoint then
-- Oops, you forgot to defer your trigger or distributed
-- transaction initiated process! I'll do it for you.
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey,
childarr(i), wf_engine.eng_deferred, wf_engine.eng_null,
SYSDATE, null, newStatus=>TRUE);
when others then
-- If anything in this process raises an exception:
-- 1. rollback any work in this process thread
-- 2. set this activity to error status
-- 3. execute the error process (if any)
-- 4. clear the error to continue with next activity
rollback to wf_savepoint;
Wf_Core.Context('Wf_Engine', 'Start_Process_Internal', itemtype, itemkey);
Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, processid,
wf_engine.eng_exception, FALSE);
Wf_Engine_Util.Execute_Error_Process(itemtype, itemkey, processid,
wf_engine.eng_exception);
Wf_Core.Clear;
return;
end;
end if;
end if;
i := i+1;
end loop;
-- Report an error if no start activities can be found.
if (i = 0) then
Wf_Core.Token('PROCESS', process);
Wf_Core.Raise('WFENG_NO_START');
end if;
exception
when others then
-- Bug 4117740
-- Call clearcache() when #SYNCH flow is in error
if ((itemkey = WF_ENGINE.eng_synch) and
(wf_core.error_name is null or wf_core.error_name <> 'WFENG_SYNCH_ITEM') and
(not WF_ENGINE.debug)) then
Wf_Item.ClearCache;
end if;
Wf_Core.Context('Wf_Engine_Util', 'Start_Process_Internal',
itemtype, itemkey);
raise;
end Start_Process_Internal;
--
-- Process_Activity (PRIVATE)
-- Execute a single activity (function, notification, or sub-process),
-- after checking parent and activity statuses and conditions.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- actid - The activity instance id.
-- threshold - Max cost to process without deferring
-- activate - A flag to indicate that if the assigned activity is currently
-- active, should process_activity() still process it?
--
procedure process_activity(
itemtype in varchar2,
itemkey in varchar2,
actid in number,
threshold in number,
activate in boolean)
is
actdate date;
-- Select all the start activities in a process with no in-transitions.
cursor starter_children(parent in pls_integer) is
SELECT C.INSTANCE_ID
FROM WF_PROCESS_ACTIVITIES P, WF_PROCESS_ACTIVITIES C,
WF_ACTIVITIES A
WHERE P.INSTANCE_ID = parent
AND P.ACTIVITY_ITEM_TYPE = C.PROCESS_ITEM_TYPE
AND P.ACTIVITY_NAME = C.PROCESS_NAME
AND C.PROCESS_VERSION = A.VERSION
AND A.NAME = C.PROCESS_NAME
AND A.ITEM_TYPE = C.PROCESS_ITEM_TYPE
AND actdate >= A.BEGIN_DATE
AND actdate < NVL(A.END_DATE, actdate+1)
AND C.START_END = wf_engine.eng_start
AND NOT EXISTS (
SELECT NULL
FROM WF_ACTIVITY_TRANSITIONS WAT
WHERE WAT.TO_PROCESS_ACTIVITY = C.INSTANCE_ID);
rerun varchar2(8); -- Activity rerun flag
cost number; -- Activity cost
status varchar2(8); -- Activity status
result varchar2(30); -- Activity result
acttype varchar2(8); -- Activity type
act_itemtype varchar2(8); -- Activity itemtype
act_name varchar2(30); -- Activity name
act_functype varchar2(30); -- Activity function type
childarr InstanceArrayTyp; -- Place holder for all the instance id
-- selected from starter_children cursor
i pls_integer := 0;
trig_savepoint exception;
pragma exception_init(trig_savepoint, -04092);
dist_savepoint exception;
pragma exception_init(dist_savepoint, -02074);
begin
-- Check this activity's parent process
-- SYNCHMODE: No need to check parent, will always be active.
if (itemkey <> wf_engine.eng_synch) then
Wf_Item_Activity_Status.Status(itemtype, itemkey,
Wf_Engine_Util.Activity_Parent_Process(itemtype, itemkey, actid),
status);
if (status is null) then
-- return WF_PARENT_PROCESS_NOT_RUNNING;
-- TO BE UPDATED
-- Actually this case should not happen
return;
elsif ((status = wf_engine.eng_completed) or
(status = wf_engine.eng_error)) then
-- Mark it as completed cause the parent process is completed/errored
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
wf_engine.eng_completed, wf_engine.eng_force, sysdate, sysdate);
return;
elsif (status = wf_engine.eng_suspended) then
-- Insert this activity as deferred
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
wf_engine.eng_deferred, null,
sysdate, null, suspended=>TRUE);
return;
elsif (status in (wf_engine.eng_notified, wf_engine.eng_waiting,
wf_engine.eng_deferred)) then
-- NOTE: This should never happened because the engine will never
-- set the status of a process to be 'WAITING' or 'NOTIFIED'
-- return;
Wf_Core.Token('ITEM_TYPE', itemtype);
Wf_Core.Token('ITEM_KEY', itemkey);
Wf_Core.Token('PROCESS', to_char(actid));
Wf_Core.Token('STATUS', status);
Wf_Core.Raise('WFSQL_INTERNAL');
end if;
end if;
-- If we came here, that means the parent process is ACTIVE
-- Get the information of this activity
-- Out of these three return variables, cost is the only one could be null
actdate := Wf_Item.Active_Date(itemtype, itemkey);
Wf_Activity.Info(actid, actdate, rerun, acttype,
cost, act_functype);
-- If this activity is currently active, do nothing
-- If this activity has already been completed, check the rerun flag
--
-- SYNCHMODE: Ignore the current status of the activity. No loop
-- reset or other processing is allowed.
if (itemkey = wf_engine.eng_synch) then
status := '';
result := '';
else
Wf_Item_Activity_Status.Result(itemtype, itemkey, actid, status, result);
if (status is not null) then
if ( (status = wf_engine.eng_active) AND (activate = FALSE) )then
-- Maybe don't have to do anything because it is running already
return;
-- Bug 2111183
-- resetting activity with status eng_notified prevents a orphaned
-- notification in WF_NOTIFICATIONS if the notification activity
-- is revisited in a loop simultaneously by two incoming transitions
elsif (status in (wf_engine.eng_completed, wf_engine.eng_error,
wf_engine.eng_notified)) then
-- Check the rerun flag to see what should be done
if (rerun = wf_engine.eng_ignore) then
-- No loop - do nothing
return;
elsif (rerun = wf_engine.eng_reset) then
-- Reset activities, cancel mode
Wf_Engine_Util.Reset_Activities(itemtype, itemkey, actid, TRUE);
elsif (rerun = wf_engine.eng_loop) then
-- Reset activities, no-cancel mode
Wf_Engine_Util.Reset_Activities(itemtype, itemkey, actid, FALSE);
end if;
elsif ((status = wf_engine.eng_suspended) AND
(acttype <> wf_engine.eng_process))then
-- Only the process type of activity can have a 'SUSPENDED' status
-- If this is not a process type activity, then THIS IS A PROBLEM
-- CAN NOT DO ANYTHING
Wf_Core.Token('ITEM_TYPE', itemtype);
Wf_Core.Token('ITEM_KEY', itemkey);
Wf_Core.Token('ACTIVITY_TYPE', acttype);
Wf_Core.Token('STATUS', status);
Wf_Core.Raise('WFSQL_INTERNAL');
end if;
end if;
end if;
-- If we came here, we have
-- (1) not yet run this activity before
-- (2) this is a deferred activity
-- (3) this is a waiting activity (including logical_and)
-- (4) this is re-runnable activity and we did a reset already
--
-- SYNCHMODE: Ignore cost, always run process immediately
if ((itemkey = wf_engine.eng_synch) or
(cost is null and act_functype = 'PL/SQL') or
(cost <= nvl(threshold, cost) and act_functype = 'PL/SQL')) then
-- If status is null, we want to create the status
-- If status is not null, we want to update the status back to active
-- except for a suspended process
if (status is null ) then
-- Insert this activity as active into the WIAS table
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
wf_engine.eng_active, null,
sysdate, null, newStatus=>TRUE);
elsif (status <> wf_engine.eng_suspended) then
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
wf_engine.eng_active, null,
sysdate, null, newStatus=>FALSE);
end if;
if (acttype = wf_engine.eng_process) then
-- PROCESS activity
-- Add this subprocess to the call stack
Wf_Process_Activity.ActivityName(actid, act_itemtype, act_name);
Wf_Engine_Util.AddProcessStack(itemtype, itemkey, act_itemtype,
act_name, actid, FALSE);
-- For loop to get all the start activities first.
-- This is to avoid the maximum open cursor problem
for child in starter_children(actid) loop
childarr(i) := child.instance_id;
i := i+1;
end loop;
childarr(i) := '';
-- SYNCHMODE: Only one starter allowed in synch process
if ((itemkey = wf_engine.eng_synch) and (i > 1)) then
Wf_Core.Token('ACTID', act_name);
Wf_Core.Token('RESULT', 'START');
Wf_Core.Raise('WFENG_SYNCH_BRANCH');
end if;
-- While loop to handle all the start activities
i := 0;
while(childarr(i) is not null) loop
Wf_Engine_Util.Process_Activity(itemtype, itemkey, childarr(i),
threshold);
i := i+1;
end loop;
else
-- Function/Notification/Event type activities
begin
Wf_Engine_Util.Execute_Activity(itemtype, itemkey, actid,
wf_engine.eng_run);
exception
when trig_savepoint or dist_savepoint then
-- Oops, you forgot to defer your trigger or distributed
-- transaction initiated process! I'll do it for you.
-- (Note this is only needed here for restarting a
-- process using CompleteActivity, all other will be caught
-- by error handling savepoints before this.)
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey,
actid, wf_engine.eng_deferred, null,
SYSDATE, null, newStatus=>TRUE);
end;
end if;
else
-- Cost is over the threshold or this is a callout function
-- Insert this activity into the WIAS table and mark it as deferred
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
wf_engine.eng_deferred, null,
sysdate, null, newStatus=>TRUE);
end if; -- end if deferred
return;
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Process_Activity', itemtype, itemkey,
to_char(actid), to_char(threshold));
raise;
end process_activity;
--
-- Reset_Activities (PRIVATE)
-- Reset completed activities to redo a loop
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- itemactid - The activity instance id.
-- cancel - Cancel the activities before resetting or not
--
procedure reset_activities(itemtype in varchar2,
itemkey in varchar2,
actid in number,
cancel in boolean)
is
actdate date;
-- Select all the start activities for this parent process
cursor starter_children(parent in pls_integer) is
SELECT C.INSTANCE_ID
FROM WF_PROCESS_ACTIVITIES P, WF_PROCESS_ACTIVITIES C,
WF_ACTIVITIES A
WHERE P.INSTANCE_ID = parent
AND P.ACTIVITY_ITEM_TYPE = C.PROCESS_ITEM_TYPE
AND P.ACTIVITY_NAME = C.PROCESS_NAME
AND C.PROCESS_VERSION = A.VERSION
AND A.NAME = C.PROCESS_NAME
AND A.ITEM_TYPE = C.PROCESS_ITEM_TYPE
AND actdate >= A.BEGIN_DATE
AND actdate < NVL(A.END_DATE, actdate+1)
AND C.START_END = wf_engine.eng_start;
-- Select the to activity(ies) by given the from activity and result
cursor to_activities(fromact in pls_integer, fromact_result varchar2) is
SELECT WAT1.FROM_PROCESS_ACTIVITY, WAT1.RESULT_CODE,
WAT1.TO_PROCESS_ACTIVITY
FROM WF_ACTIVITY_TRANSITIONS WAT1
WHERE WAT1.FROM_PROCESS_ACTIVITY = fromact
AND (WAT1.RESULT_CODE in (fromact_result, wf_engine.eng_trans_any)
OR (WAT1.RESULT_CODE = wf_engine.eng_trans_default
AND NOT EXISTS
(SELECT NULL
FROM WF_ACTIVITY_TRANSITIONS WAT2
WHERE WAT2.FROM_PROCESS_ACTIVITY = fromact
AND WAT2.RESULT_CODE = fromact_result)
)
);
childarr InstanceArrayTyp;
i pls_integer := 0; -- counter for the childarr
savearr InstanceArrayTyp; -- Save all the children and then process them
-- at the reversed order
result varchar2(30);
status varchar2(8);
typ varchar2(8);
notid pls_integer;
user varchar2(320);
pntfstatus varchar2(8);
pntfresult varchar2(30);
--
TransitionCount pls_integer := 0;
l_baseLnk NUMBER;
l_prevLnk NUMBER;
watIND NUMBER;
l_LinkCollision BOOLEAN;
begin
Wf_Item_Activity_Status.Result(itemtype, itemkey, actid, status, result);
if (status is null) then
return; -- This means the end of a path
end if;
-- Undo the current activity, depending on type
actdate := Wf_Item.Active_Date(itemtype, itemkey);
typ := Wf_Activity.Instance_Type(actid, actdate);
if (typ = wf_engine.eng_process) then
-- For loop to get the starting activities of this process
i := 0;
for child in starter_children(actid) loop
childarr(i) := child.instance_id;
i := i + 1;
end loop;
childarr(i) := '';
-- Reset all starting activities of child process.
i := 0;
while (childarr(i) is not null) loop
Wf_Engine_Util.Reset_Activities(itemtype, itemkey, childarr(i), cancel);
i := i + 1;
end loop;
elsif (typ = wf_engine.eng_notification) then
if (cancel) then
-- Run post-notification function in cancel mode if there is one.
Wf_Engine_Util.Execute_Post_NTF_Function(itemtype, itemkey, actid,
wf_engine.eng_cancel, pntfstatus, pntfresult);
-- Cancel any open notifications sent by this activity
Wf_Item_Activity_Status.Notification_Status(itemtype, itemkey, actid,
notid, user);
if (notid is not null) then
begin
Wf_Notification.CancelGroup(notid);
exception
when others then
null; -- Ignore errors in cancelling
end;
end if;
end if;
elsif (typ in (wf_engine.eng_function, wf_engine.eng_event)) then
if (cancel) then
-- Call function in cancel mode
Wf_Engine_Util.Execute_Activity(itemtype, itemkey, actid,
wf_engine.eng_cancel);
end if;
end if;
-- Move the WIAS record to the history table.
-- Note: Do NOT move this call. The move_to_history() must be before any
-- recursive calls to reset_activities() in the current process,
-- or infinite recursion will result.
Wf_Engine_Util.Move_To_History(itemtype, itemkey, actid);
-- Reset all activities following this one in current process,
-- but only if this activity really completed.
if (status = wf_engine.eng_completed) then
--
-- Check WF_CACHE
WF_CACHE.GetActivityTransitions(FromActID=>actid,
result=>result,
status=>status,
watIND=>watIND);
if (status <> WF_CACHE.task_SUCCESS) then
-- The transitions for this activity/result is not in cache, so we will
-- store them using a for loop to get all the next transition activities.
-- Then we will access the list from cache to avoid maximum open cursor
-- problem. First we need to retain the base index to be used later.
l_baseLnk := watIND;
l_linkCollision := FALSE;
for to_activity in to_activities(actid, result) loop
if (TransitionCount > 0) then --Second and succeeding iterations
--We will locally store the record index from the last loop iteration.
l_prevLnk := watIND;
--We will now generate an index for the next transition from the
--actid, result, and the current TO_PROCESS_ACTIVITY.
watIND := WF_CACHE.HashKey(actid||':'||result||':'||
WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY);
--Check to make sure a record is not already here.
if (WF_CACHE.ActivityTransitions.EXISTS(watIND)) then
if ((WF_CACHE.ActivityTransitions(watIND).FROM_PROCESS_ACTIVITY <>
to_activity.FROM_PROCESS_ACTIVITY) or
(WF_CACHE.ActivityTransitions(watIND).RESULT_CODE <>
to_activity.RESULT_CODE) or
(WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY <>
to_activity.TO_PROCESS_ACTIVITY)) then
l_linkCollision := TRUE; --We will continue
--populating this linked list, but after
--we use it, we will clear the pl/sql table.
end if;
end if;
--Now the PL/SQL table index has moved to the next link, so we will
--populate the prev_lnk with our locally stored index. This feature,
--not yet used, allows us to traverse backwards through the link list
--if needed. Since it is not yet used, it is commented out.
-- WF_CACHE.ActivityTransitions(watIND).PREV_LNK := l_prevLnk;
--l_prevLnk represents the index of the previous record, and we need
--to update its NEXT_LNK field with the current index.
WF_CACHE.ActivityTransitions(l_prevLnk).NEXT_LNK := watIND;
-- else
-- WF_CACHE.ActivityTransitions(watIND).PREV_LNK := -1;
end if;
WF_CACHE.ActivityTransitions(watIND).FROM_PROCESS_ACTIVITY :=
to_activity.FROM_PROCESS_ACTIVITY;
WF_CACHE.ActivityTransitions(watIND).RESULT_CODE :=
to_activity.RESULT_CODE;
WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY :=
to_activity.TO_PROCESS_ACTIVITY;
TransitionCount := TransitionCount+1;
end loop;
WF_CACHE.ActivityTransitions(watIND).NEXT_LNK := -1;
watIND := l_baseLnk; --Reset the index back to the beginning.
status := WF_CACHE.task_SUCCESS; --We now have the records successfully
--in cache.
end if;
-- Load a local InstanceArrayTyp, we do this because of the recursion that
-- occurs. Since the ActivityTransitions Cache is global, any hashCollision
-- would clear the cache and could cause problems as we process activities.
while (watIND <> -1) loop
childarr(i) := WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY;
i := i+1;
watIND := WF_CACHE.ActivityTransitions(watIND).NEXT_LNK;
end loop;
childarr(i) := '';
i := 0;
while (childarr(i) is not null) loop
Wf_Engine_Util.Reset_Activities(itemtype, itemkey, childarr(i), cancel);
i := i + 1;
end loop;
end if;
if (l_linkCollision) then
--When populating the linked list, we discovered that a hash collision
--caused us to overwrite a link belonging to another list. This would
--cause the other list to be incorrect. We will clear the table so the
--lists will be rebuilt after this transaction.
WF_CACHE.ActivityTransitions.DELETE;
end if;
--
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Reset_Activities', itemtype, itemkey,
to_char(actid));
raise;
end reset_activities;
--
-- Reset_Tree (PRIVATE)
-- Reset an activity and all parent activities above it in a tree
-- and prepare for re-execution. Used to reset the process to an
-- arbitrary point in HandleError.
-- IN
-- itemtype - Item type
-- itemkey - Item key
-- rootid - Instance id of process root
-- goalid - Instance id of activity to reset
-- RETURNS
-- TRUE if goalid found
--
function Reset_Tree(
itemtype in varchar2,
itemkey in varchar2,
rootid in number,
goalid in number,
actdate in date)
return boolean is
-- Cursor to select children of activity
cursor children(parentid in pls_integer, actdate in date) is
select WPA2.INSTANCE_ID
from WF_PROCESS_ACTIVITIES WPA1,
WF_ACTIVITIES WA,
WF_PROCESS_ACTIVITIES WPA2
where WPA1.INSTANCE_ID = parentid
and WPA2.PROCESS_ITEM_TYPE = WA.ITEM_TYPE
and WPA2.PROCESS_NAME = WA.NAME
and WA.ITEM_TYPE = WPA1.ACTIVITY_ITEM_TYPE
and WA.NAME = WPA1.ACTIVITY_NAME
and actdate >= WA.BEGIN_DATE
and actdate < NVL(WA.END_DATE, actdate+1)
and WPA2.PROCESS_VERSION = WA.VERSION;
childarr InstanceArrayTyp;
i number := 0;
-- Cursor to select following activities
cursor to_activities(fromact in pls_integer, fromact_result in varchar2) is
SELECT WAT1.FROM_PROCESS_ACTIVITY, WAT1.RESULT_CODE,
WAT1.TO_PROCESS_ACTIVITY
FROM WF_ACTIVITY_TRANSITIONS WAT1
WHERE WAT1.FROM_PROCESS_ACTIVITY = fromact
AND (WAT1.RESULT_CODE in (fromact_result, wf_engine.eng_trans_any)
OR (WAT1.RESULT_CODE = wf_engine.eng_trans_default
AND NOT EXISTS
(SELECT NULL
FROM WF_ACTIVITY_TRANSITIONS WAT2
WHERE WAT2.FROM_PROCESS_ACTIVITY = fromact
AND WAT2.RESULT_CODE = fromact_result)
)
);
actarr InstanceArrayTyp;
j pls_integer := 0;
status varchar2(8);
result varchar2(30);
--
TransitionCount pls_integer := 0;
l_baseLnk NUMBER;
l_prevLnk NUMBER;
watIND NUMBER;
l_LinkCollision BOOLEAN;
begin
-- Goal has been found. Reset the activity and all following it on this
-- level, then set status to waiting for possible re-execution.
if (rootid = goalid) then
Wf_Engine_Util.Reset_Activities(itemtype, itemkey, goalid, TRUE);
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, goalid,
wf_engine.eng_active, wf_engine.eng_null, sysdate, null);
return(TRUE);
end if;
-- Otherwise, loop through children of rootid.
for child in children(rootid, actdate) loop
childarr(i) := child.instance_id;
i := i + 1;
end loop;
childarr(i) := '';
i := 0;
while (childarr(i) is not null) loop
-- Check if goal is in the subtree rooted at this child
if (Wf_Engine_Util.Reset_Tree(itemtype, itemkey, childarr(i), goalid,
actdate)) then
-- Goal has been found in a child of this activity.
Wf_Item_Activity_Status.Result(itemtype, itemkey, rootid,
status, result);
-- Reset any activities FOLLOWING the root.
-- Do not reset the root itself - it is a process and its children
-- were already reset in the recursive call.
-- Likewise, do not reset actual child - it has already been reset.
if (status = wf_engine.eng_completed) then
--
-- Check WF_CACHE
WF_CACHE.GetActivityTransitions(FromActID=>rootid,
result=>result,
status=>status,
watIND=>watIND);
if (status <> WF_CACHE.task_SUCCESS) then
-- The transitions for this activity/result is not in cache, so we will
-- store them using a for loop to get all the next transition
-- activities. Then we will access the list from cache to avoid
-- maximum open cursor problem. First we need to retain the base index
-- to be used later.
l_baseLnk := watIND;
l_linkCollision := FALSE;
for to_activity in to_activities(rootid, result) loop
if (TransitionCount > 0) then --Second and succeeding iterations
--We will locally store the record index from the last loop
--iteration.
l_prevLnk := watIND;
--We will now generate an index for the next transition from the
--actid, result, and the current TO_PROCESS_ACTIVITY.
watIND := WF_CACHE.HashKey(rootid||':'||result||':'||
WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY);
--Check to make sure a record is not already here.
if (WF_CACHE.ActivityTransitions.EXISTS(watIND)) then
if ((WF_CACHE.ActivityTransitions(watIND).FROM_PROCESS_ACTIVITY <>
to_activity.FROM_PROCESS_ACTIVITY) or
(WF_CACHE.ActivityTransitions(watIND).RESULT_CODE <>
to_activity.RESULT_CODE) or
(WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY <>
to_activity.TO_PROCESS_ACTIVITY)) then
l_linkCollision := TRUE; --We will continue
--populating this linked list, but after
--we use it, we will clear the pl/sql
--table.
end if;
end if;
--Now the PL/SQL table index has moved to the next link, so we
--will populate the prev_lnk with our locally stored index.
--This feature, not yet used, allows us to traverse backwards
--through the link list if needed. Since it is not yet used,
--it is commented out.
-- WF_CACHE.ActivityTransitions(watIND).PREV_LNK := l_prevLnk;
--l_prevLnk represents the index of the previous record, and we
--need to update its NEXT_LNK field with the current index.
WF_CACHE.ActivityTransitions(l_prevLnk).NEXT_LNK := watIND;
-- else
-- WF_CACHE.ActivityTransitions(watIND).PREV_LNK := -1;
end if;
WF_CACHE.ActivityTransitions(watIND).FROM_PROCESS_ACTIVITY :=
to_activity.FROM_PROCESS_ACTIVITY;
WF_CACHE.ActivityTransitions(watIND).RESULT_CODE :=
to_activity.RESULT_CODE;
WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY :=
to_activity.TO_PROCESS_ACTIVITY;
TransitionCount := TransitionCount+1;
end loop;
WF_CACHE.ActivityTransitions(watIND).NEXT_LNK := -1;
watIND := l_baseLnk; --Reset the index back to the beginning.
status := WF_CACHE.task_SUCCESS; --We now have the records
--successfully in cache.
end if;
j := 0;
-- Load a local InstanceArrayTyp, we do this because of the recursion
-- that occurs. Since the ActivityTransitions Cache is global, any
-- hashCollision would clear the cache and could cause problems as we
-- process activities.
while (watIND <> -1) loop
actarr(j) := WF_CACHE.ActivityTransitions(watIND).TO_PROCESS_ACTIVITY;
j := j+1;
watIND := WF_CACHE.ActivityTransitions(watIND).NEXT_LNK;
end loop;
actarr(j) := '';
j := 0;
while (actarr(j) is not null) loop
Wf_Engine_Util.Reset_Activities(itemtype, itemkey, actarr(j), TRUE);
j := j + 1;
end loop;
end if;
if (l_linkCollision) then
--When populating the linked list, we discovered that a hash collision
--caused us to overwrite a link belonging to another list. This would
--cause the other list to be incorrect. We will clear the table so the
--lists will be rebuilt after this transaction.
WF_CACHE.ActivityTransitions.DELETE;
end if;
--
-- Set the root activity status to active if not already
if (nvl(status, 'x') <> wf_engine.eng_active) then
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, rootid,
wf_engine.eng_active, wf_engine.eng_null, sysdate, null);
end if;
-- Goal has been found, so exit now
return(TRUE);
end if;
i := i + 1;
end loop;
-- Goal not found anywhere.
return(FALSE);
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Reset_Tree', itemtype, itemkey,
to_char(rootid), to_char(goalid), to_char(actdate));
raise;
end Reset_Tree;
--
-- Move_To_History (PRIVATE)
-- Move the item activity status row from WF_ITEM_ACTIVITY_STATUSES to
-- WF_ITEM_ACTIVITY_STATUSES_H table.
-- IN
-- itemtype - A valid item type from (WF_ITEM_TYPES table).
-- itemkey - A string generated from the application object's primary key.
-- actid - The activity instance id.
--
procedure move_to_history(itemtype in varchar2,
itemkey in varchar2,
actid in number) is
begin
-- Copy row to history table, changing status to COMPLETE/#FORCE
-- if status is not already complete.
INSERT INTO WF_ITEM_ACTIVITY_STATUSES_H (
ITEM_TYPE,
ITEM_KEY,
PROCESS_ACTIVITY,
ACTIVITY_STATUS,
ACTIVITY_RESULT_CODE,
ASSIGNED_USER,
NOTIFICATION_ID,
OUTBOUND_QUEUE_ID,
BEGIN_DATE,
END_DATE,
DUE_DATE,
EXECUTION_TIME,
ERROR_NAME,
ERROR_MESSAGE,
ERROR_STACK,
ACTION,
PERFORMED_BY
) SELECT
ITEM_TYPE,
ITEM_KEY,
PROCESS_ACTIVITY,
wf_engine.eng_completed,
decode(ACTIVITY_STATUS,
wf_engine.eng_completed, ACTIVITY_RESULT_CODE,
wf_engine.eng_force),
ASSIGNED_USER,
NOTIFICATION_ID,
OUTBOUND_QUEUE_ID,
nvl(BEGIN_DATE, sysdate),
nvl(END_DATE, sysdate),
DUE_DATE,
EXECUTION_TIME,
ERROR_NAME,
ERROR_MESSAGE,
ERROR_STACK,
ACTION,
PERFORMED_BY
FROM WF_ITEM_ACTIVITY_STATUSES
WHERE ITEM_TYPE = itemtype
AND ITEM_KEY = itemkey
AND PROCESS_ACTIVITY = actid;
if (Wf_Engine.Debug) then
commit;
end if;
Wf_Item_Activity_Status.Delete_Status(itemtype, itemkey, actid);
EXCEPTION
when OTHERS then
Wf_Core.Context('Wf_Engine_Util', 'Move_To_History', itemtype, itemkey,
to_char(actid));
raise;
END move_to_history;
--
-- Execute_Activity (PRIVATE)
-- Execute a notification or function activity and process the result.
-- IN
-- itemtype - A valid item type from (WF_ITEM_TYPES table).
-- itemkey - A string generated from the application object's primary key.
-- actid - The activity instance id.
-- funmode - function mode (RUN/CANCEL/TIMEOUT)
--
procedure execute_activity(itemtype in varchar2,
itemkey in varchar2,
actid in number,
funmode in varchar2)
is
funcname varchar2(240); -- Name of activity function
result varchar2(370); -- Function result
id varchar2(30); -- Id for error code or notification id
notuser varchar2(320); -- Notification user
col1 pls_integer;
col2 pls_integer;
actdate date;
acttype varchar2(8);
resume_date date;
begin
actdate := Wf_Item.Active_Date(itemtype, itemkey);
acttype := Wf_Activity.Instance_Type(actid, actdate);
if (acttype = wf_engine.eng_function) then
funcname := Wf_Activity.Activity_Function(itemtype, itemkey, actid);
if (funcname is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('NAME', to_char(actid));
Wf_Core.Raise('WFENG_ACTIVITY_FUNCTION');
end if;
end if;
-- Execute the activity function
Wf_Core.Clear;
begin
if (acttype = wf_engine.eng_notification) then
Wf_Engine_Util.Notification(itemtype, itemkey, actid, funmode, result);
elsif (acttype = wf_engine.eng_function) then
Wf_Engine_Util.Function_Call(funcname, itemtype, itemkey, actid,
funmode, result);
elsif (acttype = wf_engine.eng_event) then
Wf_Engine_Util.Event_Activity(itemtype, itemkey, actid, funmode, result);
else
-- Bad activity type, don't know how to execute.
Wf_Core.Token('ITEM_TYPE', itemtype);
Wf_Core.Token('ITEM_KEY', itemkey);
Wf_Core.Token('ACTIVITY_ID', to_char(actid));
Wf_Core.Token('ACTIVITY_TYPE', acttype);
Wf_Core.Raise('WFSQL_INTERNAL');
end if;
exception
when others then
if (itemkey = wf_engine.eng_synch) then
-- SYNCHMODE: No saved errors allowed.
-- Raise exception directly to calling process.
raise;
elsif (funmode <> wf_engine.eng_cancel) then
-- Set error info columns if activity function raised exception,
-- unless running in cancel mode.
Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, actid,
wf_engine.eng_exception, FALSE);
result := wf_engine.eng_error||':'||wf_engine.eng_exception;
end if;
end;
-- The engine does not care about the result when undoing a function
if (funmode = wf_engine.eng_cancel) then
return;
end if;
-- Possible results :
-- ERROR[:errcode]
-- WAITING
-- DEFERRED[:resume_date]
-- NOTIFIED[:notid:user]
-- COMPLETE[:result]
-- result -> this implies COMPLETE:result
-- Handle different results
if (substr(result, 1, length(wf_engine.eng_error)) =
wf_engine.eng_error) then
-- Get the error code
id := substr(result, length(wf_engine.eng_error)+2, 30);
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
wf_engine.eng_error, id);
-- Call error_process to execute any error processes.
Wf_Engine_Util.Execute_Error_Process(itemtype, itemkey, actid, id);
elsif (result = wf_engine.eng_waiting) then
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
wf_engine.eng_waiting, '', to_date(NULL), to_date(NULL));
elsif (substr(result, 1, length(wf_engine.eng_deferred)) =
wf_engine.eng_deferred) then
-- Extract the resume_date if one was returned
col1 := instr(result, ':', 1, 1);
if (col1 <> 0) then
resume_date := to_date(substr(result, col1+1), wf_engine.date_format);
else
resume_date := to_date(NULL);
end if;
-- Set the status to 'DEFERRED', and reset the begin_date to the
-- extracted resume_date if there is one.
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
wf_engine.eng_deferred, wf_engine.eng_null, resume_date,
to_date(NULL));
elsif (substr(result, 1, length(wf_engine.eng_notified)) =
wf_engine.eng_notified) then
-- Get the notification id and user
col1 := instr(result, ':', 1, 1);
col2 := instr(result, ':', 1, 2);
if ((col1 <> 0) and (col2 <> 0)) then
id := substr(result, col1+1, col2-col1-1);
notuser := substr(result, col2+1, 320);
-- Set notification id and user, but only if not null.
-- This is to allow for pseudo-notifications that are only blocking
-- waiting for external completion.
if (nvl(id, wf_engine.eng_null) <> wf_engine.eng_null) then
Wf_Item_Activity_Status.Update_Notification(itemtype, itemkey, actid,
to_number(id), notuser);
end if;
end if;
if ((nvl(id, wf_engine.eng_null) <> wf_engine.eng_null) and
not Wf_Notification.OpenNotificationsExist(id)) then
-- Notification has already been closed, presumably by an
-- auto-routing rule that has already submitted the response.
-- If this is the case, the notification has been responded to
-- and is closed, but CB did NOT continue execution following
-- completion (see comments in 'complete' processing in CB).
-- Call complete_activity here to continue processing immediately
-- instead of just marking activity as notified.
result := Wf_Engine.GetItemAttrText(itemtype, itemkey, 'RESULT');
Wf_Engine_Util.Complete_Activity(itemtype, itemkey, actid, result);
else
-- Notification not auto-routed, or pseudo-notification.
-- In either case, mark status NOTIFIED to block execution.
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
wf_engine.eng_notified, '', to_date(NULL),to_date(NULL));
end if;
else -- Assume COMPLETE
-- Strip off optional 'COMPLETE:' tag
if (substr(result, 1, length(wf_engine.eng_completed)+1) =
wf_engine.eng_completed||':') then
result := substr(result, length(wf_engine.eng_completed)+2, 30);
else
result := substr(result, 1, 30);
end if;
Wf_Engine_Util.Complete_Activity(itemtype, itemkey, actid, result);
end if;
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Execute_Activity', itemtype, itemkey,
to_char(actid), funmode);
raise;
end execute_activity;
--
-- Function_Call (PRIVATE)
-- Call an arbitrary function using dynamic sql.
-- The function must conform to Workflow interface standard.
-- IN
-- funname - The name of the function that is going to be executed.
-- itemtype - A valid item type from (WF_ITEM_TYPES table).
-- itemkey - A string generated from the application object's primary key.
-- actid - The activity instance id.
-- funmode - Function mode (RUN/CANCEL/TIMEOUT)
-- OUT
-- result - The result of executing this function.
--
procedure function_call(funname in varchar2,
itemtype in varchar2,
itemkey in varchar2,
actid in number,
funmode in varchar2,
result out NOCOPY varchar2)
is
sqlbuf varchar2(2000);
temp varchar2(120);
defer_mode boolean := false;
setctx_mode boolean := false;
acttype varchar2(8);
actdate date;
executed boolean;
begin
begin
begin
savepoint do_execute;
-- First initialize context if not already done in this session
if ((wf_engine.setctx_itemtype = itemtype) and
(wf_engine.setctx_itemkey = itemkey)) then
null; -- Already initialized with correct item
else
-- NOTE: Be sure to set setctx globals BEFORE calling
-- execute_selector_function or recursive loop will develop.
wf_engine.setctx_itemtype := itemtype;
wf_engine.setctx_itemkey := itemkey;
-- check TEST_CTX
temp := Wf_Engine_Util.Execute_Selector_Function(itemtype, itemkey,
wf_engine.eng_testctx);
if (nvl(temp, 'TRUE') = 'TRUE' ) then
-- it does not care about the context (null)
-- or the context is already correct ('TRUE')
-- do nothing in either case
null;
elsif (temp = 'FALSE') then
if (wf_engine.preserved_context) then
defer_mode := true;
else
setctx_mode := true;
end if;
elsif (temp = 'NOTSET') then
setctx_mode := true;
end if;
end if;
if (defer_mode) then
-- defer to background engine means return a result of 'DEFERRED'
-- do not run the actual function, return right away.
result := wf_engine.eng_deferred;
return;
end if;
if (setctx_mode) then
temp := Wf_Engine_Util.Execute_Selector_Function(itemtype, itemkey,
wf_engine.eng_setctx);
end if;
Wf_Core.Clear;
if ( upper(funname) <> 'WF_STANDARD.NOOP') then
temp := '012345678901234567890123456789012345678901234567890123456789'||
'012345678901234567890123456789012345678901234567890123456789';
Wf_Function_Call.Execute(funname, itemtype, itemkey, actid, funmode,
temp, executed);
if (not executed) then
-- Funname came from info entered through builder
-- We may further check if there are ilegal characters like ';'
-- However, this may cause performance impact. Maybe better
-- verify somewhere else first.
-- BINDVAR_SCAN_IGNORE
sqlbuf := 'begin ' || funname || ' (:v1, :v2, :v3, :v4, :v5); end;';
execute immediate sqlbuf using
in itemtype,
in itemkey,
in actid,
in funmode,
in out temp;
end if;
-- Check for no return value error.
-- No value was returned if temp is still the placeholder
-- value sent in.
if (temp =
'012345678901234567890123456789012345678901234567890123456789'||
'012345678901234567890123456789012345678901234567890123456789')
then
if (actid is null) then
-- This is a selector function call so we expect null result
-- Set result to null so the calling function will ignore it.
temp := '';
else
--Check if the acitvity is of type Notification.
/* Bug 1341139
Check the activity type and incase of a post-notification function
make Resultout optional */
actdate := Wf_Item.Active_Date(itemtype, itemkey);
acttype := Wf_Activity.Instance_Type(actid, actdate);
if (acttype = wf_engine.eng_notification) then
temp := null;
else
-- This is a real function. Set an error.
Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, actid,
wf_engine.eng_noresult, FALSE);
temp := wf_engine.eng_error||':'||wf_engine.eng_noresult;
end if;
end if;
end if;
if (substr(temp, 1, 5) = wf_engine.eng_error) then
rollback to do_execute;
end if;
result := temp;
else
result := wf_engine.eng_completed||':'||wf_engine.eng_null;
end if;
exception
when OTHERS then
rollback to do_execute;
raise;
end;
exception
when NO_SAVEPOINT then
Wf_Core.Token('FUNCTION', funname);
Wf_Core.Token('ACTIVITY', Wf_Engine.GetActivityLabel(actid));
Wf_Core.Raise('WFENG_COMMIT_INSIDE');
end;
exception
when OTHERS then
wf_engine.setctx_itemtype := '';
wf_engine.setctx_itemkey := '';
result := wf_engine.eng_error;
Wf_Core.Context('Wf_Engine_Util', 'Function_Call', funname, itemtype,
itemkey, to_char(actid), funmode);
raise;
end function_call;
--
-- Execute_Selector_Function (PRIVATE)
-- Execute the selector function in the requested mode
-- IN
-- itemtype - itemtype
-- itemkey - itemkey
-- runmode - mode to run selector process with
-- RETURNS
-- Result of selector function, if any
--
function Execute_Selector_Function(
itemtype in varchar2,
itemkey in varchar2,
runmode in varchar2)
return varchar2
is
result varchar2(30);
status PLS_INTEGER;
witIND NUMBER;
begin
-- Look for selector function.
begin
WF_CACHE.GetItemType(itemtype, status, witIND);
if (status <> WF_CACHE.task_SUCCESS) then
SELECT NAME, WF_SELECTOR
INTO WF_CACHE.ItemTypes(witIND)
FROM WF_ITEM_TYPES
WHERE NAME = itemtype;
end if;
exception
when no_data_found then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Raise('WFENG_ITEM_TYPE');
end;
-- If no selector found, then nothing to do
if (WF_CACHE.ItemTypes(witIND).WF_SELECTOR is null) then
return(null);
end if;
-- Call selector function
begin
Wf_Engine_Util.Function_Call(WF_CACHE.ItemTypes(witIND).WF_SELECTOR,
itemtype, itemkey, null, runmode, result);
exception
when others then
-- If this is setctx call and the function failed, unset the setctx
-- globals so that subsequent calls will attempt the function again.
-- This is so repeated calls can be made in the same session when
-- debugging selector functions.
-- NOTE: Do NOT unset the flag inside function_call itself or
-- recursive loop might develop.
if (runmode = wf_engine.eng_setctx) then
wf_engine.setctx_itemtype := '';
wf_engine.setctx_itemkey := '';
end if;
raise;
end;
-- Return result unless set mode
if (runmode <> wf_engine.eng_setctx) then
return(result);
else
return(null);
end if;
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Execute_Selector_Function',
itemtype, itemkey, runmode);
raise;
end Execute_Selector_Function;
--
-- Get_Root_Process (PRIVATE)
-- Get the root process name by calling the workflow selector.
-- If there is no workflow selector available for this item type,
-- attempt to pick a default one based on starting activity.
-- IN
-- itemtype - itemtype
-- itemkey - itemkey
-- activity - starting activity instance to search for, if any
-- RETURNS
-- Root process name, or null if can't be found.
--
function Get_Root_Process(itemtype in varchar2,
itemkey in varchar2,
activity in varchar2 default '')
return varchar2
is
selector varchar2(240);
root varchar2(30); -- The root process for this item key
actdate date;
colon pls_integer;
process varchar2(30); -- Start activity parent process
label varchar2(30); -- Start activity instance label
begin
-- Look for selector function to execute
root := Wf_Engine_Util.Execute_Selector_Function(itemtype, itemkey,
wf_engine.eng_run);
-- Return root function if one found
if (root is not null) then
return(root);
end if;
-- If no selector and no start activity, return null so calling proc
-- can raise error.
if (activity is null) then
return(null);
end if;
-- Parse activity arg into and components.
colon := instr(activity, ':');
if (colon <> 0) then
-- Activity arg is :
process := substr(activity, 1, colon-1);
label := substr(activity, colon+1);
else
-- Activity arg is just instance label
process := '';
label := activity;
end if;
-- If no selector function is defined, then query if there is one and only
-- one root process for this itemtype with the given activity instance as a
-- starting activity.
-- If there is not one and only one such process return null so
-- calling function will raise error.
-- SS: Sysdate is not totally correct for the active date, but is the best
-- we can do. The item can't be created yet, because it doesn't have a
-- root process, and you can't find the root process until the item is
-- created - chicken or egg syndrome. Anyway, sysdate should be close
-- enough for almost every case since the assumption is the item will
-- be created almost immediately after finding the root process.
actdate := sysdate;
begin
select WPAP.ACTIVITY_NAME
into root
from WF_PROCESS_ACTIVITIES WPAP, WF_ACTIVITIES WAP,
WF_PROCESS_ACTIVITIES WPAC, WF_ACTIVITIES WAC
where WAP.ITEM_TYPE = get_root_process.itemtype
and WAP.NAME = 'ROOT'
and actdate >= WAP.BEGIN_DATE
and actdate < nvl(WAP.END_DATE, get_root_process.actdate+1)
and WPAP.PROCESS_ITEM_TYPE = WAP.ITEM_TYPE
and WPAP.PROCESS_NAME = WAP.NAME
and WPAP.PROCESS_VERSION = WAP.VERSION
and WAC.ITEM_TYPE = WPAP.ACTIVITY_ITEM_TYPE
and WAC.NAME = WPAP.ACTIVITY_NAME
and get_root_process.actdate >= WAC.BEGIN_DATE
and get_root_process.actdate <
nvl(WAC.END_DATE, get_root_process.actdate+1)
and WPAC.PROCESS_ITEM_TYPE = WAC.ITEM_TYPE
and WPAC.PROCESS_NAME = WAC.NAME
and WPAC.PROCESS_VERSION = WAC.VERSION
and WPAC.PROCESS_NAME = nvl(get_root_process.process, WPAC.PROCESS_NAME)
and WPAC.INSTANCE_LABEL = get_root_process.label
and WPAC.START_END = wf_engine.eng_start;
exception
when too_many_rows then
-- Multiple processes use this start activity.
-- No way to distinguish which one to use - error.
return(null);
when no_data_found then
-- No processes use this start activity. Error.
return(null);
end;
return(root);
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Get_Root_Process', itemtype, itemkey,
activity);
raise;
end Get_Root_Process;
--
-- Process_Kill_ChildProcess (PRIVATE)
-- Completes all incomplete child processes with the 'FORCE' result
-- under this process.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
--
procedure process_kill_childprocess(itemtype in varchar2,
itemkey in varchar2)
is
--Cursor get all children and/or master for this process
--and abort in cascade
--First select all Child Processes to be aborted
CURSOR child_proc (p_itemtype varchar2, p_itemkey varchar2) is
SELECT wi.item_type, wi.item_key
FROM WF_ITEMS WI
WHERE END_DATE IS NULL
AND WI.ITEM_TYPE <> p_itemtype
AND WI.ITEM_KEY <> p_itemkey
START WITH WI.ITEM_TYPE = p_itemtype
AND WI.ITEM_KEY = p_itemkey
CONNECT BY PRIOR WI.ITEM_TYPE = WI.PARENT_ITEM_TYPE
AND PRIOR WI.ITEM_KEY = WI.PARENT_ITEM_KEY;
/*
--We only kill the child process
--Select all masters
CURSOR master_proc (p_itemtype varchar2, p_itemkey varchar2) is
SELECT wi.item_type, wi.item_key
FROM WF_ITEMS WI
WHERE END_DATE IS NULL
AND WI.ITEM_TYPE <> p_itemtype
AND WI.ITEM_KEY <> p_itemkey
START WITH WI.ITEM_TYPE = p_itemtype
AND WI.ITEM_KEY = p_itemkey
CONNECT BY PRIOR WI.PARENT_ITEM_TYPE = WI.ITEM_TYPE
AND PRIOR WI.PARENT_ITEM_KEY = WI.ITEM_KEY;
*/
begin
--Now open each cursor and call abort for each
for child_curs in child_proc(itemtype , itemkey) loop
wf_engine.abortprocess(child_curs.item_type,child_curs.item_key);
end loop;
--We only kill child process for this as master
--Now master
/*
for master_curs in master_proc(itemtype , itemkey) loop
wf_engine.abortprocess(master_curs.item_type,master_curs.item_key);
end loop;
*/
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Process_kill_childprocess', itemtype, itemkey);
raise;
end Process_kill_childprocess;
--
-- Process_Kill_Children (PRIVATE)
-- Completes all incomplete children activities with the 'FORCE' result
-- under this process.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- processid - The process instance id.
--
procedure process_kill_children(itemtype in varchar2,
itemkey in varchar2,
processid in number) is
childid pls_integer;
actdate date;
cursor children_to_kill (pid in pls_integer) is
SELECT
WIAS.PROCESS_ACTIVITY, WIAS.ACTIVITY_STATUS
FROM WF_PROCESS_ACTIVITIES PA, WF_PROCESS_ACTIVITIES PA1,
WF_ACTIVITIES A1, WF_ITEM_ACTIVITY_STATUSES WIAS
WHERE PA.INSTANCE_ID = pid
AND PA.ACTIVITY_ITEM_TYPE = PA1.PROCESS_ITEM_TYPE
AND PA.ACTIVITY_NAME = PA1.PROCESS_NAME
AND PA1.PROCESS_VERSION = A1.VERSION
AND PA1.PROCESS_ITEM_TYPE = A1.ITEM_TYPE
AND PA1.PROCESS_NAME = A1.NAME
AND actdate >= A1.BEGIN_DATE
AND actdate < NVL(A1.END_DATE, actdate+1)
AND PA1.INSTANCE_ID = WIAS.PROCESS_ACTIVITY
AND WIAS.ITEM_TYPE = itemtype
AND WIAS.ITEM_KEY = itemkey
AND WIAS.ACTIVITY_STATUS <> 'COMPLETE';
childarr InstanceArrayTyp; -- Place holder for all the instance id
-- selected from children_be_suspended cursor
type StatusArrayTyp is table of varchar2(8)
index by binary_integer;
statusarr StatusArrayTyp;
i pls_integer := 0;
status varchar2(8);
notid pls_integer;
user varchar2(320);
begin
-- SYNCHMODE: Do nothing here.
-- Synchmode processes must be straight-line, no branching, no
-- blocking, so there can't be anything left to kill (and if there
-- was, we couldn't know about it anyway because nothing is saved).
if (itemkey = wf_engine.eng_synch) then
return;
end if;
-- Get the active date of the item to use for process versions.
actdate := Wf_Item.Active_Date(itemtype, itemkey);
-- For loop to get all the children processes ids
for child in children_to_kill(processid) loop
childarr(i) := child.process_activity;
statusarr(i) := child.activity_status;
i := i + 1;
end loop;
childarr(i) := '';
-- While loop to handle all the children processes
i := 0;
while (childarr(i) is not null) loop
childid := childarr(i);
if (Wf_Activity.Instance_Type(childid, actdate) =
wf_engine.eng_process) then
-- If child is a process then recursively kill its children
Wf_Engine_Util.Process_Kill_Children(itemtype, itemkey, childid);
else
-- Cancel any open notifications sent by this activity
Wf_Item_Activity_Status.Notification_Status(itemtype, itemkey,
childid, notid, user);
if (notid is not null) then
begin
Wf_Notification.CancelGroup(notid,'');
exception
when others then
null; -- Ignore errors in cancelling
end;
end if;
end if;
-- If activity is defered then remove it from the deferred queue
-- if you dont remove it then the background engine will remove
-- it when it processes and finds it doesnt correspond.
if statusarr(i) = wf_engine.eng_deferred then
wf_queue.PurgeEvent(wf_queue.DeferredQueue,
wf_queue.GetMessageHandle(wf_queue.DeferredQueue,
itemtype , itemkey, childid));
end if;
-- Complete the activity with the force result
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, childid,
wf_engine.eng_completed, wf_engine.eng_force, to_date(NULL),
SYSDATE);
-- No needs to check null type because this is internal function
i := i + 1;
end loop;
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Process_Kill_Children', itemtype,
itemkey, to_char(processid));
raise;
end process_kill_children;
--
-- Suspend_Child_Processes (PRIVATE)
-- Suspends all the immediate children process activities.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- processid - The process instance id.
--
procedure suspend_child_processes(itemtype in varchar2,
itemkey in varchar2,
processid in number) is
actdate date;
-- Select all the active children process(es) under this parent process
cursor children_be_suspended(parent in pls_integer) is
SELECT
WIAS.PROCESS_ACTIVITY
FROM WF_ITEM_ACTIVITY_STATUSES WIAS, WF_PROCESS_ACTIVITIES WPA1,
WF_ACTIVITIES WA1, WF_PROCESS_ACTIVITIES WPA2, WF_ACTIVITIES WA2
WHERE WPA1.INSTANCE_ID = processid
AND WPA1.ACTIVITY_ITEM_TYPE = WA1.ITEM_TYPE
AND WPA1.ACTIVITY_NAME = WA1.NAME
AND actdate >= WA1.BEGIN_DATE
AND actdate < NVL(WA1.END_DATE, actdate+1)
AND WA1.ITEM_TYPE = WPA2.PROCESS_ITEM_TYPE
AND WA1.NAME = WPA2.PROCESS_NAME
AND WA1.VERSION = WPA2.PROCESS_VERSION
AND WPA2.ACTIVITY_ITEM_TYPE = WA2.ITEM_TYPE
AND WPA2.ACTIVITY_NAME = WA2.NAME
AND actdate >= WA2.BEGIN_DATE
AND actdate < NVL(WA2.END_DATE, actdate+1)
AND WA2.TYPE = wf_engine.eng_process
AND WPA2.INSTANCE_ID = WIAS.PROCESS_ACTIVITY
AND WIAS.ITEM_TYPE = itemtype
AND WIAS.ITEM_KEY = itemkey
AND WIAS.ACTIVITY_STATUS = 'ACTIVE'; --use literal to force index
childarr InstanceArrayTyp; -- Place holder for all the instance id
-- selected from children_be_suspended cursor
i pls_integer := 0;
begin
-- Get the active date of the item to use for process versions.
actdate := Wf_Item.Active_Date(itemtype, itemkey);
-- For loop to get all the children processes ids
for child in children_be_suspended(processid) loop
childarr(i) := child.process_activity;
i := i + 1;
end loop;
childarr(i) := '';
-- While loop to handle all the children processes
i := 0;
while (childarr(i) is not null) loop
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, childarr(i),
wf_engine.eng_suspended, null, to_date(NULL), SYSDATE);
suspend_child_processes(itemtype, itemkey, childarr(i));
i := i + 1;
end loop;
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Suspend_Child_Processes', itemtype,
itemkey, to_char(processid));
raise;
end suspend_child_processes;
--
-- Resume_Child_Processes (PRIVATE)
-- Resumes all the children process activities.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- processid - The process instance id.
--
procedure resume_child_processes(itemtype in varchar2,
itemkey in varchar2,
processid in number) is
actdate date;
-- Select all the suspended children processes under this parent process
cursor children_be_resumed(parent in pls_integer) is
SELECT
WIAS.PROCESS_ACTIVITY
FROM WF_ITEM_ACTIVITY_STATUSES WIAS, WF_PROCESS_ACTIVITIES WPA1,
WF_PROCESS_ACTIVITIES WPA2, WF_ACTIVITIES WA
WHERE WPA1.INSTANCE_ID = processid
AND WPA1.ACTIVITY_ITEM_TYPE = WA.ITEM_TYPE
AND WPA1.ACTIVITY_NAME = WA.NAME
AND actdate >= WA.BEGIN_DATE
AND actdate < NVL(WA.END_DATE, actdate+1)
AND WA.ITEM_TYPE = WPA2.PROCESS_ITEM_TYPE
AND WA.NAME = WPA2.PROCESS_NAME
AND WA.VERSION = WPA2.PROCESS_VERSION
AND WPA2.INSTANCE_ID = WIAS.PROCESS_ACTIVITY
AND WIAS.ITEM_TYPE = itemtype
AND WIAS.ITEM_KEY = itemkey
AND WIAS.ACTIVITY_STATUS = 'SUSPEND'; -- use literal to force index
childarr InstanceArrayTyp; -- Place holder for all the instance id
-- selected from children_be_resumed cursor
i pls_integer := 0;
begin
-- Get the active date of the item to use for process versions.
actdate := Wf_Item.Active_Date(itemtype, itemkey);
-- For loop to get all the children processes id
for child in children_be_resumed(processid) loop
childarr(i) := child.process_activity;
i := i + 1;
end loop;
childarr(i) := '';
-- While loop to handle all the children processes
i := 0;
while (childarr(i) is not null) loop
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, childarr(i),
wf_engine.eng_active, null, null, SYSDATE);
resume_child_processes(itemtype, itemkey, childarr(i));
i := i + 1;
end loop;
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Resume_Child_Processes', itemtype,
itemkey, to_char(processid));
raise;
end resume_child_processes;
--
-- Notification (PRIVATE)
-- This is the default notification activity function.
-- It looks up the notification info and then sends it.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- actid - The notification process activity(instance id).
-- funcmode - Function mode (RUN/CANCEL)
-- OUT
-- result - NOTIFIED:notificationid:user
--
procedure Notification(
itemtype in varchar2,
itemkey in varchar2,
actid in number,
funcmode in varchar2,
result out NOCOPY varchar2)
is
msg varchar2(30);
msgtype varchar2(8);
prole varchar2(320);
expand_role varchar2(1);
begin
-- SYNCHMODE: Not allowed
if (itemkey = wf_engine.eng_synch) then
Wf_Core.Token('OPERATION', 'Wf_Engine.Notification');
Wf_Core.Raise('WFENG_SYNCH_DISABLED');
end if;
-- Get the message, perform role, and timeout
-- msg and prole could came back as null since they are nullable columns
Wf_Activity.Notification_Info(itemtype, itemkey, actid, msg, msgtype,
expand_role);
prole := Wf_Activity.Perform_Role(itemtype, itemkey, actid);
Wf_Engine_Util.Notification_Send(itemtype, itemkey, actid, msg, msgtype,
prole, expand_role, result);
exception
when others then
wf_core.context('Wf_Engine_Util', 'Notification', itemtype, itemkey,
to_char(actid), funcmode);
raise;
end Notification;
--
-- Notification_Send (PRIVATE)
-- Actually sends the notification
-- IN
-- itemtype - a valid item type
-- itemkey - a string generated from the application object's primary key.
-- actid - the notification process activity(instance id).
-- msg - name of msg to send
-- msgtype - its message type
-- prole - performer role
-- expand_role the expand role arg for notifications
-- OUT
-- result - notified:notificationid:user
--
procedure Notification_Send(
itemtype in varchar2,
itemkey in varchar2,
actid in number,
msg in varchar2,
msgtype in varchar2,
prole in varchar2,
expand_role in varchar2,
result out NOCOPY varchar2)
is
priority number;
notid pls_integer;
ctx varchar2(2000);
duedate date;
dummy pls_integer;
performer varchar2(320);
begin
if (msg is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Raise('WFENG_NOTIFICATION_MESSAGE');
end if;
if (prole is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Raise('WFENG_NOTIFICATION_PERFORMER');
end if;
/* Bug 2156047 */
-- clear global variables to store context info
wf_engine.g_nid := '';
wf_engine.g_text := '';
-- Construct context as itemtype:key:actid
ctx := itemtype||':'||itemkey||':'||to_char(actid);
-- Mark duedate of notification as timeout date of activity
duedate := Wf_Item_Activity_Status.Due_Date(itemtype, itemkey, actid);
-- Check for #PRIORITY activity attribute to override default
-- priority of this notification
begin
priority := Wf_Engine.GetActivityAttrNumber(itemtype, itemkey, actid,
wf_engine.eng_priority, ignore_notfound=>TRUE);
exception
when others then
if (wf_core.error_name = 'WFENG_ACTIVITY_ATTR') then
-- If no priority attr default to null
priority := '';
Wf_Core.Clear;
else
raise;
end if;
end;
-- Send notification, either to expanded role or singly
-- depending on expand_role flag.
if (expand_role = 'Y') then
notid := Wf_Notification.SendGroup(prole, msgtype, msg, duedate,
'WF_ENGINE.CB', ctx, '', priority);
else
notid := Wf_Notification.Send(prole, msgtype, msg, duedate,
'WF_ENGINE.CB', ctx, '', priority);
end if;
-- Check for a change in the performer. If the notification
-- was automatically routed by Send, the assigned_user might have
-- been updated. If so, reset the performer to the new role to
-- avoid over-writing with the old value.
performer := nvl(Wf_Activity.Perform_Role(itemtype, itemkey, actid),
prole);
-- If there are no respond-type attributes to this message,
-- then no response is expected. Instead of returning a 'NOTIFIED'
-- response, return a response of '' so that the activity will
-- complete immediately instead of waiting for the notification
-- to be responded to.
begin
select 1 into dummy from sys.dual where exists
(select null
from WF_MESSAGE_ATTRIBUTES
where MESSAGE_TYPE = msgtype
and MESSAGE_NAME = msg
AND SUBTYPE = 'RESPOND');
-- Response is expected.
-- Return result of 'NOTIFIED:notid:role'.
result := Wf_Engine.Eng_Notified||':'||to_char(notid)||':'||performer;
exception
when no_data_found then
-- No respond attributes.
-- Set notification id, then complete immediately.
Wf_Item_Activity_Status.Update_Notification(itemtype, itemkey, actid,
notid, performer);
result := Wf_Engine.Eng_Null;
end;
/* Bug 2156047 */
-- Need to cache the Notification id and Performer role for
-- executing Post Notification function
Wf_Engine.g_nid := notid;
Wf_Engine.g_text := performer;
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Notification_Send', itemtype, itemkey,
to_char(actid), msgtype||':'||msg);
raise;
end Notification_Send;
--Notification_Copy (PRIVATE)
-- Copies a notification by creating a new one with same attributes.
-- IN
-- copy_nid - the notifiation id to copy
-- itemtype -
-- itemkey -
-- OUT
-- nid - tyhe new notification id that was created
--
procedure Notification_Copy (
copy_nid in number,
old_itemkey in varchar2,
new_itemkey in varchar2,
nid in out NOCOPY number) is
gid pls_integer:=0;
cursor ntf_details is
select
notification_id,
group_id,
MESSAGE_TYPE, MESSAGE_NAME,
RECIPIENT_ROLE, ORIGINAL_RECIPIENT,
STATUS,
wf_core.random,
MAIL_STATUS, PRIORITY,
BEGIN_DATE, END_DATE, DUE_DATE,
USER_COMMENT,CALLBACK,
CONTEXT
from wf_notifications
where group_id = copy_nid;
begin
for ntf_row in ntf_details loop
-- create a new notification
select WF_NOTIFICATIONS_S.NEXTVAL
into nid
from SYS.DUAL;
-- Use nid of the first notification as group id for the rest
-- but only if notification is expand_roles
if (gid =0) then
gid := nid;
end if;
insert into WF_NOTIFICATIONS (
NOTIFICATION_ID, GROUP_ID,
MESSAGE_TYPE, MESSAGE_NAME,
RECIPIENT_ROLE, ORIGINAL_RECIPIENT,
STATUS,
ACCESS_KEY,
MAIL_STATUS, PRIORITY,
BEGIN_DATE, END_DATE, DUE_DATE,
USER_COMMENT,CALLBACK,
CONTEXT)
values (
nid, gid,
ntf_row.MESSAGE_TYPE, ntf_row.MESSAGE_NAME,
ntf_row.RECIPIENT_ROLE, ntf_row.ORIGINAL_RECIPIENT,
ntf_row.STATUS,
wf_core.random,
ntf_row.MAIL_STATUS, ntf_row.PRIORITY,
ntf_row.BEGIN_DATE, ntf_row.END_DATE, ntf_row.DUE_DATE,
ntf_row.USER_COMMENT,ntf_row.CALLBACK,
replace(ntf_row.CONTEXT,':'||old_itemkey||':',':'||new_itemkey||':'));
-- create notification attributes
insert into WF_NOTIFICATION_ATTRIBUTES (
NOTIFICATION_ID,
NAME,
TEXT_VALUE,
NUMBER_VALUE,
DATE_VALUE)
select
nid,
NAME,
TEXT_VALUE,
NUMBER_VALUE,
DATE_VALUE
from WF_NOTIFICATION_ATTRIBUTES
where notification_id = ntf_row.notification_id
union all
select nid,
NAME,
TEXT_DEFAULT,
NUMBER_DEFAULT,
DATE_DEFAULT
from WF_MESSAGE_ATTRIBUTES
where MESSAGE_TYPE = ntf_row.MESSAGE_TYPE
and MESSAGE_NAME = ntf_row.MESSAGE_NAME
and name not in
(select name
from WF_NOTIFICATION_ATTRIBUTES
where notification_id = ntf_row.notification_id);
-- Copy associated Notification Comments
INSERT INTO wf_comments
(notification_id,
from_role,
from_user,
to_role,
to_user,
comment_date,
action,
action_type,
user_comment,
proxy_role)
SELECT nid,
from_role,
from_user,
to_role,
to_user,
comment_date,
action,
action_type,
user_comment,
proxy_role
FROM wf_comments
WHERE notification_id = ntf_row.notification_id;
end loop;
nid:=gid;
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Notification_Copy');
raise;
end notification_copy;
--Notification_refresh (PRIVATE)
-- Refreshes all itemtype message attribute
-- for all sent messages in this itemtype/itmekey
-- IN
-- itemtype - a valid item type
-- itemkey - a string generated from the application object's primary key.
--
procedure notification_refresh
(itemtype in varchar2,
itemkey in varchar2) is
attr_name varchar2(30);
attr_type varchar2(8);
attr_tvalue varchar2(4000);
attr_nvalue number;
attr_dvalue date;
cursor message_attrs_cursor(itemtype varchar2, itemkey varchar2) is
select ma.NAME, ma.TYPE, ma.SUBTYPE,
ma.TEXT_DEFAULT, ma.NUMBER_DEFAULT, ma.DATE_DEFAULT,
n.notification_id
from wf_item_activity_statuses_h ias,
wf_notifications n,
wf_message_attributes ma
where ias.item_type = itemtype
and ias.item_key = itemkey
and ias.notification_id = n.notification_id
and ma.message_type = n.message_type
and ma.message_name = n.message_name
and ma.value_type = 'ITEMATTR';
begin
--
-- Look up all notification attributes and reset them
--
for message_attr_row in message_attrs_cursor(itemtype, itemkey) loop
--dont call the notification callback function because this will
--only ever be called from the engine.
attr_name := message_attr_row.name;
attr_type := message_attr_row.type;
attr_tvalue := '';
attr_nvalue := '';
attr_dvalue := '';
if (attr_type = 'NUMBER') then
attr_nvalue := wf_engine.GetItemAttrNumber(itemtype, itemkey, attr_name);
elsif (attr_type = 'DATE') then
attr_dvalue := wf_engine.GetItemAttrDate(itemtype, itemkey, attr_name);
else
attr_tvalue := wf_engine.GetItemAttrText(itemtype, itemkey, attr_name);
end if;
--
-- Update the notification attribute
--
update WF_NOTIFICATION_ATTRIBUTES
set TEXT_VALUE = attr_tvalue,
NUMBER_VALUE = attr_nvalue,
DATE_VALUE = attr_dvalue
where notification_id = message_attr_row.notification_id;
end loop;
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Notification_refresh');
raise;
end notification_refresh;
--
-- Execute_Error_Process (Private)
-- Attempts to run an error process for an activity that has error'ed out.
-- IN
-- itemtype - a valid item type
-- itemkey - a string generated from the application object's primary key.
-- actid - the notification process activity(instance id).
-- result - activity result code
--
procedure execute_error_process (
itemtype in varchar2,
itemkey in varchar2,
actid in number,
result in varchar2)
is
errortype varchar2(8) := '';
errorprocess varchar2(30) := '';
erractid pls_integer;
actdate date;
root varchar2(30);
version pls_integer;
rootid pls_integer;
errkey varchar2(240);
notid pls_integer;
user varchar2(320);
label varchar2(62);
errname varchar2(30);
errmsg varchar2(2000);
errstack varchar2(4000);
err_url varchar2(2000);
err_userkey varchar2(240);
newstatus varchar2(8);
newresult varchar2(30);
begin
actdate := Wf_Item.Active_Date(itemtype, itemkey);
--
-- Look for an error process to execute.
-- If this activity does not have an error process, look for the
-- nearest parent process activity that does have one.
--
Wf_Item.Root_Process(itemtype, itemkey, root, version);
rootid := Wf_Process_Activity.RootInstanceId(itemtype, itemkey, root);
if (rootid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', root);
Wf_Core.Raise('WFENG_ITEM_ROOT');
end if;
erractid := actid;
Wf_Activity.Error_Process(erractid, actdate, errortype, errorprocess);
while ((errorprocess is null) and (erractid <> rootid)) loop
erractid := Wf_Engine_Util.Activity_Parent_Process(itemtype, itemkey,
erractid);
Wf_Activity.Error_Process(erractid, actdate, errortype, errorprocess);
end loop;
-- If no error process, then nothing to do.
-- Provided WF_HANDLEERRORS is set to 'N'
if (errorprocess is null) then
--Bug 2769454
--We need to be able to launch atleast the deafult error
--process if no process has been defined on process or
--the parent process.
--To maintain the old functionality we would create a token
--WF_HANDLEERRORS which if set to 'Y' we would run the default_error
--process and incase of 'N' (anything other than N is traeted as Y)
-- would have the old behaviour.
--For eliminating potential infinite loop now if
--there is a scenario of the default error processing
--erroring out due to some reason
if (( wf_core.translate('WF_HANDLEERRORS') <> 'N')
AND (itemtype <> 'WFERROR')) then
--Set the error process and type to DEFAULT_ERROR
errortype := 'WFERROR';
errorprocess := 'DEFAULT_ERROR';
else
return;
end if;
end if;
-- Get key for errorprocess: concatenate WF to ensure unique
select 'WF'||to_char(WF_ERROR_PROCESSES_S.NEXTVAL)
into errkey
from SYS.DUAL;
-- Create process and set item parent columns with ids of
-- activity initiating error.
Wf_Engine.CreateProcess(errortype, errkey, errorprocess);
wf_engine.SetItemParent(errortype, errkey, itemtype, itemkey,
to_char(actid));
-- Select and set pre-defined error attributes.
wf_item_activity_status.notification_status(itemtype, itemkey, actid,
notid, user);
label := Wf_Engine.GetActivityLabel(actid);
wf_item_activity_status.error_info(itemtype, itemkey, actid,
errname, errmsg, errstack);
-- look up the monitor URL
err_url := WF_MONITOR.GetEnvelopeURL
( x_agent => wf_core.translate('WF_WEB_AGENT'),
x_item_type => itemtype,
x_item_key => itemkey,
x_admin_mode => 'YES');
-- look up the user key
err_userkey := Wf_Engine.GetItemUserKey(itemtype, itemkey);
Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
'ERROR_ITEM_TYPE', itemtype);
Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
'ERROR_ITEM_KEY', itemkey);
Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
'ERROR_ACTIVITY_LABEL', label);
Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'NUMBER',
'ERROR_ACTIVITY_ID', actid);
Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
'ERROR_RESULT_CODE', result);
Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'NUMBER',
'ERROR_NOTIFICATION_ID', to_char(notid));
Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
'ERROR_ASSIGNED_USER', user);
Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
'ERROR_NAME', errname);
Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
'ERROR_MESSAGE', errmsg);
Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
'ERROR_STACK', errstack);
Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
'ERROR_MONITOR_URL', err_url);
Wf_Engine_Util.SetErrorItemAttr(errortype, errkey, 'TEXT',
'ERROR_USER_KEY', err_userkey);
-- Run the error process.
Wf_Engine.StartProcess(errortype, errkey);
exception
when others then
-- If an error is raised in error process, do NOT raise another exception.
-- Append the new error to the original error in WIAS error columns,
-- then clear and ignore the exception.
Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, actid, '', TRUE);
Wf_Core.Clear;
end Execute_Error_Process;
--
-- SetErrorItemAttr (PRIVATE)
-- Called by execute_error_process to set error item attributes.
-- IN
-- error_type - error process itemtype
-- error_key - error process itemkey
-- attrtype - attribute type
-- item_attr - attribute name
-- avalue - attribute value
--
procedure SetErrorItemAttr (
error_type in varchar2,
error_key in varchar2,
attrtype in varchar2,
item_attr in varchar2,
avalue in varchar2)
is
begin
if (attrtype = 'TEXT') then
Wf_Engine.SetItemAttrText(error_type, error_key, item_attr, avalue);
else
Wf_Engine.SetItemAttrNumber(error_type, error_key, item_attr, to_number(avalue));
end if;
exception
when others then
if (wf_core.error_name = 'WFENG_ITEM_ATTR') then
if (attrtype = 'TEXT') then
Wf_Engine.AddItemAttr(error_type, error_key, item_attr, avalue);
WF_CORE.Clear;
else
Wf_Engine.AddItemAttr(error_type, error_key, item_attr, '',
to_number(avalue));
WF_CORE.Clear;
end if;
else
raise;
end if;
end SetErrorItemAttr;
--
-- Execute_Post_NTF_Function (PRIVATE)
-- Execute the post-notification function to see if activity should complete.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- actid - The notification process activity(instance id).
-- funmode - Run post-notification function in run or cancel mode
-- OUT
-- pntfstatus - Flag to indicate post-notification results. Values are
-- 'WAITING' - post-notification function for activity is not yet complete
-- 'COMPLETE' - post-notification function for activity is complete
-- null - this is not a post-notification activity
-- pntfresult - Result of post-notification function if pntfstatus='COMPLETE'
--
procedure Execute_Post_NTF_Function (itemtype in varchar2,
itemkey in varchar2,
actid in number,
funmode in varchar2,
pntfstatus out NOCOPY varchar2,
pntfresult out NOCOPY varchar2)
is
message varchar2(30);
msgtype varchar2(8);
expand_role varchar2(1);
funcname varchar2(240);
result varchar2(240);
errcode varchar2(30);
l_notid number;
l_responder varchar2(320);
begin
-- See if a post-notification function was attached
funcname := Wf_Activity.Activity_Function(itemtype, itemkey, actid);
-- If there is no post-notification function,
-- then no action is required so exit immediately.
if (funcname is null) then
pntfstatus := null;
pntfresult := null;
return;
end if;
/* Bug 2156047 */
-- Set global context areas.
-- This is context information for use by post ntf function
-- when executing in modes RUN, RESPOND, TRANSFER etc.
Wf_Item_Activity_Status.Notification_Status(itemtype, itemkey, actid,
l_notid, l_responder);
Wf_Engine.context_nid := l_notid;
Wf_Engine.context_text := l_responder;
-- There is a post-notification function. Execute it.
begin
Wf_Engine_Util.Function_Call(funcname, itemtype, itemkey, actid, funmode,
result);
exception
-- Set error info columns if post-notification function raised exception,
-- unless running in cancel mode.
when others then
if (funmode <> wf_engine.eng_cancel) then
Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, actid,
wf_engine.eng_exception, FALSE);
result := wf_engine.eng_error||':'||wf_engine.eng_exception;
end if;
end;
/* Bug 2156047 */
-- clear context values
Wf_Engine.context_nid := '';
Wf_Engine.context_text := '';
-- The engine does not care about the result when undoing a function
if (funmode = wf_engine.eng_cancel) then
return;
end if;
-- Handle different results
if ((result is null) or (result = wf_engine.eng_null)) then
-- Assume a null result means post-notification function is not
-- implemented.
pntfstatus := null;
pntfresult := null;
elsif (substr(result, 1, length(wf_engine.eng_error)) =
wf_engine.eng_error) then
-- Get the error code
errcode := substr(result, length(wf_engine.eng_error)+2, 30);
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
wf_engine.eng_error, errcode);
-- Call error_process to execute any error processes.
Wf_Engine_Util.Execute_Error_Process(itemtype, itemkey, actid, errcode);
-- Return status waiting to prevent activity from completing.
pntfstatus := wf_engine.eng_waiting;
pntfresult := null;
elsif (result = wf_engine.eng_waiting) then
-- Post-notification function is not yet completed.
-- Return status waiting to prevent activity from completing.
pntfstatus := wf_engine.eng_waiting;
pntfresult := null;
else
-- Result must be COMPLETE. Other statuses are not allowed for
-- post-notification functions.
-- Strip off optional 'COMPLETE:' tag from result
if (substr(result, 1, length(wf_engine.eng_completed)+1) =
wf_engine.eng_completed||':') then
result := substr(result, length(wf_engine.eng_completed)+2, 30);
end if;
-- Return complete status and result.
pntfstatus := wf_engine.eng_completed;
pntfresult := result;
end if;
return;
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Execute_Post_NTF_Function', itemtype,
itemkey, to_char(actid), funmode);
raise;
end Execute_Post_NTF_Function;
--
-- Execute_Notification_Callback (PRIVATE)
-- Look for a function on a notification activity and execute in
-- appropriate mode.
-- Called from CB when a notification is acted on.
-- IN
-- funcmode - callback mode (FORWARD, TRANSFER, RESPOND)
-- itemtype - item type of notification context
-- itemkey - item key of notification context
-- actid - activity of notification context
-- ctx_nid - notification id
-- ctx_text - new recipient role (FORWARD or TRANSFER)
--
procedure Execute_Notification_Callback(
funcmode in varchar2,
itemtype in varchar2,
itemkey in varchar2,
actid in number,
ctx_nid in number,
ctx_text in varchar2)
is
funcname varchar2(240);
result varchar2(2000);
errcode varchar2(2000);
begin
funcname := Wf_Activity.Activity_Function(itemtype, itemkey, actid);
-- No callback function, nothing to do.
if (funcname is null) then
return;
end if;
-- Set global context areas.
-- This is context information for use by callback function while
-- running.
Wf_Engine.context_nid := ctx_nid;
Wf_Engine.context_text := ctx_text;
-- Bug 3065814
-- Set all context information for the post-notification function
wf_engine.context_user := wf_notification.g_context_user;
wf_engine.context_user_comment := wf_notification.g_context_user_comment ;
wf_engine.context_recipient_role := wf_notification.g_context_recipient_role ;
wf_engine.context_original_recipient:= wf_notification.g_context_original_recipient;
wf_engine.context_from_role := wf_notification.g_context_from_role ;
wf_engine.context_new_role := wf_notification.g_context_new_role ;
wf_engine.context_more_info_role := wf_notification.g_context_more_info_role ;
wf_engine.context_proxy := wf_notification.g_context_proxy;
wf_engine.context_user_key := wf_engine.GetItemUserKey(itemtype, itemkey);
-- Call function in requested mode
Wf_Engine_Util.Function_Call(funcname, itemtype, itemkey, actid,
funcmode, result);
-- Error handling...
-- 1. If function raises its own exception, let it trickle up.
-- 2. If function returned a result of 'ERROR:...', convert it
-- to a generic exception and let that trickle up so that
-- the originating function will fail.
if (substr(result, 1, length(wf_engine.eng_error)) =
wf_engine.eng_error) then
errcode := substr(result, length(wf_engine.eng_error)+2, 2000);
Wf_Core.Token('ERRCODE', errcode);
Wf_Core.Raise('WFENG_NOTIFICATION_FUNCTION');
end if;
-- Clear global context areas
Wf_Engine.context_nid := '';
Wf_Engine.context_text := '';
--Bug 3065814
wf_engine.context_user := '';
wf_engine.context_user_comment := '';
wf_engine.context_recipient_role := '';
wf_engine.context_original_recipient:='';
wf_engine.context_from_role :='';
wf_engine.context_new_role :='';
wf_engine.context_more_info_role := '';
wf_engine.context_user_key := '';
wf_engine.context_proxy := '';
exception
when others then
-- Clear global context, just in case
Wf_Engine.context_nid := '';
Wf_Engine.context_text := '';
-- Bug 3065814
wf_engine.context_user := '';
wf_engine.context_user_comment := '';
wf_engine.context_recipient_role := '';
wf_engine.context_original_recipient:='';
wf_engine.context_from_role :='';
wf_engine.context_new_role :='';
wf_engine.context_more_info_role := '';
wf_engine.context_user_key := '';
wf_engine.context_proxy := '';
Wf_Core.Context('Wf_Engine_Util', 'Execute_Notification_Callback',
funcmode, itemtype, itemkey, to_char(actid),
to_char(ctx_nid)||':'||ctx_text);
raise;
end Execute_Notification_Callback;
--
-- Activity_Timeout (PUBLIC)
-- IN
-- actid - Process activity (instance id).
function Activity_Timeout(actid in number) return varchar2
is
waavIND NUMBER;
status PLS_INTEGER;
begin
-- Check Arguments
if (actid is null) then
Wf_Core.Token('ACTID', nvl(actid, 'NULL'));
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Check value_type flag for possible item_attribute ref.
WF_CACHE.GetActivityAttrValue(actid, '#TIMEOUT', status, waavIND);
if (status <> WF_CACHE.task_SUCCESS) then
open curs_activityattr (actid, '#TIMEOUT');
fetch curs_activityattr into WF_CACHE.ActivityAttrValues(waavIND);
close curs_activityattr;
end if;
if (WF_CACHE.ActivityAttrValues(waavIND).NUMBER_VALUE is not null) then
return(to_char(WF_CACHE.ActivityAttrValues(waavIND).NUMBER_VALUE));
elsif (WF_CACHE.ActivityAttrValues(waavIND).DATE_VALUE is not null) then
return(to_char(WF_CACHE.ActivityAttrValues(waavIND).DATE_VALUE)||' '||
to_char(WF_CACHE.ActivityAttrValues(waavIND).DATE_VALUE,
'HH24:MI:SS'));
elsif (WF_CACHE.ActivityAttrValues(waavIND).VALUE_TYPE = 'ITEMATTR') then
return(substrb(WF_CACHE.ActivityAttrValues(waavIND).TEXT_VALUE, 1, 30));
else
return(null);
end if;
exception
when no_data_found then
--Check to ensure that cursor is not open
if (curs_activityattr%ISOPEN) then
CLOSE curs_activityattr;
end if;
WF_CACHE.ActivityAttrValues(waavIND).PROCESS_ACTIVITY_ID := actid;
WF_CACHE.ActivityAttrValues(waavIND).NAME := '#TIMEOUT';
WF_CACHE.ActivityAttrValues(waavIND).VALUE_TYPE := 'CONSTANT';
WF_CACHE.ActivityAttrValues(waavIND).TEXT_VALUE := '';
WF_CACHE.ActivityAttrValues(waavIND).NUMBER_VALUE := '';
WF_CACHE.ActivityAttrValues(waavIND).DATE_VALUE := to_date(NULL);
return(null);
when others then
--Check to ensure that cursor is not open
if (curs_activityattr%ISOPEN) then
CLOSE curs_activityattr;
end if;
return(null);
end Activity_Timeout;
--
-- Event_Activity (PRIVATE)
-- Execute an event activity.
-- IN
-- itemtype - A valid item type
-- itemkey - A string generated from the application object's primary key.
-- actid - The event process activity(instance id).
-- funcmode - Function mode (RUN/CANCEL)
-- OUT
-- result - event activity reslt
--
procedure Event_Activity(
itemtype in varchar2,
itemkey in varchar2,
actid in number,
funcmode in varchar2,
result out NOCOPY varchar2)
is
event_name varchar2(240); -- Event name filter
direction varchar2(8); -- Event direction (receive/raise/send)
evtname varchar2(240); -- Event name (for raise)
evtkey varchar2(2000); -- Event key (for raise)
msgdata clob; -- Message contents as clob (for raise)
evtmsg wf_event_t; -- Event message (for send)
attr varchar2(4000); -- Attrs for event override (for send)
priority number; -- Event priority (for send)
atsign pls_integer; -- Used to parse agent@system
outagent wf_agent_t; -- Out agent override (send)
toagent wf_agent_t; -- To agent override (send)
atype varchar2(8);
asubtype varchar2(8);
aformat varchar2(240);
avalue varchar2(4000);
parameterlist wf_parameter_list_t;
parametert wf_parameter_t;
counter pls_integer;
block_mode varchar2(1);
cb_event_name varchar2(240);
cb_event_key varchar2(2000);
-- BUG 2452470 CTILLEY
-- Updated the cursor to select value_type and text_value to pass based
-- on the type since the item attribute may not be the same name as the
-- activity attribute
CURSOR CURS_ACTATTRS IS
SELECT NAME, VALUE_TYPE, TEXT_VALUE
FROM WF_ACTIVITY_ATTR_VALUES
WHERE PROCESS_ACTIVITY_ID = EVENT_ACTIVITY.ACTID
AND substrb(NAME,1,1) <> '#';
--Bug 2761887
l_length integer;
-- Bug 3908657
l_fresh_parameterlist boolean;
begin
-- Do nothing in cancel or timeout mode
if (funcmode <> wf_engine.eng_run) then
result := wf_engine.eng_null;
return;
end if;
-- Get event name and direction
Wf_Activity.Event_Info(itemtype, itemkey, actid, event_name,
direction);
if (direction = wf_engine.eng_receive) then
-- RECEIVE event
-- Block and wait for event to be received.
result := wf_engine.eng_notified||':'||wf_engine.eng_null||
':'||wf_engine.eng_null;
return;
elsif (direction = wf_engine.eng_raise) then
-- RAISE event
-- Retrieve applicable attrs
-- #EVENTNAME
evtname := Wf_Engine.GetActivityAttrText(itemtype, itemkey, actid,
wf_engine.eng_eventname);
if (evtname is null) then
Wf_Core.Token('#EVENTNAME', '');
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- #EVENTKEY
evtkey := Wf_Engine.GetActivityAttrText(itemtype, itemkey, actid,
wf_engine.eng_eventkey);
if (evtkey is null) then
Wf_Core.Token('#EVENTKEY', '');
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- #EVENTMESSAGE (may be null)
msgdata := Wf_Engine.GetActivityAttrClob(itemtype, itemkey, actid,
Wf_Engine.eng_eventmessage);
--Bug #2761887
--Now verify if we have the reserved activity attribute
--#EVENTMESSAGE2 is set
begin
evtmsg := Wf_Engine.getActivityAttrEvent(itemtype, itemkey,
actid, wf_engine.eng_defaultevent);
exception
when others then
--If this atrribute does not exist the exception is
--raised we just ignore it.
if (wf_core.error_name ='WFENG_ACTIVITY_ATTR') then
--we will initialise the event here so that the
--parameterlist is usable down the line
wf_event_t.initialize(evtmsg);
--clear the error stack
wf_core.clear;
else
--Any other error raise it
raise;
end if;
end ;
--If the clob and parameterlist exist we will just set them
l_length := dbms_lob.getlength(msgdata);
IF l_length IS NULL THEN
--Set the event message from the default #RAISEEVENT
msgdata := evtmsg.event_data ;
--we will add the parameterlist at the end
end if;
-- Check if any Activity Attributes set, these will
-- be set in the parameter list
for attr in curs_actattrs loop
-- Reset Attribute Value
avalue :=null;
-- Bug2452470 CTILLEY: Need to take into account that the activity
-- attribute may not be an item attribute and even if it is it may
-- not be the same name as the activity attribute.
-- Get the activity attribute type
if attr.value_type = 'ITEMATTR' then
wf_engine.GetItemAttrInfo(itemtype, attr.text_value, atype,
asubtype, aformat);
else
wf_engine.GetActivityAttrInfo(itemtype, itemkey, actid, attr.name,
atype, asubtype, aformat);
end if;
-- NUMBER Value
if (atype = 'NUMBER') then
avalue:= to_char(wf_engine.GetActivityAttrNumber(itemtype,itemkey,
actid, attr.name),wf_core.canonical_number_mask);
-- DATE Value
elsif (atype = 'DATE') then
avalue:=to_char(wf_engine.GetActivityAttrDate(itemtype,itemkey,
actid, attr.name),nvl(aformat,wf_core.canonical_date_mask));
-- TEXT/LOOKUP/ROLE/ATTR etc Value
else
avalue:=substr(wf_engine.GetActivityAttrText(itemtype,itemkey,
actid, attr.name),1,2000);
end if;
--Set the Value into the Parameter List
--Bug 2761887
--Lets use the addparametertolist for the event parameterlist
--so that any attribute of the same name overwrites
--existing ones in the default events parameter list.
evtmsg.AddParameterToList(attr.name,avalue);
end loop;
-- We also need to set the current work item as the master
-- work item or context into the parameter list, this will be picked
-- up during the Receive Event processing
--Bug 2761887
evtmsg.addparametertolist('#CONTEXT',itemtype||':'||itemkey);
--Now set the event parameterlistr into the parameterlist
parameterlist := evtmsg.GETPARAMETERLIST;
-- Raise event
Wf_Event.Raise(
p_event_name => evtname,
p_event_key => evtkey,
p_event_data => msgdata,
p_parameters => parameterlist);
elsif (direction = wf_engine.eng_send) then
-- SEND event
-- Get base event struct to send
-- #EVENTMESSAGE
evtmsg := Wf_Engine.GetActivityAttrEvent(itemtype, itemkey, actid,
Wf_Engine.eng_eventmessage);
if (evtmsg is null) then
Wf_Core.Token('#EVENTMESSAGE', '');
Wf_Core.Raise('WFSQL_ARGS');
end if;
-- Initialize the variables here
outagent := wf_agent_t(NULL, NULL); -- Out agent override (send)
toagent := wf_agent_t(NULL, NULL); -- To agent override (send)
parametert := wf_parameter_t(null, null);
counter := 0;
-- Other attributes are treated as over-rides to values in the
-- event message struct retrieved above.
-- Use them to reset values if present.
-- #EVENTNAME
attr := Wf_Engine.GetActivityAttrText(itemtype, itemkey, actid,
wf_engine.eng_eventname);
if (attr is not null) then
evtmsg.SetEventName(attr);
end if;
-- #EVENTKEY
attr := Wf_Engine.GetActivityAttrText(itemtype, itemkey, actid,
wf_engine.eng_eventkey);
if (attr is not null) then
evtmsg.SetEventKey(attr);
end if;
-- #EVENTOUTAGENT
attr := Wf_Engine.GetActivityAttrText(itemtype, itemkey, actid,
Wf_Engine.eng_eventoutagent);
if (attr is not null) then
-- Value must be in format @
atsign := instr(attr, '@');
if (atsign is null) then
Wf_Core.Token('#EVENTOUTAGENT', attr);
Wf_Core.Raise('WFSQL_ARGS');
end if;
outagent.setname(substr(attr, 1, atsign-1));
outagent.setsystem(substr(attr, atsign+1));
evtmsg.SetFromAgent(outagent);
end if;
-- #EVENTTOAGENT
attr := Wf_Engine.GetActivityAttrText(itemtype, itemkey, actid,
Wf_Engine.eng_eventtoagent);
if (attr is not null) then
-- Value must be in format @
atsign := instr(attr, '@');
if (atsign is null) then
Wf_Core.Token('#EVENTTOAGENT', attr);
Wf_Core.Raise('WFSQL_ARGS');
end if;
toagent.setname(substr(attr, 1, atsign-1));
toagent.setsystem(substr(attr, atsign+1));
evtmsg.SetToAgent(toagent);
end if;
-- #PRIORITY
begin
priority := Wf_Engine.GetActivityAttrNumber(itemtype, itemkey, actid,
wf_engine.eng_priority);
if (priority is not null) then
evtmsg.SetPriority(priority);
end if;
exception
when others then
if (wf_core.error_name = 'WFENG_ACTIVITY_ATTR') then
-- Ignore if priority attr not found
Wf_Core.Clear;
else
raise;
end if;
end;
-- Correlation ID
-- Set message correlation id to itemkey if not already set
if (evtmsg.GetCorrelationId is null) then
evtmsg.SetCorrelationId(itemkey);
end if;
-- Bug 2065730
-- Initialize the parameterlist with the existing parameterlist
-- of the event.
-- Obtain the activity attributes through the cursor
-- Build the attribute name and value in the parameter list
-- parameterlist
-- Call setParameterList and set the parameter list
-- This is done before passing the event to the SEND.
parameterlist := evtmsg.getParameterList();
-- Bug 3908657
-- Avoid duplicate attribute by calling AddParamToList()
-- Keep the optimization if we start with a fresh list,
-- since addParameterToList loop through all the attributes
-- to avoid duplicate, it could be expansive.
if (parameterlist is null) then
l_fresh_parameterlist := true;
else
l_fresh_parameterlist := false;
end if;
if (l_fresh_parameterlist) then
parameterlist := wf_parameter_list_t(null);
parametert.SetName('#CONTEXT');
parametert.SetValue(itemtype||':'||itemkey);
parameterlist(1) := parametert;
counter := 1;
else
evtmsg.addParameterToList('#CONTEXT',itemtype||':'||itemkey);
end if;
for attr in curs_actattrs loop
avalue :=null;
wf_engine.GetActivityAttrInfo(itemtype, itemkey, actid,
attr.name, atype, asubtype, aformat);
if (atype = 'NUMBER') then
avalue := to_char(wf_engine.GetActivityAttrNumber(
itemtype,itemkey,actid, attr.name),
wf_core.canonical_number_mask);
elsif (atype = 'DATE') then
avalue := to_char(wf_engine.GetActivityAttrDate(
itemtype,itemkey,actid, attr.name),
nvl(aformat,wf_core.canonical_date_mask));
else
avalue := substr(wf_engine.GetActivityAttrText(
itemtype,itemkey,actid, attr.name),1,2000);
end if;
if (l_fresh_parameterlist) then
-- Set the Value into the Parameter List
parameterlist.extend;
counter := counter + 1;
parametert.SetName(attr.name);
parametert.SetValue(avalue);
parameterlist(counter) := parametert;
else
evtmsg.addParameterToList(attr.name,avalue);
end if;
end loop;
if (l_fresh_parameterlist) then
evtmsg.setParameterList(parameterlist);
end if;
-- End 2065730
-- Bug 2294745 - Enh for OTA callback to workflow
-- add activity id to event parameter list
evtmsg.addParameterToList('ACTIVITY_ID',actid);
-- get block mode and add to event parameterlist
block_mode := wf_engine.GetActivityAttrText(itemtype, itemkey, actid,
wf_engine.eng_block_mode, true);
if (block_mode is not null) then
evtmsg.addParameterToList(wf_engine.eng_block_mode, block_mode);
end if;
-- get cb event name and add to event parameterlist
cb_event_name := wf_engine.GetActivityAttrText(itemtype, itemkey, actid,
wf_engine.eng_cb_event_name, true);
if (cb_event_name is not null) then
evtmsg.addParameterToList(wf_engine.eng_cb_event_name, cb_event_name);
end if;
-- get cb event key and add to event parameterlist
cb_event_key := wf_engine.GetActivityAttrText(itemtype, itemkey, actid,
wf_engine.eng_cb_event_key, true);
if (cb_event_key is not null) then
evtmsg.addParameterToList(wf_engine.eng_cb_event_key, cb_event_key);
end if;
-- End 2294745
-- Send event
Wf_Event.Send(p_event => evtmsg);
if (wf_core.Translate('WF_INSTALL')='EMBEDDED') then
ECX_ERRORLOG.Outbound_Log (p_event => evtmsg);
end if;
-- Checking for reserved attribute to store msgid.
begin
WF_ENGINE.SetItemAttrText(itemtype, itemkey,
WF_ENGINE.GetActivityAttrText(itemtype, itemkey, actid,
'#WF_EVT_MSGID'),
rawtohex(WF_EVENT.g_msgID));
exception
when others then
if (WF_CORE.error_name = 'WFENG_ACTIVITY_ATTR') then
null; --If the activity attribute is not setup by the ItemType
--developer, we will do nothing.
else
raise; --Some other problem has occured.
end if;
end;
else
-- Unrecognized event direction, raise error
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', itemkey);
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Token('DIRECTION', direction);
Wf_Core.Raise('WFSQL_INTERNAL');
end if;
--If block mode is set to 'Y'then the activity status is
--set to NOTIFIED.
if (block_mode = 'Y') then
result := wf_engine.eng_notified||':'||wf_engine.eng_null||':'
||wf_engine.eng_null;
else
-- when block_mode is null or is not 'Y'
result := 'COMPLETE:#NULL';
end if;
exception
when others then
Wf_Core.Context('Wf_Engine_Util', 'Event_Activity', itemtype,
itemkey, to_char(actid), funcmode);
raise;
end Event_Activity;
end Wf_Engine_Util;
/
--show errors package body WF_ENGINE_UTIL
--select to_date( 'SQLERROR') from user_errors
--where type = 'PACKAGE BODY'
--and name = 'WF_ENGINE_UTIL'
--/
REM ================================================================
commit;
/*=======================================================================+
| Copyright (c) 1995 Oracle Corporation Redwood Shores, California, USA|
| All rights reserved. |
+=======================================================================+
|
| DESCRIPTION
| PL/SQL body for package: WF_ACTIVITY
| NOTES
| This package contains utilities used internally by the Workflow
| Engine. It is not for public use and may be changed without notice.
*=======================================================================*/
create or replace package body WF_ACTIVITY as
/* $Header: wfengb.pls 26.108 2005/03/02 05:14:50 rwunderl ship $ */
c_actid pls_integer;
c_actdate date;
c_type varchar2(8);
c_rerun varchar2(8);
c_cost number;
c_error_type varchar2(8);
c_error_process varchar2(30);
c_message varchar2(30);
c_msgtype varchar2(8);
c_expand_role varchar(1);
c_function varchar2(240);
c_function_type varchar2(30);
c_prole varchar2(320);
c_prole_type varchar2(8);
c_start_end varchar2(8);
c_event_name varchar2(240);
c_direction varchar2(8);
--
-- ClearCache
-- Clear runtime cache
--
procedure ClearCache
is
begin
wf_activity.c_actid := '';
wf_activity.c_actdate := to_date(NULL);
wf_activity.c_type := '';
wf_activity.c_rerun := '';
wf_activity.c_cost := '';
wf_activity.c_error_type := '';
wf_activity.c_error_process := '';
wf_activity.c_message := '';
wf_activity.c_msgtype := '';
wf_activity.c_expand_role := '';
wf_activity.c_function := '';
wf_activity.c_function_type := '';
wf_activity.c_prole := '';
wf_activity.c_prole_type := '';
wf_activity.c_start_end := '';
wf_activity.c_event_name := '';
wf_activity.c_direction := '';
exception
when others then
Wf_Core.Context('Wf_Activity', 'ClearCache');
raise;
end ClearCache;
--
-- InitCache (PRIVATE)
-- Initialize package cache
-- IN
-- actid - activity instance id
-- actdate - active date
--
procedure InitCache(
actid in number,
actdate in date)
is
waIND NUMBER;
status PLS_INTEGER;
begin
-- Check for refresh
if ((actid = wf_activity.c_actid) and
(actdate = wf_activity.c_actdate)) then
return;
end if;
-- Checking global cache.
WF_CACHE.GetProcessActivityInfo(actid, actdate, status, waIND);
if (status <> WF_CACHE.task_SUCCESS) then
waIND := 0;
select WA.ITEM_TYPE, WA.NAME, WA.VERSION, WA.TYPE, WA.RERUN,
WA.EXPAND_ROLE, WA.COST, WA.ERROR_ITEM_TYPE, WA.ERROR_PROCESS,
WA.FUNCTION, WA.FUNCTION_TYPE, WA.MESSAGE, WA.BEGIN_DATE,
WA.END_DATE, WA.DIRECTION, WPA.PROCESS_ITEM_TYPE,
WPA.PROCESS_NAME, WPA.PROCESS_VERSION, WPA.ACTIVITY_ITEM_TYPE,
WPA.ACTIVITY_NAME, WPA.INSTANCE_ID, WPA.INSTANCE_LABEL,
WPA.PERFORM_ROLE, WPA.PERFORM_ROLE_TYPE, WPA.START_END,
WPA.DEFAULT_RESULT
into WF_CACHE.Activities(waIND).ITEM_TYPE,
WF_CACHE.Activities(waIND).NAME,
WF_CACHE.Activities(waIND).VERSION,
WF_CACHE.Activities(waIND).TYPE,
WF_CACHE.Activities(waIND).RERUN,
WF_CACHE.Activities(waIND).EXPAND_ROLE,
WF_CACHE.Activities(waIND).COST,
WF_CACHE.Activities(waIND).ERROR_ITEM_TYPE,
WF_CACHE.Activities(waIND).ERROR_PROCESS,
WF_CACHE.Activities(waIND).FUNCTION,
WF_CACHE.Activities(waIND).FUNCTION_TYPE,
WF_CACHE.Activities(waIND).MESSAGE,
WF_CACHE.Activities(waIND).BEGIN_DATE,
WF_CACHE.Activities(waIND).END_DATE,
WF_CACHE.Activities(waIND).DIRECTION,
WF_CACHE.ProcessActivities(actid).PROCESS_ITEM_TYPE,
WF_CACHE.ProcessActivities(actid).PROCESS_NAME,
WF_CACHE.ProcessActivities(actid).PROCESS_VERSION,
WF_CACHE.ProcessActivities(actid).ACTIVITY_ITEM_TYPE,
WF_CACHE.ProcessActivities(actid).ACTIVITY_NAME,
WF_CACHE.ProcessActivities(actid).INSTANCE_ID,
WF_CACHE.ProcessActivities(actid).INSTANCE_LABEL,
WF_CACHE.ProcessActivities(actid).PERFORM_ROLE,
WF_CACHE.ProcessActivities(actid).PERFORM_ROLE_TYPE,
WF_CACHE.ProcessActivities(actid).START_END,
WF_CACHE.ProcessActivities(actid).DEFAULT_RESULT
from WF_PROCESS_ACTIVITIES WPA, WF_ACTIVITIES WA
where WPA.INSTANCE_ID = actid
and WA.ITEM_TYPE = WPA.ACTIVITY_ITEM_TYPE
and WA.NAME = WPA.ACTIVITY_NAME
and actdate >= WA.BEGIN_DATE
and actdate < NVL(WA.END_DATE, actdate+1);
waIND :=
WF_CACHE.HashKey(WF_CACHE.ProcessActivities(actid).ACTIVITY_ITEM_TYPE ||
WF_CACHE.ProcessActivities(actid).ACTIVITY_NAME);
WF_CACHE.Activities(waIND) := WF_CACHE.Activities(0);
end if;
wf_activity.c_type := WF_CACHE.Activities(waIND).TYPE;
wf_activity.c_rerun := WF_CACHE.Activities(waIND).RERUN;
wf_activity.c_cost := WF_CACHE.Activities(waIND).COST;
wf_activity.c_error_type := WF_CACHE.Activities(waIND).ERROR_ITEM_TYPE;
wf_activity.c_error_process := WF_CACHE.Activities(waIND).ERROR_PROCESS;
wf_activity.c_expand_role := WF_CACHE.Activities(waIND).EXPAND_ROLE;
wf_activity.c_function := WF_CACHE.Activities(waIND).FUNCTION;
wf_activity.c_function_type := nvl(WF_CACHE.Activities(waIND).FUNCTION_TYPE,
'PL/SQL');
wf_activity.c_message := WF_CACHE.Activities(waIND).MESSAGE;
wf_activity.c_msgtype := WF_CACHE.Activities(waIND).ITEM_TYPE;
wf_activity.c_event_name := WF_CACHE.Activities(waIND).EVENT_NAME;
wf_activity.c_direction := WF_CACHE.Activities(waIND).DIRECTION;
wf_activity.c_prole := WF_CACHE.ProcessActivities(actid).PERFORM_ROLE;
wf_activity.c_prole_type := WF_CACHE.ProcessActivities(actid).PERFORM_ROLE_TYPE;
wf_activity.c_start_end := WF_CACHE.ProcessActivities(actid).START_END;
-- Save cache key values
wf_activity.c_actid := actid;
wf_activity.c_actdate := actdate;
exception
when others then
Wf_Core.Context('Wf_Activity', 'InitCache', to_char(actid),
to_char(actdate));
raise;
end InitCache;
--
-- Instance_Type (PRIVATE)
-- Returns the activity type by given the process activity (instance id).
-- If no data found, raise WF_INVALID_PROCESS_ACTIVITY.(Basically this means
-- the given process activity(actid) is bad.
-- IN
-- actid - Process activity(instance id).
-- actdate - Active date
--
function Instance_Type(actid in number,
actdate in date)
return varchar2
is
begin
Wf_Activity.InitCache(actid, actdate);
return(wf_activity.c_type);
exception
when NO_DATA_FOUND then
Wf_Core.Context('Wf_Activity', 'Instance_Type', to_char(actid),
to_char(actdate));
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Token('DATE', to_char(actdate));
Wf_Core.Raise('WFENG_ACTID');
when OTHERS then
Wf_Core.Context('Wf_Activity', 'Instance_Type', to_char(actid),
to_char(actdate));
raise;
end Instance_Type;
--
-- Type (PRIVATE)
-- Returns the activity type by given the activity name and item type.
-- If no data found, return null.
-- IN
-- itemtype - Activity item type
-- activity - Activity name
-- actdate - Active date
--
function Type(itemtype in varchar2,
activity in varchar2,
actdate in date)
return varchar2
is
waIND NUMBER;
status PLS_INTEGER;
begin
WF_CACHE.GetActivity(itemtype, activity, actdate, status, waIND);
if (status <> WF_CACHE.task_SUCCESS) then
select WA.ITEM_TYPE, WA.NAME, WA.VERSION, WA.TYPE, WA.RERUN,
WA.EXPAND_ROLE, WA.COST, WA.ERROR_ITEM_TYPE,
WA.ERROR_PROCESS, WA.FUNCTION, WA.FUNCTION_TYPE, WA.EVENT_NAME,
WA.MESSAGE, WA.BEGIN_DATE, WA.END_DATE, WA.DIRECTION
into WF_CACHE.Activities(waIND).ITEM_TYPE,
WF_CACHE.Activities(waIND).NAME,
WF_CACHE.Activities(waIND).VERSION,
WF_CACHE.Activities(waIND).TYPE,
WF_CACHE.Activities(waIND).RERUN,
WF_CACHE.Activities(waIND).EXPAND_ROLE,
WF_CACHE.Activities(waIND).COST,
WF_CACHE.Activities(waIND).ERROR_ITEM_TYPE,
WF_CACHE.Activities(waIND).ERROR_PROCESS,
WF_CACHE.Activities(waIND).FUNCTION,
WF_CACHE.Activities(waIND).FUNCTION_TYPE,
WF_CACHE.Activities(waIND).EVENT_NAME,
WF_CACHE.Activities(waIND).MESSAGE,
WF_CACHE.Activities(waIND).BEGIN_DATE,
WF_CACHE.Activities(waIND).END_DATE,
WF_CACHE.Activities(waIND).DIRECTION
from WF_ACTIVITIES WA
where WA.ITEM_TYPE = itemtype
and WA.NAME = activity
and actdate >= WA.BEGIN_DATE
and actdate < nvl(WA.END_DATE, actdate+1);
end if;
return (WF_CACHE.Activities(waIND).TYPE);
exception
when NO_DATA_FOUND then
return '';
when OTHERS then
Wf_Core.Context('Wf_Activity', 'Type', itemtype, activity,
to_char(actdate));
raise;
end Type;
--
-- Info (PRIVATE)
-- Returns specific information for a given activity.
-- If no data found, raise WF_INVALID_PROCESS_ACTIVITY.(Basically this means
-- the given process activity(actid) is bad.
-- IN
-- actid - Process activity(instance id).
-- actdate - Active date
-- OUT
-- rerun - Rerun/Ignore
-- type - Activity type
-- cost - Activity cost
-- function_type - Activity function type
procedure Info(actid in number,
actdate in date,
rerun out NOCOPY varchar2,
type out NOCOPY varchar2,
cost out NOCOPY number,
function_type out NOCOPY varchar2)
is
begin
Wf_Activity.InitCache(actid, actdate);
rerun := wf_activity.c_rerun;
type := wf_activity.c_type;
cost := wf_activity.c_cost;
function_type := wf_activity.c_function_type;
exception
when NO_DATA_FOUND then
Wf_Core.Context('Wf_Activity', 'Info', to_char(actid), to_char(actdate));
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Token('DATE', to_date(actdate));
Wf_Core.Raise('WFENG_ACTID');
when OTHERS then
Wf_Core.Context('Wf_Activity', 'Info', to_char(actid), to_char(actdate));
raise;
end Info;
--
-- Ending (PRIVATE)
-- Check if activity is an END activity (end process)
-- IN
-- actid - Process activity(instance id).
-- actdate - Active date
--
function Ending(actid in number,
actdate in date)
return boolean
is
begin
Wf_Activity.InitCache(actid, actdate);
if (wf_activity.c_start_end = wf_engine.eng_end) then
return(TRUE);
else
return(FALSE);
end if;
exception
when NO_DATA_FOUND then
Wf_Core.Context('Wf_Activity', 'Ending', to_char(actid), to_char(actdate));
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Token('DATE', to_char(actdate));
Wf_Core.Raise('WFENG_ACTID');
when OTHERS then
Wf_Core.Context('Wf_Activity', 'Ending', to_char(actid), to_char(actdate));
raise;
end Ending;
--
-- Error_Process (PRIVATE)
-- Returns the error item type and process name for a given activity.
-- If no data found, raise WF_INVALID_PROCESS_ACTIVITY.(Basically this means
-- the given process activity(actid) is bad.
-- IN
-- actid - Process activity(instance id).
-- actdate - Active date
-- OUT
-- errortype - the item type containing errorprocess
-- errorprocess - the process to run for an error
procedure Error_Process(actid in number,
actdate in date,
errortype in out NOCOPY varchar2,
errorprocess in out NOCOPY varchar2)
is
begin
Wf_Activity.InitCache(actid, actdate);
errortype:=wf_activity.c_error_type;
errorprocess:=wf_activity.c_error_process;
-- for backward compatability, ensure error type is set
-- this is not necessary for 2.5 onwards.
if errorprocess is not null and errortype is null then
errortype:=wf_engine.eng_wferror;
end if;
exception
when NO_DATA_FOUND then
Wf_Core.Context('Wf_Activity', 'Error_Process', to_char(actid),
to_char(actdate));
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Token('DATE', to_char(actdate));
Wf_Core.Raise('WFENG_ACTID');
when OTHERS then
Wf_Core.Context('Wf_Activity', 'Error_Process', to_char(actid),
to_char(actdate));
raise;
end Error_Process;
--
-- Activity_Function (PRIVATE)
-- returns the activity function name.
-- IN
-- itemtype - A valid item type
-- itemkey - Item key
-- actid - The activity instance id.
--
function Activity_Function(itemtype in varchar2,
itemkey in varchar2,
actid in number)
return varchar2
is
actdate date;
begin
actdate := Wf_Item.Active_Date(itemtype, itemkey);
Wf_Activity.InitCache(actid, actdate);
return(wf_activity.c_function);
exception
when others then
Wf_Core.Context('Wf_Activity', 'Activity_Function', itemtype, itemkey,
to_char(actid));
raise;
end Activity_Function;
--
-- Activity_Function (PRIVATE)
-- returns the activity function name.
-- IN
-- itemtype - A valid item type
-- itemkey - Item key
-- actid - The activity instance id.
--
function Activity_Function_Type(itemtype in varchar2,
itemkey in varchar2,
actid in number)
return varchar2
is
actdate date;
begin
actdate := Wf_Item.Active_Date(itemtype, itemkey);
Wf_Activity.InitCache(actid, actdate);
return(wf_activity.c_function_type);
exception
when others then
Wf_Core.Context('Wf_Activity', 'Activity_Function_Type', itemtype, itemkey,
to_char(actid));
raise;
end Activity_Function_type;
--
-- Notification_Info (PRIVATE)
-- Returns notification-related info about an activity.
-- If no data found, raise WF_INVALID_PROCESS_ACTIVITY.(Basically this means
-- the given process activity(actid) is bad.
-- IN
-- itemtype - Item type
-- itemkey - Item key
-- actid - Process activity (notification activity instance id).
-- OUT
-- message - Message sent by notification
-- msgtype - Message type
-- expand_role - Flag to expand recipient list
--
procedure Notification_Info(itemtype in varchar2,
itemkey in varchar2,
actid in number,
message out NOCOPY varchar2,
msgtype out NOCOPY varchar2,
expand_role out NOCOPY varchar2)
is
actdate date;
begin
actdate := Wf_Item.Active_Date(itemtype, itemkey);
Wf_Activity.InitCache(actid, actdate);
message := wf_activity.c_message;
msgtype := wf_activity.c_msgtype;
expand_role := wf_activity.c_expand_role;
exception
when no_data_found then
Wf_Core.Context('Wf_Activity', 'Notification_Info', itemtype, itemkey,
to_char(actid));
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Raise('WFENG_ITEM_ACTIVITY_STATUS');
when others then
Wf_Core.Context('Wf_Activity', 'Notification_Info', itemtype, itemkey,
to_char(actid));
raise;
end Notification_Info;
--
-- Event_Info (PRIVATE)
-- Returns event-related info about an activity.
-- IN
-- itemtype - Item type
-- itemkey - Item key
-- actid - Process activity
-- OUT
-- event_name - Event name filter
-- direction - Event direction (RECEIVE/RAISE/SEND)
--
procedure Event_Info(
itemtype in varchar2,
itemkey in varchar2,
actid in number,
event_name out NOCOPY varchar2,
direction out NOCOPY varchar2)
is
actdate date;
begin
actdate := Wf_Item.Active_Date(itemtype, itemkey);
Wf_Activity.InitCache(actid, actdate);
event_name := wf_activity.c_event_name;
direction := wf_activity.c_direction;
exception
when no_data_found then
Wf_Core.Context('Wf_Activity', 'Event_Info', itemtype, itemkey,
to_char(actid));
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Raise('WFENG_ITEM_ACTIVITY_STATUS');
when others then
Wf_Core.Context('Wf_Activity', 'Event_Info', itemtype, itemkey,
to_char(actid));
raise;
end Event_Info;
--
-- Perform_Role (PRIVATE)
-- Get performer assigned to a notification
-- IN
-- itemtype - Item type
-- itemkey - Item key
-- actid - Process activity (notification activity instance id).
-- RETURNS
-- performrole - Role notification sent to
--
function Perform_Role(
itemtype in varchar2,
itemkey in varchar2,
actid in number)
return varchar2
is
actdate date;
assuser varchar2(320);
performrole varchar2(320);
begin
-- Initialize cache for design-time data
actdate := Wf_Item.Active_Date(itemtype, itemkey);
Wf_Activity.InitCache(actid, actdate);
-- Query WIAS for any runtime assigned_user changes
select
WIAS.ASSIGNED_USER
into assuser
from WF_ITEM_ACTIVITY_STATUSES WIAS
where WIAS.ITEM_TYPE = itemtype
and WIAS.ITEM_KEY = itemkey
and WIAS.PROCESS_ACTIVITY = actid;
--
-- Decode the performrole as:
-- 1. If WIAS.assigned_user not null, use that
-- 2. If WPA.proletype = 'CONSTANT', then use WPA.prole.
-- 3. If WPA.proletype = 'ITEMATTR', then WPA.prole is an item attr ref.
if (assuser is not null) then
performrole := assuser;
elsif (wf_activity.c_prole_type = 'CONSTANT') then
performrole := wf_activity.c_prole;
else -- (must be proletype = 'ITEMATTR')
-- Let the unknown_attribute exception propagate up if raised.
-- The substr is to prevent value errors if the attr value is too
-- long.
performrole := substrb(Wf_Engine.GetItemAttrText(itemtype, itemkey,
wf_activity.c_prole), 1, 320);
end if;
return(performrole);
exception
when no_data_found then
Wf_Core.Context('Wf_Activity', 'Perform_Role', itemtype, itemkey,
to_char(actid));
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Raise('WFENG_ITEM_ACTIVITY_STATUS');
when others then
Wf_Core.Context('Wf_Activity', 'Perform_Role', itemtype, itemkey,
to_char(actid));
raise;
end Perform_Role;
--
-- Version (PRIVATE)
-- Get the version of an activity in use on the given date.
-- IN:
-- itemtype
-- activity - Activity name
-- actdate - Active date
-- RETURNS:
-- Version of activity in use on given date
--
function Version(itemtype in varchar2,
activity in varchar2,
actdate in date)
return number
is
waIND NUMBER;
status PLS_INTEGER;
begin
WF_CACHE.GetActivity(itemtype, activity, actdate, status, waIND);
if (status <> WF_CACHE.task_SUCCESS) then
select WA.ITEM_TYPE, WA.NAME, WA.VERSION, WA.TYPE, WA.RERUN, WA.EXPAND_ROLE,
WA.COST, WA.ERROR_ITEM_TYPE, WA.ERROR_PROCESS, WA.FUNCTION,
WA.FUNCTION_TYPE, WA.EVENT_NAME, WA.MESSAGE, WA.BEGIN_DATE,
WA.END_DATE, WA.DIRECTION
into WF_CACHE.Activities(waIND).ITEM_TYPE,
WF_CACHE.Activities(waIND).NAME,
WF_CACHE.Activities(waIND).VERSION,
WF_CACHE.Activities(waIND).TYPE,
WF_CACHE.Activities(waIND).RERUN,
WF_CACHE.Activities(waIND).EXPAND_ROLE,
WF_CACHE.Activities(waIND).COST,
WF_CACHE.Activities(waIND).ERROR_ITEM_TYPE,
WF_CACHE.Activities(waIND).ERROR_PROCESS,
WF_CACHE.Activities(waIND).FUNCTION,
WF_CACHE.Activities(waIND).FUNCTION_TYPE,
WF_CACHE.Activities(waIND).EVENT_NAME,
WF_CACHE.Activities(waIND).MESSAGE,
WF_CACHE.Activities(waIND).BEGIN_DATE,
WF_CACHE.Activities(waIND).END_DATE,
WF_CACHE.Activities(waIND).DIRECTION
from WF_ACTIVITIES WA
where WA.ITEM_TYPE = itemtype
and WA.NAME = activity
and actdate >= WA.BEGIN_DATE
and actdate < nvl(WA.END_DATE, actdate+1);
end if;
return (WF_CACHE.Activities(waIND).VERSION);
exception
when NO_DATA_FOUND then
Wf_Core.Context('Wf_Activity', 'Version', itemtype, activity,
to_char(actdate));
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('NAME', activity);
Wf_Core.Token('DATE', to_char(actdate));
Wf_Core.Raise('WFENG_ACTIVITY');
when OTHERS then
Wf_Core.Context('Wf_Activity', 'Version', itemtype, activity,
to_char(actdate));
raise;
end Version;
end WF_ACTIVITY;
/
--show errors package body WF_ACTIVITY
--select to_date( 'SQLERROR') from user_errors
--where type = 'PACKAGE BODY'
--and name = 'WF_ACTIVITY'
--/
REM ================================================================
commit;
/*=======================================================================+
| Copyright (c) 1995 Oracle Corporation Redwood Shores, California, USA|
| All rights reserved. |
+=======================================================================+
|
| DESCRIPTION
| PL/SQL body for package: WF_ITEM_ACTIVITY_STATUS
| NOTES
| This package contains utilities used internally by the Workflow
| Engine. It is not for public use and may be changed without notice.
*=======================================================================*/
create or replace package body WF_ITEM_ACTIVITY_STATUS as
/* $Header: wfengb.pls 26.108 2005/03/02 05:14:50 rwunderl ship $ */
-- Runtime cache
c_itemtype varchar2(8);
c_itemkey varchar2(240);
c_actid number;
c_status varchar2(8);
c_result varchar2(30);
c_assigned_user varchar2(320);
c_notification_id number;
c_begindate date;
c_enddate date;
c_duedate date;
c_errname varchar2(30);
c_errmsg varchar2(2000);
c_errstack varchar2(4000);
-- Global Execution Counter (PRIVATE)
g_ExecCounter number := 0;
--
-- ClearCache
-- Clear runtime cache
--
procedure ClearCache
is
begin
wf_item_activity_status.c_itemtype := '';
wf_item_activity_status.c_itemkey := '';
wf_item_activity_status.c_actid := '';
wf_item_activity_status.c_status := '';
wf_item_activity_status.c_result := '';
wf_item_activity_status.c_assigned_user := '';
wf_item_activity_status.c_notification_id := '';
wf_item_activity_status.c_begindate := to_date(NULL);
wf_item_activity_status.c_enddate := to_date(NULL);
wf_item_activity_status.c_duedate := to_date(NULL);
wf_item_activity_status.c_errname := '';
wf_item_activity_status.c_errmsg := '';
wf_item_activity_status.c_errstack := '';
exception
when others then
Wf_Core.Context('Wf_Item_Activity_Status', 'ClearCache');
raise;
end ClearCache;
--
-- InitCache
-- Initialize runtime cache
--
procedure InitCache(
itemtype in varchar2,
itemkey in varchar2,
actid in number,
ignore_notfound in boolean default FALSE)
is
begin
-- Check for refresh
if ((itemtype = wf_item_activity_status.c_itemtype) and
(itemkey = wf_item_activity_status.c_itemkey) and
(actid = wf_item_activity_status.c_actid)) then
return;
end if;
-- SYNCHMODE: Error if this is not current activity.
if (itemkey = wf_engine.eng_synch) then
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', itemkey);
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Raise('WFSQL_INTERNAL');
end if;
-- Query new values
select WIAS.ACTIVITY_STATUS, WIAS.ACTIVITY_RESULT_CODE,
WIAS.ASSIGNED_USER,
WIAS.NOTIFICATION_ID,
WIAS.BEGIN_DATE, WIAS.END_DATE,
WIAS.DUE_DATE,
WIAS.ERROR_NAME, WIAS.ERROR_MESSAGE,
WIAS.ERROR_STACK
into wf_item_activity_status.c_status, wf_item_activity_status.c_result,
wf_item_activity_status.c_assigned_user,
wf_item_activity_status.c_notification_id,
wf_item_activity_status.c_begindate, wf_item_activity_status.c_enddate,
wf_item_activity_status.c_duedate,
wf_item_activity_status.c_errname, wf_item_activity_status.c_errmsg,
wf_item_activity_status.c_errstack
from WF_ITEM_ACTIVITY_STATUSES WIAS
where WIAS.ITEM_TYPE = itemtype
and WIAS.ITEM_KEY = itemkey
and WIAS.PROCESS_ACTIVITY = actid;
-- Save cache key values
wf_item_activity_status.c_itemtype := itemtype;
wf_item_activity_status.c_itemkey := itemkey;
wf_item_activity_status.c_actid := actid;
exception
when NO_DATA_FOUND then
if (ignore_notfound) then
WF_ITEM_ACTIVITY_STATUS.ClearCache;
else
Wf_Core.Context('Wf_Item_Activity_Status', 'InitCache',
itemtype, itemkey, to_char(actid));
raise;
end if;
when others then
Wf_Core.Context('Wf_Item_Activity_Status', 'InitCache',
itemtype, itemkey, to_char(actid));
raise;
end InitCache;
--
-- Update_Notification (PRIVATE)
-- Update the notification id and assigned user in WF_ITEM_ACTIVITY_STATUSES
-- table for a given item activity.
-- IN
-- itemtype - Activity item type.
-- itemkey - The item key.
-- actid - Process activity (instance id).
-- notid - Notification id for this notification activity.
-- user - The perform user for this notification activity.
--
procedure Update_Notification(itemtype in varchar2,
itemkey in varchar2,
actid in number,
notid in number,
user in varchar2)
is
begin
update
WF_ITEM_ACTIVITY_STATUSES set
NOTIFICATION_ID = notid,
ASSIGNED_USER = user
where ITEM_TYPE = itemtype
and ITEM_KEY = itemkey
and PROCESS_ACTIVITY = actid;
-- Update runtime cache if this is the current row
if ((wf_item_activity_status.c_itemtype = itemtype) and
(wf_item_activity_status.c_itemkey = itemkey) and
(wf_item_activity_status.c_actid = actid)) then
wf_item_activity_status.c_assigned_user := user;
wf_item_activity_status.c_notification_id := notid;
end if;
if (Wf_Engine.Debug) then
commit;
end if;
exception
when OTHERS then
Wf_Core.Context('Wf_Item_Activity_Status', 'Update_Notification', itemtype,
itemkey, to_char(actid), to_char(notid), user);
raise;
end Update_Notification;
--
-- Root_Status (PRIVATE)
-- Returns the status and result for the root process of this item.
-- If the process is not yet active, the status and result will be null.
-- IN
-- itemtype - Activity item type.
-- itemkey - The item key.
-- OUT
-- status - Activity status for root process of this item
-- result - Result code for root process of this item
--
procedure Root_Status(itemtype in varchar2,
itemkey in varchar2,
status out NOCOPY varchar2,
result out NOCOPY varchar2)
is
root varchar2(30);
version pls_integer;
rootid pls_integer;
begin
-- Get root process
Wf_Item.root_process(itemtype, itemkey, root, version);
if (root is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
end if;
-- Get root process actid
rootid := Wf_Process_Activity.RootInstanceId(itemtype, itemkey, root);
if (rootid is null) then
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('NAME', root);
Wf_Core.Raise('WFENG_PROCESS_RUNNABLE');
end if;
-- Get status
Wf_Item_Activity_Status.Result(itemtype, itemkey, rootid, status, result);
exception
when others then
Wf_Core.Context('Wf_Item_Activity_Status', 'Root_Status',
itemtype, itemkey);
raise;
end Root_Status;
--
-- LastResult
-- Get the instid and status of current row in cache.
-- Only used in SYNCHMODE.
-- IN
-- itemtype - itemtype of item
-- itemkey - itemkey of item
-- OUT
-- actid - instance id of current activity in cache
-- status - status of current activity in cache
-- result - result of current activity in cache
-- NOTE: ### Used by Flex - inform them of any api changes.
--
procedure LastResult(
itemtype in varchar2,
itemkey in varchar2,
actid out NOCOPY number,
status out NOCOPY varchar2,
result out NOCOPY varchar2)
is
begin
-- Check that the item matches one in the cache
if ((itemtype <> nvl(wf_item_activity_status.c_itemtype, 'x')) or
(itemkey <> nvl(wf_item_activity_status.c_itemkey, 'x'))) then
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', itemkey);
Wf_Core.Raise('WFENG_SYNCH_ITEM');
end if;
actid := wf_item_activity_status.c_actid;
status := wf_item_activity_status.c_status;
result := wf_item_activity_status.c_result;
exception
when others then
Wf_Core.Context('Wf_Item_Activity_Status', 'LastResult',
itemtype, itemkey);
raise;
end LastResult;
--
-- Status (PRIVATE)
-- Returns the status for this item activity. If there is no row in
-- the WF_ITEM_ACTIVITY_STATUSES table, the status out variable will be null.
-- IN
-- itemtype - Activity item type.
-- itemkey - The item key.
-- actid - Process activity (instance id).
-- OUT
-- status - Activity status for this item activity.
--
procedure Status(itemtype in varchar2,
itemkey in varchar2,
actid in number,
status out NOCOPY varchar2)
is
begin
Wf_Item_Activity_Status.InitCache(itemtype, itemkey, actid,
ignore_notfound=>TRUE);
status := wf_item_activity_status.c_status;
return;
exception
when OTHERS then
Wf_Core.Context('Wf_Item_Activity_Status', 'Status', itemtype,
itemkey, to_char(actid));
raise;
end Status;
--
-- Result (PRIVATE)
-- Returns the status and result for this item activity. If there is no
-- row in the WF_ITEM_ACTIVITY_STATUSES table, both status and result
-- out variables will be null.
-- IN
-- itemtype - Activity item type.
-- itemkey - The item key.
-- actid - Process activity (instance id).
-- OUT
-- status - Activity status for this item activity.
-- result - Activity result for this item activity.
--
procedure Result(itemtype in varchar2,
itemkey in varchar2,
actid in number,
status out NOCOPY varchar2,
result out NOCOPY varchar2) is
begin
Wf_Item_Activity_Status.InitCache(itemtype, itemkey, actid,
ignore_notfound=>TRUE);
status := wf_item_activity_status.c_status;
result := wf_item_activity_status.c_result;
return;
exception
when NO_DATA_FOUND then
status := '';
result := '';
when OTHERS then
Wf_Core.Context('Wf_Item_Activity_Status', 'Result', itemtype,
itemkey, to_char(actid));
raise;
end Result;
--
-- Due_Date (PRIVATE)
-- Returns the duedate of an activity that will timeout
-- IN
-- itemtype - Activity item type.
-- itemkey - The item key.
-- actid - Process activity (instance id).
-- RETURNS
-- duedate - Date activity will timeout
--
function Due_Date(
itemtype in varchar2,
itemkey in varchar2,
actid in number)
return date
is
begin
Wf_Item_Activity_Status.InitCache(itemtype, itemkey, actid);
return(wf_item_activity_status.c_duedate);
exception
when others then
Wf_Core.Context('Wf_Item_Activity_Status', 'Due_Date', itemtype,
itemkey, to_char(actid));
raise;
end Due_Date;
--
-- Notification_Status (PRIVATE)
-- Returns the notification id and assigned user for this item activity.
-- If there is no row in the WF_ITEM_ACTIVITY_STATUSES table, the notid
-- and user out variables will contain null.
-- IN
-- itemtype - Activity item type.
-- itemkey - The item key.
-- actid - Process activity (instance id).
-- OUT
-- notid - The notification id for this notification activity.
-- user - The assigned user for this notification activity.
--
procedure Notification_Status(itemtype in varchar2,
itemkey in varchar2,
actid in number,
notid out NOCOPY number,
user out NOCOPY varchar2)
is
begin
Wf_Item_Activity_Status.InitCache(itemtype, itemkey, actid,
ignore_notfound=>TRUE);
notid := wf_item_activity_status.c_notification_id;
user := wf_item_activity_status.c_assigned_user;
return;
exception
when OTHERS then
Wf_Core.Context('Wf_Item_Activity_Status', 'Notification_Status', itemtype,
itemkey, to_char(actid));
raise;
end Notification_Status;
--
-- Error_Info (PRIVATE)
-- Returns all error info for an activity.
-- IN
-- itemtype - Activity item type.
-- itemkey - The item key.
-- actid - Process activity (instance id).
-- OUT
-- errname - Error name
-- errmsg - Error message
-- errstack - Error stack
--
procedure Error_Info(itemtype in varchar2,
itemkey in varchar2,
actid in number,
errname out NOCOPY varchar2,
errmsg out NOCOPY varchar2,
errstack out NOCOPY varchar2)
is
begin
Wf_Item_Activity_Status.InitCache(itemtype, itemkey, actid);
errname := wf_item_activity_status.c_errname;
errmsg := wf_item_activity_status.c_errmsg;
errstack := wf_item_activity_status.c_errstack;
return;
exception
when NO_DATA_FOUND then
Wf_Core.Context('Wf_Item_Activity_Status', 'Error_Info', itemtype,
itemkey, to_char(actid));
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Raise('WFENG_ITEM_ACTIVITY_STATUS');
when OTHERS then
Wf_Core.Context('Wf_Item_Activity_Status', 'Error_Info', itemtype,
itemkey, to_char(actid));
raise;
end Error_Info;
--
-- Set_Error (PRIVATE)
-- Set error status and save current error info for this item activity.
-- To be called when a client activity function has raised an
-- unhandled exception.
-- IN
-- itemtype - Activity item type
-- itemkey - Item Key
-- actid - Process activity id
-- errcode - Result code of activity
-- error_process - Flag if this error is in the error process,
-- not in original activity
--
procedure Set_Error(itemtype in varchar2,
itemkey in varchar2,
actid in number,
errcode in varchar2,
error_process in boolean default FALSE)
is
errname varchar2(30);
errmsg varchar2(2000);
errstack varchar2(4000);
prefix varchar2(80);
l_exist number(1);
begin
-- First look for a standard WF_CORE exception.
Wf_Core.Get_Error(errname, errmsg, errstack);
if (errname is null) then
-- If no WF_CORE exception, look for an Oracle error.
errname := to_char(sqlcode);
errmsg := sqlerrm;
end if;
if (error_process) then
-- For an error in the error process, append the error info,
-- but do NOT change the status or result. Those come from the
-- original error.
-- SYNCHMODE: This should NEVER happen in synchmode, since
-- error processes are not allowed.
if (itemkey = wf_engine.eng_synch) then
wf_core.token('OPERATION',
'Wf_Item_Activity_Status.Set_Error(error_process)');
wf_core.raise('WFENG_SYNCH_DISABLED');
end if;
prefix := substrb(' ['||Wf_Core.Translate('WFENG_ERR_PROC_ERROR')||': ',
1, 80);
update
WF_ITEM_ACTIVITY_STATUSES set
ERROR_NAME = substrb(ERROR_NAME||prefix||errname||']', 1, 30),
ERROR_MESSAGE = substrb(ERROR_MESSAGE||prefix||errmsg||']', 1, 2000),
ERROR_STACK = substrb(ERROR_STACK||prefix||errstack||']', 1, 4000)
where ITEM_TYPE = itemtype
and ITEM_KEY = itemkey
and PROCESS_ACTIVITY = actid;
-- Update runtime cache
if ((wf_item_activity_status.c_itemtype = itemtype) and
(wf_item_activity_status.c_itemkey = itemkey) and
(wf_item_activity_status.c_actid = actid)) then
wf_item_activity_status.c_errname :=
substrb(wf_item_activity_status.c_errname||prefix||errname||']',
1, 30);
wf_item_activity_status.c_errmsg :=
substrb(wf_item_activity_status.c_errmsg||prefix||errmsg||']',
1, 2000);
wf_item_activity_status.c_errstack :=
substrb(wf_item_activity_status.c_errstack||prefix||errstack||']',
1, 4000);
end if;
else
-- Update runtime cache
if ((wf_item_activity_status.c_itemtype = itemtype) and
(wf_item_activity_status.c_itemkey = itemkey) and
(wf_item_activity_status.c_actid = actid)) then
wf_item_activity_status.c_errname := errname;
wf_item_activity_status.c_errmsg := errmsg;
wf_item_activity_status.c_errstack := errstack;
wf_item_activity_status.c_status := wf_engine.eng_error;
wf_item_activity_status.c_result := errcode;
end if;
-- SYNCHMODE: In synch mode stop after updating internal cache.
-- Note the ONLY place this should be called in synchmode
-- is function_call or execute_activity if an activity raises
-- an unhandled exception.
if (itemkey = wf_engine.eng_synch) then
return;
end if;
-- Store error info and set status/result
update
WF_ITEM_ACTIVITY_STATUSES set
ACTIVITY_STATUS = wf_engine.eng_error,
ACTIVITY_RESULT_CODE = errcode,
ERROR_NAME = errname,
ERROR_MESSAGE = errmsg,
ERROR_STACK = errstack
where ITEM_TYPE = itemtype
and ITEM_KEY = itemkey
and PROCESS_ACTIVITY = actid;
end if;
-- Bug 3960517
-- No record was found in WF_ITEM_ACTIVITY_STATUSES. We will check
-- the history table. If record exists in WF_ITEM_ACTIVITY_STATUSES_H,
-- we may have re-entered a node causing engine to prepare for looping.
-- But instead of going through all the activities along the loop, it has
-- progressed through a different path. Since there maybe multiple records
-- in the history table with the same item type, item key and actid, we may
-- not be able to mark the error accordingly, so we just simply ignore the error.
if (SQL%NOTFOUND) then
select 1 into l_exist
from WF_ITEM_ACTIVITY_STATUSES_H
where ITEM_TYPE = itemtype
and ITEM_KEY= itemkey
and process_activity = actid
and rownum < 2;
--raise NO_DATA_FOUND;
end if;
if (Wf_Engine.Debug) then
commit;
end if;
exception
when NO_DATA_FOUND then
Wf_Core.Context('Wf_Item_Activity_Status', 'Set_Error',
itemtype, itemkey, to_char(actid), errcode);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Raise('WFENG_ITEM_ACTIVITY_STATUS');
when OTHERS then
Wf_Core.Context('Wf_Item_Activity_Status', 'Set_Error',
itemtype, itemkey, to_char(actid), errcode);
raise;
end Set_Error;
--
-- Delete_Status (PRIVATE)
-- Deletes the row for this item activity from WF_ITEM_ACTIVITY_STATUSES.
-- IN
-- itemtype - Activity item type.
-- itemkey - The item key.
-- actid - Process activity (instance id).
--
procedure Delete_Status(itemtype in varchar2,
itemkey in varchar2,
actid in number)
is
begin
delete
from WF_ITEM_ACTIVITY_STATUSES
where ITEM_TYPE = itemtype
and ITEM_KEY = itemkey
and PROCESS_ACTIVITY = actid;
-- Clear runtime cache if needed
if ((wf_item_activity_status.c_itemtype = itemtype) and
(wf_item_activity_status.c_itemkey = itemkey) and
(wf_item_activity_status.c_actid = actid)) then
wf_item_activity_status.c_itemtype := '';
wf_item_activity_status.c_itemkey := '';
wf_item_activity_status.c_actid := '';
end if;
if (Wf_Engine.Debug) then
commit;
end if;
exception
when OTHERS then
Wf_Core.Context('Wf_Item_Activity_Status', 'Delete_Status', itemtype,
itemkey, to_char(actid));
raise;
end Delete_Status;
--
-- Create_Status (PRIVATE)
-- If this item activity status already exists, then update its
-- status, result, begin date and end date.
-- Otherwise, create the activity status by inserting a new row into WIAS
-- table with the supplied status and result.
-- IN
-- itemtype - Activity item type.
-- itemkey - The item key.
-- actid - Process activity (instance id).
-- status - Activity status for this item activity.
-- result - Activity result for this item activity.
-- beginning - Activity begin_date, or null to leave unchanged
-- ending - Activity end_date, or null to leave unchanged
-- callout - determines if activity is a call outside the database
-- suspended - flag only ever set when called from Process_Activity
-- when parent is suspended
procedure Create_Status(itemtype in varchar2,
itemkey in varchar2,
actid in number,
status in varchar2,
result in varchar2,
beginning in date,
ending in date,
suspended in boolean,
newStatus in boolean)
is
root varchar2(30); -- Root process of activity
version pls_integer; -- Root process version
rootid pls_integer; -- Id of root process
act_fname varchar2(240);
act_ftype varchar2(30);
delay number; -- dont use pls_integer or numeric overflow can occur.
msg_id raw(16):=null;
l_result number;
-- Timeout processing stuff
duedate date;
timeout number;
msg varchar2(30);
msgtype varchar2(8);
expand_role varchar2(8);
-- status flags
l_newStatus boolean default FALSE;
begin
if (itemkey = wf_engine.eng_synch) then
-- SYNCHMODE: Only update runtime cache.
if ((wf_item_activity_status.c_itemtype = itemtype) and
(wf_item_activity_status.c_itemkey = itemkey) and
(wf_item_activity_status.c_actid = actid)) then
-- Existing row. Only update relevant parts
wf_item_activity_status.c_status := status;
if (result is not null) then
wf_item_activity_status.c_result := result;
end if;
if (beginning is not null) then
wf_item_activity_status.c_begindate := beginning;
end if;
if (ending is not null) then
wf_item_activity_status.c_enddate := ending;
end if;
else
-- Fresh new row. Re-initialize everything.
wf_item_activity_status.c_itemtype := itemtype;
wf_item_activity_status.c_itemkey := itemkey;
wf_item_activity_status.c_actid := actid;
wf_item_activity_status.c_status := status;
wf_item_activity_status.c_result := result;
wf_item_activity_status.c_begindate := beginning;
wf_item_activity_status.c_enddate := ending;
wf_item_activity_status.c_duedate := to_date(NULL);
wf_item_activity_status.c_assigned_user := '';
wf_item_activity_status.c_notification_id := '';
wf_item_activity_status.c_errname := '';
wf_item_activity_status.c_errmsg := '';
wf_item_activity_status.c_errstack := '';
end if;
else
-- NORMAL mode:
-- TIMEOUT PROCESSING
-- Calculate new timeout date if begin_date is being changed.
if (beginning is not null) then
begin
-- 1. Look first for a '#TIMEOUT' NUMBER attribute
timeout := Wf_Engine.GetActivityAttrNumber(itemtype, itemkey,
actid, wf_engine.eng_timeout_attr,
ignore_notfound=>TRUE);
if (nvl(timeout, 0) <> 0) then
-- Figure duedate as offset from begin time.
-- NOTE: Default timeout is in units of minutes, not days like
-- all other 'date as number' values, thus the 1440 fudge factor.
duedate := beginning + (timeout / 1440);
else
-- 2. Look for a '#TIMEOUT' DATE attribute
duedate := Wf_Engine.GetActivityAttrDate(itemtype, itemkey,
actid, wf_engine.eng_timeout_attr,
ignore_notfound=>TRUE);
end if;
exception
when others then
if (wf_core.error_name = 'WFENG_ACTIVITY_ATTR') then
-- No #TIMEOUT attr means no timeout
wf_core.clear;
duedate := null;
else
raise;
end if;
end;
end if;
-- DEFERRED QUEUE PROCESSING
-- if deferred, insert into the deferred queue
-- but not if parent is SUSPENDED or we get infinite loop in queue
if create_status.status = wf_engine.eng_deferred
and (not create_status.suspended )then
act_fname:= Wf_Activity.activity_function(itemtype,itemkey,actid);
act_ftype:= Wf_Activity.activity_function_type(itemtype,itemkey,actid);
-- If enqueue fails, only the activity should error and not the root
begin
if act_ftype = 'PL/SQL' then
if beginning is null then
delay :=0;
else
delay := round((beginning - sysdate)*24*60*60 + 0.5);
end if;
wf_queue.enqueue_event
(queuename=>wf_queue.DeferredQueue,
itemtype=> itemtype,
itemkey=>create_status.itemkey,
actid=>create_status.actid,
delay=>delay,
message_handle=>msg_id);
-- even when internal, keep message for cross reference.
-- msg_id :=null;
elsif act_ftype = 'EXTERNAL' then
-- this is a callout so write to OUTBOUND queue
-- do not set the correlation here for compatibility reason
wf_queue.enqueue_event
(queuename=>wf_queue.OutboundQueue,
itemtype=> create_status.itemtype,
itemkey=>create_status.itemkey,
actid=>create_status.actid,
funcname=>act_fname,
paramlist=>wf_queue.get_param_list(itemtype,itemkey,actid),
message_handle=>msg_id);
else
-- this is a callout so write to OUTBOUND queue for other type
wf_queue.enqueue_event
(queuename=>wf_queue.OutboundQueue,
itemtype=> create_status.itemtype,
itemkey=>create_status.itemkey,
actid=>create_status.actid,
correlation=>act_ftype,
funcname=>act_fname,
paramlist=>wf_queue.get_param_list(itemtype,itemkey,actid),
message_handle=>msg_id);
end if;
exception
when others then
-- If any error while enqueing, set the activity status to ERROR
Wf_Item_Activity_Status.Create_Status(itemtype, itemkey, actid,
wf_engine.eng_error, wf_engine.eng_exception,
sysdate, null, newStatus=>TRUE);
Wf_Item_Activity_Status.Set_Error(itemtype, itemkey, actid,
wf_engine.eng_exception, FALSE);
Wf_Engine_Util.Execute_Error_Process(itemtype, itemkey, actid,
wf_engine.eng_exception);
return;
end;
end if;
-- Increment Counter
g_ExecCounter := (g_ExecCounter + 1);
-- Update the status in db. The execution_time is also reset if:
-- 1. Changing status to active
-- 2. Changing status to complete/error AND the execution_time is
-- not yet set (activity has been aborted without being executed).
if not (newStatus) then
update
WF_ITEM_ACTIVITY_STATUSES set
ACTIVITY_STATUS = create_status.status,
ACTIVITY_RESULT_CODE = nvl(create_status.result, ACTIVITY_RESULT_CODE),
BEGIN_DATE = nvl(create_status.beginning, BEGIN_DATE),
END_DATE = nvl(create_status.ending, END_DATE),
DUE_DATE = decode(create_status.beginning,
to_date(NULL), DUE_DATE,
create_status.duedate),
OUTBOUND_QUEUE_ID = msg_id,
EXECUTION_TIME =
decode(create_status.status,
wf_engine.eng_active, g_ExecCounter,
wf_engine.eng_completed, nvl(EXECUTION_TIME, g_ExecCounter),
wf_engine.eng_error, nvl(EXECUTION_TIME, g_ExecCounter),
EXECUTION_TIME)
where ITEM_TYPE = create_status.itemtype
and ITEM_KEY = create_status.itemkey
and PROCESS_ACTIVITY = create_status.actid;
end if;
-- Create the status if not found
if ((newStatus) or (SQL%ROWCOUNT = 0)) then
begin
insert
into WF_ITEM_ACTIVITY_STATUSES (
ITEM_TYPE,
ITEM_KEY,
PROCESS_ACTIVITY,
ACTIVITY_STATUS,
ACTIVITY_RESULT_CODE,
ASSIGNED_USER,
NOTIFICATION_ID,
BEGIN_DATE,
END_DATE,
DUE_DATE,
EXECUTION_TIME,
OUTBOUND_QUEUE_ID
) values (
create_status.itemtype,
create_status.itemkey,
create_status.actid,
create_status.status,
create_status.result,
null,
null,
create_status.beginning,
create_status.ending,
create_status.duedate,
decode(create_status.status,
wf_engine.eng_active, g_ExecCounter,
wf_engine.eng_completed, g_ExecCounter,
wf_engine.eng_error, g_ExecCounter,
null),
create_status.msg_id
);
-- Initialize runtime cache with new row
wf_item_activity_status.c_itemtype := itemtype;
wf_item_activity_status.c_itemkey := itemkey;
wf_item_activity_status.c_actid := actid;
wf_item_activity_status.c_status := status;
wf_item_activity_status.c_result := result;
wf_item_activity_status.c_begindate := beginning;
wf_item_activity_status.c_enddate := ending;
wf_item_activity_status.c_duedate := duedate;
wf_item_activity_status.c_assigned_user := '';
wf_item_activity_status.c_notification_id := '';
wf_item_activity_status.c_errname := '';
wf_item_activity_status.c_errmsg := '';
wf_item_activity_status.c_errstack := '';
l_newStatus := TRUE;
exception
when DUP_VAL_ON_INDEX then
-- If we are attempting to insert but the record exists, we will then
-- automatically update to ensure fault tolerance.
l_newStatus := FALSE;
update
WF_ITEM_ACTIVITY_STATUSES set
ACTIVITY_STATUS = create_status.status,
ACTIVITY_RESULT_CODE = nvl(create_status.result,
ACTIVITY_RESULT_CODE),
BEGIN_DATE = nvl(create_status.beginning, BEGIN_DATE),
END_DATE = nvl(create_status.ending, END_DATE),
DUE_DATE = decode(create_status.beginning,
to_date(NULL), DUE_DATE,
create_status.duedate),
OUTBOUND_QUEUE_ID = msg_id,
EXECUTION_TIME = decode(create_status.status,
wf_engine.eng_active, g_ExecCounter,
wf_engine.eng_completed, nvl(EXECUTION_TIME,
g_ExecCounter),
wf_engine.eng_error, nvl(EXECUTION_TIME,
g_ExecCounter),
EXECUTION_TIME)
where ITEM_TYPE = create_status.itemtype
and ITEM_KEY = create_status.itemkey
and PROCESS_ACTIVITY = create_status.actid;
end;
end if;
if (not l_newStatus) then
-- Update runtime cache with new data, if current row
if ((wf_item_activity_status.c_itemtype = itemtype) and
(wf_item_activity_status.c_itemkey = itemkey) and
(wf_item_activity_status.c_actid = actid)) then
wf_item_activity_status.c_status := status;
if (result is not null) then
wf_item_activity_status.c_result := result;
end if;
if (beginning is not null) then
wf_item_activity_status.c_begindate := beginning;
wf_item_activity_status.c_duedate := duedate;
end if;
if (ending is not null) then
wf_item_activity_status.c_enddate := ending;
end if;
end if;
end if;
-- If the root process is being marked completed or active,
-- then also update the end_date of the item.
Wf_Item.Root_Process(itemtype, itemkey, root, version);
rootid := Wf_Process_Activity.RootInstanceId(itemtype, itemkey, root);
if (actid = rootid) then
if (status = wf_engine.eng_completed) then
l_result := WF_ITEM.SetEndDate(itemtype, itemkey);
elsif (status = wf_engine.eng_active) then
UPDATE WF_ITEMS SET
END_DATE = to_date(NULL)
WHERE ITEM_TYPE = itemtype
AND ITEM_KEY = itemkey;
end if;
end if;
end if;
-- High availability support. creates dependency on datamodel changes
-- and WF_HA_MIGRATION package (WFHAMIG[S|B].pls)
if (WF_HA_MIGRATION.GET_CACHED_HA_MAINT_MODE = 'MAINT') then
WF_HA_MIGRATION.SET_HA_FLAG(itemtype, itemkey);
end if;
if (Wf_Engine.Debug) then
commit;
end if;
exception
when OTHERS then
Wf_Core.Context('Wf_Item_Activity_Status', 'Create_Status', itemtype,
itemkey, to_char(actid), status, result);
raise;
end Create_Status;
--
-- Audit (PRIVATE)
-- Procedure to update the item activity status record with Audit
-- information if the activity was expedited from Status Monitor
-- IN
-- itemtype - Item Type
-- itemkey - Item Key
-- actid - Activity Id
-- action - Action performed on the activity
-- performer - User performed the action
procedure Audit(itemtype in varchar2,
itemkey in varchar2,
actid in number,
action in varchar2,
performer in varchar2)
is
l_username varchar2(320);
begin
-- if the performer is not provided get the current session user
if (performer is null) then
l_username := wfa_sec.GetUser();
else
l_username := performer;
end if;
-- update the status record with the action and performer
UPDATE wf_item_activity_statuses
SET action = Audit.action,
performed_by = l_username
WHERE item_type = Audit.itemtype
AND item_key = Audit.itemkey
AND process_activity = Audit.actid;
--Bug 3361746
--The existing handleerror API does not mandatorily require an entry in
--wf_item_activity_statuses table, hence we should not throw an
--exception if no row is found for the activity.
exception
when others then
Wf_Core.Context('Wf_Item_Activity_Status', 'Audit', itemtype, itemkey, to_char(actid));
raise;
end Audit;
end WF_ITEM_ACTIVITY_STATUS;
/
--show errors package body WF_ITEM_ACTIVITY_STATUS
--select to_date( 'SQLERROR') from user_errors
--where type = 'PACKAGE BODY'
--and name = 'WF_ITEM_ACTIVITY_STATUS'
--/
REM ================================================================
commit;
/*=======================================================================+
| Copyright (c) 1995 Oracle Corporation Redwood Shores, California, USA|
| All rights reserved. |
+=======================================================================+
|
| DESCRIPTION
| PL/SQL body for package: WF_ITEM
| NOTES
| This package contains utilities used internally by the Workflow
| Engine. It is not for public use and may be changed without notice.
*=======================================================================*/
create or replace package body WF_ITEM as
/* $Header: wfengb.pls 26.108 2005/03/02 05:14:50 rwunderl ship $ */
c_itemtype varchar2(8);
c_itemkey varchar2(240);
c_root_activity varchar2(30);
c_root_activity_version pls_integer;
c_begin_date date;
c_userkey varchar2(240);
--
-- ClearCache
-- Clear runtime cache
--
procedure ClearCache
is
begin
wf_item.c_itemtype := '';
wf_item.c_itemkey := '';
wf_item.c_root_activity := '';
wf_item.c_root_activity_version := '';
wf_item.c_begin_date := to_date(NULL);
wf_item.c_userkey := '';
-- Clear the synch attribute cache too
wf_engine.synch_attr_count := 0;
exception
when others then
Wf_Core.Context('Wf_Item', 'ClearCache');
raise;
end ClearCache;
--
-- InitCache (PRIVATE)
-- Initialize package cache
-- IN
-- itemtype - Item type
-- itemkey - Item key
--
procedure InitCache(
itemtype in varchar2,
itemkey in varchar2,
ignore_notfound in boolean default FALSE)
is
rootid number;
status varchar2(8);
begin
-- Check for refresh
if ((itemtype = wf_item.c_itemtype) and
(itemkey = wf_item.c_itemkey)) then
return;
end if;
-- SYNCHMODE: If
-- 1. Asking for an item other than the cached one AND
-- 2. The cached item is a synch process AND
-- 3. The cached item has not yet completed
-- then raise an error. Other items cannot be accessed until synch
-- item completes, because it can't be restarted from db
if (wf_item.c_itemkey = wf_engine.eng_synch) then
-- Get status of root process of cached item
-- Note: If process completed successfully, the last thing in the
-- WIAS runtime cache should be the root process, which is the
-- only reason this will work.
begin
rootid := Wf_Process_Activity.RootInstanceId(c_itemtype, c_itemkey,
c_root_activity);
Wf_Item_Activity_Status.Status(c_itemtype, c_itemkey, rootid, status);
exception
when others then
status := 'x'; -- Treat errors like incomplete process
end;
if (nvl(status, 'x') <> wf_engine.eng_completed) then
Wf_Core.Token('ITEMTYPE', itemtype);
Wf_Core.Token('ITEMKEY', itemkey);
Wf_Core.Raise('WFENG_SYNCH_ITEM');
end if;
end if;
-- Query new values
select WI.ROOT_ACTIVITY, WI.ROOT_ACTIVITY_VERSION, WI.BEGIN_DATE,
WI.USER_KEY
into wf_item.c_root_activity, wf_item.c_root_activity_version,
wf_item.c_begin_date, wf_item.c_userkey
from WF_ITEMS WI
where WI.ITEM_TYPE = InitCache.itemtype
and WI.ITEM_KEY = InitCache.itemkey;
-- Save cache key values
wf_item.c_itemtype := itemtype;
wf_item.c_itemkey := itemkey;
exception
when NO_DATA_FOUND then
if (ignore_notfound) then
WF_ITEM.ClearCache;
else
Wf_Core.Context('Wf_Item', 'InitCache', itemtype, itemkey);
raise;
end if;
when others then
Wf_Core.Context('Wf_Item', 'InitCache', itemtype, itemkey);
raise;
end InitCache;
--
-- Set_Item_Parent
-- Set parent ids of an item
-- IN
-- itemtype - Item type
-- itemkey - Item key
-- parent_itemtype - Itemtype of parent
-- parent_itemkey - Itemkey of parent
-- parent_context - Context info about parent
--
procedure Set_Item_Parent(itemtype in varchar2,
itemkey in varchar2,
parent_itemtype in varchar2,
parent_itemkey in varchar2,
parent_context in varchar2,
masterdetail in boolean)
is
ValTooLarge EXCEPTION;
pragma exception_init(ValTooLarge, -01401);
begin
update WF_ITEMS set
PARENT_ITEM_TYPE = Set_Item_Parent.parent_itemtype,
PARENT_ITEM_KEY = Set_Item_Parent.parent_itemkey,
PARENT_CONTEXT = Set_Item_Parent.parent_context
where ITEM_TYPE = Set_Item_Parent.itemtype
and ITEM_KEY = Set_Item_Parent.itemkey;
if (sql%notfound) then
raise no_data_found;
end if;
if (masterdetail) then
--Increment #WAITFORDETAIL master counter if it exists.
if (WF_ENGINE.AddToItemAttrNumber(parent_itemType, parent_itemKey,
'#WAITFORDETAIL', 1) is NOT NULL) then
if (parent_context is NOT null) then
--Increment/Create label counter.
if (length(parent_context) > 25) then
WF_CORE.Token('LABEL', parent_context);
WF_CORE.Token('LENGTH', '25');
WF_CORE.Raise('WFENG_LABEL_TOO_LARGE');
elsif (WF_ENGINE.AddToItemAttrNumber(parent_itemType, parent_itemKey,
'#CNT_'||parent_context, 1)
is NULL) then
WF_ENGINE.AddItemAttr(itemType=>parent_itemType,
itemKey=>parent_itemKey,
aname=>'#CNT_'||parent_context,
number_value=>1);
end if; --Label Counter exists
WF_ENGINE.AddItemAttr(itemType=>itemType, itemKey=>itemKey,
aname=>'#LBL_'||parent_context,
text_value=>parent_context);
else
-- Parent context is null
-- increase all known #CNT counter by 1
update WF_ITEM_ATTRIBUTE_VALUES
set NUMBER_VALUE = NUMBER_VALUE + 1
where NAME like '#CNT_%'
and NUMBER_VALUE is not null
and ITEM_TYPE = parent_itemType
and ITEM_KEY = parent_itemKey;
end if; --Parent context is not null
end if; --#WAITFORDETAIL exists
end if; --Caller is signalling that this "should" be a coordinated flow.
exception
when no_data_found then
Wf_Core.Context('Wf_Item', 'Set_Item_Parent', itemtype, itemkey,
parent_itemtype, parent_itemkey, parent_context);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
when ValTooLarge then
Wf_Core.Context('Wf_Item', 'Set_Item_Parent', itemtype, itemkey,
parent_itemtype, parent_itemkey, parent_context, 'TRUE');
WF_CORE.Token('LABEL', parent_context);
WF_CORE.Token('LENGTH', 25);
WF_CORE.Raise('WFENG_LABEL_TOO_LARGE');
when others then
Wf_Core.Context('Wf_Item', 'Set_Item_Parent', itemtype, itemkey,
parent_itemtype, parent_itemkey, parent_context);
raise;
end Set_Item_Parent;
--
-- SetItemOwner
-- Set the owner of an item
-- IN
-- itemtype - Item type
-- itemkey - Item key
-- owner - Role designated as owner of the item
--
procedure SetItemOwner(
itemtype in varchar2,
itemkey in varchar2,
owner in varchar2)
is
begin
-- Update owner column
update WF_ITEMS WI set
OWNER_ROLE = SetItemOwner.owner
where WI.ITEM_TYPE = SetItemOwner.itemtype
and WI.ITEM_KEY = SetItemOwner.itemkey;
if (sql%notfound) then
raise no_data_found;
end if;
exception
when no_data_found then
Wf_Core.Context('Wf_Item', 'SetItemOwner', itemtype, itemkey,
owner);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
when others then
Wf_Core.Context('Wf_Item', 'SetItemOwner', itemtype, itemkey,
owner);
raise;
end SetItemOwner;
--
-- SetItemUserKey
-- Set the user key of an item
-- IN
-- itemtype - Item type
-- itemkey - Item key
-- userkey - User key to be set
--
procedure SetItemUserKey(
itemtype in varchar2,
itemkey in varchar2,
userkey in varchar2)
is
begin
update WF_ITEMS WI set
USER_KEY = SetItemUserKey.userkey
where WI.ITEM_TYPE = SetItemUserKey.itemtype
and WI.ITEM_KEY = SetItemUserKey.itemkey;
if (sql%notfound) then
raise no_data_found;
end if;
-- Set value in the local cache the right item
if ((itemtype = wf_item.c_itemtype) and
(itemkey = wf_item.c_itemkey)) then
wf_item.c_userkey := userkey;
end if;
exception
when no_data_found then
Wf_Core.Context('Wf_Item', 'SetItemUserKey', itemtype, itemkey,
userkey);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
when others then
Wf_Core.Context('Wf_Item', 'SetItemUserKey', itemtype, itemkey,
userkey);
raise;
end SetItemUserKey;
--
-- GetItemUserKey
-- Get the user key of an item
-- IN
-- itemtype - Item type
-- itemkey - Item key
-- RETURNS
-- User key of the item
--
function GetItemUserKey(
itemtype in varchar2,
itemkey in varchar2)
return varchar2
is
buf varchar2(240);
begin
-- Check first for cached value
if ((itemtype = wf_item.c_itemtype) and
(itemkey = wf_item.c_itemkey)) then
return(wf_item.c_userkey);
end if;
-- No cached value, go directly to the source
select USER_KEY
into buf
from WF_ITEMS WI
where WI.ITEM_TYPE = GetItemUserKey.itemtype
and WI.ITEM_KEY = GetItemUserKey.itemkey;
return(buf);
exception
when no_data_found then
Wf_Core.Context('Wf_Item', 'GetItemUserKey', itemtype, itemkey);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
when others then
Wf_Core.Context('Wf_Item', 'GetItemUserKey', itemtype, itemkey);
raise;
end GetItemUserKey;
--
-- Item_Exist (PRIVATE)
-- Returns TRUE if this is an existing item. Otherwise return FALSE.
-- IN
-- itemtype - item type
-- itemkey - item key
--
function Item_Exist(itemtype in varchar2,
itemkey in varchar2)
return boolean
is
begin
Wf_Item.InitCache(itemtype, itemkey, ignore_notfound=>TRUE);
if (wf_item.c_itemtype is not null) then
return(TRUE);
else
return(FALSE);
end if;
exception
when OTHERS then
Wf_Core.Context('Wf_Item', 'Item_Exist', itemtype, itemkey);
raise;
end Item_Exist;
--
-- Root_Process (PRIVATE)
-- If the item exists, wflow out variable will contain the root process
-- name for this item key. Otherwise the wflow out variable will be null.
-- IN
-- itemtype - item type
-- itemkey - item key
-- OUT
-- wflow - root process
-- version - root process version
--
procedure Root_Process(itemtype in varchar2,
itemkey in varchar2,
wflow out NOCOPY varchar2,
version out NOCOPY number)
is
begin
Wf_Item.InitCache(itemtype, itemkey);
wflow := wf_item.c_root_activity;
version := wf_item.c_root_activity_version;
exception
when NO_DATA_FOUND then
wflow := '';
version := -1;
when OTHERS then
Wf_Core.Context('Wf_Item', 'Root_Process', itemtype, itemkey);
raise;
end Root_Process;
--
-- Create_Item (PRIVATE)
-- Create one row in the WF_ITEMS table with the given item type, item key
-- and the root process name.
-- IN
-- itemtype - item type
-- itemkey - item key
-- wflow - root process name for this item key.
-- actdate - active date of item
--
procedure Create_Item(
itemtype in varchar2,
itemkey in varchar2,
wflow in varchar2,
actdate in date,
user_key in varchar2,
owner_role in varchar2)
is
rootversion number;
--
status PLS_INTEGER;
wiaIND NUMBER;
wiavIND NUMBER;
cursor attrcurs(itype in varchar2) is
select WIA.ITEM_TYPE, WIA.NAME, WIA.TYPE, WIA.SUBTYPE, WIA.FORMAT,
WIA.TEXT_DEFAULT, WIA.NUMBER_DEFAULT, WIA.DATE_DEFAULT
from WF_ITEM_ATTRIBUTES WIA
where WIA.ITEM_TYPE = itype;
begin
rootversion := Wf_Activity.Version(itemtype, wflow, actdate);
if (itemkey <> wf_engine.eng_synch) then
-- NORMAL: Insert new item and attributes directly in the db
insert into WF_ITEMS (
ITEM_TYPE,
ITEM_KEY,
ROOT_ACTIVITY,
ROOT_ACTIVITY_VERSION,
OWNER_ROLE,
PARENT_ITEM_TYPE,
PARENT_ITEM_KEY,
BEGIN_DATE,
END_DATE,
USER_KEY
) values (
itemtype,
itemkey,
wflow,
rootversion,
Create_Item.owner_role,
'',
'',
actdate,
to_date(NULL),
Create_item.user_key
);
end if;
-- Initialize runtime cache (used in both NORMAL and SYNCHMODE).
wf_item.c_itemtype := itemtype;
wf_item.c_itemkey := itemkey;
wf_item.c_root_activity := wflow;
wf_item.c_root_activity_version := rootversion;
wf_item.c_begin_date := actdate;
wf_item.c_userkey := Create_item.user_key;
-- Initialize item attributes
if (itemkey <> wf_engine.eng_synch) then
-- NORMAL: store attributes in table
insert into WF_ITEM_ATTRIBUTE_VALUES (
ITEM_TYPE,
ITEM_KEY,
NAME,
TEXT_VALUE,
NUMBER_VALUE,
DATE_VALUE
) select
itemtype,
itemkey,
WIA.NAME,
WIA.TEXT_DEFAULT,
WIA.NUMBER_DEFAULT,
WIA.DATE_DEFAULT
from WF_ITEM_ATTRIBUTES WIA
where WIA.ITEM_TYPE = itemtype;
else
-- SYNCHMODE: store attributes in plsql only
for curs in attrcurs(itemtype) loop
--Getting the index for the item attribute.
WF_CACHE.GetItemAttribute(itemtype, curs.name, status, wiaIND);
--Getting the index for the item attribute value
WF_CACHE.GetItemAttrValue(itemtype, itemkey, curs.name, status, wiavIND);
--Loading the item attribute into cache for synch mode.
WF_CACHE.ItemAttributes(wiaIND).ITEM_TYPE := itemType;
WF_CACHE.ItemAttributes(wiaIND).NAME := curs.name;
WF_CACHE.ItemAttributes(wiaIND).TYPE := curs.type;
WF_CACHE.ItemAttributes(wiaIND).SUBTYPE := curs.subtype;
WF_CACHE.ItemAttributes(wiaIND).FORMAT := curs.format;
WF_CACHE.ItemAttributes(wiaIND).TEXT_DEFAULT := curs.text_default;
WF_CACHE.ItemAttributes(wiaIND).NUMBER_DEFAULT := curs.number_default;
WF_CACHE.ItemAttributes(wiaIND).DATE_DEFAULT := curs.date_default;
--Loading the item attribute value into cache for use by synch processes
--only until we introduce the item locking feature.
WF_CACHE.ItemAttrValues(wiavIND).ITEM_TYPE := itemType;
WF_CACHE.ItemAttrValues(wiavIND).ITEM_KEY := itemKey;
WF_CACHE.ItemAttrValues(wiavIND).NAME := curs.name;
WF_CACHE.ItemAttrValues(wiavIND).TEXT_VALUE := curs.text_default;
WF_CACHE.ItemAttrValues(wiavIND).NUMBER_VALUE := curs.number_default;
WF_CACHE.ItemAttrValues(wiavIND).DATE_VALUE := curs.date_default;
end loop;
end if;
exception
when DUP_VAL_ON_INDEX then
Wf_Core.Context('Wf_Item', 'Create_Item', itemtype, itemkey, wflow);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM_UNIQUE');
when OTHERS then
Wf_Core.Context('Wf_Item', 'Create_Item', itemtype, itemkey, wflow);
raise;
end Create_Item;
--
-- Active_Date (PRIVATE)
-- Return the begin date of an item
-- IN
-- itemtype
-- itemkey
-- RETURN
-- Begin date of item
--
function Active_Date(itemtype in varchar2,
itemkey in varchar2)
return date
is
begin
Wf_Item.InitCache(itemtype, itemkey);
return(wf_item.c_begin_date);
exception
when NO_DATA_FOUND then
Wf_Core.Context('Wf_Item', 'Active_Date', itemtype, itemkey);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Raise('WFENG_ITEM');
when OTHERS then
Wf_Core.Context('Wf_Item', 'Active_Date', itemtype, itemkey);
end Active_Date;
--Function Acquire_lock (PRIVATE)
--This function tries to lock the particular item (for the give
--itemtype/itemkey ) in the wf_items table. It returns true if the lock
--acquired else returns false.
--Here we will not do any error handling but return true/false
--for the case of lock_acquired or not . This leaves the caller
--the decision of what to do when a resource busy error occurs
--(ie FALSE) . For eg : Background engine will ignore it and move
--on , WF Engine will raise exception etc.
function acquire_lock(itemtype in varchar2,
itemkey in varchar2,
raise_exception in boolean)
return boolean
is
--Bug 2607770
--Cursor for acquiring lock
cursor itemlock (itemtype varchar2, itemkey varchar2) is
select '1'
from wf_items
where item_type = itemtype
and item_key = itemkey
for update nowait;
--Define an exception to capture the resource_busy error
resource_busy exception;
pragma EXCEPTION_INIT(resource_busy,-00054);
begin
--Acquire lock here by opening the cursor
OPEN itemlock (itemtype,itemkey);
--Close the cursor once the lock has been acquired
CLOSE itemlock;
return TRUE;
exception
--Capture the exception on resource-busy error
when resource_busy then
--Lets double check that the cursor is not open
if (itemlock%ISOPEN) then
CLOSE itemlock;
end if;
--check the if_raise flag . If its true then raise
--the exception
--else return false and let the caller decide what to do.
if raise_exception then
raise;
--If not able to acquire lock return FALSE
else
return FALSE;
end if;
when others then
--Lets double check that the cursor is not open
if (itemlock%ISOPEN) then
CLOSE itemlock;
end if;
--In this case we do not want a TRUE/FALSE return
--we just raise the error.
Wf_Core.Context('Wf_Item', 'Acquire_lock', itemtype, itemkey);
raise;
end;
--
-- SetEndDate (Private)
-- Sets end_date and completes any coordinated counter processing.
-- IN
-- p_itemtype - process item type
-- p_itemkey - process item key
-- RETURNS
-- number
-- NOTE:
-- This function will return a status of one of the following:
-- 0 - Item was found, active, and the end_date was set.
-- 1 - The item was not found. (ERROR)
function SetEndDate(p_itemtype in varchar2,
p_itemkey in varchar2) return number
is
l_parent_itemType VARCHAR2(8);
l_parent_itemKey VARCHAR2(240);
l_parent_context VARCHAR2(2000);
TYPE nameTAB is TABLE of VARCHAR2(30) index by binary_integer;
attrNames nameTAB;
l_result NUMBER;
i NUMBER;
begin
UPDATE wf_items
SET end_date = sysdate
WHERE item_type = p_itemType
AND item_key = p_itemKey
AND end_date is NULL
RETURNING parent_item_type, parent_item_key, parent_context
INTO l_parent_itemtype, l_parent_itemkey, l_parent_context;
if (sql%notfound) then
return 1;
end if;
--We need to perform some counter processing if they exist.
if ((l_parent_itemType is NOT null) and
(l_parent_itemKey is NOT null)) then
if (WF_ENGINE.AddToItemAttrNumber(l_parent_itemType, l_parent_itemKey,
'#WAITFORDETAIL', -1) is NOT null) then
if ((l_parent_context is NOT null) and
(WF_ENGINE.GetItemAttrText(p_itemType, p_itemKey,
'#LBL_'||l_parent_context, TRUE) is NOT NULL)) then
if (WF_ENGINE.SetItemAttrText2(p_itemType, p_itemKey,
'#LBL_'||l_parent_context, NULL)) then
l_result := WF_ENGINE.AddToItemAttrNumber(l_parent_itemType,
l_parent_itemKey,
'#CNT_'||l_parent_context,
-1);
end if;
else
SELECT TEXT_VALUE
bulk collect into attrNames
FROM WF_ITEM_ATTRIBUTE_VALUES
WHERE ITEM_TYPE = p_itemType
AND ITEM_KEY = p_itemKey
AND NAME like ('#LBL_%')
AND TEXT_VALUE is NOT null;
if (attrNames.COUNT > 0) then
for i in attrNames.FIRST..attrNames.LAST loop
if (WF_ENGINE.SetItemAttrText2(p_itemType, p_itemKey,
'#LBL_'||attrNames(i), NULL)) then
l_result := WF_ENGINE.AddToItemAttrNumber(l_parent_itemtype,
l_parent_itemkey,
'#CNT_'||attrNames(i),
-1);
end if; --#LBL_ exists as expected.
end loop;
end if; --There are non-null #LBL_ attributes.
end if; --Parent Context
end if; --We were able to decrement the #WAITFORDETAIL
end if; --This item has a parent.
return 0;
exception
when OTHERS then
WF_CORE.Context('WF_ITEM', 'SetEndDate', p_itemType, p_itemKey);
raise;
end SetEndDate;
end WF_ITEM;
/
--show errors package body wf_item
commit;
/*=======================================================================+
| Copyright (c) 1995 Oracle Corporation Redwood Shores, California, USA|
| All rights reserved. |
+=======================================================================+
|
| DESCRIPTION
| PL/SQL body for package: WF_PROCESS_ACTIVITY
| NOTES
| This package contains utilities used internally by the Workflow
| Engine. It is not for public use and may be changed without notice.
*=======================================================================*/
create or replace package body WF_PROCESS_ACTIVITY as
/* $Header: wfengb.pls 26.108 2005/03/02 05:14:50 rwunderl ship $ */
type InstanceArrayTyp is table of pls_integer
index by binary_integer;
--
-- RootInstanceId
-- Globals to cache RootInstanceId result for effeciency
--
c_itemtype varchar2(8);
c_itemkey varchar2(240);
c_process varchar2(30);
c_rootid pls_integer;
--
-- ClearCache
-- Clear runtime cache
--
procedure ClearCache
is
begin
wf_process_activity.c_itemtype := '';
wf_process_activity.c_itemkey := '';
wf_process_activity.c_process := '';
wf_process_activity.c_rootid := '';
exception
when others then
Wf_Core.Context('Wf_Process_Activity', 'ClearCache');
raise;
end ClearCache;
--
-- RootInstanceId (PRIVATE)
-- Return the instance id for the process activity under the given item
-- type. If there is no row found, returns null.
-- NOTE
-- This function only returns the 'ROOT' row for a process in
-- WF_PROCESS_ACTIVITIES. It assumes there will be exactly 1 row
-- looking like:
-- PROCESS_ITEM_TYPE = itemtype
-- PROCESS_NAME = 'ROOT'
-- PROCESS_VERSION = version
-- INSTANCE_LABEL = process
-- for each process in WF_PROCESS_ACTIVITIES.
-- IN
-- itemtype - Item type of process
-- itemkey - Item key
-- process - Process name
--
function RootInstanceId(itemtype in varchar2,
itemkey in varchar2,
process in varchar2)
return number is
actdate date;
instid pls_integer;
begin
-- Check cache for a valid value
if ((itemtype = wf_process_activity.c_itemtype) and
(itemkey = wf_process_activity.c_itemkey) and
(process = wf_process_activity.c_process)) then
return(wf_process_activity.c_rootid);
end if;
-- No joy. Select a new value.
actdate := Wf_Item.Active_Date(itemtype, itemkey);
select INSTANCE_ID
into instid
from WF_PROCESS_ACTIVITIES PA, WF_ACTIVITIES A
where A.ITEM_TYPE = itemtype
and A.NAME = 'ROOT'
and actdate >= A.BEGIN_DATE
and actdate < NVL(A.END_DATE, actdate+1)
and PA.PROCESS_NAME = 'ROOT'
and PA.PROCESS_ITEM_TYPE = itemtype
and PA.PROCESS_VERSION = A.VERSION
and PA.INSTANCE_LABEL = process;
-- Save value to cache
wf_process_activity.c_itemtype := itemtype;
wf_process_activity.c_itemkey := itemkey;
wf_process_activity.c_process := process;
wf_process_activity.c_rootid := instid;
return instid;
exception
when NO_DATA_FOUND then
return '';
when OTHERS then
Wf_Core.Context('Wf_Process_Activity', 'RootInstanceId', itemtype,
itemkey, process);
raise;
end RootInstanceId;
--
-- ActivityName
-- Return the activity type and name, given instance id
-- IN
-- actid - instance id
-- OUT
-- act_itemtype - activity itemtype
-- act_name - activity name
--
procedure ActivityName(
actid in number,
act_itemtype out NOCOPY varchar2,
act_name out NOCOPY varchar2)
is
status PLS_INTEGER;
begin
WF_CACHE.GetProcessActivity(actid, status);
if (status <> WF_CACHE.task_SUCCESS) then
select WPA.PROCESS_ITEM_TYPE, WPA.PROCESS_NAME, WPA.PROCESS_VERSION,
WPA.ACTIVITY_ITEM_TYPE, WPA.ACTIVITY_NAME, WPA.INSTANCE_ID,
WPA.INSTANCE_LABEL, WPA.PERFORM_ROLE, WPA.PERFORM_ROLE_TYPE,
WPA.START_END, WPA.DEFAULT_RESULT
into WF_CACHE.ProcessActivities(actid)
from WF_PROCESS_ACTIVITIES WPA
where WPA.INSTANCE_ID = actid;
end if;
act_itemtype := WF_CACHE.ProcessActivities(actid).ACTIVITY_ITEM_TYPE;
act_name := WF_CACHE.ProcessActivities(actid).ACTIVITY_NAME;
exception
when no_data_found then
Wf_Core.Token('ACTID', to_char(actid));
Wf_Core.Token('DATE', '');
Wf_Core.Raise('WFENG_ACTID');
when others then
Wf_Core.Context('Wf_Process_Activity', 'ActivityName', to_char(actid));
raise;
end ActivityName;
--
-- StartInstanceId (PRIVATE)
-- Returns instance_id for a start activity, given root process
-- name, itemtype, and version.
-- IN
-- itemtype - Itemtype of process
-- process - Process name
-- version - Process version
-- activity - Start activity instance label
--
function StartInstanceId(itemtype in varchar2,
process in varchar2,
version in number,
activity in varchar2)
return number
is
instid pls_integer;
colon pls_integer;
label varchar2(30);
begin
-- Parse activity arg into and components.
colon := instr(activity, ':');
if (colon <> 0) then
-- Activity arg is :
label := substr(activity, colon+1);
else
-- Activity arg is just instance label
label := activity;
end if;
select
WPA.INSTANCE_ID
into instid
from WF_PROCESS_ACTIVITIES WPA
where WPA.INSTANCE_LABEL = StartInstanceId.label
and WPA.PROCESS_ITEM_TYPE = StartInstanceId.itemtype
and WPA.PROCESS_NAME = StartInstanceId.process
and WPA.PROCESS_VERSION = StartInstanceId.version
and WPA.START_END = wf_engine.eng_start;
return instid;
exception
when no_data_found then
Wf_Core.Context('Wf_Process_Activity', 'StartInstanceId', itemtype,
process, to_char(version), activity);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('PROCESS', process);
Wf_Core.Token('NAME', activity);
Wf_Core.Raise('WFENG_NOT_START');
when too_many_rows then
Wf_Core.Context('Wf_Process_Activity', 'StartInstanceId', itemtype,
process, to_char(version), activity);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('PROCESS', process);
Wf_Core.Token('NAME', activity);
Wf_Core.Raise('WFENG_ACTIVITY_UNIQUE');
when others then
Wf_Core.Context('Wf_Process_Activity', 'StartInstanceId', itemtype,
process, to_char(version), activity);
raise;
end StartInstanceId;
--
-- ActiveInstanceId (PRIVATE)
-- Returns instance_id for an active instance of an activity.
-- NOTE
-- This is a more efficient version of FindActivity, to be used whenever
-- the current status the activity must have is already known.
-- It is also able to distinguish between duplicate activities, where
-- only one may be active at a given time.
-- IN
-- itemtype - Itemtype of item
-- itemkey - Itemkey of item
-- activity - Activity searching for, specified in the form
-- [:]
-- status - Status of activity, or null if status not known
-- RETURNS
-- Instance id of activity
--
function ActiveInstanceId(itemtype in varchar2,
itemkey in varchar2,
activity in varchar2,
status in varchar2)
return number
is
colon pls_integer;
process varchar2(30);
label varchar2(30);
instid pls_integer;
cur_actid pls_integer;
cur_status varchar2(8);
cur_result varchar2(30);
begin
-- Parse activity arg into and components.
colon := instr(activity, ':');
if (colon <> 0) then
-- Activity arg is :
process := substr(activity, 1, colon-1);
label := substr(activity, colon+1);
else
-- Activity arg is just instance label
process := '';
label := activity;
end if;
-- SYNCHMODE:
-- In synchmode, the row in the WIAS runtime cache MUST be this row,
-- because synch processes can only operate on the current activity.
--
if (itemkey = wf_engine.eng_synch) then
-- Get the current item and status in the cache
Wf_Item_Activity_Status.LastResult(itemtype, itemkey,
cur_actid, cur_status, cur_result);
-- If status doesn't match one asked for, immediate trouble
if (nvl(status, '1') <> nvl(cur_status, '2')) then
raise no_data_found;
end if;
-- Check that activity label passed in matched the current actid
select WPA.INSTANCE_ID
into instid
from WF_PROCESS_ACTIVITIES WPA
where WPA.INSTANCE_LABEL = activeinstanceid.label
and WPA.PROCESS_NAME = nvl(activeinstanceid.process, WPA.PROCESS_NAME)
and WPA.INSTANCE_ID = activeinstanceid.cur_actid;
else
-- NORMAL mode
select WPA.INSTANCE_ID
into instid
from WF_ITEM_ACTIVITY_STATUSES WIAS, WF_PROCESS_ACTIVITIES WPA
where WIAS.ITEM_TYPE = activeinstanceid.itemtype
and WIAS.ITEM_KEY = activeinstanceid.itemkey
and WIAS.ACTIVITY_STATUS = nvl(activeinstanceid.status,
WIAS.ACTIVITY_STATUS)
and WIAS.PROCESS_ACTIVITY = WPA.INSTANCE_ID
and WPA.INSTANCE_LABEL = activeinstanceid.label
and WPA.PROCESS_NAME = nvl(activeinstanceid.process, WPA.PROCESS_NAME);
end if;
return instid;
exception
when no_data_found then
return '';
when too_many_rows then
Wf_Core.Context('Wf_Process_Activity', 'ActiveInstanceId', itemtype,
itemkey, activity, status);
Wf_Core.Token('TYPE', itemtype);
Wf_Core.Token('KEY', itemkey);
Wf_Core.Token('NAME', activity);
Wf_Core.Raise('WFENG_ITEM_ACTIVITY_UNIQUE');
when others then
Wf_Core.Context('Wf_Process_Activity', 'ActiveInstanceId', itemtype,
itemkey, activity, status);
raise;
end ActiveInstanceId;
--
-- IsChild (PRIVATE)
-- Search for any occurrence of an activity in a process tree, either
-- as a direct child, or referenced in an error process attached to a
-- child.
-- This function does a recursive search of the tree. It should only be
-- used if:
-- 1. There may not be an entry in WIAS yet for this activity.
-- 2. You do not know the immediate parent of the activity.
-- IN
-- rootid - The instance_id of the parent process
-- acttype - Activity itemtype searching for
-- actname - Activity name searching for
-- actdate - Active date
-- RETURNS
-- True is activity found anywhere in process tree.
--
function IsChild(
rootid in number,
acttype in varchar2,
actname in varchar2,
actdate in date)
return boolean
is
cursor curs(parentid in pls_integer, actdate in date) is
select WPA2.INSTANCE_ID, WPA2.ACTIVITY_ITEM_TYPE, WPA2.ACTIVITY_NAME
from WF_PROCESS_ACTIVITIES WPA1,
WF_ACTIVITIES WA,
WF_PROCESS_ACTIVITIES WPA2
where WPA1.INSTANCE_ID = parentid
and WPA2.PROCESS_ITEM_TYPE = WA.ITEM_TYPE
and WPA2.PROCESS_NAME = WA.NAME
and WA.ITEM_TYPE = WPA1.ACTIVITY_ITEM_TYPE
and WA.NAME = WPA1.ACTIVITY_NAME
and actdate >= WA.BEGIN_DATE
and actdate < NVL(WA.END_DATE, actdate+1)
and WPA2.PROCESS_VERSION = WA.VERSION;
childarr InstanceArrayTyp;
i pls_integer := 0;
errid pls_integer;
found boolean;
status PLS_INTEGER;
waIND NUMBER;
begin
WF_CACHE.GetProcessActivityInfo(rootid, actdate, status, waIND);
if (status <> WF_CACHE.task_SUCCESS) then
waIND := 0;
select WA.ITEM_TYPE, WA.NAME, WA.VERSION, WA.TYPE, WA.RERUN,
WA.EXPAND_ROLE, WA.COST, WA.ERROR_ITEM_TYPE, WA.ERROR_PROCESS,
WA.FUNCTION, WA.FUNCTION_TYPE, WA.MESSAGE, WA.BEGIN_DATE,
WA.END_DATE, WA.DIRECTION, WPA.PROCESS_ITEM_TYPE,
WPA.PROCESS_NAME, WPA.PROCESS_VERSION, WPA.ACTIVITY_ITEM_TYPE,
WPA.ACTIVITY_NAME, WPA.INSTANCE_ID, WPA.INSTANCE_LABEL,
WPA.PERFORM_ROLE, WPA.PERFORM_ROLE_TYPE, WPA.START_END,
WPA.DEFAULT_RESULT
into WF_CACHE.Activities(waIND).ITEM_TYPE,
WF_CACHE.Activities(waIND).NAME,
WF_CACHE.Activities(waIND).VERSION,
WF_CACHE.Activities(waIND).TYPE,
WF_CACHE.Activities(waIND).RERUN,
WF_CACHE.Activities(waIND).EXPAND_ROLE,
WF_CACHE.Activities(waIND).COST,
WF_CACHE.Activities(waIND).ERROR_ITEM_TYPE,
WF_CACHE.Activities(waIND).ERROR_PROCESS,
WF_CACHE.Activities(waIND).FUNCTION,
WF_CACHE.Activities(waIND).FUNCTION_TYPE,
WF_CACHE.Activities(waIND).MESSAGE,
WF_CACHE.Activities(waIND).BEGIN_DATE,
WF_CACHE.Activities(waIND).END_DATE,
WF_CACHE.Activities(waIND).DIRECTION,
WF_CACHE.ProcessActivities(rootid).PROCESS_ITEM_TYPE,
WF_CACHE.ProcessActivities(rootid).PROCESS_NAME,
WF_CACHE.ProcessActivities(rootid).PROCESS_VERSION,
WF_CACHE.ProcessActivities(rootid).ACTIVITY_ITEM_TYPE,
WF_CACHE.ProcessActivities(rootid).ACTIVITY_NAME,
WF_CACHE.ProcessActivities(rootid).INSTANCE_ID,
WF_CACHE.ProcessActivities(rootid).INSTANCE_LABEL,
WF_CACHE.ProcessActivities(rootid).PERFORM_ROLE,
WF_CACHE.ProcessActivities(rootid).PERFORM_ROLE_TYPE,
WF_CACHE.ProcessActivities(rootid).START_END,
WF_CACHE.ProcessActivities(rootid).DEFAULT_RESULT
from WF_PROCESS_ACTIVITIES WPA, WF_ACTIVITIES WA
where WPA.INSTANCE_ID = rootid
and WA.ITEM_TYPE = WPA.ACTIVITY_ITEM_TYPE
and WA.NAME = WPA.ACTIVITY_NAME
and actdate >= WA.BEGIN_DATE
and actdate < NVL(WA.END_DATE, actdate+1);
waIND := WF_CACHE.HashKey(
WF_CACHE.ProcessActivities(rootid).ACTIVITY_ITEM_TYPE ||
WF_CACHE.ProcessActivities(rootid).ACTIVITY_NAME);
WF_CACHE.Activities(waIND) := WF_CACHE.Activities(0);
end if;
-- Quick check to see if root is already right
if (((WF_CACHE.ProcessActivities(rootid).PROCESS_ITEM_TYPE = acttype) and
(WF_CACHE.ProcessActivities(rootid).PROCESS_NAME = actname)) or
((WF_CACHE.Activities(waIND).ITEM_TYPE = acttype) and
(WF_CACHE.Activities(waIND).NAME = actname))) then
return(TRUE);
end if;
-- If activity at rootid has an error process, check it recursively
-- for a reference to the activity.
if (WF_CACHE.Activities(waIND).ERROR_PROCESS is not null) then
-- Get root id for the error process
begin
select WPA.INSTANCE_ID
into errid
from WF_PROCESS_ACTIVITIES WPA, WF_ACTIVITIES WA
where WPA.PROCESS_ITEM_TYPE = WF_CACHE.Activities(waIND).ERROR_ITEM_TYPE
and WPA.PROCESS_NAME = 'ROOT'
and WPA.PROCESS_VERSION = WA.VERSION
and WA.ITEM_TYPE = WPA.PROCESS_ITEM_TYPE
and WA.NAME = WPA.PROCESS_NAME
and actdate >= WA.BEGIN_DATE
and actdate < NVL(WA.END_DATE, actdate+1)
and WPA.INSTANCE_LABEL = WF_CACHE.Activities(waIND).ERROR_PROCESS;
exception
when no_data_found then
-- Error process is invalid, so ignore it
errid := '';
end;
if (errid is not null) then
-- If activity found in error process return immediately.
-- If not, continue on to check proper children of rootid.
if (IsChild(errid, acttype, actname, actdate)) then
return(TRUE);
end if;
end if;
end if;
-- Check all children of rootid
for child in curs(rootid, actdate) loop
-- Desired activity found. Return immediately.
if ((child.activity_item_type = acttype) and
(child.activity_name = actname)) then
return(TRUE);
end if;
-- Save all other children in array to be checked
childarr(i) := child.instance_id;
i := i + 1;
end loop;
childarr(i) := '';
-- Loop through and recursively search any PROCESS-type children.
i := 0;
while (childarr(i) is not null) loop
if (Wf_Activity.Instance_Type(childarr(i), actdate) =
wf_engine.eng_process) then
found := IsChild(childarr(i), acttype, actname, actdate);
-- If a non-null value is returned, then the activity was
-- found in this sub-tree. Return the value and exit immediately.
if (found) then
return(TRUE);
end if;
end if;
i := i + 1;
end loop;
-- If you made it here the activity was not found anywhere in the tree.
return(FALSE);
exception
when OTHERS then
Wf_Core.Context('Wf_Process_Activity', 'IsChild', to_char(rootid),
acttype, actname, to_char(actdate));
raise;
end IsChild;
--
-- FindActivity (PRIVATE)
-- Find the instance_id of an activity instance in the tree rooted at
-- parentid in the WPA table.
-- This function does a recursive search of the tree. It should only be
-- used if:
-- 1. There may not be an entry in WIAS yet for this activity instance.
-- (See ActiveInstanceId above)
-- 2. You do not know the immediate parent of the activity.
-- IN
-- parentid - The instance_id of the parent process.
-- activity - Activity searching for, specified in the form
-- [:]
-- actdate - Active date
-- RETURNS
-- Instance id of activity instance in process tree rooted at parentid.
-- Returns null if not found.
--
function FindActivity(parentid in number,
activity in varchar2,
actdate in date)
return number
is
colon pls_integer;
process varchar2(30);
label varchar2(30);
status PLS_INTEGER;
cursor curs(parentid in pls_integer, actdate in date) is
select WPA2.PROCESS_NAME, WPA2.INSTANCE_ID, WPA2.INSTANCE_LABEL
from WF_PROCESS_ACTIVITIES WPA1,
WF_ACTIVITIES WA,
WF_PROCESS_ACTIVITIES WPA2
where WPA1.INSTANCE_ID = parentid
and WPA2.PROCESS_ITEM_TYPE = WA.ITEM_TYPE
and WPA2.PROCESS_NAME = WA.NAME
and WA.ITEM_TYPE = WPA1.ACTIVITY_ITEM_TYPE
and WA.NAME = WPA1.ACTIVITY_NAME
and actdate >= WA.BEGIN_DATE
and actdate < NVL(WA.END_DATE, actdate+1)
and WPA2.PROCESS_VERSION = WA.VERSION;
childarr InstanceArrayTyp;
i pls_integer := 0;
childid pls_integer;
actid pls_integer := '';
wf_dup_activity exception;
begin
-- Parse activity arg into and components.
colon := instr(activity, ':');
if (colon <> 0) then
-- Activity arg is :
process := substr(activity, 1, colon-1);
label := substr(activity, colon+1);
else
-- Activity arg is just instance label
process := '';
label := activity;
end if;
WF_CACHE.GetProcessActivity(parentid, status);
if (status <> WF_CACHE.task_SUCCESS) then
select WPA.PROCESS_ITEM_TYPE, WPA.PROCESS_NAME, WPA.PROCESS_VERSION,
WPA.ACTIVITY_ITEM_TYPE, WPA.ACTIVITY_NAME, WPA.INSTANCE_ID,
WPA.INSTANCE_LABEL, WPA.PERFORM_ROLE, WPA.PERFORM_ROLE_TYPE,
WPA.START_END, WPA.DEFAULT_RESULT
into WF_CACHE.ProcessActivities(parentid)
from WF_PROCESS_ACTIVITIES WPA
where WPA.INSTANCE_ID = parentid;
end if;
if ((WF_CACHE.ProcessActivities(parentid).PROCESS_NAME =
nvl(process, WF_CACHE.ProcessActivities(parentid).PROCESS_NAME)) and
(WF_CACHE.ProcessActivities(parentid).INSTANCE_LABEL = label)) then
return(parentid);
end if;
for child in curs(parentid, actdate) loop
-- Activity with this name found.
if ((child.process_name = nvl(process, child.process_name)) and
(child.instance_label = label)) then
if ((actid is not null) and (actid <> child.instance_id)) then
-- Activity already found once in this process - raise duplicate error.
raise wf_dup_activity;
else
-- Save id of activity
actid := child.instance_id;
end if;
end if;
-- Save all other children in array to be checked
childarr(i) := child.instance_id;
i := i + 1;
end loop;
childarr(i) := '';
-- Loop through and recursively search any PROCESS-type children.
i := 0;
while (childarr(i) is not null) loop
if (Wf_Activity.Instance_Type(childarr(i), actdate) =
wf_engine.eng_process) then
childid := FindActivity(childarr(i), activity, actdate);
-- If a non-null value is returned, then the activity was
-- found somewhere in this sub-tree.
if (childid is not null) then
if ((actid is not null) and (actid <> childid)) then
-- Activity already found somewhere else. Raise error.
raise wf_dup_activity;
else
-- Save id of activity
actid := childid;
end if;
end if;
end if;
i := i + 1;
end loop;
-- Return saved actid. If activity not found anywhere in tree this
-- will still be null.
return(actid);
exception
when wf_dup_activity then
Wf_Core.Context('Wf_Process_Activity', 'FindActivity', to_char(parentid),
activity, to_char(actdate));
Wf_Core.Token('NAME', activity);
Wf_Core.Raise('WFENG_ACTIVITY_UNIQUE');
when OTHERS then
Wf_Core.Context('Wf_Process_Activity', 'FindActivity', to_char(parentid),
activity, to_char(actdate));
raise;
end FindActivity;
end WF_PROCESS_ACTIVITY;
/
--show errors package body WF_PROCESS_ACTIVITY
commit;
exit;