Sunday, 24 April 2016

End BPM process instance on a given day of month

Use case:
We need to end (close) a process instance on a given day of month. To further complicate things let's assume that a day is not fixed. An admin should be able to change the end day without re-deploying the BPM project.

With 11gR1 (11.1.1.7) you can achieve the purpose by adding timer boundary event (make it the "Interrupting Event"), choosing a "Time Cycle" type and setting the boundary timer event to kick in, let's say every 12 hours. This does exactly what you want, but it kicks in every 12 hours and creates a task every 12 hours and closes it if the down the flow condition is not met.

Instead we can try another approach.

Set the timer type to "Time Cycle" and check the "Use Expression" attribute.

Write an expression, in my case it is:

oraext:query-database(concat('SELECT GET_CLOSURE_DURATION(', "'", bpmn:getDataObject('applicationDataDO')/ns:applicationId, "'" ,') NEXTDAY FROM dual'),false(),false(),'jdbc/DATASOURCE')

where get_closure_duration(applicationid in varchar2) is a function, which returns duration in the P1Y1DT1H1S - One year, one day, one hour and one second form. The function parameter applicationid is used to further fine tune the result.

Now we write the get_closure_duration function in a such a way, that it returns the duration. For demonstration purposes, I set the end date to be 7th day of the next month at 10 PM:

FUNCTION GET_CLOSURE_DURATION(
    APPLICATIONID VARCHAR2)
  RETURN VARCHAR2
IS
  NEXTDAY   NUMBER;
  NEXTDATE  DATE;
BEGIN
NEXTDAY:=7;
NEXTDATE:=TRUNC(LAST_DAY(SYSDATE) +NEXTDAY) + 22/24;

RETURN 'P'|| TRUNC(months_between(NEXTDATE, SYSDATE) /12) ||'Y'|| mod(TRUNC(months_between(NEXTDATE, SYSDATE)),
  12) ||'M'|| TRUNC(NEXTDATE-SYSDATE) || 'DT'|| TRUNC(mod((NEXTDATE-SYSDATE) *24, 24)) ||'H'|| TRUNC(mod((NEXTDATE  -  SYSDATE) *24*60, 60)) ||'M';

END GET_CLOSURE_DURATION;

E.g, to close the application on 07-MAY-16 at 22:00 the GET_CLOSURE_DURATION function gave P0Y0M6DT7H14M when run at 24-APR-16 14:45.


Friday, 4 March 2016

JDeveloper is slow on Windows 10

Yeah, it is slow! The version of JDeveloper is Studio Edition Version 12.2.1.0.0, OS is Windows 10 Pro with 16 GB RAM, core i7 CPUs and still it is slow.
I have done all the possible tweaks, none helped so far.

The same works with no delays on Windows Server Standard (although not certified), no issues with the performance....

An update.
Finally, after I have changed some BIOS settings, namely disabled "Intel Speed Step" as well as "C-States Control" settings the JDeveloper now works at acceptable performance levels. I have to admit, that there still some random freezes, but this may be related to  the project itself.

Because of lack of time, I have not checked which setting out of two has made such a significant change.

Thursday, 3 March 2016

JDeveloper 12c (12.2.1) and MySQL database

I have started a project where I plan to use MySQL (
5.7.11, community edition) as a data backend. I use JDeveloper 12c (12.2.1.0) to develop model, UI etc.
The first problem I faced is that JDeveloper does not correctly add associations (therefore, create viewlinks) between tables. This is described in this entry https://java.net/jira/browse/ADFEMG-142 but for earlier versions of JDeveloper and MySQL, but the issue is reproduced in JDeveloper 12c as well.
Unfortunately, the reported bug has no resolution. To resolve the issue, I had to manually create or adjust the associations and viewlinks.

Wednesday, 20 January 2016

How to set maximum file upload size in BPM Human Task

To change the maximum upload size of a file, you need to make changes to web.xml file.
Here are the changes which need to be introduced.

.

Friday, 15 January 2016

How to set current user in BPM tasks

To obtain current user in BPM tasks (especially when page fragments are being used) you can follow below approach.

1. First create the following java class, the current user's login is stored in HttpSession (this survives AM activation/passivation events).

package home.common.ext;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;

import javax.servlet.http.HttpSession;

import oracle.bpel.services.workflow.WorkflowException;
import oracle.bpel.services.workflow.client.IWorkflowServiceClient;
import oracle.bpel.services.workflow.query.ITaskQueryService;
import oracle.bpel.services.workflow.verification.IWorkflowContext;
import oracle.bpel.services.workflow.worklist.adf.ADFWorklistBeanUtil;

import oracle.jbo.client.Configuration;


public class ContextUtils {
    public ContextUtils() {
        super();
    }

    public void initializeUserContextData(String applicationId) {
        FacesContext context = FacesContext.getCurrentInstance();
        ExternalContext ectx = context.getExternalContext();
        HttpSession userSession = (HttpSession)ectx.getSession(true);
        try {
            String ctx =
                (String)context.getApplication().evaluateExpressionGet(context,
                                                                       "#{pageFlowScope.bpmWorklistContext}",
                                                                       String.class);
            IWorkflowServiceClient workflowSvcClient =
                ADFWorklistBeanUtil.getWorkflowServiceClient();
            ITaskQueryService wfQueryService =
                workflowSvcClient.getTaskQueryService();
            IWorkflowContext wfContext;
            String userName = "";
            try {
                wfContext = wfQueryService.getWorkflowContext(ctx);
                userName = wfContext.getUser();
            } catch (WorkflowException e) {
                e.printStackTrace();
            }
            if (userName != null) {
                userSession.setAttribute("currentUserLogin", userName);
                System.out.println("Current user's login from workspace is: " +
                                   userName);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2. Create data control from this class (Just right click on this class and choose "Create Data Control" from the context menu);

3. Add this method as a default activity into the Task UI project.

4. The value is also available in Model project, so you can populate your WHO columns with current user login.



How to update programmatically a BPM task acquired by field

To update a BPM task programmatically one can leverage BPM APIs. Here is a short example to update an acquired by field.
Find tasknumber, for this you need to issue a select query against WFTASK table.

E.g, let's find latest task for a given instance Id:

select taskNumber, acquiredBy from wftask
where compositeInstanceId=(instance Id)
order by taskNumber desc;

The java code itself. To have this working to you have to complete the prerequisite steps.
I forgot to mention, that this is tested with BPM 11.1.1.7.8.

package home.generic;

import java.util.HashMap;
import java.util.Map;

import oracle.bpel.services.workflow.StaleObjectException;
import oracle.bpel.services.workflow.WorkflowException;
import oracle.bpel.services.workflow.client.IWorkflowServiceClient;
import oracle.bpel.services.workflow.client.IWorkflowServiceClientConstants;
import oracle.bpel.services.workflow.client.WorkflowServiceClientFactory;
import oracle.bpel.services.workflow.query.ITaskQueryService;
import oracle.bpel.services.workflow.task.ITaskService;
import oracle.bpel.services.workflow.task.model.SystemAttributesType;
import oracle.bpel.services.workflow.task.model.Task;
import oracle.bpel.services.workflow.verification.IWorkflowContext;


public class UpdateTaskAssignees {
    public static void main(String[] args) throws WorkflowException,
                                                  StaleObjectException {
        String userid = "username";
        String password = "password";
        String serverUrl =
            "t3://localhost:8001"; // host:port of the soa server
        Map connProperties =
            new HashMap();
        connProperties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.CLIENT_TYPE,
                           WorkflowServiceClientFactory.REMOTE_CLIENT);
        connProperties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_PROVIDER_URL,
                           serverUrl);
        connProperties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_INITIAL_CONTEXT_FACTORY,
                           "weblogic.jndi.WLInitialContextFactory");
        IWorkflowServiceClient wfSvcClient =
            WorkflowServiceClientFactory.getWorkflowServiceClient(connProperties,
                                                                  null, null);
        IWorkflowContext ctx =
            wfSvcClient.getTaskQueryService().authenticate(userid,
                                                           password.toCharArray(),
                                                           "jazn.com");
        ITaskQueryService querySvc = wfSvcClient.getTaskQueryService();
        ITaskService taskSvc = wfSvcClient.getTaskService();
        // Get task by its tasknumber
        Task currentTask = querySvc.getTaskDetailsByNumber(ctx, 239120);
        SystemAttributesType types = currentTask.getSystemAttributes();
        System.out.println("Currently task is acquired by: " +
                           types.getAcquiredBy());
        types.setAcquiredBy("new_login");
        currentTask.setSystemAttributes(types);
        taskSvc.updateTask(ctx, currentTask);
    }
}

Saturday, 19 January 2013

It is not supported, but if someone would like to do this, then this may save some time.

The customer asked if it is possible to resize pei_information_x fields in the per_people_extra_info table of Oracle E-Business Suite. I am not sure why, but this columns has limited field size of 150 bytes. It is business requirement to have things which is larger (especially in the multibyte env) than the allowed field sizes.

So we issued a simple command:
alter table HR.PER_PEOPLE_EXTRA_INFO modify (PEI_INFORMATION10  VARCHAR2(1024));

But when you use the following api to load data

DECLARE
  l_person_extra_id       NUMBER;
  l_err_api               VARCHAR2(1000);
  l_object_version_number NUMBER;
  l_validate              BOOLEAN DEFAULT TRUE;
  CURSOR cPersons
  IS
    (
      SELECT   *
        FROM Migration.UDK
        WHERE Person_Id IS NOT NULL
          AND Status    IS NULL
    )
  ;
BEGIN
  --hr_utility.set_trace_options ('TRACE_DEST:DBMS_OUTPUT');
  --hr_utility.trace_on;
  FOR cvPersons IN cPersons
  LOOP
    BEGIN
      hr_person_extra_info_api.create_person_extra_info( P_VALIDATE =>
      l_validate, P_PERSON_ID => cvPersons.Person_Id, P_INFORMATION_TYPE =>
      'TEST', P_PEI_INFORMATION_CATEGORY => 'TEST', P_PEI_INFORMATION1 =>
      cvPersons."bla bla", P_PEI_INFORMATION10 => cvPersons."Large keywords",
      P_PERSON_EXTRA_INFO_ID => l_person_extra_id, P_OBJECT_VERSION_NUMBER =>
      l_object_version_number);
      COMMIT;
      --hr_utility.trace_off;
      BEGIN
        UPDATE Migration.UDK
          SET Status      = 'Processed'
          ,Err_Msg        =NULL
          WHERE Person_Id = cvPersons.Person_Id;
      EXCEPTION
      WHEN OTHERS THEN
        NULL;
      END;
      COMMIT;
    EXCEPTION
    WHEN OTHERS THEN
      l_err_api := SQLERRM;
      UPDATE Migration.UDK
        SET Status      = 'Error'
        ,ERR_MSG        = l_err_api
        WHERE Person_Id = cvPersons.Person_Id;
      COMMIT;
    END;
  END LOOP;
  IF l_person_extra_id IS NOT NULL THEN
    dbms_output.put_line('person_extra_id '||l_person_extra_id);
  END IF;
END;

It ends with ORA-06502: PL/SQL: numeric or value error: character string buffer too small error. This is because before insert, the values are stored in the g_rec_type record datatype, which defines those fields to be of VARCHAR2(150). This record datatype is defined in pe_pei_shd package definition.