Skip to content

Commit 39aa9bd

Browse files
SONARJAVA-5383 Lookup Spring-web dependency version to filter out Spring specific rule (#5053)
Co-authored-by: Leonardo Pilastri <[email protected]> Improves the SpringComposedRequestMappingCheck. Introduces DependencyVersionAware API. Provides a simple example of the usage of the new API.
1 parent 6303741 commit 39aa9bd

File tree

17 files changed

+677
-8
lines changed

17 files changed

+677
-8
lines changed

.cirrus.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ build_task:
8686
build_script:
8787
- *log_develocity_url_script
8888
- source cirrus-env BUILD
89-
- regular_mvn_build_deploy_analyze -Dmaven.test.skip=true -Dsonar.skip=true -pl '!java-checks-test-sources/default,!java-checks-test-sources/aws'
89+
- regular_mvn_build_deploy_analyze -Dmaven.test.skip=true -Dsonar.skip=true -pl '!java-checks-test-sources/default,!java-checks-test-sources/aws,!java-checks-test-sources/spring-web-4.0'
9090
cleanup_before_cache_script: cleanup_maven_repository
9191

9292
test_analyze_task:
@@ -118,7 +118,7 @@ ws_scan_task:
118118
whitesource_script:
119119
- source cirrus-env QA
120120
- source set_maven_build_version $BUILD_NUMBER
121-
- mvn clean install --batch-mode -Dmaven.test.skip=true -pl '!java-checks-test-sources,!java-checks-test-sources/default,!java-checks-test-sources/aws,!java-checks-test-sources/spring-3.2'
121+
- mvn clean install --batch-mode -Dmaven.test.skip=true -pl '!java-checks-test-sources,!java-checks-test-sources/default,!java-checks-test-sources/aws,!java-checks-test-sources/spring-3.2,!java-checks-test-sources/spring-web-4.0'
122122
- source ws_scan.sh
123123
allow_failures: "true"
124124
always:

its/autoscan/src/test/java/org/sonar/java/it/AutoScanTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public void javaCheckTestSources() throws Exception {
120120
.setProjectName(PROJECT_NAME)
121121
.setProjectVersion("0.1.0-SNAPSHOT")
122122
.setSourceEncoding("UTF-8")
123-
.setSourceDirs("aws/src/main/java/,default/src/main/java/,java-17/src/main/java/,spring-3.2/src/main/java/")
123+
.setSourceDirs("aws/src/main/java/,default/src/main/java/,java-17/src/main/java/,spring-3.2/src/main/java/,spring-web-4.0/src/main/java/")
124124
.setTestDirs("default/src/test/java/,test-classpath-reader/src/test/java")
125125
.setProperty("sonar.java.source", "22")
126126
// common properties

java-checks-test-sources/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<module>java-17</module>
2222
<module>test-classpath-reader</module>
2323
<module>spring-3.2</module>
24+
<module>spring-web-4.0</module>
2425
</modules>
2526

2627
<build>
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>org.sonarsource.java</groupId>
8+
<artifactId>java-checks-test-sources</artifactId>
9+
<version>8.13.0-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>spring-web-4.0</artifactId>
13+
<name>SonarQube Java :: Checks Test Sources :: Spring Web 4.0</name>
14+
15+
<properties>
16+
<sonar.skip>true</sonar.skip>
17+
<forbiddenapis.skip>true</forbiddenapis.skip>
18+
<skipTests>true</skipTests>
19+
<maven.deploy.skip>true</maven.deploy.skip>
20+
</properties>
21+
22+
<dependencies>
23+
<dependency>
24+
<groupId>org.springframework</groupId>
25+
<artifactId>spring-web</artifactId>
26+
<version>4.0.0.RELEASE</version>
27+
<scope>provided</scope>
28+
</dependency>
29+
</dependencies>
30+
31+
<profiles>
32+
<profile>
33+
<id>analyze-tests</id>
34+
<properties>
35+
<sonar.skip>false</sonar.skip>
36+
</properties>
37+
</profile>
38+
</profiles>
39+
40+
<build>
41+
<plugins>
42+
<plugin>
43+
<groupId>org.apache.maven.plugins</groupId>
44+
<artifactId>maven-compiler-plugin</artifactId>
45+
<configuration>
46+
<release>22</release>
47+
<source>22</source>
48+
<target>22</target>
49+
</configuration>
50+
</plugin>
51+
<plugin>
52+
<groupId>org.apache.maven.plugins</groupId>
53+
<artifactId>maven-surefire-plugin</artifactId>
54+
<configuration>
55+
<argLine>--enable-preview</argLine>
56+
</configuration>
57+
</plugin>
58+
<plugin>
59+
<groupId>org.simplify4u.plugins</groupId>
60+
<artifactId>sign-maven-plugin</artifactId>
61+
<executions>
62+
<execution>
63+
<id>sign-artifacts</id>
64+
<phase>none</phase>
65+
</execution>
66+
</executions>
67+
</plugin>
68+
<plugin>
69+
<artifactId>maven-jar-plugin</artifactId>
70+
<executions>
71+
<execution>
72+
<id>default-jar</id>
73+
<phase>none</phase>
74+
</execution>
75+
</executions>
76+
</plugin>
77+
<plugin>
78+
<artifactId>maven-source-plugin</artifactId>
79+
<executions>
80+
<execution>
81+
<id>attach-sources</id>
82+
<phase>none</phase>
83+
</execution>
84+
</executions>
85+
</plugin>
86+
<plugin>
87+
<artifactId>maven-javadoc-plugin</artifactId>
88+
<executions>
89+
<execution>
90+
<id>attach-javadocs</id>
91+
<phase>none</phase>
92+
</execution>
93+
</executions>
94+
</plugin>
95+
<plugin>
96+
<artifactId>maven-install-plugin</artifactId>
97+
<executions>
98+
<execution>
99+
<id>default-install</id>
100+
<phase>none</phase>
101+
</execution>
102+
</executions>
103+
</plugin>
104+
<plugin>
105+
<groupId>com.mycila</groupId>
106+
<artifactId>license-maven-plugin</artifactId>
107+
<configuration>
108+
<licenseSets>
109+
<licenseSet>
110+
<excludes>
111+
<exclude>src/main/java/**</exclude>
112+
<exclude>src/test/java/**</exclude>
113+
</excludes>
114+
</licenseSet>
115+
</licenseSets>
116+
</configuration>
117+
</plugin>
118+
</plugins>
119+
</build>
120+
121+
</project>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package checks;
2+
3+
import org.springframework.web.bind.annotation.RequestMapping;
4+
import org.springframework.web.bind.annotation.RequestMethod;
5+
import org.springframework.web.bind.annotation.RestController;
6+
7+
import static org.springframework.web.bind.annotation.RequestMethod.POST;
8+
9+
10+
/**
11+
* This class serves as a sample for older spring-web version (4.0) where @GetMapping, @PostMapping and so on were not present
12+
* hence no issues are expected to be reported here when using the generic @RequestMapping
13+
*/
14+
@RestController
15+
@RequestMapping("/home")
16+
public class SpringComposedRequestMappingCheckSample {
17+
18+
@RequestMapping(method = RequestMethod.GET)
19+
String m2() {
20+
return "";
21+
}
22+
23+
@RequestMapping(method = {POST})
24+
String m3() {
25+
return "";
26+
}
27+
28+
@RequestMapping(method = {RequestMethod.PUT})
29+
String m4() {
30+
return "";
31+
}
32+
33+
@RequestMapping(method = RequestMethod.PATCH)
34+
String m5() {
35+
return "";
36+
}
37+
38+
}

java-checks-testkit/src/main/java/org/sonar/java/checks/Constants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@ private Constants() {
2323

2424
public static final String SPRING_3_2 = "../java-checks-test-sources/spring-3.2";
2525
public static final String SPRING_3_2_CLASSPATH = SPRING_3_2 + "/target/test-classpath.txt";
26+
public static final String SPRING_WEB_4_0_TEST_SOURCES = "../java-checks-test-sources/spring-web-4.0";
27+
public static final String SPRING_WEB_4_0_CLASSPATH = SPRING_WEB_4_0_TEST_SOURCES + "/target/test-classpath.txt";
2628
}

java-checks/src/main/java/org/sonar/java/checks/spring/SpringComposedRequestMappingCheck.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@
2020
import java.util.HashMap;
2121
import java.util.List;
2222
import java.util.Map;
23+
import java.util.Optional;
24+
import java.util.function.Function;
2325
import java.util.stream.Stream;
2426
import org.sonar.check.Rule;
27+
import org.sonar.plugins.java.api.DependencyVersionAware;
2528
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
2629
import org.sonar.plugins.java.api.JavaFileScannerContext;
30+
import org.sonar.plugins.java.api.Version;
2731
import org.sonar.plugins.java.api.semantic.Symbol;
2832
import org.sonar.plugins.java.api.tree.AnnotationTree;
2933
import org.sonar.plugins.java.api.tree.AssignmentExpressionTree;
@@ -34,7 +38,7 @@
3438
import org.sonar.plugins.java.api.tree.Tree;
3539

3640
@Rule(key = "S4488")
37-
public class SpringComposedRequestMappingCheck extends IssuableSubscriptionVisitor {
41+
public class SpringComposedRequestMappingCheck extends IssuableSubscriptionVisitor implements DependencyVersionAware {
3842

3943
private static final Map<String, String> PREFERRED_METHOD_MAP = buildPreferredMethodMap();
4044

@@ -110,4 +114,11 @@ private static Stream<ExpressionTree> extractValues(ExpressionTree argument) {
110114
}
111115
return Stream.of(expression);
112116
}
117+
118+
@Override
119+
public boolean isCompatibleWithDependencies(Function<String, Optional<Version>> dependencyFinder) {
120+
return dependencyFinder.apply("spring-web")
121+
.map(v -> v.isGreaterThanOrEqualTo("4.3"))
122+
.orElse(false);
123+
}
113124
}

java-checks/src/test/java/org/sonar/java/checks/spring/SpringComposedRequestMappingCheckTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717
package org.sonar.java.checks.spring;
1818

1919
import org.junit.jupiter.api.Test;
20+
import org.sonar.java.checks.Constants;
2021
import org.sonar.java.checks.verifier.CheckVerifier;
22+
import org.sonar.java.test.classpath.TestClasspathUtils;
23+
24+
import static org.sonar.java.checks.verifier.TestUtils.mainCodeSourcesPathInModule;
2125

2226
class SpringComposedRequestMappingCheckTest {
2327

@@ -34,4 +38,13 @@ void test() {
3438
.verifyNoIssues();
3539
}
3640

41+
@Test
42+
void test_spring_web_4_0() {
43+
CheckVerifier.newVerifier()
44+
.onFile(mainCodeSourcesPathInModule(Constants.SPRING_WEB_4_0_TEST_SOURCES, "checks/SpringComposedRequestMappingCheckSample.java"))
45+
.withCheck(new SpringComposedRequestMappingCheck())
46+
.withClassPath(TestClasspathUtils.loadFromFile(Constants.SPRING_WEB_4_0_CLASSPATH))
47+
.verifyNoIssues();
48+
}
49+
3750
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* SonarQube Java
3+
* Copyright (C) 2012-2025 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
package org.sonar.java.classpath;
18+
19+
import java.io.File;
20+
import java.util.HashMap;
21+
import java.util.List;
22+
import java.util.Map;
23+
import java.util.Optional;
24+
import java.util.regex.Matcher;
25+
import java.util.regex.Pattern;
26+
import org.sonar.plugins.java.api.Version;
27+
28+
public class DependencyVersionInference {
29+
30+
/** Cache for dependency retrieval. Indexed by artifactId. */
31+
private final Map<String, Optional<Version>> dependencyVersionsCache = new HashMap<>();
32+
33+
static Pattern makeJarPattern(String artifactId) {
34+
return Pattern.compile(artifactId + "-" + VersionImpl.VERSION_REGEX + "\\.jar");
35+
}
36+
37+
public Optional<Version> infer(String artifactId, List<File> classpath) {
38+
return dependencyVersionsCache
39+
.computeIfAbsent(artifactId, key -> infer(makeJarPattern(key), classpath));
40+
}
41+
42+
private static Optional<Version> infer(Pattern jarPattern, List<File> classpath) {
43+
for (File file : classpath) {
44+
Matcher matcher = jarPattern.matcher(file.getName());
45+
if (matcher.matches()) {
46+
return Optional.of(VersionImpl.matcherToVersion(matcher));
47+
}
48+
}
49+
return Optional.empty();
50+
}
51+
}

0 commit comments

Comments
 (0)