Question
· Mar 28, 2017

%Installer Manifest: How to ignore several kinds of compilation errors?

Due to a new project I'm getting involved inside %Installer stuff deeper than ever I'd be happy to. Most of it was pretty easy to get, thanks to documentation, articles and SAMPLES example, while I can't dig good solution for one problem. I'm trying to import and compile some classes, willing to ignore two types of errors: 5202 (NothingToCompile) and 5373 (PredecessorClassDoesNotExist - a normal case when SNMP sampling class is compiled for the 1st time, before it was registered). 

I've tried the following: 

<If Condition='#{##class(%File).Exists("${AddonDir}/AddClasses.xml")}'>
  <Import File="${AddonDir}/AddClasses.xml" IgnoreErrors="5202,5373" Flags="ck" />
</If>

While it was compiled w/o errors,  the mainfest execution failed with error: 

03/28/17-16:20:29:521 (2728) 2 Error: #5002: <PARAMETER>zImport^%Installer.Installer.1

I've found a code inside %Installer.Installer::Import(): 

If $$$ISERR(tSC),(pIgnoreErrors=1)||((","_pIgnoreErrors_",")[(","_$P($system.Status.GetErrorCodes(tSC),",")_",")) {
  Do ..Log(2,"Import","Ignoring Errors: ("_$P($system.Status.GetErrorCodes(tSC),",")_") "_$system.Status.GetErrorText(tSC))
  Set tSC = $$$OK
}

which reassured me that IgnoreErrors can be a comma separated list.

I understand that I can split my classes' export in two files and ignore all errors for 'buggy' one of them by setting IgnoreErrors="1"but still eager to know if more elegant solution exists.

Thank you.

Discussion (11)0
Log in or sign up to continue

Hi Timothy and Sergey,

Timothy, putting the literal codes list inside a variable doesn't help much: I'm getting the same error. 

Thank you anyway.

P.S. The reason of the issue is a wrong code for variable evaluation, e.g. 

 <Var Name="AddClassesErrors" Value="5202,5373" />

is transformed to

 Do tInstaller.SetVariable("AddClassesErrors","5202","5373")

This just looks like a bug - the generated code is:

 Do tInstaller.Import(tNSName,tInstaller.Evaluate("${AddonDir}/AddClasses.xml"),"ck","5202","5373","0")

While you would expect:

 Do tInstaller.Import(tNSName,tInstaller.Evaluate("${AddonDir}/AddClasses.xml"),"ck","5202,5373","0")

Here's a possible workaround (untested, but the generated code looks better):

<Var Name="AddClassesErrors" Value="5202,5373" />
<If Condition='#{##class(%File).Exists("${AddonDir}/AddClasses.xml")}'>
  <Import File="${AddonDir}/AddClasses.xml" IgnoreErrors="${AddClassesErrors}" Flags="ck" />
</If>

EDIT: actual workaround (see discussion below) is to use #{<COS_expression>} (see documentation).

<Var Name="AddClassesErrors" Value="#{&quot;5202,5373&quot;}" />
<If Condition='#{##class(%File).Exists("${AddonDir}/AddClasses.xml")}'>
  <Import File="${AddonDir}/AddClasses.xml" IgnoreErrors="${AddClassesErrors}" Flags="ck" />
</If>

That isn't valid XML - I think it'd need to be:

 <Var Name="AddClassesErrors" Value="&quot;,5202,5373,&quot;" />

The &quot; makes it smarter about it being one string, and the extra commas should make it work with the test condition in %Installer.Install:Import:

 ((","_pIgnoreErrors_",")[(","_$P($system.Status.GetErrorCodes(tSC),",")_","))

This is messy! sad

EDIT: Better option:

 <Var Name="AddClassesErrors" Value="#{&quot;5202,5373&quot;}" />

Timothy,

Both options succeded. Thank you so much! 

Just FYI, your 1st option

<Var Name="AddClassesErrors" Value="&quot;,5202,5373,&quot;" />

generated a code:

 Do tInstaller.SetVariable("AddClassesErrors",""",5202,5373,""")

and the 2nd one

<Var Name="AddClassesErrors" Value="#{&quot;5202,5373&quot;}" />

generated:

 Do tInstaller.SetVariable("AddClassesErrors",tInstaller.Evaluate("#{""5202,5373""}"))

Timothy,

Both options succeded. Thank you so much! 

Just FYI, your 1st option

<Var Name="AddClassesErrors" Value="&quot;,5202,5373,&quot;" />

generated a code:

 Do tInstaller.SetVariable("AddClassesErrors",""",5202,5373,""")

and the 2nd one

<Var Name="AddClassesErrors" Value="#{&quot;5202,5373&quot;}" />

generated:

 Do tInstaller.SetVariable("AddClassesErrors",tInstaller.Evaluate("#{""5202,5373""}"))

I've got into another issue trying to write a code which should configure some global mapping to already existing namespace. Here is its skeleton: 

 Include %occInclude

/// Create simple global mapping (main DB and cachetemp only)
Class %z.Mapping Extends %Library.RegisteredObject
{

/// Application Definition
XData qMS [ XMLNamespace = INSTALLER ]
{
<Manifest>
<If Condition='$L("${NAMESPACE}")=0'>
<!-- Report an error if the namespace wasn't specified -->
<Error Status="$$$NamespaceDoesNotExist">
<Arg Value="${NAMESPACE}"/>
</Error>
</If>

<If Condition='$zcvt("${NAMESPACE}","U")="%SYS"'>
<!-- Report an error if the namespace %SYS -->
<Error Status="$$$GeneralError">
<Arg Value="${NAMESPACE} should not be %SYS"/>
</Error>
</If>

<Invoke Class="%z.Mapping" Method="GetMainDB" CheckStatus="0" Return="MainDB" >
<Arg Value="${NAMESPACE}"/>
</Invoke>

<If Condition='$L("${MainDB}")=0'>
<!-- Report an error if the globals database was not detected -->
<Error Status="$$$GeneralError">
<Arg Value="${NAMESPACE} has no Globals database" />
</Error>
</If>

<Var Name="DBRESOURCE" Value="%DB_%DEFAULT"/>

<Namespace Name="${NAMESPACE}" Create="no" >

<Log Level="0" Text="${NAMESPACE} is ready to be configured using ${MainDB} and ${DBRESOURCE}" />
 
 <Configuration>

<!-- ??? Configure the database that should exist upto this step -->
  <Database Name="${MainDB}"
 Dir="${MainDB}"
 Create="no"
 Resource="${DBRESOURCE}"
 PublicPermissions=""
  />
<!-- ??? Configure the database that should exist upto this step -->

  <GlobalMapping Global="Q(&quot;0!&quot;):(END)"  From="CACHETEMP"/>
  <GlobalMapping Global="Q(&quot;0mseSTATUS&quot;):(&quot;0mseSTATUS~&quot;)"  From="${MainDB}"/>
  <GlobalMapping Global="Qa"     From="CACHETEMP"/>
  <GlobalMapping Global="Qi"     From="CACHETEMP"/>
  <GlobalMapping Global="Qi(&quot;MO&quot;)"     From="${MainDB}"/>
 
  </Configuration>

</Namespace>

</Manifest>
}

/// This is a method generator whose code is generated by XGL.
ClassMethod setup(ByRef pVars, pLogLevel As %Integer, pInstaller As %Installer.Installer, pLogger As %Installer.AbstractLogger) As %Status [ CodeMode = objectgenerator, Internal ]
{
#; Let our XGL document generate code for this method. 
Quit ##class(%Installer.Manifest).%Generate(%compiledclass, %code, "qMS")
}

ClassMethod GetMainDB(pNamespace) As %String
{
new $namespace $namespace="%SYS"
sc=##class(Config.Namespaces).Get(pNamespace,.p)
if 'sc $system.OBJ.DisplayError(sc) set ret="" gmdbq
ret=p("Globals")
gmdbq
quit ret
}

/// Invoke the installer passing in some variables
/// zn "USER1" s sc=$system.OBJ.Load("mapping.xml","ck") d:'sc $system.Status.DisplayError(sc)
/// s sc=##class(%z.Mapping).RunInstallWithLog($zu(12)_"mapping.log",2) d:'sc $system.Status.DisplayError(sc)
/// 
ClassMethod RunInstallWithLog(pLogfile As %String, pLogLevel = 1) As %Status
{
#dim tVars
#dim tStatus As %Status

Set tVars("NAMESPACE") = $zu(5)

// Construct a file logger
#dim tLogger As %Installer.FileLogger = ##class(%Installer.FileLogger).%New(1,pLogfile)

// Invoke the installer
Set tStatus = ..setup(.tVars,pLogLevel,,tLogger)
Do:$$$ISERR(tStatus) $system.OBJ.DisplayError(tStatus)
Quit tStatus
}

}

I've started it at the same Cache instance where I wrote it with following commands:

zn "USER1" s sc=##class(%z.Mapping).RunInstallWithLog($zu(12)_"mapping.log",2) d:'sc $system.Status.DisplayError(sc)

And it works. But if I'm excluding an [unneeded] code fragment that resides between two lines:

<!-- ??? Configure the database that should exist upto this step -->

I'm getting several errors, please see below. Why this fragment is still needed nevertheless I marked my namespace with Create="no" attribute?

​2017-03-31 19:24:51 0 %z.Mapping: Installation starting at 2017-03-31 19:24:51, LogLevel=2
2017-03-31 19:24:51 0 : USER1 is ready to be configured using USER1 and %DB_%DEFAULT
2017-03-31 19:24:51 1 CreateNamespace: Creating namespace USER1 using /
2017-03-31 19:24:51 2 CreateNamespace: Modifying namespace USER1
2017-03-31 19:24:51 0 %z.Mapping: ERROR #5002: Cache error: <SUBSCRIPT>%LoadData+6^Config.Databases.1 ^SYS("CONFIG","CACHE","Databases","")
ERROR #5659: Property 'Config.Namespaces::Globals(3@Config.Namespaces,ID=CACHE||Namespaces||USER1)' required
ERROR #7202: Datatype value '' length less than MINLEN allowed of 1
ERROR #5659: Property 'Config.Namespaces::Routines(3@Config.Namespaces,ID=CACHE||Namespaces||USER1)' required
ERROR #7202: Datatype value '' length less than MINLEN allowed of 1
2017-03-31 19:24:51 0 %z.Mapping: ERROR #ConfigFailed: Unknown status code: <Ins>ConfigFailed (,,,,,,,)
  > ERROR #5002: Cache error: <SUBSCRIPT>%LoadData+6^Config.Databases.1 ^SYS("CONFIG","CACHE","Databases","")
  > ERROR #5659: Property 'Config.Namespaces::Globals(3@Config.Namespaces,ID=CACHE||Namespaces||USER1)' required
  > ERROR #7202: Datatype value '' length less than MINLEN allowed of 1
  > ERROR #5659: Property 'Config.Namespaces::Routines(3@Config.Namespaces,ID=CACHE||Namespaces||USER1)' required
  > ERROR #7202: Datatype value '' length less than MINLEN allowed of 1
2017-03-31 19:24:51 0 %z.Mapping: Installation failed at 2017-03-31 19:24:51
2017-03-31 19:24:51 0 %Installer: Elapsed time .178622s