Skip to content

Commit 595a052

Browse files
mweirauchodrotbohm
authored andcommitted
GH-160 - Prevent missing ArchUnit runtime dependency.
Adds ArchUnit as an explicit dependency to spring-modulith-runtime. Registers auto-configuration that throws an exception in case ArchUnit is missing from the classpath (as it is likely that it's declared as test scope dependency only) and a corresponding failure analyzer to give guidance on how to solve the problem.
1 parent e16efe9 commit 595a052

File tree

6 files changed

+116
-0
lines changed

6 files changed

+116
-0
lines changed

spring-modulith-runtime/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
2222
<version>${project.version}</version>
2323
</dependency>
2424

25+
<dependency>
26+
<groupId>com.tngtech.archunit</groupId>
27+
<artifactId>archunit</artifactId>
28+
<version>${archunit.version}</version>
29+
</dependency>
30+
2531
<dependency>
2632
<groupId>org.jgrapht</groupId>
2733
<artifactId>jgrapht-core</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.modulith.runtime.autoconfigure;
17+
18+
/**
19+
* An Exception carrying information about a missing runtime dependency to be
20+
* analyzed by {@link MissingRuntimeDependencyFailureAnalyzer}.
21+
*
22+
* @author Michael Weirauch
23+
*/
24+
class MissingRuntimeDependencyException extends RuntimeException {
25+
26+
private static final long serialVersionUID = 1L;
27+
28+
private final String dependencyName;
29+
30+
public MissingRuntimeDependencyException(String dependencyName) {
31+
this.dependencyName = dependencyName;
32+
}
33+
34+
public String getDependencyName() {
35+
return dependencyName;
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.modulith.runtime.autoconfigure;
17+
18+
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
19+
import org.springframework.boot.diagnostics.FailureAnalysis;
20+
import org.springframework.boot.diagnostics.FailureAnalyzer;
21+
22+
/**
23+
* {@link FailureAnalyzer} for {@link MissingRuntimeDependencyException}.
24+
*
25+
* @author Michael Weirauch
26+
*/
27+
class MissingRuntimeDependencyFailureAnalyzer extends AbstractFailureAnalyzer<MissingRuntimeDependencyException> {
28+
29+
@Override
30+
protected FailureAnalysis analyze(Throwable rootFailure, MissingRuntimeDependencyException cause) {
31+
return new FailureAnalysis(
32+
String.format("Spring Modulith requires the dependency '%s' to be on the runtime classpath.",
33+
cause.getDependencyName()),
34+
"Add the missing dependency to the runtime classpath of your project.", cause);
35+
}
36+
}

spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfiguration.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.beans.factory.config.BeanDefinition;
2626
import org.springframework.boot.autoconfigure.AutoConfiguration;
2727
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
28+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
2829
import org.springframework.boot.context.event.ApplicationStartedEvent;
2930
import org.springframework.context.ApplicationContext;
3031
import org.springframework.context.ApplicationListener;
@@ -45,10 +46,20 @@
4546
* Bean.
4647
*
4748
* @author Oliver Drotbohm
49+
* @author Michael Weirauch
4850
*/
4951
@AutoConfiguration
5052
class SpringModulithRuntimeAutoConfiguration {
5153

54+
@AutoConfiguration
55+
@ConditionalOnMissingClass("com.tngtech.archunit.core.importer.ClassFileImporter")
56+
static class ArchUnitRuntimeDependencyMissingConfiguration {
57+
58+
ArchUnitRuntimeDependencyMissingConfiguration() {
59+
throw new MissingRuntimeDependencyException("archunit");
60+
}
61+
}
62+
5263
private static final Logger LOGGER = LoggerFactory.getLogger(SpringModulithRuntimeAutoConfiguration.class);
5364
private final AsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
5465

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Failure analyzers
2+
org.springframework.boot.diagnostics.FailureAnalyzer=\
3+
org.springframework.modulith.runtime.autoconfigure.MissingRuntimeDependencyFailureAnalyzer

spring-modulith-runtime/src/test/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfigurationIntegrationTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,21 @@
1818
import static org.assertj.core.api.Assertions.*;
1919

2020
import org.junit.jupiter.api.Test;
21+
import org.springframework.beans.BeanInstantiationException;
2122
import org.springframework.boot.autoconfigure.AutoConfigurations;
2223
import org.springframework.boot.autoconfigure.SpringBootApplication;
24+
import org.springframework.boot.test.context.FilteredClassLoader;
2325
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
2426
import org.springframework.modulith.runtime.ApplicationModulesRuntime;
2527
import org.springframework.modulith.runtime.ApplicationRuntime;
2628

29+
import com.tngtech.archunit.core.importer.ClassFileImporter;
30+
2731
/**
2832
* Integration test for {@link SpringModulithRuntimeAutoConfiguration}.
2933
*
3034
* @author Oliver Drotbohm
35+
* @author Michael Weirauch
3136
*/
3237
class SpringModulithRuntimeAutoConfigurationIntegrationTests {
3338

@@ -48,4 +53,22 @@ void bootstrapRegistersRuntimeInstances() {
4853
context.close();
4954
});
5055
}
56+
57+
@Test
58+
void missingArchUnitRuntimeDependencyEscalatesOnContextStartup() {
59+
60+
new ApplicationContextRunner()
61+
.withUserConfiguration(SampleApp.class)
62+
.withConfiguration(AutoConfigurations.of(SpringModulithRuntimeAutoConfiguration.class))
63+
.withClassLoader(new FilteredClassLoader(ClassFileImporter.class))
64+
.run(context -> {
65+
66+
assertThat(context).hasFailed();
67+
assertThat(context.getStartupFailure().getCause())
68+
.isInstanceOf(BeanInstantiationException.class)
69+
.cause().isInstanceOf(MissingRuntimeDependencyException.class);
70+
71+
context.close();
72+
});
73+
}
5174
}

0 commit comments

Comments
 (0)