44

I just want to use maven placeholder in my Java class at compile time in order to reduce duplication.

Something like that:

pom.xml

<properties>
  <some.version>1.0</some.version>
</properties>

SomeVersion.java

package some.company;

public class SomeVersion {

    public static String getVersion() {
        return "${some.version}"
    }

}
Donald Duck
  • 8,409
  • 22
  • 75
  • 99
Dmytro Chyzhykov
  • 1,814
  • 1
  • 20
  • 17

4 Answers4

66

simply create file app.properties in src/main/resources with content like this

application.version=${project.version}

then enable maven filtering like this

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>

That's all - in app code just read properties file

ClassPathResource resource = new ClassPathResource( "app.properties" );
p = new Properties();
InputStream inputStream = null;
try {
    inputStream = resource.getInputStream();
    p.load( inputStream );
} catch ( IOException e ) {
    LOGGER.error( e.getMessage(), e );
} finally {
    Closeables.closeQuietly( inputStream );
}

and provide method like this

public static String projectVersion() {
    return p.getProperty( "application.version" );
}
alex
  • 8,904
  • 6
  • 49
  • 75
Andrey Borisov
  • 3,160
  • 18
  • 18
  • 1
    +1 - this is definitely better than feeding the source code through a preprocessor. – Stephen C Jul 31 '12 at 13:22
  • 5
    Isn't `ClassPathResource` spring-specific? OP did not include the `spring` tag. – fegemo Jul 22 '16 at 18:14
  • This approach, in my own view, is better than the uses of **properties-maven-plugin** (It works too) described [here](https://stackoverflow.com/a/26589696/3072683) – aspadacio Sep 25 '17 at 18:50
  • The property in my case is returning value as ${project.version} Also I have to files in my resource folder but I am reading only one of them in which is i have written version=${project.version} – Nitish Kumar Oct 23 '17 at 08:01
  • 2
    Just for the heads up ClassPathResource works with spring only. If you want to implement this logic in plain java it won't work – Nitish Kumar Oct 23 '17 at 10:35
  • @NitishKumar maybe you get this cause you skipped the pom file part (setting the file in to be filtered). @fegemo you can do it with simple java with `getClass.getClassLoader.getResourceAsStream("app.properties")` – Ohad Bitton Nov 06 '19 at 10:55
  • This might actually be a dangerous thing if using build pipelines because your files will always change on remote repositories, creating conflicts later one when pushing changes – TheRealChx101 May 23 '20 at 07:11
  • In my code I already write one resource ein webresource tag, when I tried to add another resource with your code it gives me error, do you know any solution? – Sonn Jan 19 '23 at 18:07
10

Even though it's not a very nice solution it is possible with the default maven resource plugin.

First you need to specify the resource plugin.

<project>
  <build>
    <!-- Configure the source files as resources to be filtered
      into a custom target directory -->
    <resources>
      <resource>
        <directory>src/main/java</directory>
        <filtering>true</filtering>
        <targetPath>../filtered-sources/java</targetPath>
      </resource>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
    </resources>
  </build>
</project>

Afterwards you will need to change the 'default' configuration of the compiler plugin.

<project>
  <build>
      <!-- Overrule the default pom source directory to match
            our generated sources so the compiler will pick them up -->
      <sourceDirectory>target/filtered-sources/java</sourceDirectory>
  </build>
</project> 
Jeroen
  • 3,076
  • 1
  • 17
  • 16
  • I agree it's not the best or perhaps the most proper way of doing it, but it is *a* way of doing it. – Jeroen Jul 31 '12 at 14:08
  • @Jeroen yes, I agree with you. I can't reproduce that way, but it makes my knowledge wider. Thanks for the answer. – Dmytro Chyzhykov Jul 31 '12 at 14:11
  • 2
    To do this, use the templating-maven-plugin. It does some of the things included in the maven-resources-plugin, but without the burden of writing tens of XML lines. See http://mojo.codehaus.org/templating-maven-plugin/ and my answer on another SO question: http://stackoverflow.com/a/18452939/345845 – Baptiste Mathus Jan 02 '14 at 10:18
5

The simplest way I know of doing that is to use Templating Maven Plugin.

Add plugin declaration to your pom:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>templating-maven-plugin</artifactId>
    <version>1.0.0</version>
    <executions>
        <execution>
            <id>filter-src</id>
            <goals>
                <goal>filter-sources</goal><!-- add this if you filter main sources -->
                <goal>filter-test-sources</goal><!-- add this if you filter test sources -->
            </goals>
        </execution>
    </executions>
</plugin>

If you're filtering main sources:

  • Create folder src/main/java-templates
  • Move the source files you want to be filtered to that folder. Reproduce package tree structure, as if you were in src/main.

If you're filtering tests sources too:

  • Create folder src/test/java-templates
  • Move the source files you want to be filtered to that folder. Reproduce package tree structure, as if you were in src/test.

Assuming that your sources contain valid placeholders like:

package some.company;

public class SomeVersion {

    public static String getVersion() {
        return "${project.version}"
    }

}

Now when you compile or test your project, those placeholders should be already valued.

Hope it helps.

aymens
  • 714
  • 9
  • 7
3

If you are working with Spring, you can inject a property. The steps are:

  1. Inside POM file you define all profiles needed and each profile must have your custom property, in your case

<profile>
 <id>dev</id>
 <properties>
  <some.version>Dev Value</some.version>
 </properties>
</profile>
  1. In the section build of your profile, you define the filtering injection.
  2. Under the your project resources directory, you create a properties file (any mnemonic christian name) and put your prop to be injected:

custom.some.version=${some.version}

  1. On the spring-context file you define the properties placeholder and define your bean or beanProperty:

<context:property-placeholder location="classpath*:/META-INF/*.properties"/>
...
<bean id="customConfig" class="com.brand.CustomConfig">
 <property name="someVersion" value="${custom.some.version}" />
</bean>
...
  1. Create your class.
package com.brand;

public class CustomConfig {
  private String someVersion;

  public getSomeVersion() {
  return this.someVersion;
  }

  public setSomeVersion(String someVersion) {
  this.someVersion = someVersion;
  }
}
  1. Inject where you want to use. This example is with autowired bean, but you can use and autowired property too.
package com.brand.sub

public class YourLogicClass {
  @Autowired
  private CustomConfig customConfig;

  // ... your code
}

On the final compilation, you have the correct values.