Xpath notation supports subset of xpath notation to identify the elements to change in an XML document. The following restrictions apply:


  • All parent elements must be identified using a full path, starting from the root node.
  • Wildcards are not permitted.
  • The "|" operator is not permitted to select multiple elements.


The following are valid xpath descriptors for DeployHub:


/root/branch/element

All elements under branch.

/root/branch/element[2]

The 2nd element under branch.

/root/branch/element[@attr]

Every element under branch containing the named attribute.

/root/branch/element[@attr=val]

Every element under branch with the specified attribute set to the named value.

/root/branch/element[last()]

The last element under branch.

/root/branch/element[last()-1]

The penultimate element under branch.

/root/branch/element[position()>1]

Every element under branch apart from the first one.

/root/branch/element[position()<3]

The first two elements under branch.



The statements that can be included in the modify statement block are listed below:


Text_replace

This statement can only be included if the modifier parameter in the parent modify statement is set to "text". It is used to replace one string with another in the file specified by the file parameter in the parent modify statement.


text_replace takes two mandatory and one optional named parameter:


find

The string to locate in the source file. This is a regular expression. Mandatory.

replace

The string which will replace all occurrences of the find string in the source file. Mandatory.

line

Boolean (true/false). Optional - defaults to false. If present and set to true, sets text_replace into line processing mode – each line of the file is read and processed individually. This is useful when using regular expressions to process beginning and ends of lines.


Example: Assuming we have a file called "config.txt" which looks like this:


Stack: _STACK_

Memory: _MEMORY_

Restart: Yes


Replace the placeholders _STACK_ and _MEMORY_ with the value of attributes stored against the target Environment:


modify(file: 'config.txt', modifier: "text") {

   text_replace(find: "_STACK_", replace: "$STACK");

   text_replace(find: "_MEMORY_", replace: "$MEMORY");

}


STACK and MEMORY are attributes held against the different target Environments. This ensures that the config.txt file is changed to reflect the values specific to that Environment.


If STACK and MEMORY are attributes held against each Endpoint, you can make a Endpoint-specific copy of the config.txt file. In this case, each Endpoint will receive its own copy of the config.txt file, modified according to its own attributes.


modify(file: 'config.txt', modifier: "text", endpointspecific: true) {

   text_replace(find: "_STACK_", replace: "$STACK");

   text_replace(find: "_MEMORY_", replace: "$MEMORY");

}


In this case, an implicit loop is performed for each Endpoint in the current Endpoint list. This pushes each Endpoint in turn (along with its Endpoint attributes STACK and MEMORY) onto the stack. The file is then modified and a copy taken for each Endpoint. During a subsequent deployment, the version of the file specific to each Endpoint is deployed, so each Endpoint gets its own copy of the file with STACK and MEMORY set appropriately.


Example: Add an extra line to the end of the file:


modify(file: 'config.txt', modifier: "text") {

   text_replace(find: "_STACK_", replace: "$STACK");

   text_replace(find: "_MEMORY_", replace: "$MEMORY");

   text_replace(find: '$', value: "MaxDisk: $MAXDISK");

}


As we have not specified line processing mode, the regular expression $ refers to the end of the file. The new config directive is then added at the end of the file. Note we use single quotes around the regular expression to avoid DeployHub trying to expand the $.


Assuming MAXDISK is set to 20G in the targeted Environment the result will be something like:


Stack: 128M

Memory: 1024M

Restart: Yes

MaxDisk: 20G


Example: Add an entry to the start of the file:


modify(file: 'trans.txt', modifier: "text", serverspecific:true) {

   text_replace(find: "_STACK_", replace: "$STACK");

   text_replace(find: "_MEMORY_", replace: "$MEMORY");

   text_replace(find: '^', replace: "MaxDisk: $MAXDISK");

}


As we have not specified line processing mode, the regular expression ^ refers to the beginning of the file. The new config directive is then added at the end of the file.


Assuming MAXDISK is set to 20G in the targeted Environment the result will be something like:


MaxDisk: 20G

Stack: 128M

Memory: 1024M

Restart: Yes


Example: Use line processing mode to add an automatic comment to every line. This uses the regular expression substitution markers "(" and ")" and replaces them in the target string by using \1, \2 etc.


modify(file: 'config.txt', modifier: "text", serverspecific: true) {

   text_replace(find: "_STACK_", replace: "$STACK");

   text_replace(find: "_MEMORY_", replace: "$MEMORY");

   text_replace(

       find: '^(.*): (.*)$',

       replace: "\1: \2 // set \1 to \2",

       line: true);

}


Result:


Stack: 128M // set Stack to 128M

Memory: 1024M // set Memory to 1024M

Restart: Yes // set Restarts to Yes

Set_Text

Set_text can only be included if the modifier parameter in the parent modify statement is set to "xml". It is used to set the text of the specified XML element(s).


set_text takes two named parameters:


xpath

An xpath descriptor indicating the location(s) in the XML document of the element(s) to be modified.

value

The value of the text to add to the element(s).


Example: Given an input file "servers.xml" that looks like this:


<deployhub>

   <server name="server1" type="unix" />

   <server name="server2" type="windows" />

   <server name="server3" type="as400" />

</deployhub>


Set text for each server:


modify(file: 'servers.xml', modifier: "xml") {

   set_text(xpath: "/deployhub/server", value: "server_text");

}


Result:

<deployhub>

   <server name="server1" type="unix">server_text</server>

   <server name="server2" type="windows">server_text</server>

   <server name="server3" type="as400">server_text</server>

</deployhub>


NOTE: If the selected element(s) already have text, then that text is replaced by the set_text operation.


Example: Set text for Endpoints of type "windows":


modify(file: 'testfile.xml', modifier: "xml") {

   set_text(xpath: "/openmake/server[@type=windows]",

   value: "server_text");

}


Result:


<deployhub>

   <server name="server1" type="unix" />

   <server name="server2" type="windows">server_text</server>

   <server name="server3" type="as400" />

</deployhub>


Add_element

This statement can only be included if the modifier parameter in the parent modify statement is set to "xml". It is used to add an XML element into an XML document.


add_element takes three named parameters:


xpath

An xpath descriptor indicating the location(s) in the XML document where the element is to be inserted.

pos

A string indicating the position of the insert location relative to the xpath. Is one of:

"before"

The specified element is added before the element identified by the xpath.

"after"

The specified element is added after the element identified by the xpath.

"inside"

The specified element is added inside the element identified by the xpath.

element

The element to insert. Must be in XML syntax.


Example: Given an input file "servers.xml" that looks like this:


<deployhub>

   <server name="server1" type="unix" />

   <server name="server2" type="windows" />

   <server name="server3" type="as400" />

</deployhub>


Add a "Component" element to each Endpoint:


modify(file: 'servers.xml', modifier: "xml") {

   add_element(xpath: "/deployhub/server",

          pos: "inside", value: '<Component name="mycomp" />');

}


Result:


<deployhub>

   <server name="server1" type="unix">

       <Component name="mycomp" />

   </server>

   <server name="server2" type="windows">

       <Component name="mycomp" />

   </server>

   <server name="server3" type="as400">

       <Component name="mycomp" />

   </server>

</deployhub>


Example: Add a "Component" element to the second "server" element:


modify(file: 'servers.xml', modifier: "xml") {

   add_element(xpath: "/deployhub/server[2]",

          pos: "inside", value: '<Component name="mycomp" />');

}


Result:


<deployhub>

   <server name="server1" type="unix" />

   <server name="server2" type="windows">

       <Component name="mycomp" />

   </server>

   <server name="server3" type="as400" />

</deployhub>


Example: Add a "Component" element to each server of type "unix":


modify(file: 'servers.xml', modifier: "xml") {

   add_element(xpath: '/deployhub/server[@type=unix]',

          pos: "inside", value: '<Component name="mycomp" />');

}


Result:


<deployhub>

   <server name="server1" type="unix">

       <Component name="mycomp" />

   </server>

   <server name="server2" type="windows" />

   <server name="server3" type="as400" />

</deployhub>


Example: Add different Component elements to Endpoints of specific types:


modify(file: 'servers.xml', modifier: "xml") {

   add_element(xpath: '/deployhub/server[@type=unix]',

          pos: "inside", value: '<Component name="mycomp1" />');

   add_element(xpath: '/deployhub/server[@type=windows]',

          pos: "inside", value: '<Component name="mycomp2" />');

}


Result:


<deployhub>

   <server name="server1" type="unix">

       <Component name="mycomp1" />

   </server>

   <server name="server2" type="windows">

       <Component name="mycomp2" />

   </server>

   <server name="server3" type="as400" />

</ deployhub>



Example: Add a new "server" element:


modify(file: 'servers.xml', modifier: "xml") {

   add_element(xpath: "/deployhub /server[last()]",

          pos: "after",

          value: '<server name="server4" type="windows" />');

}


Result:


<deployhub>

   <server name="server1" type="unix" />

   <server name="server2" type="windows" />

   <server name="server3" type="as400" />

   <server name="server4" type="windows" />

</deployhub>


Example: Add a "Component" element to every Endpoint apart from the first one.


modify(file: 'servers.xml', modifier: "xml") {

   add_element(xpath: "/deployhub/server[position()>1]",

          pos: "inside", value: '<Component name="mycomp" />');

}


Result:


<deployhub>

   <server name="server1" type="unix" />

   <server name="server2" type="windows">

       <Component name="mycomp" />

   </server>

   <server name="server3" type="as400">

       <Component name="mycomp" />

   </server>

</deployhub>


Example: Create an entry specific for each Endpoint in the targeted Environment.


modify(file: 'servers.xml', modifier: "xml", serverspecific: true) {

   add_element(xpath: "/deployhub/server[last()]",

          pos: "after",

          value: "<server name='${server.name}'"

                 " type='${server.type}' />");

}


This example shows several techniques:


  1. The serverspecific flag performs an implicit loop through all the Endpoints in the current Endpoint list.
  2. A copy of the servers.xml file is taken for each Endpoint.
  3. Each Endpoint-specific copy of the servers.xml file is modified to insert an element specific to that Endpoint (in this case including its name and type taken from the $server object.
  4. Note the value string is made up of two concatenated strings.
  5. The value string is surrounded by double quotes in order that variable expansion (of the $server attributes name and type) can occur. To ensure the XML is syntactically correct, the values are surrounded by single quotes. This will not prevent variable expansion because the single quotes are effectively "escaped" by the surrounding double quotes (this is identical to the way Linux/Unix shells perform expansion). The XML parser will substitute double quotes.


Result:


Transferred to Endpoint "midtier1":


<deployhub>

   <server name="server1" type="unix" />

   <server name="server2" type="windows" />

   <server name="server3" type="as400" />

   <server name="midtier1" type="unix" />

</deployhub>


Transferred to Endpoint "fronttier1":


<deployhub>

   <server name="server1" type="unix" />

   <server name="server2" type="windows" />

   <server name="server3" type="as400" />

   <server name="fronttier1" type="windows" />

</deployhub>


Set_attribute

This statement can only be included if the modifier parameter in the parent modify statement is set to "xml". It is used to modify or create an attribute in specified element(s) in an XML document.


set_attribute takes two named parameters:


xpath

An xpath descriptor indicating the location(s) of the element and the attribute to modify.

value

The new value for the specified attribute in the selected element(s).

NOTE:The xpath must specify an attribute using @ syntax – see examples below.

Example: Given an input file "servers.xml" that looks like this:


<deployhub>

   <server name="server1" type="unix" />

   <server name="server2" type="windows" />

   <server name="server3" type="as400" />

</deployhub>


Change server2 to be of type "unix":


modify(file: 'servers.xml', modifier: "xml") {

   set_attribute(xpath: "/deployhub/server[@name=server2]/@type",

   value: "unix");

}


Result:


<deployhub>

   <server name="server1" type="unix" />

   <server name="server2" type="unix" />

   <server name="server3" type="as400" />

</deployhub>



Example: Add a new attribute to every Endpoint apart from the first one.


modify(file: 'servers.xml', modifier: "xml") {

   set_attribute(xpath: "/deployhub/server[position()>1]/@newattr",

   value: "newval");

}


Result:


<deployhub>

   <server name="server1" type="unix" />

   <server name="server2" type="windows" newattr="newval" />

   <server name="server3" type="as400" newattr="newval" />

</deployhub>


Example: Add an attribute specific for each Endpoint:


psloop {

 modify(file: 'servers.xml', modifier: "xml") {

   set_attribute(

      xpath: "/openmake/server[@name=${server.name}/@serverattr",

      value: "$serverval");

 }

}


This works by looping through each Endpoint in the current Endpoint set (using psloop). Inside the body of the loop, each Endpoint is pushed onto the stack in sequence (setting the $server object along with all the Endpoint's attributes). The element associated with the current Endpoint is then located (with the xpath /openmake/server/[@name=${server.name}] and the attribute serverattr is then set to the Endpoint’s attribute serverval.


Assuming that "server1" has an attribute "serverval" set to "val1", "server2" has an attribute "serverval" set to "val2" and "server3" has an attribute set to "val3", the result will be:


<deployhub>

   <server name="server1" type="unix" serverattr="val1" />

   <server name="server2" type="windows" serverattr="val2" />

   <server name="server3" type="as400" serverattr="val3" />

</deployhub>


Remove_element

This statement can only be included if the modifier parameter in the parent modify statement is set to "xml". It is used to remove an XML element from an XML document.


remove_element takes a single named parameter:


xpath        An xpath descriptor indicating the elements(s) in the XML document to be removed.


Example: Given an input file "servers.xml" that looks like this:


<deployhub>

   <server name="server1" type="unix" />

   <server name="server2" type="windows" />

   <server name="server3" type="as400" />

</deployhub>


Remove server2:


modify(file: 'servers.xml', modifier: "xml") {

   remove_element(xpath: "/deployhub/server[@name=server2]");

}


Result:


<deployhub>

   <server name="server1" type="unix" />

   <server name="server3" type="as400" />

</deployhub>


Example. Remove all Unix Endpoints:


modify(file: 'servers.xml', modifier: "xml") {

       remove_element(xpath: "/deployhub/server[@type=unix]");

}


Result:


<deployhub>

   <server name="server2" type="windows" />

   <server name="server3" type="as400" />

</deployhub>


Example: Ensure each Endpoint gets its own copy of the file with its own entry removed.


modify(file: 'servers.xml', modifier: "xml", serverspecific: true) {

   remove_element(xpath: "/deployhub/server[@name=${server.name}]");

}