Question
· May 16

Business Process BPL context: objects deleted after CALL

I'm creating a Business Process that:

  1. Transforms one object into another using DTL
  2. Sends this object to an operation
  3. Transform the object obtained in first point into another one using DTL
  4. Sends the second object to an operation

The Business Process is created with BPL, and objects are stored in BP context. When I execute this Process, in the 3rd point the object obtained in first transformation doesn't exist. It's empty.

I have tried to make transformation before making CALL's, I mean:

  1. Transforms one object into another using DTL
  2. Transform the object obtained in first point into another one using DTL
  3. Sends first object to an operation
  4. Sends the second object to an operation

And after sending the first object, the second doesn't exist.

I thought it was a bug from new version 2025.1, but I have tested it on 2024.3 and it happens the same.

I have created a Business Process with code and it works perfectly fine, so I think the problem is with the context.

Is this a bug? Or is an expected functionallity?

Product version: IRIS 2025.1
$ZV: IRIS for UNIX (Ubuntu Server LTS for ARM64 Containers) 2025.1 (Build 223U) Tue Mar 11 2025 18:22:49 EDT [Health:8.2.2]
Discussion (6)0
Log in or sign up to continue

I reuse object that I get on first transformation and I transform it to another object, after a call.

This is the BPL that I use:

Class test.bp.TestProcess Extends Ens.BusinessProcessBPL
{

Storage Default
{
<Type>%Storage.Persistent</Type>
}

/// BPL Definition
XData BPL [ XMLNamespace = "http://www.intersystems.com/bpl" ]
{
<process language='objectscript' request='Ens.Request' response='Ens.Response' height='2100' width='2000' >
<context>
<property name='dataRsp' type='test.msg.GetDataResponse' instantiate='0' />
<property name='patient' type='HS.FHIR.DTL.vR4.Model.Resource.Patient' instantiate='0' />
<property name='fhirRsp' type='test.msg.SendResponse' instantiate='0' />
<property name='otherPatient' type='HS.FHIR.DTL.vR4.Model.Resource.Patient' instantiate='0' />
</context>
<sequence xend='200' yend='800' >
<code name='LOGASSERT 1' xpos='200' ypos='250' >
<![CDATA[ $$$LOGASSERT("1. Get data")]]>
</code>
<call name='GetData' target='TestBo' async='0' xpos='200' ypos='350' >
<request type='test.msg.GetDataRequest' >
</request>
<response type='test.msg.GetDataResponse' >
<assign property="context.dataRsp" value="callresponse" action="set" languageOverride="" />
</response>
</call>
<code name='LOGASSERT 2' xpos='335' ypos='600' >
<![CDATA[ $$$LOGASSERT("2. Transform to FHIR")]]>
</code>
<transform name='Transform 1' class='test.dtl.DataToPatient' source='context.dataRsp' target='context.patient' xpos='335' ypos='700' />
<code name='LOGASSERT 3' xpos='335' ypos='800' >
<![CDATA[ $$$LOGASSERT("3. Send to FHIR Repo 1")]]>
</code>
<call name='Send to FHIR Repo 1' target='TestBo' async='0' xpos='335' ypos='900' >
<request type='test.msg.SendRequest' >
<assign property="callrequest" value="##class(test.Util).generatePostFHIRReq(context.patient)" action="set" languageOverride="" />
</request>
<response type='test.msg.SendResponse' >
</response>
</call>
<code name='LOGASSERT 4' xpos='470' ypos='1150' >
<![CDATA[ $$$LOGASSERT("4. Transform to FHIR again (for other repo)")]]>
</code>
<!-- <transform name='REPEAT: Transform 1' class='test.dtl.DataToPatient' source='context.dataRsp' target='context.patient' xpos='335' ypos='700' /> -->
<transform name='Transform 2' class='test.dtl.DataToOtherPatient' source='context.patient' target='context.otherPatient' xpos='470' ypos='1350' />
<code name='LOGASSERT 5' xpos='470' ypos='1450' >
<![CDATA[ $$$LOGASSERT("5. Send to FHIR Repo 2")]]>
</code>
<call name='Send to FHIR Repo 2' target='TestBo' async='0' xpos='470' ypos='1550' >
<request type='test.msg.SendRequest' >
<assign property="callrequest" value="##class(test.Util).generatePostFHIRReq(context.otherPatient)" action="set" languageOverride="" />
</request>
<response type='test.msg.SendResponse' >
</response>
</call>
</sequence>
</process>
}

}

There is a commented line. If I activate this line (basically, repeat first transformation), then everything works.

The generatePostFHIRReq method:

ClassMethod generatePostFHIRReq(fhir As HS.FHIR.DTL.vR4.Model.Base.DomainResource) As test.msg.SendRequest
{
	#dim fhirReq As test.msg.SendRequest
	
	set fhirReq = ##class(test.msg.SendRequest).%New()
	set fhirReq.url = "/"_fhir.resourceType
	set fhirReq.body = ##class(%Stream.GlobalCharacter).%New()
	do fhirReq.body.Write(fhir.ToJSON().Read())
	
	return fhirReq
}

What I need is to send a FHIR resource to 2 different FHIR repositories. And for the second one I need to change some properties from de first transformation. I want to reuse the object obtained in first transformation, because sometimes the resource fot both FHIR repositories is the same.

HS.FHIR.DTL.vR4.Model.Resource.Patient is a registered object and not a persistent object, so there's no guarantee it will exist beyond a current BP State. You set context.patient in "Transform 1", so it will be gone from process memory after "Send to FHIR Repo 1" sends the request, but before it gets the reply back.

As a solution you can serialize FHIR resource to json and persist that.

You're welcome. Here's a bit more info about how BPL BPs work. After you compile a BPL BP, two classes get created into the package with the same name as a full BPL class name:

  • Thread1 class contains methods S1, S2, ... SN, which correspond to activities within BPL
  • Context class has all context variables and also the next state which BPL would execute (i.e., S5)

Also BPL class is persistent and stores requests currently being processed.

BPL works by executing S methods in a Thread class and correspondingly updating the BPL class table, Context table, and Thread1 table where one message "being processed" is one row in a BPL table. After the request is processed, BPL deletes the BPL, Context, and Thread entries. Since BPL BPs are asynchronous, one BPL job can simultaneously process many requests by saving information between S calls and switching between different requests.
For example, BPL processed one request till it got to a sync activity - waiting for an answer from BO. It would save the current context to disk, with %NextState property (in Thread1 class) set to response activity S method, and work on other requests until BO answers. After BO answers, BPL would load Context into memory and execute the method corresponding to a state saved in %NextState property.

That's why registered objects as context properties can (and would) be lost between states - as they are not persisted.