encode special characters using maven-antrun-plugin

Recently I was deploying an java web application on heroku, but one of the fields, which were picked up from system properties to be used during the maven build to set the credentials of the datasource, contained a non escaped character, which brought the deployment to fail:

21:37:17,355 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-7) MSC000001: Failed to start service jboss.deployment.unit."ROOT.war".PARSE: org.jboss.msc.service.StartException in service jboss.deployment.unit."ROOT.war".PARSE: WFLYSRV0153: Failed to process phase PARSE of deployment "ROOT.war"

        at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:154)

        at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1948)

        at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1881)

        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)

        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)

        at java.lang.Thread.run(Thread.java:748)

Caused by: org.jboss.as.server.deployment.DeploymentUnitProcessingException: Unexpected character '<' (code 60) (expected a name start character)

 at [row,col {unknown-source}]: [25,26]

        at org.jboss.as.connector.deployers.ds.processors.DsXmlDeploymentParsingProcessor.deploy(DsXmlDeploymentParsingProcessor.java:105)

        at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:147)

        ... 5 more

Caused by: com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character '<' (code 60) (expected a name start character)

 at [row,col {unknown-source}]: [25,26]

        at com.ctc.wstx.sr.StreamScanner.throwUnexpectedChar(StreamScanner.java:647)

        at com.ctc.wstx.sr.StreamScanner.parseFullName(StreamScanner.java:1933)

        at com.ctc.wstx.sr.StreamScanner.parseEntityName(StreamScanner.java:2057)

        at com.ctc.wstx.sr.StreamScanner.fullyResolveEntity(StreamScanner.java:1525)

        at com.ctc.wstx.sr.BasicStreamReader.nextFromTree(BasicStreamReader.java:2748)

        at com.ctc.wstx.sr.BasicStreamReader.next(BasicStreamReader.java:1073)

        at com.ctc.wstx.sr.BasicStreamReader.getElementText(BasicStreamReader.java:670)

        at org.jboss.jca.common.metadata.common.AbstractParser.rawElementText(AbstractParser.java:166)

        at org.jboss.jca.common.metadata.common.AbstractParser.elementAsString(AbstractParser.java:153)

        at org.jboss.jca.common.metadata.ds.DsParser.parseDataSource(DsParser.java:1149)

        at org.jboss.jca.common.metadata.ds.DsParser.parseDataSources(DsParser.java:177)

        at org.jboss.jca.common.metadata.ds.DsParser.parse(DsParser.java:120)

        at org.jboss.jca.common.metadata.ds.DsParser.parse(DsParser.java:79)

        at org.jboss.as.connector.deployers.ds.processors.DsXmlDeploymentParsingProcessor.deploy(DsXmlDeploymentParsingProcessor.java:90)

        ... 6 more

To come around this problem, we need to encode those characters as proper xml entities. So here is how I did this:

First, introduce the system property as maven property inside of the property section:

 <properties>
<db.url>${JDBC_DATABASE_URL}</db.url>
<db.user>${JDBC_DATABASE_USERNAME}</db.user>
<db.password>${JDBC_DATABASE_PASSWORD}</db.password>
</properties>

Second, define the maven-antrun-plugin as plugin to be executed during validate phase, the phase, before even resources are copied or filtered by maven. Important is that the flag exportAntProperties is set to true. I was not able to override an existing property, therefore I created new ones.

<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>validate</phase>
<configuration>
<exportAntProperties>true</exportAntProperties>
<target>
<script language="javascript">
<![CDATA[ 

if (!String.prototype.encodeHTML) {
String.prototype.encodeHTML = function () {
return this.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;');
};
}

project.setProperty("enc.db.url",project.getProperty("db.url").encodeHTML());
project.setProperty("enc.db.user",project.getProperty("db.user").encodeHTML());
project.setProperty("enc.db.password",project.getProperty("db.password").encodeHTML());

]]>
</script>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>

Third, use the properties in any resouce file while filtering:

<datasources xmlns="http://www.jboss.org/ironjacamar/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.jboss.org/ironjacamar/schema http://docs.jboss.org/ironjacamar/schema/datasources_1_0.xsd">
<!-- The datasource is bound into JNDI at this location. We reference
this in META-INF/persistence.xml -->
<datasource jndi-name="java:jboss/datasources/GreeterQuickstartDS"
pool-name="greeter-quickstart" enabled="true" use-java-context="true">
<connection-url>${enc.db.url}</connection-url>
<driver>postgresql</driver> 
<security>
<user-name>${enc.db.user}</user-name>
<password>${enc.db.password}</password>
</security>

</datasource>
</datasources>

See the complete code example at my github repo https://github.com/mwiede/greeter.