diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ce62a8..537ca76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Add new rule EC1245 - Make non reassigned variables constants +- Add new rule EC205 - Force Lazy fetch types for JPA associations +- Add new rule EC1245 - Avoid energy consuming methods ### Changed diff --git a/pom.xml b/pom.xml index fa0c7e9..44e0519 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ 1.7 - 1.5.1 + 1.5.7-SNAPSHOT diff --git a/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java b/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java index f4ef343..8b3a140 100644 --- a/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java +++ b/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java @@ -17,28 +17,14 @@ */ package fr.greencodeinitiative.java; -import java.util.Collections; -import java.util.List; - -import fr.greencodeinitiative.java.checks.ArrayCopyCheck; -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.AvoidSpringRepositoryCallInLoopOrStreamCheck; -import fr.greencodeinitiative.java.checks.AvoidStatementForDMLQueries; -import fr.greencodeinitiative.java.checks.AvoidUsageOfStaticCollections; -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.*; import org.sonar.plugins.java.api.CheckRegistrar; import org.sonar.plugins.java.api.JavaCheck; import org.sonarsource.api.sonarlint.SonarLintSide; +import java.util.Collections; +import java.util.List; + /** * Provide the "checks" (implementations of rules) classes that are going be executed during * source code analysis. @@ -62,7 +48,10 @@ public class JavaCheckRegistrar implements CheckRegistrar { InitializeBufferWithAppropriateSize.class, AvoidSetConstantInBatchUpdate.class, FreeResourcesOfAutoCloseableInterface.class, - AvoidMultipleIfElseStatement.class + AvoidMultipleIfElseStatement.class, + AvoidEnergyConsumingMethods.class, + MakeNonReassignedVariablesConstants.class, + ForceLazyFetchTypeForJPA.class ); /** diff --git a/src/main/java/fr/greencodeinitiative/java/checks/AvoidEnergyConsumingMethods.java b/src/main/java/fr/greencodeinitiative/java/checks/AvoidEnergyConsumingMethods.java new file mode 100644 index 0000000..d721da9 --- /dev/null +++ b/src/main/java/fr/greencodeinitiative/java/checks/AvoidEnergyConsumingMethods.java @@ -0,0 +1,199 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.InputFileScannerContext; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.semantic.Symbol; +import org.sonar.plugins.java.api.tree.BaseTreeVisitor; +import org.sonar.plugins.java.api.tree.MethodInvocationTree; +import org.sonar.plugins.java.api.tree.MethodTree; +import org.sonar.plugins.java.api.tree.Tree; +import org.sonarsource.analyzer.commons.annotations.DeprecatedRuleKey; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * This rule aims to identify and flag methods in Java code that have high energy consumption + * due to the use of computationally expensive operations. The goal is to encourage developers + * to write more energy-efficient code by limiting the use of costly operations within a single method. + *
+ *
+ * + * The rule works by assigning a "cost" to specific expensive operations commonly recognized for their + * high computational demands. These operations include: + * + *
+ * + * For each occurrence of these operations within a method, a predefined number of points is assigned. + * If the total points for a method exceed a certain threshold (e.g., 5 points), the method is considered + * to be energy-inefficient. A warning is then issued to indicate that the method may have high energy + * consumption and suggest the developer to refactor or optimize the method. + *
+ *
+ * + * Example: + *
+ * {@code
+ * public class Example {
+ *     public void exampleMethod() {
+ *         String data = "example";
+ *         data = data.replaceAll("e", "a"); // 1 point
+ *         FileInputStream fis = new FileInputStream("file.txt"); // 1 point
+ *         // Other operations
+ *     }
+ * }
+ * }
+ * 
+ *
+ * In the above example, the `exampleMethod` uses `String.replaceAll()` and `FileInputStream`, + * each contributing 1 point to the total score. If additional expensive operations are added + * and the total score exceeds 5 points, the rule will flag this method. + *
+ *
+ * + * This rule helps developers identify potential performance bottlenecks and encourages + * the adoption of more efficient coding practices to reduce the energy footprint of their applications. + * + * @author Massil TAGUEMOUT - CGI FRANCE + */ +@Rule(key = "EC1245") +@DeprecatedRuleKey(repositoryKey = "greencodeinitiative-java", ruleKey = "EC1245") +public class AvoidEnergyConsumingMethods extends IssuableSubscriptionVisitor { + + private static final int ENERGY_THRESHOLD = 5; + private static final Map COSTLY_METHODS = new HashMap<>(); + + static { + // Reference costly methods in the map and their points + // Reflection methods + COSTLY_METHODS.put("java.lang.Class.forName", 1); + COSTLY_METHODS.put("java.lang.reflect.Method.invoke", 1); + COSTLY_METHODS.put("java.lang.reflect.Field.get", 1); + COSTLY_METHODS.put("java.lang.reflect.Field.set", 1); + COSTLY_METHODS.put("java.lang.reflect.Constructor.newInstance", 1); + COSTLY_METHODS.put("java.lang.Class.getMethods", 1); + COSTLY_METHODS.put("java.lang.Class.getDeclaredMethods", 1); + COSTLY_METHODS.put("java.lang.Class.getFields", 1); + COSTLY_METHODS.put("java.lang.Class.getDeclaredFields", 1); + COSTLY_METHODS.put("java.lang.Class.getConstructors", 1); + COSTLY_METHODS.put("java.lang.Class.getDeclaredConstructors", 1); + + // String methods + COSTLY_METHODS.put("java.lang.String.concat", 1); + COSTLY_METHODS.put("java.lang.String.substring", 1); + COSTLY_METHODS.put("java.lang.String.replace", 1); + COSTLY_METHODS.put("java.lang.String.matches", 1); + COSTLY_METHODS.put("java.lang.String.split", 1); + + // Collections + COSTLY_METHODS.put("java.util.Vector.add", 1); + COSTLY_METHODS.put("java.util.Vector.get", 1); + COSTLY_METHODS.put("java.util.Vector.remove", 1); + COSTLY_METHODS.put("java.util.Hashtable.put", 1); + COSTLY_METHODS.put("java.util.Hashtable.get", 1); + COSTLY_METHODS.put("java.util.Hashtable.remove", 1); + COSTLY_METHODS.put("java.util.Collections.synchronizedList", 1); + COSTLY_METHODS.put("java.util.Collections.synchronizedMap", 1); + COSTLY_METHODS.put("java.util.Collections.synchronizedSet", 1); + COSTLY_METHODS.put("java.util.Collections.synchronizedSortedMap", 1); + COSTLY_METHODS.put("java.util.Collections.synchronizedSortedSet", 1); + + // File I/O + COSTLY_METHODS.put("java.io.FileInputStream.read", 1); + COSTLY_METHODS.put("java.io.FileOutputStream.write", 1); + COSTLY_METHODS.put("java.io.FileReader.read", 1); + COSTLY_METHODS.put("java.io.FileWriter.write", 1); + COSTLY_METHODS.put("java.io.RandomAccessFile.read", 1); + COSTLY_METHODS.put("java.io.RandomAccessFile.write", 1); + COSTLY_METHODS.put("java.nio.file.Files.readAllBytes", 1); + COSTLY_METHODS.put("java.nio.file.Files.write", 1); + + // Network I/O + COSTLY_METHODS.put("java.net.Socket.getInputStream", 1); + COSTLY_METHODS.put("java.net.Socket.getOutputStream", 1); + COSTLY_METHODS.put("java.net.ServerSocket.accept", 1); + COSTLY_METHODS.put("java.net.HttpURLConnection.connect", 1); + COSTLY_METHODS.put("java.net.HttpURLConnection.getInputStream", 1); + COSTLY_METHODS.put("java.net.HttpURLConnection.getOutputStream", 1); + + // JNI + COSTLY_METHODS.put("java.lang.System.loadLibrary", 1); + } + + @Override + public List nodesToVisit() { + return List.of(Tree.Kind.METHOD); + } + + @Override + public void visitNode(Tree tree) { + if (tree.is(Tree.Kind.METHOD)) { + MethodTree methodTree = (MethodTree) tree; + int score = calculateMethodEnergyScore(methodTree); + + if (score > ENERGY_THRESHOLD) { + reportIssue(methodTree.simpleName(), + "This method is considered energy-consuming with a score of " + score + + " (maximum score of " + ENERGY_THRESHOLD + " recommended)"); + } + } + } + + private int calculateMethodEnergyScore(MethodTree methodTree) { + final AvoidEnergyConsumingMethodsVisitor visitor = new AvoidEnergyConsumingMethodsVisitor(); + methodTree.accept(visitor); + return visitor.getScore(); + } + + @Override + public boolean scanWithoutParsing(InputFileScannerContext inputFileScannerContext) { + return super.scanWithoutParsing(inputFileScannerContext); + } + + + private static class AvoidEnergyConsumingMethodsVisitor extends BaseTreeVisitor { + + private int score = 0; + + @Override + public void visitMethodInvocation(MethodInvocationTree methodInvocationTree) { + Symbol symbol = methodInvocationTree.methodSymbol(); + String fullyQualifiedName = symbol.owner().type().fullyQualifiedName() + "." + symbol.name(); + + score += COSTLY_METHODS.getOrDefault(fullyQualifiedName, 0); + super.visitMethodInvocation(methodInvocationTree); + } + + public int getScore() { + return score; + } + + } +} diff --git a/src/main/java/fr/greencodeinitiative/java/checks/ForceLazyFetchTypeForJPA.java b/src/main/java/fr/greencodeinitiative/java/checks/ForceLazyFetchTypeForJPA.java new file mode 100644 index 0000000..29534f1 --- /dev/null +++ b/src/main/java/fr/greencodeinitiative/java/checks/ForceLazyFetchTypeForJPA.java @@ -0,0 +1,145 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.tree.*; + +import java.lang.reflect.Field; +import java.util.List; + + +/** + * This rule enforces the use of `FetchType.LAZY` for JPA associations to minimize energy consumption + * and improve application performance. The rule targets the following JPA annotations: + *
    + *
  • {@code @OneToMany}
  • + *
  • {@code @ManyToMany}
  • + *
  • {@code @OneToOne}
  • + *
  • {@code @ManyToOne}
  • + *
+ * + * JPA provides two types of fetch strategies for associations: `FetchType.EAGER` and `FetchType.LAZY`. + * - `FetchType.EAGER` loads the related entities immediately, which can result in significant performance + * overhead and increased energy consumption, especially when loading large collections or deeply nested + * entity graphs. + * - `FetchType.LAZY` defers the loading of the related entities until they are accessed, reducing the initial + * load time, memory usage, and energy consumption. + *
+ *
+ * + * This rule scans for the above-mentioned JPA annotations and checks if the `fetch` attribute is set to + * `FetchType.LAZY`. If the `fetch` attribute is either not defined or not set to `FetchType.LAZY`, the rule + * issues a warning. + *
+ *
+ * + * Example: + *
+ * {@code
+ * @Entity
+ * public class Order {
+ *     @OneToMany(fetch = FetchType.LAZY) // Correct usage
+ *     private List orderItems;
+ *
+ *     @ManyToOne // Noncompliant, should specify fetch = FetchType.LAZY
+ *     private Customer customer;
+ * }
+ * }
+ * 
+ * + * Benefits of enforcing `FetchType.LAZY`: + *
    + *
  • Reduces initial data load, decreasing memory usage and startup time.
  • + *
  • Prevents unnecessary data retrieval, lowering CPU and IO usage.
  • + *
  • Improves overall application performance and responsiveness.
  • + *
  • Minimizes energy consumption by avoiding the overhead of loading large amounts of data eagerly.
  • + *
+ * + * By ensuring that JPA associations use `FetchType.LAZY`, developers can create more efficient and + * scalable applications that are better suited for high-performance environments with lower energy footprints. + * + * @author Massil TAGUEMOUT - CGI FRANCE + */ +@Rule(key = "EC205") +public class ForceLazyFetchTypeForJPA extends IssuableSubscriptionVisitor { + + @Override + public List nodesToVisit() { + return List.of(Tree.Kind.CLASS); + } + + @Override + public void visitNode(Tree tree) { + ClassTree classTree = (ClassTree) tree; + for (Tree member : classTree.members()) { + if (member.is(Tree.Kind.VARIABLE)) { + VariableTree variableTree = (VariableTree) member; + checkFetchTypeLazy(variableTree); + } + } + } + + private void checkFetchTypeLazy(VariableTree variableTree) { + List annotations = variableTree.modifiers().annotations(); + for (AnnotationTree annotation : annotations) { + String annotationName = annotation.annotationType().toString(); + if (isJpaAssociationAnnotation(annotationName)) { + checkLazyFetchType(annotation, variableTree); + } + } + } + + private boolean isJpaAssociationAnnotation(String annotationName) { + return annotationName.equals("OneToMany") || + annotationName.equals("ManyToMany") || + annotationName.equals("OneToOne") || + annotationName.equals("ManyToOne"); + } + + private void checkLazyFetchType(AnnotationTree annotation, VariableTree variableTree) { + boolean fetchTypeDefined = false; + boolean fetchTypeLazy = false; + + for (ExpressionTree argument : annotation.arguments()) { + if (argument.is(Tree.Kind.ASSIGNMENT)) { + AssignmentExpressionTree assignment = (AssignmentExpressionTree) argument; + if (assignment.variable().toString().equals("fetch")) { + fetchTypeDefined = true; + ExpressionTree expression = assignment.expression(); + if (expression.is(Tree.Kind.MEMBER_SELECT)) { + MemberSelectExpressionTree memberSelect = (MemberSelectExpressionTree) expression; + String expressionText = ""; + try { + Field expressionField = memberSelect.getClass().getDeclaredField("expression"); + expressionField.setAccessible(true); + expressionText = expressionField.get(memberSelect).toString(); + } catch (NoSuchFieldException | IllegalAccessException ignored) {} + + fetchTypeLazy = memberSelect.identifier().name().equals("LAZY") && expressionText.equals("FetchType"); + } + } + } + } + + if (!fetchTypeDefined || !fetchTypeLazy) { + reportIssue(variableTree.simpleName(), "FetchType should be explicitly set to LAZY for JPA associations."); + } + } +} diff --git a/src/main/java/fr/greencodeinitiative/java/checks/MakeNonReassignedVariablesConstants.java b/src/main/java/fr/greencodeinitiative/java/checks/MakeNonReassignedVariablesConstants.java new file mode 100644 index 0000000..c285090 --- /dev/null +++ b/src/main/java/fr/greencodeinitiative/java/checks/MakeNonReassignedVariablesConstants.java @@ -0,0 +1,120 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.tree.*; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + + +/** + * This rule aims to promote the use of constants in Java code by identifying local variables + * that are never reassigned after their initial assignment and recommending their conversion + * to constants using the `final` modifier. By making variables constants, developers can + * not only enhance code readability and maintainability but also improve the energy efficiency + * of their applications. + *
+ *
+ * The rule functions by analyzing local variable declarations within methods. If a variable is + * declared and assigned a value, but never reassigned afterwards, it is a candidate for being + * marked as `final`. This practice can help the compiler optimize the code, leading to potential + * energy savings during execution. + *
+ *
+ * Example: + *
+ * {@code
+ * public class Example {
+ *     public void exampleMethod() {
+ *         int x = 10;
+ *         String y = "example"; // Noncompliant, should be final
+ *         x = 20; // This reassignment means x should not be final
+ *     }
+ * }
+ * }
+ * 
+ * + * In the above example, the variable `y` is never reassigned after its initial assignment, + * so it should be declared as `final`. The variable `x`, on the other hand, is reassigned, + * so it should not be declared as `final`. + *
+ *
+ * + * By converting such non-reassigned variables to constants, the following benefits can be achieved: + *
    + *
  • Enhanced Readability: Declaring variables as `final` makes it clear that their value + * will not change after initialization, improving the readability of the code.
  • + *
  • Improved Maintainability: Constants reduce the likelihood of accidental reassignments, + * making the code easier to maintain and less prone to bugs.
  • + *
  • Optimized Performance: The compiler and JIT (Just-In-Time) compiler can apply more + * aggressive optimizations on `final` variables, potentially leading to more efficient bytecode + * and better runtime performance.
  • + *
  • Energy Efficiency: Optimized code typically executes faster and consumes less energy, + * contributing to the overall energy efficiency of the application.
  • + *
+ * + * This rule helps developers write more efficient, robust, and maintainable code by encouraging + * the use of constants where appropriate. + * + * @author Massil TAGUEMOUT - CGI FRANCE + */ +@Rule(key = "EC82") +public class MakeNonReassignedVariablesConstants extends IssuableSubscriptionVisitor { + + @Override + public List nodesToVisit() { + return List.of(Tree.Kind.METHOD, Tree.Kind.CONSTRUCTOR); + } + + @Override + public void visitNode(Tree tree) { + MethodTree methodTree = (MethodTree) tree; + Set reassignedVariables = new HashSet<>(); + Set declaredVariables = new HashSet<>(); + + // Collect reassigned and declared variables + methodTree.accept(new BaseTreeVisitor() { + @Override + public void visitVariable(VariableTree variableTree) { + declaredVariables.add(variableTree); + super.visitVariable(variableTree); + } + + @Override + public void visitAssignmentExpression(AssignmentExpressionTree tree) { + if (tree.variable().is(Tree.Kind.IDENTIFIER)) { + reassignedVariables.add(((IdentifierTree) tree.variable()).name()); + } + super.visitAssignmentExpression(tree); + } + }); + + // Check for variables that are declared but not reassigned and recommend making them final + for (VariableTree variableTree : declaredVariables) { + String variableName = variableTree.simpleName().name(); + if (!reassignedVariables.contains(variableName) && !variableTree.symbol().isFinal()) { + reportIssue(variableTree.simpleName(), "Consider declaring '" + variableName + "' as a constant by using the 'final' modifier."); + } + } + } + +} diff --git a/src/test/files/AvoidEnergyConsumingMethodsCheck.java b/src/test/files/AvoidEnergyConsumingMethodsCheck.java new file mode 100644 index 0000000..171aa7d --- /dev/null +++ b/src/test/files/AvoidEnergyConsumingMethodsCheck.java @@ -0,0 +1,271 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.nio.file.*; +import java.sql.*; +import java.util.*; + +public class AvoidEnergyConsumingMethodsCheck { + + // Reflection Methods Example + public void reflectionMethodsTest_ok() throws Exception { + Class clazz = Class.forName("com.sonar.SomeClass"); + Method method = clazz.getMethod("someMethod"); + } + + public void reflectionMethodsTest_ko() throws Exception { // Noncompliant + Class clazz = Class.forName("com.sonar.SomeClass"); + Method method = clazz.getMethod("someMethod"); + Object instance = clazz.getDeclaredConstructor().newInstance(); + method.invoke(instance); + + Field field = clazz.getDeclaredField("someField"); + field.setAccessible(true); + Object value = field.get(instance); + field.set(instance, value); + field.set(instance, value); + } + + // String Manipulation Example + public void stringManipulationTest_ok() { + String result = ""; + result += "example"; + } + + public void stringManipulationTest_ko() { // Noncompliant + String result = ""; + for (int i = 0; i < 10; i++) { + result += "example"; + } + + String str = "This is a test"; + str = str.replace("test", "sample"); + str = str.substring(5, 10); + boolean matches = str.matches("\\d+"); + String[] parts = str.split(" "); + String[] parts = str.split(" "); + String[] parts = str.split(" "); + } + + // Synchronized Collections Example + public void synchronizedCollectionsTest_ok() { + List list = Collections.synchronizedList(new ArrayList<>()); + list.add(1); + + Map map = Collections.synchronizedMap(new HashMap<>()); + map.put("key1", "value1"); + } + + public void synchronizedCollectionsTest_ko() { // Noncompliant + List list = Collections.synchronizedList(new ArrayList<>()); + list.add(1); + list.add(2); + list.get(0); + + Map map = Collections.synchronizedMap(new HashMap<>()); + map.put("key1", "value1"); + map.get("key1"); + map.remove("key1"); + + Set set = Collections.synchronizedSet(new HashSet<>()); + Set set = Collections.synchronizedSet(new HashSet<>()); + Set set = Collections.synchronizedSet(new HashSet<>()); + Set set = Collections.synchronizedSet(new HashSet<>()); + set.add("item1"); + set.remove("item1"); + } + + // File IO Example + public void fileIOTest_ok() throws IOException { + try (FileInputStream fis = new FileInputStream("file.txt")) { + fis.read(); + } + } + + public void fileIOTest_ko() throws IOException { // Noncompliant + try (FileInputStream fis = new FileInputStream("file.txt")) { + int data = fis.read(); + while (data != -1) { + data = fis.read(); + } + } + + try (FileOutputStream fos = new FileOutputStream("file.txt")) { + fos.write("Hello, World!".getBytes()); + fos.write("Hello, World!".getBytes()); + fos.write("Hello, World!".getBytes()); + } + + Path path = Paths.get("file.txt"); + Path path = Paths.get("file.txt"); + byte[] fileBytes = Files.readAllBytes(path); + Files.write(path, "Hello, World!".getBytes()); + } + + // Network IO Example + public void networkIOTest_ok() throws IOException { + try (Socket socket = new Socket("localhost", 8080)) { + socket.getInputStream(); + } + } + + public void networkIOTest_ko() throws IOException { // Noncompliant + try (Socket socket = new Socket("localhost", 8080)) { + InputStream is = socket.getInputStream(); + OutputStream os = socket.getOutputStream(); + OutputStream os = socket.getOutputStream(); + os.write("Hello".getBytes()); + is.read(); + } + + try (ServerSocket serverSocket = new ServerSocket(8080)) { + Socket clientSocket = serverSocket.accept(); + InputStream is = clientSocket.getInputStream(); + OutputStream os = clientSocket.getOutputStream(); + os.write("Hello".getBytes()); + is.read(); + } + + URL url = new URL("http://example.com"); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.connect(); + connection.connect(); + InputStream is = connection.getInputStream(); + OutputStream os = connection.getOutputStream(); + os.write("Hello".getBytes()); + is.read(); + } + + // JNI Example + public void jniTest_ok() { + System.loadLibrary("nativeLib"); + } + + public void jniTest_ko() { // Noncompliant + System.loadLibrary("nativeLib"); + System.loadLibrary("nativeLib"); + System.loadLibrary("nativeLib"); + System.loadLibrary("nativeLib"); + System.loadLibrary("nativeLib"); + System.loadLibrary("nativeLib"); + } + + // Combined Example + public void combinedTest_ok() throws Exception { + // Reflection + Class clazz = Class.forName("com.sonar.SomeClass"); + Method method = clazz.getMethod("someMethod"); + + // String manipulation + String result = ""; + for (int i = 0; i < 2; i++) { + result += "example"; + } + + // File IO + try (FileInputStream fis = new FileInputStream("file.txt")) { + fis.read(); + } + + // Network IO + try (Socket socket = new Socket("localhost", 8080)) { + socket.getInputStream(); + } + } + + public void combinedTest_ko() throws Exception { // Noncompliant + // Reflection + Class clazz = Class.forName("com.sonar.SomeClass"); + Method method = clazz.getMethod("someMethod"); + Object instance = clazz.getDeclaredConstructor().newInstance(); + method.invoke(instance); + + // String manipulation + String result = ""; + for (int i = 0; i < 10; i++) { + result += "example"; + } + + // File IO + try (FileInputStream fis = new FileInputStream("file.txt")) { + int data = fis.read(); + while (data != -1) { + data = fis.read(); + } + } + + // Network IO + try (Socket socket = new Socket("localhost", 8080)) { + InputStream is = socket.getInputStream(); + OutputStream os = socket.getOutputStream(); + os.write("Hello".getBytes()); + is.read(); + } + } + + // Combined Costly Methods Example + public void combinedCostlyMethodsTest_ok() { + // String manipulation + String result = ""; + for (int i = 0; i < 2; i++) { + result += "example"; + } + + // Reflection + try { + Class clazz = Class.forName("com.sonar.SomeClass"); + Method method = clazz.getMethod("someMethod"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void combinedCostlyMethodsTest_ko() { // Noncompliant + // String manipulation + String result = ""; + for (int i = 0; i < 10; i++) { + result += "example"; + } + + // Reflection + try { + Class clazz1 = Class.forName("com.sonar.SomeClass"); + Class clazz = Class.forName("com.sonar.SomeClass"); + Method method = clazz.getMethod("someMethod"); + Object instance = clazz.getDeclaredConstructor().newInstance(); + method.invoke(instance); + } catch (Exception e) { + e.printStackTrace(); + } + + // File IO + try (FileInputStream fis = new FileInputStream("file.txt")) { + int data = fis.read(); + while (data != -1) { + data = fis.read(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + +} \ No newline at end of file diff --git a/src/test/files/ForceLazyFetchTypeForJPACheck.java b/src/test/files/ForceLazyFetchTypeForJPACheck.java new file mode 100644 index 0000000..d07a8b7 --- /dev/null +++ b/src/test/files/ForceLazyFetchTypeForJPACheck.java @@ -0,0 +1,67 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.ManyToMany; +import javax.persistence.OneToOne; + +@Entity +public class JpaEntity { + + @OneToMany + private List children; // Noncompliant + + @ManyToMany + private Set others; // Noncompliant + + @OneToOne + private OneEntity one; // Noncompliant + + @ManyToOne + private ManyEntity many; // Noncompliant + + @OneToMany(fetch = FetchType.EAGER) + private List someEntitiesEager; // Noncompliant + + @ManyToMany(fetch = FetchType.EAGER) + private Set othersEager; // Noncompliant + + @OneToOne(fetch = FetchType.EAGER) + private OneEntity oneEager; // Noncompliant + + @ManyToOne(fetch = FetchType.EAGER) + private ManyEntity manyEager; // Noncompliant + + @OneToMany(fetch = FetchType.LAZY) + private List someEntitiesLazy; + + @ManyToMany(fetch = FetchType.LAZY) + private Set othersLazy; + + @OneToOne(fetch = FetchType.LAZY) + private OneEntity oneLazy; + + @ManyToOne(fetch = FetchType.LAZY) + private ManyEntity manyLazy; + +} \ No newline at end of file diff --git a/src/test/files/MakeNonReassignedVariablesConstantsCheck.java b/src/test/files/MakeNonReassignedVariablesConstantsCheck.java new file mode 100644 index 0000000..499bd89 --- /dev/null +++ b/src/test/files/MakeNonReassignedVariablesConstantsCheck.java @@ -0,0 +1,50 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +public class MakeNonReassignedVariablesConstantsCheck { + + public void variableReassignedTest() { + int y = 10; + final double PI = 3.14159; + + y = x + 5; + + System.out.println(y); + System.out.println(PI); + } + + public void variableNotReassignedTest() { + int y = 10; // Noncompliant + final double PI = 3.14159; + + System.out.println(y); + System.out.println(PI); + } + + /* + public void nonReassignedVariable_ko(int x, final String name) { + final int y = 10; + final double PI = 3.14159; + + System.out.println(x); + System.out.println(name); + System.out.println(y); + System.out.println(PI); + }*/ +} \ No newline at end of file diff --git a/src/test/java/fr/greencodeinitiative/java/checks/AvoidEnergyConsumingMethodsTest.java b/src/test/java/fr/greencodeinitiative/java/checks/AvoidEnergyConsumingMethodsTest.java new file mode 100644 index 0000000..b2c23f1 --- /dev/null +++ b/src/test/java/fr/greencodeinitiative/java/checks/AvoidEnergyConsumingMethodsTest.java @@ -0,0 +1,33 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; + +class AvoidEnergyConsumingMethodsTest { + + @Test + void test() { + CheckVerifier.newVerifier() + .onFile("src/test/files/AvoidEnergyConsumingMethodsCheck.java") + .withCheck(new AvoidEnergyConsumingMethods()) + .verifyIssues(); + } + +} \ No newline at end of file diff --git a/src/test/java/fr/greencodeinitiative/java/checks/ForceLazyFetchTypeForJPATest.java b/src/test/java/fr/greencodeinitiative/java/checks/ForceLazyFetchTypeForJPATest.java new file mode 100644 index 0000000..8e11261 --- /dev/null +++ b/src/test/java/fr/greencodeinitiative/java/checks/ForceLazyFetchTypeForJPATest.java @@ -0,0 +1,33 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; + +class ForceLazyFetchTypeForJPATest { + + @Test + void test() { + CheckVerifier.newVerifier() + .onFile("src/test/files/ForceLazyFetchTypeForJPACheck.java") + .withCheck(new ForceLazyFetchTypeForJPA()) + .verifyIssues(); + } + +} \ No newline at end of file diff --git a/src/test/java/fr/greencodeinitiative/java/checks/MakeNonReassignedVariablesConstantsTest.java b/src/test/java/fr/greencodeinitiative/java/checks/MakeNonReassignedVariablesConstantsTest.java new file mode 100644 index 0000000..054da3e --- /dev/null +++ b/src/test/java/fr/greencodeinitiative/java/checks/MakeNonReassignedVariablesConstantsTest.java @@ -0,0 +1,33 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; + +class MakeNonReassignedVariablesConstantsTest { + + @Test + void test() { + CheckVerifier.newVerifier() + .onFile("src/test/files/MakeNonReassignedVariablesConstantsCheck.java") + .withCheck(new MakeNonReassignedVariablesConstants()) + .verifyIssues(); + } + +} \ No newline at end of file