Maven Resource Filtering: injecting POM' properties in your resources
Tags: mulesoft, maven, log4j2, java
Published: 2022-09-17 16:43:31

In my previous blog post [How to output Log4J2 logs as JSON?](https://dev.to/gauthierplm/how-to-output-log4j2-logs-as-json-5an3), 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`](https://maven.apache.org/plugins/maven-resources-plugin/index.html) 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`](https://maven.apache.org/plugins/maven-resources-plugin/index.html), 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:

```XML
<project>
  ...
  <build>
    ...
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
      ...
    </resources>
    ...
  </build>
  ...
</project>
```

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}.
```

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.
```

### 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:

```XML
<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>
```

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:

```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>
```

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?](https://dev.to/gauthierplm/how-to-output-log4j2-logs-as-json-5an3)):

```XML
...
<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>
...
```

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](https://www.datadoghq.com/) or [Splunk](https://www.splunk.com/).

## Conclusion

Resource filtering is a very useful feature of Maven's [`maven-resource-plugin`](https://maven.apache.org/plugins/maven-resources-plugin/index.html) 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.