Add support for customizing layers in Maven

This commit adds an additional 'layers/configuration' property that can
be used to refer to a separate layers configuration file. This separate
file defines:

* The layers and their order of precedence,
* How libraries are handled using filters that match against the
coordinates of each library, and
* How classes are handled using filters that match against the location
of the entry

An XSD to validate the XML configuration file is available.

Closes gh-20295

Co-authored-by: Stephane Nicoll <snicoll@pivotal.io>
This commit is contained in:
Madhura Bhave
2020-03-11 17:02:48 -07:00
committed by Stephane Nicoll
parent 896d2c8579
commit e49e62df5c
38 changed files with 2024 additions and 37 deletions

View File

@@ -71,6 +71,129 @@ The `layout` property defaults to a guess based on the archive type (`jar` or `w
* `ZIP` (alias to `DIR`): similar to the `JAR` layout using `PropertiesLauncher`.
* `NONE`: Bundle all dependencies and project resources. Does not bundle a bootstrap loader.
[[repackage-layers]]
=== Layered jar
By default, a repackaged jar contains the application's classes and dependencies in `BOOT-INF/classes` and `BOOT-INF/lib` respectively.
For cases where a docker image needs to be built from the contents of the jar, the jar format can be enhanced to support layer folders.
To use this feature, the layering feature must be enabled:
[source,xml,indent=0,subs="verbatim,attributes"]
----
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>{gradle-project-version}</version>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>
</plugins>
</build>
</project>
----
By default, the following layers are created:
* `application` for any other classes and resources.
* `resources` for static resources at the default locations, i.e. `META-INF/resources/`, `resources/`, `static/`, `public/`.
* `snapshot-dependencies` for any dependency whose version contains `SNAPSHOT`.
* `dependencies` for any other dependency.
The layers order is important as it determines how likely previous layers can be cached when part of the application changes.
The default order is `application`, `resources`, `snapshot-dependencies` and `dependencies`.
Content that is likely to change should be added first, followed by layers that are less likely to change.
[[repackage-layers-configuration]]
==== Custom Layers configuration
Depending on your application, you may want to tune how layers are created and add new ones.
This can be done using a separate configuration file that should be registered as shown in the following example:
[source,xml,indent=0,subs="verbatim,attributes"]
----
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>{gradle-project-version}</version>
<configuration>
<layers>
<enabled>true</enabled>
<configuration>${project.basedir}/src/layers.xml</configuration>
</layers>
</configuration>
</plugin>
</plugins>
</build>
</project>
----
The configuration file lists the layers and their order as well as the strategies to apply to libraries and classes.
The following example shows what the implicit layer configuration described above does:
[source,xml,indent=0,subs="verbatim,attributes"]
----
<layers-configuration xmlns="http://www.springframework.org/schema/boot/layers"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/boot/layers
https://www.springframework.org/schema/boot/layers/layers-configuration.xsd">
<layers>
<layer>application</layer>
<layer>resources</layer>
<layer>snapshots</layer>
<layer>dependencies</layer>
</layers>
<libraries>
<layer-content layer="snapshot-dependencies">
<coordinates>
<include>*:*:*SNAPSHOT</include>
</coordinates>
</layer-content>
<layer-content layer="dependencies">
<coordinates>
<include>*:*</include>
</coordinates>
</layer-content>
</libraries>
<classes>
<layer-content layer="resources">
<locations>
<include>META-INF/resources/**</include>
<include>resources/**</include>
<include>static/**</include>
<include>public/**</include>
</locations>
</layer-content>
<layer-content layer="application">
<locations>
<include>**</include>
</locations>
</layer-content>
</classes>
</layers-configuration>
----
Each `layer-content` element defines a strategy to include an entry of the jar in a layer.
When an entry matches a strategy, it is included in the layer and further strategies are ignored.
This is illustrated by the `dependencies` and `application` layers that have a "catch-all" include filter used to add any libraries or classes that were not processed by previous strategies.
The content of a libraries layer can be customized using filters on the coordinates.
The format is `groupId:artifactId[:version]`.
In the example above, any artifact whose version ends with `SNAPSHOT` is going to be included in the `snapshot-dependencies` layer.
The content of a classes layer can be customized using filters on location of the entry using Ant-style pattern matching.
include::goals/repackage.adoc[leveloffset=+1]
@@ -400,31 +523,8 @@ This example excludes any artifact belonging to the `com.foo` group:
[[repackage-layered-jars]]
==== Packaging layered jars
By default, the repackaged jar contains the application's classes and dependencies in `BOOT-INF/classes` and `BOOT-INF/lib` respectively.
For cases where a docker image needs to be built from the contents of the jar, the jar format can be enhanced to support layer folders.
To use this feature, the layering feature must be enabled:
[source,xml,indent=0,subs="verbatim,attributes"]
----
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>{gradle-project-version}</version>
<configuration>
<layered>
<enabled>true</enabled>
</layered>
</configuration>
</plugin>
</plugins>
</build>
</project>
----
[[repackage-layered-jars-tools]]
==== Layered jar tools
When you create a layered jar, the `spring-boot-layertools` jar will be added as a dependency to your jar.
With this jar on the classpath, you can launch your application in a special mode which allows the bootstrap code to run something entirely different from your application, for example, something that extracts the layers.
@@ -440,13 +540,59 @@ If you wish to exclude this dependency, you can do so in the following manner:
<artifactId>spring-boot-maven-plugin</artifactId>
<version>{gradle-project-version}</version>
<configuration>
<layered>
<layers>
<enabled>true</enabled>
<includeLayerTools>false</enabled>
</layered>
</layers>
</configuration>
</plugin>
</plugins>
</build>
</project>
----
----
[[repackage-layered-jars-additional-layers]]
==== Custom layers configuration
While the default setup creates two layers for libraries, you may want to isolate the dependencies of your project in a dedicated layer.
This allows to reuse the cache for external dependencies when an internal dependency has changed, as shown by the following example:
[source,xml,indent=0,subs="verbatim,attributes"]
----
<layers-configuration xmlns="http://www.springframework.org/schema/boot/layers"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/boot/layers
https://www.springframework.org/schema/boot/layers/layers-configuration.xsd">
<layers>
<layer>application</layer>
<layer>resources</layer>
<layer>snapshots</layer>
<layer>company-dependencies</layer>
<layer>dependencies</layer>
</layers>
<libraries>
<layer-content layer="snapshot-dependencies">
<coordinates>
<include>*:*:*SNAPSHOT</include>
</coordinates>
</layer-content>
<layer-content layer="company-dependencies">
<coordinates>
<include>com.acme:*</include>
</coordinates>
</layer-content>
<layer-content layer="dependencies">
<coordinates>
<include>*:*</include>
</coordinates>
</layer-content>
</libraries>
<classes>
...
</classes>
</layers-configuration>
----
The configuration above creates an additional `company-dependencies` layer with all libraries with the `com.acme` groupId.