Scott Roth · Aug 3, 2023 go to post

The issue was with the code that I was using in my Business Operation that was not calling the SSL Configuration. I have since reverted my Business Operation code back to the more standard code that is described in the Documentation, however I am still running into issues making sure the correct format of the Header is being sent in the REST API call, and how to interpret the JSON that is being returned by the REST API.

Scott Roth · Aug 3, 2023 go to post

According to WRC there is a memory leak with the way the Class Path of the driver is being handled. The reason for this issue was that I had the Class Path defined within the SQL Gateway. I thought this was only due to the jTDS driver we were using, but it is an overall issue with any Java Gateway Service. So I updated the Java Gateway service I was using for the Microsoft JDBC Connection for that Database to now have the Class Path of the driver, and I have not seen issues since.

Scott Roth · Jul 31, 2023 go to post

Does the error mean that I can't do just a POST, I need to use SendFormArray?

Here is my Test code...

Scott Roth · Jul 31, 2023 go to post

I enabled DEBUG = 2 on EnsLib.HTTP.OutboundAdapter, but I am still not seeing the entire Request message being sent within the Foreground display that pops up when I execute the Test. 

I was able to $$$LOGINFO(tHTTPRequest.OutputHeaders()) and verified that the header segment is being sent, but it is still throwing a 403 Forbidden error back at me.

Scott Roth · Jul 28, 2023 go to post

I have tried that but still unable to see the Request or Response being sent...

Class User.REST.Epic.EpicOperation Extends (EnsLib.REST.Operation, Ens.Util.JSON)

{

Parameter DEBUG As %Integer = 2;

Parameter INVOCATION = "Queue";

Method getPatientLocationRequest(pRequest As User.REST.Epic.Msg.GetPatientLocationRequest, Output pResponse As EnsLib.HTTP.GenericMessage) As %Status

{

    set tSC = $$$OK

    try{

      set tHTTPRequest = ##class(%Net.HttpRequest).%New()

      do tHTTPRequest.SetHeader("Epic-Client-ID","ed7ca30e-c16a-4053-9233-bff4f5661bb4")

      $$$TRACE("HttpRequest: "_tHTTPRequest) <this returns me just a pointer 8@%Net.HttpRequest>

      set tRequest = ##class(%DynamicObject).%New()

      set tRequest.PatientID = pRequest.PatientID

      set tRequest.PatientIDType = pRequest.PatientIDType

      set tRequest.ContactID = pRequest.ContactID

      set tRequest.ContactIDType = pRequest.ContactIDType

      set tRequest.UserID = pRequest.UserID

      set tRequest.UserIDType = pRequest.UserIDType

      set reqMsg = tHTTPRequest.EntityBody.Write(tRequest)

      $$$TRACE("reqMsg: "_reqMsg)

      set tSC = tHTTPRequest.EntityBody.Write(tRequest.%ToJSON())

      set tURL = ..Adapter.URL

      set tSC = ..Adapter.Post(tURL)

      set stream = "".....

Scott Roth · Jul 27, 2023 go to post

The issue was with the Custom Header field I needed for connecting to Epic.

Scott Roth · Jul 25, 2023 go to post

I am able to call the API without any issues using POSTMAN using the same username/password.

Scott Roth · Jul 20, 2023 go to post

For others I figured out the issue. Had to use the Base64 formatted Certificate Chain (p7b) from Windows ADCS (Active Directory Certificate Service).

  1. Download Base64 p7b to /etc/pki/ca-trust/source/anchors/ in RedHat
  2. Change ownership group to include irisusr
  3. Change permissions to Read (666)
  4. Convert p7b to pem
  • sudo openssl pkcs7 -in xxxxx.p7b -print_certs -out xxxxx.pem

When I went through testing the request I got the following...

DEVCLIN>set request=##class(%Net.HttpRequest).%New()

DEVCLIN>set request.Server = "xxxxxxxxxxxx"

DEVCLIN>set request.Port=443

DEVCLIN>set request.SSLConfiguration="OSUWMC"

DEVCLIN>set request.Https=1

DEVCLIN>set tSC=request.Get("/",2)
HTTP/1.1 200 OK
ACCEPT-RANGES: bytes
CACHE-CONTROL: private
CONTENT-ENCODING: gzip
CONTENT-LENGTH: 467
CONTENT-TYPE: text/html
DATE: Thu, 20 Jul 2023 20:08:54 GMT
ETAG: "b072b0f23afdd01:0"
LAST-MODIFIED: Fri, 02 Oct 2015 17:51:21 GMT
NTCOENT-LENGTH: 701
SERVER: Microsoft-IIS/8.5
X-POWERED-BY: ASP.NET

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>IIS Windows Server</title>
<style type="text/css">
<!--
body {
        color:#000000;
        background-color:#0072C6;
        margin:0;
}

#container {
        margin-left:auto;
        margin-right:auto;
        text-align:center;
        }

a img {
        border:none;
}

-->
</style>
</head>
<body>
<div id="container">
<a href="http://go.microsoft.com/fwlink/?linkid=66138&amp;clcid=0x409"><img src="iis-85.png" alt="IIS" width="960" height="600" /></a>
</div>
</body>
</html
>

Scott Roth · Jul 7, 2023 go to post

I am not sure I understand... I was using the example that was provided in the documentation.

https://docs.intersystems.com/healthconnectlatest/csp/docbook/DocBook.UI.Page.cls?KEY=EREST_operation#EREST_operation_json

If I have to have a Custom Header do I not have to define in this case the Client ID as part of the %DynamicObject?

    set tRequest = ##class(%DynamicAbstractObject).%New()

    set tRequest.clientID = "Epic-Client-ID = xxxxxxxx"

if I am using a Request Class Structure (osuwmc.Epic.Access.Request.GetPatientLocationByVisit2JsonRequest) isn't the set tRequest.payload = pRequest line correct?

Scott Roth · Jun 20, 2023 go to post

%ALL was the issue. Why will this only work in Chrome and not Microsoft Edge? Chrome is a pig when it comes to Memory so I have been looking for an alternative.

Scott Roth · Jun 20, 2023 go to post

I did try Chrome, but now I am receiving...

WebTerminal lost connection with server (code 1006).

Scott Roth · Jun 16, 2023 go to post

I don't think we will be going to 2023.2 anytime soon, as we just got to 2022.1 in May. It will be another year at least before we upgrade again. I am just trying to use this on the version we have currently and not through VSCode, I was trying to get this running for my team to use in edition to the Management Portal.

Scott Roth · Jun 15, 2023 go to post

Thanks, we tried taking an existing connection that was using the Local Interface Address of the VIP (ens192:1) and tracing it through the firewall to see what IP address it was associated with.

When we changed the port on the existing connection even though the Local Interface was set to the VIP, it started to send the other IP address assocated with ens192.

But if we changed the port back to the original port with the Local Interface set to the VIP it reverted back to sending the VIP in the packet header. 

I am questioning if we truly stop/started the object after making the change, but that is here nor there. 

I did open a ticket with WRC to get more information on how to troubleshoot the Local Interface setting, and the TCP Adapters definitions.

Scott Roth · Jun 7, 2023 go to post

Lookup tables are useful for 1 to 1 mapping that may need to change based on the system you are sending too. They are also helpful in filtering data. In many cases we use Lookup tables to filter down the data based on location or etc. from going to the endpoint system. In those cases, we have actually given those system users access to their own tables to maintain them without Integration's involvement.

Scott Roth · May 16, 2023 go to post

Upgraded to the latest driver, without any luck. Everything I read says it is not possible to Authenticate against Active Directory when you are not coming from a non Windows environment.

Scott Roth · May 16, 2023 go to post

6.2 is what I already had loaded so I went with it. I will try the latest.

Scott Roth · May 16, 2023 go to post

I tried the URL and when I went to test the connection I got an error about "authenticationScheme=NTLM" being invalid. Once I removed attribute, I am now getting "Driver can not be loaded" at least from the Management Portal. If I try it manually via Terminal to test connection it fails with a -1. When I look at the JDBC Gateway log I am seeing the following..

.%Net.Remote.Gateway..%WriteOutput.....May 16, 2023 1:48:05 PM com.microsoft.sqlserver.jdbc.AuthenticationJNI <clinit>.WARNING: Failed to load the sqljdbc_auth.dll cause : no sqljdbc_auth in java.library.path.
 

  • Driver name: com.microsoft.sqlserver.jdbc.SQLServerDriver
  • URL: jdbc:sqlserver://CPDDB.osumc.edu:1739;database=CPD;trustServerCertificate=true;integratedSecurity=true;domain=osumc;authentication=NotSpecified
  • Class path: 
     
Scott Roth · May 16, 2023 go to post

We were using a Local Windows Authentication account with a Hardcoded user name and password for our jobs to connect to MS SQL for the longest time, but it was frowned upon as everyone knew the user and password to sign into the database. So we were asked to make a Service Account on the Domain through Active Directory. In our AIX/Linux world we had not use Active Directory authentication so it became an issue to try to figure out how to make it work. That's when I discovered jTDS. I am going to try what @Jeff Morgan pointed out below to see if I can make it work as I could never find the correct connection string to make the Microsoft Driver to work for us.

Scott Roth · May 16, 2023 go to post

Wow how did you pin point that down? I never did see a ERROR #5023: Remote Gateway Error: java.lang.AbstractMethodError within the Operation/Process that would of shown me that.

Is there a better method to using Domain Service Accounts in Active Directory to connect to MS SQL through JDBC on Unix/Linux other than jTDS?

Scott Roth · May 16, 2023 go to post

We are using the  jTDS (jtds-1.3.1.jar) driver because we are using Domain Service Accounts to access the databases. We are doing this because I could not get the Microsoft JDBC drivers to authenticate properly using the Domain Service Accounts in Active Directory being on Unix/Linux. 

:>java -version
openjdk version "1.8.0_362"
OpenJDK Runtime Environment (build 1.8.0_362-b09)
OpenJDK 64-Bit Server VM (build 25.362-b09, mixed mode)
 

Scott Roth · May 16, 2023 go to post

Yes with WRC we saw a new Connection using the %JDBCGateway created each time. So if we already had %JDBCGateway running and executed each statement it would spawn a new connection that would say Established when you do a netstat -anp at the OS Level.

Scott Roth · May 16, 2023 go to post

I used the Linked Table Wizard to create the code for the table, then added the Class Method as a way to query the table in the one case...

/// Generated by the Link Table wizard on 2020-12-07 13:29:25.  Note that you can access the data in this class only when the external database is accessible.

Class osuwmc.CPD.vProviderNPIEns Extends %Library.Persistent [ Not ProcedureBlock, SqlRowIdPrivate, SqlTableName = vProviderNPIEns, StorageStrategy = GSQLStorage ]

{

/// Specifies details for the SQL Gateway Connection that this class uses

Parameter CONNECTION = "jTDS-CPD-Prod,NOCREATE";

/// Specifies the external database that this class uses

Parameter EXTDBNAME = "Microsoft SQL Server";

/// Determines if INSERT statements for this external table attempt to retrieve auto-generated keys.  Set this to 0 if this external table does not support auto generated keys.

Parameter EXTERNALGENERATEDKEYS = 1;

/// Specifies the external table to which this class refers

Parameter EXTERNALTABLENAME = "dbo.vProvider_NPI_Ens";

Property DoctorNumber As %String(EXTERNALSQLNAME = "DoctorNumber", EXTERNALSQLTYPE = 1, MAXLEN = 6) [ ReadOnly, Required, SqlColumnNumber = 2, SqlFieldName = DoctorNumber ];

Property NPI As %String(EXTERNALSQLNAME = "NPI", EXTERNALSQLTYPE = 12, MAXLEN = 10) [ ReadOnly, SqlColumnNumber = 3, SqlFieldName = NPI ];

Index MainIndex On (DoctorNumber, NPI) [ IdKey, PrimaryKey ];

Index ProviderDoctorNumber On DoctorNumber;

ClassMethod FindNPI(DoctorNumber As %String) As %String [ SqlProc ]

{

  set NPI = ""

  &sql(SELECT NPI into :NPI

    FROM osuwmc_CPD.vProviderNPIEns WHERE DoctorNumber=:DoctorNumber)

   

  IF SQLCODE <0 {write "SQLCODE error ",SQLCODE," ",%msg QUIT}

  ELSEIF SQLCODE = 100 {write "Query return no results" QUIT}

  quit NPI

}

Storage GSQLStorage

{

<ExtentSize>111002</ExtentSize>

<Selectivity>1</Selectivity>

<StreamLocation>^osuwmc.CPD.vProviderNPIEnsS</StreamLocation>

<Type>%Storage.SQL</Type>

}

}

In the Second Case I used the Linked Table Wizard, but call the sql code from the DTL, so I am not sure how that would be an issue.

Scott Roth · May 2, 2023 go to post

Working with WRC, our custom code was a copy of EnsLib.Email.AlertOperation that referenced older code that was not referenced anymore in 2022.1. Updated the referenced code and was able to get it to work.

Scott Roth · May 2, 2023 go to post

I see what you mean in 2022.1 it will not compile but however it does compile in 2018.1.3.

2022.1

Compilation started on 05/02/2023 08:05:23 with qualifiers 'cuk'

Compiling class osuwmc.AlertEmail

Compiling routine osuwmc.AlertEmail.1

ERROR: osuwmc.AlertEmail.cls

ERROR:  osuwmc.AlertEmail.1(13) : MPP5646 : ##expression on '$$macroText^%occMessages($lb("""Ensemble Alert from configuration item '%1' in system '<PROTECT> 202 macroText+51^%occMessages ^IRIS.Msg("Ensemble","en",2740803238),/ensemble/TEST/mgr/enslib/' on node '%3'""","""Ensemble"""))' failed with an error: %2

 TEXT:      Set pMailMessage.Subject = pMailMessage.Subject _ $$FormatText^%occMessages(##safeexpression($$macroText^%occMessages($lb("""Ensemble Alert from configuration item '%1' in system '%2' on node '%3'""","""Ensemble"""))),pAlertRequest.SourceConfigName,tSystemName,tNodeName)

ERROR:  osuwmc.AlertEmail.1(16) : MPP5646 : ##expression on '$$macroText^%occMessages($lb("""Ensemble Alert from configuration item '%1' on system '<PROTECT> 202 macroText+51^%occMessages ^IRIS.Msg("Ensemble","en",3633416132),/ensemble/TEST/mgr/enslib/'""","""Ensemble"""))' failed with an error: %2

 TEXT:      Set pMailMessage.Subject = pMailMessage.Subject _ $$FormatText^%occMessages(##safeexpression($$macroText^%occMessages($lb("""Ensemble Alert from configuration item '%1' on system '%2'""","""Ensemble"""))),pAlertRequest.SourceConfigName,tSystemName)

ERROR:  osuwmc.AlertEmail.1(25) : MPP5646 : ##expression on '$$macroText^%occMessages($lb("""Ensemble alert email triggered at %1 [<PROTECT> 202 macroText+51^%occMessages ^IRIS.Msg("Ensemble","en",2328442859),/ensemble/TEST/mgr/enslib/ UTC]""","""Ensemble"""))' failed with an error: %2

 TEXT:    Set tTimeMessage = $select(..IncludeUTCTimes: $$FormatText^%occMessages(##safeexpression($$macroText^%occMessages($lb("""Ensemble alert email triggered at %1 [%2 UTC]""","""Ensemble"""))),tNowLocal,tNow), 1 : $$FormatText^%occMessages(##safeexpression($$macroText^%occMessages($lb("""Ensemble alert email triggered at %1""","""Ensemble"""))),tNowLocal))

ERROR:  osuwmc.AlertEmail.1(25) : MPP5646 : ##expression on '$$macroText^%occMessages($lb("""Ensemble alert email triggered at %1""","""Ensemble"""))' failed with an error: <PROTECT> 202 macroText+51^%occMessages ^IRIS.Msg("Ensemble","en",3301903811),/ensemble/TEST/mgr/enslib/

 TEXT:    Set tTimeMessage = $select(..IncludeUTCTimes: $$FormatText^%occMessages(,tNowLocal,tNow), 1 : $$FormatText^%occMessages(##safeexpression($$macroText^%occMessages($lb("""Ensemble alert email triggered at %1""","""Ensemble"""))),tNowLocal))

ERROR: osuwmc.AlertEmail.cls

ERROR:  osuwmc.AlertEmail.1(14) : MPP5646 : ##expression on '$$macroText^%occMessages($lb("""Ensemble ManagedAlert from configuration item '%1' in system '<PROTECT> 202 macroText+51^%occMessages ^IRIS.Msg("Ensemble","en",197323985),/ensemble/TEST/mgr/enslib/' on node '%3'""","""Ensemble"""))' failed with an error: %2

 TEXT:      Set tMailMessage.Subject = tMailMessage.Subject _ $$FormatText^%occMessages(##safeexpression($$macroText^%occMessages($lb("""Ensemble ManagedAlert from configuration item '%1' in system '%2' on node '%3'""","""Ensemble"""))),tManagedAlert.SourceConfigName,tSystemName,tNodeName)

ERROR:  osuwmc.AlertEmail.1(17) : MPP5646 : ##expression on '$$macroText^%occMessages($lb("""Ensemble ManagedAlert from configuration item '%1' on system '<PROTECT> 202 macroText+51^%occMessages ^IRIS.Msg("Ensemble","en",466437042),/ensemble/TEST/mgr/enslib/'""","""Ensemble"""))' failed with an error: %2

 TEXT:      Set tMailMessage.Subject = tMailMessage.Subject _ $$FormatText^%occMessages(##safeexpression($$macroText^%occMessages($lb("""Ensemble ManagedAlert from configuration item '%1' on system '%2'""","""Ensemble"""))),tManagedAlert.SourceConfigName,tSystemName)

Detected 6 errors during compilation in 0.091s.

2018.1.3

Compilation started on 05/02/2023 08:13:51 with qualifiers 'cuk'

Class osuwmc.AlertEmail is up-to-date.

Compilation finished successfully in 0.199s.

Scott Roth · Apr 25, 2023 go to post

it is possible to use a shell script to sign into a Terminal Session as _system or whatever to execute Ens.Director Start or Stop Production. We do something similar to start/stop objects using EnableConfigItem via shell script.

Scott Roth · Apr 20, 2023 go to post

We found that if we execute EnableConfigItem from the shell on the OS level we can kick off multiple processes (multi-threading) up to 125 instances much faster than doing this from an Object Script in Terminal.

Scott Roth · Apr 20, 2023 go to post

I didn't realize I had posted this same error years ago. The answer was in my other post. In the DTL I was setting the Path = tSC which was being used in other ways.