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:
parent
896d2c8579
commit
e49e62df5c
|
|
@ -25,6 +25,7 @@ import java.io.InputStream;
|
|||
* Encapsulates information about a single library that may be packed into the archive.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Scott Frederick
|
||||
* @since 1.1.2
|
||||
* @see Libraries
|
||||
*/
|
||||
|
|
@ -38,6 +39,8 @@ public class Library {
|
|||
|
||||
private final boolean unpackRequired;
|
||||
|
||||
private final LibraryCoordinates coordinates;
|
||||
|
||||
/**
|
||||
* Create a new {@link Library}.
|
||||
* @param file the source file
|
||||
|
|
@ -66,10 +69,15 @@ public class Library {
|
|||
* @param unpackRequired if the library needs to be unpacked before it can be used
|
||||
*/
|
||||
public Library(String name, File file, LibraryScope scope, boolean unpackRequired) {
|
||||
this(name, file, scope, unpackRequired, null);
|
||||
}
|
||||
|
||||
public Library(String name, File file, LibraryScope scope, boolean unpackRequired, LibraryCoordinates coordinates) {
|
||||
this.name = (name != null) ? name : file.getName();
|
||||
this.file = file;
|
||||
this.scope = scope;
|
||||
this.unpackRequired = unpackRequired;
|
||||
this.coordinates = coordinates;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -114,6 +122,14 @@ public class Library {
|
|||
return this.unpackRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@linkplain LibraryCoordinates coordinates} of the library.
|
||||
* @return the coordinates
|
||||
*/
|
||||
public LibraryCoordinates getCoordinates() {
|
||||
return this.coordinates;
|
||||
}
|
||||
|
||||
long getLastModified() {
|
||||
return this.file.lastModified();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Encapsulates information about the Maven artifact coordinates of a library.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public final class LibraryCoordinates {
|
||||
|
||||
private final String groupId;
|
||||
|
||||
private final String artifactId;
|
||||
|
||||
private final String version;
|
||||
|
||||
/**
|
||||
* Create a new instance from discrete elements.
|
||||
* @param groupId the group ID
|
||||
* @param artifactId the artifact ID
|
||||
* @param version the version
|
||||
*/
|
||||
public LibraryCoordinates(String groupId, String artifactId, String version) {
|
||||
this.groupId = groupId;
|
||||
this.artifactId = artifactId;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance from a String value in the form
|
||||
* {@code groupId:artifactId:version} where the version is optional.
|
||||
* @param coordinates the coordinates
|
||||
*/
|
||||
public LibraryCoordinates(String coordinates) {
|
||||
String[] elements = coordinates.split(":");
|
||||
Assert.isTrue(elements.length >= 2, "Coordinates must contain at least 'groupId:artifactId'");
|
||||
this.groupId = elements[0];
|
||||
this.artifactId = elements[1];
|
||||
if (elements.length > 2) {
|
||||
this.version = elements[2];
|
||||
}
|
||||
else {
|
||||
this.version = null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getGroupId() {
|
||||
return this.groupId;
|
||||
}
|
||||
|
||||
public String getArtifactId() {
|
||||
return this.artifactId;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools.layer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.loader.tools.Layer;
|
||||
import org.springframework.boot.loader.tools.Layers;
|
||||
import org.springframework.boot.loader.tools.Library;
|
||||
import org.springframework.boot.loader.tools.layer.classes.ResourceStrategy;
|
||||
import org.springframework.boot.loader.tools.layer.library.LibraryStrategy;
|
||||
|
||||
/**
|
||||
* Implementation of {@link Layers} representing user-provided layers.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public class CustomLayers implements Layers {
|
||||
|
||||
private final List<Layer> layers;
|
||||
|
||||
private final List<ResourceStrategy> resourceStrategies;
|
||||
|
||||
private final List<LibraryStrategy> libraryStrategies;
|
||||
|
||||
public CustomLayers(List<Layer> layers, List<ResourceStrategy> resourceStrategies,
|
||||
List<LibraryStrategy> libraryStrategies) {
|
||||
this.layers = new ArrayList<>(layers);
|
||||
this.resourceStrategies = new ArrayList<>(resourceStrategies);
|
||||
this.libraryStrategies = new ArrayList<>(libraryStrategies);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Layer> iterator() {
|
||||
return this.layers.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Layer getLayer(String resourceName) {
|
||||
for (ResourceStrategy strategy : this.resourceStrategies) {
|
||||
Layer matchingLayer = strategy.getMatchingLayer(resourceName);
|
||||
if (matchingLayer != null) {
|
||||
return matchingLayer;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Resource '" + resourceName + "' did not match any layer.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Layer getLayer(Library library) {
|
||||
for (LibraryStrategy strategy : this.libraryStrategies) {
|
||||
Layer matchingLayer = strategy.getMatchingLayer(library);
|
||||
if (matchingLayer != null) {
|
||||
return matchingLayer;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Library '" + library.getName() + "' did not match any layer.");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools.layer.classes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link ResourceFilter} implementations.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public abstract class AbstractResourceFilter implements ResourceFilter {
|
||||
|
||||
private final List<String> includes = new ArrayList<>();
|
||||
|
||||
private final List<String> excludes = new ArrayList<>();
|
||||
|
||||
public AbstractResourceFilter(List<String> includes, List<String> excludes) {
|
||||
this.includes.addAll(includes);
|
||||
this.excludes.addAll(excludes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResourceIncluded(String resourceName) {
|
||||
return isMatch(resourceName, this.includes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResourceExcluded(String resourceName) {
|
||||
return isMatch(resourceName, this.excludes);
|
||||
}
|
||||
|
||||
protected abstract boolean isMatch(String resourceName, List<String> toMatch);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools.layer.classes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.loader.tools.Layer;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link ResourceStrategy} with custom filters.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public class FilteredResourceStrategy implements ResourceStrategy {
|
||||
|
||||
private final Layer layer;
|
||||
|
||||
private final List<ResourceFilter> filters = new ArrayList<>();
|
||||
|
||||
public FilteredResourceStrategy(String layer, List<ResourceFilter> filters) {
|
||||
Assert.notEmpty(filters, "Filters should not be empty for custom strategy.");
|
||||
this.layer = new Layer(layer);
|
||||
this.filters.addAll(filters);
|
||||
}
|
||||
|
||||
public Layer getLayer() {
|
||||
return this.layer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Layer getMatchingLayer(String resourceName) {
|
||||
boolean isIncluded = false;
|
||||
for (ResourceFilter filter : this.filters) {
|
||||
if (filter.isResourceExcluded(resourceName)) {
|
||||
return null;
|
||||
}
|
||||
if (!isIncluded && filter.isResourceIncluded(resourceName)) {
|
||||
isIncluded = true;
|
||||
}
|
||||
}
|
||||
return (isIncluded) ? this.layer : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools.layer.classes;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ResourceFilter} based on the resource location.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public class LocationFilter extends AbstractResourceFilter {
|
||||
|
||||
private static final AntPathMatcher MATCHER = new AntPathMatcher();
|
||||
|
||||
public LocationFilter(List<String> includes, List<String> excludes) {
|
||||
super(includes, excludes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMatch(String resourceName, List<String> toMatch) {
|
||||
return toMatch.stream().anyMatch((pattern) -> MATCHER.match(pattern, resourceName));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools.layer.classes;
|
||||
|
||||
/**
|
||||
* A filter that can tell if a resource has been included or excluded.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public interface ResourceFilter {
|
||||
|
||||
/**
|
||||
* Return true if the resource is included by the filter.
|
||||
* @param resourceName the resource name
|
||||
* @return true if the resource is included
|
||||
*/
|
||||
boolean isResourceIncluded(String resourceName);
|
||||
|
||||
/**
|
||||
* Return true if the resource is included by the filter.
|
||||
* @param resourceName the resource name
|
||||
* @return true if the resource is excluded
|
||||
*/
|
||||
boolean isResourceExcluded(String resourceName);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools.layer.classes;
|
||||
|
||||
import org.springframework.boot.loader.tools.Layer;
|
||||
|
||||
/**
|
||||
* A strategy used to match a resource to a layer.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public interface ResourceStrategy {
|
||||
|
||||
/**
|
||||
* Return a {@link Layer} for the given resource. If no matching layer is found,
|
||||
* {@code null} is returned.
|
||||
* @param resourceName the name of the resource
|
||||
* @return the matching layer or {@code null}
|
||||
*/
|
||||
Layer getMatchingLayer(String resourceName);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools.layer.library;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.boot.loader.tools.Library;
|
||||
import org.springframework.boot.loader.tools.LibraryCoordinates;
|
||||
|
||||
/**
|
||||
* An implementation of {@link LibraryFilter} based on the library's coordinates.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Scott Frederick
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public class CoordinateFilter implements LibraryFilter {
|
||||
|
||||
private final List<String> includes = new ArrayList<>();
|
||||
|
||||
private final List<String> excludes = new ArrayList<>();
|
||||
|
||||
public CoordinateFilter(List<String> includes, List<String> excludes) {
|
||||
this.includes.addAll(includes);
|
||||
this.excludes.addAll(excludes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLibraryIncluded(Library library) {
|
||||
return isMatch(library, this.includes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLibraryExcluded(Library library) {
|
||||
return isMatch(library, this.excludes);
|
||||
}
|
||||
|
||||
private boolean isMatch(Library library, List<String> toMatch) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
LibraryCoordinates coordinates = library.getCoordinates();
|
||||
if (coordinates != null) {
|
||||
if (coordinates.getGroupId() != null) {
|
||||
builder.append(coordinates.getGroupId());
|
||||
}
|
||||
builder.append(":");
|
||||
if (coordinates.getArtifactId() != null) {
|
||||
builder.append(coordinates.getArtifactId());
|
||||
}
|
||||
builder.append(":");
|
||||
if (coordinates.getVersion() != null) {
|
||||
builder.append(coordinates.getVersion());
|
||||
}
|
||||
}
|
||||
else {
|
||||
builder.append("::");
|
||||
}
|
||||
String input = builder.toString();
|
||||
for (String patternString : toMatch) {
|
||||
Pattern pattern = buildPatternForString(patternString);
|
||||
if (pattern.matcher(input).matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Pattern buildPatternForString(String pattern) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < pattern.length(); i++) {
|
||||
char c = pattern.charAt(i);
|
||||
if (c == '.') {
|
||||
builder.append("\\.");
|
||||
}
|
||||
else if (c == '*') {
|
||||
builder.append(".*");
|
||||
}
|
||||
else {
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
return Pattern.compile(builder.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools.layer.library;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.loader.tools.Layer;
|
||||
import org.springframework.boot.loader.tools.Library;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link LibraryStrategy} with custom filters.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public class FilteredLibraryStrategy implements LibraryStrategy {
|
||||
|
||||
private final Layer layer;
|
||||
|
||||
private final List<LibraryFilter> filters = new ArrayList<>();
|
||||
|
||||
public FilteredLibraryStrategy(String layer, List<LibraryFilter> filters) {
|
||||
Assert.notEmpty(filters, "Filters should not be empty for custom strategy.");
|
||||
this.layer = new Layer(layer);
|
||||
this.filters.addAll(filters);
|
||||
}
|
||||
|
||||
public Layer getLayer() {
|
||||
return this.layer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Layer getMatchingLayer(Library library) {
|
||||
boolean isIncluded = false;
|
||||
for (LibraryFilter filter : this.filters) {
|
||||
if (filter.isLibraryExcluded(library)) {
|
||||
return null;
|
||||
}
|
||||
if (!isIncluded && filter.isLibraryIncluded(library)) {
|
||||
isIncluded = true;
|
||||
}
|
||||
}
|
||||
return (isIncluded) ? this.layer : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools.layer.library;
|
||||
|
||||
import org.springframework.boot.loader.tools.Library;
|
||||
|
||||
/**
|
||||
* A filter that can tell if a {@link Library} has been included or excluded.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public interface LibraryFilter {
|
||||
|
||||
/**
|
||||
* Return true if the {@link Library} is included by the filter.
|
||||
* @param library the library
|
||||
* @return true if the library is included
|
||||
*/
|
||||
boolean isLibraryIncluded(Library library);
|
||||
|
||||
/**
|
||||
* Return true if the {@link Library} is excluded by the filter.
|
||||
* @param library the library
|
||||
* @return true if the library is excluded
|
||||
*/
|
||||
boolean isLibraryExcluded(Library library);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools.layer.library;
|
||||
|
||||
import org.springframework.boot.loader.tools.Layer;
|
||||
import org.springframework.boot.loader.tools.Library;
|
||||
|
||||
/**
|
||||
* A strategy used to match a library to a layer.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public interface LibraryStrategy {
|
||||
|
||||
/**
|
||||
* Return a {@link Layer} for the given {@link Library}. If no matching layer is
|
||||
* found, {@code null} is returned.
|
||||
* @param library the library
|
||||
* @return the matching layer or {@code null}
|
||||
*/
|
||||
Layer getMatchingLayer(Library library);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link LibraryCoordinates}.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class LibraryCoordinatesTests {
|
||||
|
||||
@Test
|
||||
void parseCoordinatesWithAllElements() {
|
||||
LibraryCoordinates coordinates = new LibraryCoordinates("com.acme:my-library:1.0.0");
|
||||
assertThat(coordinates.getGroupId()).isEqualTo("com.acme");
|
||||
assertThat(coordinates.getArtifactId()).isEqualTo("my-library");
|
||||
assertThat(coordinates.getVersion()).isEqualTo("1.0.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseCoordinatesWithoutVersion() {
|
||||
LibraryCoordinates coordinates = new LibraryCoordinates("com.acme:my-library");
|
||||
assertThat(coordinates.getGroupId()).isEqualTo("com.acme");
|
||||
assertThat(coordinates.getArtifactId()).isEqualTo("my-library");
|
||||
assertThat(coordinates.getVersion()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseCoordinatesWithEmptyElements() {
|
||||
LibraryCoordinates coordinates = new LibraryCoordinates(":my-library:");
|
||||
assertThat(coordinates.getGroupId()).isEqualTo("");
|
||||
assertThat(coordinates.getArtifactId()).isEqualTo("my-library");
|
||||
assertThat(coordinates.getVersion()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseCoordinatesWithExtraElements() {
|
||||
LibraryCoordinates coordinates = new LibraryCoordinates("com.acme:my-library:1.0.0.BUILD-SNAPSHOT:11111");
|
||||
assertThat(coordinates.getGroupId()).isEqualTo("com.acme");
|
||||
assertThat(coordinates.getArtifactId()).isEqualTo("my-library");
|
||||
assertThat(coordinates.getVersion()).isEqualTo("1.0.0.BUILD-SNAPSHOT");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseCoordinatesWithoutMinimumElements() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new LibraryCoordinates("com.acme"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools.layer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.loader.tools.Layer;
|
||||
import org.springframework.boot.loader.tools.Library;
|
||||
import org.springframework.boot.loader.tools.LibraryCoordinates;
|
||||
import org.springframework.boot.loader.tools.layer.classes.FilteredResourceStrategy;
|
||||
import org.springframework.boot.loader.tools.layer.classes.LocationFilter;
|
||||
import org.springframework.boot.loader.tools.layer.library.CoordinateFilter;
|
||||
import org.springframework.boot.loader.tools.layer.library.FilteredLibraryStrategy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link CustomLayers}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class CustomLayersTests {
|
||||
|
||||
@Test
|
||||
void customLayersAreAvailable() {
|
||||
Layer first = new Layer("first");
|
||||
Layer second = new Layer("second");
|
||||
CustomLayers customLayers = new CustomLayers(Arrays.asList(first, second), Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
List<Layer> actualLayers = new ArrayList<>();
|
||||
customLayers.iterator().forEachRemaining(actualLayers::add);
|
||||
assertThat(actualLayers).containsExactly(first, second);
|
||||
}
|
||||
|
||||
@Test
|
||||
void layerForResourceIsFound() {
|
||||
FilteredResourceStrategy resourceStrategy = new FilteredResourceStrategy("test", Collections
|
||||
.singletonList(new LocationFilter(Collections.singletonList("META-INF/**"), Collections.emptyList())));
|
||||
Layer targetLayer = new Layer("test");
|
||||
CustomLayers customLayers = new CustomLayers(Collections.singletonList(targetLayer),
|
||||
Collections.singletonList(resourceStrategy), Collections.emptyList());
|
||||
assertThat(customLayers.getLayer("META-INF/manifest.mf")).isNotNull().isEqualTo(targetLayer);
|
||||
}
|
||||
|
||||
@Test
|
||||
void layerForResourceIsNotFound() {
|
||||
FilteredResourceStrategy resourceStrategy = new FilteredResourceStrategy("test", Collections
|
||||
.singletonList(new LocationFilter(Collections.singletonList("META-INF/**"), Collections.emptyList())));
|
||||
CustomLayers customLayers = new CustomLayers(Collections.singletonList(new Layer("test")),
|
||||
Collections.singletonList(resourceStrategy), Collections.emptyList());
|
||||
assertThatIllegalStateException().isThrownBy(() -> customLayers.getLayer("com/acme"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void layerForLibraryIsFound() {
|
||||
FilteredLibraryStrategy libraryStrategy = new FilteredLibraryStrategy("test", Collections
|
||||
.singletonList(new CoordinateFilter(Collections.singletonList("com.acme:*"), Collections.emptyList())));
|
||||
Layer targetLayer = new Layer("test");
|
||||
CustomLayers customLayers = new CustomLayers(Collections.singletonList(targetLayer), Collections.emptyList(),
|
||||
Collections.singletonList(libraryStrategy));
|
||||
assertThat(customLayers.getLayer(mockLibrary("com.acme:test"))).isNotNull().isEqualTo(targetLayer);
|
||||
}
|
||||
|
||||
@Test
|
||||
void layerForLibraryIsNotFound() {
|
||||
FilteredLibraryStrategy libraryStrategy = new FilteredLibraryStrategy("test", Collections
|
||||
.singletonList(new CoordinateFilter(Collections.singletonList("com.acme:*"), Collections.emptyList())));
|
||||
CustomLayers customLayers = new CustomLayers(Collections.singletonList(new Layer("test")),
|
||||
Collections.emptyList(), Collections.singletonList(libraryStrategy));
|
||||
assertThatIllegalStateException().isThrownBy(() -> customLayers.getLayer(mockLibrary("org.another:test")));
|
||||
}
|
||||
|
||||
private Library mockLibrary(String coordinates) {
|
||||
Library library = mock(Library.class);
|
||||
given(library.getCoordinates()).willReturn(new LibraryCoordinates(coordinates));
|
||||
return library;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools.layer.classes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link FilteredResourceStrategy}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
class FilteredResourceStrategyTests {
|
||||
|
||||
@Test
|
||||
void createWhenFiltersNullShouldThrowException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new FilteredResourceStrategy("custom", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenFiltersEmptyShouldThrowException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new FilteredResourceStrategy("custom", Collections.emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLayerShouldReturnLayerName() {
|
||||
FilteredResourceStrategy strategy = new FilteredResourceStrategy("custom",
|
||||
Collections.singletonList(new TestFilter1()));
|
||||
assertThat(strategy.getLayer().toString()).isEqualTo("custom");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMatchingLayerWhenFilterMatchesIncludes() {
|
||||
FilteredResourceStrategy strategy = new FilteredResourceStrategy("custom",
|
||||
Collections.singletonList(new TestFilter1()));
|
||||
assertThat(strategy.getMatchingLayer("ABCD").toString()).isEqualTo("custom");
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesWhenFilterMatchesIncludesAndExcludesFromSameFilter() {
|
||||
FilteredResourceStrategy strategy = new FilteredResourceStrategy("custom",
|
||||
Collections.singletonList(new TestFilter1()));
|
||||
assertThat(strategy.getMatchingLayer("AZ")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesWhenFilterMatchesIncludesAndExcludesFromAnotherFilter() {
|
||||
List<ResourceFilter> filters = new ArrayList<>();
|
||||
filters.add(new TestFilter1());
|
||||
filters.add(new TestFilter2());
|
||||
FilteredResourceStrategy strategy = new FilteredResourceStrategy("custom", filters);
|
||||
assertThat(strategy.getMatchingLayer("AY")).isNull();
|
||||
}
|
||||
|
||||
private static class TestFilter1 implements ResourceFilter {
|
||||
|
||||
@Override
|
||||
public boolean isResourceIncluded(String resourceName) {
|
||||
return resourceName.startsWith("A");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResourceExcluded(String resourceName) {
|
||||
return resourceName.endsWith("Z");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TestFilter2 implements ResourceFilter {
|
||||
|
||||
@Override
|
||||
public boolean isResourceIncluded(String resourceName) {
|
||||
return resourceName.startsWith("B");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResourceExcluded(String resourceName) {
|
||||
return resourceName.endsWith("Y");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools.layer.classes;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link LocationFilter}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class LocationFilterTests {
|
||||
|
||||
@Test
|
||||
void isResourceIncludedWhenPatternMatchesWithWildcard() {
|
||||
LocationFilter filter = new LocationFilter(Collections.singletonList("META-INF/**"), Collections.emptyList());
|
||||
assertThat(filter.isResourceIncluded("META-INF/resources/application.yml")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isResourceIncludedWhenPatternDoesNotMatch() {
|
||||
LocationFilter filter = new LocationFilter(Collections.singletonList("META-INF/**"), Collections.emptyList());
|
||||
assertThat(filter.isResourceIncluded("src/main/resources/application.yml")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isResourceExcludedWhenPatternMatchesWithWildcard() {
|
||||
LocationFilter filter = new LocationFilter(Collections.emptyList(), Collections.singletonList("META-INF/**"));
|
||||
assertThat(filter.isResourceExcluded("META-INF/resources/application.yml")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isResourceExcludedWhenPatternDoesNotMatch() {
|
||||
LocationFilter filter = new LocationFilter(Collections.emptyList(), Collections.singletonList("META-INF/**"));
|
||||
assertThat(filter.isResourceExcluded("src/main/resources/application.yml")).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools.layer.library;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.loader.tools.Library;
|
||||
import org.springframework.boot.loader.tools.LibraryCoordinates;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link CoordinateFilter}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
class CoordinateFilterTests {
|
||||
|
||||
@Test
|
||||
void isLibraryIncludedWhenGroupIdIsNullAndToMatchHasWildcard() {
|
||||
List<String> includes = Collections.singletonList("*:*");
|
||||
CoordinateFilter filter = new CoordinateFilter(includes, Collections.emptyList());
|
||||
Library library = mock(Library.class);
|
||||
given(library.getCoordinates()).willReturn(new LibraryCoordinates(null, null, null));
|
||||
assertThat(filter.isLibraryIncluded(library)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLibraryIncludedWhenArtifactIdIsNullAndToMatchHasWildcard() {
|
||||
List<String> includes = Collections.singletonList("org.acme:*");
|
||||
CoordinateFilter filter = new CoordinateFilter(includes, Collections.emptyList());
|
||||
Library library = mock(Library.class);
|
||||
given(library.getCoordinates()).willReturn(new LibraryCoordinates("org.acme", null, null));
|
||||
assertThat(filter.isLibraryIncluded(library)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLibraryIncludedWhenVersionIsNullAndToMatchHasWildcard() {
|
||||
List<String> includes = Collections.singletonList("org.acme:something:*");
|
||||
CoordinateFilter filter = new CoordinateFilter(includes, Collections.emptyList());
|
||||
Library library = mock(Library.class);
|
||||
given(library.getCoordinates()).willReturn(new LibraryCoordinates("org.acme", "something", null));
|
||||
assertThat(filter.isLibraryIncluded(library)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLibraryIncludedWhenGroupIdDoesNotMatch() {
|
||||
List<String> includes = Collections.singletonList("org.acme:*");
|
||||
CoordinateFilter filter = new CoordinateFilter(includes, Collections.emptyList());
|
||||
Library library = mock(Library.class);
|
||||
given(library.getCoordinates()).willReturn(new LibraryCoordinates("other.foo", null, null));
|
||||
assertThat(filter.isLibraryIncluded(library)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLibraryIncludedWhenArtifactIdDoesNotMatch() {
|
||||
List<String> includes = Collections.singletonList("org.acme:test:*");
|
||||
CoordinateFilter filter = new CoordinateFilter(includes, Collections.emptyList());
|
||||
Library library = mock(Library.class);
|
||||
given(library.getCoordinates()).willReturn(new LibraryCoordinates("org.acme", "other", null));
|
||||
assertThat(filter.isLibraryIncluded(library)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLibraryIncludedWhenArtifactIdMatches() {
|
||||
List<String> includes = Collections.singletonList("org.acme:test:*");
|
||||
CoordinateFilter filter = new CoordinateFilter(includes, Collections.emptyList());
|
||||
Library library = mock(Library.class);
|
||||
given(library.getCoordinates()).willReturn(new LibraryCoordinates("org.acme", "test", null));
|
||||
assertThat(filter.isLibraryIncluded(library)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLibraryIncludedWhenVersionDoesNotMatch() {
|
||||
List<String> includes = Collections.singletonList("org.acme:test:*SNAPSHOT");
|
||||
CoordinateFilter filter = new CoordinateFilter(includes, Collections.emptyList());
|
||||
Library library = mock(Library.class);
|
||||
given(library.getCoordinates()).willReturn(new LibraryCoordinates("org.acme", "test", "1.0.0"));
|
||||
assertThat(filter.isLibraryIncluded(library)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isLibraryIncludedWhenVersionMatches() {
|
||||
List<String> includes = Collections.singletonList("org.acme:test:*SNAPSHOT");
|
||||
CoordinateFilter filter = new CoordinateFilter(includes, Collections.emptyList());
|
||||
Library library = mock(Library.class);
|
||||
given(library.getCoordinates()).willReturn(new LibraryCoordinates("org.acme", "test", "1.0.0-SNAPSHOT"));
|
||||
assertThat(filter.isLibraryIncluded(library)).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.tools.layer.library;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.loader.tools.Library;
|
||||
import org.springframework.boot.loader.tools.LibraryScope;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link FilteredLibraryStrategy}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
class FilteredLibraryStrategyTests {
|
||||
|
||||
@Test
|
||||
void createWhenFiltersNullShouldThrowException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new FilteredLibraryStrategy("custom", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenFiltersEmptyShouldThrowException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new FilteredLibraryStrategy("custom", Collections.emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLayerShouldReturnLayerName() {
|
||||
FilteredLibraryStrategy strategy = new FilteredLibraryStrategy("custom",
|
||||
Collections.singletonList(new TestFilter1Library()));
|
||||
assertThat(strategy.getLayer().toString()).isEqualTo("custom");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMatchingLayerWhenFilterMatchesIncludes() {
|
||||
FilteredLibraryStrategy strategy = new FilteredLibraryStrategy("custom",
|
||||
Collections.singletonList(new TestFilter1Library()));
|
||||
Library library = mockLibrary("A-Compile", LibraryScope.COMPILE);
|
||||
assertThat(strategy.getMatchingLayer(library).toString()).isEqualTo("custom");
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesWhenFilterMatchesIncludesAndExcludesFromSameFilter() {
|
||||
FilteredLibraryStrategy strategy = new FilteredLibraryStrategy("custom",
|
||||
Collections.singletonList(new TestFilter1Library()));
|
||||
Library library = mockLibrary("A-Runtime", LibraryScope.RUNTIME);
|
||||
assertThat(strategy.getMatchingLayer(library)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesWhenFilterMatchesIncludesAndExcludesFromAnotherFilter() {
|
||||
List<LibraryFilter> filters = new ArrayList<>();
|
||||
filters.add(new TestFilter1Library());
|
||||
filters.add(new TestFilter2Library());
|
||||
FilteredLibraryStrategy strategy = new FilteredLibraryStrategy("custom", filters);
|
||||
Library library = mockLibrary("A-Provided", LibraryScope.PROVIDED);
|
||||
assertThat(strategy.getMatchingLayer(library)).isNull();
|
||||
}
|
||||
|
||||
private Library mockLibrary(String name, LibraryScope runtime) {
|
||||
Library library = mock(Library.class);
|
||||
given(library.getName()).willReturn(name);
|
||||
given(library.getScope()).willReturn(runtime);
|
||||
return library;
|
||||
}
|
||||
|
||||
private static class TestFilter1Library implements LibraryFilter {
|
||||
|
||||
@Override
|
||||
public boolean isLibraryIncluded(Library library) {
|
||||
return library.getName().contains("A");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLibraryExcluded(Library library) {
|
||||
return library.getScope().equals(LibraryScope.RUNTIME);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TestFilter2Library implements LibraryFilter {
|
||||
|
||||
@Override
|
||||
public boolean isLibraryIncluded(Library library) {
|
||||
return library.getName().contains("B");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLibraryExcluded(Library library) {
|
||||
return library.getScope().equals(LibraryScope.PROVIDED);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -305,4 +305,15 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenJarIsRepackagedWithTheCustomLayeredLayout(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("jar-layered-custom").execute((project) -> {
|
||||
File repackaged = new File(project, "jar/target/jar-layered-0.0.1.BUILD-SNAPSHOT.jar");
|
||||
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/layers/application/classes/")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/layers/my-dependencies-name/lib/jar-release")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/layers/snapshot-dependencies/lib/jar-snapshot")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/layers/configuration/classes/application.yml");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>jar-release</artifactId>
|
||||
<version>0.0.1.RELEASE</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>jar</name>
|
||||
<description>Release Jar dependency</description>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>jar-snapshot</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>jar</name>
|
||||
<description>Snapshot Jar dependency</description>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>jar-layered</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>@java.version@</maven.compiler.source>
|
||||
<maven.compiler.target>@java.version@</maven.compiler.target>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>@project.groupId@</groupId>
|
||||
<artifactId>@project.artifactId@</artifactId>
|
||||
<version>@project.version@</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<layers>
|
||||
<enabled>true</enabled>
|
||||
<configuration>${project.basedir}/src/layers.xml</configuration>
|
||||
</layers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>jar-snapshot</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>jar-release</artifactId>
|
||||
<version>0.0.1.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<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.xsd">
|
||||
<layers>
|
||||
<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>
|
||||
<classes>
|
||||
<layer-content layer="configuration">
|
||||
<locations>
|
||||
<include>**/application*.*</include>
|
||||
</locations>
|
||||
</layer-content>
|
||||
<layer-content layer="application">
|
||||
<locations>
|
||||
<include>**</include>
|
||||
</locations>
|
||||
</layer-content>
|
||||
</classes>
|
||||
</layers-configuration>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.test;
|
||||
|
||||
public class SampleApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>aggregator</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>@java.version@</maven.compiler.source>
|
||||
<maven.compiler.target>@java.version@</maven.compiler.target>
|
||||
</properties>
|
||||
<modules>
|
||||
<module>jar-snapshot</module>
|
||||
<module>jar-release</module>
|
||||
<module>jar</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
|
@ -22,10 +22,10 @@
|
|||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<layered>
|
||||
<layers>
|
||||
<enabled>true</enabled>
|
||||
<includeLayerTools>false</includeLayerTools>
|
||||
</layered>
|
||||
</layers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@
|
|||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<layered>
|
||||
<layers>
|
||||
<enabled>true</enabled>
|
||||
</layered>
|
||||
</layers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
|
|
|||
|
|
@ -16,12 +16,17 @@
|
|||
|
||||
package org.springframework.boot.maven;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.model.Dependency;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
|
|
@ -31,6 +36,8 @@ import org.apache.maven.project.MavenProject;
|
|||
import org.apache.maven.project.MavenProjectHelper;
|
||||
import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
|
||||
import org.apache.maven.shared.artifact.filter.collection.ScopeFilter;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import org.springframework.boot.loader.tools.Layout;
|
||||
import org.springframework.boot.loader.tools.LayoutFactory;
|
||||
|
|
@ -109,7 +116,7 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
|||
* @since 2.3.0
|
||||
*/
|
||||
@Parameter
|
||||
private Layered layered;
|
||||
private Layers layers;
|
||||
|
||||
/**
|
||||
* Return a {@link Packager} configured for this MOJO.
|
||||
|
|
@ -126,13 +133,31 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
|||
getLog().info("Layout: " + this.layout);
|
||||
packager.setLayout(this.layout.layout());
|
||||
}
|
||||
if (this.layered != null && this.layered.isEnabled()) {
|
||||
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.setLayout(new LayeredJar());
|
||||
packager.setIncludeRelevantJarModeJars(this.layered.isIncludeLayerTools());
|
||||
packager.setIncludeRelevantJarModeJars(this.layers.isIncludeLayerTools());
|
||||
}
|
||||
return packager;
|
||||
}
|
||||
|
||||
private Document getDocumentIfAvailable(File configurationFile) throws Exception {
|
||||
InputSource inputSource = new InputSource(new FileInputStream(configurationFile));
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
return builder.parse(inputSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link Libraries} that the packager can use.
|
||||
* @param unpacks any libraries that require unpack
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import org.apache.maven.plugin.logging.Log;
|
|||
import org.springframework.boot.loader.tools.Libraries;
|
||||
import org.springframework.boot.loader.tools.Library;
|
||||
import org.springframework.boot.loader.tools.LibraryCallback;
|
||||
import org.springframework.boot.loader.tools.LibraryCoordinates;
|
||||
import org.springframework.boot.loader.tools.LibraryScope;
|
||||
|
||||
/**
|
||||
|
|
@ -39,6 +40,7 @@ import org.springframework.boot.loader.tools.LibraryScope;
|
|||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Scott Frederick
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class ArtifactsLibraries implements Libraries {
|
||||
|
|
@ -78,7 +80,9 @@ public class ArtifactsLibraries implements Libraries {
|
|||
name = artifact.getGroupId() + "-" + name;
|
||||
this.log.debug("Renamed to: " + name);
|
||||
}
|
||||
callback.library(new Library(name, artifact.getFile(), scope, isUnpackRequired(artifact)));
|
||||
LibraryCoordinates coordinates = new LibraryCoordinates(artifact.getGroupId(), artifact.getArtifactId(),
|
||||
artifact.getVersion());
|
||||
callback.library(new Library(name, artifact.getFile(), scope, isUnpackRequired(artifact), coordinates));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.maven;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.boot.loader.tools.Layer;
|
||||
import org.springframework.boot.loader.tools.layer.CustomLayers;
|
||||
import org.springframework.boot.loader.tools.layer.classes.FilteredResourceStrategy;
|
||||
import org.springframework.boot.loader.tools.layer.classes.ResourceFilter;
|
||||
import org.springframework.boot.loader.tools.layer.classes.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;
|
||||
|
||||
/**
|
||||
* Produces a {@link CustomLayers} based on the given {@link Document}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public class CustomLayersProvider {
|
||||
|
||||
public CustomLayers getLayers(Document document) {
|
||||
Element root = document.getDocumentElement();
|
||||
NodeList nl = root.getChildNodes();
|
||||
List<Layer> layers = new ArrayList<>();
|
||||
List<LibraryStrategy> libraryStrategies = new ArrayList<>();
|
||||
List<ResourceStrategy> resourceStrategies = new ArrayList<>();
|
||||
for (int i = 0; i < nl.getLength(); i++) {
|
||||
Node node = nl.item(i);
|
||||
if (node instanceof Element) {
|
||||
Element ele = (Element) node;
|
||||
String nodeName = ele.getNodeName();
|
||||
if ("layers".equals(nodeName)) {
|
||||
layers.addAll(getLayers(ele));
|
||||
}
|
||||
if ("libraries".equals(nodeName)) {
|
||||
libraryStrategies.addAll(getLibraryStrategies(ele.getChildNodes()));
|
||||
}
|
||||
if ("classes".equals(nodeName)) {
|
||||
resourceStrategies.addAll(getResourceStrategies(ele.getChildNodes()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return new CustomLayers(layers, resourceStrategies, libraryStrategies);
|
||||
}
|
||||
|
||||
private List<LibraryStrategy> getLibraryStrategies(NodeList nodes) {
|
||||
List<LibraryStrategy> strategy = new ArrayList<>();
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
Node item = nodes.item(i);
|
||||
if (item instanceof Element) {
|
||||
Element element = (Element) item;
|
||||
String layer = element.getAttribute("layer");
|
||||
if ("layer-content".equals(element.getTagName())) {
|
||||
List<LibraryFilter> filters = new ArrayList<>();
|
||||
NodeList filterList = item.getChildNodes();
|
||||
if (filterList.getLength() == 0) {
|
||||
throw new IllegalArgumentException("Filters for layer-content must not be empty.");
|
||||
}
|
||||
for (int k = 0; k < filterList.getLength(); k++) {
|
||||
Node filter = filterList.item(k);
|
||||
if (filter instanceof Element) {
|
||||
List<String> includeList = getPatterns((Element) filter, "include");
|
||||
List<String> excludeList = getPatterns((Element) filter, "exclude");
|
||||
addLibraryFilter(filters, filter, includeList, excludeList);
|
||||
}
|
||||
}
|
||||
strategy.add(new FilteredLibraryStrategy(layer, filters));
|
||||
}
|
||||
}
|
||||
}
|
||||
return strategy;
|
||||
}
|
||||
|
||||
private void addLibraryFilter(List<LibraryFilter> filters, Node filter, List<String> includeList,
|
||||
List<String> excludeList) {
|
||||
if ("coordinates".equals(filter.getNodeName())) {
|
||||
filters.add(new CoordinateFilter(includeList, excludeList));
|
||||
}
|
||||
}
|
||||
|
||||
private List<ResourceStrategy> getResourceStrategies(NodeList strategies) {
|
||||
List<ResourceStrategy> strategy = new ArrayList<>();
|
||||
for (int i = 0; i < strategies.getLength(); i++) {
|
||||
Node item = strategies.item(i);
|
||||
List<ResourceFilter> filters = new ArrayList<>();
|
||||
if (item instanceof Element) {
|
||||
Element element = (Element) item;
|
||||
String layer = element.getAttribute("layer");
|
||||
if ("layer-content".equals(element.getTagName())) {
|
||||
NodeList filterList = item.getChildNodes();
|
||||
if (filterList.getLength() == 0) {
|
||||
throw new IllegalArgumentException("Filters for layer-content must not be empty.");
|
||||
}
|
||||
for (int k = 0; k < filterList.getLength(); k++) {
|
||||
Node filter = filterList.item(k);
|
||||
if (filter instanceof Element) {
|
||||
List<String> includeList = getPatterns((Element) filter, "include");
|
||||
List<String> excludeList = getPatterns((Element) filter, "exclude");
|
||||
addFilter(filters, filter, includeList, excludeList);
|
||||
}
|
||||
}
|
||||
strategy.add(new FilteredResourceStrategy(layer, filters));
|
||||
}
|
||||
}
|
||||
}
|
||||
return strategy;
|
||||
}
|
||||
|
||||
private void addFilter(List<ResourceFilter> filters, Node filter, List<String> includeList,
|
||||
List<String> excludeList) {
|
||||
if ("locations".equals(filter.getNodeName())) {
|
||||
filters.add(
|
||||
new org.springframework.boot.loader.tools.layer.classes.LocationFilter(includeList, excludeList));
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getPatterns(Element element, String key) {
|
||||
NodeList patterns = element.getElementsByTagName(key);
|
||||
List<String> values = new ArrayList<>();
|
||||
for (int j = 0; j < patterns.getLength(); j++) {
|
||||
Node item = patterns.item(j);
|
||||
if (item instanceof Element) {
|
||||
values.add(item.getTextContent());
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private List<Layer> getLayers(Element element) {
|
||||
List<Layer> layers = new ArrayList<>();
|
||||
NodeList nl = element.getChildNodes();
|
||||
for (int i = 0; i < nl.getLength(); i++) {
|
||||
Node node = nl.item(i);
|
||||
if (node instanceof Element) {
|
||||
Element ele = (Element) node;
|
||||
String nodeName = ele.getNodeName();
|
||||
if ("layer".equals(nodeName)) {
|
||||
layers.add(new Layer(ele.getTextContent()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return layers;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,18 +16,22 @@
|
|||
|
||||
package org.springframework.boot.maven;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Layer configuration options.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public class Layered {
|
||||
public class Layers {
|
||||
|
||||
private boolean enabled;
|
||||
|
||||
private boolean includeLayerTools = true;
|
||||
|
||||
private File configuration;
|
||||
|
||||
/**
|
||||
* Whether layered jar layout is enabled.
|
||||
* @return true if the layered layout is enabled.
|
||||
|
|
@ -44,4 +48,18 @@ public class Layered {
|
|||
return this.includeLayerTools;
|
||||
}
|
||||
|
||||
/**
|
||||
* The location of the layers configuration file. If no file is provided, a default
|
||||
* configuration is used with four layers: {@code application}, {@code resources},
|
||||
* {@code snapshot-dependencies} and {@code dependencies}.
|
||||
* @return the layers configuration file
|
||||
*/
|
||||
public File getConfiguration() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
public void setConfiguration(File configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
<?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="classesType" name="classes" 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="classesType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Strategies that should be applied to classes. 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="classesLayerContentType" name="layer-content" maxOccurs="unbounded"
|
||||
minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Strategy to apply on classes.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="classesLayerContentType" 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>
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.maven;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import org.springframework.boot.loader.tools.Library;
|
||||
import org.springframework.boot.loader.tools.LibraryCoordinates;
|
||||
import org.springframework.boot.loader.tools.layer.CustomLayers;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link CustomLayersProvider}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
public class CustomLayersProviderTests {
|
||||
|
||||
private CustomLayersProvider customLayersProvider;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.customLayersProvider = new CustomLayersProvider();
|
||||
}
|
||||
|
||||
@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");
|
||||
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);
|
||||
assertThat(layers.getLayer(snapshot).toString()).isEqualTo("snapshot-dependencies");
|
||||
assertThat(layers.getLayer(groupId).toString()).isEqualTo("my-deps");
|
||||
assertThat(layers.getLayer(otherDependency).toString()).isEqualTo("my-dependencies-name");
|
||||
assertThat(layers.getLayer("META-INF/resources/test.css").toString()).isEqualTo("my-resources");
|
||||
assertThat(layers.getLayer("application.yml").toString()).isEqualTo("configuration");
|
||||
assertThat(layers.getLayer("test").toString()).isEqualTo("application");
|
||||
}
|
||||
|
||||
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));
|
||||
return library;
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLayerResolverWhenDocumentContainsLibraryLayerWithNoFilters() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.customLayersProvider.getLayers(getDocument("library-layer-no-filter.xml")))
|
||||
.withMessage("Filters for layer-content must not be empty.");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLayerResolverWhenDocumentContainsResourceLayerWithNoFilters() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.customLayersProvider.getLayers(getDocument("resource-layer-no-filter.xml")))
|
||||
.withMessage("Filters for layer-content must not be empty.");
|
||||
}
|
||||
|
||||
private Document getDocument(String resourceName) throws Exception {
|
||||
ClassPathResource resource = new ClassPathResource(resourceName);
|
||||
InputSource inputSource = new InputSource(resource.getInputStream());
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder documentBuilder = factory.newDocumentBuilder();
|
||||
return documentBuilder.parse(inputSource);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<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>configuration</layer>
|
||||
<layer>application</layer>
|
||||
<layer>my-resources</layer>
|
||||
<layer>snapshot-dependencies</layer>
|
||||
<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>
|
||||
<classes>
|
||||
<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>
|
||||
</classes>
|
||||
</layers-configuration>
|
||||
|
|
@ -0,0 +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.xsd">
|
||||
<layers>
|
||||
<layer>my-deps</layer>
|
||||
</layers>
|
||||
<libraries>
|
||||
<layer-content layer="my-deps"/>
|
||||
</libraries>
|
||||
</layers-configuration>
|
||||
|
|
@ -0,0 +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.xsd">
|
||||
<layers>
|
||||
<layer>my-layer</layer>
|
||||
</layers>
|
||||
<classes>
|
||||
<layer-content layer="my-layer"/>
|
||||
</classes>
|
||||
</layers-configuration>
|
||||
Loading…
Reference in New Issue