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"