Enrico Parisi · Aug 4, 2017 go to post

If you are already familiar with BouncyCastle in C# probably the easiest way for you is to use the Java Gateway or the .NET gateway to call out the Java or C# BouncyCastle library.

In either case, I'd suggest to write a little or some little (i.e. limited to your needs) wrapper that you then call from COS.

HTH

Enrico

Enrico Parisi · Jan 6, 2020 go to post

I think the most efficient way to test if a column exists in the result set is using the columnIndex property of the metadata (instance of %SQL.StatementMetadata):

SAMPLES>Set rs=##class(%SQL.Statement).%ExecDirect(.st,"select name,age from Sample.Person")
SAMPLES>Write rs.%Next()
1
SAMPLES>Set rsmd = rs.%GetMetadata()
SAMPLES>If $data(rsmd.columnIndex("NAME")) Write rs.Name
Xiang,Gertrude X.
SAMPLES>If $data(rsmd.columnIndex("NOTEXISTENT")) Write ""

Enrico

Enrico Parisi · Jan 6, 2020 go to post

Sorry, last line should be:
SAMPLES>If '$data(rsmd.columnIndex("NOTEXISTENT")) Write ""  

Enrico

Enrico Parisi · Jan 28, 2020 go to post

I think you mistyped, the condition should be:


PostCodeLastCharacter(HL7.{PID:11(1).5})=1

Enrico

Enrico Parisi · Feb 1, 2020 go to post

Hi Timothy,

very nice article, I think that regarding restoring of the current device:

Set$io = tDevice

  The proper way to set the current device ($io) is issuing the Use command:

Use tDevice

  From the doc:

You issue a USE command to change the current device.
.....

This special variable cannot be modified using the SET command. Attempting to do so results in a <SYNTAX> error.

https://cedocs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?…

Enrico

Enrico Parisi · Feb 1, 2020 go to post

Hi Stella,

I dubt the code that returns "a LIST error" is the code you posted here.
There seems to be a few problems with the code posted, including:
- I don't think the query will compile with " as Count" (Count is a reserved word in SQL)
- You should get an <UNDEFINED> error when referencing the Count variable in the for loop

Please note that Settings in Ens.Config.Item is a "list Of Ens.Config.Setting", that's an instance of %Library.ListOfObjects, not a $list.

In general, when I code, today I try to avoid %ResultSet, that I consider deprecated, in favor of %SQL.Statement. But that's, maybe, my opinion.

As to a possible solution, I'm not sure the full list can be obtained with a single SQL query because the result consists of a "main" collection of all productions hosts and, for each host, a collection of settings.
Lists in Object Scrtipt Classes are not projected to SQL by default (SQLPROJECTION and/or STORAGEDEFAULT may change that default).

So, to get the list of all Settings, two loops are necessary, one for all hosts, another for all settings within the host, something like:

ClassMethod SettingsQuery()
{

    Set sc=$$$OK
    Try {
        Set sqlQuery="Select Production, Name, Settings from Ens_Config.Item "
        Set stSql=##class(%SQL.Statement).%New()
        Set stSql.%ObjectSelectMode=1
        Set rsSql=##class(%SQL.Statement).%ExecDirect(.stSql, sqlQuery)
        If rsSql.%SQLCODE < 0 {
            Set sc = $$$ERROR($$$GeneralError,"%SQLCODE="_rsSql.%SQLCODE_", %Message="_rsSql.%Message)
            Quit
        }
        
        While rsSql.%Next(.sc) {
            If $$$ISERR(sc) Quit
            Write rsSql.Production.Name," -> ",rsSql.Name," has ",rsSql.Settings.Count()," settings:",!
            For SettingCounter=1:1:rsSql.Settings.Count() {
                Write ?10,rsSql.Settings.GetAt(SettingCounter).Name," = ",rsSql.Settings.GetAt(SettingCounter).Value,!
            }
            Write !
        }
    } Catch CatchError {
        #dim CatchError as %Exception.SystemException
        Set sc=CatchError.AsStatus()
    }
    Quit sc
}

Now, you got what you asked....but, I'm not sure you got what you want/need. ;)
There are several issues/pitfalls.
There are Business Host settings and Adapter settings, there are settings with default values....and more...

I REALLY wish ISC provided an interface to get a list of all settings, along with "actual" values (as used by the production, wherever the value comes from....) of each/all Business hosts within a production.
But...I keep wishing, any other has the same wish? :)

Enrico

P.S.: Please note that the code posted works in IRIS but does not work with Ensemble/HS due to a bug on how %ListOf* are handled when %ObjectSelectMode=1

P.P.S: how can I paste properly insented and "colored" code directly from Studio?

Enrico Parisi · Feb 27, 2020 go to post


Hi Brad,
we have done quite a few similar project migrating, cloning and "splitting" Ensemble/HealthShare systems to/from different sites or systems.
In fact we are working on one migration (with some change in the target site) right now.
The actual recipe depends on the environment and the requirements, in your case is a "simple" move, and that simplify the job.
It is indeed possible to upgrade during the migration, but of course it adds a little more complexity and planning (AND TESTING!!).
One important aspect is the down time you can afford, if any. I've never done a migration with zero down time, I would say that around 30 minutes to one hour is feasible, in your case that does not require any modification maybe in less that 30 minutes it can be done.
Ensemble is an integration platform, it often connect to many systems and receive connection from many systems, often there are some sort of web/csp app where user are connecting, this must be taken in to account to simplify and speed up switch time.
Ideally you want to have full control of the "communication switch" so when the "new" system take over you can switch (divert) all the connection to the new system.
We usually install a temporary reverse proxy for all the HTTP/HTTPS traffic (soap, web/csp pages, rest, etc.) that "redirect" the traffic to the "old" system and ask all the connecting systems to "point" to the reverse proxy BEFORE (a week or two) "D day" (final migration switch date).
Then we make sure that ALL the HTTP/HTTPS traffic goes through the proxy (we always find some external system that "forgot" to change the endpoint...).
The "D day" you change the reverse proxy configuration to "redirect" the traffic to the new system, so this is under YOUR control.
For HL7 traffic we usually prepare in the "old" system a separate production that simply redirect received HL7 to the "new" system.
You need one Business Service and one Business Operation for each TCP/IP HL7 inbound adapters in all your productions (i.e. one for each HL7 port in your system).
This production can be implemented adding BS's (EnsLib.HL7.Service.TCPService) and BO's (EnsLib.HL7.Operation.TCPOperation) with simple point & click configuration.
When all your production in the old system have stopped and started in the new system, starting this production will redirect all the HL7 traffic to the new system, all under YOUR control.
As soon as the new system is up&runnign you can ask all the connecting partners/systems (HTTP/HTTPS and HL7) to change (at their convenience) the endpoint to the new system.
I believe that having the "main switch" in your control is crucial....particularly when you have tens of systems/partners connecting to Ensemble/HS.
Depending to your environment there maybe more type of connection to take care of...
Regarding setting up and migrating the system, the first goal is to replicate the configuration, usually this involve a fresh installation (better if using same drive/disk letters and structures), a preliminary copy of all the databases and configuration using some sort of backup (there are different options) and as much TESTING as possible in your environment. Are third party test systems available? If not you may need to build some mockup of the external system to perform some basic testing.
We usually take the opportunity to spit code ("routines") databases and globals (data) databases, if not already so.
The advantage of dividing code and data is the ability to change code in the target system and when performing the last migration/copy only data is transferred (actually, the database may contain code no longer used...no problem).
The final migration should only transfer the data (not config) and involve some sort of backup and journal file from backup to "shutdown", this way you can minimize the downtime to the minimum.
Please add in the middle of each step above: test, test, test and at the end test, test and test :)
There are many other aspects I forgot or depends on specific environments and requirements, these are only a few tips from my experience.
If you have any specific question, just ask, I can't commit for an answer, but maybe somebody else will ;)
Ciao,
Enrico  

Enrico Parisi · Sep 2, 2022 go to post

I don't think you can add SqlProc at class level, but you can modify the class definition programmatically.

Look at the class %Dictionary.MethodDefinition and the property SqlProc, if you set it to 1 is the same as adding [SqlProc] to the method.

%Dictionary.MethodDefinition is a child class of %Dictionary.ClassDefinition.

Of course I advise you to test it in a temporary system/namespace before...

Hope it helps,

Enrico

Enrico Parisi · Sep 6, 2022 go to post

Unfortunately it does not work just as it always has, a %ListOfObjects cannot be passed to a .NET method that expect a .NET array as argument, like the setAddressArray() method.

It simply does not work.

Enrico Parisi · Sep 7, 2022 go to post

Hi Stuart,

thank you so much for your answer and effort, I feel sorry for your vacation!!

As mentioned, to keep things simple in my test instead of using my "real stuff" (somewhat more complex) I'm using the "old" test code %Net.Remote.DotNet.Test and corresponding .NET project that I have compiled with .NET Framework 4.5, this provides a simple testbed to test basic functionality.

Using the now undocumented legacy gateway the test works fine

Then, when I:

set ^%SYS("Gateway","Remote","Recast",namespace)=1

and reimport proxy classes, I can see that the generated proxy classes are different, so the "recast" had some effect.

Unfortunately running the test code....fail in the very first line:

USER>do ##class(%Net.Remote.DotNet.Test).Test(58555)
 
Errore #5023: Errore del gateway di Java: <ZJGTW>%dispatch+17^%Net.Remote.Base.1

It fails in the line (the first call to .NET class):

Set student=##class(remote.test.Student).%New(gateway,29,"976-01-6712")

So...neither this approach works...at all.

Thank you again,

Enrico

Enrico Parisi · Sep 7, 2022 go to post

I realized the problem using "Recast", the gateway needs to be instantiated using the new gateway.

I had to fix a couple of things related to how the datatypes are casted and now it works.

However, this is not exactly what I was looking for, as mentioned my goal is to convert existing code that use the no longer documented "legacy gateway" to "new code" using the current (poorly) documented $system.external.

My goal is to move "forward" my code for current and future releases of IRIS.
Removing the need of the imported proxy classes also simplify the IRIS upgrade process.

Thank you again and enjoy your vacation!

Enrico
 

Enrico Parisi · Sep 8, 2022 go to post

The solution in simple....if you know it:

 set addressArray=netGate.new("remote.test.Address[2]")
 do addressArray.%set(0, home)
 do addressArray.%set(1, home2)  set person = netGate.new("remote.test.Person")
 do person.setAddressArray(addressArray)
 set addressArray2=person.getAddressArray()
 for i=0:1:1 {
      set addr = addressArray2.%get(i)
      !, addr.city
 }
 

In addition to %set() and %get() method for arrays there are also %setall(), %getall() methods.

Hopefully these methods will be documented sometime in the future.

Enrico

Enrico Parisi · Dec 14, 2022 go to post

Frankly I cannot see any advantage in using the sample you posted, it makes things more complicated with no advantage and no, it's not faster than just use a plain simple property.

There are cases when those "tricks" (calculated properties, Set/Get methods) can be useful, but that's not the case of the code in your sample.

Regarding VALUELIST, it makes it easy and simple to define a (typically small) set of valid values for a property, then, if necessary, you can optionally provide a "user friendly" representation of those value. If you don't find it useful or don't need it, well....don't use it.

Enrico

Enrico Parisi · Dec 14, 2022 go to post

Try enabling the log in the Java Gateway. In Management Portal:

System Administration > Configuration > Connectivity > External Language Servers > Edit External Language Server

Edit the "%Java Server" (you may need to stop it, if started) and in Advanced Settings specify a Log File.

Then reproduce the problem and check the log file.

The log file (in some case) can grow significantly, you may want to disable it when done.

Enrico

Enrico Parisi · Dec 20, 2022 go to post

Hi Yuri, thank you for your sample, I noticed you don't handle/consider the response from EPRSave().

Are you actually using that API in a production system?

Enrico Parisi · Dec 21, 2022 go to post

In your sample the response from EPRSave() is never used/tested.

Do client.EPRSave(request, .response)

Are you actually using that API in a production system?

Enrico Parisi · Dec 21, 2022 go to post

Use Microsoft Windows to install IIS.
Once installed you can install InterSystems IRIS, which will ask the user if IIS should be configured.

Unfortunately, as of latest IRIS and any previous IRIS/Caché, after default/standard configuration by install kit the SMP won't work properly without additional manual configuration of IIS.

I suggest ISC to fix this before removing PWS.

Enrico Parisi · Dec 21, 2022 go to post

I don't think alerts are stored in any file, they are stored in the Interoperability Event Log accessible from System Management Portal or via SQL using Ens_Util.Log table alerts have Type=6/Alert

Enrico Parisi · Dec 22, 2022 go to post

Sorry but I don't understand the problem/issue, starting from 2023 a web server is a prerequisite for IRIS, I's say that is almost mandatory unless you have some very particular use case.

What's the difficulty of installing Apache or IIS in windows?

Providing that IRIS installation properly configure the WEB Server (IIS or Apache), I don't see the difficulty. Or I'm missing something?

Enrico Parisi · Dec 22, 2022 go to post

I haven't seen a database corruption for many years, I literally forgot the last time I saw it, it may be 15+ years or more. And in the past (last millennium) I've seen and dealt db corruption.

Out of curiosity, what are the symptoms or your corruption? How did it happened?

Enrico Parisi · Jan 11, 2023 go to post

If you use custom port there is no session, that's expected/by design.

A session (%session) is a %CSP.Session object that is available only when the SOAP service uses a standard request via a properly configured web server (apache, IIS, other), to configure it check the "EnableStandardRequests" setting in the Business Service, configure the Web Application and the web server (or use default namespace Web Application), then invoke the service via the web server port.

In my experience I prefer to use standard request via web server, in general there is no need or valid reason (I can think of) to use custom port, unless you need something very specific, for example you don't have a web server (so...no SMP, something very unusual...).

Enrico

Enrico Parisi · Jan 11, 2023 go to post

Exactly for that situation I use:

If##class(Ens.Job).ShouldBeQuiescent() || ##class(Ens.Job).ShouldTerminate() {
    ; do your closing housekeeping or...whatever..Quit
}

In your case maybe only ShouldTerminate() would be sufficient.

Enrico

Enrico Parisi · Jan 11, 2023 go to post

It's very difficult....nearly impossible to help with the little info provided.

The error does not looks like an IRIS/Caché error, is it an IRIS/Caché table or an external table?

How are you invoking the query? System Management Portal? Embedded SQL? xDBC?

Enrico

Enrico Parisi · Jan 12, 2023 go to post

You can change the port from 57772 to 80 in System Managment Portal: System Administrtion -> Configuration -> Additional Settings -> Startup -> WebServerPort

Or cache.cpf configurtion file:

WebServerPort=57772  to WebServerPort=80

Better, proper and suggested solution: install a properly configured web server using port 80, don't use the little web server installed by Ensemble.

Enrico