Enrico Parisi · Jun 2, 2025 go to post

That won't work, you are just setting a %Net.HttpRequest object in a (private) variable called HttpRequest that is destroyed when the method ends.

You need to set/change the %Net.HttpRequest instance contained in the ..%Client property of the adapted AFTER the web service client class is set/instantiated in ..%Client.

Did you had a chance to test the modified adapter I've posted? Maybe tomorrow I'll have time to test it.

Enrico Parisi · Jun 2, 2025 go to post

You can extend EnsLib.SOAP.OutboundAdapter and add support for LocalInterface and use the "new" adapter in your BO.

With "inspiration" 😁 from EnsLib.HTTP.OutboundAdapter, something like:

Class Community.adapter.SoapOutbound Extends EnsLib.SOAP.OutboundAdapter
{

/// In a multi-homed system, specify which network interface the TCP connection should go through.  An empty value means to use any interface. <br/>
/// To be able to bind to IPv6 interfaces you may need to enable IPv6 in your instance.  This is done in the System Management Portal under 
/// System Administration > Configuration > Additional Settings > Startup, by editing the IPv6 setting.
Property LocalInterface As %String(MAXLEN = 250);
Parameter SETTINGS = "LocalInterface:Connection:selector?context={Ens.ContextSearch/TCPLocalInterfaces}";
Method OnInit() As %Status
{
	Set ..%Client.HttpRequest=##class(%Net.HttpRequest).%New()
	Set ..%Client.HttpRequest.LocalInterface=$ZStrip($P(..LocalInterface,"("),"*W")
	Return ##super()
}

}

I did not tested it, try it and let us know.

Nete, if for any reason you reinstantiate ..%Client.HttpRequest, then you need to set the LocalInterface  property.

Having LocalInterface support in EnsLib.SOAP.OutboundAdapter "out of the box" can be worth an entry in the ideas portal.

Enrico Parisi · Jun 2, 2025 go to post

$query argument can be a global or a public variable, so when using local variable array change the method signature:

ClassMethod test(array) [ public= array ]

Or use a temp global or, even better, a Process Private Global instead.

Enrico Parisi · May 29, 2025 go to post

In the...good old days, we used to do that in one line 😂

s node="array" f  s node=$q(@node) q:node=""  w node,"=",@node,!

array(1)=1
array(1,1)=1,1
array(1,1,1)=1,1,1
array(1,1,1,1)=1,1,1,1
array(1,1,2)=1,1,2
array(1,1,3)=1,1,3
array(2)=2
array(2,1)=2,1
array(2,1,1)=2,1,1

Enrico Parisi · May 28, 2025 go to post

If I recall correctly I had a similar issue in some old Ensemble system but I'm not 100% sure the global was really that.

If you can stop the production, then I think it's safe to kill ^Ens.AppData, with the production running I don't think killing it it's a good idea.

A curiosity, do you use EnsLib.SQL.Snapshot class in some SQL host (BO)?
Is the content of ^Ens.AppData somewhat related to EnsLib.SQL.Snapshot?
Looking at the content of ^Ens.AppData, what type of adapter/operation is likely using it?

Enrico Parisi · May 27, 2025 go to post

When I was migrating a system I had to export and import SQL Gateway Connection, so in the source system I exported to a tab delimited file and in the target system I imported the definitions.

The code to create the imported definition is:

	Set SQLConnection=##class(%SQLConnection).%New()
	Set SQLConnection.DSN=$p(line,tab,1)
	Set SQLConnection.Name=$p(line,tab,2)
	Set SQLConnection.ReverseOJ=$p(line,tab,3)
	Set SQLConnection.URL=$p(line,tab,4)
	Set SQLConnection.Usr=$p(line,tab,5)
	Set SQLConnection.bUnicodeStream=$p(line,tab,6)
	Set SQLConnection.classpath=$p(line,tab,7)
	Set SQLConnection.driver=$p(line,tab,8)
	Set SQLConnection.isJDBC=$p(line,tab,9)
	Set SQLConnection.needlongdatalen=$p(line,tab,10)
	Set SQLConnection.noconcat=$p(line,tab,11)
	Set SQLConnection.nodefq=$p(line,tab,12)
	Set SQLConnection.nofnconv=$p(line,tab,13)
	Set SQLConnection.nvl=$p(line,tab,14)
	Set SQLConnection.properties=$p(line,tab,15)
	Set SQLConnection.pwd=$p(line,tab,16)
	Set SQLConnection.useCAST=$p(line,tab,17)
	Set SQLConnection.useCASTCHAR=$p(line,tab,18)
	Set SQLConnection.useCOALESCE=$p(line,tab,19)
	Set SQLConnection.xadriver=$p(line,tab,20)
	Set sc=SQLConnection.%Save()
Enrico Parisi · May 25, 2025 go to post

If you want/need your code to handle ANY situation (JSON size), then you should use the first option.

Let's try with the second option:

USER>Set Body={}.%FromJSONFile("c:\temp\BigSample.json")
 
USER>Set Request = ##class(%Net.HttpRequest).%New()
 
USER>Set Request.Server = "server"
 
USER>Set Request.Location = "location"
 
USER>Set Request.ContentType = "application/json"
 
USER>Do Request.EntityBody.Write(Body.%ToJSON())
DO Request.EntityBody.Write(Body.%ToJSON())
^
<STRINGSTACK>
USER>

As you see, it does not work. Now the first option:

SER>Set Body={}.%FromJSONFile("c:\temp\BigSample.json")
 
USER>Set Request = ##class(%Net.HttpRequest).%New()
 
USER>Set Request.Server = "server"
 
USER>Set Request.Location = "location"
 
USER>Set Request.ContentType = "application/json"
 
USER>Do Body.%ToJSON(Request.EntityBody)
 
USER>Write Request.EntityBody.Size
18378462
USER>Write ##class(%File).%New("c:\temp\BigSample.json").Size
50273869
USER>

Curiosity: the difference between the original json file size and stream loaded into Request.EntityBody stream is due to the fact that the file is formatted with indents while the json exported stream is "compact" (minified) with no indents and new lines.

Enrico Parisi · May 24, 2025 go to post

This is new info, you did not mention this in your original question.

Can you provide some context on your implementation?

Would you mind to share the code you are using to save "data inside response.Data"?

What class is "response"?

Enrico Parisi · May 23, 2025 go to post

From the example in the first post it seems the stream is not a property of an Ens.Message, that's why is not purged.

Enrico Parisi · May 22, 2025 go to post

You explicitly saved the stream using CopyFromAndSave(), so it will persist (on disk) until it's deleted.

It's like saving a persistent class, it persist even when the code has finished executing. Fortunately! ☺

What's the point of saving a stream that you don't want to persist?

Enrico Parisi · May 21, 2025 go to post

What namespace are you logged when you try to create that routine?

Maybe in your namespace the %Test (or %Test*, or...) is mapped to a read only database?

Otherwise you think you are authenticated as superuser but in fact you are not.

Enrico Parisi · May 21, 2025 go to post

In your generated (from wsdl) SOAP client class change the LOCATION class parameter with correct url including port number.

Another option is to set the Location property in your code when you use the client class, somthing like:

Set wsClient=##class(your.generated.SoapClient).%New()
Set wsClient.Location="https://path.to.server:NNNN/path.to.service"
Set client.SSLConfiguration ="SSLConfig"

Enrico Parisi · May 20, 2025 go to post

It looks like there is some disk I/O difference between the two systems.

How does the two systems storage (disk, controller etc.) compares?

Are the 2 systems equally configured in terms available RAM and of global buffers?

Is the shared memory using large pages? (check messages.log during startup)

I'd monitor and compare disk usage and I/O while running.

Enrico Parisi · May 16, 2025 go to post

I had a similar issues with IRIS 2021.2.

In one case a class with an object reference calculated property failed to compile.
The workaround was to add %JSONINCLUDE = "OUTPUTONLY" to the property.

The second case was a class that contains a property (not calculated) that is an array of %Stream.GlobalBinary (that was not supported) and despite I added %JSONINCLUDE = "NONE" the class did not compile.
For this I got a quick fix modifying a library class.

My suggestion is first to to reproduce the issue with a VERY SIMPLE test case and test it using latest IRIS version 2025.1 and, whatever the test result is, report it to WRC including the result of your test using the latest IRIS version.

Enrico Parisi · May 16, 2025 go to post

05/16/25-15:43:17:397 (11856) 2 Failed to allocate 882MB shared memory: 411MB global buffers, 300MB routine buffers
05/16/25-15:43:17:398 (11856) 2 Unable to allocate shared memory minimum of 128MB

 

It must be a very small system!

Enrico Parisi · May 13, 2025 go to post

Maybe you are sending the HTTP request using an incorrect Content-Type HTTP header?

Check what Content-Type HTTP header the receiving end is expecting.

Enrico Parisi · May 13, 2025 go to post

Every routine, class or global that start with "%" is mapped to a system database, usually "%SYS".

In your case %Test.mac is (by default) mapped to the %SYS database.

It seems that you (the user you connect to IRIS) don't have permissions to write to the %SYS database.

Please note that during IRIS upgrade all routines starting with "%" are DELETED, unless they start with %z or %Z, so I suggest to use a different name or, better, create your code in other namespace/database with consistent naming (package name) and map it from your application namespaces. If you need your code in all namespaces, create a mapping for the %ALL (pseudo) namespace.

Enrico Parisi · May 13, 2025 go to post

Hi Daniel 😊

can you elaborate "$p($view(-1,-3),"^",6)"? 😉

Your code gives the impression that to implement this solution requires knowledge that we "simple humans" don't have.

Fortunately this is not the case, instead of the cryptic, obscure, arcane and undocumented $p($view(-1,-3),"^",6) the simple $ZNAME special variable can be used. 😃

Enrico Parisi · May 9, 2025 go to post

I'd implement a custom function, create a class like:

Class Community.CustomFunctions Extends Ens.Rule.FunctionSet
{

/// Returns Age in years from DOB in YYYYMMDD format
ClassMethod GetAge(DateOfBirth As %String) As %Integer
{
    Quit (($H-$ZDATETIMEH(DateOfBirth,8)) \ 365.25)
}
/// Returns Age in days from DOB in YYYYMMDD format
ClassMethod GetAgeDays(DateOfBirth As %String) As %Integer
{
	Quit ($H-$ZDATETIMEH(DateOfBirth,8))
}

}

Then from any Rule or DTL transformation you can use these two function as any other built in function.

Make sure DOB is not null ("") before calling the function.

Enrico Parisi · May 9, 2025 go to post

If you tell us what format you use for date of birth, then we can give you the code you may use

Enrico Parisi · May 8, 2025 go to post

You cannot assign to arbitrary variable in rules.  (edited, in fact, you can see other posts! 🙂)

To store some temporary data you can assign it to aux.RuleActionUserData

Enrico Parisi · May 7, 2025 go to post

In case you want only the portalUrl and pureID of the first element of "item" array, then all you need is:
Write responseData.items.%Get(0).portalUrl
Write responseData.items.%Get(0).pureID

Enrico Parisi · May 7, 2025 go to post

You case is simpler, you do know the json structure and all you need is to iterate the "items" array and find all the portalUrl and pureID properties. Note that since there can be many "items", there may be many portalUrl and pureID.
 

	Set itr=responseData.items.%GetIterator()
	while itr.%GetNext(.key, .value) {
		Write value.portalUrl,!
		Write value.pureID,!
	}

P.S.: it's unlikely that the Response you get from a REST call is a %DynamicAbstractObject since, by definition, that's an abstract class and cannot be instantiated, what you actually get is a subclass of %DynamicAbstractObject, a %DynamicObject in this case or a %DynamicArray when the response is an array.

Enrico Parisi · May 7, 2025 go to post

%Stream.DynamicBinary it's a stream that use a different storage, if you need your data in a  %Stream.TmpCharacter just copy the stream using CopyFrom() method.

Both %Stream.DynamicBinary  and %Stream.TmpCharacter are %StreamObject descendant, the use/have the same interface (methods) but a different storage.

Enrico Parisi · May 7, 2025 go to post

If you receive "special characters, such as non-utf-8 characters either control characters or unicode characters" it means that somewhere upstream character set conversion is not properly configured/handled.

I'm not sure that removing characters from a text is a proper solution, instead I'd fix the problem from the source identifying where the character set conversion is not properly configured/handled and fixing it.

Surely after removing some characters the text can be printed and  parsed by downstream systems, but....it's going to be a different text, potentially with a different meaning!

Enrico Parisi · May 6, 2025 go to post

You did not provide details, so I try to guess.

You are writing a Business Operation that use the EnsLib.HTTP.OutboundAdapter, if so, double check the signature of the method SendFormDataArray() that is:

Method SendFormDataArray(Output pHttpResponse As %Net.HttpResponse, pOp As %String, pHttpRequestIn As %Net.HttpRequest, pFormVarNames As %String = "", ByRef pData, pURL As %String) As %Status

Your call is:

set tSC = ..Adapter.SendFormDataArray(.tHTTPResponse, "POST", tHTTPRequest, tURL, tPayload)

I think the passed arguments don't match the method signature.

Enrico Parisi · May 4, 2025 go to post

Is it very complex to develop a new DICOM adapter that will use global streams instead?

It should not for InterSystems.

Note that the DICOM adapter/implementation in IRIS use some external ISC undocumented libraries.