Article
Eduard Lebedyuk · Feb 19, 2016 12m read

# Deploying Applications in InterSystems Cache with %Installer

Suppose you have developed your own app with InterSystems technologies stack and now want to perform multiple deployments on the customers' side. During the development process you've composed a detailed installation guide for your application, because you need to not only import classes, but also fine-tune the environment according to your needs.
To address this specific task, InterSystems has created a special tool called %Installer. Read on to find out how to use it.

### %Installer

Using this tool, you can define the installation manifest, which describes the desired Caché configuration instead of the installation steps. You need only to describe what you want, and Caché will automatically generate the necessary code to modify the environment for you. Therefore, you should distribute only the manifest itself, while all the installation code will be generated for the specific Caché server at compile time.

To define a manifest, create a new XData block with detailed description of the target configuration and then implement a method to generate Caché ObjectScript  code for this XData block (this code is always the same). Once the manifest is ready, you can access it from a terminal or Caché ObjectScript code or  automatically during Caché installation. The manifest must be executed in the %SYS namespace. Manifests can handle both system parameters (superport, OS, mgr directory, etc.) and arbitrary user-supplied parameters. In general, each installation class must meet the following requirements:

• Contain a link to %occInclude.inc
• Contain an XData block with the Caché server configuration
• The block can be have any valid name
• Add [XMLNamespace = INSTALLER] after the block's name if you want to see prompts from Studio
• The root item (there must only be  one) is called <Manifest> and comprises all other items
• You also need to implement the setup() method which will generate the necessary program code for the XData block.

### Installer basics

You can execute an installation manifest in several ways:

• In the %SYS namespace using a terminal or from a Caché ObjectScript code
do ##class(MyPackage.MyInstaller).setup()
• Automatically during the installation of Caché. To do this, export the installer's class into DefaultInstallerClass.xml stored in the folder with Caché installation package (i.e. where setup_cache.exe or cinstall are stored). During Caché installation, this class will be imported into the %SYS namespace and executed via the setup() method.

Example

Let's consider a simple example. Create the class called App.Installer containing an installer that will generate a new namespace with the user-defined name, create default web app and import code into this new namespace:

Include %occInclude
Class App.Installer {

/// You can see generated method in zsetup+1^App.Installer.1
XData Install [ XMLNamespace = INSTALLER ]
{
<Manifest>
<If Condition='(##class(Config.Namespaces).Exists("${Namespace}")=0)'> <Log Text="Creating namespace${Namespace}" Level="0"/>
<Namespace Name="${Namespace}" Create="yes" Code="${Namespace}" Ensemble="0" Data="${Namespace}"> <Configuration> <Database Name="${Namespace}" Dir="${MGRDIR}${Namespace}" Create="yes"/>
</Configuration>
</Namespace>
<Log Text="End Creating namespace ${Namespace}" Level="0"/> </If> <Role Name="AppRole" Description="Role to access and use the App" Resources="%DB_CACHESYS:RW,%Admin_Secure:U" /> <Namespace Name="${Namespace}" Create="no">
<CSPApplication Url="/csp/${Namespace}" Directory="${CSPDIR}${Namespace}" AuthenticationMethods="64" IsNamespaceDefault="true" Grant="AppRole" /> <IfDef Var="SourceDir"> <Log Text="SourceDir defined - offline install from${SourceDir}" Level="0"/>
<Import File="${SourceDir}"/> </IfDef> </Namespace> </Manifest> } ///Entry point method, you need to call /// At class compile time it generate Caché ObjectScript code from the manifest /// After that you can run this installer from a terminal: /// Set pVars("Namespace")="NewNamespace" /// Set pVars("SourceDir")="C:\temp\distr\" /// Do ##class(App.Installer).setup(.pVars) ClassMethod setup(ByRef pVars, pLogLevel As %Integer = 0, pInstaller As %Installer.Installer) As %Status [ CodeMode = objectgenerator, Internal ] { Quit ##class(%Installer.Manifest).%Generate(%compiledclass, %code, "Install") } } In this example, installer performs the following actions: • Checks if a namespace with the same name as the value of Namespace variable exists (for clarity let's specify that the Namespace variable is set to NewNamespace) • If not, then logs that a new namespace called NewNamespace will be created • Defines a new namespace: • Name is NewNamespace • Creates a new namespace • Routines database is NewNamespace • Do not enable Ensemble • Globals database is NewNamespace • Creates a new database • Name is NewNamespace; • Creates it in the mgr/NewNamespace folder (note that the MGRDIR variable is available by default) • Creation of the namespace is completed and logged • Creates new role: AppRole (with resources %DB_CACHESYS:RW and %Admin_Secure:U) • Default web application /csp/NewNamespace is created (it also grants AppRole automatically) • If SourceDir variable is defined then imports all files from there into NewNamespace To run this installer in a terminal, execute the following commands: Set pVars("Namespace")="NewNamespace" Set pVars("SourceDir")="C:\temp\distr\" Do ##class(App.Installer).setup(.pVars) During execution terminal displays relevant information: 2016-02-17 19:26:17 0 App.Installer: Installation starting at 2016-02-17 19:26:17, LogLevel=0 2016-02-17 19:26:17 0 : Creating namespace NewNamespace 2016-02-17 19:26:17 0 : End Creating namespace NewNamespace 2016-02-17 19:26:17 0 : SourceDir defined - offline install from C:\temp\distr\ 2016-02-17 19:26:18 0 App.Installer: Installation succeeded at 2016-02-17 19:26:18 2016-02-17 19:26:18 0 %Installer: Elapsed time .545148s To receive even more information about what is going on specify LogLevel (from 0 (default) to 3 (raw); higher number = more information). Do ##class(App.Installer).setup(.pVars, 3) Now let's talk about what can be done in installer manifest. ### Availible nodes A manifest is composed of the following items:  Node Parent node Attributes (default values) Description Arg Invoke, Error Value – value of an argument Passes an argument into a method called via Invoke or Error ClassMapping Configuration Package – a package to be mappedFrom – name of the source database used for mapping Creates a class mapping from a database to the namespace which contains this Configuration item Compile Namespace Class – names of classes for compilationFlags – compilation flags (ck)IgnoreErrors – ignore errors (0) Compilers classes. Calls$System.OBJ.Compile(Class, Flags) Configuration Namespace Used for creating namespaces and databases. Closing tag activates mappings and updates the CPF file CopyClass Namespace Src — source classTarget — target classReplace — remove the source class (0) Copies or moves the source class definition to the target one CopyDir Manifest Src — source directoryTarget — target directoryIgnoreErrors — ignore errors (0) Copies a directory CopyFile Manifest Src — source fileTarget — target fileIgnoreErrors — ignore errors (0) Copies a file Credential Production Name – name of the access credentialsUsername – user namePassword – user passwordOverwrite – overwrite if the account already exists Creates or overrides the access credentials CSPApplication Namespace AuthenticationMethods – enabled authentication methodsAutoCompile – automatic compilation (in CSP settings)CSPZENEnabled – the CSP/ZEN flagChangePasswordPage – path to change password pageCookiePath – session cookie pathCustomErrorPage – path to custom error pageDefaultSuperclass – default superclassDefaultTimeout – session timeoutDescription – descriptionDirectory – path to CSP filesEventClass – event class nameGrant – list of roles assigned upon logging into the systemGroupById – group by Id propertyInboundWebServicesEnabled – inbound web services flagIsNamespaceDefault – Namespace Default Application flagLockCSPName – Lock CSP Name flagLoginClass – path to login pagePackageName – package name propertyPermittedClasses – permitted classes propertRecurse – recurce flag (serve subdirectories) (0)Resource – resource required to access web appServeFiles – service files propertyServeFilesTimeout – time, in seconds, of how long to cache static files.TwoFactorEnabled – two-step authenticationUrl – name of the web applicationUseSessionCookie – use cookies for the session Creates or modifies a web application. For details, refer to documentation and the Security.Applications class Database Configuration BlockSize – block size in bytes of the databaseClusterMountMode – mount the database as a part of the clusterCollation – sorting orderCreate – whether to create a new database (yes/no/overwrite (yes))Dir – directoryEncrypted – encrypt databaseEncryptionKeyID – ID of the encryption keyExpansionSize – size in MB to expand byInitialSize – initial sizeMaximumSize – maximum sizeMountAtStartup – mount upon launchMountRequired – specifies that the database MUST be successfully mounted at startup.Name – database namePublicPermissions – public permissionsResource – resourceStreamLocation – directory where the streams associated with this database go. Creates or modifies a database. For details, refer to documentation, the Config.Databases and SYS.Database classes Default Manifest Name – variable nameValue – variable valueDir – variable value (if this is a path to a folder/file) Sets the variable value (if it is not set yet) Else Manifest, Namespace Will be executed when the if statement is false Error Manifest Status – error codeSource – source of the error Throws an exception. Note that ${} and #{} syntax is not available ForEach Manifest Index – variable nameValues – list of values for the variable Collection-controlled loop GlobalMapping Configuration Global – global nameFrom – name of the database for mapping Collation – sorting order (Caché Standard) Maps a global If Manifest, Namespace Condition – a conditional statement Conditional if statement IfDef Manifest, Namespace Var – variable name Conditional if statement used when the variable is already set IfNotDef Manifest, Namespace Var – variable name Conditional if statement used when the variable is not set yet Import Namespace File – file/folder for importFlags — compilation flags (ck)IgnoreErrors — ignore errors (0)Recurse – import recursively (0) Imports files. Calls:$System.OBJ.ImportDir(File,,Flags,,Recurse) and $System.OBJ.Load(File, Flags) Invoke Namespace Class – class nameMethod – method nameCheckStatus – check the returned statusReturn – write the result into a variable Makes a call to a method of a class with various arguments and returns the execution results LoadPage Namespace Name – path to the CSP pageDir – folder with CSP pagesFlags — compilation flags (ck)IgnoreErrors — ignore errors (0) Loads CSP file using$System.CSP.LoadPage(Name, Flags) and $System.CSP.LoadPageDir(Dir, Flags) Log Manifest Level – logging level from 0 (minimum) up to 3 (detailed)Text – string up to 32,000 characters in length Adds a message to a log when the logging level is greater or equal to the "level" attribute Manifest Root item. The only one root item in a manifest; contains all other items Namespace Manifest Name – name of the namespaceCreate – whether to create a new namespace (yes/no/overwrite (yes))Code – database for program codeData – databaseEnsemble – enable Ensemble for the namespaceAll other attributes are applicable to the Ensemble web applications Defines the installer's scope Production Namespace Name – production nameAutoStart – automatic launch of the production Configures the Ensemble production Resource Manifest Name – resource nameDescription – resource descriptionPermission – public permissions Creates or modifies a resource. Role Manifest Name – name of the roleDescription – role description (can't contain commas)Resources – resourcesgiven to role, represented as "MyResource:RW,MyResource1:RWU" RolesGranted – whether to grant the corresponding roles Creates a new role RoutineMapping Configuration Routines – routine nameType – routines type (MAC, INT, INC, OBJ or ALL)From – source database Creates a new mapping for routines Setting Production Item – configurable itemTarget – parameter type: Item, Host, AdapterSetting – parameter nameValue – parameter value Configures an item in the Ensemble production Makes a call to the Ens.Production:ApplySettings method SystemSetting Manifest Name – class.property of the Config packageValue – attribute value Sets the values for attributes of the Config package (using the Modify method) User Manifest Username – user namePasswordVar – variable containing the passwordRoles – list of user's rolesFullname – full nameNamespace – startup namespaceRoutine – starting routineExpirationDate – date after which the user will be deactivatedChangePassword – change the password upon next login into the systemEnabled – whether the user is activated Creates or modifies a user. Var Manifest Name — variable nameValue – variable value Assigns a value to the variable ## Variables User-supplied variables Some attributes can contain expressions (strings) that are expanded when the manifest is executed. There are three types of expressions that can be expanded, as follows: •${<Variable_name>} – value of the variable (user-defined or environment variable; see below) is calculated during the execution of the manifest;
• ${#<Parameter_name>} – will be replaced with the value of the specified parameter from the installer's class during compilation; • #{<Caché_ObjectScript_code>} — the value of the specified Caché ObjectScript statement will be calculated during the execution of the manifest. Make sure to put quotation marks as required. Parameter values are defined during compilation and therefore can be a part of a variable or Caché ObjectScript statement. Since variables are interpreted before the Caché ObjectScript code, you can use them in Caché ObjectScript statements, e.g: #{$ZCVT("\${NAMESPACE}","L")}.

System variables

The following variables are always available:

 Variable Description Sample value SourceDir (Available only when the installer is run) Directory from which the installation (setup_cache.exe or cinstall) is running. /InterSystems/distr/ ISCUpgrade (Available only when the installer is run) Indicates whether this is a new installation or an upgrade. This variable is either  0 (new installation) or 1  (upgrade). 0 (installation)1 (update) CFGDIR See INSTALLDIR. /InterSystems/Cache/ CFGFILE Path to the CPF file /InterSystems/Cache/cache.cpf CFGNAME Name of the instance CACHE CPUCOUNT Number of CPU cores 4 CSPDIR The CSP directory /InterSystems/Cache/csp/ HOSTNAME Name of the web server SCHOOL15 HTTPPORT Port of the web server 80 INSTALLDIR Directory where Caché is installed /InterSystems/Cache/ MGRDIR Management directory (mgr) /InterSystems/Cache/mgr/ PLATFORM Operating system UNIX PORT Port of the Caché superserver 1972 PROCESSOR Name of the platform x86-64 VERSION Version of Caché 2015.1.1

### Debugging

Sometimes it may be hard to understand what values can be assigned as node attributes' values. To figure this out, check the generated int code for the setup method. In most cases, the main call is made to the tInstaller.<ElementName> which is an object of the %Installer.Installer class which, in turn, will make direct calls to the system methods. Alternatively, you can check the code of the %Installer.<ElementName> class in which the node attributes are class properties. The program code is generated in the %OnBeforeGenerateCode, %OnGenerateCode and %OnAfterGenerateCode methods.

For debugging purposes, I recommend that you wrap a call to the installer into a transaction. For example, you can use the TSTART/TROLLBACK commands to easily undo all changes made within Caché  (however, external changes, such as creating a new database file, will not be reverted).

Lastly, don't forget to set LogLevel to 3.

### Examples

The MDX2JSON project provides an installer. To install the project, import the installer.xml file containing the MDX2JSON.Installer class into any namespace. You can perform import either from the SMP or by drag&dropping the file into Studio.
Then run the following command in a terminal:

do ##class(MDX2JSON.Installer).setup()

As a result, Caché will load application files from the GitHub repository and then perform installation in the default MDX2JSON namespace/MDX2JSON database, map the MDX2SJON package to %All and SAMPLES, map the ^MDX2SJON global to %All and SAMPLES  create the REST application called /MDX2JSON, and so on – you will see all these steps in the terminal. For more detailed information on MDX2JSON installer, please refer to the project readme.

Even more examples

Example from the documentation.
The Sample.Installer class in the Samples namespace.
The CacheGitHubCI projects provides an installer.
The SYSMON Dashboards project provides an installer.
The DeepSee Audit project provides an installer.

### Summary

%Installer is a convenient tool for distributing and deploying applications based on InterSystems Caché and Ensemble.

### References

Documentation

15
3 3,772
Discussion (9)4

Great article.

I like to use this approach with CacheUpdater tool. It adds a Task in Caché with parameters of github repo URL and credentials if needed.

So if I need to update the app  with new code from github repo I just run the task.

Hi David! How does HL7 schema look like? Is it a class or Global or what?

How do you import an HL7 Schema from ObjectScript?

You can call any method with Invoke.

I'm not aware of anything HL7 specific in %Installer, but maybe HealthShare has something.

I see.

So I guess I should use EnsLib.HL7.SchemaDocument class https://docs.intersystems.com/irisforhealthlatest/csp/documatic/%25CSP.Documatic.cls?APP=1&LIBRARY=ENSLIB&CLASSNAME=EnsLib.HL7.SchemaDocument but can't find an example of how import/export programatically.

In response to @Evgeny Shvarov I am talking about Custom HL7 Schemas (https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.cls?KEY=EHL72_tools#EHL72_customhl7schema_editor) in IRIS for Health. Use custom schemas is something very usual and I think that to include this import feature in Installer could be very useful... I'll try to make it works and if I am success I'll post the how-to :-D

##class(EnsLib.EDI.XML.SchemaXSD).Import("C:\path\to\schema.xsd")

Do auto install using DefaultInstallerClass.xml wotk with IRIS For Health? In my expirience it does not. But after IRIS is installed I can manualy import the DefaultInstallerClass.xml and run the setup method in terminal and it works.

That is how my install package looks:

Maybe I'm missing something.