Thursday, 11 July 2013

Human Task : maximum escalation level

PROBLEM STATEMENT:
------------------
While designing the human work flow task in JDeveloper to configure the
deadlines TAB. we configure the maximum escalation level as 3.
But, this maximum escalation level is applicable for the entire task flow
duration.
Customer would like to set the maximum escalation level at the stage
participant configuration of assignment TAB. 


EXPLANATION:
-----------
This behavior is expected.

The "escalation policy" is applicable to the task as a whole and not for individual participants.

This can be confirmed by checking the Routing Slip of the Human Task
(HumanTask1.task , from Jdev source mode)
 <globalConfiguration>
<escalationPolicy renewalDurationType="STATIC" renewalDuration="PT2M">
    <numberOfTimesEscalated type="STATIC">3</numberOfTimesEscalated>
</escalationPolicy>
 </globalConfiguration>

As can be seen the "numberOfTimesEscalated" is configured at the
globalConfiguration level.
(i.e) escalation policy is applicable for the whole Task.
Participant level escalation policy  is not implemented/supported.

Moreover , section 27.2.1.4 Task Deadlines in  link
http://docs.oracle.com/cd/E14571_01/integration.1111/e10224/bp_introhwf.htm
clearly states that the deadlines are associated with a task :
"Human workflow supports the specification of deadlines associated with a
task."

To clarify further ,
The "Escalation Level" set would determine the number of times the "task will
be escalated" , and is not applicable per/participant.

SOA : Configuration Wizard Known Issue

Environment :  11.1.1.6.0 / Linux.

Start the domain creation GUI with ./config.sh
Click next (to create a domain)
Check Oracle Service Bus OWSM Extension - 11.1.1.6 [Oracle_OSB1]
A popup comes up with Error CFGFWK-64072
Click OK, and Next

The problem happens and at this point, we would be unable to change the domain name
as cannot type anything at all.

The issue only happens when the config wizard is executed under a specific circumstance .
JDK1.7 - PUTTY - X-Server (on Windows) - Fails
JDK1.6 - PUTTY - X-Server (on Windows) - Works
JDK1.7 - VNC - Linux - Works
JDK1.7 - Windows - Works


We've confirmed that the same behavior can also be reproduced by executing
the config script in a vanilla WLS 10.3.6.0 environment.

FTP Adapter : BatchNotificationHandler

PROBLEM STATEMENT:
-------------------
BatchHandler Notifications do not appear to be working as expected and/or
documented in
http://docs.oracle.com/cd/E23943_01/integration.1111/e10231/adptr_file.htm

The batch notification callback configured via binding.jca property

<binding.jca config="DebatchingIn_ftp.jca">
<property name="batchNotificationHandler">
oracle.sample.SampleBatchCalloutHandler</property>
</binding.jca>

does not appear to be invoked.


SOLUTION:
---------
please make the following changes to the binding.jca in the SOA composite :
1. Property Name = batchNotificationHandler
2. Value = java://{custom_class}

(i.e)
<property name="batchNotificationHandler">
java://oracle.sample.SampleBatchCalloutHandler
</property>

With this changes , the batching notification call out custom class methods are being invoked.


On invoking the composite , the log  has the
following entries :
13/06/2012 11:13:37 AM
oracle.sample.SampleBatchCalloutHandler onInitiateBatch
INFO:  Entering onInitiateBatch
13/06/2012 11:13:37
AM oracle.sample.SampleBatchCalloutHandler onCompletedBatch
INFO:  Entering onCompletedBatch
13/06/2012 11:13:37
AM oracle.sample.SampleBatchCalloutHandler onCompletedBatch
INFO:  Exiting onCompletedBatch

SOA 10G : EJB transaction-timeout

Setting individual EJB transaction-timeout:
A. Unzip the ejb_ob_engine.jar file located under
SOA_ORACLE_HOME/j2ee/home/applications/orabpel
B. Modify the META-INF/orion-ejb-jar.xml file inside by changing
transaction-timeout on all the EJBs.
Set transaction-timeout to 3600
C. Rejar ejb_ob_engine.jar.
D. Rename
$SOA_Home\j2ee\oc4j_soa\application-deployments\orabpel\ejb_ob_engine
Restart the OC4J container.
[On restart application-deployments\orabpel\ejb_ob_engine will be recreated
with the modified transaction-timeout values]


2.Setting Global transaction-timeout
A. Remove all the transaction-timeout attributes from the
META-INF/orion-ejb-jar.xml file
(See the previous steps for accessing this file).
B. Change the transaction-timeout value in
SOA_ORACLE_HOME/j2ee/home/config/transaction-manager.xml
C. Restart the OC4J container.

10g BPEL retrieve statistics using custom java code

How to retrieve statistics details from the 10g BPEL Administration console by using custom java code.

Java Code :

import com.collaxa.common.util.Statistics;

import java.util.Properties;

import com.collaxa.xml.XMLHelper;

import com.oracle.bpel.client.BPELProcessId;
import com.oracle.bpel.client.IBPELDomainHandle;
import com.oracle.bpel.client.IBPELProcessHandle;
import com.oracle.bpel.client.IInstanceHandle;
import com.oracle.bpel.client.Locator;
import com.oracle.bpel.client.NormalizedMessage;
import com.oracle.bpel.client.delivery.IDeliveryService;
import com.collaxa.cube.fe.util.FormatUtils;

import com.oracle.bpel.client.util.WhereCondition;

import com.oracle.bpel.client.util.WhereConditionHelper;

import java.text.SimpleDateFormat;

import java.util.Calendar;
import java.io.*;

public class GetBPELStats {
    public static final String DATE_FORMAT_NOW = "dd.MM.yyyy HH:mm:ss:SSS";
   
    public GetBPELStats() {
    }

    public static void main(String[] args) {

        if ((args != null) & (args.length != 0)) {
            System.out.println("Usage: GetBPELStats");
            System.exit(1);
        } else {
            try {
                // properties in the classpath
                Properties props = new java.util.Properties();

                // read the properties file
                java.net.URL url =
                    ClassLoader.getSystemResource("context.properties");
                props.load(url.openStream());
                //System.out.println(props);

                String bpeldomain = props.getProperty("bpeldomain");
                if (bpeldomain == null) {
                    System.out.println("BPEL domain not specified in context.properties, assuming 'default'");
                    bpeldomain = "default";
                }

                String bpelpassword = props.getProperty("bpelpassword");
                if (bpelpassword == null) {
                    System.out.println("BPEL password not specified in context.properties, assuming ********");
                    bpeldomain = "welcome1";
                }

                // get a connection to the server
                Locator locator = new Locator(bpeldomain, bpelpassword, props);

                IBPELDomainHandle domain = locator.lookupDomain();
               
                System.out.println("GetBPELStats for SOA10g ");
                Calendar cal = Calendar.getInstance();
                SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_NOW);
                System.out.println("Date: " + sdf.format(cal.getTime()));
               
               
                System.out.println("===> Successfully connected to domain " +
                                   bpeldomain);
               
                Statistics[] asyncStats = domain.getAsyncStatistics();
               
                System.out.println("------------------------------");
                System.out.println("Async BPEL Domain Statistics:");
                for(int i = 0; i < asyncStats.length; i++)
                {
                    Statistics stat = asyncStats[i];
                    System.out.println((new StringBuilder()).append("<key name=\"").append(stat.getKey()).append("\"><stats count=\"").append(stat.getCount()).append("\" min=\"").append(stat.getMin()).append("\" max=\"").append(stat.getMax()).append("\" average=\"").append(stat.getAvg()).append("\"/></key>").toString());
                }
                if (asyncStats.length==0)
                {
                    System.out.println("There are no Async BPEL Domain Statistics:");
                }
                System.out.println("------------------------------");

               
                Statistics[] syncStats = domain.getSyncStatistics();
                System.out.println("------------------------------");
                System.out.println("Sync BPEL Domain Statistics:");
                for(int i = 0; i < syncStats.length; i++)
                {
                    Statistics stat = syncStats[i];
                    System.out.println((new StringBuilder()).append("<key name=\"").append(stat.getKey()).append("\"><stats count=\"").append(stat.getCount()).append("\" min=\"").append(stat.getMin()).append("\" max=\"").append(stat.getMax()).append("\" average=\"").append(stat.getAvg()).append("\"/></key>").toString());
               

                }
                if (syncStats.length==0)
                {
                    System.out.println("There are no Sync BPEL Domain Statistics:");
                }
                System.out.println("------------------------------");


                Statistics[] requestStats = domain.getRequestStatistics();
                System.out.println("------------------------------");
                System.out.println("BPEL Engine Requests Statistics:");
                for(int i = 0; i < requestStats.length; i++)
                {
                    Statistics stat = requestStats[i];
                    System.out.println((new StringBuilder()).append("<key name=\"").append(stat.getKey()).append("\"><stats count=\"").append(stat.getCount()).append("\" min=\"").append(stat.getMin()).append("\" max=\"").append(stat.getMax()).append("\" average=\"").append(stat.getAvg()).append("\"/></key>").toString());
                }
                if (requestStats.length==0)
                {
                    System.out.println("There are no PEL Engine Requests Statistics:");
                }
                System.out.println("------------------------------");


                //To clear the statistics from memory
                //domain.clearStatistics();


            } catch (Exception e) {
                System.out.println("Exception in GetBPELStats: " + e);
                e.printStackTrace();
            }
        }

    }

}



EXPLANATION:
------------------
The above class relies on BPELDomainHandle object and obtains the statistics using the
following API calls:
domain.getAsyncStatistics   : Async BPEL Domain Statistics
domain.getSyncStatistics    : Sync BPEL Domain Statistics
domain.getRequestStatistics : Engine Requests Statistics

The statistics are built and  stored in the memory using HashMaps.
The engine relies on the data in the HashMaps and calculates the statistics
at runtime.
When we clear the statistics , the data in the HashMaps are cleared.
To clear the statistics from memory using Java Code , we can use
-> domain.clearStatistics();

The "Statistics" page(jsp) in BPEL Administration page , uses the same API's
as GetBPELStats.java .In addition the JSP page formats the statistics are
presents them as a Tree(hierarchy) structure.

SOA : Tranasaction/Scope/Rollback

PROBLEM STATEMENT:
------------------
Transaction in a scope doesn't rollback when a fault is received from a partner link.

1.AsynchronousBPEL is 1-way async process which inserts records into test2 table
2.SynchronousBPEL is sync bpel process inserting records into test1 table,
transaction set to "required" which gets executed in same transaction from
parent
3.col1 on both tables will always have a constant value as "Test" assignedin BPEL
4.Col2 will have the input value passed from AsyncBPEL process 

After updating the test1 table with a duplicate entry, the test2 table
throws a key violation. This should cause the test1 table value to be
rolled back.

EXPLANATION:
---------
Though the user case reproduces the issue , the cause lies with the design of the BPEL process itself .

The CatchAll{} fault handler in the main scope of AsynchronousBPEL process prevents the fault/exception from bubbling up to the engine .
Since the fault is handled by the BPEL process  , the engine is unaware of the exception -
causing the transaction in SynchronousBPEL to be not rolled back.

This behavior can be verified by looking through the audit trail.
In the audit trail we will observe that the state of the SynchronousBPEL 
would remain as "Completed" meaning that , the transaction in SynchronousBPEL
 was never rolled back.

Until the BPEL engine is notified of the underlying exception , it cannot
ascertain the cause and rollback the dependent transactions.


To achieve this , the use case will have to "Rethrow" the fault from
CatchAll{} fault handler in the main scope of AsynchronousBPEL process.This
way , the engine will be notified of the exception and appropriate action
initiated(In this case ,transaction rollback of SynchronousBPEL).
Of course , this rethrow can happen after the required action has been
undertaken
(e.g) notification via email.
  <faultHandlers>
    <catchAll>
      <sequence name="Sequence1">
        <invoke name="Notify"/>    <!--  action on the fault -->
        <rethrow name="Rethrow"/>  <!-- throw back the exception to the
engine --->
      </sequence>
    </catchAll>
  </faultHandlers>


With this change in place , a failure in inserting data into
table test 2(Invoke2) would also result in rollback of the insert into table
test1.

OSB Adapter Framework : Execution Boundaries


Consider the following scenario where OSB projects use the same WM (WorkManager)
This WM has been defined with a max-threads-constraint of  <count>1</count>.

Customer has 2 AQ projects , however the second project fails to start up with exception:
<BEA-002936> <maximum thread constraint test.MaxThreadsConstraint is reached>

The customer's expectation is for both the projects to use individual WM threads.

The above expectation is incorrect.To understand the behavior , we should understand
the execution boundaries of  Adapter Framework:

PHASE 1: executed only once
The first execution comes into play only once , when the first Adapter proxy is activated.
During this phase , a conditional check is performed to confirm the state of the Adapter.
If the state is not "STARTED"(i.e) no previous proxy with AQAdapter  has been activated, the Adapter is initialized
with a BootStrap WorkManager and its state is updated to "STARTED".

This BootStrap WorkManager is then used by all AQ proxy services during dequeue activation.


PHASE 2: executed for each AQ proxy service
Following phase 1 , the second phase is executed wherein , the dequeue agent is activated using the WM from the first phase.
When the next AQ Adapter proxy is activated , the first phase is skipped since state is "STARTED" and the
execution enters the second phase where the dequeue agent is activated using the same WM (from the first phase)

As we can see , the adapter framework uses the same WorkManager for all the dequeue agents.


I've performed multiple tests to prove the above theory :
CASE 1:
Create 2 WM's - Project1.WorkManager & Project2.WorkManager
Create 2 MaxThreadConstraints - Project1.MaxThreadConstraint & Project2.MaxThreadConstraints
Associate
    Project1.MaxThreadConstraints to Project1.WorkManager
    Project2.MaxThreadConstraints to Project2.WorkManager

Create 2 Proxy Services - Project1.ProxyServices & Project2.ProxyServices
Associate
  Project1.ProxyServices with Project1.WorkManager
  Project2.ProxyServices with Project2.WorkManager

Enable Project1.ProxyServices , we should observe that the dequeue agent is activated
Enable Project2.ProxyServices , we should observe that the dequeue agent is not activated , and
the following WARNING message appears in the log
<BEA-002936> <maximum thread constraint Project1.MaxThreadConstraint is reached>


As we can see, the constraint being mentioned is Project1.MaxThreadConstraint
(i.e) the constraint belonging to the WM which was loaded first.

Without restarting the server deactivate Project2.ProxyServices
Modify the WM to point to "default"
Reactivate the proxy.
We will still observe that the dequeue agent is not activated 
      - this is because the WM used by the adapter is still Project1.WorkManager.
      - this also confirms that the runtime WM change to the proxy service has no effect
        on the underlying Adapter layer.

CASE 2:
Following CASE 1,deactivate both the proxies and  restart the server.
Activate Project2.ProxyServices (which uses the default WM)
Activate Project1.ProxyServices (which uses Project1.WorkManager)
Observe that both the dequeue agents are activated.

This is because , the first WM to be associted to the Adapter is "default" which does not have an MaxThreadConstraint set.
Even though Project1 refers to a custom WM , the Adapter would still end up using the  "default" WM.

POSSIBLE SOLUTION:
-----------------
Since we cannot rely on the order of proxy service (and hence the associated
WM) startup ,we should
create one custom WM with suitable MaxThreadConstraints value.
Associating all the proxies with the same custom WM should resolve the issue.

Email response from SOA Adapter base development :
yes , only one workmanager is handed down to the JCA framework as per design.