Commit e743d5fe authored by Andy Wilkinson's avatar Andy Wilkinson

Exclude by jar type when running and packaging with Maven

This commit updates the Maven Plugin to filter dependencies based on
the Spring-Boot-Jar-Type entry in their manifest. Jars with a
Spring-Boot-Jar-Type of dependencies-starter or annotation-processor
are excluded.

See gh-22036
parent 3ba7d989
...@@ -713,30 +713,6 @@ With Maven the dependency should be declared as optional, as shown in the follow ...@@ -713,30 +713,6 @@ With Maven the dependency should be declared as optional, as shown in the follow
</dependency> </dependency>
---- ----
If you have defined `@ConfigurationProperties` in your application, make sure to configure the `spring-boot-maven-plugin` to prevent the `repackage` goal from adding the dependency into the fat jar:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
----
With Gradle 4.5 and earlier, the dependency should be declared in the `compileOnly` configuration, as shown in the following example: With Gradle 4.5 and earlier, the dependency should be declared in the `compileOnly` configuration, as shown in the following example:
[source,groovy,indent=0,subs="verbatim,quotes,attributes"] [source,groovy,indent=0,subs="verbatim,quotes,attributes"]
......
...@@ -104,6 +104,7 @@ public abstract class AbstractDependencyFilterMojo extends AbstractMojo { ...@@ -104,6 +104,7 @@ public abstract class AbstractDependencyFilterMojo extends AbstractMojo {
if (this.excludes != null && !this.excludes.isEmpty()) { if (this.excludes != null && !this.excludes.isEmpty()) {
filters.addFilter(new ExcludeFilter(this.excludes)); filters.addFilter(new ExcludeFilter(this.excludes));
} }
filters.addFilter(new JarTypeFilter());
return filters; return filters;
} }
......
/*
* 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.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarFile;
import org.apache.maven.artifact.Artifact;
/**
* A {@link DependencyFilter} that filters dependencies based on the jar type declared in
* their manifest.
*
* @author Andy Wilkinson
*/
class JarTypeFilter extends DependencyFilter {
private static final Set<String> EXCLUDED_JAR_TYPES = Collections
.unmodifiableSet(new HashSet<>(Arrays.asList("annotation-processor", "dependencies-starter")));
JarTypeFilter() {
super(Collections.emptyList());
}
@Override
protected boolean filter(Artifact artifact) {
try (JarFile jarFile = new JarFile(artifact.getFile())) {
String jarType = jarFile.getManifest().getMainAttributes().getValue("Spring-Boot-Jar-Type");
return jarType != null && EXCLUDED_JAR_TYPES.contains(jarType);
}
catch (IOException ex) {
return false;
}
}
}
...@@ -16,17 +16,25 @@ ...@@ -16,17 +16,25 @@
package org.springframework.boot.maven; package org.springframework.boot.maven;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter; import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
import org.apache.maven.shared.artifact.filter.collection.ScopeFilter; import org.apache.maven.shared.artifact.filter.collection.ScopeFilter;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
...@@ -39,6 +47,9 @@ import static org.mockito.Mockito.mock; ...@@ -39,6 +47,9 @@ import static org.mockito.Mockito.mock;
*/ */
class DependencyFilterMojoTests { class DependencyFilterMojoTests {
@TempDir
static Path temp;
@Test @Test
void filterDependencies() throws MojoExecutionException { void filterDependencies() throws MojoExecutionException {
TestableDependencyFilterMojo mojo = new TestableDependencyFilterMojo(Collections.emptyList(), "com.foo"); TestableDependencyFilterMojo mojo = new TestableDependencyFilterMojo(Collections.emptyList(), "com.foo");
...@@ -97,20 +108,50 @@ class DependencyFilterMojoTests { ...@@ -97,20 +108,50 @@ class DependencyFilterMojoTests {
assertThat(artifacts).containsExactly(one, three, four); assertThat(artifacts).containsExactly(one, three, four);
} }
@Test
void excludeByJarType() throws MojoExecutionException {
TestableDependencyFilterMojo mojo = new TestableDependencyFilterMojo(Collections.emptyList(), "");
Artifact one = createArtifact("com.foo", "one", null, "dependencies-starter");
Artifact two = createArtifact("com.bar", "two");
Set<Artifact> artifacts = mojo.filterDependencies(one, two);
assertThat(artifacts).containsExactly(two);
}
private static Artifact createArtifact(String groupId, String artifactId) { private static Artifact createArtifact(String groupId, String artifactId) {
return createArtifact(groupId, artifactId, null); return createArtifact(groupId, artifactId, null);
} }
private static Artifact createArtifact(String groupId, String artifactId, String scope) { private static Artifact createArtifact(String groupId, String artifactId, String scope) {
return createArtifact(groupId, artifactId, scope, null);
}
private static Artifact createArtifact(String groupId, String artifactId, String scope, String jarType) {
Artifact a = mock(Artifact.class); Artifact a = mock(Artifact.class);
given(a.getGroupId()).willReturn(groupId); given(a.getGroupId()).willReturn(groupId);
given(a.getArtifactId()).willReturn(artifactId); given(a.getArtifactId()).willReturn(artifactId);
if (scope != null) { if (scope != null) {
given(a.getScope()).willReturn(scope); given(a.getScope()).willReturn(scope);
} }
given(a.getFile()).willReturn(createArtifactFile(jarType));
return a; return a;
} }
private static File createArtifactFile(String jarType) {
Path jarPath = temp.resolve(UUID.randomUUID().toString() + ".jar");
Manifest manifest = new Manifest();
manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
if (jarType != null) {
manifest.getMainAttributes().putValue("Spring-Boot-Jar-Type", jarType);
}
try {
new JarOutputStream(new FileOutputStream(jarPath.toFile()), manifest).close();
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
return jarPath.toFile();
}
private static final class TestableDependencyFilterMojo extends AbstractDependencyFilterMojo { private static final class TestableDependencyFilterMojo extends AbstractDependencyFilterMojo {
private final ArtifactsFilter[] additionalFilters; private final ArtifactsFilter[] additionalFilters;
......
/*
* 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.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import org.apache.maven.artifact.Artifact;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link JarTypeFilter}.
*
* @author Andy Wilkinson
*/
class JarTypeFilterTests {
@TempDir
Path temp;
@Test
void whenArtifactHasNoJarTypeThenItIsIncluded() {
assertThat(new JarTypeFilter().filter(createArtifact(null))).isFalse();
}
@Test
void whenArtifactHasJarTypeThatIsNotExcludedThenItIsIncluded() {
assertThat(new JarTypeFilter().filter(createArtifact("something-included"))).isFalse();
}
@Test
void whenArtifactHasDependenciesStarterJarTypeThenItIsExcluded() {
assertThat(new JarTypeFilter().filter(createArtifact("dependencies-starter"))).isTrue();
}
@Test
void whenArtifactHasAnnotationProcessorJarTypeThenItIsExcluded() {
assertThat(new JarTypeFilter().filter(createArtifact("annotation-processor"))).isTrue();
}
private Artifact createArtifact(String jarType) {
Path jarPath = this.temp.resolve("test.jar");
Manifest manifest = new Manifest();
manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
if (jarType != null) {
manifest.getMainAttributes().putValue("Spring-Boot-Jar-Type", jarType);
}
try {
new JarOutputStream(new FileOutputStream(jarPath.toFile()), manifest).close();
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
Artifact artifact = mock(Artifact.class);
given(artifact.getFile()).willReturn(jarPath.toFile());
return artifact;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment