Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CRJVM208 - Avoid spring repository calls in loops with Stream #157

Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public class JavaRulesDefinition implements RulesDefinition {
public void define(Context context) {
NewRepository repository = context.createRepository(REPOSITORY_KEY, LANGUAGE).setName(NAME);

SonarRuntime sonarRuntime = SonarRuntimeImpl.forSonarQube(Version.create(9, 8), SonarQubeSide.SCANNER, SonarEdition.DEVELOPER);
SonarRuntime sonarRuntime = SonarRuntimeImpl.forSonarQube(Version.create(9, 9), SonarQubeSide.SCANNER, SonarEdition.DEVELOPER);

RuleMetadataLoader ruleMetadataLoader = new RuleMetadataLoader(RESOURCE_BASE_PATH, sonarRuntime);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,14 @@
*/
package fr.greencodeinitiative.java;

import fr.greencodeinitiative.java.checks.*;
import org.sonar.plugins.java.api.JavaCheck;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import fr.greencodeinitiative.java.checks.ArrayCopyCheck;
import fr.greencodeinitiative.java.checks.AvoidConcatenateStringsInLoop;
import fr.greencodeinitiative.java.checks.AvoidFullSQLRequest;
import fr.greencodeinitiative.java.checks.AvoidGettingSizeCollectionInLoop;
import fr.greencodeinitiative.java.checks.AvoidMultipleIfElseStatement;
import fr.greencodeinitiative.java.checks.AvoidRegexPatternNotStatic;
import fr.greencodeinitiative.java.checks.AvoidSQLRequestInLoop;
import fr.greencodeinitiative.java.checks.AvoidSetConstantInBatchUpdate;
import fr.greencodeinitiative.java.checks.AvoidSpringRepositoryCallInLoopCheck;
import fr.greencodeinitiative.java.checks.AvoidStatementForDMLQueries;
import fr.greencodeinitiative.java.checks.AvoidUsageOfStaticCollections;
import fr.greencodeinitiative.java.checks.AvoidUsingGlobalVariablesCheck;
import fr.greencodeinitiative.java.checks.FreeResourcesOfAutoCloseableInterface;
import fr.greencodeinitiative.java.checks.IncrementCheck;
import fr.greencodeinitiative.java.checks.InitializeBufferWithAppropriateSize;
import fr.greencodeinitiative.java.checks.NoFunctionCallWhenDeclaringForLoop;
import fr.greencodeinitiative.java.checks.OptimizeReadFileExceptions;
import fr.greencodeinitiative.java.checks.UnnecessarilyAssignValuesToVariables;
import fr.greencodeinitiative.java.checks.UseCorrectForLoop;
import org.sonar.plugins.java.api.JavaCheck;

public final class RulesList {

private RulesList() {
Expand Down Expand Up @@ -77,7 +59,8 @@ public static List<Class<? extends JavaCheck>> getJavaChecks() {
AvoidUsingGlobalVariablesCheck.class,
AvoidSetConstantInBatchUpdate.class,
FreeResourcesOfAutoCloseableInterface.class,
AvoidMultipleIfElseStatement.class
AvoidMultipleIfElseStatement.class,
AvoidSpringRepositoryCallInStreamCheck.class
));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package fr.greencodeinitiative.java.checks;

import org.sonar.check.Priority;
import org.sonar.check.Rule;
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
import org.sonar.plugins.java.api.semantic.MethodMatchers;
import org.sonar.plugins.java.api.tree.*;

import java.util.List;
import java.util.Objects;
import java.util.Optional;

@Rule(key = "ECCRJVM208",
name = "Developpement",
description = AvoidSpringRepositoryCallInStreamCheck.REPOSITORY_METHODS_INSIDE_STREAM,
priority = Priority.MINOR,
tags = {"bug" })
public class AvoidSpringRepositoryCallInStreamCheck extends IssuableSubscriptionVisitor {

public static final String REPOSITORY_METHODS_INSIDE_STREAM = "Avoid calling repository methods inside stream";
private static final String SPRING_REPOSITORY = "org.springframework.data.repository.Repository";

public static final String STREAM = "Stream";

private static final MethodMatchers REPOSITORY_METHOD =
MethodMatchers.create().ofSubTypes(SPRING_REPOSITORY).anyName().withAnyParameters()
.build();
@Override
public List<Tree.Kind> nodesToVisit() {
return List.of(Tree.Kind.LAMBDA_EXPRESSION);
}

@Override
public void visitNode(Tree tree) {

LambdaExpressionTree lambda = (LambdaExpressionTree) tree;
if (Objects.requireNonNull(Objects.requireNonNull(Objects.requireNonNull(lambda.parent()).parent()).parent()).is(Tree.Kind.MEMBER_SELECT)){
String methodName = ( (MemberSelectExpressionTree) Objects.requireNonNull(Objects.requireNonNull(Objects.requireNonNull(lambda.parent()).parent()).parent())).expression().symbolType().name();
if (methodName.equals(STREAM)){
lambda.body().accept(new AvoidSpringRepositoryCallInStreamCheckVisitor());
}
}

}



private class AvoidSpringRepositoryCallInStreamCheckVisitor extends BaseTreeVisitor {
@Override
public void visitMethodInvocation(MethodInvocationTree tree) {
if (REPOSITORY_METHOD.matches(tree)) {
reportIssue(tree, REPOSITORY_METHODS_INSIDE_STREAM);
} else {
super.visitMethodInvocation(tree);
}
}

}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<p>Using Spring repository in loop induced unnecessary calculation by the cpu so unless energy consumption.</p>
<h2>Noncompliant Code Example</h2>
<pre>
private final List&lt;Integer&gt; ids = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

ids.stream().map(element ->
employeeRepository.findById(element).orElse(null);
).collect(Collectors.toList());


</pre>
<h2>Compliant Solution</h2>
<pre>
private final List&lt;Integer&gt; ids = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List&lt;Employee&gt; employees = employeeRepository.findAllById(ids);
</pre>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"title": "Avoid Spring repository call in stream",
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "50min"
},
"tags": [
"eco-conception"
],
"defaultSeverity": "Minor"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package fr.greencodeinitiative.java.checks;

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.*;
import java.util.stream.Collectors;

public class AvoidSpringRepositoryCallInStreamCheck {
private EmployeeRepository employeeRepository;

public List<Employee> smellGetAllEmployeesByIds(List<Integer> ids) {
return ids.stream().map(element ->
{
return employeeRepository.findById(element).orElse(null);// Noncompliant
}
).collect(Collectors.toList());
}




public List<Employee> smellGetAllEmployeesByIdsWithoutStream(List<Integer> ids) {
return employeeRepository.findAllById(ids);// Compliant
}


public class Employee {}

public interface EmployeeRepository extends JpaRepository<Employee, Integer> {}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ void checkNumberRules() {
final JavaCheckRegistrar registrar = new JavaCheckRegistrar();
registrar.register(context);

assertThat(context.checkClasses()).hasSize(19);
assertThat(context.checkClasses()).hasSize(20);
assertThat(context.testCheckClasses()).isEmpty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private static class MockedSonarRuntime implements SonarRuntime {

@Override
public Version getApiVersion() {
return Version.create(9, 9);
return Version.create(9, 10);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package fr.greencodeinitiative.java.checks;

import fr.greencodeinitiative.java.utils.FilesUtils;
import org.junit.Rule;
import org.junit.jupiter.api.Test;
import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.java.checks.verifier.CheckVerifier;

class AvoidSpringRepositoryCallInStreamCheckTest {
@Rule
public LogTester logTester = new LogTester().setLevel(LoggerLevel.DEBUG);
@Test
void test() {
CheckVerifier.newVerifier()
.onFile("src/test/files/AvoidSpringRepositoryCallInStreamCheck.java")
.withCheck(new AvoidSpringRepositoryCallInStreamCheck())
.withClassPath(FilesUtils.getClassPath("target/test-jars"))
.verifyIssues();
}

}