Maven (Part 4) - Adding and Filtering Resources
Maven (Part 4) - Adding and Filtering Resources

Maven (Part 4) - Adding and Filtering Resources

in
  1. Adding Resources to JAR
  2. Filtering Resource Files

Adding Resources to JAR

In Maven, another common use case that can be achieved without altering the POM above is to package resources within the JAR file. For this common task, Maven relies again on the Standard Directory Layout, meaning that by using the standard Maven convention, you simply need to place these resources within the standard directory structure to have them packaged into the JAR.

As seen in the example below, we’ve added a directory ${project.basedir}/src/main/resources, where we can place any resources we want to be packaged into the JAR. The simple rule Maven follows is this: any directory or file placed within the ${project.basedir}/src/main/resources directory will be packaged into the JAR and will have the exact same structure starting from the bottom of the JAR.

my-app
|-- pom.xml
`-- src
    |-- main
    |   |-- java
    |   |   `-- com
    |   |       `-- mycompany
    |   |           `-- app
    |   |               `-- App.java
    |   `-- resources
    |       `-- META-INF
    |           `-- application.properties
    `-- test
        `-- java
            `-- com
                `-- mycompany
                    `-- app
                        `-- AppTest.java

So, as you can see in our example, we have a META-INF directory containing an application.properties file. If you unzip the JAR created by Maven and inspect it, you’ll see something like this:

|-- META-INF
|   |-- MANIFEST.MF
|   `-- application.properties
|   `-- maven
|       `-- com.mycompany.app
|           `-- my-app
|               |-- pom.properties
|               `-- pom.xml
`-- com
    `-- mycompany
        `-- app
            `-- App.class

As you can observe, the contents of ${project.basedir}/src/main/resources can be found starting from the bottom of the JAR, and our application.properties file is located within the META-INF directory. You’ll also notice other files such as META-INF/MANIFEST.MF, and pom.xml and pom.properties. These are standard configurations generated by Maven when building the JAR. You can choose to create your own manifest, but if you don’t, Maven will generate a default one. (pom.xml and pom.properties are packaged in the JAR, so every artifact generated by Maven is self-descriptive, and you can also use metadata in your own applications if needed. A simple use case might be retrieving the version of the application. Manipulating the POM files requires some Maven tools, but you can use standard Java APIs to use properties, as shown below:

# Generated by Maven
# Tue Oct 04 15:43:21 GMT-05:00 2005
version=1.0-SNAPSHOT
groupId=com.mycompany.app
artifactId=my-app

To add resources to the classpath for unit tests, follow the same pattern as adding resources to the JAR, except the directory where resources are placed is ${project.basedir}/src/test/resources. At this point, you’ll have a project directory structure like this:

my-app
|-- pom.xml
`-- src
    |-- main
    |   |-- java
    |   |   `-- com
    |   |       `-- mycompany
    |   |           `-- app
    |   |               `-- App.java
    |   `-- resources
    |       `-- META-INF
    |           |-- application.properties
    `-- test
        |-- java
        |   `-- com
        |       `-- mycompany
        |           `-- app
        |               `-- AppTest.java
        `-- resources
            `-- test.properties

In your unit tests, you can access resources required for testing using a simple code snippet like this:

// ...
// Retrieve the resource
InputStream is = getClass().getResourceAsStream("/test.properties");
// Do something with the resource
// ...

Filtering Resource Files

Sometimes, resource files need to contain a value that can only be provided during the build.

To achieve this in Maven, you can use the ${<property name>} syntax to reference a property containing that value within the resource file. This property can be defined in the pom.xml, settings.xml, an external properties file, or system properties.

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>
 
  <name>Maven Quick Start Archetype</name>
  <url>http://maven.apache.org</url>
 
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
 
  <build>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
    </resources>
  </build>
</project>

We must add the previously non-existent build, resources, and resource elements. Additionally, we must explicitly declare that resources are located in the src/main/resources directory. All this information was provided as default values before, but since the default value of filtering is false, we need to add it to the pom.xml to override this default value and set filtering to true.

To reference properties defined in pom.xml, the property name uses the XML element name where the defined value is, with pom as an alias for the project (root) element. Therefore, ${project.name} refers to the project’s name, ${project.version} refers to the project’s version, ${project.build.finalName} refers to the final name of the file created during the project packaging, and so on. Note that some

elements in the POM have default values, so they can be used here without being explicitly defined in pom.xml. Similarly, values in the user’s settings.xml can be referenced using property names starting with settings (for example, ${settings.localRepository} refers to the path of the user’s local repository).

Continuing our example, let’s add a few properties in the application.properties file (located in the src/main/resources directory) that will be provided when filtering resources:

# application.properties
application.name=${project.name}
application.version=${project.version}

With these in place, you can execute the following command (process-resources is a build lifecycle phase during which resources are copied and filtered):

mvn process-resources

And the application.properties file under target/classes (which will eventually be placed in the JAR) will look like this:

# application.properties
application.name=Maven Quick Start Archetype
application.version=1.0-SNAPSHOT

To reference properties defined in an external file, simply add a reference to that external file in the pom.xml. First, let’s create an external properties file and name it src/main/filters/filter.properties:

# filter.properties
my.filter.value=hello!

Next, we’ll add a reference to this new file in pom.xml:

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>
 
  <name>Maven Quick Start Archetype</name>
  <url>http://maven.apache.org</url>
 
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
 
  <build>
    <filters>
      <filter>src/main/filters/filter.properties</filter>
    </filters>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
    </resources>
  </build>
</project>

Then, we can add a reference to this property in the application.properties file:

# application.properties
application.name=${project.name}
application.version=${project.version}
message=${my.filter.value}

The next time you execute the mvn process-resources command, the new property value will be added to application.properties. If you don’t define the my.filter.value property in the external file, you can also define it in the properties section of pom.xml, achieving the same effect (without needing to reference src/main/filters/filter.properties):

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>
 
  <name>Maven Quick Start Archetype</name>
  <url>http://maven.apache.org</url>
 
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
 
  <build>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
    </resources>
  </build>
 
  <properties>
    <my.filter.value>hello</my.filter.value>
  </properties>
</project>

Filtering resources can also fetch values from system properties; these properties can be Java built-in system properties (like java.version or user.home) or properties defined using standard Java -D parameters on the command line. As a continued example, let’s change the application.properties file to look like this:

# application.properties
java.version=${java.version}
command.line.prop=${command.line.prop}

Now, when you execute the following command (note the definition of the command.line.prop property in the command line), the application.properties file will contain the values from system properties:

mvn process-resources "-Dcommand.line.prop=hello again"