Article
· Nov 5, 2016 9m read

How to - Customize Ensemble Settings

This article will provide tips and tricks on customizing Ensemble business hosts with configurable settings.

Ensemble production Settings are configurable values that control the behavior of a production and its hosts.

The documentation for adding and removing Settings and specifying categories and controls for Settings is provided at the link:

http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=EGDV_prog#EGDV_prog_settings

“To provide new settings for a production, a business host, or an adapter, modify its class definition as follows:

  1. Add a property for each configuration setting you wish to define.
  2. Add a class parameter called SETTINGS to the class.
  3. Set the value of SETTINGS to be a comma-delimited list of the names of the properties you have just defined.”

It is possible to customize the Settings for desired new production/hosts behavior by defining subclasses of Ensemble classes.

To demonstrate customization of Settings I have created a sample HL7 production - Sample.HowToSettings.Production.

There are 3 enabled Business Services in the sample production - Service1, Service2 and HL7FTPService.

Service1 and Service2 are dummy services not doing any useful work and used only for demonstration of the settings customizations.

HL7FTPService is the main service based on the standard EnsLib.HL7.Service.FTPService class. It accepts HL7 messages from the remote FTP server and sends them to the MsgRouter which forward selected messages to a file through HL7FileOperation.

The HL7FTPServiceStandard (with its class EnsLib.HL7.Service.FTPService) service is also part of the production. In the production configuration it is disabled and serves only for comparison of its settings with the HL7FTPService settings.

Let's have a look at the Service1 and Service2 code, first.

/// Sample service demonstrating how to customize Configuration Settings with formats:
/// String, Number, Check Box, Drop-Down list. 
Class Sample.HowToSettings.Service.Service1 Extends Ens.BusinessService
{

Parameter ADAPTER = "Ens.InboundAdapter";

/// string
Property foo As %String;

/// check-box
Property bar As %Boolean;

/// number
Property number As %Numeric [ InitialExpression = 3 ];

/// drop-down list
Property colors As %String(VALUELIST = ",Red,Green,Blue,White,Black") [ InitialExpression = "White" ];

/// Additional Settings 
Parameter SETTINGS = "foo, bar, number, colors";

Method OnProcessInput(pInput As %RegisteredObject, Output pOutput As %RegisteredObject) As %Status
{
$$$LOGINFO("Method Not Implemented")
Quit $$$OK
}

}

Properties ‘foo’, ‘bar’, ‘number’ and ‘colors’ are defined as SETTINGS and represent correspondingly a string, a check-box, a number and a list with drop-down values.

Note that there is no setting category defined for these properties in the code and therefore according to the documentation they will be shown in the corresponding Business Service settings view in ‘Additional Settings’ category.

Here is how the newly defined business services Settings – ‘foo’, ‘bar’, ‘number’, ‘colors’ are shown in the Service1 view:

In Service2 and its associated class I have attempted to demonstrate how it is possible to redefine the Settings, use the settings categories and remove the previously defined settings in the base classes.

/// Sample service demonstrating how to customize Configuration Settings:

/// Custom Settings category, remove a setting from previously defined settings

Class Sample.HowToSettings.Service.Service2 Extends Sample.HowToSettings.Service.Service1

{

/// Custom Settings category, remove OS from configuration settings

Parameter SETTINGS = "foo:Custom,      bar:Custom, -colors";

}

The Settings ‘foo’, ‘bar’ defined before in Service1 are now redefined with their new explicitly set settings category ‘Custom’ and ‘colors’ Setting is now removed (notice the removal command - in front of the 'colors'). The setting ‘number’ is unchanged and stays as before with ‘Additional’ settings category.

Here is the Service2 view with its Settings – ‘foo’, ‘bar’, ‘number’ from the Ensemble Management Portal:

The settings defined in the Service1 and Service2 are configurable. A user / administrator can change them on the running or stopped production and click on the Apply button to apply the changes to the host or production.

 The new value of the above settings could be a text, check-box, number or one of the selected predefined values for the drop-down list of the ‘foo’, ‘bar’, ‘number’, ‘colors’ correspondingly.

The main HL7FTPService was created to accommodate some customers’ requirements to save/archive FTP files moved from the FTP server to Ensemble production into the FTP server file system. The standard HL7FTPService does provide only the Ensemble server archiving functionality but not the FTP server archiving.

To achieve the required functionality it is possible to use the FTP protocol commands - RNFR and RNTO which are used in the underlying ‘Rename’ method of the Adapter.

To accomplish the new functionality additional code required is simple to implement and it is shown below.

/// Sample HL7/FTP service demonstrating how to use customized Configuration Setting
Class Sample.HowToSettings.Service.FTPService Extends EnsLib.HL7.Service.FTPService
{

Parameter ADAPTER = "Sample.HowToSettings.FTP.InboundAdapter";

/// Configuration Setting for
/// Path on the FTP server (related to the File Path setting) to save a copy of each file sent from the server. 
/// If not given, copy of the file will be stored in the FTP root directory.
Property RemoteArchivePath As %String(MAXLEN = 1000);

Parameter SETTINGS = "RemoteArchivePath:Basic";

Method OnProcessInput(pFTPStream As %Stream.Object, Output pOutput As %RegisteredObject) As %Status
{
Set tSC=$$$OK
$$$sysTRACE("Got FTP Stream "_pFTPStream_"("_pFTPStream.Size_") for file '"_pFTPStream.Attributes("Filename")_"'")
Set tIOStream=##Class(%IO.MetaCharacterStream).%New(pFTPStream) Quit:'$IsObject(tIOStream) %objlasterror
Set tIOStream.Name=pFTPStream.Attributes("Filename")_$C(13,10)_"via FTP "_..Adapter.FTPServer_":"_..Adapter.FTPPort_" path '"_..Adapter.FilePath_"'"
Set:$ZCVT(..Adapter.Charset,"l")="binary" pFTPStream.LineTerminator = $C(13) // Default delimiter for method ReadLine() of the stream class, when in binary mode

Do ..%Parser.Reset()
While 'tIOStream.AtEnd { ; Loop over docs in the file
Set tSC1=..%Parser.ParseFramedIOStream(tIOStream,,1)
Set:$$$ISERR(tSC1) tSC=$$$ADDSC(tSC,tSC1)
Quit:$$$StatusEquals(tSC1,$$$EnsErrGeneral) // !!! quit on fatal errors only
}
Do ..%Parser.Reset() ; release the file

// begin customization code
// get the Filename, attach the time stamp to it and move the file into the Archive directory
// the Rename method below is using RNFR and RNTO FTP commands (RFC 959), effectively moving the file into the new directory
Set tSource=pFTPStream.Attributes("Filename")
Set tArchive=..Adapter.CreateTimestamp(tSource,"%f_%Q")
Set tSC=..Adapter.FTP.Rename(tSource,..RemoteArchivePath_"/"_tArchive) 
// end

Quit tSC
}

}

There is one catch however. When the ‘Rename’ function is used it is actually not archiving the FTP source file but instead moves it to a new location on the FTP server. That means that the file will not be in the source directory of the FTP server and an attempt to delete it after it has been transferred by the service to Ensemble will fail.

Consequently we need to remove the DeleteFromServer setting from the customized Business Service / Adapter and set its property value to 0 ( do not delete the file). That is done in the code of the customized FTP adapter:

/// Sample customized Adapter that receives files via the FTP protocol.
Class Sample.HowToSettings.FTP.InboundAdapter Extends EnsLib.FTP.InboundAdapter
{

/// do not delete file from the server and remove the DeleteFromServer setting
Parameter SETTINGS = "-DeleteFromServer";

Property DeleteFromServer As %Boolean [ InitialExpression = 0 ];

}

Comparing the views of the HL7FTPService and the HL7FTPServiceStandard we can see that DeleteFromServer Setting is not shown in the customized Service/Adapter view and therefore a user cannot set the check-box to enable it. And because the property value by default is 0 the adapter will not attempt to call the FTP server function to delete the file from the FTP server after it is transferred to Ensemble and moved to the new archive directory on the FTP server.

Current value of the RemoteArchivePath is ‘Archive’. This value sets the relative path to the root directory defined for the specific user logged in to the FTP server. Should a user/administrator decide to change it, she will simply type a new value in the RemoteArchivePath setting, say ‘newArchiveDirectory’ and click the Apply button to apply the changes to HL7FTPService.

If it is desirable to have the customized setting heading with distinct separate words (like in the HL7FTPService) in the production configuration view then the global CacheMsg should be updated in the related namespace (e.g. Ensemble) where the production will be running as it is shown below for the RemoteArchivePath:

  Ensemble>set ^CacheMsg("EnsColumns","en","RemoteArchivePath")="Remote Archive Path"

After that change the RemoteArchivePath setting name is shown in the view as ‘Remote Archive Path’.

The important special inheritance property of the SETTINGS parameter is such that the newly defined Settings do not override previously defined Settings but instead are added to the previously defined Settings or modifying them.

In the example of the RemoteArchivePath Setting all previously defined Settings for HL7FTPService are still in place as we can see from the service view, for instance ‘FTP Server’, ‘FTP Port’ are still there.

In another example of this special inheritance property the Settings defined in EnsLib.HL7.Service.Standard class  (the base class of the Service) are all present in the customized Service used in the sample production.

Parameter SETTINGS = "TargetConfigNames:Basic:selector?multiSelect=1&context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId},
                      SearchTableClass::selector?context={Ens.ContextSearch/SearchTableClasses?host=EnsLib.HL7.Service.Standard},
                      LocalFacilityApplication,
                      Framing:Connection:selector?context={Ens.ContextSearch/getDisplayList?host=@currHostId&prop=Framing},
                      AckMode, UseAckCommitCodes, IgnoreInboundAck, AddNackERR, NackErrorCode, BatchHandling,
                      MessageSchemaCategory:Basic:selector?context={Ens.ContextSearch/SchemaCategories?host=EnsLib.HL7.Service.Standard},
                      DefCharEncoding::selector?context={Ens.ContextSearch/CharacterSets}, DocTypeResolution, SaveReplies";

Syntax of the Parameter SETTINGS definition allows to have spaces between Settings values but not the new line (CR/LF) characters. That could be used to make the review of the Parameter easier, like it is shown in the example above. The new lines above were introduced only for the convenience of the display.

The Parameter code above also demonstrates how to use special controls for Settings . For instance look at the controls for TargetConfigNames used to select the required target host (in the sample production it is MsgRouter) and HL7 Schema selection with MessageSchemaCategory.

For further details, please, check the documentation link:

http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=EGDV_prog#EGDV_prog_settings_options

I hope that the provided examples of the hosts Settings customization would be useful when building your own new/customized Ensemble Settings. 

Let me know if you will have any questions or concerns about the Settings customization. Cheers.

Discussion (6)3
Log in or sign up to continue

Thanks for the great article.  I have a few questions/issues and would appreciate some help.

1.  I'm trying to remove the DeleteFromServer and ArchivePath properties but they still appear in the Production even though I've added the - to the Parameter SETTINGS.

Parameter SETTINGS = "-DeleteFromServer, -ArchivePath, RemoteArchive, TargetConfigNames";

2.  For the Remote Archive, I can get the rename to work but that assumes the archive path exists.  I wanted to do some checking to see if the path exists (if not create) but can't seem to get the method calls right.  Am I reading the documentation incorrectly since I thought doing ..Adapter.FTP then references the FTP session object (%Net.FtpSession, or for SFTP %Net.SSH.SFTP) then method MakeDirectory or MkDir would be accessible.  So my call looks like ..Adapter.FTP.MakeDirectory(.RemoteArchivePath) but it errors with METHOD DOES NOT EXIST. 

1. You have whitespaces after commas. It should be:

Parameter SETTINGS = "-DeleteFromServer,-ArchivePath,RemoteArchive,TargetConfigNames";

Also I think you're defining your settings in Operation? I'm not sure it would work. I think you need to redefine adapter settings in adapter, like so:

Class App.FTP.InboundAdapter Extends EnsLib.FTP.InboundAdapter {

Parameter SETTINGS = "-DeleteFromServer,-ArchivePath";

}

Not sure why you're including RemoteArchive in your settings, as it's already there in ancestors. I removed it.

After that use your new adapter in your operation.

2. What does $classname(..Adapter.FTP) return?

Hey Eduard,

Thanks so much for the quick response! 

#1.  It looks like creating a new App.FTP.InboundAdapter class extending the EnsLib version and removing the adapter settings in there worked.  I also removed those property/SETTINGS from my App.FTP.InboundService Class as mentioned.

#2.  $classname(..Adapter.FTP) was returning the FTPInboundAdapter but using ..Adapter.FTP.%sftpSession.MkDir() did work.

Thanks!