Friday, January 03, 2025

Oracle Integration Gen2 PCS to Gen3 OPA Upgrade: Part 8

In series of articles, I discuss some challenges you may face when upgrading Oracle Integration Gen2 (OIC 2) Process (PCS) to Gen3 (OIC 3) Oracle Process Automation (OPA) applications. In this episode I discuss a challenge with the Boundary Error event handling. The previous article can be found here.

Disclaimer: The below provides a snapshot at the time of writing. Things may have changed by the time you read it. It is also important to mention that this does not necessarily represent the view of Oracle.

Specifically for PCS to OPA migration you are also referred to the following:

In OIC Process there are two ways to handle faults with integrations:

·       Use the out-of-the-box fault policies (the default when activating),

·       Model fault handling and deactivate fault policies.

As I already argued in the article that describes the Custom Fault Handling Pattern [http://kettenisblogs.blogspot.com/2022/10/oic-structured-process-custom-fault.html], the first option is probably not what is required. Instead, you may prefer using an Error Boundary event and model explicit fault handling as part of the process flow.

This is what I have done in a small PCS application to demo the working. First, I have modeled a Business Type with the name Fault that has attributes to hold fault information:

 

Next to the pretty standard code, summary and detail attributes, I also have attributes for a (unique) businessId and a timestamp for when the fault occurred. I then created a Business Exception with name IntegrationFault, based on the Fault Business Type. I then used the Business Exception to throw, catch and handle any fault in the process, like I did in the below Reusable Subprocess (applying the Service Handler pattern as I first introduced in an article about describing how to do “multi-threading” in PCS [http://kettenisblogs.blogspot.com/2022/02/oic-parallel-gateway-and-multi.html]):

 

Not shown in detail, but one of the Boundary Errors catches the RemoteFault and the other the BindingFault. Both are then forwarded to an Error End event raising the IntegrationFault Business Exception. Mind, the Service Handler does not handle the exception itself, which therefore is thrown back to the calling process:

As you can see, the calling process has an Event Subprocess that catches all IntegrationFaults. Granted, a bit overcomplicated for such a simple process, but in a real process application you probably have 2 or more integration calls and soon you will appreciate the Service Handler and Custom Fault Handling patterns 😉.

To easily showcase the result, I have put a Human Task in the Fault Handler, displaying the fault details.

For an APIInvocationError for a Bad Request this looks like this:


And for a Not Found like this:


In case of a 401 Unauthorized or 403 Forbidden, it looks like this:


Mind, this won’t return the 401 but instead a 500, which makes sense as this fault is caused by using wrong credentials as configured in OIC, so there is no point for the consumer to try again until this has been fixed on the OIC side (hence the 500). However, the actual, useful information is dug down in [CDATA] and it is not trivial to return in a easy-to-read way.

In case of the Integration not being available this looks like this:


As you can see, in this case the error is returned (by OIC) as an HTML page.

 

The challenge

When migrating an application handling Business Exceptions from PCS to OPA, you will find that the specific Exception caught by the Boundary Error events is not supported by OPA. This implies that any PCS process with a Boundary Error event that is configured to catch a specific error, cannot be (in-place) upgraded and instead must be refactored / migrated. I did not check the situation where you chose to catch all business and system exceptions, assuming you make all this effort to catch the error as specific as possible to simplify analyzing issues (compare with Java Exception versus NullPointerException, NumberFormatException, etc.).

 

The remedy

The good news is that this challenge can be addressed by introducing (what even results in) a simplification of the process, as unlike OIC 2 with its RemoteFault and BindingFault, in case of OIC 3 you only need to catch a single Service Invocation Failure to handle both.

So, in this model that got invalid after the import:

I can delete one of the Boundary Errors events and configure the other to catch the Service Invocation Failure:


The original IntegrationFault Business Exception has been migrated, although the result is not exactly the same. Any Business Exception has a code, message, and details attribute. The first two are fixed, but for details you can choose what type it should have, and when migrating from OIC 2 this became the Fault Business Type I created before. That Fault already had a code and message attribute of its own but that cannot be used in mappings (as you can see in the next picture).

You will find that the data association also became invalid as all source fields are different:


Completely new are the errorName and details.payload and details.uri elements, of which the payload contains the detailed error information. Not to lose the errorName along the way, I added it to my custom Fault Business Type. So, the mappings can be remedied as follows:


I don’t need to fix the mapping to the Error End event as for me that is just fine. Note how the fault is mapped 1:1 to the input.errorInfo.details, as that attribute is based on the same Business Type. The only data I need to add to it, are the businessId and the timestamp.


So, now my Service Handler is done, and I also don’t need to reconfigure the Event Subprocess as that catches the IntegrationFault that was migrated as-is, with its mapping based on the Fault Business Type:

Now let’s see how that works out runtime. I will skip discussing the change I had to make to the UI, as that is just to showcase the result. As you saw before, on OIC 2 I handled the 400 Bad Request. That gives practically the same result on OIC 3:


A 404 Not Found thrown by the backend, for example because it can’t find an city having a specific id, can also be handled the same way (picture concerning a Service Request but the idea is the same):


But from here one it has changed to what I consider to be an improvement. A 401 Unauthorized raised by the Integration’s Trigger connection is returned as a Service Invocation Failure which I can handle as the same way as all other faults. Notice though that there are payload details because the Integration never started:


An Invoke to a back-end using wrong credentials results in a oracle.cloud.connector.api.CloudInvocationException. When returned to the calling process using Fault Return, this also results in a Service Invocation Failure, this time with details (because the Integration was started, and I used the mapping to the Fault Return to propagate the details):


Another improvement (at least in my opinion) is the way that a deactivated or non-deployed Integration call is handled. This now results in a 404 Not Found, instead of the 503 Service Unavailable that OIC 2 used to return. You might consider not showing the detailed message, as that gives away the Integration’s URI:


 

Previous articles on the subject:

 

Sunday, September 15, 2024

Oracle Integration Gen2 PCS to Gen3 OPA Upgrade: Part 7

In series of articles, I discuss some challenges you may face when upgrading Oracle Integration Gen2 (OIC 2) Process (PCS) to Gen3 (OIC 3) Oracle Process Automation (OPA) applications. In this episode I discuss a challenge you may face with task assignment to an individual (instead of the default role assignment) when migrating from Oracle OIC 2 Process (PCS) to OIC 3 Oracle Process Automation (OPA). The previous article can be found here.

Disclaimer: The below provides a snapshot at the time of writing. Things may have changed by the time you read it. It is also important to mention that this does not necessarily represent the view of Oracle.

Specifically for PCS to OPA migration you are also referred to the following:

 

In the following example I have 2 Human Tasks, of which 1 is assigned based on the swimlane, and the other by assigning it to a specific person:



The first task has assignment set to “Any member of current swimlane role”:


 

And the other has it set to some task payload element, which is TaskAssignmentForm.processRequest.id:


 

When running this process, in Workspace you can see the first being assigned to the process-specific role, which is SMP Task Assignment.ProcessOwner. The “(R)” at the end confirms the assignment is role-based:

 

The second task is assigned to me directly:



The Challenge

I can import, validate and activate the process without issues. Looking at the properties all looks good for the first task:


 

And the second one:


 


When running the process, the first task is assigned to the swimlane role, as it was for PCS:


However, the second one runs into an error


The message points out that the element with name input_taskAssignmentForm cannot be resolved.

On closer inspection of the design-time task assignment you can see it has been migrated to use input.TaskAssignmentForm.processRequest.id, which as such looks OK as it basically is the same element as it was for PCS:


 


However, the issue is that on assignment this element is not initiated yet. So, practically speaking this is a kind-of NullPointerException.


The Remedy

The resolution is quite simple, I should use an expression pointing to an element of the process payload itself instead, which in my case is called taskAssignmentFormDataObject:


 

When I now run the process, also the second task is assigned properly:


 

Note: in PCS you don’t have access to the process payload in the task assignment expression editor. So, there is no way I could have prevented this issue in PCS already.

Next articles on the subject:

Previous articles on the subject:

Wednesday, September 11, 2024

Oracle Integration Gen2 PCS to Gen3 OPA Upgrade: Part 6

In series of articles, I discuss some challenges you may face when upgrading Oracle Integration Gen2 (OIC 2) Process (PCS) to Gen3 (OIC 3) Oracle Process Automation (OPA) applications. In this episode I add to and partly correct the previous one where I talked about the correlation challenge. I also discuss challenges with the interface of the Message Start. The previous article can be found here.

Disclaimer: The below provides a snapshot at the time of writing. Things may have changed by the time you read it. It is also important to mention that this does not necessarily represent the view of Oracle.

For PCS to OPA migration you are also referred to the following:


Process Receive Interface

Last time I discussed how correlation of inter-process calls with a Send-Receive combination only works when you use a separate, simple type argument to correlate on.

As it turns out, for a Send-Receive combination the Receive does not support exposing an interface based on a (complex) Business Type at all. So, what I missed the previous time is that with a complex type argument the mapping failed completely as none of the request elements were mapped. The following explains the challenge in detail as well as the remedy.

In in the following example, I have a simple PCS process with a complex Business Type.

The following first (parent) process has a Send - Receive combination which is calling the second (child) process that has a Message Start and which does a call back to the first by means of a Send activity.

 


The Receive of the first process exposes an interface based on the Complex Type, which I can map 1:1. As explained in the previous article, correlation happens based on the person.id element.

In PCS this all works just fine.

The Challenge

I can import and activate the process in OPA without getting a validation error. Already knowing that correlation only works when I have a simple type id in the payload, I already fixed that. However, when I run the process the mapping of the Receive fails "silently" meaning I don’t get a runtime error, but instead of mapping the approvedPerson input argument back to the person data object, it has mapped what appears to be (part of) the JSON schema of the input argument. This is what I literally get back from the mapping:

{number=false, boolean=false, null=false, string=false, array=false, dataFormatName=application/json, nodeType=OBJECT, value=false, object=true}

For most processes this probably makes it fail in the next step as the business object got corrupted, but you only find out runtime.

The Remedy

The remedy basically is the same as for the correlation challenge: use root-level, simple types. Meaning, that you must create an interface where each data element to pass on, is at the root-level:

 

And (obviously) you must map each element individually in both the Send back to the child process as well as in the Receive of the parent:

 

Message Start Interface

All PCS Structured Processes I have designed with a Message Start use a complex Business Type. Main reason being that for PCS this can make the input mapping easier, as it supports mapping of the parent element only, which makes it simple and prevents the need to cater for empty elements. 

I also normalize the data structure. For example, instead of having a flattened data structure with top-level elements name, streetName, houseNumber, postalCode and city, I have an interface with at top-level name and address, and then streetName, etc. as child elements of the address element based on a separate Address Business Type. This does not only help to better understand the structure and usage of the interface, but also makes it more resilient to change. 

For example, to add a correspondence address, I can easily reuse the Address Business Type to add a correspondenceAddress next to address (instead of having to work around it by adding correspondenceStreetName, etc.

The Challenge

Currently, OPA only supports a Message Start with either simple type, root-level arguments or based on a complex Business Type of 1 level deep. So, in the example mentioned before I still must use top-level elements for streetName, and all the others including the array.

The following shows the interface of a dummy application I created in PCS and then migrated to OPA to verify that indeed the results I had with a real-live migrated application was caused by this.

First, I tried with a more complex type having a sub-element based on a SubType Business Type of 2 elements, and another sub-element that is an array of a Project Business Type with 1 element.


When I try to start an instance of this process using this request:

{
  "params": {
    "applicationName": "SMPMessageStartComplexRequest",
    "processName": "SMPMessageStartComplexRequest"
  },
  "dataObject": {
    "complexTypeIn": {
      "name": "john",
      "subType": {
        "approve": "yes",
        "reject": "no"
      },
      "projects": [
        {
          "projectNumber": "1"
        },
        {
          "projectNumber": "2"
        }
      ]
    }
  }
}

The result is an empty mapping:

 

The Remedy

The challenge can be remedied by flattening the request so that the approve and reject elements are at the same level as name as is the list with projects.


When I start an instance of this, using the request:

{
  "params": {
    "applicationName": "SMPMessageStartComplexRequest",
    "processName": "SMPMessageStartSimpleRequest"
  },
  "dataObject": {
    "nameIn" : "John",
    "simpleTypeIn": {
      "approve": "yes",
      "reject": "no"
    },
    "projectsIn": [
      {
        "projectNumber": "1"
      },
      {
        "projectNumber": "2"
      }
    ]
  }
}

The result is as I would expect it to be:





Of course, internally I can map the individual input arguments to a data objects that is based on the complex Business Type.

Next articles on the subject:

Previous articles on the subject: