<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Oracle Tales</title>
	<atom:link href="http://www.stijf.com/wordpress/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.stijf.com/wordpress</link>
	<description>Just another Oracle weblog</description>
	<lastBuildDate>Sun, 14 Feb 2010 10:16:44 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.3</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Selecting from custom types in (workflow) tables</title>
		<link>http://www.stijf.com/wordpress/2010/02/selecting-from-custom-types-in-workflow-tables/</link>
		<comments>http://www.stijf.com/wordpress/2010/02/selecting-from-custom-types-in-workflow-tables/#comments</comments>
		<pubDate>Sun, 14 Feb 2010 10:16:44 +0000</pubDate>
		<dc:creator>Arian Stijf</dc:creator>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[Scripts]]></category>
		<category><![CDATA[Workflow]]></category>
		<category><![CDATA[eBS]]></category>

		<guid isPermaLink="false">http://www.stijf.com/wordpress/?p=389</guid>
		<description><![CDATA[&#160;
After a long silence, it&#8217;s time to write another blog entry. I received a request to write about Oracle Reports. And I think that will be a new series (Even though I didn&#8217;t finish the workflow series yet).&#160;
&#160;
But the last few days, I&#8217;ve been working on cleaning up workflow tables.&#160;
Most of these tables are very [...]]]></description>
			<content:encoded><![CDATA[<p>&nbsp;</p>
<p>After a long silence, it&rsquo;s time to write another blog entry. I received a request to write about Oracle Reports. And I think that will be a new series (Even though I didn&rsquo;t finish the workflow series yet).&nbsp;</p>
<p>&nbsp;</p>
<p>But the last few days, I&rsquo;ve been working on cleaning up workflow tables.&nbsp;</p>
<p>Most of these tables are very straightforward and you can find queries and descriptions in the workflow series. However, there are some more complex cases. There are the advanced queuing (aq) tables. And also some data hidden in wf_item_attribute_values for items started by the Business Event System (BES) (reminder to self: Write that article about BES too).</p>
<p>&nbsp;</p>
<p>In the aq-tables, the payload of the message (i.e. the data transferred by the message) is stored in a custom type.&nbsp;The same goes for &nbsp;the event_data in wf_item_attribute_values.&nbsp;In this article we&rsquo;ll see how we can get the data from those &lsquo;strange&rsquo; columns.&nbsp;</p>
<p>&nbsp;</p>
<p>Let us start with wf_item_attribute_values. Processes that are started from a business event, store the data from the originating business event in the column &lsquo;EVENT_VALUE&rsquo;. This has a type &lsquo;WF_EVENT_T&rsquo;. When you query it in a sql*plus session, you will see a huge column filled with something like:&nbsp;</p>
<p>&nbsp;</p>
<pre>EVENT_VALUE(PRIORITY, SEND_DATE, RECEIVE_DATE, CORRELATION_ID, PARAMETER_LIST(NAME, VALUE), EVENT_NA
----------------------------------------------------------------------------------------------------
WF_EVENT_T(0, &#39;17-SEP-09&#39;, NULL, NULL, WF_PARAMETER_LIST_T(WF_PARAMETER_T(&#39;TASK_ID&#39;, &#39;16719879&#39;), WF(&#39;ABORT_WORKFLOW&#39;, &#39;N&#39;), WF_PARAMETER_T(&#39;SUB_GUID&#39;, &#39;73BAB9A51BAF5307E04400144F687CA0&#39;)), &#39;oracle.ap, NULL, NULL)</pre>
<p>&nbsp;</p>
<p>The problem that many ebs-dba&rsquo;s are facing is how to select the data inside this column. In this case, it would be the task_id that we are interested in. Oracle delivers several API&#39;s for use in PL/SQL. But sometimes you want plain SQL.&nbsp;</p>
<p>One way to do it is to use a clever substr/instr construction or a regular expression. But the efficient way to do it, is to tell Oracle which info you want.&nbsp;</p>
<p>Let&rsquo;s take a look at the custom type. As mentioned, it is WF_EVENT_T. We can find the description in the DBA_TYPES table.</p>
<p>&nbsp;</p>
<pre>select owner,type_name,typecode,attributes,methods
from dba_types
where type_name=&#39;WF_EVENT_T&#39;;

OWNER                          TYPE_NAME                      TYPECODE                       ATTRIBUTES    METHODS
------------------------------ ------------------------------ ------------------------------ ---------- ----------
APPS                           WF_EVENT_T                     OBJECT                                 13         31</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>This tells us that the WF_EVENT_T is an object type.&nbsp;</p>
<p>We can find its attributes in DBA_TYPE_ATTRS:&nbsp;</p>
<pre>select owner,type_name,attr_name,attr_type_owner,attr_type_name,length
from dba_type_attrs
where type_name=&rsquo;WF_EVENT_T&rsquo;;

OWNER		TYPE_NAME	ATTR_NAME         	ATTR_TYPE_OWNER		ATTR_TYPE_NAME
---------	------------	-------------------	----------------	--------------
APPS   		WF_EVENT_T      CORRELATION_ID					VARCHAR2
APPS     	WF_EVENT_T      ERROR_MESSAGE       				VARCHAR2
APPS     	WF_EVENT_T      ERROR_STACK         				VARCHAR2
APPS     	WF_EVENT_T      ERROR_SUBSCRIPTION  				RAW
APPS     	WF_EVENT_T      EVENT_DATA          				CLOB
APPS     	WF_EVENT_T      EVENT_KEY					VARCHAR2
APPS     	WF_EVENT_T      EVENT_NAME          				VARCHAR2
APPS     	WF_EVENT_T      FROM_AGENT         	APPS			WF_AGENT_T
APPS     	WF_EVENT_T      PARAMETER_LIST    	APPS			WF_PARAMETER_LIST_T
APPS     	WF_EVENT_T      PRIORITY         				NUMBER
APPS     	WF_EVENT_T      RECEIVE_DATE     				DATE
APPS     	WF_EVENT_T     	SEND_DATE        				DATE
APPS     	WF_EVENT_T     	TO_AGENT         	APPS			WF_AGENT_T<span class="Apple-style-span" style="white-space: normal;">
</span></pre>
<p>You see that the attributes are defined including their datatype, which can be a seeded datatype (VARCHAR2) or a custom one (WF_PARAMETER_LIST_T). Now that we now the attributes of the type, we can select them directly. To select the &lsquo;PRIORITY&rsquo;, just use an extra qualifier:&nbsp;</p>
<pre>select v.event_value.priority
from   wf_item_attribute_values v
where  ROWNUM = 1;

EVENT_VALUE.PRIORITY
--------------------
                   0<span class="Apple-style-span" style="white-space: normal;">
</span></pre>
<p>But how about the &lsquo;PARAMETER_LIST&rsquo;? That is where the task_id was stored. Let&rsquo;s check the WF_PARAMETER_LIST_T definition:&nbsp;</p>
<pre>select owner,type_name,typecode,attributes,methods
from   dba_types
where  type_name =&#39;WF_PARAMETER_LIST_T&#39;;

OWNER	  TYPE_NAME         	TYPECODE  		ATTRIBUTES  METHODS
--------- -------------------	-------------------	----------- -------
APPS      WF_PARAMETER_LIST_T	COLLECTION       		  0       0
</pre>
<p>&nbsp;</p>
<p>This time, the type is a collection. We can find more info about a collection with:&nbsp;</p>
<pre>select type_name,coll_type,elem_type_owner,elem_type_name
from 	 dba_coll_types
where  type_name=&#39;WF_PARAMETER_LIST_T&#39;;

TYPE_NAME                      COLL_TYPE                      ELEM_TYPE_OWNER                ELEM_TYPE_NAME
------------------------------ ------------------------------ ------------------------------ --------------
WF_PARAMETER_LIST_T            VARYING ARRAY                  APPS                           WF_PARAMETER_T
</pre>
<p>&nbsp;</p>
<p>So the WF_PARAMETER_LIST_T is a Varray of WF_PARAMETER_T. Before we look at selecting from Varrays, we first check what WF_PARAMETER_T looks like:</p>
<pre>select owner,type_name,typecode,attributes,methods
from   dba_types
where  type_name =&#39;WF_PARAMETER_T&#39;;

OWNER	  TYPE_NAME           TYPECODE	ATTRIBUTES METHODS
--------- ------------------- ---------	---------- ----------
APPS  	  WF_PARAMETER_T      OBJECT             2          4
</pre>
<p>&nbsp;</p>
<p>That is an object type again. So we select:&nbsp;</p>
<pre>select owner,type_name,attr_name,attr_type_owner,attr_type_name
from   dba_type_attrs
where  type_name=&#39;WF_PARAMETER_T&#39;;

OWNER	TYPE_NAME             ATTR_NAME	ATTR_TYPE_OWNER	    ATTR_TYPE_NAME
--------- ------------------- --------- ------------------- --------------
APPS	WF_PARAMETER_T        NAME			    VARCHAR2
APPS	WF_PARAMETER_T	      VALUE			    VARCHAR2
</pre>
<p>&nbsp;</p>
<p>Ok. We now know the whole structure of the parameter list. Back to the Varray. &nbsp;A Varray (Varying Array) is of course an array structure. Since this is similar to a table structure, you can cast the Varray into a table. Then you use the casted table to select your data. Let&rsquo;s do that to get the names from the parameter list.</p>
<pre>select t.name
from   wf_item_attribute_values v
,      table(v.event_value.parameter_list) t
Where  v.event_value IS NOT NULL
And    ROWNUM = 1;

NAME
------------------------------
TASK_ID
</pre>
<p>&nbsp;</p>
<p>Now that&rsquo;s a neat trick. We can join our table to its own column!&nbsp;</p>
<p>In our case, we only have a task_id parameter. We could do the same again, to get the value of the parameter from the value column.</p>
<p>But to join wf_item_attribute_values to itself is a very expensive operation. Take a look at the explain plan:&nbsp;</p>
<pre>PLAN_TABLE_OUTPUT

-----------------------------------------------------------------------------------------------------------------
| Id  | Operation                           |  Name                     | Rows  | Bytes | Cost  | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                           |     1 |    62 |   432M|       |       |
|*  1 |  COUNT STOPKEY                      |                           |       |       |       |       |       |
|   2 |   NESTED LOOPS                      |                           |   207G|    11T|   432M|       |       |
|   3 |    PARTITION RANGE ALL              |                           |       |       |       |     1 |    77 |
|   4 |     PARTITION HASH ALL              |                           |       |       |       |     1 |     8 |
|*  5 |      TABLE ACCESS FULL              | WF_ITEM_ATTRIBUTE_VALUES  |    25M|  1453M|   948K|     1 |   616 |
|   6 |    COLLECTION ITERATOR PICKLER FETCH|                           |       |       |       |       |       |
-----------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(ROWNUM=1)
   5 - filter(SYS_OP_NOEXPAND(&quot;V&quot;.&quot;EVENT_VALUE&quot;) IS NOT NULL)

Note: cpu costing is off
</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>That can kind of hurt if it,were it not for the NL with a COUNT STOPKEY (Because of the rownum=1). The reason for the expensive plan, is because for every row from wf_item_attribute_values, Oracle needs to get the data from event_value. It basically does a carthesian join with itself.</p>
<p>So here is another way to get your data:&nbsp;</p>
<pre>select v.item_key
,      (select value
        from table(v.event_value.parameter_list) t
        where t.name=&#39;TASK_ID&#39; ) task_id
from   wf_item_attribute_values v
where  v.event_value is not null
and    rownum=1;

ITEM_KEY						TASK_ID
-------------------------------------------------	--------
oracle.apps.jtf.cac.task.createTask-155563676		16719879
</pre>
<p>&nbsp;</p>
<p>Now we use a scalar subquery, where Oracle will access only the event_value for the rows that will be returned to the user. You can see this in the explain plan by the collection iterator picklefetch (= the operation that collects the data from a collection type) being pushed up to just before the select.&nbsp;</p>
<pre>PLAN_TABLE_OUTPUT

---------------------------------------------------------------------------------------------------------------
| Id  | Operation                         |  Name                     | Rows  | Bytes | Cost  | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |                           |     1 |    60 |   948K|       |       |
|*  1 |  COLLECTION ITERATOR PICKLER FETCH|                           |       |       |       |       |       |
|*  2 |  COUNT STOPKEY                    |                           |       |       |       |       |       |
|   3 |   PARTITION RANGE ALL             |                           |       |       |       |     1 |    77 |
|   4 |    PARTITION HASH ALL             |                           |       |       |       |     1 |     8 |
|*  5 |     TABLE ACCESS FULL             | WF_ITEM_ATTRIBUTE_VALUES  |    25M|  1453M|   948K|     1 |   616 |
---------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(SYS_OP_ATG(VALUE(KOKBF$),1,2,2)=&#39;TASK_ID&#39;)
   2 - filter(ROWNUM=1)
   5 - filter(SYS_OP_NOEXPAND(&quot;SYS_ALIAS_1&quot;.&quot;EVENT_VALUE&quot;) IS NOT NULL)

Note: cpu costing is off
</pre>
<p>&nbsp;</p>
<p>Still expensive. But much better.</p>
<p>&nbsp;</p>
<p>So that will let us peek into the &nbsp;event_value column on wf_item_attribute values. Be aware that different processes will enter different parameters into the event_value column. But with the information above you should be able to understand how to select the data and use it in your own queries.&nbsp;</p>
<p>&nbsp;</p>
<p>Now let&rsquo;s take a look at the AQ-tables?&nbsp;</p>
<p>As I mentioned, they carry their payloads in custom types too. One that most ebs-dba&rsquo;s will have seen is WF_NOTIFICATION_OUT. When the workflow engine generates a notification, it will be stored in &lsquo;WF_NOTIFICATIONS&rsquo;. However, a message will be posted on &lsquo;WF_NOTIFICATION_OUT&rsquo; too. This message will be read by the workflow notification mailer, which will use it to get the relevant data from WF_NOTIFICATIONS.</p>
<p>&nbsp;</p>
<p>If you haven&rsquo;t run the notification mailer for a long time, you might want to clean up WF_NOTIFICATION_OUT a bit. There is a script available from Metalink to do the job. (There is also a quicker but unsupported way). But you might want to see the records in WF_NOTIFICATION_OUT related to WF_NOTIFICATIONS to decide if a cleanup is appropriate.&nbsp;</p>
<p>&nbsp;</p>
<p>Let&rsquo;s look at WF_NOTIFICATION_OUT:</p>
<pre>Name                    Null?    Type
----------------------- -------- ----------------
Q_NAME                           VARCHAR2(30)
MSGID                   NOT NULL RAW(16)
CORRID                           VARCHAR2(128)
PRIORITY                         NUMBER
STATE                            NUMBER
DELAY                            DATE
EXPIRATION                       NUMBER
TIME_MANAGER_INFO                DATE
LOCAL_ORDER_NO                   NUMBER
CHAIN_NO                         NUMBER
CSCN                             NUMBER
DSCN                             NUMBER
ENQ_TIME                         DATE
ENQ_UID                          NUMBER
ENQ_TID                          VARCHAR2(30)
DEQ_TIME                         DATE
DEQ_UID                          NUMBER
DEQ_TID                          VARCHAR2(30)
RETRY_COUNT                      NUMBER
EXCEPTION_QSCHEMA                VARCHAR2(30)
EXCEPTION_QUEUE                  VARCHAR2(30)
STEP_NO                          NUMBER
RECIPIENT_KEY                    NUMBER
DEQUEUE_MSGID                    RAW(16)
SENDER_NAME                      VARCHAR2(30)
SENDER_ADDRESS                   VARCHAR2(1024)
SENDER_PROTOCOL                  NUMBER
USER_DATA                        SYS.AQ$_JMS_TEXT _MESSAGE
</pre>
<p>&nbsp;</p>
<p>The regular AQ-information is there. And our payload in USER_DATA. This time it&rsquo;s an AQ-type.&nbsp;</p>
<p>Let&rsquo;s follow the same procedure:&nbsp;</p>
<pre>select type_name,typecode,attributes,methods
from   dba_types
where  type_name=&#39;AQ$_JMS_TEXT_MESSAGE&#39;;

TYPE_NAME                      TYPECODE                       ATTRIBUTES    METHODS
------------------------------ ------------------------------ ---------- ----------
AQ$_JMS_TEXT_MESSAGE           OBJECT                                  4         34
</pre>
<p>An object type. So let&rsquo;s see its attributes:&nbsp;</p>
<pre>select type_name,attr_name,attr_type_owner,attr_type_name
from   dba_type_attrs
where  type_name=&#39;AQ$_JMS_TEXT_MESSAGE&#39;;

TYPE_NAME                      ATTR_NAME                      ATTR_TYPE_OWNER                ATTR_TYPE_NAME
------------------------------ ------------------------------ ------------------------------ --------------
AQ$_JMS_TEXT_MESSAGE           HEADER                         SYS                            AQ$_JMS_HEADER
AQ$_JMS_TEXT_MESSAGE           TEXT_LEN                                                      INTEGER
AQ$_JMS_TEXT_MESSAGE           TEXT_LOB                                                      CLOB
AQ$_JMS_TEXT_MESSAGE           TEXT_VC                                                       VARCHAR2
</pre>
<p>&nbsp;</p>
<p>We can already read the text_len, text_lob and text_vc. The last 2 contain an XML with a reference to the notification. But the information that I want to show you is in the header. This is a type &lsquo;AQ$_JMS_HEADER&rsquo;. When we check this one, we see that it again is an object with these attributes:</p>
<pre>select type_name,typecode,attributes,methods
from   dba_types
where  type_name=&#39;AQ$_JMS_HEADER&#39;;

TYPE_NAME                      TYPECODE                       ATTRIBUTES    METHODS
------------------------------ ------------------------------ ---------- ----------
AQ$_JMS_HEADER                 OBJECT                                  7         31

select type_name,attr_name,attr_type_owner,attr_type_name
from   dba_type_attrs
where  type_name=&#39;AQ$_JMS_HEADER&#39;;

TYPE_NAME                      ATTR_NAME                      ATTR_TYPE_OWNER                ATTR_TYPE_NAME
------------------------------ ------------------------------ ------------------------------ -------
AQ$_JMS_HEADER                 APPID                                                         VARCHAR2
AQ$_JMS_HEADER                 GROUPID                                                       VARCHAR2
AQ$_JMS_HEADER                 GROUPSEQ                                                      INTEGER
AQ$_JMS_HEADER                 PROPERTIES                     SYS                            AQ$_JMS_USERPROPARRAY
AQ$_JMS_HEADER                 REPLYTO                        SYS                            AQ$_AGENT
AQ$_JMS_HEADER                 TYPE                                                          VARCHAR2
AQ$_JMS_HEADER                 USERID                                                        VARCHAR2
</pre>
<p>&nbsp;</p>
<p>As you can imagine, we need to drill down into &lsquo;PROPERTIES&rsquo;. The other attributes might or might not contain any data, depending on the notification. properties is a Varray of &lsquo;AQ$_JMS_USERPROPERTY&rsquo;:</p>
<pre>select type_name,typecode,attributes,methods
from   dba_types
where  type_name=&#39;AQ$_JMS_USERPROPARRAY&#39;;

TYPE_NAME                      TYPECODE                       ATTRIBUTES    METHODS
------------------------------ ------------------------------ ---------- ----------
AQ$_JMS_USERPROPARRAY          COLLECTION                              0          0

select type_name,coll_type,elem_type_owner,elem_type_name
from 	 dba_coll_types
where  type_name=&#39;AQ$_JMS_USERPROPARRAY&#39;;

TYPE_NAME                      COLL_TYPE                      ELEM_TYPE_OWNER                ELEM_TYPE_NAME
------------------------------ ------------------------------ ------------------------------ --------------------
AQ$_JMS_USERPROPARRAY          VARYING ARRAY                  SYS                            AQ$_JMS_USERPROPERTY
</pre>
<p>&nbsp;</p>
<p>One more level to check:&nbsp;</p>
<pre>select type_name,typecode,attributes,methods
from   dba_types
where  type_name=&#39;AQ$_JMS_USERPROPERTY&#39;;

TYPE_NAME                      TYPECODE                       ATTRIBUTES    METHODS
------------------------------ ------------------------------ ---------- ----------
AQ$_JMS_USERPROPERTY           OBJECT                                  5          0

select type_name,attr_name,attr_type_owner,attr_type_name
from   dba_type_attrs
where  type_name=&#39;AQ$_JMS_USERPROPERTY&#39;;

TYPE_NAME                      ATTR_NAME                      ATTR_TYPE_OWNER                ATTR_TYPE_NAME
------------------------------ ------------------------------ ------------------------------ -------
AQ$_JMS_USERPROPERTY           JAVA_TYPE                                                     INTEGER
AQ$_JMS_USERPROPERTY           NAME                                                          VARCHAR2
AQ$_JMS_USERPROPERTY           NUM_VALUE                                                     NUMBER
AQ$_JMS_USERPROPERTY           STR_VALUE                                                     VARCHAR2
AQ$_JMS_USERPROPERTY           TYPE                                                          INTEGER<span class="Apple-style-span" style="white-space: normal;">
</span></pre>
<p>&nbsp;</p>
<p>So let&rsquo;s see what properties we have. I just query the whole contents of properties for the first row in wf_notification_out (This particular system doesn&rsquo;t have a WF-mailer running. If it is running, chances are that you won&rsquo;t have any records in WF_NOTIFICATION_OUT).</p>
<pre>select p.*
from   (select *
        from wf_notification_out
        where rownum=1) n
,      table(n.user_data.header.properties) p;

NAME	              	TYPE  STR_VALUE	                                   NUM_VALUE JAVA_TYPE
----------------------- ----- -------------------------------------------- --------- ---------
BES_EVENT_NAME	      	  100 oracle.apps.wf.notification.send                              27
BES_EVENT_KEY	      	  100                                               39388680        27
BES_PRIORITY	      	  200                                                     50        23
BES_SEND_DATE	       	  100 2009/03/06 01:12:23		                            27
BES_RECEIVE_DATE	  100 2009/03/06 01:12:34		                            27
BES_FROM_AGENT	      	  100 WF_NOTIFICATION_OUT@TESTDB.STIJF.COM                          27
BES_ERROR_SUBSCRIPTION    100 C10E7C2EF71253C1E0340800208D03E1		           	    27
NOTIFICATION_ID	      	  100 39388680		                                            27
ROLE		       	  100 FND_RESP535:21704		                                    27
GROUP_ID		  100 39388680		                                            27
Q_CORRELATION_ID	  100 XDPWFSTD		                                            27
</pre>
<p>There you go. Among others, the &lsquo;NOTIFICATION_ID&rsquo; is there. It maps to the notification_id on &lsquo;WF_NOTIFICATIONS&rsquo;. And by now, we have seen enough to select it directly in our queries:</p>
<pre>select n.msgid,(select str_value
                from   table(n.user_data.header.properties)
		where  name=&#39;NOTIFICATION_ID&#39;) notification_id
from   wf_notification_out n
where  rownum=1;

MSGID			    	 NOTIFICATION_ID
-------------------------------- ---------------
64672D4985E57075E04400144F687CA0	39388680<span class="Apple-style-span" style="white-space: normal;">
</span></pre>
<p>That concludes this article. Based on the above, you will be able to select the data you want.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stijf.com/wordpress/2010/02/selecting-from-custom-types-in-workflow-tables/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>New article on custom types available</title>
		<link>http://www.stijf.com/wordpress/2010/02/new-article-on-custom-types-available/</link>
		<comments>http://www.stijf.com/wordpress/2010/02/new-article-on-custom-types-available/#comments</comments>
		<pubDate>Sat, 13 Feb 2010 17:57:57 +0000</pubDate>
		<dc:creator>Arian Stijf</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.stijf.com/wordpress/?p=384</guid>
		<description><![CDATA[I just wrote an item about selecting from the special column types in the workflow tables. (for example EVENT_VALUE in WF_ITEM_ATTRIBUTE_VALUE and how to select notification_id from the USER_DATA in WF_NOTIFICATION_OUT)
Didn&#39;t have any time to format it in HTML format yet. So for the moment it is only available in PDF format from the link-page [...]]]></description>
			<content:encoded><![CDATA[<p>I just wrote an item about selecting from the special column types in the workflow tables. (for example EVENT_VALUE in WF_ITEM_ATTRIBUTE_VALUE and how to select notification_id from the USER_DATA in WF_NOTIFICATION_OUT)</p>
<p>Didn&#39;t have any time to format it in HTML format yet. So for the moment it is only available in PDF format from the <a href="http://www.stijf.com/wordpress/about/link-page/">link-page</a> and from <a href="http://www.stijf.com/wordpress/PDF/Workflow object types.pdf">here</a>.&nbsp;</p>
<p>Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stijf.com/wordpress/2010/02/new-article-on-custom-types-available/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Another workflow series redo</title>
		<link>http://www.stijf.com/wordpress/2009/10/another-workflow-series-redo/</link>
		<comments>http://www.stijf.com/wordpress/2009/10/another-workflow-series-redo/#comments</comments>
		<pubDate>Mon, 05 Oct 2009 17:11:31 +0000</pubDate>
		<dc:creator>Arian Stijf</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.stijf.com/wordpress/?p=377</guid>
		<description><![CDATA[Ok. For the third time, I&#8217;m redoing the workflow series.
Since the wordpress interface keeps giving me headache with code and code results, I put it in PDF files.&#160;
You can find them all from the links page&#160;.
Or separately. Part 1 is here.
Part 2 is here
The others will follow soon.
&#160;
]]></description>
			<content:encoded><![CDATA[<p>Ok. For the third time, I&#8217;m redoing the workflow series.</p>
<p>Since the wordpress interface keeps giving me headache with code and code results, I put it in PDF files.&nbsp;</p>
<p>You can find them all from the<a href="http://www.stijf.com/wordpress/about/link-page/"> links page&nbsp;</a>.</p>
<p>Or separately. Part 1 is <a href="http://www.stijf.com/wordpress/PDF/oracle-workflow-for-ebs-dbas-part-1.pdf">here</a>.</p>
<p>Part 2 is <a href="http://www.stijf.com/wordpress/PDF/workflow-for-ebs-dbas-part-2.pdf">here</a></p>
<p>The others will follow soon.</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stijf.com/wordpress/2009/10/another-workflow-series-redo/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Oracle workflow for eBS DBA&#8217;s (Part 4b ;-)</title>
		<link>http://www.stijf.com/wordpress/2009/07/oracle-workflow-for-ebs-dbas-part-4b/</link>
		<comments>http://www.stijf.com/wordpress/2009/07/oracle-workflow-for-ebs-dbas-part-4b/#comments</comments>
		<pubDate>Fri, 31 Jul 2009 17:15:54 +0000</pubDate>
		<dc:creator>Arian Stijf</dc:creator>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[Workflow]]></category>
		<category><![CDATA[eBS]]></category>
		<category><![CDATA[abort]]></category>
		<category><![CDATA[abortprocess]]></category>
		<category><![CDATA[DBA]]></category>
		<category><![CDATA[resume]]></category>
		<category><![CDATA[resumeprocess]]></category>
		<category><![CDATA[script]]></category>
		<category><![CDATA[suspend]]></category>
		<category><![CDATA[suspendprocess]]></category>

		<guid isPermaLink="false">http://www.stijf.com/wordpress/?p=369</guid>
		<description><![CDATA[&#160;A reader pointed out that I didn&#8217;t fulfill my promise to write about suspend, resume and abort in part 4 of &#8216;Oracle workflow for eBS DBA&#8217;s.
&#160;
So to make up for that omission, I will first write a separate note about it here. Then I&#8217;ll incorporate it in the article at a later time.&#160;
&#160;
Let&#8217;s start with [...]]]></description>
			<content:encoded><![CDATA[<p>&nbsp;A reader pointed out that I didn&#8217;t fulfill my promise to write about suspend, resume and abort in part 4 of &#8216;Oracle workflow for eBS DBA&#8217;s.</p>
<div>&nbsp;</div>
<div>So to make up for that omission, I will first write a separate note about it here. Then I&#8217;ll incorporate it in the article at a later time.&nbsp;</div>
<div>&nbsp;</div>
<div>Let&#8217;s start with the abort.&nbsp;</div>
<div>In part 3, we saw that we can get workflows in a state where they will never be able to continue again. We did this by setting the &#8216;On Revisit&#8217; property to ignore.&nbsp;</div>
<div>&nbsp;</div>
<div>The correct way to handle these workflows is to run a workflow background engine with parameter &#8216;process_stuck&#8217; set to &#8216;TRUE&#8217;. That will set the item status to &#8216;Error&#8217; and run the appropriate error process.&nbsp;</div>
<div>&nbsp;</div>
<div>But there may be reasons where you want to just abort the item, without error processing.&nbsp;</div>
<div>&nbsp;</div>
<div>For those situations Oracle provides the &#8216;wf_engine.abortprocess&#8217; API.&nbsp;</div>
<div>The API will set the status of the process to complete. If a result is needed, you can set this on the call to the API. It defaults to &#8216;#FORCE&#8217; (wf_engine.eng_force constant).</div>
<div>&nbsp;</div>
<div>Let&#8217;s see how this works. First I used the &#8216;MAIN_DBA_PROCESS&#8217; from part 3 of the series, and I set the &#8216;On Revisit&#8217; for the &#8216;LOOP_COUNTER&#8217; to &#8216;Ignore&#8217;.</div>
<div>Now when I run the process, we get this result:&nbsp;</div>
<div>&nbsp;</div>
<div>
<pre language="SQL">
INSTANCE_LABEL   FUNCTION                      BEGIN_DATE         END_DATE           STATUS   RESULT   OUTBOUND_QUEUE_ID
DBA_MAIN_PROCESS &nbsp;                             31-7-2009 12:36:58                   &nbsp;ACTIVE   #NULL&nbsp;
START            START                         31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE #NULL&nbsp;
INITIALIZE_FLOW  XXX_WF_DBA.init               31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE COMPLETE&nbsp;
COMPARETEXT      WF_STANDARD.COMPARE           31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE EQ&nbsp;
CHECK_INVALIDS   XXX_CHECK_INVALIDS            31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE Y&nbsp;
GET_INVALIDS     XXX_WF_UTILS.get_invalids     31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE &nbsp;
LOOPCOUNTER      WF_STANDARD.LOOPCOUNTER       31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE LOOP&nbsp;
PROCESS_INVALIDS XXX_WF_UTILS.process_invalids 31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE &nbsp;
DEFER            WF_STANDARD.DEFER             31-7-2009 12:37:33 31-7-2009 12:37:33 COMPLETE #NULL&nbsp;
AND              WF_STANDARD.ANDJOIN           31-7-2009 12:37:33                   &nbsp;WAITING &nbsp;
TRACK_FLOW-1     TRACK_FLOW_PROGRESS           31-7-2009 12:37:42 31-7-2009 12:37:42 COMPLETE &nbsp;</pre>
</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>Now we can abort the item with the API:</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>
<pre language="SQL">
begin
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; wf_engine.abortprocess(itemtype=&gt;'DBA_TYPE'
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;,itemkey=&gt;'30'
  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;,process=&gt;'ROOT:DBA_MAIN_PROCESS');
end;</pre>
</div>
<div>&nbsp;</div>
<div>Note how we have to indicate that we want to abort the root process of the DBA_MAIN_PROCESS. The workflow engine needs to know unambiguously which process to abort. The way to do that is to set &#8216;process:&lt;activity&gt;&#8217; to indicate the process. In our case this would be &#8216;ROOT:DBA_MAIN_PROCESS&#8217;.</div>
<div>&nbsp;</div>
<div>And this is the result afterwards.&nbsp;</div>
<div>&nbsp;</div>
<div>
<pre language="SQL">
INSTANCE_LABEL FUNCTION BEGIN_DATE END_DATE STATUS RESULT OUTBOUND_QUEUE_ID
DBA_MAIN_PROCESS &nbsp;                             31-7-2009 12:36:58 31-7-2009 12:39:10 COMPLETE #FORCE&nbsp;
START            START                         31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE #NULL&nbsp;
INITIALIZE_FLOW  XXX_WF_DBA.init               31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE COMPLETE&nbsp;
COMPARETEXT      WF_STANDARD.COMPARE           31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE EQ&nbsp;
CHECK_INVALIDS   XXX_CHECK_INVALIDS            31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE Y&nbsp;
GET_INVALIDS     XXX_WF_UTILS.get_invalids     31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE &nbsp;
LOOPCOUNTER      WF_STANDARD.LOOPCOUNTER       31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE LOOP&nbsp;
PROCESS_INVALIDS XXX_WF_UTILS.process_invalids 31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE &nbsp;
DEFER            WF_STANDARD.DEFER             31-7-2009 12:37:33 31-7-2009 12:37:33 COMPLETE #NULL&nbsp;
AND              WF_STANDARD.ANDJOIN           31-7-2009 12:37:33 31-7-2009 12:39:10 COMPLETE #FORCE&nbsp;
TRACK_FLOW-1     TRACK_FLOW_PROGRESS           31-7-2009 12:37:42 31-7-2009 12:37:42 COMPLETE &nbsp;</pre>
</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>Compare this with the result from running a background engine with parameter &#8216;process_stuck=&gt;TRUE&#8217;:</div>
<div>&nbsp;</div>
<div>
<pre language="SQL&gt;INSTANCE_LABEL FUNCTION BEGIN_DATE END_DATE STATUS RESULT OUTBOUND_QUEUE_ID
DBA_MAIN_PROCESS &nbsp;31-7-2009 12:36:58 &nbsp;ERROR #STUCK&nbsp;
START START 31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE #NULL&nbsp;
INITIALIZE_FLOW XXX_WF_DBA.init 31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE COMPLETE&nbsp;
COMPARETEXT WF_STANDARD.COMPARE 31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE EQ&nbsp;
CHECK_INVALIDS XXX_CHECK_INVALIDS 31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE Y&nbsp;
GET_INVALIDS XXX_WF_UTILS.get_invalids 31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE &nbsp;
LOOPCOUNTER WF_STANDARD.LOOPCOUNTER 31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE LOOP&nbsp;
PROCESS_INVALIDS XXX_WF_UTILS.process_invalids 31-7-2009 12:36:58 31-7-2009 12:36:58 COMPLETE &nbsp;
DEFER WF_STANDARD.DEFER 31-7-2009 12:37:33 31-7-2009 12:37:33 COMPLETE #NULL&nbsp;
AND WF_STANDARD.ANDJOIN 31-7-2009 12:37:33 &nbsp;WAITING &nbsp;
TRACK_FLOW-1 TRACK_FLOW_PROGRESS 31-7-2009 12:37:42 31-7-2009 12:37:42 COMPLETE &nbsp;&lt;/pre&gt;&lt;/div&gt;
&lt;div&gt;&nbsp;&lt;/div&gt;
&lt;div&gt;&nbsp;&lt;/div&gt;
&lt;div&gt;The 'wf_engine.abortprocess' will also complete any subprocesses. Since ATG RUP7 a parameter &nbsp;is added to the API: Cascade. Be aware that the default value is 'False'. So when using the API with cascade=&gt;FALSE, you might have some child items hanging around.&nbsp;&lt;/div&gt;
&lt;div&gt;&nbsp;&lt;/div&gt;
&lt;div&gt;Now what would happen to processes that have a long running query or function still running at the time of the 'abortprocess'.&nbsp;&lt;/div&gt;
&lt;div&gt;&nbsp;&lt;/div&gt;
&lt;div&gt;You might be in for a less pleasant surprise. Since the engine running the item, will hold a lock on the wf_ tables involved. So the 'wf_engine.abortprocess'&nbsp;will wait on the lock. Which is probably not desirable.&lt;/div&gt;
&lt;div&gt;&nbsp;&lt;/div&gt;
&lt;div&gt;You can avoid this waittime with the last parameter for this function (verify_lock). When verify_lock=&gt;TRUE, then the abortprocess will check if there is&nbsp;a lock on the items involved, and come back with an error if that is the case:&nbsp;&lt;/div&gt;
&lt;div&gt;&nbsp;&lt;/div&gt;
&lt;div&gt;&lt;pre language=">
ORA-20002: 3150: Process 'DBA_TYPE/33' is being worked upon. Please retry the current request on the process later.
ORA-06512: at &quot;APPS.WF_CORE&quot;, line 300
ORA-06512: at &quot;APPS.WF_ENGINE&quot;, line 4528
ORA-06512: at line 2</pre>
</div>
<div>&nbsp;</div>
<div>Of course this error can be captured and handled as we saw in &#8216;Oracle workflow for eBS DBA&#8217;s (Part 4)&#8217;</div>
<div>&nbsp;</div>
<div>Then lets take a look at the &#8216;wf_engine.Suspend&#8217; function. This is basically a &#8216;pause&#8217;-API for a workflow item. It sets the active process to &#8216;SUSPEND&#8217;.&nbsp;</div>
<div>&nbsp;</div>
<div>The workflow engine will not pick it up any more until the wf_engine.resume API is called.&nbsp;</div>
<div>&nbsp;</div>
<div>Let&#8217;s see the resume and suspend with a small example. I used the same dba_control_process. After launching it, it will be deferred. Instead of running a background engine, we suspend it.&nbsp;</div>
<div>&nbsp;</div>
<div>
<pre language="SQL">
begin
&nbsp;&nbsp; &nbsp; wf_engine.suspend(itemtype=&gt;'DBA_TYPE',itemkey=&gt;'34');
end;</pre>
</div>
<div>&nbsp;</div>
<div>And the status becomes:</div>
<div>&nbsp;</div>
<div>
<pre language="SQL">
INSTANCE_LABEL      FUNCTION          BEGIN_DATE         END_DATE           STATUS   RESULT OUTBOUND_QUEUE_ID
DBA_CONTROL_PROCESS &nbsp;                 31-7-2009 18:30:45 &nbsp;                  SUSPEND  #NULL&nbsp;
START               START             31-7-2009 18:30:45 31-7-2009 18:30:45 COMPLETE #NULL&nbsp;
DEFER               WF_STANDARD.DEFER 31-7-2009 18:31:02 &nbsp;                  DEFERRED #NULL  6FFEFFF31C2604F5E0440003BAB3AD6B</pre>
</div>
<div>&nbsp;</div>
<div>The interesting thing is that the deferred status is still there. Including its queue_id. So when we run a background engine. We&#8217;ll see that it indeed picks up the item. It dequeues the message but leaves the status on deferred.&nbsp;</div>
<div>&nbsp;</div>
<div>Now when we resume the process:</div>
<div>&nbsp;</div>
<div>
<pre language="SQL">
begin
&nbsp;&nbsp; &nbsp;wf_engine.resume(itemtype=&gt;'DBA_TYPE',itemkey=&gt;'34');
end;</pre>
</div>
<div>&nbsp;</div>
<div>The function is performed, and the item continues as usual.</div>
<div>&nbsp;</div>
]]></content:encoded>
			<wfw:commentRss>http://www.stijf.com/wordpress/2009/07/oracle-workflow-for-ebs-dbas-part-4b/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>More on 1 != 1</title>
		<link>http://www.stijf.com/wordpress/2009/07/more-on-1-1/</link>
		<comments>http://www.stijf.com/wordpress/2009/07/more-on-1-1/#comments</comments>
		<pubDate>Tue, 28 Jul 2009 08:51:19 +0000</pubDate>
		<dc:creator>Arian Stijf</dc:creator>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[Scripts]]></category>

		<guid isPermaLink="false">http://www.stijf.com/wordpress/?p=360</guid>
		<description><![CDATA[I was reading the WorkflowFaq Blog today, where they discuss when 1 != 1 in Oracle.
Of I did some testing with this. First of course I reproduced the issue easy enough:
&#160;

SQL&#62; select 1/3*3 from dual;
&#160;

&#160;&#160;&#160;&#160; 1/3*3
----------
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 1
&#160;

SQL&#62; select * from dual where 1/3*3=1;
No rows selected.
It is obviously a rounding issue. So what happens if we [...]]]></description>
			<content:encoded><![CDATA[<p>I was reading the <a href="http://www.workflowfaq.com/when-does-1-1/156">WorkflowFaq Blog </a>today, where they discuss when 1 != 1 in Oracle.</p>
<p>Of I did some testing with this. First of course I reproduced the issue easy enough:</p>
<p>&nbsp;</p>
<pre language="SQL">
SQL&gt; select 1/3*3 from dual;</pre>
<p>&nbsp;</p>
<pre language="SQL">
&nbsp;&nbsp;&nbsp;&nbsp; 1/3*3
----------
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1</pre>
<p>&nbsp;</p>
<pre language="SQL">
SQL&gt; select * from dual where 1/3*3=1;</pre>
<p>No rows selected.</p>
<p>It is obviously a rounding issue. So what happens if we force Oracle to round the result?</p>
<p>&nbsp;</p>
<pre language="SQL">
SQL&gt; select * from dual where round(1/3*3)=1;</pre>
<p>&nbsp;</p>
<pre language="SQL">
D
-
X</pre>
<p>&nbsp;</p>
<p>If the rounding went wrong, it will most likely be on the low side (0.3333&#8230;&#8230;*3=0.9999&#8230;&#8230;)</p>
<p>&nbsp;</p>
<pre language="SQL">
SQL&gt; select trunc(1/3*3) from dual where ceil(1/3*3)=1;</pre>
<p>&nbsp;</p>
<pre language="SQL">
D
-
0</pre>
<p>&nbsp;</p>
<p>We can see the real contents by dumping the result:</p>
<p>&nbsp;</p>
<pre language="SQL">
SQL&gt; select dump(1/3*3) d from dual&nbsp; 2&nbsp; union&nbsp; 3&nbsp; select dump(1) d from dual;</pre>
<p>&nbsp;</p>
<pre language="SQL">
D
--------------------------------------------------------------------------------
Typ=2 Len=21: 192,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,10
0,100,100,100,100</pre>
<p>&nbsp;</p>
<pre language="SQL">
Typ=2 Len=2: 193,2&nbsp;</pre>
<p>I will go into the internal number format in a while. For now, it is sufficient to say that&nbsp;the 1/3*3 is indeed&nbsp;0.9999999&#8230;&#8230;.</p>
<p>Now how many decimals can we get?</p>
<p>&nbsp;</p>
<pre language="SQL">
SQL&gt; select to_char(1/3*3,'9.99') from dual;</pre>
<p>&nbsp;</p>
<pre language="SQL">
TO_CH
-----
&nbsp;1.00</pre>
<p>&nbsp;</p>
<pre language="SQL">
SQL&gt; select to_char(1/3*3,'9.99999999999999999999999999999999999999999') from dual;</pre>
<p>&nbsp;</p>
<pre language="SQL">
TO_CHAR(1/3*3,'9.999999999999999999999999999
--------------------------------------------
&nbsp; .99999999999999999999999999999999999999990</pre>
<p>&nbsp;</p>
<p>There is the real value of 1/3*3.</p>
<p>The Oracle documentation says that the precision of the number format is 126 binary digits, approximately 38 decimals digits.</p>
<p>So when we force a mask with more decimals, Oracle will show the real value.</p>
<p>&Agrave;n interesting &#8216;feature&#8217;. And definitely something to keep in mind.</p>
<p>One last note on the dumping of numbers. <br />
dump(&lt;number&gt;,10) will dump the decimal value. <br />
dump(&lt;number&gt;,16) will dump the hexadecimal value.</p>
<p>How do we translate it back to the real value then? <br />
Let&#8217;s do the excercise for a few numbers: 1234.5678 and -1234.5678.</p>
<p>There is some difference between positive and negative values. So let&#8217;s do the positive one first.</p>
<p>&nbsp;</p>
<pre language="SQL">
SQL&gt; select dump(1234.5678,10),dump(1234.5678,16) from dual;</pre>
<p>&nbsp;</p>
<pre language="SQL">
DUMP(1234.5678,10)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DUMP(1234.5678,16)
---------------------------- --------------------------
Typ=2 Len=5: 194,13,35,57,79 Typ=2 Len=5: c2,d,23,39,4f</pre>
<p>&nbsp;</p>
<p>We start with the second to the last numbers. And we subtract 1:<br />
194,12,34,56,78</p>
<p>Ignore the 194 for now, and put the decimal . after the second number: 12.345678</p>
<p>Now subtract 193 from the first value: 194-193=1.</p>
<p>Multiply the decimal value this number of times by 100: 12.345678 * 100 = 1234.5678.<br />
(In our case we multiply by 100 once if the first value would be higher, mulitply more often).</p>
<p>
The negative values are a little different:</p>
<p>&nbsp;</p>
<pre language="SQL">
SQL&gt; select dump(-1234.5678,10),dump(-1234.5678,16) from dual;</pre>
<p>&nbsp;</p>
<pre language="SQL">
DUMP(-1234.5678,10)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DUMP(-1234.5678,16)
------------------------------- ------------------------------
Typ=2 Len=6: 61,89,67,45,23,102 Typ=2 Len=6: 3d,59,43,2d,17,66</pre>
<p>&nbsp;</p>
<p>The 102 at the end means it is a negative value. Discard the 102 for the rest of the calculation.</p>
<p>61,89,67,45,23.</p>
<p>Subtract the second to the last number from 101.<br />
101-89=12, 101-67=34, 101-45=56,101-23=78&nbsp; gives<br />
61,12 34 56 78</p>
<p>Put the decimal . after the second number. So we get 12.345678</p>
<p>Now subtract 62 from the first value and multiply by 100: 61-62 = -1.</p>
<p>Finally we divide the value this number of times by 100. In our case we have a negative, so we <br />
multiply instead:</p>
<p>12.345678 * 100 = 1234.5678 -&gt; And we had a negative. So -1234.5678</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stijf.com/wordpress/2009/07/more-on-1-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Thought of the day</title>
		<link>http://www.stijf.com/wordpress/2009/07/thought-of-the-day/</link>
		<comments>http://www.stijf.com/wordpress/2009/07/thought-of-the-day/#comments</comments>
		<pubDate>Mon, 27 Jul 2009 22:14:32 +0000</pubDate>
		<dc:creator>Arian Stijf</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Oracle]]></category>

		<guid isPermaLink="false">http://www.stijf.com/wordpress/?p=358</guid>
		<description><![CDATA[Just came acrosss this quote on Jonathan Lewis&#8217; blog. And I thought it would be worth to remember you guys:
Then we ALWAYS have to remember that correlation is not the same as causation.
&#160;
]]></description>
			<content:encoded><![CDATA[<p>Just came acrosss this quote on Jonathan Lewis&#8217; blog. And I thought it would be worth to remember you guys:</p>
<p>Then we ALWAYS have to remember that correlation is not the same as causation.</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stijf.com/wordpress/2009/07/thought-of-the-day/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Redoing the first parts of the workflow series</title>
		<link>http://www.stijf.com/wordpress/2009/07/redoing-the-first-parts-of-the-workflow-series/</link>
		<comments>http://www.stijf.com/wordpress/2009/07/redoing-the-first-parts-of-the-workflow-series/#comments</comments>
		<pubDate>Sun, 26 Jul 2009 20:33:07 +0000</pubDate>
		<dc:creator>Arian Stijf</dc:creator>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[Workflow]]></category>
		<category><![CDATA[format]]></category>

		<guid isPermaLink="false">http://www.stijf.com/wordpress/?p=354</guid>
		<description><![CDATA[Just to inform you that I&#8217;m in the process of reformatting the first parts of the &#8216;Oracle Workflow for eBS DBA&#8217;s&#8217; series.
So there might be some links going wrong. But I&#8217;ll make sure the links page always has the correct links. Please check back there, if you think something is wrong.
Also you can leave a [...]]]></description>
			<content:encoded><![CDATA[<p>Just to inform you that I&#8217;m in the process of reformatting the first parts of the &#8216;Oracle Workflow for eBS DBA&#8217;s&#8217; series.</p>
<p>So there might be some links going wrong. But I&#8217;ll make sure the links page always has the correct links. Please check back there, if you think something is wrong.</p>
<p>Also you can leave a comment, of course!&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stijf.com/wordpress/2009/07/redoing-the-first-parts-of-the-workflow-series/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Oracle Workflow for eBS DBA&#8217;s (Part 1)</title>
		<link>http://www.stijf.com/wordpress/2009/07/oracle-workflow-for-ebs-dbas-part-1/</link>
		<comments>http://www.stijf.com/wordpress/2009/07/oracle-workflow-for-ebs-dbas-part-1/#comments</comments>
		<pubDate>Sun, 26 Jul 2009 20:22:57 +0000</pubDate>
		<dc:creator>Arian Stijf</dc:creator>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[Workflow]]></category>
		<category><![CDATA[eBS]]></category>
		<category><![CDATA[DBA]]></category>
		<category><![CDATA[definition]]></category>
		<category><![CDATA[function]]></category>
		<category><![CDATA[itemkey]]></category>
		<category><![CDATA[itemtype]]></category>
		<category><![CDATA[process]]></category>
		<category><![CDATA[workflow builder]]></category>

		<guid isPermaLink="false">http://www.stijf.com/wordpress/?p=349</guid>
		<description><![CDATA[For many Oracle eBS-DBA&#8217;s workflow is a strange and hardly understood module.Still it is widely used in 11i and 12i. So let&#8217;s dive into its workings in more detail.
This is part one of a so far unlimited series. Don&#8217;t be put off by the lack of code in this part. We first need to go [...]]]></description>
			<content:encoded><![CDATA[<p>For many Oracle eBS-DBA&rsquo;s workflow is a strange and hardly understood module.Still it is widely used in 11i and 12i. So let&rsquo;s dive into its workings in more detail.</p>
<p>This is part one of a so far unlimited series. Don&rsquo;t be put off by the lack of code in this part. We first need to go through the basics. In the next parts we&rsquo;ll get more action.<br />
During this series, I used an 11.5.10 instance on a 9.2.0.8 database. The basic statements in these articles will still hold for earlier and later versions, but small modifications may be needed. <br />
In this first part we go into the definitions and the basics of the Workflow engine. We start with some definitions, and then we build a simple basic workflow. <br />
We&rsquo;ll see how this relates to the wf_tables in the database.</p>
<address>The basic terminology <br />
&nbsp;</address>
<p>First. What is a workflow? A workflow is a sequence of functions and events that follow a certain path based on decisions made during the progress of the sequence. <br />
Most of us know the pictures from workflow builder. With the pictograms for functions joined together with lines. <br />
That set is a definition of a workflow. In the Oracle workflow world it is called a &lsquo;process&rsquo;. The nodes in the process can be functions, processes or notifications.</p>
<p>All these are grouped together in containers that Oracle called an &lsquo;Itemtype&rsquo;.&nbsp;The itemtype is very important, since it will become part of the primary key in the underlying tables.<br />
The actual workflows that are running according to the definition of the itemtype are called &lsquo;Items&acute;. The item is started as a specific process within an &lsquo;itemtype&rsquo;. And it is uniquely identified by the &lsquo;itemtype&rsquo; and an &lsquo;itemkey&rsquo;.</p>
<p>Every process consists of 2 or more nodes, which are joined together by transitions. At least 2 nodes are required, because a process needs a &rsquo;start&rsquo; and a &rsquo;stop&rsquo;-node. <br />
Ok. We talked enough for now. Let&rsquo;s build a process and find out the rest along the way. <br />
By the way, all the definitions above will be linked to a glossary later on.</p>
<address>Getting Started</address>
<p>To start building our process, we first need the itemtype. To create an itemtype, we use &lsquo;Workflow builder&rsquo;. In workflow builder, when we click the new button we are greeted with this screen:</p>
<p><img class="alignnone size-full wp-image-24" title="wf builder start" alt="wf builder start" width="389" height="159" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/wf-builder-start.JPG" /></p>
<p>On right clicking the &lsquo;untitled&rsquo; map, we can create a new itemtype.</p>
<p><img class="alignnone size-full wp-image-26" title="New_item_type" alt="New_item_type" width="544" height="362" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/New_item_type.JPG" /></p>
<p>Internal name is the unique name that will be used in the table keys for this itemtype and its items. It is limited to 8 characters. So choose wisely! <br />
Display name is the name that will be shown to users when they need to interact with items from this itemtype. <br />
The description&hellip;&hellip;.. you can guess that one. <br />
We will discuss the other three fields in a later article.</p>
<address>My first Itemtype</address>
<p>I choose to start building a flow that will do some DBA checks and tries to fix problems or notify the DBA if there is a problem. <br />
During the course of building this flow, we&rsquo;ll stumble on different features of the workflow engine. <br />
The first step is to build the itemtype. <br />
I called it: DBA_TYPE. <br />
With a display name: DBA Itemtype <br />
And a description: Itemtype to hold DBA processes and functions.</p>
<p><img class="alignnone size-full wp-image-27" title="dba_item_type" alt="dba_item_type" width="540" height="358" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/dba_item_type.JPG" /></p>
<p>When you open your newly created itemtype, you see the components that can be created within this itemtype. <br />
You&rsquo;ll remember that the flow definition was called a process. So next we create a new &lsquo;Process&rsquo;:&nbsp;</p>
<p><img class="alignnone size-full wp-image-28" title="dba_main_process" alt="dba_main_process" width="536" height="354" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/dba_main_process1.JPG" /></p>
<p>Because this is a process that we will only be calling from our client, we have no use for the result type at the moment. <br />
Later on, we&rsquo;ll see nested processes, where the result of a process will determine the direction of the calling process. <br />
When we go to the Process Detail (right click the process). We again have a virgin drawing board. <br />
This will be where the actual flow is created. Every process consists of activities (functions, notifications and processes) and the transitions between them (based on the results of the activities). <br />
Also every process has to start with a &lsquo;Start&rsquo; Activity and finish with an &lsquo;End&rsquo; activity. (Take care to avoid loose ends, since the end finalizes the flow and gives back control, or makes the flow purgeable). <br />
So first we create a new function to start our flow.</p>
<p><img class="alignnone size-full wp-image-29" title="start_1" alt="start_1" width="536" height="355" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/start_1.jpg" /></p>
<p>Note the wf_standard.noop for the function. This is a dummy function because the only purpose of this node is to indicate the starting point for the process. <br />
Even though we named this function &lsquo;START&rsquo;, we still need to flag it as a &lsquo;Start&rsquo; function. That is in the node tab.</p>
<p><img class="alignnone size-full wp-image-30" title="start_2" alt="start_2" width="536" height="354" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/start_2.JPG" /></p>
<p>We then create an &lsquo;END&rsquo; function in the same way.<br />
Finally we create our own function.<br />
&nbsp;</p>
<p>Now we have an item_type with 1 process, and 3 functions. It&rsquo;s time to connect the functions together.<br />
Right click START, and drag to INITIALIZE_FLOW. Then right click there and drag to END. The result should be like:&nbsp;&nbsp;</p>
<p><img class="alignnone size-full wp-image-32" title="process-1" alt="process-1" width="311" height="105" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/process-1.JPG" /></p>
<p>Now we have a runnable flow.</p>
<p>You can even start it already, if you create an empty packaged function: &lsquo; XXX_WF_DBA.init&rsquo;.</p>
<p>But there is more work to be done.<br />
&nbsp;</p>
<p>First we are going to see how this is recorded in the wf_ tables in our database in part 2 of our series.<br />
&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stijf.com/wordpress/2009/07/oracle-workflow-for-ebs-dbas-part-1/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Oracle Workflow for eBS DBA&#8217;s (Part 4)</title>
		<link>http://www.stijf.com/wordpress/2009/07/oracle-workflow-for-ebs-dbas-part-4/</link>
		<comments>http://www.stijf.com/wordpress/2009/07/oracle-workflow-for-ebs-dbas-part-4/#comments</comments>
		<pubDate>Sun, 26 Jul 2009 15:37:40 +0000</pubDate>
		<dc:creator>Arian Stijf</dc:creator>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Workflow]]></category>
		<category><![CDATA[eBS]]></category>
		<category><![CDATA[background process]]></category>
		<category><![CDATA[continueflow]]></category>
		<category><![CDATA[defer]]></category>
		<category><![CDATA[launch]]></category>
		<category><![CDATA[wait]]></category>
		<category><![CDATA[waitforflow]]></category>

		<guid isPermaLink="false">http://www.stijf.com/wordpress/?p=329</guid>
		<description><![CDATA[&#160;Welcome to Part 4 of the series Oracle Workflow for eBS DBA&#8217;s.
The previous articles can be found here: 1, 2, 3
&#160;
In this part we will look at the controls for the workflow items. This includes &#8216;Defer thread&#8217;, &#8216;Block&#8217;, &#8216;Wait for flow&#8217; and &#8216;Wait&#8217;. Also we will see the suspend and abort functions from the workflow [...]]]></description>
			<content:encoded><![CDATA[<p>&nbsp;Welcome to Part 4 of the series Oracle Workflow for eBS DBA&rsquo;s.</p>
<div>The previous articles can be found here: <a href="http://www.stijf.com/wordpress/2009/07/workflow-for-ebs-dbas-part-1/">1</a>, <a href="http://www.stijf.com/wordpress/2009/07/workflow-for-ebs-dbas-part-2/">2</a>, <a href="http://www.stijf.com/wordpress/2009/07/workflow-for-ebs-dbas-part-3/">3</a></div>
<div>&nbsp;</div>
<div>In this part we will look at the controls for the workflow items. This includes &lsquo;Defer thread&rsquo;, &lsquo;Block&rsquo;, &lsquo;Wait for flow&rsquo; and &lsquo;Wait&rsquo;. Also we will see the suspend and abort functions from the workflow engine.</div>
<div>These functions allow the execution of a workflow item to be halted, delayed or taken over from online processing to background processing. The &#8216;Wait for Flow&#8217; allows some interaction between different items.</div>
<div>&nbsp;</div>
<div>The first function we will look into is the &lsquo;Defer Thread&rsquo; (or short &lsquo;Defer&rsquo;).</div>
<div>&nbsp;</div>
<div>By default the session that starts an item executes all functions in that item. That means that long running activities are also executed online. The &lsquo;Defer&rsquo; function makes it possible to queue those long running activities to be executed by a background process.</div>
<div>&nbsp;</div>
<div>Let&rsquo;s start with a small process to show how this works. We create an item with only a function &lsquo;Defer&rsquo; (copied from the STANDARD itemtype). I created the following process:</div>
<p>&nbsp;<img class="alignnone size-full wp-image-330" title="process_defer" height="105" alt="process_defer" width="462" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/process_defer.JPG" /></p>
<p>The &#8216;Defer Thread&#8217; has no node attributes.</p>
<p>When we run this process, and check the activity_statuses we get the following result:<br />
&nbsp;</p>
<pre language="SQL">
<div><span style="font-family: Courier New">select wpa.instance_label
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case when wpa.start_end is not null then wpa.start_end else wa.function end function
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wias.begin_date
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wias.end_date
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wias.activity_status status
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wias.activity_result_code result
,&nbsp;&nbsp;&nbsp;&nbsp; outbound_queue_id
from&nbsp; wf_item_activity_statuses wias
join&nbsp; wf_process_activities wpa
 &nbsp;&nbsp;&nbsp;&nbsp; on (wias.process_activity=wpa.instance_id)
join&nbsp; wf_activities wa
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; on (wpa.activity_item_type=wa.item_type
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and wpa.activity_name=wa.name
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and wa.end_date is null)
join&nbsp; wf_activities_tl wat 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; on (wa.item_type=wat.item_type 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;and wa.name=wat.name 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and wa.version=wat.version 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and wat.language='US')
where wias.item_type='DBA_TYPE'
and&nbsp;&nbsp; wias.item_key='14'
order by wias.begin_date,wias.execution_time;
&nbsp;</span></div>
</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre language="SQL">
INSTANCE_LABEL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FUNCTION&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; END_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STATUS&nbsp;&nbsp; RESULT OUTBOUND_QUEUE_ID
------------------- ----------------- ------------------ ------------------ -------- ------ -----------------
DBA_CONTROL_PROCESS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24-7-2009 14:02:46&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACTIVE&nbsp;&nbsp; #NULL 
START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24-7-2009 14:02:46 24-7-2009 14:02:46 COMPLETE #NULL 
DEFER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WF_STANDARD.DEFER 24-7-2009 14:02:46&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DEFERRED #NULL&nbsp; 6F728D74CA8142E6E0440003BAB3AD6B&nbsp;</pre>
<p>&nbsp;</p>
<p>Even though control was given back to the client, the item did not complete yet. The root is still active and the second function got status &lsquo;Deferred&rsquo;. This function is waiting to be processed by a background engine.<br />
Before we start a background engine, we first take a look at the deferring mechanism.</p>
<p>The WF_STANDARD.DEFER function only sets the result_code of the process_activity to &lsquo;DEFERRED&rsquo;. To be precise it is set to the constant &lsquo;wf_engine.eng_deferred&rsquo;. When the engine encounters this result_code, it will queue a message to the &lsquo;WF_DEFERRED_QUEUE_M&rsquo;queue with a payload of item_type, item_key and process_activity.&nbsp;<br />
&nbsp;</p>
<p>The table for the &lsquo;WF_DEFERRED_QUEUE_M&rsquo; is &lsquo;WF_DEFERRED_TABLE_M&rsquo;. The queue table can be queried directly or through the queuing view. (Which shows some translated columns). The key is the outbound_queue_id from the wf_item_activity_statuses:<br />
&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre language="SQL">
select queue
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; corr_id
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; msg_priority
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; msg_state
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; enq_time
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def.user_data.itemtype
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def.user_data.itemkey
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def.user_data.actid
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; consumer_name
from applsys.AQ$WF_DEFERRED_TABLE_M def
where msg_id=(select outbound_queue_id
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;      from wf_item_activity_statuses
&nbsp;&nbsp;&nbsp;           where item_type='DBA_TYPE'
&nbsp;&nbsp;&nbsp;           and item_key='14'
&nbsp;&nbsp;&nbsp;           and activity_status='DEFERRED');</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre language="SQL">
QUEUE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CORR_ID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PRIO MSG_STATE ENQ_TIME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TYPE&nbsp;&nbsp;&nbsp;&nbsp; KEY ACTID&nbsp; CONSUMER_NAME
------------------- ------------ ---- --------- ------------------ -------- --- ------ -------------
WF_DEFERRED_QUEUE_M APPSDBA_TYPE&nbsp;&nbsp;&nbsp; 1 READY&nbsp;&nbsp;&nbsp;&nbsp; 24-7-2009 14:02:44 DBA_TYPE 14&nbsp; 245513 APPS</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>No surprises there. The message is queued, and ready to be picked up.</p>
<p>The queue is read by the &lsquo;Workflow Background Process&rsquo;. In eBS this is a concurrent request under the &lsquo;System Administrator&rsquo; responsibility. We will discuss the parameters later in this article.</p>
<p>An alternative is to run a background engine directly from SQL. That is what we will do now.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre language="SQL">
Begin
&nbsp;&nbsp;&nbsp; Wf_engine.background (itemtype=&gt;&rsquo;DBA_TYPE&rsquo;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,process_deferred=&gt;TRUE
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,process_timeout=&gt;FALSE
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,process_stuck=&gt;FALSE);
End;</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>We will discuss the timeout and stuck parameters later in this article. And idem for 2 parameters for &lsquo;Treshold&rsquo;.</p>
<p>The background engine will dequeue the message from the queue, and process the workflow item as of that process_activity.</p>
<p>When we look at the &lsquo;wf_item_activity_statuses&rsquo; we notice something strange.</p>
<p>&nbsp;</p>
<pre language="SQL">
INSTANCE_LABEL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FUNCTION&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN_DATE END_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STATUS&nbsp;&nbsp; RESULT
DBA_CONTROL_PROCESS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24-7-2009&nbsp; 14:02:46 24-7-2009 14:20:43 COMPLETE #NULL 
START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24-7-2009&nbsp; 14:02:46 24-7-2009 14:02:46 COMPLETE #NULL 
DEFER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WF_STANDARD.DEFER 24-7-2009&nbsp; 14:20:43 24-7-2009 14:20:43 COMPLETE #NULL 
END&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; END&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24-7-2009&nbsp; 14:20:43 24-7-2009 14:20:43 COMPLETE #NULL</pre>
<p>&nbsp;</p>
<p>The begin_date for the &lsquo;Defer&rsquo; is the time that it was run by the background engine. This is because after dequeueing the workflow engine will restart the process starting with this process_activity. So it will actually be re-executed at that time. <br />
We can see that more clearly with an adjusted &lsquo;Track_flow_progress&rsquo; function (see part 2 of this series for the original). This version will return a completion code of &lsquo;wf_engine.eng_deferred&rsquo; to defer the item. We then replace the original &lsquo;Defer&rsquo; function with our &lsquo;track_flow_progress&rsquo; function.</p>
<p>&nbsp;</p>
<pre language="SQL">
CREATE OR REPLACE Procedure track_flow_progress (p_item_type IN VARCHAR2
&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;,p_item_key IN VARCHAR2
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;,p_actid IN NUMBER
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;,p_funcmode IN VARCHAR2
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;,p_result OUT VARCHAR2) IS
v_activity_name varchar2(30);
v_activity_version number;
v_process_name varchar2(30);
v_instance_label varchar2(30);
v_count number;
begin
&nbsp;&nbsp;&nbsp; select activity_name,wa.version,process_name,instance_label
&nbsp;&nbsp;&nbsp; into&nbsp;&nbsp; v_activity_name, v_activity_version, v_process_name, v_instance_label
&nbsp;&nbsp;&nbsp; from&nbsp;&nbsp; wf_process_activities wpa 
&nbsp;&nbsp;&nbsp; join&nbsp;&nbsp; wf_activities wa
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;     on (wpa.activity_item_type=wa.item_type 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;    and wpa.activity_name=wa.name 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;    and wa.end_date is null)
&nbsp;&nbsp;&nbsp; where&nbsp; wpa.instance_id=p_actid;
&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp; insert into xxx_track_flow (id
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,item_type
&nbsp;                              ,item_key
                              &nbsp;,activity_name
                              &nbsp;,activity_version
                              &nbsp;,process_name
                              &nbsp;,instance_id
                              &nbsp;,instance_label
                              &nbsp;,funcmode)
&nbsp;&nbsp; values (xxx_track_flow_s.nextval
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,p_item_type        &nbsp; ,p_item_key
&nbsp;         ,v_activity_name
&nbsp;         ,v_activity_version
&nbsp;         ,v_process_name
&nbsp;         ,p_actid
&nbsp;         ,v_instance_label
&nbsp;         ,p_funcmode
          );

&nbsp;&nbsp;&nbsp; select count(*)
&nbsp;&nbsp;&nbsp; into&nbsp;&nbsp; v_count 
&nbsp;&nbsp;&nbsp; from&nbsp;&nbsp; xxx_track_flow x
&nbsp;&nbsp;&nbsp; where&nbsp; item_type=p_item_type
&nbsp;&nbsp;&nbsp; and&nbsp;&nbsp;&nbsp; item_key=p_item_key
&nbsp;&nbsp;&nbsp; and&nbsp;&nbsp;&nbsp; instance_id=p_actid;

&nbsp;&nbsp;&nbsp; if v_count=1 then
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; p_result:=wf_engine.eng_deferred;
&nbsp;&nbsp;&nbsp; end if;
&nbsp; end;
/</pre>
<p>&nbsp;</p>
<p>Let&rsquo;s see what happens when we put this instead of the standard &lsquo;Defer&rsquo;<br />
&nbsp;</p>
<p>&nbsp;<img class="alignnone size-full wp-image-331" title="process_defer2" height="105" alt="process_defer2" width="393" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/process_defer2.JPG" /></p>
<p>&nbsp;</p>
<pre language="SQL">
begin
&nbsp; wf_engine.launchprocess(itemtype=&gt;'DBA_TYPE',itemkey=&gt;'15',process=&gt;'DBA_CONTROL_PROCESS');
end;</pre>
<p>&nbsp;</p>
<p>We can confirm that the process is deferred:</p>
<p>&nbsp;</p>
<pre language="SQL">
INSTANCE_LABEL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FUNCTION&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; END_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STATUS&nbsp;&nbsp; RESULT 
DBA_CONTROL_PROCESS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24-7-2009 17:44:32&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACTIVE&nbsp;&nbsp; #NULL 
START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24-7-2009 17:44:32 24-7-2009 17:44:32 COMPLETE #NULL 
TRACK_FLOW&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TRACK_FLOW_PROGRESS 24-7-2009 17:44:32&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DEFERRED #NULL</pre>
<p>&nbsp;</p>
<p>The track_flow_progress function has executed once:</p>
<p>&nbsp;</p>
<pre language="SQL">
select count(*)
from&nbsp;&nbsp; xxx_track_flow
where&nbsp; item_type=&rsquo;DBA_TYPE&rsquo; 
and&nbsp;&nbsp;&nbsp; item_key=&rsquo;15&rsquo;;</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre language="SQL">
COUNT(*)
--------
1</pre>
<p>&nbsp;</p>
<p>Now when we run the background engine. And check again:</p>
<p>&nbsp;</p>
<pre language="SQL">
INSTANCE_LABEL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FUNCTION&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; END_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STATUS&nbsp;&nbsp; RESULT 
DBA_CONTROL_PROCESS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24-7-2009 17:44:32 24-7-2009 17:57:56 COMPLETE #NULL 
START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24-7-2009 17:44:32 24-7-2009 17:44:32 COMPLETE #NULL 
TRACK_FLOW&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TRACK_FLOW_PROGRESS 24-7-2009 17:57:56 24-7-2009 17:57:56 COMPLETE #NULL 
END&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; END&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24-7-2009 17:57:56 24-7-2009 17:57:56 COMPLETE #NULL</pre>
<p>&nbsp;</p>
<p>And &lsquo;track_flow_progress&rsquo;:</p>
<p>&nbsp;</p>
<pre language="SQL">
ID ITEM_TYPE KEY ACTIVITY_NAME VERSION PROCESS_NAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INSTANCE INSTANCE_LABEL FUNCMODE
174 DBA_TYPE 15&nbsp; TRACK_FLOW&nbsp;&nbsp;&nbsp; 35&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DBA_CONTROL_PROCESS 245549&nbsp;&nbsp; TRACK_FLOW&nbsp;&nbsp;&nbsp;&nbsp; RUN175 DBA_TYPE 15&nbsp; TRACK_FLOW&nbsp;&nbsp;&nbsp; 35&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DBA_CONTROL_PROCESS 245549&nbsp;&nbsp; TRACK_FLOW&nbsp;&nbsp;&nbsp;&nbsp; RUN</pre>
<p>&nbsp;</p>
<p>The flow has completed now, and you can see the 2 calls to Track_flow_progress. <br />
This is something that you need to be aware of, whenever an issue arises with a function that defers itself.</p>
<p>Now let&rsquo;s look at the &lsquo;costing&rsquo;-model for the workflow engine. The goal of the background engines is to process activities with a long runtime, or that need heavy resources. <br />
As we saw, the call to the background engine picks up all deferred items for a certain item_type. Especially with multiple or complex processes within an item_type, it might be convenient to make a further split.<br />
This is done by assigning costs to different activities. Originally the cost is meant to be the runtime of an activity in seconds. (In workflow builder. It is stored in microseconds in the database). <br />
When calling the background process, you can enter 2 parameters: <br />
Mintreshold and maxtreshold.<br />
The background engine will only process activities until the next defer. It does not matter if it is within the cost range of the background engine. The item will be deferred, and wait for the next run of an eligible background engine.</p>
<p>This is a good time to show the versioning of the workflow engine in action. We investigated the versioning system of the workflow in part 1 (and 2). Where we saw that the workflow will always follow the same process definition. <br />
We can now see that this also goes for the activity_level. When we update the cost of an activity, while an item is active, the&nbsp;background engine will still use the original cost. Consider this:</p>
<p>I put a cost of 10 (seconds) to our&nbsp; &lsquo;Track_flow_progress&rsquo; activity:<br />
&nbsp;</p>
<input type="image" height="354" width="536" src="http://www.stijf.com/wordpress/wp-content/uploads/item_cost1.JPG" />
<p>When we run an item now, it is deferred on the &lsquo;Track_flow_progress&rsquo; activity. Deferring an item automatically means that the workflow engine will commit. (After all, the queue message needs to be visible to the background engine).</p>
<p>&nbsp;</p>
<pre language="SQL">
INSTANCE_LABEL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; COST FUNCTION&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; END_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STATUS&nbsp;&nbsp; RESULT
------------------- ---- ------------------- ------------------ ------------------ -------- ------
DBA_CONTROL_PROCESS 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24-7-2009 20:12:51&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACTIVE&nbsp;&nbsp; #NULL 
START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp; START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24-7-2009 20:12:51 24-7-2009 20:12:51 COMPLETE #NULL 
TRACK_FLOW-1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1000 TRACK_FLOW_PROGRESS 24-7-2009 20:12:51&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DEFERRED</pre>
<p>&nbsp;</p>
<p>While the first item is deferred, I increased the cost of the activity to 20:</p>
<p>
<input type="image" height="354" width="536" src="http://www.stijf.com/wordpress/wp-content/uploads/item_cost2.JPG" />&nbsp;</p>
<p>And started another item. This item is also deferred:</p>
<p>&nbsp;</p>
<pre language="SQL">
INSTANCE_LABEL      COST ITEM_KEY FUNCTION            BEGIN_DATE         END_DATE           STATUS   RESULT
DBA_CONTROL_PROCESS    0 17&nbsp;                          24-7-2009 20:13:01&nbsp;                   ACTIVE   #NULL
START                  0 17       START               24-7-2009 20:13:01 24-7-2009 20:13:01 COMPLETE #NULL
TRACK_FLOW          2000 17       TRACK_FLOW_PROGRESS 24-7-2009 20:13:01&nbsp;                   DEFERRED
&nbsp;</pre>
<p>&nbsp;</p>
<p>Now when I run a background engine with minthreshold 0 and maxthreshold 1100 it nicely picks up the item with key &#8216;16&#8242;. Only when the background engine is run with a maxthreshold of 2000 or more, it runs the item with key &#8216;17&#8242;.</p>
<p>One final thing to mention about cost, is that the &lsquo;online&rsquo; workflow engine will not defer activities with a cost under 50 microseconds.&nbsp; (0.50 in workflow builder). <br />
You can change this threshold by adjusting wf_engine.threshold. In a pl/sql procedure call: wf_engine.threshold:= n;</p>
<p>All items launched with this setting, will use the adjusted threshold. Anything above this threshold needs to be picked up by the background engine.</p>
<p>Let&rsquo;s take a look at the background engine then. We already saw that it uses the WF_DEFERRED_QUEUE_M to select activities to process.</p>
<p>But a common problem in eBS environments is the long run time of the background processes. <br />
The first point of action would be to purge your obsolete workflow data. Try to use the concurrent program from eBS to do that. In a later part of this series, we will look at the purging mechanism and suggest some ways to optimize it. <br />
But if after the purging the background engine still runs for a long time, some other factors might be involved. <br />
A common issue is with processes that are dependent on some checks. For example, you might check if a condition is true. And if not, defer the process without a timeout. Every time the workflow background engine runs this function, it will be deferred again. Causing it to be re-queued for the background engine. Ultimately causing the background engine to get in a loop on this activity. Rather than deferring these activities, the developer should use a &lsquo;Block&rsquo;, or preferably the &lsquo;Business Event System&rsquo;. Using a &lsquo;Wait&rsquo; with sufficient time-out time might also be an option.</p>
<p>A similar thing can happen with time-out values with very short wait times.</p>
<p>When you notice a high CPU usage for QMON (QMNx), it might be time to coalesce the AQ IOT&rsquo;s (Index Organized Tables). At the time of writing Oracle published note: 271855.1 on Metalink to do this.</p>
<p>&nbsp;</p>
<p>So far for the deferred function. Let&rsquo;s take a look at the next control function: Block. <br />
The block function is also available from the &lsquo;STANDARD&rsquo; itemtype. It holds your item until the wf_engine.completeactivity API is called. <br />
Originally this was developed to let the item wait for a call from an external system. But with the arrival of BES (Business Event System), which we&rsquo;ll discuss later in this series, it&rsquo;s usefulness has decreased.</p>
<p>However, the function is still found in lots of places. So we&rsquo;ll look at the internals. <br />
The block function is a call to &lsquo;WF_STANDARD.BLOCK&rsquo;. This procedure returns a resultcode of wf_engine.eng_notified. In wf_item_activity_statuses the activity_status of the process_activity will be &lsquo;NOTIFIED&rsquo;.<br />
This status is also used for Notifications (see next part in the series), when a reply from the user is expected. Since there is no notification, the process_activity cannot continue.</p>
<p>I changed the &lsquo;Control Process&rsquo; to include a &lsquo;Block&rsquo; activity as follows:&nbsp;<br />
&nbsp;</p>
<input type="image" height="105" width="397" src="http://www.stijf.com/wordpress/wp-content/uploads/item_block.JPG" />
<p>Now when we launch an item, we see that it will hold on the block.</p>
<p>&nbsp;</p>
<pre language="SQL">
INSTANCE_LABEL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; COST FUNCTION&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; END_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STATUS&nbsp;&nbsp; RESULT
DBA_CONTROL_PROCESS 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24-7-2009 20:55:00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACTIVE&nbsp;&nbsp; #NULL 
START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24-7-2009 20:55:00 24-7-2009 20:55:00 COMPLETE #NULL 
BLOCK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; WF_STANDARD.BLOCK 24-7-2009 20:55:00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NOTIFIED&nbsp;</pre>
<p>&nbsp;</p>
<p>To progress this item we now have to call the API: &lsquo;wf_engine.completeactivity&rsquo;. This API will initiate a workflow engine to continue the item.</p>
<p>The parameters for the API besides itemtype and itemkey are activity and result. <br />
Activity is a concatenation of the process_name, &lsquo;:&rsquo; and the instance_label from wf_process_activities.</p>
<p>In our case the call would be:</p>
<p>&nbsp;</p>
<pre language="SQL">
begin
wf_engine.completeactivity(itemtype=&gt;'DBA_TYPE'
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,itemkey=&gt;'20'
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,activity=&gt;'DBA_CONTROL_PROCESS:BLOCK'
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,result=&gt;wf_engine.eng_completed);
end;</pre>
<p>&nbsp;</p>
<p>When the activity has a result type, the result has to match one of the lookup codes in the result type. Otherwise any value is allowed, even tough it is a good habit to use the result codes in use by the wf_engine.</p>
<p>So what happens when an item does not get&nbsp; the &lsquo;CompleteActivity&rsquo; call? Since the &lsquo;Block&rsquo; is designed for External Completion, it is not unlikely that an error in the external system will cause the &lsquo;CompleteActivity&rsquo; not to be called. In that case the item will be in the &lsquo;Notified&rsquo; state forever.</p>
<p>For these situations, you can put a &lsquo;TimeOut&rsquo; on the process activity. A Time-out is either an absolute timestamp obtained from an item_attribute or a relative time set on the process_activity. <br />
For this example, I set a relative time of 5 minutes on the &lsquo;Block&rsquo; activity:<br />
&nbsp;</p>
<p>&nbsp;<br />
<input type="image" height="355" width="537" src="http://www.stijf.com/wordpress/wp-content/uploads/timeout1.JPG" /></p>
<p>And of course an extra transition for the &lsquo;Time out&rsquo;, to a second &lsquo;End&rsquo;&nbsp; node.</p>
<p>Now when we run the process, we see that the &lsquo;Due_date&rsquo; column in WF_ITEM_ACTIVITY_STATUSES has been set:<br />
&nbsp;</p>
<p>&nbsp;</p>
<pre language="SQL">
INSTANCE_LABEL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; COST FUNCTION&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; END_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STATUS&nbsp;&nbsp; RESULT DUE_DATE
DBA_CONTROL_PROCESS 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 25-7-2009 17:26:05&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACTIVE&nbsp;&nbsp; #NULL 
START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp; START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 25-7-2009 17:26:05 25-7-2009 17:26:05 COMPLETE #NULL 
BLOCK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; WF_STANDARD.BLOCK 25-7-2009 17:26:05&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NOTIFIED&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 25-7-2009 17:31:05</pre>
<p>&nbsp;</p>
<p>&nbsp;<br />
It is set for exactly 5 minutes after the begin_date of the process_activity.</p>
<p>&nbsp;</p>
<p>After 5 minutes we can start a background process. With parameter &lsquo;process_timeout=&gt;TRUE&rsquo;, or no parameter, since TRUE is the default.</p>
<p>Then we see that it has ran through the timeout transition:<br />
&nbsp;</p>
<p>&nbsp;</p>
<pre language="SQL">
INSTANCE_LABEL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; COST FUNCTION&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; END_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STATUS&nbsp;&nbsp; RESULT&nbsp;&nbsp; DUE_DATE
DBA_CONTROL_PROCESS 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 25-7-2009 17:26:05 25-7-2009 17:31:55 COMPLETE #NULL 
START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp; START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 25-7-2009 17:26:05 25-7-2009 17:26:05 COMPLETE #NULL 
BLOCK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; WF_STANDARD.BLOCK 25-7-2009 17:26:05 25-7-2009 17:31:55 COMPLETE #TIMEOUT 25-7-2009 17:31:05
END-1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp; END&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 25-7-2009 17:31:55 25-7-2009 17:31:55 COMPLETE #NULL</pre>
<p>&nbsp;</p>
<p>Note that this time, the begin_date of the &lsquo;block&rsquo; activity did not change. This is because the activity was not rerun. It was just cut-off, and the engine picked up at the &lsquo;timeout&rsquo; transition.</p>
<p>Even when the timeout period has expired, it is still possible to call the &lsquo;CompleteActivity&rsquo; API. In that case, the workflow engine will still take the default transition, instead of the timeout one. <br />
But once the item has gone through the timeout transition, it is not possible to call the &lsquo;CompleteActivity&rsquo; API anymore. Trying to do so will result in a</p>
<p>&lsquo;ORA-20002: 3133: Activity instance &lsquo;DBA_CONTROL_PROCESS:BLOCK&rsquo; is not a notified activity for item &lsquo;DBA_TYPE/21&rsquo;.</p>
<p>This&nbsp; error is usually innocent. It just means that the item has progressed beyond the process activity that is called to be completed. And a time-out is the most common cause. Of course all programs calling the &lsquo;CompleteActivity&rsquo; API need error handling for this.</p>
<p>At this point, I&rsquo;ll give a short example of the error handling. In a later part in this series, we&rsquo;ll go deeper into workflow troubleshooting and error handling.</p>
<p>Now when we call the &lsquo;CompleteActivity&rsquo; API for the item above, we run the following:</p>
<p>&nbsp;</p>
<pre language="SQL">
declare
&nbsp;&nbsp;&nbsp; v_errorname varchar2(30);
&nbsp;&nbsp;&nbsp; v_errormsg&nbsp; varchar2(2000);
&nbsp;&nbsp;&nbsp; v_errorstack varchar2(32000);
&nbsp;&nbsp;&nbsp; invalid_action EXCEPTION;
&nbsp;&nbsp;&nbsp; PRAGMA EXCEPTION_INIT(invalid_action, -20002);
begin
&nbsp;&nbsp;&nbsp;&nbsp; wf_engine.completeactivity(itemtype=&gt;'DBA_TYPE'
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,itemkey=&gt;'21'
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,activity=&gt;'DBA_CONTROL_PROCESS:BLOCK'
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,result=&gt;wf_engine.eng_completed);
exception 
&nbsp;&nbsp;&nbsp; when invalid_action THEN
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wf_core.get_error(v_errorname,v_errormsg,v_errorstack);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dbms_output.put_line(v_errorname);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dbms_output.put_line(v_errormsg);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dbms_output.put_line(v_errorstack);
end;
/</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre language="SQL">
WFENG_NOT_NOTIFIED3133: Activity instance 'DBA_CONTROL_PROCESS:BLOCK' is not a notified activityfor item 'DBA_TYPE/21'.

&nbsp;

Wf_Engine.CompleteActivity(DBA_TYPE, 21, DBA_CONTROL_PROCESS:BLOCK, COMPLETE)
</pre>
<p>&nbsp;</p>
<p>Let&rsquo;s go through the procedure. <br />
First we see a declaration of a custom exception for ORA-20002. This is the custom error that workflow will issue. It is optional in this case, since any error can only occur from the API-call. But that might not always be the case.</p>
<p>In the exception block we first call &lsquo;wf_core.get_error&rsquo; to retrieve the information about this error. If needed we can parse this error further. But the error_name already tells us that we tried to complete a process_activity that is not in a notified state. Based on this the caller should be able to decide what needs to be done.</p>
<p>With that we can move to the next function in this article. <br />
That will be the &lsquo;WAIT&rsquo; function. The &lsquo;WAIT&rsquo; is used to postpone further processing of the item to a time set by an item_attribute or relative to the start of the process_activity. <br />
It works differently from the way the &lsquo;timeout&rsquo; worked on the &lsquo;Block&rsquo; activity. That was only picked up by a background engine with parameter &lsquo;process_timeout=&gt;TRUE&rsquo;. <br />
Let&rsquo;s replace the block in our process with a &lsquo;Wait&rsquo; function. The &lsquo;Wait&rsquo; can be copied from the &lsquo;STANDARD&rsquo; itemtype. It also needs a lookup type &lsquo;Wait Mode&rsquo;. This lookup type defines how to determine the timestamp to complete the process activity. <br />
The possible values are &lsquo;Day of the week&rsquo;, &lsquo;Day of the month&rsquo;, &lsquo;Absolute date&rsquo; and &lsquo;Relative time&rsquo;.</p>
<p>When you add the &lsquo;Wait&rsquo; function to your item, this will be the first &lsquo;node attribute&rsquo; to set. As usual this can be set with a constant, or an item attribute value. When you choose to set it to a fixed date, you can also set the &lsquo;Time of day&rsquo; attribute to set the timestamp. This is of course not needed for the &lsquo;Relative time&rsquo; mode.</p>
<p>All node attributes can be set to a constant or an item_attribute_value. In our sample we will first set a relative time, based on an item attribute value. (for a change).</p>
<p>We use one function to set a new item_attribute:&nbsp;<br />
&nbsp;<br />
<input type="image" height="355" width="537" src="http://www.stijf.com/wordpress/wp-content/uploads/delay_time_assign.JPG" /></p>
<p>And the second node is a &#8216;Wait&#8217; till the requested time:</p>
<input type="image" height="354" width="536" src="http://www.stijf.com/wordpress/wp-content/uploads/wait_relative.JPG" />
<p>The relative time is in {days} . {fraction of day}. So 5/(24*60) is 5 minutes. 0.03 is just about 4.5 minutes therefore.&nbsp;</p>
<p>When we launch a new item, and check the status:</p>
<p>&nbsp;</p>
<pre language="SQL">
INSTANCE_LABEL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; COST FUNCTION&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; END_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STATUS&nbsp;&nbsp; RESULT DUE_DATE
DBA_CONTROL_PROCESS 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 25-7-2009 20:23:22&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACTIVE&nbsp;&nbsp; #NULL 
START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp; START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 25-7-2009 20:23:22 25-7-2009 20:23:22 COMPLETE #NULL 
ASSIGN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; WF_STANDARD.ASSIGN 25-7-2009 20:23:22 25-7-2009 20:23:22 COMPLETE #NULL 
WAIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; WF_STANDARD.WAIT&nbsp;&nbsp; 25-7-2009 20:27:41&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DEFERRED #NULL</pre>
<p>&nbsp;</p>
<p>So the &lsquo;Wait&rsquo; activity is deferred. But it does not get a due_date. Instead the begin_date is set to the time till which the activity has to wait. <br />
Since the status=&rsquo;DEFERRED&rsquo;, we also have a record in the wf_deferred_queue_m:</p>
<p>&nbsp;</p>
<pre language="SQL">
select queue
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; corr_id
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; msg_state
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; enq_time 
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; delay
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; deq_time
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def.user_data.itemtype itemtype
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def.user_data.itemkey itemkey
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def.user_data.actid actid
&nbsp;from applsys.AQ$WF_DEFERRED_TABLE_M def
where msg_id=(select outbound_queue_id
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;    &nbsp;&nbsp;&nbsp; from wf_item_activity_statuses
      &nbsp;&nbsp;&nbsp;     where item_type='DBA_TYPE'
&nbsp;&nbsp;&nbsp;           and item_key='22'
&nbsp;&nbsp;&nbsp;           and activity_status='DEFERRED');</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre language="SQL">
QUEUE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CORR_ID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MSG_STATE ENQ_TIME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DELAY&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ITEMTYPE ITEMKEY ACTID
WF_DEFERRED_QUEUE_M APPSDBA_TYPE WAIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 25-7-2009 20:23:19 25-7-2009 20:27:39 DBA_TYPE 22&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 245973</pre>
<p>&nbsp;</p>
<p>So the message has been queued with a delay till the start_time of the process_activity.</p>
<p>Note that the msg_state=&rsquo;WAIT&rsquo;. This means that the message cannot be dequeued yet. One or more QMON processes need to be running. (Set with &lsquo;alter system set aq_tm_processes=x scope=both;). These processes are responsible for moving messages from the &lsquo;WAIT&rsquo; state to the &lsquo;READY&rsquo; state at the designated moment.<br />
Depending on the load on your system and the number of messages to be handled, you can put more QMON processes. But for most systems one QMON process will be sufficient for the workflow activity. If you need more than one QMON process, you&rsquo;ll also have to be running multiple background engines.</p>
<p>Only messages with msg_state &lsquo;READY&rsquo; can be dequeued and processed by the background engine. <br />
When the message is&nbsp; eligible to be dequeued, you can run a background engine to process the process_activity as any &lsquo;Deferred&rsquo; activity. The begin_date of the process_activity will then be updated to the time it was processed by the background engine.</p>
<p>Now let&rsquo;s change the process to use a &lsquo;Wait&rsquo; till a day of the week. In this case we&rsquo;ll wait till Monday morning. First we set the attribute to the &lsquo;Date&rsquo; type.&nbsp;<br />
&nbsp;</p>
<input type="image" height="354" width="536" src="http://www.stijf.com/wordpress/wp-content/uploads/delay_time2.JPG" />
<p>Then we change the &lsquo;Assign activity&rsquo;. Here we set the value to any date with the timestamp that we want. The date-part of the assignment will be ignored by the &lsquo;Wait&rsquo; function.</p>
<input type="image" height="354" width="536" src="http://www.stijf.com/wordpress/wp-content/uploads/delay_time_assign2.JPG" />
<p>&nbsp;</p>
<p>Finally we update the Node attributes on the &lsquo;Wait&rsquo; activity to be &lsquo;Day of the Week&rsquo;, a &lsquo;Monday&rsquo;, and the &lsquo;Delay time&rsquo; attribute for &lsquo;Time of the Day&rsquo;.</p>
<p>Now when we launch a process, we can see that the activity is indeed postponed till the next Monday. (In this case 27th july 2009). At 8.00AM.</p>
<p>&nbsp;</p>
<pre language="SQL">
INSTANCE_LABEL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; COST FUNCTION&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; END_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STATUS&nbsp;&nbsp; RESULT DUE_DATE
DBA_CONTROL_PROCESS 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 25-7-2009 20:50:14&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACTIVE&nbsp;&nbsp; #NULL 
START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp; START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 25-7-2009 20:50:14 25-7-2009 20:50:14 COMPLETE #NULL 
ASSIGN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; WF_STANDARD.ASSIGN 25-7-2009 20:50:14 25-7-2009 20:50:14 COMPLETE #NULL 
WAIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; WF_STANDARD.WAIT&nbsp;&nbsp; 27-7-2009 8:00:00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DEFERRED #NULL</pre>
<p>&nbsp;</p>
<p>
At this point it&rsquo;s time to look at a special &lsquo;feature&rsquo; of the &lsquo;DEFERRED&rsquo; status. Before I said that you can put an activity in &lsquo;DEFER&rsquo; mode just by returning a result_code of wf_engine.eng_defer. <br />
There is a bit more to that. The WF_STANDARD.DEFER procedure checks whether it is being executed for the first or second time. The first time it is executed, it is to set the status to &lsquo;DEFERRED&rsquo;. That time, the result_code from wf_item_activity_statuses has not been set yet. So the procedure returns the result_code &lsquo;wf_engine.eng_defer&rsquo;. When it is being called, while the result_code in wf_item_activity_statuses has been set, it will assume it is being called from a background process and return &lsquo;wf_engine.eng_completed&rsquo;. Which will then signal the workflow engine to continue with the item.</p>
<p>We can use this mechanism to our own advantage. When an item is deferred,&nbsp; and we want it to be run immediately, without the background process, then we can retry the process activity. The workflow engine has an API for this:</p>
<p>&nbsp;</p>
<pre language="SQL">
Begin
Wf_engine.handleerror(itemtype=&gt;&rsquo;DBA_TYPE&rsquo;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,itemkey=&gt;&rsquo;21&rsquo;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,activity=&gt;&rsquo;DBA_CONTROL_PROCESS:DEFER&rsquo;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,command=&gt;&rsquo;RETRY&rsquo;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );
End;</pre>
<p>&nbsp;</p>
<p>We will see more about the handleerror API in a later part. With the command &lsquo;RETRY&rsquo;, it will re-execute the indicated process_activity. In the case of a &lsquo;DEFER&rsquo;, it will therefore complete the activity. And continue the process.<br />
The same mechanism also applies to the &lsquo;WAIT&rsquo; function, since it basically only defers an activity.</p>
<p>Be aware of this, when activities are re-executed. Since it may not always be the desired result to complete the activity.</p>
<p>Now let&rsquo;s move on to the last controls that we are discussing in this part.</p>
<p>We start with the &lsquo;Launch Process&rsquo; function. You can copy it from the &lsquo;STANDARD&rsquo; item_type. Including the lookup_type &lsquo;YES/NO&rsquo;, if you don&rsquo;t already have that. The lookup_type is required for one of&nbsp; it&rsquo;s activity_attributes.</p>
<p>It will be clear what this function is doing from the name. It launches another item from the current item. There is no restriction on the item_type. The function will perform the same &lsquo;wf_engine.launch&rsquo;, that we execute from sql*plus during this series.</p>
<p>The function has six activity_attributes, one of which is mandatory (2 in our case, since we don&rsquo;t have an automatic selection for the process yet.). <br />
The first attribute is item_type. This shouldn&rsquo;t require a further explanation.<br />
Item_key is optional. If no item_key is provided, the function will build an item of the &lsquo;&lt;current item_type&gt;: &lt;current item_key&gt;-n&rsquo; where n is a sequence number for the number of items launched by the current item. The sequence is stored in a new item_attribute: &lsquo;LAUNCH_COUNT&rsquo;. <br />
Then comes the process_name. It is possible to use a selector function to automatically start the right process within an item_type. But when this is not used, you need to provide the process_name.<br />
User_key and owner are optional, and refer to a user-defined reference key and a workflow role that will assume ownership of this item. <br />
The last attribute is &lsquo;Defer Immediate&rsquo;, with a lookup_type of &lsquo;YES/NO&rsquo;. When set to no, the new item will be launched immediately, and be executed by the current workflow engine. <br />
When &lsquo;Defer Immediate&rsquo; is set to &lsquo;Yes&rsquo;, the activity will be deferred. And both the &lsquo;Launch Process&rsquo; activity as the new item will be executed by a background engine.</p>
<p>After the &lsquo;Launch Process&rsquo; activity, the item will continue it&rsquo;s own processing. (Without the &lsquo;Defer Immediate&rsquo; attribute set to &lsquo;Yes&rsquo;, the engine will first process the new item as far as possible, before returning to the original item). There is no further relationship between the two items.</p>
<p>The last two controls do create a relationship between two or more items. It is possible to create parent/child relationships on the item-level.</p>
<p>We are going to create a sample of this by using &lsquo;DBA_CONTROL_PROCESS&rsquo; to launch &lsquo;DBA_MAIN_PROCESS&rsquo; and wait for it&rsquo;s completion. <br />
On the &lsquo;Launch Process&rsquo; function it is not possible to specify the parent/child relationship. So we have to use the &lsquo;wf_engine.CreateProcess&rsquo; and &lsquo;wf_engine.StartProcess&rsquo; functions. The first one only creates an item, without executing it. <br />
After the&nbsp; &lsquo;CreateProcess&rsquo;, we can call the &lsquo;SetItemParent&rsquo; API to set the parent/child relationship.</p>
<p>So we have to create a new procedure:</p>
<p>&nbsp;</p>
<pre language="SQL">
CREATE OR REPLACE PROCEDURE XXX_LAUNCH_CHILD (p_item_type IN VARCHAR2
&nbsp;                                   &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;,p_item_key IN VARCHAR2
                                    &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;,p_actid IN NUMBER
                         &nbsp;           &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;,p_funcmode IN VARCHAR2
                                    &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;,p_result OUT VARCHAR2) IS
BEGIN
&nbsp; if p_funcmode='RUN' then
&nbsp;&nbsp;&nbsp; wf_engine.CreateProcess(itemtype=&gt;p_item_type
                &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ,itemkey=&gt;p_item_key||'-1'
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                    ,process=&gt;'DBA_MAIN_PROCESS');
&nbsp;&nbsp;&nbsp; wf_engine.SetItemParent(itemtype=&gt;p_item_type
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;               &nbsp;&nbsp;&nbsp;&nbsp; ,itemkey=&gt;p_item_key||'-1'
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                   ,parent_itemtype=&gt;p_item_type
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                   ,parent_itemkey=&gt;p_item_key
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                   ,parent_context=&gt;NULL
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                   ,masterdetail=&gt;TRUE);
&nbsp;&nbsp;&nbsp; wf_engine.StartProcess(itemtype=&gt;p_item_type
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                ,itemkey=&gt;p_item_key||'-1');
&nbsp;end if;
&nbsp;p_result:=wf_engine.eng_completed;
END XXX_LAUNCH_CHILD;
/</pre>
<p>&nbsp;</p>
<p>The &lsquo;CreateProcess&rsquo; and &lsquo;StartProcess&rsquo; should be obvious in their usage. In the &lsquo;SetItemParent, you can indicate a &lsquo;parent_context&rsquo;.&nbsp; This is a free text value, that can be referenced again in the &lsquo;WaitforFlow&rsquo; and &lsquo;ContinueFlow&rsquo; functions. In case you launch multiple child items, you use the context value to know which items should continue. We will come back to this at the end of the example.</p>
<p>First we are going to build our process with the new function:</p>
<input type="image" height="354" width="536" src="http://www.stijf.com/wordpress/wp-content/uploads/launch_child.JPG" />
<p>The process will look like this:</p>
<input type="image" height="105" width="415" src="http://www.stijf.com/wordpress/wp-content/uploads/item_wait_flow1.JPG" />
<p>And of course we need to put the &lsquo;ContinueFlow&rsquo; in the &lsquo;DBA_MAIN_PROCESS&rsquo;. Actually twice, since I want to put it in front of the &lsquo;End&rsquo; and we have 2 exits.&nbsp;</p>
<input type="image" height="624" width="785" src="http://www.stijf.com/wordpress/wp-content/uploads/main_with_continue.JPG" />
<p>Now when we start the &lsquo;DBA_CONTROL_PROCESS&rsquo; we can see what happens:</p>
<p>&nbsp;</p>
<pre language="SQL">
select wpa.instance_label
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wias.item_key
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case when wpa.start_end is not null then wpa.start_end else wa.function end function
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wias.begin_date
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wias.end_date
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wias.activity_status status
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wias.activity_result_code result
from&nbsp;&nbsp; wf_item_activity_statuses wias
join&nbsp;&nbsp; wf_process_activities wpa 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; on (wias.process_activity=wpa.instance_id)
join&nbsp;&nbsp; wf_activities wa 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; on (wpa.activity_item_type=wa.item_type 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and wpa.activity_name=wa.name 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and wa.end_date is null)
join&nbsp;&nbsp; wf_activities_tl wat 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; on (wa.item_type=wat.item_type 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and wa.name=wat.name 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and wa.version=wat.version 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and wat.language='US')
where&nbsp; wias.item_type='DBA_TYPE'
and&nbsp;&nbsp;&nbsp; wias.item_key like '20%' 
order by wias.begin_date,wias.execution_time;</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre language="SQL">
INSTANCE_LABEL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; KEY FUNCTION BEGIN_DATE END_DATE STATUS RESULT
DBA_CONTROL_PROCESS 20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACTIVE&nbsp;&nbsp; #NULL
START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20&nbsp;&nbsp; START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37 26-7-2009 14:26:37 COMPLETE #NULL
XXX_LAUNCH_CHILD&nbsp;&nbsp;&nbsp; 20&nbsp;&nbsp; xxx_launch_child&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37 26-7-2009 14:26:38 COMPLETE COMPLETE
DBA_MAIN_PROCESS&nbsp;&nbsp;&nbsp; 20-1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACTIVE&nbsp;&nbsp; #NULL
START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37 26-7-2009 14:26:37 COMPLETE #NULL
INITIALIZE_FLOW&nbsp;&nbsp;&nbsp;&nbsp; 20-1 XXX_WF_DBA.init&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37 26-7-2009 14:26:37 COMPLETE COMPLETE
COMPARETEXT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 WF_STANDARD.COMPARE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37 26-7-2009 14:26:37 COMPLETE EQ
CHECK_INVALIDS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 XXX_CHECK_INVALIDS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37 26-7-2009 14:26:37 COMPLETE Y
DEFER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 WF_STANDARD.DEFER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DEFERRED #NULL
GET_INVALIDS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 XXX_WF_UTILS.get_invalids 26-7-2009 14:26:37 26-7-2009 14:26:38 COMPLETE 
LOOPCOUNTER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 WF_STANDARD.LOOPCOUNTER&nbsp;&nbsp; 26-7-2009 14:26:38 26-7-2009 14:26:38 COMPLETE EXIT
AND&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 WF_STANDARD.ANDJOIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:38&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WAITING 
WAITFORFLOW&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20&nbsp;&nbsp; WF_STANDARD.WAITFORFLOW&nbsp;&nbsp; 26-7-2009 14:26:38&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NOTIFIED</pre>
<p>&nbsp;</p>
<p>I purposely left the &lsquo;Defer&rsquo; in the &lsquo;DBA_MAIN_PROCESS&rsquo; so we can see the status while the &lsquo;DBA_MAIN_PROCESS&rsquo; is running. When the workflow engine could not process &lsquo;DBA_MAIN_PROCESS&rsquo; further, it returned to the original item, and executed &lsquo;WAITFORFLOW&rsquo;, which of course got the &lsquo;NOTIFIED&rsquo; status. <br />
We can also see the parent_child relationship by querying &lsquo;WF_ITEMS&rsquo;:</p>
<p>&nbsp;</p>
<pre language="SQL">
select item_type
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; item_key key
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin_date
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parent_item_type parent_type
,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parent_item_key&nbsp; parent_key
from&nbsp;&nbsp; wf_items 
where&nbsp; item_type='DBA_TYPE'
and&nbsp;&nbsp;&nbsp; item_key like '20%'</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre language="SQL">
ITEM_TYPE KEY&nbsp; BEGIN_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PARENT_TYPE PARENT_KEY
DBA_TYPE&nbsp; 20-1 26-7-2009 14:26:37 DBA_TYPE&nbsp;&nbsp;&nbsp; 20
DBA_TYPE&nbsp; 20&nbsp;&nbsp; 26-7-2009 14:26:37&nbsp;&nbsp;</pre>
<p>&nbsp;</p>
<p>Now when we run a background engine, and check again:</p>
<p>&nbsp;</p>
<pre language="SQL">
INSTANCE_LABEL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; KEY&nbsp; FUNCTION&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; END_DATE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STATUS&nbsp;&nbsp; RESULT
DBA_CONTROL_PROCESS 20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37 26-7-2009 14:38:40 COMPLETE #NULL
START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20&nbsp;&nbsp; START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37 26-7-2009 14:26:37 COMPLETE #NULL
XXX_LAUNCH_CHILD&nbsp;&nbsp;&nbsp; 20&nbsp;&nbsp; xxx_launch_child&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37 26-7-2009 14:26:38 COMPLETE COMPLETE
DBA_MAIN_PROCESS&nbsp;&nbsp;&nbsp; 20-1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37 26-7-2009 14:38:40 COMPLETE #NULL
START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 START&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37 26-7-2009 14:26:37 COMPLETE #NULL
INITIALIZE_FLOW&nbsp;&nbsp;&nbsp;&nbsp; 20-1 XXX_WF_DBA.init&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37 26-7-2009 14:26:37 COMPLETE COMPLETE
COMPARETEXT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 WF_STANDARD.COMPARE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37 26-7-2009 14:26:37 COMPLETE EQ
CHECK_INVALIDS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 XXX_CHECK_INVALIDS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:26:37 26-7-2009 14:26:37 COMPLETE Y
GET_INVALIDS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 XXX_WF_UTILS.get_invalids 26-7-2009 14:26:37 26-7-2009 14:26:38 COMPLETE 
LOOPCOUNTER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 WF_STANDARD.LOOPCOUNTER&nbsp;&nbsp; 26-7-2009 14:26:38 26-7-2009 14:26:38 COMPLETE EXIT
WAITFORFLOW&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20&nbsp;&nbsp; WF_STANDARD.WAITFORFLOW&nbsp;&nbsp; 26-7-2009 14:26:38 26-7-2009 14:38:40 COMPLETE #NULL
DEFER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 WF_STANDARD.DEFER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:38:40 26-7-2009 14:38:40 COMPLETE #NULL
AND&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 WF_STANDARD.ANDJOIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:38:40 26-7-2009 14:38:40 COMPLETE #NULL
CONTINUEFLOW&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 WF_STANDARD.CONTINUEFLOW&nbsp; 26-7-2009 14:38:40 26-7-2009 14:38:40 COMPLETE #NULL
END&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20&nbsp;&nbsp; END&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:38:40 26-7-2009 14:38:40 COMPLETE #NULL
END-1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20-1 END&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 26-7-2009 14:38:40 26-7-2009 14:38:40 COMPLETE #NULL</pre>
<p>&nbsp;</p>
<p>The ContinueFlow nicely completed the &lsquo;WaitforFlow&rsquo; and both items&nbsp; completed successfully.</p>
<p>So how does the &lsquo;ContinueFlow&rsquo; activity know which process_activity to continue? The first check being done is&nbsp; whether the activity_attribute &lsquo;Waiting Flow&rsquo; is set to Master or Detail. When it is set to &lsquo;Detail, the ContinueFlow is a parent process. So it will look for it&rsquo;s children in wf_items, and complete all activities that have the instance_label from the &lsquo;Waiting Activity Label&rsquo; activity_attribute.</p>
<p>When it is set to &lsquo;Master&rsquo;, this is just one of the (possibly) multiple children that have completed. So it will count the number of children for the parent item. And check all of them to see if they have reached the&nbsp; &lsquo;ContinueFlow&rsquo; process_activity. When all children have reached the &lsquo;ContinueFlow&rsquo;, the last item will complete the activity on the parent item.</p>
<p>Of course it is possible for a parent item to have multiple &lsquo;WaitforFlow&rsquo; activities, for different child items. In this case, you need to distinguish the different parent/child relationships.&nbsp; That is where the &lsquo;parent_context&rsquo; parameter from the &lsquo;SetItemParent&rsquo; function comes in. <br />
The parent_context can be used to group together all child items that were started from the same process_activity.</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stijf.com/wordpress/2009/07/oracle-workflow-for-ebs-dbas-part-4/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Workflow for eBS DBA&#8217;s (Part 3)</title>
		<link>http://www.stijf.com/wordpress/2009/07/workflow-for-ebs-dbas-part-3/</link>
		<comments>http://www.stijf.com/wordpress/2009/07/workflow-for-ebs-dbas-part-3/#comments</comments>
		<pubDate>Thu, 23 Jul 2009 09:32:27 +0000</pubDate>
		<dc:creator>Arian Stijf</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.stijf.com/wordpress/?p=72</guid>
		<description><![CDATA[Workflow for ebs DBA&#8217;s (part 3) In this part of our series (previous parts to be found here and here) we are going to look into what I call workflow constructs. These are the constructs that determine the direction of your item. Like if (or case for that matter), and/or, loops, waits / holds.
If / [...]]]></description>
			<content:encoded><![CDATA[<p>Workflow for ebs DBA&rsquo;s (part 3) In this part of our series (previous parts to be found <a title="Workflow for eBS DBAs part 1" target="_blank" href="http://www.stijf.com/wordpress/2009/07/workflow-for-ebs-dbas-part-1">here </a>and <a title="Workflow for eBS DBA's part 2" target="_blank" href="http://www.stijf.com/wordpress/workflow-for-ebs-dbas-part-2">here</a>) we are going to look into what I call workflow constructs. These are the constructs that determine the direction of your item. Like if (or case for that matter), and/or, loops, waits / holds.</p>
<address>If / Case</address>
<p>We pick up our initial workflow from part 2. And the first thing to do is a check if we are on the right database. Remember we assigned the db_name to an item_attribute. Now we will check if this is the version we want. To follow the progress of the item, I use a &lsquo;track_flow_progress&rsquo; function, that inserts into a table the timestamp and some information from the process_activity that it is called from. Table creation:</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
  create table xxx_track_flow (id number
  		 	  ,item_type varchar2(8)
			  ,item_key varchar2(240)
			  ,activity_name varchar2(30)
			  ,activity_version number
			  ,process_name varchar2(30)
			  ,instance_id number
			  ,instance_label varchar2(30)
			  ,funcmode varchar2(30)
		      );</pre>
<p></code>&nbsp; &nbsp; &nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
create sequence xxx_track_flow_s start with 1 increment by 1;</pre>
<p></code>&nbsp; And the procedure itself.</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
  Create or replace Procedure track_flow_progress (p_item_type IN VARCHAR2
  				 	   ,p_item_key IN VARCHAR2
				 	   ,p_actid IN NUMBER
				 	   ,p_funcmode IN VARCHAR2
				 	   ,p_result OUT VARCHAR2) IS
  v_activity_name varchar2(30);
  v_activity_version number;
  v_process_name varchar2(30);
  v_instance_label varchar2(30);
  begin
  	   select activity_name,wa.version,process_name,instance_label
	   into	  v_activity_name, v_activity_version, v_process_name, v_instance_label
	   from   wf_process_activities wpa join wf_activities wa
	   		  on (wpa.activity_item_type=wa.item_type
			      and wpa.activity_name=wa.name
			      and wpa.process_version=wa.version)
	   where  wpa.instance_id=p_actid;

	   insert into xxx_track_flow (id, item_type, item_key, activity_name, activity_version, process_name, instance_id, instance_label, funcmode)
	   values (xxx_track_flow_s.nextval, p_item_type, p_item_key, v_activity_name, v_activity_version, v_process_name, p_actid, v_instance_label, p_funcmode);
  end;</pre>
<p></code>&nbsp; This will record the basic information about the process activity being called. We put this in a new function in our item type: &nbsp; <img class="alignnone size-full wp-image-87" title="track_flow" height="353" alt="track_flow" width="536" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/track_flow.JPG" /> Now we can define a function to check the db_name. For comparison on item attribute values, Oracle delivers a standard function: &nbsp; <img class="alignnone size-full wp-image-88" title="create compare text" height="354" alt="create compare text" width="536" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/create-compare-text.JPG" /> This can be copied from the STANDARD itemtype that is delivered with the workflow engine. It is available from an eBS database, or on your workflow builder client from $ORACLE_HOME\wf\data\US\WFSTD.wft. We already see some interesting things about this function. First it has a &#8216;Result Type&#8217;. The &#8216;Result Type&#8217; indicates the possible exits from our function. It refers to a lookup type that is also defined in the STANDARD itemtype. When you expand the lookup type, you see that it has 4 lookup codes: &#8216;Equal&#8217;, &#8216;Greater Than&#8217;, &#8216;Less Than&#8217; and &#8216;NULL&#8217;. These codes are the possible return values for our function. At this point we can copy the lookup type and the function from the &lsquo;STANDARD&rsquo; itemtype. Later we&rsquo;ll build our own lookup types. To make a comparison, you need at least 2 parameters. The value to be tested, and the reference value that it will be compared to. When you expand the function &lsquo;Compare Text&rsquo;, there are 2 attributes defined for this function. These attributes are not the same as the item attributes that we used in the previous article. The attributes linked to the function are &lsquo;Activity Attributes&rsquo;. We can find them in the wf_activity_attributes(_tl) table:</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
select waa.activity_item_type
,	  waa.activity_name
,	  waa.activity_version
,	  waa.name
,	  sequence
,	  type
,	  value_type
,	  format
,	  display_name
,	  description
from wf_activity_attributes waa join wf_activity_attributes_tl waat
on (waa.activity_item_type=waat.activity_item_type
    and waa.activity_name=waat.activity_name
    and waa.activity_version=waat.activity_version
    and waa.name=waat.name
    and waat.language='US')
where waa.activity_item_type='DBA_TYPE'
and waa.activity_name='COMPARETEXT'
and waa.activity_version=1;</pre>
<p></code><code></p>
<pre>
ACTIVITY ACTIVITY_NAME   ACTIVITY_VERSION NAME        SEQUENCE   TYPE     VALUE_TY FORMAT DISPLAY_NAME    DESCRIPTION
-------- --------------- ---------------- ----------- ---------- -------- -------- ------ --------------- ---------------
DBA_TYPE COMPARETEXT                    1 VALUE1               0 VARCHAR2 CONSTANT        Test value      Value to comp
DBA_TYPE COMPARETEXT                    1 VALUE2               1 VARCHAR2 CONSTANT 2000   Reference value Standard a</pre>
<p></code>&nbsp; The sequence is used to indicate the order in which the attributes are shown in the workflow builder. It is not relevant for us any more. I just put it there to indicate the purpose. &nbsp; Now we can put the function in our process. I put it between &lsquo;initialize flow&rsquo; and the &lsquo;End&rsquo;. &nbsp; <img class="alignnone size-full wp-image-89" title="create compare text" height="354" alt="create compare text" width="536" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/create-compare-text1.JPG" /> &nbsp; When you draw the transitions, you&rsquo;ll find that you are asked for the result that applies to this transition. I chose to draw one transition for result &lsquo;Equal&rsquo;. Leading to a &lsquo;track flow&rsquo; function and then &lsquo;End&rsquo;. And another transition for result &lsquo;Default&rsquo;. Leading also to a &lsquo;track flow&rsquo; function and then &lsquo;End&rsquo;. The result looking like this. <img class="alignnone size-full wp-image-90" title="process with compare" height="356" alt="process with compare" width="620" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/process-with-compare.JPG" /> On the &lsquo;Compare Text&rsquo; function we now have to fill in the reference and test values. This is done on the node attribute tab of the activity. Here we choose item attribute &lsquo;Database instance name&rsquo; for the test value (we assigned the DB_NAME to this item attribute in the init function. For the reference value we use TSTEBST1. (the db_name for the test database I used). <img class="alignnone size-full wp-image-91" title="Node attributes compare" height="354" alt="Node attributes compare" width="536" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/Node-attributes-compare.JPG" /> Before we run the process, we will first look at the process definition so far. Let&rsquo;s go back to our &lsquo;process definition&rsquo; query. I wrote in part 2 that I had taken a shortcut. And this is where it comes into play. Without adjustment, the query would not allow for multiple possibilities. So I adjusted the query a bit and included a result_code_origin, that will show which path we followed:</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
select wpa3.process_item_type
, 	   wpa3.process_name
, 	   wpa3.process_version
, 	   wpa3.instance_id
,	   wa3.item_type
,	   wa3.type
,	   wa3.name
,	   wpa3.instance_label
,	   wa3.function
,	   trans.result_code
,	   trans.p_from_act
,	   trans.p_res_code from_res_code
from   (select r*100+l2 cntr
	    ,      wat2.result_code
		,	   wat2.p_res_code
		,	   wat2.p_from_act
	   , 	   case when wat2.start_end = 'END'
	   		    and d.l2=2
			then wat2.to_process_activity
			else wat2.from_process_activity
			   end e_proc
	   from (select wat1.from_process_activity
	   		 ,		wat1.to_process_activity
			 , 		wat1.result_code
			 ,		wpa2.start_end
			 ,		rownum r
			 , 		prior wat1.result_code p_res_code
			 , 		prior wat1.from_process_activity p_from_act
	  	     from   wf_activity_transitions wat1
			 join   wf_process_activities wpa2
			 on 	(wat1.to_process_activity=wpa2.instance_id)
			 connect by prior wat1.to_process_activity = wat1.from_process_activity
			 start with wat1.from_process_activity=(select wpa1.instance_id
				  	 	 	    					from   wf_process_activities wpa1
											 		join   wf_activities wa1
											  		on     (wpa1.process_item_type=wa1.item_type
											  		 		and wpa1.process_name=wa1.name
											  		 		and wpa1.process_version=wa1.version
											  		 		and wa1.end_date is null)
											  		 where  wpa1.process_item_type='OEOL'
											  		 and    wpa1.process_name='XXR_BILL_ONLY_IB'
											  		 and    wpa1.start_end='START'
											        )
	        ) wat2
		join  (select level l2
			   from dual
			   connect by level&lt;3) d
	    on ((start_end='END' and d.l2=2) or d.l2=1)
		) trans
join    wf_process_activities wpa3
on 		(trans.e_proc = wpa3.instance_id)
join    wf_activities wa3
on 		(wpa3.activity_item_type=wa3.item_type
		 and wpa3.activity_name=wa3.name
		 and wa3.end_date is null)
order by cntr;</pre>
<p></code>&nbsp; &nbsp; &nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
  L1 PROCESS_ PROCESS_NAME    PROCESS_VERSION INSTANCE_ID RESU RESU ITEM_TYP NAME            INSTANCE_LABEL  TYPE     FUNCTION
---- -------- --------------- --------------- ----------- ---- ---- -------- --------------- --------------- -------- -------------------------
   1 DBA_TYPE DBA_MAIN_PROCES              11      244904 *         DBA_TYPE START           START           FUNCTION WF_STANDARD.NOOP
   2 DBA_TYPE DBA_MAIN_PROCES              11      244902 *    *    DBA_TYPE INITIALIZE_FLOW INITIALIZE_FLOW FUNCTION XXX_WF_DBA.init
   3 DBA_TYPE DBA_MAIN_PROCES              11      244906 *    *    DBA_TYPE COMPARETEXT     COMPARETEXT     FUNCTION WF_STANDARD.COMPARE
   3 DBA_TYPE DBA_MAIN_PROCES              11      244906 EQ   *    DBA_TYPE COMPARETEXT     COMPARETEXT     FUNCTION WF_STANDARD.COMPARE
   4 DBA_TYPE DBA_MAIN_PROCES              11      244908 *    EQ   DBA_TYPE TRACK_FLOW      TRACK_FLOW      FUNCTION TRACK_FLOW_PROGRESS
   4 DBA_TYPE DBA_MAIN_PROCES              11      244910 *    EQ   DBA_TYPE END             END             FUNCTION WF_STANDARD.NOOP
   4 DBA_TYPE DBA_MAIN_PROCES              11      244912 *    *    DBA_TYPE TRACK_FLOW      TRACK_FLOW-1    FUNCTION TRACK_FLOW_PROGRESS
   4 DBA_TYPE DBA_MAIN_PROCES              11      244914 *    *    DBA_TYPE END             END-1           FUNCTION WF_STANDARD.NOOP</pre>
<p></code>&nbsp; So far so good. Now let&rsquo;s run the process, and see if the split is working as designed. &nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
begin
wf_engine.launchprocess(itemtype=&gt;'DBA_TYPE',itemkey=&gt;'4',process=&gt;'DBA_MAIN_PROCESS');
end;</pre>
<p></code>&nbsp; &nbsp; &nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
select item_type,item_key,activity_name,process_name,instance_id,instance_label,funcmode
from   xxx_track_flow
where  item_type='DBA_TYPE'
and    item_key='4';</pre>
<p></code>&nbsp; &nbsp; &nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
ITEM_TYP ITEM_KEY   ACTIVITY_NAME   PROCESS_NAME    INSTANCE_ID INSTANCE_LABEL  FUNCMODE
-------- ---------- --------------- --------------- ----------- --------------- --------
DBA_TYPE 4          TRACK_FLOW      DBA_MAIN_PROCES      244899 TRACK_FLOW      RUN</pre>
<p></code>&nbsp; I included the instance_label because it shows the difference between the EQ and * transitions. In this case, the activity with instance_label &lsquo;TRACK_FLOW&rsquo; is called through the EQ transition. The instance_label with &lsquo;TRACK_FLOW-1&rsquo; is called trough the * transition. You can see the label from the node-tab in Workflow Builder. As you see, the use of result_types enables you to split a flow. However it is worth noting that only one transition is called at a time. When this transition leads to an &lsquo;End&rsquo;-node, the other transitions will not be called.</p>
<address>The AND/OR functions</address>
<p>We will now make a split based on the number of invalid objects. When there are invalid objects (my database has 10), we will call a recompile function. Another transition continues with the process doing the main checks that we want to perform. After compilation of these objects (I just use a dummy function for this) we want both transitions to join again. First I created the required function for checking if there are any invalids. This function returns a &lsquo;Y&rsquo; or &lsquo;N&rsquo;. For this, we of course need a new result type: <img class="alignnone size-full wp-image-92" title="lookup_type" height="354" alt="lookup_type" width="536" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/lookup_type.JPG" /> With a lookup code for &lsquo;N&rsquo; and &lsquo;Y&rsquo; (only &lsquo;N&rsquo; is shown here). <img class="alignnone size-full wp-image-93" title="lookup_code_n" height="354" alt="lookup_code_n" width="536" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/lookup_code_n.JPG" /> &nbsp; And a function to check for invalids &nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
CREATE OR REPLACE PROCEDURE XXX_CHECK_INVALIDS(p_item_type IN VARCHAR2
  				 				,p_item_key IN VARCHAR2
				 				,p_actid IN NUMBER
				 				,p_funcmode IN VARCHAR2
				 				,p_result OUT VARCHAR2) IS
v_invalids varchar2(1) := 'N';
BEGIN
	 IF p_funcmode='RUN' then
	 	select case when count(*)&gt;0 then 'Y' else 'N' end inv
		into v_invalids
		from all_objects
		where status='INVALID';
     END IF;
	 p_result := v_invalids;

END XXX_CHECK_INVALIDS;
/</pre>
<p></code>&nbsp; And a function in our item_type: <img class="alignnone size-full wp-image-94" title="check_invalids" height="354" alt="check_invalids" width="536" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/check_invalids.JPG" /> I will first show you what happens when we split the process flow, without any further action: <img class="alignnone size-full wp-image-96" title="check_invalid1" height="465" alt="check_invalid1" width="710" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/check_invalid11.JPG" /> The transition straight up will become our &lsquo;MAIN&rsquo; process. While the diversion on the &lsquo;Yes&rsquo; transition will merge back after the requested action. So what happens when we run this: &nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
select item_type,item_key,activity_name,process_name,instance_id,instance_label,funcmode
from xxx_track_flow
where item_type='DBA_TYPE'
and item_key='5';</pre>
<p></code>&nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
ITEM_TYPE	ITEM_KEY	ACTIVITY_NAME	PROCESS_NAME	INSTANCE_ID	INSTANCE_LABEL	FUNCMODE
DBA_TYPE	5	TRACK_FLOW	DBA_MAIN_PROCESS	245051	TRACK_FLOW-2	RUN</pre>
<p></code>&nbsp; This will be a bit unexpected for most. The transition for &lsquo;Any&rsquo; and &lsquo;Yes&rsquo; are both valid, so 2 rows In the track_flow table would be expected, right? Let&rsquo;s see what wf_item_activity_statuses has to say. &nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
select wias.item_type
,      wias.item_key
,	   wpa.instance_label
,      case when wpa.start_end is not null then wpa.start_end else wa.function end function
,      wias.begin_date
,      wias.end_date
,      wias.activity_status status
,      wias.activity_result_code result
from wf_item_activity_statuses wias
join wf_process_activities wpa on (wias.process_activity=wpa.instance_id)
join wf_activities wa on (wpa.activity_item_type=wa.item_type and wpa.activity_name=wa.name and wa.end_date is null)
join wf_activities_tl wat on (wa.item_type=wat.item_type and wa.name=wat.name and wa.version=wat.version and wat.language='US')
where wias.item_type='DBA_TYPE'
and   wias.item_key='5'
order by wias.begin_date,wias.execution_time;</pre>
<p></code>&nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
ITEM_TYPE	ITEM_KEY	INSTANCE_LABEL	FUNCTION		BEGIN_DATE	END_DATE		STATUS	RESULT
DBA_TYPE	5	DBA_MAIN_PROCESS			22-7-2009 13:07:13	22-7-2009 13:07:13	COMPLETE	#NULL
DBA_TYPE	5	START		START		22-7-2009 13:07:13	22-7-2009 13:07:13	COMPLETE	#NULL
DBA_TYPE	5	INITIALIZE_FLOW	XXX_WF_DBA.init	22-7-2009 13:07:13	22-7-2009 13:07:13	COMPLETE	COMPLETE
DBA_TYPE	5	COMPARETEXT	WF_STANDARD.COMPARE	22-7-2009 13:07:13	22-7-2009 13:07:13	COMPLETE	EQ
DBA_TYPE	5	CHECK_INVALIDS	XXX_CHECK_INVALIDS	22-7-2009 13:07:13	22-7-2009 13:07:13	COMPLETE	Y
DBA_TYPE	5	TRACK_FLOW-2	TRACK_FLOW_PROGRESS	22-7-2009 13:07:13	22-7-2009 13:07:13	COMPLETE
DBA_TYPE	5	END-1		END		22-7-2009 13:07:13	22-7-2009 13:07:13	COMPLETE	#NULL
DBA_TYPE	5	TRACK_FLOW-1	TRACK_FLOW_PROGRESS	22-7-2009 13:07:13	22-7-2009 13:07:13	COMPLETE	#FORCE</pre>
<p></code>&nbsp; Now that&rsquo;s a surprise. We see that both TRACK_FLOW&rsquo;s have executed. But &#8216;TRACK_FLOW-1&#8242; has a result code of &#8216;#FORCE&#8217;. The workflow engine has taken the first applicable transition. In this case the &lsquo;Any&rsquo;. This led to an &lsquo;END&rsquo;-node. So the engine &lsquo;forced&rsquo; the second transition to abort. The only way to make sure every transition is completely executed, is to put an &lsquo;AND&rsquo; function in. Copy the function from the standard itemtype, and build the following flow: <img class="alignnone size-full wp-image-97" title="check_invalid2" height="555" alt="check_invalid2" width="720" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/check_invalid2.JPG" /> Now when we run the process, we see the following results: &nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
ITEM_TYPE	ITEM_KEY	ACTIVITY_NAME	PROCESS_NAME	INSTANCE_ID	INSTANCE_LABEL	FUNCMODE
DBA_TYPE	6	TRACK_FLOW	DBA_MAIN_PROCESS	245120	TRACK_FLOW-2	RUN
DBA_TYPE	6	TRACK_FLOW	DBA_MAIN_PROCESS	245118	TRACK_FLOW-1	RUN</pre>
<p></code>&nbsp; &nbsp; And for the activity_statuses:</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
ITEM_TYPE	ITEM_KEY	INSTANCE_LABEL	FUNCTION		BEGIN_DATE	END_DATE		STATUS	RESULT
DBA_TYPE	6	DBA_MAIN_PROCESS			22-7-2009 13:57:58	22-7-2009 13:57:59	COMPLETE	#NULL
DBA_TYPE	6	START	START			22-7-2009 13:57:58	22-7-2009 13:57:58	COMPLETE	#NULL
DBA_TYPE	6	INITIALIZE_FLOW	XXX_WF_DBA.init	22-7-2009 13:57:58	22-7-2009 13:57:58	COMPLETE	COMPLETE
DBA_TYPE	6	COMPARETEXT	WF_STANDARD.COMPARE	22-7-2009 13:57:58	22-7-2009 13:57:58	COMPLETE	EQ
DBA_TYPE	6	CHECK_INVALIDS	XXX_CHECK_INVALIDS	22-7-2009 13:57:58	22-7-2009 13:57:59	COMPLETE	Y
DBA_TYPE	6	TRACK_FLOW-2	TRACK_FLOW_PROGRESS	22-7-2009 13:57:59	22-7-2009 13:57:59	COMPLETE
DBA_TYPE	6	TRACK_FLOW-1	TRACK_FLOW_PROGRESS	22-7-2009 13:57:59	22-7-2009 13:57:59	COMPLETE
DBA_TYPE	6	AND		WF_STANDARD.ANDJOIN	22-7-2009 13:57:59	22-7-2009 13:57:59	COMPLETE	#NULL
DBA_TYPE	6	END-1		END		22-7-2009 13:57:59	22-7-2009 13:57:59	COMPLETE	#NULL</pre>
<p></code>&nbsp; This time both transitions were executed, and completed normally. In the &#8216;WF_STANDARD&#8217; itemtype there is also an &lsquo;OR&rsquo; function. This will continue as soon as any transition arrives there. In our case it would have the same result as the first process (a &#8216;#FORCE&#8217; result on the activity in the second transition). Last but not least, we will look into loops. Loops are easily implemented in workflow. Even though it is often misused. We will build a loop that touches every invalid object in the database, and records information about it. For this we use a package (the nice thing about them is that they allow global use of a PL/SQL table): &nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
CREATE OR REPLACE PACKAGE XXX_WF_UTILS AS

  TYPE r_invalids IS RECORD  (owner all_objects.owner%type
  	    	 		 ,object_name all_objects.object_name%type
				 ,object_type all_objects.object_type%type);

  TYPE t_invalids IS TABLE OF r_invalids INDEX BY BINARY_INTEGER;

  v_invalids t_invalids;

  Procedure get_invalids (p_item_type IN VARCHAR2
   			   ,p_item_key IN VARCHAR2
			   ,p_actid IN NUMBER
		    	   ,p_funcmode IN VARCHAR2
			   ,p_result OUT VARCHAR2);

END XXX_WF_UTILS;
/</pre>
<p></code>&nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
CREATE OR REPLACE PACKAGE BODY XXX_WF_UTILS AS

  Procedure get_invalids (p_item_type IN VARCHAR2
   		     ,p_item_key IN VARCHAR2
		     ,p_actid IN NUMBER
		     ,p_funcmode IN VARCHAR2
		     ,p_result OUT VARCHAR2) is
  Begin
       If p_funcmode=&rsquo;RUN&rsquo; then
          select owner,object_name,object_type bulk collect into xxx_wf_utils.v_invalids from dba_objects where status='INVALID';
          wf_engine.SetItemAttrNumber(itemtype=&gt;p_item_type,itemkey=&gt;p_item_key
                                 ,aname=&gt;'NO_INVALID_OBJECTS',avalue=&gt;xxx_wf_utils.v_invalids.count);
     End if;
  end;  

END XXX_WF_UTILS;
/</pre>
<p></code>&nbsp; This procedure will collect the invalid objects in a PL/SQL table and write the number of records into a new attribute (&#8217;NO_INVALID_OBJECTS&#8217;). This is a new attribute with type number. After this, we create a loop in the workflow. In the Standard itemtype, there is a function &lsquo;Loop Counter&rsquo; that does this very neatly. Finally we need a processor to handle the selected values. For this we create a table and expand the &#8216;XXX_WF_UTILS&#8217; with a second procedure.</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
create table xxx_wf_invalids (id, owner, object_name, object_type)
as select object_id,owner,object_name,object_type from dba_objects where 1=0;</pre>
<p></code>&nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
Procedure process_invalids (p_item_type IN VARCHAR2
  				 		 ,p_item_key IN VARCHAR2
				 		 ,p_actid IN NUMBER
				 		 ,p_funcmode IN VARCHAR2
				 		 ,p_result OUT VARCHAR2) is
  v_counter number;
  begin
       if p_funcmode=&rsquo;RUN&rsquo; then
  	   v_counter:=wf_engine.GetItemAttrNumber(itemtype=&gt;p_item_type,itemkey=&gt;p_item_key,aname=&gt;'COUNT_LOOP');
	   insert into xxx_wf_invalids (id, owner, object_name, object_type)
	   values (v_counter
	   		  ,xxx_wf_utils.v_invalids(v_counter).owner
	   		  ,xxx_wf_utils.v_invalids(v_counter).object_name
	   		  ,xxx_wf_utils.v_invalids(v_counter).object_type);
	    wf_engine.SetItemAttrNumber(itemtype=&gt;p_item_type,itemkey=&gt;p_item_key,aname=&gt;'COUNT_LOOP',avalue=&gt;v_counter+1);
       end if;
  end;</pre>
<p></code>&nbsp; As you can see, we also need a new item_attribute to count the number of executions: &lsquo;COUNT_LOOP&rsquo;. This is defined with a default value of 1. <img class="alignnone size-full wp-image-98" title="count_loop" height="354" alt="count_loop" width="536" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/count_loop.JPG" /> During every execution, the counter is increased. And it serves as a pointer for the pl/sql table. Now we add the &#8216;Loop Counter&#8217; and &#8216;Process Invalid&#8217; functions to our process: <img class="alignnone size-full wp-image-99" title="invalids loop" height="561" alt="invalids loop" width="707" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/invalids-loop.JPG" /> The &#8216;Loop Counter&#8217; activity has an activity-attribute: &#8216;Loop Limit&#8217;. This indicates the number of times the loop will be traversed. We stored the number of invalid objects in attribute &lsquo;Number of Invalid Objects&rsquo;. So we can use that to set the attribute on the process activity. <img class="alignnone size-full wp-image-100" title="loop_item_attribute" height="351" alt="loop_item_attribute" width="534" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/loop_item_attribute.JPG" /> The &lsquo;Loop Limit&rsquo; attribute is also stored in the &lsquo;WF_ITEM_ATTRIBUTE_VALUES&rsquo; table. But since it is an activity attribute, it has a name that is dynamically created. In this case the name is: &#8216;LOOP_COUNT:245195&#8242; where &#8216;LOOP_COUNT&#8217; is the first 10 characters of the activity name. And 245195 is the instance_id from the process_activity. Because it is not easy to query the attribute value (The name changes with different versions of the workflow, so we would need an initialization function to get the correct name), we created the &lsquo;COUNT_LOOP&rsquo; attribute. Only one thing is remaining now. The workflow engine needs to know how to handle the process activities when they are called in a loop. For this, there is an &lsquo;On Revisit&rsquo; field on the Detail tab of the process activity. Since &lsquo;LOOPCOUNTER&rsquo; is the first activity to be revisited, it is called the &lsquo;Pivot&rsquo; activity. The On Revisit property is only effective for this activity. Let&rsquo;s see what happens on the different settings for On Revisit. First we start with &lsquo;Loop&rsquo;. <img class="alignnone size-full wp-image-101" title="on_revisit_loop" height="196" alt="on_revisit_loop" width="311" src="http://www.stijf.com/wordpress/wp-content/uploads/2009/07/on_revisit_loop.JPG" /> Now when we run the workflow, we see the following in XXX_WF_INVALIDS:</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
ID	OWNER	OBJECT_NAME		OBJECT_TYPE
1	SYS	DBMS_SUMADV		PACKAGE BODY
2	ORDSYS	ORDIMGEXTCODEC_PKG		PACKAGE BODY
3	ORDSYS	ORDIMG_PKG		PACKAGE BODY
4	CTXSYS	DRIDISP			PACKAGE BODY
5	CTXSYS	CTX_DOC			PACKAGE BODY
6	APPS	ECE_PO_ARCHIVE_PKG		PACKAGE BODY
7	APPS	PA_MC_CURRENCY_PKG		PACKAGE BODY
8	APPS	CN_FORMULA_GEN_PKG		PACKAGE BODY
9	APPS	AMS_DCF_DEFAULT_VALUES	PACKAGE BODY
10	APPS	AMS_DCF_TITLE		PACKAGE BODY</pre>
<p></code>&nbsp; So the workflow has ran as expected. We can verify this in wf_item_activity_statuses: &nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
ITEM_TYPE	KEY	INSTANCE_LABEL	FUNCTION			BEGIN_DATE	END_DATE		STATUS    RESULT
DBA_TYPE	7	DBA_MAIN_PROCESS				22-7-2009 16:12:23	22-7-2009 16:12:23	COMPLETE	#NULL
DBA_TYPE	7	START		START			22-7-2009 16:12:23	22-7-2009 16:12:23	COMPLETE	#NULL
DBA_TYPE	7	INITIALIZE_FLOW	XXX_WF_DBA.init		22-7-2009 16:12:23	22-7-2009 16:12:23	COMPLETE COMPLETE
DBA_TYPE	7	COMPARETEXT	WF_STANDARD.COMPARE		22-7-2009 16:12:23	22-7-2009 16:12:23	COMPLETE	EQ
DBA_TYPE	7	CHECK_INVALIDS	XXX_CHECK_INVALIDS		22-7-2009 16:12:23	22-7-2009 16:12:23	COMPLETE	Y
DBA_TYPE	7	TRACK_FLOW-2	TRACK_FLOW_PROGRESS		22-7-2009 16:12:23	22-7-2009 16:12:23	COMPLETE
DBA_TYPE	7	GET_INVALIDS	XXX_WF_UTILS.get_invalids	22-7-2009 16:12:23	22-7-2009 16:12:23	COMPLETE
DBA_TYPE	7	LOOPCOUNTER	WF_STANDARD.LOOPCOUNTER	22-7-2009 16:12:23	22-7-2009 16:12:23	COMPLETE	EXIT
DBA_TYPE	7	AND		WF_STANDARD.ANDJOIN		22-7-2009 16:12:23	22-7-2009 16:12:23	COMPLETE	#NULL
DBA_TYPE	7	END-1		END			22-7-2009 16:12:23	22-7-2009 16:12:23	COMPLETE	#NULL</pre>
<p></code>&nbsp; You can see here that &#8216;GET_INVALIDS&#8217; and &#8216;LOOPCOUNTER&#8217; have been executed. But they are supposed to have been executed 10 times, right? Since the primary key of wf_item_activity_statuses is item_type, item_key, process_activity this information can not be recorded here. When a process_activity is called more than once, the original information from wf_item_activity_statuses is moved to a history table: &#8216;wf_item_activity_statuses_h&#8217;. I used the same query but now on &#8216;wf_item_activity_statuses_h&#8217; and I reduced the rowcount to 10. &nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
ITEM_TYPE	ITEM_KEY	INSTANCE_LABEL	FUNCTION	BEGIN_DATE	END_DATE	STATUS	RESULT
DBA_TYPE	7	LOOPCOUNTER	WF_STANDARD.LOOPCOUNTER	22-7-2009 16:21:43	22-7-2009 16:21:43	COMPLETE	LOOP
DBA_TYPE	7	PROCESS_INVALIDS	XXX_WF_UTILS.process_invalids	22-7-2009 16:21:43	22-7-2009 16:21:43	COMPLETE
DBA_TYPE	7	LOOPCOUNTER	WF_STANDARD.LOOPCOUNTER	22-7-2009 16:21:43	22-7-2009 16:21:43	COMPLETE	LOOP
DBA_TYPE	7	PROCESS_INVALIDS	XXX_WF_UTILS.process_invalids	22-7-2009 16:21:43	22-7-2009 16:21:43	COMPLETE
DBA_TYPE	7	LOOPCOUNTER	WF_STANDARD.LOOPCOUNTER	22-7-2009 16:21:43	22-7-2009 16:21:43	COMPLETE	LOOP
DBA_TYPE	7	PROCESS_INVALIDS	XXX_WF_UTILS.process_invalids	22-7-2009 16:21:43	22-7-2009 16:21:43	COMPLETE
DBA_TYPE	7	LOOPCOUNTER	WF_STANDARD.LOOPCOUNTER	22-7-2009 16:21:43	22-7-2009 16:21:43	COMPLETE	LOOP
DBA_TYPE	7	PROCESS_INVALIDS	XXX_WF_UTILS.process_invalids	22-7-2009 16:21:43	22-7-2009 16:21:43	COMPLETE
DBA_TYPE	7	LOOPCOUNTER	WF_STANDARD.LOOPCOUNTER	22-7-2009 16:21:43	22-7-2009 16:21:43	COMPLETE	LOOP
DBA_TYPE	7	PROCESS_INVALIDS	XXX_WF_UTILS.process_invalids	22-7-2009 16:21:43	22-7-2009 16:21:43	COMPLETE</pre>
<p></code>&nbsp; Now let&rsquo;s go back to the On Revisit property and set it to &#8216;Ignore&#8217;. After running the process we see: &nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
ITEM_TYPE	ITEM_KEY	INSTANCE_LABEL	FUNCTION			BEGIN_DATE	END_DATE	STATUS	RESULT
DBA_TYPE	8	DBA_MAIN_PROCESS				22-7-2009 16:27:23			ACTIVE	#NULL
DBA_TYPE	8	START		START			22-7-2009 16:27:23	22-7-2009 16:27:23	COMPLETE	#NULL
DBA_TYPE	8	INITIALIZE_FLOW	XXX_WF_DBA.init		22-7-2009 16:27:23	22-7-2009 16:27:23	COMPLETE COMPLETE
DBA_TYPE	8	COMPARETEXT	WF_STANDARD.COMPARE		22-7-2009 16:27:23	22-7-2009 16:27:23	COMPLETE	EQ
DBA_TYPE	8	CHECK_INVALIDS	XXX_CHECK_INVALIDS		22-7-2009 16:27:23	22-7-2009 16:27:23	COMPLETE	Y
DBA_TYPE	8	TRACK_FLOW-2	TRACK_FLOW_PROGRESS		22-7-2009 16:27:23	22-7-2009 16:27:23	COMPLETE
DBA_TYPE	8	AND		WF_STANDARD.ANDJOIN		22-7-2009 16:27:23			WAITING
DBA_TYPE	8	GET_INVALIDS	XXX_WF_UTILS.get_invalids	22-7-2009 16:27:23	22-7-2009 16:27:24	COMPLETE
DBA_TYPE	8	LOOPCOUNTER	WF_STANDARD.LOOPCOUNTER	22-7-2009 16:27:24	22-7-2009 16:27:24	COMPLETE	LOOP
DBA_TYPE	8	PROCESS_INVALIDS	XXX_WF_UTILS.process_invalids	22-7-2009 16:27:24	22-7-2009 16:27:24	COMPLETE</pre>
<p></code>&nbsp; That doesn&rsquo;t look too good. Our item is hanging, without any process_activities to be run. The last process to complete was &lsquo;Process_invalids&rsquo;. When it came back to &#8216;Loop Counter&#8217;, we told the engine to ignore the process. The engine could no longer determine where to continue, and stopped the processing. The last option is to set On Revisit to &#8216;Reset&#8217;. When we try that, we see that it completed the process again:</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
ITEM_TYPE	ITEM_KEY	INSTANCE_LABEL	FUNCTION			BEGIN_DATE	END_DATE		STATUS	RESULT
DBA_TYPE	9	DBA_MAIN_PROCESS				23-7-2009 9:21:45	23-7-2009 9:21:46	COMPLETE	#NULL
DBA_TYPE	9	START		START			23-7-2009 9:21:45	23-7-2009 9:21:45	COMPLETE	#NULL
DBA_TYPE	9	INITIALIZE_FLOW	XXX_WF_DBA.init		23-7-2009 9:21:45	23-7-2009 9:21:45	COMPLETE COMPLETE
DBA_TYPE	9	COMPARETEXT	WF_STANDARD.COMPARE		23-7-2009 9:21:45	23-7-2009 9:21:45	COMPLETE	EQ
DBA_TYPE	9	CHECK_INVALIDS	XXX_CHECK_INVALIDS		23-7-2009 9:21:45	23-7-2009 9:21:46	COMPLETE	Y
DBA_TYPE	9	TRACK_FLOW-2	TRACK_FLOW_PROGRESS		23-7-2009 9:21:46	23-7-2009 9:21:46	COMPLETE
DBA_TYPE	9	GET_INVALIDS	XXX_WF_UTILS.get_invalids	23-7-2009 9:21:46	23-7-2009 9:21:46	COMPLETE
DBA_TYPE	9	LOOPCOUNTER	WF_STANDARD.LOOPCOUNTER	23-7-2009 9:21:46	23-7-2009 9:21:46	COMPLETE	EXIT
DBA_TYPE	9	AND		WF_STANDARD.ANDJOIN		23-7-2009 9:21:46	23-7-2009 9:21:46	COMPLETE	#NULL
DBA_TYPE	9	END-1		END			23-7-2009 9:21:46	23-7-2009 9:21:46	COMPLETE	#NULL</pre>
<p></code>&nbsp; But you will see the difference between &lsquo;Reset&rsquo; and &lsquo;Loop&rsquo; when you check &#8216;XXX_TRACK_PROGRESS&#8217;: &nbsp; &nbsp;</p>
<div>&nbsp;</div>
<p><code></p>
<pre>
ID	ITEM_TYPE	ITEM_KEY	ACTIVITY_NAME	ACTIVITY_VERSION	PROCESS_NAME	INSTANCE_ID	INSTANCE_LABEL	FUNCMODE
87	DBA_TYPE	9	TRACK_FLOW	32	DBA_MAIN_PROCESS	245443	TRACK_FLOW-2	RUN
88	DBA_TYPE	9	TRACK_FLOW	32	DBA_MAIN_PROCESS	245453	TRACK_FLOW-1	RUN
89	DBA_TYPE	9	TRACK_FLOW	32	DBA_MAIN_PROCESS	245453	TRACK_FLOW-1	CANCEL
90	DBA_TYPE	9	TRACK_FLOW	32	DBA_MAIN_PROCESS	245453	TRACK_FLOW-1	RUN
91	DBA_TYPE	9	TRACK_FLOW	32	DBA_MAIN_PROCESS	245453	TRACK_FLOW-1	CANCEL
92	DBA_TYPE	9	TRACK_FLOW	32	DBA_MAIN_PROCESS	245453	TRACK_FLOW-1	RUN
93	DBA_TYPE	9	TRACK_FLOW	32	DBA_MAIN_PROCESS	245453	TRACK_FLOW-1	CANCEL
94	DBA_TYPE	9	TRACK_FLOW	32	DBA_MAIN_PROCESS	245453	TRACK_FLOW-1	RUN
95	DBA_TYPE	9	TRACK_FLOW	32	DBA_MAIN_PROCESS	245453	TRACK_FLOW-1	CANCEL
96	DBA_TYPE	9	TRACK_FLOW	32	DBA_MAIN_PROCESS	245453	TRACK_FLOW-1	RUN</pre>
<p></code>&nbsp; I limited the output to 10 records. But as you can see, a new funcmode showed up. Every time the &#8216;LOOPCOUNTER&#8217; was revisited, it reran the process activities in the loop with a funcmode of &lsquo;Cancel&rsquo;. This is a feature that will allow you to undo the process activities, before they will be done again. That is just one of the reasons to always check the funcmode in your procedures! It may be clear that the &lsquo;Loop&rsquo; is the correct setting for the On Revisit property in this case. With that we end part 3 of our series. The next part will be about the workflow controls, like &lsquo;Defer&rsquo;, &lsquo;Wait&rsquo;, &lsquo;Wait for Flow&rsquo;, and external calls. &nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stijf.com/wordpress/2009/07/workflow-for-ebs-dbas-part-3/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
