Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions its/ruling/src/test/java/org/sonar/java/it/AutoScanTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,15 @@ public void javaCheckTestSources() throws Exception {
softly.assertThat(newTotal).isEqualTo(knownTotal);
softly.assertThat(rulesCausingFPs).hasSize(6);
softly.assertThat(rulesNotReporting).hasSize(7);
softly.assertThat(rulesSilenced).hasSize(76);
softly.assertThat(rulesSilenced).hasSize(77);

/**
* 4. Check total number of differences (FPs + FNs)
*
* No differences would mean that we find the same issues with and without the bytecode and libraries
*/
String differences = Files.readString(pathFor(TARGET_ACTUAL + PROJECT_KEY + "-no-binaries_differences"));
softly.assertThat(differences).isEqualTo("Issues differences: 3481");
softly.assertThat(differences).isEqualTo("Issues differences: 3483");

softly.assertAll();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2909,6 +2909,12 @@
"falseNegatives": 5,
"falsePositives": 0
},
{
"ruleKey": "S6817",
"hasTruePositives": false,
"falseNegatives": 2,
"falsePositives": 0
},
{
"ruleKey": "S6818",
"hasTruePositives": false,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package checks.spring;

import javax.annotation.Nullable;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;

@Configuration
public class AsyncMethodsOnConfigurationClassCheck {

@Async // Noncompliant [[sc=3;ec=9;quickfixes=qf1]] {{Remove this "@Async" annotation from this method.}}
// fix@qf1 {{Remove "@Async"}}
// edit@qf1 [[sc=3;ec=9]] {{}}
public void asyncMethod() {
}

public void method() { // Compliant
}

@Nullable
@Async // Noncompliant
public void someMethod() {
}

}

class NotAConfigurationClass {

@Async // Compliant
public void asyncMethod() {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,13 @@
import org.sonar.java.checks.serialization.SerializableObjectInSessionCheck;
import org.sonar.java.checks.serialization.SerializableSuperConstructorCheck;
import org.sonar.java.checks.spring.AsyncMethodsCalledViaThisCheck;
import org.sonar.java.checks.spring.AsyncMethodsOnConfigurationClassCheck;
import org.sonar.java.checks.spring.AsyncMethodsReturnTypeCheck;
import org.sonar.java.checks.spring.AutowiredOnMultipleConstructorsCheck;
import org.sonar.java.checks.spring.AutowiredOnConstructorWhenMultipleConstructorsCheck;
import org.sonar.java.checks.spring.AutowiredOnMultipleConstructorsCheck;
import org.sonar.java.checks.spring.ControllerWithSessionAttributesCheck;
import org.sonar.java.checks.spring.ModelAttributeNamingConventionForSpELCheck;
import org.sonar.java.checks.spring.FieldDependencyInjectionCheck;
import org.sonar.java.checks.spring.ModelAttributeNamingConventionForSpELCheck;
import org.sonar.java.checks.spring.OptionalRestParametersShouldBeObjectsCheck;
import org.sonar.java.checks.spring.PersistentEntityUsedAsRequestParameterCheck;
import org.sonar.java.checks.spring.RequestMappingMethodPublicCheck;
Expand Down Expand Up @@ -283,6 +284,7 @@ public final class CheckList {
AssertionsInProductionCodeCheck.class,
AssertsOnParametersOfPublicMethodCheck.class,
AsyncMethodsCalledViaThisCheck.class,
AsyncMethodsOnConfigurationClassCheck.class,
AsyncMethodsReturnTypeCheck.class,
AtLeastOneConstructorCheck.class,
AuthorizationsStrongDecisionsCheck.class,
Expand Down Expand Up @@ -945,8 +947,7 @@ public final class CheckList {
UnusedPrivateFieldCheck.class,
VerifiedServerHostnamesCheck.class,
VolatileNonPrimitiveFieldCheck.class,
WeakSSLContextCheck.class
);
WeakSSLContextCheck.class);

private CheckList() {
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* SonarQube Java
* Copyright (C) 2012-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.java.checks.spring;

import java.util.List;
import org.sonar.check.Rule;
import org.sonar.java.checks.helpers.QuickFixHelper;
import org.sonar.java.reporting.JavaQuickFix;
import org.sonar.java.reporting.JavaTextEdit;
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
import org.sonar.plugins.java.api.tree.ClassTree;
import org.sonar.plugins.java.api.tree.MethodTree;
import org.sonar.plugins.java.api.tree.Tree;

@Rule(key = "S6817")
public class AsyncMethodsOnConfigurationClassCheck extends IssuableSubscriptionVisitor {

@Override
public List<Tree.Kind> nodesToVisit() {
return List.of(Tree.Kind.CLASS);
}

@Override
public void visitNode(Tree tree) {
ClassTree classTree = (ClassTree) tree;
boolean isConfiguration = classTree.modifiers().annotations().stream()
.anyMatch(annotation -> annotation.annotationType().symbolType().is("org.springframework.context.annotation.Configuration"));

if (isConfiguration) {
classTree.members().stream()
.filter(member -> member.is(Tree.Kind.METHOD))
.map(MethodTree.class::cast)
.forEach(member -> member.modifiers().annotations().stream()
.filter(annotation -> annotation.annotationType().symbolType().is("org.springframework.scheduling.annotation.Async"))
.findFirst()
.ifPresent(annotation -> QuickFixHelper.newIssue(context)
.forRule(this)
.onTree(annotation)
.withMessage("Remove this \"@Async\" annotation from this method.")
.withQuickFix(() -> JavaQuickFix.newQuickFix("Remove \"@Async\"")
.addTextEdit(JavaTextEdit.removeTree(annotation))
.build())
.report()));
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<h2>Why is this an issue?</h2>
<p><code>@Configuration</code> is a class-level annotation indicating that an object is a source of bean definitions. <code>@Configuration</code>
classes declare beans through <code>@Bean</code>-annotated methods. Calls to <code>@Bean</code> methods on <code>@Configuration</code> classes can
also be used to define inter-bean dependencies. The <code>@Bean</code> annotation indicates that a method instantiates, configures, and initializes a
new object to be managed by the Spring IoC container.</p>
<p>Annotating a method of a bean with <code>@Async</code> will make it execute in a separate thread. In other words, the caller will not wait for the
completion of the called method.</p>
<p>The <code>@Async</code> annotation is not supported on methods declared within a <code>@Configuration</code> class. This is because
<code>@Async</code> methods are typically used for asynchronous processing, and they require certain infrastructure to be set up, which may not be
available or appropriate in a <code>@Configuration</code> class.</p>
<h2>How to fix it</h2>
<p>Don’t use <code>@Async</code> annotations on methods of <code>@Configuration</code> classes.</p>
<h3>Code examples</h3>
<h4>Noncompliant code example</h4>
<pre data-diff-id="1" data-diff-type="noncompliant">
@EnableAsync
@Configuration
public class MyConfiguration {

@Async // Noncompliant - This is not allowed
public void asyncMethod() {
// ...
}
}
</pre>
<h4>Compliant solution</h4>
<pre data-diff-id="1" data-diff-type="compliant">
@EnableAsync
@Configuration
public class MyConfiguration {

public void method() {
// ...
}
}
</pre>
<h2>Resources</h2>
<h3>Documentation</h3>
<ul>
<li> <a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/Async.html">Spring
Framework - @Async</a> </li>
<li> <a href="https://docs.spring.io/spring-framework/reference/core/beans/java/configuration-annotation.html">Spring Framework - Using the
@Configuration annotation</a> </li>
<li> <a href="https://docs.spring.io/spring-framework/reference/core/beans/java/basic-concepts.html">Spring Framework - Basic Concepts: @Bean and
@Configuration</a> </li>
</ul>
<h3>Articles &amp; blog posts</h3>
<ul>
<li> <a href="https://www.baeldung.com/spring-async">Baeldung - How To Do @Async in Spring</a> </li>
</ul>

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"title": "Use of the \"@Async\" annotation on methods declared within a \"@Configuration\" class in Spring Boot",
"type": "BUG",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"spring"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-6817",
"sqKey": "S6817",
"scope": "Main",
"quickfix": "covered",
"code": {
"impacts": {
"RELIABILITY": "HIGH"
},
"attribute": "LOGICAL"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,7 @@
"S6810",
"S6813",
"S6814",
"S6817",
"S6818",
"S6829",
"S6837"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* SonarQube Java
* Copyright (C) 2012-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.java.checks.spring;

import org.junit.jupiter.api.Test;
import org.sonar.java.checks.verifier.internal.InternalCheckVerifier;

import static org.sonar.java.checks.verifier.TestUtils.mainCodeSourcesPath;

class AsyncMethodsOnConfigurationClassCheckTest {

@Test
void test() {
InternalCheckVerifier.newInstance()
.onFile(mainCodeSourcesPath("checks/spring/AsyncMethodsOnConfigurationClassCheck.java"))
.withCheck(new AsyncMethodsOnConfigurationClassCheck())
.withQuickFixes()
.verifyIssues();
}

}