views:

597

answers:

4

I have a properties file, let say my-file.properties. In addition to that, I have several configuration files for my application where some information must be filled regarding the content of my-file.properties file.

my-file.properties:

application.version=1.0
application.build=42
user.name=foo
user.password=bar

Thus, in my configuration files, I will find some ${application.version}, ${user.name} that will be replaced by their value taken in the properties file...

When I build my application using Maven2, I only need to specify the properties file and say that my resources files are filtered (as in this answer to another problem). However, I need to achieve the same thing by using only Ant.

I've seen that Ant offers a filter task. However, it forces me to use the pattern @property.key@ (i.e. @user.name@ instead of #{user.name}) in my configuration files, which is not acceptable in my case.

How can I solve my problem?

+2  A: 

Ant knows a concept named Filterchains, that is useful here. Use the ReplaceTokens-filter and specify the begintoken and endtoken as empty (normally that's '@'). That should do the trick.

Mnementh
This solution does not seems to work, at least with my attempts. begintoken and endtoken cannot be empty. I can't neither set begintoken to "${" as it is considered as a syntax error...
romaintaz
+1  A: 

One hooooooorible way to make this work, inspired by the solution of Mnementh, is with the following code:

    <!-- Read the property file -->
    <property file="my-file.properties"/>
    <copy todir="${dist-files}" overwrite="true">
        <fileset dir="${src-files}">
            <include name="*.properties"/>
        </fileset>
        <filterchain>
            <filterreader classname="org.apache.tools.ant.filters.ReplaceTokens">
                <!-- Define the begin and end tokens -->
                <param type="tokenchar" name="begintoken" value="$"/>
                <param type="tokenchar" name="endtoken" value="}"/>
                <!-- Define one token per entry in the my-file.properties. Arggh -->
                <param type="token" name="{application.version" value="${application.version}"/>
                <param type="token" name="{user.name" value="${user.name}"/>
                ...
            </filterreader>
        </filterchain>
    </copy>

Explanations:

I am using the ReplaceTokens reader to look for all $...} pattern. I cannot search for ${...} patterns, as the begintoken is a char and not a String. Then, I set the list of tokens starting with a { (i.e. I see {user.name instead of user.name). Hopefully, I have "only" about 20 lines in my-file.properties, so I need to define "only" 20 tokens in my Ant file...

Is there any simple and stupid solution to solve this simple and stupid problem??

romaintaz
+2  A: 

You can define a custom FilterReader. So you have a couple of choices:

  1. Extend/copy the org.apache.tools.ant.filters.ReplaceTokens class and define a Map property that references another properties file containing all the replacements. This is still a bit of a chore as you have to define all the replacements.
  2. Extend/copy the org.apache.tools.ant.filters.ReplaceTokens class with additional processing that just substitutes the matched token with a version with the correct garnish. Of course you'd have to be really careful where you use this type as it will match anything with the begin and end token.

So in the read() method of ReplaceTokens, replace:

final String replaceWith = (String) hash.get(key.toString());

with a call to a getReplacement() method:

...
final String replaceWith = getReplacement(key.toString);
...

private String getReplacement(String key) {
    //first check if we have a replacement defined
    if(has.containsKey(key)) {
        return (String)hash.get(key);
    }

    //now use our built in rule, use a StringBuilder if you want to be tidy
    return "$" + key + "}";
}

To use this, you'd ensure your class is packaged and on Ant's path and modify your filter:

<filterreader classname="my.custom.filters.ReplaceTokens">
    <!-- Define the begin and end tokens -->
    <param type="tokenchar" name="begintoken" value="$"/>
    <param type="tokenchar" name="endtoken" value="}"/>
    <!--Can still define explicit tokens, any not
    defined explicitly will be replaced by the generic rule -->
</filterreader>
Rich Seller
+2  A: 

I think expandproperties is what you are looking for. This acts just like Maven2's resource filters.


INPUT

For instance, if you have src directory (one of many files):

<link href="${css.files.remote}/css1.css"/>

src/test.txt


PROCESS

And in my ANT build file we have this:

<project default="default">
   <!-- The remote location of any CSS files -->
   <property name="css.files.remote" value="/css/theCSSFiles" />     
   ...
   <target name="ExpandPropertiesTest">

 <mkdir dir="./filtered"/>

 <copy todir="./filtered">

      <filterchain>
         <expandproperties/>
           </filterchain>  

                  <fileset dir="./src" />
            </copy>
</target>
</project>

build.xml


OUTPUT

*When you run the ExpandPropertiesTest target you will have the following in your filtered directory: *

    <link href="/css/theCSSFiles/css1.css"/>

filtered/test.txt

leeand00
Thank you! It was really what I was looking for...
romaintaz
@romaintaz sure thing! I hope that simplifies the build file a bit. I know it did mine.
leeand00