This page will focus on the basics of the <xpatch> task in general. Another CustomConfigTarget will give practical advice on how the build's use of this tag can help you keep your configuration customizations separate from the Cocoon source tree, and provide separate configurations for your different environments or projects.

What is the xpatch task? The javadoc has this (and only this) to say: "Ant task to patch xmlfiles." Here's a little more definition:

Task Definition

The Cocoon build declares xpatch in init-tasks: {{{<taskdef name="xpatch" classname="XConfToolTask" classpath="${tools.tasks.dest}"/> }}} ;:The source is found in tools/src/anttasks

For use of XPatch independent of Cocoon, you have to run ant init-tasks inside the Cocoon distribution which will create a number of classes in the anttasks directory which you can copy anywhere you want, but the classpath attribute has to be adapted accordingly. - JakobFix

Xpatch element

{{{<xpatch file="path/to/file.xml"

Attribute

Description

file

The file to modify

srcdir

(optional) The base path to resolve the target file and includes. Defaults to "."

includes

Files matching this pattern will be processed by the task

;:Because this task extends org.apache.ant.MatchingTask, other attributes and nested tags are recognized. See the Ant documentation for the full list of possibilities.

Patch Files

For starters, the patch files recognized by xpatch follow the following basic rules:

''Example'' (enable-uploads.xweb)

{{{<xweb xpath="/web-app/servlet/init-param[param-name='enable-uploads']/param-value"

>true</xweb>}}} ;:Note the sensitivity to whitespace necessary in this case.

Attribute

Description

xpath

An xpath expression that specifies the context node for the patch insertion. This must return exactly one node and is required even when using insert-after.

unless

(optional) An xpath expression which if present in the target document will supress this patch. This is often necessary to avoid multiple insertions when subsequent builds do not also reset the target document.

unless-path

(optional) A proposed alternate name for unless that may make its definition clearer.

if-prop

(optional) The name of a build property which must evaluate to true (Boolean.valueOf()) or this patch file will be skipped.

remove

(optional) An xpath expression of node(s) to remove before inserting this patch. Can be an alternative method for avoiding multiple insertions. This expression can return a node-set of more than one node. All matching nodes will be removed

insert-before

(optional) An xpath expression that specifies where in the document the patch will be placed. by default, the expression is evaluated relative to the node specified by the xpath attribute. If neither this nor insert-after are specified, the node will be the last child node of the context specified in the xpath attribute.

insert-after

(optional) As above. Ignored if insert-before is specified.

add-comments

(optional) If specified, it overrides the ant task value

add-attribute

(optional) The name of an attribute to add to the context node specified by xpath. Only one attribute per patch file is supported.

value

(optional, but required if add-attribute is specified) The value to assign to attribute specified in add-attribute

add-attribute-name

(optional) Add attribute name with the specified value

replace-properties

(optional) By default, Ant properties are replaced with their values throughout the patch file. If this becomes a problem (e.g. with JEXL expressions), set replace-properties to false


Addendum

About the xpath context for the various expressions -- (MarcPortier)

- The xpath expressions in the attributes @xpath and @remove start from the context of the document-root.

- The others (@insert-before, @insert-after, @unless(-path)) start from the context pointed to by @xpath.


Example

Here's an example inserting the load-class directive (web.xml) for a database driver in case it doesn't exist already. There's a catch with the web.xml structure - you have to be rather precise where you insert another init-param element, since other elements can appear on the same level, and the order is important.

{{{<xweb unless="/web-app/servlet/init-param[param-name = 'load-class'][contains(param-value,'com.mysql.jdbc.Driver')]"

</xweb>}}}

The unless attribute checks whether there exists an init-param already referring to the mysql jdbc driver class, the xpath attribute points to the context (root) node where the init-param will be inserted, and the insert-after contains another XPath expression selecting the last already existing init-param, after which the new one will be inserted. - StevenNoels

This can lead to multiple 'load-class' entries. Does it work though? - JoergHeinicke

<xweb xpath="/web-app/servlet/init-param[param-name='load-class']/param-value"
      unless="/web-app/servlet/init-param[param-name='load-class'][contains(param-value,'com.mysql.jdbc.Driver')]"
      insert-after="text()">
          com.mysql.jdbc.Driver
</xweb>

This won't work if there is not already a 'load-class' entry. - JoergHeinicke

The next example adds the pool-grow attribute and the pool-min attribute, both with a vaule of 2, and the pool-max attribute with a value of 50 to the encodeURL transformer definition. It will not add comments to the output file.

<xmap xpath="/sitemap/components/transformers/transformer[@name='encodeURL']"
  add-attribute-pool-grow="2" add-attribute-pool-max="50" add-attribute-pool-min="2"
  add-comments="false"/>

Namespaces

I have the impression that namespaces are not supported. Consider the following patch file patch.xdy:

<xdy 
  xmlns:dy="http://www.example.org/dy/1.0"
  xpath="/dy:publications/dy:publication[@id='${dy.document.id}']"
  remove="/dy:publications/dy:publication[@id='${dy.document.id}']/dy:edition[@id='${dy.edition.id}']"
>
  <dy:edition id="${dy.edition.id}">
    <dy:title>${dy.complete.title}</dy:title>
    <dy:shorttitle>${dy.short.title}</dy:shorttitle>
    <dy:description>${dy.comment}</dy:description>
    <dy:language code="${dy.language.code}">${dy.language.name}</dy:language>
    <dy:pubdate date="${dy.edition.date}">${dy.edition.displaydate}</dy:pubdate>
  </dy:edition>
</xdy>

It is skipped consistently when using namespaces, however when removing the namespace prefixes and its declaration in the xdy root element, the patch is applied correctly. - JakobFix

XPatchTaskUsage (last edited 2009-09-20 23:43:05 by localhost)