Change propagation

Txture's Change Propagation feature allows to define Propagation Rules (Main Menu / Admin / Propagation Rules). The Change Propagation is a powerful customization tool which allows you to propagate changes across your Assets and Links, performing arbitrary calculations along the way.

Propagation Rules View

Change Propagation is useful for several applications:

  • Root-Cause Analysis: propagate online status upwards the deployment stack
  • Calculate metrics: cost aggregation, energy consumption aggregation...
  • Location propagation: if a server is deployed in a specific security zone, it propagates this information to the applications deployed on it
  • Custom validation: an Application is only valid if there is a deployment stack for it

For a concrete example use case, please see the Propagating Online Status Example

Basic principles

When any change occurs in the Txture dataset (e.g. an import execution, a manual change on an asset...), the Change Propagation triggers for the affected element(s). The Change Propagation also triggers if a new rule is created or an existing one is updated.

Change Propagation: Basic Principle

The figure above shows the basic principles of Change Propagation in Txture. Each circle represents an asset. The asset in the center (drawn in red) has been modified (e.g. by a manual change). This element acts as the "source" of the propagation mechanism. All other elements which have propagation dependencies to this asset (highlighted in orange, labeled with 1) will be re-evaluated. In case that an asset labeled with 1 changes due to the Change Propagation, the assets which have dependencies on it (highlighted in yellow and labeled with 2) will be re-evaluated. All other assets (highlighted in gray) will remain unchanged. A propagation dependency is a dependency alongside a link. Propagation dependencies can either go in the same direction as the link, or in the inverse direction.

The evaluation of propagation rules works recursively. If an affected asset is modified by the propagation process, it is considered as changed, thus it may affect further assets.

Change Propagation overrides all other changes:

Change Propagation is a trigger mechanism which is executed after every change. This includes manual changes as well as changes by automations, such as Importers and Consolidations. This also means that the value inferred by change propagation rules will always overwrite any other change (on the target property of a rule) before the asset is saved into the repository. For example, let's assume that there is a numeric Cost property, and there is a rule to compute this cost. The Cost of My Application is currently inferred to be 500. If a user manually changes this value to 700, then change propagation will trigger, and override the Cost to be 500 again. The value 700 is never saved in the repository.

Propagation Dependencies

Propagation Dependencies

The figure above shows two examples for propagation dependencies. In Example 1, we want to infer the online status of a Virtual Machine. To do so, we depend on the online status of the Physical Machine on which it runs. We therefore have a dependency on an outgoing link. Conversely, in Example 2, in order to determine the health status of a Business Service, we depend on the health status of an Application which supports the Business Service in order to determine its own health.

The direction of propagation dependencies is configurable in the Administration user interface. Please note that propagation dependencies can only exist along an existing link. It is not possible to define a propagation dependency between two assets which are not connected by a link.

Propagation Rules

The third and final element of Change Propagation is the actual Rule Set. The rules define the behavior of the Change Propagation mechanism. In more detail, each Propagation Rule specifies:

  • The Asset Type to which it applies
  • The Property on the Asset which will receive the result of the rule evaluation
  • The Propagation Dependencies required to evaluate the rule
  • The Rule Script which specifies the evaluation details

Every rule will be applied to all instances of the Type it is attached to! This is also true for instances which are created after the rule was saved.

A Change Propagation Rule

The figure above shows an example for a rule configuration. This rule is attached to a Business Process and assigns a value to the Migrated property. In order to calculate the Migrated value for any given Business Process, we depend on the Migration Status property of the Applications which support the Business Process. This information is inferred from the content of the Rule Script. This groovy script defines how the value for the output property is determined.

Rule Management

The Propagation Rule Manager can be found in the Txture Admin Area.

Propagation Rule Manager

The rule manager lists all Propagation Rules which are currently configured in Txture. The main area (1) is a tree view which lists all rules. The top layer (by default) groups the rules by asset type. This view can also be grouped by output property via the "Group by" dropdown (2). Each group lists the number of rules it contains, as well as how many of them are currently enabled (3). If any of your rules have compile errors, they are also displayed here. All rules in a group can be enabled or disabled in bulk by using the buttons in the group header. The innermost panels each display an individual rule (4). If your rule has a description assigned, it is also shown here. This area allows you to enable or disable the rule, to delete it and to edit it in the Rule Editor.

Finally, there are two Add buttons for creating new rules. The global Add button at the very top will ask you for an Asset Type as well as an output Property. By using the Add button found within each group, either the Asset Type (if your view is grouped by Asset Type) or the output Property (if grouped by Property) is inferred automatically from context.

One Rule per Asset Type and Output Property:

For each Asset Type and Output Property, you can only define one Propagation Rule. The rule manager will not permit adding multiple rules with the same Asset Type and Output Property.

Rule Editor

Rule Editor

The rule editor allows you to create, edit, debug and test Propagation Rules. The header shows the Asset Type as well as the output Property of the current rule (1). A rule description can be added in this section as well. Below is the actual Rule Script editor (2). The code entered in this area is automatically compiled by Txture, and any errors are shown in-line. If your code compiled successfully, a green checkmark is displayed below the text area. The next section lists the dependencies of your script (4). The final section allows you to perform a test run of the rule on a selected asset.

Rule Scripts

Txture's Change Propagation is based on Rule Scripts. Scripts are written in Groovy and use the Txture API (more details below).

Statically Typed Groovy:

Rule Scripts use the Groovy scripting language. However, in order to ensure a basic safety net that the rule will work as intended, the rules will be compiled with a static typechecker which will point out any errors such as misspelled names or non-existing properties. This also entails that dynamic features of the Groovy language cannot be used.

When configuring the Script Rules, you have the full Groovy ecosystem available. However, all Rule Scripts must be pure functions in the mathematical sense. This means:

  • A Rule Script must not contain any internal state.
    • You must not define static variables
    • You must not define or access member variables
  • A Rule Script must not modify any external state.
    • You must not modify any variables outside the scope of the Rule Script itself
    • You must not make calls to external services (e.g. REST services, XML web services...)
    • You must not halt the executing thread (e.g. via Thread.sleep(), waiting for locks or other methods)
    • You must not attempt to stop any concurrent thread, and in particular you must not shut down the executing process
  • A Rule Script must not depend on any data, except for the specified links and properties.
    • You must not make calls to external services (e.g. REST services, XML web services...)
    • You must not access any file on the hard drive
    • You must not access the network
    • You must not ask for user input (via command line or otherwise)
    • You must not access any functions on the operating system level
    • You must not make use of Reflection and related technologies
  • A Rule Script must be idempotent, i.e. it must deliver the same output for the same input.
    • You must not use algorithms based on random numbers
    • You must not use algorithms based on system time

Txture will invoke the Rule Script bodies as needed, which may entail several hundred or even thousand calls per propagation evaluation. The execution order of Rule Scripts is (intentionally) not defined. Rule Scripts will be executed until further runs produce no more changes.

The value which is returned by the Rule Script body will be assigned to the output property. Therefore, the type of the returned value has to be compatible with the Property Type of the output Property (for example, if the output Property has Property Type Number, then the Rule Script must ensure to return a numeric value).

Txture Transactions & Change Propagation

Commit Protocol

Change Propagation is a step embedded in the Txture transaction protocol, as shown in the image above. Please consider the following facts carefully:

  • Change Propagation is triggered after all regular changes have been submitted. If a property value of a given asset has been changed from a to b and then back to a again (within the same transaction), a Propagation Rule will never witness the value b.

  • Change Propagation may access metadata such as the lastModifiedDate of an asset, a link, or an individual property value. However, as illustrated in the image above, the trigger which updates the lastModifiedDates occurs after the Change Propagation. So the lastModifiedDates observed by a Propagation Rule will be the previous change date, not the current one.

Change Propagation and the Last Modified Date:

While it is technically possible to create a Propagation Rule which operates on the lastModifiedDate of an asset, link or property value, it usually doesn't produce the expected outcome. The use of lastModifiedDate within a Propagation Rule is therefore strongly discouraged.

Debugging Rule Scripts

Rule Scripts can be tested against an Asset of your choice. Select the desired Asset in the Rule Editor to execute the rule against it. It is important to note that the Asset will not be modified by this process; any changes to the output Property value which would normally be performed by the rule will instead be discarded during a test run.

After performing a test run, the rule editor shows the Execution Result (which would become the new property value for the tested Asset) as well as the Logs produced by the execution. We encourage you to make use of this feature extensively to make sure your rules behave as intended.

In order to produce Logs, you can use one of two methods in your rule script:

  • log.debug(message) will log the message object to the log output. Please note that the message can be anything and does not need to be a string. Log messages produced by log.debug will be visible only in the rule editor.
  • log.info(message) behaves in a similar fashion as log.debug, except that log statemets produced by log.info will (in addition to the rule editor log ouptut) also show up in your change propagation log files when the rule script is actually executed. This can be useful for field testing and debugging, but should be used with care as rules are potentially executed very often, leading to a large number of log statements. In general, prefer log.debug(message).

Scripting API

Txture provides a Scripting API for easy interoperability with the Txture assets. A global variable named asset is accessible in every Rule Script. It always refers to the current asset for which the script is being evaluated. Users with prior programming experience may think of this variable as the this reference in a Rule Script. Starting from the asset, you can specify a path to any of the specified dependencies.

Code Names

At several points in the Scripting API, you can refer to Link targets or Property values by specifying an identifier. This identifier is the code name. The following table provides an overview of how code names are inferred from plain names.

Plain Property or Link Name Code Name Comment on Code Name
Phone (Office) phoneOffice Whitespaces, braces and special characters are eliminated in code names.
E-Mail eMail Plus, minus and other related characters are also eliminated in code names.
EMail EMail Multiple successive uppercase letters are retained (note that the property name uses no dash, but the two consecutive capital letters EM).
under_score under_score The underscore character is allowed and retained.
Mobile mobile All regular characters remain the same. Note that all properties start with a lower-case letter.
First name firstName The first character in a Property or Link code name is always lower-cased (except for successive uppercase letters, see below). Whenever a character (including whitespace) is elminated, the next character is upper case.
B2B b2B Numbers inside a name are allowed and are retained.
URL URL Multiple successive uppercase letters are retained.

Code names for Types follow the same rules, except that the first letter in a Type name is always a capital (upper-case) letter.

The following character groups are considered when determining the code name:

Character Range Treatment
a-z, A-Z Retained
0-9 (at the start of a name) Invalid for all Txture Identifiers
0-9 (inside a name) Retained
äöüßÄÖÜ Retained
_ (at the start of a name) Invalid for all Txture Identifiers
_ (inside a name) Retained
<>.,:;-+#@&%/*!?§~|=^° Eliminated
Whitespace Eliminated, next character is upper-cased
All other characters Invalid for all Txture Identifiers

Script Paths

Script Paths are provided by the Txture Scripting API. They allow you to easily access dependencies and their properties. All Script Paths start with the asset variable. All Script Paths exhibit one of the following structures:

  • Querying the Property values of the Assets itself
    • asset.props.<property name>.<conditional expression>
  • Querying the Property values of neighboring Assets
    • asset.links.<in / out>.<Link Name>.<Type Name> .props. <conditional expression>

Placeholders are written in italic. The available conditional expressions depend on the multiplicity of links and properties, as well as the Property Type used before in the path. For more information, please see the section on Conditional Expressions in Script Paths.

Propagation Rules can only depend on the Properties of an asset, or on Properties of its immediate neighbors. It is not possible to depend on assets (or their Properties) which are not directly linked to the current asset.

asset.props

Signature: OwnProperties asset.props

Returns an accessor to the properties of the current asset. From here, use a property code name (or a quoted identifier). Please note that you must declare required own properties as dependencies. Otherwise, a compile error will occur.

Returns: The own properties of the asset, limited to the ones specified in the rule.

Example:

asset.props.firstName

asset.links.in

Signature: IncomingReferences asset.links.in

Returns an accessor to the incoming links of an asset. From there, you can specify a link name, and optionally restrict to a Type.

Returns: An accessor to the incoming links of an asset.

Example:

// restricts to the "runsOn" link incoming from "VirtualMachine"
asset.links.in.runsOn.VirtualMachine
// restricts to the incoming "runs on" link, regardless of opposite Type
asset.links.in.runsOn

asset.links.out

Signature: OutgoingReferences asset.links.out

Returns an accessor to the outgoing links of an asset. From there, you can restrict by Type, or specify a link name.

Returns: An accessor to the outgoing links of an asset.

Example:

// restricts to the "runsOn" links which lead to an instance of "VirtualMachine"
asset.links.out.runsOn.VirtualMachine
// restricts to the "runsOn" links, regardless of the opposite Type
asset.links.out.runsOn

links.props

Signature: NeighborProperties links.props

Returns an accessor to the properties of the current asset collection. Note that this collection will be multiplicity-many, even if the property is multiplicity-one, if the preceding link is multiplicity-many.

Returns: An accessor to the properties of the asset collection.

Example:

// accesses the collection of "status" properties on all entities which are reachable via the outgoing "runsOn" link
asset.links.out.runsOn.props.status
// accesses the collection of "cost" properties on all entities of type Service which are reachable via the incoming "depends on" link
asset.links.in.dependsOn.Service.props.cost

links.linkProps

Signature: LinkProperties links.linkProps

Returns an accessor to the Link Properties of the current Link collection. Note that this collection will be multiplicity-many, even if the Property is multiplicity-one, if the preceding Link is multiplicity-many.

Returns: An accessor to the properties of the asset collection.

Example:

// accesses the collection of "port" Properties on all assets which are reachable via the outgoing "runsOn" link
asset.links.out.runsOn.linkProps.port
// accesses the collection of "protocol" Properties on all assets of type Service which are reachable via the incoming "depends on" link
asset.links.in.communicatesWith.Service.linkProps.protocol

Conditional Expressions in Script Paths

In order to allow for easy querying of the asset and its neighbors, both OwnProperties and NeighborProperties offer a variety of helper methods. Which methods are available depend on three factors:

  • The Property Type (Text, Number, Boolean) of the selected Property
  • The Multiplicity (single-valued or multi-valued) of the selected **Property **
  • The Multiplicity (single-valued or multi-valued) of traversed Links in the path

For example, if runsOn is a multiplicity-one Link, and the Property status is a multiplicity-one Text property, then the path

asset.links.out.runsOn.props.status

... will offer all conditional methods for single-valued Text Properties. However, if runsOn is a multiplicity-many Link, the same path expression will offer conditional methods for multiplicity-many Text Properties. Note that multiplicity-one Properties are also treated as multiplicity-many if there are multiple Links with the same name and no target Type restriction is specified. For example, if there are two multiplicity-one Links named runsOn which lead to PhysicalMachine and VirtualMachine respectively, then the following path

asset.links.out.runsOn.props.status

... will offer multiplicity-many conditional methods (because asset.links.out.runsOn refers to the union of the PhysicalMachine and the VirtualMachine on which we run on), even though both runsOn and status are multiplicity-one. However, asset.links.out.runsOn.PhysicalMachine.props.status will be multiplicity-one.

The following flowchart summarizes the decision logic for property multiplicity:

Property Multiplicity Flowchart

Skipping Assets

It can sometimes be useful to instruct a rule not to touch a particular asset under certain circumstances.

asset.skip

Signature: <Nothing> asset.skip()

Skips the asset for this rule only. The rule engine will treat the asset as if the rule had been executed on it and produced no changes. This method should always be used in conjunction with the return keyword.

Returns: Nothing. This method will always raise an internal exception which will be caught by the rule engine, causing the script execution to abort.

Example:


if(asset.name == "Don't touch me"){
  return asset.skip()
}

// proceed with regular rule code here
skip() will always cancel the rule execution:

Internally, skip() will throw an exception which is caught and handled by the rule engine. This means that your rule will never continue to be executed after calling skip(). It is therefore our convention to write return skip() instead of just skip() to indicate to a human reader of the rule body that the control flow exits the rule here. The actual results of writing just skip() or return skip() are exactly the same. It is also highly recommended to call return skip() exclusively within an if statement. A rule which unconditionally calls skip() in its body will never produce any results.

Use skip() sparingly:

While return skip() can be very useful, please be tentative in its usage. Overuse of skip() in your rules makes it a lot harder to reason about your rules and find issues within the rule code, because in many cases it will not be clear if your rule produced the wrong output, or did not run in the first place due to a (potentially misplaced) skip().

Text Methods

The following methods are available on multiplicity-one Properties which are of type Text, in addition to the methods available on all multiplicity-one Properties.

StringProperty.value()

Signature: String / List<String> StringProperty.value()

Returns the raw primitive value of this Property.

Important: Please note that this method will return null if it is invoked on a Property where isPresent(): boolean returns false! If you are uncertain if a Property is present or not, please use value(defaultValue: String): String instead.

Please note that it is usually not necessary to ever use .value(), as the majority of operations (e.g. string concatenation) can be performed on the property itself.

Returns: The raw property value. Can be null (not set), a single String or a List of Strings, depending on your path and structure.

Example:

// gets the name of our asset as string (or null if not set)
def nameAsString = asset.props.name.value(); // returns the name of the asset as String, or null if not set.

StringProperty.value(default)

Signature: String StringProperty.value(default: String)

Returns the raw primitive value of this Property, or the given default if it is unassigned.

Please note that it is usually not necessary to ever use .value(), as the majority of operations (e.g. string concatenation) can be performed on the property itself.

This method is only available on single-valued properties.

Parameters:

  • default — The default value to return if the property is unassigned.

Returns: The raw property value, or the given default if unassigned.

Example:

// gets the name of the contact person of our asset, or "John Doe" if none is set
def contactPersonName = asset.props.contactPerson.value("John Doe");

StringProperty.contains(text)

Signature: boolean StringProperty.contains(text: String)

Checks if the value at hand contains the given text (case insensitive). Will return true if the text is contained, otherwise false. Please note that this method always returns false if no value is present.

Parameters:

  • text — The text to check if it is contained in the property value.

Returns:true if the property value contains the given text, otherwise false.

Example:

// checks if the first name of our contact person contains "John"
def isJohn = asset.links.out.contactPerson.props.firstName.contains("John")
// checks if the name of our asset contains "My App"
def isMyApp = asset.props.name.contains("My App")

StringProperty.startsWith(text)

Signature: boolean StringProperty.startsWith(text: String)

Checks if the text at hand starts with the given text (case insensitive). Will return true if the text starts with the given one, otherwise false. Please note that this method always returns false if no value is present.

Parameters:

  • text — The text to check if the property value starts with this string.

Returns:true if the property value starts with the given text, otherwise false.

Example:

// checks if the first name of our contact person starts with "John"
def isJohn = asset.links.out.contactPerson.props.firstName.startsWith("John")
// checks if the name of our asset starts with "My App"
def isMyApp = asset.props.name.startsWith("My App")

StringProperty.endsWith(text)

Signature: boolean StringProperty.endsWith(text: String)

Checks if the text at hand ends with the given text (case insensitive). Will return true if the text ebdswith the given one, otherwise false. Please note that this method always returns false if no value is present.

Parameters:

  • text — The text to check if the property value ends with this string.

Returns:true if the property value ends with the given text, otherwise false.

Example:

// checks if the first name of our contact person ends with "John"
def isJohn = asset.links.out.contactPerson.props.firstName.endsWith("John")
// checks if the name of our asset ends with "My App"
def isMyApp = asset.props.name.endsWith("My App")
StringProperty.matches(regularExpression)

Signature: boolean StringProperty.matches(regularExpression: String)

Checks if the text at hand matches the given Java Regular Expression. Returns true if the text matches the given regular expression, otherwise false.

Parameters:

  • regularExpression — The regular expression to check against.

Returns:true if the property value ends with the given text, otherwise false.

Example:

// checks if the name of the operating system on which our asset runs contains the text "Windows"
def runsOnWindows = asset.links.out.runsOn.props.operatingSystem.matches(".*Windows.*")
// checks if the location name of our asset contains the text "Vienna"
def deployedInVienna = asset.props.location.matches(".*Vienna.*")
StringProperty.is(other, caseSensitive)

Signature: boolean StringProperty.is(String other, Boolean caseSensitive)

An extended version of the general is(other: Object): boolean method which accepts a second argument which indicates whether or not the match should be case sensitive. Setting the caseSensitive parameter to true is equivalent to calling is(other: Object): boolean (i.e. without a second parameter).

Parameters:

  • other — The other string to compare with.
  • caseSensitive — Use true for case-sensitive comparison, or false for case-insensitive comparison.

Returns:true if the property value is equal to the given text (taking the given case sensitivity into account), otherwise false.

Example:

// checks if our asset is running on a machine which uses windows (case insensitive)
def runsOnWindows = asset.links.out.runsOn.props.operatingSystem.is("windows", false)
// checks if the location of our asset is vienna (case sensitive)
def deployedInVienna = asset.props.location.is("Vienna", true)
StringProperty.length()

Signature: boolean StringProperty.length()

Returns the number of characters in the text at hand. Will return zero if the Property is unassigned.

Returns: The number of characters in the text property value, or zero if unassigned.

Example:

// gets the number of characters in the name of the operating system of the machine we run on
def length = asset.links.out.runsOn.props.operatingSystem.length()
// gets the number of characters in our location name
def locationNameLength = asset.props.location.length()

Number Methods

The following methods are available on multiplicity-one Properties which are of type Number, in addition to the methods available on all multiplicity-one Properties.

NumericProperty.value()

Signature: Double NumericProperty.value()

Returns the raw primitive value of this Property.

Important: Please note that this method will return null if it is invoked on a Property where isPresent(): boolean returns false! If you are uncertain if a Property is present or not, please use isPresent(defaultValue: double): double instead.

Please note that it is usually not necessary to ever use .value(), as most operations (e.g. arithmetics, comparisons...) can be performed directly on the property value.

Returns: The raw value of this property as a Double, null (unassigned) or a List<Double> (for multi-valued properties).

Example:

// gets the number of cores as a double, or null if not set.
def cores = asset.props.cores.value();

NumericProperty.value(default)

Signature: Double NumericProperty.value(Double default)

Returns the raw primitive value of this Property. If the Property is not present, the given default value will be returned instead.

Parameters:

  • The default number to return if the property is unassigned.

Returns: The raw value of this property as a Double, or the given default if unassigned.

Example:

// gets the cost associated with this asset, or 100.0 if no cost is assigned.
def cost = asset.props.cost.value(100.0);
NumericProperty.abs()

Signature: Double NumericProperty.abs()

Returns the absolute value of the stored number, as a number value (i.e. further conditional expressions can be chained). Has no effect if the Property is unassigned.

Returns: The absolute value of the stored number.

Example:

// checks if the absolute number of cores on our asset is 4
def isFourOrMinusFour = asset.props.cores.abs().is(4)
// checks if the absolute number of cores on the server we run on is 4
def isFourOrMinusFour = asset.links.out.runsOn.props.cores.abs().is(4)
NumericProperty.is(other, tolerance)

Signature: Boolean NumericProperty.is(Double other, Double tolerance)

Compares the value at hand with other, and returns true if the difference between them is less than or equal to tolerance, otherwise returns false.

For instance, if the value 3.5 is stored in a Property, then is(3, 0.5) and is(4, 0.5) both return true (the tolerance is applied both in positive and negative directions). This method will always return false if the Property is unassigned. Please note that the tolerance is an absolute value. For a tolerance which is relative to the size of the first argument, please use the method boolean isApproximately(double other, double tolerancePercent) instead.

Parameters:

  • other — The other value to compare against.
  • tolerance — The equality tolerance (as an absolute value), applied in both positive and negative direction.

Returns:true if the property value is equal to other within the given tolerance, otherwise false.

Example:

// checks if the cost on our asset is 100 with a tolerance of 0.50
def costIs100 = asset.props.cost.is(100, 0.50)
// checks if the rating on the asset we depend on is 4 with a tolerance of 0.3
def ratingIs4 = asset.props.out.dependsOn.props.rating.is(4, 0.3)
NumericProperty.isApproximately(other, tolerancePercent)

Signature: Boolean NumericProperty.is(Double other, Double tolerancePercent)

Compares the value at hand with other, and returns true if the difference between them is less than tolerancePercent of the first parameter, otherwise returns false. This method will always return false if the Property is unassigned. The tolerancePercent parameter is given in percent (i.e. a value of 100 means 100% tolerance).

Parameters:

  • other — The other value to compare against.
  • tolerancePercent — The equality tolerance (as a fraction of the other parameter), applied in both positive and negative direction.

Returns:true if the property value is equal to other within the given tolerancePercent, otherwise false.

Example:

// checks for equality with 100 with 1% tolerance
def costIs100 = asset.props.cost.isApproximately(100, 1)
// checks for equality with 4 with 5% tolerance
def ratingIs4 = asset.props.out.dependsOn.props.rating.isApproximately(4, 5)
NumericProperty.isInRange(lower, upper)

Signature: Boolean NumericProperty.isInRange(Double lower, Double upper)

Checks if the value at hand is in between the given bounds (inclusive).

Parameters:

  • lower — The lower bound in the range to check (inclusive).
  • upper — The upper bound in the range to check (inclusive).

Returns:true if the property value is within the given range, otherwise false. Unassigned values always produce false.

Example:

// checks if the cost is between 90 and 110
def costIsInRange = asset.props.cost.isInRange(90, 110)
// checks if the rating is between 3 and 5
def ratingIsBetween3And5 = asset.props.out.dependsOn.props.rating.isInRange(3, 5)
NumericProperty.isLeq(other)

Signature: Boolean NumericProperty.isLeq(Double other)

Checks if the value at hand is less than or equal to other. This method will always return false if the Property is unassigned.

Parameters:

  • other — The value to check against.

Returns:true if the property value is less than or equal to other, otherwise false. Unassigned values always produce false.

Example:

// checks if the cost is less than or equal to 75
def costIsMax75 = asset.props.cost.isLeq(75)
// checks if the rating is less than or equal to 3
def ratingIsLessThan3 = asset.props.out.dependsOn.props.rating.isLeq(3)
NumericProperty.isGeq(other)

Signature: Boolean NumericProperty.isGeq(Double other)

Checks if the value at hand is greater than or equal to other. This method will always return false if the Property is unassigned.

Parameters:

  • other — The value to check against.

Returns:true if the property value is greater than or equal to other, otherwise false. Unassigned values always produce false.

Example:

// checks if the cost is greater than or equal to 60
def costIsMin60 = asset.props.cost.isGeq(60)
// checks if the rating is greater than or equal to 4
def ratingIs4OrMore = asset.props.out.dependsOn.props.rating.isGeq(4)
NumericProperty.isLt(other)

Signature: Boolean NumericProperty.isLt(Double other)

Checks if the value at hand is (strictly) less than to other. This method will always return false if the Property is unassigned.

Parameters:

  • other — The value to check against.

Returns:true if the property value is (strictly) less than other, otherwise false. Unassigned values always produce false.

Example:

// checks if the cost is less than or equal to 75
def costIsMax75 = asset.props.cost.isLeq(75)
// checks if the rating is less than or equal to 3
def ratingIsLessThan3 = asset.props.out.dependsOn.props.rating.isLeq(3)
NumericProperty.isGt(other)

Signature: Boolean NumericProperty.isGt(Double other)

Checks if the value at hand is (strictly) greater than other. This method will always return false if the Property is unassigned.

Parameters:

  • other — The value to check against.

Returns:true if the property value is (strictly) greater than other, otherwise false. Unassigned values always produce false.

Example:

// checks if the cost is strictly greater than 60
def costIsGreaterThan60 = asset.props.cost.isGt(60)
// checks if the rating is greater than 4
def ratingIsHigherThan3 = asset.props.out.dependsOn.props.rating.isGt(3)

Boolean Methods

The following methods are available on multiplicity-one Properties which are of type Boolean, in addition to the methods available on all multiplicity-one Properties.

BooleanProperty.value()

Signature: Boolean BooleanProperty.value()

Returns the raw primitive value of this Property.

Important: Please note that this method will return null if it is invoked on a Property where isPresent(): boolean returns false! If you are uncertain if a Property is present or not, please use isPresent(defaultValue: boolean): boolean instead.

Please note that it is usually not necessary to call .value() at all; most operators on Booleans are already available on the property value itself.

Returns: The raw boolean value assigned to this property, or null if unassigned.

Example:

// returns the active state of this asset, or null if no active state is assigned.
def isActive = asset.props.active.value()

BooleanProperty.value(default)

Signature: Boolean BooleanProperty.value(default: Boolean)

Returns the raw primitive value of this Property. If the Property is not present, the given default value will be returned instead.

Parameters:

  • default — The default value to return if the property is unassigned.

Returns: The raw boolean value assigned to this property, or the given default if unassigned.

Example:

// returns the active state of this asset, or false if no active state is assigned.
def isActive = asset.props.active.value(false)

Range Methods

The following methods are available on multiplicity-one Properties which are of type Range, in addition to the methods available on all multiplicity-one Properties.

RangeProperty.contains(number)

Signature: Boolean RangeProperty.value(number: Double)

Checks if this range contains the given value. Returns true if the value is contained, otherwise false. If this Property is unassigned, this method will always return false.

Parameters:

  • number — The value to check if it is contained in the range property or not.

Returns:true if the given number is contained within the range property, otherwise false. Unassigned properties will always return false.

Example:

// checks if the value 12 is contained in the maintenanceTime range
def isInMaintenanceAtNoon = asset.props.maintenanceTime.contains(12)

RangeProperty.lower()

Signature: Double RangeProperty.lower()

Returns the lower bound of the range as a multiplicity-one Number Property. If this Property is unassigned, the returned Number Property will also be unassigned.

Returns: The lower bound of the property value range.

Example:

// checks if the value 0 is the lower bound of the maintenanceTime range
def maintenanceStartsAtMidnight = asset.props.maintenanceTime.lower().is(0)
RangeProperty.upper()

Signature: Double RangeProperty.upper()

Returns the upper bound of the range as a multiplicity-one Number Property. If this Property is unassigned, the returned Number Property will also be unassigned.

Returns: The upper bound of the property value range.

Example:

// checks if the value 0 is the upper bound of the maintenanceTime range
def maintenanceEndsAtMidnight = asset.props.maintenanceTime.upper().is(0)

upper(): NumberProperty

Returns the upper bound of the range as a multiplicity-one Number Property. If this Property is unassigned, the returned Number Property will also be unassigned.

Example

// checks if the value 0 is the upper bound of the maintenanceTime range
def maintenanceEndsAtMidnight = asset.props.maintenanceTime.upper().is(0)

Methods on Multiplicity-Many Properties

General

The following methods are available on all multiplicity-many properties (regardless of the Property Type).

Property.is(values)

Signature: Boolean Property.is(List values)

Checks if the values at hand are equal to the given list of values. Returns false if the Property is unassigned.

Parameters:

  • values — The list of values to compare against.

Returns:true if the property values are equal to the given values, otherwise false. Always false for unassigned properties.

Example:

// checks if the maintainers are "John" and "Jack"
def maintainedByJohnAndJack = asset.props.maintainers.name.is(Lists.newArrayList("John", "Jack"))

Property.value()

Signature: List Property.value()

Returns the raw value of this Property as a List. If the Property is unassigned, an empty list will be returned. The type of elements in this list depend on the Property Type.

Returns: The property value as a raw list.

Example:

// checks if the maintainers are "John" and "Jack"
def maintainers = asset.props.maintainers.name.value()

Property.size()

Signature: Integer Property.size()

Returns the size (number of elements) of the list at hand. Will be zero if the Property is unassigned.

Note: size() can also be called directly on link collections. This is especially useful to count the number of links attached to a particular asset.

Returns: The number of elements in the property value list, or zero if unassigned.

Basic Example:

// counts the names of assets on which we run
def numberOfClusterMembers = asset.links.out.runsOn.props.name.size()

Link Collections Example:

// counts the assets on which we run
def numberOfClusterMembers = asset.links.out.runsOn.size()

Property.first()

Signature: Property Property.first()

Returns the first entry from the list of present values. The returned Property has the same type as this Property, but is multiplicity-one. Note that the returned Property may be unassigned if this Property is unassigned.

Returns: The first element of the property values, wrapped in a property.

Example:

// checks if the first maintainer in the list is "Jack"
def firstMaintainerIsJack = asset.props.maintainers.first().is("Jack")

Property.last()

Signature: Property Property.last()

Returns the last entry from the list of present values as a Property. The returned Property has the same type as this Property, but is multiplicity-one. Note that the returned Property may be unassigned if this Property is unassigned.

Returns: The last element of the property values, wrapped in a property.

Example:

// checks if the last maintainer in the list is "Jack"
def lastMaintainerIsJack = asset.props.maintainers.last().is("Jack")

Property.filter(predicate)

Signature: Property Property.filter(Predicate predicate)

Returns a multiplicity-many Property which has the same values as the Property at hand, except that they are filtered by the given predicate. Predicates are formulated in Groovy by using curly braces { }. Inside the curly braces, use the keyword it to reference the current value. Please note that it will always be of the same type as the Property. For instance, if filter(...) is called on a Text Property, it will be of type String. If the Property is of type Number, it will be of type double, etc.

Parameters:

  • predicate — The predicate to filter by. Input values for which the predicate returns true will appear in the output list (i.e. will be kept), input values for which the predicate returns false will not appear in the output list (i.e. will be filtered out).

Returns: A multi-valued property containing the filtered values.

Example:

// filters the list of maintainers for all names starting with "John" and counts the result list
def maintainersNamedJohn = asset.props.maintainers.filter({ it.startsWith("John") }).size()

Property.any(object)

Signature: Boolean Property.any(T object)

Checks if any element in the list is equal to the given object, i.e. checks if the list contains the given element. Note that the passed object must match with the Type of the Property at hand. For instance, when calling any(...) on a Text Property, the passed value must be of type String. This method will always return false if the Property is unassigned. If the list is empty, this method also returns false.

The method contains(object: T): boolean is an alias for any(object: T): boolean and has exactly the same semantics.

Parameters:

  • object — The object to look for in the property values list.

Returns:true if the property values contains the given object, otherwise false. Always false if unassigned.

Example:

// checks if any of the names in the maintainers property is "John Doe"
def johnDoeIsMaintainer = asset.props.maintainers.any("John Doe")

Property.anyHaving(predicate)

Signature: Boolean Property.anyHaving(Predicate predicate)

Evaluates the given predicate on every entry of the list at hand, and returns true if the predicate evaluated to true for at least one of the entries, or false otherwise. Please note that this method will always return false if the Property is unassigned. Predicates are formulated in Groovy by using curly braces { }. Inside the curly braces, use the keyword it to reference the current value. Please note that it will always be of the same type as the Property. For instance, if anyHaving(...) is called on a Text Property, it will be of type String. If the Property is of type Number, it will be of type double, etc. This method will always return false if the Property is unassigned. If the list is empty, this method also returns false.

Parameters:

  • predicate — The predicate to filter by. If any input value causes the predicate to return true, then anyHaving will also return true.

Returns:true if any value within the property values matches the given predicate, otherwise false. Always false if unassigned.

Example:

// checks if any of the names in the maintainers property starts with "John"
def johnIsMaintainer = asset.props.maintainers.anyHaving({ it.startsWith("John") })

Property.all(object)

Signature: Boolean Property.all(T object)

Checks if all elements in the list at hand are equal to the given one, i.e. checks if the list contains only the given element (possibly multiple times). Note that the passed object must match with the Type of the Property at hand. For instance, when calling all(...) on a Text Property, the passed value must be of type String. This method will always return true if the Property is unassigned. If the list is empty, this method also returns true.

Parameters:

  • object — The object to look for in the property values list.

Returns:true if all property values are equal to the given object, otherwise false. Always true if unassigned.

Example:

// checks if all Server assets on which we run have their status set to "online"
def allServersOnline = asset.links.out.runsOn.Server.props.status.all("online")

Property.allHaving(predicate)

Signature: Boolean Property.allHaving(Predicate predicate)

Evaluates the given predicate on all entries of the list at hand, and returns true if the predicate evaluated to true for all entries, or false otherwise. Predicates are formulated in Groovy by using curly braces { }. Inside the curly braces, use the keyword it to reference the current value. Please note that it will always be of the same type as the Property. For instance, if allHaving(...) is called on a Text Property, it will be of type String. If the Property is of type Number, it will be of type double, etc. This method will always return true if the Property is unassigned. If the list is empty, this method also returns true.

Parameters:

  • predicate — The predicate to filter by. If all input values causes the predicate to return true, then anyHaving will also return true.

Returns:true if all values within the property values matches the given predicate, otherwise false. Always true if unassigned.

Example:

// checks if all Server assets on which we run have their status set to something other than "offline"
def noServersOffline = asset.links.out.runsOn.Server.props.status.allHaving({ it.equals("offline") == false })

Property.atLeast(amount, object)

Signature: Boolean Property.allHaving(int amount, T object)

Checks if at least amount entries in the list at hand are equal to the given object and returns true if this is the case, otherwise returns false. In other words, this method checks if the list contains the given object at least amount times.  Note that the passed object must match with the Type of the Property at hand. For instance, when calling atLeast(...) on a Text Property, the passed value must be of type String. This method will return false if the Property is unassigned or empty (unless amount is in addition set to zero, in which case it will return true).

Parameters:

  • amount — The number of elements in the property value which must be equal to the given object in order for the check to succeed.
  • object — The element to look for in the property values list.

Returns:true if at least amount elements in the property value are equal to the given object, otherwise false.

Example:

// checks if at least 2 Server assets on which we run have their status set to "online"
def atLeast2ServersOnline = asset.links.out.runsOn.Server.props.status.atLeast(2, "online")

Property.atLeast(percent, object)

Signature: Boolean Property.allHaving(double percent, T object)

Checks if at least percent percent of all entries in the list are equal to the given object and returns true if this is the case, or false otherwise. The percentage is given as a floating point number in a range between 0 (0%) and 1 (100%).  Note that the passed object must match with the Type of the Property at hand. For instance, when calling atLeast(...) on a Text Property, the passed value must be of type String. This method will return false if the Property is unassigned or empty.

Parameters:

  • percent — The fraction of elements in the property value which must be equal to the given object in order for the check to succeed.
  • object — The element to look for in the property values list.

Returns:true if at least percent % of elements in the property value are equal to the given object, otherwise false.

Example:

// checks if at least 50% of all Server assets on which we run have their status set to "online"
def atLeastHalfTheServersAreOnline = asset.links.out.runsOn.Server.props.status.atLeast(0.5, "online")

Property.atLeastHaving(amount, predicate)

Signature: Boolean Property.allHaving(int amount, Predicate predicate)

Evaluates the given predicate on all entries of the list at hand, and returns true if the predicate evaluated to true for at least amount entries, or false otherwise. Predicates are formulated in Groovy by using curly braces { }. Inside the curly braces, use the keyword it to reference the current value. Please note that it will always be of the same type as the Property. For instance, if atLeastHaving(...) is called on a Text Property, it will be of type String. If the Property is of type Number, it will be of type double, etc. This method will return false if the Property is unassigned or empty (unless amount is in addition set to zero, in which case it will return true).

Parameters:

  • amount — The number of elements in the property value which must match the given predicate in order for the check to succeed.
  • predicate — The predicate to apply.

Returns:true if at least amount elements in the property value match the given predicate, otherwise false.

Example:

// checks if at least 2 Server assets on which we run have their status set to something other than "offline"
def atLeast2ServersNotOffline = asset.links.out.runsOn.Server.props.status.atLeastHaving(2, { it.equals("offline") == false })

Property.atLeastHaving(percent, predicate)

Signature: Boolean Property.allHaving(double percent, Predicate predicate)

Evaluates the given predicate on all entries of the list at hand, and returns true if the predicate evaluated to true for at least percent percent of all entities, or false otherwise. The percentage is given as a floating point number in a range between 0 (0%) and 1 (100%). Predicates are formulated in Groovy by using curly braces { }. Inside the curly braces, use the keyword it to reference the current value. Please note that it will always be of the same type as the Property. For instance, if atLeastHaving(...) is called on a Text Property, it will be of type String. If the Property is of type Number, it will be of type double, etc. This method will return false if the Property is unassigned or empty.

Parameters:

  • percent — The fraction of elements in the property value which must match the given predicate in order for the check to succeed.
  • predicate — The predicate to apply.

Returns:true if at least percent % of elements in the property value match the given predicate, otherwise false.

Example:

// checks if at least 50% of all Server assets on which we run have their status set to something other than "offline"
def atLeastHalfTheServersNotOffline = asset.links.out.runsOn.Server.props.status.atLeastHaving(0.5, { it.equals("offline") == false })

Property.atMost(amount, object)

Signature: Boolean Property.allHaving(int amount, T object)

Checks if at most amount entries in the list at hand are equal to the given object and returns true if this is the case, otherwise returns false. In other words, this method checks if the list contains the given object at most amount times. Note that the passed object must match with the Type of the Property at hand. For instance, when calling atMost(...) on a Text Property, the passed value must be of type String. This method will return true if the Property is unassigned or empty (unless amount is in addition set to zero, in which case it will return false).

Parameters:

  • amount — The number of elements in the property value which must be equal to the given object in order for the check to succeed.
  • object — The object to look for in the property values list.

Returns:true if at most amount elements in the property value are equal to the given object, otherwise false.

Example:

// checks if at most 2 Server assets on which we run have their status set to "offline"
def atMost2ServersNotOnline = asset.links.out.runsOn.Server.props.status.atMost(2, "offline")

Property.atMost(percentage, object)

Signature: Boolean Property.allHaving(double percentage, T object)

Checks if at most percent percent of all entries in the list are equal to the given object and returns true if this is thie case, otherwise returns false. The percentage is given as a floating point number in a range between 0 (0%) and 1 (100%). Note that the passed object must match with the Type of the Property at hand. For instance, when calling atMost(...) on a Text Property, the passed value must be of type String. This method will return false if the Property is unassigned or empty.

Parameters:

  • percentage — The fraction of elements in the property value which may be equal to the given object in order for the check to succeed.
  • object — The object to look for in the property values list.

Returns:true if at most percentage % of elements in the property value are equal to the given object, otherwise false.

Example:

// checks if at most 50% of the Server assets on which we run have their status set to "offline"
def atMostHalfTheServersOffline = asset.links.out.runsOn.Server.props.status.atMost(0.5, "offline")

Property.atMostHaving(amount, predicate)

Signature: Boolean Property.allHaving(int amout, Predicate predicate)

Checks if at most percent percent of all entries in the list are equal to the given object and returns true if this is thie case, otherwise returns false. The percentage is given as a floating point number in a range between 0 (0%) and 1 (100%). Note that the passed object must match with the Type of the Property at hand. For instance, when calling atMost(...) on a Text Property, the passed value must be of type String. This method will return false if the Property is unassigned or empty.

Parameters:

  • amount — The number of elements in the property value which may be equal to the given object in order for the check to succeed.
  • predicate — The predicate to apply.

Returns:true if at most amount elements in the property value match the given predicate, otherwise false.

Example:

// checks if at most 2 Server assets on which we run have their status set to something other than "online"
def atMost2ServersNotOnline = asset.links.out.runsOn.Server.props.status.atMostHaving(2, { it.equals("online") == false })

Property.atMostHaving(percentage, predicate)

Signature: Boolean Property.allHaving(double percentage, Predicate predicate)

Evaluates the given predicate on all entries of the list at hand, and returns true if the predicate evaluated to true for at most percent percent of all entries, or false otherwise. The percentage is given as a floating point number in a range between 0 (0%) and 1 (100%). Predicates are formulated in Groovy by using curly braces { }. Inside the curly braces, use the keyword it to reference the current value. Please note that it will always be of the same type as the Property. For instance, if atMostHaving(...) is called on a Text Property, it will be of type String. If the Property is of type Number, it will be of type double, etc.  This method will return false if the Property is unassigned or empty.

Parameters:

  • percentage — The fraction of elements in the property value which may be equal to the given object in order for the check to succeed.
  • predicate — The predicate to apply.

Returns:true if at most percentage % of elements in the property value match the given predicate, otherwise false.

Example:

// checks if at most 50% of the Server assets on which we run have their status set to something other than "online"
def atMostHalfTheServersNotOnline = asset.links.out.runsOn.Server.props.status.atMostHaving(0.5, { it.equals("online") == false })

Property.exactly(amount, object)

Signature: Boolean Property.allHaving(int amount, T object)

Checks if exactly amount elements in the list at hand are equal to the given object. In other words, checks if the list at hand contains the given object exactly amount times. Note that the passed object must match with the Type of the Property at hand. For instance, when calling exactly(...) on a Text Property, the passed value must be of type String. This method returns false if the Property is unassigned or empty (unless amount is in addition set to zero, in which case it will return true).

Parameters:

  • amount — The number of elements in the property value which must be equal to the given object in order for the check to succeed.
  • object — The object to check if it is contained in the property values list.

Returns:true if exaclty amount elements in the property value are equal to the given object, otherwise false.

Example:

// checks if exactly 1 of the databases we depend on has its active flag set to true
def exactlyOneDatabaseActive = asset.links.out.database.props.active.exactly(1, true)

Property.exactly(percentage, object)

Signature: Boolean Property.allHaving(double percentage, T object)

Checks if exactly percent percent of all entries in the list are equal to the given object and returns true if this is thie case, otherwise returns false. The percentage is given as a floating point number in a range between 0 (0%) and 1 (100%). Note that the passed object must match with the Type of the Property at hand. For instance, when calling atMost(...) on a Text Property, the passed value must be of type String. This method will return false if the Property is unassigned or empty.

Important: due to floating point precision issues it is recommended to use exactly(amount: int, object: T): boolean instead.

Parameters:

  • percentage — The fraction of elements in the property value which must be equal to the given object in order for the check to succeed.
  • object — The object to check if it is contained in the property values list.

Returns:true if exaclty amount elements in the property value are equal to the given object, otherwise false.

Example:

// checks if exactly 50% of the databases on which we depend have their status set to "online"
def exactlyHalfTheDatabasesAreOnline = asset.links.out.database.props.status.exactly(0.5, "online")

Property.exactlyHaving(amount, predicate)

Signature: Boolean Property.allHaving(int amount, Predicate predicate)

Evaluates the given predicate on all entries of the list at hand, and returns true if the predicate evaluated to true for exactly amount entries, or false otherwise. Predicates are formulated in Groovy by using curly braces { }. Inside the curly braces, use the keyword it to reference the current value. Please note that it will always be of the same type as the Property. For instance, if exactlyHaving(...) is called on a Text Property, it will be of type String. If the Property is of type Number, it will be of type double, etc.  This method will return false if the Property is unassigned or empty (unless amount is in addition set to zero, in which case this method will return true).

Parameters:

  • amount — The number of elements in the property value which must match the given predicate in order for the check to succeed.
  • predicate — The object to check if it is contained in the property values list.

Returns:true if exaclty amount elements in the property value match the given predicate, otherwise false.

Example:

// checks if the names in the contacts Property have at least one entry containing the text "John"
def exactlyHaving = asset.props.contacts.exactlyHaving(1, { it.contains("John") })

Property.exactlyHaving(percentage, predicate)

Signature: Boolean Property.allHaving(double percentage, Predicate predicate)

Evaluates the given predicate on all entries of the list at hand, and returns true if the predicate evaluated to true for exactly percent percent of all entries, or false otherwise. The percentage is given as a floating point number in a range between 0 (0%) and 1 (100%). Predicates are formulated in Groovy by using curly braces { }. Inside the curly braces, use the keyword it to reference the current value. Please note that it will always be of the same type as the Property. For instance, if exactlyHaving(...) is called on a Text Property, it will be of type String. If the Property is of type Number, it will be of type double, etc.  This method will return false if the Property is unassigned or empty.

Important: due to floating point precision issues it is recommended to use exactlyHaving(amount: int, predicate: Predicate): boolean instead.

Parameters:

  • percentage — The fraction of elements in the property value which must match the given predicate in order for the check to succeed.
  • predicate — The object to check if it is contained in the property values list.

Returns:true if exaclty percentage % of elements in the property value match the given predicate, otherwise false.

Example:

// checks if exactly 50% of the databases we depend on are online
def exactlyHalfOfAllDatabasesAreOnline = asset.links.out.databases.props.status.exactlyHaving(0.5, { it.equals("online") })

Multi-Text Methods

Multiplicity-many Text-typed Properties do not have additional methods other than the general ones which are always available.

Multi-Number Methods

The following methods are available on multiplicity-many Properties which are of type Number, in addition to the methods available on all multiplicity-many Properties.

NumberProperty.avg()

Signature: NumberProperty NumberProperty.avg()

Returns the average of all values as a multiplicity-one Number Property. Any null values in the list are skipped for this calculation. Please note that the returned Property will be unassigned if this Property is unassigned or contains no non-null entries.

Returns: Returns the average of all numbers within this property value, as a numeric property.

Example:

// returns the average cost of all servers on which our asset runs (skipping null values), or zero if unspecified.
def averageCostOfServers = asset.links.out.runsOn.props.cost.avg().value(0)

NumberProperty.avg(defaultForNull)

Signature: NumberProperty NumberProperty.avg(double defaultForNull)

Returns the average of all values as a multiplicity-one Number Property. Any null values in the list will be replaced with defaultForNull for this calculation only. Please note that the returned Property will be unassigned if this Property is unassigned.

Parameters:

  • defaultForNull — The default value to use in the average calculation instead of null values.

Returns: Returns the average of all numbers within this property value, as a numeric property.

Example:

// returns the average cost of all servers on which our asset runs (treating null values as zero), or 100 if unspecified.
def averageCostOfServers = asset.links.out.runsOn.props.cost.avg(0).value(100)

NumberProperty.sum()

Signature: NumberProperty NumberProperty.sum()

Returns the sum of all values as a multiplicity-one Number Propertry. Any null values in this list are skipped for this calculation. Please note that the returned Property will be unassigned if this Property is unassigned or contains no non-null entries.

Returns: Returns the sum of all numbers within this property value, as a numeric property.

Example:

// returns the total cost of all servers on which our asset runs (skipping null values), or zero if unspecified.
def totalCostOfServers = asset.links.out.runsOn.props.cost.sum().value(0)

NumberProperty.sum(defaultForNull)

Signature: NumberProperty NumberProperty.sum(double defaultForNull)

Returns the sum of all values as a multiplicity-one Number Property. Any null values in the list will be replaced with defaultForNull for this calculation only. Please note that the returned Property will be unassigend if this Property is unassigned.

Parameters:

  • defaultForNull — The default value to use during sum calculation instead of null values.

Returns: Returns the sum of all numbers within this property value, as a numeric property.

Example:

// returns the total cost of all servers on which our asset runs (treating null values as 1), or 100 if unspecified.
def totalCostOfServers = asset.links.out.runsOn.props.cost.sum(1).value(100)

NumberProperty.max()

Signature: NumberProperty NumberProperty.max()

Returns the maximum of all values in this list as a mulitplicity-one Number Property. Please note that the returned Property will be unassigned if this Property is unassigned or contains no non-null entries.

Returns: Returns the maximum of all numbers within this property value, as a numeric property.

Example:

// returns the maximum cost of all servers on which our asset runs, or 0 if unspecified.
def highestServerCost = asset.links.out.runsOn.props.cost.max().value(0)

NumberProperty.min()

Signature: NumberProperty NumberProperty.min()

Returns the minimum of all values in this list as a multiplicity-one Number Property. Please note that the returned Property will be unassigned if this Property is unassigned or contains no non-null entries.

Returns: Returns the minimum of all numbers within this property value, as a numeric property.

Example:

// returns the minimum cost of all servers on which our asset runs, or 0 if unspecified.
def lowestServerCost = asset.links.out.runsOn.props.cost.min().value(0)

Multi-Boolean Methods

The following methods are available on multiplicity-many Properties which are of type Boolean, in addition to the methods available on all multiplicity-many Properties.

BooleanProperty.or(defaultValue)

Signature: Boolean BooleanProperty.or(boolean defaultValue)

Applies an or operation to all booleans in the list, returning the result. If the list is empty or unassigned, the given defaultValue will be used.

For instance, if the list at hand consists of [true, false, true], then the or operation resolves to ((true or false) or true) which is (true or true) which finally results in true.

Parameters:

  • defaultValue — The default value to use instead of null values.

Returns:true if at least one property value is true, otherwise false.

Example:

// checks if any of the servers our asset runs on is active (returning false if the list is empty)
def anyServerActive = asset.links.out.runsOn.props.active.or(false)

BooleanProperty.and(defaultValue)

Signature: Boolean BooleanProperty.and(boolean defaultValue)

Applies an and operation to all booleans in the list, returning the result. If the list is empty or unassigned, the given defaultValue will be used.

For instance, if the list at hand consists of [true, false, true], then the and operation resolves to ((true and false) and true) which is (false and true) which finally results in false.

Parameters:

  • defaultValue — The default value to use instead of null values.

Returns:false if at least one property value is false, otherwise true.

Example:

// checks if all of our servers are active (returning false if the list is empty)
def allServersActive = asset.links.out.runsOn.props.active.and(false)

BooleanProperty.xor(defaultValue)

Signature: Boolean BooleanProperty.xor(boolean defaultValue)

Applies an xor operation to all booleans in the list, returning the result. If the list is empty or unassigned, the given defaultValue will be used.

For instance, if the list at hand consists  [true, false, true], then the xor operation resolves to ((true xor false) xor true) which is (true xor true) which finally results in false.

Parameters:

  • defaultValue — The default value to use instead of null values.

Returns:true if the number of true property values is odd, otherwise false.

Example:

// checks if exactly one of our servers is active (returning false if the list is empty)
def exactlyOneServerActive = asset.links.out.runsOn.props.active.xor(false)

Multi-Range Methods

Multiplicity-many Range-typed Properties do not have additional methods other than the general ones which are always available.

Limitations

  • Propagation Rules are always applied to all scenarios. It is not possible to write rules for only one separated scenario.

FAQ

How is the execution of propagation rules triggered?

A propagation rule always keeps the assets up to date:

  • When a new rule is created, the entire rule set is applied to all assets.
  • Every time a rule is changed, the rule set is reapplied to all assets.
  • Every change to an asset triggers the execution of the rule set again.