Skip to content

Commit 4f809fc

Browse files
authored
Merge pull request #10165 from geoand/#10164
Warn when a spring-web class is not annotated with @RestController
2 parents b7aa52a + 94d8573 commit 4f809fc

File tree

3 files changed

+102
-0
lines changed

3 files changed

+102
-0
lines changed

extensions/spring-web/deployment/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@
4141
<artifactId>rest-assured</artifactId>
4242
<scope>test</scope>
4343
</dependency>
44+
<dependency>
45+
<groupId>org.assertj</groupId>
46+
<artifactId>assertj-core</artifactId>
47+
<scope>test</scope>
48+
</dependency>
4449
</dependencies>
4550

4651
<build>

extensions/spring-web/deployment/src/main/java/io/quarkus/spring/web/deployment/SpringWebProcessor.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.jboss.jandex.IndexView;
2626
import org.jboss.jandex.MethodInfo;
2727
import org.jboss.jandex.Type;
28+
import org.jboss.logging.Logger;
2829
import org.jboss.resteasy.core.MediaTypeMap;
2930
import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters;
3031
import org.jboss.resteasy.spi.ResteasyDeployment;
@@ -63,6 +64,8 @@
6364

6465
public class SpringWebProcessor {
6566

67+
private static final Logger LOGGER = Logger.getLogger(SpringWebProcessor.class.getName());
68+
6669
private static final DotName REST_CONTROLLER_ANNOTATION = DotName
6770
.createSimple("org.springframework.web.bind.annotation.RestController");
6871

@@ -162,6 +165,8 @@ public void process(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem,
162165
BuildProducer<ServletInitParamBuildItem> initParamProducer,
163166
BuildProducer<ResteasyDeploymentCustomizerBuildItem> deploymentCustomizerProducer) {
164167

168+
validateControllers(beanArchiveIndexBuildItem);
169+
165170
final IndexView index = beanArchiveIndexBuildItem.getIndex();
166171
final Collection<AnnotationInstance> annotations = index.getAnnotations(REST_CONTROLLER_ANNOTATION);
167172
if (annotations.isEmpty()) {
@@ -190,6 +195,35 @@ public void accept(ResteasyDeployment resteasyDeployment) {
190195
reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, false, SpringResourceBuilder.class.getName()));
191196
}
192197

198+
/**
199+
* Make sure the controllers have the proper annotation and warn if not
200+
*/
201+
private void validateControllers(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem) {
202+
Collection<AnnotationInstance> annotations = beanArchiveIndexBuildItem.getIndex().getAnnotations(REQUEST_MAPPING);
203+
Set<DotName> classesWithoutRestController = new HashSet<>();
204+
for (AnnotationInstance annotation : annotations) {
205+
ClassInfo targetClass;
206+
if (annotation.target().kind() == AnnotationTarget.Kind.CLASS) {
207+
targetClass = annotation.target().asClass();
208+
} else if (annotation.target().kind() == AnnotationTarget.Kind.METHOD) {
209+
targetClass = annotation.target().asMethod().declaringClass();
210+
} else {
211+
continue;
212+
}
213+
214+
if (targetClass.classAnnotation(REST_CONTROLLER_ANNOTATION) == null) {
215+
classesWithoutRestController.add(targetClass.name());
216+
}
217+
}
218+
219+
if (!classesWithoutRestController.isEmpty()) {
220+
for (DotName dotName : classesWithoutRestController) {
221+
LOGGER.warn("Class '" + dotName
222+
+ "' uses '@RequestMapping' but was not annotated with '@RestContoller' and will therefore be ignored.");
223+
}
224+
}
225+
}
226+
193227
@BuildStep(onlyIf = IsDevelopment.class)
194228
@Record(STATIC_INIT)
195229
public void registerWithDevModeNotFoundMapper(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem,
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package io.quarkus.spring.web.test;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import java.util.List;
6+
import java.util.logging.LogRecord;
7+
8+
import org.jboss.shrinkwrap.api.ShrinkWrap;
9+
import org.jboss.shrinkwrap.api.spec.JavaArchive;
10+
import org.junit.jupiter.api.Test;
11+
import org.junit.jupiter.api.extension.RegisterExtension;
12+
import org.springframework.web.bind.annotation.GetMapping;
13+
import org.springframework.web.bind.annotation.RequestMapping;
14+
import org.springframework.web.bind.annotation.RestController;
15+
16+
import io.quarkus.test.ProdBuildResults;
17+
import io.quarkus.test.ProdModeTestResults;
18+
import io.quarkus.test.QuarkusProdModeTest;
19+
20+
public class MissingRestControllerTest {
21+
22+
@RegisterExtension
23+
static final QuarkusProdModeTest config = new QuarkusProdModeTest()
24+
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
25+
.addClasses(NonAnnotatedController.class, ProperController.class))
26+
.setApplicationName("missing-rest-controller")
27+
.setApplicationVersion("0.1-SNAPSHOT")
28+
.setLogRecordPredicate(r -> "io.quarkus.spring.web.deployment.SpringWebProcessor".equals(r.getLoggerName()));
29+
30+
@ProdBuildResults
31+
private ProdModeTestResults prodModeTestResults;
32+
33+
@Test
34+
public void testBuildLogs() {
35+
List<LogRecord> buildLogRecords = prodModeTestResults.getRetainedBuildLogRecords();
36+
assertThat(buildLogRecords).isNotEmpty();
37+
assertThat(buildLogRecords).hasOnlyOneElementSatisfying(r -> {
38+
assertThat(r.getMessage())
39+
.contains("but was not annotated with")
40+
.contains(NonAnnotatedController.class.getName())
41+
.doesNotContain(ProperController.class.getName());
42+
});
43+
}
44+
45+
@RequestMapping("/non")
46+
public static class NonAnnotatedController {
47+
48+
@GetMapping("/hello")
49+
public String greet() {
50+
return "hello";
51+
}
52+
}
53+
54+
@RestController
55+
@RequestMapping("/proper")
56+
public static class ProperController {
57+
58+
@GetMapping("/hello")
59+
public String greet() {
60+
return "hello";
61+
}
62+
}
63+
}

0 commit comments

Comments
 (0)