You can check for XML invalid characters by decoding the encoded payload. For example:

zw $SYSTEM.Encryption.Base64Decode("VXRpbHMJOyBVdGlsaXR5IE1ldGhvZHM7MjAyMy0wOS0xMSAxNjo1Nzo0MiBBTgoJcSAKY2hrQ3RybChmaXg9MCkKCWsgXmdnRwoJcyB0PSJeUkZHIgoJcyBjdHI9MAoJdyAiUkZHIiwhCglmICBzIHQ9")
"Utils"_$c(9)_"; Utility Methods;2023-09-11 16:57:42 AN"_$c(10,9)_"q "_$c(10)_"chkCtrl(fix=0)"_$c(10,9)_"k ^ggG"_$c(10,9)_"s t=""^RFG"""_$c(10,9)_"s ctr=0"_$c(10,9)_"w ""RFG"",!"_$c(10,9)_"f  s t="

Look for a $C( ? ) where ? is in 0,1,2,3 .. 30,31.

Note: Tab $C(9) and  New line ($C(10) and $C(13,10) are fine.

Sometimes cut-n-paste from an email / word document will use $C(22) as a quote character.

Sometimes a sending application can have pauses sending data and this is mitigated by increasing the read-timeout.

This is also indicative of the thread / process at the sending system closing the connection.

Curious if this always happens on a standard configured description of the particular observation. Or is this free text entered by a particular upstream user (eg: copy and paste from word document, eg: Line breaks not escaped in HL7, ASCII 22 being used for double quote ). Is the input content incompatible with the encoding that is being used to transmit the message and crashes the transmit process. Suggest review the error log on the sending system could be useful.

Yes. Needs to use an SSL Configuration for HTTPS. Otherwise connection will close as handshake fails.

I tend to isolate to test a connection from server context if possible:

set request=##class(%Net.HttpRequest).%New()
set request.Server="www.intersystems.com"
set request.Port=443
set request.SSLConfiguration="TEST"
set request.Https=1
set tSC=request.Get("/",2)
do $SYSTEM.Status.DisplayError(tSC)

Switching the SSL off causes the error:

set request.Https=0
set tSC=request.Get("/",2)
do $SYSTEM.Status.DisplayError(tSC)

ERROR #6097: Error '<READ>Read+28^%Net.HttpRequest.1' while using TCP/IP device '9999'

Hi Evgeny,

Not saying this is best way but this indirection can be achieved with python eval. For example:

$Classmethod equivalent

classname="%SYSTEM.SYS"
methodname="ProcessID"
eval(f"iris.cls(\"{classname}\").{methodname}()")

$Property equivalent

Instantiating a Python exception and then iterate over properties printing out name and value:

myerror=iris.cls("%Exception.PythonException")._New("MyOops",123,"def+123^XYZ","SomeData")

for propertyname in ["Name","Code","Data","Location"]:
    propvalue=eval(f"myerror.{propertyname}")

    print(f"Property Name {propertyname} has value {propvalue}\n")

output was:

Property Name Name has value MyOops
 
Property Name Code has value 123
 
Property Name Data has value SomeData
 
Property Name Location has value def+123^XYZ

Hi Evgeny,

The function $Parameter in conjunction with $This, to access these.

For example:

value='$Parameter($This, "[Parameter Name]")'

For example adding to Stream example yesterday.

Vanilla Source class.

Class Test.Str2Stream.ExampleIn Extends (%Persistent, %XML.Adaptor)
{
Property Filename As %String;
Property Content As %String;
}

DTL class. Where the magic happens:

Class Test.Str2Stream.Trans Extends Ens.DataTransformDTL [ DependsOn = (Test.Str2Stream.ExampleIn, Ens.StreamContainer) ]
{
Parameter Salutation = "YeaBuddy";
Parameter Reaction = "LightWeight";
Parameter IGNOREMISSINGSOURCE = 1;
Parameter REPORTERRORS = 1;
Parameter TREATEMPTYREPEATINGFIELDASNULL = 0;
XData DTL [ XMLNamespace = "http://www.intersystems.com/dtl" ]
{
<transform sourceClass='Test.Str2Stream.ExampleIn' targetClass='Ens.StreamContainer' create='new' language='objectscript' >
<assign value='##class(Test.Str2Stream).StringToStream(source.Content,source.Filename)' property='target.Stream' action='set' />
<assign value='target.Stream.WriteLine($Parameter($This,"Salutation"))' property='x' action='set' />
<assign value='target.Stream.WriteLine("These plates are "_$Parameter($This,"Reaction"))' property='x' action='set' />
</transform>
}
}

Testing In:

<ExampleIn>
  <Filename>ABC</Filename>
  <Content>The session begins...
</Content>
</ExampleIn>

Testing output:

After a bit of digging I came up with the following equivalents.

$Horolog

>>> iris.cls("%SYSTEM.SYS").Horolog()
'66647,85547'

Equivalent access:

>>> var=iris.cls("%Library.UTC").NowLocal()
>>> var
'2023-06-22 23:50:04.386'
>>> iris.cls("%Library.UTC").ConvertTimeStampToHorolog(var)
'66647,85804.386'

$NAMESPACE ($ZNSPACE)

>>> iris.cls("%SYSTEM.SYS").NameSpace()
'USER'

ZN [Namespace] - aka change namespace

Keep your object fingers in the car at all times!!

Any created object script references need to be cleared BEFORE changing back. Is it necessary.

>>> iris.cls("%SYSTEM.Process").SetNamespace("%SYS")
'%SYS'

$JOB

>>> iris.cls("%SYSTEM.SYS").ProcessID()
'1548'

$SYSTEM - Instance name

>>> iris.cls("%SYS.System").GetInstanceName()
'IRIS123'

But you might have same name on different operating system / container so:

>>> iris.cls("%SYS.System").GetUniqueInstanceName()
'THEMACHINE.YOURDOMAIN.COM:IRIS123'

$ZTIMESTAMP

>>> iris.cls("%SYSTEM.SYS").TimeStamp()
'66647,81615.3832864'

$ZTIMEZONE – Contains the time zone offset from the Greenwich meridian

>>> iris.cls("%SYSTEM.SYS").TimeZone()
0

$ZVERSION – Contains a string describing the current version of InterSystems IRIS

>>> iris.cls("%SYSTEM.Version").Format(0)
'IRIS for Windows (x86-64) 202x.x.0 (Build xxU) Thu xx 2023 06:22:16 EDT'

Adding some code collateral to help explore challenge:

Class definition top

Include (%occInclude, %syConfig)

Some code to listing maps in a database

ZN "%SYS"
set pNamespace="HSCUSTOM"
set cns="Map."_pNamespace
set map=""
set found=0
for {
  set map=$O(^CONFIG(cns,map),+1,db)
  quit:map=""
  set len=$L($P(map,"("),"_")
  set globalMap=$P(map,"_",len,999)
  write !,"global match:""",globalMap,""" to Database:",db}
}

Example output:

...
global match:"IRIS.MsgNames("EnsSearchTable")" to Database:ENSLIB
global match:"IRIS.MsgNames("EnsWf")" to Database:ENSLIB
global match:"IRIS.MsgNames("EnsXPATH")" to Database:ENSLIB
global match:"IRIS.MsgNames("EnsebXML")" to Database:ENSLIB
global match:"IRIS.MsgNames("Ensemble")" to Database:ENSLIB
global match:"IRIS.MsgNames("ITK")" to Database:ENSLIB
global match:"IRIS.MsgNames("RuleEditor")" to Database:ENSLIB
....

Delete a map

set tSC=##Class(Config.MapGlobals).Delete(pNamespace,globalMatch,,$$$CPFSave)

Create a map

set global="IRIS.MsgNames("EnsSearchTable")"  // example like variable "gm" above.

kill params
sset params("Database")="CUSTOMLIB"  // The database you want to use
set params("Collation")=""
set tSC = ##Class(Config.MapGlobals).Create(pNamespace,global,.params,,$$$CPFSave)

// Always apply any pending changes
// Always confirm in testing that the configuration "sticks" after a system restart

do ##class(Config.CPF).Activate()

Alternatives to programmatic approach

Extended global syntax can be useful to copy start / end data between previously mapped and currently mapped database.

Hi Evgeny,

The following is a tool System Management Specialists may use.

You can achieve Jobbing and also running arbitrary ObjectScript code by employing RunLegacyTask.

For example run arbitrary code:

USER>zw ^ABC

USER>D $SYSTEM.Python.Shell()
 
Python 3.9.5 (default, Mar 14 2023, 06:58:44) [MSC v.1927 64 bit (AMD64)] on win32
Type quit() or Ctrl-D to exit this shell.
>>> myjob=iris.cls("%SYS.Task.RunLegacyTask")._New()

>>> myjob.ExecuteCode="Set ^ABC=""123"""

>>> res=myjob.OnTask()

>>> res
1
>>> quit()

USER>zw ^ABC
^ABC=123

For example Jobbing a Routine.

Routine (Test.mac) source code:

 Test
  Set ^ABC=345
  Quit

Use legacy Task to Job it off:

USER>zw ^ABC
^ABC=123
 
USER>D $SYSTEM.Python.Shell()
 
Python 3.9.5 (default, Mar 14 2023, 06:58:44) [MSC v.1927 64 bit (AMD64)] on win32
Type quit() or Ctrl-D to exit this shell.

>>> myjob=iris.cls("%SYS.Task.RunLegacyTask")._New()
>>> myjob.ExecuteCode="Job Test^Test"
>>> res=myjob.OnTask()
>>> res
1
>>> quit()
 
USER>zw ^ABC
^ABC=345

The status result can be useful to understand problems.

For example the Routine line label "Test" was miss-typed as "TEST"

>>> myjob=iris.cls("%SYS.Task.RunLegacyTask")._New()
>>> myjob.ExecuteCode="Job TEST^Test"
>>> res=myjob.OnTask()
>>> iris.cls("%SYSTEM.Status").DisplayError(res)
 
ERROR #5001: <NOLINE>zexecuteCode+3^%SYS.Task.RunLegacyTask.11

Enjoy IRIS. No limits :)

Hi Evgeny,

An option is to use a FunctionSet, for example:

 Class Test.Str2Stream Extends Ens.Rule.FunctionSet
{ ClassMethod StringToStream(initValue As %String = "undefined", filename As %String = "") As %GlobalCharacterStream
{
set ret=##class(%GlobalCharacterStream).%New()
set ret.Attributes("Filename")=filename
do ret.Write(initValue)
quit ret
}
}

This will let you see the nice single line "From Source property" and "To Target Property"

Where assignment value was:

##class(Test.Str2Stream).StringToStream(source.Content,source.Filename)

The Gotcha

If you have a test form input message:

<ExampleIn>
  <Filename>ABC</Filename>
  <Content>ho ho ho</Content>
</ExampleIn>

And this is not populating output like:

<StreamContainer>
  <OriginalFilename>ABC</OriginalFilename>
  <Stream><![CDATA[ho ho ho]</Stream>
  <Type>GC</Type>
</StreamContainer>

Then go back and check the input message type extends %XML.Adaptor.

 Class Test.Str2Stream.ExampleIn Extends (%Persistent, %XML.Adaptor)

ie: It is a limitation of the Test form that it expects to be able to correlate the content from XML to Test Object.

Hope this helps.

One way to approach this is with WebServerURLPrefix.

The Web Gateway has special handling when it detects the first part of application URL matches the instance name of a connection.

Where:

https://srv2/[Instance name 1]/csp/sys/UtilHome.csp

https://srv2/[Instance name 2]/csp/sys/UtilHome.csp  

See: https://docs.intersystems.com/irisforhealth20231/csp/docbook/DocBook.UI....

To let Apache know to expect this traffic could use:

<Location /instance1>
   CSP On 
</Location>
<Location /instance2>
   CSP On
</Location>

See: https://docs.intersystems.com/irisforhealth20231/csp/docbook/DocBook.UI....

I would say Yes in the sense that HL7 messages can be constructed entirely from scratch via code.

The main supporting method for this on EnsLib.HL7.Message the method "SetValueAt" can use to add and replace content for an HL7 message.
There are various ImportFrom methods (File, Stream, String) where a vanilla base message can be correlated to an HL7 Message.
The DocType is assigned by method PokeDocType, after which virtual property paths used by "SetValueAt" method will assign content in the right place as expected.
The DTL (Data Transform Language) documents can visually assist converting some other form of structured data into new HL7 messages. They are dynamic in that they can respond not just to a source reference message but leverage Lookup tables to control the content created for output HL7 messages. Then new configuration can be added to lookup tables and this extends the behavior of an existing DTL.

For some code example I have a UnitTest framework class that contains code demonstrating correlation of flat content to an HL7 message, setting the DocType and comparing virtual document paths. https://openexchange.intersystems.com/package/UnitTest-DTL-HL7

I think you hit the utility of the documented approach. To not accidentally delete the databases.

If you forgot to delete databases as part of namespace removal, they can be manually tidied up (System Administration->Configuration->System Configuration->Databases).

Questions I would also be asking when directed by Customer from a Vanilla "ENSEMBLE" name, to a purposeful named Namespace:

  • Are IRIS.DAT(s) located in the correct disk volume / Drive name. ie: Sufficient capacity for expected use. (Is it supposed to be on the same volume / location as where services are installed).
  • Is there a backup strategy. AND did I retest it yet?
  • Are the databases journal enabled.
  • Check Users don't still have "ENSEMBLE" as default namespace location
  • Check Scheduled Tasks are not expecting "ENSEMBLE" namespace
  • Update documented standard build script

One more thought. If you are using Unix / Linux for hosting IRIS, a more recent option is mergeCPF, to update both Namespace, Database and Application settings. Suitable for:

  • migration activity as a testable automated script
  • configuration and setup from vanilla docker images.

https://docs.intersystems.com/iris20231/csp/docbook/Doc.View.cls?KEY=ACMF

This allows applying updates with a simple text file AND it does both traditional CPF settings AND the [csp web] Application settings in one go.

Your question encouraged me to share a little web calculator for CreateApplication settings.

So less time needed to figure out Application setting names and values.

https://openexchange.intersystems.com/package/MergeCPF-Application-Settings

Am going to make some assumptions that you have been given an IRIS.DAT file and you want to mount that Database file on a new instance for some evaluation activity.

This is likely to not be exactly the situation but can serve as an example to help understand what you need to achieve.

A Namespace is a CODE and DATA context that database processes normally operate in.

To use the Database file it is "mounted" as a Database configuration.

The Database configuration is used by a Namespace, either as the DEFAULT for code and routines OR the data is MAPPED for use by a namespace.

As an example I create a copy of an empty database at: C:\InterSystems\somewhere\db\empty2
  This directory contains the IRIS.DAT.
In System Management Portal I can "Create a Database" (System Administration->Configuration->System Configuration->LocalDatabases)
  Use the path of new IRIS DAT file: "C:\InterSystems\somewhere\db\empty2"
  There will be a warning:  "Database file, IRIS.DAT, already exists in directory."

If you then navigate with System Explorer -> Globals

  And use "Lookin : Database" for the example is "EMPTY2"
  Here I added the Global Test

If the IRIS.DAT has both code (eg: SQL Table definitions) and Data, possibly you might want to create a new namespace and make the mounted database from earlier as the default database for routine and code for that namespace.
  So "Namespaces -> Create New Namespace

Alternatively, you may want to apply the new Data with code in another existing database for example SQL Table definitions. Here you could decide what data you want to map from the new database file to an existing namespace. (See: Explorer for Globals above)
  Navigate to Namespaces (System Administration->Configuration->System Configuration->Namespaces)
  Select "Global Mappings" of the namespace you want to "access data from the new Dat file".

Example to map the whole of global "Test" to existing namespace. Note this is will "hide" any data in "Test" that might exist in the default database of the pre-existing namespace.

Hint: Be sure to have database files unmounted or instance shut down when first learning to copy / swap database files around.

There are advanced ways of doing things. Occasionally you might get an IRIS database file from an operating system with a different Endian composition. The tool cvendian can help translate this to a target operating system endian mode. When moving big DAT files about it can be useful to run an SHA-1 or MD5 checksum hash on the database file before and after copying to ensure there is no corruption.

Hope this gave some ideas and helps progress the direction needed for your specific situation.