Search code examples
javaeclipsemavenpropertiesapplication.properties

Working with the right application.properties when building maven project


I am not that experienced with maven. I am still learning and right now I am trying to do a simple project that will build for different environments populating different paths (I am also getting some guidance from chatGPT).

This is the simple structure:

enter image description here

My java file:

package com.example;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class MyApp {
    public static void main(String[] args) {
        try {
            Properties props = new Properties();
            InputStream stream = MyApp.class.getResourceAsStream("/application.properties");

            props.load(stream);
            stream.close();
            System.out.println("Path: " + props.getProperty("path.oldscores"));
            
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

and the pom:

<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
  </dependencies>


<build>
    <plugins>
        <plugin>
            <artifactId>maven-resources-plugin</artifactId>
            <version>3.2.0</version>
            <executions>
                <execution>
                    <id>copy-resources</id>
                    <phase>package</phase>
                    <goals>
                        <goal>copy-resources</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>${project.build.directory}</outputDirectory>
                        <resources>
                            <resource>
                                <directory>src/main/resources</directory>
                                <filtering>true</filtering>
                            </resource>
                        </resources>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
        <plugin>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.2.0</version>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <mainClass>com.example.MyApp</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
    </plugins>
</build>



  <profiles>
    <profile>
      <id>dev</id>
      <properties>
        <oldscores>/Users/Eclipse Workspace/TEST/my-app/oldscores.txt</oldscores>
      </properties>
    </profile>
    <profile>
      <id>prod</id>
      <properties>
        <env>prod</env>
      </properties>
    </profile>
    <profile>
      <id>test</id>
      <properties>
        <env>test</env>
      </properties>
    </profile>
  </profiles>

</project>

If I build it "mvn clean install -Pdev" I see that the application.properties in the outputDirectory specified gets populated:

enter image description here

my problem is with this line from my java file:

InputStream stream = MyApp.class.getResourceAsStream("/application.properties");

to my understanding we are still invoking the application.properties from the root (which is also confusing me because /application.properties would mean that this file is in the same location as where the pom is. If I execute this java file with this configuration I get:

Path: ${oldscores}

it is calling the application.properties from src/main/resources:

enter image description here

If I change it to:

InputStream stream = MyApp.class.getResourceAsStream("/target/application.properties");

which to me makes sense since now we are looking in root -> target -> and the properties file, I get:

enter image description here

Please help me understand where my confusion is and how I can extract the properties that get populated in the target folder or if this is even the correct way of doing this.

Thanks!


Solution

  • Class#getResourceAsStream is loading resources from the class loader (where MyApp is loaded from).

    If you include a leading /, it will look up the resources from the classpath root (or classloader root), which would be target/classes (where classes and resources are copied to) or src/main/resources. This does NOT refer to your project root.

    In order to fix it, you could try to tell Maven to move the application.properties to target/classes (where it would normally be moved to):

            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <execution>
                        <id>copy-resources</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.outputDirectory}</outputDirectory><!-- alternative: ${project.build.directory}/classes -->
                            <resources>
                                <resource>
                                    <directory>src/main/resources</directory>
                                    <filtering>true</filtering>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
    

    Resources will also be part of the application when you package it (e.g. in a JAR).

    Alternatively, If you want to access files from where you are executing the project, you can use APIs for dealing with files:

    Path path=Path.of("application.properties");
    try(InputStream is=new BufferedInputStream(Files.newInputStream(path)){
        //use is here
    }
    

    But be warned that this approach would require the file to be present when packaging the application and it will not include anything from inside the application.