Refine layer customization for Maven and Gradle
Simplify layer customization logic for both Maven and Gradle and refactor some internals of the Gradle plugin. Both Maven and Gradle now use a simpler customization format that consists of `application`, `dependencies` and `layer order` sections. The `application`, `dependencies` configurations support one or more `into` blocks that are used to select content for a specific layer. Closes gh-20526
This commit is contained in:
@@ -26,6 +26,7 @@ import org.junit.jupiter.api.TestTemplate;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.boot.loader.tools.FileUtils;
|
||||
import org.springframework.boot.loader.tools.JarModeLibrary;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -296,8 +297,7 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
||||
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/layers/application/classes/")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/layers/dependencies/lib/jar-release")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/layers/snapshot-dependencies/lib/jar-snapshot")
|
||||
.hasEntryWithNameStartingWith(
|
||||
"BOOT-INF/layers/dependencies/lib/spring-boot-jarmode-layertools.jar");
|
||||
.hasEntryWithNameStartingWith(jarModeLayerTools());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -308,8 +308,7 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
||||
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/layers/application/classes/")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/layers/dependencies/lib/jar-release")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/layers/snapshot-dependencies/lib/jar-snapshot")
|
||||
.doesNotHaveEntryWithNameStartingWith(
|
||||
"BOOT-INF/layers/dependencies/lib/spring-boot-jarmode-layertools.jar");
|
||||
.doesNotHaveEntryWithNameStartingWith(jarModeLayerTools());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -333,6 +332,13 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
||||
assertThat(firstHash).isEqualTo(secondHash);
|
||||
}
|
||||
|
||||
private String jarModeLayerTools() {
|
||||
JarModeLibrary library = JarModeLibrary.LAYER_TOOLS;
|
||||
String version = library.getCoordinates().getVersion();
|
||||
String layer = (version == null || !version.contains("SNAPSHOT")) ? "dependencies" : "snapshot-dependencies";
|
||||
return "BOOT-INF/layers/" + layer + "/lib/" + library.getName();
|
||||
}
|
||||
|
||||
private String buildJarWithOutputTimestamp(MavenBuild mavenBuild) {
|
||||
AtomicReference<String> jarHash = new AtomicReference<>();
|
||||
mavenBuild.project("jar-output-timestamp").execute((project) -> {
|
||||
|
||||
@@ -1,35 +1,23 @@
|
||||
<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/layers/layers-configuration-2.3.xsd">
|
||||
<layers>
|
||||
<layers 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/layers/layers-2.3.xsd">
|
||||
<application>
|
||||
<into layer="configuration">
|
||||
<include>**/application*.*</include>
|
||||
</into>
|
||||
<into layer="application" />
|
||||
</application>
|
||||
<dependencies>
|
||||
<into layer="snapshot-dependencies">
|
||||
<include>*:*:*-SNAPSHOT</include>
|
||||
</into>
|
||||
<into layer="my-dependencies-name" />
|
||||
</dependencies>
|
||||
<layerOrder>
|
||||
<layer>configuration</layer>
|
||||
<layer>application</layer>
|
||||
<layer>snapshot-dependencies</layer>
|
||||
<layer>my-dependencies-name</layer>
|
||||
</layers>
|
||||
<libraries>
|
||||
<layer-content layer="snapshot-dependencies">
|
||||
<coordinates>
|
||||
<include>*:*:*-SNAPSHOT</include>
|
||||
</coordinates>
|
||||
</layer-content>
|
||||
<layer-content layer="my-dependencies-name">
|
||||
<coordinates>
|
||||
<include>*:*:*</include>
|
||||
</coordinates>
|
||||
</layer-content>
|
||||
</libraries>
|
||||
<application>
|
||||
<layer-content layer="configuration">
|
||||
<locations>
|
||||
<include>**/application*.*</include>
|
||||
</locations>
|
||||
</layer-content>
|
||||
<layer-content layer="application">
|
||||
<locations>
|
||||
<include>**</include>
|
||||
</locations>
|
||||
</layer-content>
|
||||
</application>
|
||||
</layers-configuration>
|
||||
</layerOrder>
|
||||
</layers>
|
||||
@@ -48,6 +48,7 @@ import org.springframework.boot.loader.tools.Layouts.None;
|
||||
import org.springframework.boot.loader.tools.Layouts.War;
|
||||
import org.springframework.boot.loader.tools.Libraries;
|
||||
import org.springframework.boot.loader.tools.Packager;
|
||||
import org.springframework.boot.loader.tools.layer.CustomLayers;
|
||||
|
||||
/**
|
||||
* Abstract base class for classes that work with an {@link Packager}.
|
||||
@@ -135,15 +136,7 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
||||
}
|
||||
if (this.layers != null && this.layers.isEnabled()) {
|
||||
if (this.layers.getConfiguration() != null) {
|
||||
try {
|
||||
Document document = getDocumentIfAvailable(this.layers.getConfiguration());
|
||||
CustomLayersProvider customLayersProvider = new CustomLayersProvider();
|
||||
packager.setLayers(customLayersProvider.getLayers(document));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Failed to process custom layers configuration "
|
||||
+ this.layers.getConfiguration().getAbsolutePath(), ex);
|
||||
}
|
||||
packager.setLayers(getCustomLayers(this.layers.getConfiguration()));
|
||||
}
|
||||
packager.setLayout(new LayeredJar());
|
||||
packager.setIncludeRelevantJarModeJars(this.layers.isIncludeLayerTools());
|
||||
@@ -151,8 +144,19 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
||||
return packager;
|
||||
}
|
||||
|
||||
private Document getDocumentIfAvailable(File configurationFile) throws Exception {
|
||||
InputSource inputSource = new InputSource(new FileInputStream(configurationFile));
|
||||
private CustomLayers getCustomLayers(File configuration) {
|
||||
try {
|
||||
Document document = getDocumentIfAvailable(configuration);
|
||||
return new CustomLayersProvider().getLayers(document);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(
|
||||
"Failed to process custom layers configuration " + configuration.getAbsolutePath(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Document getDocumentIfAvailable(File xmlFile) throws Exception {
|
||||
InputSource inputSource = new InputSource(new FileInputStream(xmlFile));
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
return builder.parse(inputSource);
|
||||
|
||||
@@ -80,8 +80,7 @@ public class ArtifactsLibraries implements Libraries {
|
||||
name = artifact.getGroupId() + "-" + name;
|
||||
this.log.debug("Renamed to: " + name);
|
||||
}
|
||||
LibraryCoordinates coordinates = new LibraryCoordinates(artifact.getGroupId(), artifact.getArtifactId(),
|
||||
artifact.getVersion());
|
||||
LibraryCoordinates coordinates = new ArtifactLibraryCoordinates(artifact);
|
||||
callback.library(new Library(name, artifact.getFile(), scope, coordinates, isUnpackRequired(artifact)));
|
||||
}
|
||||
}
|
||||
@@ -122,4 +121,37 @@ public class ArtifactsLibraries implements Libraries {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link LibraryCoordinates} backed by a Maven {@link Artifact}.
|
||||
*/
|
||||
private static class ArtifactLibraryCoordinates implements LibraryCoordinates {
|
||||
|
||||
private final Artifact artifact;
|
||||
|
||||
ArtifactLibraryCoordinates(Artifact artifact) {
|
||||
this.artifact = artifact;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupId() {
|
||||
return this.artifact.getGroupId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArtifactId() {
|
||||
return this.artifact.getArtifactId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return this.artifact.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.artifact.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
package org.springframework.boot.maven;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
@@ -26,113 +28,76 @@ import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.boot.loader.tools.Layer;
|
||||
import org.springframework.boot.loader.tools.Library;
|
||||
import org.springframework.boot.loader.tools.layer.ApplicationContentFilter;
|
||||
import org.springframework.boot.loader.tools.layer.ContentFilter;
|
||||
import org.springframework.boot.loader.tools.layer.ContentSelector;
|
||||
import org.springframework.boot.loader.tools.layer.CustomLayers;
|
||||
import org.springframework.boot.loader.tools.layer.application.FilteredResourceStrategy;
|
||||
import org.springframework.boot.loader.tools.layer.application.LocationFilter;
|
||||
import org.springframework.boot.loader.tools.layer.application.ResourceFilter;
|
||||
import org.springframework.boot.loader.tools.layer.application.ResourceStrategy;
|
||||
import org.springframework.boot.loader.tools.layer.library.CoordinateFilter;
|
||||
import org.springframework.boot.loader.tools.layer.library.FilteredLibraryStrategy;
|
||||
import org.springframework.boot.loader.tools.layer.library.LibraryFilter;
|
||||
import org.springframework.boot.loader.tools.layer.library.LibraryStrategy;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.boot.loader.tools.layer.IncludeExcludeContentSelector;
|
||||
import org.springframework.boot.loader.tools.layer.LibraryContentFilter;
|
||||
|
||||
/**
|
||||
* Produces a {@link CustomLayers} based on the given {@link Document}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.3.0
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class CustomLayersProvider {
|
||||
class CustomLayersProvider {
|
||||
|
||||
public CustomLayers getLayers(Document document) {
|
||||
CustomLayers getLayers(Document document) {
|
||||
Element root = document.getDocumentElement();
|
||||
NodeList nodes = root.getChildNodes();
|
||||
List<Layer> layers = new ArrayList<>();
|
||||
List<LibraryStrategy> libraryStrategies = new ArrayList<>();
|
||||
List<ResourceStrategy> resourceStrategies = new ArrayList<>();
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
Node node = nodes.item(i);
|
||||
if (node instanceof Element) {
|
||||
processNode(layers, libraryStrategies, resourceStrategies, (Element) node);
|
||||
List<ContentSelector<String>> applicationSelectors = getApplicationSelectors(root);
|
||||
List<ContentSelector<Library>> librarySelectors = getLibrarySelectors(root);
|
||||
List<Layer> layers = getLayers(root);
|
||||
return new CustomLayers(layers, applicationSelectors, librarySelectors);
|
||||
}
|
||||
|
||||
private List<ContentSelector<String>> getApplicationSelectors(Element root) {
|
||||
return getSelectors(root, "application", ApplicationContentFilter::new);
|
||||
}
|
||||
|
||||
private List<ContentSelector<Library>> getLibrarySelectors(Element root) {
|
||||
return getSelectors(root, "dependencies", LibraryContentFilter::new);
|
||||
}
|
||||
|
||||
private List<Layer> getLayers(Element root) {
|
||||
Element layerOrder = getChildElement(root, "layerOrder");
|
||||
if (layerOrder == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return getChildNodeTextContent(layerOrder, "layer").stream().map(Layer::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private <T> List<ContentSelector<T>> getSelectors(Element root, String elementName,
|
||||
Function<String, ContentFilter<T>> filterFactory) {
|
||||
Element element = getChildElement(root, elementName);
|
||||
if (element == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ContentSelector<T>> selectors = new ArrayList<>();
|
||||
NodeList children = element.getChildNodes();
|
||||
for (int i = 0; i < children.getLength(); i++) {
|
||||
Node child = children.item(i);
|
||||
if (child instanceof Element) {
|
||||
ContentSelector<T> selector = getSelector((Element) child, filterFactory);
|
||||
selectors.add(selector);
|
||||
}
|
||||
}
|
||||
return new CustomLayers(layers, resourceStrategies, libraryStrategies);
|
||||
return selectors;
|
||||
}
|
||||
|
||||
private void processNode(List<Layer> layers, List<LibraryStrategy> libraryStrategies,
|
||||
List<ResourceStrategy> resourceStrategies, Element node) {
|
||||
String nodeName = node.getNodeName();
|
||||
if ("layers".equals(nodeName)) {
|
||||
layers.addAll(getLayers(node));
|
||||
}
|
||||
NodeList contents = node.getChildNodes();
|
||||
if ("libraries".equals(nodeName)) {
|
||||
libraryStrategies.addAll(getStrategies(contents,
|
||||
(StrategyFactory<LibraryFilter, LibraryStrategy>) FilteredLibraryStrategy::new,
|
||||
CoordinateFilter::new, "coordinates"::equals));
|
||||
}
|
||||
if ("application".equals(nodeName)) {
|
||||
resourceStrategies.addAll(getStrategies(contents,
|
||||
(StrategyFactory<ResourceFilter, ResourceStrategy>) FilteredResourceStrategy::new,
|
||||
LocationFilter::new, "locations"::equals));
|
||||
}
|
||||
private <T> ContentSelector<T> getSelector(Element element, Function<String, ContentFilter<T>> filterFactory) {
|
||||
Layer layer = new Layer(element.getAttribute("layer"));
|
||||
List<String> includes = getChildNodeTextContent(element, "include");
|
||||
List<String> excludes = getChildNodeTextContent(element, "exclude");
|
||||
return new IncludeExcludeContentSelector<>(layer, includes, excludes, filterFactory);
|
||||
}
|
||||
|
||||
private List<Layer> getLayers(Element element) {
|
||||
List<Layer> layers = new ArrayList<>();
|
||||
NodeList childNodes = element.getChildNodes();
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Node childNode = childNodes.item(i);
|
||||
if (childNode instanceof Element) {
|
||||
Element childElement = (Element) childNode;
|
||||
if ("layer".equals(childElement.getNodeName())) {
|
||||
layers.add(new Layer(childElement.getTextContent()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return layers;
|
||||
}
|
||||
|
||||
private <T, E> List<T> getStrategies(NodeList nodes, StrategyFactory<E, T> strategyFactory,
|
||||
FilterFactory<E> filterFactory, Predicate<String> filterPredicate) {
|
||||
List<T> contents = new ArrayList<>();
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
Node node = nodes.item(i);
|
||||
if (node instanceof Element) {
|
||||
Element element = (Element) node;
|
||||
if ("layer-content".equals(element.getTagName())) {
|
||||
List<E> filters = getFilters(node, filterFactory, filterPredicate);
|
||||
String layer = element.getAttribute("layer");
|
||||
contents.add(strategyFactory.getStrategy(layer, filters));
|
||||
}
|
||||
}
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
private <E> List<E> getFilters(Node node, FilterFactory<E> factory, Predicate<String> predicate) {
|
||||
NodeList childNodes = node.getChildNodes();
|
||||
Assert.state(childNodes.getLength() > 0, "Filters for layer-content must not be empty.");
|
||||
List<E> filters = new ArrayList<>();
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Node childNode = childNodes.item(i);
|
||||
if (childNode instanceof Element) {
|
||||
List<String> include = getPatterns((Element) childNode, "include");
|
||||
List<String> exclude = getPatterns((Element) childNode, "exclude");
|
||||
if (predicate.test(childNode.getNodeName())) {
|
||||
filters.add(factory.getFilter(include, exclude));
|
||||
}
|
||||
}
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
private List<String> getPatterns(Element element, String key) {
|
||||
private List<String> getChildNodeTextContent(Element element, String tagName) {
|
||||
List<String> patterns = new ArrayList<>();
|
||||
NodeList nodes = element.getElementsByTagName(key);
|
||||
for (int j = 0; j < nodes.getLength(); j++) {
|
||||
Node node = nodes.item(j);
|
||||
NodeList nodes = element.getElementsByTagName(tagName);
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
Node node = nodes.item(i);
|
||||
if (node instanceof Element) {
|
||||
patterns.add(node.getTextContent());
|
||||
}
|
||||
@@ -140,16 +105,15 @@ public class CustomLayersProvider {
|
||||
return patterns;
|
||||
}
|
||||
|
||||
interface StrategyFactory<E, T> {
|
||||
|
||||
T getStrategy(String layer, List<E> filters);
|
||||
|
||||
}
|
||||
|
||||
interface FilterFactory<E> {
|
||||
|
||||
E getFilter(List<String> includes, List<String> excludes);
|
||||
|
||||
private Element getChildElement(Element element, String tagName) {
|
||||
NodeList nodes = element.getElementsByTagName(tagName);
|
||||
if (nodes.getLength() == 0) {
|
||||
return null;
|
||||
}
|
||||
if (nodes.getLength() > 1) {
|
||||
throw new IllegalStateException("Multiple '" + tagName + "' nodes found");
|
||||
}
|
||||
return (Element) nodes.item(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<xsd:schema elementFormDefault="qualified"
|
||||
xmlns="http://www.springframework.org/schema/boot/layers"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://www.springframework.org/schema/boot/layers">
|
||||
<xsd:element name="layers" type="layersType" />
|
||||
<xsd:complexType name="layersType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="application" type="applicationType" />
|
||||
<xsd:element name="dependencies" type="dependenciesType" />
|
||||
<xsd:element name="layerOrder" type="layerOrderType" />
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="applicationType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The 'into layer' selections that should be applied to application classes and resources.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence maxOccurs="unbounded">
|
||||
<xsd:element name="into" type="intoType" />
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="dependenciesType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The 'into layer' selections that should be applied to dependencies.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence maxOccurs="unbounded">
|
||||
<xsd:element name="into" type="intoType" />
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="layerOrderType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The order that layers should be added (starting with the least frequently changed layer).
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="layer" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The layer name.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:minLength value="1" />
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="intoType">
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element type="xsd:string" name="include"
|
||||
minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Pattern of the elements to include.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element type="xsd:string" name="exclude"
|
||||
minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Pattern of the elements to exclude.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
<xsd:attribute type="xsd:string" name="layer"
|
||||
use="required" />
|
||||
</xsd:complexType>
|
||||
|
||||
</xsd:schema>
|
||||
@@ -1,96 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<xsd:schema elementFormDefault="qualified"
|
||||
xmlns="http://www.springframework.org/schema/boot/layers"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://www.springframework.org/schema/boot/layers">
|
||||
<xsd:element name="layers-configuration" type="layersConfigurationType"/>
|
||||
<xsd:complexType name="layersConfigurationType">
|
||||
<xsd:sequence>
|
||||
<xsd:element type="layersType" name="layers"/>
|
||||
<xsd:element type="librariesType" name="libraries" minOccurs="0"/>
|
||||
<xsd:element type="applicationType" name="application" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="layersType">
|
||||
<xsd:sequence>
|
||||
<xsd:element type="xsd:string" name="layer" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of a layer. Each layer in the configuration must be referenced once and the
|
||||
order matches how the content is likely to change. Put layers that are frequently
|
||||
updated first, layers that are more stable (such as non-snapshot dependencies) last.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="librariesType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Strategies that should be applied to libraries. If no strategies are defined, two
|
||||
layers are created out-of-the-box. A "snapshot-dependencies" layer with SNAPSHOT
|
||||
libraries and a "dependencies" layer with all the other libraries.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element type="librariesLayerContentType" name="layer-content">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Strategy to apply on libraries.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="librariesLayerContentType" mixed="true">
|
||||
<xsd:sequence>
|
||||
<xsd:element type="filterType" name="coordinates" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute type="xsd:string" name="layer" use="required"/>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="applicationType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Strategies that should be applied to application classes and resources. If no strategies are defined, a single
|
||||
"application" layer is created out-of-the-box.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element type="applicationLayerContentType" name="layer-content" maxOccurs="unbounded"
|
||||
minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Strategy to apply on application classes and resources.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="applicationLayerContentType" mixed="true">
|
||||
<xsd:sequence>
|
||||
<xsd:element type="filterType" name="locations" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute type="xsd:string" name="layer" use="required"/>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="filterType">
|
||||
<xsd:sequence>
|
||||
<xsd:element type="xsd:string" name="exclude" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Pattern of the elements to exclude. An exclude pattern takes precedence over an
|
||||
include pattern and must be declared first.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element type="xsd:string" name="include" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Pattern of the elements to include.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
</xsd:schema>
|
||||
@@ -52,8 +52,8 @@ public class CustomLayersProviderTests {
|
||||
@Test
|
||||
void getLayerResolverWhenDocumentValid() throws Exception {
|
||||
CustomLayers layers = this.customLayersProvider.getLayers(getDocument("layers.xml"));
|
||||
assertThat(layers).extracting("name").containsExactly("configuration", "application", "my-resources",
|
||||
"snapshot-dependencies", "my-deps", "my-dependencies-name");
|
||||
assertThat(layers).extracting("name").containsExactly("my-deps", "my-dependencies-name",
|
||||
"snapshot-dependencies", "my-resources", "configuration", "application");
|
||||
Library snapshot = mockLibrary("test-SNAPSHOT.jar", "org.foo", "1.0.0-SNAPSHOT");
|
||||
Library groupId = mockLibrary("my-library", "com.acme", null);
|
||||
Library otherDependency = mockLibrary("other-library", "org.foo", null);
|
||||
@@ -68,22 +68,26 @@ public class CustomLayersProviderTests {
|
||||
private Library mockLibrary(String name, String groupId, String version) {
|
||||
Library library = mock(Library.class);
|
||||
given(library.getName()).willReturn(name);
|
||||
given(library.getCoordinates()).willReturn(new LibraryCoordinates(groupId, null, version));
|
||||
given(library.getCoordinates()).willReturn(LibraryCoordinates.of(groupId, null, version));
|
||||
return library;
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLayerResolverWhenDocumentContainsLibraryLayerWithNoFilters() {
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.customLayersProvider.getLayers(getDocument("library-layer-no-filter.xml")))
|
||||
.withMessage("Filters for layer-content must not be empty.");
|
||||
void getLayerResolverWhenDocumentContainsLibraryLayerWithNoFilters() throws Exception {
|
||||
CustomLayers layers = this.customLayersProvider.getLayers(getDocument("dependencies-layer-no-filter.xml"));
|
||||
Library library = mockLibrary("my-library", "com.acme", null);
|
||||
assertThat(layers.getLayer(library).toString()).isEqualTo("my-deps");
|
||||
assertThatIllegalStateException().isThrownBy(() -> layers.getLayer("application.yml"))
|
||||
.withMessageContaining("match any layer");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLayerResolverWhenDocumentContainsResourceLayerWithNoFilters() {
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.customLayersProvider.getLayers(getDocument("resource-layer-no-filter.xml")))
|
||||
.withMessage("Filters for layer-content must not be empty.");
|
||||
void getLayerResolverWhenDocumentContainsResourceLayerWithNoFilters() throws Exception {
|
||||
CustomLayers layers = this.customLayersProvider.getLayers(getDocument("application-layer-no-filter.xml"));
|
||||
Library library = mockLibrary("my-library", "com.acme", null);
|
||||
assertThat(layers.getLayer("application.yml").toString()).isEqualTo("my-layer");
|
||||
assertThatIllegalStateException().isThrownBy(() -> layers.getLayer(library))
|
||||
.withMessageContaining("match any layer");
|
||||
}
|
||||
|
||||
private Document getDocument(String resourceName) throws Exception {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<layers 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.xsd">
|
||||
<application>
|
||||
<into layer="my-layer" />
|
||||
</application>
|
||||
<layerOrder>
|
||||
<layer>my-layer</layer>
|
||||
</layerOrder>
|
||||
</layers>
|
||||
@@ -0,0 +1,11 @@
|
||||
<layers 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-2.3.xsd">
|
||||
<dependencies>
|
||||
<into layer="my-deps" />
|
||||
</dependencies>
|
||||
<layerOrder>
|
||||
<layer>my-deps</layer>
|
||||
</layerOrder>
|
||||
</layers>
|
||||
@@ -1,47 +1,32 @@
|
||||
<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-2.3.xsd">
|
||||
<layers>
|
||||
<layer>configuration</layer>
|
||||
<layer>application</layer>
|
||||
<layer>my-resources</layer>
|
||||
<layer>snapshot-dependencies</layer>
|
||||
<layers 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-2.3.xsd">
|
||||
<application>
|
||||
<into layer="my-resources">
|
||||
<include>META-INF/resources/**</include>
|
||||
<exclude>*.properties</exclude>
|
||||
</into>
|
||||
<into layer="configuration">
|
||||
<include>**/application*.*</include>
|
||||
</into>
|
||||
<into layer="application" />
|
||||
</application>
|
||||
<dependencies>
|
||||
<into layer="snapshot-dependencies">
|
||||
<include>*:*:*-SNAPSHOT</include>
|
||||
</into>
|
||||
<into layer="my-deps">
|
||||
<include>com.acme:*</include>
|
||||
</into>
|
||||
<into layer="my-dependencies-name"/>
|
||||
</dependencies>
|
||||
<layerOrder>
|
||||
<layer>my-deps</layer>
|
||||
<layer>my-dependencies-name</layer>
|
||||
</layers>
|
||||
<libraries>
|
||||
<layer-content layer="snapshot-dependencies">
|
||||
<coordinates>
|
||||
<include>*:*:*-SNAPSHOT</include>
|
||||
</coordinates>
|
||||
</layer-content>
|
||||
<layer-content layer="my-deps">
|
||||
<coordinates>
|
||||
<include>com.acme:*</include>
|
||||
</coordinates>
|
||||
</layer-content>
|
||||
<layer-content layer="my-dependencies-name">
|
||||
<coordinates>
|
||||
<include>*:*:*</include>
|
||||
</coordinates>
|
||||
</layer-content>
|
||||
</libraries>
|
||||
<application>
|
||||
<layer-content layer="my-resources">
|
||||
<locations>
|
||||
<include>META-INF/resources/**</include>
|
||||
</locations>
|
||||
</layer-content>
|
||||
<layer-content layer="configuration">
|
||||
<locations>
|
||||
<include>**/application*.*</include>
|
||||
</locations>
|
||||
</layer-content>
|
||||
<layer-content layer="application">
|
||||
<locations>
|
||||
<include>**</include>
|
||||
</locations>
|
||||
</layer-content>
|
||||
</application>
|
||||
</layers-configuration>
|
||||
<layer>snapshot-dependencies</layer>
|
||||
<layer>my-resources</layer>
|
||||
<layer>configuration</layer>
|
||||
<layer>application</layer>
|
||||
</layerOrder>
|
||||
</layers>
|
||||
@@ -1,11 +0,0 @@
|
||||
<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-2.3.xsd">
|
||||
<layers>
|
||||
<layer>my-deps</layer>
|
||||
</layers>
|
||||
<libraries>
|
||||
<layer-content layer="my-deps"/>
|
||||
</libraries>
|
||||
</layers-configuration>
|
||||
@@ -1,11 +1,11 @@
|
||||
<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-2.3.xsd">
|
||||
<layers>
|
||||
<layer>my-layer</layer>
|
||||
</layers>
|
||||
<layers 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-2.3.xsd">
|
||||
<application>
|
||||
<layer-content layer="my-layer"/>
|
||||
<into layer="my-layer" />
|
||||
</application>
|
||||
</layers-configuration>
|
||||
<layerOrder>
|
||||
<layer>my-layer</layer>
|
||||
</layerOrder>
|
||||
</layers>
|
||||
Reference in New Issue
Block a user