Maven Resource Filtering: injecting POM' properties in your resources

gauthierplm

Gauthier POGAM--LE MONTAGNER

Posted on September 17, 2022

Maven Resource Filtering: injecting POM' properties in your resources

In my previous blog post How to output Log4J2 logs as JSON?, I mentioned that we can extract some properties from our project' pom.xml to add them to our logs. This can be done using Maven' maven-resource-plugin and its filtering capabilities.

I will explain what is resource filtering, how it can be configured, and how to apply this to extract properties from a pom.xml to inject them into a log4j2.xml configuration.

What is resource filtering?

Before compiling the source or your tests, the resources of your application are copied from your project' resource folder (src/main/resources) to Maven' target folder. Maven automatically copies these resources using maven-resource-plugin, one of Maven' standard plugins, called by most goals that require it (such as compile or test-compile).

These resources are any file that your application will use but that should not be compiled, such as properties files, assets, TLS certificates,... Most applications have at least one resource file: a logger configuration file like Log4J2' log4j2.xml file.

When building your project, you might want these files to contain some of the many properties defined in your pom.xml, such as your application' name, version, or one of the properties defined in your pom.xml. By default, however, Maven won't apply any change to your resources. You will have to configure it to injects the properties you want in your resources files. Injecting properties in your resources is what we call filtering the resources of your project.

Enabling resource filtering

To filter your resources, you must tell Maven which files it should filter, and in these files you should define the variables Maven should replace.

Enabling filtering in Maven is pretty simple: you just have to enable it in the configuration of your application resources. This can be done by editing the resource tag in the build section of your application to add the property <filtering>true</filtering> to your resources configuration:

<project>
  ...
  <build>
    ...
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
      ...
    </resources>
    ...
  </build>
  ...
</project>
Enter fullscreen mode Exit fullscreen mode

Once enabled, every resources in src/main/resources will be filtered. Therefore, we can now use filtering to replace variables in our resource files. Let's create a file test.txt and add the following content in it:

Hello, I am project ${project.name}, version ${project.version}. You can identify me by my full name ${project.groupId}:${project.artifactId}.
Enter fullscreen mode Exit fullscreen mode

You can now run the command mvn compile and open target/classes/test.txt. You will find the following content:

Hello, I am project my-test-app, version 1.0.0-SNAPSHOT. You can identify me by my full name com.company.dev:my-test-app.
Enter fullscreen mode Exit fullscreen mode

Filter specific files

By default, filtering applies to every files in your resources that are not binary files (like images). To avoid replacing content that use the same syntax as a property but which should not be replaced, it is recommended to manually select which files are filtered and exclude everything else.

Fortunately, Maven makes it very simple to configure which resources should be filtered by using the <includes> tag:

<project>
  ...
  <build>
    ...
    <resources>
      <!-- Enable filtering for specific resources -->
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
        <includes>
          <include>test.txt</include>
        </includes>
      </resource>
      <!-- Disable filtering for everything else.
           This part is necessary so Maven continues to copy non-filtered resources.
      -->
      <resource>
        <directory>src/main/resources</directory>
        <filtering>false</filtering>
        <excludes>
          <exclude>test.txt</exclude>
        </excludes>
      </resource>
      ...
    </resources>
    ...
  </build>
  ...
</project>
Enter fullscreen mode Exit fullscreen mode

You will note that we added the <resource> tag twice: one with filtering enabled, and a second time without it. If we only added the first tag with the <includes> configured, Maven would only copy (and filter) the resources included in this configuration (in our case, only log4j2.xml) and would ignore every other file.

To ensure all resources of our application are copied including non-filtered ones, we must add a second <resource> tag with filtering disabled and which excludes the filtered files. The list of files to exclude in the second <resource> tag is the same as the included files in the first resource configuration.

With the configuration above, only our test.txt file is filtered and any other file in our resource folder (or its sub-folders) will not be filtered, allowing us to use ${project.name} without worrying that it could be replaced by Maven.

Configuring resources filtering for Log4j2

Applying resource filtering to automatically inject some information now becomes very easy. The first step is to take the code above and enable filtering for log4j2.xml:

<project>
  ...
  <build>
    ...
    <resources>
      <!-- Enable filtering for specific resources -->
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
        <includes>
          <include>log4j2.xml</include>
        </includes>
      </resource>
      <!-- Disable filtering for everything else.
           This part is necessary so Maven continues to copy non-filtered resources.
      -->
      <resource>
        <directory>src/main/resources</directory>
        <filtering>false</filtering>
        <excludes>
          <exclude>log4j2.xml</exclude>
        </excludes>
      </resource>
      ...
    </resources>
    ...
  </build>
  ...
</project>
Enter fullscreen mode Exit fullscreen mode

With our Log4J2 configuration file now filtered, we can easily use the properties defined in our pom.xml to enrich our logs. Let's configure a RollingFile appender that output JSON logs (more info in my post How to output Log4J2 logs as JSON?):

...
<RollingFile name="json-file" fileName="${sys:mule.home}${sys:file.separator}logs${sys:file.separator}${project.artifactId}.log.json"
                 filePattern="${sys:mule.home}${sys:file.separator}logs${sys:file.separator}${project.artifactId}-%i.log.json">
  <JSONLayout compact="true" eventEol="true" properties="true" stacktraceAsString="true" includeTimeMillis="true">
    <!--  Project properties -->
    <KeyValuePair key="appName" value="${project.name}" />
    <KeyValuePair key="version" value="${project.version}" />
  </JSONLayout>
</RollingFile>
...
Enter fullscreen mode Exit fullscreen mode

Our logs will now come with two additional properties: the application name and its version. You can now leverage these additional information, or any other properties extracted from your pom.xml, to enrich your logs and improve their filtering in a tool like Datadog or Splunk.

Conclusion

Resource filtering is a very useful feature of Maven's maven-resource-plugin plugin, allowing you to inject properties from your pom.xml into your resources. With a simple configuration of that plugin, references to properties are injected into your resources when your application is built.

💖 💪 🙅 🚩
gauthierplm
Gauthier POGAM--LE MONTAGNER

Posted on September 17, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related